@otto-code/protocol 0.8.17 → 0.8.19

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.
@@ -39,13 +39,77 @@ export declare const ProviderProfileModelSchema: z.ZodObject<{
39
39
  * Otto tools natively (see the openai-compat provider) can be scoped to a subset
40
40
  * of these groups; omitting the selection means all groups. Kept deliberately
41
41
  * coarse - users pick groups, not individual tools.
42
+ *
43
+ * The list is APPEND-ONLY, and the wire never carries it as a closed enum (see
44
+ * normalizeOttoToolGroups): an older peer must be able to parse a newer peer's
45
+ * selection, dropping names it does not know rather than failing the message.
42
46
  */
43
- export declare const OTTO_TOOL_GROUPS: readonly ["preview", "browser", "web", "agents", "terminals", "schedules", "artifacts", "widgets", "workspace"];
47
+ export declare const OTTO_TOOL_GROUPS: readonly ["preview", "browser", "web", "agents", "terminals", "schedules", "artifacts", "widgets", "workspace", "orchestration", "knowledge", "memory", "permissions", "providers", "tasks", "voice"];
44
48
  export type OttoToolGroup = (typeof OTTO_TOOL_GROUPS)[number];
49
+ /**
50
+ * The taxonomy as it stood before the "agents" split. A stored selection that
51
+ * uses only these names predates the split and is migrated forward by
52
+ * expandLegacyOttoToolGroups.
53
+ */
54
+ export declare const LEGACY_OTTO_TOOL_GROUPS: readonly ["preview", "browser", "web", "agents", "terminals", "schedules", "artifacts", "widgets", "workspace"];
55
+ /**
56
+ * The categories carved out of the legacy "agents" catch-all. They inherit
57
+ * "agents" when a pre-split selection is migrated forward, and collapse back
58
+ * into it when a current selection is projected back for an older peer.
59
+ */
60
+ export declare const AGENTS_DERIVED_TOOL_GROUPS: readonly ["orchestration", "knowledge", "memory", "permissions", "providers", "tasks", "voice"];
61
+ export declare function isOttoToolGroup(value: unknown): value is OttoToolGroup;
62
+ /**
63
+ * Post-validation normalization for a stored or wire-carried group selection.
64
+ * The schemas that carry it are `string[]` on purpose - wire schemas are pure
65
+ * structural declarations - and this is the one place unknown names are
66
+ * dropped, so a group added by a newer peer degrades to "not selected" instead
67
+ * of failing the whole message.
68
+ *
69
+ * Returns undefined for a non-array, which every caller reads as "all groups".
70
+ */
71
+ export declare function normalizeOttoToolGroups(value: unknown): OttoToolGroup[] | undefined;
72
+ /**
73
+ * Migrate a pre-split selection forward: every category carved out of "agents"
74
+ * inherits whatever "agents" was set to. Without this, upgrading would silently
75
+ * strip project knowledge, orchestration, memory, permissions, provider lookups,
76
+ * tasks and voice from anyone who had ever touched a tool toggle.
77
+ */
78
+ export declare function expandLegacyOttoToolGroups(groups: readonly OttoToolGroup[]): OttoToolGroup[];
79
+ /**
80
+ * Project a current selection back into the pre-split taxonomy for the legacy
81
+ * key we keep writing. "agents" stands in for the whole family, so a peer that
82
+ * predates the split still grants the tools the user left enabled rather than
83
+ * withholding them.
84
+ */
85
+ export declare function toLegacyOttoToolGroups(groups: readonly OttoToolGroup[]): OttoToolGroup[];
86
+ /**
87
+ * Read a persisted selection that may be in either taxonomy.
88
+ *
89
+ * COMPAT(ottoToolGroupsV2): added in v0.8.20. The v2 key wins when present; the
90
+ * legacy key is migrated forward otherwise. A second key rather than a
91
+ * heuristic, because the two shapes are genuinely ambiguous - "no new groups
92
+ * listed" reads as "pre-split config" one way and "the user turned all seven
93
+ * off" the other, and silently re-enabling tools somebody disabled is the worse
94
+ * way to be wrong. Drop the legacy branch when the floor is >= v0.8.20.
95
+ */
96
+ export declare function resolveStoredOttoToolGroups(input: {
97
+ v2?: unknown;
98
+ legacy?: unknown;
99
+ }): OttoToolGroup[] | undefined;
100
+ /** Both persisted shapes for one selection: the current key and its legacy projection. */
101
+ export declare function serializeOttoToolGroups(groups: readonly OttoToolGroup[]): {
102
+ toolGroups: OttoToolGroup[];
103
+ toolGroupsV2: OttoToolGroup[];
104
+ };
45
105
  /**
46
106
  * Map a tool name to its group. Covers both Otto's catalog tools and the
47
- * openai-compat builtin web tools (web_search/web_fetch → "web"). Unknown/
48
- * lifecycle tools fall under "agents".
107
+ * openai-compat builtin web tools (web_search/web_fetch → "web").
108
+ *
109
+ * "agents" is still the fallback, but it now means what it says - spawning and
110
+ * steering chats - instead of "everything not otherwise classified". It used to
111
+ * collect 43 of the catalog's 72 tools, which made it impossible to switch off
112
+ * one capability without losing six unrelated ones.
49
113
  */
50
114
  export declare function ottoToolGroupForName(name: string): OttoToolGroup;
51
115
  /**
@@ -378,17 +442,8 @@ export declare const ProviderOverrideSchema: z.ZodObject<{
378
442
  }, z.core.$strip>>>;
379
443
  }, z.core.$strip>>>;
380
444
  disallowedTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
381
- ottoToolGroups: z.ZodOptional<z.ZodArray<z.ZodEnum<{
382
- preview: "preview";
383
- browser: "browser";
384
- web: "web";
385
- agents: "agents";
386
- terminals: "terminals";
387
- schedules: "schedules";
388
- artifacts: "artifacts";
389
- widgets: "widgets";
390
- workspace: "workspace";
391
- }>>>;
445
+ ottoToolGroups: z.ZodOptional<z.ZodArray<z.ZodString>>;
446
+ ottoToolGroupsV2: z.ZodOptional<z.ZodArray<z.ZodString>>;
392
447
  mcpServers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodDiscriminatedUnion<[z.ZodObject<{
393
448
  type: z.ZodLiteral<"stdio">;
394
449
  command: z.ZodString;
@@ -459,17 +514,8 @@ export declare const ProviderOverridesSchema: z.ZodRecord<z.ZodString, z.ZodObje
459
514
  }, z.core.$strip>>>;
460
515
  }, z.core.$strip>>>;
461
516
  disallowedTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
462
- ottoToolGroups: z.ZodOptional<z.ZodArray<z.ZodEnum<{
463
- preview: "preview";
464
- browser: "browser";
465
- web: "web";
466
- agents: "agents";
467
- terminals: "terminals";
468
- schedules: "schedules";
469
- artifacts: "artifacts";
470
- widgets: "widgets";
471
- workspace: "workspace";
472
- }>>>;
517
+ ottoToolGroups: z.ZodOptional<z.ZodArray<z.ZodString>>;
518
+ ottoToolGroupsV2: z.ZodOptional<z.ZodArray<z.ZodString>>;
473
519
  mcpServers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodDiscriminatedUnion<[z.ZodObject<{
474
520
  type: z.ZodLiteral<"stdio">;
475
521
  command: z.ZodString;
@@ -39,6 +39,10 @@ export const ProviderProfileModelSchema = z.object({
39
39
  * Otto tools natively (see the openai-compat provider) can be scoped to a subset
40
40
  * of these groups; omitting the selection means all groups. Kept deliberately
41
41
  * coarse - users pick groups, not individual tools.
42
+ *
43
+ * The list is APPEND-ONLY, and the wire never carries it as a closed enum (see
44
+ * normalizeOttoToolGroups): an older peer must be able to parse a newer peer's
45
+ * selection, dropping names it does not know rather than failing the message.
42
46
  */
43
47
  export const OTTO_TOOL_GROUPS = [
44
48
  "preview",
@@ -50,29 +54,165 @@ export const OTTO_TOOL_GROUPS = [
50
54
  "artifacts",
51
55
  "widgets",
52
56
  "workspace",
57
+ // Split out of the "agents" catch-all, which had grown to hold 60% of the
58
+ // catalog and so could not be switched off for one reason without losing six
59
+ // unrelated capabilities with it.
60
+ "orchestration",
61
+ "knowledge",
62
+ "memory",
63
+ "permissions",
64
+ "providers",
65
+ "tasks",
66
+ "voice",
67
+ ];
68
+ /**
69
+ * The taxonomy as it stood before the "agents" split. A stored selection that
70
+ * uses only these names predates the split and is migrated forward by
71
+ * expandLegacyOttoToolGroups.
72
+ */
73
+ export const LEGACY_OTTO_TOOL_GROUPS = [
74
+ "preview",
75
+ "browser",
76
+ "web",
77
+ "agents",
78
+ "terminals",
79
+ "schedules",
80
+ "artifacts",
81
+ "widgets",
82
+ "workspace",
83
+ ];
84
+ /**
85
+ * The categories carved out of the legacy "agents" catch-all. They inherit
86
+ * "agents" when a pre-split selection is migrated forward, and collapse back
87
+ * into it when a current selection is projected back for an older peer.
88
+ */
89
+ export const AGENTS_DERIVED_TOOL_GROUPS = [
90
+ "orchestration",
91
+ "knowledge",
92
+ "memory",
93
+ "permissions",
94
+ "providers",
95
+ "tasks",
96
+ "voice",
97
+ ];
98
+ const OTTO_TOOL_GROUP_SET = new Set(OTTO_TOOL_GROUPS);
99
+ const LEGACY_OTTO_TOOL_GROUP_SET = new Set(LEGACY_OTTO_TOOL_GROUPS);
100
+ const AGENTS_DERIVED_TOOL_GROUP_SET = new Set(AGENTS_DERIVED_TOOL_GROUPS);
101
+ export function isOttoToolGroup(value) {
102
+ return typeof value === "string" && OTTO_TOOL_GROUP_SET.has(value);
103
+ }
104
+ /**
105
+ * Post-validation normalization for a stored or wire-carried group selection.
106
+ * The schemas that carry it are `string[]` on purpose - wire schemas are pure
107
+ * structural declarations - and this is the one place unknown names are
108
+ * dropped, so a group added by a newer peer degrades to "not selected" instead
109
+ * of failing the whole message.
110
+ *
111
+ * Returns undefined for a non-array, which every caller reads as "all groups".
112
+ */
113
+ export function normalizeOttoToolGroups(value) {
114
+ if (!Array.isArray(value)) {
115
+ return undefined;
116
+ }
117
+ const seen = new Set(value.filter(isOttoToolGroup));
118
+ return OTTO_TOOL_GROUPS.filter((group) => seen.has(group));
119
+ }
120
+ /**
121
+ * Migrate a pre-split selection forward: every category carved out of "agents"
122
+ * inherits whatever "agents" was set to. Without this, upgrading would silently
123
+ * strip project knowledge, orchestration, memory, permissions, provider lookups,
124
+ * tasks and voice from anyone who had ever touched a tool toggle.
125
+ */
126
+ export function expandLegacyOttoToolGroups(groups) {
127
+ const selected = new Set(groups.filter((group) => LEGACY_OTTO_TOOL_GROUP_SET.has(group)));
128
+ if (selected.has("agents")) {
129
+ for (const derived of AGENTS_DERIVED_TOOL_GROUPS) {
130
+ selected.add(derived);
131
+ }
132
+ }
133
+ return OTTO_TOOL_GROUPS.filter((group) => selected.has(group));
134
+ }
135
+ /**
136
+ * Project a current selection back into the pre-split taxonomy for the legacy
137
+ * key we keep writing. "agents" stands in for the whole family, so a peer that
138
+ * predates the split still grants the tools the user left enabled rather than
139
+ * withholding them.
140
+ */
141
+ export function toLegacyOttoToolGroups(groups) {
142
+ const selected = new Set(groups.filter((group) => LEGACY_OTTO_TOOL_GROUP_SET.has(group)));
143
+ if (groups.some((group) => AGENTS_DERIVED_TOOL_GROUP_SET.has(group))) {
144
+ selected.add("agents");
145
+ }
146
+ return LEGACY_OTTO_TOOL_GROUPS.filter((group) => selected.has(group));
147
+ }
148
+ /**
149
+ * Read a persisted selection that may be in either taxonomy.
150
+ *
151
+ * COMPAT(ottoToolGroupsV2): added in v0.8.20. The v2 key wins when present; the
152
+ * legacy key is migrated forward otherwise. A second key rather than a
153
+ * heuristic, because the two shapes are genuinely ambiguous - "no new groups
154
+ * listed" reads as "pre-split config" one way and "the user turned all seven
155
+ * off" the other, and silently re-enabling tools somebody disabled is the worse
156
+ * way to be wrong. Drop the legacy branch when the floor is >= v0.8.20.
157
+ */
158
+ export function resolveStoredOttoToolGroups(input) {
159
+ const v2 = normalizeOttoToolGroups(input.v2);
160
+ if (v2) {
161
+ return v2;
162
+ }
163
+ const legacy = normalizeOttoToolGroups(input.legacy);
164
+ return legacy ? expandLegacyOttoToolGroups(legacy) : undefined;
165
+ }
166
+ /** Both persisted shapes for one selection: the current key and its legacy projection. */
167
+ export function serializeOttoToolGroups(groups) {
168
+ const normalized = OTTO_TOOL_GROUPS.filter((group) => groups.includes(group));
169
+ return { toolGroups: toLegacyOttoToolGroups(normalized), toolGroupsV2: normalized };
170
+ }
171
+ /**
172
+ * Ordered: the first matching rule wins, so a name that could satisfy two rules
173
+ * resolves the same way every time. Prefer a shared substring over an exact-name
174
+ * list, so a tool added later lands in its category without a second edit here.
175
+ */
176
+ const OTTO_TOOL_GROUP_RULES = [
177
+ { group: "preview", prefixes: ["preview_"] },
178
+ { group: "browser", prefixes: ["browser_"] },
179
+ { group: "web", names: ["web_search", "web_fetch"] },
180
+ { group: "terminals", contains: ["terminal"] },
181
+ // "heartbeat" rather than just create_heartbeat: delete_heartbeat used to fall
182
+ // through to the catch-all, so switching Schedules off left an agent able to
183
+ // delete heartbeats it could no longer create.
184
+ { group: "schedules", contains: ["schedule", "heartbeat"] },
185
+ { group: "artifacts", contains: ["artifact"] },
186
+ { group: "widgets", names: ["show_widget", "widget_contract"] },
187
+ { group: "workspace", contains: ["worktree", "workspace"] },
188
+ { group: "orchestration", contains: ["orchestration"] },
189
+ { group: "knowledge", contains: ["project_"] },
190
+ { group: "memory", contains: ["lesson"] },
191
+ { group: "permissions", contains: ["permission"] },
192
+ { group: "tasks", contains: ["task"] },
193
+ // "model" must stay below the rules above it: it is one character from
194
+ // matching set_chat_mode, which belongs to the chat family.
195
+ { group: "providers", contains: ["provider", "model"] },
196
+ { group: "voice", names: ["speak"] },
53
197
  ];
54
198
  /**
55
199
  * Map a tool name to its group. Covers both Otto's catalog tools and the
56
- * openai-compat builtin web tools (web_search/web_fetch → "web"). Unknown/
57
- * lifecycle tools fall under "agents".
200
+ * openai-compat builtin web tools (web_search/web_fetch → "web").
201
+ *
202
+ * "agents" is still the fallback, but it now means what it says - spawning and
203
+ * steering chats - instead of "everything not otherwise classified". It used to
204
+ * collect 43 of the catalog's 72 tools, which made it impossible to switch off
205
+ * one capability without losing six unrelated ones.
58
206
  */
59
207
  export function ottoToolGroupForName(name) {
60
- if (name.startsWith("preview_"))
61
- return "preview";
62
- if (name.startsWith("browser_"))
63
- return "browser";
64
- if (name === "web_search" || name === "web_fetch")
65
- return "web";
66
- if (name.includes("terminal"))
67
- return "terminals";
68
- if (name.includes("schedule") || name === "create_heartbeat")
69
- return "schedules";
70
- if (name.includes("artifact"))
71
- return "artifacts";
72
- if (name === "show_widget" || name === "widget_contract")
73
- return "widgets";
74
- if (name.includes("worktree") || name.includes("workspace"))
75
- return "workspace";
208
+ for (const rule of OTTO_TOOL_GROUP_RULES) {
209
+ if (rule.prefixes?.some((prefix) => name.startsWith(prefix)))
210
+ return rule.group;
211
+ if (rule.names?.includes(name))
212
+ return rule.group;
213
+ if (rule.contains?.some((fragment) => name.includes(fragment)))
214
+ return rule.group;
215
+ }
76
216
  return "agents";
77
217
  }
78
218
  const McpStdioServerConfigSchema = z.object({
@@ -339,8 +479,15 @@ export const ProviderOverrideSchema = z.object({
339
479
  /**
340
480
  * Which Otto tool groups to inject for this provider (natively-injected
341
481
  * providers only). Omitted = all groups. Empty array = no Otto tools.
482
+ *
483
+ * COMPAT(ottoToolGroupsV2): added in v0.8.20. `ottoToolGroups` is the legacy
484
+ * projection kept for older peers; `ottoToolGroupsV2` is authoritative. Both
485
+ * are `string[]` rather than an enum so an older peer drops group names it
486
+ * does not know instead of failing to parse the provider entry - read them
487
+ * through resolveStoredOttoToolGroups, never directly.
342
488
  */
343
- ottoToolGroups: z.array(z.enum(OTTO_TOOL_GROUPS)).optional(),
489
+ ottoToolGroups: z.array(z.string().min(1)).optional(),
490
+ ottoToolGroupsV2: z.array(z.string().min(1)).optional(),
344
491
  /**
345
492
  * MCP servers for providers whose tool loop the daemon hosts (openai-compat).
346
493
  * Merged with any per-agent AgentSessionConfig.mcpServers; the per-agent
@@ -341,6 +341,39 @@ export declare const ScheduleInspectResponseSchema: z.ZodObject<{
341
341
  failed: "failed";
342
342
  succeeded: "succeeded";
343
343
  }>;
344
+ target: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
345
+ type: z.ZodLiteral<"agent">;
346
+ agentId: z.ZodGUID;
347
+ }, z.core.$strip>, z.ZodObject<{
348
+ type: z.ZodLiteral<"new-agent">;
349
+ config: z.ZodObject<{
350
+ provider: z.ZodString;
351
+ cwd: z.ZodString;
352
+ personality: z.ZodOptional<z.ZodString>;
353
+ agentProfile: z.ZodOptional<z.ZodString>;
354
+ modeId: z.ZodOptional<z.ZodString>;
355
+ model: z.ZodOptional<z.ZodString>;
356
+ thinkingOptionId: z.ZodOptional<z.ZodString>;
357
+ archiveOnFinish: z.ZodOptional<z.ZodBoolean>;
358
+ isolation: z.ZodOptional<z.ZodEnum<{
359
+ worktree: "worktree";
360
+ local: "local";
361
+ }>>;
362
+ title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
363
+ providerOptions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodJSONSchema>>;
364
+ approvalPolicy: z.ZodOptional<z.ZodString>;
365
+ sandboxMode: z.ZodOptional<z.ZodString>;
366
+ networkAccess: z.ZodOptional<z.ZodBoolean>;
367
+ webSearch: z.ZodOptional<z.ZodBoolean>;
368
+ extra: z.ZodOptional<z.ZodObject<{
369
+ codex: z.ZodOptional<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
370
+ claude: z.ZodOptional<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
371
+ }, z.core.$strip>>;
372
+ featureValues: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
373
+ systemPrompt: z.ZodOptional<z.ZodString>;
374
+ mcpServers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
375
+ }, z.core.$strip>;
376
+ }, z.core.$strip>], "type">>;
344
377
  agentId: z.ZodNullable<z.ZodGUID>;
345
378
  workspaceId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
346
379
  personalityName: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -367,6 +400,39 @@ export declare const ScheduleLogsResponseSchema: z.ZodObject<{
367
400
  failed: "failed";
368
401
  succeeded: "succeeded";
369
402
  }>;
403
+ target: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
404
+ type: z.ZodLiteral<"agent">;
405
+ agentId: z.ZodGUID;
406
+ }, z.core.$strip>, z.ZodObject<{
407
+ type: z.ZodLiteral<"new-agent">;
408
+ config: z.ZodObject<{
409
+ provider: z.ZodString;
410
+ cwd: z.ZodString;
411
+ personality: z.ZodOptional<z.ZodString>;
412
+ agentProfile: z.ZodOptional<z.ZodString>;
413
+ modeId: z.ZodOptional<z.ZodString>;
414
+ model: z.ZodOptional<z.ZodString>;
415
+ thinkingOptionId: z.ZodOptional<z.ZodString>;
416
+ archiveOnFinish: z.ZodOptional<z.ZodBoolean>;
417
+ isolation: z.ZodOptional<z.ZodEnum<{
418
+ worktree: "worktree";
419
+ local: "local";
420
+ }>>;
421
+ title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
422
+ providerOptions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodJSONSchema>>;
423
+ approvalPolicy: z.ZodOptional<z.ZodString>;
424
+ sandboxMode: z.ZodOptional<z.ZodString>;
425
+ networkAccess: z.ZodOptional<z.ZodBoolean>;
426
+ webSearch: z.ZodOptional<z.ZodBoolean>;
427
+ extra: z.ZodOptional<z.ZodObject<{
428
+ codex: z.ZodOptional<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
429
+ claude: z.ZodOptional<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
430
+ }, z.core.$strip>>;
431
+ featureValues: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
432
+ systemPrompt: z.ZodOptional<z.ZodString>;
433
+ mcpServers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
434
+ }, z.core.$strip>;
435
+ }, z.core.$strip>], "type">>;
370
436
  agentId: z.ZodNullable<z.ZodGUID>;
371
437
  workspaceId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
372
438
  personalityName: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -611,6 +677,39 @@ export declare const ScheduleRunOnceResponseSchema: z.ZodObject<{
611
677
  failed: "failed";
612
678
  succeeded: "succeeded";
613
679
  }>;
680
+ target: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
681
+ type: z.ZodLiteral<"agent">;
682
+ agentId: z.ZodGUID;
683
+ }, z.core.$strip>, z.ZodObject<{
684
+ type: z.ZodLiteral<"new-agent">;
685
+ config: z.ZodObject<{
686
+ provider: z.ZodString;
687
+ cwd: z.ZodString;
688
+ personality: z.ZodOptional<z.ZodString>;
689
+ agentProfile: z.ZodOptional<z.ZodString>;
690
+ modeId: z.ZodOptional<z.ZodString>;
691
+ model: z.ZodOptional<z.ZodString>;
692
+ thinkingOptionId: z.ZodOptional<z.ZodString>;
693
+ archiveOnFinish: z.ZodOptional<z.ZodBoolean>;
694
+ isolation: z.ZodOptional<z.ZodEnum<{
695
+ worktree: "worktree";
696
+ local: "local";
697
+ }>>;
698
+ title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
699
+ providerOptions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodJSONSchema>>;
700
+ approvalPolicy: z.ZodOptional<z.ZodString>;
701
+ sandboxMode: z.ZodOptional<z.ZodString>;
702
+ networkAccess: z.ZodOptional<z.ZodBoolean>;
703
+ webSearch: z.ZodOptional<z.ZodBoolean>;
704
+ extra: z.ZodOptional<z.ZodObject<{
705
+ codex: z.ZodOptional<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
706
+ claude: z.ZodOptional<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
707
+ }, z.core.$strip>>;
708
+ featureValues: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
709
+ systemPrompt: z.ZodOptional<z.ZodString>;
710
+ mcpServers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
711
+ }, z.core.$strip>;
712
+ }, z.core.$strip>], "type">>;
614
713
  agentId: z.ZodNullable<z.ZodGUID>;
615
714
  workspaceId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
616
715
  personalityName: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -702,6 +801,39 @@ export declare const ScheduleUpdateResponseSchema: z.ZodObject<{
702
801
  failed: "failed";
703
802
  succeeded: "succeeded";
704
803
  }>;
804
+ target: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
805
+ type: z.ZodLiteral<"agent">;
806
+ agentId: z.ZodGUID;
807
+ }, z.core.$strip>, z.ZodObject<{
808
+ type: z.ZodLiteral<"new-agent">;
809
+ config: z.ZodObject<{
810
+ provider: z.ZodString;
811
+ cwd: z.ZodString;
812
+ personality: z.ZodOptional<z.ZodString>;
813
+ agentProfile: z.ZodOptional<z.ZodString>;
814
+ modeId: z.ZodOptional<z.ZodString>;
815
+ model: z.ZodOptional<z.ZodString>;
816
+ thinkingOptionId: z.ZodOptional<z.ZodString>;
817
+ archiveOnFinish: z.ZodOptional<z.ZodBoolean>;
818
+ isolation: z.ZodOptional<z.ZodEnum<{
819
+ worktree: "worktree";
820
+ local: "local";
821
+ }>>;
822
+ title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
823
+ providerOptions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodJSONSchema>>;
824
+ approvalPolicy: z.ZodOptional<z.ZodString>;
825
+ sandboxMode: z.ZodOptional<z.ZodString>;
826
+ networkAccess: z.ZodOptional<z.ZodBoolean>;
827
+ webSearch: z.ZodOptional<z.ZodBoolean>;
828
+ extra: z.ZodOptional<z.ZodObject<{
829
+ codex: z.ZodOptional<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
830
+ claude: z.ZodOptional<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
831
+ }, z.core.$strip>>;
832
+ featureValues: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
833
+ systemPrompt: z.ZodOptional<z.ZodString>;
834
+ mcpServers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
835
+ }, z.core.$strip>;
836
+ }, z.core.$strip>], "type">>;
705
837
  agentId: z.ZodNullable<z.ZodGUID>;
706
838
  workspaceId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
707
839
  personalityName: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -58,6 +58,39 @@ export declare const ScheduleRunSchema: z.ZodObject<{
58
58
  failed: "failed";
59
59
  succeeded: "succeeded";
60
60
  }>;
61
+ target: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
62
+ type: z.ZodLiteral<"agent">;
63
+ agentId: z.ZodGUID;
64
+ }, z.core.$strip>, z.ZodObject<{
65
+ type: z.ZodLiteral<"new-agent">;
66
+ config: z.ZodObject<{
67
+ provider: z.ZodString;
68
+ cwd: z.ZodString;
69
+ personality: z.ZodOptional<z.ZodString>;
70
+ agentProfile: z.ZodOptional<z.ZodString>;
71
+ modeId: z.ZodOptional<z.ZodString>;
72
+ model: z.ZodOptional<z.ZodString>;
73
+ thinkingOptionId: z.ZodOptional<z.ZodString>;
74
+ archiveOnFinish: z.ZodOptional<z.ZodBoolean>;
75
+ isolation: z.ZodOptional<z.ZodEnum<{
76
+ worktree: "worktree";
77
+ local: "local";
78
+ }>>;
79
+ title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
80
+ providerOptions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodJSONSchema>>;
81
+ approvalPolicy: z.ZodOptional<z.ZodString>;
82
+ sandboxMode: z.ZodOptional<z.ZodString>;
83
+ networkAccess: z.ZodOptional<z.ZodBoolean>;
84
+ webSearch: z.ZodOptional<z.ZodBoolean>;
85
+ extra: z.ZodOptional<z.ZodObject<{
86
+ codex: z.ZodOptional<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
87
+ claude: z.ZodOptional<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
88
+ }, z.core.$strip>>;
89
+ featureValues: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
90
+ systemPrompt: z.ZodOptional<z.ZodString>;
91
+ mcpServers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
92
+ }, z.core.$strip>;
93
+ }, z.core.$strip>], "type">>;
61
94
  agentId: z.ZodNullable<z.ZodGUID>;
62
95
  workspaceId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
63
96
  personalityName: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -142,6 +175,39 @@ export declare const StoredScheduleSchema: z.ZodObject<{
142
175
  failed: "failed";
143
176
  succeeded: "succeeded";
144
177
  }>;
178
+ target: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
179
+ type: z.ZodLiteral<"agent">;
180
+ agentId: z.ZodGUID;
181
+ }, z.core.$strip>, z.ZodObject<{
182
+ type: z.ZodLiteral<"new-agent">;
183
+ config: z.ZodObject<{
184
+ provider: z.ZodString;
185
+ cwd: z.ZodString;
186
+ personality: z.ZodOptional<z.ZodString>;
187
+ agentProfile: z.ZodOptional<z.ZodString>;
188
+ modeId: z.ZodOptional<z.ZodString>;
189
+ model: z.ZodOptional<z.ZodString>;
190
+ thinkingOptionId: z.ZodOptional<z.ZodString>;
191
+ archiveOnFinish: z.ZodOptional<z.ZodBoolean>;
192
+ isolation: z.ZodOptional<z.ZodEnum<{
193
+ worktree: "worktree";
194
+ local: "local";
195
+ }>>;
196
+ title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
197
+ providerOptions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodJSONSchema>>;
198
+ approvalPolicy: z.ZodOptional<z.ZodString>;
199
+ sandboxMode: z.ZodOptional<z.ZodString>;
200
+ networkAccess: z.ZodOptional<z.ZodBoolean>;
201
+ webSearch: z.ZodOptional<z.ZodBoolean>;
202
+ extra: z.ZodOptional<z.ZodObject<{
203
+ codex: z.ZodOptional<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
204
+ claude: z.ZodOptional<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
205
+ }, z.core.$strip>>;
206
+ featureValues: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
207
+ systemPrompt: z.ZodOptional<z.ZodString>;
208
+ mcpServers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
209
+ }, z.core.$strip>;
210
+ }, z.core.$strip>], "type">>;
145
211
  agentId: z.ZodNullable<z.ZodGUID>;
146
212
  workspaceId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
147
213
  personalityName: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -68,6 +68,11 @@ export const ScheduleRunSchema = z.object({
68
68
  startedAt: z.string(),
69
69
  endedAt: z.string().nullable(),
70
70
  status: z.enum(["running", "succeeded", "failed"]),
71
+ // The exact schedule target requested when this run was claimed. Keeping the
72
+ // snapshot on the run means later edits do not rewrite history, and gives
73
+ // future Workflow/artifact target adapters one compatibility-safe audit slot.
74
+ // Optional because persisted runs before v0.9 do not carry it.
75
+ target: ScheduleTargetSchema.optional(),
71
76
  agentId: z.guid().nullable(),
72
77
  workspaceId: z.string().nullable().optional(),
73
78
  // Who actually executed this run - the resolved personality (if any),