@maudecode/talaria-web-contracts 1.18.0 → 1.20.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 (95) hide show
  1. package/dist/context-detail.d.ts +75 -0
  2. package/dist/context-detail.js +18 -0
  3. package/dist/context-detail.js.map +1 -0
  4. package/dist/gateway-session.d.ts +475 -0
  5. package/dist/gateway-session.js +106 -0
  6. package/dist/gateway-session.js.map +1 -0
  7. package/dist/index.d.ts +4 -0
  8. package/dist/index.js +4 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/openapi.js +4 -0
  11. package/dist/openapi.js.map +1 -1
  12. package/dist/router.d.ts +5503 -251
  13. package/dist/routes/automation.d.ts +100 -18
  14. package/dist/routes/automation.js +7 -4
  15. package/dist/routes/automation.js.map +1 -1
  16. package/dist/routes/chat.d.ts +1605 -47
  17. package/dist/routes/chat.js +7 -2
  18. package/dist/routes/chat.js.map +1 -1
  19. package/dist/routes/core.d.ts +15 -4
  20. package/dist/routes/core.js +2 -2
  21. package/dist/routes/core.js.map +1 -1
  22. package/dist/routes/git.d.ts +9 -0
  23. package/dist/routes/git.js +6 -1
  24. package/dist/routes/git.js.map +1 -1
  25. package/dist/routes/raw.js +10 -6
  26. package/dist/routes/raw.js.map +1 -1
  27. package/dist/routes/sessions.d.ts +3189 -181
  28. package/dist/routes/sessions.js +24 -7
  29. package/dist/routes/sessions.js.map +1 -1
  30. package/dist/routes/settings.d.ts +445 -11
  31. package/dist/routes/settings.js +5 -4
  32. package/dist/routes/settings.js.map +1 -1
  33. package/dist/routes/tools.d.ts +945 -34
  34. package/dist/routes/tools.js +75 -8
  35. package/dist/routes/tools.js.map +1 -1
  36. package/dist/routes/workspaces.d.ts +4 -4
  37. package/dist/run-status.d.ts +39 -0
  38. package/dist/run-status.js +13 -0
  39. package/dist/run-status.js.map +1 -0
  40. package/dist/sidecar/methods.d.ts +1906 -52
  41. package/dist/sidecar/methods.js +9 -2
  42. package/dist/sidecar/methods.js.map +1 -1
  43. package/dist/sidecar/namespaces.d.ts +2421 -54
  44. package/dist/sidecar/namespaces.js +390 -36
  45. package/dist/sidecar/namespaces.js.map +1 -1
  46. package/dist/sidecar/rpc.d.ts +1 -0
  47. package/dist/sidecar/rpc.js +2 -0
  48. package/dist/sidecar/rpc.js.map +1 -1
  49. package/dist/sidecar/runtime.d.ts +74 -0
  50. package/dist/sidecar/runtime.js +29 -0
  51. package/dist/sidecar/runtime.js.map +1 -1
  52. package/dist/sidecar/version.d.ts +1 -1
  53. package/dist/sidecar/version.js +1 -1
  54. package/dist/speed.d.ts +14 -0
  55. package/dist/speed.js +6 -0
  56. package/dist/speed.js.map +1 -0
  57. package/dist/sse.d.ts +868 -5
  58. package/dist/sse.js +30 -7
  59. package/dist/sse.js.map +1 -1
  60. package/dist/transcribe-stream.d.ts +34 -0
  61. package/dist/transcribe-stream.js +23 -0
  62. package/dist/transcribe-stream.js.map +1 -0
  63. package/dist/views.d.ts +2627 -176
  64. package/dist/views.js +321 -52
  65. package/dist/views.js.map +1 -1
  66. package/fixtures/sidecar/aux.json +142 -0
  67. package/fixtures/sidecar/chat.json +28 -0
  68. package/fixtures/sidecar/checkpoints.json +12 -7
  69. package/fixtures/sidecar/commands.json +216 -108
  70. package/fixtures/sidecar/config.json +15 -0
  71. package/fixtures/sidecar/connectors.json +218 -0
  72. package/fixtures/sidecar/context-detail.json +77 -0
  73. package/fixtures/sidecar/cron.json +1004 -32
  74. package/fixtures/sidecar/dashboard_plugins.json +59 -0
  75. package/fixtures/sidecar/gateway.json +202 -0
  76. package/fixtures/sidecar/goals.json +5 -3
  77. package/fixtures/sidecar/kanban.json +139 -49
  78. package/fixtures/sidecar/memory.json +50 -0
  79. package/fixtures/sidecar/models.json +122 -0
  80. package/fixtures/sidecar/personalities.json +92 -0
  81. package/fixtures/sidecar/plugin-catalog.json +4 -0
  82. package/fixtures/sidecar/plugins.json +171 -27
  83. package/fixtures/sidecar/process.json +63 -0
  84. package/fixtures/sidecar/profiles.json +2 -0
  85. package/fixtures/sidecar/providers.json +14 -1
  86. package/fixtures/sidecar/rpc.json +39 -1
  87. package/fixtures/sidecar/runtime.json +130 -14
  88. package/fixtures/sidecar/skills.json +256 -13
  89. package/fixtures/sidecar/state_db.json +29 -2
  90. package/fixtures/sidecar/stt.json +62 -1
  91. package/fixtures/sidecar/text.json +20 -2
  92. package/fixtures/sidecar/toolsets.json +468 -0
  93. package/fixtures/sidecar/usage.json +22 -2
  94. package/fixtures/sidecar/write_approval.json +98 -0
  95. package/package.json +1 -1
@@ -1,17 +1,36 @@
1
+ import { ContextDetailSchema } from '../context-detail.js';
1
2
  import { z } from 'zod';
3
+ import { ProviderPhaseSchema } from '../run-status.js';
4
+ import { SpeedTierSchema } from '../speed.js';
5
+ import { GatewayClarifyBatchSchema, GatewayCreateParamsSchema, GatewaySessionParamsSchema, GatewaySessionResultSchema } from '../gateway-session.js';
6
+ /** Warnings and wording come from the Agent's selection guard registry, including future guard kinds. */
7
+ export const ModelSelectionWarningSchema = z.object({ kind: z.string(), title: z.string(), model: z.string(), provider: z.string(), message: z.string() });
2
8
  /** Shared param shapes. */
3
9
  export const ProfileHomeParams = z.object({ profile_home: z.string().min(1) });
4
10
  export const BaseHomeParams = z.object({ base_home: z.string().min(1) });
5
11
  const Ok = z.object({ ok: z.boolean() });
6
12
  const Json = z.unknown();
7
13
  const Loose = z.record(z.string(), z.unknown());
14
+ /** Profile-scoped toolset choices; clients render policy and selection without deriving them. */
15
+ export const ToolsetsCatalogSchema = z.object({
16
+ toolsets: z.array(z.object({
17
+ name: z.string(), description: z.string(), available: z.boolean(), selected: z.boolean(),
18
+ unavailable_reason: z.enum(['disabled', 'unavailable']).nullable(),
19
+ })),
20
+ selected_toolsets: z.array(z.string()), using_defaults: z.boolean(),
21
+ });
22
+ export const TOOLSETS_METHODS = {
23
+ 'toolsets.catalog': { params: ProfileHomeParams.extend({ enabled_toolsets: z.array(z.string()).nullable() }), result: ToolsetsCatalogSchema },
24
+ };
8
25
  // ── goals ──────────────────────────────────────────────────────────────
9
26
  export const GoalStateSchema = z.object({
10
27
  goal: z.string(), status: z.string(), turns_used: z.number().int(), max_turns: z.number().int(),
11
28
  last_verdict: z.string().nullable(), last_reason: z.string().nullable(), paused_reason: z.string().nullable(),
12
29
  });
13
30
  const GoalSession = ProfileHomeParams.extend({ session_id: z.string().min(1), default_max_turns: z.number().int().positive().optional() });
14
- export const GoalStatusSchema = z.object({ goal: GoalStateSchema.nullable(), active: z.boolean(), message: z.string(), message_key: z.string().optional(), message_args: z.array(Json).optional() });
31
+ /** TAL-808: `parked` is an active goal's live wait barrier: its reason and, for a timed wait, the Unix-seconds deadline. */
32
+ export const GoalParkedSchema = z.object({ reason: z.string().nullable(), until: z.number().nullable() });
33
+ export const GoalStatusSchema = z.object({ goal: GoalStateSchema.nullable(), active: z.boolean(), parked: GoalParkedSchema.nullable(), message: z.string(), message_key: z.string().optional(), message_args: z.array(Json).optional() });
15
34
  export const GoalCommandResultSchema = z.object({
16
35
  ok: z.boolean(), action: z.string(), message: z.string(), goal: GoalStateSchema.nullable(), error: z.string().optional(), kickoff_prompt: z.string().optional(),
17
36
  message_key: z.string().optional(), message_args: z.array(Json).optional(),
@@ -22,10 +41,11 @@ export const GoalDecisionSchema = z.object({
22
41
  });
23
42
  export const GOALS_METHODS = {
24
43
  'goals.get': { params: GoalSession, result: GoalStatusSchema },
25
- 'goals.command': { params: GoalSession.extend({ args: z.string(), stream_running: z.boolean().optional() }), result: GoalCommandResultSchema },
44
+ // `workspace`: the session's declared workspace, where the Agent runs goal gates (TAL-882).
45
+ 'goals.command': { params: GoalSession.extend({ args: z.string(), stream_running: z.boolean().optional(), workspace: z.string().optional() }), result: GoalCommandResultSchema },
26
46
  'goals.snapshot': { params: GoalSession, result: z.object({ goal: GoalStateSchema.nullable(), snapshot: z.string().nullable() }) },
27
47
  'goals.restore': { params: GoalSession.extend({ snapshot: z.string().nullable() }), result: z.object({ goal: GoalStateSchema.nullable() }) },
28
- 'goals.evaluate': { params: GoalSession.extend({ last_response: z.string(), user_initiated: z.boolean().optional() }), result: GoalDecisionSchema },
48
+ 'goals.evaluate': { params: GoalSession.extend({ last_response: z.string(), user_initiated: z.boolean().optional(), workspace: z.string().optional() }), result: GoalDecisionSchema },
29
49
  };
30
50
  // ── commands / plugins ─────────────────────────────────────────────────
31
51
  export const CommandSchema = z.object({
@@ -33,35 +53,118 @@ export const CommandSchema = z.object({
33
53
  cli_only: z.boolean(), gateway_only: z.boolean(),
34
54
  /** `commands.exec` runs it: the sidecar's runtime commands and every plugin command (TAL-561). */
35
55
  exec: z.boolean(),
56
+ /**
57
+ * An Agent prompt built-in, which every Agent surface answers by sending its builder's prompt as the turn (TAL-817):
58
+ * `expand` when `commands.expand_prompt` can build it on this Agent, `unsupported` when it cannot; null for any other command.
59
+ */
60
+ prompt: z.enum(['expand', 'unsupported']).nullable(),
36
61
  });
37
62
  /**
38
63
  * A model-provider plugin installed and enabled in the profile (TAL-288); bundled providers are built-ins. `setup` is the Agent's
39
64
  * own verdict without spawning the plugin's CLI: `not_loaded` means the installed plugin could not be loaded (import failure, or its
40
65
  * provider id or directory name is taken by another profile's plugin in this process-wide registry).
41
66
  */
67
+ /**
68
+ * TAL-806: one field of a plugin manifest's `config_schema` (Agent `plugin_settings_fields`) with its current value from
69
+ * `plugins.entries.<key>.settings`, else `default`. A `secret` field carries its `.env` name and `has_value`, never a value.
70
+ */
71
+ export const PluginSettingFieldSchema = z.object({
72
+ key: z.string(), type: z.enum(['string', 'number', 'boolean', 'enum', 'json', 'secret']), label: z.string(), description: z.string(), required: z.boolean(),
73
+ choices: z.array(z.string()).optional(), default: Json.optional(), value: Json.optional(), env: z.string().optional(), has_value: z.boolean().optional(),
74
+ });
75
+ /** Persistence succeeded; this separately describes application to live plugin MCP connections. */
76
+ export const PluginRuntimeApplicationSchema = z.enum(['applied', 'restart_required', 'unknown']);
77
+ /** TAL-1276: one OAuth client a plugin manifest declares, its grant's state as `hermes auth status` maps it, and the hosts the code is approved at and exchanged at. */
78
+ export const PluginOAuthStateSchema = z.enum(['signed_in', 'sign_in_needed', 'revoked', 'logged_out']);
79
+ export const PluginOAuthClientSchema = z.object({ id: z.string(), state: PluginOAuthStateSchema, authorize_host: z.string(), token_host: z.string() });
80
+ export const PluginUpdateResultSchema = z.discriminatedUnion('status', [
81
+ z.object({ key: z.string(), status: z.literal('updated') }),
82
+ z.object({ key: z.string(), status: z.literal('needs_consent'), verdict: z.literal('caution'), findings: z.string(), consent_token: z.string().min(1) }),
83
+ z.object({ key: z.string(), status: z.literal('blocked'), verdict: z.string(), findings: z.string() }),
84
+ ]);
85
+ export const PluginCatalogQuerySchema = z.object({ query: z.string().trim().max(200).default(''), offset: z.coerce.number().int().min(0).max(10000).default(0), limit: z.coerce.number().int().min(1).max(50).default(20) });
86
+ export const PluginInstallInputSchema = z.object({ name: z.string().regex(/^[a-z0-9_-]{1,64}$/), consent_token: z.string().min(1).max(128).optional() });
87
+ export const PluginCatalogSchema = z.object({ supported: z.boolean(), read_only: z.boolean().optional(), entries: z.array(z.object({
88
+ name: z.string(), title: z.string(), description: z.string(), repo: z.string(), sha: z.string(), tier: z.string(), category: z.string(),
89
+ requirements: z.array(z.string()), installed_key: z.string().nullable(), installed_sha: z.string().nullable(), can_install: z.boolean(), refusal: z.string().nullable(),
90
+ })), total: z.number().int().nonnegative(), next_offset: z.number().int().nullable() });
91
+ export const PluginInstallResultSchema = z.discriminatedUnion('status', [
92
+ z.object({ name: z.string(), key: z.string(), status: z.literal('installed'), enabled: z.literal(false) }),
93
+ z.object({ name: z.string(), status: z.literal('needs_consent'), findings: z.string(), consent_token: z.string() }),
94
+ z.object({ name: z.string(), status: z.literal('blocked'), findings: z.string() }),
95
+ ]);
42
96
  export const PluginProviderSchema = z.object({ name: z.string(), display_name: z.string(), auth_type: z.string(), setup: z.enum(['ready', 'missing_cli', 'needs_setup', 'not_loaded', 'unavailable']) });
97
+ /** Profile-owned Agent dashboards use Web protocol panels; native Desktop bundles need migration. */
98
+ export const AgentDashboardPluginSchema = z.object({
99
+ name: z.string().regex(/^[a-z][a-z0-9_-]{0,63}$/), label: z.string().max(80), version: z.string().max(40), description: z.string().max(300),
100
+ enabled: z.boolean(), panel: z.string().nullable(), has_api: z.boolean(),
101
+ });
102
+ export const AgentDashboardResponseSchema = z.object({ status: z.number().int().min(200).max(599), body: z.string(), content_type: z.string() });
103
+ const DashboardNameParams = ProfileHomeParams.extend({ name: z.string() });
43
104
  /** A skill bundle: `/<name>` loads its `skill_count` skills in one message (TAL-265). */
44
105
  export const CommandBundleSchema = z.object({ name: z.string(), description: z.string(), skill_count: z.number().int(), source: z.literal('bundle') });
45
106
  /** `message` is the user message that loads the bundle's skills; members not installed or disabled are in `missing_skills` or left out. */
46
107
  export const CommandBundleInvocationSchema = z.object({ name: z.string(), source: z.literal('bundle'), message: z.string(), loaded_skills: z.array(z.string()), missing_skills: z.array(z.string()) });
47
108
  export const COMMANDS_METHODS = {
48
109
  'commands.registry': { params: ProfileHomeParams, result: z.object({ commands: z.array(CommandSchema) }) },
49
- 'commands.exec': { params: ProfileHomeParams.extend({ command: z.string().min(1) }), result: z.object({ output: z.string(), source: z.enum(['agent', 'plugin']) }) },
110
+ /**
111
+ * With `session_id`, a plugin command runs under that chat's session binding and `workspace` (TAL-904) and receives the session
112
+ * as its handler declares it; every recognised command fires the Agent's `pre_command` hook with that session (TAL-1267).
113
+ */
114
+ 'commands.exec': { params: ProfileHomeParams.extend({ command: z.string().min(1), session_id: z.string().min(1).optional(), workspace: z.string().optional() }), result: z.object({ output: z.string(), source: z.enum(['agent', 'plugin']) }) },
115
+ /**
116
+ * `/<prompt built-in> [args]` as the turn text (`message`) the Agent's builder writes for the session's profile, workspace and toolsets.
117
+ * `name` is the built-in's canonical name, null when the command is not one; `message` is null when this Agent has no builder for it.
118
+ * A failing builder errors `prompt_expand_failed`. Every built-in fires the Agent's `pre_command` hook with `session_id` (TAL-1279).
119
+ */
120
+ 'commands.expand_prompt': { params: ProfileHomeParams.extend({ command: z.string().min(1), session_id: z.string().min(1).optional(), workspace: z.string(), enabled_toolsets: z.array(z.string()).nullable() }), result: z.object({ name: z.string().nullable(), message: z.string().nullable() }) },
50
121
  'commands.bundles': { params: ProfileHomeParams, result: z.object({ bundles: z.array(CommandBundleSchema) }) },
51
122
  'commands.bundle_resolve': { params: ProfileHomeParams.extend({ command: z.string().min(1) }), result: CommandBundleInvocationSchema },
52
123
  'commands.moa_preset': { params: ProfileHomeParams.extend({ preset: z.string().nullable().optional() }), result: z.object({ moa: Loose }) },
124
+ 'plugins.dashboard_list': { params: ProfileHomeParams, result: z.object({ supported: z.boolean(), plugins: z.array(AgentDashboardPluginSchema) }) },
125
+ 'plugins.dashboard_toggle': { params: DashboardNameParams.extend({ enabled: z.boolean() }), result: z.object({ ok: z.literal(true) }) },
126
+ 'plugins.dashboard_asset': { params: DashboardNameParams.extend({ path: z.string() }), result: AgentDashboardResponseSchema },
127
+ /** Buffered HTTP only. Cancellation abandons the result; the Agent has no handler-cancel operation. */
128
+ 'plugins.dashboard_request': { params: DashboardNameParams.extend({ path: z.string(), method: z.string(), query: z.string(), headers: z.record(z.string(), z.string()), body: z.string(), authenticated: z.boolean(), websocket: z.boolean() }), result: AgentDashboardResponseSchema },
53
129
  'plugins.providers': { params: ProfileHomeParams, result: z.object({ providers: z.array(PluginProviderSchema) }) },
54
- 'plugins.list': { params: ProfileHomeParams.extend({ selected_providers: z.record(z.string(), z.string()).optional() }), result: z.object({ plugins: z.array(z.object({ name: z.string(), key: z.string(), version: z.string(), description: z.string(), enabled: z.boolean(), kind: z.string(), activation: z.string(), hooks: z.array(z.string()), is_active_provider: z.boolean().optional() })), supported_hooks: z.array(z.string()) }) },
130
+ 'plugins.list': { params: ProfileHomeParams.extend({ selected_providers: z.record(z.string(), z.string()).optional() }), result: z.object({ plugins: z.array(z.object({ name: z.string(), key: z.string(), version: z.string(), description: z.string(), enabled: z.boolean(), kind: z.string(), activation: z.string(), hooks: z.array(z.string()), is_active_provider: z.boolean().optional(), update_available: z.boolean().optional(), latest_revision: z.string().optional() })), supported_hooks: z.array(z.string()) }) },
131
+ 'plugins.catalog': { params: ProfileHomeParams.extend(PluginCatalogQuerySchema.shape), result: PluginCatalogSchema },
132
+ 'plugins.install': { params: ProfileHomeParams.extend(PluginInstallInputSchema.shape), result: PluginInstallResultSchema },
133
+ 'plugins.update': { params: ProfileHomeParams.extend({ key: z.string().min(1), accept_caution: z.boolean().optional(), consent_token: z.string().min(1).max(128).optional() }), result: PluginUpdateResultSchema },
134
+ /** TAL-820: each profile's `plugins.isolation` and installed third-party plugin count, read without loading a plugin. */
135
+ 'plugins.isolation': { params: BaseHomeParams, result: z.object({ profiles: z.array(z.object({ name: z.string(), isolation: z.enum(['in_process', 'host']), third_party_plugins: z.number().int() })) }) },
136
+ /** TAL-806: plugins whose manifest declares settings; `supported` is false on an Agent without `hermes_cli.plugins_settings`. */
137
+ 'plugins.settings': { params: ProfileHomeParams, result: z.object({ supported: z.boolean(), plugins: z.array(z.object({ key: z.string(), fields: z.array(PluginSettingFieldSchema) })) }) },
138
+ /**
139
+ * TAL-1276: plugins whose manifest declares OAuth clients (Agent v0.22.3+), each client's sign-in state as
140
+ * `hermes auth status <plugin>:<id>` reports it, and the hosts the CLI names before signing in: the code is approved
141
+ * at `authorize_host` and exchanged at `token_host`. `supported` is false on an Agent without the plugin OAuth helpers.
142
+ */
143
+ 'plugins.oauth_status': { params: ProfileHomeParams, result: z.object({ supported: z.boolean(), plugins: z.array(z.object({ key: z.string(), clients: z.array(PluginOAuthClientSchema) })) }) },
144
+ /** TAL-1276: `hermes auth logout <plugin>:<id>`; `removed` is false when no grant was stored. Errors `oauth_unsupported` on older Agents. */
145
+ 'plugins.oauth_logout': { params: ProfileHomeParams.extend({ key: z.string().min(1), id: z.string().min(1) }), result: z.object({ key: z.string(), id: z.string(), removed: z.boolean() }) },
146
+ /**
147
+ * TAL-806: write non-secret `values` through the Agent's locked plugin-state writer and return the refreshed fields.
148
+ * An undeclared key, a secret or a type mismatch is invalid params; a managed install or key errors `plugin_settings_managed`.
149
+ * `secret_envs` (secret key → `.env` name the caller wrote) is checked against the current manifest before anything is
150
+ * written; a key that is no longer a secret stored under that name errors `plugin_settings_changed`.
151
+ */
152
+ 'plugins.settings_save': { params: ProfileHomeParams.extend({ key: z.string().min(1), values: z.record(z.string(), Json), secret_envs: z.record(z.string(), z.string()).optional() }), result: z.object({ key: z.string(), written: z.array(z.string()), fields: z.array(PluginSettingFieldSchema), runtime_application: PluginRuntimeApplicationSchema.optional() }) },
55
153
  };
56
154
  // ── kanban ─────────────────────────────────────────────────────────────
57
155
  const Board = ProfileHomeParams.extend({ board: z.string().nullable().optional() });
58
- /** `claim_live`: a running task's worker still holds its claim. `has_completion_evidence`: the task stores a result, which the Agent requires to complete it (TAL-557). */
59
- export const KanbanTaskSchema = z.object({ id: z.string(), title: z.string(), status: z.string(), priority: z.number().int(), claim_live: z.boolean(), has_completion_evidence: z.boolean() }).catchall(Json);
156
+ /** `claim_live`: a running task's worker still holds its claim. `has_completion_evidence`: the task stores a result, which the Agent requires to complete it (TAL-557).
157
+ * `agent_move_targets`: the Agent workflow's manual moves from the task's status, `null` before Agent 0.21.6 (TAL-796). */
158
+ export const KanbanTaskSchema = z.object({ id: z.string(), title: z.string(), status: z.string(), priority: z.number().int(), claim_live: z.boolean(), has_completion_evidence: z.boolean(), agent_move_targets: z.array(z.string()).nullable() }).catchall(Json);
60
159
  const TaskEnvelope = z.object({ task: KanbanTaskSchema, read_only: z.boolean() });
160
+ /** Task detail's links (TAL-809): `link_tasks` names each linked task that still exists; `current_run_started_at` is the active run's start, `null` without one (`started_at` is the first-ever start). */
161
+ export const KanbanTaskLinksShape = { links: z.object({ parents: z.array(z.string()), children: z.array(z.string()) }), link_tasks: z.array(z.object({ id: z.string(), title: z.string(), status: z.string() })) };
162
+ export const KanbanRunStartShape = { current_run_started_at: z.number().nullable() };
61
163
  export const KanbanBoardSchema = z.union([
62
164
  z.object({ changed: z.literal(false), latest_event_id: z.number().int(), read_only: z.boolean() }),
63
165
  z.object({
64
- changed: z.literal(true), columns: z.array(z.object({ name: z.string(), tasks: z.array(KanbanTaskSchema) })), tenants: z.array(z.string()), assignees: z.array(z.string()),
166
+ /** The Agent workflow's columns in board order (TAL-796): `name` is the status key, `label` the Agent's English label. */
167
+ changed: z.literal(true), columns: z.array(z.object({ name: z.string(), label: z.string(), tasks: z.array(KanbanTaskSchema) })), tenants: z.array(z.string()), assignees: z.array(z.string()),
65
168
  latest_event_id: z.number().int(), read_only: z.boolean(),
66
169
  filters: z.object({ tenant: z.string().nullable(), assignee: z.string().nullable(), include_archived: z.boolean(), only_mine: z.boolean(), profile: z.string().nullable() }),
67
170
  }),
@@ -76,7 +179,7 @@ export const KANBAN_METHODS = {
76
179
  'kanban.update_board': { params: ProfileHomeParams.extend({ slug: z.string(), board_spec: Loose }), result: z.object({ board: KanbanBoardMetaSchema, read_only: z.boolean() }) },
77
180
  'kanban.delete_board': { params: ProfileHomeParams.extend({ slug: z.string(), delete: z.boolean().optional() }), result: z.object({ result: Json, current: z.string(), read_only: z.boolean() }) },
78
181
  'kanban.switch_board': { params: ProfileHomeParams.extend({ slug: z.string() }), result: z.object({ current: z.string(), read_only: z.boolean() }) },
79
- 'kanban.task': { params: Board.extend({ task_id: z.string() }), result: z.object({ task: KanbanTaskSchema, comments: z.array(Loose), events: z.array(Loose), links: z.object({ parents: z.array(z.string()), children: z.array(z.string()) }), runs: z.array(Loose), read_only: z.boolean() }) },
182
+ 'kanban.task': { params: Board.extend({ task_id: z.string() }), result: z.object({ task: KanbanTaskSchema.extend(KanbanRunStartShape), comments: z.array(Loose), events: z.array(Loose), ...KanbanTaskLinksShape, runs: z.array(Loose), read_only: z.boolean() }) },
80
183
  'kanban.create_task': { params: Board.extend({ task: Loose }), result: TaskEnvelope },
81
184
  'kanban.patch_task': { params: Board.extend({ task_id: z.string(), patch: Loose }), result: TaskEnvelope },
82
185
  'kanban.task_action': { params: Board.extend({ task_id: z.string(), action: z.enum(['block', 'unblock']), reason: z.string().nullable().optional(), confirm_running_exit: z.boolean().optional() }), result: TaskEnvelope },
@@ -86,29 +189,52 @@ export const KANBAN_METHODS = {
86
189
  'kanban.normalize_board': { params: Board, result: z.object({ board: z.string() }) },
87
190
  'kanban.events': { params: Board.extend({ since: z.number().int().optional(), limit: z.number().int().optional() }), result: z.object({ events: z.array(KanbanEventSchema), cursor: z.number().int(), latest_event_id: z.number().int(), read_only: z.boolean() }) },
88
191
  'kanban.config': { params: Board, result: z.object({ columns: z.array(z.string()), assignees: z.array(KanbanAssigneeSchema), default_tenant: z.string(), lane_by_profile: z.boolean(), include_archived_by_default: z.boolean(), render_markdown: z.boolean(), read_only: z.boolean() }) },
89
- 'kanban.stats': { params: Board, result: Loose },
192
+ /** `column_order`: the Agent workflow's column keys in board order (TAL-796). */
193
+ 'kanban.stats': { params: Board, result: z.object({ column_order: z.array(z.string()) }).catchall(Json) },
90
194
  'kanban.assignees': { params: Board, result: z.object({ assignees: z.array(KanbanAssigneeSchema) }) },
91
195
  'kanban.task_log': { params: Board.extend({ task_id: z.string(), tail: z.number().int().optional() }), result: z.object({ task_id: z.string(), path: z.string(), exists: z.boolean(), size_bytes: z.number().int(), content: z.string(), truncated: z.boolean() }) },
92
196
  'kanban.bulk': { params: Board.extend({ bulk: Loose }), result: z.object({ results: z.array(z.object({ id: z.string(), ok: z.boolean(), error: z.string().optional() })), read_only: z.boolean() }) },
93
197
  'kanban.dispatch': { params: Board.extend({ dry_run: z.boolean().optional(), max: z.number().int().optional() }), result: Loose },
94
198
  };
95
199
  // ── state_db ───────────────────────────────────────────────────────────
200
+ /** Busy Agent work, or retained canonical history that this Agent cannot safely retire. */
201
+ export const SessionDeleteRefusedSchema = z.enum(['active_turn', 'runtime_history_unsupported']);
96
202
  const Session = ProfileHomeParams.extend({ session_id: z.string().min(1) });
97
203
  export const STATE_DB_METHODS = {
98
204
  'state_db.sync_start': { params: Session.extend({ model: z.string().nullable().optional() }), result: Ok },
99
205
  'state_db.sync_usage': { params: Session.extend({ input_tokens: z.number().int().optional(), output_tokens: z.number().int().optional(), estimated_cost: z.number().nullable().optional(), model: z.string().nullable().optional(), title: z.string().nullable().optional(), message_count: z.number().int().nullable().optional(), cache_read_tokens: z.number().int().optional(), cache_write_tokens: z.number().int().optional(), api_call_count: z.number().int().nullable().optional() }), result: Ok },
100
206
  /** TAL-574: `manual` writes a user-requested title (rename or regenerate) with `user` provenance; otherwise it is an `llm` auto title. */
101
207
  'state_db.sync_title': { params: Session.extend({ title: z.string(), manual: z.boolean().optional() }), result: Ok },
102
- 'state_db.delete_cli_session': { params: Session, result: Ok },
208
+ /**
209
+ * TAL-792: `include_compression_lineage` also removes the session's compression continuations, resolved and removed in the
210
+ * same transaction (tip first). `refused` when another Agent process holds a live turn lease or compression lock on any
211
+ * row the delete would remove, or the Agent lacks retirement APIs for retained canonical history; nothing is removed. `deleted_ids` is the transaction's committed target set,
212
+ * including continuations/delegates; it remains present when only post-commit artifact cleanup failed.
213
+ */
214
+ 'state_db.delete_cli_session': { params: Session.extend({ include_compression_lineage: z.boolean().optional() }), result: Ok.extend({ refused: SessionDeleteRefusedSchema.optional(), deleted_ids: z.array(z.string().min(1)).optional() }) },
103
215
  /** TAL-258: append one row through the Agent's `SessionDB.append_message` (bumps `message_count`); `ok` is false without a state.db. */
104
216
  'state_db.append_message': { params: Session.extend({ role: z.string().min(1), content: z.string(), tool_name: z.string().nullable().optional(), timestamp: z.number().nullable().optional() }), result: Ok },
217
+ /**
218
+ * TAL-1274: the Agent's low-disk session auto-prune question for the profile (Agent v0.22.3 `hermes_cli.prune_advisor`), as the
219
+ * Agent's own `lines` naming `/autoprune on|off|later`; null while the Agent would not ask (disk not low, `sessions.auto_prune`
220
+ * set, or snoozed) or has no advisor. May measure state.db, which takes seconds on a large store.
221
+ */
222
+ 'state_db.prune_advice': { params: ProfileHomeParams, result: z.object({ advice: z.object({ lines: z.array(z.string()).min(1) }).nullable() }) },
105
223
  };
106
224
  // ── profiles ───────────────────────────────────────────────────────────
107
225
  /** TAL-213: the profile's canonical Bot Chat (its session titled exactly "Bot Chat") and that chat's live compression tip. */
108
226
  export const CanonicalBotChatSchema = z.object({ session_id: z.string(), tip_session_id: z.string() });
227
+ /**
228
+ * TAL-805: a profile's gateway under the host multiplexer. `own` runs its own gateway process, `served` is served by the
229
+ * live host gateway, `parked` was stopped through the host (`hermes -p X gateway stop`) and nothing runs, `standalone` opted out of the host
230
+ * and is not running, `stopped` is none of these. Only `own` and `served` are running; Agents without the multiplexer
231
+ * helpers report `own` or `stopped`.
232
+ */
233
+ export const GatewayModeSchema = z.enum(['own', 'served', 'parked', 'standalone', 'stopped']);
109
234
  export const ProfileRowSchema = z.object({
110
235
  name: z.string(), path: z.string(), is_default: z.boolean(), gateway_running: z.boolean(), model: z.string().nullable(), provider: z.string().nullable(), has_env: z.boolean(),
111
236
  visible: z.boolean(), skill_count: z.number().int(), enabled_skills: z.number().int(), total_skills: z.number().int(),
237
+ gateway_mode: GatewayModeSchema.default('stopped'),
112
238
  // Absent reads as no identity and no chat, so hand-built rows (test fakes) need not spell them out.
113
239
  display_name: z.string().default(''), description: z.string().default(''), has_avatar: z.boolean().default(false),
114
240
  canonical_session: CanonicalBotChatSchema.nullable().default(null),
@@ -121,15 +247,19 @@ export const PROFILES_METHODS = {
121
247
  'profiles.skills_stats': { params: ProfileHomeParams, result: z.object({ enabled: z.number().int(), total: z.number().int() }) },
122
248
  };
123
249
  // ── skills ─────────────────────────────────────────────────────────────
124
- export const SkillRowSchema = z.object({ name: z.string(), description: z.string(), category: z.string().nullable(), disabled: z.boolean() });
250
+ export const SkillCatalogIdentitySchema = z.object({
251
+ load_name: z.string().optional(), declared_name: z.string().optional(),
252
+ status: z.enum(['unique', 'ambiguous', 'shadowed']).optional(), tier: z.number().int().optional(),
253
+ });
254
+ export const SkillRowSchema = SkillCatalogIdentitySchema.extend({ name: z.string(), description: z.string(), category: z.string().nullable(), disabled: z.boolean() });
125
255
  export const SkillViewSchema = z.union([
126
- z.object({ success: z.literal(true), name: z.string(), description: z.string(), tags: z.array(z.string()), related_skills: z.array(z.string()), content: z.string(), path: z.string(), skill_dir: z.string().nullable(), linked_files: z.record(z.string(), z.array(z.string())) }),
256
+ z.object({ success: z.literal(true), name: z.string(), description: z.string(), tags: z.array(z.string()), related_skills: z.array(z.string()), content: z.string(), path: z.string(), skill_dir: z.string().nullable(), linked_files: z.record(z.string(), z.array(z.string())), editable: z.boolean().optional() }),
127
257
  z.object({ success: z.literal(false), error: z.string(), available_skills: z.array(z.string()).optional(), available_skills_truncated: z.boolean().optional(), total_skills: z.number().int().optional(), hint: z.string().optional() }),
128
258
  ]);
129
259
  export const SKILLS_METHODS = {
130
260
  'skills.list': { params: ProfileHomeParams.extend({ category: z.string().nullable().optional() }), result: z.object({ success: z.boolean().optional(), skills: z.array(SkillRowSchema), categories: z.array(z.string()), count: z.number().int(), message: z.string().optional() }) },
131
261
  'skills.view': { params: ProfileHomeParams.extend({ name: z.string().min(1) }), result: SkillViewSchema },
132
- 'skills.find': { params: ProfileHomeParams.extend({ name: z.string().min(1) }), result: z.object({ found: z.boolean(), skill_dir: z.string().nullable(), skill_md: z.string().nullable() }) },
262
+ 'skills.find': { params: ProfileHomeParams.extend({ name: z.string().min(1) }), result: z.object({ found: z.boolean(), skill_dir: z.string().nullable(), skill_md: z.string().nullable(), load_name: z.string().optional(), declared_name: z.string().optional(), sibling_load_names: z.array(z.string()).optional() }) },
133
263
  };
134
264
  // ── mcp ────────────────────────────────────────────────────────────────
135
265
  export const MCP_METHODS = {
@@ -137,18 +267,90 @@ export const MCP_METHODS = {
137
267
  'mcp.registry_tools': { params: ProfileHomeParams, result: z.object({ tools: z.array(z.object({ name: z.string(), server: z.string(), schema: Loose })) }) },
138
268
  'mcp.reload': { params: ProfileHomeParams, result: z.object({ output: z.string() }) },
139
269
  };
270
+ // ── connectors (TAL-818) ───────────────────────────────────────────────
271
+ const ConnectorSlug = z.string().regex(/^[a-z0-9][a-z0-9_-]*$/);
272
+ /** One portal policy layer's rules (the Agent's `PolicyBody`): `allow` names the apps that stay on, `deny` the ones off; `tools` turns single tools off. */
273
+ export const ConnectorPolicyBodySchema = z.object({
274
+ mode: z.enum(['unrestricted', 'deny-all', 'allow', 'deny']),
275
+ connectors: z.array(z.string()).optional(), disabled_connectors: z.array(z.string()).optional(),
276
+ tools: z.record(z.string(), z.object({ disable: z.array(z.string()) })).optional(),
277
+ tags: z.object({ enable: z.array(z.string()).optional(), disable: z.array(z.string()).optional() }).optional(),
278
+ });
279
+ /** A connection operation's target (`tools/connectors/operation.py`): a hosted app account, or an MCP server the card approves. */
280
+ export const ConnectorOperationTargetSchema = z.object({
281
+ name: z.string(), kind: z.string(), state: z.enum(['pending', 'initiated', 'connected', 'skipped', 'failed', 'expired', 'not_connected']), detail: z.string(),
282
+ connect_url: z.string().nullable().optional(),
283
+ required_env: z.array(z.object({ name: z.string(), prompt: z.string(), required: z.boolean(), secret: z.boolean() })),
284
+ });
285
+ /** `owner`: `account` for the Connectors page's connect, `session` for a chat card, which `tool_call_id` opened. */
286
+ export const ConnectorOperationSnapshotSchema = z.object({ op_id: z.string(), settled: z.boolean(), owner: z.enum(['account', 'session']), tool_call_id: z.string().nullable(), targets: z.array(ConnectorOperationTargetSchema) });
287
+ const ConnectorOp = ProfileHomeParams.extend({ op_id: z.string().min(1) });
288
+ /**
289
+ * Failures carry `data.condition`: `connectors_unavailable`, `connectors_sign_in`, `connectors_forbidden`, `connectors_org_required`,
290
+ * `connector_not_found`, `connection_not_found`, `policy_conflict` (the member rules changed since `expected_revision`), `policy_invalid`,
291
+ * `operation_not_found`, `operation_settled`, `operation_refused`, `connectors_failed`.
292
+ */
293
+ export const CONNECTORS_METHODS = {
294
+ /** The Agent's `connectors_available()`; never creates an identity. */
295
+ 'connectors.available': { params: ProfileHomeParams, result: z.object({ available: z.boolean() }) },
296
+ 'connectors.catalog': { params: ProfileHomeParams, result: z.object({ connectors: z.array(z.object({ slug: z.string(), name: z.string(), description: z.string(), category: z.string(), logo_url: z.string().nullable() })) }) },
297
+ 'connectors.accounts': { params: ProfileHomeParams, result: z.object({ accounts: z.array(z.object({ connection_id: z.string(), connector: z.string(), status: z.string(), status_reason: z.string().nullable(), label: z.string(), alias: z.string().nullable(), active: z.boolean(), disabled: z.boolean(), created_at: z.string() })) }) },
298
+ 'connectors.remove': { params: ProfileHomeParams.extend({ connection_id: z.string().min(1) }), result: z.object({ connection_id: z.string(), connector: z.string(), status: z.literal('removed') }) },
299
+ 'connectors.tools': { params: ProfileHomeParams.extend({ connector: ConnectorSlug, refresh: z.boolean().optional() }), result: z.object({ connector: z.string(), tools: z.array(z.object({ slug: z.string(), name: z.string(), description: z.string(), facet: z.string(), hints: z.array(z.string()), categories: z.array(z.string()), deprecated: z.boolean() })) }) },
300
+ 'connectors.policy.get': { params: ProfileHomeParams, result: z.object({ layers: z.array(z.object({ kind: z.enum(['org', 'role', 'member']), revision: z.string(), body: ConnectorPolicyBodySchema })), effective: ConnectorPolicyBodySchema.extend({ revision: z.string() }) }) },
301
+ /** One member-rule change: `enabled` turns the app on or off; `tool` with `tool_enabled` turns one of its tools on or off. */
302
+ 'connectors.policy.set': { params: ProfileHomeParams.extend({ connector: ConnectorSlug, expected_revision: z.string().min(1), enabled: z.boolean().optional(), tool: z.string().min(1).optional(), tool_enabled: z.boolean().optional() }), result: z.object({ revision: z.string() }) },
303
+ /** Starts (or rejoins) the account-owned connect and waits for its authorization link. */
304
+ 'connectors.connect': { params: ProfileHomeParams.extend({ connector: ConnectorSlug }), result: ConnectorOperationSnapshotSchema },
305
+ 'connectors.operation': { params: ConnectorOp, result: ConnectorOperationSnapshotSchema },
306
+ 'connectors.respond': { params: ConnectorOp.extend({ action: z.enum(['skip', 'retry', 'approve', 'continue']), target: z.string().optional(), env: z.record(z.string(), z.string()).optional() }), result: ConnectorOperationSnapshotSchema },
307
+ };
140
308
  // ── stt ────────────────────────────────────────────────────────────────
141
309
  export const STT_METHODS = {
142
- 'stt.capability': { params: ProfileHomeParams, result: z.object({ available: z.boolean(), provider: z.string() }) },
143
- 'stt.transcribe': { params: ProfileHomeParams.extend({ audio_b64: z.string().min(1), suffix: z.string().optional() }), result: z.object({ transcript: z.string() }) },
310
+ /**
311
+ * `streaming` (TAL-802): `stt.streaming` is on and the provider has a live wire with credentials, so `stt.stream` would open.
312
+ * `warm_supported` (TAL-916): the available provider is the local engine and the Agent can pre-load it (`tools.stt_lease`),
313
+ * so `stt.warm` does real work.
314
+ */
315
+ 'stt.capability': { params: ProfileHomeParams, result: z.object({ available: z.boolean(), provider: z.string(), streaming: z.boolean(), warm_supported: z.boolean() }) },
316
+ /**
317
+ * TAL-916: pre-load the profile's local engine (first-use download and model load) so the next `stt.transcribe` starts
318
+ * hot. Blocks until loaded; a no-op (`warmed: false`) for remote providers, and `action: 'unsupported'` on an Agent
319
+ * without the warmer.
320
+ */
321
+ 'stt.warm': { params: ProfileHomeParams, result: z.object({ provider: z.string(), warmed: z.boolean(), action: z.string(), error: z.string().optional() }) },
322
+ 'stt.transcribe': { params: ProfileHomeParams.extend({ audio_b64: z.string().min(1), suffix: z.string().optional() }), result: z.object({ transcript: z.string(), filtered: z.boolean() }) },
323
+ /**
324
+ * TAL-802: one live dictation. Opens the profile's streaming session for s16le mono PCM at `sample_rate` (the sidecar
325
+ * resamples), emits `ready` with the `stream_id` that `stt.stream_audio` feeds, then `partial` text as the provider
326
+ * recognizes it, and returns the session's final transcript once audio ends. Cancelling the call cancels the session.
327
+ */
328
+ 'stt.stream': {
329
+ params: ProfileHomeParams.extend({ sample_rate: z.number().int().min(8000).max(48000) }),
330
+ result: z.object({ transcript: z.string(), filtered: z.boolean(), provider: z.string() }),
331
+ stream: z.discriminatedUnion('event', [
332
+ z.object({ event: z.literal('ready'), data: z.object({ stream_id: z.string() }) }),
333
+ z.object({ event: z.literal('partial'), data: z.object({ text: z.string() }) }),
334
+ ]),
335
+ },
336
+ /** TAL-802: feed one PCM chunk to an open `stt.stream`, in order; `end` marks the end of speech. */
337
+ 'stt.stream_audio': { params: z.object({ stream_id: z.string().min(1), audio_b64: z.string().optional(), end: z.boolean().optional() }), result: z.object({ ok: z.literal(true) }) },
144
338
  };
145
339
  // ── cron ───────────────────────────────────────────────────────────────
146
- export const CronJobSchema = z.object({ id: z.string(), name: z.string().nullable().optional(), profile: z.string().nullable(), toast_notifications: z.boolean(), monitor: z.string(), continuity: z.boolean() }).catchall(Json);
340
+ /** `running_elsewhere`: a live execution owner. `recovery_only`: a retained snapshot, not a source for new task context. */
341
+ export const CronJobSchema = z.object({ id: z.string(), name: z.string().nullable().optional(), profile: z.string().nullable(), toast_notifications: z.boolean(), monitor: z.string(), continuity: z.boolean(), running_elsewhere: z.boolean().optional(), recovery_only: z.boolean().optional() }).catchall(Json);
147
342
  const CronJob = ProfileHomeParams.extend({ job_id: z.string().min(1) });
148
343
  const JobEnvelope = z.object({ job: CronJobSchema });
149
344
  export const CronUsageSchema = z.object({ model: z.string().optional(), provider: z.string().optional(), estimated_cost_usd: z.number().optional(), duration_seconds: z.number().optional(), input_tokens: z.number().int().nullable().optional(), output_tokens: z.number().int().nullable().optional(), total_tokens: z.number().int().nullable().optional() });
345
+ export const CronBlueprintSlotSchema = z.object({ name: z.string(), type: z.string(), label: z.string(), default: z.string().nullable(), options: z.array(z.string()), optional: z.boolean(), strict: z.boolean(), help: z.string() });
346
+ export const CronBlueprintsSchema = z.object({ blueprints: z.array(z.object({ key: z.string(), title: z.string(), description: z.string(), category: z.string(), source: z.enum(['builtin', 'plugin']), plugin: z.string().nullable(), schedule_label: z.string(), slots: z.array(CronBlueprintSlotSchema) })) });
347
+ export const CronBlueprintFillInputSchema = z.object({ key: z.string().min(1), values: z.record(z.string(), z.string()) });
348
+ export const CronBlueprintFillSchema = z.object({ draft: z.object({ name: z.string(), prompt: z.string(), schedule: z.string(), deliver: z.string(), skills: z.array(z.string()) }) });
150
349
  export const CRON_METHODS = {
151
- 'cron.list': { params: ProfileHomeParams, result: z.object({ jobs: z.array(CronJobSchema) }) },
350
+ 'cron.blueprints': { params: ProfileHomeParams, result: CronBlueprintsSchema },
351
+ 'cron.blueprint_fill': { params: CronBlueprintFillInputSchema.extend({ profile_home: z.string().optional() }), result: CronBlueprintFillSchema },
352
+ /** `probe_store` adds the store's write probe (TAL-810): null when writes land or the Agent predates the probe. */
353
+ 'cron.list': { params: ProfileHomeParams.extend({ probe_store: z.boolean().optional() }), result: z.object({ jobs: z.array(CronJobSchema), store_health: z.object({ store: z.string(), error: z.string(), since: z.string(), fix: z.string() }).nullable().optional() }) },
152
354
  'cron.get': { params: CronJob, result: z.object({ job: CronJobSchema.nullable() }) },
153
355
  'cron.create': { params: ProfileHomeParams.extend({ job: Loose, execution_home: z.string().nullable().optional() }), result: JobEnvelope },
154
356
  'cron.update': { params: CronJob.extend({ updates: Loose }), result: JobEnvelope },
@@ -164,15 +366,23 @@ export const CRON_METHODS = {
164
366
  'cron.history': { params: CronJob.extend({ offset: z.number().int().optional(), limit: z.number().int().optional() }), result: z.object({ job_id: z.string(), runs: z.array(z.object({ filename: z.string(), size: z.number().int(), modified: z.number(), usage: CronUsageSchema })), total: z.number().int(), offset: z.number().int() }) },
165
367
  'cron.run_detail': { params: CronJob.extend({ filename: z.string().min(1) }), result: z.object({ job_id: z.string(), filename: z.string(), content: z.string(), snippet: z.string(), usage: CronUsageSchema }) },
166
368
  'cron.output': { params: CronJob.extend({ limit: z.number().int().optional() }), result: z.object({ job_id: z.string(), outputs: z.array(z.object({ filename: z.string(), content: z.string() })) }) },
167
- 'cron.delivery_options': { params: z.object({}), result: z.object({ platforms: z.array(z.object({ value: z.string(), label: z.string() })) }) },
369
+ 'cron.delivery_options': { params: ProfileHomeParams, result: z.object({ platforms: z.array(z.object({ value: z.string(), label: z.string() })) }) },
168
370
  };
169
371
  // ── providers / models ─────────────────────────────────────────────────
372
+ /**
373
+ * `providers.model_ids` `fallback` (TAL-799): the Agent served its curated list because the live catalog was unreachable.
374
+ * `providers.local_models` (TAL-798): the GGUFs staged for the Agent's managed llama.cpp provider `provider_id`, and the
375
+ * running server's base URL (`current_endpoint`, null while it is off).
376
+ */
170
377
  export const PROVIDERS_METHODS = {
171
378
  'providers.registry': { params: ProfileHomeParams, result: z.object({ providers: z.record(z.string(), z.object({ id: z.string(), name: z.string(), auth_type: z.string() }).catchall(Json)) }) },
172
379
  'providers.auth_status': { params: ProfileHomeParams.extend({ provider: z.string().nullable().optional() }), result: z.object({ status: z.object({ logged_in: z.boolean() }).catchall(Json) }) },
173
- 'providers.model_ids': { params: ProfileHomeParams.extend({ provider: z.string().min(1), force_refresh: z.boolean().optional() }), result: z.object({ provider: z.string(), model_ids: z.array(z.string()) }) },
380
+ 'providers.model_ids': { params: ProfileHomeParams.extend({ provider: z.string().min(1), force_refresh: z.boolean().optional() }), result: z.object({ provider: z.string(), model_ids: z.array(z.string()), fallback: z.boolean().default(false) }) },
381
+ 'providers.local_models': { params: ProfileHomeParams, result: z.object({ provider_id: z.string(), models: z.array(z.string()), current_endpoint: z.string().nullable() }) },
174
382
  'providers.resolve_runtime': { params: ProfileHomeParams.extend({ requested: z.string().nullable().optional(), api_key: z.string().nullable().optional(), base_url: z.string().nullable().optional(), target_model: z.string().nullable().optional() }), result: z.object({ runtime: Loose }) },
175
383
  'providers.credential_pool': { params: ProfileHomeParams.extend({ provider: z.string().min(1) }), result: z.object({ available: z.boolean(), strategy: z.string(), entries: z.array(Loose) }) },
384
+ 'models.selection_warnings': { params: ProfileHomeParams.extend({ session_id: z.string().min(1), model: z.string().min(1), provider: z.string().nullable(), current_model: z.string().nullable(), current_provider: z.string().nullable(), context_tokens: z.number().int().positive().nullable(), messages: z.array(Loose) }), result: z.object({ warnings: z.array(ModelSelectionWarningSchema), context_tokens: z.number().int().positive().nullable(), provider: z.string(), base_url: z.string() }) },
385
+ 'models.speed_tiers': { params: ProfileHomeParams.extend({ routes: z.array(z.object({ model: z.string().min(1), provider: z.string(), base_url: z.string().optional() })) }), result: z.object({ tiers: z.array(z.array(SpeedTierSchema)) }) },
176
386
  'models.context_length': { params: ProfileHomeParams.extend({ model: z.string().min(1), base_url: z.string().optional(), api_key: z.string().optional(), provider: z.string().optional(), config_context_length: z.number().int().nullable().optional() }), result: z.object({ model: z.string(), context_length: z.number().int().nullable() }) },
177
387
  'models.estimate_tokens': { params: z.object({ messages: z.array(Loose) }), result: z.object({ tokens: z.number().int() }) },
178
388
  'models.capabilities': { params: ProfileHomeParams.extend({ provider: z.string(), model: z.string() }), result: z.object({ capabilities: Loose.nullable() }) },
@@ -183,14 +393,21 @@ export const PROVIDERS_METHODS = {
183
393
  * `oauth.start` answers the code to show; a sidecar thread polls the provider and writes the credential through the
184
394
  * Agent's own auth store. A flow belongs to the profile home that started it: poll and cancel from another home do not
185
395
  * find it. `error` is the Agent's reason for a flow that ended without a credential.
396
+ *
397
+ * TAL-1276: provider `plugin:<key>:<id>` is the browser sign-in of a plugin-declared OAuth client. Its start has no
398
+ * `user_code`; `verification_url` is the authorize link, `redirect_uri` the loopback address the browser is sent back
399
+ * to, and `authorize_host`/`token_host` the hosts the code goes to. It finishes when that redirect reaches the sidecar's
400
+ * listener or through `oauth.submit_redirect` with the address the browser showed (`oauth_redirect_invalid` for any
401
+ * other address, `oauth_flow_ended` once the flow ended).
186
402
  */
187
403
  export const OAuthFlowStatusSchema = z.enum(['pending', 'approved', 'denied', 'expired', 'cancelled', 'error']);
188
404
  const OAuthFlow = z.object({ flow_id: z.string(), provider: z.string(), status: OAuthFlowStatusSchema, error: z.string().nullable() });
189
405
  const OAuthFlowParams = ProfileHomeParams.extend({ flow_id: z.string().min(1) });
190
406
  export const OAUTH_METHODS = {
191
- 'oauth.start': { params: ProfileHomeParams.extend({ provider: z.string().min(1) }), result: z.object({ flow_id: z.string(), provider: z.string(), status: z.literal('pending'), user_code: z.string(), verification_url: z.string(), expires_in: z.number().int(), interval: z.number().int() }) },
407
+ 'oauth.start': { params: ProfileHomeParams.extend({ provider: z.string().min(1) }), result: z.object({ flow_id: z.string(), provider: z.string(), status: z.literal('pending'), user_code: z.string().optional(), verification_url: z.string(), expires_in: z.number().int(), interval: z.number().int(), redirect_uri: z.string().optional(), authorize_host: z.string().optional(), token_host: z.string().optional() }) },
192
408
  'oauth.poll': { params: OAuthFlowParams, result: OAuthFlow },
193
409
  'oauth.cancel': { params: OAuthFlowParams, result: OAuthFlow },
410
+ 'oauth.submit_redirect': { params: OAuthFlowParams.extend({ url: z.string().min(1) }), result: OAuthFlow },
194
411
  };
195
412
  // ── aux / text / process / usage / gateway ─────────────────────────────
196
413
  export const AuxUsageSchema = z.object({ prompt_tokens: z.number().int().optional(), completion_tokens: z.number().int().optional(), total_tokens: z.number().int().optional() });
@@ -200,13 +417,55 @@ export const AUX_METHODS = {
200
417
  * of the task's auxiliary route; `credential_missing` means it has no credential. `finish_reason` is the provider's, when known.
201
418
  */
202
419
  'aux.complete': { params: ProfileHomeParams.extend({ task: z.string().min(1), messages: z.array(Loose).min(1), main_runtime: Loose.nullable().optional(), main_fallback: z.boolean().optional(), max_tokens: z.number().int().nullable().optional(), temperature: z.number().nullable().optional(), model: z.string().nullable().optional(), provider: z.string().nullable().optional() }), result: z.object({ model: z.string(), text: z.string(), usage: AuxUsageSchema.nullable(), finish_reason: z.string().nullable().default(null) }), stream: z.discriminatedUnion('event', [z.object({ event: z.literal('token'), data: z.object({ text: z.string() }) })]) },
420
+ /**
421
+ * TAL-800: the profile's auxiliary slots in Agent order (built-ins, then the profile's plugin tasks). `reasoning_effort`
422
+ * is the slot's resolved effort (`''` = provider default); `effective` is the route an `inherit_from` task resolves to now.
423
+ * `reserved`: the Agent's other `auxiliary.*` keys (settings and unlisted routes); a reset never touches them.
424
+ */
425
+ 'aux.tasks': { params: ProfileHomeParams, result: z.object({ tasks: z.array(z.object({ key: z.string().min(1), label: z.string(), description: z.string(), inherit_from: z.string().nullable(), reasoning_effort: z.string(), effective: z.object({ provider: z.string(), model: z.string(), base_url: z.string() }).nullable() })), reserved: z.array(z.string()) }) },
203
426
  'aux.resolve': { params: ProfileHomeParams.extend({ task: z.string().min(1), main_runtime: Loose.nullable().optional() }), result: z.object({ configured: z.boolean(), model: z.string().nullable(), error: z.string().optional() }) },
204
427
  };
205
428
  export const TEXT_METHODS = {
206
429
  'text.image_mode': { params: ProfileHomeParams.extend({ provider: z.string(), model: z.string(), cfg: Loose.nullable().optional(), requested_provider: z.string().optional() }), result: z.object({ mode: z.enum(['native', 'text']), reason: z.string(), supports_vision: z.boolean().nullable() }) },
207
430
  'text.portal_tags': { params: ProfileHomeParams, result: z.object({ client_tag: z.string().nullable(), conversation_tag: z.string().nullable(), tags: z.union([z.array(z.string()), Loose]) }) },
431
+ /** TAL-906: the Agent's read-aloud script (`prepare_spoken_text`); `text` as written on an Agent without it. */
432
+ 'text.spoken': { params: z.object({ text: z.string() }), result: z.object({ spoken_text: z.string() }) },
433
+ };
434
+ /**
435
+ * TAL-886: the Agent's `MemoryStore` reading of a MEMORY.md / USER.md body: its char count and the profile's limit for that store.
436
+ * TAL-784: `memory.write` replaces a memory page file with `content` only while its bytes still hash (SHA-256 hex) to `base`,
437
+ * holding the Agent's per-file lock (`<file>.lock`); `written: false` means the file changed and is untouched.
438
+ */
439
+ export const MEMORY_METHODS = {
440
+ 'memory.check': { params: ProfileHomeParams.extend({ target: z.enum(['memory', 'user']), content: z.string() }), result: z.object({ count: z.number().int(), limit: z.number().int() }) },
441
+ 'memory.write': { params: ProfileHomeParams.extend({ section: z.enum(['memory', 'user', 'soul']), base: z.string(), content: z.string() }), result: z.object({ written: z.boolean() }) },
208
442
  };
209
- export const ProcessEventSchema = z.object({ process_id: z.string(), consumed: z.boolean(), type: z.string().optional(), session_key: z.string().optional(), origin_ui_session_id: z.string().optional() }).catchall(Json);
443
+ /**
444
+ * TAL-819: one write the Agent staged for approval (`skills.write_approval` / `memory.write_approval`). `target` is the skill
445
+ * name(s), or `memory` / `user` for the memory store; `op` the staged action; `diff` the unified diff an approval would apply
446
+ * (a skill create shows the new file, a write that would fail says why); `revision` is the SHA-256 of that diff, which an approval
447
+ * must send back. `approvable` is false when no diff could be built or the Agent would refuse the write (`diff` says why;
448
+ * `revision` is empty); such a write can only be rejected.
449
+ */
450
+ export const PendingWriteSchema = z.object({
451
+ id: z.string().regex(/^[A-Za-z0-9_-]{1,64}$/), kind: z.enum(['skills', 'memory']), target: z.string(), op: z.string(), summary: z.string(),
452
+ origin: z.string(), created_at: z.number(), diff: z.string(), approvable: z.boolean(), revision: z.string(),
453
+ });
454
+ export const PendingWriteRefSchema = z.object({ id: PendingWriteSchema.shape.id, revision: z.string() });
455
+ const WriteApprovalParams = ProfileHomeParams.extend({ kind: z.enum(['skills', 'memory']), writes: z.array(PendingWriteRefSchema).min(1) });
456
+ const WriteApprovalResult = z.object({ results: z.array(z.object({ id: z.string(), done: z.boolean(), message: z.string() })) });
457
+ /**
458
+ * TAL-819: the profile's staged writes of `kind`, oldest first, and whether its gate is on; `supported` is false on an Agent
459
+ * without the review handlers. `approve` / `reject` run the Agent's `/skills` or `/memory` handler per write; `done` is true
460
+ * once the record is gone (applied or dropped) and `message` is the handler's text. An approval whose diff no longer has the
461
+ * reviewed `revision` is refused (`done: false`) without applying.
462
+ */
463
+ export const WRITE_APPROVAL_METHODS = {
464
+ 'write_approval.pending': { params: ProfileHomeParams.extend({ kind: z.enum(['skills', 'memory']) }), result: z.object({ supported: z.boolean(), enabled: z.boolean(), items: z.array(PendingWriteSchema) }) },
465
+ 'write_approval.approve': { params: WriteApprovalParams, result: WriteApprovalResult },
466
+ 'write_approval.reject': { params: WriteApprovalParams, result: WriteApprovalResult },
467
+ };
468
+ export const ProcessEventSchema = z.object({ process_id: z.string(), consumed: z.boolean(), type: z.string().optional(), session_key: z.string().optional(), origin_ui_session_id: z.string().optional(), child_process: z.boolean().optional(), reconciliation_profile_home: z.string().optional() }).catchall(Json);
210
469
  export const PROCESS_METHODS = {
211
470
  'process.drain': { params: ProfileHomeParams.extend({ max_events: z.number().int().positive().optional() }), result: z.object({ events: z.array(ProcessEventSchema) }) },
212
471
  /** TAL-533: re-adopt the base home's and every named profile's checkpointed background processes in a fresh sidecar. */
@@ -216,12 +475,13 @@ export const PROCESS_METHODS = {
216
475
  /** TAL-532: the subset the agent already holds from its own turn (consumed via wait/log, or poll-observed). */
217
476
  'process.consumed': { params: z.object({ process_ids: z.array(z.string().min(1)) }), result: z.object({ consumed: z.array(z.string()) }) },
218
477
  /** TAL-459: the Agent's durable delivery ledger. A null claim means another consumer delivered (or holds) it; "" means nothing durable to acknowledge. */
219
- 'process.claim_delivery': { params: ProfileHomeParams.extend({ event: Loose, consumer: z.string().min(1) }), result: z.object({ claim_id: z.string().nullable() }) },
478
+ 'process.claim_delivery': { params: ProfileHomeParams.extend({ event: Loose, consumer: z.string().min(1) }), result: z.object({ claim_id: z.string().nullable(), retryable: z.boolean().optional() }) },
220
479
  'process.complete_delivery': { params: ProfileHomeParams.extend({ event: Loose, claim_id: z.string() }), result: Ok },
221
480
  'process.release_delivery': { params: ProfileHomeParams.extend({ event: Loose, claim_id: z.string() }), result: Ok },
222
481
  /** Hands back a claim the busy target never admitted, without spending one of the Agent's delivery attempts. */
223
482
  'process.defer_delivery': { params: ProfileHomeParams.extend({ event: Loose, claim_id: z.string() }), result: Ok },
224
483
  'process.format_notification': { params: z.object({ event: Loose }), result: z.object({ text: z.string() }) },
484
+ 'process.stop': { params: ProfileHomeParams.extend({ session_id: z.string().min(1), process_id: z.string().min(1) }), result: z.object({ outcome: z.enum(['stopped', 'not_running', 'not_found']) }) },
225
485
  'process.list': { params: ProfileHomeParams, result: z.object({ sessions: z.array(Loose) }) },
226
486
  /** TAL-372: the Agent's view of these WebUI sessions' background work: ledger delegations (with live status) and notified processes. */
227
487
  'process.background_list': {
@@ -244,7 +504,7 @@ export const PROCESS_METHODS = {
244
504
  export const AccountUsageSnapshotSchema = z.object({ provider: z.string().nullable(), available: z.boolean(), unavailable_reason: z.string().nullable().optional(), windows: z.array(Loose), details: z.array(Json) }).catchall(Json);
245
505
  /**
246
506
  * TAL-548: one credential-pool entry's local state (no network, no secrets); the server derives a quota source per entry.
247
- * `matches_api_key`: the entry's key is the server-resolved `api_key` passed to `usage.pool`.
507
+ * `matches_api_key`: the entry's key is the configured key, passed as `api_key` or resolved from `env_vars` in the profile's secret scope.
248
508
  */
249
509
  export const PoolEntrySchema = z.object({
250
510
  credential_id: z.string(), label: z.string(), status: z.enum(['available', 'exhausted', 'dead']), unavailable_reason: z.string().nullable(), retry_after: z.string().nullable(),
@@ -252,7 +512,7 @@ export const PoolEntrySchema = z.object({
252
512
  });
253
513
  const BalanceNumber = z.number().nullable();
254
514
  /**
255
- * TAL-548: one key-based balance read. The key is the pool entry's (`credential_id`) or the server-resolved `api_key`.
515
+ * TAL-548: one key-based balance read. The key is the pool entry's (`credential_id`), a raw `api_key`, or resolved from `env_vars` in the profile's secret scope.
256
516
  * `ok` carries the provider's sanitised fields: OpenRouter `quota`/`label`, DeepSeek `balances`/`is_available`, OpenCode Go
257
517
  * `windows`; `http_error` carries the status code.
258
518
  */
@@ -263,21 +523,41 @@ export const BalanceResultSchema = z.object({
263
523
  windows: z.array(z.object({ key: z.enum(['rolling', 'weekly', 'monthly']), used_percent: z.number(), reset_at: z.string(), rate_limited: z.boolean() })),
264
524
  });
265
525
  const CredentialId = z.string().min(1).nullable().optional();
526
+ /** TAL-807: the Agent's `CredentialPool.limit_state`: every live account benched until `resets_at`, else the given models no usable account serves until their ISO reset. */
527
+ export const PoolLimitSchema = z.discriminatedUnion('scope', [
528
+ z.object({ scope: z.literal('account'), resets_at: z.string() }),
529
+ z.object({ scope: z.literal('models'), models: z.record(z.string(), z.string()) }),
530
+ ]);
266
531
  export const USAGE_METHODS = {
267
532
  'usage.account': { params: ProfileHomeParams.extend({ provider: z.string().min(1), base_url: z.string().nullable().optional(), api_key: z.string().nullable().optional(), credential_id: CredentialId }), result: z.object({ snapshot: AccountUsageSnapshotSchema.nullable() }) },
268
- 'usage.pool': { params: ProfileHomeParams.extend({ provider: z.string().min(1), api_key: z.string().nullable().optional() }), result: z.object({ entries: z.array(PoolEntrySchema) }) },
533
+ 'usage.pool': { params: ProfileHomeParams.extend({ provider: z.string().min(1), api_key: z.string().nullable().optional(), env_vars: z.array(z.string().min(1)).optional() }), result: z.object({ entries: z.array(PoolEntrySchema), has_api_key: z.boolean().optional() }) },
269
534
  /** TAL-548: the providers whose persisted credential pool holds an account the user added, keyed or not. */
270
- 'usage.pool_providers': { params: ProfileHomeParams, result: z.object({ providers: z.array(z.string()) }) },
271
- 'usage.balance': { params: ProfileHomeParams.extend({ provider: z.enum(['openrouter', 'deepseek', 'opencode-go']), credential_id: CredentialId, api_key: z.string().nullable().optional() }), result: BalanceResultSchema },
535
+ 'usage.pool_providers': { params: ProfileHomeParams.extend({ key_envs: z.record(z.string(), z.array(z.string().min(1))).optional() }), result: z.object({ providers: z.array(z.string()), keys: z.record(z.string(), z.object({ present: z.boolean(), revision: z.string().nullable() })).default({}) }) },
536
+ /** TAL-807: pool rate limits by provider slug, only for pooled providers that are limited; `models` are bare ids. */
537
+ 'usage.limits': { params: ProfileHomeParams.extend({ providers: z.array(z.object({ slug: z.string().min(1), models: z.array(z.string()) })) }), result: z.object({ limits: z.record(z.string(), PoolLimitSchema) }) },
538
+ 'usage.balance': { params: ProfileHomeParams.extend({ provider: z.enum(['openrouter', 'deepseek', 'opencode-go']), credential_id: CredentialId, api_key: z.string().nullable().optional(), env_vars: z.array(z.string().min(1)).optional() }), result: BalanceResultSchema },
272
539
  };
273
540
  export const CONFIG_METHODS = {
274
541
  // `config_path` is the server-resolved file (honouring HERMES_CONFIG_PATH); the sidecar reads and writes exactly that path.
275
542
  'config.get': { params: ProfileHomeParams.extend({ config_path: z.string().min(1) }), result: z.object({ path: z.string(), exists: z.boolean(), config: Loose }) },
276
543
  'config.set': { params: ProfileHomeParams.extend({ config_path: z.string().min(1), config: Loose }), result: z.object({ ok: z.literal(true), path: z.string() }) },
544
+ /** TAL-797: `.env` edits through the Agent's locked writer; `null` removes a key, an empty value is skipped; line breaks, spaces, quotes, `#` and non-ASCII are refused. */
545
+ 'config.env_write': { params: ProfileHomeParams.extend({ updates: z.record(z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/), z.string().nullable()) }), result: z.object({ ok: z.literal(true), path: z.string() }) },
277
546
  'models.reasoning_efforts': { params: ProfileHomeParams.extend({ model: z.string(), provider: z.string() }), result: z.object({ efforts: z.array(z.string()), supports_reasoning: z.boolean().nullable() }) },
547
+ /**
548
+ * TAL-901: the Agent's personalities for `config` (its built-ins, then the top-level `personalities:` block, then
549
+ * `agent.personalities`), by case-folded name in the Agent's order; `prompt` is the rendered overlay text.
550
+ */
551
+ 'personalities.list': { params: z.object({ config: Loose }), result: z.object({ personalities: z.array(z.object({ name: z.string().min(1), description: z.string(), prompt: z.string() })) }) },
278
552
  };
279
553
  export const WORKTREE_METHODS = {
280
554
  'worktree.create': { params: ProfileHomeParams.extend({ repo_root: z.string().min(1) }), result: z.object({ path: z.string(), branch: z.string(), repo_root: z.string(), base: z.string().nullable() }) },
555
+ /**
556
+ * TAL-1254: relock Talaria's worktrees for this sidecar (`hermes pid=<pid>`) so the Agent's worktree sweep reads them as
557
+ * live. `foreign_lock`: a lock that is not a `hermes pid=` one holds the tree and stays. A tree this sidecar does not
558
+ * hold yet (a running process's `hermes pid=` lock, or a git failure) is absent from `results`; the caller retries it.
559
+ */
560
+ 'worktree.retain': { params: z.object({ paths: z.array(z.string().min(1)) }), result: z.object({ results: z.record(z.string(), z.enum(['retained', 'missing', 'foreign_lock'])) }) },
281
561
  };
282
562
  /** TAL-913: a workspace's checkpoints, read and restored through the Agent's `CheckpointManager`; `store` is its shared git store. */
283
563
  const CheckpointTarget = ProfileHomeParams.extend({ workspace: z.string().min(1) });
@@ -286,7 +566,14 @@ export const CHECKPOINTS_METHODS = {
286
566
  'checkpoints.list': { params: CheckpointTarget, result: z.object({ checkpoints: z.array(AgentCheckpointSchema), store: z.string() }) },
287
567
  /** `diff` is `git diff <checkpoint> --cached` over the workspace: `a/` is the checkpoint, `b/` the files now. */
288
568
  'checkpoints.diff': { params: CheckpointTarget.extend({ checkpoint: z.string().min(1) }), result: z.object({ stat: z.string(), diff: z.string() }) },
289
- 'checkpoints.restore': { params: CheckpointTarget.extend({ checkpoint: z.string().min(1) }), result: z.object({ restored_to: z.string(), reason: z.string() }) },
569
+ /**
570
+ * Safe unless `all` (TAL-1260): only files still as the Agent last wrote them are reverted, and `kept_user_edits` names the
571
+ * hand-edited ones left in place. `restored_files` is what a safe restore acted on; null when the whole tree was checked out.
572
+ */
573
+ 'checkpoints.restore': {
574
+ params: CheckpointTarget.extend({ checkpoint: z.string().min(1), all: z.boolean().optional() }),
575
+ result: z.object({ restored_to: z.string(), reason: z.string(), restored_files: z.array(z.string()).nullable(), kept_user_edits: z.array(z.string()), skipped_oversize: z.array(z.string()), failed_deletes: z.array(z.string()) }),
576
+ },
290
577
  };
291
578
  // ── chat / approval / clarify ──────────────────────────────────────────
292
579
  export const ChatUsageSchema = z.object({ prompt_tokens: z.number().int(), completion_tokens: z.number().int(), cache_read_tokens: z.number().int(), cache_write_tokens: z.number().int(), estimated_cost_usd: z.number().nullable() });
@@ -297,10 +584,16 @@ export const ChatUsageSchema = z.object({ prompt_tokens: z.number().int(), compl
297
584
  * A completed `compressed` turn also carries `context_messages`, its `messages` with tool output pruned and capped for the model (row for row),
298
585
  * and `post_compression_context_tokens_estimate`, the next request's estimated size (TAL-539); both are null otherwise.
299
586
  */
587
+ /**
588
+ * The turn agent's context figures (`context_length`, `threshold_tokens`, `last_prompt_tokens`, `compression_count`). TAL-1005:
589
+ * `next_turn_threshold_tokens` is the compression threshold the next turn runs under, the primary runtime's when a fallback ran
590
+ * this one (the Agent restores the primary each turn); absent when the Agent reports none.
591
+ */
592
+ const ChatContextSchema = z.looseObject({ next_turn_threshold_tokens: z.number().int().positive().optional() });
300
593
  export const ChatStartResultSchema = z.object({
301
594
  status: z.enum(['completed', 'cancelled', 'error']), messages: z.array(Loose), context_messages: z.array(Loose).nullable().optional(), post_compression_context_tokens_estimate: z.number().int().nullable().optional(), final_response: z.string(), error: z.string().nullable(),
302
595
  failed: z.boolean(), partial: z.boolean(), compression_exhausted: z.boolean(),
303
- tool_limit_reached: z.boolean(), max_iterations_summary_request: z.string(), usage: ChatUsageSchema, context: Loose, model: z.string(), provider: z.string(), compressed: z.boolean(), agent_session_id: z.string(),
596
+ tool_limit_reached: z.boolean(), max_iterations_summary_request: z.string(), usage: ChatUsageSchema, context: ChatContextSchema, model: z.string(), provider: z.string(), compressed: z.boolean(), agent_session_id: z.string(),
304
597
  token_sent: z.boolean(), pending_steer: z.string(), live_tool_calls: z.array(Loose),
305
598
  });
306
599
  const Text = z.object({ text: z.string() }).catchall(Json);
@@ -309,6 +602,8 @@ const Text = z.object({ text: z.string() }).catchall(Json);
309
602
  * (scalars as sent, text and nested values as capped text) or its capped text. The server decides `is_error` from it and never forwards it; `is_error` is only an older sidecar's `false`.
310
603
  * A `todo` tool's frame also carries `todo_result`, the tool's full result text, which the server turns into `todo_state` (TAL-397) and never forwards.
311
604
  * A result with a string `diff` also carries `result_diff`, that whole diff, which the server turns into `edit_diff` (TAL-448) and never forwards.
605
+ * TAL-909: `display_metadata` is the dict the sidecar returned for the Agent to persist on the tool-result row (a file edit's unified `diff`
606
+ * from the Agent's before-snapshot); the server turns its `diff` into `edit_diff` when the result has none, and never forwards it.
312
607
  */
313
608
  const ToolFrame = z.object({ event_type: z.string(), name: z.string().nullable().optional(), preview: Json.optional(), args: Loose.optional(), tid: z.string().optional(), is_error: z.boolean().optional() }).catchall(Json);
314
609
  export const ChatStreamSchema = z.discriminatedUnion('event', [
@@ -320,15 +615,31 @@ export const ChatStreamSchema = z.discriminatedUnion('event', [
320
615
  /** TAL-397: the Agent's session counters, before the content frame that follows a change. */
321
616
  z.object({ event: z.literal('usage'), data: ChatUsageSchema }),
322
617
  z.object({ event: z.literal('tool'), data: ToolFrame }),
618
+ z.object({ event: z.literal('tool_progress'), data: z.object({ tool_call_id: z.string(), message: z.string().nullable().optional(), progress: z.number().nullable().optional(), total: z.number().nullable().optional(), percent: z.number().nullable().optional() }) }),
323
619
  z.object({ event: z.literal('tool_complete'), data: ToolFrame }),
324
620
  z.object({ event: z.literal('approval'), data: Loose }),
325
621
  z.object({ event: z.literal('clarify'), data: Loose }),
326
622
  z.object({ event: z.literal('clarify_resolved'), data: Loose }),
623
+ /** TAL-818: a `manage_connections` card opened; `tool_call_id` is the call that opened it. */
624
+ z.object({ event: z.literal('connection'), data: ConnectorOperationSnapshotSchema }),
327
625
  /** The Agent stopped waiting on an approval: `reason` is its settle cause (`resolved`, `timeout`, `interrupted`, `session_closed`). */
328
626
  z.object({ event: z.literal('approval_resolved'), data: z.looseObject({ approval_id: z.string(), session_id: z.string().optional(), reason: z.string().optional() }) }),
329
627
  z.object({ event: z.literal('compressing'), data: Loose }),
628
+ /** TAL-1006: the Agent finished the compaction the last `compressing` started; an aborted one sends none. */
629
+ z.object({ event: z.literal('compression_finished'), data: Loose }),
330
630
  z.object({ event: z.literal('warning'), data: Loose }),
331
631
  z.object({ event: z.literal('status'), data: Loose }),
632
+ /**
633
+ * TAL-803: the Agent's `AgentNotice` (`kind` `sticky` or `ttl`, `ttl_ms` only for `ttl`), the clear of one by `key`, and
634
+ * `notices_reset` before a new agent for the session is built: the notices of the one it replaces with
635
+ * `lapses_with_agent` lapse (an Agent startup warning, raised once per process, does not).
636
+ */
637
+ z.object({ event: z.literal('notice'), data: z.looseObject({ id: z.string().nullable().optional(), key: z.string().nullable().optional(), level: z.string(), kind: z.string(), ttl_ms: z.number().int().nullable().optional(), text: z.string(), lapses_with_agent: z.boolean().optional() }) }),
638
+ z.object({ event: z.literal('notice_clear'), data: z.looseObject({ key: z.string() }) }),
639
+ z.object({ event: z.literal('notices_reset'), data: Loose }),
640
+ z.object({ event: z.literal('provider_phase'), data: z.object({ phase: ProviderPhaseSchema.nullable() }) }),
641
+ /** TAL-821: the Agent's live rate-limit wait (`until`, epoch seconds: the limit's reset or the Agent's retry, as `until_kind` says); a null `until` ends it. */
642
+ z.object({ event: z.literal('rate_limit_wait'), data: z.looseObject({ provider: z.string().nullable(), until: z.number().nullable(), until_kind: z.enum(['reset', 'retry']).optional() }) }),
332
643
  ]);
333
644
  export const CHAT_METHODS = {
334
645
  'chat.start': {
@@ -343,6 +654,14 @@ export const CHAT_METHODS = {
343
654
  persist_user_message: z.string().optional(),
344
655
  /** TAL-529: a `/btw` side question, whose throwaway session the Agent keeps out of the profile's state.db. */
345
656
  ephemeral: z.boolean().optional(),
657
+ /** TAL-801: a spoken turn, which the Agent runs on the profile's `auxiliary.voice_chat` slot when it has that route. */
658
+ voice_turn: z.boolean().optional(),
659
+ /** TAL-818: attach the Agent's `connection_callback`, so `manage_connections` streams a `connection` frame and waits on the card. */
660
+ connect_card: z.boolean().optional(),
661
+ /** Bind turn-local skill capture only for a client that renders the masked prompt. */
662
+ skill_secret: z.boolean().optional(),
663
+ /** TAL-903: `workspace` is a host path (the profile's terminal runs locally), so the Agent anchors AGENTS.md discovery and its memory provider there. A remote backend's target-side path is never read on the host. */
664
+ workspace_local: z.boolean().optional(),
346
665
  }),
347
666
  result: ChatStartResultSchema,
348
667
  stream: ChatStreamSchema,
@@ -362,11 +681,11 @@ export const CHAT_METHODS = {
362
681
  /**
363
682
  * TAL-424: deliver `pending[index]` now with the Agent's `redirect`: `delivery: redirect` restarts the model request with
364
683
  * it, `delivery: steer` puts it last on the slot while tools yield. Not redirected: it stays pending, `requeued` in its
365
- * place (`kept`) or last (`last`); `withdrawn: false` when the Agent already took it.
684
+ * place (`kept`) or last (`last`); `withdrawn: false` when the Agent already took it, or `reason: not_running` when the run or Agent is absent.
366
685
  */
367
686
  'chat.steer_now': {
368
687
  params: z.object({ stream_id: z.string().optional(), session_id: z.string().optional(), pending: z.array(z.string().min(1)).min(1), index: z.number().int().nonnegative() }),
369
- result: z.object({ redirected: z.boolean(), withdrawn: z.boolean(), delivery: z.enum(['redirect', 'steer']).optional(), requeued: z.enum(['kept', 'last']).optional() }),
688
+ result: z.object({ redirected: z.boolean(), withdrawn: z.boolean(), delivery: z.enum(['redirect', 'steer']).optional(), requeued: z.enum(['kept', 'last']).optional(), reason: z.enum(['not_running']).optional() }),
370
689
  },
371
690
  /**
372
691
  * TAL-255: manual `/compress` of `conversation_history` through the Agent's `compress_now` on a throwaway agent. Nothing is
@@ -385,22 +704,57 @@ export const CHAT_METHODS = {
385
704
  commit_token: z.string().nullable(),
386
705
  }),
387
706
  },
707
+ /** TAL-1333: safe metadata from the existing matching resident Agent, never a new Agent. */
708
+ 'chat.context_detail': { params: ProfileHomeParams.extend({ session_id: z.string().min(1), model: z.string().nullable(), model_provider: z.string().nullable(), workspace: z.string().nullable() }), result: ContextDetailSchema },
388
709
  /** TAL-255: second phase of `chat.compress`; `finalized: false` when the token is unknown or already expired. */
389
710
  'chat.compress_finalize': { params: z.object({ commit_token: z.string().min(1), committed: z.boolean() }), result: z.object({ finalized: z.boolean() }) },
711
+ /** Archive only emits on_session_delete for the committed local transition; it never closes the Agent session. */
712
+ 'chat.finalize_session': { params: ProfileHomeParams.extend({ session_id: z.string().min(1), reason: z.enum(['clear', 'delete', 'archive']), lineage: z.array(z.string().min(1)).max(10_000).optional() }), result: z.object({ messages: z.array(z.string()) }) },
390
713
  'chat.evict_agent': { params: z.object({ session_id: z.string().min(1), clear_session: z.boolean().optional() }), result: z.object({ evicted: z.boolean() }) },
391
714
  'chat.commit_memory': { params: ProfileHomeParams.extend({ session_id: z.string().min(1) }), result: z.object({ committed: z.boolean() }) },
715
+ /**
716
+ * TAL-902: `/refine [focus]`, the Agent's on-demand memory/skill review of `messages` (the session's model context) on the
717
+ * session's cached agent, else one built from the turn parameters and cached like a turn's. `output` is the Agent's
718
+ * wording: the review started, or why not (a turn is running, the conversation is empty).
719
+ */
720
+ 'chat.refine': {
721
+ params: ProfileHomeParams.extend({
722
+ session_id: z.string().min(1), messages: z.array(Loose), focus: z.string(), model: z.string(), model_provider: z.string().nullable().optional(),
723
+ enabled_toolsets: z.array(z.string()).nullable().optional(), max_iterations: z.number().int().nullable().optional(), max_tokens: z.number().int().nullable().optional(),
724
+ reasoning_config: z.object({ enabled: z.boolean(), effort: z.string().optional() }).nullable().optional(),
725
+ /** TAL-903: the session's workspace, as `chat.start` sends it, so the agent built here is the one its next turn reuses. */
726
+ workspace: z.string().optional(), workspace_local: z.boolean().optional(),
727
+ }),
728
+ result: z.object({ output: z.string() }),
729
+ },
392
730
  'approval.respond': { params: ProfileHomeParams.extend({ session_id: z.string().min(1), choice: z.enum(['once', 'session', 'always', 'deny']), request_id: z.string().nullable().optional() }), result: z.object({ ok: z.boolean(), resolved: z.number().int(), choice: z.string() }) },
393
731
  'approval.pending': { params: z.object({ session_id: z.string().min(1) }), result: z.object({ pending: z.array(Loose) }) },
394
732
  'approval.set_yolo': { params: z.object({ session_id: z.string().min(1), enabled: z.boolean() }), result: z.object({ yolo_enabled: z.boolean(), released: z.number().int() }) },
395
- 'clarify.respond': { params: z.object({ stream_id: z.string().optional(), session_id: z.string().optional(), clarify_id: z.string().optional(), response: z.string().min(1) }), result: z.object({ ok: z.boolean(), clarify_id: z.string().optional() }) },
733
+ 'clarify.respond': { params: z.object({ stream_id: z.string().optional(), session_id: z.string().optional(), clarify_id: z.string().optional(), response: z.string() }), result: z.object({ ok: z.boolean(), clarify_id: z.string().optional() }) },
396
734
  };
397
- const GatewayActionParams = ProfileHomeParams.extend({ cli_profile: z.string().nullable().optional(), quick_timeout_seconds: z.number().optional(), background_wait_seconds: z.number().optional() });
398
- /** A lifecycle run of `hermes gateway <action>`. `busy` means another action holds the lock; `timed_out` marks a child killed after `background_wait_seconds`. `detail` is CLI output, never shown to a browser. */
399
- const GatewayActionResult = z.object({ status: z.enum(['completed', 'failed', 'busy']), message: z.string(), detail: z.string().optional(), returncode: z.number().int().optional(), timed_out: z.boolean().optional() });
735
+ const GatewayActionParams = ProfileHomeParams.extend({ interrupt: z.boolean().optional(), cli_profile: z.string().nullable().optional(), quick_timeout_seconds: z.number().optional(), background_wait_seconds: z.number().optional() });
736
+ /**
737
+ * A lifecycle run of `hermes --profile <name> gateway <action>` for the profile at `profile_home` (TAL-894). `busy` means another
738
+ * action holds the lock; `busy_live_work` is an Agent live-work refusal. `interrupt` explicitly consents to interruption.
739
+ * `timed_out` marks a child killed after `background_wait_seconds` (a restart defaults to the Agent CLI's own
740
+ * restart budget plus 30 s) or cancelled. `detail` is CLI output, never shown to a browser; `reason` is the Agent's first error line of a
741
+ * failed run (TAL-884), the one line the server shows.
742
+ */
743
+ const GatewayActionResult = z.object({ status: z.enum(['completed', 'failed', 'busy', 'busy_live_work']), message: z.string(), detail: z.string().optional(), returncode: z.number().int().optional(), reason: z.string().optional(), timed_out: z.boolean().optional() });
400
744
  const GatewayActionStream = z.discriminatedUnion('event', [z.object({ event: z.literal('progress'), data: z.object({ phase: z.enum(['started', 'draining']) }) })]);
401
745
  export const GATEWAY_ACTIONS = ['start', 'stop', 'restart'];
402
746
  export const GATEWAY_METHODS = {
747
+ 'gateway.session': { params: GatewaySessionParamsSchema, result: GatewaySessionResultSchema, stream: z.discriminatedUnion('event', [
748
+ z.object({ event: z.literal('snapshot'), data: GatewaySessionResultSchema.extend({ replaying: z.boolean().optional() }) }),
749
+ z.object({ event: z.literal('event'), data: Loose }),
750
+ z.object({ event: z.literal('replay_complete'), data: z.object({}) }),
751
+ z.object({ event: z.literal('clarify_batch'), data: GatewayClarifyBatchSchema }),
752
+ z.object({ event: z.literal('restarting'), data: z.object({ restarting: z.literal(true) }) }),
753
+ ]) },
754
+ 'gateway.create': { params: GatewayCreateParamsSchema, result: GatewaySessionResultSchema },
403
755
  'gateway.control': { params: GatewayActionParams.extend({ action: z.enum(GATEWAY_ACTIONS) }), result: GatewayActionResult, stream: GatewayActionStream },
404
756
  'gateway.restart': { params: GatewayActionParams, result: GatewayActionResult, stream: GatewayActionStream },
757
+ /** TAL-805: the gateway mode of the profile `name` at `profile_home`; the root profile (`is_default`) is the host itself. */
758
+ 'gateway.mode': { params: ProfileHomeParams.extend({ name: z.string().min(1), is_default: z.boolean() }), result: z.object({ gateway_mode: GatewayModeSchema }) },
405
759
  };
406
760
  //# sourceMappingURL=namespaces.js.map