@zq-silk/yui 0.15.8 → 0.15.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/ARCHITECTURE.md +2 -0
  2. package/ARCHITECTURE.zh-CN.md +151 -0
  3. package/README.md +211 -14
  4. package/dist/agent/launchEnvironment.js +7 -0
  5. package/dist/artifacts/artifactCapability.js +74 -0
  6. package/dist/artifacts/artifactCommitLock.js +249 -0
  7. package/dist/artifacts/artifactPaths.js +151 -0
  8. package/dist/artifacts/gitArtifactRef.js +146 -0
  9. package/dist/artifacts/managedGit.js +332 -0
  10. package/dist/artifacts/taskArtifactRepository.js +277 -0
  11. package/dist/cli/commandCatalog.js +40 -16
  12. package/dist/cli/interactionPolicy.js +3 -3
  13. package/dist/cli/updateOrchestrator.js +24 -1
  14. package/dist/cli/updatePorts.js +7 -3
  15. package/dist/cli/upgradeCommand.js +42 -2
  16. package/dist/cli.js +403 -93
  17. package/dist/commands/globalRoleCommands.js +314 -4
  18. package/dist/commands/operatorCommands.js +33 -2
  19. package/dist/commands/projectCommands.js +6 -7
  20. package/dist/commands/releaseCommands.js +18 -0
  21. package/dist/commands/taskActivationCommands.js +22 -0
  22. package/dist/commands/taskActor.js +25 -0
  23. package/dist/commands/taskCommands.js +846 -155
  24. package/dist/commands/taskIntegrationCommands.js +16 -38
  25. package/dist/commands/taskIntegrationQueueCommands.js +1 -1
  26. package/dist/commands/taskRemoteDeliveryCommand.js +6 -6
  27. package/dist/commands/taskRoleRuntimeStatus.js +35 -0
  28. package/dist/context/runContextPack.js +28 -16
  29. package/dist/context/taskContext.js +64 -5
  30. package/dist/controller/agentHostObservation.js +155 -0
  31. package/dist/controller/clientRuntime.js +17 -2
  32. package/dist/controller/controller.js +11 -2
  33. package/dist/controller/fileSchedulerStoreAdapter.js +446 -13
  34. package/dist/controller/globalInputDelivery.js +119 -0
  35. package/dist/controller/jobControl.js +6 -2
  36. package/dist/controller/resourceInventory.js +14 -4
  37. package/dist/controller/resourceInventoryLinux.js +2 -6
  38. package/dist/controller/runtime.js +81 -6
  39. package/dist/controller/runtimeEventInbox.js +32 -3
  40. package/dist/controller/runtimeEventProcessor.js +26 -6
  41. package/dist/controller/runtimeHookRunFence.js +75 -19
  42. package/dist/controller/structuredProviderObservation.js +133 -70
  43. package/dist/coordination/workMailboxQueue.js +5 -0
  44. package/dist/execution/workItemExecutionProjection.js +1 -1
  45. package/dist/executor/agentExecutor.js +64 -4
  46. package/dist/executor/executorRegistry.js +3 -0
  47. package/dist/executor/fileRoleLaunchPlanner.js +78 -118
  48. package/dist/integration/deliveryObligation.js +2 -1
  49. package/dist/integration/gitIntegrationService.js +312 -382
  50. package/dist/integration/integrationAttempt.js +30 -4
  51. package/dist/integration/integrationQueueService.js +7 -7
  52. package/dist/integration/integrationSourceApplication.js +323 -0
  53. package/dist/kernel/builtinCapabilities.js +32 -24
  54. package/dist/message/globalInterrupt.js +33 -0
  55. package/dist/message/inputControlResolution.js +106 -0
  56. package/dist/message/message.js +423 -0
  57. package/dist/message/messageContinuation.js +126 -3
  58. package/dist/message/taskInterrupt.js +34 -0
  59. package/dist/observability/orchestrationMetrics.js +1 -1
  60. package/dist/plugins/pluginService.js +11 -3
  61. package/dist/release/releaseHandover.js +22 -0
  62. package/dist/release/releaseWorkflowPorts.js +15 -7
  63. package/dist/repository/gitWorkspace.js +72 -15
  64. package/dist/repository/taskWorkspaceCoordinator.js +134 -0
  65. package/dist/repository/taskWorkspacePreparer.js +120 -49
  66. package/dist/repository/workItemCandidateSnapshot.js +34 -0
  67. package/dist/resources/projectResource.js +0 -48
  68. package/dist/resources/projectResourceService.js +3 -81
  69. package/dist/resources/resourceDiscovery.js +3 -2
  70. package/dist/runtime/agentHost.js +152 -72
  71. package/dist/runtime/agentHostCompatibility.js +127 -0
  72. package/dist/runtime/agentHostProtocol.js +53 -0
  73. package/dist/runtime/executionEnvironment.js +0 -19
  74. package/dist/runtime/launchBroker.js +6 -0
  75. package/dist/runtime/sessionReconciliation.js +4 -4
  76. package/dist/runtime/taskRuntimeIsolation.js +30 -6
  77. package/dist/runtime/tmuxAdapters.js +5 -3
  78. package/dist/scheduler/operatorEvent.js +4 -0
  79. package/dist/scheduler/taskExecutionProjection.js +12 -1
  80. package/dist/scheduler/wakeReason.js +7 -1
  81. package/dist/scheduler/wakeupQueue.js +2 -0
  82. package/dist/setup/setupCommand.js +29 -16
  83. package/dist/storage/homeLayout.js +130 -0
  84. package/dist/storage/migrations/artifactsToGit.js +338 -0
  85. package/dist/storage/migrations/collapseWorktreeLayout.js +963 -0
  86. package/dist/storage/migrations/integrationContinuation.js +104 -0
  87. package/dist/storage/migrations/submitIntent.js +126 -0
  88. package/dist/storage/migrations/unifyHomeLayout.js +925 -0
  89. package/dist/storage/sqliteSchema.js +173 -7
  90. package/dist/storage/sqliteStore.js +41 -22
  91. package/dist/storage/storageVersions.js +1 -1
  92. package/dist/storage/storeRpc.js +2 -1
  93. package/dist/storage/upgrade/upgradeOrchestrator.js +95 -2
  94. package/dist/task/archiveDiagnostics.js +128 -0
  95. package/dist/task/nextAction.js +44 -11
  96. package/dist/task/taskActivation.js +26 -0
  97. package/dist/task/taskActivationService.js +85 -69
  98. package/dist/task/taskSubmission.js +236 -0
  99. package/dist/web/assets/client/app.js +58 -2
  100. package/dist/web/assets/client/components.js +1 -0
  101. package/dist/web/assets/client/i18n.js +6 -0
  102. package/dist/web/assets/client/taskSurface.js +202 -7
  103. package/dist/web/assets/client/view.js +7 -4
  104. package/dist/web/assets/shell.js +23 -0
  105. package/dist/web/assets/styles/layout.js +1 -1
  106. package/dist/web/assets/styles/widgets.js +12 -0
  107. package/dist/web/webServer.js +135 -4
  108. package/dist/web/webSnapshot.js +4 -3
  109. package/dist/web/webTaskSurface.js +225 -8
  110. package/dist/workItem/workItem.js +14 -10
  111. package/dist/workspace/workItemChangeSetManager.js +18 -2
  112. package/docs/agent-result-consumption.md +2 -0
  113. package/docs/agent-result-consumption.zh-CN.md +81 -0
  114. package/docs/agent-runtime-drivers.md +2 -0
  115. package/docs/agent-runtime-drivers.zh-CN.md +77 -0
  116. package/docs/architecture/README.md +44 -32
  117. package/docs/architecture/README.zh-CN.md +43 -0
  118. package/docs/architecture/capabilities-and-resources.md +118 -79
  119. package/docs/architecture/capabilities-and-resources.zh-CN.md +83 -0
  120. package/docs/managed-turn-and-session-runtime.md +2 -0
  121. package/docs/managed-turn-and-session-runtime.zh-CN.md +180 -0
  122. package/docs/observability/README.md +2 -0
  123. package/docs/observability/README.zh-CN.md +71 -0
  124. package/docs/plugin-sdk.md +320 -217
  125. package/docs/plugin-sdk.zh-CN.md +293 -0
  126. package/docs/provider-runtime.md +2 -0
  127. package/docs/provider-runtime.zh-CN.md +132 -0
  128. package/docs/release-workflow.md +41 -0
  129. package/docs/release-workflow.zh-CN.md +266 -0
  130. package/docs/roles-and-configuration.md +2 -0
  131. package/docs/roles-and-configuration.zh-CN.md +96 -0
  132. package/docs/sqlite-control-plane-design.md +225 -1
  133. package/docs/sqlite-control-plane-design.zh-CN.md +62 -0
  134. package/docs/task-dag-semantics.md +80 -57
  135. package/docs/task-dag-semantics.zh-CN.md +59 -0
  136. package/docs/task-delivery.md +2 -0
  137. package/docs/task-delivery.zh-CN.md +82 -0
  138. package/docs/task-local-identity.md +2 -0
  139. package/docs/task-local-identity.zh-CN.md +58 -0
  140. package/docs/testing/verification-levels.md +26 -0
  141. package/docs/testing/verification-levels.zh-CN.md +80 -0
  142. package/i18n/README.zh-CN.md +199 -10
  143. package/package.json +2 -1
  144. package/skills/yui-leader/SKILL.md +88 -331
  145. package/skills/yui-leader/references/execution.md +405 -0
  146. package/skills/yui-leader/references/integration.md +52 -2
  147. package/skills/yui-leader/references/planning.md +109 -0
  148. package/skills/yui-leader/references/task-plugins.md +8 -4
  149. package/skills/yui-operator/SKILL.md +22 -4
  150. package/skills/yui-runtime/SKILL.md +27 -0
  151. package/skills/yui-runtime/references/publication.md +20 -0
@@ -1,14 +1,53 @@
1
+ import { recordTaskInterruptResult } from "../message/taskInterrupt.js";
2
+ import { recordGlobalInterruptResult, recordGlobalSteerResult } from "../message/globalInterrupt.js";
3
+ import { runGlobalRoleCommand } from "../commands/globalRoleCommands.js";
1
4
  import { readTaskContext, readTaskContextDelta, inspectTaskContext, withContextObservations } from "../context/taskContext.js";
2
5
  import { BUILTIN_CAPABILITIES } from "../kernel/builtinCapabilities.js";
3
6
  import { capabilitySchemaError } from "../kernel/capabilitySchema.js";
4
- import { updateTaskMetadataCommand, sendTaskMessageCommand } from "../commands/taskCommands.js";
7
+ import { updateTaskMetadataCommand, sendTaskMessageCommand, runTaskCommand } from "../commands/taskCommands.js";
5
8
  import { webLocalMutation, WebRequestRejected } from "./webMutation.js";
6
9
  import { runTaskInputCommand } from "../commands/taskInputCommands.js";
10
+ import { sendAgentHostSteerControl, sendAgentHostCancelControl, AGENT_HOST_CONTROL_PROTOCOL, foldSteerLiveReceipt, foldInterruptLiveReceipt } from "../runtime/agentHost.js";
11
+ const DEFAULT_WEB_HOST_CONTROL = {
12
+ steer: sendAgentHostSteerControl,
13
+ cancel: sendAgentHostCancelControl
14
+ };
15
+ /** The store-only CLI argv for the shared application-layer primitive
16
+ * (decision-3 §7). The Web surface never re-implements the queue/steer/interrupt
17
+ * decisions; it drives the exact same command the CLI drives. */
18
+ function controlArgv(taskId, input) {
19
+ if (input.action === "queue") {
20
+ return ["message", "queue", taskId, input.body, "--request-id", input.requestId,
21
+ ...(input.to === undefined ? [] : ["--to", input.to]),
22
+ ...(input.workItem === undefined ? [] : ["--work-item", input.workItem]),
23
+ ...(input.reviewRound === undefined ? [] : ["--review-round", input.reviewRound])];
24
+ }
25
+ if (input.action === "steer") {
26
+ return ["message", "steer", taskId, input.body, "--request-id", input.requestId,
27
+ "--expected-target", input.expectedTarget, "--to", input.to,
28
+ ...(input.workItem === undefined ? [] : ["--work-item", input.workItem]),
29
+ ...(input.reviewRound === undefined ? [] : ["--review-round", input.reviewRound])];
30
+ }
31
+ return ["role", "interrupt", taskId, input.role, "--expected-target", input.expectedTarget,
32
+ ...(input.thenMessage === undefined ? [] : ["--then-message", input.thenMessage]),
33
+ "--request-id", input.requestId];
34
+ }
35
+ /** A queue is delivered to the Leader mailbox only when it is unaddressed or
36
+ * addressed to the Leader; an addressed Worker/Reviewer queue goes to the Task
37
+ * mailbox. A steer/interrupt is a live control op, so it only reconciles the
38
+ * Task. This mirrors the mailbox the core command itself enqueues. */
39
+ function controlNotifiesLeader(input) {
40
+ return input.action === "queue" && (input.to === undefined || input.to === "leader");
41
+ }
42
+ function controlTarget(taskId, input) {
43
+ const roleName = input.action === "interrupt" ? input.role : input.to ?? "leader";
44
+ return { scope: "task", taskId, roleName };
45
+ }
7
46
  /** Installed only by the local-user Web composition root. HTTP authenticates
8
47
  * its token before using this port; input never supplies a caller or Role.
9
48
  * Managed capability RPC keeps its own Session authentication unchanged.
10
49
  */
11
- export function createWebTaskSurface(store, options = {}, observations = []) {
50
+ export function createWebTaskSurface(store, options = {}, observations = [], hostControl = DEFAULT_WEB_HOST_CONTROL) {
12
51
  const environment = {};
13
52
  const commandOptions = { ...options, environment, runtime: undefined };
14
53
  // Notifications are after the outer transaction. Their failure must not be
@@ -22,18 +61,196 @@ export function createWebTaskSurface(store, options = {}, observations = []) {
22
61
  options.runtime?.notifyStateChanged(taskId);
23
62
  };
24
63
  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));
64
+ globalState: (roleName) => {
65
+ const role = store.getGlobalRole(roleName);
66
+ if (role === null)
67
+ throw new WebRequestRejected("Global Role not found.");
68
+ const sessions = store.getGlobalRoleSessionSet(roleName);
69
+ return {
70
+ roleName,
71
+ nativeSessionId: sessions?.sessions[sessions.activeAgentId]?.nativeSessionId,
72
+ turn: sessions?.providerBinding?.run ?? null,
73
+ authority: sessions?.providerBinding?.authority ?? null,
74
+ interrupts: sessions?.interrupts ?? {},
75
+ messages: store.listGlobalRoleMessages(roleName).map(message => ({
76
+ id: message.id, body: message.body, inputControl: message.inputControl,
77
+ control: message.control, delivery: message.delivery, notDelivered: message.notDelivered
78
+ }))
79
+ };
80
+ },
81
+ globalControl: async (roleName, input) => {
82
+ const argv = input.action === "interrupt"
83
+ ? ["interrupt", roleName, "--request-id", input.requestId, "--expected-target", input.expectedTarget,
84
+ ...(input.thenMessage === undefined ? [] : ["--then-message", input.thenMessage])]
85
+ : ["message", input.action, roleName, input.body, "--request-id", input.requestId,
86
+ ...(input.action === "steer" ? ["--expected-target", input.expectedTarget] : [])];
87
+ const result = webLocalMutation(store, tx => runGlobalRoleCommand(argv, tx, {
88
+ env: {}, yuiHome: options.yuiHome, jsonOutput: true
89
+ }));
90
+ if (typeof result === "string") {
91
+ if (input.action === "queue")
92
+ void options.runtime?.notifyMailboxChanged?.({
93
+ kind: "global-role-runtime", roleName
94
+ });
95
+ return { action: input.action, ...JSON.parse(result) };
96
+ }
97
+ if (result.kind !== "input-steer" && result.kind !== "input-interrupt") {
98
+ throw new WebRequestRejected("Global input cannot perform a Session lifecycle operation.");
99
+ }
100
+ if (options.yuiHome === undefined)
101
+ throw new Error("Global control requires a configured Yui Home.");
102
+ if (result.kind === "input-steer") {
103
+ let control;
104
+ try {
105
+ control = await hostControl.steer({
106
+ home: options.yuiHome, scope: "global", roleName,
107
+ control: { protocol: AGENT_HOST_CONTROL_PROTOCOL, type: "steer-turn",
108
+ nativeSessionId: result.target.nativeSessionId, nativeTurnId: result.target.nativeTurnId,
109
+ authority: result.target.authority,
110
+ run: { attemptId: result.receiptId, boundedText: result.text } }
111
+ });
112
+ }
113
+ catch (error) {
114
+ recordGlobalSteerResult(store, roleName, result.messageId, { state: "steer-unknown", outcome: "pending" });
115
+ throw error;
116
+ }
117
+ const steer = foldSteerLiveReceipt(control);
118
+ recordGlobalSteerResult(store, roleName, result.messageId, steer);
119
+ return { action: "steer", roleName, messageId: result.messageId, steer };
120
+ }
121
+ let control;
122
+ try {
123
+ control = await hostControl.cancel({
124
+ home: options.yuiHome, scope: "global", roleName,
125
+ control: { protocol: AGENT_HOST_CONTROL_PROTOCOL, type: "cancel", nativeOnly: true,
126
+ nativeSessionId: result.target.nativeSessionId,
127
+ authority: result.target.authority, attemptId: result.target.attemptId }
128
+ });
129
+ }
130
+ catch (error) {
131
+ recordGlobalInterruptResult(store, roleName, result.receiptId, { state: "interrupt-unknown", outcome: "cancel-requested" });
132
+ throw error;
133
+ }
134
+ const interrupt = foldInterruptLiveReceipt(control);
135
+ recordGlobalInterruptResult(store, roleName, result.receiptId, interrupt);
136
+ return { action: "interrupt", roleName, receiptId: result.receiptId, interrupt };
137
+ },
138
+ message: (taskId, body, intent, requestId) => {
139
+ // Preserve the submission's intent and frozen receipt separately from
140
+ // queue/steer controls; an omitted intent still means discuss.
141
+ const { message, task, queuedForLeader, feedback } = webLocalMutation(store, (tx) => sendTaskMessageCommand(tx, taskId, body, undefined, commandOptions, undefined, intent, requestId));
31
142
  notify(taskId, queuedForLeader);
32
143
  return { record: message, revision: message.createdAt,
33
144
  disposition: queuedForLeader ? "queued" : "saved",
34
145
  planning: task.status === "draft",
146
+ ...(feedback === undefined ? {} : { submission: feedback }),
35
147
  target: { scope: "task", taskId, roleName: "leader" } };
36
148
  },
149
+ /**
150
+ * The decision-3 three-action input-control path for the local-user Web
151
+ * surface. Its store-only phase is the identical shared application-layer
152
+ * primitive the CLI uses (`runTaskCommand`), run inside `webLocalMutation`
153
+ * so a rejected input is provably not-submitted. A ready steer/interrupt
154
+ * returns a live intent; the single Agent Host edge then runs OUTSIDE the
155
+ * transaction exactly as cli.ts performs it — never a fallback, retarget, or
156
+ * fourth action. A committed input whose live edge fails is delivery-unknown,
157
+ * not not-submitted: it throws a plain error so the receipt is "unknown" and
158
+ * the durable Message is retained (decision-3 §1/§3/§5, message-5 gap F).
159
+ */
160
+ control: async (taskId, input) => {
161
+ const execution = webLocalMutation(store, (tx) => runTaskCommand(controlArgv(taskId, input), tx, commandOptions));
162
+ if (execution.kind === "output") {
163
+ // A queue receipt, or a steer/interrupt that was saved-but-not-delivered
164
+ // or an idempotent replay: fully durable, no live edge, exact disposition.
165
+ notify(taskId, controlNotifiesLeader(input));
166
+ const data = execution.data;
167
+ const settlement = data.delivery ?? data.steer ?? data.interrupt;
168
+ return {
169
+ action: input.action, disposition: settlement?.state ?? "saved",
170
+ target: controlTarget(taskId, input),
171
+ ...(data.message === undefined ? {} : { record: data.message, revision: data.message.createdAt }),
172
+ ...(data.delivery === undefined ? {} : { delivery: data.delivery }),
173
+ ...(data.steer === undefined ? {} : { steer: data.steer }),
174
+ ...(data.interrupt === undefined ? {} : { interrupt: data.interrupt })
175
+ };
176
+ }
177
+ // A resolved live control op. Core has persisted the Message (steer) and
178
+ // recorded the one `pending` control attempt; both are already committed.
179
+ const home = options.yuiHome;
180
+ if (home === undefined) {
181
+ throw new Error("Live Agent Host control requires a configured Yui home.");
182
+ }
183
+ if (execution.kind === "input-steer") {
184
+ let control;
185
+ try {
186
+ control = await hostControl.steer({
187
+ home, scope: "task", taskId: execution.taskId, roleName: execution.roleName,
188
+ control: {
189
+ protocol: AGENT_HOST_CONTROL_PROTOCOL, type: "steer-turn",
190
+ nativeSessionId: execution.target.nativeSessionId,
191
+ nativeTurnId: execution.target.nativeTurnId ?? execution.target.attemptId,
192
+ authority: execution.target.authority,
193
+ run: { attemptId: execution.receiptId, boundedText: execution.text }
194
+ }
195
+ });
196
+ }
197
+ catch (error) {
198
+ throw new Error(`Steer message ${execution.messageId} is saved but the native steer did not `
199
+ + `complete: ${error instanceof Error ? error.message : String(error)}. The Message is `
200
+ + "retained and its outcome is recorded from the Host; whether the Provider accepted it may "
201
+ + "be delivery-unknown. Re-read the Session before acting; do not reissue the same input "
202
+ + "under a new requestId or a different action.");
203
+ }
204
+ notify(taskId);
205
+ // decision-3 §7 live acceptance: fold the actual Host outcome rather than
206
+ // presume success. `steered` is the only proven delivery; pending is
207
+ // delivery-unknown; rejected/unavailable did not deliver. No fallback.
208
+ const steer = foldSteerLiveReceipt(control);
209
+ return { action: "steer", disposition: steer.state,
210
+ taskId, roleName: execution.roleName, messageId: execution.messageId,
211
+ target: { scope: "task", taskId, roleName: execution.roleName },
212
+ steer };
213
+ }
214
+ let control;
215
+ if (execution.kind !== "input-interrupt") {
216
+ // The three-action argv only ever yields output/input-steer/input-interrupt;
217
+ // any other intent means the shared command was mis-dispatched, not a
218
+ // control outcome to fold. Fail closed rather than guess.
219
+ throw new Error(`Unexpected control execution kind: ${execution.kind}.`);
220
+ }
221
+ try {
222
+ control = await hostControl.cancel({
223
+ home, scope: "task", taskId: execution.taskId, roleName: execution.roleName,
224
+ control: {
225
+ protocol: AGENT_HOST_CONTROL_PROTOCOL, type: "cancel",
226
+ nativeOnly: true,
227
+ nativeSessionId: execution.target.nativeSessionId,
228
+ // Native cancel names the exact original execution attempt it stops,
229
+ // never the durable receiptId of this interrupt operation.
230
+ attemptId: execution.target.attemptId,
231
+ authority: execution.target.authority
232
+ }
233
+ });
234
+ }
235
+ catch (error) {
236
+ recordTaskInterruptResult(store, execution.taskId, execution.receiptId, { state: "interrupt-unknown", outcome: "cancel-requested" });
237
+ throw new Error(`Interrupt ${execution.receiptId} of ${execution.taskId}/${execution.roleName} did not complete: `
238
+ + `${error instanceof Error ? error.message : String(error)}. No process was killed; `
239
+ + "re-read the Session before retrying.");
240
+ }
241
+ notify(taskId);
242
+ // The proof is `control.cancellation`, not the bare `cancel-requested`
243
+ // outcome: only a proven stop-request is `interrupted`. A then-handoff, if
244
+ // any, was already claimed durably by Core and is delivered once by the
245
+ // ordinary continuation path — never re-driven from this receipt.
246
+ const interrupt = foldInterruptLiveReceipt(control);
247
+ recordTaskInterruptResult(store, execution.taskId, execution.receiptId, interrupt);
248
+ return { action: "interrupt", disposition: interrupt.state,
249
+ taskId, roleName: execution.roleName,
250
+ target: { scope: "task", taskId, roleName: execution.roleName },
251
+ ...(execution.thenMessageId === undefined ? {} : { thenMessageId: execution.thenMessageId }),
252
+ interrupt };
253
+ },
37
254
  read: async (taskId) => withContextObservations(readTaskContext(store, taskId, environment), observations),
38
255
  delta: (taskId, input) => readTaskContextDelta(store, taskId, input, environment),
39
256
  inspect: (taskId, input) => inspectTaskContext(store, taskId, input, environment),
@@ -1,3 +1,4 @@
1
+ import { validateGitArtifactRef } from "../artifacts/gitArtifactRef.js";
1
2
  import { normalizedUniqueIdentities, normalizedUniqueText, requireIdentity, requireText, requireTimestamp } from "../domain/validation.js";
2
3
  import { validateReviewConfig } from "../review/reviewConfig.js";
3
4
  import { taskFinalReviewConfig, validateTaskFinalReviewContract } from "../review/taskFinalReviewContract.js";
@@ -112,7 +113,7 @@ export function submitWorkItemCandidate(workItem, input, now) {
112
113
  ...(input.taskMainSnapshot === undefined
113
114
  ? {}
114
115
  : { taskMainSnapshot: input.taskMainSnapshot }),
115
- ...(input.artifactRefs === undefined ? {} : { artifactRefs: input.artifactRefs.map((ref) => ({ ...ref })) }),
116
+ ...(input.artifactRefs === undefined ? {} : { artifactRefs: input.artifactRefs.map((ref) => validateGitArtifactRef(ref)) }),
116
117
  createdAt: now.toISOString()
117
118
  });
118
119
  const { outcome: _outcome, endedAt: _endedAt, ...base } = workItem;
@@ -502,17 +503,20 @@ export function validateWorkItemCandidate(candidate) {
502
503
  if (candidate.artifactRefs !== undefined) {
503
504
  if (!Array.isArray(candidate.artifactRefs))
504
505
  throw new Error("Candidate Artifact refs must be an array.");
505
- const ids = new Set();
506
+ const identities = new Set();
506
507
  for (const ref of candidate.artifactRefs) {
507
- requireIdentity(ref.artifactId, "Candidate Artifact id");
508
- if (ref.taskId !== candidate.taskId || !["content", "external-version", "receipt"].includes(ref.kind)
509
- || ids.has(ref.artifactId))
510
- throw new Error("Candidate Artifact scope, kind or identity is invalid.");
511
- if ((ref.kind !== "external-version" || ref.digest !== undefined)
512
- && (typeof ref.digest !== "string" || !/^[a-f0-9]{64}$/u.test(ref.digest))) {
513
- throw new Error("Candidate Artifact digest is invalid.");
508
+ // Pure, no-I/O shape check: the commit self-certifies the frozen bytes,
509
+ // so a valid pinned reference is complete evidence on its own. Existence
510
+ // is proven lazily when the bytes are resolved on the async read path.
511
+ const valid = validateGitArtifactRef(ref);
512
+ if (valid.taskId !== candidate.taskId) {
513
+ throw new Error("Candidate Artifact scope, commit or path is invalid.");
514
514
  }
515
- ids.add(ref.artifactId);
515
+ const identity = `${valid.commit}:${valid.relativePath}`;
516
+ if (identities.has(identity)) {
517
+ throw new Error("Candidate Artifact scope, commit or path is invalid.");
518
+ }
519
+ identities.add(identity);
516
520
  }
517
521
  }
518
522
  if (typeof candidate.source !== "object" || candidate.source === null) {
@@ -1,4 +1,5 @@
1
1
  import { isDeepStrictEqual } from "node:util";
2
+ import { lstat } from "node:fs/promises";
2
3
  import { createWorkItemChangeSet } from "../integration/changeSet.js";
3
4
  import { createChangeSetManifest } from "../integration/changeSetManifest.js";
4
5
  import { deriveManifestTags } from "../integration/manifestTags.js";
@@ -60,11 +61,25 @@ export class WorkItemChangeSetManager {
60
61
  const git = new NodeGitWorkspace();
61
62
  const projects = [];
62
63
  for (const entry of writableEntries(workspace)) {
63
- if (!await git.isClean(entry.path)) {
64
+ const path = await lstat(entry.path).catch(error => {
65
+ if (error.code === "ENOENT")
66
+ return null;
67
+ throw error;
68
+ });
69
+ if (path?.isSymbolicLink())
70
+ throw new Error(`WorkItem path is a symbolic link: ${entry.path}.`);
71
+ if (path !== null && !await git.isClean(entry.path)) {
64
72
  throw new Error(`WorkItem Project workspace is not clean: ${item.id}/${entry.projectId}.`);
65
73
  }
66
- const workspaceHeadCommit = (await git.inspect(entry.path, "HEAD")).baseCommit;
67
74
  const resultCommit = candidate?.gitSnapshot?.projects.find(({ projectId }) => projectId === entry.projectId)?.commit;
75
+ // Absence is a filesystem fact, not proof of integration or Git cleanup.
76
+ // Check any retained branch against the same frozen Candidate; the
77
+ // cleanup primitive separately removes its exact Git registration.
78
+ const repository = this.store.getTaskWorkspace(taskId)?.entries.find(e => e.projectId === entry.projectId);
79
+ const workspaceHeadCommit = path !== null ? (await git.inspect(entry.path, "HEAD")).baseCommit
80
+ : repository !== undefined && await git.refExists(repository.path, entry.branch)
81
+ ? (await git.inspect(repository.path, entry.branch)).baseCommit
82
+ : resultCommit;
68
83
  if (candidate?.workspace === undefined
69
84
  || !isDeepStrictEqual(candidate.workspace, workspace)
70
85
  || resultCommit === undefined
@@ -109,6 +124,7 @@ export class WorkItemChangeSetManager {
109
124
  }
110
125
  const unresolved = this.store.listIntegrationAttempts(task.id).find((attempt) => (attempt.status === "running"
111
126
  || attempt.status === "blocked"
127
+ || attempt.status === "conflicted"
112
128
  || attempt.status === "validating"));
113
129
  if (unresolved !== undefined) {
114
130
  throw new Error(`Task has an unresolved Integration Attempt: ${task.id}/${unresolved.id}.`);
@@ -1,3 +1,5 @@
1
+ <p align="right"><strong>English</strong> | <a href="./agent-result-consumption.zh-CN.md">简体中文</a></p>
2
+
1
3
  # Agent result consumption
2
4
 
3
5
  Every explicitly dispatched AgentRun produces one durable original result.
@@ -0,0 +1,81 @@
1
+ <p align="right"><a href="./agent-result-consumption.md">English</a> | <strong>简体中文</strong></p>
2
+
3
+ # Agent 结果消费
4
+
5
+ 每一次显式派发的 AgentRun 都产生一个持久的原始结果。普通通知和原生对话不会
6
+ 隐式创建 Run。所有权链上的下一个 Agent 读取这个精确结果并判断它意味着什么。
7
+
8
+ ## 唯一的原始结果
9
+
10
+ `AgentRunResult.output` 是 Agent 撰写的报告。Core 保留其字节,不解析、不分类、
11
+ 也不校验语义内容。Markdown、JSON 和普通散文都是合法的;缺少标题或结论不够有力
12
+ 都是质量证据,而不是运行时失败。
13
+
14
+ Core 另外记录 Provider 身份/状态、完成时间、诊断、失败原因和系统拥有的工作区
15
+ 证据。非空输出必须符合当前传输上限且不含 NUL。缺失或不可传输的文本会让 Run
16
+ 失败,而不编造散文。当之后某个 Core 拥有的边界失败时,一份已到达的报告仍可以
17
+ 留在一个 failed 的 Run 上。
18
+
19
+ 终态结果会附带保存一条引用 Message。`task message show <task/message>` 和
20
+ Context inspect 展开同一个 `resultRef`。ReviewRound 不持有第二份报告。文件和
21
+ 持久业务产物归 Artifact 或受管 Git 结果。
22
+
23
+ ## 执行不等于验收
24
+
25
+ Run 生命周期是 `active / completed / failed`。Provider 结果、输入接受和资源静止
26
+ 是彼此独立的事实。Run completed 只表示它所需的 Core 执行边界成功,而不表示答案
27
+ 正确或 WorkItem 已验收。
28
+
29
+ 可写的 replicated Lane 需要其精确的、Core 拥有的工作区证据。分支错误、快照脏、
30
+ owner 不符或范围不符都会让该边界失败,但不会替换一份已到达的原始报告。
31
+
32
+ 只有 Leader 决定证据是否足以验收、继续工作、再审查或放弃。Core 从不从 Agent
33
+ 文本中推导 findings、投票或修复拓扑。
34
+
35
+ ## 直接执行与复制执行
36
+
37
+ 直接的 WorkItem 执行使用一个 main Run,没有 ExecutionGroup。直接 Review 同样
38
+ 使用一个 main Reviewer Run。
39
+
40
+ 复制执行在一个冻结 Assignment 上使用彼此不同的 Producer Lane。每个 Producer
41
+ 保留自己的原始结果和精确血缘。Leader 显式地把彼此不同的终态来源 Run 引用交给
42
+ 综合。来源必须属于确切的 Group/Lane 且有结果,可以是 completed 或 failed。不存在
43
+ 自动综合、投票、最少成功 Producer 数,也不要求在选择来源前等待所有 Lane。
44
+
45
+ 综合快照保留所选来源顺序,以及它们原始输出、诊断和来源的有界视图,不复制完整
46
+ transcript。只有 main 综合结果能提供 WorkItem Candidate 或权威的 replicated
47
+ Review 结果。Producer 从不直接进入 Integration 或验收。重试针对失败的那次精确
48
+ 执行,不会静默重跑已成功的 Producer。
49
+
50
+ ## Review
51
+
52
+ ReviewRound 拥有冻结的 Candidate 或 Task-head 身份、工作区来源、执行拓扑、精确
53
+ 的 main Reviewer Run、生命周期和 Core 诊断。它的 completed 状态是结构性执行证据,
54
+ 而不是从报告里提取出来的“通过”。
55
+
56
+ Candidate 审查遵循其捕获的审查规则。Task-final 审查可以被显式请求,或由不可变的
57
+ final-review 合同要求。一次被请求的审查是证据,而不是要求此后每次改动都审查的
58
+ 自动新策略。当前交付需要审查时,它必须覆盖确切的治理候选或 head 以及 completed
59
+ 的 main Run。
60
+
61
+ Reviewer 应检查完整的有界范围,并把重要 findings 一并报告。Leader 读取完整报告,
62
+ 把 findings 路由给原 owner,直接修复 Task-main 上的小问题,只为有独立价值的实质
63
+ 工作创建新的 WorkItem。
64
+
65
+ ## Leader 消费
66
+
67
+ 唤醒窗口指向带结果的事件,包括在窗口之前创建、但在窗口之内完成的 Run。读取精确
68
+ 来源:
69
+
70
+ ```sh
71
+ yui task wake show <task> <wake>
72
+ yui task run show <task/run>
73
+ yui task message show <task/message>
74
+ ```
75
+
76
+ 一个可选的报告结构是:结果、改动/发现、验证、不确定性和下一步动作。它是沟通
77
+ 建议,不是机器协议。
78
+
79
+ 验收与 Task 完成仍是显式操作,并带有当前 Git、审查、范围和资源检查。参见
80
+ [执行与会话](managed-turn-and-session-runtime.zh-CN.md)和
81
+ [Task 依赖](task-dag-semantics.zh-CN.md)。
@@ -1,3 +1,5 @@
1
+ <p align="right"><strong>English</strong> | <a href="./agent-runtime-drivers.zh-CN.md">简体中文</a></p>
2
+
1
3
  # Agent runtime Drivers
2
4
 
3
5
  AgentEndpoint supplies the common execution boundary. Drivers translate native
@@ -0,0 +1,77 @@
1
+ <p align="right"><a href="./agent-runtime-drivers.md">English</a> | <strong>简体中文</strong></p>
2
+
3
+ # Agent 运行时 Driver
4
+
5
+ AgentEndpoint 提供通用执行边界。Driver 把原生事件、错误和受支持的观察来源翻译
6
+ 成 `RuntimeObservation`;Controller、Store、CLI 和 Web 消费这份共享合同。
7
+
8
+ ## 职责
9
+
10
+ 连接实现拥有启动、协议、prompt 投递、resume 和中断。Driver 拥有原生身份提取、
11
+ 观察能力、事件/错误映射和用量归一化。Core 拥有权威、精确的请求关联、持久归并
12
+ 和投影;Agent 选择恢复方式并判断语义进展。
13
+
14
+ 内置 Driver 身份为 `openai/codex`、`anthropic/claude-code` 和
15
+ `acp/agent-client-protocol`。ACP 是协议 Driver,不是产品标签。接入一个 ACP peer
16
+ 不需要另一套业务状态模型。
17
+
18
+ 能力必须如实声明。未知的 resume、取消、活动或用量行为不能从产品名推断。
19
+
20
+ ## 观察路径
21
+
22
+ 原生结构化事实通过精确的 Session/请求围栏,进入运行时收件箱,并归并为持久观察
23
+ 和原始结果。一个单独采样的用量来源可以馈入同一份规范合同。Driver 不能选择另一个
24
+ actor、指派后继 Run,也不能绕过围栏。
25
+
26
+ Yui Run 身份与 Provider 原生 Turn 身份不可互换。一个显式派发的 Run 保留其已接受
27
+ 的原生关联;直接的原生对话不是另一个隐式 Run。重放按精确事实身份去重,迟到事件
28
+ 不能终结一个后继。
29
+
30
+ 受管 Codex 使用 App Server 事件。Claude 映射其结构化流以及受支持的 Hook/来源
31
+ 负载。ACP 映射协议 Session 更新和 prompt 响应。终端文本、信任对话框和 prompt
32
+ 字形都不是生命周期事实。
33
+
34
+ ## 状态与错误证据
35
+
36
+ 持久 Run 生命周期是 `active / completed / failed`。输入处置、原生等待/活动、Goal、
37
+ Session 生命周期和进程存在回答的是不同问题。UI 投影不得把一个排队中的请求当作
38
+ Agent 忙碌的证明,也不得把一个存活进程当作接受。
39
+
40
+ 标准 Agent 错误保留 source、phase、category、code、输入处置、Session 处置以及
41
+ 序列化的原生错误。类别包括 availability、rate-limit、transport、access、
42
+ invalid-request、context、session、runtime、conflict、cancelled 和 unknown。
43
+ 映射报告证据,而不是重试策略。无法识别的错误保持 unknown。
44
+
45
+ 运行时活动与工作流进展使用彼此独立的证据。一个工具边界可能显示原生活动;一个
46
+ 持久且被接受的结果才显示语义进展。token、CPU、RSS 和面板存在都不能替代接受或
47
+ Task 完成。
48
+
49
+ ## 用量
50
+
51
+ 用量是只读的,范围限定在确切的原生 Session。输入/输出总量与缓存/推理分解、请求
52
+ 上下文和剩余容量区分开来。稳定的 activity ID 对请求快照去重;累计增量只在具备
53
+ 有效有序的同 Session 证据时使用。
54
+
55
+ 缺失、部分、混合或已回滚的观察保持“未观察”,而不是猜测。增量观察者报告健康度
56
+ 和覆盖度;采样不阻塞生命周期事件。度量绝不触发模型选择、唤醒、重试、资源释放
57
+ 或接受。
58
+
59
+ ## 原生子代
60
+
61
+ 原生 subagent 是父对话内部的协作,不是 Yui Role、Lane 或独立的受管工作区 owner。
62
+ 当 Provider 暴露血缘和结果引用时,continuation 观察可以记录它们。
63
+
64
+ 尽力而为的子代结果通过父代返回。只有持久化的内容回执才支持 `durable-result`;
65
+ 存活的子代或声称的成功都不行。被报告的结果仍是不受信任数据。一段丢失的尽力而为
66
+ 对话可能需要重做工作。当需要独立的持久性和验收时,选择受管的 WorkItem 执行;
67
+ 复制是另一个单独的选择。
68
+
69
+ ## 接入与验证
70
+
71
+ 一个新的连接实现必须提供如实的控制/观察能力,并把它们与精确的身份、错误和终态
72
+ 映射配对。Provider 专有的协议细节留在边缘,不进入 Task 规划、Store 语义或 Web
73
+ 业务规则。
74
+
75
+ 针对变更的一次性证据应覆盖被改动的关联、权限、取消或观察边界。永久测试保持在
76
+ [验证策略](testing/verification-levels.zh-CN.md)中的主要路径。真实
77
+ Provider/模型验证需要显式授权,并且必须把原生进程证据与夹具输出区分开。
@@ -1,38 +1,50 @@
1
- # 当前架构与文档导航
1
+ <p align="right"><strong>English</strong> | <a href="./README.zh-CN.md">简体中文</a></p>
2
2
 
3
- 以下文档描述当前源码合同。能力边界不等于所有真实 Provider 场景已经验证。
3
+ # Architecture and documentation map
4
4
 
5
- ## 阅读入口
5
+ These documents describe the current source contracts. A capability boundary is
6
+ not a claim that every real Provider scenario has been validated.
6
7
 
7
- - [README](../../README.md):安装、配置和日常使用。
8
- - [中文 README](../../i18n/README.zh-CN.md):同一产品入口的中文说明。
9
- - [总体架构](../../ARCHITECTURE.md):职责、权威和端到端流程。
10
- - [能力、资源与 Surface](capabilities-and-resources.md):扩展入口、实例所有权和资源效果。
8
+ ## Start here
11
9
 
12
- ## 领域合同
10
+ - [README](../../README.md): install, configure and everyday use.
11
+ - [Chinese README](../../i18n/README.zh-CN.md): the same product entry in
12
+ Simplified Chinese.
13
+ - [Architecture overview](../../ARCHITECTURE.md): responsibilities, authority and
14
+ the end-to-end flow.
15
+ - [Capabilities, resources and Surfaces](capabilities-and-resources.md): the
16
+ extension ingress, instance ownership and resource effects.
13
17
 
14
- | 问题 | 当前文档 |
18
+ ## Domain contracts
19
+
20
+ | Question | Current document |
15
21
  | --- | --- |
16
- | Session、AgentRun、消息和激活如何配合? | [执行与会话](../managed-turn-and-session-runtime.md) |
17
- | 谁消费结果、综合与审查? | [结果消费](../agent-result-consumption.md) |
18
- | WorkItem 依赖何时满足? | [Task 依赖](../task-dag-semantics.md) |
19
- | Task 内记录如何引用? | [局部身份](../task-local-identity.md) |
20
- | Role、Profile 与运行配置如何生效? | [角色与配置](../roles-and-configuration.md) |
21
- | 怎样交付、集成和归档? | [交付生命周期](../task-delivery.md) |
22
- | Provider、ACP 与配置事实如何接入? | [Provider Runtime](../provider-runtime.md) |
23
- | 运行观察和错误由谁解释? | [Agent Drivers](../agent-runtime-drivers.md) |
24
- | 如何创建、验证与采用插件? | [插件 SDK](../plugin-sdk.md) |
25
- | 数据、升级与并发的边界是什么? | [SQLite Store](../sqlite-control-plane-design.md) |
26
- | 如何执行获授权的发布操作? | [发布流程](../release-workflow.md) |
27
- | 如何查看当前运行证据? | [可观察性](../observability/README.md) |
28
- | 哪些验证应长期保留? | [验证策略](../testing/verification-levels.md) |
29
-
30
- ## 维护约定
31
-
32
- 行为变更同步修改所属合同和必要的入口说明。具体 CLI 参数以
33
- `src/cli/commandCatalog.ts` 和命令处理器为准;公开领域类型以运行源码为准,
34
- 不另外维护一套目标模型或生成的离线副本。
35
-
36
- Project Skill 管理 Yui 的开发与验证规则;通用 Role Skills 管理 Agent 使用
37
- Yui 的职责。仓库文档不替代 `YUI_HOME` 中维护的 Project Knowledge,也不授予
38
- 共享环境、真实模型或外部系统的执行权限。
22
+ | How do Session, AgentRun, messages and activation fit together? | [Session and AgentRun runtime](../managed-turn-and-session-runtime.md) |
23
+ | Who consumes results, synthesis and review? | [Result consumption](../agent-result-consumption.md) |
24
+ | When is a WorkItem dependency satisfied? | [Task dependencies](../task-dag-semantics.md) |
25
+ | How are records referenced inside a Task? | [Task-local identity](../task-local-identity.md) |
26
+ | How do Roles, Profiles and run configuration take effect? | [Roles and configuration](../roles-and-configuration.md) |
27
+ | How do delivery, integration and archive work? | [Task delivery](../task-delivery.md) |
28
+ | How do Provider, ACP and configuration facts connect? | [Provider runtime](../provider-runtime.md) |
29
+ | Who interprets runtime observations and errors? | [Agent Drivers](../agent-runtime-drivers.md) |
30
+ | How are plugins created, validated and adopted? | [Plugin SDK](../plugin-sdk.md) |
31
+ | What are the data, upgrade and concurrency boundaries? | [SQLite control plane](../sqlite-control-plane-design.md) |
32
+ | How do authorized release operations run? | [Release workflow](../release-workflow.md) |
33
+ | How do I read current runtime evidence? | [Observability](../observability/README.md) |
34
+ | Which checks should be kept permanently? | [Verification policy](../testing/verification-levels.md) |
35
+
36
+ ## Maintenance conventions
37
+
38
+ When behavior changes, update the owning contract and any entry-point text in the
39
+ same change. The exact CLI flags are defined by `src/cli/commandCatalog.ts` and
40
+ the command handlers; public domain types follow the running source. We do not
41
+ maintain a separate target model or a generated offline copy.
42
+
43
+ Each document is bilingual: `X.md` is the English version and `X.zh-CN.md` is the
44
+ Simplified Chinese one. When behavior changes, update both language versions
45
+ together so they stay in sync.
46
+
47
+ The Project Skill owns Yui's development and validation rules; the generic Role
48
+ Skills own how an Agent uses Yui. Repository documents do not replace the Project
49
+ Knowledge maintained under `YUI_HOME`, and they do not grant execution access to
50
+ shared environments, real models or external systems.