@zq-silk/yui 0.16.0 → 0.16.2
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 +12 -0
- package/ARCHITECTURE.zh-CN.md +8 -0
- package/README.md +3 -0
- package/dist/agent/managedRuntimeEnvironment.js +0 -4
- package/dist/agentRun/agentRun.js +7 -17
- package/dist/cli/agentConfigurationPicker.js +24 -21
- package/dist/cli/commandCatalog.js +18 -10
- package/dist/cli/roleWizard.js +41 -30
- package/dist/cli/updateCommand.js +35 -13
- package/dist/cli/updateOrchestrator.js +73 -33
- package/dist/cli/updatePorts.js +51 -20
- package/dist/cli.js +35 -26
- package/dist/commands/controllerCommands.js +1 -1
- package/dist/commands/executionAuditCommands.js +2 -2
- package/dist/commands/projectCommands.js +1 -1
- package/dist/commands/releaseCommands.js +9 -38
- package/dist/commands/taskCommands.js +84 -81
- package/dist/commands/taskCompletionGate.js +0 -117
- package/dist/commands/taskRoleRuntimeStatus.js +1 -1
- package/dist/commands/taskUpstreamCommands.js +93 -54
- package/dist/context/runContextPack.js +30 -54
- package/dist/context/runInputContract.js +9 -0
- package/dist/context/sessionBootstrapManifest.js +11 -19
- package/dist/controller/agentHostObservation.js +4 -2
- package/dist/controller/controller.js +8 -6
- package/dist/controller/fileSchedulerStoreAdapter.js +85 -18
- package/dist/controller/globalInputDelivery.js +28 -7
- package/dist/controller/globalRuntimeAttention.js +34 -0
- package/dist/controller/operatorNotification.js +31 -0
- package/dist/controller/providerRetryAdmission.js +3 -1
- package/dist/controller/providerRetryDelivery.js +10 -3
- package/dist/controller/runtime.js +1 -27
- package/dist/controller/sessionOwnerReconciliation.js +42 -24
- package/dist/controller/structuredProviderObservation.js +27 -0
- package/dist/controller/updateReconciliation.js +72 -20
- package/dist/doctor/doctor.js +2 -1
- package/dist/executor/agentAdapter.js +28 -96
- package/dist/executor/agentConfigurationCatalog.js +13 -68
- package/dist/executor/agentConfigurationFields.js +120 -0
- package/dist/executor/agentConfigurationProbe.js +55 -80
- package/dist/executor/agentExecutor.js +5 -2
- package/dist/executor/executorRegistry.js +0 -9
- package/dist/executor/fileRoleLaunchPlanner.js +12 -11
- package/dist/interaction/operatorPresentation.js +2 -1
- package/dist/message/messageContinuation.js +8 -9
- package/dist/observability/executionAudit.js +1 -10
- package/dist/observability/runtimeIdentity.js +0 -23
- package/dist/output/agentConfigurationPresentation.js +4 -2
- package/dist/release/releaseHandover.js +2 -2
- package/dist/release/releaseIdempotencyStore.js +0 -23
- package/dist/release/releaseWorkflowPorts.js +8 -4
- package/dist/repository/gitWorkspace.js +66 -19
- package/dist/repository/taskWorkspacePreparer.js +75 -68
- package/dist/resources/resourceRegistry.js +90 -44
- package/dist/resources/sqliteResourceRegistry.js +3 -5
- package/dist/runtime/acpSession.js +21 -5
- package/dist/runtime/agentEndpoint.js +4 -1
- package/dist/runtime/agentHost.js +22 -62
- package/dist/runtime/agentHostCleanup.js +85 -0
- package/dist/runtime/agentHostProtocol.js +1 -1
- package/dist/runtime/builtinAgentDrivers.js +46 -119
- package/dist/runtime/builtinTranscriptObserver.js +10 -6
- package/dist/runtime/codexAppServerRuntime.js +55 -89
- package/dist/runtime/jsonLineChannel.js +35 -7
- package/dist/runtime/providerContinuationReconciliationService.js +42 -3
- package/dist/runtime/providerRetry.js +15 -0
- package/dist/runtime/providerRuntimeReconciler.js +12 -2
- package/dist/runtime/structuredProviderHost.js +106 -81
- package/dist/scheduler/activeRoleRunDelivery.js +5 -1
- package/dist/scheduler/operatorInputNotificationProcessor.js +7 -17
- package/dist/storage/upgrade/upgradeOrchestrator.js +0 -10
- package/dist/task/nextAction.js +33 -23
- package/dist/task/task.js +2 -1
- package/dist/web/assets/assetManifest.js +2 -0
- package/dist/web/assets/client/app.js +72 -3
- package/dist/web/assets/client/components.js +2 -1
- package/dist/web/assets/client/i18n.js +2 -0
- package/dist/web/assets/client/taskSummary.js +345 -0
- package/dist/web/assets/client/taskSurface.js +44 -26
- package/dist/web/assets/client/view.js +33 -0
- package/dist/web/assets/shell.js +1 -0
- package/dist/web/assets/styles/cards.js +21 -0
- package/dist/web/webServer.js +39 -5
- package/dist/web/webSessions.js +165 -0
- package/dist/web/webSnapshot.js +23 -0
- package/dist/web/webTaskSurface.js +23 -0
- package/docs/managed-turn-and-session-runtime.md +31 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +23 -0
- package/docs/observability/README.md +47 -0
- package/docs/observability/README.zh-CN.md +37 -0
- package/docs/project-refresh.md +9 -0
- package/docs/project-refresh.zh-CN.md +8 -0
- package/docs/provider-retry.md +35 -0
- package/docs/release-workflow.md +139 -20
- package/docs/release-workflow.zh-CN.md +115 -14
- package/docs/roles-and-configuration.md +30 -0
- package/docs/roles-and-configuration.zh-CN.md +20 -0
- package/docs/task-delivery.md +109 -0
- package/docs/task-delivery.zh-CN.md +80 -0
- package/docs/task-discovery.md +9 -0
- package/docs/task-discovery.zh-CN.md +6 -0
- package/docs/testing/verification-levels.md +33 -12
- package/docs/testing/verification-levels.zh-CN.md +28 -8
- package/i18n/README.zh-CN.md +2 -0
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +11 -0
- package/skills/yui-leader/references/execution.md +22 -0
- package/skills/yui-leader/references/planning.md +6 -2
- package/skills/yui-operator/SKILL.md +45 -13
- package/skills/yui-operator/references/task-delivery.md +99 -0
- package/skills/yui-runtime/SKILL.md +5 -3
- package/skills/yui-runtime/references/publication.md +56 -5
- package/skills/yui-runtime/references/recovery.md +12 -2
- package/dist/runtime/agentHostCompatibility.js +0 -127
|
@@ -61,71 +61,110 @@ async function integrateUpstream(args, store, home, options) {
|
|
|
61
61
|
}
|
|
62
62
|
const service = new GitIntegrationService(store.rootDirectory(), store, git, now, options.environment, undefined, options.jobPort);
|
|
63
63
|
const results = [];
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
64
|
+
let failure;
|
|
65
|
+
const selected = task.projectBindings.filter(binding => projectRef === undefined || binding.projectId === projectRef);
|
|
66
|
+
if (projectRef !== undefined && selected.length === 0) {
|
|
67
|
+
throw usageError(`Task Project not found: ${task.id}/${projectRef}.`);
|
|
68
|
+
}
|
|
69
|
+
for (const binding of selected) {
|
|
70
|
+
let phase = "prepare";
|
|
71
|
+
let integrationId;
|
|
72
|
+
try {
|
|
73
|
+
const project = store.getProject(binding.projectId);
|
|
74
|
+
if (project === null)
|
|
75
|
+
throw usageError(`Project not found: ${binding.projectId}.`);
|
|
76
|
+
if (project.remoteUrl === undefined) {
|
|
77
|
+
throw usageError(`Project has no remote URL: ${project.id}.`);
|
|
78
|
+
}
|
|
79
|
+
if (binding.baseCommit === undefined || binding.currentCommit === undefined) {
|
|
80
|
+
throw usageError(`Task Project has no activated commit boundary: ${task.id}/${project.id}.`);
|
|
81
|
+
}
|
|
82
|
+
const entry = workspaceProjectEntry(workspace, project.id);
|
|
83
|
+
if (entry === undefined) {
|
|
84
|
+
throw usageError(`Task workspace has no entry for Project: ${project.id}.`);
|
|
85
|
+
}
|
|
86
|
+
const head = (await git.inspect(entry.path, entry.branch)).baseCommit;
|
|
87
|
+
if (head !== binding.currentCommit) {
|
|
88
|
+
throw usageError(`Task Project current commit diverged from its main clone: ${task.id}/${project.id}.`);
|
|
89
|
+
}
|
|
90
|
+
phase = "resolve-remote";
|
|
91
|
+
const remote = await git.resolveRemoteHead({
|
|
92
|
+
remoteUrl: project.remoteUrl,
|
|
93
|
+
branch: binding.baseRef
|
|
94
|
+
});
|
|
95
|
+
const attempt = store.transaction((tx) => {
|
|
96
|
+
const created = createIntegrationAttempt({
|
|
97
|
+
id: tx.nextIntegrationAttemptId(task.id),
|
|
98
|
+
taskId: task.id,
|
|
99
|
+
projectId: project.id,
|
|
100
|
+
targetRef: entry.branch,
|
|
101
|
+
beforeCommit: binding.currentCommit,
|
|
102
|
+
source: {
|
|
103
|
+
kind: "upstream",
|
|
104
|
+
branch: remote.branch,
|
|
105
|
+
remoteCommit: remote.commit,
|
|
106
|
+
taskBaseCommit: binding.baseCommit,
|
|
107
|
+
strategy: "rebase"
|
|
108
|
+
},
|
|
109
|
+
checkCommands: parsed.many.get("--check") ?? [],
|
|
110
|
+
rerunChecks: parsed.one.has("--rerun-checks")
|
|
111
|
+
}, now());
|
|
112
|
+
tx.saveIntegrationAttempt(task.id, created);
|
|
113
|
+
return created;
|
|
114
|
+
});
|
|
115
|
+
integrationId = attempt.id;
|
|
116
|
+
phase = "integrate";
|
|
117
|
+
const result = await service.integrate(task.id, attempt.id);
|
|
118
|
+
results.push(result);
|
|
119
|
+
// Each Project has an independent candidate and Job. --latest admits all
|
|
120
|
+
// requested Projects, then the Agent continues the returned exact attempts.
|
|
121
|
+
if (result.status !== "committed" && result.status !== "checks-running")
|
|
122
|
+
break;
|
|
83
123
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
source: {
|
|
96
|
-
kind: "upstream",
|
|
97
|
-
branch: remote.branch,
|
|
98
|
-
remoteCommit: remote.commit,
|
|
99
|
-
taskBaseCommit: binding.baseCommit,
|
|
100
|
-
strategy: "rebase"
|
|
101
|
-
},
|
|
102
|
-
checkCommands: parsed.many.get("--check") ?? [],
|
|
103
|
-
rerunChecks: parsed.one.has("--rerun-checks")
|
|
104
|
-
}, now());
|
|
105
|
-
tx.saveIntegrationAttempt(task.id, created);
|
|
106
|
-
return created;
|
|
107
|
-
});
|
|
108
|
-
const result = await service.integrate(task.id, attempt.id);
|
|
109
|
-
results.push(result);
|
|
110
|
-
// Each Project has an independent candidate and Job. --latest admits all
|
|
111
|
-
// requested Projects, then the Agent continues the returned exact attempts.
|
|
112
|
-
if (result.status !== "committed" && result.status !== "checks-running")
|
|
124
|
+
catch (error) {
|
|
125
|
+
// Earlier Projects may already have advanced HEAD or admitted a Job.
|
|
126
|
+
// Preserve those receipts and the exact uncertain attempt; never turn a
|
|
127
|
+
// partially executed batch into a usage error or replay it automatically.
|
|
128
|
+
failure = {
|
|
129
|
+
projectId: binding.projectId,
|
|
130
|
+
phase,
|
|
131
|
+
message: error instanceof Error ? error.message : String(error),
|
|
132
|
+
...(integrationId === undefined ? {} : { integrationId }),
|
|
133
|
+
effect: integrationId === undefined ? "not-started" : "inspect-required"
|
|
134
|
+
};
|
|
113
135
|
break;
|
|
136
|
+
}
|
|
114
137
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
138
|
+
const attempted = new Set([
|
|
139
|
+
...results.map(result => result.attempt.projectId),
|
|
140
|
+
...(failure === undefined ? [] : [failure.projectId])
|
|
141
|
+
]);
|
|
142
|
+
const remainingProjectIds = selected.map(binding => binding.projectId)
|
|
143
|
+
.filter(id => !attempted.has(id));
|
|
144
|
+
const stage = failure !== undefined
|
|
145
|
+
|| results.some(result => result.status !== "committed" && result.status !== "checks-running")
|
|
146
|
+
? "integration-blocked"
|
|
147
|
+
: results.some(result => result.status === "checks-running")
|
|
148
|
+
? "checks-running" : "integrated";
|
|
118
149
|
const lines = results.flatMap(result => {
|
|
119
150
|
const { attempt, status } = result;
|
|
120
151
|
return [
|
|
121
|
-
` ${attempt.id} ${attempt.projectId}: ${
|
|
152
|
+
` ${attempt.id} ${attempt.projectId}: ${status} (rebase)`,
|
|
153
|
+
` Expected HEAD: ${attempt.beforeCommit}; committed HEAD: ${attempt.afterCommit ?? "not committed"}`,
|
|
154
|
+
` Candidate: ${attempt.candidateCommit ?? "not prepared"}`,
|
|
122
155
|
...(status === "checks-running" ? [
|
|
123
156
|
` Job ${result.job.id}; continue with yui task integration continue ${task.id}/${attempt.id}`
|
|
124
157
|
] : [])
|
|
125
158
|
];
|
|
126
159
|
});
|
|
127
160
|
return {
|
|
128
|
-
output: `Upstream Integration results for Task ${task.id}:\n${lines.join("\n")}\n
|
|
129
|
-
|
|
161
|
+
output: `Upstream Integration results for Task ${task.id} (${stage}):\n${lines.join("\n")}\n`
|
|
162
|
+
+ (failure === undefined ? "" : (` ${failure.projectId} stopped during ${failure.phase}: ${failure.message}\n`
|
|
163
|
+
+ (failure.integrationId === undefined
|
|
164
|
+
? " No Integration was started for this Project.\n"
|
|
165
|
+
: ` Inspect yui task integration show ${task.id}/${failure.integrationId}; effects require inspection, not batch replay.\n`)))
|
|
166
|
+
+ (remainingProjectIds.length === 0 ? "" : ` Not attempted: ${remainingProjectIds.join(", ")}\n`),
|
|
167
|
+
data: { taskId: task.id, stage, strategy: "rebase", integrations: results,
|
|
168
|
+
remainingProjectIds, ...(failure === undefined ? {} : { failure }) }
|
|
130
169
|
};
|
|
131
170
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { providerRetryProjection } from "../runtime/providerRetry.js";
|
|
2
2
|
import { operationalTaskRecords } from "../task/taskRecordRetirement.js";
|
|
3
3
|
import { TASK_COMPLETION_PUBLISHED_TREE_AUTHORIZED_EVENT } from "../task/publicationReference.js";
|
|
4
|
-
import { RUN_INPUT_MAX_DELTAS } from "./runInputContract.js";
|
|
4
|
+
import { RUN_INPUT_MAX_DELTAS, requireRunContextSnapshotRef } from "./runInputContract.js";
|
|
5
|
+
import { requireIdentity } from "../domain/validation.js";
|
|
5
6
|
import { assertWorkItemDependenciesCompleted } from "../workItem/dependencyGate.js";
|
|
6
7
|
import { governingWorkItemCandidate, workItemExecutionGroupById } from "../workItem/workItem.js";
|
|
7
8
|
import { formatGitArtifactRef, validateGitArtifactRef } from "../artifacts/gitArtifactRef.js";
|
|
@@ -209,26 +210,14 @@ export function contextSnapshotDeltaRefIds(store, snapshot) {
|
|
|
209
210
|
}
|
|
210
211
|
export function buildRunContextPack(store, taskId, runId) {
|
|
211
212
|
const run = requireExactRun(store, taskId, runId);
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
let snapshotRef;
|
|
215
|
-
if (run.inputs[0].input.contextSnapshotRef !== undefined) {
|
|
216
|
-
const expected = run.inputs[0].input.contextSnapshotRef;
|
|
217
|
-
const snapshot = store.getContextSnapshot(taskId, expected.id);
|
|
218
|
-
if (snapshot === null)
|
|
219
|
-
throw new Error(`AgentRun Context Snapshot is missing: ${expected.id}.`);
|
|
220
|
-
validateContextSnapshot(snapshot);
|
|
221
|
-
if (snapshot.digest !== expected.digest || snapshot.taskId !== taskId) {
|
|
222
|
-
throw new Error(`AgentRun Context Snapshot identity drifted: ${expected.id}.`);
|
|
223
|
-
}
|
|
224
|
-
pointers = snapshot.refs;
|
|
225
|
-
snapshotRef = contextSnapshotRef(snapshot);
|
|
226
|
-
}
|
|
213
|
+
const snapshot = readRunContextSnapshot(store, run);
|
|
214
|
+
const pointers = snapshot.refs;
|
|
227
215
|
if (pointers.length > RUN_CONTEXT_PACK_MAX_REFS) {
|
|
228
216
|
throw new Error(`AgentRun Context exceeds ${RUN_CONTEXT_PACK_MAX_REFS} authorized refs.`);
|
|
229
217
|
}
|
|
230
218
|
const view = contextView(run);
|
|
231
|
-
const writableProjectIds =
|
|
219
|
+
const writableProjectIds = run.effective.executionAuthority === "planning"
|
|
220
|
+
? [] : run.effective.writeProjectIds;
|
|
232
221
|
const summaries = pointers.map((ref) => Object.freeze({
|
|
233
222
|
refId: ref.refId,
|
|
234
223
|
store: ref.store,
|
|
@@ -246,7 +235,7 @@ export function buildRunContextPack(store, taskId, runId) {
|
|
|
246
235
|
adapterId: run.effective.adapterId,
|
|
247
236
|
workspace: run.effective.workspace.root
|
|
248
237
|
}),
|
|
249
|
-
|
|
238
|
+
snapshot: contextSnapshotRef(snapshot),
|
|
250
239
|
input: run.inputs[0].input,
|
|
251
240
|
authority: Object.freeze({ view, readableRefs: pointers, writableProjectIds }),
|
|
252
241
|
pointers,
|
|
@@ -302,17 +291,15 @@ function readLiveTaskState(store, taskId) {
|
|
|
302
291
|
});
|
|
303
292
|
}
|
|
304
293
|
export function expandRunContextRef(store, taskId, runId, refId, refStore) {
|
|
305
|
-
|
|
306
|
-
const
|
|
307
|
-
const
|
|
294
|
+
requireIdentity(refStore, "Context ref store");
|
|
295
|
+
const run = requireExactRun(store, taskId, runId);
|
|
296
|
+
const snapshot = readRunContextSnapshot(store, run);
|
|
297
|
+
const authorized = snapshot.refs.filter((ref) => (ref.refId === refId && ref.store === refStore));
|
|
298
|
+
const selector = `${refStore}/${refId}`;
|
|
308
299
|
if (authorized.length !== 1) {
|
|
309
300
|
throw new Error(`AgentRun Context ref is not uniquely authorized: ${selector}.`);
|
|
310
301
|
}
|
|
311
|
-
const
|
|
312
|
-
const snapshotRef = run.inputs[0].input.contextSnapshotRef;
|
|
313
|
-
const materialized = snapshotRef === undefined
|
|
314
|
-
? collectAuthorizedContext(store, run).find(({ ref }) => (contextRefIdentity(ref) === contextRefIdentity(authorized[0])))
|
|
315
|
-
: store.getContextSnapshot(taskId, snapshotRef.id)?.resources.find(({ ref }) => (contextRefIdentity(ref) === contextRefIdentity(authorized[0])));
|
|
302
|
+
const materialized = snapshot.resources.find(({ ref }) => (contextRefIdentity(ref) === contextRefIdentity(authorized[0])));
|
|
316
303
|
if (materialized === undefined || materialized.ref.digest !== authorized[0].digest) {
|
|
317
304
|
throw new Error(`AgentRun Context ref is unavailable or drifted: ${selector}.`);
|
|
318
305
|
}
|
|
@@ -329,15 +316,12 @@ export function expandRunContextRef(store, taskId, runId, refId, refStore) {
|
|
|
329
316
|
/** Fail-closed delta cursor resolution for one immutable AgentRun lineage. */
|
|
330
317
|
export function buildRunContextDelta(store, taskId, runId, after) {
|
|
331
318
|
const pack = buildRunContextPack(store, taskId, runId);
|
|
332
|
-
if (after === pack.digest || after === pack.snapshot
|
|
319
|
+
if (after === pack.digest || after === pack.snapshot.digest) {
|
|
333
320
|
return Object.freeze({ schemaVersion: 1, after, cursor: pack.digest, refs: [] });
|
|
334
321
|
}
|
|
335
322
|
const run = requireExactRun(store, taskId, runId);
|
|
336
|
-
const
|
|
337
|
-
const
|
|
338
|
-
? null
|
|
339
|
-
: store.getContextSnapshot(taskId, snapshotRef.id);
|
|
340
|
-
const parentDigest = snapshot?.parentRef?.digest;
|
|
323
|
+
const snapshot = readRunContextSnapshot(store, run);
|
|
324
|
+
const parentDigest = snapshot.parentRef?.digest;
|
|
341
325
|
if (!((parentDigest !== undefined && after === parentDigest)
|
|
342
326
|
|| (parentDigest === undefined && after === "none"))) {
|
|
343
327
|
throw new Error("AgentRun Context delta cursor is outside the frozen Snapshot lineage.");
|
|
@@ -356,6 +340,19 @@ function requireExactRun(store, taskId, runId) {
|
|
|
356
340
|
}
|
|
357
341
|
return run;
|
|
358
342
|
}
|
|
343
|
+
export function readRunContextSnapshot(store, run) {
|
|
344
|
+
const expected = requireRunContextSnapshotRef(run.inputs[0].input);
|
|
345
|
+
const snapshot = store.getContextSnapshot(run.taskId, expected.id);
|
|
346
|
+
if (snapshot === null)
|
|
347
|
+
throw new Error(`AgentRun Context Snapshot is missing: ${expected.id}.`);
|
|
348
|
+
if (snapshot.id !== expected.id || snapshot.taskId !== run.taskId
|
|
349
|
+
|| expected.taskId !== run.taskId || snapshot.digest !== expected.digest
|
|
350
|
+
|| snapshot.sequence !== expected.sequence || snapshot.scope !== expected.scope
|
|
351
|
+
|| snapshot.scopeRef !== expected.scopeRef) {
|
|
352
|
+
throw new Error(`AgentRun Context Snapshot identity drifted: ${expected.id}.`);
|
|
353
|
+
}
|
|
354
|
+
return validateContextSnapshot(snapshot);
|
|
355
|
+
}
|
|
359
356
|
function collectAuthorizedContext(store, run) {
|
|
360
357
|
const task = store.getTask(run.taskId);
|
|
361
358
|
if (task === null)
|
|
@@ -575,16 +572,7 @@ function collectSourceRunContext(store, run, sourceRunIds) {
|
|
|
575
572
|
}
|
|
576
573
|
/** The frozen refs, not the Group's changing lane state, define provenance. */
|
|
577
574
|
export function synthesisSourceRunIds(store, run) {
|
|
578
|
-
const
|
|
579
|
-
if (ref === undefined)
|
|
580
|
-
throw new Error(`Synthesis AgentRun has no Context Snapshot: ${run.id}.`);
|
|
581
|
-
const snapshot = store.getContextSnapshot(run.taskId, ref.id);
|
|
582
|
-
if (snapshot === null || snapshot.digest !== ref.digest
|
|
583
|
-
|| snapshot.sequence !== ref.sequence || snapshot.scope !== ref.scope
|
|
584
|
-
|| snapshot.scopeRef !== ref.scopeRef || snapshot.taskId !== run.taskId) {
|
|
585
|
-
throw new Error(`Synthesis Context Snapshot is missing or drifted: ${ref.id}.`);
|
|
586
|
-
}
|
|
587
|
-
validateContextSnapshot(snapshot);
|
|
575
|
+
const snapshot = readRunContextSnapshot(store, run);
|
|
588
576
|
return snapshot.resources.filter(({ ref: entry, value }) => (entry.store === "source-run"
|
|
589
577
|
&& value.executionGroupId === run.sourceExecutionGroupId)).map(({ ref: entry }) => entry.refId);
|
|
590
578
|
}
|
|
@@ -621,18 +609,6 @@ function contextView(run) {
|
|
|
621
609
|
return "operator";
|
|
622
610
|
return "worker";
|
|
623
611
|
}
|
|
624
|
-
function writableProjects(store, run, view) {
|
|
625
|
-
if (run.effective.executionAuthority === "planning")
|
|
626
|
-
return Object.freeze([]);
|
|
627
|
-
if (view === "leader")
|
|
628
|
-
return Object.freeze(store.getTask(run.taskId)?.projectBindings.map(({ projectId }) => projectId) ?? []);
|
|
629
|
-
if (view === "reviewer") {
|
|
630
|
-
return Object.freeze(run.workspace?.entries.filter(({ access }) => access === "write").map(({ projectId }) => projectId) ?? []);
|
|
631
|
-
}
|
|
632
|
-
if (run.workItemId === undefined)
|
|
633
|
-
return Object.freeze([]);
|
|
634
|
-
return Object.freeze(store.getWorkItem(run.taskId, run.workItemId)?.writeProjectIds ?? []);
|
|
635
|
-
}
|
|
636
612
|
function completionActions(view, authority) {
|
|
637
613
|
if (authority === "planning")
|
|
638
614
|
return Object.freeze(["finish-turn", "request-input", "request-activation"]);
|
|
@@ -33,6 +33,13 @@ export function validateRunInput(value) {
|
|
|
33
33
|
}
|
|
34
34
|
return value;
|
|
35
35
|
}
|
|
36
|
+
/** Execution must never substitute current Task facts for missing frozen evidence. */
|
|
37
|
+
export function requireRunContextSnapshotRef(input) {
|
|
38
|
+
if (input.contextSnapshotRef === undefined) {
|
|
39
|
+
throw new Error("AgentRun Context Snapshot is required for execution; the record remains audit-only.");
|
|
40
|
+
}
|
|
41
|
+
return validateContextSnapshotRef(input.contextSnapshotRef);
|
|
42
|
+
}
|
|
36
43
|
export function createRunInputEnvelope(context, input) {
|
|
37
44
|
validateRunInput(input);
|
|
38
45
|
const normalized = normalizeEnvelopeContext(context, input);
|
|
@@ -65,6 +72,8 @@ export function serializeRunInputEnvelope(value) {
|
|
|
65
72
|
throw new Error("AgentRun input protocol is unsupported.");
|
|
66
73
|
}
|
|
67
74
|
const normalized = normalizeEnvelope(value);
|
|
75
|
+
if (normalized.purpose !== "global")
|
|
76
|
+
requireRunContextSnapshotRef(normalized);
|
|
68
77
|
const subject = Object.entries(normalized.subject)
|
|
69
78
|
.map(([key, id]) => `${key}:${id}`)
|
|
70
79
|
.join(",");
|
|
@@ -4,7 +4,6 @@ import { dirname, join, resolve } from "node:path";
|
|
|
4
4
|
import { writeTextFileAtomically } from "../storage/durableFile.js";
|
|
5
5
|
import { SESSION_BOOTSTRAP_MANIFEST_SCHEMA_VERSION, SESSION_CONTEXT_PROTOCOL, sessionManifestCompatibilityDigest } from "./sessionProtocolIdentity.js";
|
|
6
6
|
export { SESSION_BOOTSTRAP_MANIFEST_SCHEMA_VERSION, SESSION_CONTEXT_PROTOCOL, sessionManifestCompatibilityDigest } from "./sessionProtocolIdentity.js";
|
|
7
|
-
const ordinarySessionCli = ["#!/bin/sh", "exec yui \"$@\"", ""].join("\n");
|
|
8
7
|
/** Portable read pointers accompany every managed input, including the first
|
|
9
8
|
* notification on a fresh Session. No Task content or credentials are copied. */
|
|
10
9
|
export function withSessionContextPointer(text, environment) {
|
|
@@ -24,9 +23,8 @@ export function withSessionContextPointer(text, environment) {
|
|
|
24
23
|
/**
|
|
25
24
|
* A managed Session wrapper answers exactly one question: which installation
|
|
26
25
|
* runs this command. It therefore carries only the resolved entry point, never
|
|
27
|
-
* a package or build identity.
|
|
28
|
-
*
|
|
29
|
-
* ordinary update into a broken Session.
|
|
26
|
+
* a package or build identity. Current Sessions may survive a compatible
|
|
27
|
+
* installation relocation; this is not a cross-version runtime adapter.
|
|
30
28
|
*/
|
|
31
29
|
function renderSessionCli(entryPoint) {
|
|
32
30
|
return [
|
|
@@ -108,8 +106,8 @@ export function materializeSessionBootstrap(input) {
|
|
|
108
106
|
// Provider command runners may rebuild PATH independently of the managed
|
|
109
107
|
// process environment, so a bare `yui` could resolve to another install or
|
|
110
108
|
// Home. The wrapper pins the resolved entry point instead. Package identity
|
|
111
|
-
// stays out of it:
|
|
112
|
-
//
|
|
109
|
+
// stays out of it: current Session authority and the CLI/Controller protocol
|
|
110
|
+
// boundary authorize the command.
|
|
113
111
|
const sessionCliContent = renderSessionCli(input.entryPoint);
|
|
114
112
|
const sessionCliDigest = digest(sessionCliContent);
|
|
115
113
|
const sessionCliPath = resolve(join(home, "runtime", "session-cli", `yui-${sessionCliDigest}.sh`));
|
|
@@ -185,11 +183,9 @@ function quoteShellWord(value) {
|
|
|
185
183
|
return `'${value.replaceAll("'", "'\"'\"'")}'`;
|
|
186
184
|
}
|
|
187
185
|
/**
|
|
188
|
-
* Retargets
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
* an earlier release may still pin a control-plane digest; retargeting it here
|
|
192
|
-
* is what removes that stale pin from a live Home.
|
|
186
|
+
* Retargets current managed wrappers to the resolved installation entry point.
|
|
187
|
+
* Only a valid Session Manifest may nominate a wrapper, and only the exact
|
|
188
|
+
* two-argument shell form generated here is changed.
|
|
193
189
|
*/
|
|
194
190
|
export function refreshManagedSessionCliWrappers(homeInput, entryPoint) {
|
|
195
191
|
const home = resolve(homeInput);
|
|
@@ -217,8 +213,7 @@ export function refreshManagedSessionCliWrappers(homeInput, entryPoint) {
|
|
|
217
213
|
wrapperPaths.add(wrapperPath);
|
|
218
214
|
}
|
|
219
215
|
catch {
|
|
220
|
-
//
|
|
221
|
-
// block current Sessions or an otherwise compatible package update.
|
|
216
|
+
// An invalid Manifest cannot nominate a wrapper for mutation.
|
|
222
217
|
skipped += 1;
|
|
223
218
|
}
|
|
224
219
|
}
|
|
@@ -234,7 +229,7 @@ export function refreshManagedSessionCliWrappers(homeInput, entryPoint) {
|
|
|
234
229
|
current += 1;
|
|
235
230
|
continue;
|
|
236
231
|
}
|
|
237
|
-
if (
|
|
232
|
+
if (!isManagedSessionCli(content)) {
|
|
238
233
|
skipped += 1;
|
|
239
234
|
continue;
|
|
240
235
|
}
|
|
@@ -244,12 +239,9 @@ export function refreshManagedSessionCliWrappers(homeInput, entryPoint) {
|
|
|
244
239
|
}
|
|
245
240
|
return Object.freeze({ refreshed, current, skipped });
|
|
246
241
|
}
|
|
247
|
-
/**
|
|
248
|
-
* Both wrapper shapes Yui has written: the current entry-point-only form and
|
|
249
|
-
* the earlier form that also pinned a control-plane digest.
|
|
250
|
-
*/
|
|
242
|
+
/** Match exactly the two shell-quoted words emitted by renderSessionCli. */
|
|
251
243
|
function isManagedSessionCli(content) {
|
|
252
|
-
return /^#!\/bin\/sh\nexec [
|
|
244
|
+
return /^#!\/bin\/sh\nexec '(?:[^'\n]|'"'"')*' '(?:[^'\n]|'"'"')*' "\$@"\n$/u.test(content);
|
|
253
245
|
}
|
|
254
246
|
function writeImmutableText(path, content) {
|
|
255
247
|
writeTextFileAtomically(path, content);
|
|
@@ -16,7 +16,7 @@ export function resolveAgentHostObservation(store, event) {
|
|
|
16
16
|
"conversation.observed", "goal.updated", "goal.cleared",
|
|
17
17
|
"turn.accepted", "turn.completed", "turn.failed", "turn.cancelled",
|
|
18
18
|
"input.accepted", "input.rejected", "input.delivery-unknown",
|
|
19
|
-
"operation.started", "operation.completed", "operation.failed", "activity.observed"
|
|
19
|
+
"operation.started", "operation.completed", "operation.failed", "activity.observed", "observer.health"
|
|
20
20
|
];
|
|
21
21
|
if (!hostKinds.includes(kind)
|
|
22
22
|
|| (kind === "host.observed" ? input.authority !== "host"
|
|
@@ -44,7 +44,9 @@ export function resolveAgentHostObservation(store, event) {
|
|
|
44
44
|
const directStart = kind === "turn.accepted" && fence.receiptId === `direct:${fence.nativeTurnId}`;
|
|
45
45
|
const additionalInput = kind.startsWith("input.") && (fence.receiptId?.startsWith("native-input:") || fence.receiptId?.startsWith("turn-input:")
|
|
46
46
|
|| fence.receiptId?.startsWith("steer:"));
|
|
47
|
-
|
|
47
|
+
// Post-execution diagnostics may describe an already-terminal exact input.
|
|
48
|
+
// This permits evidence collection, never a terminal transition.
|
|
49
|
+
const terminal = ["turn.completed", "turn.failed", "turn.cancelled", "session.ended", "session.failed", "observer.health"].includes(kind);
|
|
48
50
|
if (fence.taskId === undefined) {
|
|
49
51
|
if (host.startupRunId !== undefined)
|
|
50
52
|
throw new RuntimeHookRunFenceError("Global Host cannot carry a startup Run.");
|
|
@@ -143,7 +143,7 @@ export async function runControllerSchedulerPass(store, delivery, now, workspace
|
|
|
143
143
|
}));
|
|
144
144
|
const laterWakeups = await processLeaderWakeups(store, delivery, now, exactTaskSelection(laterWakeupTaskIds));
|
|
145
145
|
const inputNotifications = includeOperator
|
|
146
|
-
? await processOperatorInputNotifications(store,
|
|
146
|
+
? await processOperatorInputNotifications(store, selection, now)
|
|
147
147
|
: [];
|
|
148
148
|
return {
|
|
149
149
|
activeRunDeliveries,
|
|
@@ -1407,18 +1407,20 @@ export class FileTaskController {
|
|
|
1407
1407
|
else if (wakeup.status === "dispatched")
|
|
1408
1408
|
settled.add(key);
|
|
1409
1409
|
}
|
|
1410
|
-
const operatorRetries = result.inputNotifications.filter((notification) => notification.reason === "operator-
|
|
1411
|
-
|
|
1412
|
-
&& this.#operatorStartupRetryArmed));
|
|
1410
|
+
const operatorRetries = result.inputNotifications.filter((notification) => notification.reason === "operator-unavailable"
|
|
1411
|
+
&& this.#operatorStartupRetryArmed);
|
|
1413
1412
|
if (operatorRetries.length > 0) {
|
|
1414
1413
|
retry.set("operator", {
|
|
1415
1414
|
identity: operatorRetries.map((notification) => (`${notification.sourceKind}:${notification.taskId}:${notification.sourceId}`)).join("|")
|
|
1416
1415
|
});
|
|
1417
1416
|
}
|
|
1418
|
-
else if (result.inputNotifications.some((notification) => notification.status === "
|
|
1419
|
-
|| notification.status === "already-
|
|
1417
|
+
else if (result.inputNotifications.some((notification) => notification.status === "queued"
|
|
1418
|
+
|| notification.status === "already-queued")) {
|
|
1420
1419
|
this.#operatorStartupRetryArmed = false;
|
|
1421
1420
|
settled.add("operator");
|
|
1421
|
+
// The Global queue owns delivery now. Request its next pass without
|
|
1422
|
+
// retaining a second attention retry loop while the Provider is busy.
|
|
1423
|
+
resignal.add("global-role:operator");
|
|
1422
1424
|
}
|
|
1423
1425
|
for (const key of settled)
|
|
1424
1426
|
this.#clearDeliveryRetry(key);
|