@co0ontty/wand 4.94.0 → 4.95.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 (197) hide show
  1. package/README.md +4 -1
  2. package/dist/ai-text-runner.d.ts +4 -0
  3. package/dist/ai-text-runner.js +44 -4
  4. package/dist/build-info.json +3 -3
  5. package/dist/cli-api.d.ts +1 -0
  6. package/dist/cli-api.js +7 -2
  7. package/dist/cli.js +47 -0
  8. package/dist/conversation-execution-preference.d.ts +7 -0
  9. package/dist/conversation-execution-preference.js +26 -0
  10. package/dist/conversation-organize-contract.d.ts +70 -0
  11. package/dist/conversation-organize-contract.js +1 -0
  12. package/dist/conversation-organize-service.d.ts +46 -0
  13. package/dist/conversation-organize-service.js +505 -0
  14. package/dist/conversation-organize.d.ts +41 -0
  15. package/dist/conversation-organize.js +97 -0
  16. package/dist/conversation-service.d.ts +2 -1
  17. package/dist/conversation-service.js +10 -4
  18. package/dist/core-turn-tracker.d.ts +5 -1
  19. package/dist/core-turn-tracker.js +17 -1
  20. package/dist/decision-expert-service.d.ts +2 -0
  21. package/dist/decision-expert-service.js +17 -1
  22. package/dist/decision-skill.d.ts +0 -1
  23. package/dist/decision-skill.js +5 -64
  24. package/dist/deployment-lock.js +4 -1
  25. package/dist/employee-im-config.d.ts +17 -0
  26. package/dist/employee-im-config.js +85 -0
  27. package/dist/employee-im-lifecycle.d.ts +35 -0
  28. package/dist/employee-im-lifecycle.js +168 -0
  29. package/dist/employee-im-types.d.ts +80 -0
  30. package/dist/employee-im-types.js +32 -0
  31. package/dist/employee-im.d.ts +35 -0
  32. package/dist/employee-im.js +157 -0
  33. package/dist/employee-knowledge.js +3 -2
  34. package/dist/employee-text.d.ts +1 -0
  35. package/dist/employee-text.js +28 -13
  36. package/dist/git-quick-commit.js +21 -3
  37. package/dist/managed-skill.d.ts +6 -0
  38. package/dist/managed-skill.js +99 -0
  39. package/dist/model-auto-assign.js +1 -1
  40. package/dist/process-manager.js +1 -1
  41. package/dist/prompt-optimizer.js +1 -1
  42. package/dist/scheduled-task-activity.d.ts +16 -0
  43. package/dist/scheduled-task-activity.js +42 -0
  44. package/dist/scheduled-task-agenda.d.ts +20 -0
  45. package/dist/scheduled-task-agenda.js +82 -0
  46. package/dist/scheduled-task-calendar.d.ts +37 -0
  47. package/dist/scheduled-task-calendar.js +131 -0
  48. package/dist/scheduled-task-read-range.d.ts +7 -0
  49. package/dist/scheduled-task-read-range.js +35 -0
  50. package/dist/scheduled-task-service.d.ts +6 -1
  51. package/dist/scheduled-task-service.js +61 -1
  52. package/dist/scheduled-task-state.d.ts +1 -1
  53. package/dist/scheduled-task-types.d.ts +38 -0
  54. package/dist/server-asset-routes.js +3 -2
  55. package/dist/server-conversation-organize-routes.d.ts +4 -0
  56. package/dist/server-conversation-organize-routes.js +75 -0
  57. package/dist/server-conversation-routes.js +2 -1
  58. package/dist/server-employee-im-routes.d.ts +9 -0
  59. package/dist/server-employee-im-routes.js +40 -0
  60. package/dist/server-employee-routes.js +1 -1
  61. package/dist/server-scheduled-task-routes.js +24 -0
  62. package/dist/server-session-lifecycle-routes.js +8 -3
  63. package/dist/server-session-routes.js +18 -0
  64. package/dist/server-session-worktree-routes.js +3 -3
  65. package/dist/server-workspace-routes.d.ts +3 -0
  66. package/dist/server-workspace-routes.js +14 -1
  67. package/dist/server.js +39 -5
  68. package/dist/session-ai-context.d.ts +11 -1
  69. package/dist/session-ai-context.js +21 -2
  70. package/dist/session-metadata-service.js +1 -1
  71. package/dist/session-registry.js +6 -0
  72. package/dist/session-request.js +3 -0
  73. package/dist/session-topic.js +1 -1
  74. package/dist/silicon-employee-draft.js +1 -1
  75. package/dist/speech-polisher-service.d.ts +2 -0
  76. package/dist/speech-polisher-service.js +28 -6
  77. package/dist/storage/conversation-organization-repository.d.ts +13 -0
  78. package/dist/storage/conversation-organization-repository.js +33 -0
  79. package/dist/storage/migrations.js +10 -0
  80. package/dist/storage/secrets-repository.d.ts +7 -0
  81. package/dist/storage/secrets-repository.js +28 -1
  82. package/dist/storage/tasks-repository.js +4 -0
  83. package/dist/storage-schedules.d.ts +8 -1
  84. package/dist/storage-schedules.js +24 -0
  85. package/dist/storage-schema.js +9 -0
  86. package/dist/storage.d.ts +6 -1
  87. package/dist/storage.js +13 -0
  88. package/dist/structured-session-manager.d.ts +1 -0
  89. package/dist/structured-session-manager.js +18 -4
  90. package/dist/system-ai-call.d.ts +49 -0
  91. package/dist/system-ai-call.js +28 -0
  92. package/dist/system-ai-session-recorder.d.ts +30 -0
  93. package/dist/system-ai-session-recorder.js +239 -0
  94. package/dist/system-cli.d.ts +6 -0
  95. package/dist/system-cli.js +93 -0
  96. package/dist/system-skill.d.ts +5 -0
  97. package/dist/system-skill.js +19 -0
  98. package/dist/task-organize-contract.js +2 -2
  99. package/dist/task-title-organize-service.d.ts +1 -1
  100. package/dist/task-title-organize-service.js +9 -5
  101. package/dist/task-title-organize.js +13 -6
  102. package/dist/task-title-service.js +1 -1
  103. package/dist/task-title.js +1 -1
  104. package/dist/user-memory.js +1 -1
  105. package/dist/wand-task-sync.js +2 -1
  106. package/dist/web-ui/content/ai-teams.js +12 -12
  107. package/dist/web-ui/content/module-manifest.json +1 -1
  108. package/dist/web-ui/content/modules/{0051bd3d55f10ab7/library-bridge-DTMQIHVJ.js → 09842858f9d34a4e/library-bridge.js} +2 -2
  109. package/dist/web-ui/content/modules/1021ae40c5761e3f/sha256.js +1 -0
  110. package/dist/web-ui/content/modules/134f6120c0acaf57/host.js +1 -0
  111. package/dist/web-ui/content/modules/1d518ae6ed66fe5a/chunk.js +3 -0
  112. package/dist/web-ui/content/modules/1f93b5bd70381bb6/runtime.js +539 -0
  113. package/dist/web-ui/content/modules/26c68d4decdd18b4/markdown.js +1 -0
  114. package/dist/web-ui/content/modules/2ed8bafd5b4fecc5/workspace-window.js +1 -0
  115. package/dist/web-ui/content/modules/31ffc2f05f87652c/host.js +1 -0
  116. package/dist/web-ui/content/modules/35aaa85b9d0937af/chunk.js +20 -0
  117. package/dist/web-ui/content/modules/3664d0620f124292/chunk.js +3 -0
  118. package/dist/web-ui/content/modules/3cea480770cb184c/chunk.js +1 -0
  119. package/dist/web-ui/content/modules/3d12adf54647c1e2/host-bridge.js +1 -0
  120. package/dist/web-ui/content/modules/459cca2482a9a4ba/host.js +18 -0
  121. package/dist/web-ui/content/modules/474c9647130ef3e9/task-board-host.js +2 -0
  122. package/dist/web-ui/content/modules/4f74d2517f29134c/chunk.js +15 -0
  123. package/dist/web-ui/content/modules/52a218d74002a011/host.js +30 -0
  124. package/dist/web-ui/content/modules/54f2b10659ef8475/employee-card.js +1 -0
  125. package/dist/web-ui/content/modules/703fb1c670f7b199/chunk.js +1 -0
  126. package/dist/web-ui/content/modules/75511da23f051718/host.js +1 -0
  127. package/dist/web-ui/content/modules/{0051bd3d55f10ab7/host-2IVIZ2YC.js → 799b19da948bec8d/host.js} +3 -2
  128. package/dist/web-ui/content/modules/{0051bd3d55f10ab7/host-XNIR3NTJ.js → 7ef73b6c20d2504f/host.js} +3 -2
  129. package/dist/web-ui/content/modules/8f8f7a0a8874d29a/host.js +1 -0
  130. package/dist/web-ui/content/modules/90f2cd73cdf3a2cd/chat-render.js +25 -0
  131. package/dist/web-ui/content/modules/9510efce6caaa9ec/chunk.js +1 -0
  132. package/dist/web-ui/content/modules/9a0e65188739957f/chunk.js +67 -0
  133. package/dist/web-ui/content/modules/9eae1033f47e4526/host.js +1 -0
  134. package/dist/web-ui/content/modules/a2c5b246e57fa165/panel.js +1 -0
  135. package/dist/web-ui/content/modules/a50ce28f45bfeb2e/host.js +1 -0
  136. package/dist/web-ui/content/modules/adfc53789d5bedb2/chunk.js +1 -0
  137. package/dist/web-ui/content/modules/bcecb0cc14b7d2bd/chunk.js +1 -0
  138. package/dist/web-ui/content/modules/c4706f47c23f33e4/host.js +1 -0
  139. package/dist/web-ui/content/modules/{0051bd3d55f10ab7/host-Q524TMSR.js → c7de873095c061ab/host.js} +4 -3
  140. package/dist/web-ui/content/modules/d789f84a3f8765ae/page.js +6 -0
  141. package/dist/web-ui/content/modules/db4e2dbc7aed6353/chunk.js +1 -0
  142. package/dist/web-ui/content/modules/e4fd9906521b4aa3/chunk.js +1 -0
  143. package/dist/web-ui/content/modules/eb95c3ee0d42827a/chunk.js +6 -0
  144. package/dist/web-ui/content/modules/{0051bd3d55f10ab7/host-PHHNGVNL.js → ec68d93a95c1d5d6/host.js} +2 -2
  145. package/dist/web-ui/content/modules/f33ed89d1ccd3a2d/chunk.js +1 -0
  146. package/dist/web-ui/content/modules/f6502dab121f9d91/chunk.js +1 -0
  147. package/dist/web-ui/content/modules/fac83e61c0a8c8a7/host.js +1 -0
  148. package/dist/web-ui/content/scripts.js +1 -1
  149. package/dist/web-ui/embedded-assets.d.ts +200 -27
  150. package/dist/web-ui/embedded-assets.js +5 -4
  151. package/dist/web-ui/react/workspaces/sidebar-manage.d.ts +5 -0
  152. package/dist/web-ui/react/workspaces/sidebar-manage.js +27 -3
  153. package/dist/web-ui/running-activity.d.ts +22 -9
  154. package/dist/web-ui/running-activity.js +58 -42
  155. package/dist/web-ui/running-current-action.d.ts +12 -0
  156. package/dist/web-ui/running-current-action.js +74 -0
  157. package/dist/web-ui/scripts.d.ts +4 -0
  158. package/dist/web-ui/scripts.js +17 -8
  159. package/package.json +4 -2
  160. package/scripts/recover-web-update.mjs +26 -19
  161. package/scripts/recover-web-update.sh +9 -6
  162. package/scripts/update-node.sh +3 -1
  163. package/skills/wand-system/SKILL.md +69 -0
  164. package/skills/wand-system/references/operations.md +100 -0
  165. package/skills/wand-system/scripts/wand.mjs +34 -0
  166. package/dist/web-ui/content/modules/0051bd3d55f10ab7/chat-render-RY2I2CF3.js +0 -24
  167. package/dist/web-ui/content/modules/0051bd3d55f10ab7/chunk-2F6J2MKX.js +0 -1
  168. package/dist/web-ui/content/modules/0051bd3d55f10ab7/chunk-6C6A2RHG.js +0 -1
  169. package/dist/web-ui/content/modules/0051bd3d55f10ab7/chunk-7AFM245W.js +0 -1
  170. package/dist/web-ui/content/modules/0051bd3d55f10ab7/chunk-ECWYA3U5.js +0 -1
  171. package/dist/web-ui/content/modules/0051bd3d55f10ab7/chunk-MHX2QXIS.js +0 -111
  172. package/dist/web-ui/content/modules/0051bd3d55f10ab7/chunk-RAAPETNH.js +0 -1
  173. package/dist/web-ui/content/modules/0051bd3d55f10ab7/chunk-RXKBHYUR.js +0 -38
  174. package/dist/web-ui/content/modules/0051bd3d55f10ab7/chunk-T35D57FW.js +0 -1
  175. package/dist/web-ui/content/modules/0051bd3d55f10ab7/chunk-U4EDP4TI.js +0 -1
  176. package/dist/web-ui/content/modules/0051bd3d55f10ab7/chunk-WHXQR7JT.js +0 -20
  177. package/dist/web-ui/content/modules/0051bd3d55f10ab7/chunk-YOXXNXOX.js +0 -2
  178. package/dist/web-ui/content/modules/0051bd3d55f10ab7/chunk-YQJ6XYV7.js +0 -1
  179. package/dist/web-ui/content/modules/0051bd3d55f10ab7/employee-card-NV34TZIH.js +0 -1
  180. package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-2AQAIUNO.js +0 -1
  181. package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-AZXNFCRW.js +0 -1
  182. package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-EPEQX22R.js +0 -1
  183. package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-FZYIMOLC.js +0 -1
  184. package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-G6GL4SNL.js +0 -1
  185. package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-PEILC5NU.js +0 -1
  186. package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-RO5MB6RB.js +0 -69
  187. package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-WOQRK6NG.js +0 -1
  188. package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-XRVOIEP6.js +0 -1
  189. package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-ZDKOQEMS.js +0 -1
  190. package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-bridge-PMQZRG5G.js +0 -1
  191. package/dist/web-ui/content/modules/0051bd3d55f10ab7/page-PY4JUL4W.js +0 -3
  192. package/dist/web-ui/content/modules/0051bd3d55f10ab7/panel-BBQFC55Q.js +0 -1
  193. package/dist/web-ui/content/modules/0051bd3d55f10ab7/runtime.js +0 -974
  194. package/dist/web-ui/content/modules/0051bd3d55f10ab7/sha256-ANXV72UC.js +0 -1
  195. package/dist/web-ui/content/modules/0051bd3d55f10ab7/task-board-host-PXZSWBBG.js +0 -2
  196. package/dist/web-ui/content/modules/0051bd3d55f10ab7/workspace-agent-picker-DJSECSY6.js +0 -1
  197. package/dist/web-ui/content/modules/0051bd3d55f10ab7/workspace-window-MNMKSOBG.js +0 -1
@@ -225,4 +225,14 @@ export const STORAGE_MIGRATIONS = [
225
225
  PRIMARY KEY(file_path, session_id)
226
226
  );
227
227
  `) },
228
+ { version: 6, name: "conversation-organization-jobs", up: db => db.exec(`
229
+ CREATE TABLE IF NOT EXISTS conversation_organization_jobs (
230
+ id TEXT PRIMARY KEY, request_token TEXT NOT NULL UNIQUE, scope_key TEXT NOT NULL,
231
+ status TEXT NOT NULL, state_json TEXT NOT NULL, updated_at TEXT NOT NULL
232
+ );
233
+ CREATE INDEX IF NOT EXISTS idx_conversation_organization_running ON conversation_organization_jobs(status);
234
+ CREATE TABLE IF NOT EXISTS conversation_organization_cache (
235
+ scope_key TEXT PRIMARY KEY, fingerprint TEXT NOT NULL, updated_at TEXT NOT NULL
236
+ );
237
+ `) },
228
238
  ];
@@ -1,3 +1,5 @@
1
+ import { type EmployeeImConfig } from "../employee-im-types.js";
2
+ import { type EmployeeImSettings } from "../employee-im-config.js";
1
3
  import { DatabaseSync } from "node:sqlite";
2
4
  import { type PasswordVault, type PasswordVaultItem, type PasswordVaultItemFilter, type PasswordVaultItemInput } from "../password-manager.js";
3
5
  import type { ConnectorMeta } from "./mappers.js";
@@ -41,6 +43,11 @@ export declare class SecretsRepository {
41
43
  * Rows that cannot be read (no legacy secret, corrupt) are left as they are and counted.
42
44
  */
43
45
  private reencryptLegacyVaultRows;
46
+ /** Public configuration is a separate projection: no credential is returned to clients or models. */
47
+ getEmployeeImConfig(employeeId: string): EmployeeImConfig;
48
+ /** Server delivery path only; callers must never expose this object over HTTP/WS. */
49
+ getEmployeeImSettings(employeeId: string): EmployeeImSettings;
50
+ saveEmployeeImConfig(employeeId: string, input: unknown): EmployeeImConfig;
44
51
  /** Encrypt an at-rest secret with the v2 key; empty values stay NULL. */
45
52
  private encryptStoredSecret;
46
53
  private decryptPasswordItem;
@@ -1,4 +1,6 @@
1
1
  import crypto from "node:crypto";
2
+ import { emptyEmployeeImConfig } from "../employee-im-types.js";
3
+ import { normalizeStoredEmployeeImConfig, parseEmployeeImConfig } from "../employee-im-config.js";
2
4
  import { DEFAULT_PASSWORD_VAULT_ID, VaultDecryptError, decryptVaultSecretTyped, encryptVaultSecretV2, isVaultSecretV2, verifyAdminPassword, DEFAULT_PASSWORD_VAULT_NAME, itemMatchesFilter, normalizePasswordItemInput, normalizeVaultName, nowIso } from "../password-manager.js";
3
5
  import { hashAuthToken } from "./auth-token.js";
4
6
  import { loadVaultKey } from "./vault-keys.js";
@@ -66,7 +68,9 @@ export class SecretsRepository {
66
68
  .get(pattern, pattern, pattern);
67
69
  if (item)
68
70
  return true;
69
- return !!this.db.prepare("SELECT 1 FROM connectors WHERE token LIKE ? LIMIT 1").get(pattern);
71
+ if (this.db.prepare("SELECT 1 FROM connectors WHERE token LIKE ? LIMIT 1").get(pattern))
72
+ return true;
73
+ return !!this.db.prepare("SELECT 1 FROM employee_im_settings WHERE credentials LIKE ? LIMIT 1").get(pattern);
70
74
  }
71
75
  requireVaultCipherKey() {
72
76
  if (this.cipherKey)
@@ -127,6 +131,29 @@ export class SecretsRepository {
127
131
  console.warn(`[wand] ${failed} vault value(s) could not be re-encrypted and were left as stored`);
128
132
  return converted;
129
133
  }
134
+ /** Public configuration is a separate projection: no credential is returned to clients or models. */
135
+ getEmployeeImConfig(employeeId) {
136
+ const row = this.db.prepare("SELECT config_json FROM employee_im_settings WHERE employee_id = ?").get(employeeId);
137
+ return row ? normalizeStoredEmployeeImConfig(JSON.parse(row.config_json)) : emptyEmployeeImConfig();
138
+ }
139
+ /** Server delivery path only; callers must never expose this object over HTTP/WS. */
140
+ getEmployeeImSettings(employeeId) {
141
+ const row = this.db.prepare("SELECT credentials FROM employee_im_settings WHERE employee_id = ?").get(employeeId);
142
+ const raw = row?.credentials ? decryptVaultSecretTyped(row.credentials, this.vaultKeys()) : undefined;
143
+ return { config: this.getEmployeeImConfig(employeeId), credentials: raw ? JSON.parse(raw) : {} };
144
+ }
145
+ saveEmployeeImConfig(employeeId, input) {
146
+ return this.s.transaction(() => {
147
+ if (!this.s.getSiliconEmployeeDefinition(employeeId))
148
+ throw new Error("员工不存在。");
149
+ const settings = parseEmployeeImConfig(input, this.getEmployeeImSettings(employeeId));
150
+ const encrypted = this.encryptStoredSecret(JSON.stringify(settings.credentials));
151
+ this.db.prepare(`INSERT INTO employee_im_settings (employee_id, config_json, credentials) VALUES (?, ?, ?)
152
+ ON CONFLICT(employee_id) DO UPDATE SET config_json = excluded.config_json, credentials = excluded.credentials`)
153
+ .run(employeeId, JSON.stringify(settings.config), encrypted);
154
+ return settings.config;
155
+ });
156
+ }
130
157
  /** Encrypt an at-rest secret with the v2 key; empty values stay NULL. */
131
158
  encryptStoredSecret(value) {
132
159
  if (!value)
@@ -2,6 +2,7 @@ import crypto from "node:crypto";
2
2
  import { firstLayoutTabId } from "../layout-tree.js";
3
3
  import { nowIso } from "../password-manager.js";
4
4
  import { isSessionProvider } from "../session-provider.js";
5
+ import { isSystemAiCall } from "../system-ai-call.js";
5
6
  import { isThinkingEffort } from "../structured-provider-common.js";
6
7
  import { DEFAULT_ITERATION_NAME, DEFAULT_WAND_TASK_PRIORITY, isTaskExecutionSubject, normalizeWandTaskAgentMode } from "../task-types.js";
7
8
  import { GLOBAL_WORKSPACE_ID } from "../types.js";
@@ -475,6 +476,8 @@ export class TaskRepository {
475
476
  }
476
477
  /** Atomic exclusive ownership change; cwd, output and execution are untouched. */
477
478
  moveSessionToWorkspaceTask(sessionId, taskId) {
479
+ if (taskId && isSystemAiCall(this.s.getSessionSlim(sessionId)))
480
+ throw new Error("系统调用记录不能绑定到业务任务。");
478
481
  this.s.notifyTaskTitleChange();
479
482
  this.s.transaction(() => {
480
483
  const session = this.s.getSessionWorkspace(sessionId);
@@ -521,6 +524,7 @@ export class TaskRepository {
521
524
  const provider = session.provider;
522
525
  const agent = isSessionProvider(provider) ? {
523
526
  provider,
527
+ ...(session.structuredState?.engine === "core" ? { engine: "sdk" } : session.sessionKind === "pty" || session.structuredState?.engine === "cli" ? { engine: "cli" } : {}),
524
528
  model: session.selectedModel || session.structuredState?.model || "default",
525
529
  thinkingEffort: isThinkingEffort(session.thinkingEffort) ? session.thinkingEffort : "off",
526
530
  mode: normalizeWandTaskAgentMode(provider, session.mode),
@@ -1,5 +1,8 @@
1
1
  import type { DatabaseSync } from "node:sqlite";
2
- import type { ScheduledTask, ScheduledTaskRun } from "./scheduled-task-types.js";
2
+ import type { ScheduledTask, ScheduledTaskRun, ScheduledTaskRunFact } from "./scheduled-task-types.js";
3
+ /** Guard legacy non-ISO values before indexing: SQLite's special `now` input
4
+ * must never make a read timestamp depend on the current clock. */
5
+ export declare const SCHEDULED_CLAIM_DATE_SQL = "CASE WHEN json_extract(run_json, '$.claimedAt') GLOB '????-??-??T*' THEN julianday(json_extract(run_json, '$.claimedAt')) ELSE NULL END";
3
6
  /** Same SQLite connection and transaction owner as WandStorage; no separate lifecycle. */
4
7
  export declare class ScheduledTaskRepository {
5
8
  private db;
@@ -16,5 +19,9 @@ export declare class ScheduledTaskRepository {
16
19
  getScheduledTaskRun(id: string): ScheduledTaskRun | null;
17
20
  listOpenScheduledTaskRuns(taskId?: string): ScheduledTaskRun[];
18
21
  getOpenScheduledTaskRunsForSession(sessionId: string): ScheduledTaskRun[];
22
+ /** Date-indexed, lightweight facts; prompts never enter calendar/activity payloads. */
23
+ listScheduledTaskRunFacts(from: string, to: string, taskId?: string, limit?: number): ScheduledTaskRunFact[];
24
+ countInvalidScheduledTaskRunDates(taskId?: string): number;
25
+ latestScheduledTaskRun(taskId: string): ScheduledTaskRun | null;
19
26
  listScheduledTaskRuns(taskId?: string, limit?: number): ScheduledTaskRun[];
20
27
  }
@@ -1,3 +1,6 @@
1
+ /** Guard legacy non-ISO values before indexing: SQLite's special `now` input
2
+ * must never make a read timestamp depend on the current clock. */
3
+ export const SCHEDULED_CLAIM_DATE_SQL = "CASE WHEN json_extract(run_json, '$.claimedAt') GLOB '????-??-??T*' THEN julianday(json_extract(run_json, '$.claimedAt')) ELSE NULL END";
1
4
  /** Same SQLite connection and transaction owner as WandStorage; no separate lifecycle. */
2
5
  export class ScheduledTaskRepository {
3
6
  db;
@@ -68,6 +71,27 @@ export class ScheduledTaskRepository {
68
71
  .all(sessionId);
69
72
  return rows.map(row => JSON.parse(row.run_json));
70
73
  }
74
+ /** Date-indexed, lightweight facts; prompts never enter calendar/activity payloads. */
75
+ listScheduledTaskRunFacts(from, to, taskId, limit = 1001) {
76
+ const bounded = Math.max(1, Math.min(5001, Math.floor(limit)));
77
+ const date = SCHEDULED_CLAIM_DATE_SQL;
78
+ const where = `${taskId ? "task_id = ? AND " : ""}${date} >= julianday(?) AND ${date} <= julianday(?)`;
79
+ const args = taskId ? [taskId, from, to, bounded] : [from, to, bounded];
80
+ const rows = this.db.prepare(`SELECT json_remove(run_json, '$.prompt') AS fact_json FROM scheduled_task_runs
81
+ WHERE ${where} ORDER BY ${date} DESC, id DESC LIMIT ?`).all(...args);
82
+ return rows.map(row => JSON.parse(row.fact_json));
83
+ }
84
+ countInvalidScheduledTaskRunDates(taskId) {
85
+ const where = `(${SCHEDULED_CLAIM_DATE_SQL}) IS NULL`;
86
+ const row = (taskId ? this.db.prepare(`SELECT COUNT(*) AS count FROM scheduled_task_runs WHERE task_id = ? AND ${where}`).get(taskId)
87
+ : this.db.prepare(`SELECT COUNT(*) AS count FROM scheduled_task_runs WHERE ${where}`).get());
88
+ return row.count;
89
+ }
90
+ latestScheduledTaskRun(taskId) {
91
+ const row = this.db.prepare(`SELECT run_json FROM scheduled_task_runs WHERE task_id = ?
92
+ ORDER BY (${SCHEDULED_CLAIM_DATE_SQL}) DESC, id DESC LIMIT 1`).get(taskId);
93
+ return row ? JSON.parse(row.run_json) : null;
94
+ }
71
95
  listScheduledTaskRuns(taskId, limit = 50) {
72
96
  const bounded = Math.max(1, Math.min(500, Number.isFinite(limit) ? Math.floor(limit) : 50));
73
97
  const rows = (taskId
@@ -3,6 +3,7 @@ import { chmodSync, existsSync, mkdirSync } from "node:fs";
3
3
  import path from "node:path";
4
4
  import { DatabaseSync } from "node:sqlite";
5
5
  import { STORAGE_MIGRATIONS } from "./storage/migrations.js";
6
+ import { SCHEDULED_CLAIM_DATE_SQL } from "./storage-schedules.js";
6
7
  /** Additive schema only. Domain migrations and transactions remain owned by WandStorage. */
7
8
  const INIT_SQL = `
8
9
  CREATE TABLE IF NOT EXISTS auth_sessions (
@@ -300,6 +301,12 @@ const INIT_SQL = `
300
301
  );
301
302
  CREATE INDEX IF NOT EXISTS idx_silicon_employees_archived ON silicon_employees(archived_at);
302
303
 
304
+ CREATE TABLE IF NOT EXISTS employee_im_settings (
305
+ employee_id TEXT PRIMARY KEY REFERENCES silicon_employees(id) ON DELETE CASCADE,
306
+ config_json TEXT NOT NULL,
307
+ credentials TEXT
308
+ );
309
+
303
310
  CREATE TABLE IF NOT EXISTS employee_knowledge (
304
311
  id TEXT PRIMARY KEY,
305
312
  employee_id TEXT NOT NULL REFERENCES silicon_employees(id) ON DELETE CASCADE,
@@ -586,6 +593,8 @@ function applyBaselineSchema(db) {
586
593
  CREATE INDEX IF NOT EXISTS idx_scheduled_task_runs ON scheduled_task_runs(task_id, scheduled_at DESC);
587
594
  CREATE INDEX IF NOT EXISTS idx_scheduled_task_runs_session ON scheduled_task_runs(json_extract(run_json, '$.receipt.sessionId'));
588
595
  CREATE INDEX IF NOT EXISTS idx_scheduled_task_runs_open ON scheduled_task_runs(json_extract(run_json, '$.executionFinishedAt'), json_extract(run_json, '$.state'));
596
+ CREATE INDEX IF NOT EXISTS idx_scheduled_task_runs_claimed ON scheduled_task_runs((${SCHEDULED_CLAIM_DATE_SQL}), id);
597
+ CREATE INDEX IF NOT EXISTS idx_scheduled_task_runs_task_claimed ON scheduled_task_runs(task_id, (${SCHEDULED_CLAIM_DATE_SQL}), id);
589
598
  `);
590
599
  ensureAuthSessionSchema(db);
591
600
  ensureCommandSessionSchema(db);
package/dist/storage.d.ts CHANGED
@@ -3,6 +3,7 @@ import type { ConnectorMeta } from "./storage/mappers.js";
3
3
  import { parseWandTaskAgent } from "./storage/mappers.js";
4
4
  export * from "./storage/internal.js";
5
5
  import { ConversationRepository } from "./storage/conversations-repository.js";
6
+ import { ConversationOrganizationRepository } from "./storage/conversation-organization-repository.js";
6
7
  import { PreferencesRepository } from "./storage/preferences-repository.js";
7
8
  import { WorkspaceRepository } from "./storage/workspaces-repository.js";
8
9
  import { TaskRepository } from "./storage/tasks-repository.js";
@@ -20,10 +21,11 @@ export { ensureDatabaseFile } from "./storage-schema.js";
20
21
  type RepositoryMethods<T> = Pick<T, {
21
22
  [K in keyof T]: T[K] extends (...args: never[]) => unknown ? K : never;
22
23
  }[keyof T]>;
23
- export interface WandStorage extends RepositoryMethods<ConversationRepository>, RepositoryMethods<PreferencesRepository>, RepositoryMethods<WorkspaceRepository>, RepositoryMethods<TaskRepository>, RepositoryMethods<SecretsRepository>, RepositoryMethods<EmployeeRepository>, RepositoryMethods<UserMemoryRepository>, RepositoryMethods<AiTeamRepository>, RepositoryMethods<MissionRepository>, RepositoryMethods<SessionRepository>, RepositoryMethods<UploadRepository> {
24
+ export interface WandStorage extends RepositoryMethods<ConversationRepository>, RepositoryMethods<ConversationOrganizationRepository>, RepositoryMethods<PreferencesRepository>, RepositoryMethods<WorkspaceRepository>, RepositoryMethods<TaskRepository>, RepositoryMethods<SecretsRepository>, RepositoryMethods<EmployeeRepository>, RepositoryMethods<UserMemoryRepository>, RepositoryMethods<AiTeamRepository>, RepositoryMethods<MissionRepository>, RepositoryMethods<SessionRepository>, RepositoryMethods<UploadRepository> {
24
25
  }
25
26
  export declare class WandStorage {
26
27
  readonly conversations: ConversationRepository;
28
+ readonly conversationOrganizations: ConversationOrganizationRepository;
27
29
  readonly preferences: PreferencesRepository;
28
30
  readonly workspaces: WorkspaceRepository;
29
31
  readonly tasks: TaskRepository;
@@ -83,4 +85,7 @@ export declare class WandStorage {
83
85
  listOpenScheduledTaskRuns(...args: Parameters<ScheduledTaskRepository["listOpenScheduledTaskRuns"]>): ReturnType<ScheduledTaskRepository["listOpenScheduledTaskRuns"]>;
84
86
  getOpenScheduledTaskRunsForSession(...args: Parameters<ScheduledTaskRepository["getOpenScheduledTaskRunsForSession"]>): ReturnType<ScheduledTaskRepository["getOpenScheduledTaskRunsForSession"]>;
85
87
  listScheduledTaskRuns(...args: Parameters<ScheduledTaskRepository["listScheduledTaskRuns"]>): ReturnType<ScheduledTaskRepository["listScheduledTaskRuns"]>;
88
+ listScheduledTaskRunFacts(...args: Parameters<ScheduledTaskRepository["listScheduledTaskRunFacts"]>): ReturnType<ScheduledTaskRepository["listScheduledTaskRunFacts"]>;
89
+ countInvalidScheduledTaskRunDates(...args: Parameters<ScheduledTaskRepository["countInvalidScheduledTaskRunDates"]>): ReturnType<ScheduledTaskRepository["countInvalidScheduledTaskRunDates"]>;
90
+ latestScheduledTaskRun(...args: Parameters<ScheduledTaskRepository["latestScheduledTaskRun"]>): ReturnType<ScheduledTaskRepository["latestScheduledTaskRun"]>;
86
91
  }
package/dist/storage.js CHANGED
@@ -6,6 +6,7 @@ import { openStorageDatabase } from "./storage-schema.js";
6
6
  import { parseWandTaskAgent } from "./storage/mappers.js";
7
7
  export * from "./storage/internal.js";
8
8
  import { ConversationRepository } from "./storage/conversations-repository.js";
9
+ import { ConversationOrganizationRepository } from "./storage/conversation-organization-repository.js";
9
10
  import { PreferencesRepository } from "./storage/preferences-repository.js";
10
11
  import { WorkspaceRepository } from "./storage/workspaces-repository.js";
11
12
  import { TaskRepository } from "./storage/tasks-repository.js";
@@ -20,6 +21,7 @@ export { parseWandTaskAgent };
20
21
  export { ensureDatabaseFile } from "./storage-schema.js";
21
22
  export class WandStorage {
22
23
  conversations;
24
+ conversationOrganizations;
23
25
  preferences;
24
26
  workspaces;
25
27
  tasks;
@@ -81,6 +83,7 @@ export class WandStorage {
81
83
  this.dbPath = dbPath;
82
84
  this.db = openStorageDatabase(dbPath);
83
85
  this.conversations = new ConversationRepository(this.db, this);
86
+ this.conversationOrganizations = new ConversationOrganizationRepository(this.db);
84
87
  this.preferences = new PreferencesRepository(this.db, this);
85
88
  this.workspaces = new WorkspaceRepository(this.db, this);
86
89
  this.tasks = new TaskRepository(this.db, this);
@@ -221,9 +224,19 @@ export class WandStorage {
221
224
  listScheduledTaskRuns(...args) {
222
225
  return this.schedules.listScheduledTaskRuns(...args);
223
226
  }
227
+ listScheduledTaskRunFacts(...args) {
228
+ return this.schedules.listScheduledTaskRunFacts(...args);
229
+ }
230
+ countInvalidScheduledTaskRunDates(...args) {
231
+ return this.schedules.countInvalidScheduledTaskRunDates(...args);
232
+ }
233
+ latestScheduledTaskRun(...args) {
234
+ return this.schedules.latestScheduledTaskRun(...args);
235
+ }
224
236
  }
225
237
  const REPOSITORY_FORWARDS = [
226
238
  ["conversations", ConversationRepository],
239
+ ["conversationOrganizations", ConversationOrganizationRepository],
227
240
  ["preferences", PreferencesRepository],
228
241
  ["workspaces", WorkspaceRepository],
229
242
  ["tasks", TaskRepository],
@@ -102,6 +102,7 @@ export declare class StructuredSessionManager {
102
102
  */
103
103
  private readonly seenIdempotencyKeys;
104
104
  private readonly relayHandlers;
105
+ private readonly systemCallDrainReleases;
105
106
  private emitEvent;
106
107
  /**
107
108
  * 静默期心跳:回合在飞且连续 TURN_QUIET_HEARTBEAT_MS 没有任何事件时,用现有的
@@ -25,6 +25,7 @@ import { resolveSessionCwd } from "./session-cwd.js";
25
25
  import { isScheduledConversationSession } from "./conversation-session-preview.js";
26
26
  import { isSessionProvider, providerCliCommand } from "./session-provider.js";
27
27
  import { resolveSessionProvider, resolveSystemAiContext } from "./session-ai-context.js";
28
+ import { isSystemAiCall, SYSTEM_AI_READ_ONLY } from "./system-ai-call.js";
28
29
  import { CodexRunner } from "./structured-codex-adapter.js";
29
30
  import { ClaudeCliRunner } from "./structured-claude-adapter.js";
30
31
  import { OpenCodeRunner } from "./structured-opencode-adapter.js";
@@ -124,6 +125,7 @@ export class StructuredSessionManager {
124
125
  */
125
126
  seenIdempotencyKeys = new BoundedKeySet(IDEMPOTENCY_KEY_LIMIT);
126
127
  relayHandlers = new Map();
128
+ systemCallDrainReleases = new Set();
127
129
  emitEvent = null;
128
130
  /**
129
131
  * 静默期心跳:回合在飞且连续 TURN_QUIET_HEARTBEAT_MS 没有任何事件时,用现有的
@@ -194,7 +196,7 @@ export class StructuredSessionManager {
194
196
  });
195
197
  // SDK capability is probed only when Wand Agent is explicitly selected.
196
198
  for (const snapshot of this.storage.loadSessions()) {
197
- if ((snapshot.sessionKind ?? "pty") !== "structured")
199
+ if (isSystemAiCall(snapshot) || (snapshot.sessionKind ?? "pty") !== "structured")
198
200
  continue;
199
201
  const restoredStatus = snapshot.status === "running" ? "idle" : snapshot.status;
200
202
  let provider;
@@ -295,12 +297,16 @@ export class StructuredSessionManager {
295
297
  if (this.disposed)
296
298
  throw new Error("StructuredSessionManager has been disposed.");
297
299
  this.coreRestartDrains += 1;
300
+ const releaseSystemCalls = CoreTurnTracker.beginSystemCallDrain();
301
+ this.systemCallDrainReleases.add(releaseSystemCalls);
298
302
  let released = false;
299
303
  return () => {
300
304
  if (released)
301
305
  return;
302
306
  released = true;
303
307
  this.coreRestartDrains -= 1;
308
+ releaseSystemCalls();
309
+ this.systemCallDrainReleases.delete(releaseSystemCalls);
304
310
  if (this.coreRestartDrains === 0)
305
311
  this.resumeQueuedPiMessages();
306
312
  };
@@ -322,6 +328,9 @@ export class StructuredSessionManager {
322
328
  if (this.disposed)
323
329
  return;
324
330
  this.disposed = true;
331
+ for (const release of this.systemCallDrainReleases)
332
+ release();
333
+ this.systemCallDrainReleases.clear();
325
334
  this.unacceptedTeamStarts.clear();
326
335
  // 静默心跳不跨服务关闭存活(会话级 timer 不允许泄漏)。
327
336
  this.turnHeartbeat.dispose();
@@ -1115,6 +1124,8 @@ export class StructuredSessionManager {
1115
1124
  this.metadata.generateTopicForPrompt(id, input);
1116
1125
  }
1117
1126
  createSession(options) {
1127
+ if (isSystemAiCall(options))
1128
+ throw new Error(SYSTEM_AI_READ_ONLY);
1118
1129
  if (this.disposed)
1119
1130
  throw new Error("StructuredSessionManager has been disposed.");
1120
1131
  const id = randomUUID();
@@ -1546,7 +1557,7 @@ export class StructuredSessionManager {
1546
1557
  prompt,
1547
1558
  groups: this.config.modelGroups,
1548
1559
  evaluate: this.autoAssignEvaluate(),
1549
- ai: resolveSystemAiContext(session, this.config, this.storage.getSystemSiliconEmployee(), this.openRouter),
1560
+ ai: resolveSystemAiContext(session, this.config, this.storage.getSystemSiliconEmployee(), this.openRouter, { storage: this.storage }),
1550
1561
  cwd: session.cwd,
1551
1562
  language: this.config.language,
1552
1563
  });
@@ -2232,7 +2243,9 @@ export class StructuredSessionManager {
2232
2243
  }
2233
2244
  this.saveAuthoritativeSession(cancelled);
2234
2245
  // 仍发 "ended" 事件让各端停掉"回复中"指示 / 灵动岛,但携带的 status 是 idle。
2235
- this.emitStructuredSnapshot(cancelled, "ended");
2246
+ this.emitStructuredSnapshot(cancelled, "ended", {
2247
+ ...(session.structuredState?.inFlight || session.status === "running" ? { turnOutcome: "stopped" } : {}),
2248
+ });
2236
2249
  return cancelled;
2237
2250
  }
2238
2251
  delete(id) {
@@ -2339,7 +2352,7 @@ export class StructuredSessionManager {
2339
2352
  });
2340
2353
  return true;
2341
2354
  }
2342
- emitStructuredSnapshot(session, eventType = "output") {
2355
+ emitStructuredSnapshot(session, eventType = "output", outcome = {}) {
2343
2356
  // 排队消息只通过 payload.queuedMessages 单独下发,由各端在消息卡片外的「排队条」
2344
2357
  // 里纵向渲染——绝不再把它们当成 __queued 占位 turn 混进 messages 消息流里,否则会
2345
2358
  // 和排队条重复显示(旧的「显示异常」根因)。
@@ -2348,6 +2361,7 @@ export class StructuredSessionManager {
2348
2361
  ...payload,
2349
2362
  status: session.status,
2350
2363
  exitCode: session.exitCode,
2364
+ ...outcome,
2351
2365
  };
2352
2366
  this.emit({
2353
2367
  type: eventType,
@@ -0,0 +1,49 @@
1
+ import type { AiCliCandidate, AiTextRequest } from "./types.js";
2
+ /** Reserved server-owned provenance. These are real one-shot calls, not resumable conversations. */
3
+ export declare const SYSTEM_AI_CALL_PREFIX = "system-ai:";
4
+ export declare const SYSTEM_AI_READ_ONLY = "\u8FD9\u662F\u7CFB\u7EDF\u8C03\u7528\u8BB0\u5F55\uFF0C\u53EA\u80FD\u67E5\u770B\u65E5\u5FD7\uFF0C\u4E0D\u80FD\u7EE7\u7EED\u53D1\u9001\u6216\u6062\u590D\u6267\u884C\u3002";
5
+ export declare function isSystemAiCall(session: {
6
+ automationId?: unknown;
7
+ } | null | undefined): boolean;
8
+ export declare const SYSTEM_AI_PURPOSES: {
9
+ readonly "system-text": "系统文本生成";
10
+ readonly "session-title": "会话标题生成";
11
+ readonly "task-title": "任务标题生成";
12
+ readonly "task-organize": "旧版任务标题归类";
13
+ readonly "conversation-organize-job": "会话自动归类作业";
14
+ readonly "conversation-organize": "会话内容任务规划";
15
+ readonly "conversation-summary": "长会话内容摘要";
16
+ readonly "prompt-optimize": "提示词优化";
17
+ readonly "git-commit": "提交说明生成";
18
+ readonly "git-tag": "版本说明生成";
19
+ readonly "git-fallback": "提交执行兜底";
20
+ readonly "model-auto-assign": "模型分组选择";
21
+ readonly "user-memory": "短期习惯分析";
22
+ readonly "employee-draft": "员工招聘草稿";
23
+ readonly decision: "有界决策";
24
+ readonly "speech-polish": "口述文字整理";
25
+ };
26
+ export type SystemAiPurpose = keyof typeof SYSTEM_AI_PURPOSES;
27
+ export interface AiCallTrace {
28
+ readonly sessionId: string;
29
+ candidateStarted(candidate: AiCliCandidate): void;
30
+ localStarted(model: string): void;
31
+ candidateSkipped(candidate: AiCliCandidate, reason: string): void;
32
+ candidateFailed(candidate: AiCliCandidate, error: unknown, phase: "availability" | "execution" | "result"): void;
33
+ candidateSucceeded(raw: string, parsed: unknown): void;
34
+ usageReported(usage: {
35
+ inputTokens: number;
36
+ outputTokens: number;
37
+ model: string;
38
+ }): void;
39
+ applicationResult(value: unknown): void;
40
+ complete(): void;
41
+ fail(error: unknown, cancelled?: boolean): void;
42
+ }
43
+ export interface AiCallTraceFactory {
44
+ start(purpose: SystemAiPurpose, request: AiTextRequest, cwd: string): AiCallTrace;
45
+ }
46
+ /** Audit failure is not a provider refusal and must never trigger another paid candidate. */
47
+ export declare class AiCallTraceError extends Error {
48
+ constructor();
49
+ }
@@ -0,0 +1,28 @@
1
+ /** Reserved server-owned provenance. These are real one-shot calls, not resumable conversations. */
2
+ export const SYSTEM_AI_CALL_PREFIX = "system-ai:";
3
+ export const SYSTEM_AI_READ_ONLY = "这是系统调用记录,只能查看日志,不能继续发送或恢复执行。";
4
+ export function isSystemAiCall(session) {
5
+ return typeof session?.automationId === "string" && session.automationId.startsWith(SYSTEM_AI_CALL_PREFIX);
6
+ }
7
+ export const SYSTEM_AI_PURPOSES = {
8
+ "system-text": "系统文本生成",
9
+ "session-title": "会话标题生成",
10
+ "task-title": "任务标题生成",
11
+ "task-organize": "旧版任务标题归类",
12
+ "conversation-organize-job": "会话自动归类作业",
13
+ "conversation-organize": "会话内容任务规划",
14
+ "conversation-summary": "长会话内容摘要",
15
+ "prompt-optimize": "提示词优化",
16
+ "git-commit": "提交说明生成",
17
+ "git-tag": "版本说明生成",
18
+ "git-fallback": "提交执行兜底",
19
+ "model-auto-assign": "模型分组选择",
20
+ "user-memory": "短期习惯分析",
21
+ "employee-draft": "员工招聘草稿",
22
+ "decision": "有界决策",
23
+ "speech-polish": "口述文字整理",
24
+ };
25
+ /** Audit failure is not a provider refusal and must never trigger another paid candidate. */
26
+ export class AiCallTraceError extends Error {
27
+ constructor() { super("系统调用日志无法保存,已停止后续模型调用。"); this.name = "AiCallTraceError"; }
28
+ }
@@ -0,0 +1,30 @@
1
+ import type { SiliconEmployee } from "./employee-types.js";
2
+ import { type ConversationSessionUpdate } from "./conversation-types.js";
3
+ import type { SessionLogger } from "./session-logger.js";
4
+ import type { WandStorage } from "./storage.js";
5
+ import type { ProcessEvent } from "./types.js";
6
+ import { type AiCallTraceFactory } from "./system-ai-call.js";
7
+ /** Only copies business text. Never logs credential commands, runtime config, or provider error bodies. */
8
+ export declare function redactSystemAiText(value: string): string;
9
+ export declare class SystemAiSessionRecorder {
10
+ private readonly storage;
11
+ private closed;
12
+ private readonly active;
13
+ private logger?;
14
+ private emit?;
15
+ private notifyConversation?;
16
+ constructor(storage: WandStorage);
17
+ attach(options: {
18
+ logger?: SessionLogger;
19
+ emit?: (event: ProcessEvent) => void;
20
+ notifyConversation?: (update: ConversationSessionUpdate) => void;
21
+ }): void;
22
+ /** A server-owned one-shot has no detached runner to adopt after a restart. Never replay it. */
23
+ recoverInterrupted(): void;
24
+ forEmployee(employee: SiliconEmployee, sourceSessionId?: string): AiCallTraceFactory;
25
+ remove(sessionId: string): void;
26
+ appendOutcome(sessionId: string, label: string, value: unknown): void;
27
+ private persist;
28
+ private start;
29
+ }
30
+ export declare function systemAiSessions(storage: WandStorage): SystemAiSessionRecorder;