@zq-silk/yui 0.15.7 → 0.15.8

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 (281) hide show
  1. package/ARCHITECTURE.md +192 -399
  2. package/README.md +127 -1149
  3. package/dist/agent/adapterCatalog.js +15 -2
  4. package/dist/agent/agent.js +23 -3
  5. package/dist/agent/argumentPolicy.js +7 -1
  6. package/dist/agent/connectionPlan.js +62 -0
  7. package/dist/agent/executionComponents.js +158 -0
  8. package/dist/agent/launchEnvironment.js +31 -3
  9. package/dist/agent/managedRuntimeEnvironment.js +3 -5
  10. package/dist/{turn/turn.js → agentRun/agentRun.js} +166 -109
  11. package/dist/{turn/turnIdentity.js → agentRun/runIdentity.js} +4 -4
  12. package/dist/brief/taskBrief.js +12 -0
  13. package/dist/cli/agentConfigurationPicker.js +13 -0
  14. package/dist/cli/commandCatalog.js +157 -70
  15. package/dist/cli/interactionCandidates.js +5 -5
  16. package/dist/cli/interactionPolicy.js +38 -8
  17. package/dist/cli/invocationRouter.js +1 -1
  18. package/dist/cli/managedDiagnostics.js +28 -0
  19. package/dist/cli/operatorWizard.js +1 -7
  20. package/dist/cli/roleOptionOrder.js +27 -0
  21. package/dist/cli/roleWizard.js +50 -14
  22. package/dist/cli/updateOrchestrator.js +1 -1
  23. package/dist/cli/updatePorts.js +3 -4
  24. package/dist/cli.js +178 -95
  25. package/dist/commands/agentCommands.js +72 -14
  26. package/dist/commands/capabilityCommands.js +9 -6
  27. package/dist/commands/configCommands.js +20 -20
  28. package/dist/commands/deliveryGuardPreflight.js +2 -2
  29. package/dist/commands/executionAuditCommands.js +24 -24
  30. package/dist/commands/globalRoleCommands.js +1 -1
  31. package/dist/commands/grantCommands.js +4 -4
  32. package/dist/commands/operatorCommands.js +1 -7
  33. package/dist/commands/projectCommands.js +4 -4
  34. package/dist/commands/resourcesCommands.js +2 -2
  35. package/dist/commands/roleConfiguration.js +25 -5
  36. package/dist/commands/roleRuntimeGuard.js +4 -5
  37. package/dist/commands/sessionCommands.js +3 -7
  38. package/dist/commands/taskActivationCommands.js +259 -0
  39. package/dist/commands/taskActor.js +28 -49
  40. package/dist/commands/taskCommands.js +1188 -710
  41. package/dist/commands/taskContextCommand.js +39 -583
  42. package/dist/commands/taskExecutionCommands.js +32 -32
  43. package/dist/commands/taskInputCommands.js +40 -104
  44. package/dist/commands/taskIntegrationCommands.js +3 -2
  45. package/dist/commands/taskIntegrationQueueCommands.js +1 -1
  46. package/dist/commands/taskNextActionCommand.js +8 -8
  47. package/dist/commands/taskOverviewCommand.js +33 -45
  48. package/dist/commands/taskRemoteDeliveryCommand.js +2 -2
  49. package/dist/commands/taskRoleRuntimeStatus.js +133 -102
  50. package/dist/commands/telemetryCommands.js +36 -38
  51. package/dist/config/configCatalog.js +4 -4
  52. package/dist/config/yuiConfig.js +8 -8
  53. package/dist/context/contextSnapshot.js +10 -10
  54. package/dist/context/dispatchContext.js +11 -11
  55. package/dist/context/roleSessionContext.js +6 -3
  56. package/dist/context/{turnContextPack.js → runContextPack.js} +146 -81
  57. package/dist/context/{turnInputContract.js → runInputContract.js} +73 -60
  58. package/dist/context/sessionBootstrapManifest.js +21 -2
  59. package/dist/context/sourceRunContext.js +30 -0
  60. package/dist/context/taskContext.js +458 -0
  61. package/dist/context/wakeNotification.js +27 -27
  62. package/dist/controller/agentRuntimeObserver.js +21 -24
  63. package/dist/controller/capabilityBridge.js +17 -6
  64. package/dist/controller/clientRuntime.js +65 -92
  65. package/dist/controller/controller.js +114 -188
  66. package/dist/controller/fileSchedulerStoreAdapter.js +787 -887
  67. package/dist/controller/jobControl.js +54 -85
  68. package/dist/controller/resourceInventory.js +8 -27
  69. package/dist/controller/resourceInventoryLinux.js +12 -13
  70. package/dist/controller/runtime.js +529 -479
  71. package/dist/controller/runtimeEventInbox.js +55 -25
  72. package/dist/controller/runtimeEventProcessor.js +22 -31
  73. package/dist/controller/{runtimeHookTurnFence.js → runtimeHookRunFence.js} +91 -115
  74. package/dist/controller/runtimeLaunchCoordinator.js +80 -426
  75. package/dist/controller/runtimeObservationHook.js +14 -18
  76. package/dist/controller/sessionNotify.js +16 -24
  77. package/dist/controller/sessionOwnerReconciliation.js +168 -50
  78. package/dist/controller/structuredProviderObservation.js +138 -99
  79. package/dist/coordination/workMailbox.js +3 -3
  80. package/dist/coordination/workMailboxQueue.js +36 -33
  81. package/dist/core/boundedRpc.js +8 -1
  82. package/dist/core/controllerClient.js +20 -1
  83. package/dist/core/controllerServer.js +4 -4
  84. package/dist/doctor/doctor.js +13 -2
  85. package/dist/domain/agentResultTransport.js +9 -9
  86. package/dist/execution/codexThreadNaming.js +2 -8
  87. package/dist/execution/executionHealth.js +51 -63
  88. package/dist/execution/reviewMainRun.js +137 -0
  89. package/dist/execution/workItemExecution.js +28 -29
  90. package/dist/execution/workItemExecutionProjection.js +99 -107
  91. package/dist/execution/workItemMainRun.js +141 -0
  92. package/dist/executor/agentAdapter.js +227 -20
  93. package/dist/executor/agentConfigurationCatalog.js +126 -4
  94. package/dist/executor/agentConfigurationProbe.js +162 -4
  95. package/dist/executor/agentExecutor.js +79 -78
  96. package/dist/executor/effectiveLaunch.js +105 -18
  97. package/dist/executor/executorRegistry.js +29 -44
  98. package/dist/executor/fileRoleLaunchPlanner.js +229 -154
  99. package/dist/executor/workspacePreflightClassification.js +16 -16
  100. package/dist/grant/capabilityGrant.js +6 -3
  101. package/dist/input/inputRequest.js +12 -10
  102. package/dist/integration/gitIntegrationService.js +4 -11
  103. package/dist/integration/integrationQueueService.js +4 -4
  104. package/dist/interaction/operatorPresentation.js +1 -1
  105. package/dist/kernel/builtinCapabilities.js +247 -12
  106. package/dist/kernel/capabilityRegistry.js +64 -18
  107. package/dist/kernel/instanceHost.js +12 -1
  108. package/dist/kernel/kernelPorts.js +2 -2
  109. package/dist/lifecycle/canonicalLifecycleEvent.js +44 -49
  110. package/dist/lifecycle/exactRunTerminalization.js +449 -0
  111. package/dist/message/message.js +62 -6
  112. package/dist/message/messageContinuation.js +204 -0
  113. package/dist/observability/executionAudit.js +70 -72
  114. package/dist/observability/faultClassification.js +2 -2
  115. package/dist/observability/orchestrationMetrics.js +8 -8
  116. package/dist/operator/operatorSessionHistory.js +1 -7
  117. package/dist/output/agentConfigurationPresentation.js +8 -3
  118. package/dist/output/agentRunConfigurationPresentation.js +128 -0
  119. package/dist/output/rolePresentation.js +54 -3
  120. package/dist/plugins/pluginChild.js +104 -0
  121. package/dist/plugins/pluginIntent.js +26 -0
  122. package/dist/plugins/pluginInterpreter.js +43 -0
  123. package/dist/plugins/pluginPackage.js +101 -0
  124. package/dist/plugins/pluginProcess.js +112 -0
  125. package/dist/plugins/pluginService.js +380 -0
  126. package/dist/profile/agentProfile.js +1 -1
  127. package/dist/repository/gitWorkspace.js +26 -4
  128. package/dist/repository/project.js +19 -4
  129. package/dist/repository/taskBaseFreshness.js +13 -13
  130. package/dist/repository/taskWorkspaceCoordinator.js +20 -27
  131. package/dist/repository/taskWorkspacePreparer.js +344 -83
  132. package/dist/resources/autoResourceGc.js +3 -3
  133. package/dist/resources/liveReferences.js +3 -3
  134. package/dist/resources/projectResource.js +123 -0
  135. package/dist/resources/projectResourceService.js +421 -0
  136. package/dist/resources/resourceDiscovery.js +6 -6
  137. package/dist/resources/resourceGc.js +1 -1
  138. package/dist/resources/resourceRegistrar.js +1 -1
  139. package/dist/resources/resourceTypes.js +1 -1
  140. package/dist/review/deltaRecheck.js +3 -3
  141. package/dist/review/reviewAcceptance.js +16 -16
  142. package/dist/review/reviewDecision.js +7 -7
  143. package/dist/review/reviewRound.js +21 -20
  144. package/dist/review/reviewerAvailability.js +2 -2
  145. package/dist/role/role.js +51 -7
  146. package/dist/role/taskRoleUpdate.js +30 -0
  147. package/dist/runtime/acpProtocol.js +425 -0
  148. package/dist/runtime/acpSession.js +731 -0
  149. package/dist/runtime/acpSessionConfiguration.js +260 -0
  150. package/dist/runtime/agentDriver.js +30 -11
  151. package/dist/runtime/agentEndpoint.js +278 -0
  152. package/dist/runtime/agentEndpointIdentity.js +86 -0
  153. package/dist/runtime/agentEndpointOwnership.js +239 -0
  154. package/dist/runtime/agentError.js +2 -10
  155. package/dist/runtime/agentHost.js +565 -314
  156. package/dist/runtime/agentRunConfiguration.js +258 -0
  157. package/dist/runtime/builtinAgentDrivers.js +134 -18
  158. package/dist/runtime/builtinAgentErrorMappers.js +55 -3
  159. package/dist/runtime/builtinTranscriptUsage.js +1 -1
  160. package/dist/runtime/claude-process-owner +0 -0
  161. package/dist/runtime/codexAppServerRuntime.js +38 -30
  162. package/dist/runtime/codexInteractiveHost.js +41 -6
  163. package/dist/runtime/continuationManager.js +2 -6
  164. package/dist/runtime/executionEnvironment.js +30 -0
  165. package/dist/runtime/firstProgressAdvisory.js +11 -11
  166. package/dist/runtime/index.js +4 -3
  167. package/dist/runtime/jsonLineChannel.js +109 -0
  168. package/dist/runtime/launchBroker.js +91 -16
  169. package/dist/runtime/launchDiagnostics.js +2 -2
  170. package/dist/runtime/lifecycleReservation.js +10 -18
  171. package/dist/runtime/managedCaller.js +61 -17
  172. package/dist/runtime/nativeSessionControl.js +102 -0
  173. package/dist/runtime/ports.js +6 -21
  174. package/dist/runtime/processExitObservation.js +8 -7
  175. package/dist/runtime/promptEnvelope.js +17 -6
  176. package/dist/runtime/providerContinuation.js +3 -9
  177. package/dist/runtime/providerContinuationReconciliationService.js +4 -13
  178. package/dist/runtime/providerControl.js +2 -7
  179. package/dist/runtime/providerRuntimeIdentity.js +110 -222
  180. package/dist/runtime/providerRuntimeReconciler.js +5 -9
  181. package/dist/runtime/runtimeBinding.js +0 -1
  182. package/dist/runtime/runtimeContinuationProjection.js +4 -7
  183. package/dist/runtime/runtimeDeadlines.js +9 -0
  184. package/dist/runtime/runtimeHealthPolicy.js +1 -1
  185. package/dist/runtime/runtimeObservation.js +29 -65
  186. package/dist/runtime/runtimeProjection.js +43 -51
  187. package/dist/runtime/runtimeSessionCandidate.js +1 -3
  188. package/dist/runtime/sessionLaunchRequest.js +3 -7
  189. package/dist/runtime/sessionOwnerIdentity.js +7 -54
  190. package/dist/runtime/sessionOwnerRegistry.js +22 -17
  191. package/dist/runtime/sessionReconciliation.js +4 -8
  192. package/dist/runtime/sessionTerminationGuard.js +70 -259
  193. package/dist/runtime/sessionTokenMetrics.js +5 -16
  194. package/dist/runtime/structuredProviderHost.js +237 -117
  195. package/dist/runtime/taskRuntimeIsolation.js +39 -122
  196. package/dist/runtime/tmuxAdapters.js +39 -86
  197. package/dist/scheduler/activeRoleRunDelivery.js +354 -0
  198. package/dist/scheduler/leaderWakeupProcessor.js +75 -266
  199. package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
  200. package/dist/scheduler/ports.js +80 -9
  201. package/dist/scheduler/{roleTurnLiveness.js → roleRunLiveness.js} +26 -30
  202. package/dist/scheduler/{roleTurnStall.js → roleRunStall.js} +128 -139
  203. package/dist/scheduler/taskExecutionProjection.js +120 -124
  204. package/dist/scheduler/taskObservabilityProjection.js +29 -29
  205. package/dist/scheduler/taskWake.js +11 -4
  206. package/dist/scheduler/wakeReason.js +9 -1
  207. package/dist/setup/setupCommand.js +1 -0
  208. package/dist/storage/migrations/agentRunContract.js +159 -0
  209. package/dist/storage/migrations/removeRuntimeGeneration.js +207 -0
  210. package/dist/storage/sqliteSchema.js +431 -5
  211. package/dist/storage/sqliteStore.js +375 -220
  212. package/dist/storage/storageVersions.js +1 -1
  213. package/dist/storage/storeRpc.js +10 -5
  214. package/dist/storage/taskStore.js +13 -11
  215. package/dist/storage/upgrade/upgradeOrchestrator.js +5 -7
  216. package/dist/surface/surfaceContributions.js +102 -0
  217. package/dist/task/completionReadiness.js +32 -6
  218. package/dist/task/deliveryGuard.js +16 -16
  219. package/dist/task/draftPlan.js +72 -12
  220. package/dist/task/nextAction.js +144 -128
  221. package/dist/task/remoteDelivery.js +6 -6
  222. package/dist/task/task.js +184 -18
  223. package/dist/task/taskActivation.js +301 -0
  224. package/dist/task/taskActivationService.js +392 -0
  225. package/dist/task/taskRecordReference.js +5 -4
  226. package/dist/task/taskRecordRetirement.js +1 -1
  227. package/dist/telemetry/sqliteTelemetryStore.js +55 -68
  228. package/dist/telemetry/telemetryConfig.js +14 -14
  229. package/dist/telemetry/telemetryWiring.js +2 -2
  230. package/dist/web/assets/assetManifest.js +2 -0
  231. package/dist/web/assets/client/app.js +120 -20
  232. package/dist/web/assets/client/components.js +87 -54
  233. package/dist/web/assets/client/i18n.js +83 -41
  234. package/dist/web/assets/client/markdown.js +1 -1
  235. package/dist/web/assets/client/taskSurface.js +353 -0
  236. package/dist/web/assets/client/view.js +49 -44
  237. package/dist/web/assets/shell.js +1 -1
  238. package/dist/web/assets/styles/cards.js +22 -4
  239. package/dist/web/controllerWeb.js +60 -0
  240. package/dist/web/webMutation.js +28 -0
  241. package/dist/web/webServer.js +118 -8
  242. package/dist/web/webSnapshot.js +81 -74
  243. package/dist/web/webTaskSurface.js +64 -0
  244. package/dist/workItem/dependencyGate.js +1 -1
  245. package/dist/workItem/workItem.js +80 -48
  246. package/dist/workspace/workItemChangeSetManager.js +16 -9
  247. package/docs/agent-result-consumption.md +94 -0
  248. package/docs/agent-runtime-drivers.md +91 -0
  249. package/docs/architecture/README.md +38 -0
  250. package/docs/architecture/capabilities-and-resources.md +79 -0
  251. package/docs/managed-turn-and-session-runtime.md +222 -0
  252. package/docs/observability/README.md +81 -0
  253. package/docs/plugin-sdk.md +290 -0
  254. package/docs/provider-runtime.md +163 -0
  255. package/docs/release-workflow.md +303 -0
  256. package/docs/roles-and-configuration.md +113 -0
  257. package/docs/sqlite-control-plane-design.md +76 -0
  258. package/docs/task-dag-semantics.md +57 -0
  259. package/docs/task-delivery.md +103 -0
  260. package/docs/task-local-identity.md +6 -6
  261. package/docs/testing/verification-levels.md +86 -0
  262. package/i18n/README.zh-CN.md +98 -739
  263. package/package.json +2 -2
  264. package/skills/yui-leader/SKILL.md +130 -103
  265. package/skills/yui-leader/references/integration.md +39 -0
  266. package/skills/yui-leader/references/replicated-execution.md +42 -0
  267. package/skills/yui-leader/references/task-plugins.md +33 -0
  268. package/skills/yui-operator/SKILL.md +30 -59
  269. package/skills/yui-reviewer/SKILL.md +35 -36
  270. package/skills/yui-runtime/SKILL.md +88 -24
  271. package/skills/yui-runtime/references/publication.md +22 -0
  272. package/skills/yui-runtime/references/recovery.md +64 -0
  273. package/skills/yui-worker/SKILL.md +37 -39
  274. package/dist/cli/roleOptionCatalog.js +0 -68
  275. package/dist/context/sourceTurnContext.js +0 -30
  276. package/dist/execution/reviewMainTurn.js +0 -161
  277. package/dist/execution/workItemMainTurn.js +0 -164
  278. package/dist/lifecycle/exactTurnTerminalization.js +0 -407
  279. package/dist/runtime/preallocatedNativeSession.js +0 -13
  280. package/dist/runtime/runtimeStopReceipt.js +0 -42
  281. package/dist/scheduler/activeRoleTurnDelivery.js +0 -315
@@ -0,0 +1,60 @@
1
+ import { parseWebCommandOptions, startYuiWebServer } from "./webServer.js";
2
+ /** HTTP listener ownership only, alongside the Controller's one Kernel Host.
3
+ * The authenticated Controller control socket starts/stops the exact listener;
4
+ * no capability identity is accepted through this lifecycle API. */
5
+ export function createControllerWeb(store, dependencies) {
6
+ let current;
7
+ let starting = false;
8
+ const close = async () => {
9
+ const owned = current;
10
+ if (!owned)
11
+ return;
12
+ current = undefined;
13
+ owned.server.closeTerminals();
14
+ await new Promise((resolve, reject) => {
15
+ owned.server.close((error) => error ? reject(error) : resolve());
16
+ owned.server.closeAllConnections();
17
+ });
18
+ };
19
+ return {
20
+ close,
21
+ async dispatch(method, value) {
22
+ if (!value || typeof value !== "object" || Array.isArray(value))
23
+ throw new Error("Invalid Web listener request.");
24
+ const params = value;
25
+ if (method === "web.status") {
26
+ if (Object.keys(params).length)
27
+ throw new Error("Web status takes no arguments.");
28
+ return current ? { id: current.id, url: current.url } : null;
29
+ }
30
+ if (method === "web.stop") {
31
+ if (Object.keys(params).some((key) => key !== "id") || typeof params.id !== "string")
32
+ throw new Error("Expected Web listener id.");
33
+ if (!current)
34
+ return { stopped: false };
35
+ if (current.id !== params.id)
36
+ throw new Error("Web listener identity changed.");
37
+ await close();
38
+ return { stopped: true };
39
+ }
40
+ if (method !== "web.start" || Object.keys(params).some((key) => !["id", "host", "port"].includes(key))
41
+ || typeof params.id !== "string" || !params.id.trim()
42
+ || typeof params.host !== "string" || typeof params.port !== "number") {
43
+ throw new Error("Expected id, host and port for Web start.");
44
+ }
45
+ const options = parseWebCommandOptions(["--host", params.host, "--port", String(params.port)]);
46
+ if (starting || current)
47
+ throw new Error("A Web listener is already starting or running in this Controller.");
48
+ starting = true;
49
+ try {
50
+ const server = await startYuiWebServer(store, options, dependencies);
51
+ const url = `http://${options.host === "::1" ? "[::1]" : options.host}:${options.port}`;
52
+ current = { id: params.id, server, url };
53
+ return { id: params.id, url };
54
+ }
55
+ finally {
56
+ starting = false;
57
+ }
58
+ }
59
+ };
60
+ }
@@ -0,0 +1,28 @@
1
+ export class WebRequestRejected extends Error {
2
+ disposition = "not-submitted";
3
+ }
4
+ /** For Store-only mutations with notifications excluded: an exception from
5
+ * the callback, propagated unchanged after rollback, proves non-submission.
6
+ * Commit/rollback failures and post-commit notifications remain unknown. */
7
+ export function webLocalMutation(store, apply) {
8
+ let rejected = false;
9
+ let callbackError;
10
+ try {
11
+ return store.transaction((tx) => {
12
+ try {
13
+ return apply(tx);
14
+ }
15
+ catch (error) {
16
+ rejected = true;
17
+ callbackError = error;
18
+ throw error;
19
+ }
20
+ });
21
+ }
22
+ catch (error) {
23
+ if (rejected && error === callbackError) {
24
+ throw new WebRequestRejected(error instanceof Error ? error.message : "Mutation rejected.");
25
+ }
26
+ throw error;
27
+ }
28
+ }
@@ -2,6 +2,7 @@ import { randomBytes, timingSafeEqual } from "node:crypto";
2
2
  import { createServer } from "node:http";
3
3
  import WebSocket, { WebSocketServer } from "ws";
4
4
  import { usageError } from "../errors/cliError.js";
5
+ import { WebRequestRejected } from "./webMutation.js";
5
6
  import { DASHBOARD_HTML, findWebAsset } from "./assets/assetManifest.js";
6
7
  import { buildWebDashboardSnapshot, buildWebTaskDetail } from "./webSnapshot.js";
7
8
  const MAX_JSON_BODY_BYTES = 16 * 1024;
@@ -53,12 +54,17 @@ export function createYuiWebServer(store, dependencies = {}) {
53
54
  server.on("upgrade", (request, socket, head) => {
54
55
  void handleTerminalUpgrade(request, socket, head, webSocketServer, dependencies.terminal, token);
55
56
  });
56
- server.on("close", () => {
57
+ let terminalsClosed = false;
58
+ const closeTerminals = () => {
59
+ if (terminalsClosed)
60
+ return;
61
+ terminalsClosed = true;
57
62
  for (const socket of webSocketServer.clients)
58
63
  socket.terminate();
59
64
  webSocketServer.close();
60
- });
61
- return server;
65
+ };
66
+ server.on("close", closeTerminals);
67
+ return Object.assign(server, { closeTerminals });
62
68
  }
63
69
  export async function startYuiWebServer(store, options, dependencies = {}) {
64
70
  const server = createYuiWebServer(store, dependencies);
@@ -75,7 +81,7 @@ async function handleHttpRequest(request, response, store, dependencies, token,
75
81
  setSecurityHeaders(response);
76
82
  const method = request.method ?? "GET";
77
83
  if (!isLoopbackHost(headerValue(request, "host"))) {
78
- sendJson(response, 403, { error: "Invalid Host." }, method === "HEAD");
84
+ sendJson(response, 403, { error: "Invalid Host.", disposition: "not-submitted" }, method === "HEAD");
79
85
  return;
80
86
  }
81
87
  let pathname;
@@ -83,7 +89,102 @@ async function handleHttpRequest(request, response, store, dependencies, token,
83
89
  pathname = new URL(request.url ?? "/", "http://localhost").pathname;
84
90
  }
85
91
  catch {
86
- sendJson(response, 400, { error: "Invalid URL." }, method === "HEAD");
92
+ sendJson(response, 400, { error: "Invalid URL.", disposition: "not-submitted" }, method === "HEAD");
93
+ return;
94
+ }
95
+ // The loopback page token is the actual user ingress. A request body cannot
96
+ // select Operator/Leader authority; all API reads use this boundary too.
97
+ if (pathname.startsWith("/api/") && !tokenMatches(headerValue(request, "x-yui-web-token"), token)) {
98
+ sendJson(response, 403, { error: "Invalid Yui web token.", disposition: "not-submitted" }, method === "HEAD");
99
+ return;
100
+ }
101
+ const panelTarget = /^\/api\/tasks\/([^/]+)\/panels$/.exec(pathname);
102
+ if (panelTarget && dependencies.panels) {
103
+ try {
104
+ const taskId = decodeURIComponent(panelTarget[1]);
105
+ if (method === "GET") {
106
+ sendJson(response, 200, { panels: dependencies.panels.list(taskId), observedAt: now().toISOString() }, false);
107
+ }
108
+ else if (method === "POST") {
109
+ const body = await readMutationBody(request);
110
+ if (!body || typeof body !== "object" || Array.isArray(body)
111
+ || Object.keys(body).some((key) => !["ref", "input"].includes(key))
112
+ || !("ref" in body) || !("input" in body))
113
+ throw new WebRequestRejected("Expected panel ref and input.");
114
+ const ref = body.ref;
115
+ if (!ref || typeof ref !== "object" || typeof ref.capability !== "string"
116
+ || typeof ref.contractVersion !== "string" || !ref.provider
117
+ || typeof ref.provider.id !== "string" || typeof ref.provider.generation !== "string") {
118
+ throw new WebRequestRejected("Invalid panel reference.");
119
+ }
120
+ const result = await dependencies.panels.read(taskId, ref, body.input);
121
+ sendJson(response, 200, { result, observedAt: now().toISOString() }, false);
122
+ }
123
+ else
124
+ sendJson(response, 405, { error: "Method not allowed.", disposition: "not-submitted" }, false);
125
+ }
126
+ catch (error) {
127
+ sendJson(response, 409, { error: error instanceof Error ? error.message : "Panel unavailable.",
128
+ disposition: "not-submitted" }, false);
129
+ }
130
+ return;
131
+ }
132
+ const surfaceTarget = /^\/api\/tasks\/([^/]+)\/(context|delta|inspect|metadata|messages)$/.exec(pathname);
133
+ if (surfaceTarget && dependencies.surface) {
134
+ try {
135
+ let taskId;
136
+ try {
137
+ taskId = decodeURIComponent(surfaceTarget[1]);
138
+ }
139
+ catch {
140
+ throw new WebRequestRejected("Invalid Task URL encoding.");
141
+ }
142
+ const action = surfaceTarget[2];
143
+ const query = new URL(request.url, "http://localhost").searchParams;
144
+ let value;
145
+ if (method === "GET" && action === "context") {
146
+ value = await dependencies.surface.read(taskId);
147
+ }
148
+ else if (method === "GET" && action === "delta") {
149
+ value = dependencies.surface.delta(taskId, {
150
+ after: query.get("after") ?? "",
151
+ ...(query.has("continuation") ? { continuation: query.get("continuation") } : {})
152
+ });
153
+ }
154
+ else if (method === "GET" && action === "inspect") {
155
+ value = dependencies.surface.inspect(taskId, {
156
+ store: query.get("store") ?? "", refId: query.get("ref") ?? "",
157
+ ...(query.has("digest") ? { digest: query.get("digest") } : {})
158
+ });
159
+ }
160
+ else if (method === "POST" && action === "messages") {
161
+ const body = await readMutationBody(request);
162
+ if (typeof body !== "object" || body === null || Array.isArray(body)
163
+ || Object.keys(body).some((key) => !["body", "requestId"].includes(key))
164
+ || !("requestId" in body) || typeof body.requestId !== "string" || !body.requestId.trim()
165
+ || !("body" in body) || typeof body.body !== "string")
166
+ throw new WebRequestRejected("Expected body and requestId only.");
167
+ value = { ...dependencies.surface.message(taskId, body.body), requestId: body.requestId };
168
+ }
169
+ else if (method === "POST" && action === "metadata") {
170
+ const body = await readMutationBody(request);
171
+ if (typeof body !== "object" || body === null || Array.isArray(body)
172
+ || Object.keys(body).some((key) => !["patch", "requestId"].includes(key))
173
+ || !("requestId" in body) || typeof body.requestId !== "string" || !body.requestId.trim()
174
+ || !("patch" in body))
175
+ throw new WebRequestRejected("Expected patch and requestId only.");
176
+ value = { ...dependencies.surface.update(taskId, body.patch), requestId: body.requestId };
177
+ }
178
+ else {
179
+ sendJson(response, 405, { error: "Method not allowed.", disposition: "not-submitted" }, false);
180
+ return;
181
+ }
182
+ sendJson(response, 200, value, false);
183
+ }
184
+ catch (error) {
185
+ sendJson(response, 409, { error: error instanceof Error ? error.message : "Surface unavailable.",
186
+ disposition: error instanceof WebRequestRejected ? "not-submitted" : "unknown" }, false);
187
+ }
87
188
  return;
88
189
  }
89
190
  if (method === "GET" || method === "HEAD") {
@@ -130,7 +231,7 @@ async function handleHttpRequest(request, response, store, dependencies, token,
130
231
  }
131
232
  catch (error) {
132
233
  sendJson(response, 400, {
133
- error: error instanceof Error ? error.message : "Invalid input answer."
234
+ error: error instanceof Error ? error.message : "Invalid input answer.", disposition: "not-submitted"
134
235
  }, false);
135
236
  return;
136
237
  }
@@ -143,13 +244,14 @@ async function handleHttpRequest(request, response, store, dependencies, token,
143
244
  }
144
245
  catch (error) {
145
246
  sendJson(response, 409, {
146
- error: error instanceof Error ? error.message : "Unable to answer input request."
247
+ error: error instanceof Error ? error.message : "Unable to answer input request.",
248
+ disposition: error instanceof WebRequestRejected ? "not-submitted" : "unknown"
147
249
  }, false);
148
250
  }
149
251
  return;
150
252
  }
151
253
  response.setHeader("allow", "GET, HEAD");
152
- sendJson(response, 405, { error: "Method not allowed." }, method === "HEAD");
254
+ sendJson(response, 405, { error: "Method not allowed.", disposition: "not-submitted" }, method === "HEAD");
153
255
  }
154
256
  async function handleTerminalUpgrade(request, socket, head, webSocketServer, terminalPort, token) {
155
257
  try {
@@ -340,6 +442,14 @@ function parseWebInputAnswer(value) {
340
442
  }
341
443
  throw new Error("Exactly one non-empty choiceKey or text is required.");
342
444
  }
445
+ async function readMutationBody(request) {
446
+ try {
447
+ return await readJsonBody(request);
448
+ }
449
+ catch (error) {
450
+ throw new WebRequestRejected(error instanceof Error ? error.message : "Invalid request body.");
451
+ }
452
+ }
343
453
  async function readJsonBody(request) {
344
454
  const chunks = [];
345
455
  let size = 0;
@@ -1,10 +1,11 @@
1
- import { isRoleTurnStalled, latestTurnDurableProgressAt } from "../scheduler/roleTurnStall.js";
1
+ import { isRoleRunStalled, latestRunDurableProgressAt } from "../scheduler/roleRunStall.js";
2
2
  import { retiredTaskRecordIds } from "../task/taskRecordRetirement.js";
3
3
  import { buildTaskExecutionProjection } from "../scheduler/taskExecutionProjection.js";
4
4
  import { projectWorkItemExecution } from "../execution/workItemExecutionProjection.js";
5
5
  import { classifyRuntimeHealth, projectRuntimeTaskEvents } from "../runtime/runtimeProjection.js";
6
6
  import { builtinDriverIdForAdapter } from "../runtime/builtinAgentDrivers.js";
7
- import { formatTurnReceiptId } from "../task/taskRecordReference.js";
7
+ import { formatRunReceiptId } from "../task/taskRecordReference.js";
8
+ import { runExecutionObservation } from "../agentRun/agentRun.js";
8
9
  import { resolveRuntimeHealth } from "../config/yuiConfig.js";
9
10
  import { projectSessionTokenMetrics, resolveSessionTokenIdentity } from "../runtime/sessionTokenMetrics.js";
10
11
  import { projectTaskRemoteDelivery } from "../task/remoteDelivery.js";
@@ -14,7 +15,7 @@ export function buildWebDashboardSnapshot(store, now = new Date()) {
14
15
  draft: 0,
15
16
  active: 0,
16
17
  completed: 0,
17
- retired: 0,
18
+ cancelled: 0,
18
19
  archived: 0
19
20
  };
20
21
  const projectNames = new Map(reader.listProjects().map((project) => [project.id, project.name]));
@@ -31,8 +32,8 @@ export function buildWebDashboardSnapshot(store, now = new Date()) {
31
32
  attention.push({ taskId: task.id, taskTitle: task.title, request });
32
33
  }
33
34
  const events = reader.listEvents?.(task.id) ?? [];
34
- const needsAttentionCount = reader.listTurns(task.id)
35
- .filter((turn) => turn.status === "active" && isRoleTurnStalled(events, turn.id))
35
+ const needsAttentionCount = reader.listRuns(task.id)
36
+ .filter((run) => run.status === "active" && isRoleRunStalled(events, run.id))
36
37
  .length;
37
38
  const execution = buildTaskExecutionProjection(reader, task.id, task, now);
38
39
  const names = task.projectBindings.flatMap(({ projectId }) => {
@@ -69,41 +70,51 @@ export function buildWebTaskDetail(store, taskId, now = new Date()) {
69
70
  const name = projectNamesById.get(projectId);
70
71
  return name === undefined ? [] : [name];
71
72
  });
72
- const turns = reader.listTurns(taskId);
73
+ const runs = reader.listRuns(taskId);
73
74
  const events = reader.listEvents?.(taskId) ?? [];
74
75
  const retiredMessageIds = retiredTaskRecordIds(events, "message");
75
- const needsAttentionTurns = turns
76
- .filter((turn) => turn.status === "active" && isRoleTurnStalled(events, turn.id))
77
- .map((turn) => ({
78
- turnId: turn.id,
79
- roleName: turn.roleName,
80
- progressAt: latestStallProgress(events, turn.id),
81
- kind: latestStallField(events, turn.id, "kind") ?? "workflow-not-progressing",
82
- classification: latestStallField(events, turn.id, "classification") ?? "truly-stalled"
76
+ const needsAttentionRuns = runs
77
+ .filter((run) => run.status === "active" && isRoleRunStalled(events, run.id))
78
+ .map((run) => ({
79
+ runId: run.id,
80
+ roleName: run.roleName,
81
+ progressAt: latestStallProgress(events, run.id),
82
+ kind: latestStallField(events, run.id, "kind") ?? "workflow-not-progressing",
83
+ classification: latestStallField(events, run.id, "classification") ?? "truly-stalled"
83
84
  }));
84
- const activeTurns = new Map(turns
85
- .filter((turn) => turn.status === "active")
86
- .map((turn) => [turn.roleName, turn]));
87
- const activeTurnHealth = turns
88
- .filter((turn) => turn.status === "active")
89
- .map((turn) => projectWebTurnRuntimeHealth(reader, taskId, turn, events, now, resolveRuntimeHealth(reader.getConfig().runtimeHealth)));
85
+ const activeRuns = new Map(runs
86
+ .filter((run) => run.status === "active")
87
+ .map((run) => [run.roleName, run]));
88
+ const activeRunHealth = runs
89
+ .filter((run) => run.status === "active")
90
+ .map((run) => projectWebRunRuntimeHealth(reader, taskId, run, events, now, resolveRuntimeHealth(reader.getConfig().runtimeHealth)));
90
91
  const roles = reader.listRoles(taskId).map((role) => {
91
- const activeTurn = activeTurns.get(role.name);
92
+ const activeRun = activeRuns.get(role.name);
92
93
  const sessions = reader.getTaskRoleSessionSet(taskId, role.name);
93
- const activeSession = sessions?.sessions[sessions.activeAgentId];
94
- const effectiveLaunch = activeTurn?.effective ?? activeSession?.effective ?? null;
94
+ const activeSession = sessions?.sessions[activeRun?.effective.agentId ?? sessions.activeAgentId];
95
+ const effectiveLaunch = activeRun?.effective ?? activeSession?.effective ?? null;
96
+ const delivery = sessions?.providerBinding?.run?.status;
95
97
  return {
96
98
  ...role,
97
- // Presentation only: workflow activity is derived from Turn; the
98
- // native Session contributes lifecycle detail when no Turn is active.
99
- status: activeTurn === undefined ? activeSession?.status ?? "idle" : "running",
99
+ // An open record is not proof that its Agent is running.
100
+ status: delivery === "accepted" ? "running"
101
+ : delivery === "submitting" || delivery === "deferred" ? "waiting"
102
+ : activeSession === undefined && activeRun === undefined ? "idle" : "unknown",
100
103
  sessionTokens: projectSessionTokenMetrics(events, resolveSessionTokenIdentity(activeSession === undefined
101
104
  ? null
102
105
  : { taskId, roleName: role.name, ...activeSession })),
103
106
  effectiveLaunch,
104
- effectiveLaunchSource: activeTurn === undefined
107
+ observedAt: now.toISOString(),
108
+ runtimeSession: activeSession === undefined ? null : {
109
+ agentId: activeSession.agentId,
110
+ adapterId: activeSession.adapterId,
111
+ nativeSessionId: activeSession.nativeSessionId ?? null,
112
+ status: activeSession.status,
113
+ endpointImplementation: activeSession.endpointImplementation ?? null
114
+ },
115
+ effectiveLaunchSource: activeRun === undefined
105
116
  ? activeSession === undefined ? null : "session"
106
- : "turn",
117
+ : "run",
107
118
  launchDrift: effectiveLaunch !== null
108
119
  && effectiveLaunch.sourceDesiredRevision !== role.launchRevision
109
120
  };
@@ -128,10 +139,10 @@ export function buildWebTaskDetail(store, taskId, now = new Date()) {
128
139
  workItems: workItems.map((item) => ({
129
140
  ...item,
130
141
  observability: workItemObservability.get(item.id),
131
- execution: projectWorkItemExecution(item, turns, roleSessionSets)
142
+ execution: projectWorkItemExecution(item, runs, roleSessionSets, reader)
132
143
  })),
133
- turns,
134
- runtimeHealth: { needsAttentionTurns, activeTurns: activeTurnHealth },
144
+ runs: runs.map((run) => ({ ...run, execution: runExecutionObservation(run, reader.getTaskRoleSessionSet(taskId, run.roleName)?.providerBinding, events) })),
145
+ runtimeHealth: { needsAttentionRuns, activeRuns: activeRunHealth },
135
146
  reviewRounds: reader.listReviewRounds(taskId),
136
147
  openInputs: inputs.filter((request) => request.status === "open"),
137
148
  messages: reader.listMessages(taskId).map((message) => ({
@@ -149,69 +160,68 @@ function webRemoteDelivery(reader, task) {
149
160
  events: reader.listEvents?.(task.id) ?? [],
150
161
  publications: reader.listPublicationReferences(task.id),
151
162
  managedWorkspaces: reader.listManagedWorkspaces(task.id),
152
- turns: reader.listTurns(task.id),
163
+ runs: reader.listRuns(task.id),
153
164
  currentCandidate: null
154
165
  });
155
166
  }
156
- function latestStallProgress(events, turnId) {
157
- return latestStallField(events, turnId, "progressAt");
167
+ function latestStallProgress(events, runId) {
168
+ return latestStallField(events, runId, "progressAt");
158
169
  }
159
170
  /**
160
- * Layered runtime health for one active Turn, computed from the same stored
171
+ * Layered runtime health for one active AgentRun, computed from the same stored
161
172
  * observations and durable semantic fold as the CLI status projection. The
162
173
  * Web snapshot has no live tmux pane, so host state stays "unknown"; the
163
174
  * classifier still surfaces session/turn/operation/observer layers and the
164
175
  * scheduler's durable `turn.stalled` episode is surfaced as
165
176
  * `stalled-candidate`.
166
177
  */
167
- function projectWebTurnRuntimeHealth(reader, taskId, turn, events, now, policy) {
168
- const stalled = isRoleTurnStalled(events, turn.id);
169
- const sessions = reader.getTaskRoleSessionSet(taskId, turn.roleName);
170
- const session = sessions?.sessions[turn.effective.agentId];
171
- const stallReason = "the live active Turn has no durable progress in the configured stall window";
172
- if (session?.runtimeGenerationId === undefined) {
178
+ function projectWebRunRuntimeHealth(reader, taskId, run, events, now, policy) {
179
+ const stalled = isRoleRunStalled(events, run.id);
180
+ const sessions = reader.getTaskRoleSessionSet(taskId, run.roleName);
181
+ const session = sessions?.sessions[run.effective.agentId];
182
+ const stallReason = "the live active AgentRun has no durable progress in the configured stall window";
183
+ if (session?.nativeSessionId === undefined) {
173
184
  return {
174
- turnId: turn.id,
175
- roleName: turn.roleName,
185
+ runId: run.id,
186
+ roleName: run.roleName,
176
187
  layer: stalled ? "stalled-candidate" : "awaiting-provider-acceptance",
177
- reason: stalled ? stallReason : "the active Turn is awaiting a Provider Session",
188
+ reason: stalled ? stallReason : "the active AgentRun is awaiting a Provider Session",
178
189
  stalled,
179
- lastSemanticProgressAt: turn.createdAt
190
+ lastSemanticProgressAt: run.createdAt
180
191
  };
181
192
  }
182
193
  let driverId;
183
194
  try {
184
- driverId = builtinDriverIdForAdapter(turn.effective.adapterId);
195
+ driverId = builtinDriverIdForAdapter(run.effective.adapterId);
185
196
  }
186
197
  catch {
187
198
  return {
188
- turnId: turn.id,
189
- roleName: turn.roleName,
199
+ runId: run.id,
200
+ roleName: run.roleName,
190
201
  layer: stalled ? "stalled-candidate" : "runtime-unobservable",
191
202
  reason: stalled ? stallReason : "the Agent Driver is not a built-in driver",
192
203
  stalled,
193
- lastSemanticProgressAt: turn.createdAt
204
+ lastSemanticProgressAt: run.createdAt
194
205
  };
195
206
  }
196
- const providerTurn = sessions?.providerBinding?.turn;
207
+ const providerTurn = sessions?.providerBinding?.run;
197
208
  const fence = {
198
209
  taskId,
199
- roleName: turn.roleName,
200
- turnId: turn.id,
201
- agentId: turn.effective.agentId,
210
+ roleName: run.roleName,
211
+ runId: run.id,
212
+ agentId: run.effective.agentId,
202
213
  driverId,
203
- runtimeGenerationId: session.runtimeGenerationId,
204
214
  nativeSessionId: session.nativeSessionId,
205
- nativeTurnId: providerTurn?.turnId === turn.id
206
- ? providerTurn.nativeTurnId ?? turn.id
207
- : turn.id,
208
- receiptId: providerTurn?.turnId === turn.id
215
+ nativeTurnId: providerTurn?.runId === run.id
216
+ ? providerTurn.nativeTurnId ?? run.id
217
+ : run.id,
218
+ receiptId: providerTurn?.runId === run.id
209
219
  ? providerTurn.attemptId
210
- : formatTurnReceiptId(taskId, turn.id)
220
+ : formatRunReceiptId(taskId, run.id)
211
221
  };
212
- const projection = projectRuntimeTaskEvents(fence, turn.createdAt, events);
222
+ const projection = projectRuntimeTaskEvents(fence, run.createdAt, events);
213
223
  const view = {
214
- getTurn: (taskId, turnId) => reader.listTurns(taskId).find((candidate) => candidate.id === turnId) ?? null,
224
+ getRun: (taskId, runId) => reader.listRuns(taskId).find((candidate) => candidate.id === runId) ?? null,
215
225
  listEvents: () => events,
216
226
  getWorkItem: (workItemTaskId, workItemId) => reader.listWorkItems(workItemTaskId).find((item) => item.id === workItemId) ?? null,
217
227
  listReviewRounds: (taskId) => reader.listReviewRounds(taskId),
@@ -219,8 +229,8 @@ function projectWebTurnRuntimeHealth(reader, taskId, turn, events, now, policy)
219
229
  listIntegrationAttempts: (taskId) => reader.listIntegrationAttempts(taskId),
220
230
  listInputRequests: (taskId) => reader.listInputRequests(taskId)
221
231
  };
222
- const semanticProgress = latestTurnDurableProgressAt(view, taskId, turn.roleName, turn.id)
223
- ?? { progressAt: turn.createdAt };
232
+ const semanticProgress = latestRunDurableProgressAt(view, taskId, run.roleName, run.id)
233
+ ?? { progressAt: run.createdAt };
224
234
  const classification = classifyRuntimeHealth({
225
235
  projection,
226
236
  semanticProgressAt: semanticProgress.progressAt,
@@ -228,8 +238,8 @@ function projectWebTurnRuntimeHealth(reader, taskId, turn, events, now, policy)
228
238
  policy
229
239
  });
230
240
  return {
231
- turnId: turn.id,
232
- roleName: turn.roleName,
241
+ runId: run.id,
242
+ roleName: run.roleName,
233
243
  layer: stalled ? "stalled-candidate" : classification.layer,
234
244
  reason: stalled ? stallReason : classification.reason,
235
245
  stalled,
@@ -239,10 +249,10 @@ function projectWebTurnRuntimeHealth(reader, taskId, turn, events, now, policy)
239
249
  lastSemanticProgressAt: classification.lastSemanticProgressAt
240
250
  };
241
251
  }
242
- function latestStallField(events, turnId, field) {
252
+ function latestStallField(events, runId, field) {
243
253
  const stalled = events
244
- .filter((event) => event.type === "turn.stalled"
245
- && event.payload.turnId === turnId
254
+ .filter((event) => event.type === "run.stalled"
255
+ && event.payload.runId === runId
246
256
  && event.payload.status !== "diagnostic-only")
247
257
  .sort((left, right) => Date.parse(right.createdAt) - Date.parse(left.createdAt))[0];
248
258
  return stalled?.payload[field];
@@ -250,11 +260,8 @@ function latestStallField(events, turnId, field) {
250
260
  function countWorkItems(items) {
251
261
  const counts = {
252
262
  total: items.length,
253
- pending: 0,
254
- running: 0,
255
- awaiting_acceptance: 0,
256
- completed: 0,
257
- failed: 0,
263
+ open: 0,
264
+ accepted: 0,
258
265
  retired: 0
259
266
  };
260
267
  const mutable = counts;
@@ -268,7 +275,7 @@ function compareDashboardTasks(left, right) {
268
275
  active: 0,
269
276
  draft: 1,
270
277
  completed: 2,
271
- retired: 3,
278
+ cancelled: 3,
272
279
  archived: 4
273
280
  };
274
281
  return statusOrder[left.status] - statusOrder[right.status]
@@ -0,0 +1,64 @@
1
+ import { readTaskContext, readTaskContextDelta, inspectTaskContext, withContextObservations } from "../context/taskContext.js";
2
+ import { BUILTIN_CAPABILITIES } from "../kernel/builtinCapabilities.js";
3
+ import { capabilitySchemaError } from "../kernel/capabilitySchema.js";
4
+ import { updateTaskMetadataCommand, sendTaskMessageCommand } from "../commands/taskCommands.js";
5
+ import { webLocalMutation, WebRequestRejected } from "./webMutation.js";
6
+ import { runTaskInputCommand } from "../commands/taskInputCommands.js";
7
+ /** Installed only by the local-user Web composition root. HTTP authenticates
8
+ * its token before using this port; input never supplies a caller or Role.
9
+ * Managed capability RPC keeps its own Session authentication unchanged.
10
+ */
11
+ export function createWebTaskSurface(store, options = {}, observations = []) {
12
+ const environment = {};
13
+ const commandOptions = { ...options, environment, runtime: undefined };
14
+ // Notifications are after the outer transaction. Their failure must not be
15
+ // reclassified as a rejection of a mutation that already committed.
16
+ const notify = (taskId, leader = false) => {
17
+ if (options.runtime?.notifyMailboxChanged) {
18
+ void options.runtime.notifyMailboxChanged(leader
19
+ ? { kind: "role", taskId, roleName: "leader" } : { kind: "task", taskId });
20
+ }
21
+ else
22
+ options.runtime?.notifyStateChanged(taskId);
23
+ };
24
+ return {
25
+ message: (taskId, body) => {
26
+ // `queuedForLeader` is the fact the shared transaction actually committed,
27
+ // not a re-derivation from Task status. A Draft queues its Leader exactly
28
+ // like an active Task (planning is a Leader conversation), so reporting
29
+ // "saved" here would have understated a wake that really did happen.
30
+ const { message, task, queuedForLeader } = webLocalMutation(store, (tx) => sendTaskMessageCommand(tx, taskId, body, "leader", commandOptions));
31
+ notify(taskId, queuedForLeader);
32
+ return { record: message, revision: message.createdAt,
33
+ disposition: queuedForLeader ? "queued" : "saved",
34
+ planning: task.status === "draft",
35
+ target: { scope: "task", taskId, roleName: "leader" } };
36
+ },
37
+ read: async (taskId) => withContextObservations(readTaskContext(store, taskId, environment), observations),
38
+ delta: (taskId, input) => readTaskContextDelta(store, taskId, input, environment),
39
+ inspect: (taskId, input) => inspectTaskContext(store, taskId, input, environment),
40
+ update: (taskId, input) => {
41
+ const descriptor = BUILTIN_CAPABILITIES.find((entry) => entry.name === "task.update");
42
+ const error = capabilitySchemaError(descriptor.inputSchema, { taskId, patch: input });
43
+ if (error)
44
+ throw new WebRequestRejected(error);
45
+ const patch = input;
46
+ const record = webLocalMutation(store, (tx) => updateTaskMetadataCommand(tx, taskId, {
47
+ ...patch,
48
+ ...(patch.description === "" ? { description: null } : {}),
49
+ ...(patch.tags?.length === 0 ? { tags: null } : {})
50
+ }, commandOptions));
51
+ notify(taskId);
52
+ return { record, revision: record.updatedAt };
53
+ },
54
+ answer: (taskId, inputId, answer) => {
55
+ const result = webLocalMutation(store, (tx) => runTaskInputCommand([
56
+ "answer", inputId, "--task", taskId,
57
+ ...("choiceKey" in answer ? ["--choice", answer.choiceKey] : ["--text", answer.text])
58
+ ], tx, commandOptions));
59
+ const data = result.data;
60
+ notify(taskId, true);
61
+ return data.request;
62
+ }
63
+ };
64
+ }
@@ -21,7 +21,7 @@ export class WorkItemDependencyGateError extends Error {
21
21
  export function assertWorkItemDependenciesCompleted(store, item) {
22
22
  const unmetDependencies = item.dependsOn.flatMap((dependencyId) => {
23
23
  const dependency = store.getWorkItem(item.taskId, dependencyId);
24
- if (dependency?.status === "completed")
24
+ if (dependency?.status === "accepted")
25
25
  return [];
26
26
  return [{
27
27
  id: dependencyId,