@xopcai/xopc 0.0.297 → 0.0.299

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 (151) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/{activity-store-DKWpcUF7.js → activity-store-C-DSdak3.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-BfYFd0FF.js → agent-avatar-dicebear-BnrB5fWD.js} +1 -1
  5. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-CaA7l5Xv.js +1 -0
  6. package/dist/gateway/static/root/assets/agents-DNxSNGao.js +1 -0
  7. package/dist/gateway/static/root/assets/api-BhJmc8E-.js +1 -0
  8. package/dist/gateway/static/root/assets/{api-Bq0dy2ht.js → api-DS4w0i5N.js} +1 -1
  9. package/dist/gateway/static/root/assets/{app-management-settings-panel-D43Mgx8B.js → app-management-settings-panel-Dfrcz-vZ.js} +1 -1
  10. package/dist/gateway/static/root/assets/{appearance-settings-DlXRpU1B.js → appearance-settings-BLB_xmtb.js} +1 -1
  11. package/dist/gateway/static/root/assets/apps-page-Bg-7fqIS.js +1 -0
  12. package/dist/gateway/static/root/assets/{archive-plugin-CSML9uqu.js → archive-plugin-CISHdvCz.js} +1 -1
  13. package/dist/gateway/static/root/assets/{attachment-load-CWG19Y6q.js → attachment-load-c0q_Q6IQ.js} +1 -1
  14. package/dist/gateway/static/root/assets/{automations-page-EmWwQYaa.js → automations-page-BOiNdKcv.js} +1 -1
  15. package/dist/gateway/static/root/assets/automations-workspace-DDdsNbjH.js +6 -0
  16. package/dist/gateway/static/root/assets/{binary-plugins-BZS8DPaq.js → binary-plugins-BO1hALPX.js} +1 -1
  17. package/dist/gateway/static/root/assets/{block-editor-ByjiDUgd.js → block-editor-BWzZ4hDH.js} +3 -3
  18. package/dist/gateway/static/root/assets/{browser-automation-inputs-Ba7bkmFQ.js → browser-automation-inputs-DvwCwLOp.js} +1 -1
  19. package/dist/gateway/static/root/assets/{browser-automations-page-Dt_L_Cpo.js → browser-automations-page-B8LfUUco.js} +2 -2
  20. package/dist/gateway/static/root/assets/browser-settings-page-BtZbX85K.js +3 -0
  21. package/dist/gateway/static/root/assets/capabilities-settings-panel-mNpAFqTf.js +8 -0
  22. package/dist/gateway/static/root/assets/channels-settings-BBX7Gp4q.js +1 -0
  23. package/dist/gateway/static/root/assets/{channels-status-swr-Czx0fIBL.js → channels-status-swr-CgtFOwNh.js} +1 -1
  24. package/dist/gateway/static/root/assets/{chat-terminal-dock-Bqo71n61.js → chat-terminal-dock-CLYsf1w_.js} +1 -1
  25. package/dist/gateway/static/root/assets/{computer-model-settings-WsDcg3Cu.js → computer-model-settings-DYWcZu2X.js} +1 -1
  26. package/dist/gateway/static/root/assets/{computer-settings-page-CqSG1Z5G.js → computer-settings-page-B4pC7mRs.js} +1 -1
  27. package/dist/gateway/static/root/assets/connectors-page-BVUnp0UV.js +2 -0
  28. package/dist/gateway/static/root/assets/{cron-expression-Dk8yzEE2.js → cron-expression-DKSgHs4I.js} +1 -1
  29. package/dist/gateway/static/root/assets/{date-picker-q4WA5x1M.js → date-picker-DRTglPVS.js} +1 -1
  30. package/dist/gateway/static/root/assets/{dependency-picker-zHQy10Yn.js → dependency-picker-_-7WsbTH.js} +1 -1
  31. package/dist/gateway/static/root/assets/{desktop-pet-DhkPZj6W.js → desktop-pet-CR7n2K67.js} +1 -1
  32. package/dist/gateway/static/root/assets/{desktop-pet-settings-BIwU_kdY.js → desktop-pet-settings-Bu7g83kd.js} +1 -1
  33. package/dist/gateway/static/root/assets/device-pairing-wizard-D5z7BvaO.js +1 -0
  34. package/dist/gateway/static/root/assets/{extension-debug-page-BBgQYoL7.js → extension-debug-page-BI8pvu2u.js} +1 -1
  35. package/dist/gateway/static/root/assets/{extension-page-D_cUtXzx.js → extension-page-EZC07dGv.js} +1 -1
  36. package/dist/gateway/static/root/assets/{extension-settings-page-C9W-nxmA.js → extension-settings-page-DLYC6gVA.js} +1 -1
  37. package/dist/gateway/static/root/assets/{gateway-config-swr-Dkc1As9z.js → gateway-config-swr-B2xVR9gt.js} +1 -1
  38. package/dist/gateway/static/root/assets/gateway-settings-C17LDur8.js +1 -0
  39. package/dist/gateway/static/root/assets/{gateway-startup-retry-DnLnaPev.js → gateway-startup-retry-c7s8h7px.js} +1 -1
  40. package/dist/gateway/static/root/assets/{heartbeat-settings-vqqTCSuv.js → heartbeat-settings-BAMr_vYa.js} +1 -1
  41. package/dist/gateway/static/root/assets/home-page-DG19Sjrn.js +1 -0
  42. package/dist/gateway/static/root/assets/{image-generation-api-DTXI5ZGN.js → image-generation-api-DUjbV88R.js} +1 -1
  43. package/dist/gateway/static/root/assets/{imports-page-DTqPlG9Z.js → imports-page-BaQmVh_d.js} +1 -1
  44. package/dist/gateway/static/root/assets/index-B7JCOaCc.js +81 -0
  45. package/dist/gateway/static/root/assets/index-Bt6a_Yxc.css +2 -0
  46. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-CGmRXeAV.js → keyboard-shortcuts-settings-DfNQK_hR.js} +1 -1
  47. package/dist/gateway/static/root/assets/local-app-workbench-page-h48NEuKs.js +2 -0
  48. package/dist/gateway/static/root/assets/{local-apps-page-8EVAomX7.js → local-apps-page-DFMrfoGF.js} +1 -1
  49. package/dist/gateway/static/root/assets/{locale-store-Bvh7i-qf.js → locale-store-CwuCkjo7.js} +1 -1
  50. package/dist/gateway/static/root/assets/{logs-page-hLfq_60v.js → logs-page-CD9MfQDD.js} +2 -2
  51. package/dist/gateway/static/root/assets/{management-settings-APknJ5Qn.js → management-settings-Dib_yIDQ.js} +1 -1
  52. package/dist/gateway/static/root/assets/{markdown-view-D5qRss8E.js → markdown-view-3YQc6QFV.js} +1 -1
  53. package/dist/gateway/static/root/assets/{media-plugins-DnchdxYn.js → media-plugins-DlZXjN6z.js} +1 -1
  54. package/dist/gateway/static/root/assets/messages-D6CRVWtT.js +3 -0
  55. package/dist/gateway/static/root/assets/note-detail-panel-CS0W3ESn.js +5 -0
  56. package/dist/gateway/static/root/assets/notes-page-BNMVGN6Y.js +8 -0
  57. package/dist/gateway/static/root/assets/{page-tabs-BlJpkM2b.js → page-tabs-BLzm5EXm.js} +1 -1
  58. package/dist/gateway/static/root/assets/{preview-open-alternatives-Db4HBrWW.js → preview-open-alternatives-B6QLKIZy.js} +1 -1
  59. package/dist/gateway/static/root/assets/{proactive-page-Cc35IKkK.js → proactive-page-W_uN2_ie.js} +1 -1
  60. package/dist/gateway/static/root/assets/{product-open-page-DZkUoQug.js → product-open-page-DLm3cUhY.js} +1 -1
  61. package/dist/gateway/static/root/assets/project-detail-page-Gfze4S0P.js +1 -0
  62. package/dist/gateway/static/root/assets/projects-page-DT7zmH0f.js +1 -0
  63. package/dist/gateway/static/root/assets/{registry-api-Z5aZS7Pw.js → registry-api-Cxx1-pCg.js} +1 -1
  64. package/dist/gateway/static/root/assets/remote-access-hub-CXQkXSU2.js +1 -0
  65. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-BerFoTmq.js → runtime-tools-settings-panel-DHkNbhpP.js} +2 -2
  66. package/dist/gateway/static/root/assets/{schema-form-BiEwDJQH.js → schema-form-BTLg2fN1.js} +1 -1
  67. package/dist/gateway/static/root/assets/sessions-page-CxTLRoxv.js +1 -0
  68. package/dist/gateway/static/root/assets/settings-advanced-gate-BgtDlDZh.js +1 -0
  69. package/dist/gateway/static/root/assets/{settings-form-section-064-WN2e.js → settings-form-section-D9rWLTBX.js} +1 -1
  70. package/dist/gateway/static/root/assets/{settings-page-CknC1u66.js → settings-page-BR9XJ0j9.js} +1 -1
  71. package/dist/gateway/static/root/assets/setup-status-panel-BSo76NwE.js +1 -0
  72. package/dist/gateway/static/root/assets/{share-preview-page-D_-ggSSf.js → share-preview-page-Dx2IpPEp.js} +1 -1
  73. package/dist/gateway/static/root/assets/shares-settings-BMF0Bo06.js +1 -0
  74. package/dist/gateway/static/root/assets/skill-reload-api-DNBw3BJR.js +1 -0
  75. package/dist/gateway/static/root/assets/{skills-page-J1NQE7pm.js → skills-page-CaX9EQtn.js} +1 -1
  76. package/dist/gateway/static/root/assets/{skills-page.utils-wM2PHuMb.js → skills-page.utils-CxWdcFKs.js} +1 -1
  77. package/dist/gateway/static/root/assets/{system-settings-panel-BpLdnG9I.js → system-settings-panel-BkiXkDSq.js} +1 -1
  78. package/dist/gateway/static/root/assets/task-detail-page-UBuGZQkl.js +1 -0
  79. package/dist/gateway/static/root/assets/{theme-store-DBrRX9jz.js → theme-store-Dt7ob2wD.js} +1 -1
  80. package/dist/gateway/static/root/assets/tool-activity-BtJkgjFf.js +1 -0
  81. package/dist/gateway/static/root/assets/tunnel-api-CEXwG7vJ.js +1 -0
  82. package/dist/gateway/static/root/assets/{use-autosave-M_tMNgrm.js → use-autosave-BHxjmV8l.js} +1 -1
  83. package/dist/gateway/static/root/assets/user-model-page-Dvtr3CPj.js +2 -0
  84. package/dist/gateway/static/root/assets/{voice-api-key-field-CYPTjigV.js → voice-api-key-field-BCNM-BaQ.js} +1 -1
  85. package/dist/gateway/static/root/assets/{voice-config-api-CzjqrH53.js → voice-config-api-De1DTzxK.js} +1 -1
  86. package/dist/gateway/static/root/assets/{work-discovery-overlay-CkjhVk1T.js → work-discovery-overlay-tig5sF-Q.js} +1 -1
  87. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-DadGKMJG.js → workflow-run-setup-panel-OcS30LVZ.js} +2 -2
  88. package/dist/gateway/static/root/assets/workflows-page-AqlMn0Bx.js +6 -0
  89. package/dist/gateway/static/root/index.html +11 -11
  90. package/dist/package.js +1 -1
  91. package/dist/packages/gateway-contract/src/tool-activity.js +48 -0
  92. package/dist/src/agent/memory/compaction.js +1 -1
  93. package/dist/src/agent/service/direct-turn-helpers.js +11 -4
  94. package/dist/src/agent/service/process-direct-streaming.js +2 -1
  95. package/dist/src/auth/oauth/xopc-cloud.js +22 -9
  96. package/dist/src/auth/oauth-provider-lock.js +29 -0
  97. package/dist/src/chat-commands/builtins/config.d.ts +1 -0
  98. package/dist/src/chat-commands/builtins/config.js +13 -9
  99. package/dist/src/chat-commands/builtins/model.js +2 -1
  100. package/dist/src/chat-commands/builtins/session.js +10 -7
  101. package/dist/src/chat-commands/builtins/skill.js +0 -1
  102. package/dist/src/chat-commands/builtins/thinking.js +12 -8
  103. package/dist/src/chat-commands/builtins/tts.js +7 -7
  104. package/dist/src/chat-commands/builtins/understanding.js +2 -1
  105. package/dist/src/chat-commands/command-parse.js +5 -6
  106. package/dist/src/chat-commands/context.js +19 -16
  107. package/dist/src/cli/commands/browser-cli-helpers.js +4 -1
  108. package/dist/src/config/schema.js +1 -1
  109. package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
  110. package/dist/src/gateway/chat-stream/mapper.js +1 -0
  111. package/dist/src/gateway/chat-stream/protocol.d.ts +2 -1
  112. package/dist/src/gateway/hono/routes/browser.js +3 -1
  113. package/dist/src/gateway/hono/routes/session-input-handler.d.ts +3 -1
  114. package/dist/src/gateway/hono/routes/session-input-handler.js +21 -1
  115. package/dist/src/gateway/hono/routes/tasks.js +1 -1
  116. package/dist/src/session/reset-triggers.d.ts +2 -0
  117. package/dist/src/session/reset-triggers.js +11 -1
  118. package/dist/src/session/turn-outcome-projector.js +1 -3
  119. package/dist/src/user-context/config.js +2 -2
  120. package/package.json +3 -3
  121. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-CjMbr_tR.js +0 -1
  122. package/dist/gateway/static/root/assets/agents-Cd56crFB.js +0 -1
  123. package/dist/gateway/static/root/assets/api-DPVNGtF-.js +0 -1
  124. package/dist/gateway/static/root/assets/apps-page-DKM50qxI.js +0 -1
  125. package/dist/gateway/static/root/assets/automations-workspace-BeT43VCC.js +0 -6
  126. package/dist/gateway/static/root/assets/browser-settings-page-41LCBsl6.js +0 -8
  127. package/dist/gateway/static/root/assets/capabilities-settings-panel-y-B20Hls.js +0 -8
  128. package/dist/gateway/static/root/assets/channels-settings-CSh7VaIN.js +0 -1
  129. package/dist/gateway/static/root/assets/connectors-page-DRJgZgHk.js +0 -2
  130. package/dist/gateway/static/root/assets/device-pairing-wizard-CYW-SA1I.js +0 -1
  131. package/dist/gateway/static/root/assets/gateway-settings-BzGDSxs8.js +0 -1
  132. package/dist/gateway/static/root/assets/home-page-Bg2FLMtL.js +0 -1
  133. package/dist/gateway/static/root/assets/index-BWAwKQMv.js +0 -75
  134. package/dist/gateway/static/root/assets/index-CdrKvNSO.css +0 -2
  135. package/dist/gateway/static/root/assets/local-app-workbench-page-D-Ak9bTF.js +0 -2
  136. package/dist/gateway/static/root/assets/messages-CRy5Dn65.js +0 -3
  137. package/dist/gateway/static/root/assets/note-detail-panel-D6Zx-Q4b.js +0 -5
  138. package/dist/gateway/static/root/assets/notes-page--sNX8iOX.js +0 -8
  139. package/dist/gateway/static/root/assets/project-detail-page-B03RQcfa.js +0 -1
  140. package/dist/gateway/static/root/assets/projects-page-C9DLyRWZ.js +0 -1
  141. package/dist/gateway/static/root/assets/remote-access-hub-DZBWTZrQ.js +0 -1
  142. package/dist/gateway/static/root/assets/sessions-page-B4hnQBev.js +0 -1
  143. package/dist/gateway/static/root/assets/settings-advanced-gate-NpUbJw3z.js +0 -1
  144. package/dist/gateway/static/root/assets/setup-status-panel-DI7Clo_p.js +0 -1
  145. package/dist/gateway/static/root/assets/shares-settings-Y0WYEYSb.js +0 -1
  146. package/dist/gateway/static/root/assets/skill-reload-api-FaKOLTFa.js +0 -1
  147. package/dist/gateway/static/root/assets/task-detail-page-BsDOJlNH.js +0 -1
  148. package/dist/gateway/static/root/assets/tool-activity-R8V9l6nk.js +0 -1
  149. package/dist/gateway/static/root/assets/tunnel-api-C4xWYFhH.js +0 -1
  150. package/dist/gateway/static/root/assets/user-model-page-82Rgg2gv.js +0 -2
  151. package/dist/gateway/static/root/assets/workflows-page-BEmYJRnU.js +0 -6
@@ -367,14 +367,24 @@ async function login(callbacks, options) {
367
367
  }) === "device_code" ? loginWithDeviceCode(callbacks, options.scope) : loginWithBrowser(callbacks, options.scope);
368
368
  }
369
369
  async function refreshToken(current, signal) {
370
- const response = await tokenRequest(new URLSearchParams({
371
- grant_type: "refresh_token",
372
- client_id: CLIENT_ID,
373
- refresh_token: current.refresh
374
- }), signal);
375
- const body = await readJson(response);
376
- if (!response.ok) throw new Error(body.error_description ?? body.error ?? "XOPC OAuth refresh failed");
377
- return credentials(body);
370
+ for (let attempt = 1; attempt <= 2; attempt += 1) try {
371
+ const attemptSignal = signal ? AbortSignal.any([signal, AbortSignal.timeout(REFRESH_ATTEMPT_TIMEOUT_MS)]) : AbortSignal.timeout(REFRESH_ATTEMPT_TIMEOUT_MS);
372
+ const response = await tokenRequest(new URLSearchParams({
373
+ grant_type: "refresh_token",
374
+ client_id: CLIENT_ID,
375
+ refresh_token: current.refresh
376
+ }), attemptSignal);
377
+ const body = await readJson(response);
378
+ if (!response.ok) {
379
+ const detail = body.error_description ?? body.error ?? "XOPC OAuth refresh failed";
380
+ throw new OAuthRefreshRejectedError(body.error === "invalid_grant" ? `XOPC authorization expired; reconnect the affected service in Settings. ${detail}` : detail);
381
+ }
382
+ return credentials(body);
383
+ } catch (error) {
384
+ if (error instanceof OAuthRefreshRejectedError || attempt === 2 || signal?.aborted) throw error;
385
+ await setTimeout$1(REFRESH_RETRY_DELAY_MS, void 0, signal ? { signal } : void 0);
386
+ }
387
+ throw new Error("XOPC OAuth refresh failed");
378
388
  }
379
389
  function createXopcOAuthProvider(options) {
380
390
  return {
@@ -389,7 +399,7 @@ function createXopcOAuthProvider(options) {
389
399
  getApiKey: (value) => value.access
390
400
  };
391
401
  }
392
- var CLIENT_ID, DEFAULT_CONSOLE_URL, MODEL_SERVICE_SCOPE, TUNNEL_REGISTRATION_SCOPE, HOSTED_SHARE_SCOPE, CALLBACK_PATH, AUTHORIZATION_TIMEOUT_MS, xopcCloudOAuthProvider, xopcTunnelOAuthProvider, xopcShareOAuthProvider;
402
+ var CLIENT_ID, DEFAULT_CONSOLE_URL, MODEL_SERVICE_SCOPE, TUNNEL_REGISTRATION_SCOPE, HOSTED_SHARE_SCOPE, CALLBACK_PATH, AUTHORIZATION_TIMEOUT_MS, REFRESH_ATTEMPT_TIMEOUT_MS, REFRESH_RETRY_DELAY_MS, OAuthRefreshRejectedError, xopcCloudOAuthProvider, xopcTunnelOAuthProvider, xopcShareOAuthProvider;
393
403
  var init_xopc_cloud = __esmMin((() => {
394
404
  init_pkce();
395
405
  init_resolution();
@@ -400,6 +410,9 @@ var init_xopc_cloud = __esmMin((() => {
400
410
  HOSTED_SHARE_SCOPE = "shares:read shares:write offline_access";
401
411
  CALLBACK_PATH = "/oauth/callback";
402
412
  AUTHORIZATION_TIMEOUT_MS = 5 * 6e4;
413
+ REFRESH_ATTEMPT_TIMEOUT_MS = 7e3;
414
+ REFRESH_RETRY_DELAY_MS = 100;
415
+ OAuthRefreshRejectedError = class extends Error {};
403
416
  xopcCloudOAuthProvider = createXopcOAuthProvider({
404
417
  id: "xopc-cloud",
405
418
  name: "XOPC Model Service",
@@ -15,6 +15,34 @@ function isAlreadyExists(error) {
15
15
  function isMissing(error) {
16
16
  return error?.code === "ENOENT";
17
17
  }
18
+ function isProcessMissing(error) {
19
+ return error?.code === "ESRCH";
20
+ }
21
+ async function removeAbandonedLock(path) {
22
+ let owner;
23
+ try {
24
+ owner = await readFile(path, "utf8");
25
+ } catch (error) {
26
+ if (isMissing(error)) return true;
27
+ throw error;
28
+ }
29
+ const pid = Number.parseInt(owner.split(":", 1)[0] ?? "", 10);
30
+ if (!Number.isSafeInteger(pid) || pid <= 0) return false;
31
+ try {
32
+ process.kill(pid, 0);
33
+ return false;
34
+ } catch (error) {
35
+ if (!isProcessMissing(error)) return false;
36
+ }
37
+ try {
38
+ if (await readFile(path, "utf8") !== owner) return false;
39
+ await rm(path, { force: true });
40
+ return true;
41
+ } catch (error) {
42
+ if (isMissing(error)) return true;
43
+ throw error;
44
+ }
45
+ }
18
46
  async function removeStaleLock(path) {
19
47
  try {
20
48
  const info = await stat(path);
@@ -47,6 +75,7 @@ async function acquireFileLock(providerId, options) {
47
75
  };
48
76
  } catch (error) {
49
77
  if (!isAlreadyExists(error)) throw error;
78
+ if (await removeAbandonedLock(path)) continue;
50
79
  if (await removeStaleLock(path)) continue;
51
80
  if (Date.now() >= deadline) throw new Error(`Timed out waiting for OAuth credential lock for ${providerId}`);
52
81
  await setTimeout(LOCK_RETRY_MS, void 0, options.signal ? { signal: options.signal } : void 0);
@@ -1,4 +1,5 @@
1
1
  /**
2
2
  * /config — read and write persistent config (~/.xopc/xopc.json) from chat.
3
3
  */
4
+ export declare function redactConfigForDisplay(value: unknown): unknown;
4
5
  export declare function registerConfigCommand(): void;
@@ -19,12 +19,13 @@ function isBlockedPath(path) {
19
19
  for (const blocked of BLOCKED_WRITE_PATHS) if (joined === blocked || joined.startsWith(`${blocked}.`)) return true;
20
20
  return false;
21
21
  }
22
- function redactConfigForDisplay(plain) {
23
- if (plain.gateway && typeof plain.gateway === "object") {
24
- const gw = plain.gateway;
25
- if (gw.auth && typeof gw.auth === "object") gw.auth.token = "[redacted]";
26
- }
27
- if (plain.providers !== void 0) plain.providers = "[redacted]";
22
+ const SENSITIVE_CONFIG_KEY = /(?:api[_-]?key|token|secret|password|passwd|credential|private[_-]?key|authorization|cookie)$/i;
23
+ function redactConfigForDisplay(value) {
24
+ if (Array.isArray(value)) return value.map(redactConfigForDisplay);
25
+ if (!value || typeof value !== "object") return value;
26
+ const out = {};
27
+ for (const [key, entry] of Object.entries(value)) out[key] = SENSITIVE_CONFIG_KEY.test(key) ? "[redacted]" : redactConfigForDisplay(entry);
28
+ return out;
28
29
  }
29
30
  function senderMayWritePersistentConfig(ctx) {
30
31
  const { source, senderId, isGroup } = ctx;
@@ -84,9 +85,12 @@ const configCommand = {
84
85
  const action = parts[0]?.toLowerCase();
85
86
  const rest = parts.slice(1).join(" ").trim();
86
87
  if (!action || action === "show" || action === "get") {
88
+ if (!senderMayWritePersistentConfig(ctx)) return {
89
+ content: "⚠️ You are not allowed to view configuration from this chat surface.",
90
+ success: false
91
+ };
87
92
  const config = ctx.getConfig?.() ?? loadConfig(resolveConfigPath());
88
- const plain = JSON.parse(JSON.stringify(config));
89
- redactConfigForDisplay(plain);
93
+ const plain = redactConfigForDisplay(JSON.parse(JSON.stringify(config)));
90
94
  if (rest) {
91
95
  const parsed = parseConfigPath(rest);
92
96
  if (!parsed.ok || !parsed.path) return {
@@ -183,4 +187,4 @@ function registerConfigCommand() {
183
187
  commandRegistry.register(configCommand);
184
188
  }
185
189
  //#endregion
186
- export { registerConfigCommand };
190
+ export { redactConfigForDisplay, registerConfigCommand };
@@ -78,7 +78,8 @@ const switchCommand = {
78
78
  const modelName = modelId.split("/").pop() || modelId;
79
79
  return {
80
80
  content: joinBlocks(`✅ Switched to ${code(modelId)} (${modelName}).`, "This model will be used for your next message."),
81
- success: true
81
+ success: true,
82
+ metadata: { sessionConfigChanged: true }
82
83
  };
83
84
  }
84
85
  return {
@@ -16,7 +16,7 @@ const newCommand = {
16
16
  await ctx.setTyping(true);
17
17
  await ctx.resetSession();
18
18
  return {
19
- content: "",
19
+ content: "✅ New session started. Previous transcript archived; model and session overrides kept.",
20
20
  success: true
21
21
  };
22
22
  }
@@ -42,7 +42,7 @@ const listCommand = {
42
42
  sessions.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
43
43
  const content = "📋 Your Sessions:\n\n" + sessions.slice(0, 10).map((s) => {
44
44
  const indicator = s.isActive ? "▶️" : " ";
45
- const name = getSessionDisplayName(s.key);
45
+ const name = s.name ?? getSessionDisplayName(s.key);
46
46
  const date = s.updatedAt.toLocaleDateString();
47
47
  return `${indicator} ${name}\n ${s.messageCount} messages · ${date}`;
48
48
  }).join("\n\n");
@@ -53,7 +53,7 @@ const listCommand = {
53
53
  type: "session-list",
54
54
  sessions: sessions.slice(0, 5).map((s) => ({
55
55
  ...s,
56
- name: getSessionDisplayName(s.key)
56
+ name: s.name ?? getSessionDisplayName(s.key)
57
57
  })),
58
58
  currentSession: ctx.conversationId
59
59
  }]
@@ -67,7 +67,7 @@ const listCommand = {
67
67
  const clearCommand = {
68
68
  id: "session.clear",
69
69
  name: "clear",
70
- description: "Clear current session without archiving",
70
+ description: "Clear current session and archive its previous transcript",
71
71
  category: "session",
72
72
  scope: [
73
73
  "global",
@@ -76,10 +76,9 @@ const clearCommand = {
76
76
  ],
77
77
  handler: async (ctx) => {
78
78
  await ctx.setTyping(true);
79
- const messages = await ctx.getSession();
80
- await ctx.clearSession();
79
+ await ctx.resetSession();
81
80
  return {
82
- content: `🗑️ Session cleared. ${messages.length} messages deleted.`,
81
+ content: "🗑️ Session cleared. Previous transcript archived.",
83
82
  success: true
84
83
  };
85
84
  }
@@ -226,6 +225,10 @@ const archiveCommand = {
226
225
  "group"
227
226
  ],
228
227
  handler: async (ctx) => {
228
+ if (ctx.channelId === "webchat") return {
229
+ content: "Archive this chat from the sidebar so the app can navigate away safely.",
230
+ success: false
231
+ };
229
232
  await ctx.setTyping(true);
230
233
  await ctx.archiveSession();
231
234
  return {
@@ -70,7 +70,6 @@ function parseInstallArgs(args) {
70
70
  const skillCommand = {
71
71
  id: "system.skill",
72
72
  name: "skill",
73
- aliases: ["skills"],
74
73
  description: "Install skills from explicit sources. Subcommands: install <source>",
75
74
  category: "tool",
76
75
  scope: ["global", "private"],
@@ -49,8 +49,9 @@ const thinkCommand = {
49
49
  xhigh: "Extra high thinking effort",
50
50
  max: "Maximum thinking effort",
51
51
  adaptive: "Auto-adjust based on task complexity"
52
- }[level]}\n\nApplies for this gateway process (until restart). Persists only if you use /config set.`,
53
- success: true
52
+ }[level]}\n\nSaved for this conversation until you change it.`,
53
+ success: true,
54
+ metadata: { sessionConfigChanged: true }
54
55
  };
55
56
  }
56
57
  };
@@ -88,8 +89,9 @@ const reasoningCommand = {
88
89
  off: "Hide reasoning from user",
89
90
  on: "Show reasoning after completion",
90
91
  stream: "Stream reasoning in real-time"
91
- }[level]}\n\nApplies for this gateway process (until restart). Persists only if you use /config set.`,
92
- success: true
92
+ }[level]}\n\nSaved for this conversation until you change it.`,
93
+ success: true,
94
+ metadata: { sessionConfigChanged: true }
93
95
  };
94
96
  }
95
97
  };
@@ -116,8 +118,9 @@ const verboseCommand = {
116
118
  const newLevel = await ctx.getVerboseLevel?.() === "on" ? "off" : "on";
117
119
  await setSessionAgentField(ctx.getSessionConfigStore?.(), ctx.conversationId, "verboseLevel", newLevel);
118
120
  return {
119
- content: `📝 *Verbose Mode*\n\nMode: *${newLevel}*\n\n${newLevel === "on" ? "Verbose output enabled (process lifetime)." : "Verbose output disabled (process lifetime)."}`,
120
- success: true
121
+ content: `📝 *Verbose Mode*\n\nMode: *${newLevel}*\n\n${newLevel === "on" ? "Verbose output enabled for this conversation." : "Verbose output disabled for this conversation."}`,
122
+ success: true,
123
+ metadata: { sessionConfigChanged: true }
121
124
  };
122
125
  }
123
126
  const level = normalizeVerboseLevel(args.trim());
@@ -131,8 +134,9 @@ const verboseCommand = {
131
134
  off: "Minimal output",
132
135
  on: "Normal verbose output",
133
136
  full: "Full verbose output with all details"
134
- }[level]}\n\nApplies for this gateway process (until restart). Persists only if you use /config set.`,
135
- success: true
137
+ }[level]}\n\nSaved for this conversation until you change it.`,
138
+ success: true,
139
+ metadata: { sessionConfigChanged: true }
136
140
  };
137
141
  }
138
142
  };
@@ -84,7 +84,7 @@ Voice: *${currentVoice}*
84
84
  switch (arg) {
85
85
  case "on":
86
86
  case "enable": {
87
- const success = await ctx.updateConfig?.("tts.enabled", true);
87
+ const success = await ctx.updateConfig?.("messages.tts.enabled", true);
88
88
  const base = success ? "✅ TTS enabled. Use `/tts always` or `/tts inbound` to set trigger mode." : "❌ Failed to enable TTS.";
89
89
  return {
90
90
  content: success ? appendTtsReadinessNote(base, ctx.getConfig?.()) : base,
@@ -93,7 +93,7 @@ Voice: *${currentVoice}*
93
93
  }
94
94
  case "off":
95
95
  case "disable": {
96
- const success = await ctx.updateConfig?.("tts.enabled", false);
96
+ const success = await ctx.updateConfig?.("messages.tts.enabled", false);
97
97
  return {
98
98
  content: success ? "✅ TTS disabled." : "❌ Failed to disable TTS.",
99
99
  success: !!success
@@ -103,8 +103,8 @@ Voice: *${currentVoice}*
103
103
  case "inbound":
104
104
  case "tagged": {
105
105
  const mode = arg;
106
- const success = await ctx.updateConfig?.("tts.trigger", mode);
107
- if (success && !isEnabled) await ctx.updateConfig?.("tts.enabled", true);
106
+ const success = await ctx.updateConfig?.("messages.tts.trigger", mode);
107
+ if (success && !isEnabled) await ctx.updateConfig?.("messages.tts.enabled", true);
108
108
  const base = success ? `✅ TTS trigger mode set to *${mode}*${!isEnabled ? " and TTS enabled" : ""}.` : `❌ Failed to set TTS trigger mode.`;
109
109
  return {
110
110
  content: success ? appendTtsReadinessNote(base, ctx.getConfig?.()) : base,
@@ -112,7 +112,7 @@ Voice: *${currentVoice}*
112
112
  };
113
113
  }
114
114
  case "never": {
115
- const success = await ctx.updateConfig?.("tts.trigger", "off");
115
+ const success = await ctx.updateConfig?.("messages.tts.trigger", "off");
116
116
  return {
117
117
  content: success ? "✅ TTS trigger mode set to *off*." : "❌ Failed to set TTS trigger mode.",
118
118
  success: !!success
@@ -165,7 +165,7 @@ Voice: *${currentVoice}*
165
165
  content: `❌ Invalid provider: ${provider}\nValid providers: openai, alibaba, minimax, edge`,
166
166
  success: false
167
167
  };
168
- const success = await ctx.updateConfig?.("tts.provider", provider);
168
+ const success = await ctx.updateConfig?.("messages.tts.provider", provider);
169
169
  const base = success ? `✅ TTS provider set to *${provider}*.` : "❌ Failed to set TTS provider.";
170
170
  return {
171
171
  content: success ? appendTtsReadinessNote(base, ctx.getConfig?.()) : base,
@@ -175,7 +175,7 @@ Voice: *${currentVoice}*
175
175
  if (subcommand === "voice" && subarg) {
176
176
  const voice = subarg;
177
177
  const provider = currentProvider;
178
- const success = await ctx.updateConfig?.(`tts.${provider}.voice`, voice);
178
+ const success = await ctx.updateConfig?.(`messages.tts.providers.${provider}.voice`, voice);
179
179
  return {
180
180
  content: success ? `✅ TTS voice set to *${voice}* for ${provider}.` : "❌ Failed to set TTS voice.",
181
181
  success: !!success
@@ -101,7 +101,8 @@ const learningCommand = {
101
101
  const mode = (await store.get(ctx.conversationId))?.userContextMode ?? "enabled";
102
102
  return {
103
103
  content: globallyEnabled && mode === "enabled" ? "User understanding is enabled for this conversation." : globallyEnabled && mode === "temporary" ? "This is a temporary conversation. Stored user context is not read, and this conversation does not update user understanding." : globallyEnabled ? "User understanding is off for this conversation. Existing chat history is still stored; use /remember for anything you explicitly want saved." : "User understanding is disabled globally. Existing chat history is still stored.",
104
- success: true
104
+ success: true,
105
+ ...action !== "status" ? { metadata: { sessionConfigChanged: true } } : {}
105
106
  };
106
107
  }
107
108
  };
@@ -5,8 +5,7 @@
5
5
  */
6
6
  function normalizeTelegramCommandName(command) {
7
7
  const at = command.indexOf("@");
8
- if (at === -1) return command;
9
- return command.slice(0, at);
8
+ return (at === -1 ? command : command.slice(0, at)).toLowerCase();
10
9
  }
11
10
  /**
12
11
  * Parse a `/command` from message text. Handles:
@@ -20,14 +19,14 @@ function parseSlashCommand(text) {
20
19
  const line = part.trim();
21
20
  if (!line.startsWith("/")) continue;
22
21
  const withoutPrefix = line.slice(1);
23
- const spaceIndex = withoutPrefix.indexOf(" ");
24
- if (spaceIndex === -1) return {
22
+ const whitespaceIndex = withoutPrefix.search(/\s/);
23
+ if (whitespaceIndex === -1) return {
25
24
  command: normalizeTelegramCommandName(withoutPrefix),
26
25
  args: ""
27
26
  };
28
27
  return {
29
- command: normalizeTelegramCommandName(withoutPrefix.slice(0, spaceIndex)),
30
- args: withoutPrefix.slice(spaceIndex + 1).trim()
28
+ command: normalizeTelegramCommandName(withoutPrefix.slice(0, whitespaceIndex)),
29
+ args: withoutPrefix.slice(whitespaceIndex + 1).trim()
31
30
  };
32
31
  }
33
32
  return null;
@@ -86,13 +86,6 @@ var CommandContextImpl = class {
86
86
  await this.clearSession();
87
87
  return;
88
88
  }
89
- await this.deps.bus.publishOutbound({
90
- channel: this.source,
91
- chat_id: this.chatId,
92
- content: "✅ New session started. Previous transcript archived; model and session overrides kept.",
93
- type: "message",
94
- metadata: this.outboundMetadata()
95
- });
96
89
  log.info({ conversationId: this.conversationId }, "Session reset");
97
90
  }
98
91
  async clearSession() {
@@ -120,15 +113,25 @@ var CommandContextImpl = class {
120
113
  log.info({ conversationId: this.conversationId }, "Session archived");
121
114
  }
122
115
  async listSessions() {
123
- const messages = await this.getSession();
124
- return [{
125
- key: this.conversationId,
126
- name: getSessionDisplayName(this.conversationId),
127
- messageCount: messages.length,
128
- createdAt: /* @__PURE__ */ new Date(),
129
- updatedAt: /* @__PURE__ */ new Date(),
130
- isActive: true
131
- }];
116
+ const { items } = await this.deps.sessionStore.list({
117
+ channel: this.channelId,
118
+ limit: 100,
119
+ sortBy: "updatedAt",
120
+ sortOrder: "desc"
121
+ });
122
+ return items.filter((item) => {
123
+ if (this.channelId === "webchat") return true;
124
+ const routing = item.routing;
125
+ if (!routing) return item.key === this.conversationId;
126
+ return routing.peerId === this.chatId && (!this.accountId || routing.accountId === this.accountId) && (!this.threadId || routing.threadId === this.threadId);
127
+ }).map((item) => ({
128
+ key: item.key,
129
+ name: item.name ?? getSessionDisplayName(item.key),
130
+ messageCount: item.messageCount,
131
+ createdAt: new Date(item.createdAt),
132
+ updatedAt: new Date(item.updatedAt),
133
+ isActive: item.key === this.conversationId
134
+ }));
132
135
  }
133
136
  async switchSession(conversationId) {
134
137
  log.info({
@@ -8,7 +8,10 @@ async function doctorCli() {
8
8
  console.log("Browser Control v2");
9
9
  console.log(`Driver: ${config.browser.driver.kind}`);
10
10
  if (!readiness) {
11
- console.log("Status: ready");
11
+ if (config.browser.driver.kind === "extension") {
12
+ console.log("Artifacts: ready");
13
+ console.log("Connection: not checked (use the Gateway browser status to verify the live extension endpoint)");
14
+ } else console.log("Status: ready");
12
15
  return;
13
16
  }
14
17
  console.error(`Status: ${readiness.hint.reason}`);
@@ -1,7 +1,7 @@
1
1
  import { __esmMin } from "../../_virtual/_rolldown/runtime.js";
2
+ import { ComputerConfigSchema, init_config } from "../computer/config.js";
2
3
  import { AgentModelsOverrideSchema, AgentsConfigSchema as AgentsConfigSchema$1, DEFAULT_AGENT_MODEL_REF, ImageGenerationRouteSchema, ModelRouteSchema } from "../agent-config/schema.js";
3
4
  import { init_agent_config } from "../agent-config/index.js";
4
- import { ComputerConfigSchema, init_config } from "../computer/config.js";
5
5
  import { DEFAULT_CONTEXT_COMPACTION_POLICY, UserContextConfigSchema, init_config as init_config$1 } from "../user-context/config.js";
6
6
  import { init_default_model } from "./default-model.js";
7
7
  import { init_public_url, validatePublicUrl } from "./public-url.js";
@@ -277,4 +277,4 @@ export declare const GATEWAY_BUILTIN_TOOLS: readonly [{
277
277
  readonly zh: "使用已验证的参数执行选定的外部工具。";
278
278
  };
279
279
  }];
280
- export declare const GATEWAY_BUILTIN_TOOL_IDS: ("automation" | "workflow" | "find" | "image" | "web_search" | "web_fetch" | "browser_use" | "grep" | "session_search" | "read_file" | "list_dir" | "write_file" | "apply_patch" | "exec_command" | "update_plan" | "todo" | "clarify" | "web_extract" | "image_generate" | "skills_list" | "skill_view" | "skill_manage" | "skills_marketplace_search" | "xopc_tool_search" | "xopc_tool_describe" | "xopc_tool_execute" | "user_context_search" | "user_context_get" | "user_context_update" | "knowledge_search" | "knowledge_get" | "knowledge_write" | "send_message" | "send_media" | "read_media" | "create_share" | "session_status" | "memory_maintenance" | "computer_use" | "tool_manual" | "delegate_task" | "skill_install" | "text_to_speech" | "execute_code" | "create_desktop_pet")[];
280
+ export declare const GATEWAY_BUILTIN_TOOL_IDS: ("automation" | "workflow" | "find" | "image" | "knowledge_search" | "knowledge_get" | "knowledge_write" | "user_context_search" | "user_context_get" | "user_context_update" | "web_search" | "web_fetch" | "browser_use" | "grep" | "session_search" | "read_file" | "list_dir" | "write_file" | "apply_patch" | "exec_command" | "update_plan" | "todo" | "clarify" | "web_extract" | "image_generate" | "skills_list" | "skill_view" | "skill_manage" | "skills_marketplace_search" | "xopc_tool_search" | "xopc_tool_describe" | "xopc_tool_execute" | "send_message" | "send_media" | "read_media" | "create_share" | "session_status" | "memory_maintenance" | "computer_use" | "tool_manual" | "delegate_task" | "skill_install" | "text_to_speech" | "execute_code" | "create_desktop_pet")[];
@@ -110,6 +110,7 @@ var ChatStreamMapper = class {
110
110
  const outcome = parseTurnOutcome(event.outcome);
111
111
  return outcome ? [this.make("turn_outcome", outcome)] : [];
112
112
  }
113
+ case "session_config_updated": return [this.make("session_config_updated", {})];
113
114
  case "error": return [this.make("error", {
114
115
  code: "AGENT_RUN_ERROR",
115
116
  message: String(event.content ?? event.message ?? "Unknown error"),
@@ -143,6 +143,7 @@ export type TurnDiffEvent = ChatStreamEnvelope<'turn_diff', {
143
143
  removed: number;
144
144
  }>;
145
145
  export type TurnOutcomeEvent = ChatStreamEnvelope<'turn_outcome', TurnOutcome>;
146
+ export type SessionConfigUpdatedEvent = ChatStreamEnvelope<'session_config_updated', Record<string, never>>;
146
147
  export type ReviewStartEvent = ChatStreamEnvelope<'review_start', {
147
148
  messageId: string;
148
149
  reviewId: string;
@@ -220,4 +221,4 @@ export type StreamErrorEvent = ChatStreamEnvelope<'error', {
220
221
  recoverable?: boolean;
221
222
  petFeedback: PetFeedback;
222
223
  }>;
223
- export type ChatStreamEvent = RunStartEvent | UserMessageEvent | UserTranscriptEvent | AssistantMessageStartEvent | AssistantDeltaEvent | ThinkingDeltaEvent | ThinkingEndEvent | ToolStartEvent | ToolUpdateEvent | ToolEndEvent | CommandStartedEvent | CommandOutputDeltaEvent | CommandCompletedEvent | PatchAppliedEvent | TurnPlanUpdatedEvent | TaskPlanUpdatedEvent | TurnDiffEvent | TurnOutcomeEvent | ReviewStartEvent | ReviewDeltaEvent | ReviewEndEvent | ReviewEvent | AssistantMessageEndEvent | ProgressEvent | CompactionEvent | TtsAudioEvent | ClarifyRequestEvent | RunEndEvent | StreamErrorEvent;
224
+ export type ChatStreamEvent = RunStartEvent | UserMessageEvent | UserTranscriptEvent | AssistantMessageStartEvent | AssistantDeltaEvent | ThinkingDeltaEvent | ThinkingEndEvent | ToolStartEvent | ToolUpdateEvent | ToolEndEvent | CommandStartedEvent | CommandOutputDeltaEvent | CommandCompletedEvent | PatchAppliedEvent | TurnPlanUpdatedEvent | TaskPlanUpdatedEvent | TurnDiffEvent | TurnOutcomeEvent | SessionConfigUpdatedEvent | ReviewStartEvent | ReviewDeltaEvent | ReviewEndEvent | ReviewEvent | AssistantMessageEndEvent | ProgressEvent | CompactionEvent | TtsAudioEvent | ClarifyRequestEvent | RunEndEvent | StreamErrorEvent;
@@ -18,6 +18,7 @@ async function extensionStatus(service, includeLocalArtifacts) {
18
18
  const connected = endpoints.length > 0;
19
19
  const extensionVersion = endpoint?.appVersion ?? null;
20
20
  const doctor = includeLocalArtifacts ? await import("../../../browser/providers/browser-ext-install.js").then(({ browserExtDoctor }) => browserExtDoctor({ runtimeExtensionVersion: extensionVersion ?? void 0 })) : void 0;
21
+ const setupState = connected ? "connected" : doctor ? doctor.installed === true ? "installed_not_connected" : "not_installed" : void 0;
21
22
  return {
22
23
  running: true,
23
24
  socketConnected: connected,
@@ -46,7 +47,8 @@ async function extensionStatus(service, includeLocalArtifacts) {
46
47
  } : {} },
47
48
  bridgeHeld: false,
48
49
  refCount: endpoints.length,
49
- transport: "gateway-realtime"
50
+ transport: "gateway-realtime",
51
+ ...setupState ? { setupState } : {}
50
52
  };
51
53
  }
52
54
  function isLocalOwnerRequest(c, service) {
@@ -1,6 +1,8 @@
1
1
  import type { Context } from 'hono';
2
2
  import type { AuthenticatedRouteDeps } from './deps.js';
3
- export declare function submitSessionInput(c: Context, deps: AuthenticatedRouteDeps, conversationId: string): Promise<(Response & import("hono").TypedResponse<{
3
+ export declare function submitSessionInput(c: Context, deps: AuthenticatedRouteDeps, conversationId: string, options?: {
4
+ taskConversation?: boolean;
5
+ }): Promise<(Response & import("hono").TypedResponse<{
4
6
  ok: false;
5
7
  error: {
6
8
  code: string;
@@ -3,6 +3,11 @@ import { init_src } from "../../../../packages/gateway-contract/src/index.js";
3
3
  import { endpointTurnClaimSchema, init_src as init_src$1 } from "../../../../packages/endpoint-tools-protocol/src/index.js";
4
4
  import { init_providers, resolveModel } from "../../../providers/index.js";
5
5
  import { validateWebchatAttachments, validateWebchatContent } from "../../chat-limits.js";
6
+ import { isTaskDestructiveCommand } from "../../../session/reset-triggers.js";
7
+ import { parseSlashCommand } from "../../../chat-commands/command-parse.js";
8
+ import { commandRegistry } from "../../../chat-commands/registry.js";
9
+ import "../../../chat-commands/index.js";
10
+ import { isVoiceLikeAttachment } from "../../../channels/attachments/voice-stt-webchat.js";
6
11
  import { parseTurnContextRefs } from "../../../agent/source-context/types.js";
7
12
  import { getModelThinking } from "../../../providers/model-thinking.js";
8
13
  import { withModelConfigLock } from "../../../session/model-config-lock.js";
@@ -12,7 +17,7 @@ init_src();
12
17
  init_src$1();
13
18
  init_providers();
14
19
  const MAX_TURN_CONTEXTS = 5;
15
- async function submitSessionInput(c, deps, conversationId) {
20
+ async function submitSessionInput(c, deps, conversationId, options) {
16
21
  const body = await c.req.json().catch(() => null);
17
22
  if (!body || !conversationId) return c.json({
18
23
  ok: false,
@@ -39,6 +44,21 @@ async function submitSessionInput(c, deps, conversationId) {
39
44
  }
40
45
  }, 400);
41
46
  const content = typeof body.content === "string" ? body.content : "";
47
+ if (options?.taskConversation && isTaskDestructiveCommand(content)) return c.json({
48
+ ok: false,
49
+ error: {
50
+ code: "BAD_REQUEST",
51
+ message: "A task has one continuous conversation."
52
+ }
53
+ }, 400);
54
+ const parsedCommand = parseSlashCommand(content);
55
+ if (parsedCommand && commandRegistry.has(parsedCommand.command) && attachments?.some((attachment) => !isVoiceLikeAttachment(attachment))) return c.json({
56
+ ok: false,
57
+ error: {
58
+ code: "BAD_REQUEST",
59
+ message: "Slash commands do not accept file or image attachments."
60
+ }
61
+ }, 400);
42
62
  const contentError = validateWebchatContent(content);
43
63
  if (contentError) return c.json({
44
64
  ok: false,
@@ -222,7 +222,7 @@ function registerTaskRoutes(authenticated, deps) {
222
222
  message: "Task executor changed; refresh the conversation"
223
223
  }
224
224
  }, 409);
225
- return submitSessionInput(c, deps, active.conversationId);
225
+ return submitSessionInput(c, deps, active.conversationId, { taskConversation: true });
226
226
  });
227
227
  authenticated.patch("/api/tasks/:id/conversation/config", taskRateLimit, async (c) => {
228
228
  const body = await c.req.json().catch(() => null);
@@ -17,4 +17,6 @@ export declare function resolveResetTriggers(configured?: string[]): readonly st
17
17
  export declare function bareResetAckMessage(matchedTrigger?: string): string;
18
18
  /** Slash command names that overlap reset triggers — skip when init already reset. */
19
19
  export declare const RESET_OVERLAP_COMMANDS: Set<string>;
20
+ export declare const TASK_DESTRUCTIVE_COMMANDS: Set<string>;
21
+ export declare function isTaskDestructiveCommand(body: string): boolean;
20
22
  export declare function shouldSkipResetOverlapCommand(command: string | undefined, resetTriggered: boolean): boolean;
@@ -53,9 +53,19 @@ const RESET_OVERLAP_COMMANDS = new Set([
53
53
  "reset",
54
54
  "restart"
55
55
  ]);
56
+ const TASK_DESTRUCTIVE_COMMANDS = new Set([
57
+ ...RESET_OVERLAP_COMMANDS,
58
+ "clear",
59
+ "archive"
60
+ ]);
61
+ function isTaskDestructiveCommand(body) {
62
+ const normalized = stripLeadingEnvelopeTimestamp(body.trim());
63
+ const match = /^\/([^\s@]+)(?:@[^\s]+)?(?:\s|$)/.exec(normalized);
64
+ return Boolean(match && TASK_DESTRUCTIVE_COMMANDS.has(match[1].toLowerCase()));
65
+ }
56
66
  function shouldSkipResetOverlapCommand(command, resetTriggered) {
57
67
  if (!resetTriggered || !command) return false;
58
68
  return RESET_OVERLAP_COMMANDS.has(command.toLowerCase());
59
69
  }
60
70
  //#endregion
61
- export { DEFAULT_RESET_TRIGGERS, RESET_OVERLAP_COMMANDS, bareResetAckMessage, matchResetTriggers, resolveResetTriggers, shouldSkipResetOverlapCommand, stripLeadingEnvelopeTimestamp };
71
+ export { DEFAULT_RESET_TRIGGERS, RESET_OVERLAP_COMMANDS, TASK_DESTRUCTIVE_COMMANDS, bareResetAckMessage, isTaskDestructiveCommand, matchResetTriggers, resolveResetTriggers, shouldSkipResetOverlapCommand, stripLeadingEnvelopeTimestamp };
@@ -74,7 +74,6 @@ function projectTurnOutcome(params) {
74
74
  const diffs = [];
75
75
  let added = 0;
76
76
  let removed = 0;
77
- let failedToolCount = 0;
78
77
  let createdAtMs = 0;
79
78
  let verificationSummary = null;
80
79
  for (const source of params.rows) {
@@ -85,7 +84,6 @@ function projectTurnOutcome(params) {
85
84
  if (Number.isFinite(timestamp)) createdAtMs = Math.max(createdAtMs, timestamp);
86
85
  if (row.role !== "toolResult" && row.role !== "tool") continue;
87
86
  const details = record(row.details) ?? {};
88
- if (row.isError === true && record(details.verification)?.kind !== "check") failedToolCount += 1;
89
87
  const explicit = Array.isArray(details.artifacts) ? details.artifacts.flatMap((value) => {
90
88
  const parsed = TurnOutcomeDeliverableSchema.safeParse(value);
91
89
  return parsed.success ? [parsed.data] : [];
@@ -149,7 +147,7 @@ function projectTurnOutcome(params) {
149
147
  const evidenceItems = [...evidence.values()];
150
148
  const collectedArtifacts = [...deliverables.values()];
151
149
  const artifactItems = mergeTurnOutcomeDeliverables(collectedArtifacts);
152
- const partial = !verificationSummary && failedToolCount > 0 || evidenceItems.some((item) => item.status !== "passed") || verificationSummary?.required === true && verificationSummary.changed === true && !evidenceItems.some((item) => item.status === "passed") || collectedArtifacts.some((item) => item.availability !== "available");
150
+ const partial = evidenceItems.some((item) => item.status !== "passed") || verificationSummary?.required === true && verificationSummary.changed === true && !evidenceItems.some((item) => item.status === "passed") || collectedArtifacts.some((item) => item.availability !== "available");
153
151
  const diff = diffs.join("\n");
154
152
  const diffTruncated = diff.length > MAX_OUTCOME_DIFF_CHARS;
155
153
  const createdAt = new Date(createdAtMs || Date.now()).toISOString();
@@ -20,7 +20,7 @@ var init_config = __esmMin((() => {
20
20
  minMessagesBeforeCompact: 10,
21
21
  keepRecentTokens: 2e4,
22
22
  recentTurnsPreserve: 3,
23
- summaryMaxTokens: 8e3,
23
+ summaryMaxTokens: 16e3,
24
24
  summaryChunkTokens: 24e3,
25
25
  summaryTimeoutMs: 18e4,
26
26
  summaryRetries: 2,
@@ -37,7 +37,7 @@ var init_config = __esmMin((() => {
37
37
  minMessagesBeforeCompact: z.number().int().min(2).default(10),
38
38
  keepRecentTokens: z.number().int().min(1e3).default(2e4),
39
39
  recentTurnsPreserve: z.number().int().min(1).max(12).default(3),
40
- summaryMaxTokens: z.number().int().min(256).default(8e3),
40
+ summaryMaxTokens: z.number().int().min(256).default(16e3),
41
41
  summaryChunkTokens: z.number().int().min(1e3).default(24e3),
42
42
  summaryTimeoutMs: z.number().int().min(1e3).max(6e5).default(18e4),
43
43
  summaryRetries: z.number().int().min(0).max(5).default(2),