@co0ontty/wand 4.56.1 → 4.57.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 (120) hide show
  1. package/dist/build-info.json +3 -3
  2. package/dist/server-session-routes.js +4 -4
  3. package/dist/structured-permission.d.ts +14 -0
  4. package/dist/structured-permission.js +56 -0
  5. package/dist/structured-session-manager.d.ts +9 -0
  6. package/dist/structured-session-manager.js +178 -0
  7. package/dist/taskboard-bridge.js +12 -1
  8. package/dist/web-ui/content/scripts.js +70 -70
  9. package/dist/web-ui/embedded-assets.js +1 -1
  10. package/package.json +1 -1
  11. package/vendor/codex-taskboard/dist/web/assets/AiChat-BNn4VJ5a.js +12 -0
  12. package/vendor/codex-taskboard/dist/web/assets/GanttView-D8wnfGrG.css +1 -0
  13. package/vendor/codex-taskboard/dist/web/assets/GanttView-rGWhuEX9.js +39 -0
  14. package/vendor/codex-taskboard/dist/web/assets/abnfDiagram-N423BO3Z-DbFG6lbA.js +1 -0
  15. package/vendor/codex-taskboard/dist/web/assets/arc-BjSQqbzd.js +1 -0
  16. package/vendor/codex-taskboard/dist/web/assets/architecture-TIHT7OUA-C9-t8p4o.js +1 -0
  17. package/vendor/codex-taskboard/dist/web/assets/architectureDiagram-T3A2C74G-Db4kJZwO.js +36 -0
  18. package/vendor/codex-taskboard/dist/web/assets/array-BifhSqXX.js +1 -0
  19. package/vendor/codex-taskboard/dist/web/assets/blockDiagram-VBNYF7ZC-C-BywuWV.js +132 -0
  20. package/vendor/codex-taskboard/dist/web/assets/c4Diagram-5PPSVZJV-Dq-80rjP.js +10 -0
  21. package/vendor/codex-taskboard/dist/web/assets/channel-C7mnz8Xc.js +1 -0
  22. package/vendor/codex-taskboard/dist/web/assets/chunk-2GRJ4B5K-B2G57us9.js +1 -0
  23. package/vendor/codex-taskboard/dist/web/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
  24. package/vendor/codex-taskboard/dist/web/assets/chunk-3NCLNEKW-DVGIRib_.js +1 -0
  25. package/vendor/codex-taskboard/dist/web/assets/chunk-4I5QYGJK-kNoGFqr0.js +1 -0
  26. package/vendor/codex-taskboard/dist/web/assets/chunk-5RXB4S5H-4iv6-xM6.js +231 -0
  27. package/vendor/codex-taskboard/dist/web/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
  28. package/vendor/codex-taskboard/dist/web/assets/chunk-6Q2QTUOP-1J34vxHr.js +88 -0
  29. package/vendor/codex-taskboard/dist/web/assets/chunk-7BUUIJ7U-Bb538aSH.js +1 -0
  30. package/vendor/codex-taskboard/dist/web/assets/chunk-7Z6QIM7H-CDQpQlxX.js +10 -0
  31. package/vendor/codex-taskboard/dist/web/assets/chunk-GF5L2VYU-Dg92oHHl.js +206 -0
  32. package/vendor/codex-taskboard/dist/web/assets/chunk-I66GZJ75-Bk8_gFs2.js +125 -0
  33. package/vendor/codex-taskboard/dist/web/assets/chunk-J7OUQ5F2-CJRcenlr.js +2 -0
  34. package/vendor/codex-taskboard/dist/web/assets/chunk-JQJVKLGR-BLBV_Sus.js +156 -0
  35. package/vendor/codex-taskboard/dist/web/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
  36. package/vendor/codex-taskboard/dist/web/assets/chunk-KBJHAD2P-Da9Yjz0p.js +1 -0
  37. package/vendor/codex-taskboard/dist/web/assets/chunk-KEIR6QF5-Dj-OpFgW.js +161 -0
  38. package/vendor/codex-taskboard/dist/web/assets/chunk-NSK5VX7P-Cb56tnGK.js +2 -0
  39. package/vendor/codex-taskboard/dist/web/assets/chunk-QR6OTTB3-BwoszTxn.js +62 -0
  40. package/vendor/codex-taskboard/dist/web/assets/chunk-RYQCIY6F-D_L2RdcQ.js +1 -0
  41. package/vendor/codex-taskboard/dist/web/assets/chunk-UBXNYLIW-YX5mN8cl.js +1 -0
  42. package/vendor/codex-taskboard/dist/web/assets/chunk-W5SLKNZC-DlOLhhDO.js +1 -0
  43. package/vendor/codex-taskboard/dist/web/assets/chunk-WRU74C26-B7SBmP0K.js +70 -0
  44. package/vendor/codex-taskboard/dist/web/assets/chunk-XXDRQBXY-BuE3VzE_.js +1 -0
  45. package/vendor/codex-taskboard/dist/web/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
  46. package/vendor/codex-taskboard/dist/web/assets/chunk-ZIRB5QZD-C6fEPe3t.js +32 -0
  47. package/vendor/codex-taskboard/dist/web/assets/classDiagram-JCYQIIEL-KIdCVvbA.js +1 -0
  48. package/vendor/codex-taskboard/dist/web/assets/classDiagram-v2-OCEON4UE-KIdCVvbA.js +1 -0
  49. package/vendor/codex-taskboard/dist/web/assets/cose-bilkent-JH36ORCC-BzQvy5TC.js +1 -0
  50. package/vendor/codex-taskboard/dist/web/assets/cynefin-VYW2F7L2-D1_5O1XR.js +1 -0
  51. package/vendor/codex-taskboard/dist/web/assets/cynefinDiagram-MW4NZA55-DOW_U5QN.js +62 -0
  52. package/vendor/codex-taskboard/dist/web/assets/cytoscape.esm-DnGd5Fuo.js +321 -0
  53. package/vendor/codex-taskboard/dist/web/assets/dagre-VZM6K2ZE-0Dntc3NT.js +4 -0
  54. package/vendor/codex-taskboard/dist/web/assets/dagre-dpRSp0QF.js +1 -0
  55. package/vendor/codex-taskboard/dist/web/assets/defaultLocale-C8Fc0cco.js +1 -0
  56. package/vendor/codex-taskboard/dist/web/assets/diagram-7IWD3JNH-D7GleYAM.js +30 -0
  57. package/vendor/codex-taskboard/dist/web/assets/diagram-B4RE2ZJO-DCXTe6q5.js +3 -0
  58. package/vendor/codex-taskboard/dist/web/assets/diagram-LBJQPF4R-ZFJyXEXl.js +24 -0
  59. package/vendor/codex-taskboard/dist/web/assets/diagram-Q27KOJAE-DUGCuGd9.js +24 -0
  60. package/vendor/codex-taskboard/dist/web/assets/diagram-UB23O5K3-DRmp4Nng.js +41 -0
  61. package/vendor/codex-taskboard/dist/web/assets/dist-D9sYb5Oa.js +1 -0
  62. package/vendor/codex-taskboard/dist/web/assets/ebnfDiagram-BXEA7PRR-CkKoaf7T.js +1 -0
  63. package/vendor/codex-taskboard/dist/web/assets/erDiagram-JOGREHBK-nBX5z4R4.js +85 -0
  64. package/vendor/codex-taskboard/dist/web/assets/eventmodeling-45OFAUF4-pexmK65N.js +1 -0
  65. package/vendor/codex-taskboard/dist/web/assets/flowDiagram-UKHOOZJN-aroohRbL.js +1 -0
  66. package/vendor/codex-taskboard/dist/web/assets/ganttDiagram-PKOTCBZU-BOJWUTXa.js +292 -0
  67. package/vendor/codex-taskboard/dist/web/assets/gitGraph-TEB2WS4Q-DxnuhSZU.js +1 -0
  68. package/vendor/codex-taskboard/dist/web/assets/gitGraphDiagram-DS77QQ5N-Cte975TC.js +106 -0
  69. package/vendor/codex-taskboard/dist/web/assets/graphlib-DS17s2tU.js +1 -0
  70. package/vendor/codex-taskboard/dist/web/assets/index-Docmx4uQ.css +1 -0
  71. package/vendor/codex-taskboard/dist/web/assets/index-Du07lEGz.js +121 -0
  72. package/vendor/codex-taskboard/dist/web/assets/info-DKCQHKI2-DOTjgYxk.js +1 -0
  73. package/vendor/codex-taskboard/dist/web/assets/infoDiagram-6WML65LV-SpyV9UJd.js +2 -0
  74. package/vendor/codex-taskboard/dist/web/assets/init-D6jRqBbL.js +1 -0
  75. package/vendor/codex-taskboard/dist/web/assets/ishikawaDiagram-WSZJBQD7-T0PZwoUZ.js +70 -0
  76. package/vendor/codex-taskboard/dist/web/assets/journeyDiagram-NVQOT4AX-B88b8UgX.js +139 -0
  77. package/vendor/codex-taskboard/dist/web/assets/kanban-definition-27J2QSJJ-CIlfNnl_.js +89 -0
  78. package/vendor/codex-taskboard/dist/web/assets/katex-B7rAX3Vi.js +257 -0
  79. package/vendor/codex-taskboard/dist/web/assets/line-fWrwPZhB.js +1 -0
  80. package/vendor/codex-taskboard/dist/web/assets/linear-B7l8qgEw.js +1 -0
  81. package/vendor/codex-taskboard/dist/web/assets/map-BaFkSB1l.js +1 -0
  82. package/vendor/codex-taskboard/dist/web/assets/mermaid-parser.core-C5gls2nC.js +7 -0
  83. package/vendor/codex-taskboard/dist/web/assets/mermaid.core-POz2Xcvr.js +11 -0
  84. package/vendor/codex-taskboard/dist/web/assets/mindmap-definition-FAOFIHXS-BM0-egZ5.js +96 -0
  85. package/vendor/codex-taskboard/dist/web/assets/ordinal-hYBb2elL.js +1 -0
  86. package/vendor/codex-taskboard/dist/web/assets/packet-7NZHBO7P-DQZzyVos.js +1 -0
  87. package/vendor/codex-taskboard/dist/web/assets/path-BWPyau1x.js +1 -0
  88. package/vendor/codex-taskboard/dist/web/assets/pegDiagram-VL7TDLO6-B8aEwl-7.js +1 -0
  89. package/vendor/codex-taskboard/dist/web/assets/pie-RZYD4A2V-DRUflilR.js +1 -0
  90. package/vendor/codex-taskboard/dist/web/assets/pieDiagram-7S7Q4E2Y-0cDDxlua.js +39 -0
  91. package/vendor/codex-taskboard/dist/web/assets/purify.es-Bwt4ulIK.js +3 -0
  92. package/vendor/codex-taskboard/dist/web/assets/quadrantDiagram-CIZ2JOQS-CBgb0YZ0.js +7 -0
  93. package/vendor/codex-taskboard/dist/web/assets/radar-I7S5WNFK-tRocoKxq.js +1 -0
  94. package/vendor/codex-taskboard/dist/web/assets/railroad-3IZDKUUU-DaS4ZlX_.js +1 -0
  95. package/vendor/codex-taskboard/dist/web/assets/railroad-abnf-AHOZXSZD-BN1T5dBT.js +1 -0
  96. package/vendor/codex-taskboard/dist/web/assets/railroad-ebnf-EBAXGLYW-Cuf8qyKs.js +1 -0
  97. package/vendor/codex-taskboard/dist/web/assets/railroad-peg-LSFZ7HO6-B511_EYq.js +1 -0
  98. package/vendor/codex-taskboard/dist/web/assets/railroadDiagram-AXF67PYL-QtPo-JZb.js +1 -0
  99. package/vendor/codex-taskboard/dist/web/assets/requirementDiagram-LRYGKXZP-CuBIF85r.js +84 -0
  100. package/vendor/codex-taskboard/dist/web/assets/rolldown-runtime-QTnfLwEv.js +1 -0
  101. package/vendor/codex-taskboard/dist/web/assets/rough.esm-CSKSodPl.js +1 -0
  102. package/vendor/codex-taskboard/dist/web/assets/sankeyDiagram-W5VNT64P-CRA-b_za.js +40 -0
  103. package/vendor/codex-taskboard/dist/web/assets/sequenceDiagram-SI44F4Z6-CoddVWwY.js +162 -0
  104. package/vendor/codex-taskboard/dist/web/assets/sizeCapture-X5ZJPWSS-B0uUizjq.js +1 -0
  105. package/vendor/codex-taskboard/dist/web/assets/src-_wZywoZs.js +1 -0
  106. package/vendor/codex-taskboard/dist/web/assets/stateDiagram-OKZ733FA-D8DMM2ln.js +1 -0
  107. package/vendor/codex-taskboard/dist/web/assets/stateDiagram-v2-UEYNNEHI-CVp0eYAS.js +1 -0
  108. package/vendor/codex-taskboard/dist/web/assets/swimlanes-SLNWSIFB-C1fC7EFu.js +2 -0
  109. package/vendor/codex-taskboard/dist/web/assets/swimlanesDiagram-ULZ7WXOC-CPd58Xso.js +8 -0
  110. package/vendor/codex-taskboard/dist/web/assets/timeline-definition-Z64GVDOM-BUBv8aKP.js +120 -0
  111. package/vendor/codex-taskboard/dist/web/assets/treeView-QDETBFTQ-C-Bve2kY.js +1 -0
  112. package/vendor/codex-taskboard/dist/web/assets/treemap-6X3UGDF4-BcphHDdV.js +1 -0
  113. package/vendor/codex-taskboard/dist/web/assets/vennDiagram-T6HMQDX7-0wQnwAyA.js +34 -0
  114. package/vendor/codex-taskboard/dist/web/assets/wardley-OPB4EBWU-CJZ4BevB.js +1 -0
  115. package/vendor/codex-taskboard/dist/web/assets/wardleyDiagram-T6FBY63Y-U9nwPrkN.js +78 -0
  116. package/vendor/codex-taskboard/dist/web/assets/xychartDiagram-ELKLHX3M-CyCqnzgE.js +7 -0
  117. package/vendor/codex-taskboard/dist/web/codex-agent-logo.png +0 -0
  118. package/vendor/codex-taskboard/dist/web/codex-app-icon.png +0 -0
  119. package/vendor/codex-taskboard/dist/web/favicon.svg +7 -0
  120. package/vendor/codex-taskboard/dist/web/index.html +20 -0
@@ -1,6 +1,6 @@
1
1
  {
2
- "commit": "477c21d747e18e9f4df6942ab0f47bd817f66ff0",
3
- "builtAt": "2026-09-10T03:53:28.222Z",
4
- "version": "4.56.1",
2
+ "commit": "d240da17b4bfee37b304e0a51c90506979357d8c",
3
+ "builtAt": "2026-09-10T04:56:25.817Z",
4
+ "version": "4.57.0",
5
5
  "channel": "stable"
6
6
  }
@@ -1395,7 +1395,7 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
1395
1395
  app.post("/api/sessions/:id/approve-permission", (req, res) => {
1396
1396
  try {
1397
1397
  if (sessions.ownerOf(req.params.id) === "structured") {
1398
- res.status(404).json({ error: "结构化会话没有运行时授权请求。" });
1398
+ res.json(sessionResponseDTO(structured.approvePermission(req.params.id)));
1399
1399
  return;
1400
1400
  }
1401
1401
  const snapshot = sessions.get(req.params.id);
@@ -1412,7 +1412,7 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
1412
1412
  app.post("/api/sessions/:id/deny-permission", (req, res) => {
1413
1413
  try {
1414
1414
  if (sessions.ownerOf(req.params.id) === "structured") {
1415
- res.status(404).json({ error: "结构化会话没有运行时授权请求。" });
1415
+ res.json(sessionResponseDTO(structured.denyPermission(req.params.id)));
1416
1416
  return;
1417
1417
  }
1418
1418
  const snapshot = sessions.get(req.params.id);
@@ -1429,7 +1429,7 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
1429
1429
  app.post("/api/sessions/:id/toggle-auto-approve", (req, res) => {
1430
1430
  try {
1431
1431
  if (sessions.ownerOf(req.params.id) === "structured") {
1432
- res.status(404).json({ error: "结构化会话没有运行时授权请求。" });
1432
+ res.json(sessionResponseDTO(structured.toggleAutoApprove(req.params.id)));
1433
1433
  return;
1434
1434
  }
1435
1435
  const snapshot = sessions.get(req.params.id);
@@ -1453,7 +1453,7 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
1453
1453
  return;
1454
1454
  }
1455
1455
  if (sessions.ownerOf(req.params.id) === "structured") {
1456
- res.status(404).json({ error: "结构化会话没有运行时授权请求。" });
1456
+ res.json(sessionResponseDTO(structured.resolveEscalation(req.params.id, requestId, resolution)));
1457
1457
  return;
1458
1458
  }
1459
1459
  res.json(sessionResponseDTO(processes.resolveEscalation(req.params.id, requestId, resolution)));
@@ -0,0 +1,14 @@
1
+ import type { EscalationRequest } from "./types.js";
2
+ export interface StructuredToolPermissionContext {
3
+ title?: string;
4
+ description?: string;
5
+ blockedPath?: string;
6
+ displayName?: string;
7
+ toolUseID?: string;
8
+ decisionReason?: string;
9
+ }
10
+ export declare function inferStructuredEscalation(toolName: string, input: Record<string, unknown>, ctx?: StructuredToolPermissionContext): Pick<EscalationRequest, "scope" | "target" | "reason">;
11
+ export declare function structuredPermissionDenied(message: string): {
12
+ behavior: "deny";
13
+ message: string;
14
+ };
@@ -0,0 +1,56 @@
1
+ const FILE_TOOLS = new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
2
+ const COMMAND_TOOLS = new Set(["Bash", "BashOutput"]);
3
+ const NETWORK_TOOLS = new Set(["WebFetch", "WebSearch"]);
4
+ const DANGEROUS_SHELL_RE = /\bsudo\b|\brm\s+-[rf]{1,2}\b|\bmkfs\b|\bdd\s+if=|\bshutdown\b|\breboot\b/i;
5
+ function firstString(input, keys) {
6
+ for (const key of keys) {
7
+ const value = input[key];
8
+ if (typeof value === "string" && value.trim())
9
+ return value.trim();
10
+ }
11
+ return undefined;
12
+ }
13
+ function truncateTarget(value) {
14
+ if (!value)
15
+ return undefined;
16
+ const compact = value.replace(/\s+/g, " ").trim();
17
+ if (!compact)
18
+ return undefined;
19
+ return compact.length > 200 ? `${compact.slice(0, 197)}...` : compact;
20
+ }
21
+ export function inferStructuredEscalation(toolName, input, ctx = {}) {
22
+ let scope = "unknown";
23
+ let target = firstString(input, ["file_path", "path", "command", "url", "query", "description", "prompt", "name"]);
24
+ if (FILE_TOOLS.has(toolName)) {
25
+ scope = "write_file";
26
+ target = firstString(input, ["file_path", "path"]) ?? target;
27
+ }
28
+ else if (COMMAND_TOOLS.has(toolName)) {
29
+ const command = firstString(input, ["command"]) ?? target;
30
+ scope = command && DANGEROUS_SHELL_RE.test(command) ? "dangerous_shell" : "run_command";
31
+ target = command;
32
+ }
33
+ else if (NETWORK_TOOLS.has(toolName)) {
34
+ scope = "network";
35
+ target = firstString(input, ["url", "query"]) ?? target;
36
+ }
37
+ else if (typeof ctx.blockedPath === "string" && ctx.blockedPath.trim()) {
38
+ scope = "outside_workspace";
39
+ target = ctx.blockedPath.trim();
40
+ }
41
+ if (!target && typeof ctx.blockedPath === "string" && ctx.blockedPath.trim()) {
42
+ target = ctx.blockedPath.trim();
43
+ }
44
+ const reason = (ctx.title?.trim()
45
+ || ctx.description?.trim()
46
+ || ctx.decisionReason?.trim()
47
+ || `需要授权使用 ${toolName}`).slice(0, 300);
48
+ return {
49
+ scope,
50
+ target: truncateTarget(target),
51
+ reason,
52
+ };
53
+ }
54
+ export function structuredPermissionDenied(message) {
55
+ return { behavior: "deny", message };
56
+ }
@@ -63,6 +63,10 @@ export declare class StructuredSessionManager {
63
63
  * Only populated while an SDK call is in flight.
64
64
  */
65
65
  private readonly pendingSdkQueries;
66
+ /** In-flight canUseTool waiters. One pending prompt per session. */
67
+ private readonly pendingPermissions;
68
+ /** approve_turn memory, scoped to the current SDK query. */
69
+ private readonly turnApprovalMemory;
66
70
  private readonly interruptedWith;
67
71
  private readonly interruptedSkills;
68
72
  /**
@@ -172,6 +176,8 @@ export declare class StructuredSessionManager {
172
176
  setSessionMode(sessionId: string, mode: ExecutionMode): SessionSnapshot;
173
177
  /** Toggle auto-approve for the session. */
174
178
  toggleAutoApprove(sessionId: string): SessionSnapshot;
179
+ approvePermission(sessionId: string): SessionSnapshot;
180
+ denyPermission(sessionId: string): SessionSnapshot;
175
181
  /** Resolve a specific escalation by requestId. */
176
182
  resolveEscalation(sessionId: string, requestId: string, resolution: unknown): SessionSnapshot;
177
183
  stop(id: string): SessionSnapshot;
@@ -188,6 +194,9 @@ export declare class StructuredSessionManager {
188
194
  private flushNextQueuedMessage;
189
195
  private emit;
190
196
  private incrementApprovalStats;
197
+ private rememberTurnApproval;
198
+ private settlePendingPermission;
199
+ private handleCanUseTool;
191
200
  private runCodexStreaming;
192
201
  private runGrokStreaming;
193
202
  private runOpenCodeStreaming;
@@ -12,6 +12,7 @@ import { CodexRunner } from "./structured-codex-adapter.js";
12
12
  import { CodexProtocolReducer } from "./structured-codex-protocol.js";
13
13
  import { normalizeStructuredToolResultContent } from "./structured-content.js";
14
14
  import { buildAppendSystemPromptParts, buildClaudeSdkThinking, ClaudeCliRunner, derivePermissionPolicy, } from "./structured-claude-adapter.js";
15
+ import { inferStructuredEscalation, structuredPermissionDenied } from "./structured-permission.js";
15
16
  import { captureTaskMeta, extractClaudeAssistantMessage, extractClaudeModelName, normalizeClaudeToolInput, stampParentTaskResults, stampSelfTask, tagSubagentBlocks, ClaudeCliProtocolReducer, } from "./structured-claude-protocol.js";
16
17
  import { OpenCodeRunner, applyOpenCodeEvent } from "./structured-opencode-adapter.js";
17
18
  import { GrokRunner, applyGrokEvent } from "./structured-grok-adapter.js";
@@ -297,6 +298,10 @@ export class StructuredSessionManager {
297
298
  * Only populated while an SDK call is in flight.
298
299
  */
299
300
  pendingSdkQueries = new Map();
301
+ /** In-flight canUseTool waiters. One pending prompt per session. */
302
+ pendingPermissions = new Map();
303
+ /** approve_turn memory, scoped to the current SDK query. */
304
+ turnApprovalMemory = new Map();
300
305
  interruptedWith = new Map();
301
306
  interruptedSkills = new Map();
302
307
  /**
@@ -475,6 +480,11 @@ export class StructuredSessionManager {
475
480
  continue;
476
481
  execution.interrupt();
477
482
  }
483
+ for (const [sessionId, waiter] of this.pendingPermissions) {
484
+ waiter.resolve(structuredPermissionDenied("服务已关闭"));
485
+ this.pendingPermissions.delete(sessionId);
486
+ }
487
+ this.turnApprovalMemory.clear();
478
488
  for (const query of this.pendingSdkQueries.values()) {
479
489
  void query.interrupt().catch(() => { });
480
490
  }
@@ -1206,11 +1216,15 @@ export class StructuredSessionManager {
1206
1216
  if (!this.isCurrentRequest(id, requestId)) {
1207
1217
  return current;
1208
1218
  }
1219
+ this.settlePendingPermission(id, undefined, structuredPermissionDenied("会话执行失败"));
1220
+ this.turnApprovalMemory.delete(id);
1209
1221
  const failed = {
1210
1222
  ...current,
1211
1223
  status: "failed",
1212
1224
  exitCode: 1,
1213
1225
  endedAt: new Date().toISOString(),
1226
+ pendingEscalation: null,
1227
+ permissionBlocked: false,
1214
1228
  structuredState: {
1215
1229
  ...current.structuredState,
1216
1230
  inFlight: false,
@@ -1410,11 +1424,40 @@ export class StructuredSessionManager {
1410
1424
  toggleAutoApprove(sessionId) {
1411
1425
  const session = this.requireSession(sessionId);
1412
1426
  const newVal = !session.autoApprovePermissions;
1427
+ if (newVal && session.pendingEscalation) {
1428
+ const resolved = this.resolveEscalation(sessionId, session.pendingEscalation.requestId, "approve_once");
1429
+ const updated = { ...resolved, autoApprovePermissions: true };
1430
+ this.sessions.set(sessionId, updated);
1431
+ this.storage.updateSessionRuntimeMetadata(updated);
1432
+ this.emit({
1433
+ type: "status",
1434
+ sessionId,
1435
+ data: { sessionKind: "structured", autoApprovePermissions: true },
1436
+ });
1437
+ return updated;
1438
+ }
1413
1439
  const updated = { ...session, autoApprovePermissions: newVal };
1414
1440
  this.sessions.set(sessionId, updated);
1415
1441
  this.storage.updateSessionRuntimeMetadata(updated);
1442
+ this.emit({
1443
+ type: "status",
1444
+ sessionId,
1445
+ data: { sessionKind: "structured", autoApprovePermissions: newVal },
1446
+ });
1416
1447
  return updated;
1417
1448
  }
1449
+ approvePermission(sessionId) {
1450
+ const pending = this.requireSession(sessionId).pendingEscalation;
1451
+ if (!pending)
1452
+ throw new Error("当前会话没有待处理的授权请求。");
1453
+ return this.resolveEscalation(sessionId, pending.requestId, "approve_once");
1454
+ }
1455
+ denyPermission(sessionId) {
1456
+ const pending = this.requireSession(sessionId).pendingEscalation;
1457
+ if (!pending)
1458
+ throw new Error("当前会话没有待处理的授权请求。");
1459
+ return this.resolveEscalation(sessionId, pending.requestId, "deny");
1460
+ }
1418
1461
  /** Resolve a specific escalation by requestId. */
1419
1462
  resolveEscalation(sessionId, requestId, resolution) {
1420
1463
  const session = this.requireSession(sessionId);
@@ -1433,6 +1476,12 @@ export class StructuredSessionManager {
1433
1476
  if (approved && scope) {
1434
1477
  this.incrementApprovalStats(session, scope);
1435
1478
  }
1479
+ if (approved && resolution === "approve_turn") {
1480
+ this.rememberTurnApproval(sessionId, pending.scope, pending.target);
1481
+ }
1482
+ this.settlePendingPermission(sessionId, pending.requestId, approved
1483
+ ? { behavior: "allow" }
1484
+ : structuredPermissionDenied("User rejected permission request"));
1436
1485
  const updated = {
1437
1486
  ...session,
1438
1487
  pendingEscalation: null,
@@ -1453,6 +1502,8 @@ export class StructuredSessionManager {
1453
1502
  return updated;
1454
1503
  }
1455
1504
  stop(id) {
1505
+ this.settlePendingPermission(id, undefined, structuredPermissionDenied("已停止"));
1506
+ this.turnApprovalMemory.delete(id);
1456
1507
  const session = this.requireSession(id);
1457
1508
  this.interruptedWith.delete(id);
1458
1509
  this.interruptedSkills.delete(id);
@@ -1499,6 +1550,8 @@ export class StructuredSessionManager {
1499
1550
  return cancelled;
1500
1551
  }
1501
1552
  delete(id) {
1553
+ this.settlePendingPermission(id, undefined, structuredPermissionDenied("会话已删除"));
1554
+ this.turnApprovalMemory.delete(id);
1502
1555
  const runnerExecution = this.pendingRunnerExecutions.get(id);
1503
1556
  const sdkQuery = this.pendingSdkQueries.get(id);
1504
1557
  const sdkAbort = this.pendingSdkAbort.get(id);
@@ -1643,6 +1696,123 @@ export class StructuredSessionManager {
1643
1696
  stats.total++;
1644
1697
  session.approvalStats = stats;
1645
1698
  }
1699
+ rememberTurnApproval(sessionId, scope, target) {
1700
+ let memory = this.turnApprovalMemory.get(sessionId);
1701
+ if (!memory) {
1702
+ memory = { scopes: new Set(), targets: new Set() };
1703
+ this.turnApprovalMemory.set(sessionId, memory);
1704
+ }
1705
+ memory.scopes.add(scope);
1706
+ if (target)
1707
+ memory.targets.add(target);
1708
+ }
1709
+ settlePendingPermission(sessionId, requestId, result) {
1710
+ const waiter = this.pendingPermissions.get(sessionId);
1711
+ if (!waiter)
1712
+ return;
1713
+ if (requestId !== undefined && waiter.requestId !== requestId)
1714
+ return;
1715
+ this.pendingPermissions.delete(sessionId);
1716
+ waiter.resolve(result);
1717
+ }
1718
+ async handleCanUseTool(sessionId, requestId, toolName, input, ctx) {
1719
+ const deny = (message) => structuredPermissionDenied(message);
1720
+ if (ctx.signal.aborted || this.disposed)
1721
+ return deny("已取消");
1722
+ const current = this.currentSessionForRequest(sessionId, requestId);
1723
+ if (!current)
1724
+ return deny("会话已结束");
1725
+ const inferred = inferStructuredEscalation(toolName, input, ctx);
1726
+ if (current.autoApprovePermissions) {
1727
+ this.incrementApprovalStats(current, inferred.scope);
1728
+ this.storage.updateSessionRuntimeMetadata(current);
1729
+ return { behavior: "allow" };
1730
+ }
1731
+ const memory = this.turnApprovalMemory.get(sessionId);
1732
+ if (memory && (memory.scopes.has(inferred.scope) || (inferred.target && memory.targets.has(inferred.target)))) {
1733
+ return { behavior: "allow" };
1734
+ }
1735
+ const existing = this.pendingPermissions.get(sessionId);
1736
+ if (existing) {
1737
+ await new Promise((resolve) => {
1738
+ const previous = existing.resolve;
1739
+ existing.resolve = (result) => {
1740
+ previous(result);
1741
+ resolve(result);
1742
+ };
1743
+ });
1744
+ if (ctx.signal.aborted || this.disposed)
1745
+ return deny("已取消");
1746
+ return this.handleCanUseTool(sessionId, requestId, toolName, input, ctx);
1747
+ }
1748
+ const escalation = {
1749
+ requestId: ctx.toolUseID ? `sdk-${ctx.toolUseID}` : `sdk-${Date.now()}`,
1750
+ scope: inferred.scope,
1751
+ runner: "json",
1752
+ source: "tool_permission_request",
1753
+ target: inferred.target,
1754
+ reason: inferred.reason,
1755
+ };
1756
+ return await new Promise((resolve) => {
1757
+ let settled = false;
1758
+ const finish = (result) => {
1759
+ if (settled)
1760
+ return;
1761
+ settled = true;
1762
+ ctx.signal.removeEventListener("abort", onAbort);
1763
+ if (this.pendingPermissions.get(sessionId)?.requestId === escalation.requestId) {
1764
+ this.pendingPermissions.delete(sessionId);
1765
+ }
1766
+ resolve(result);
1767
+ };
1768
+ const onAbort = () => {
1769
+ finish(deny("已取消"));
1770
+ const latest = this.currentSessionForRequest(sessionId, requestId);
1771
+ if (latest?.pendingEscalation?.requestId === escalation.requestId) {
1772
+ const cleared = {
1773
+ ...latest,
1774
+ pendingEscalation: null,
1775
+ permissionBlocked: false,
1776
+ };
1777
+ this.sessions.set(sessionId, cleared);
1778
+ this.storage.updateSessionRuntimeMetadata(cleared);
1779
+ this.emit({
1780
+ type: "status",
1781
+ sessionId,
1782
+ data: { permissionBlocked: false, sessionKind: "structured" },
1783
+ });
1784
+ }
1785
+ };
1786
+ ctx.signal.addEventListener("abort", onAbort, { once: true });
1787
+ this.pendingPermissions.set(sessionId, { requestId: escalation.requestId, resolve: finish });
1788
+ const latest = this.currentSessionForRequest(sessionId, requestId);
1789
+ if (!latest || ctx.signal.aborted) {
1790
+ finish(deny("会话已结束"));
1791
+ return;
1792
+ }
1793
+ const updated = {
1794
+ ...latest,
1795
+ pendingEscalation: escalation,
1796
+ permissionBlocked: true,
1797
+ };
1798
+ this.sessions.set(sessionId, updated);
1799
+ this.storage.updateSessionRuntimeMetadata(updated);
1800
+ this.emit({
1801
+ type: "status",
1802
+ sessionId,
1803
+ data: {
1804
+ sessionKind: "structured",
1805
+ permissionBlocked: true,
1806
+ permissionRequest: {
1807
+ scope: escalation.scope,
1808
+ target: escalation.target,
1809
+ prompt: escalation.reason,
1810
+ },
1811
+ pendingEscalation: escalation,
1812
+ },
1813
+ });
1814
+ });
1815
+ }
1646
1816
  // ---------------------------------------------------------------------------
1647
1817
  // Streaming codex exec --json execution
1648
1818
  // ---------------------------------------------------------------------------
@@ -2323,6 +2493,7 @@ export class StructuredSessionManager {
2323
2493
  async runClaudeSdkStreaming(sessionId, session, prompt, requestId, skills) {
2324
2494
  const abortController = new AbortController();
2325
2495
  this.pendingSdkAbort.set(sessionId, abortController);
2496
+ this.turnApprovalMemory.delete(sessionId);
2326
2497
  const isManaged = session.mode === "managed";
2327
2498
  let killedForAskUserQuestion = false;
2328
2499
  // 权限策略 + 系统提示词都通过共享 helper 派生,与 CLI runner 一字不差。
@@ -2339,6 +2510,11 @@ export class StructuredSessionManager {
2339
2510
  env: sdkEnv,
2340
2511
  permissionMode: permPolicy.permissionMode,
2341
2512
  ...(permPolicy.permissionMode === "bypassPermissions" ? { allowDangerouslySkipPermissions: true } : {}),
2513
+ ...(permPolicy.permissionMode !== "bypassPermissions"
2514
+ ? {
2515
+ canUseTool: (toolName, input, ctx) => this.handleCanUseTool(sessionId, requestId, toolName, input, ctx),
2516
+ }
2517
+ : {}),
2342
2518
  ...(permPolicy.allowedTools ? { allowedTools: permPolicy.allowedTools } : {}),
2343
2519
  ...(isManaged ? { disallowedTools: ["AskUserQuestion"] } : {}),
2344
2520
  skills,
@@ -2714,6 +2890,8 @@ export class StructuredSessionManager {
2714
2890
  }
2715
2891
  }
2716
2892
  // Cleanup
2893
+ this.settlePendingPermission(sessionId, undefined, structuredPermissionDenied("本轮已结束"));
2894
+ this.turnApprovalMemory.delete(sessionId);
2717
2895
  const releasedAbort = this.releasePendingSdkAbort(sessionId, abortController);
2718
2896
  const releasedQuery = this.releasePendingSdkQuery(sessionId, queryHandle);
2719
2897
  if (releasedAbort || releasedQuery)
@@ -153,7 +153,18 @@ export function taskboardIntegrationScript() {
153
153
  });
154
154
  };
155
155
  const scan = () => document.querySelectorAll("[data-task-id]").forEach(setup);
156
- new MutationObserver(scan).observe(document.documentElement, {childList:true, subtree:true});
156
+ let readyAnnounced = false;
157
+ const announceReady = (repeat = false) => {
158
+ const root = document.getElementById("root");
159
+ if (!root || !root.childElementCount || window.parent === window || (readyAnnounced && !repeat)) return;
160
+ readyAnnounced = true;
161
+ window.parent.postMessage({type:"wand-taskboard-ready"}, location.origin);
162
+ };
163
+ new MutationObserver(() => { scan(); announceReady(); }).observe(document.documentElement, {childList:true, subtree:true});
164
+ window.addEventListener("load", () => announceReady());
165
+ window.addEventListener("message", (event) => {
166
+ if (event.origin === location.origin && event.source === window.parent && event.data?.type === "wand-taskboard-ready-request") announceReady(true);
167
+ });
157
168
  scan();
158
169
  })();
159
170
  </script>`;