@otto-code/protocol 0.7.5 → 0.8.0

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.
Files changed (66) hide show
  1. package/dist/agent-attention-notification.d.ts +2 -0
  2. package/dist/agent-attention-notification.js +2 -1
  3. package/dist/agent-deep-link.d.ts +8 -0
  4. package/dist/agent-deep-link.js +49 -0
  5. package/dist/agent-labels.js +4 -4
  6. package/dist/agent-personalities.d.ts +2 -2
  7. package/dist/agent-personalities.js +17 -17
  8. package/dist/agent-teams.d.ts +4 -4
  9. package/dist/agent-teams.js +5 -5
  10. package/dist/agent-types.d.ts +12 -10
  11. package/dist/agent-types.js +1 -1
  12. package/dist/artifacts/rpc-schemas.js +4 -4
  13. package/dist/artifacts/types.js +3 -3
  14. package/dist/binary-frames/file-transfer.d.ts +1 -0
  15. package/dist/binary-frames/file-transfer.js +1 -0
  16. package/dist/browser-automation/rpc-schemas.js +1 -1
  17. package/dist/client-capabilities.d.ts +3 -0
  18. package/dist/client-capabilities.js +9 -0
  19. package/dist/daemon-endpoints.d.ts +1 -1
  20. package/dist/default-personalities.js +11 -11
  21. package/dist/effort.d.ts +1 -1
  22. package/dist/effort.js +2 -2
  23. package/dist/forge-manifest.d.ts +66 -0
  24. package/dist/forge-manifest.js +92 -0
  25. package/dist/generated/validation/ws-outbound.aot.js +77653 -51519
  26. package/dist/git-hosting.js +3 -3
  27. package/dist/git-remote.d.ts +17 -0
  28. package/dist/git-remote.js +23 -2
  29. package/dist/judge-verdict.d.ts +1 -1
  30. package/dist/judge-verdict.js +2 -2
  31. package/dist/messages.d.ts +10634 -135
  32. package/dist/messages.js +1744 -193
  33. package/dist/model-tiers.d.ts +3 -3
  34. package/dist/model-tiers.js +4 -4
  35. package/dist/observed-subagent-title.d.ts +2 -2
  36. package/dist/observed-subagent-title.js +4 -4
  37. package/dist/orchestration.d.ts +3 -3
  38. package/dist/orchestration.js +32 -32
  39. package/dist/otto-config-schema.d.ts +21 -0
  40. package/dist/otto-config-schema.js +23 -1
  41. package/dist/provider-config.d.ts +92 -3
  42. package/dist/provider-config.js +58 -6
  43. package/dist/provider-manifest.d.ts +11 -1
  44. package/dist/provider-manifest.js +53 -5
  45. package/dist/schedule/cadence.d.ts +6 -0
  46. package/dist/schedule/cadence.js +28 -0
  47. package/dist/schedule/rpc-schemas.d.ts +4 -4
  48. package/dist/schedule/rpc-schemas.js +1 -1
  49. package/dist/schedule/types.d.ts +1 -1
  50. package/dist/schedule/types.js +3 -3
  51. package/dist/tool-call-display.d.ts +1 -1
  52. package/dist/tool-call-display.js +3 -3
  53. package/dist/tool-name-normalization.d.ts +2 -2
  54. package/dist/tool-name-normalization.js +2 -2
  55. package/dist/validation/ws-outbound-schema-metadata.d.ts +1950 -52
  56. package/dist/widgets/bridge.d.ts +5 -5
  57. package/dist/widgets/bridge.js +5 -5
  58. package/dist/widgets/document.d.ts +1 -1
  59. package/dist/widgets/document.js +4 -4
  60. package/dist/widgets/icons.d.ts +1 -1
  61. package/dist/widgets/icons.js +1 -1
  62. package/dist/widgets/theme.d.ts +2 -2
  63. package/dist/widgets/theme.js +2 -2
  64. package/dist/widgets/types.d.ts +5 -5
  65. package/dist/widgets/types.js +5 -5
  66. package/package.json +1 -1
@@ -2,6 +2,7 @@ export type AgentAttentionReason = "finished" | "error" | "permission";
2
2
  export interface AgentAttentionNotificationData {
3
3
  [key: string]: unknown;
4
4
  serverId: string;
5
+ workspaceId?: string;
5
6
  agentId: string;
6
7
  reason: AgentAttentionReason;
7
8
  }
@@ -13,6 +14,7 @@ export interface AgentAttentionNotificationPayload {
13
14
  interface BuildAgentAttentionNotificationPayloadInput {
14
15
  reason: AgentAttentionReason;
15
16
  serverId: string;
17
+ workspaceId: string;
16
18
  agentId: string;
17
19
  assistantMessage?: string | null;
18
20
  permissionRequest?: NotificationPermissionRequest | null;
@@ -74,7 +74,7 @@ const PERMISSION_INPUT_DESCRIPTION_KEYS = [
74
74
  "query",
75
75
  "prompt",
76
76
  ];
77
- // Deterministic field extraction NOT an AI summary. Picks an existing string
77
+ // Deterministic field extraction - NOT an AI summary. Picks an existing string
78
78
  // field out of the tool's input (by the priority list, then any string field)
79
79
  // so the notification can show it verbatim instead of raw JSON.
80
80
  const describePermissionInput = (input) => {
@@ -180,6 +180,7 @@ export function buildAgentAttentionNotificationPayload(input) {
180
180
  body,
181
181
  data: {
182
182
  serverId: input.serverId,
183
+ workspaceId: input.workspaceId,
183
184
  agentId: input.agentId,
184
185
  reason: input.reason,
185
186
  },
@@ -0,0 +1,8 @@
1
+ export interface AgentDeepLinkTarget {
2
+ serverId: string;
3
+ agentId: string;
4
+ }
5
+ export declare function buildAgentDeepLinkRoute(target: AgentDeepLinkTarget): `/h/${string}/agent/${string}`;
6
+ export declare function buildAgentDeepLink(target: AgentDeepLinkTarget): string;
7
+ export declare function parseAgentDeepLink(input: string): AgentDeepLinkTarget | null;
8
+ //# sourceMappingURL=agent-deep-link.d.ts.map
@@ -0,0 +1,49 @@
1
+ function normalizeSegment(value) {
2
+ return value.trim();
3
+ }
4
+ function normalizeAgentDeepLinkTarget(target) {
5
+ const serverId = normalizeSegment(target.serverId);
6
+ const agentId = normalizeSegment(target.agentId);
7
+ if (!serverId || !agentId) {
8
+ throw new Error("Agent deep links require a server ID and agent ID.");
9
+ }
10
+ return { serverId, agentId };
11
+ }
12
+ export function buildAgentDeepLinkRoute(target) {
13
+ const { serverId, agentId } = normalizeAgentDeepLinkTarget(target);
14
+ return `/h/${encodeURIComponent(serverId)}/agent/${encodeURIComponent(agentId)}`;
15
+ }
16
+ export function buildAgentDeepLink(target) {
17
+ return `otto:/${buildAgentDeepLinkRoute(target)}`;
18
+ }
19
+ export function parseAgentDeepLink(input) {
20
+ let url;
21
+ try {
22
+ url = new URL(input);
23
+ }
24
+ catch {
25
+ return null;
26
+ }
27
+ if (url.protocol !== "otto:" ||
28
+ url.hostname !== "h" ||
29
+ url.username ||
30
+ url.password ||
31
+ url.port ||
32
+ url.search ||
33
+ url.hash) {
34
+ return null;
35
+ }
36
+ const segments = url.pathname.split("/").filter(Boolean);
37
+ if (segments.length !== 3 || segments[1] !== "agent") {
38
+ return null;
39
+ }
40
+ try {
41
+ const serverId = normalizeSegment(decodeURIComponent(segments[0] ?? ""));
42
+ const agentId = normalizeSegment(decodeURIComponent(segments[2] ?? ""));
43
+ return serverId && agentId ? { serverId, agentId } : null;
44
+ }
45
+ catch {
46
+ return null;
47
+ }
48
+ }
49
+ //# sourceMappingURL=agent-deep-link.js.map
@@ -11,9 +11,9 @@ export function isDelegatedAgent(agent) {
11
11
  // ── Orchestration node labels (projects/orchestration-graphs) ───────────────
12
12
  // Stamped by the daemon on agents it spawns as orchestration participants. The
13
13
  // otto-tool catalog reads the policy label to enforce the tool binary:
14
- // "deterministic" the daemon does all linking; the node gets NO orchestration
14
+ // "deterministic" - the daemon does all linking; the node gets NO orchestration
15
15
  // tools (spawning/steering agents, runs), NO preview/dev-server tools, and NO
16
- // browser tools. "autonomous" full otto toolset EXCEPT start_run
16
+ // browser tools. "autonomous" - full otto toolset EXCEPT start_run
17
17
  // (orchestrations never nest orchestrations).
18
18
  export const ORCHESTRATION_POLICY_LABEL = "otto.orchestration-policy";
19
19
  export const ORCHESTRATION_POLICIES = ["deterministic", "autonomous"];
@@ -26,10 +26,10 @@ export function getOrchestrationPolicyFromLabels(labels) {
26
26
  export const ORCHESTRATION_RUN_ID_LABEL = "otto.orchestration-run-id";
27
27
  // The node's declared output fields, JSON-encoded, stamped on the spawned
28
28
  // agent. The otto-tool catalog reads it to register that agent's submit_output
29
- // tool which is why this rides a label rather than a spawn option: the
29
+ // tool - which is why this rides a label rather than a spawn option: the
30
30
  // catalog is built per agent from the agent's own record, so every provider
31
31
  // (MCP-served and native-loop alike) inherits the tool with no per-provider
32
- // plumbing. Malformed JSON reads as "no declared fields" a node that can't
32
+ // plumbing. Malformed JSON reads as "no declared fields" - a node that can't
33
33
  // parse its own contract falls back to prose rather than failing to spawn.
34
34
  export const ORCHESTRATION_OUTPUT_FIELDS_LABEL = "otto.orchestration-output-fields";
35
35
  export function getOutputFieldsFromLabels(labels) {
@@ -24,7 +24,7 @@ export declare function personalityCanLaunch(personality: Pick<AgentPersonality,
24
24
  export interface PersonalitySelectionSummary {
25
25
  tier: PersonalityRoleTier;
26
26
  canLaunch: boolean;
27
- /** The "why you'd choose me" blurb each of the personality's roles, joined. */
27
+ /** The "why you'd choose me" blurb - each of the personality's roles, joined. */
28
28
  guidance: string;
29
29
  }
30
30
  /**
@@ -40,7 +40,7 @@ export declare const ORCHESTRATOR_METHOD_DIRECTIVE: string;
40
40
  * spawn. The orchestrator gets the full conductor method; other coordinators
41
41
  * (chatter/artificer/scheduler) get a lighter delegate nudge; focused workers
42
42
  * are told to stay on the task someone is waiting on. Roleless spawns get
43
- * nothing. This is guidance, not a gate the tools stay available either way.
43
+ * nothing. This is guidance, not a gate - the tools stay available either way.
44
44
  */
45
45
  export declare function composeRoleFocusDirective(roles: readonly string[] | undefined): string | undefined;
46
46
  export type PersonalityUnavailableCode = "provider-missing" | "provider-disabled" | "provider-not-ready" | "model-missing" | "mode-missing";
@@ -1,7 +1,7 @@
1
1
  import { PERSONALITY_ROLES } from "./messages.js";
2
2
  // Pure, dependency-free personality helpers shared by the daemon (spawn-time
3
3
  // resolution) and the app (picker availability + role filtering). Effort
4
- // resolution is NOT here it needs the model's advertised thinking options and
4
+ // resolution is NOT here - it needs the model's advertised thinking options and
5
5
  // lives with the daemon's effort resolver; availability does not depend on it.
6
6
  const ROLE_SET = new Set(PERSONALITY_ROLES);
7
7
  // Retired role names, mapped to their canonical replacement. "worker" was split
@@ -41,7 +41,7 @@ export const PERSONALITY_ROLE_INFO = {
41
41
  // ── Surfaces ──────────────────────────────────────────────────────────────
42
42
  chatter: {
43
43
  tier: "coordinator",
44
- guidance: "Interactive driver converse, plan, and delegate. Pick to run a chat or coordinate work.",
44
+ guidance: "Interactive driver - converse, plan, and delegate. Pick to run a chat or coordinate work.",
45
45
  },
46
46
  artificer: {
47
47
  tier: "coordinator",
@@ -54,37 +54,37 @@ export const PERSONALITY_ROLE_INFO = {
54
54
  // ── Thinking workers (read-only, structured findings) ─────────────────────
55
55
  researcher: {
56
56
  tier: "focused",
57
- guidance: "Read-only surveyor maps the code or domain and reports files, types, patterns, and gotchas. Pick to gather facts; proposes no solutions and edits nothing.",
57
+ guidance: "Read-only surveyor - maps the code or domain and reports files, types, patterns, and gotchas. Pick to gather facts; proposes no solutions and edits nothing.",
58
58
  },
59
59
  planner: {
60
60
  tier: "focused",
61
- guidance: "Planning specialist turns a goal into a typed, sequenced phase plan for others to execute. Pick to draft an actionable plan; stays on the plan and doesn't dispatch.",
61
+ guidance: "Planning specialist - turns a goal into a typed, sequenced phase plan for others to execute. Pick to draft an actionable plan; stays on the plan and doesn't dispatch.",
62
62
  },
63
63
  judger: {
64
64
  tier: "focused",
65
- guidance: "Review specialist evaluates work or a plan against criteria and returns a structured verdict. Pick for a focused review; stays on task.",
65
+ guidance: "Review specialist - evaluates work or a plan against criteria and returns a structured verdict. Pick for a focused review; stays on task.",
66
66
  },
67
67
  advisor: {
68
68
  tier: "focused",
69
- guidance: "Read-only second opinion weighs the trade-offs and returns one recommendation. Pick for advice; never edits and does not fan out.",
69
+ guidance: "Read-only second opinion - weighs the trade-offs and returns one recommendation. Pick for advice; never edits and does not fan out.",
70
70
  },
71
71
  // ── Making workers (produce code, design, or text) ────────────────────────
72
72
  coder: {
73
73
  tier: "focused",
74
- guidance: "Focused implementer writes code for one sub-task others are waiting on. Pick to get a coding job done; stays on task.",
74
+ guidance: "Focused implementer - writes code for one sub-task others are waiting on. Pick to get a coding job done; stays on task.",
75
75
  },
76
76
  designer: {
77
77
  tier: "focused",
78
- guidance: "Design maker styling and layout plus the human-skill text (copy, naming). Pick for the look-and-feel or the words; stays on task.",
78
+ guidance: "Design maker - styling and layout plus the human-skill text (copy, naming). Pick for the look-and-feel or the words; stays on task.",
79
79
  },
80
80
  writer: {
81
81
  tier: "focused",
82
- guidance: "Fast small-text specialist commit messages, summaries, names. Pick for quick text; stays on the one task.",
82
+ guidance: "Fast small-text specialist - commit messages, summaries, names. Pick for quick text; stays on the one task.",
83
83
  },
84
84
  // ── Conductor ─────────────────────────────────────────────────────────────
85
85
  orchestrator: {
86
86
  tier: "coordinator",
87
- guidance: "The sole conductor plans team-shaped work, dispatches typed tasks to the right teammates, gathers, and synthesizes. Pick to run a multi-agent workflow.",
87
+ guidance: "The sole conductor - plans team-shaped work, dispatches typed tasks to the right teammates, gathers, and synthesizes. Pick to run a multi-agent workflow.",
88
88
  },
89
89
  };
90
90
  /**
@@ -111,21 +111,21 @@ export function summarizePersonalityForSelection(personality) {
111
111
  guidance: roles.map((role) => PERSONALITY_ROLE_INFO[role].guidance).join(" "),
112
112
  };
113
113
  }
114
- // The conductor's standing directive the distilled `/epic` method taught to
114
+ // The conductor's standing directive - the distilled `/epic` method taught to
115
115
  // the sole orchestrator role at spawn, so orchestration is emergent (the agent
116
116
  // recognizes team-shaped work and runs it) rather than something a user must
117
117
  // invoke. Kept here as one exported constant so the wording is testable and
118
118
  // shared. See projects/agent-orchestration/agent-orchestration.md.
119
- export const ORCHESTRATOR_METHOD_DIRECTIVE = "You are the orchestrator the team's sole conductor. Team-shaped work is yours to run, and you should reach for it naturally, not only when asked. " +
120
- "First apply the complexity gate: if a task is small and not splittable, just do it no ceremony. Only orchestrate when the work is large, parallelizable, or benefits from independent perspectives. " +
121
- "When you do orchestrate: (1) if the shape is unclear, dispatch a researcher to survey and a planner to draft a typed plan; (2) declare that plan as a Run with start_run phases typed research/plan/implement/design/verify/gate/deliver, fanning out candidates where several angles help and attaching a judger to grade them, looping until enough pass; (3) put a gate before irreversible or costly steps so the user approves; (4) synthesize the passing results into the deliverable. " +
122
- "Prefer start_run over hand-spawning and tracking agents yourself the runtime fans out, gathers typed verdicts, and enforces the loop for you. Every phase maps to a teammate's role; if the active team lacks a role a phase needs, say so plainly and stop rather than papering over the gap.";
119
+ export const ORCHESTRATOR_METHOD_DIRECTIVE = "You are the orchestrator - the team's sole conductor. Team-shaped work is yours to run, and you should reach for it naturally, not only when asked. " +
120
+ "First apply the complexity gate: if a task is small and not splittable, just do it - no ceremony. Only orchestrate when the work is large, parallelizable, or benefits from independent perspectives. " +
121
+ "When you do orchestrate: (1) if the shape is unclear, dispatch a researcher to survey and a planner to draft a typed plan; (2) declare that plan as a Run with start_run - phases typed research/plan/implement/design/verify/gate/deliver, fanning out candidates where several angles help and attaching a judger to grade them, looping until enough pass; (3) put a gate before irreversible or costly steps so the user approves; (4) synthesize the passing results into the deliverable. " +
122
+ "Prefer start_run over hand-spawning and tracking agents yourself - the runtime fans out, gathers typed verdicts, and enforces the loop for you. Every phase maps to a teammate's role; if the active team lacks a role a phase needs, say so plainly and stop rather than papering over the gap.";
123
123
  /**
124
124
  * The in-context "role directive" injected into a personality's system prompt at
125
125
  * spawn. The orchestrator gets the full conductor method; other coordinators
126
126
  * (chatter/artificer/scheduler) get a lighter delegate nudge; focused workers
127
127
  * are told to stay on the task someone is waiting on. Roleless spawns get
128
- * nothing. This is guidance, not a gate the tools stay available either way.
128
+ * nothing. This is guidance, not a gate - the tools stay available either way.
129
129
  */
130
130
  export function composeRoleFocusDirective(roles) {
131
131
  const normalized = normalizePersonalityRoles(roles);
@@ -139,7 +139,7 @@ export function composeRoleFocusDirective(roles) {
139
139
  if (normalized.some((role) => PERSONALITY_ROLE_INFO[role].tier === "coordinator")) {
140
140
  return `You are a coordinator personality (roles: ${roleList}). You front interactive work and may delegate: use list_personalities to see who else is available, and spawn other agents or hand off to the team's orchestrator whenever delegating gets the work done faster or better.`;
141
141
  }
142
- return `You are a focused worker personality (roles: ${roleList}). Someone is waiting on this specific task stay on it and finish it. You can still call list_personalities to see the roster, but don't spawn sub-agents or start side workflows unless it is genuinely essential to completing this job.`;
142
+ return `You are a focused worker personality (roles: ${roleList}). Someone is waiting on this specific task - stay on it and finish it. You can still call list_personalities to see the roster, but don't spawn sub-agents or start side workflows unless it is genuinely essential to completing this job.`;
143
143
  }
144
144
  /**
145
145
  * Decide whether a personality is usable against a provider's current snapshot.
@@ -3,7 +3,7 @@ import type { AgentPersonality, AgentTeam, PersonalityRole } from "./messages.js
3
3
  * The dynamic "Team's Scheduler" schedule binding. Stored in the schedule's
4
4
  * `personality` field in place of a personality name; resolved at RUN time to
5
5
  * the active team's first available member carrying the Scheduler role
6
- * (member order). Deliberately contains "@" personality names are sanitized
6
+ * (member order). Deliberately contains "@" - personality names are sanitized
7
7
  * to [A-Za-z0-9_-] at the authoring surface, so the sentinel can never collide
8
8
  * with a real name. No active team, or no available Scheduler member, is a
9
9
  * hard run failure with a named error (same loudness as a bound personality
@@ -17,7 +17,7 @@ export interface AgentTeamsConfigView {
17
17
  export declare function findAgentTeam(teams: readonly AgentTeam[] | undefined, teamId: string | null | undefined): AgentTeam | null;
18
18
  /**
19
19
  * Resolve the host's active team. A dangling `activeTeamId` (team deleted, or
20
- * a patch raced a delete) reads as "no team active" teamlessness is a valid
20
+ * a patch raced a delete) reads as "no team active" - teamlessness is a valid
21
21
  * state and must never error. The daemon additionally heals a dangling id back
22
22
  * to null on the next config patch; this helper is the read-side tolerance.
23
23
  */
@@ -42,13 +42,13 @@ export declare function pruneTeamMemberIds(memberIds: readonly string[] | undefi
42
42
  * team at all, so the delete confirm can offer to clean them up. Dangling member
43
43
  * ids resolve to nothing and drop out, same as everywhere else.
44
44
  *
45
- * Pass the teams that will REMAIN after the delete the caller owns the filter,
45
+ * Pass the teams that will REMAIN after the delete - the caller owns the filter,
46
46
  * so this stays a pure set operation with no notion of "the team being deleted".
47
47
  */
48
48
  export declare function resolveExclusiveTeamMembers(team: Pick<AgentTeam, "memberIds"> | null | undefined, otherTeams: readonly Pick<AgentTeam, "memberIds">[] | undefined, personalities: readonly AgentPersonality[] | undefined): AgentPersonality[];
49
49
  /**
50
50
  * The union of all members' roles, normalized and returned in canonical
51
- * `PERSONALITY_ROLES` order the team card's role-pill strip.
51
+ * `PERSONALITY_ROLES` order - the team card's role-pill strip.
52
52
  */
53
53
  export declare function teamRoleUnion(team: Pick<AgentTeam, "memberIds"> | null | undefined, personalities: readonly AgentPersonality[] | undefined): PersonalityRole[];
54
54
  /**
@@ -1,14 +1,14 @@
1
1
  import { normalizePersonalityRoles } from "./agent-personalities.js";
2
2
  // Pure, dependency-free team helpers shared by the daemon (spawn-time active
3
3
  // team resolution, list_personalities scoping) and the app (team cards,
4
- // pickers, the Active Team switcher). Availability is deliberately NOT here
4
+ // pickers, the Active Team switcher). Availability is deliberately NOT here -
5
5
  // a team is never "out of commission"; its members are individually available
6
6
  // or not, judged by checkPersonalityAvailability per member.
7
7
  /**
8
8
  * The dynamic "Team's Scheduler" schedule binding. Stored in the schedule's
9
9
  * `personality` field in place of a personality name; resolved at RUN time to
10
10
  * the active team's first available member carrying the Scheduler role
11
- * (member order). Deliberately contains "@" personality names are sanitized
11
+ * (member order). Deliberately contains "@" - personality names are sanitized
12
12
  * to [A-Za-z0-9_-] at the authoring surface, so the sentinel can never collide
13
13
  * with a real name. No active team, or no available Scheduler member, is a
14
14
  * hard run failure with a named error (same loudness as a bound personality
@@ -23,7 +23,7 @@ export function findAgentTeam(teams, teamId) {
23
23
  }
24
24
  /**
25
25
  * Resolve the host's active team. A dangling `activeTeamId` (team deleted, or
26
- * a patch raced a delete) reads as "no team active" teamlessness is a valid
26
+ * a patch raced a delete) reads as "no team active" - teamlessness is a valid
27
27
  * state and must never error. The daemon additionally heals a dangling id back
28
28
  * to null on the next config patch; this helper is the read-side tolerance.
29
29
  */
@@ -86,7 +86,7 @@ export function pruneTeamMemberIds(memberIds, personalities) {
86
86
  * team at all, so the delete confirm can offer to clean them up. Dangling member
87
87
  * ids resolve to nothing and drop out, same as everywhere else.
88
88
  *
89
- * Pass the teams that will REMAIN after the delete the caller owns the filter,
89
+ * Pass the teams that will REMAIN after the delete - the caller owns the filter,
90
90
  * so this stays a pure set operation with no notion of "the team being deleted".
91
91
  */
92
92
  export function resolveExclusiveTeamMembers(team, otherTeams, personalities) {
@@ -100,7 +100,7 @@ export function resolveExclusiveTeamMembers(team, otherTeams, personalities) {
100
100
  }
101
101
  /**
102
102
  * The union of all members' roles, normalized and returned in canonical
103
- * `PERSONALITY_ROLES` order the team card's role-pill strip.
103
+ * `PERSONALITY_ROLES` order - the team card's role-pill strip.
104
104
  */
105
105
  export function teamRoleUnion(team, personalities) {
106
106
  const roles = resolveTeamMembers(team, personalities).flatMap((personality) => personality.roles ?? []);
@@ -110,6 +110,7 @@ export interface ProviderSnapshotEntry {
110
110
  provider: AgentProvider;
111
111
  status: ProviderStatus;
112
112
  enabled: boolean;
113
+ source?: "builtin" | "custom";
113
114
  error?: string;
114
115
  models?: AgentModelDefinition[];
115
116
  modes?: AgentMode[];
@@ -177,18 +178,18 @@ export interface AgentRunOptions {
177
178
  * origin. Powers the visualizer's context ring/bar colored segments (the same
178
179
  * five categories the vendored render layer draws). Every field is optional and
179
180
  * a token count: a provider fills as many categories as it can attribute and
180
- * omits the rest, so richness degrades gracefully per provider Claude
181
+ * omits the rest, so richness degrades gracefully per provider - Claude
181
182
  * attributes the most; a provider that can attribute nothing omits the whole
182
183
  * object and consumers fall back to occupancy-only (no colored segments, the
183
184
  * pre-composition behavior). Counts are best-effort estimates, not billed usage;
184
185
  * they need not sum exactly to `contextWindowUsedTokens` (the consumer scales).
185
186
  */
186
187
  export interface ContextComposition {
187
- /** System prompt + tool/function definitions the fixed base cost. */
188
+ /** System prompt + tool/function definitions - the fixed base cost. */
188
189
  systemPrompt?: number;
189
190
  /** User-authored input messages. */
190
191
  userMessages?: number;
191
- /** Tool results file contents, search output, command output (usually the
192
+ /** Tool results - file contents, search output, command output (usually the
192
193
  * largest and most volatile category). */
193
194
  toolResults?: number;
194
195
  /** The agent's own reasoning / thinking blocks. */
@@ -198,14 +199,14 @@ export interface ContextComposition {
198
199
  }
199
200
  /**
200
201
  * One provider-reported context-window category. `name` is a provider-supplied
201
- * *display label* ("Messages", "System prompt", "MCP tools") deliberately an
202
+ * *display label* ("Messages", "System prompt", "MCP tools") - deliberately an
202
203
  * OPEN-ENDED string and not an enum, so each provider reports the split it
203
204
  * actually has instead of being squeezed into categories it can't populate.
204
205
  *
205
206
  * Structurally identical to `AgentContextUsageCategory` on the pull path
206
207
  * (`agent.context.get_usage`, see messages.ts) *by design*: the two carry the
207
208
  * same accounting, one pushed on the agent snapshot and one pulled on demand.
208
- * Keep them in sync a divergence here is how the context meter and the
209
+ * Keep them in sync - a divergence here is how the context meter and the
209
210
  * visualizer would start disagreeing about the same agent.
210
211
  */
211
212
  export interface AgentContextCategory {
@@ -218,7 +219,7 @@ export interface AgentUsage {
218
219
  inputTokens?: number;
219
220
  cachedInputTokens?: number;
220
221
  /**
221
- * Prompt tokens spent writing (not reading) the prompt cache this turn
222
+ * Prompt tokens spent writing (not reading) the prompt cache this turn -
222
223
  * Anthropic's `cache_creation_input_tokens`, billed above normal input.
223
224
  * Disjoint from `inputTokens`/`cachedInputTokens`. Claude-specific today;
224
225
  * other providers omit it. Optional/additive.
@@ -235,7 +236,7 @@ export interface AgentUsage {
235
236
  */
236
237
  contextComposition?: ContextComposition;
237
238
  /**
238
- * The provider's OWN context-window split, with its own labels the same
239
+ * The provider's OWN context-window split, with its own labels - the same
239
240
  * accounting the `agent.context.get_usage` RPC returns, pushed here on the
240
241
  * agent snapshot so stream/backfill consumers (the visualizer) read the very
241
242
  * numbers the context meter shows rather than a parallel estimate.
@@ -373,6 +374,7 @@ export type AgentTimelineItem = {
373
374
  type: "user_message";
374
375
  text: string;
375
376
  messageId?: string;
377
+ clientMessageId?: string;
376
378
  } | {
377
379
  type: "assistant_message";
378
380
  text: string;
@@ -490,7 +492,7 @@ export interface ObservedSubagentUpdate {
490
492
  requiresAttention?: boolean;
491
493
  usage?: AgentUsage;
492
494
  /**
493
- * True once this run is known to outlive an interrupt of the parent's turn
495
+ * True once this run is known to outlive an interrupt of the parent's turn -
494
496
  * the provider backgrounded it, so the parent's teardown does not take it
495
497
  * down. Sticky: the daemon keeps it set for the row's whole life, and
496
498
  * propagates it to nested rows (a child of a backgrounded run survives too).
@@ -509,7 +511,7 @@ export interface ObservedSubagentUpdate {
509
511
  */
510
512
  toolUseCount?: number;
511
513
  /**
512
- * The tool this subagent is running (or ran last) the "spinning _on a 90s
514
+ * The tool this subagent is running (or ran last) - the "spinning _on a 90s
513
515
  * Bash_" signal. Neutral field any provider can set; unlike the counters it is
514
516
  * NOT monotonic (latest wins) and the daemon drops it once the row goes
515
517
  * terminal. Claude reads it from `task_progress.last_tool_name`. Absent ⇒ the
@@ -521,7 +523,7 @@ export interface ObservedSubagentUpdate {
521
523
  * A background shell task reported by a provider's own Bash tool (Claude:
522
524
  * `run_in_background`). `key` is a provider-local stable identifier (Claude:
523
525
  * the Bash tool_use id); the daemon namespaces it under the owning agent.
524
- * Unlike {@link ObservedSubagentUpdate} this has no transcript/pane it's a
526
+ * Unlike {@link ObservedSubagentUpdate} this has no transcript/pane - it's a
525
527
  * plain status row (command, status, elapsed) in the Background Tasks track.
526
528
  */
527
529
  export interface BackgroundShellTaskUpdate {
@@ -26,7 +26,7 @@ export function getAgentStreamEventTurnId(event) {
26
26
  // accept the setting and ignore it.
27
27
  //
28
28
  // This is a *boundary*, not an instruction. "read" means the write tools are
29
- // not offered, so there is no write to make never a line of prompt asking the
29
+ // not offered, so there is no write to make - never a line of prompt asking the
30
30
  // model to behave. A control a user relies on when deciding to run something
31
31
  // unattended has to be true in exactly the cases where the model is confused.
32
32
  export const WORKSPACE_ACCESS_LEVELS = ["none", "read", "write"];
@@ -20,13 +20,13 @@ export const ArtifactCreateRequestSchema = z.object({
20
20
  systemPrompt: z.string().optional(),
21
21
  // Snapshotted spinner colors of the chosen Agent Personality (optional).
22
22
  spinner: ArtifactSpinnerSchema.optional(),
23
- // Snapshotted human name of the chosen Agent Personality (optional) shown
23
+ // Snapshotted human name of the chosen Agent Personality (optional) - shown
24
24
  // on the artifact card's identity line as the "who generated it".
25
25
  personalityName: z.string().optional(),
26
26
  requestId: z.string(),
27
27
  });
28
28
  // Update edits an artifact's metadata (name/description/project/provider/
29
- // model/thinking) WITHOUT re-running generation editing never regenerates;
29
+ // model/thinking) WITHOUT re-running generation - editing never regenerates;
30
30
  // the user triggers that separately. Every field except artifactId is
31
31
  // optional; only provided fields overwrite.
32
32
  export const ArtifactUpdateRequestSchema = z.object({
@@ -41,7 +41,7 @@ export const ArtifactUpdateRequestSchema = z.object({
41
41
  requestId: z.string(),
42
42
  });
43
43
  // Regenerate re-runs generation for an existing artifact using its stored
44
- // config. It carries no field updates edit those first via update.request.
44
+ // config. It carries no field updates - edit those first via update.request.
45
45
  export const ArtifactRegenerateRequestSchema = z.object({
46
46
  type: z.literal("artifact.regenerate.request"),
47
47
  artifactId: z.string(),
@@ -166,7 +166,7 @@ export const ArtifactDeletedNotificationSchema = z.object({
166
166
  artifactId: z.string(),
167
167
  }),
168
168
  });
169
- // Input validation helper extracts CreateArtifactInput from a create request
169
+ // Input validation helper - extracts CreateArtifactInput from a create request
170
170
  export function validateArtifactCreateRequest(data) {
171
171
  const result = ArtifactCreateRequestSchema.safeParse(data);
172
172
  if (!result.success) {
@@ -27,7 +27,7 @@ export const ArtifactMetadataSchema = z.object({
27
27
  generationModel: z.string().nullable(),
28
28
  // Requested generation mode/effort, persisted so regeneration re-runs with
29
29
  // the same settings. Optional: records written before these fields existed
30
- // omit them (no migrations). The mode is a *request* the artifact service
30
+ // omit them (no migrations). The mode is a *request* - the artifact service
31
31
  // only honors unattended modes and otherwise resolves the provider's
32
32
  // unattended default, so generation never stalls on an approval prompt.
33
33
  generationModeId: z.string().nullable().optional(),
@@ -39,7 +39,7 @@ export const ArtifactMetadataSchema = z.object({
39
39
  generationSpinner: ArtifactSpinnerSchema.nullable().optional(),
40
40
  // Human name of the Agent Personality that generated (last generated) this
41
41
  // artifact, snapshotted at create/regenerate time like the provider/model
42
- // above the "who actually did it" the card's identity line shows alongside
42
+ // above - the "who actually did it" the card's identity line shows alongside
43
43
  // provider/model. Absent ⇒ no personality was used (plain provider/model
44
44
  // selection). Purely additive (no daemon floor needed).
45
45
  generationPersonalityName: z.string().nullable().optional(),
@@ -71,7 +71,7 @@ export const ArtifactRunSchema = z.object({
71
71
  });
72
72
  // The full on-disk record: the lean metadata plus its generation run history.
73
73
  // `runs` defaults to [] so records written before run history existed parse
74
- // unchanged (no migrations same approach the rest of the store takes).
74
+ // unchanged (no migrations - same approach the rest of the store takes).
75
75
  // list_artifacts / broadcasts keep sending ArtifactMetadata (runs stripped);
76
76
  // only inspect_artifact returns this fuller shape.
77
77
  export const StoredArtifactSchema = ArtifactMetadataSchema.extend({
@@ -13,6 +13,7 @@ export declare const FileBeginMetadataSchema: z.ZodObject<{
13
13
  "utf-8": "utf-8";
14
14
  }>;
15
15
  modifiedAt: z.ZodString;
16
+ revision: z.ZodOptional<z.ZodString>;
16
17
  fileName: z.ZodOptional<z.ZodString>;
17
18
  }, z.core.$strip>;
18
19
  export interface FileBegin {
@@ -10,6 +10,7 @@ export const FileBeginMetadataSchema = z.object({
10
10
  size: z.number().int().nonnegative(),
11
11
  encoding: z.enum(["utf-8", "binary"]),
12
12
  modifiedAt: z.string(),
13
+ revision: z.string().optional(),
13
14
  fileName: z.string().optional(),
14
15
  });
15
16
  export function encodeFileTransferFrame(input) {
@@ -418,7 +418,7 @@ export const BrowserAutomationScreenshotElementResultSchema = z.object({
418
418
  dataBase64: z.string().min(1),
419
419
  width: z.number().int().nonnegative(),
420
420
  height: z.number().int().nonnegative(),
421
- /** Render scale used for the clip above 1 means the element was re-rendered zoomed in. */
421
+ /** Render scale used for the clip - above 1 means the element was re-rendered zoomed in. */
422
422
  scale: z.number().positive().optional(),
423
423
  });
424
424
  export const BrowserAutomationUploadResultSchema = z.object({
@@ -1,7 +1,10 @@
1
1
  export declare const CLIENT_CAPS: {
2
+ readonly selectiveAgentTimeline: "selective_agent_timeline";
2
3
  readonly reasoningMergeEnum: "reasoning_merge_enum";
3
4
  readonly customModeIcons: "custom_mode_icons";
4
5
  readonly terminalReflowableSnapshot: "terminal_reflowable_snapshot";
6
+ readonly providerSubagents: "provider_subagents";
7
+ readonly projectUpdates: "project_updates";
5
8
  readonly browserHost: "browser_host";
6
9
  };
7
10
  export type ClientCapability = (typeof CLIENT_CAPS)[keyof typeof CLIENT_CAPS];
@@ -1,4 +1,8 @@
1
1
  export const CLIENT_CAPS = {
2
+ // COMPAT(selectiveAgentTimeline): added in v0.1.106. Capable clients receive
3
+ // agent streams only for their explicit viewed set. Remove after 2027-01-12
4
+ // once the supported client floor is >= v0.1.106.
5
+ selectiveAgentTimeline: "selective_agent_timeline",
2
6
  reasoningMergeEnum: "reasoning_merge_enum",
3
7
  // COMPAT(customModeIcons): added in v0.1.84. Old clients pin AgentModeIcon to
4
8
  // a closed enum and crash rendering unknown values; daemon downgrades icons
@@ -11,6 +15,11 @@ export const CLIENT_CAPS = {
11
15
  // Old clients use a strict TerminalState schema and would reject the extra fields.
12
16
  // Drop the gate (always send the flags) when floor >= v0.1.88.
13
17
  terminalReflowableSnapshot: "terminal_reflowable_snapshot",
18
+ // COMPAT(providerSubagents): added in v0.1.107. The daemon emits provider-owned
19
+ // child descriptors and timelines only to clients that understand the new messages.
20
+ providerSubagents: "provider_subagents",
21
+ // COMPAT(projectUpdates): added in v0.1.109, remove gate after 2027-01-15.
22
+ projectUpdates: "project_updates",
14
23
  browserHost: "browser_host",
15
24
  };
16
25
  //# sourceMappingURL=client-capabilities.js.map
@@ -32,7 +32,7 @@ export declare function buildRelayWebSocketUrl(params: {
32
32
  role: RelayRole;
33
33
  /**
34
34
  * Per-connection routing identifier used by the daemon to open server data sockets.
35
- * Clients should NOT provide this the relay assigns a routing ID on connect.
35
+ * Clients should NOT provide this - the relay assigns a routing ID on connect.
36
36
  */
37
37
  connectionId?: string;
38
38
  version?: RelayProtocolVersion | 1 | 2;