@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.
- package/ARCHITECTURE.md +2 -0
- package/ARCHITECTURE.zh-CN.md +151 -0
- package/README.md +211 -14
- package/dist/agent/launchEnvironment.js +7 -0
- package/dist/artifacts/artifactCapability.js +74 -0
- package/dist/artifacts/artifactCommitLock.js +249 -0
- package/dist/artifacts/artifactPaths.js +151 -0
- package/dist/artifacts/gitArtifactRef.js +146 -0
- package/dist/artifacts/managedGit.js +332 -0
- package/dist/artifacts/taskArtifactRepository.js +277 -0
- package/dist/cli/commandCatalog.js +40 -16
- package/dist/cli/interactionPolicy.js +3 -3
- package/dist/cli/updateOrchestrator.js +24 -1
- package/dist/cli/updatePorts.js +7 -3
- package/dist/cli/upgradeCommand.js +42 -2
- package/dist/cli.js +403 -93
- package/dist/commands/globalRoleCommands.js +314 -4
- package/dist/commands/operatorCommands.js +33 -2
- package/dist/commands/projectCommands.js +6 -7
- package/dist/commands/releaseCommands.js +18 -0
- package/dist/commands/taskActivationCommands.js +22 -0
- package/dist/commands/taskActor.js +25 -0
- package/dist/commands/taskCommands.js +846 -155
- package/dist/commands/taskIntegrationCommands.js +16 -38
- package/dist/commands/taskIntegrationQueueCommands.js +1 -1
- package/dist/commands/taskRemoteDeliveryCommand.js +6 -6
- package/dist/commands/taskRoleRuntimeStatus.js +35 -0
- package/dist/context/runContextPack.js +28 -16
- package/dist/context/taskContext.js +64 -5
- package/dist/controller/agentHostObservation.js +155 -0
- package/dist/controller/clientRuntime.js +17 -2
- package/dist/controller/controller.js +11 -2
- package/dist/controller/fileSchedulerStoreAdapter.js +446 -13
- package/dist/controller/globalInputDelivery.js +119 -0
- package/dist/controller/jobControl.js +6 -2
- package/dist/controller/resourceInventory.js +14 -4
- package/dist/controller/resourceInventoryLinux.js +2 -6
- package/dist/controller/runtime.js +81 -6
- package/dist/controller/runtimeEventInbox.js +32 -3
- package/dist/controller/runtimeEventProcessor.js +26 -6
- package/dist/controller/runtimeHookRunFence.js +75 -19
- package/dist/controller/structuredProviderObservation.js +133 -70
- package/dist/coordination/workMailboxQueue.js +5 -0
- package/dist/execution/workItemExecutionProjection.js +1 -1
- package/dist/executor/agentExecutor.js +64 -4
- package/dist/executor/executorRegistry.js +3 -0
- package/dist/executor/fileRoleLaunchPlanner.js +78 -118
- package/dist/integration/deliveryObligation.js +2 -1
- package/dist/integration/gitIntegrationService.js +312 -382
- package/dist/integration/integrationAttempt.js +30 -4
- package/dist/integration/integrationQueueService.js +7 -7
- package/dist/integration/integrationSourceApplication.js +323 -0
- package/dist/kernel/builtinCapabilities.js +32 -24
- package/dist/message/globalInterrupt.js +33 -0
- package/dist/message/inputControlResolution.js +106 -0
- package/dist/message/message.js +423 -0
- package/dist/message/messageContinuation.js +126 -3
- package/dist/message/taskInterrupt.js +34 -0
- package/dist/observability/orchestrationMetrics.js +1 -1
- package/dist/plugins/pluginService.js +11 -3
- package/dist/release/releaseHandover.js +22 -0
- package/dist/release/releaseWorkflowPorts.js +15 -7
- package/dist/repository/gitWorkspace.js +72 -15
- package/dist/repository/taskWorkspaceCoordinator.js +134 -0
- package/dist/repository/taskWorkspacePreparer.js +120 -49
- package/dist/repository/workItemCandidateSnapshot.js +34 -0
- package/dist/resources/projectResource.js +0 -48
- package/dist/resources/projectResourceService.js +3 -81
- package/dist/resources/resourceDiscovery.js +3 -2
- package/dist/runtime/agentHost.js +152 -72
- package/dist/runtime/agentHostCompatibility.js +127 -0
- package/dist/runtime/agentHostProtocol.js +53 -0
- package/dist/runtime/executionEnvironment.js +0 -19
- package/dist/runtime/launchBroker.js +6 -0
- package/dist/runtime/sessionReconciliation.js +4 -4
- package/dist/runtime/taskRuntimeIsolation.js +30 -6
- package/dist/runtime/tmuxAdapters.js +5 -3
- package/dist/scheduler/operatorEvent.js +4 -0
- package/dist/scheduler/taskExecutionProjection.js +12 -1
- package/dist/scheduler/wakeReason.js +7 -1
- package/dist/scheduler/wakeupQueue.js +2 -0
- package/dist/setup/setupCommand.js +29 -16
- package/dist/storage/homeLayout.js +130 -0
- package/dist/storage/migrations/artifactsToGit.js +338 -0
- package/dist/storage/migrations/collapseWorktreeLayout.js +963 -0
- package/dist/storage/migrations/integrationContinuation.js +104 -0
- package/dist/storage/migrations/submitIntent.js +126 -0
- package/dist/storage/migrations/unifyHomeLayout.js +925 -0
- package/dist/storage/sqliteSchema.js +173 -7
- package/dist/storage/sqliteStore.js +41 -22
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +2 -1
- package/dist/storage/upgrade/upgradeOrchestrator.js +95 -2
- package/dist/task/archiveDiagnostics.js +128 -0
- package/dist/task/nextAction.js +44 -11
- package/dist/task/taskActivation.js +26 -0
- package/dist/task/taskActivationService.js +85 -69
- package/dist/task/taskSubmission.js +236 -0
- package/dist/web/assets/client/app.js +58 -2
- package/dist/web/assets/client/components.js +1 -0
- package/dist/web/assets/client/i18n.js +6 -0
- package/dist/web/assets/client/taskSurface.js +202 -7
- package/dist/web/assets/client/view.js +7 -4
- package/dist/web/assets/shell.js +23 -0
- package/dist/web/assets/styles/layout.js +1 -1
- package/dist/web/assets/styles/widgets.js +12 -0
- package/dist/web/webServer.js +135 -4
- package/dist/web/webSnapshot.js +4 -3
- package/dist/web/webTaskSurface.js +225 -8
- package/dist/workItem/workItem.js +14 -10
- package/dist/workspace/workItemChangeSetManager.js +18 -2
- package/docs/agent-result-consumption.md +2 -0
- package/docs/agent-result-consumption.zh-CN.md +81 -0
- package/docs/agent-runtime-drivers.md +2 -0
- package/docs/agent-runtime-drivers.zh-CN.md +77 -0
- package/docs/architecture/README.md +44 -32
- package/docs/architecture/README.zh-CN.md +43 -0
- package/docs/architecture/capabilities-and-resources.md +118 -79
- package/docs/architecture/capabilities-and-resources.zh-CN.md +83 -0
- package/docs/managed-turn-and-session-runtime.md +2 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +180 -0
- package/docs/observability/README.md +2 -0
- package/docs/observability/README.zh-CN.md +71 -0
- package/docs/plugin-sdk.md +320 -217
- package/docs/plugin-sdk.zh-CN.md +293 -0
- package/docs/provider-runtime.md +2 -0
- package/docs/provider-runtime.zh-CN.md +132 -0
- package/docs/release-workflow.md +41 -0
- package/docs/release-workflow.zh-CN.md +266 -0
- package/docs/roles-and-configuration.md +2 -0
- package/docs/roles-and-configuration.zh-CN.md +96 -0
- package/docs/sqlite-control-plane-design.md +225 -1
- package/docs/sqlite-control-plane-design.zh-CN.md +62 -0
- package/docs/task-dag-semantics.md +80 -57
- package/docs/task-dag-semantics.zh-CN.md +59 -0
- package/docs/task-delivery.md +2 -0
- package/docs/task-delivery.zh-CN.md +82 -0
- package/docs/task-local-identity.md +2 -0
- package/docs/task-local-identity.zh-CN.md +58 -0
- package/docs/testing/verification-levels.md +26 -0
- package/docs/testing/verification-levels.zh-CN.md +80 -0
- package/i18n/README.zh-CN.md +199 -10
- package/package.json +2 -1
- package/skills/yui-leader/SKILL.md +88 -331
- package/skills/yui-leader/references/execution.md +405 -0
- package/skills/yui-leader/references/integration.md +52 -2
- package/skills/yui-leader/references/planning.md +109 -0
- package/skills/yui-leader/references/task-plugins.md +8 -4
- package/skills/yui-operator/SKILL.md +22 -4
- package/skills/yui-runtime/SKILL.md +27 -0
- 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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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) => (
|
|
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
|
|
506
|
+
const identities = new Set();
|
|
506
507
|
for (const ref of candidate.artifactRefs) {
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
if (
|
|
512
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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}.`);
|
|
@@ -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)。
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
17
|
-
|
|
|
18
|
-
| WorkItem
|
|
19
|
-
| Task
|
|
20
|
-
|
|
|
21
|
-
|
|
|
22
|
-
| Provider
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
|
29
|
-
|
|
30
|
-
##
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
`src/cli/commandCatalog.ts`
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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.
|