@zq-silk/yui 0.16.2 → 1.0.0-alpha.1
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/README.md +6 -4
- package/dist/agent/agent.js +4 -9
- package/dist/agent/executionComponents.js +4 -4
- package/dist/agentRun/agentRun.js +8 -8
- package/dist/artifacts/managedGit.js +3 -56
- package/dist/brief/taskBrief.js +3 -3
- package/dist/cli/updateOrchestrator.js +2 -13
- package/dist/cli/updatePorts.js +36 -69
- package/dist/cli/upgradeCommand.js +4 -8
- package/dist/cli.js +4 -7
- package/dist/commands/controllerCommands.js +1 -1
- package/dist/commands/taskCommands.js +8 -1
- package/dist/commands/taskRoleRuntimeStatus.js +1 -1
- package/dist/context/runInputContract.js +1 -1
- package/dist/controller/agentRuntimeObserver.js +4 -4
- package/dist/controller/clientRuntime.js +19 -44
- package/dist/controller/controller.js +16 -4
- package/dist/controller/fileSchedulerStoreAdapter.js +6 -6
- package/dist/controller/globalInputDelivery.js +3 -1
- package/dist/controller/jobSupervisor.js +3 -3
- package/dist/controller/providerRetryDelivery.js +128 -118
- package/dist/controller/runtime.js +1 -1
- package/dist/controller/structuredProviderObservation.js +1 -1
- package/dist/coordination/workMailbox.js +4 -4
- package/dist/core/controllerIdentity.js +25 -0
- package/dist/core/controllerProcessIdentity.js +2 -2
- package/dist/core/controllerServer.js +4 -2
- package/dist/core/protocol.js +1 -1
- package/dist/domain/validation.js +6 -0
- package/dist/event/taskEvent.js +3 -3
- package/dist/execution/workItemExecution.js +2 -2
- package/dist/executor/agentExecutor.js +6 -6
- package/dist/executor/effectiveLaunch.js +3 -3
- package/dist/grant/capabilityGrant.js +2 -2
- package/dist/input/inputRequest.js +4 -4
- package/dist/integration/changeSet.js +3 -3
- package/dist/integration/integrationAttempt.js +3 -3
- package/dist/integration/integrationSourceApplication.js +1 -1
- package/dist/job/durableJob.js +1 -1
- package/dist/job/jobRunner.js +2 -2
- package/dist/message/message.js +8 -6
- package/dist/milestone/milestone.js +3 -3
- package/dist/profile/agentProfile.js +3 -3
- package/dist/release/runtimeRelease.js +9 -7
- package/dist/repository/project.js +3 -3
- package/dist/resources/liveReferences.js +1 -1
- package/dist/resources/sqliteResourceRegistry.js +2 -2
- package/dist/review/reviewRound.js +5 -5
- package/dist/role/role.js +7 -8
- package/dist/runtime/acpProtocol.js +2 -3
- package/dist/runtime/agentDriverObservation.js +1 -1
- package/dist/runtime/agentHost.js +3 -3
- package/dist/runtime/agentHostProtocol.js +2 -2
- package/dist/runtime/codexInteractiveHost.js +1 -1
- package/dist/runtime/launchBroker.js +1 -1
- package/dist/runtime/processExitObservation.js +1 -1
- package/dist/runtime/providerContinuationReconciliationService.js +84 -75
- package/dist/runtime/providerRuntimeIdentity.js +3 -3
- package/dist/runtime/runtimeCoherence.js +7 -3
- package/dist/runtime/runtimeObservation.js +3 -3
- package/dist/runtime/sessionOwnerIdentity.js +1 -1
- package/dist/runtime/taskRuntimeIsolation.js +3 -3
- package/dist/runtime/tmuxAdapters.js +3 -3
- package/dist/scheduler/taskWake.js +1 -1
- package/dist/storage/baselineSchema.js +606 -0
- package/dist/storage/homeLayout.js +5 -16
- package/dist/storage/recordValidation.js +16 -4
- package/dist/storage/sqliteSchema.js +106 -1741
- package/dist/storage/sqliteStore.js +16 -14
- package/dist/storage/storageSchema.js +8 -7
- package/dist/storage/storageVersions.js +23 -16
- package/dist/storage/taskStore.js +3 -29
- package/dist/storage/upgrade/upgradeOrchestrator.js +14 -102
- package/dist/task/task.js +15 -7
- package/dist/task/taskActivation.js +5 -4
- package/dist/telemetry/sqliteTelemetryStore.js +2 -2
- package/dist/verification/gateArtifact.js +5 -3
- package/dist/verification/verificationPlan.js +6 -7
- package/dist/workItem/workItem.js +6 -6
- package/dist/workspace/cleanupInspection.js +1 -9
- package/dist/worktree/managedWorkspace.js +3 -3
- package/docs/managed-turn-and-session-runtime.md +26 -14
- package/docs/managed-turn-and-session-runtime.zh-CN.md +18 -10
- package/docs/release-workflow.md +60 -300
- package/docs/release-workflow.zh-CN.md +46 -234
- package/docs/sqlite-control-plane-design.md +48 -289
- package/docs/sqlite-control-plane-design.zh-CN.md +37 -53
- package/docs/storage-baseline.md +139 -0
- package/docs/storage-baseline.zh-CN.md +111 -0
- package/docs/task-delivery.md +3 -4
- package/docs/task-delivery.zh-CN.md +3 -3
- package/docs/testing/verification-levels.md +40 -180
- package/docs/testing/verification-levels.zh-CN.md +27 -131
- package/i18n/README.zh-CN.md +5 -4
- package/package.json +1 -1
- package/dist/storage/migrations/agentFailureContext.js +0 -22
- package/dist/storage/migrations/agentRunContract.js +0 -159
- package/dist/storage/migrations/artifactsToGit.js +0 -338
- package/dist/storage/migrations/collapseWorktreeLayout.js +0 -963
- package/dist/storage/migrations/currentInputContract.js +0 -86
- package/dist/storage/migrations/currentRuntimeContract.js +0 -228
- package/dist/storage/migrations/historicalVerificationPlan.js +0 -35
- package/dist/storage/migrations/integrationContinuation.js +0 -105
- package/dist/storage/migrations/narrowAgentFailureContext.js +0 -65
- package/dist/storage/migrations/notificationOnlyWakes.js +0 -74
- package/dist/storage/migrations/removeRuntimeGeneration.js +0 -207
- package/dist/storage/migrations/submitIntent.js +0 -126
- package/dist/storage/migrations/unifyHomeLayout.js +0 -925
- package/dist/storage/migrations/verificationPlanV1.js +0 -162
- package/dist/storage/migrations/verificationPolicy.js +0 -74
- package/dist/storage/migrations/workItemHistory.js +0 -46
package/README.md
CHANGED
|
@@ -34,8 +34,9 @@ not from terminal windows you juggle or details you have to remember.
|
|
|
34
34
|
- **Isolated by default** — repository work happens in managed Git worktrees;
|
|
35
35
|
the stable checkout stays read-only.
|
|
36
36
|
|
|
37
|
-
> **Status:**
|
|
38
|
-
>
|
|
37
|
+
> **Status:** 1.0.0-alpha.1, with a clean storage 1.0 baseline. Default updates
|
|
38
|
+
> support only declared minor upgrades within one storage major. Existing v37
|
|
39
|
+
> Homes require the separate [one-time conversion](docs/storage-baseline.md).
|
|
39
40
|
|
|
40
41
|
[Quick start](#quick-start) · [Working through conversation](#working-through-conversation) · [Architecture](#architecture) · [Design principles](#design-principles)
|
|
41
42
|
|
|
@@ -383,8 +384,9 @@ npm users do not need to compile it.
|
|
|
383
384
|
|
|
384
385
|
To exercise your checkout, run `make install-local`, then use the absolute
|
|
385
386
|
`<checkout>/output/dev/bin/yui` launcher. It defaults to an isolated Home under
|
|
386
|
-
that checkout; run its `setup` before stateful use.
|
|
387
|
-
|
|
387
|
+
that checkout; run its `setup` before stateful use. Development tooling does not
|
|
388
|
+
manage global installations; do not use the global `yui` to validate local changes.
|
|
389
|
+
Live-model, paid or shared-resource
|
|
388
390
|
tests require an explicit request for those resources.
|
|
389
391
|
|
|
390
392
|
## Community and support
|
package/dist/agent/agent.js
CHANGED
|
@@ -9,7 +9,7 @@ export function createConfiguredAgent(id, adapterId, command, baseArgs, environm
|
|
|
9
9
|
validateAgentBaseArguments(adapterId, baseArgs);
|
|
10
10
|
const timestamp = now.toISOString();
|
|
11
11
|
return {
|
|
12
|
-
schemaVersion:
|
|
12
|
+
schemaVersion: 1,
|
|
13
13
|
id: normalizedId,
|
|
14
14
|
// An unnamed component resolves from the plan, which for ACP means the
|
|
15
15
|
// unidentified entry rather than a guess at which product is installed.
|
|
@@ -45,18 +45,13 @@ export function resolveAgentEnvironment(agent, processEnvironment = process.env)
|
|
|
45
45
|
}));
|
|
46
46
|
}
|
|
47
47
|
export function validateConfiguredAgent(agent) {
|
|
48
|
-
if (agent.schemaVersion !==
|
|
48
|
+
if (agent.schemaVersion !== 1)
|
|
49
49
|
throw new Error("Agent schema version is invalid.");
|
|
50
50
|
requireSafeIdentity(agent.id, "Agent id");
|
|
51
51
|
if (!isAgentAdapterId(agent.adapterId))
|
|
52
52
|
throw new Error(`Agent adapter is unsupported: ${agent.adapterId}.`);
|
|
53
|
-
//
|
|
54
|
-
// stored
|
|
55
|
-
// absent value here is a corrupt record, not an old one, and resolving it to
|
|
56
|
-
// the plan default would invent a product identity for data that never lost
|
|
57
|
-
// one. That default belongs to the constructor, where an operator naming
|
|
58
|
-
// only a plan is a real and supported request — this validator reads records
|
|
59
|
-
// that were already written, where the same silence means something else.
|
|
53
|
+
// Creation resolves an omitted component before persistence. Reading a
|
|
54
|
+
// stored record must not invent a missing product identity.
|
|
60
55
|
if (agent.component === undefined) {
|
|
61
56
|
throw new Error(`Agent is missing its execution component: ${agent.id}.`);
|
|
62
57
|
}
|
|
@@ -138,10 +138,10 @@ export function displayExecutionComponent(adapterId, componentId) {
|
|
|
138
138
|
/**
|
|
139
139
|
* Resolve the component for a connection plan, given what the caller stated.
|
|
140
140
|
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* to the
|
|
144
|
-
*
|
|
141
|
+
* Creation may name only a connection plan. Resolve its default before
|
|
142
|
+
* persistence; current stored Agents and bindings require an explicit component.
|
|
143
|
+
* ACP defaults to the unidentified entry, never a product inferred from command,
|
|
144
|
+
* arguments or environment.
|
|
145
145
|
*/
|
|
146
146
|
export function resolveAgentExecutionComponent(adapterId, componentId) {
|
|
147
147
|
if (componentId === undefined)
|
|
@@ -36,7 +36,7 @@ export function createRun(id, taskId, roleName, mode, input, now, context) {
|
|
|
36
36
|
if (snapshot.taskId !== taskId)
|
|
37
37
|
throw new Error("AgentRun Context Snapshot belongs to another Task.");
|
|
38
38
|
return {
|
|
39
|
-
schemaVersion:
|
|
39
|
+
schemaVersion: 1,
|
|
40
40
|
id: requireSafeIdentity(id, "AgentRun id"),
|
|
41
41
|
taskId: requireSafeIdentity(taskId, "Task id"),
|
|
42
42
|
roleName: requireSafeIdentity(roleName, "Role name"),
|
|
@@ -132,8 +132,8 @@ export function validateRun(run) {
|
|
|
132
132
|
"createdAt",
|
|
133
133
|
"updatedAt"
|
|
134
134
|
], "AgentRun");
|
|
135
|
-
if (run.schemaVersion !==
|
|
136
|
-
throw new Error("AgentRun must use schemaVersion
|
|
135
|
+
if (run.schemaVersion !== 1)
|
|
136
|
+
throw new Error("AgentRun must use schemaVersion 1.");
|
|
137
137
|
validateTaskRecordReference({ taskId: run.taskId, localId: run.id }, "run");
|
|
138
138
|
requireSafeIdentity(run.roleName, "Role name");
|
|
139
139
|
if (run.mode !== "new" && run.mode !== "resume") {
|
|
@@ -283,8 +283,8 @@ export function validateRun(run) {
|
|
|
283
283
|
throw new Error("Execution AgentRun cannot carry Review effective provenance.");
|
|
284
284
|
}
|
|
285
285
|
for (const record of run.inputs) {
|
|
286
|
-
//
|
|
287
|
-
//
|
|
286
|
+
// Validate readable evidence, not readiness to execute. A missing Snapshot
|
|
287
|
+
// must still allow inspection, failure settlement and explicit retirement.
|
|
288
288
|
createRunInputEnvelope(runEnvelopeContext(run), record.input);
|
|
289
289
|
}
|
|
290
290
|
if (!["active", "completed", "failed"].includes(run.status)) {
|
|
@@ -335,7 +335,7 @@ function finishRun(run, status, output, now, failureReason, provider, systemEvid
|
|
|
335
335
|
...run,
|
|
336
336
|
status,
|
|
337
337
|
result: {
|
|
338
|
-
schemaVersion:
|
|
338
|
+
schemaVersion: 1,
|
|
339
339
|
...(output === undefined ? {} : { output: requireResultText(output, "AgentRun result output") }),
|
|
340
340
|
...(diagnostic === undefined
|
|
341
341
|
? {}
|
|
@@ -352,8 +352,8 @@ function finishRun(run, status, output, now, failureReason, provider, systemEvid
|
|
|
352
352
|
return validateRun(terminal);
|
|
353
353
|
}
|
|
354
354
|
function validateRunResult(result) {
|
|
355
|
-
if (result === undefined || result.schemaVersion !==
|
|
356
|
-
throw new Error("A terminal AgentRun requires AgentRunResult schemaVersion
|
|
355
|
+
if (result === undefined || result.schemaVersion !== 1) {
|
|
356
|
+
throw new Error("A terminal AgentRun requires AgentRunResult schemaVersion 1.");
|
|
357
357
|
}
|
|
358
358
|
rejectUnknownFields(result, [
|
|
359
359
|
"schemaVersion",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { execFile
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
2
|
import { promisify } from "node:util";
|
|
3
3
|
const executeFile = promisify(execFile);
|
|
4
4
|
/**
|
|
@@ -259,8 +259,8 @@ function isExternalProgramConfigKey(key) {
|
|
|
259
259
|
* list of repo-local keys that can execute an external program. Empty means the
|
|
260
260
|
* repository's own config is within the managed boundary.
|
|
261
261
|
*
|
|
262
|
-
* PURE: no I/O. Callers
|
|
263
|
-
*
|
|
262
|
+
* PURE: no I/O. Callers read the config with the managed runner and pass the
|
|
263
|
+
* bytes here, so the security policy lives
|
|
264
264
|
* in exactly one testable place. Reading `--local` deliberately excludes the
|
|
265
265
|
* command-line `-c` hardening flags (which are safe and not persisted) and the
|
|
266
266
|
* null-device'd global/system config; it sees only what is written in the repo.
|
|
@@ -277,56 +277,3 @@ export function externalProgramConfigViolations(localConfigListZ) {
|
|
|
277
277
|
}
|
|
278
278
|
return [...offending].sort();
|
|
279
279
|
}
|
|
280
|
-
/**
|
|
281
|
-
* SYNCHRONOUS managed invocation, used ONLY where an async runner is
|
|
282
|
-
* structurally impossible: a storage `migrateData(db)` step runs inside
|
|
283
|
-
* `db.transaction(...)`, which better-sqlite3 requires to be synchronous, yet
|
|
284
|
-
* the 18->19 migration must build per-Task artifact repositories on disk. This
|
|
285
|
-
* shares the SAME hardening as {@link spawnManagedGit} — identical argument
|
|
286
|
-
* refusal, identical scrubbed environment, identical prepended flags — so the
|
|
287
|
-
* synchronous path never weakens the trust boundary. It is not exported for
|
|
288
|
-
* ordinary runtime use; the async runner remains the only production write path.
|
|
289
|
-
*/
|
|
290
|
-
function spawnManagedGitSync(repoPath, args, options) {
|
|
291
|
-
assertSafeArguments(args);
|
|
292
|
-
const env = managedGitEnvironment();
|
|
293
|
-
if (options?.commitDates !== undefined) {
|
|
294
|
-
// Deterministic history: fixed author/committer dates make a rebuild of the
|
|
295
|
-
// same source data reproduce the same commit ids. This only sets metadata.
|
|
296
|
-
env.GIT_AUTHOR_DATE = options.commitDates.author;
|
|
297
|
-
env.GIT_COMMITTER_DATE = options.commitDates.committer;
|
|
298
|
-
}
|
|
299
|
-
try {
|
|
300
|
-
const stdout = execFileSync("git", [...HARDENING_FLAGS, "-C", repoPath, ...args], {
|
|
301
|
-
encoding: "buffer",
|
|
302
|
-
env,
|
|
303
|
-
maxBuffer: options?.maxBuffer ?? 16 * 1024 * 1024,
|
|
304
|
-
timeout: options?.timeoutMs ?? 30_000,
|
|
305
|
-
windowsHide: true
|
|
306
|
-
});
|
|
307
|
-
return { stdout: stdout, stderr: Buffer.alloc(0) };
|
|
308
|
-
}
|
|
309
|
-
catch (error) {
|
|
310
|
-
throw new ManagedGitError([...args], readErrorStream(error), error);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
/** Synchronous counterpart to {@link managedGit}; returns trimmed UTF-8 stdout. */
|
|
314
|
-
export function managedGitSync(repoPath, args, options) {
|
|
315
|
-
return spawnManagedGitSync(repoPath, args, options).stdout.toString("utf8");
|
|
316
|
-
}
|
|
317
|
-
/** Synchronous counterpart to {@link managedGitBuffer}; returns raw stdout bytes. */
|
|
318
|
-
export function managedGitSyncBuffer(repoPath, args, options) {
|
|
319
|
-
return spawnManagedGitSync(repoPath, args, options).stdout;
|
|
320
|
-
}
|
|
321
|
-
/** Synchronous counterpart to {@link managedGitSucceeds}; never throws on non-zero exit. */
|
|
322
|
-
export function managedGitSyncSucceeds(repoPath, args, options) {
|
|
323
|
-
try {
|
|
324
|
-
spawnManagedGitSync(repoPath, args, options);
|
|
325
|
-
return true;
|
|
326
|
-
}
|
|
327
|
-
catch (error) {
|
|
328
|
-
if (error instanceof ManagedGitError)
|
|
329
|
-
return false;
|
|
330
|
-
throw error;
|
|
331
|
-
}
|
|
332
|
-
}
|
package/dist/brief/taskBrief.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export function createTaskBrief(input, now) {
|
|
2
2
|
return {
|
|
3
|
-
schemaVersion:
|
|
3
|
+
schemaVersion: 1,
|
|
4
4
|
objective: requireText(input.objective, "Task objective"),
|
|
5
5
|
boundaries: normalizeBoundaries(input.boundaries),
|
|
6
6
|
technicalApproach: optionalText(input.technicalApproach, "Task technical approach"),
|
|
@@ -22,8 +22,8 @@ export function updateTaskBrief(brief, patch, updatedBy, now) {
|
|
|
22
22
|
}, now);
|
|
23
23
|
}
|
|
24
24
|
export function validateTaskBrief(brief) {
|
|
25
|
-
if (brief.schemaVersion !==
|
|
26
|
-
throw new Error("Task Brief requires schemaVersion
|
|
25
|
+
if (brief.schemaVersion !== 1) {
|
|
26
|
+
throw new Error("Task Brief requires schemaVersion 1.");
|
|
27
27
|
}
|
|
28
28
|
if (!Array.isArray(brief.boundaries) || typeof brief.technicalApproach !== "string"
|
|
29
29
|
|| typeof brief.updatedAt !== "string" || !Number.isFinite(Date.parse(brief.updatedAt))) {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* verified before the Controller is restarted.
|
|
8
8
|
*/
|
|
9
9
|
import { UpdateControllerReconciliationError } from "../controller/updateReconciliation.js";
|
|
10
|
+
import { isControllerIdentity } from "../core/controllerIdentity.js";
|
|
10
11
|
export function runUpdate(ports, options) {
|
|
11
12
|
let staged;
|
|
12
13
|
try {
|
|
@@ -61,8 +62,6 @@ function runStagedUpdate(ports, staged, home) {
|
|
|
61
62
|
action: preflight.action,
|
|
62
63
|
recoverable: true,
|
|
63
64
|
version: staged.version,
|
|
64
|
-
...(preflight.blockers === undefined ? {} : { blockers: preflight.blockers }),
|
|
65
|
-
...(preflight.retryCommand === undefined ? {} : { retryCommand: preflight.retryCommand }),
|
|
66
65
|
...(preflight.sceneUnchanged === true ? { sceneUnchanged: true } : {})
|
|
67
66
|
};
|
|
68
67
|
}
|
|
@@ -159,8 +158,7 @@ function runCoordinatedUpdate(ports, staged, home) {
|
|
|
159
158
|
return restoreControllerOrReport(ports, home, captured.lifecycle, {
|
|
160
159
|
outcome: "aborted", phase: "preflight",
|
|
161
160
|
message: fencedPreflight.message, action: fencedPreflight.action,
|
|
162
|
-
recoverable: true, version: staged.version
|
|
163
|
-
...(fencedPreflight.blockers === undefined ? {} : { blockers: fencedPreflight.blockers })
|
|
161
|
+
recoverable: true, version: staged.version
|
|
164
162
|
});
|
|
165
163
|
}
|
|
166
164
|
return activateAndVerify(ports, staged, home, captured.lifecycle, fencedPreflight);
|
|
@@ -391,15 +389,6 @@ function isStorageMigrationResult(value) {
|
|
|
391
389
|
function isPositivePid(value) {
|
|
392
390
|
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
393
391
|
}
|
|
394
|
-
function isControllerIdentity(value) {
|
|
395
|
-
return isRecord(value)
|
|
396
|
-
&& typeof value.executablePath === "string"
|
|
397
|
-
&& value.executablePath.length > 0
|
|
398
|
-
&& Array.isArray(value.args)
|
|
399
|
-
&& value.args.every((arg) => typeof arg === "string")
|
|
400
|
-
&& typeof value.version === "string"
|
|
401
|
-
&& value.version.length > 0;
|
|
402
|
-
}
|
|
403
392
|
function isRecord(value) {
|
|
404
393
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
405
394
|
}
|
package/dist/cli/updatePorts.js
CHANGED
|
@@ -25,11 +25,13 @@
|
|
|
25
25
|
* mismatch fails closed.
|
|
26
26
|
*/
|
|
27
27
|
import { spawnSync } from "node:child_process";
|
|
28
|
-
import { accessSync, constants, existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync } from "node:fs";
|
|
28
|
+
import { accessSync, constants, existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, statSync } from "node:fs";
|
|
29
29
|
import { delimiter, dirname, isAbsolute, join, resolve } from "node:path";
|
|
30
30
|
import { fileURLToPath } from "node:url";
|
|
31
31
|
import { runtimeError } from "../errors/cliError.js";
|
|
32
32
|
import { isConcreteVersion } from "../domain/validation.js";
|
|
33
|
+
import { parseControllerIdentity } from "../core/controllerIdentity.js";
|
|
34
|
+
import { isMinorStorageUpgrade, isStorageVersion, storageVersionParts } from "../storage/storageVersions.js";
|
|
33
35
|
import { STORAGE_DOCTOR_CHECK_NAMES } from "../doctor/doctor.js";
|
|
34
36
|
import { acquireHandoverLock } from "../release/runtimeRelease.js";
|
|
35
37
|
import { updateStagingRoot } from "../storage/homeLayout.js";
|
|
@@ -507,18 +509,16 @@ function restoreControllerIdentity(home, identity, environment, spawn) {
|
|
|
507
509
|
"const { spawn } = require('node:child_process');",
|
|
508
510
|
"const values = process.argv.slice(1);",
|
|
509
511
|
"const handoverOwnerPid = Number(values.pop());",
|
|
510
|
-
"const
|
|
511
|
-
"const args = JSON.parse(values.pop());",
|
|
512
|
-
"const executable = values.pop();",
|
|
512
|
+
"const identity = JSON.parse(values.pop());",
|
|
513
513
|
"const home = values.pop();",
|
|
514
514
|
"const runtimeModule = values.pop();",
|
|
515
515
|
"(async () => {",
|
|
516
516
|
" const { ensureFileTaskControllerIdentity } = await import(runtimeModule);",
|
|
517
|
-
" await ensureFileTaskControllerIdentity(home,
|
|
517
|
+
" await ensureFileTaskControllerIdentity(home, identity, {",
|
|
518
518
|
" environment: process.env,",
|
|
519
519
|
" handoverOwnerPid,",
|
|
520
520
|
" spawnController: (_home, launchEnv) => {",
|
|
521
|
-
" const child = spawn(
|
|
521
|
+
" const child = spawn(identity.executablePath, identity.args, { detached: true, stdio: 'ignore', env: launchEnv });",
|
|
522
522
|
" child.unref();",
|
|
523
523
|
" }",
|
|
524
524
|
" });",
|
|
@@ -529,9 +529,7 @@ function restoreControllerIdentity(home, identity, environment, spawn) {
|
|
|
529
529
|
helper,
|
|
530
530
|
UPDATE_CLIENT_RUNTIME_PATH,
|
|
531
531
|
home,
|
|
532
|
-
identity
|
|
533
|
-
JSON.stringify(identity.args),
|
|
534
|
-
identity.version,
|
|
532
|
+
JSON.stringify(parseControllerIdentity(identity)),
|
|
535
533
|
String(process.pid)
|
|
536
534
|
], { cwd: process.cwd(), env: launchEnvironment, shell: false, stdio: "pipe" });
|
|
537
535
|
assertSpawnOk(result, "restore the previously running Controller identity");
|
|
@@ -656,21 +654,6 @@ function structuredErrorMessage(result) {
|
|
|
656
654
|
}
|
|
657
655
|
return undefined;
|
|
658
656
|
}
|
|
659
|
-
function parseControllerIdentity(value) {
|
|
660
|
-
if (typeof value.executablePath !== "string"
|
|
661
|
-
|| value.executablePath.length === 0
|
|
662
|
-
|| !Array.isArray(value.args)
|
|
663
|
-
|| value.args.some((arg) => typeof arg !== "string")
|
|
664
|
-
|| typeof value.version !== "string"
|
|
665
|
-
|| value.version.length === 0) {
|
|
666
|
-
throw new Error("Authenticated Controller identity is malformed; treating ownership as unknown-active.");
|
|
667
|
-
}
|
|
668
|
-
return {
|
|
669
|
-
executablePath: value.executablePath,
|
|
670
|
-
args: value.args,
|
|
671
|
-
version: value.version
|
|
672
|
-
};
|
|
673
|
-
}
|
|
674
657
|
function controllerErrorCodeFromResult(result) {
|
|
675
658
|
for (const buffer of [result.stdout, result.stderr]) {
|
|
676
659
|
try {
|
|
@@ -775,23 +758,22 @@ function assertActivatedControllerIdentity(identity, activatedBinary, activatedV
|
|
|
775
758
|
* entrypoint derivation as the production startup identity check (P1-1, rr23).
|
|
776
759
|
*/
|
|
777
760
|
export function activatedControllerEntrypoint(activatedBinary) {
|
|
778
|
-
|
|
779
|
-
try {
|
|
780
|
-
resolvedBinary = realpathSync(activatedBinary);
|
|
781
|
-
}
|
|
782
|
-
catch {
|
|
783
|
-
// `verify` already checked existsSync. Keep the fallback deterministic for
|
|
784
|
-
// test seams and fail closed later if the identity does not match it.
|
|
785
|
-
resolvedBinary = resolve(activatedBinary);
|
|
786
|
-
}
|
|
761
|
+
const resolvedBinary = realpathSync(activatedBinary);
|
|
787
762
|
const direct = join(dirname(resolvedBinary), "controller", "controllerMain.js");
|
|
788
|
-
if (existsSync(direct))
|
|
789
|
-
return direct;
|
|
790
763
|
// npm may expose a non-symlink launcher under <prefix>/bin. Resolve the
|
|
791
764
|
// package's canonical global layout when it is present.
|
|
792
765
|
const prefix = resolve(dirname(resolvedBinary), "..");
|
|
793
766
|
const packageEntrypoint = join(prefix, "lib", "node_modules", PACKAGE_NAME, "dist", "controller", "controllerMain.js");
|
|
794
|
-
|
|
767
|
+
for (const candidate of [direct, packageEntrypoint]) {
|
|
768
|
+
if (!existsSync(candidate))
|
|
769
|
+
continue;
|
|
770
|
+
const entrypoint = realpathSync(candidate);
|
|
771
|
+
if (!statSync(entrypoint).isFile()) {
|
|
772
|
+
throw runtimeError(`Controller entrypoint is not a regular file: ${entrypoint}.`);
|
|
773
|
+
}
|
|
774
|
+
return entrypoint;
|
|
775
|
+
}
|
|
776
|
+
throw runtimeError(`Controller entrypoint is missing for the activated binary: ${resolvedBinary}.`);
|
|
795
777
|
}
|
|
796
778
|
function interpretPreflight(result) {
|
|
797
779
|
// Require a valid `{ ok:true, data }` success envelope before trusting any
|
|
@@ -841,7 +823,6 @@ function interpretPreflight(result) {
|
|
|
841
823
|
action: "Use a staged binary that supports the update-preflight contract; do not force the update."
|
|
842
824
|
};
|
|
843
825
|
}
|
|
844
|
-
const blockers = parseUpdateBlockers(data.blockers);
|
|
845
826
|
return {
|
|
846
827
|
status: "blocked",
|
|
847
828
|
stage: typeof data.stage === "string" ? data.stage : "preflight",
|
|
@@ -849,8 +830,6 @@ function interpretPreflight(result) {
|
|
|
849
830
|
action: typeof data.action === "string"
|
|
850
831
|
? data.action
|
|
851
832
|
: "Resolve the reported condition and retry.",
|
|
852
|
-
...(blockers === undefined ? {} : { blockers }),
|
|
853
|
-
...(typeof data.retryCommand === "string" ? { retryCommand: data.retryCommand } : {}),
|
|
854
833
|
...(data.sceneUnchanged === true ? { sceneUnchanged: true } : {})
|
|
855
834
|
};
|
|
856
835
|
}
|
|
@@ -864,9 +843,13 @@ function parseUpdatePreflightResult(data) {
|
|
|
864
843
|
const homeClassification = data.classification;
|
|
865
844
|
if (!isRecord(homeClassification) || !isRecord(homeClassification.classification))
|
|
866
845
|
return null;
|
|
846
|
+
const from = homeClassification.storageVersion;
|
|
847
|
+
const to = homeClassification.currentStorageVersion;
|
|
848
|
+
if (!isStorageVersion(from) || !isStorageVersion(to))
|
|
849
|
+
return null;
|
|
867
850
|
const classification = homeClassification.classification;
|
|
868
851
|
if (data.status === "already-current") {
|
|
869
|
-
if (data.stepCount !== 0
|
|
852
|
+
if (data.stepCount !== 0 || from !== to
|
|
870
853
|
|| classification.verdict !== "USABLE"
|
|
871
854
|
|| classification.status !== "current")
|
|
872
855
|
return null;
|
|
@@ -875,8 +858,17 @@ function parseUpdatePreflightResult(data) {
|
|
|
875
858
|
if (data.stepCount <= 0
|
|
876
859
|
|| classification.verdict !== "MIGRATABLE"
|
|
877
860
|
|| classification.status !== "migration-ready"
|
|
861
|
+
|| !isMinorStorageUpgrade(from, to)
|
|
878
862
|
|| !data.steps.every(isStorageMigrationStep))
|
|
879
863
|
return null;
|
|
864
|
+
let previous = from;
|
|
865
|
+
for (const step of data.steps) {
|
|
866
|
+
if (step.fromVersion !== previous)
|
|
867
|
+
return null;
|
|
868
|
+
previous = step.toVersion;
|
|
869
|
+
}
|
|
870
|
+
if (previous !== to)
|
|
871
|
+
return null;
|
|
880
872
|
return { status: "migration-ready", stepCount: data.stepCount };
|
|
881
873
|
}
|
|
882
874
|
function interpretStorageMigration(result) {
|
|
@@ -912,38 +904,13 @@ function interpretStorageMigration(result) {
|
|
|
912
904
|
}
|
|
913
905
|
function isStorageMigrationStep(value) {
|
|
914
906
|
return isRecord(value)
|
|
915
|
-
&&
|
|
916
|
-
&&
|
|
917
|
-
&& value.
|
|
907
|
+
&& isStorageVersion(value.fromVersion)
|
|
908
|
+
&& isStorageVersion(value.toVersion)
|
|
909
|
+
&& isMinorStorageUpgrade(value.fromVersion, value.toVersion)
|
|
910
|
+
&& storageVersionParts(value.toVersion).minor === storageVersionParts(value.fromVersion).minor + 1
|
|
918
911
|
&& typeof value.name === "string"
|
|
919
912
|
&& value.name.length > 0;
|
|
920
913
|
}
|
|
921
|
-
function parseUpdateBlockers(value) {
|
|
922
|
-
if (value === undefined)
|
|
923
|
-
return undefined;
|
|
924
|
-
if (!Array.isArray(value))
|
|
925
|
-
return undefined;
|
|
926
|
-
const parsed = [];
|
|
927
|
-
for (const item of value) {
|
|
928
|
-
if (!isRecord(item) || typeof item.reason !== "string" || item.reason.length === 0) {
|
|
929
|
-
return undefined;
|
|
930
|
-
}
|
|
931
|
-
const optional = ["taskId", "roleName", "runId", "nativeSessionId"];
|
|
932
|
-
if (optional.some((key) => item[key] !== undefined && typeof item[key] !== "string")) {
|
|
933
|
-
return undefined;
|
|
934
|
-
}
|
|
935
|
-
parsed.push({
|
|
936
|
-
...(typeof item.taskId === "string" ? { taskId: item.taskId } : {}),
|
|
937
|
-
...(typeof item.roleName === "string" ? { roleName: item.roleName } : {}),
|
|
938
|
-
...(typeof item.runId === "string" ? { runId: item.runId } : {}),
|
|
939
|
-
...(typeof item.nativeSessionId === "string"
|
|
940
|
-
? { nativeSessionId: item.nativeSessionId }
|
|
941
|
-
: {}),
|
|
942
|
-
reason: item.reason
|
|
943
|
-
});
|
|
944
|
-
}
|
|
945
|
-
return parsed;
|
|
946
|
-
}
|
|
947
914
|
/**
|
|
948
915
|
* Extract the validated `data` object from a spawn result's `{ ok: true, data }`
|
|
949
916
|
* JSON envelope, or `null` when the result is not a trustworthy success envelope.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** `yui upgrade` plans or applies the supported linear storage migration chain. */
|
|
2
2
|
import { runStorageUpgrade } from "../storage/upgrade/upgradeOrchestrator.js";
|
|
3
3
|
import { ensureFileTaskController, ensureFileTaskControllerIdentity, stopFileTaskController } from "../controller/clientRuntime.js";
|
|
4
|
+
import { parseControllerIdentity } from "../core/controllerIdentity.js";
|
|
4
5
|
import { callController, ControllerClientError } from "../core/controllerClient.js";
|
|
5
6
|
import { spawn } from "node:child_process";
|
|
6
7
|
import { runtimeError, usageError } from "../errors/cliError.js";
|
|
@@ -99,16 +100,11 @@ async function captureUpgradeController(home) {
|
|
|
99
100
|
return undefined;
|
|
100
101
|
throw error;
|
|
101
102
|
}
|
|
102
|
-
const
|
|
103
|
-
if (!Number.isSafeInteger(
|
|
104
|
-
|| typeof identity.executablePath !== "string" || !identity.executablePath
|
|
105
|
-
|| !Array.isArray(identity.args) || identity.args.some(arg => typeof arg !== "string")
|
|
106
|
-
|| typeof identity.version !== "string" || !identity.version) {
|
|
103
|
+
const pid = value?.pid;
|
|
104
|
+
if (!Number.isSafeInteger(pid) || pid < 1) {
|
|
107
105
|
throw runtimeError("Cannot capture the exact Controller for a reversible upgrade preflight.");
|
|
108
106
|
}
|
|
109
|
-
return { pid:
|
|
110
|
-
executablePath: identity.executablePath, args: identity.args, version: identity.version
|
|
111
|
-
} };
|
|
107
|
+
return { pid: pid, identity: parseControllerIdentity(value) };
|
|
112
108
|
}
|
|
113
109
|
async function runUpdateOwnedUpgrade(home, environment) {
|
|
114
110
|
const ownerText = environment.YUI_UPDATE_HANDOVER_OWNER_PID;
|
package/dist/cli.js
CHANGED
|
@@ -100,7 +100,7 @@ import { requireManagedTaskCaller, resolveManagedTaskReader } from "./runtime/ma
|
|
|
100
100
|
import { assertRuntimeCoherence } from "./runtime/runtimeCoherence.js";
|
|
101
101
|
import { runSetupCommand, validateSetupInvocation } from "./setup/setupCommand.js";
|
|
102
102
|
import { openCurrentTaskStore, validateCurrentTaskStore } from "./storage/currentTaskStore.js";
|
|
103
|
-
import {
|
|
103
|
+
import { SqliteSchemaError } from "./storage/sqliteSchema.js";
|
|
104
104
|
import { inspectStorageSchema } from "./storage/storageSchema.js";
|
|
105
105
|
import { resolveYuiHome } from "./storage/taskStore.js";
|
|
106
106
|
import { renderArchiveDiagnostics, taskArchiveDiagnostics } from "./task/archiveDiagnostics.js";
|
|
@@ -141,7 +141,7 @@ void main().catch((error) => {
|
|
|
141
141
|
});
|
|
142
142
|
function runtimeFailureMessage(error) {
|
|
143
143
|
const message = error instanceof Error ? error.message : String(error);
|
|
144
|
-
if (!(error instanceof
|
|
144
|
+
if (!(error instanceof SqliteSchemaError))
|
|
145
145
|
return message;
|
|
146
146
|
try {
|
|
147
147
|
return `${message}\n${describeCliHomeInvocation({
|
|
@@ -542,12 +542,9 @@ export async function main() {
|
|
|
542
542
|
validateCurrentTaskStore(home);
|
|
543
543
|
const controllerMethod = method;
|
|
544
544
|
const updateHandoverOwner = process.env.YUI_UPDATE_HANDOVER_OWNER_PID;
|
|
545
|
-
//
|
|
546
|
-
// activated restart child, but it remains that child's direct parent while
|
|
547
|
-
// holding the exact handover lock. Inherit only that OS-backed relationship;
|
|
548
|
-
// every unrelated live lock still compares foreign and fails closed.
|
|
545
|
+
// Maintenance children must explicitly name their exact lock owner.
|
|
549
546
|
const updateHandoverOwnerPid = updateHandoverOwner === undefined
|
|
550
|
-
?
|
|
547
|
+
? undefined
|
|
551
548
|
: Number(updateHandoverOwner);
|
|
552
549
|
if (updateHandoverOwnerPid !== undefined
|
|
553
550
|
&& (!Number.isSafeInteger(updateHandoverOwnerPid) || updateHandoverOwnerPid < 1)) {
|
|
@@ -49,7 +49,7 @@ export function renderControllerResourceStatus(snapshot, verbose, width = defaul
|
|
|
49
49
|
{ header: "Reason", minWidth: 12, maxWidth: 30 }
|
|
50
50
|
], visibleDomains.map((domain) => [
|
|
51
51
|
domain.yuiHome,
|
|
52
|
-
domain.domainKind ?? "
|
|
52
|
+
domain.domainKind ?? "unmarked",
|
|
53
53
|
domain.liveness === undefined
|
|
54
54
|
? "—"
|
|
55
55
|
: `${domain.liveness}/${domain.disposition ?? "review"}`,
|
|
@@ -4916,7 +4916,14 @@ function retryRunOperation(args, store, options) {
|
|
|
4916
4916
|
const now = clock(options);
|
|
4917
4917
|
const previous = store.transaction((tx) => {
|
|
4918
4918
|
const run = requireRun(tx, args[0], options);
|
|
4919
|
-
|
|
4919
|
+
// Review, synthesis and bounded automatic recovery reuse exact evidence.
|
|
4920
|
+
// Explicit ordinary retries below
|
|
4921
|
+
// create a new Run and Snapshot from current authorized facts; missing old
|
|
4922
|
+
// evidence must not prevent that explicit recovery.
|
|
4923
|
+
if (run.purpose === "review" || run.sourceExecutionGroupId !== undefined
|
|
4924
|
+
|| options.providerRetryChainId !== undefined) {
|
|
4925
|
+
readRunContextSnapshot(tx, run);
|
|
4926
|
+
}
|
|
4920
4927
|
return run;
|
|
4921
4928
|
});
|
|
4922
4929
|
if (previous.purpose === "review") {
|
|
@@ -446,7 +446,7 @@ function projectTaskRoleRuntime(run, session, tmux, events, _mailbox, store, tas
|
|
|
446
446
|
const fence = { ...basicFence, ...(nativeTurnId === undefined ? {} : { nativeTurnId }) };
|
|
447
447
|
let projection = projectRuntimeTaskEvents(fence, createdAt, events);
|
|
448
448
|
projection = projectRuntimeObservation(projection, createRuntimeObservation({
|
|
449
|
-
schemaVersion:
|
|
449
|
+
schemaVersion: 1,
|
|
450
450
|
eventId: `runtime-host-${receiptId}`,
|
|
451
451
|
semanticKey: `runtime-host-${receiptId}`,
|
|
452
452
|
kind: "host.observed",
|
|
@@ -36,7 +36,7 @@ export function validateRunInput(value) {
|
|
|
36
36
|
/** Execution must never substitute current Task facts for missing frozen evidence. */
|
|
37
37
|
export function requireRunContextSnapshotRef(input) {
|
|
38
38
|
if (input.contextSnapshotRef === undefined) {
|
|
39
|
-
throw new Error("AgentRun Context Snapshot is required for execution
|
|
39
|
+
throw new Error("AgentRun Context Snapshot is required for this execution. Inspect the Run and retire or explicitly retry it; original evidence is never reconstructed.");
|
|
40
40
|
}
|
|
41
41
|
return validateContextSnapshotRef(input.contextSnapshotRef);
|
|
42
42
|
}
|
|
@@ -102,7 +102,7 @@ export class AgentRuntimeObserver {
|
|
|
102
102
|
? tokenObservationIdentity("baseline", fence, source.sourceId, baselineKey)
|
|
103
103
|
: usageObservationIdentity(fence, source.sourceId, latestOccurrence);
|
|
104
104
|
this.inbox.enqueueObservation(createRuntimeObservation({
|
|
105
|
-
schemaVersion:
|
|
105
|
+
schemaVersion: 1,
|
|
106
106
|
eventId: identity.eventId,
|
|
107
107
|
semanticKey: identity.semanticKey,
|
|
108
108
|
kind: "activity.observed",
|
|
@@ -137,7 +137,7 @@ export class AgentRuntimeObserver {
|
|
|
137
137
|
const health = JSON.stringify([sample.status, sample.detail ?? null]);
|
|
138
138
|
if (state.health !== health) {
|
|
139
139
|
this.inbox.enqueueObservation(createRuntimeObservation({
|
|
140
|
-
schemaVersion:
|
|
140
|
+
schemaVersion: 1,
|
|
141
141
|
eventId: observationId("health", fence, source.sourceId, health),
|
|
142
142
|
semanticKey: observationId("health", fence, source.sourceId, health),
|
|
143
143
|
kind: "observer.health",
|
|
@@ -183,7 +183,7 @@ export class AgentRuntimeObserver {
|
|
|
183
183
|
usages.forEach((occurrence, usageIndex) => {
|
|
184
184
|
const identity = usageObservationIdentity(fence, source.sourceId, occurrence);
|
|
185
185
|
this.inbox.enqueueObservation(createRuntimeObservation({
|
|
186
|
-
schemaVersion:
|
|
186
|
+
schemaVersion: 1,
|
|
187
187
|
eventId: identity.eventId,
|
|
188
188
|
semanticKey: identity.semanticKey,
|
|
189
189
|
kind: "activity.observed",
|
|
@@ -213,7 +213,7 @@ export class AgentRuntimeObserver {
|
|
|
213
213
|
});
|
|
214
214
|
if (activityChanged && state.cursor !== undefined) {
|
|
215
215
|
this.inbox.enqueueObservation(createRuntimeObservation({
|
|
216
|
-
schemaVersion:
|
|
216
|
+
schemaVersion: 1,
|
|
217
217
|
eventId: observationId("activity", fence, source.sourceId, sample.activityId),
|
|
218
218
|
semanticKey: observationId("activity", fence, source.sourceId, sample.activityId),
|
|
219
219
|
kind: "activity.observed",
|