@zq-silk/yui 0.15.12 → 0.16.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 (236) hide show
  1. package/ARCHITECTURE.md +3 -2
  2. package/ARCHITECTURE.zh-CN.md +3 -2
  3. package/dist/artifacts/artifactCommitLock.js +16 -55
  4. package/dist/artifacts/taskArtifactRepository.js +3 -3
  5. package/dist/cli/agentConfigurationPicker.js +1 -1
  6. package/dist/cli/commandCatalog.js +57 -41
  7. package/dist/cli/completion.js +18 -18
  8. package/dist/cli/completionWizard.js +1 -1
  9. package/dist/cli/dynamicCompletion.js +1 -1
  10. package/dist/cli/interactionPolicy.js +7 -3
  11. package/dist/cli/invocationAuthority.js +64 -0
  12. package/dist/cli/managedDiagnostics.js +1 -1
  13. package/dist/cli/parseRepeatable.js +35 -0
  14. package/dist/cli/updatePorts.js +26 -5
  15. package/dist/cli.js +1280 -1243
  16. package/dist/commands/agentCommands.js +4 -4
  17. package/dist/commands/capabilityCommands.js +1 -1
  18. package/dist/commands/configCommands.js +6 -57
  19. package/dist/commands/configOverview.js +2 -2
  20. package/dist/commands/durableJobCommands.js +12 -6
  21. package/dist/commands/globalRoleCommands.js +53 -29
  22. package/dist/commands/grantCommands.js +0 -3
  23. package/dist/commands/jobCommands.js +1 -4
  24. package/dist/commands/operatorCommands.js +3 -3
  25. package/dist/commands/projectCommands.js +1 -10
  26. package/dist/commands/resourcesCommands.js +10 -40
  27. package/dist/commands/roleConfiguration.js +2 -6
  28. package/dist/commands/taskActivationCommands.js +4 -8
  29. package/dist/commands/taskAgentCapabilities.js +9 -0
  30. package/dist/commands/taskCommandSupport.js +155 -0
  31. package/dist/commands/taskCommandTypes.js +1 -0
  32. package/dist/commands/taskCommands.js +271 -710
  33. package/dist/commands/taskCompletionGate.js +1 -1
  34. package/dist/commands/taskExecutionCommands.js +1 -1
  35. package/dist/commands/taskFactCommands.js +325 -0
  36. package/dist/commands/taskInputCommands.js +12 -42
  37. package/dist/commands/taskIntegrationCommands.js +17 -57
  38. package/dist/commands/taskNextActionCommand.js +7 -6
  39. package/dist/commands/taskPublicationAdoptCommand.js +3 -3
  40. package/dist/commands/taskPublicationCommands.js +1 -1
  41. package/dist/commands/taskPublicationVerifyCommand.js +1 -1
  42. package/dist/commands/taskRemoteDeliveryCommand.js +2 -94
  43. package/dist/commands/taskUpstreamCommands.js +19 -8
  44. package/dist/commands/workflowCommands.js +6 -1
  45. package/dist/completion/completionInstaller.js +26 -26
  46. package/dist/completion/completionState.js +26 -26
  47. package/dist/completion/fileCompletionManager.js +6 -11
  48. package/dist/config/configCatalog.js +0 -2
  49. package/dist/config/timeZone.js +14 -0
  50. package/dist/config/yuiConfig.js +0 -29
  51. package/dist/context/runContextPack.js +2 -3
  52. package/dist/context/taskCatalog.js +3 -5
  53. package/dist/context/taskContext.js +139 -151
  54. package/dist/context/wakeRunReferences.js +11 -0
  55. package/dist/controller/agentCapabilities.js +58 -0
  56. package/dist/controller/agentRuntimeObserver.js +6 -11
  57. package/dist/controller/clientRuntime.js +22 -14
  58. package/dist/controller/controller.js +9 -22
  59. package/dist/controller/fileSchedulerStoreAdapter.js +117 -212
  60. package/dist/controller/jobClient.js +4 -4
  61. package/dist/controller/jobControl.js +60 -25
  62. package/dist/controller/jobSupervisor.js +4 -4
  63. package/dist/controller/providerRetryDelivery.js +7 -7
  64. package/dist/controller/runtime.js +51 -44
  65. package/dist/controller/runtimeEventProcessor.js +0 -5
  66. package/dist/controller/sessionOwnerReconciliation.js +5 -0
  67. package/dist/controller/taskAgentError.js +35 -0
  68. package/dist/coordination/workMailboxQueue.js +2 -24
  69. package/dist/core/controllerClient.js +2 -2
  70. package/dist/core/fileLockOwner.js +74 -0
  71. package/dist/decision/decision.js +17 -0
  72. package/dist/doctor/doctor.js +18 -9
  73. package/dist/event/taskEvent.js +12 -0
  74. package/dist/execution/workItemExecutionProjection.js +2 -2
  75. package/dist/executor/agentCapabilityConfig.js +43 -0
  76. package/dist/executor/agentConfigurationCatalog.js +49 -11
  77. package/dist/executor/agentExecutor.js +5 -8
  78. package/dist/executor/fileRoleLaunchPlanner.js +11 -53
  79. package/dist/executor/taskAgentCapabilities.js +64 -0
  80. package/dist/integration/deliveryObligation.js +1 -69
  81. package/dist/integration/gitIntegrationService.js +60 -84
  82. package/dist/integration/integrationAttempt.js +3 -14
  83. package/dist/integration/integrationSourceApplication.js +4 -12
  84. package/dist/integration/manifestTags.js +2 -2
  85. package/dist/job/durableJob.js +4 -25
  86. package/dist/job/jobAssignmentScope.js +22 -0
  87. package/dist/job/jobOperation.js +16 -0
  88. package/dist/job/jobRunner.js +2 -1
  89. package/dist/job/stepDirectory.js +15 -0
  90. package/dist/kernel/builtinCapabilities.js +15 -11
  91. package/dist/kernel/kernelPorts.js +1 -17
  92. package/dist/lifecycle/exactRunTerminalization.js +1 -9
  93. package/dist/message/inputControlResolution.js +3 -4
  94. package/dist/message/message.js +17 -20
  95. package/dist/message/messageContinuation.js +1 -1
  96. package/dist/milestone/milestone.js +11 -0
  97. package/dist/observability/orchestrationMetrics.js +24 -1
  98. package/dist/observability/runtimeIdentity.js +1 -17
  99. package/dist/output/agentConfigurationPresentation.js +2 -0
  100. package/dist/output/timePresentation.js +1 -14
  101. package/dist/plugins/pluginService.js +12 -4
  102. package/dist/release/releaseWorkflowPorts.js +45 -96
  103. package/dist/release/runtimeRelease.js +9 -15
  104. package/dist/repository/projectMaintenanceLock.js +23 -64
  105. package/dist/repository/taskBaseFreshness.js +1 -1
  106. package/dist/repository/taskWorkspaceCoordinator.js +13 -22
  107. package/dist/repository/taskWorkspacePreparer.js +29 -25
  108. package/dist/repository/workspaceCleanupInspection.js +5 -5
  109. package/dist/resources/autoResourceGc.js +4 -77
  110. package/dist/resources/liveReferences.js +7 -54
  111. package/dist/resources/resourceDiscovery.js +5 -118
  112. package/dist/resources/resourceGc.js +233 -181
  113. package/dist/resources/resourceRegistrar.js +5 -4
  114. package/dist/resources/resourceRegistry.js +3 -37
  115. package/dist/resources/resourceRegistryStore.js +13 -36
  116. package/dist/resources/sqliteResourceRegistry.js +43 -26
  117. package/dist/review/deltaRecheck.js +1 -1
  118. package/dist/review/reviewAcceptance.js +1 -1
  119. package/dist/review/reviewDecision.js +1 -1
  120. package/dist/review/reviewRound.js +8 -7
  121. package/dist/review/taskFinalReviewContractResolution.js +1 -1
  122. package/dist/runtime/acpProtocol.js +4 -51
  123. package/dist/runtime/acpSession.js +10 -75
  124. package/dist/runtime/acpSessionConfiguration.js +1 -8
  125. package/dist/runtime/agentEndpoint.js +3 -2
  126. package/dist/runtime/agentFailureContext.js +43 -0
  127. package/dist/runtime/agentHost.js +15 -15
  128. package/dist/runtime/codexInteractiveHost.js +2 -2
  129. package/dist/runtime/index.js +0 -1
  130. package/dist/runtime/managedCaller.js +21 -1
  131. package/dist/runtime/providerErrors.js +38 -0
  132. package/dist/runtime/runtimeObservation.js +0 -5
  133. package/dist/runtime/runtimeSessionCandidate.js +2 -3
  134. package/dist/runtime/sessionOwnerIdentity.js +1 -1
  135. package/dist/runtime/structuredProviderHost.js +4 -41
  136. package/dist/runtime/tmuxAdapters.js +6 -8
  137. package/dist/scheduler/activeRoleRunDelivery.js +7 -7
  138. package/dist/scheduler/leaderWakeupProcessor.js +19 -6
  139. package/dist/scheduler/operatorEvent.js +12 -20
  140. package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
  141. package/dist/scheduler/ports.js +5 -8
  142. package/dist/scheduler/roleRunLiveness.js +4 -4
  143. package/dist/scheduler/roleRunStall.js +16 -23
  144. package/dist/scheduler/taskExecutionProjection.js +25 -33
  145. package/dist/scheduler/taskObservabilityProjection.js +0 -1
  146. package/dist/scheduler/taskWake.js +5 -10
  147. package/dist/scheduler/wakeReason.js +2 -0
  148. package/dist/scheduler/wakeupQueue.js +2 -4
  149. package/dist/setup/setupCommand.js +1 -1
  150. package/dist/storage/contextRecords.js +106 -0
  151. package/dist/storage/currentTaskStore.js +1 -1
  152. package/dist/storage/homeLayout.js +13 -17
  153. package/dist/storage/migrations/agentFailureContext.js +22 -0
  154. package/dist/storage/migrations/currentInputContract.js +86 -0
  155. package/dist/storage/migrations/currentRuntimeContract.js +228 -0
  156. package/dist/storage/migrations/historicalVerificationPlan.js +35 -0
  157. package/dist/storage/migrations/integrationContinuation.js +5 -4
  158. package/dist/storage/migrations/narrowAgentFailureContext.js +65 -0
  159. package/dist/storage/migrations/notificationOnlyWakes.js +74 -0
  160. package/dist/storage/migrations/verificationPlanV1.js +162 -0
  161. package/dist/storage/migrations/verificationPolicy.js +74 -0
  162. package/dist/storage/migrations/workItemHistory.js +46 -0
  163. package/dist/storage/persistenceWorker.js +12 -4
  164. package/dist/storage/recordValidation.js +87 -0
  165. package/dist/storage/sqliteSchema.js +142 -1
  166. package/dist/storage/sqliteStore.js +156 -162
  167. package/dist/storage/storageSchema.js +2 -14
  168. package/dist/storage/storageVersions.js +1 -1
  169. package/dist/storage/storeRpc.js +15 -7
  170. package/dist/storage/taskStore.js +3 -7
  171. package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
  172. package/dist/task/completionReadiness.js +5 -24
  173. package/dist/task/draftPlan.js +0 -53
  174. package/dist/task/nextAction.js +7 -13
  175. package/dist/task/remoteDeliveryService.js +89 -0
  176. package/dist/task/taskActivation.js +2 -25
  177. package/dist/task/taskActivationService.js +0 -2
  178. package/dist/task/taskRecordReference.js +0 -1
  179. package/dist/task/taskSubmission.js +3 -14
  180. package/dist/telemetry/sqliteTelemetryBatch.js +29 -0
  181. package/dist/telemetry/sqliteTelemetryStore.js +49 -49
  182. package/dist/telemetry/telemetryWiring.js +4 -3
  183. package/dist/tmux/tmuxManager.js +29 -20
  184. package/dist/verification/gateArtifact.js +15 -24
  185. package/dist/verification/gateArtifactStore.js +14 -7
  186. package/dist/verification/verificationGateService.js +29 -88
  187. package/dist/verification/verificationPlan.js +27 -72
  188. package/dist/web/assets/client/app.js +1 -1
  189. package/dist/web/assets/client/taskSurface.js +4 -4
  190. package/dist/web/webServer.js +4 -6
  191. package/dist/web/webSnapshot.js +3 -83
  192. package/dist/web/webTaskSurface.js +17 -46
  193. package/dist/workItem/workItem.js +1 -12
  194. package/docs/architecture/capabilities-and-resources.md +14 -2
  195. package/docs/architecture/capabilities-and-resources.zh-CN.md +11 -1
  196. package/docs/managed-turn-and-session-runtime.md +83 -10
  197. package/docs/managed-turn-and-session-runtime.zh-CN.md +65 -11
  198. package/docs/plugin-sdk.md +4 -2
  199. package/docs/provider-runtime.md +3 -1
  200. package/docs/provider-runtime.zh-CN.md +4 -2
  201. package/docs/release-workflow.md +190 -7
  202. package/docs/release-workflow.zh-CN.md +139 -5
  203. package/docs/roles-and-configuration.md +29 -0
  204. package/docs/roles-and-configuration.zh-CN.md +21 -0
  205. package/docs/sqlite-control-plane-design.md +16 -3
  206. package/docs/sqlite-control-plane-design.zh-CN.md +13 -2
  207. package/docs/task-dag-semantics.md +3 -3
  208. package/docs/task-dag-semantics.zh-CN.md +2 -2
  209. package/docs/task-delivery.md +91 -5
  210. package/docs/task-delivery.zh-CN.md +72 -4
  211. package/docs/task-discovery.md +10 -11
  212. package/docs/task-discovery.zh-CN.md +8 -9
  213. package/docs/testing/verification-levels.md +145 -9
  214. package/docs/testing/verification-levels.zh-CN.md +103 -8
  215. package/package.json +1 -1
  216. package/skills/yui-leader/SKILL.md +5 -0
  217. package/skills/yui-leader/references/integration.md +9 -5
  218. package/skills/yui-leader/references/planning.md +9 -3
  219. package/skills/yui-operator/SKILL.md +15 -10
  220. package/skills/yui-runtime/SKILL.md +6 -6
  221. package/skills/yui-runtime/references/recovery.md +47 -0
  222. package/dist/agent/agentRegistry.js +0 -10
  223. package/dist/agentRun/runIdentity.js +0 -22
  224. package/dist/commands/deliveryGuardPreflight.js +0 -30
  225. package/dist/commands/taskIntegrationQueueCommands.js +0 -226
  226. package/dist/commands/taskOverviewCommand.js +0 -377
  227. package/dist/completion/completionWizard.js +0 -125
  228. package/dist/context/dispatchContext.js +0 -110
  229. package/dist/context/wakeNotification.js +0 -144
  230. package/dist/integration/integrationCheckEvidenceReuse.js +0 -53
  231. package/dist/integration/integrationQueueEntry.js +0 -191
  232. package/dist/integration/integrationQueueService.js +0 -810
  233. package/dist/release/fakeReleasePorts.js +0 -55
  234. package/dist/runtime/sessionOwnerRegistry.js +0 -137
  235. package/dist/task/deliveryGuard.js +0 -226
  236. /package/dist/{commands/taskActor.js → task/taskAuthority.js} +0 -0
@@ -1,125 +0,0 @@
1
- import { resolve } from "node:path";
2
- import { usageError } from "../errors/cliError.js";
3
- import { installCompletion, uninstallCompletion } from "./completionInstaller.js";
4
- import { COMPLETION_SHELLS, activationBlock, activationIsAutomatic, currentCompletionShell, inspectCompletionStates, renderCompletionStateTable, shellQuote, suggestedCompletionInstallation } from "./completionState.js";
5
- export async function runCompletionWizard(operation, store, env, identity, question, options = {}) {
6
- const states = inspectCompletionStates(store.getConfig(), env, identity);
7
- const current = currentCompletionShell(env);
8
- let shell;
9
- if (options.shell === undefined) {
10
- const defaultSelection = options.defaultSelection ?? "current-shell";
11
- const defaultHint = defaultSelection === "skip"
12
- ? " [skip]"
13
- : current === undefined ? "" : ` [${current}]`;
14
- const skipHint = defaultSelection === "skip" ? "" : " (or skip)";
15
- const answer = await question(`${renderCompletionStateTable(states, options.width)}\n`
16
- + `Choose shell by number or name${defaultHint}${skipHint}: `);
17
- const normalized = answer.trim().toLowerCase();
18
- if (normalized === "skip" || (normalized.length === 0 && defaultSelection === "skip")) {
19
- return `Completion ${operation} skipped.\n`;
20
- }
21
- shell = parseSelection(answer, current);
22
- }
23
- else {
24
- shell = options.shell;
25
- }
26
- const state = states.find((candidate) => candidate.shell === shell);
27
- if (state === undefined)
28
- throw usageError("Completion shell selection is invalid.");
29
- if (operation === "uninstall") {
30
- if (state.installation === undefined) {
31
- return `Completion ${shell} is not installed.\n`;
32
- }
33
- const confirm = normalize(await question(`Remove managed ${shell} completion?\n`
34
- + `Script: ${state.installation.scriptPath}\n`
35
- + `Activation: ${state.installation.activationPath}\n`
36
- + "Remove now? [y/N]: "));
37
- if (confirm !== "y" && confirm !== "yes") {
38
- return `Completion ${shell} uninstall skipped.\n`;
39
- }
40
- uninstallCompletion(store, shell, identity);
41
- return `Completion ${shell} uninstalled.\n`;
42
- }
43
- const suggested = state.installation
44
- ?? suggestedCompletionInstallation(shell, env, identity);
45
- const automatic = activationIsAutomatic(shell, suggested, env, identity);
46
- const decision = normalize(await question(`Selected: ${shell} (${state.action})\n`
47
- + `Script: ${suggested.scriptPath}\n`
48
- + `Activation: ${suggested.activationPath}`
49
- + `${automatic ? " (automatic)" : " (managed startup block)"}\n`
50
- + "Install using these paths? [Y/n/customize]: "));
51
- if (decision === "n" || decision === "no") {
52
- return `Completion ${shell} installation skipped.\n`;
53
- }
54
- let installation = suggested;
55
- let customized = false;
56
- if (decision === "customize") {
57
- const scriptPath = resolve((await question(`Completion script path [${suggested.scriptPath}]: `)).trim()
58
- || suggested.scriptPath);
59
- const activationPath = resolve((await question(`Activation file path [${suggested.activationPath}]: `)).trim()
60
- || suggested.activationPath);
61
- installation = { scriptPath, activationPath };
62
- customized = true;
63
- }
64
- else if (decision !== "" && decision !== "y" && decision !== "yes") {
65
- throw usageError("Choose Y, n, or customize.");
66
- }
67
- const needsActivation = !activationIsAutomatic(shell, installation, env, identity);
68
- let activate = false;
69
- let activationReady = !needsActivation;
70
- if (needsActivation) {
71
- const activationAnswer = normalize(await question(`${activationBlock(shell, installation, identity)}\n`
72
- + `Update ${installation.activationPath} with the managed Yui block? [Y/n]: `));
73
- if (activationAnswer !== ""
74
- && activationAnswer !== "y"
75
- && activationAnswer !== "yes"
76
- && activationAnswer !== "n"
77
- && activationAnswer !== "no") {
78
- throw usageError("Choose Y or n.");
79
- }
80
- activate = activationAnswer === ""
81
- || activationAnswer === "y"
82
- || activationAnswer === "yes";
83
- activationReady = activate || (!customized && state.status === "Installed");
84
- }
85
- installCompletion(store, shell, installation, env, identity, activate);
86
- if (!activationReady) {
87
- return `Completion ${shell} script installed; activation still required.\n`;
88
- }
89
- const result = `Completion ${shell} ${pastTense(state.action)}.\n`;
90
- return needsActivation
91
- ? `${result}${activationGuidance(shell, installation, env, identity)}`
92
- : result;
93
- }
94
- function activationGuidance(shell, installation, env, identity) {
95
- const unchanged = "The current shell is unchanged.\n";
96
- const defaultActivationPath = suggestedCompletionInstallation(shell, env, identity).activationPath;
97
- if (installation.activationPath !== defaultActivationPath) {
98
- return `${unchanged}From a ${shell} session, load the custom activation file: source ${shellQuote(installation.activationPath)}\n`;
99
- }
100
- return currentCompletionShell(env) === shell
101
- ? `${unchanged}Restart the current shell to activate completion: exec ${shell}\n`
102
- : `${unchanged}Switch this terminal to ${shell} to activate completion: exec ${shell}\n`;
103
- }
104
- function parseSelection(value, current) {
105
- const normalized = value.trim().toLowerCase();
106
- if (normalized.length === 0 && current !== undefined)
107
- return current;
108
- if (/^[1-3]$/.test(normalized)) {
109
- return COMPLETION_SHELLS[Number.parseInt(normalized, 10) - 1];
110
- }
111
- if (COMPLETION_SHELLS.includes(normalized)) {
112
- return normalized;
113
- }
114
- throw usageError("Choose Bash, Zsh, or Fish by number or name.");
115
- }
116
- function pastTense(action) {
117
- switch (action) {
118
- case "Install": return "installed";
119
- case "Refresh": return "refreshed";
120
- case "Repair": return "repaired";
121
- }
122
- }
123
- function normalize(value) {
124
- return value?.trim().toLowerCase() ?? "";
125
- }
@@ -1,110 +0,0 @@
1
- export function buildRoleContext(context) {
2
- return context.role.name === "leader"
3
- ? buildLeaderContext(context)
4
- : buildWorkerContext(context);
5
- }
6
- export function buildLeaderContext(context) {
7
- return renderDispatchContext("leader", context);
8
- }
9
- export function buildWorkerContext(context) {
10
- return renderDispatchContext("worker", context);
11
- }
12
- const WORKER_RUN_COMPLETION_MARKER = "Yui Role AgentRun result requirement:";
13
- const WORKER_RUN_COMPLETION_REQUIREMENT = [
14
- WORKER_RUN_COMPLETION_MARKER,
15
- "End the Provider turn with a truthful final report. Yui records that native turn result "
16
- + "for the current AgentRun automatically; no completion command is required.",
17
- "If you cannot finally determine success, failure, completeness, or the correct "
18
- + "disposition, do not guess, silently stop, or hide uncertainty behind a success "
19
- + "summary. Label the final report uncertain, incomplete, "
20
- + "blocked, or requiring Leader judgment.",
21
- "Report the most complete truthful evidence available and, when applicable: exact AgentRun, "
22
- + "WorkItem, and native Session identity; actions actually performed; changed paths "
23
- + "and commit/worktree state; checks actually run and their outcomes; provider, runtime, "
24
- + "or permission errors; the last confirmed lifecycle boundary; work not performed; "
25
- + "unresolved assumptions or decisions; residual risks; confidence; and bounded next options.",
26
- "The Provider turn ending closes only this AgentRun. It does not imply Leader acceptance, "
27
- + "WorkItem completion, ChangeSet capture, Integration, or Task completion. Review AgentRuns "
28
- + "report findings, verification gaps, and limits; the Leader decides disposition."
29
- ].join("\n");
30
- export function ensureWorkerRunCompletionRequirement(input) {
31
- return input.endsWith(`\n\n${WORKER_RUN_COMPLETION_REQUIREMENT}`)
32
- ? input
33
- : `${input}\n\n${WORKER_RUN_COMPLETION_REQUIREMENT}`;
34
- }
35
- function renderDispatchContext(kind, context) {
36
- const profile = context.role;
37
- const binding = profile.agentBindings[profile.activeAgentId];
38
- const profileLines = [
39
- `Task: ${context.taskId}`,
40
- `Role: ${profile.name}`,
41
- `Active Agent: ${profile.activeAgentId}`,
42
- `Runtime: ${binding.adapterId}; model: ${binding.config.model ?? "CLI default"}; effort: ${binding.config.effort ?? "CLI default"}; permission: ${binding.config.permission.strategy}`,
43
- profile.description === undefined ? null : `Description: ${profile.description}`,
44
- ...(profile.responsibilities ?? []).map((item) => `Responsibility: ${item}`),
45
- ...(profile.constraints ?? []).map((item) => `Constraint: ${item}`),
46
- profile.expectedOutput === undefined ? null : `Expected output: ${profile.expectedOutput}`
47
- ].filter((line) => line !== null);
48
- const workLines = kind === "worker"
49
- ? renderWorkerScope(context)
50
- : [];
51
- const rendered = [
52
- `Follow the injected yui-${kind} Skill for this Yui dispatch.`,
53
- renderContextLayers(context, kind),
54
- profileLines.join("\n"),
55
- workLines.length === 0 ? null : workLines.join("\n"),
56
- "Yui dispatch:",
57
- requireText(context.input, "dispatch input")
58
- ].filter((section) => section !== null && section.length > 0)
59
- .join("\n\n");
60
- return kind === "worker"
61
- ? ensureWorkerRunCompletionRequirement(rendered)
62
- : rendered;
63
- }
64
- function renderContextLayers(context, kind) {
65
- const projects = context.projectPolicy ?? [];
66
- const policyLines = projects.length === 0
67
- ? ["- none (this Task is not Project-backed)"]
68
- : projects.map(({ projectId, directory }) => {
69
- const label = directory === undefined ? projectId : `${directory} (${projectId})`;
70
- return `- ${label}: \`yui project show ${projectId}\`; then \`yui project knowledge list ${projectId}\` and show the relevant entries`;
71
- });
72
- return [
73
- "Context layers:",
74
- "- Yui Core: durable identity, lifecycle, access, workspace, and exact handoff safety.",
75
- `- Generic ${kind} Skill: reusable role behavior and evidence discipline.`,
76
- "- Project Policy: project-owned build, test, migration, release, and review rules; it is not a Yui Core default.",
77
- ...["Project Policy references:", ...policyLines],
78
- "- Task Contract: the current Task brief, WorkItem objective, acceptance criteria, and dispatch input.",
79
- "Do not import rules from another Project or Task, and do not infer Project Policy from repository files alone."
80
- ].join("\n");
81
- }
82
- function renderWorkerScope(context) {
83
- if (context.workItem === undefined || context.workspace === undefined)
84
- return [];
85
- const writable = context.workspace.entries.filter(({ access }) => access === "write");
86
- const contextOnly = context.workspace.entries.filter(({ access }) => access === "read");
87
- const projectLines = (label, entries) => [
88
- `${label}:`,
89
- ...(entries.length === 0
90
- ? ["- none"]
91
- : entries.map(({ directory, projectId }) => `- ${directory} (${projectId})`))
92
- ];
93
- return [
94
- `WorkItem: ${context.workItem.id}`,
95
- `Workspace root: ${context.workspace.root}`,
96
- ...projectLines("Writable Projects", writable),
97
- ...projectLines("Context-only Projects", contextOnly),
98
- `Read the full Task state with \`yui task context ${context.taskId}\`.`,
99
- `Read the current WorkItem scope with \`yui task work list ${context.taskId}\`.`,
100
- "Modify only Writable Projects. If another Project must change, stop and ask "
101
- + "the Task Leader to expand this WorkItem scope before continuing."
102
- ];
103
- }
104
- function requireText(value, label) {
105
- const normalized = value.trim();
106
- if (normalized.length === 0 || normalized.includes("\0")) {
107
- throw new Error(`${label} is required.`);
108
- }
109
- return normalized;
110
- }
@@ -1,144 +0,0 @@
1
- import { renderWakeReason } from "../scheduler/wakeReason.js";
2
- import { operationalTaskRecords } from "../task/taskRecordRetirement.js";
3
- /**
4
- * Issue 04 (context token budget) — long-term design:
5
- *
6
- * A Leader wake is a NOTIFICATION, not a context dump. The wake envelope
7
- * carries only what the Agent needs for immediate orientation: the wake id,
8
- * aggregated reason tags, delta window, and a bounded list of active frozen
9
- * Task Reviews. The Agent reads delta content on demand with
10
- * `yui task wake show <wake-id>` and the full projection with
11
- * `yui task context <task>`.
12
- *
13
- * The envelope is mode-agnostic: fresh Sessions and resumed Sessions
14
- * receive the same minimal text. The native Session is a disposable cache of
15
- * working context; Yui's durable Task records (including the wake ledger) are
16
- * the checkpoint.
17
- */
18
- /** Structural guardrail: the envelope stays far below a normal model context window. */
19
- export const WAKE_ENVELOPE_HARD_BYTES = 2_000;
20
- /** Maximum reason tags rendered before elision to a count. */
21
- const REASON_DISPLAY_LIMIT = 6;
22
- export function buildTaskWakeEnvelope(reader, request) {
23
- const task = reader.getTask(request.taskId);
24
- if (task === null)
25
- throw new Error(`Task not found: ${request.taskId}.`);
26
- if (request.reasons.length === 0) {
27
- throw new Error(`Wake envelope ${request.wakeId} must carry at least one reason.`);
28
- }
29
- const fromTime = Date.parse(request.fromCursor);
30
- const events = reader.listEvents(request.taskId);
31
- const deltaEvents = events.filter((record) => Date.parse(record.createdAt) > fromTime);
32
- const runs = operationalTaskRecords(reader.listRuns(request.taskId), events, "run");
33
- const referencedRunIds = referencedWakeRunIds(runs, events, deltaEvents);
34
- const changedRunIds = new Set([
35
- ...runs
36
- .filter((record) => Date.parse(record.createdAt) > fromTime)
37
- .map(({ id }) => id),
38
- ...referencedRunIds
39
- ]);
40
- const counts = {
41
- events: deltaEvents.length,
42
- messages: operationalTaskRecords(reader.listMessages(request.taskId), events, "message")
43
- .filter((record) => Date.parse(record.createdAt) > fromTime).length,
44
- runs: changedRunIds.size
45
- };
46
- const activeReviews = reader.listReviewRounds(request.taskId).filter((round) => ((round.scope ?? "work-item") === "task"
47
- && (round.status === "pending" || round.status === "running")));
48
- const renderReviewOrientation = (limit) => activeReviews.slice(0, limit).map((round) => {
49
- const projects = round.taskCandidate?.projects ?? [];
50
- const heads = projects.slice(0, 2).map(({ projectId, commit }) => (`${projectId}@${commit.slice(0, 12)}`)).join("+");
51
- return `${round.id}/${round.reviewerRoleName}`
52
- + `/${round.deltaRecheck === undefined ? "full" : "delta"}`
53
- + `[${round.status}]`
54
- + `@${heads || round.reviewBaseCommit.slice(0, 12)}`
55
- + `${projects.length > 2 ? `+${projects.length - 2}` : ""}`;
56
- }).join(", ");
57
- const render = (reasonLimit, resultRunLimit, reviewLimit) => {
58
- const reviewOrientation = renderReviewOrientation(reviewLimit);
59
- return [
60
- `Wake: ${request.wakeId} — delta since ${request.fromCursor}`,
61
- ` Reasons: ${renderReasons(request.reasons, reasonLimit)}`,
62
- ` Changed: ${counts.events} events, ${counts.messages} messages, ${counts.runs} AgentRuns`
63
- + ` → yui task wake show ${request.taskId} ${request.wakeId}`,
64
- ` Result AgentRuns: ${renderResultRuns(request.taskId, referencedRunIds, resultRunLimit)}`,
65
- ` Active Task Reviews: ${activeReviews.length === 0
66
- ? "none"
67
- : reviewLimit === 0
68
- ? `${activeReviews.length} → yui task context ${request.taskId}`
69
- : `${reviewOrientation}${activeReviews.length > reviewLimit
70
- ? `, … (+${activeReviews.length - reviewLimit})`
71
- : ""}`}`,
72
- `Full context: yui task context ${request.taskId}`
73
- ].join("\n");
74
- };
75
- let reasonLimit = Math.min(REASON_DISPLAY_LIMIT, request.reasons.length);
76
- let resultRunLimit = Math.min(4, referencedRunIds.length);
77
- let reviewLimit = Math.min(3, activeReviews.length);
78
- let body = render(reasonLimit, resultRunLimit, reviewLimit);
79
- while (byteLength(body) + 1 > WAKE_ENVELOPE_HARD_BYTES) {
80
- if (resultRunLimit > 0)
81
- resultRunLimit -= 1;
82
- else if (reasonLimit > 0)
83
- reasonLimit -= 1;
84
- else if (reviewLimit > 0)
85
- reviewLimit -= 1;
86
- else
87
- break;
88
- body = render(reasonLimit, resultRunLimit, reviewLimit);
89
- }
90
- if (byteLength(body) + 1 > WAKE_ENVELOPE_HARD_BYTES) {
91
- body = fitUtf8([
92
- `Wake: ${request.wakeId}`,
93
- `Inspect: yui task wake show ${request.taskId} ${request.wakeId}`,
94
- `Full context: yui task context ${request.taskId}`
95
- ].join("\n"), WAKE_ENVELOPE_HARD_BYTES - 1);
96
- }
97
- const totalBytes = byteLength(body) + 1;
98
- return Object.freeze({
99
- taskId: request.taskId,
100
- wakeId: request.wakeId,
101
- fromCursor: request.fromCursor,
102
- referencedRunIds: Object.freeze(referencedRunIds),
103
- totalBytes,
104
- text: `${body}\n`
105
- });
106
- }
107
- export function referencedWakeRunIds(runs, allEvents, terminalEvents) {
108
- const turnsById = new Map(operationalTaskRecords(runs, allEvents, "run").map((run) => [run.id, run]));
109
- return [...new Set(terminalEvents.flatMap((event) => {
110
- if (!["run.completed", "run.failed", "run.cancelled"].includes(event.type))
111
- return [];
112
- const runId = event.payload.runId;
113
- return runId !== undefined && turnsById.has(runId) ? [runId] : [];
114
- }))];
115
- }
116
- function renderReasons(reasons, limit) {
117
- if (limit === 0)
118
- return `${reasons.length} reason tags`;
119
- const selected = reasons.slice(0, limit);
120
- const elided = reasons.length - selected.length;
121
- const rendered = selected.map(renderWakeReason).join(", ");
122
- return elided === 0 ? rendered : `${rendered}, … (+${elided} more)`;
123
- }
124
- function renderResultRuns(taskId, runIds, limit) {
125
- if (runIds.length === 0)
126
- return "none";
127
- if (limit === 0)
128
- return `${runIds.length} → inspect the wake delta`;
129
- return `${runIds.slice(0, limit)
130
- .map((runId) => `${runId} → yui task run show ${taskId}/${runId}`)
131
- .join(", ")}${runIds.length > limit ? `, … (+${runIds.length - limit})` : ""}`;
132
- }
133
- function fitUtf8(value, maxBytes) {
134
- let fitted = "";
135
- for (const character of value) {
136
- if (byteLength(fitted) + byteLength(character) > maxBytes)
137
- break;
138
- fitted += character;
139
- }
140
- return fitted;
141
- }
142
- function byteLength(value) {
143
- return Buffer.byteLength(value, "utf8");
144
- }
@@ -1,53 +0,0 @@
1
- /** Internal, Controller-derived release fence carried by Integration jobs. */
2
- export const INTEGRATION_RUNTIME_RELEASE_ENV = "YUI_INTEGRATION_RUNTIME_RELEASE_ID";
3
- /**
4
- * Find exact reusable evidence without creating cache state. Every identity
5
- * field is read from the existing IntegrationAttempt and DurableJob records.
6
- */
7
- export function findReusableIntegrationCheckEvidence(input) {
8
- const attempts = [...input.attempts]
9
- .filter((attempt) => (attempt.taskId === input.taskId
10
- && attempt.id !== input.currentAttemptId
11
- && attempt.projectId === input.projectId
12
- && sameOrderedText(attempt.checkCommands, input.checkCommands)))
13
- .sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
14
- for (const attempt of attempts) {
15
- if (attempt.jobId === undefined)
16
- continue;
17
- const job = input.jobs.find((candidate) => (candidate.id === attempt.jobId
18
- && candidate.taskId === input.taskId
19
- && candidate.owner.kind === "integration-attempt"
20
- && candidate.owner.integrationAttemptId === attempt.id));
21
- const resultSteps = job?.result?.steps ?? [];
22
- const logPaths = resultSteps.map((step) => (input.logPathFor?.(job, step.logPath) ?? step.logPath));
23
- if (job === undefined
24
- || job.projectId !== input.projectId
25
- || job.head !== input.candidateCommit
26
- || job.status !== "succeeded"
27
- || job.result?.outcome !== "succeeded"
28
- || job.env[INTEGRATION_RUNTIME_RELEASE_ENV] !== input.runtimeReleaseId
29
- || job.steps.length !== input.checkCommands.length
30
- || job.steps.some((step, index) => (step.name !== `check-${index + 1}`
31
- || step.command !== input.checkCommands[index]))
32
- || resultSteps.length !== input.checkCommands.length
33
- || resultSteps.some((step, index) => (step.name !== `check-${index + 1}`
34
- || step.exitCode !== 0
35
- || step.signal !== null
36
- || step.timedOut
37
- || step.head !== input.candidateCommit
38
- || (input.logExists !== undefined && !input.logExists(logPaths[index]))))) {
39
- continue;
40
- }
41
- const checks = resultSteps.map((step, index) => ({
42
- name: input.checkCommands[index],
43
- outcome: "passed",
44
- details: `Reused successful check evidence from ${attempt.id}/${job.id}.`,
45
- logPath: logPaths[index]
46
- }));
47
- return Object.freeze({ sourceAttempt: attempt, sourceJob: job, checks });
48
- }
49
- return null;
50
- }
51
- function sameOrderedText(left, right) {
52
- return left.length === right.length && left.every((value, index) => value === right[index]);
53
- }
@@ -1,191 +0,0 @@
1
- import { normalizedUniqueIdentities, normalizedUniqueText, requireIdentity, requireText, requireTimestamp } from "../domain/validation.js";
2
- import { validateTaskRecordReference } from "../task/taskRecordReference.js";
3
- export function createIntegrationQueueEntry(input, now) {
4
- const timestamp = now.toISOString();
5
- return validateIntegrationQueueEntry({
6
- schemaVersion: 1,
7
- id: input.id,
8
- taskId: input.taskId,
9
- projectId: input.projectId,
10
- changeSetId: input.changeSetId,
11
- targetRef: input.targetRef,
12
- status: "queued",
13
- checkCommands: normalizedUniqueText(input.checkCommands ?? [], "Integration queue check command"),
14
- evidenceRefs: normalizedUniqueText(input.evidenceRefs ?? [], "Integration queue evidence"),
15
- createdAt: timestamp,
16
- updatedAt: timestamp
17
- });
18
- }
19
- /**
20
- * An entry whose ChangeSet is already represented on the target (an ancestor
21
- * or a same-tree commit) converges directly to committed with its proof. The
22
- * entry still names the committed IntegrationAttempt that recorded the
23
- * convergence, so every acceptance and provenance consumer reads one
24
- * integration authority.
25
- */
26
- export function createConvergedIntegrationQueueEntry(input, now) {
27
- const timestamp = now.toISOString();
28
- return validateIntegrationQueueEntry({
29
- schemaVersion: 1,
30
- id: input.id,
31
- taskId: input.taskId,
32
- projectId: input.projectId,
33
- changeSetId: input.changeSetId,
34
- targetRef: input.targetRef,
35
- status: "committed",
36
- checkCommands: [],
37
- targetBefore: input.targetHead,
38
- targetAfter: input.targetHead,
39
- evidenceRefs: [input.proof],
40
- ...(input.integrationAttemptId === undefined
41
- ? {}
42
- : { integrationAttemptId: input.integrationAttemptId }),
43
- createdAt: timestamp,
44
- updatedAt: timestamp,
45
- endedAt: timestamp
46
- });
47
- }
48
- export function markIntegrationQueueRunning(entry, targetBefore, now) {
49
- validateIntegrationQueueEntry(entry);
50
- if (!["queued", "conflicted"].includes(entry.status)) {
51
- throw new Error(`Integration queue entry cannot run from ${entry.status}: ${entry.id}.`);
52
- }
53
- const { conflictSummary: _previousConflict, ...rest } = entry;
54
- return validateIntegrationQueueEntry({
55
- ...rest,
56
- status: "running",
57
- targetBefore,
58
- updatedAt: now.toISOString()
59
- });
60
- }
61
- export function recordIntegrationQueueAttempt(entry, integrationAttemptId, now) {
62
- validateIntegrationQueueEntry(entry);
63
- if (entry.status !== "running") {
64
- throw new Error(`Integration queue entry is not running: ${entry.id}/${entry.status}.`);
65
- }
66
- return validateIntegrationQueueEntry({
67
- ...entry,
68
- integrationAttemptId,
69
- updatedAt: now.toISOString()
70
- });
71
- }
72
- export function markIntegrationQueueBlocked(entry, conflictSummary, now) {
73
- validateIntegrationQueueEntry(entry);
74
- if (entry.status !== "running") {
75
- throw new Error(`Integration queue entry is not running: ${entry.id}/${entry.status}.`);
76
- }
77
- return validateIntegrationQueueEntry({
78
- ...entry,
79
- status: "conflicted",
80
- conflictSummary: requireText(conflictSummary, "Integration queue conflict summary"),
81
- updatedAt: now.toISOString()
82
- });
83
- }
84
- export function markIntegrationQueueCommitted(entry, targetAfter, now) {
85
- validateIntegrationQueueEntry(entry);
86
- if (entry.status !== "running" && entry.status !== "conflicted") {
87
- throw new Error(`Integration queue entry cannot commit from ${entry.status}: ${entry.id}.`);
88
- }
89
- const timestamp = now.toISOString();
90
- return validateIntegrationQueueEntry({
91
- ...entry,
92
- status: "committed",
93
- targetAfter,
94
- updatedAt: timestamp,
95
- endedAt: timestamp
96
- });
97
- }
98
- /** A conflicted entry is manually retried after its underlying issue was fixed. */
99
- export function markIntegrationQueueRequeued(entry, now) {
100
- validateIntegrationQueueEntry(entry);
101
- if (entry.status !== "conflicted") {
102
- throw new Error(`Integration queue entry cannot requeue from ${entry.status}: ${entry.id}.`);
103
- }
104
- return validateIntegrationQueueEntry({
105
- ...entry,
106
- status: "queued",
107
- updatedAt: now.toISOString()
108
- });
109
- }
110
- export function recordIntegrationQueueAffectedPaths(entry, affectedPaths, now) {
111
- validateIntegrationQueueEntry(entry);
112
- if (entry.status !== "queued") {
113
- throw new Error(`Integration queue entry cannot recompute overlap from ${entry.status}: ${entry.id}.`);
114
- }
115
- const previous = new Set(entry.affectedPaths ?? []);
116
- const merged = [...new Set([...previous, ...affectedPaths])].sort();
117
- return validateIntegrationQueueEntry({
118
- ...entry,
119
- affectedPaths: merged,
120
- updatedAt: now.toISOString()
121
- });
122
- }
123
- export function markIntegrationQueueSuperseded(entry, reason, now) {
124
- validateIntegrationQueueEntry(entry);
125
- if (!["queued", "conflicted"].includes(entry.status)) {
126
- throw new Error(`Integration queue entry cannot be superseded from ${entry.status}: ${entry.id}.`);
127
- }
128
- const timestamp = now.toISOString();
129
- return validateIntegrationQueueEntry({
130
- ...entry,
131
- status: "superseded",
132
- supersedeReason: requireText(reason, "Integration queue supersede reason"),
133
- updatedAt: timestamp,
134
- endedAt: timestamp
135
- });
136
- }
137
- export function validateIntegrationQueueEntry(entry) {
138
- if (entry.schemaVersion !== 1) {
139
- throw new Error("IntegrationQueueEntry must use schemaVersion 1.");
140
- }
141
- validateTaskRecordReference({ taskId: entry.taskId, localId: entry.id }, "integrationQueue");
142
- validateTaskRecordReference({ taskId: entry.taskId, localId: entry.changeSetId }, "changeSet");
143
- requireIdentity(entry.projectId, "Project id");
144
- requireText(entry.targetRef, "Integration queue target ref");
145
- normalizedUniqueText(entry.checkCommands, "Integration queue check command");
146
- normalizedUniqueText(entry.evidenceRefs, "Integration queue evidence");
147
- if (![
148
- "queued",
149
- "running",
150
- "conflicted",
151
- "committed",
152
- "superseded"
153
- ].includes(entry.status)) {
154
- throw new Error(`Integration queue status is invalid: ${String(entry.status)}.`);
155
- }
156
- if (entry.targetBefore !== undefined)
157
- requireCommit(entry.targetBefore, "Integration queue targetBefore");
158
- if (entry.targetAfter !== undefined)
159
- requireCommit(entry.targetAfter, "Integration queue targetAfter");
160
- if (entry.integrationAttemptId !== undefined) {
161
- normalizedUniqueIdentities([entry.integrationAttemptId], "Integration attempt id");
162
- }
163
- if (entry.conflictSummary !== undefined) {
164
- requireText(entry.conflictSummary, "Integration queue conflict summary");
165
- }
166
- if (entry.affectedPaths !== undefined) {
167
- normalizedUniqueText(entry.affectedPaths, "Integration queue affected path");
168
- }
169
- if (entry.supersedeReason !== undefined) {
170
- requireText(entry.supersedeReason, "Integration queue supersede reason");
171
- }
172
- requireTimestamp(entry.createdAt, "Integration queue entry createdAt");
173
- requireTimestamp(entry.updatedAt, "Integration queue entry updatedAt");
174
- if (["committed", "superseded"].includes(entry.status)) {
175
- requireTimestamp(entry.endedAt ?? "", "Integration queue entry endedAt");
176
- }
177
- if (entry.status === "committed") {
178
- requireCommit(entry.targetAfter ?? "", "Integration queue targetAfter");
179
- }
180
- if (entry.status === "running") {
181
- requireCommit(entry.targetBefore ?? "", "Integration queue targetBefore");
182
- }
183
- return entry;
184
- }
185
- function requireCommit(value, label) {
186
- const normalized = requireText(value, label).toLowerCase();
187
- if (!/^[a-f0-9]{40}(?:[a-f0-9]{24})?$/u.test(normalized)) {
188
- throw new Error(`${label} is invalid.`);
189
- }
190
- return normalized;
191
- }