@maudecode/talaria-web-contracts 1.18.0 → 1.19.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 (82) hide show
  1. package/dist/gateway-session.d.ts +229 -0
  2. package/dist/gateway-session.js +46 -0
  3. package/dist/gateway-session.js.map +1 -0
  4. package/dist/index.d.ts +3 -0
  5. package/dist/index.js +3 -0
  6. package/dist/index.js.map +1 -1
  7. package/dist/openapi.js +4 -0
  8. package/dist/openapi.js.map +1 -1
  9. package/dist/router.d.ts +4265 -220
  10. package/dist/routes/automation.d.ts +104 -24
  11. package/dist/routes/automation.js +6 -3
  12. package/dist/routes/automation.js.map +1 -1
  13. package/dist/routes/chat.d.ts +1338 -43
  14. package/dist/routes/chat.js +5 -1
  15. package/dist/routes/chat.js.map +1 -1
  16. package/dist/routes/core.d.ts +17 -6
  17. package/dist/routes/core.js +2 -2
  18. package/dist/routes/core.js.map +1 -1
  19. package/dist/routes/raw.js +1 -1
  20. package/dist/routes/raw.js.map +1 -1
  21. package/dist/routes/sessions.d.ts +2367 -156
  22. package/dist/routes/sessions.js +18 -7
  23. package/dist/routes/sessions.js.map +1 -1
  24. package/dist/routes/settings.d.ts +427 -9
  25. package/dist/routes/settings.js +2 -2
  26. package/dist/routes/settings.js.map +1 -1
  27. package/dist/routes/tools.d.ts +645 -18
  28. package/dist/routes/tools.js +65 -8
  29. package/dist/routes/tools.js.map +1 -1
  30. package/dist/run-status.d.ts +39 -0
  31. package/dist/run-status.js +13 -0
  32. package/dist/run-status.js.map +1 -0
  33. package/dist/sidecar/methods.d.ts +1074 -17
  34. package/dist/sidecar/methods.js +9 -2
  35. package/dist/sidecar/methods.js.map +1 -1
  36. package/dist/sidecar/namespaces.d.ts +1469 -33
  37. package/dist/sidecar/namespaces.js +298 -31
  38. package/dist/sidecar/namespaces.js.map +1 -1
  39. package/dist/sidecar/runtime.d.ts +61 -0
  40. package/dist/sidecar/runtime.js +20 -0
  41. package/dist/sidecar/runtime.js.map +1 -1
  42. package/dist/sidecar/version.d.ts +1 -1
  43. package/dist/sidecar/version.js +1 -1
  44. package/dist/speed.d.ts +14 -0
  45. package/dist/speed.js +6 -0
  46. package/dist/speed.js.map +1 -0
  47. package/dist/sse.d.ts +797 -5
  48. package/dist/sse.js +24 -7
  49. package/dist/sse.js.map +1 -1
  50. package/dist/transcribe-stream.d.ts +34 -0
  51. package/dist/transcribe-stream.js +23 -0
  52. package/dist/transcribe-stream.js.map +1 -0
  53. package/dist/views.d.ts +2097 -127
  54. package/dist/views.js +273 -47
  55. package/dist/views.js.map +1 -1
  56. package/fixtures/sidecar/aux.json +134 -0
  57. package/fixtures/sidecar/chat.json +14 -0
  58. package/fixtures/sidecar/checkpoints.json +6 -6
  59. package/fixtures/sidecar/commands.json +216 -108
  60. package/fixtures/sidecar/config.json +15 -0
  61. package/fixtures/sidecar/connectors.json +218 -0
  62. package/fixtures/sidecar/cron.json +1001 -31
  63. package/fixtures/sidecar/gateway.json +39 -0
  64. package/fixtures/sidecar/goals.json +5 -3
  65. package/fixtures/sidecar/kanban.json +139 -49
  66. package/fixtures/sidecar/memory.json +50 -0
  67. package/fixtures/sidecar/models.json +122 -0
  68. package/fixtures/sidecar/personalities.json +92 -0
  69. package/fixtures/sidecar/plugins.json +116 -27
  70. package/fixtures/sidecar/process.json +31 -0
  71. package/fixtures/sidecar/profiles.json +2 -0
  72. package/fixtures/sidecar/providers.json +14 -1
  73. package/fixtures/sidecar/rpc.json +35 -1
  74. package/fixtures/sidecar/runtime.json +130 -14
  75. package/fixtures/sidecar/skills.json +256 -13
  76. package/fixtures/sidecar/state_db.json +19 -2
  77. package/fixtures/sidecar/stt.json +62 -1
  78. package/fixtures/sidecar/text.json +20 -2
  79. package/fixtures/sidecar/toolsets.json +468 -0
  80. package/fixtures/sidecar/usage.json +22 -2
  81. package/fixtures/sidecar/write_approval.json +98 -0
  82. package/package.json +1 -1
@@ -1,17 +1,35 @@
1
1
  import { z } from 'zod';
2
+ import { ProviderPhaseSchema } from '../run-status.js';
3
+ import { SpeedTierSchema } from '../speed.js';
4
+ import { GatewaySessionParamsSchema, GatewaySessionResultSchema } from '../gateway-session.js';
5
+ /** Warnings and wording come from the Agent's selection guard registry, including future guard kinds. */
6
+ export const ModelSelectionWarningSchema = z.object({ kind: z.string(), title: z.string(), model: z.string(), provider: z.string(), message: z.string() });
2
7
  /** Shared param shapes. */
3
8
  export const ProfileHomeParams = z.object({ profile_home: z.string().min(1) });
4
9
  export const BaseHomeParams = z.object({ base_home: z.string().min(1) });
5
10
  const Ok = z.object({ ok: z.boolean() });
6
11
  const Json = z.unknown();
7
12
  const Loose = z.record(z.string(), z.unknown());
13
+ /** Profile-scoped toolset choices; clients render policy and selection without deriving them. */
14
+ export const ToolsetsCatalogSchema = z.object({
15
+ toolsets: z.array(z.object({
16
+ name: z.string(), description: z.string(), available: z.boolean(), selected: z.boolean(),
17
+ unavailable_reason: z.enum(['disabled', 'unavailable']).nullable(),
18
+ })),
19
+ selected_toolsets: z.array(z.string()), using_defaults: z.boolean(),
20
+ });
21
+ export const TOOLSETS_METHODS = {
22
+ 'toolsets.catalog': { params: ProfileHomeParams.extend({ enabled_toolsets: z.array(z.string()).nullable() }), result: ToolsetsCatalogSchema },
23
+ };
8
24
  // ── goals ──────────────────────────────────────────────────────────────
9
25
  export const GoalStateSchema = z.object({
10
26
  goal: z.string(), status: z.string(), turns_used: z.number().int(), max_turns: z.number().int(),
11
27
  last_verdict: z.string().nullable(), last_reason: z.string().nullable(), paused_reason: z.string().nullable(),
12
28
  });
13
29
  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() });
30
+ /** TAL-808: `parked` is an active goal's live wait barrier: its reason and, for a timed wait, the Unix-seconds deadline. */
31
+ export const GoalParkedSchema = z.object({ reason: z.string().nullable(), until: z.number().nullable() });
32
+ 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
33
  export const GoalCommandResultSchema = z.object({
16
34
  ok: z.boolean(), action: z.string(), message: z.string(), goal: GoalStateSchema.nullable(), error: z.string().optional(), kickoff_prompt: z.string().optional(),
17
35
  message_key: z.string().optional(), message_args: z.array(Json).optional(),
@@ -22,10 +40,11 @@ export const GoalDecisionSchema = z.object({
22
40
  });
23
41
  export const GOALS_METHODS = {
24
42
  'goals.get': { params: GoalSession, result: GoalStatusSchema },
25
- 'goals.command': { params: GoalSession.extend({ args: z.string(), stream_running: z.boolean().optional() }), result: GoalCommandResultSchema },
43
+ // `workspace`: the session's declared workspace, where the Agent runs goal gates (TAL-882).
44
+ 'goals.command': { params: GoalSession.extend({ args: z.string(), stream_running: z.boolean().optional(), workspace: z.string().optional() }), result: GoalCommandResultSchema },
26
45
  'goals.snapshot': { params: GoalSession, result: z.object({ goal: GoalStateSchema.nullable(), snapshot: z.string().nullable() }) },
27
46
  '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 },
47
+ 'goals.evaluate': { params: GoalSession.extend({ last_response: z.string(), user_initiated: z.boolean().optional(), workspace: z.string().optional() }), result: GoalDecisionSchema },
29
48
  };
30
49
  // ── commands / plugins ─────────────────────────────────────────────────
31
50
  export const CommandSchema = z.object({
@@ -33,12 +52,25 @@ export const CommandSchema = z.object({
33
52
  cli_only: z.boolean(), gateway_only: z.boolean(),
34
53
  /** `commands.exec` runs it: the sidecar's runtime commands and every plugin command (TAL-561). */
35
54
  exec: z.boolean(),
55
+ /**
56
+ * An Agent prompt built-in, which every Agent surface answers by sending its builder's prompt as the turn (TAL-817):
57
+ * `expand` when `commands.expand_prompt` can build it on this Agent, `unsupported` when it cannot; null for any other command.
58
+ */
59
+ prompt: z.enum(['expand', 'unsupported']).nullable(),
36
60
  });
37
61
  /**
38
62
  * A model-provider plugin installed and enabled in the profile (TAL-288); bundled providers are built-ins. `setup` is the Agent's
39
63
  * own verdict without spawning the plugin's CLI: `not_loaded` means the installed plugin could not be loaded (import failure, or its
40
64
  * provider id or directory name is taken by another profile's plugin in this process-wide registry).
41
65
  */
66
+ /**
67
+ * TAL-806: one field of a plugin manifest's `config_schema` (Agent `plugin_settings_fields`) with its current value from
68
+ * `plugins.entries.<key>.settings`, else `default`. A `secret` field carries its `.env` name and `has_value`, never a value.
69
+ */
70
+ export const PluginSettingFieldSchema = z.object({
71
+ key: z.string(), type: z.enum(['string', 'number', 'boolean', 'enum', 'json', 'secret']), label: z.string(), description: z.string(), required: z.boolean(),
72
+ choices: z.array(z.string()).optional(), default: Json.optional(), value: Json.optional(), env: z.string().optional(), has_value: z.boolean().optional(),
73
+ });
42
74
  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']) });
43
75
  /** A skill bundle: `/<name>` loads its `skill_count` skills in one message (TAL-265). */
44
76
  export const CommandBundleSchema = z.object({ name: z.string(), description: z.string(), skill_count: z.number().int(), source: z.literal('bundle') });
@@ -46,22 +78,45 @@ export const CommandBundleSchema = z.object({ name: z.string(), description: z.s
46
78
  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
79
  export const COMMANDS_METHODS = {
48
80
  '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']) }) },
81
+ /** With `session_id`, a plugin command runs under that chat's session binding and `workspace` (TAL-904). */
82
+ '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']) }) },
83
+ /**
84
+ * `/<prompt built-in> [args]` as the turn text (`message`) the Agent's builder writes for the session's profile, workspace and toolsets.
85
+ * `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.
86
+ * A failing builder errors `prompt_expand_failed`.
87
+ */
88
+ 'commands.expand_prompt': { params: ProfileHomeParams.extend({ command: z.string().min(1), workspace: z.string(), enabled_toolsets: z.array(z.string()).nullable() }), result: z.object({ name: z.string().nullable(), message: z.string().nullable() }) },
50
89
  'commands.bundles': { params: ProfileHomeParams, result: z.object({ bundles: z.array(CommandBundleSchema) }) },
51
90
  'commands.bundle_resolve': { params: ProfileHomeParams.extend({ command: z.string().min(1) }), result: CommandBundleInvocationSchema },
52
91
  'commands.moa_preset': { params: ProfileHomeParams.extend({ preset: z.string().nullable().optional() }), result: z.object({ moa: Loose }) },
53
92
  'plugins.providers': { params: ProfileHomeParams, result: z.object({ providers: z.array(PluginProviderSchema) }) },
54
93
  '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()) }) },
94
+ /** TAL-820: each profile's `plugins.isolation` and installed third-party plugin count, read without loading a plugin. */
95
+ '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() })) }) },
96
+ /** TAL-806: plugins whose manifest declares settings; `supported` is false on an Agent without `hermes_cli.plugins_settings`. */
97
+ 'plugins.settings': { params: ProfileHomeParams, result: z.object({ supported: z.boolean(), plugins: z.array(z.object({ key: z.string(), fields: z.array(PluginSettingFieldSchema) })) }) },
98
+ /**
99
+ * TAL-806: write non-secret `values` through the Agent's locked plugin-state writer and return the refreshed fields.
100
+ * An undeclared key, a secret or a type mismatch is invalid params; a managed install or key errors `plugin_settings_managed`.
101
+ * `secret_envs` (secret key → `.env` name the caller wrote) is checked against the current manifest before anything is
102
+ * written; a key that is no longer a secret stored under that name errors `plugin_settings_changed`.
103
+ */
104
+ '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) }) },
55
105
  };
56
106
  // ── kanban ─────────────────────────────────────────────────────────────
57
107
  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);
108
+ /** `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).
109
+ * `agent_move_targets`: the Agent workflow's manual moves from the task's status, `null` before Agent 0.21.6 (TAL-796). */
110
+ 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
111
  const TaskEnvelope = z.object({ task: KanbanTaskSchema, read_only: z.boolean() });
112
+ /** 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). */
113
+ 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() })) };
114
+ export const KanbanRunStartShape = { current_run_started_at: z.number().nullable() };
61
115
  export const KanbanBoardSchema = z.union([
62
116
  z.object({ changed: z.literal(false), latest_event_id: z.number().int(), read_only: z.boolean() }),
63
117
  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()),
118
+ /** The Agent workflow's columns in board order (TAL-796): `name` is the status key, `label` the Agent's English label. */
119
+ 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
120
  latest_event_id: z.number().int(), read_only: z.boolean(),
66
121
  filters: z.object({ tenant: z.string().nullable(), assignee: z.string().nullable(), include_archived: z.boolean(), only_mine: z.boolean(), profile: z.string().nullable() }),
67
122
  }),
@@ -76,7 +131,7 @@ export const KANBAN_METHODS = {
76
131
  'kanban.update_board': { params: ProfileHomeParams.extend({ slug: z.string(), board_spec: Loose }), result: z.object({ board: KanbanBoardMetaSchema, read_only: z.boolean() }) },
77
132
  '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
133
  '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() }) },
134
+ '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
135
  'kanban.create_task': { params: Board.extend({ task: Loose }), result: TaskEnvelope },
81
136
  'kanban.patch_task': { params: Board.extend({ task_id: z.string(), patch: Loose }), result: TaskEnvelope },
82
137
  '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 +141,46 @@ export const KANBAN_METHODS = {
86
141
  'kanban.normalize_board': { params: Board, result: z.object({ board: z.string() }) },
87
142
  '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
143
  '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 },
144
+ /** `column_order`: the Agent workflow's column keys in board order (TAL-796). */
145
+ 'kanban.stats': { params: Board, result: z.object({ column_order: z.array(z.string()) }).catchall(Json) },
90
146
  'kanban.assignees': { params: Board, result: z.object({ assignees: z.array(KanbanAssigneeSchema) }) },
91
147
  '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
148
  '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
149
  'kanban.dispatch': { params: Board.extend({ dry_run: z.boolean().optional(), max: z.number().int().optional() }), result: Loose },
94
150
  };
95
151
  // ── state_db ───────────────────────────────────────────────────────────
152
+ /** Busy Agent work, or retained canonical history that this Agent cannot safely retire. */
153
+ export const SessionDeleteRefusedSchema = z.enum(['active_turn', 'runtime_history_unsupported']);
96
154
  const Session = ProfileHomeParams.extend({ session_id: z.string().min(1) });
97
155
  export const STATE_DB_METHODS = {
98
156
  'state_db.sync_start': { params: Session.extend({ model: z.string().nullable().optional() }), result: Ok },
99
157
  '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
158
  /** TAL-574: `manual` writes a user-requested title (rename or regenerate) with `user` provenance; otherwise it is an `llm` auto title. */
101
159
  '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 },
160
+ /**
161
+ * TAL-792: `include_compression_lineage` also removes the session's compression continuations, resolved and removed in the
162
+ * same transaction (tip first). `refused` when another Agent process holds a live turn lease or compression lock on any
163
+ * 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,
164
+ * including continuations/delegates; it remains present when only post-commit artifact cleanup failed.
165
+ */
166
+ '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
167
  /** TAL-258: append one row through the Agent's `SessionDB.append_message` (bumps `message_count`); `ok` is false without a state.db. */
104
168
  '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 },
105
169
  };
106
170
  // ── profiles ───────────────────────────────────────────────────────────
107
171
  /** TAL-213: the profile's canonical Bot Chat (its session titled exactly "Bot Chat") and that chat's live compression tip. */
108
172
  export const CanonicalBotChatSchema = z.object({ session_id: z.string(), tip_session_id: z.string() });
173
+ /**
174
+ * TAL-805: a profile's gateway under the host multiplexer. `own` runs its own gateway process, `served` is served by the
175
+ * live host gateway, `parked` was stopped through the host (`hermes -p X gateway stop`) and nothing runs, `standalone` opted out of the host
176
+ * and is not running, `stopped` is none of these. Only `own` and `served` are running; Agents without the multiplexer
177
+ * helpers report `own` or `stopped`.
178
+ */
179
+ export const GatewayModeSchema = z.enum(['own', 'served', 'parked', 'standalone', 'stopped']);
109
180
  export const ProfileRowSchema = z.object({
110
181
  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
182
  visible: z.boolean(), skill_count: z.number().int(), enabled_skills: z.number().int(), total_skills: z.number().int(),
183
+ gateway_mode: GatewayModeSchema.default('stopped'),
112
184
  // Absent reads as no identity and no chat, so hand-built rows (test fakes) need not spell them out.
113
185
  display_name: z.string().default(''), description: z.string().default(''), has_avatar: z.boolean().default(false),
114
186
  canonical_session: CanonicalBotChatSchema.nullable().default(null),
@@ -121,15 +193,19 @@ export const PROFILES_METHODS = {
121
193
  'profiles.skills_stats': { params: ProfileHomeParams, result: z.object({ enabled: z.number().int(), total: z.number().int() }) },
122
194
  };
123
195
  // ── skills ─────────────────────────────────────────────────────────────
124
- export const SkillRowSchema = z.object({ name: z.string(), description: z.string(), category: z.string().nullable(), disabled: z.boolean() });
196
+ export const SkillCatalogIdentitySchema = z.object({
197
+ load_name: z.string().optional(), declared_name: z.string().optional(),
198
+ status: z.enum(['unique', 'ambiguous', 'shadowed']).optional(), tier: z.number().int().optional(),
199
+ });
200
+ export const SkillRowSchema = SkillCatalogIdentitySchema.extend({ name: z.string(), description: z.string(), category: z.string().nullable(), disabled: z.boolean() });
125
201
  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())) }),
202
+ 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
203
  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
204
  ]);
129
205
  export const SKILLS_METHODS = {
130
206
  '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
207
  '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() }) },
208
+ '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
209
  };
134
210
  // ── mcp ────────────────────────────────────────────────────────────────
135
211
  export const MCP_METHODS = {
@@ -137,18 +213,90 @@ export const MCP_METHODS = {
137
213
  'mcp.registry_tools': { params: ProfileHomeParams, result: z.object({ tools: z.array(z.object({ name: z.string(), server: z.string(), schema: Loose })) }) },
138
214
  'mcp.reload': { params: ProfileHomeParams, result: z.object({ output: z.string() }) },
139
215
  };
216
+ // ── connectors (TAL-818) ───────────────────────────────────────────────
217
+ const ConnectorSlug = z.string().regex(/^[a-z0-9][a-z0-9_-]*$/);
218
+ /** 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. */
219
+ export const ConnectorPolicyBodySchema = z.object({
220
+ mode: z.enum(['unrestricted', 'deny-all', 'allow', 'deny']),
221
+ connectors: z.array(z.string()).optional(), disabled_connectors: z.array(z.string()).optional(),
222
+ tools: z.record(z.string(), z.object({ disable: z.array(z.string()) })).optional(),
223
+ tags: z.object({ enable: z.array(z.string()).optional(), disable: z.array(z.string()).optional() }).optional(),
224
+ });
225
+ /** A connection operation's target (`tools/connectors/operation.py`): a hosted app account, or an MCP server the card approves. */
226
+ export const ConnectorOperationTargetSchema = z.object({
227
+ name: z.string(), kind: z.string(), state: z.enum(['pending', 'initiated', 'connected', 'skipped', 'failed', 'expired', 'not_connected']), detail: z.string(),
228
+ connect_url: z.string().nullable().optional(),
229
+ required_env: z.array(z.object({ name: z.string(), prompt: z.string(), required: z.boolean(), secret: z.boolean() })),
230
+ });
231
+ /** `owner`: `account` for the Connectors page's connect, `session` for a chat card, which `tool_call_id` opened. */
232
+ 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) });
233
+ const ConnectorOp = ProfileHomeParams.extend({ op_id: z.string().min(1) });
234
+ /**
235
+ * Failures carry `data.condition`: `connectors_unavailable`, `connectors_sign_in`, `connectors_forbidden`, `connectors_org_required`,
236
+ * `connector_not_found`, `connection_not_found`, `policy_conflict` (the member rules changed since `expected_revision`), `policy_invalid`,
237
+ * `operation_not_found`, `operation_settled`, `operation_refused`, `connectors_failed`.
238
+ */
239
+ export const CONNECTORS_METHODS = {
240
+ /** The Agent's `connectors_available()`; never creates an identity. */
241
+ 'connectors.available': { params: ProfileHomeParams, result: z.object({ available: z.boolean() }) },
242
+ '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() })) }) },
243
+ '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() })) }) },
244
+ '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') }) },
245
+ '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() })) }) },
246
+ '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() }) }) },
247
+ /** One member-rule change: `enabled` turns the app on or off; `tool` with `tool_enabled` turns one of its tools on or off. */
248
+ '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() }) },
249
+ /** Starts (or rejoins) the account-owned connect and waits for its authorization link. */
250
+ 'connectors.connect': { params: ProfileHomeParams.extend({ connector: ConnectorSlug }), result: ConnectorOperationSnapshotSchema },
251
+ 'connectors.operation': { params: ConnectorOp, result: ConnectorOperationSnapshotSchema },
252
+ '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 },
253
+ };
140
254
  // ── stt ────────────────────────────────────────────────────────────────
141
255
  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() }) },
256
+ /**
257
+ * `streaming` (TAL-802): `stt.streaming` is on and the provider has a live wire with credentials, so `stt.stream` would open.
258
+ * `warm_supported` (TAL-916): the available provider is the local engine and the Agent can pre-load it (`tools.stt_lease`),
259
+ * so `stt.warm` does real work.
260
+ */
261
+ 'stt.capability': { params: ProfileHomeParams, result: z.object({ available: z.boolean(), provider: z.string(), streaming: z.boolean(), warm_supported: z.boolean() }) },
262
+ /**
263
+ * TAL-916: pre-load the profile's local engine (first-use download and model load) so the next `stt.transcribe` starts
264
+ * hot. Blocks until loaded; a no-op (`warmed: false`) for remote providers, and `action: 'unsupported'` on an Agent
265
+ * without the warmer.
266
+ */
267
+ 'stt.warm': { params: ProfileHomeParams, result: z.object({ provider: z.string(), warmed: z.boolean(), action: z.string(), error: z.string().optional() }) },
268
+ 'stt.transcribe': { params: ProfileHomeParams.extend({ audio_b64: z.string().min(1), suffix: z.string().optional() }), result: z.object({ transcript: z.string(), filtered: z.boolean() }) },
269
+ /**
270
+ * TAL-802: one live dictation. Opens the profile's streaming session for s16le mono PCM at `sample_rate` (the sidecar
271
+ * resamples), emits `ready` with the `stream_id` that `stt.stream_audio` feeds, then `partial` text as the provider
272
+ * recognizes it, and returns the session's final transcript once audio ends. Cancelling the call cancels the session.
273
+ */
274
+ 'stt.stream': {
275
+ params: ProfileHomeParams.extend({ sample_rate: z.number().int().min(8000).max(48000) }),
276
+ result: z.object({ transcript: z.string(), filtered: z.boolean(), provider: z.string() }),
277
+ stream: z.discriminatedUnion('event', [
278
+ z.object({ event: z.literal('ready'), data: z.object({ stream_id: z.string() }) }),
279
+ z.object({ event: z.literal('partial'), data: z.object({ text: z.string() }) }),
280
+ ]),
281
+ },
282
+ /** TAL-802: feed one PCM chunk to an open `stt.stream`, in order; `end` marks the end of speech. */
283
+ '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
284
  };
145
285
  // ── 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);
286
+ /** `running_elsewhere`: a live execution owner. `recovery_only`: a retained snapshot, not a source for new task context. */
287
+ 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
288
  const CronJob = ProfileHomeParams.extend({ job_id: z.string().min(1) });
148
289
  const JobEnvelope = z.object({ job: CronJobSchema });
149
290
  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() });
291
+ 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() });
292
+ 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) })) });
293
+ export const CronBlueprintFillInputSchema = z.object({ key: z.string().min(1), values: z.record(z.string(), z.string()) });
294
+ 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
295
  export const CRON_METHODS = {
151
- 'cron.list': { params: ProfileHomeParams, result: z.object({ jobs: z.array(CronJobSchema) }) },
296
+ 'cron.blueprints': { params: ProfileHomeParams, result: CronBlueprintsSchema },
297
+ 'cron.blueprint_fill': { params: CronBlueprintFillInputSchema.extend({ profile_home: z.string().optional() }), result: CronBlueprintFillSchema },
298
+ /** `probe_store` adds the store's write probe (TAL-810): null when writes land or the Agent predates the probe. */
299
+ '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
300
  'cron.get': { params: CronJob, result: z.object({ job: CronJobSchema.nullable() }) },
153
301
  'cron.create': { params: ProfileHomeParams.extend({ job: Loose, execution_home: z.string().nullable().optional() }), result: JobEnvelope },
154
302
  'cron.update': { params: CronJob.extend({ updates: Loose }), result: JobEnvelope },
@@ -167,12 +315,20 @@ export const CRON_METHODS = {
167
315
  'cron.delivery_options': { params: z.object({}), result: z.object({ platforms: z.array(z.object({ value: z.string(), label: z.string() })) }) },
168
316
  };
169
317
  // ── providers / models ─────────────────────────────────────────────────
318
+ /**
319
+ * `providers.model_ids` `fallback` (TAL-799): the Agent served its curated list because the live catalog was unreachable.
320
+ * `providers.local_models` (TAL-798): the GGUFs staged for the Agent's managed llama.cpp provider `provider_id`, and the
321
+ * running server's base URL (`current_endpoint`, null while it is off).
322
+ */
170
323
  export const PROVIDERS_METHODS = {
171
324
  '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
325
  '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()) }) },
326
+ '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) }) },
327
+ 'providers.local_models': { params: ProfileHomeParams, result: z.object({ provider_id: z.string(), models: z.array(z.string()), current_endpoint: z.string().nullable() }) },
174
328
  '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
329
  'providers.credential_pool': { params: ProfileHomeParams.extend({ provider: z.string().min(1) }), result: z.object({ available: z.boolean(), strategy: z.string(), entries: z.array(Loose) }) },
330
+ '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() }) },
331
+ '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
332
  '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
333
  'models.estimate_tokens': { params: z.object({ messages: z.array(Loose) }), result: z.object({ tokens: z.number().int() }) },
178
334
  'models.capabilities': { params: ProfileHomeParams.extend({ provider: z.string(), model: z.string() }), result: z.object({ capabilities: Loose.nullable() }) },
@@ -200,13 +356,55 @@ export const AUX_METHODS = {
200
356
  * of the task's auxiliary route; `credential_missing` means it has no credential. `finish_reason` is the provider's, when known.
201
357
  */
202
358
  '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() }) })]) },
359
+ /**
360
+ * TAL-800: the profile's auxiliary slots in Agent order (built-ins, then the profile's plugin tasks). `reasoning_effort`
361
+ * is the slot's resolved effort (`''` = provider default); `effective` is the route an `inherit_from` task resolves to now.
362
+ * `reserved`: the Agent's other `auxiliary.*` keys (settings and unlisted routes); a reset never touches them.
363
+ */
364
+ '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
365
  '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
366
  };
205
367
  export const TEXT_METHODS = {
206
368
  '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
369
  '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]) }) },
370
+ /** TAL-906: the Agent's read-aloud script (`prepare_spoken_text`); `text` as written on an Agent without it. */
371
+ 'text.spoken': { params: z.object({ text: z.string() }), result: z.object({ spoken_text: z.string() }) },
372
+ };
373
+ /**
374
+ * 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.
375
+ * TAL-784: `memory.write` replaces a memory page file with `content` only while its bytes still hash (SHA-256 hex) to `base`,
376
+ * holding the Agent's per-file lock (`<file>.lock`); `written: false` means the file changed and is untouched.
377
+ */
378
+ export const MEMORY_METHODS = {
379
+ 'memory.check': { params: ProfileHomeParams.extend({ target: z.enum(['memory', 'user']), content: z.string() }), result: z.object({ count: z.number().int(), limit: z.number().int() }) },
380
+ 'memory.write': { params: ProfileHomeParams.extend({ section: z.enum(['memory', 'user', 'soul']), base: z.string(), content: z.string() }), result: z.object({ written: z.boolean() }) },
208
381
  };
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);
382
+ /**
383
+ * TAL-819: one write the Agent staged for approval (`skills.write_approval` / `memory.write_approval`). `target` is the skill
384
+ * name(s), or `memory` / `user` for the memory store; `op` the staged action; `diff` the unified diff an approval would apply
385
+ * (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
386
+ * must send back. `approvable` is false when no diff could be built or the Agent would refuse the write (`diff` says why;
387
+ * `revision` is empty); such a write can only be rejected.
388
+ */
389
+ export const PendingWriteSchema = z.object({
390
+ id: z.string().regex(/^[A-Za-z0-9_-]{1,64}$/), kind: z.enum(['skills', 'memory']), target: z.string(), op: z.string(), summary: z.string(),
391
+ origin: z.string(), created_at: z.number(), diff: z.string(), approvable: z.boolean(), revision: z.string(),
392
+ });
393
+ export const PendingWriteRefSchema = z.object({ id: PendingWriteSchema.shape.id, revision: z.string() });
394
+ const WriteApprovalParams = ProfileHomeParams.extend({ kind: z.enum(['skills', 'memory']), writes: z.array(PendingWriteRefSchema).min(1) });
395
+ const WriteApprovalResult = z.object({ results: z.array(z.object({ id: z.string(), done: z.boolean(), message: z.string() })) });
396
+ /**
397
+ * TAL-819: the profile's staged writes of `kind`, oldest first, and whether its gate is on; `supported` is false on an Agent
398
+ * without the review handlers. `approve` / `reject` run the Agent's `/skills` or `/memory` handler per write; `done` is true
399
+ * once the record is gone (applied or dropped) and `message` is the handler's text. An approval whose diff no longer has the
400
+ * reviewed `revision` is refused (`done: false`) without applying.
401
+ */
402
+ export const WRITE_APPROVAL_METHODS = {
403
+ 'write_approval.pending': { params: ProfileHomeParams.extend({ kind: z.enum(['skills', 'memory']) }), result: z.object({ supported: z.boolean(), enabled: z.boolean(), items: z.array(PendingWriteSchema) }) },
404
+ 'write_approval.approve': { params: WriteApprovalParams, result: WriteApprovalResult },
405
+ 'write_approval.reject': { params: WriteApprovalParams, result: WriteApprovalResult },
406
+ };
407
+ 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
408
  export const PROCESS_METHODS = {
211
409
  'process.drain': { params: ProfileHomeParams.extend({ max_events: z.number().int().positive().optional() }), result: z.object({ events: z.array(ProcessEventSchema) }) },
212
410
  /** TAL-533: re-adopt the base home's and every named profile's checkpointed background processes in a fresh sidecar. */
@@ -216,7 +414,7 @@ export const PROCESS_METHODS = {
216
414
  /** TAL-532: the subset the agent already holds from its own turn (consumed via wait/log, or poll-observed). */
217
415
  'process.consumed': { params: z.object({ process_ids: z.array(z.string().min(1)) }), result: z.object({ consumed: z.array(z.string()) }) },
218
416
  /** 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() }) },
417
+ '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
418
  'process.complete_delivery': { params: ProfileHomeParams.extend({ event: Loose, claim_id: z.string() }), result: Ok },
221
419
  'process.release_delivery': { params: ProfileHomeParams.extend({ event: Loose, claim_id: z.string() }), result: Ok },
222
420
  /** Hands back a claim the busy target never admitted, without spending one of the Agent's delivery attempts. */
@@ -244,7 +442,7 @@ export const PROCESS_METHODS = {
244
442
  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
443
  /**
246
444
  * 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`.
445
+ * `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
446
  */
249
447
  export const PoolEntrySchema = z.object({
250
448
  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 +450,7 @@ export const PoolEntrySchema = z.object({
252
450
  });
253
451
  const BalanceNumber = z.number().nullable();
254
452
  /**
255
- * TAL-548: one key-based balance read. The key is the pool entry's (`credential_id`) or the server-resolved `api_key`.
453
+ * 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
454
  * `ok` carries the provider's sanitised fields: OpenRouter `quota`/`label`, DeepSeek `balances`/`is_available`, OpenCode Go
257
455
  * `windows`; `http_error` carries the status code.
258
456
  */
@@ -263,18 +461,32 @@ export const BalanceResultSchema = z.object({
263
461
  windows: z.array(z.object({ key: z.enum(['rolling', 'weekly', 'monthly']), used_percent: z.number(), reset_at: z.string(), rate_limited: z.boolean() })),
264
462
  });
265
463
  const CredentialId = z.string().min(1).nullable().optional();
464
+ /** 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. */
465
+ export const PoolLimitSchema = z.discriminatedUnion('scope', [
466
+ z.object({ scope: z.literal('account'), resets_at: z.string() }),
467
+ z.object({ scope: z.literal('models'), models: z.record(z.string(), z.string()) }),
468
+ ]);
266
469
  export const USAGE_METHODS = {
267
470
  '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) }) },
471
+ '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
472
  /** 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 },
473
+ '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({}) }) },
474
+ /** TAL-807: pool rate limits by provider slug, only for pooled providers that are limited; `models` are bare ids. */
475
+ '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) }) },
476
+ '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
477
  };
273
478
  export const CONFIG_METHODS = {
274
479
  // `config_path` is the server-resolved file (honouring HERMES_CONFIG_PATH); the sidecar reads and writes exactly that path.
275
480
  'config.get': { params: ProfileHomeParams.extend({ config_path: z.string().min(1) }), result: z.object({ path: z.string(), exists: z.boolean(), config: Loose }) },
276
481
  'config.set': { params: ProfileHomeParams.extend({ config_path: z.string().min(1), config: Loose }), result: z.object({ ok: z.literal(true), path: z.string() }) },
482
+ /** 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. */
483
+ '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
484
  '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() }) },
485
+ /**
486
+ * TAL-901: the Agent's personalities for `config` (its built-ins, then the top-level `personalities:` block, then
487
+ * `agent.personalities`), by case-folded name in the Agent's order; `prompt` is the rendered overlay text.
488
+ */
489
+ '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
490
  };
279
491
  export const WORKTREE_METHODS = {
280
492
  '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() }) },
@@ -297,10 +509,16 @@ export const ChatUsageSchema = z.object({ prompt_tokens: z.number().int(), compl
297
509
  * A completed `compressed` turn also carries `context_messages`, its `messages` with tool output pruned and capped for the model (row for row),
298
510
  * and `post_compression_context_tokens_estimate`, the next request's estimated size (TAL-539); both are null otherwise.
299
511
  */
512
+ /**
513
+ * The turn agent's context figures (`context_length`, `threshold_tokens`, `last_prompt_tokens`, `compression_count`). TAL-1005:
514
+ * `next_turn_threshold_tokens` is the compression threshold the next turn runs under, the primary runtime's when a fallback ran
515
+ * this one (the Agent restores the primary each turn); absent when the Agent reports none.
516
+ */
517
+ const ChatContextSchema = z.looseObject({ next_turn_threshold_tokens: z.number().int().positive().optional() });
300
518
  export const ChatStartResultSchema = z.object({
301
519
  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
520
  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(),
521
+ 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
522
  token_sent: z.boolean(), pending_steer: z.string(), live_tool_calls: z.array(Loose),
305
523
  });
306
524
  const Text = z.object({ text: z.string() }).catchall(Json);
@@ -309,6 +527,8 @@ const Text = z.object({ text: z.string() }).catchall(Json);
309
527
  * (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
528
  * 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
529
  * 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.
530
+ * 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`
531
+ * from the Agent's before-snapshot); the server turns its `diff` into `edit_diff` when the result has none, and never forwards it.
312
532
  */
313
533
  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
534
  export const ChatStreamSchema = z.discriminatedUnion('event', [
@@ -324,11 +544,26 @@ export const ChatStreamSchema = z.discriminatedUnion('event', [
324
544
  z.object({ event: z.literal('approval'), data: Loose }),
325
545
  z.object({ event: z.literal('clarify'), data: Loose }),
326
546
  z.object({ event: z.literal('clarify_resolved'), data: Loose }),
547
+ /** TAL-818: a `manage_connections` card opened; `tool_call_id` is the call that opened it. */
548
+ z.object({ event: z.literal('connection'), data: ConnectorOperationSnapshotSchema }),
327
549
  /** The Agent stopped waiting on an approval: `reason` is its settle cause (`resolved`, `timeout`, `interrupted`, `session_closed`). */
328
550
  z.object({ event: z.literal('approval_resolved'), data: z.looseObject({ approval_id: z.string(), session_id: z.string().optional(), reason: z.string().optional() }) }),
329
551
  z.object({ event: z.literal('compressing'), data: Loose }),
552
+ /** TAL-1006: the Agent finished the compaction the last `compressing` started; an aborted one sends none. */
553
+ z.object({ event: z.literal('compression_finished'), data: Loose }),
330
554
  z.object({ event: z.literal('warning'), data: Loose }),
331
555
  z.object({ event: z.literal('status'), data: Loose }),
556
+ /**
557
+ * TAL-803: the Agent's `AgentNotice` (`kind` `sticky` or `ttl`, `ttl_ms` only for `ttl`), the clear of one by `key`, and
558
+ * `notices_reset` before a new agent for the session is built: the notices of the one it replaces with
559
+ * `lapses_with_agent` lapse (an Agent startup warning, raised once per process, does not).
560
+ */
561
+ 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() }) }),
562
+ z.object({ event: z.literal('notice_clear'), data: z.looseObject({ key: z.string() }) }),
563
+ z.object({ event: z.literal('notices_reset'), data: Loose }),
564
+ z.object({ event: z.literal('provider_phase'), data: z.object({ phase: ProviderPhaseSchema.nullable() }) }),
565
+ /** 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. */
566
+ 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
567
  ]);
333
568
  export const CHAT_METHODS = {
334
569
  'chat.start': {
@@ -343,6 +578,14 @@ export const CHAT_METHODS = {
343
578
  persist_user_message: z.string().optional(),
344
579
  /** TAL-529: a `/btw` side question, whose throwaway session the Agent keeps out of the profile's state.db. */
345
580
  ephemeral: z.boolean().optional(),
581
+ /** TAL-801: a spoken turn, which the Agent runs on the profile's `auxiliary.voice_chat` slot when it has that route. */
582
+ voice_turn: z.boolean().optional(),
583
+ /** TAL-818: attach the Agent's `connection_callback`, so `manage_connections` streams a `connection` frame and waits on the card. */
584
+ connect_card: z.boolean().optional(),
585
+ /** Bind turn-local skill capture only for a client that renders the masked prompt. */
586
+ skill_secret: z.boolean().optional(),
587
+ /** 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. */
588
+ workspace_local: z.boolean().optional(),
346
589
  }),
347
590
  result: ChatStartResultSchema,
348
591
  stream: ChatStreamSchema,
@@ -362,11 +605,11 @@ export const CHAT_METHODS = {
362
605
  /**
363
606
  * TAL-424: deliver `pending[index]` now with the Agent's `redirect`: `delivery: redirect` restarts the model request with
364
607
  * 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.
608
+ * 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
609
  */
367
610
  'chat.steer_now': {
368
611
  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() }),
612
+ 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
613
  },
371
614
  /**
372
615
  * TAL-255: manual `/compress` of `conversation_history` through the Agent's `compress_now` on a throwaway agent. Nothing is
@@ -387,20 +630,44 @@ export const CHAT_METHODS = {
387
630
  },
388
631
  /** TAL-255: second phase of `chat.compress`; `finalized: false` when the token is unknown or already expired. */
389
632
  'chat.compress_finalize': { params: z.object({ commit_token: z.string().min(1), committed: z.boolean() }), result: z.object({ finalized: z.boolean() }) },
633
+ 'chat.finalize_session': { params: ProfileHomeParams.extend({ session_id: z.string().min(1), reason: z.enum(['clear', 'delete']) }), result: z.object({ messages: z.array(z.string()) }) },
390
634
  'chat.evict_agent': { params: z.object({ session_id: z.string().min(1), clear_session: z.boolean().optional() }), result: z.object({ evicted: z.boolean() }) },
391
635
  'chat.commit_memory': { params: ProfileHomeParams.extend({ session_id: z.string().min(1) }), result: z.object({ committed: z.boolean() }) },
636
+ /**
637
+ * TAL-902: `/refine [focus]`, the Agent's on-demand memory/skill review of `messages` (the session's model context) on the
638
+ * session's cached agent, else one built from the turn parameters and cached like a turn's. `output` is the Agent's
639
+ * wording: the review started, or why not (a turn is running, the conversation is empty).
640
+ */
641
+ 'chat.refine': {
642
+ params: ProfileHomeParams.extend({
643
+ session_id: z.string().min(1), messages: z.array(Loose), focus: z.string(), model: z.string(), model_provider: z.string().nullable().optional(),
644
+ enabled_toolsets: z.array(z.string()).nullable().optional(), max_iterations: z.number().int().nullable().optional(), max_tokens: z.number().int().nullable().optional(),
645
+ reasoning_config: z.object({ enabled: z.boolean(), effort: z.string().optional() }).nullable().optional(),
646
+ /** TAL-903: the session's workspace, as `chat.start` sends it, so the agent built here is the one its next turn reuses. */
647
+ workspace: z.string().optional(), workspace_local: z.boolean().optional(),
648
+ }),
649
+ result: z.object({ output: z.string() }),
650
+ },
392
651
  '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
652
  'approval.pending': { params: z.object({ session_id: z.string().min(1) }), result: z.object({ pending: z.array(Loose) }) },
394
653
  '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() }) },
654
+ '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
655
  };
397
656
  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() });
657
+ /**
658
+ * A lifecycle run of `hermes --profile <name> gateway <action>` for the profile at `profile_home` (TAL-894). `busy` means another
659
+ * action holds the lock; `timed_out` marks a child killed after `background_wait_seconds` (a restart defaults to the Agent CLI's own
660
+ * 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
661
+ * failed run (TAL-884), the one line the server shows.
662
+ */
663
+ const GatewayActionResult = z.object({ status: z.enum(['completed', 'failed', 'busy']), message: z.string(), detail: z.string().optional(), returncode: z.number().int().optional(), reason: z.string().optional(), timed_out: z.boolean().optional() });
400
664
  const GatewayActionStream = z.discriminatedUnion('event', [z.object({ event: z.literal('progress'), data: z.object({ phase: z.enum(['started', 'draining']) }) })]);
401
665
  export const GATEWAY_ACTIONS = ['start', 'stop', 'restart'];
402
666
  export const GATEWAY_METHODS = {
667
+ 'gateway.session': { params: GatewaySessionParamsSchema, result: GatewaySessionResultSchema, stream: z.object({ event: z.enum(['snapshot', 'event']), data: Loose }) },
403
668
  'gateway.control': { params: GatewayActionParams.extend({ action: z.enum(GATEWAY_ACTIONS) }), result: GatewayActionResult, stream: GatewayActionStream },
404
669
  'gateway.restart': { params: GatewayActionParams, result: GatewayActionResult, stream: GatewayActionStream },
670
+ /** TAL-805: the gateway mode of the profile `name` at `profile_home`; the root profile (`is_default`) is the host itself. */
671
+ 'gateway.mode': { params: ProfileHomeParams.extend({ name: z.string().min(1), is_default: z.boolean() }), result: z.object({ gateway_mode: GatewayModeSchema }) },
405
672
  };
406
673
  //# sourceMappingURL=namespaces.js.map