@otto-code/protocol 0.6.3 → 0.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-teams.d.ts +10 -0
- package/dist/agent-teams.js +18 -0
- package/dist/agent-types.d.ts +7 -0
- package/dist/generated/validation/ws-outbound.aot.js +15400 -12953
- package/dist/messages.d.ts +2356 -8
- package/dist/messages.js +408 -1
- package/dist/provider-config.d.ts +12 -0
- package/dist/provider-config.js +15 -0
- package/dist/provider-manifest.js +13 -0
- package/dist/validation/ws-outbound-schema-metadata.d.ts +416 -1
- package/package.json +2 -1
package/dist/messages.js
CHANGED
|
@@ -46,6 +46,28 @@ const MutableStructuredGenerationProviderSchema = z
|
|
|
46
46
|
const MutableMetadataGenerationConfigSchema = z
|
|
47
47
|
.object({
|
|
48
48
|
providers: z.array(MutableStructuredGenerationProviderSchema).default([]),
|
|
49
|
+
// Master switch for daemon-side metadata generation (chat auto-titles,
|
|
50
|
+
// agent progress summaries, and other structured side-generations). Default
|
|
51
|
+
// true preserves today's behavior. Read by the generation path (WP-B).
|
|
52
|
+
enabled: z.boolean().default(true),
|
|
53
|
+
// When true, metadata generation prefers a role-matched Writer personality
|
|
54
|
+
// over the cheap default tier. Default false — cheap-tier routing is the
|
|
55
|
+
// default. Read by the generation routing (WP-B).
|
|
56
|
+
preferWriterPersonalities: z.boolean().default(false),
|
|
57
|
+
})
|
|
58
|
+
.passthrough();
|
|
59
|
+
// Daemon-wide agent behavior toggles. Each maps to a Claude-tier capability;
|
|
60
|
+
// providers that can't honor a setting silently ignore it (WP-E wires the
|
|
61
|
+
// reads). All default true so a fresh host behaves exactly like today.
|
|
62
|
+
const MutableAgentBehaviorsConfigSchema = z
|
|
63
|
+
.object({
|
|
64
|
+
// Native next-prompt predictions (Claude prompt_suggestion stream events).
|
|
65
|
+
promptSuggestions: z.boolean().default(true),
|
|
66
|
+
// Agent-authored progress summaries emitted during a turn.
|
|
67
|
+
agentProgressSummaries: z.boolean().default(true),
|
|
68
|
+
// Default value of an agent's notifyOnFinish when the spawn path leaves it
|
|
69
|
+
// unspecified (the current implicit default).
|
|
70
|
+
notifyOnFinishDefault: z.boolean().default(true),
|
|
49
71
|
})
|
|
50
72
|
.passthrough();
|
|
51
73
|
export const TerminalProfileSchema = z
|
|
@@ -293,16 +315,57 @@ export const ModelTierOverrideSchema = z
|
|
|
293
315
|
tier: ModelTierSchema,
|
|
294
316
|
})
|
|
295
317
|
.passthrough();
|
|
318
|
+
// A remembered provider endpoint: a base URL together with the credential it
|
|
319
|
+
// was saved with, so pointing a provider back at a previous endpoint is one
|
|
320
|
+
// pick instead of re-typing the key. Entries are scoped by the connection
|
|
321
|
+
// env-var pair they belong to (OPENAI_BASE_URL/OPENAI_API_KEY vs
|
|
322
|
+
// ANTHROPIC_BASE_URL/ANTHROPIC_AUTH_TOKEN), which is exactly what the provider
|
|
323
|
+
// settings sheet keys its dropdown off — so every openai-compatible provider
|
|
324
|
+
// entry on the host shares one pool, and Claude-compatible entries share
|
|
325
|
+
// another. Deliberately `z.string()` rather than an enum: a future env-var
|
|
326
|
+
// family must not make old entries unparseable.
|
|
327
|
+
export const SavedProviderEndpointSchema = z
|
|
328
|
+
.object({
|
|
329
|
+
/** Stable identity, `${baseUrlKey}::${baseUrl}` — dedupes on re-save. */
|
|
330
|
+
id: z.string().min(1),
|
|
331
|
+
baseUrlKey: z.string().min(1),
|
|
332
|
+
apiKeyKey: z.string().min(1),
|
|
333
|
+
baseUrl: z.string().min(1),
|
|
334
|
+
apiKey: z.string().default(""),
|
|
335
|
+
/** User-facing name; the UI falls back to the URL when absent. */
|
|
336
|
+
label: z.string().optional(),
|
|
337
|
+
/** Epoch ms of the last save, used to order the dropdown newest-first. */
|
|
338
|
+
savedAt: z.number().optional(),
|
|
339
|
+
})
|
|
340
|
+
.passthrough();
|
|
296
341
|
export const MutableDaemonConfigSchema = z
|
|
297
342
|
.object({
|
|
298
343
|
mcp: z
|
|
299
344
|
.object({
|
|
300
345
|
injectIntoAgents: z.boolean(),
|
|
346
|
+
// Daemon-wide Otto tool-group allowlist for the MCP (Claude) path.
|
|
347
|
+
// undefined = all groups enabled (mirrors openai-compat's per-provider
|
|
348
|
+
// ottoToolGroups semantics). An empty array = no Otto tools. Read by the
|
|
349
|
+
// MCP catalog gating (WP-A).
|
|
350
|
+
toolGroups: z.array(z.enum(OTTO_TOOL_GROUPS)).optional(),
|
|
301
351
|
})
|
|
302
352
|
.passthrough(),
|
|
353
|
+
// Defaults off, matching the daemon's own resolution — browser tools are an
|
|
354
|
+
// explicit opt-in, so an omitted section must never read as on.
|
|
303
355
|
browserTools: MutableBrowserToolsConfigSchema.default({ enabled: false }),
|
|
356
|
+
// Daemon-wide agent behavior toggles (Claude-tier capabilities). Defaults to
|
|
357
|
+
// all-on so a new client parsing an old daemon's config sees today's behavior.
|
|
358
|
+
agentBehaviors: MutableAgentBehaviorsConfigSchema.default({
|
|
359
|
+
promptSuggestions: true,
|
|
360
|
+
agentProgressSummaries: true,
|
|
361
|
+
notifyOnFinishDefault: true,
|
|
362
|
+
}),
|
|
304
363
|
providers: z.record(z.string(), MutableDaemonProviderConfigSchema).default({}),
|
|
305
|
-
metadataGeneration: MutableMetadataGenerationConfigSchema.default({
|
|
364
|
+
metadataGeneration: MutableMetadataGenerationConfigSchema.default({
|
|
365
|
+
providers: [],
|
|
366
|
+
enabled: true,
|
|
367
|
+
preferWriterPersonalities: false,
|
|
368
|
+
}),
|
|
306
369
|
autoArchiveAfterMerge: z.boolean().default(false),
|
|
307
370
|
enableTerminalAgentHooks: z.boolean().default(false),
|
|
308
371
|
appendSystemPrompt: z.string().default(""),
|
|
@@ -323,12 +386,19 @@ export const MutableDaemonConfigSchema = z
|
|
|
323
386
|
// Gated by the modelTierOverrides feature; defaults empty so a new client
|
|
324
387
|
// parsing an old daemon's config still sees a well-formed array.
|
|
325
388
|
modelTierOverrides: z.array(ModelTierOverrideSchema).default([]),
|
|
389
|
+
// Per-host remembered provider endpoints (base URL + credential), pooled by
|
|
390
|
+
// env-var family. Gated by the savedProviderEndpoints feature; defaults
|
|
391
|
+
// empty so a new client parsing an old daemon's config still sees a
|
|
392
|
+
// well-formed array.
|
|
393
|
+
savedProviderEndpoints: z.array(SavedProviderEndpointSchema).default([]),
|
|
326
394
|
})
|
|
327
395
|
.passthrough();
|
|
328
396
|
export const MutableDaemonConfigPatchSchema = z
|
|
329
397
|
.object({
|
|
330
398
|
mcp: MutableDaemonConfigSchema.shape.mcp.partial().optional(),
|
|
331
399
|
browserTools: MutableBrowserToolsConfigSchema.partial().optional(),
|
|
400
|
+
// Gated by server_info features.agentBehaviorToggles; patches deep-merge.
|
|
401
|
+
agentBehaviors: MutableAgentBehaviorsConfigSchema.partial().optional(),
|
|
332
402
|
// A null entry removes the provider's config entirely (custom provider
|
|
333
403
|
// uninstall). Gated by server_info features.providerRemove — old daemons
|
|
334
404
|
// reject null values.
|
|
@@ -356,6 +426,9 @@ export const MutableDaemonConfigPatchSchema = z
|
|
|
356
426
|
// Gated by server_info features.modelTierOverrides. Replaces the full array
|
|
357
427
|
// (read-modify-write), so removing an entry clears that model's tag.
|
|
358
428
|
modelTierOverrides: z.array(ModelTierOverrideSchema).optional(),
|
|
429
|
+
// Gated by server_info features.savedProviderEndpoints. Replaces the full
|
|
430
|
+
// array (read-modify-write), so forgetting an endpoint drops it from disk.
|
|
431
|
+
savedProviderEndpoints: z.array(SavedProviderEndpointSchema).optional(),
|
|
359
432
|
})
|
|
360
433
|
.partial()
|
|
361
434
|
.passthrough();
|
|
@@ -480,6 +553,8 @@ const ContextCompositionSchema = z.object({
|
|
|
480
553
|
const AgentUsageSchema = z.object({
|
|
481
554
|
inputTokens: z.number().optional(),
|
|
482
555
|
cachedInputTokens: z.number().optional(),
|
|
556
|
+
// Cache-write (prompt-cache creation) tokens; Claude-specific, optional/additive.
|
|
557
|
+
cacheCreationInputTokens: z.number().optional(),
|
|
483
558
|
outputTokens: z.number().optional(),
|
|
484
559
|
totalCostUsd: z.number().optional(),
|
|
485
560
|
contextWindowMaxTokens: z.number().optional(),
|
|
@@ -1455,6 +1530,26 @@ export const ActivityCountersSchema = z.object({
|
|
|
1455
1530
|
toolsCalled: z.number().default(0),
|
|
1456
1531
|
artifactsCreated: z.number().default(0),
|
|
1457
1532
|
schedulesExecuted: z.number().default(0),
|
|
1533
|
+
// Usage & cost accounting (WP-G). Additive/defaulted like every counter above,
|
|
1534
|
+
// so old daemons emit 0 and old clients drop the unknown leaves. "In"/"Out"
|
|
1535
|
+
// are token totals; *CostMicroUsd are integer micro-USD (usd*1e6) to stay
|
|
1536
|
+
// summable — populated only for turns reporting a real provider cost (Claude).
|
|
1537
|
+
// The client detects whether the daemon actually populates these via
|
|
1538
|
+
// features.usageCostCategories (see below).
|
|
1539
|
+
costMicroUsd: z.number().default(0),
|
|
1540
|
+
mainChatTokensIn: z.number().default(0),
|
|
1541
|
+
mainChatTokensOut: z.number().default(0),
|
|
1542
|
+
mainChatCostMicroUsd: z.number().default(0),
|
|
1543
|
+
generationsTokensIn: z.number().default(0),
|
|
1544
|
+
generationsTokensOut: z.number().default(0),
|
|
1545
|
+
generationsCostMicroUsd: z.number().default(0),
|
|
1546
|
+
subagentTokensIn: z.number().default(0),
|
|
1547
|
+
subagentTokensOut: z.number().default(0),
|
|
1548
|
+
subagentCostMicroUsd: z.number().default(0),
|
|
1549
|
+
compactionTokensIn: z.number().default(0),
|
|
1550
|
+
compactionTokensOut: z.number().default(0),
|
|
1551
|
+
claudeTokensIn: z.number().default(0),
|
|
1552
|
+
claudeTokensOut: z.number().default(0),
|
|
1458
1553
|
});
|
|
1459
1554
|
export const StatsActivityGetRequestMessageSchema = z.object({
|
|
1460
1555
|
type: z.literal("stats.activity.get.request"),
|
|
@@ -1482,6 +1577,100 @@ export const StatsActivityGetResponseMessageSchema = z.object({
|
|
|
1482
1577
|
export const ActivityStatsChangedSchema = z.object({
|
|
1483
1578
|
type: z.literal("activity_stats_changed"),
|
|
1484
1579
|
});
|
|
1580
|
+
// One itemized row of the usage ledger — a single token/cost-bearing activity
|
|
1581
|
+
// (a chat turn, a sub-agent turn, or a background generation). The aggregate
|
|
1582
|
+
// ActivityCounters above are the rollup of this same event stream; the ledger is
|
|
1583
|
+
// the scrollable detail behind the tiles (usage-ledger project). `kind` and
|
|
1584
|
+
// `provider` are plain strings (not enums) so an OLD client still parses a NEW
|
|
1585
|
+
// daemon that emits a kind it hasn't heard of — it renders it generically rather
|
|
1586
|
+
// than failing the whole message. All token/cost leaves default to 0.
|
|
1587
|
+
export const UsageEventSchema = z.object({
|
|
1588
|
+
/** Stable unique id for the row (daemon-generated). */
|
|
1589
|
+
id: z.string(),
|
|
1590
|
+
/** Epoch milliseconds when the activity was recorded. */
|
|
1591
|
+
at: z.number(),
|
|
1592
|
+
/** "chat" | "subagent" | "generation" today; open for future kinds. */
|
|
1593
|
+
kind: z.string(),
|
|
1594
|
+
/** Finer label within the kind (e.g. a generation's purpose, a sub-agent name). */
|
|
1595
|
+
subtype: z.string().optional(),
|
|
1596
|
+
/** Agent provider id (e.g. "claude", an openai-compat endpoint id). */
|
|
1597
|
+
provider: z.string(),
|
|
1598
|
+
/** Model id/name if known at the increment site. */
|
|
1599
|
+
model: z.string().optional(),
|
|
1600
|
+
/** input + cached + cache-creation tokens (same "in" split the counters use). */
|
|
1601
|
+
tokensIn: z.number().default(0),
|
|
1602
|
+
/**
|
|
1603
|
+
* The portion of `tokensIn` served from the provider prompt cache (cache-read),
|
|
1604
|
+
* billed at a fraction of fresh input. The fresh (full-rate) portion is
|
|
1605
|
+
* `tokensIn - cachedTokensIn`. Absent when the provider reports no cache reads
|
|
1606
|
+
* (e.g. openai-compat endpoints with no caching), which reads as all-fresh.
|
|
1607
|
+
*/
|
|
1608
|
+
cachedTokensIn: z.number().optional(),
|
|
1609
|
+
/** output tokens. */
|
|
1610
|
+
tokensOut: z.number().default(0),
|
|
1611
|
+
/** Real provider spend in integer micro-USD (usd*1e6); 0 for token-only providers. */
|
|
1612
|
+
costMicroUsd: z.number().default(0),
|
|
1613
|
+
/** Mid-turn compaction slice folded into this turn's usage, if any (token-only). */
|
|
1614
|
+
compactionTokensIn: z.number().optional(),
|
|
1615
|
+
compactionTokensOut: z.number().optional(),
|
|
1616
|
+
/** The agent this activity belonged to, for tracing back to the chat. */
|
|
1617
|
+
agentId: z.string().optional(),
|
|
1618
|
+
/**
|
|
1619
|
+
* How many model round-trips this row aggregates. A chat row is one query, but
|
|
1620
|
+
* a sub-agent row covers a whole delegated task that internally ran many
|
|
1621
|
+
* rounds — and each round re-reads the growing context, so `cachedTokensIn` is
|
|
1622
|
+
* cumulative cache-READS, not a cache size. Surfacing the count is what makes a
|
|
1623
|
+
* large cached figure legible instead of looking like a bug. Absent when the
|
|
1624
|
+
* provider doesn't report it.
|
|
1625
|
+
*/
|
|
1626
|
+
rounds: z.number().optional(),
|
|
1627
|
+
/**
|
|
1628
|
+
* Sub-agent rows only — the spawn-tree identity that lets the Log group rows
|
|
1629
|
+
* the way a human reads the run (chat turn → its sub-agents → their
|
|
1630
|
+
* sub-agents) instead of by settle time, which async sub-agents crossing turn
|
|
1631
|
+
* boundaries makes wrong. `startedAt` is when the sub-agent was first
|
|
1632
|
+
* observed (epoch ms; a row belongs to the turn that spawned it, not the turn
|
|
1633
|
+
* it happened to settle in), `subagentKey` is its stable observed key, and
|
|
1634
|
+
* `parentSubagentKey` is the spawning sub-agent's key — absent for depth-1
|
|
1635
|
+
* sub-agents spawned by the chat itself.
|
|
1636
|
+
*/
|
|
1637
|
+
startedAt: z.number().optional(),
|
|
1638
|
+
subagentKey: z.string().optional(),
|
|
1639
|
+
parentSubagentKey: z.string().optional(),
|
|
1640
|
+
});
|
|
1641
|
+
export const UsageLogGetRequestMessageSchema = z.object({
|
|
1642
|
+
type: z.literal("usage.log.get.request"),
|
|
1643
|
+
requestId: z.string(),
|
|
1644
|
+
/** Max rows to return (daemon clamps). Newest-first. */
|
|
1645
|
+
limit: z.number().optional(),
|
|
1646
|
+
/** Cursor: return only rows strictly older than this epoch-ms (for "load more"). */
|
|
1647
|
+
before: z.number().optional(),
|
|
1648
|
+
});
|
|
1649
|
+
export const UsageLogGetResponseMessageSchema = z.object({
|
|
1650
|
+
type: z.literal("usage.log.get.response"),
|
|
1651
|
+
payload: z.object({
|
|
1652
|
+
requestId: z.string(),
|
|
1653
|
+
/** Newest-first page of ledger rows. */
|
|
1654
|
+
events: z.array(UsageEventSchema).default([]),
|
|
1655
|
+
/** True when older rows exist beyond this page (paginate with `before`). */
|
|
1656
|
+
hasMore: z.boolean().default(false),
|
|
1657
|
+
}),
|
|
1658
|
+
});
|
|
1659
|
+
// Wipe every daemon-wide usage counter AND the itemized usage ledger back to
|
|
1660
|
+
// zero — the "Reset" action on the Metrics screen. One RPC clears both sinks
|
|
1661
|
+
// (the day-bucketed ActivityStatsStore and the UsageLogStore) so the tiles and
|
|
1662
|
+
// the Log tab start fresh together. Gated behind features.statsReset so an old
|
|
1663
|
+
// daemon (no handler) never receives a request the client thinks it can send.
|
|
1664
|
+
export const StatsActivityResetRequestMessageSchema = z.object({
|
|
1665
|
+
type: z.literal("stats.activity.reset.request"),
|
|
1666
|
+
requestId: z.string(),
|
|
1667
|
+
});
|
|
1668
|
+
export const StatsActivityResetResponseMessageSchema = z.object({
|
|
1669
|
+
type: z.literal("stats.activity.reset.response"),
|
|
1670
|
+
payload: z.object({
|
|
1671
|
+
requestId: z.string(),
|
|
1672
|
+
}),
|
|
1673
|
+
});
|
|
1485
1674
|
export const AgentContextGetUsageRequestMessageSchema = z.object({
|
|
1486
1675
|
type: z.literal("agent.context.get_usage.request"),
|
|
1487
1676
|
agentId: z.string(),
|
|
@@ -1694,6 +1883,164 @@ export const SuggestedTasksChangedSchema = z.object({
|
|
|
1694
1883
|
tasks: z.array(SuggestedTaskInfoSchema),
|
|
1695
1884
|
}),
|
|
1696
1885
|
});
|
|
1886
|
+
// Context Management — the daemon's accounting of everything a provider sends
|
|
1887
|
+
// before the user types (see projects/context-management/context-management.md).
|
|
1888
|
+
//
|
|
1889
|
+
// Two distinctions carry the whole feature and must not be collapsed on the
|
|
1890
|
+
// wire: an `import` edge is inlined into the request while a `reference` edge
|
|
1891
|
+
// costs only its link text, and `costClass` separates weight that rides every
|
|
1892
|
+
// request from weight that loads only when the agent touches an area.
|
|
1893
|
+
//
|
|
1894
|
+
// All numbers are estimates (chars/4) and `confidence` says how much to trust
|
|
1895
|
+
// the file set: `exact` when Otto composed the payload itself, `convention`
|
|
1896
|
+
// when resolved from a provider's documented layout, `unverified` for
|
|
1897
|
+
// subprocess-owned agents we cannot see into.
|
|
1898
|
+
// COMPAT(contextManagement): added in v0.6.5, drop the gate when daemon floor >= v0.6.5.
|
|
1899
|
+
export const ContextScopeSchema = z.enum([
|
|
1900
|
+
"enterprise",
|
|
1901
|
+
"global",
|
|
1902
|
+
"project",
|
|
1903
|
+
"local",
|
|
1904
|
+
"subdirectory",
|
|
1905
|
+
"runtime",
|
|
1906
|
+
]);
|
|
1907
|
+
export const ContextCategorySchema = z.enum([
|
|
1908
|
+
"context_files",
|
|
1909
|
+
"memory_index",
|
|
1910
|
+
"skills_roster",
|
|
1911
|
+
"mcp_tools",
|
|
1912
|
+
"otto_injected",
|
|
1913
|
+
"system_prompt",
|
|
1914
|
+
]);
|
|
1915
|
+
export const ContextCostClassSchema = z.enum(["fixed", "conditional", "referenced"]);
|
|
1916
|
+
export const ContextSeveritySchema = z.enum(["ok", "notice", "warn", "critical"]);
|
|
1917
|
+
export const ContextConfidenceSchema = z.enum(["exact", "convention", "unverified"]);
|
|
1918
|
+
export const ContextFindingKindSchema = z.enum([
|
|
1919
|
+
"dead_import",
|
|
1920
|
+
"dead_reference",
|
|
1921
|
+
"duplicate_across_scope",
|
|
1922
|
+
"duplicate_within_file",
|
|
1923
|
+
"oversized_memory_entry",
|
|
1924
|
+
"import_cycle",
|
|
1925
|
+
"depth_capped",
|
|
1926
|
+
]);
|
|
1927
|
+
export const ContextRangeSchema = z.object({
|
|
1928
|
+
start: z.number(),
|
|
1929
|
+
end: z.number(),
|
|
1930
|
+
});
|
|
1931
|
+
export const ContextFindingSchema = z.object({
|
|
1932
|
+
kind: ContextFindingKindSchema,
|
|
1933
|
+
message: z.string(),
|
|
1934
|
+
range: ContextRangeSchema.optional(),
|
|
1935
|
+
relatedNodeIds: z.array(z.string()).optional(),
|
|
1936
|
+
// The node this finding is about. Redundant while the finding sits on its
|
|
1937
|
+
// node, load-bearing once the report flattens them all into one list — that
|
|
1938
|
+
// list is the "Worth fixing" tab, and without this a row cannot say where it
|
|
1939
|
+
// came from or take you there.
|
|
1940
|
+
nodeId: z.string().optional(),
|
|
1941
|
+
// 1-based line of `range.start` in that node's file, so the fix list can jump
|
|
1942
|
+
// the editor without the client re-reading bytes to count newlines.
|
|
1943
|
+
line: z.number().optional(),
|
|
1944
|
+
// Last line of the range, so the client can select the whole offending span
|
|
1945
|
+
// rather than dropping a cursor at the top of it.
|
|
1946
|
+
lineEnd: z.number().optional(),
|
|
1947
|
+
});
|
|
1948
|
+
export const ContextNodeSchema = z.object({
|
|
1949
|
+
id: z.string(),
|
|
1950
|
+
path: z.string(),
|
|
1951
|
+
relPath: z.string(),
|
|
1952
|
+
scope: ContextScopeSchema,
|
|
1953
|
+
category: ContextCategorySchema,
|
|
1954
|
+
costClass: ContextCostClassSchema,
|
|
1955
|
+
bytes: z.number(),
|
|
1956
|
+
estTokens: z.number(),
|
|
1957
|
+
// Extra parents that also reach this node. The node is listed and counted
|
|
1958
|
+
// exactly once; these render as a dimmed "also imported by" chip.
|
|
1959
|
+
alsoImportedByNodeIds: z.array(z.string()),
|
|
1960
|
+
findings: z.array(ContextFindingSchema),
|
|
1961
|
+
});
|
|
1962
|
+
export const ContextEdgeSchema = z.object({
|
|
1963
|
+
fromNodeId: z.string(),
|
|
1964
|
+
// Null when the target could not be resolved — pairs with a dead_* finding.
|
|
1965
|
+
toNodeId: z.string().nullable(),
|
|
1966
|
+
kind: z.enum(["import", "reference"]),
|
|
1967
|
+
rawTarget: z.string(),
|
|
1968
|
+
// Byte range of the whole reference token in the parent file, which is what
|
|
1969
|
+
// makes "Always load" <-> "Link only" a single-span edit.
|
|
1970
|
+
range: ContextRangeSchema,
|
|
1971
|
+
});
|
|
1972
|
+
export const ContextCategoryTotalSchema = z.object({
|
|
1973
|
+
category: ContextCategorySchema,
|
|
1974
|
+
estTokens: z.number(),
|
|
1975
|
+
sharePercent: z.number(),
|
|
1976
|
+
severity: ContextSeveritySchema,
|
|
1977
|
+
});
|
|
1978
|
+
export const ContextReportSchema = z.object({
|
|
1979
|
+
workspaceId: z.string(),
|
|
1980
|
+
provider: z.string(),
|
|
1981
|
+
// The window the report was evaluated against — from the active model, or
|
|
1982
|
+
// the client's what-if picker. Severity is meaningless without it.
|
|
1983
|
+
windowTokens: z.number(),
|
|
1984
|
+
scannedAt: z.string(),
|
|
1985
|
+
confidence: ContextConfidenceSchema,
|
|
1986
|
+
supported: z.boolean(),
|
|
1987
|
+
supportsImports: z.boolean(),
|
|
1988
|
+
nodes: z.array(ContextNodeSchema),
|
|
1989
|
+
edges: z.array(ContextEdgeSchema),
|
|
1990
|
+
categoryTotals: z.array(ContextCategoryTotalSchema),
|
|
1991
|
+
fixedTotal: z.number(),
|
|
1992
|
+
conditionalTotal: z.number(),
|
|
1993
|
+
referencedTotal: z.number(),
|
|
1994
|
+
workingRoom: z.number(),
|
|
1995
|
+
aggregateSeverity: ContextSeveritySchema,
|
|
1996
|
+
findings: z.array(ContextFindingSchema),
|
|
1997
|
+
});
|
|
1998
|
+
// Pushed with the full current report whenever a watched context file changes.
|
|
1999
|
+
// Full-report reconciliation, same idiom as suggested_tasks_changed.
|
|
2000
|
+
export const ContextReportChangedSchema = z.object({
|
|
2001
|
+
type: z.literal("context_report_changed"),
|
|
2002
|
+
payload: z.object({
|
|
2003
|
+
workspaceId: z.string(),
|
|
2004
|
+
report: ContextReportSchema.nullable(),
|
|
2005
|
+
}),
|
|
2006
|
+
});
|
|
2007
|
+
// `provider` and `windowTokens` are the what-if pickers: omitted means "the
|
|
2008
|
+
// active agent's provider and its model's real window".
|
|
2009
|
+
export const ContextReportGetRequestMessageSchema = z.object({
|
|
2010
|
+
type: z.literal("context.report.get.request"),
|
|
2011
|
+
requestId: z.string(),
|
|
2012
|
+
workspaceId: z.string(),
|
|
2013
|
+
provider: z.string().optional(),
|
|
2014
|
+
windowTokens: z.number().optional(),
|
|
2015
|
+
});
|
|
2016
|
+
export const ContextReportGetResponseMessageSchema = z.object({
|
|
2017
|
+
type: z.literal("context.report.get.response"),
|
|
2018
|
+
payload: z.object({
|
|
2019
|
+
requestId: z.string(),
|
|
2020
|
+
report: ContextReportSchema.nullable(),
|
|
2021
|
+
}),
|
|
2022
|
+
});
|
|
2023
|
+
// Converts one edge between "always loaded" and "link only". Server-side
|
|
2024
|
+
// because the parent file may live outside the workspace root.
|
|
2025
|
+
export const ContextEdgeConvertRequestMessageSchema = z.object({
|
|
2026
|
+
type: z.literal("context.edge.convert.request"),
|
|
2027
|
+
requestId: z.string(),
|
|
2028
|
+
workspaceId: z.string(),
|
|
2029
|
+
// The parent file holding the reference — its `ContextNode.path`, not its
|
|
2030
|
+
// id: ids are case-folded on Windows and are not safe to write through.
|
|
2031
|
+
filePath: z.string(),
|
|
2032
|
+
rawTarget: z.string(),
|
|
2033
|
+
range: ContextRangeSchema,
|
|
2034
|
+
target: z.enum(["import", "reference"]),
|
|
2035
|
+
});
|
|
2036
|
+
export const ContextEdgeConvertResponseMessageSchema = z.object({
|
|
2037
|
+
type: z.literal("context.edge.convert.response"),
|
|
2038
|
+
payload: z.object({
|
|
2039
|
+
requestId: z.string(),
|
|
2040
|
+
ok: z.boolean(),
|
|
2041
|
+
error: z.string().optional(),
|
|
2042
|
+
}),
|
|
2043
|
+
});
|
|
1697
2044
|
// Aggregate outcome for a start/dismiss over one or more tasks. `succeeded`/
|
|
1698
2045
|
// `failed` count the tasks acted on so the client can report "Started 3 tasks";
|
|
1699
2046
|
// `error` collects any per-task failure messages (the failed tasks' chips stay).
|
|
@@ -2723,6 +3070,10 @@ export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
|
|
|
2723
3070
|
ProviderDiagnosticRequestMessageSchema,
|
|
2724
3071
|
ProviderUsageListRequestMessageSchema,
|
|
2725
3072
|
StatsActivityGetRequestMessageSchema,
|
|
3073
|
+
ContextReportGetRequestMessageSchema,
|
|
3074
|
+
ContextEdgeConvertRequestMessageSchema,
|
|
3075
|
+
StatsActivityResetRequestMessageSchema,
|
|
3076
|
+
UsageLogGetRequestMessageSchema,
|
|
2726
3077
|
AgentContextGetUsageRequestMessageSchema,
|
|
2727
3078
|
ResumeAgentRequestMessageSchema,
|
|
2728
3079
|
ImportAgentRequestMessageSchema,
|
|
@@ -3036,8 +3387,19 @@ export const ServerInfoStatusPayloadSchema = z
|
|
|
3036
3387
|
observedSubagents: z.boolean().optional(),
|
|
3037
3388
|
// COMPAT(backgroundShellTasks): added in v0.5.3, drop the gate when daemon floor >= v0.5.3.
|
|
3038
3389
|
backgroundShellTasks: z.boolean().optional(),
|
|
3390
|
+
// COMPAT(retainedTranscripts): added in v0.6.4, drop the gate when daemon floor >= v0.6.4.
|
|
3391
|
+
// Daemon retains schedule/artifact generation-agent chats for read-only
|
|
3392
|
+
// viewing after the run. See docs/safe-unattended.md.
|
|
3393
|
+
retainedTranscripts: z.boolean().optional(),
|
|
3039
3394
|
// COMPAT(suggestedTasks): added in v0.5.6, drop the gate when daemon floor >= v0.5.6.
|
|
3040
3395
|
suggestedTasks: z.boolean().optional(),
|
|
3396
|
+
// Daemon can resolve and evaluate the provider's context graph, serve
|
|
3397
|
+
// context.report.* and push context_report_changed. Without it the
|
|
3398
|
+
// client hides both the Context Management tab and the composer
|
|
3399
|
+
// warning entirely — there is no degraded client-side fallback, since
|
|
3400
|
+
// only the daemon can see the files a provider loads.
|
|
3401
|
+
// COMPAT(contextManagement): added in v0.6.5, drop the gate when daemon floor >= v0.6.5.
|
|
3402
|
+
contextManagement: z.boolean().optional(),
|
|
3041
3403
|
// COMPAT(textEditor): added in v0.4.4, drop the gate when daemon floor >= v0.4.4.
|
|
3042
3404
|
textEditor: z.boolean().optional(),
|
|
3043
3405
|
// COMPAT(projectSearch): added in v0.4.4, drop the gate when daemon floor >= v0.4.4.
|
|
@@ -3070,6 +3432,8 @@ export const ServerInfoStatusPayloadSchema = z
|
|
|
3070
3432
|
agentTeams: z.boolean().optional(),
|
|
3071
3433
|
// COMPAT(modelTierOverrides): added in v0.5.2, drop the gate when daemon floor >= v0.5.2.
|
|
3072
3434
|
modelTierOverrides: z.boolean().optional(),
|
|
3435
|
+
// COMPAT(savedProviderEndpoints): added in v0.6.5, drop the gate when daemon floor >= v0.6.5.
|
|
3436
|
+
savedProviderEndpoints: z.boolean().optional(),
|
|
3073
3437
|
// COMPAT(agentOrchestration): added in v0.5.3, drop the gate when daemon floor >= v0.5.3.
|
|
3074
3438
|
agentOrchestration: z.boolean().optional(),
|
|
3075
3439
|
// COMPAT(activityStats): added in v0.5.3, drop the gate when daemon floor >= v0.5.3.
|
|
@@ -3093,6 +3457,44 @@ export const ServerInfoStatusPayloadSchema = z
|
|
|
3093
3457
|
// Set when the daemon emits agent_stream `rate_limit_updated` events (Claude
|
|
3094
3458
|
// plan rate-limit status). Warnings degrade silently on an old daemon (no event).
|
|
3095
3459
|
rateLimitEvents: z.boolean().optional(),
|
|
3460
|
+
// COMPAT(openaiCompatMaxToolRounds): added in v0.6.4, drop the gate when daemon floor >= v0.6.4.
|
|
3461
|
+
// Set when the daemon honors the provider-level `maxToolRounds` override for
|
|
3462
|
+
// openai-compat agents. The client gates the Agents-tab control on this so an
|
|
3463
|
+
// old daemon (which silently ignores the field and keeps the fixed 50-round
|
|
3464
|
+
// cap) shows "Update the host" instead of a knob that does nothing.
|
|
3465
|
+
openaiCompatMaxToolRounds: z.boolean().optional(),
|
|
3466
|
+
// COMPAT(mcpToolGroups): added in v0.6.4, drop the gate when daemon floor >= v0.6.4.
|
|
3467
|
+
// Set when the daemon honors `mcp.toolGroups` — per-group gating of the
|
|
3468
|
+
// Otto tool catalog on the MCP (Claude) path. Old daemons register every
|
|
3469
|
+
// group regardless, so the client hides the categorized section instead
|
|
3470
|
+
// of showing category switches that do nothing.
|
|
3471
|
+
mcpToolGroups: z.boolean().optional(),
|
|
3472
|
+
// COMPAT(agentBehaviorToggles): added in v0.6.4, drop the gate when daemon floor >= v0.6.4.
|
|
3473
|
+
// Set when the daemon persists `agentBehaviors.*` (promptSuggestions,
|
|
3474
|
+
// agentProgressSummaries, notifyOnFinishDefault). The reads are wired by
|
|
3475
|
+
// Claude-tier providers (WP-E); the client gates the toggle cards on this.
|
|
3476
|
+
agentBehaviorToggles: z.boolean().optional(),
|
|
3477
|
+
// COMPAT(metadataGenerationEnabled): added in v0.6.4, drop the gate when daemon floor >= v0.6.4.
|
|
3478
|
+
// Set when the daemon persists `metadataGeneration.{enabled,preferWriterPersonalities}`.
|
|
3479
|
+
// The generation path (WP-B) reads them; the client gates the toggle cards on this.
|
|
3480
|
+
metadataGenerationEnabled: z.boolean().optional(),
|
|
3481
|
+
// COMPAT(usageCostCategories): added in v0.6.4, drop the gate when daemon floor >= v0.6.4.
|
|
3482
|
+
// Set when the daemon populates the per-category token/cost counters in
|
|
3483
|
+
// ActivityCounters (mainChat/generations/subagents/compaction + Claude
|
|
3484
|
+
// provider split + micro-USD cost). An old daemon leaves them all at 0,
|
|
3485
|
+
// so the client hides the Usage & Cost column's category grid rather than
|
|
3486
|
+
// presenting a column of zeros as if it were truthful accounting.
|
|
3487
|
+
usageCostCategories: z.boolean().optional(),
|
|
3488
|
+
// COMPAT(usageLog): added in v0.6.4, drop the gate when daemon floor >= v0.6.4.
|
|
3489
|
+
// Set when the daemon serves the itemized usage ledger (usage.log.get).
|
|
3490
|
+
// The client gates the Metrics screen's "Log" tab on this; an old daemon
|
|
3491
|
+
// simply doesn't offer the tab.
|
|
3492
|
+
usageLog: z.boolean().optional(),
|
|
3493
|
+
// COMPAT(statsReset): added in v0.6.4, drop the gate when daemon floor >= v0.6.4.
|
|
3494
|
+
// Set when the daemon handles stats.activity.reset (wipe all usage
|
|
3495
|
+
// counters + the itemized ledger). The client gates the Metrics screen's
|
|
3496
|
+
// "Reset" button on this; an old daemon simply doesn't offer it.
|
|
3497
|
+
statsReset: z.boolean().optional(),
|
|
3096
3498
|
})
|
|
3097
3499
|
.optional(),
|
|
3098
3500
|
})
|
|
@@ -5278,6 +5680,7 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
|
|
|
5278
5680
|
TasksSuggestedStartResponseMessageSchema,
|
|
5279
5681
|
TasksSuggestedDismissResponseMessageSchema,
|
|
5280
5682
|
SuggestedTasksChangedSchema,
|
|
5683
|
+
ContextReportChangedSchema,
|
|
5281
5684
|
AgentPersonalitySetResponseMessageSchema,
|
|
5282
5685
|
AgentRewindResponseMessageSchema,
|
|
5283
5686
|
UpdateAgentResponseMessageSchema,
|
|
@@ -5363,6 +5766,10 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
|
|
|
5363
5766
|
ProviderDiagnosticResponseMessageSchema,
|
|
5364
5767
|
ProviderUsageListResponseMessageSchema,
|
|
5365
5768
|
StatsActivityGetResponseMessageSchema,
|
|
5769
|
+
ContextReportGetResponseMessageSchema,
|
|
5770
|
+
ContextEdgeConvertResponseMessageSchema,
|
|
5771
|
+
StatsActivityResetResponseMessageSchema,
|
|
5772
|
+
UsageLogGetResponseMessageSchema,
|
|
5366
5773
|
ActivityStatsChangedSchema,
|
|
5367
5774
|
AgentContextGetUsageResponseMessageSchema,
|
|
5368
5775
|
ListCommandsResponseSchema,
|
|
@@ -86,6 +86,16 @@ export declare const MCP_TOOL_PERMISSION_MODES: readonly ["always-ask", "trust-r
|
|
|
86
86
|
* conversation is compacted automatically.
|
|
87
87
|
*/
|
|
88
88
|
export declare const COMPACTION_THRESHOLD_PERCENTS: readonly [50, 60, 70, 80, 90];
|
|
89
|
+
/**
|
|
90
|
+
* Max model→tool→model rounds per turn for providers whose tool loop the
|
|
91
|
+
* daemon owns (openai-compat). The turn stops with an error after this many
|
|
92
|
+
* rounds without a final answer — a runaway-loop safety valve, most often hit
|
|
93
|
+
* by smaller local models that keep calling tools instead of converging.
|
|
94
|
+
* Bounds keep the setting a sane guard rail rather than an off switch.
|
|
95
|
+
*/
|
|
96
|
+
export declare const MAX_TOOL_ROUNDS_DEFAULT = 50;
|
|
97
|
+
export declare const MAX_TOOL_ROUNDS_MIN = 1;
|
|
98
|
+
export declare const MAX_TOOL_ROUNDS_MAX = 1000;
|
|
89
99
|
/**
|
|
90
100
|
* Compaction tuning for providers whose conversation the daemon owns
|
|
91
101
|
* (openai-compat). These set the provider-level defaults; the per-agent
|
|
@@ -167,6 +177,7 @@ export declare const ProviderOverrideSchema: z.ZodObject<{
|
|
|
167
177
|
keepRecentTokens: z.ZodOptional<z.ZodNumber>;
|
|
168
178
|
hideSelector: z.ZodOptional<z.ZodBoolean>;
|
|
169
179
|
}, z.core.$strip>>;
|
|
180
|
+
maxToolRounds: z.ZodOptional<z.ZodNumber>;
|
|
170
181
|
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
171
182
|
order: z.ZodOptional<z.ZodNumber>;
|
|
172
183
|
}, z.core.$strip>;
|
|
@@ -239,6 +250,7 @@ export declare const ProviderOverridesSchema: z.ZodRecord<z.ZodString, z.ZodObje
|
|
|
239
250
|
keepRecentTokens: z.ZodOptional<z.ZodNumber>;
|
|
240
251
|
hideSelector: z.ZodOptional<z.ZodBoolean>;
|
|
241
252
|
}, z.core.$strip>>;
|
|
253
|
+
maxToolRounds: z.ZodOptional<z.ZodNumber>;
|
|
242
254
|
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
243
255
|
order: z.ZodOptional<z.ZodNumber>;
|
|
244
256
|
}, z.core.$strip>>;
|
package/dist/provider-config.js
CHANGED
|
@@ -117,6 +117,16 @@ export const MCP_TOOL_PERMISSION_MODES = ["always-ask", "trust-read-only"];
|
|
|
117
117
|
* conversation is compacted automatically.
|
|
118
118
|
*/
|
|
119
119
|
export const COMPACTION_THRESHOLD_PERCENTS = [50, 60, 70, 80, 90];
|
|
120
|
+
/**
|
|
121
|
+
* Max model→tool→model rounds per turn for providers whose tool loop the
|
|
122
|
+
* daemon owns (openai-compat). The turn stops with an error after this many
|
|
123
|
+
* rounds without a final answer — a runaway-loop safety valve, most often hit
|
|
124
|
+
* by smaller local models that keep calling tools instead of converging.
|
|
125
|
+
* Bounds keep the setting a sane guard rail rather than an off switch.
|
|
126
|
+
*/
|
|
127
|
+
export const MAX_TOOL_ROUNDS_DEFAULT = 50;
|
|
128
|
+
export const MAX_TOOL_ROUNDS_MIN = 1;
|
|
129
|
+
export const MAX_TOOL_ROUNDS_MAX = 1000;
|
|
120
130
|
/**
|
|
121
131
|
* Compaction tuning for providers whose conversation the daemon owns
|
|
122
132
|
* (openai-compat). These set the provider-level defaults; the per-agent
|
|
@@ -165,6 +175,11 @@ export const ProviderOverrideSchema = z.object({
|
|
|
165
175
|
* (openai-compat). Per-agent feature values win over these.
|
|
166
176
|
*/
|
|
167
177
|
compaction: ProviderCompactionConfigSchema.optional(),
|
|
178
|
+
/**
|
|
179
|
+
* Max model→tool→model rounds per turn for daemon-hosted providers
|
|
180
|
+
* (openai-compat). Omitted = the built-in default (MAX_TOOL_ROUNDS_DEFAULT).
|
|
181
|
+
*/
|
|
182
|
+
maxToolRounds: z.number().int().min(MAX_TOOL_ROUNDS_MIN).max(MAX_TOOL_ROUNDS_MAX).optional(),
|
|
168
183
|
enabled: z.boolean().optional(),
|
|
169
184
|
order: z.number().optional(),
|
|
170
185
|
});
|
|
@@ -124,6 +124,19 @@ const MOCK_LOAD_TEST_MODES = [
|
|
|
124
124
|
icon: "ShieldOff",
|
|
125
125
|
colorTier: "dangerous",
|
|
126
126
|
},
|
|
127
|
+
{
|
|
128
|
+
id: "dontAsk",
|
|
129
|
+
label: "Don't Ask",
|
|
130
|
+
description: "Runs without prompting — actions not pre-approved are denied",
|
|
131
|
+
icon: "ShieldCheck",
|
|
132
|
+
colorTier: "moderate",
|
|
133
|
+
// Dev-only mirror of Claude's guarded unattended mode so deterministic E2E
|
|
134
|
+
// specs can exercise the system-assigned surfaces: the unattended coercion
|
|
135
|
+
// target for mock schedule runs, and the locked mode badge on a live agent
|
|
136
|
+
// stuck in a non-user-selectable mode.
|
|
137
|
+
isUnattended: true,
|
|
138
|
+
userSelectable: false,
|
|
139
|
+
},
|
|
127
140
|
];
|
|
128
141
|
const MOCK_SLOW_MODES = [
|
|
129
142
|
{
|