@otto-code/protocol 0.6.7 → 0.7.1

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.
@@ -9,4 +9,25 @@ export declare const ORCHESTRATION_POLICIES: readonly ["deterministic", "autonom
9
9
  export type OrchestrationPolicy = (typeof ORCHESTRATION_POLICIES)[number];
10
10
  export declare function getOrchestrationPolicyFromLabels(labels: Record<string, unknown> | null | undefined): OrchestrationPolicy | null;
11
11
  export declare const ORCHESTRATION_RUN_ID_LABEL = "otto.orchestration-run-id";
12
+ export declare const ORCHESTRATION_OUTPUT_FIELDS_LABEL = "otto.orchestration-output-fields";
13
+ export interface OrchestrationOutputField {
14
+ key: string;
15
+ type: string;
16
+ description?: string;
17
+ required?: boolean;
18
+ }
19
+ export declare function getOutputFieldsFromLabels(labels: Record<string, unknown> | null | undefined): OrchestrationOutputField[] | null;
20
+ export declare const ORCHESTRATION_TOOL_GROUPS_LABEL = "otto.orchestration-tool-groups";
21
+ export declare function getToolGroupsFromLabels(labels: Record<string, unknown> | null | undefined): string[] | null;
22
+ export declare const ORCHESTRATION_QUERY_TOOLS_LABEL = "otto.orchestration-query-tools";
23
+ export interface OrchestrationQueryToolLabel {
24
+ name: string;
25
+ description: string;
26
+ kind: string;
27
+ parameters?: OrchestrationOutputField[];
28
+ command?: string[];
29
+ url?: string;
30
+ path?: string;
31
+ }
32
+ export declare function getQueryToolsFromLabels(labels: Record<string, unknown> | null | undefined): OrchestrationQueryToolLabel[] | null;
12
33
  //# sourceMappingURL=agent-labels.d.ts.map
@@ -24,4 +24,70 @@ export function getOrchestrationPolicyFromLabels(labels) {
24
24
  // First-class run attribution for orchestration children (parentage rides
25
25
  // PARENT_AGENT_ID_LABEL; this ties the child to the run record itself).
26
26
  export const ORCHESTRATION_RUN_ID_LABEL = "otto.orchestration-run-id";
27
+ // The node's declared output fields, JSON-encoded, stamped on the spawned
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
30
+ // catalog is built per agent from the agent's own record, so every provider
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
33
+ // parse its own contract falls back to prose rather than failing to spawn.
34
+ export const ORCHESTRATION_OUTPUT_FIELDS_LABEL = "otto.orchestration-output-fields";
35
+ export function getOutputFieldsFromLabels(labels) {
36
+ return parseJsonArrayLabel(labels, ORCHESTRATION_OUTPUT_FIELDS_LABEL, (candidate) => {
37
+ return typeof candidate.key === "string" && typeof candidate.type === "string";
38
+ });
39
+ }
40
+ // The node's per-node Otto tool-group allowlist, JSON-encoded. Read by the tool
41
+ // catalog to narrow (never widen) what this one agent may reach.
42
+ export const ORCHESTRATION_TOOL_GROUPS_LABEL = "otto.orchestration-tool-groups";
43
+ export function getToolGroupsFromLabels(labels) {
44
+ const raw = labels?.[ORCHESTRATION_TOOL_GROUPS_LABEL];
45
+ if (typeof raw !== "string" || raw.trim().length === 0) {
46
+ return null;
47
+ }
48
+ try {
49
+ const parsed = JSON.parse(raw);
50
+ if (!Array.isArray(parsed)) {
51
+ return null;
52
+ }
53
+ const groups = parsed.filter((entry) => typeof entry === "string");
54
+ // An empty allowlist is meaningful ("no Otto tools at all"), so it is
55
+ // returned as an empty array rather than collapsing to null.
56
+ return groups;
57
+ }
58
+ catch {
59
+ return null;
60
+ }
61
+ }
62
+ // The node's query tools, JSON-encoded. Read by the tool catalog, which
63
+ // registers each one for this agent alone.
64
+ export const ORCHESTRATION_QUERY_TOOLS_LABEL = "otto.orchestration-query-tools";
65
+ export function getQueryToolsFromLabels(labels) {
66
+ return parseJsonArrayLabel(labels, ORCHESTRATION_QUERY_TOOLS_LABEL, (candidate) => {
67
+ return typeof candidate.name === "string" && typeof candidate.kind === "string";
68
+ });
69
+ }
70
+ // Labels are strings, so structured node config rides as JSON. Malformed JSON
71
+ // reads as "nothing declared" everywhere: a node that cannot parse its own
72
+ // configuration should lose the capability, never fail to spawn.
73
+ function parseJsonArrayLabel(labels, key, isValid) {
74
+ const raw = labels?.[key];
75
+ if (typeof raw !== "string" || raw.trim().length === 0) {
76
+ return null;
77
+ }
78
+ let parsed;
79
+ try {
80
+ parsed = JSON.parse(raw);
81
+ }
82
+ catch {
83
+ return null;
84
+ }
85
+ if (!Array.isArray(parsed)) {
86
+ return null;
87
+ }
88
+ const entries = parsed.filter((entry) => {
89
+ return typeof entry === "object" && entry !== null && isValid(entry);
90
+ });
91
+ return entries.length > 0 ? entries : null;
92
+ }
27
93
  //# sourceMappingURL=agent-labels.js.map
@@ -196,6 +196,24 @@ export interface ContextComposition {
196
196
  /** Content returned by child / observed sub-agents. */
197
197
  subagentResults?: number;
198
198
  }
199
+ /**
200
+ * One provider-reported context-window category. `name` is a provider-supplied
201
+ * *display label* ("Messages", "System prompt", "MCP tools") — deliberately an
202
+ * OPEN-ENDED string and not an enum, so each provider reports the split it
203
+ * actually has instead of being squeezed into categories it can't populate.
204
+ *
205
+ * Structurally identical to `AgentContextUsageCategory` on the pull path
206
+ * (`agent.context.get_usage`, see messages.ts) *by design*: the two carry the
207
+ * 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
+ * visualizer would start disagreeing about the same agent.
210
+ */
211
+ export interface AgentContextCategory {
212
+ name: string;
213
+ tokens: number;
214
+ /** Deferred content (e.g. on-demand tool schemas) is not counted in the total. */
215
+ isDeferred?: boolean;
216
+ }
199
217
  export interface AgentUsage {
200
218
  inputTokens?: number;
201
219
  cachedInputTokens?: number;
@@ -216,6 +234,18 @@ export interface AgentUsage {
216
234
  * shows occupancy only. Added for the visualizer context ring/bar.
217
235
  */
218
236
  contextComposition?: ContextComposition;
237
+ /**
238
+ * The provider's OWN context-window split, with its own labels — the same
239
+ * accounting the `agent.context.get_usage` RPC returns, pushed here on the
240
+ * agent snapshot so stream/backfill consumers (the visualizer) read the very
241
+ * numbers the context meter shows rather than a parallel estimate.
242
+ *
243
+ * Preferred over {@link ContextComposition} wherever present. The estimate
244
+ * remains the coarse tier for providers that can't report a real split;
245
+ * absence of both ⇒ occupancy only. Optional/additive: absence is the
246
+ * graceful-degrade signal, so no capability flag is needed.
247
+ */
248
+ contextCategories?: AgentContextCategory[];
219
249
  }
220
250
  export declare const TOOL_CALL_ICON_NAMES: readonly ["wrench", "square_terminal", "eye", "pencil", "search", "bot", "sparkles", "brain", "mic_vocal"];
221
251
  export type ToolCallIconName = (typeof TOOL_CALL_ICON_NAMES)[number];
@@ -459,6 +489,21 @@ export interface ObservedSubagentUpdate {
459
489
  status: "initializing" | "running" | "idle" | "error" | "closed";
460
490
  requiresAttention?: boolean;
461
491
  usage?: AgentUsage;
492
+ /**
493
+ * Tool invocations this subagent has made so far (cumulative). Neutral field
494
+ * any provider can set; kept monotonic by the daemon so a status-only final
495
+ * update can't drop the readout. Claude reads it from the SDK's per-task
496
+ * `usage.tool_uses`. Absent ⇒ the row shows no count.
497
+ */
498
+ toolUseCount?: number;
499
+ /**
500
+ * The tool this subagent is running (or ran last) — the "spinning _on a 90s
501
+ * Bash_" signal. Neutral field any provider can set; unlike the counters it is
502
+ * NOT monotonic (latest wins) and the daemon drops it once the row goes
503
+ * terminal. Claude reads it from `task_progress.last_tool_name`. Absent ⇒ the
504
+ * row omits it; never guess a value.
505
+ */
506
+ currentTool?: string;
462
507
  }
463
508
  /**
464
509
  * A background shell task reported by a provider's own Bash tool (Claude:
@@ -517,6 +562,9 @@ export interface AgentRunResult {
517
562
  timeline: AgentTimelineItem[];
518
563
  canceled?: boolean;
519
564
  }
565
+ export declare const WORKSPACE_ACCESS_LEVELS: readonly ["none", "read", "write"];
566
+ export type WorkspaceAccess = (typeof WORKSPACE_ACCESS_LEVELS)[number];
567
+ export declare function isWorkspaceAccess(value: unknown): value is WorkspaceAccess;
520
568
  export interface AgentSessionConfig {
521
569
  provider: AgentProvider;
522
570
  cwd: string;
@@ -532,6 +580,13 @@ export interface AgentSessionConfig {
532
580
  title?: string | null;
533
581
  approvalPolicy?: string;
534
582
  sandboxMode?: string;
583
+ /**
584
+ * Ceiling on what this session may do to its workspace (see
585
+ * WORKSPACE_ACCESS_LEVELS). Absent ⇒ "write", today's behaviour. Set by graph
586
+ * nodes that declare an access level; each provider adapter narrows its own
587
+ * tool surface to match.
588
+ */
589
+ workspaceAccess?: string;
535
590
  networkAccess?: boolean;
536
591
  webSearch?: boolean;
537
592
  extra?: {
@@ -19,4 +19,18 @@ export const TOOL_CALL_ICON_NAMES = [
19
19
  export function getAgentStreamEventTurnId(event) {
20
20
  return "turnId" in event ? event.turnId : undefined;
21
21
  }
22
+ // ── Workspace access ────────────────────────────────────────────────────────
23
+ // How much of its workspace one agent session may touch. Provider-neutral by
24
+ // intent: each adapter maps it onto whatever its own runtime enforces, and a
25
+ // provider that cannot enforce it must say so (AgentCapabilities) rather than
26
+ // accept the setting and ignore it.
27
+ //
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
30
+ // model to behave. A control a user relies on when deciding to run something
31
+ // unattended has to be true in exactly the cases where the model is confused.
32
+ export const WORKSPACE_ACCESS_LEVELS = ["none", "read", "write"];
33
+ export function isWorkspaceAccess(value) {
34
+ return value === "none" || value === "read" || value === "write";
35
+ }
22
36
  //# sourceMappingURL=agent-types.js.map