@poolzin/pool-bot 2026.2.23 → 2026.2.25

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 (235) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/acp/client.js +207 -18
  3. package/dist/acp/secret-file.js +22 -0
  4. package/dist/agents/agent-scope.js +10 -0
  5. package/dist/agents/bash-process-registry.test-helpers.js +29 -0
  6. package/dist/agents/bash-tools.exec-approval-request.js +20 -0
  7. package/dist/agents/bash-tools.exec-host-gateway.js +230 -0
  8. package/dist/agents/bash-tools.exec-host-node.js +235 -0
  9. package/dist/agents/bash-tools.exec-types.js +1 -0
  10. package/dist/agents/bash-tools.process.js +224 -218
  11. package/dist/agents/content-blocks.js +16 -0
  12. package/dist/agents/model-fallback.js +96 -101
  13. package/dist/agents/models-config.providers.js +299 -182
  14. package/dist/agents/pi-embedded-payloads.js +1 -0
  15. package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +34 -0
  16. package/dist/agents/skills.test-helpers.js +13 -0
  17. package/dist/agents/stable-stringify.js +12 -0
  18. package/dist/agents/subagent-registry.mocks.shared.js +12 -0
  19. package/dist/agents/test-helpers/assistant-message-fixtures.js +29 -0
  20. package/dist/agents/test-helpers/pi-tools-sandbox-context.js +27 -0
  21. package/dist/agents/tool-policy-shared.js +108 -0
  22. package/dist/agents/tools/browser-tool.js +160 -54
  23. package/dist/agents/tools/cron-tool.test-helpers.js +12 -0
  24. package/dist/agents/tools/discord-actions-moderation-shared.js +27 -0
  25. package/dist/agents/tools/image-tool.js +214 -99
  26. package/dist/agents/tools/sessions-history-tool.js +140 -108
  27. package/dist/agents/workspace.js +222 -46
  28. package/dist/auto-reply/commands-registry.js +15 -18
  29. package/dist/auto-reply/fallback-state.js +114 -0
  30. package/dist/auto-reply/model-runtime.js +68 -0
  31. package/dist/auto-reply/reply/agent-runner-execution.js +36 -4
  32. package/dist/auto-reply/reply/agent-runner.js +165 -39
  33. package/dist/auto-reply/reply/commands-setunset-standard.js +13 -0
  34. package/dist/browser/config.js +26 -0
  35. package/dist/browser/navigation-guard.js +31 -0
  36. package/dist/browser/routes/agent.act.js +431 -424
  37. package/dist/browser/routes/agent.shared.js +47 -3
  38. package/dist/browser/routes/agent.snapshot.js +122 -116
  39. package/dist/browser/routes/agent.storage.js +303 -297
  40. package/dist/browser/routes/tabs.js +154 -100
  41. package/dist/browser/server-lifecycle.js +37 -0
  42. package/dist/build-info.json +3 -3
  43. package/dist/channels/allow-from.js +25 -0
  44. package/dist/channels/plugins/account-action-gate.js +13 -0
  45. package/dist/channels/plugins/message-actions.js +10 -0
  46. package/dist/channels/telegram/api.js +18 -0
  47. package/dist/cli/argv.js +84 -21
  48. package/dist/cli/banner.js +2 -1
  49. package/dist/cli/exec-approvals-cli.js +92 -124
  50. package/dist/cli/memory-cli.js +158 -61
  51. package/dist/cli/nodes-cli/register.push.js +63 -0
  52. package/dist/cli/nodes-media-utils.js +21 -0
  53. package/dist/cli/plugins-cli.js +245 -61
  54. package/dist/cli/program/build-program.js +3 -1
  55. package/dist/cli/program/command-registry.js +223 -136
  56. package/dist/cli/program/help.js +43 -12
  57. package/dist/cli/route.js +1 -1
  58. package/dist/cli/test-runtime-capture.js +24 -0
  59. package/dist/commands/agent.js +163 -87
  60. package/dist/commands/channels.mock-harness.js +23 -0
  61. package/dist/commands/daemon-install-runtime-warning.js +11 -0
  62. package/dist/commands/onboard-helpers.js +4 -4
  63. package/dist/commands/sessions.test-helpers.js +61 -0
  64. package/dist/compat/legacy-names.js +2 -2
  65. package/dist/config/commands.js +3 -0
  66. package/dist/config/config.js +1 -1
  67. package/dist/config/env-substitution.js +62 -34
  68. package/dist/config/env-vars.js +9 -0
  69. package/dist/config/io.js +571 -171
  70. package/dist/config/merge-patch.js +50 -4
  71. package/dist/config/redact-snapshot.js +404 -76
  72. package/dist/config/schema.js +58 -570
  73. package/dist/config/validation.js +140 -85
  74. package/dist/config/zod-schema.hooks.js +40 -11
  75. package/dist/config/zod-schema.installs.js +20 -0
  76. package/dist/config/zod-schema.js +8 -7
  77. package/dist/control-ui/assets/{index-HRr1grwl.js → index-Dvkl4Xlx.js} +2 -1
  78. package/dist/control-ui/assets/{index-HRr1grwl.js.map → index-Dvkl4Xlx.js.map} +1 -1
  79. package/dist/control-ui/index.html +1 -1
  80. package/dist/daemon/cmd-argv.js +21 -0
  81. package/dist/daemon/cmd-set.js +58 -0
  82. package/dist/daemon/service-types.js +1 -0
  83. package/dist/discord/monitor/exec-approvals.js +357 -162
  84. package/dist/gateway/auth.js +38 -3
  85. package/dist/gateway/call.js +149 -68
  86. package/dist/gateway/canvas-capability.js +75 -0
  87. package/dist/gateway/control-plane-audit.js +28 -0
  88. package/dist/gateway/control-plane-rate-limit.js +53 -0
  89. package/dist/gateway/events.js +1 -0
  90. package/dist/gateway/hooks.js +109 -54
  91. package/dist/gateway/http-common.js +22 -0
  92. package/dist/gateway/method-scopes.js +169 -0
  93. package/dist/gateway/net.js +23 -0
  94. package/dist/gateway/openresponses-http.js +120 -110
  95. package/dist/gateway/probe-auth.js +2 -0
  96. package/dist/gateway/protocol/index.js +3 -2
  97. package/dist/gateway/protocol/schema/protocol-schemas.js +2 -0
  98. package/dist/gateway/protocol/schema/push.js +18 -0
  99. package/dist/gateway/protocol/schema.js +1 -0
  100. package/dist/gateway/server-http.js +236 -52
  101. package/dist/gateway/server-methods/agent.js +162 -24
  102. package/dist/gateway/server-methods/chat.js +461 -130
  103. package/dist/gateway/server-methods/config.js +193 -150
  104. package/dist/gateway/server-methods/nodes.helpers.js +12 -0
  105. package/dist/gateway/server-methods/nodes.js +251 -69
  106. package/dist/gateway/server-methods/push.js +53 -0
  107. package/dist/gateway/server-reload-handlers.js +2 -3
  108. package/dist/gateway/server-runtime-config.js +5 -0
  109. package/dist/gateway/server-runtime-state.js +2 -0
  110. package/dist/gateway/server-ws-runtime.js +1 -0
  111. package/dist/gateway/server.impl.js +296 -139
  112. package/dist/gateway/session-preview.test-helpers.js +11 -0
  113. package/dist/gateway/startup-auth.js +126 -0
  114. package/dist/gateway/test-helpers.agent-results.js +15 -0
  115. package/dist/gateway/test-helpers.mocks.js +37 -14
  116. package/dist/gateway/test-helpers.server.js +161 -77
  117. package/dist/hooks/bundled/session-memory/handler.js +165 -34
  118. package/dist/hooks/gmail-watcher-lifecycle.js +23 -0
  119. package/dist/infra/archive-path.js +49 -0
  120. package/dist/infra/device-pairing.js +148 -167
  121. package/dist/infra/exec-approvals-allowlist.js +19 -70
  122. package/dist/infra/exec-approvals-analysis.js +44 -17
  123. package/dist/infra/exec-safe-bin-policy.js +269 -0
  124. package/dist/infra/fixed-window-rate-limit.js +33 -0
  125. package/dist/infra/git-root.js +61 -0
  126. package/dist/infra/heartbeat-active-hours.js +2 -2
  127. package/dist/infra/heartbeat-reason.js +40 -0
  128. package/dist/infra/heartbeat-runner.js +72 -32
  129. package/dist/infra/install-source-utils.js +91 -7
  130. package/dist/infra/node-pairing.js +50 -105
  131. package/dist/infra/npm-integrity.js +45 -0
  132. package/dist/infra/npm-pack-install.js +40 -0
  133. package/dist/infra/outbound/channel-adapters.js +20 -7
  134. package/dist/infra/outbound/message-action-runner.js +107 -327
  135. package/dist/infra/outbound/message.js +59 -36
  136. package/dist/infra/outbound/outbound-policy.js +52 -25
  137. package/dist/infra/outbound/outbound-send-service.js +58 -71
  138. package/dist/infra/pairing-files.js +10 -0
  139. package/dist/infra/plain-object.js +9 -0
  140. package/dist/infra/push-apns.js +365 -0
  141. package/dist/infra/restart-sentinel.js +16 -1
  142. package/dist/infra/restart.js +229 -26
  143. package/dist/infra/scp-host.js +54 -0
  144. package/dist/infra/update-startup.js +86 -9
  145. package/dist/media/inbound-path-policy.js +114 -0
  146. package/dist/media/input-files.js +16 -0
  147. package/dist/memory/test-manager.js +8 -0
  148. package/dist/plugin-sdk/temp-path.js +47 -0
  149. package/dist/plugins/discovery.js +217 -23
  150. package/dist/plugins/hook-runner-global.js +16 -0
  151. package/dist/plugins/loader.js +192 -26
  152. package/dist/plugins/logger.js +8 -0
  153. package/dist/plugins/manifest-registry.js +3 -0
  154. package/dist/plugins/path-safety.js +34 -0
  155. package/dist/plugins/registry.js +5 -2
  156. package/dist/plugins/runtime/index.js +271 -206
  157. package/dist/providers/github-copilot-models.js +4 -1
  158. package/dist/security/audit-channel.js +8 -19
  159. package/dist/security/audit-extra.async.js +354 -182
  160. package/dist/security/audit-extra.js +11 -1
  161. package/dist/security/audit-extra.sync.js +340 -33
  162. package/dist/security/audit-fs.js +31 -13
  163. package/dist/security/audit.js +145 -371
  164. package/dist/security/dm-policy-shared.js +24 -0
  165. package/dist/security/external-content.js +20 -8
  166. package/dist/security/fix.js +49 -85
  167. package/dist/security/scan-paths.js +20 -0
  168. package/dist/security/secret-equal.js +3 -7
  169. package/dist/security/windows-acl.js +30 -15
  170. package/dist/shared/node-list-parse.js +13 -0
  171. package/dist/shared/operator-scope-compat.js +37 -0
  172. package/dist/shared/text-chunking.js +29 -0
  173. package/dist/slack/blocks.test-helpers.js +31 -0
  174. package/dist/slack/monitor/mrkdwn.js +8 -0
  175. package/dist/telegram/bot-message-dispatch.js +366 -164
  176. package/dist/telegram/draft-stream.js +30 -7
  177. package/dist/telegram/reasoning-lane-coordinator.js +128 -0
  178. package/dist/terminal/prompt-select-styled.js +9 -0
  179. package/dist/test-utils/command-runner.js +6 -0
  180. package/dist/test-utils/internal-hook-event-payload.js +10 -0
  181. package/dist/test-utils/model-auth-mock.js +12 -0
  182. package/dist/test-utils/provider-usage-fetch.js +14 -0
  183. package/dist/test-utils/temp-home.js +33 -0
  184. package/dist/tui/components/chat-log.js +9 -0
  185. package/dist/tui/tui-command-handlers.js +36 -27
  186. package/dist/tui/tui-event-handlers.js +122 -32
  187. package/dist/tui/tui.js +181 -45
  188. package/dist/utils/mask-api-key.js +10 -0
  189. package/dist/utils/run-with-concurrency.js +39 -0
  190. package/dist/web/media.js +4 -0
  191. package/docs/tools/slash-commands.md +5 -1
  192. package/extensions/bluebubbles/package.json +1 -1
  193. package/extensions/copilot-proxy/package.json +1 -1
  194. package/extensions/diagnostics-otel/package.json +1 -1
  195. package/extensions/discord/package.json +1 -1
  196. package/extensions/feishu/package.json +1 -1
  197. package/extensions/feishu/src/external-keys.ts +19 -0
  198. package/extensions/google-antigravity-auth/package.json +1 -1
  199. package/extensions/google-gemini-cli-auth/package.json +1 -1
  200. package/extensions/googlechat/package.json +1 -1
  201. package/extensions/imessage/package.json +1 -1
  202. package/extensions/irc/package.json +1 -1
  203. package/extensions/line/package.json +1 -1
  204. package/extensions/llm-task/package.json +1 -1
  205. package/extensions/lobster/package.json +1 -1
  206. package/extensions/lobster/src/windows-spawn.ts +193 -0
  207. package/extensions/matrix/CHANGELOG.md +5 -0
  208. package/extensions/matrix/package.json +1 -1
  209. package/extensions/matrix/src/matrix/actions/limits.ts +6 -0
  210. package/extensions/mattermost/package.json +1 -1
  211. package/extensions/mattermost/src/mattermost/reactions.test-helpers.ts +83 -0
  212. package/extensions/memory-core/package.json +1 -1
  213. package/extensions/memory-lancedb/package.json +1 -1
  214. package/extensions/minimax-portal-auth/package.json +1 -1
  215. package/extensions/msteams/CHANGELOG.md +5 -0
  216. package/extensions/msteams/package.json +1 -1
  217. package/extensions/nextcloud-talk/package.json +1 -1
  218. package/extensions/nostr/CHANGELOG.md +5 -0
  219. package/extensions/nostr/package.json +1 -1
  220. package/extensions/open-prose/package.json +1 -1
  221. package/extensions/openai-codex-auth/package.json +1 -1
  222. package/extensions/signal/package.json +1 -1
  223. package/extensions/slack/package.json +1 -1
  224. package/extensions/telegram/package.json +1 -1
  225. package/extensions/tlon/package.json +1 -1
  226. package/extensions/twitch/CHANGELOG.md +5 -0
  227. package/extensions/twitch/package.json +1 -1
  228. package/extensions/voice-call/CHANGELOG.md +5 -0
  229. package/extensions/voice-call/package.json +1 -1
  230. package/extensions/whatsapp/package.json +1 -1
  231. package/extensions/zalo/CHANGELOG.md +5 -0
  232. package/extensions/zalo/package.json +1 -1
  233. package/extensions/zalouser/CHANGELOG.md +5 -0
  234. package/extensions/zalouser/package.json +1 -1
  235. package/package.json +1 -1
@@ -1,24 +1,99 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { agentCommand } from "../../commands/agent.js";
3
2
  import { listAgentIds } from "../../agents/agent-scope.js";
3
+ import { BARE_SESSION_RESET_PROMPT } from "../../auto-reply/reply/session-reset-prompt.js";
4
+ import { agentCommand } from "../../commands/agent.js";
4
5
  import { loadConfig } from "../../config/config.js";
5
6
  import { resolveAgentIdFromSessionKey, resolveExplicitAgentSessionKey, resolveAgentMainSessionKey, updateSessionStore, } from "../../config/sessions.js";
6
7
  import { registerAgentRunContext } from "../../infra/agent-events.js";
7
8
  import { resolveAgentDeliveryPlan, resolveAgentOutboundTarget, } from "../../infra/outbound/agent-delivery.js";
9
+ import { classifySessionKeyShape, normalizeAgentId } from "../../routing/session-key.js";
8
10
  import { defaultRuntime } from "../../runtime.js";
11
+ import { normalizeInputProvenance } from "../../sessions/input-provenance.js";
9
12
  import { resolveSendPolicy } from "../../sessions/send-policy.js";
10
13
  import { normalizeSessionDeliveryFields } from "../../utils/delivery-context.js";
11
14
  import { INTERNAL_MESSAGE_CHANNEL, isDeliverableMessageChannel, isGatewayMessageChannel, normalizeMessageChannel, } from "../../utils/message-channel.js";
12
- import { normalizeAgentId } from "../../routing/session-key.js";
15
+ import { resolveAssistantIdentity } from "../assistant-identity.js";
13
16
  import { parseMessageWithAttachments } from "../chat-attachments.js";
17
+ import { resolveAssistantAvatarUrl } from "../control-ui-shared.js";
18
+ import { GATEWAY_CLIENT_CAPS, hasGatewayClientCap } from "../protocol/client-info.js";
14
19
  import { ErrorCodes, errorShape, formatValidationErrors, validateAgentIdentityParams, validateAgentParams, validateAgentWaitParams, } from "../protocol/index.js";
15
- import { loadSessionEntry } from "../session-utils.js";
20
+ import { canonicalizeSpawnedByForAgent, loadSessionEntry, pruneLegacyStoreKeys, resolveGatewaySessionStoreTarget, } from "../session-utils.js";
16
21
  import { formatForLog } from "../ws-log.js";
17
- import { resolveAssistantIdentity } from "../assistant-identity.js";
18
- import { resolveAssistantAvatarUrl } from "../control-ui-shared.js";
19
22
  import { waitForAgentJob } from "./agent-job.js";
23
+ import { injectTimestamp, timestampOptsFromConfig } from "./agent-timestamp.js";
24
+ import { normalizeRpcAttachmentsToChatAttachments } from "./attachment-normalize.js";
25
+ import { sessionsHandlers } from "./sessions.js";
26
+ const RESET_COMMAND_RE = /^\/(new|reset)(?:\s+([\s\S]*))?$/i;
27
+ function isGatewayErrorShape(value) {
28
+ if (!value || typeof value !== "object") {
29
+ return false;
30
+ }
31
+ const candidate = value;
32
+ return typeof candidate.code === "string" && typeof candidate.message === "string";
33
+ }
34
+ async function runSessionResetFromAgent(params) {
35
+ return await new Promise((resolve) => {
36
+ let settled = false;
37
+ const settle = (result) => {
38
+ if (settled) {
39
+ return;
40
+ }
41
+ settled = true;
42
+ resolve(result);
43
+ };
44
+ const respond = (ok, payload, error) => {
45
+ if (!ok) {
46
+ settle({
47
+ ok: false,
48
+ error: isGatewayErrorShape(error)
49
+ ? error
50
+ : errorShape(ErrorCodes.UNAVAILABLE, String(error ?? "sessions.reset failed")),
51
+ });
52
+ return;
53
+ }
54
+ const payloadObj = payload;
55
+ const key = typeof payloadObj?.key === "string" ? payloadObj.key : params.key;
56
+ const sessionId = payloadObj?.entry && typeof payloadObj.entry.sessionId === "string"
57
+ ? payloadObj.entry.sessionId
58
+ : undefined;
59
+ settle({ ok: true, key, sessionId });
60
+ };
61
+ const resetResult = sessionsHandlers["sessions.reset"]({
62
+ req: {
63
+ type: "req",
64
+ id: `${params.idempotencyKey}:reset`,
65
+ method: "sessions.reset",
66
+ },
67
+ params: {
68
+ key: params.key,
69
+ reason: params.reason,
70
+ },
71
+ context: params.context,
72
+ client: params.client,
73
+ isWebchatConnect: params.isWebchatConnect,
74
+ respond,
75
+ });
76
+ void (async () => {
77
+ try {
78
+ await resetResult;
79
+ if (!settled) {
80
+ settle({
81
+ ok: false,
82
+ error: errorShape(ErrorCodes.UNAVAILABLE, "sessions.reset completed without returning a response"),
83
+ });
84
+ }
85
+ }
86
+ catch (err) {
87
+ settle({
88
+ ok: false,
89
+ error: errorShape(ErrorCodes.UNAVAILABLE, String(err)),
90
+ });
91
+ }
92
+ })();
93
+ });
94
+ }
20
95
  export const agentHandlers = {
21
- agent: async ({ params, respond, context }) => {
96
+ agent: async ({ params, respond, context, client, isWebchatConnect }) => {
22
97
  const p = params;
23
98
  if (!validateAgentParams(p)) {
24
99
  respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `invalid agent params: ${formatValidationErrors(validateAgentParams.errors)}`));
@@ -34,6 +109,7 @@ export const agentHandlers = {
34
109
  let resolvedGroupChannel = groupChannelRaw || undefined;
35
110
  let resolvedGroupSpace = groupSpaceRaw || undefined;
36
111
  let spawnedByValue = typeof request.spawnedBy === "string" ? request.spawnedBy.trim() : undefined;
112
+ const inputProvenance = normalizeInputProvenance(request.inputProvenance);
37
113
  const cached = context.dedupe.get(`agent:${idem}`);
38
114
  if (cached) {
39
115
  respond(cached.ok, cached.payload, cached.error, {
@@ -41,18 +117,7 @@ export const agentHandlers = {
41
117
  });
42
118
  return;
43
119
  }
44
- const normalizedAttachments = request.attachments
45
- ?.map((a) => ({
46
- type: typeof a?.type === "string" ? a.type : undefined,
47
- mimeType: typeof a?.mimeType === "string" ? a.mimeType : undefined,
48
- fileName: typeof a?.fileName === "string" ? a.fileName : undefined,
49
- content: typeof a?.content === "string"
50
- ? a.content
51
- : ArrayBuffer.isView(a?.content)
52
- ? Buffer.from(a.content.buffer, a.content.byteOffset, a.content.byteLength).toString("base64")
53
- : undefined,
54
- }))
55
- .filter((a) => a.content) ?? [];
120
+ const normalizedAttachments = normalizeRpcAttachmentsToChatAttachments(request.attachments);
56
121
  let message = request.message.trim();
57
122
  let images = [];
58
123
  if (normalizedAttachments.length > 0) {
@@ -93,7 +158,12 @@ export const agentHandlers = {
93
158
  const requestedSessionKeyRaw = typeof request.sessionKey === "string" && request.sessionKey.trim()
94
159
  ? request.sessionKey.trim()
95
160
  : undefined;
96
- const requestedSessionKey = requestedSessionKeyRaw ??
161
+ if (requestedSessionKeyRaw &&
162
+ classifySessionKeyShape(requestedSessionKeyRaw) === "malformed_agent") {
163
+ respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `invalid agent params: malformed session key "${requestedSessionKeyRaw}"`));
164
+ return;
165
+ }
166
+ let requestedSessionKey = requestedSessionKeyRaw ??
97
167
  resolveExplicitAgentSessionKey({
98
168
  cfg,
99
169
  agentId,
@@ -109,13 +179,51 @@ export const agentHandlers = {
109
179
  let sessionEntry;
110
180
  let bestEffortDeliver = false;
111
181
  let cfgForAgent;
182
+ let resolvedSessionKey = requestedSessionKey;
183
+ let skipTimestampInjection = false;
184
+ const resetCommandMatch = message.match(RESET_COMMAND_RE);
185
+ if (resetCommandMatch && requestedSessionKey) {
186
+ const resetReason = resetCommandMatch[1]?.toLowerCase() === "new" ? "new" : "reset";
187
+ const resetResult = await runSessionResetFromAgent({
188
+ key: requestedSessionKey,
189
+ reason: resetReason,
190
+ idempotencyKey: idem,
191
+ context,
192
+ client,
193
+ isWebchatConnect,
194
+ });
195
+ if (!resetResult.ok) {
196
+ respond(false, undefined, resetResult.error);
197
+ return;
198
+ }
199
+ requestedSessionKey = resetResult.key;
200
+ resolvedSessionId = resetResult.sessionId ?? resolvedSessionId;
201
+ const postResetMessage = resetCommandMatch[2]?.trim() ?? "";
202
+ if (postResetMessage) {
203
+ message = postResetMessage;
204
+ }
205
+ else {
206
+ // Keep bare /new and /reset behavior aligned with chat.send:
207
+ // reset first, then run a fresh-session greeting prompt in-place.
208
+ message = BARE_SESSION_RESET_PROMPT;
209
+ skipTimestampInjection = true;
210
+ }
211
+ }
212
+ // Inject timestamp into user-authored messages that don't already have one.
213
+ // Channel messages (Discord, Telegram, etc.) get timestamps via envelope
214
+ // formatting in a separate code path — they never reach this handler.
215
+ // See: https://github.com/moltbot/moltbot/issues/3658
216
+ if (!skipTimestampInjection) {
217
+ message = injectTimestamp(message, timestampOptsFromConfig(cfg));
218
+ }
112
219
  if (requestedSessionKey) {
113
220
  const { cfg, storePath, entry, canonicalKey } = loadSessionEntry(requestedSessionKey);
114
221
  cfgForAgent = cfg;
115
222
  const now = Date.now();
116
223
  const sessionId = entry?.sessionId ?? randomUUID();
117
224
  const labelValue = request.label?.trim() || entry?.label;
118
- spawnedByValue = spawnedByValue || entry?.spawnedBy;
225
+ const sessionAgent = resolveAgentIdFromSessionKey(canonicalKey);
226
+ spawnedByValue = canonicalizeSpawnedByForAgent(cfg, sessionAgent, spawnedByValue || entry?.spawnedBy);
119
227
  let inheritedGroup;
120
228
  if (spawnedByValue && (!resolvedGroupId || !resolvedGroupChannel || !resolvedGroupSpace)) {
121
229
  try {
@@ -151,6 +259,7 @@ export const agentHandlers = {
151
259
  providerOverride: entry?.providerOverride,
152
260
  label: labelValue,
153
261
  spawnedBy: spawnedByValue,
262
+ spawnDepth: entry?.spawnDepth,
154
263
  channel: entry?.channel ?? request.channel?.trim(),
155
264
  groupId: resolvedGroupId ?? entry?.groupId,
156
265
  groupChannel: resolvedGroupChannel ?? entry?.groupChannel,
@@ -162,7 +271,7 @@ export const agentHandlers = {
162
271
  const sendPolicy = resolveSendPolicy({
163
272
  cfg,
164
273
  entry,
165
- sessionKey: requestedSessionKey,
274
+ sessionKey: canonicalKey,
166
275
  channel: entry?.channel,
167
276
  chatType: entry?.chatType,
168
277
  });
@@ -172,23 +281,47 @@ export const agentHandlers = {
172
281
  }
173
282
  resolvedSessionId = sessionId;
174
283
  const canonicalSessionKey = canonicalKey;
284
+ resolvedSessionKey = canonicalSessionKey;
175
285
  const agentId = resolveAgentIdFromSessionKey(canonicalSessionKey);
176
286
  const mainSessionKey = resolveAgentMainSessionKey({ cfg, agentId });
177
287
  if (storePath) {
178
288
  await updateSessionStore(storePath, (store) => {
289
+ const target = resolveGatewaySessionStoreTarget({
290
+ cfg,
291
+ key: requestedSessionKey,
292
+ store,
293
+ });
294
+ pruneLegacyStoreKeys({
295
+ store,
296
+ canonicalKey: target.canonicalKey,
297
+ candidates: target.storeKeys,
298
+ });
179
299
  store[canonicalSessionKey] = nextEntry;
180
300
  });
181
301
  }
182
302
  if (canonicalSessionKey === mainSessionKey || canonicalSessionKey === "global") {
183
303
  context.addChatRun(idem, {
184
- sessionKey: requestedSessionKey,
304
+ sessionKey: canonicalSessionKey,
185
305
  clientRunId: idem,
186
306
  });
187
307
  bestEffortDeliver = true;
188
308
  }
189
- registerAgentRunContext(idem, { sessionKey: requestedSessionKey });
309
+ registerAgentRunContext(idem, { sessionKey: canonicalSessionKey });
190
310
  }
191
311
  const runId = idem;
312
+ const connId = typeof client?.connId === "string" ? client.connId : undefined;
313
+ const wantsToolEvents = hasGatewayClientCap(client?.connect?.caps, GATEWAY_CLIENT_CAPS.TOOL_EVENTS);
314
+ if (connId && wantsToolEvents) {
315
+ context.registerToolEventRecipient(runId, connId);
316
+ // Register for any other active runs *in the same session* so
317
+ // late-joining clients (e.g. page refresh mid-response) receive
318
+ // in-progress tool events without leaking cross-session data.
319
+ for (const [activeRunId, active] of context.chatAbortControllers) {
320
+ if (activeRunId !== runId && active.sessionKey === requestedSessionKey) {
321
+ context.registerToolEventRecipient(activeRunId, connId);
322
+ }
323
+ }
324
+ }
192
325
  const wantsDelivery = request.deliver === true;
193
326
  const explicitTo = typeof request.replyTo === "string" && request.replyTo.trim()
194
327
  ? request.replyTo.trim()
@@ -241,7 +374,7 @@ export const agentHandlers = {
241
374
  images,
242
375
  to: resolvedTo,
243
376
  sessionId: resolvedSessionId,
244
- sessionKey: requestedSessionKey,
377
+ sessionKey: resolvedSessionKey,
245
378
  thinking: request.thinking,
246
379
  deliver,
247
380
  deliveryTargetMode,
@@ -266,6 +399,7 @@ export const agentHandlers = {
266
399
  runId,
267
400
  lane: request.lane,
268
401
  extraSystemPrompt: request.extraSystemPrompt,
402
+ inputProvenance,
269
403
  }, defaultRuntime, context.deps)
270
404
  .then((result) => {
271
405
  const payload = {
@@ -312,6 +446,10 @@ export const agentHandlers = {
312
446
  const sessionKeyRaw = typeof p.sessionKey === "string" ? p.sessionKey.trim() : "";
313
447
  let agentId = agentIdRaw ? normalizeAgentId(agentIdRaw) : undefined;
314
448
  if (sessionKeyRaw) {
449
+ if (classifySessionKeyShape(sessionKeyRaw) === "malformed_agent") {
450
+ respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `invalid agent.identity.get params: malformed session key "${sessionKeyRaw}"`));
451
+ return;
452
+ }
315
453
  const resolved = resolveAgentIdFromSessionKey(sessionKeyRaw);
316
454
  if (agentId && resolved !== agentId) {
317
455
  respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `invalid agent.identity.get params: agent "${agentIdRaw}" does not match session key agent "${resolved}"`));