Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/agent-config/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,13 @@ export interface PersistedSession {
cacheReadTokens: number
cacheWriteTokens: number
}
/**
* Calibration factor for the in-composer Context gauge — actual
* `input_tokens` / pre-turn estimate from the most recent completed
* turn. Persisted so a resumed session keeps its calibration instead
* of starting from 1.0 again.
*/
contextEstimateScale?: number
}

// ── Main config ─────────────────────────────────────────────────────
Expand Down
94 changes: 94 additions & 0 deletions packages/agent-core/src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,100 @@ export interface ToolCallbacks {
getParentForkContext?: () => ParentForkContext | undefined
}

/**
* Names of every tool registered by `buildTools` itself (i.e. not from
* MCP servers or OAuth connector managers). Used by `categorizeTools`
* to split a flattened tool list into "system" vs. "MCP/connector" for
* the Context gauge popover. Keep this in sync when adding/removing
* built-in tools above. New tools added to `buildAntonCoreTools` should
* also land here.
*/
export const BUILT_IN_TOOL_NAMES = new Set<string>([
// Filesystem + shell + git + http
'shell',
'read',
'write',
'edit',
'glob',
'list',
'grep',
'git',
'http_api',
// Browser + media
'browser',
'image',
'clipboard',
// Web (Anton-native)
'web_search',
'web_research',
// Task / planning
'todo',
'task_tracker',
'plan',
'ask_user',
'artifact',
// Sub-agent / routine
'sub_agent',
'spawn_sub_agent',
'shared_state',
'routine',
'deliver_result',
// Anton platform
'memory',
'notification',
'database',
'publish',
'skill',
'set_session_title',
'update_project_context',
'activate_workflow',
// Pi-SDK low-level (rarely surfaced but possible)
'filesystem',
'process',
'network',
])

export interface CategorizedToolSizes {
/** Built-in tool count. */
systemToolCount: number
/** MCP-server + direct-OAuth-connector tool count. */
mcpToolCount: number
/** Sum of `name + description + JSON.stringify(parameters)` lengths for built-ins. */
systemToolChars: number
/** Same, for MCP/connector tools. */
mcpToolChars: number
}

/**
* Split a flattened `AgentTool[]` (as returned by `buildTools`) into
* built-in vs. MCP/connector buckets and report char-budget per bucket.
* The Context gauge popover divides the chars by 4 to estimate tokens.
*/
export function categorizeTools(tools: AgentTool[]): CategorizedToolSizes {
let systemToolCount = 0
let mcpToolCount = 0
let systemToolChars = 0
let mcpToolChars = 0
for (const tool of tools) {
const isBuiltIn = BUILT_IN_TOOL_NAMES.has(tool.name)
let chars = tool.name.length + (tool.description?.length ?? 0)
try {
chars += JSON.stringify(tool.parameters ?? {}).length
} catch {
// Parameter schemas should always serialize, but be defensive — a
// bad schema must not break the gauge.
}
if (isBuiltIn) {
systemToolCount += 1
systemToolChars += chars
} else {
mcpToolCount += 1
mcpToolChars += chars
}
}
return { systemToolCount, mcpToolCount, systemToolChars, mcpToolChars }
}

export function buildTools(
config: AgentConfig,
callbacks?: ToolCallbacks,
Expand Down
35 changes: 35 additions & 0 deletions packages/agent-core/src/harness/codex-harness-session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,7 @@ export class CodexHarnessSession {
totalTokens?: number
}
last?: { inputTokens?: number; outputTokens?: number; cachedInputTokens?: number }
modelContextWindow?: number
}
}
| undefined
Expand All @@ -1000,6 +1001,40 @@ export class CodexHarnessSession {
cacheWriteTokens: 0,
},
})

// Drive the in-composer Context gauge for harness sessions. The
// harness CLI doesn't expose how its own prompt is split, so the
// popover renders a 2-row breakdown (Messages + Free space).
//
// Prefer `last.inputTokens` (per-turn input that just hit the model)
// over `total.inputTokens` for the gauge, because `total` is the
// SUM of input across every turn this thread (including cached
// tokens) — on long conversations it routinely exceeds the actual
// prompt size and would drive the gauge past 100%. Fall back to
// `total` only when `last` isn't reported.
const contextWindow = p?.tokenUsage?.modelContextWindow ?? 0
if (contextWindow > 0) {
const lastInput = p?.tokenUsage?.last?.inputTokens
const totalInput = p?.tokenUsage?.total?.inputTokens ?? 0
const messages = Math.min(
contextWindow,
typeof lastInput === 'number' && lastInput > 0 ? lastInput : totalInput,
)
this.emit({
type: 'context_update',
breakdown: {
contextWindow,
systemPrompt: 0,
systemTools: 0,
mcpTools: 0,
skills: 0,
memoryFiles: 0,
messages,
autocompactBuffer: 0,
source: 'harness',
},
})
}
}

private onCompacted() {
Expand Down
43 changes: 43 additions & 0 deletions packages/agent-core/src/prompt-layers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,49 @@ Your native tools (filesystem, shell, code editing, git, web search, etc.) remai
)
}

// ── Per-layer size accounting (for the Context gauge popover) ──────

/**
* Char-budget breakdown of every layer that contributes to a Pi-SDK
* session's prompt. Returned alongside the assembled string so the
* server can populate `ContextBreakdown` without re-running the layer
* builders. Char counts get divided by 4 at the call site to estimate
* tokens — same heuristic as `estimateMessageTokens` in compaction.ts.
*/
export interface SessionPromptLayerSizes {
identity: number
workspaceRules: number
userRules: number
currentContext: number
surface: number
memory: number
projectMemoryInstructions: number
agentContext: number
connectors: number
projectTypeGuidelines: number
referenceKnowledge: number
skills: number
workflows: number
}

export function emptyPromptLayerSizes(): SessionPromptLayerSizes {
return {
identity: 0,
workspaceRules: 0,
userRules: 0,
currentContext: 0,
surface: 0,
memory: 0,
projectMemoryInstructions: 0,
agentContext: 0,
connectors: 0,
projectTypeGuidelines: 0,
referenceKnowledge: 0,
skills: 0,
workflows: 0,
}
}

// ── High-level entry point ──────────────────────────────────────────

export interface HarnessContextPromptOpts {
Expand Down
Loading