@zq-silk/yui 0.15.9 → 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/dist/agent/launchEnvironment.js +7 -0
- package/dist/cli/commandCatalog.js +26 -6
- 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 +336 -93
- package/dist/commands/globalRoleCommands.js +314 -4
- package/dist/commands/projectCommands.js +6 -7
- package/dist/commands/releaseCommands.js +18 -0
- package/dist/commands/taskActor.js +25 -0
- package/dist/commands/taskCommands.js +511 -83
- 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/taskContext.js +38 -2
- package/dist/controller/agentHostObservation.js +155 -0
- package/dist/controller/clientRuntime.js +17 -2
- package/dist/controller/controller.js +3 -0
- 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/message/globalInterrupt.js +33 -0
- package/dist/message/inputControlResolution.js +106 -0
- package/dist/message/message.js +367 -0
- package/dist/message/messageContinuation.js +126 -3
- package/dist/message/taskInterrupt.js +34 -0
- package/dist/observability/orchestrationMetrics.js +1 -1
- 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/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 +26 -8
- package/dist/storage/homeLayout.js +130 -0
- package/dist/storage/migrations/collapseWorktreeLayout.js +963 -0
- package/dist/storage/migrations/integrationContinuation.js +104 -0
- package/dist/storage/migrations/unifyHomeLayout.js +925 -0
- package/dist/storage/sqliteSchema.js +136 -4
- package/dist/storage/sqliteStore.js +40 -1
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +1 -0
- package/dist/storage/upgrade/upgradeOrchestrator.js +95 -2
- package/dist/task/archiveDiagnostics.js +128 -0
- package/dist/task/nextAction.js +44 -11
- package/dist/web/assets/client/app.js +55 -0
- 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 +106 -0
- 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 +117 -1
- package/dist/web/webSnapshot.js +4 -3
- package/dist/web/webTaskSurface.js +222 -5
- package/dist/workspace/workItemChangeSetManager.js +18 -2
- package/docs/release-workflow.md +39 -0
- package/docs/release-workflow.zh-CN.md +29 -0
- package/docs/sqlite-control-plane-design.md +223 -1
- package/docs/testing/verification-levels.md +24 -0
- package/docs/testing/verification-levels.zh-CN.md +11 -0
- package/package.json +1 -1
- package/skills/yui-leader/references/execution.md +151 -49
- package/skills/yui-leader/references/integration.md +52 -2
- package/skills/yui-operator/SKILL.md +6 -1
- package/skills/yui-runtime/SKILL.md +27 -0
- package/skills/yui-runtime/references/publication.md +20 -0
|
@@ -122,6 +122,13 @@ export function operationalAgentEnvironment(adapterId, source) {
|
|
|
122
122
|
|| `${dirname(process.execPath)}:/usr/local/bin:/usr/bin:/bin`,
|
|
123
123
|
HOME: source.HOME || homedir(),
|
|
124
124
|
TERM: usableInteractiveTerminal(source.TERM),
|
|
125
|
+
// Base fallback only. Every managed Task launch has its isolation
|
|
126
|
+
// environment (TMPDIR = the Home-side runtime `roots.temporary`) assigned
|
|
127
|
+
// OVER this by the launch planner, so this branch is reached only by
|
|
128
|
+
// non-isolated global/ad-hoc launches with no managed runtime. Those run in
|
|
129
|
+
// the operator's own context, where their inherited TMPDIR (or the system
|
|
130
|
+
// temp) is the correct scratch — forcing it under control Home would
|
|
131
|
+
// misroute user scratch into Yui-managed storage.
|
|
125
132
|
TMPDIR: source.TMPDIR || tmpdir()
|
|
126
133
|
};
|
|
127
134
|
}
|
|
@@ -408,8 +408,8 @@ const taskChildren = [
|
|
|
408
408
|
},
|
|
409
409
|
{
|
|
410
410
|
name: "archive",
|
|
411
|
-
summary: "Archive a Task
|
|
412
|
-
usage: "yui task archive <id> (--integrated [--force]
|
|
411
|
+
summary: "Archive a terminal Task; explicit --force commits despite delivery/cleanup warnings, retaining unsafe resources.",
|
|
412
|
+
usage: "yui task archive <id> (--integrated|--abandon) [--force]",
|
|
413
413
|
options: ["--integrated", "--abandon", "--force"]
|
|
414
414
|
},
|
|
415
415
|
{ name: "reconcile", summary: "Run one immediate Controller reconciliation.", usage: "yui task reconcile <id>" },
|
|
@@ -435,7 +435,7 @@ const taskChildren = [
|
|
|
435
435
|
{
|
|
436
436
|
name: "message",
|
|
437
437
|
summary: "Manage durable Task messages.",
|
|
438
|
-
sections: [{ id: "manage", title: "Commands", entries: ["send", "handoff", "list", "show", "update", "retire"] }],
|
|
438
|
+
sections: [{ id: "manage", title: "Commands", entries: ["send", "queue", "steer", "handoff", "list", "show", "update", "retire"] }],
|
|
439
439
|
children: [
|
|
440
440
|
{
|
|
441
441
|
name: "handoff",
|
|
@@ -454,6 +454,20 @@ const taskChildren = [
|
|
|
454
454
|
},
|
|
455
455
|
fileOptions: ["--body-file"]
|
|
456
456
|
},
|
|
457
|
+
{
|
|
458
|
+
name: "queue",
|
|
459
|
+
summary: "Queue an input for delivery at the recipient's next legal opportunity (idempotent by request id).",
|
|
460
|
+
usage: "yui task message queue <id> (<body>|--body-file <path|->) --request-id <id> [--to <role> --work-item <id>|--review-round <id>]",
|
|
461
|
+
options: ["--body-file", "--request-id", "--to", "--work-item", "--review-round"],
|
|
462
|
+
fileOptions: ["--body-file"]
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
name: "steer",
|
|
466
|
+
summary: "Steer only a Role's exact current native Turn; saved and reported without fallback when unsupported.",
|
|
467
|
+
usage: "yui task message steer <id> (<body>|--body-file <path|->) --request-id <id> --expected-target <turn> --to <role> [--work-item <id>|--review-round <id>]",
|
|
468
|
+
options: ["--body-file", "--request-id", "--expected-target", "--to", "--work-item", "--review-round"],
|
|
469
|
+
fileOptions: ["--body-file"]
|
|
470
|
+
},
|
|
457
471
|
{
|
|
458
472
|
name: "list",
|
|
459
473
|
summary: "List Task messages.",
|
|
@@ -622,7 +636,7 @@ const taskChildren = [
|
|
|
622
636
|
summary: "Manage Roles within a Task.",
|
|
623
637
|
sections: [{ id: "manage", title: "Commands", entries: [
|
|
624
638
|
"add", "list", "status", "show", "update", "remove", "bind", "unbind",
|
|
625
|
-
"session", "view", "takeover", "release"
|
|
639
|
+
"session", "interrupt", "view", "takeover", "release"
|
|
626
640
|
] }],
|
|
627
641
|
children: [
|
|
628
642
|
{
|
|
@@ -680,6 +694,12 @@ const taskChildren = [
|
|
|
680
694
|
summary: "Attach read-only to an independent Provider presentation surface.",
|
|
681
695
|
usage: "yui task role view <task> <role>"
|
|
682
696
|
},
|
|
697
|
+
{
|
|
698
|
+
name: "interrupt",
|
|
699
|
+
summary: "Interrupt a Role's exact current native Turn via native cancel; optionally deliver a saved Message once after a proven terminal.",
|
|
700
|
+
usage: "yui task role interrupt <task> <role> --expected-target <turn> [--then-message <task/message>] [--request-id <id>]",
|
|
701
|
+
options: ["--expected-target", "--then-message", "--request-id"]
|
|
702
|
+
},
|
|
683
703
|
{
|
|
684
704
|
name: "takeover",
|
|
685
705
|
summary: "Enter the PTY input gateway for an independent Provider process.",
|
|
@@ -706,7 +726,7 @@ const taskChildren = [
|
|
|
706
726
|
children: [
|
|
707
727
|
{
|
|
708
728
|
name: "create",
|
|
709
|
-
summary: "Create a work
|
|
729
|
+
summary: "Create a separately managed result; a coherent Task may use zero WorkItems. Omit --role for Leader-direct execution; --role (including leader) selects a managed AgentRun executor. Isolate writable direct work before editing, then submit, integrate and accept its Candidate.",
|
|
710
730
|
usage: "yui task work create <task> <title> [--project <project> ...] [--base-ref <project>=<ref> ...] [--objective <text>] [--accept <criterion> ...] [--after <work> ...] [--role <name>]",
|
|
711
731
|
options: ["--project", "--base-ref", "--objective", "--accept", "--after", "--role"]
|
|
712
732
|
},
|
|
@@ -922,7 +942,7 @@ const taskChildren = [
|
|
|
922
942
|
},
|
|
923
943
|
{
|
|
924
944
|
name: "abort",
|
|
925
|
-
summary: "Abandon
|
|
945
|
+
summary: "Abandon an unadvanced Integration while preserving evidence; reconcile an already-applied target.",
|
|
926
946
|
usage: "yui task integration abort <task>/<integration> --reason <text>",
|
|
927
947
|
options: ["--reason"]
|
|
928
948
|
},
|
|
@@ -574,9 +574,9 @@ export const INTERACTION_POLICIES = Object.freeze([
|
|
|
574
574
|
entity: "integration-attempt",
|
|
575
575
|
provider: "integration-attempts",
|
|
576
576
|
actionTarget: true,
|
|
577
|
-
...(command === "continue" ? { statuses: ["blocked", "validating"] } : {}),
|
|
578
|
-
...(command === "resolve" ? { statuses: ["blocked"] } : {}),
|
|
579
|
-
...(command === "abort" ? { statuses: ["running", "blocked"] } : {})
|
|
577
|
+
...(command === "continue" ? { statuses: ["running", "conflicted", "blocked", "validating"] } : {}),
|
|
578
|
+
...(command === "resolve" ? { statuses: ["blocked", "conflicted"] } : {}),
|
|
579
|
+
...(command === "abort" ? { statuses: ["running", "conflicted", "blocked", "validating"] } : {})
|
|
580
580
|
}],
|
|
581
581
|
...(command === "resolve"
|
|
582
582
|
? { trailingOptions: { "--option": "value", "--rationale": "value" } }
|
|
@@ -101,7 +101,30 @@ function runStagedUpdate(ports, staged, home) {
|
|
|
101
101
|
const captured = captureControllerLifecycle(ports, staged.version, home);
|
|
102
102
|
if ("outcome" in captured)
|
|
103
103
|
return captured;
|
|
104
|
-
|
|
104
|
+
// An older Controller can finish a launch between preflight and drain.
|
|
105
|
+
// Recheck after its exact stop, before changing the install or storage.
|
|
106
|
+
let fencedPreflight;
|
|
107
|
+
try {
|
|
108
|
+
fencedPreflight = ports.preflight(staged, home);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
return restoreControllerOrReport(ports, home, captured.lifecycle, {
|
|
112
|
+
outcome: "aborted", phase: "preflight",
|
|
113
|
+
message: `Quiesced compatibility preflight failed: ${messageOf(error)}`,
|
|
114
|
+
action: "The install and storage are unchanged; inspect the compatibility failure before retrying.",
|
|
115
|
+
recoverable: true, version: staged.version
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
if (fencedPreflight.status === "blocked") {
|
|
119
|
+
return restoreControllerOrReport(ports, home, captured.lifecycle, {
|
|
120
|
+
outcome: "aborted", phase: "preflight",
|
|
121
|
+
message: fencedPreflight.message, action: fencedPreflight.action,
|
|
122
|
+
recoverable: true, version: staged.version,
|
|
123
|
+
...(fencedPreflight.blockers === undefined ? {} : { blockers: fencedPreflight.blockers }),
|
|
124
|
+
...(fencedPreflight.sceneUnchanged === true ? { sceneUnchanged: true } : {})
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return activateAndVerify(ports, staged, home, captured.lifecycle, fencedPreflight);
|
|
105
128
|
}
|
|
106
129
|
finally {
|
|
107
130
|
releaseHandover?.();
|
package/dist/cli/updatePorts.js
CHANGED
|
@@ -25,14 +25,15 @@
|
|
|
25
25
|
* mismatch fails closed.
|
|
26
26
|
*/
|
|
27
27
|
import { spawnSync } from "node:child_process";
|
|
28
|
-
import { accessSync, constants, existsSync, mkdtempSync, readFileSync, realpathSync, rmSync } from "node:fs";
|
|
29
|
-
import { tmpdir } from "node:os";
|
|
28
|
+
import { accessSync, constants, existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync } from "node:fs";
|
|
30
29
|
import { delimiter, dirname, isAbsolute, join, resolve } from "node:path";
|
|
31
30
|
import { fileURLToPath } from "node:url";
|
|
32
31
|
import { runtimeError } from "../errors/cliError.js";
|
|
33
32
|
import { isConcreteVersion } from "../domain/validation.js";
|
|
34
33
|
import { STORAGE_DOCTOR_CHECK_NAMES } from "../doctor/doctor.js";
|
|
35
34
|
import { acquireHandoverLock } from "../release/runtimeRelease.js";
|
|
35
|
+
import { updateStagingRoot } from "../storage/homeLayout.js";
|
|
36
|
+
import { resolveYuiHome } from "../storage/taskStore.js";
|
|
36
37
|
const PACKAGE_NAME = "@zq-silk/yui";
|
|
37
38
|
const PACKAGE_SPEC = `${PACKAGE_NAME}@latest`;
|
|
38
39
|
function resolveExecutable(command, environmentPath) {
|
|
@@ -64,7 +65,7 @@ function failedSpawnResult(message) {
|
|
|
64
65
|
};
|
|
65
66
|
}
|
|
66
67
|
/** Build the real ports. `spawn` is injectable so tests avoid real installs. */
|
|
67
|
-
export function createUpdatePorts(environment, spawn = spawnSync, stagingRoot =
|
|
68
|
+
export function createUpdatePorts(environment, spawn = spawnSync, stagingRoot = updateStagingRoot(resolveYuiHome(environment))) {
|
|
68
69
|
// The production adapter must not let a later PATH change select a
|
|
69
70
|
// different npm during staging, activation, or recovery. Test doubles are
|
|
70
71
|
// intentionally left untouched so deterministic tests can dispatch on the
|
|
@@ -105,6 +106,9 @@ export function createUpdatePorts(environment, spawn = spawnSync, stagingRoot =
|
|
|
105
106
|
throw runtimeError(`Refusing to stage a non-concrete version (${String(version)}): only an exact `
|
|
106
107
|
+ "major.minor.patch version can be pinned for an update.");
|
|
107
108
|
}
|
|
109
|
+
// The Home staging parent may not exist yet on a Home that has never
|
|
110
|
+
// run an update; create it (private) before minting a throwaway prefix.
|
|
111
|
+
mkdirSync(stagingRoot, { recursive: true, mode: 0o700 });
|
|
108
112
|
const stagingPath = mkdtempSync(join(stagingRoot, "yui-update-stage-"));
|
|
109
113
|
let ownsStaging = true;
|
|
110
114
|
try {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/** `yui upgrade` plans or applies the supported linear storage migration chain. */
|
|
2
2
|
import { runStorageUpgrade } from "../storage/upgrade/upgradeOrchestrator.js";
|
|
3
|
-
import { ensureFileTaskController, stopFileTaskController } from "../controller/clientRuntime.js";
|
|
3
|
+
import { ensureFileTaskController, ensureFileTaskControllerIdentity, stopFileTaskController } from "../controller/clientRuntime.js";
|
|
4
|
+
import { callController, ControllerClientError } from "../core/controllerClient.js";
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
4
6
|
import { runtimeError, usageError } from "../errors/cliError.js";
|
|
5
7
|
import { acquireHandoverLock, isForeignHandoverLockHeld, isHandoverLockHeld } from "../release/runtimeRelease.js";
|
|
6
8
|
/**
|
|
@@ -43,12 +45,29 @@ async function runInteractiveUpgrade(home, environment) {
|
|
|
43
45
|
const handover = acquireHandoverLock(home);
|
|
44
46
|
let controllerWasRunning = false;
|
|
45
47
|
try {
|
|
48
|
+
const previous = await captureUpgradeController(home);
|
|
46
49
|
const stopped = await stopFileTaskController(home, {
|
|
47
50
|
environment,
|
|
48
|
-
handoverOwnerPid: process.pid
|
|
51
|
+
handoverOwnerPid: process.pid,
|
|
52
|
+
...(previous === undefined ? {} : { expectedPid: previous.pid })
|
|
49
53
|
});
|
|
50
54
|
controllerWasRunning = stopped.stopped;
|
|
51
55
|
const result = await runStorageUpgrade({ home, mode: "execute" });
|
|
56
|
+
if (controllerWasRunning && result.outcome === "blocked") {
|
|
57
|
+
if (previous === undefined) {
|
|
58
|
+
throw runtimeError("Upgrade was blocked without changing storage; the stopped Controller identity is unknown. Keep the Home quiesced.");
|
|
59
|
+
}
|
|
60
|
+
await ensureFileTaskControllerIdentity(home, previous.identity, {
|
|
61
|
+
environment, handoverOwnerPid: process.pid,
|
|
62
|
+
spawnController: (_home, launchEnvironment) => {
|
|
63
|
+
const child = spawn(previous.identity.executablePath, [...previous.identity.args], {
|
|
64
|
+
env: launchEnvironment, detached: true, stdio: "ignore"
|
|
65
|
+
});
|
|
66
|
+
child.unref();
|
|
67
|
+
return child.pid;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
52
71
|
if (controllerWasRunning
|
|
53
72
|
&& (result.outcome === "upgraded" || result.outcome === "already-current")) {
|
|
54
73
|
try {
|
|
@@ -70,6 +89,27 @@ async function runInteractiveUpgrade(home, environment) {
|
|
|
70
89
|
handover.release();
|
|
71
90
|
}
|
|
72
91
|
}
|
|
92
|
+
async function captureUpgradeController(home) {
|
|
93
|
+
let value;
|
|
94
|
+
try {
|
|
95
|
+
value = await callController(home, "controller.identity", {});
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
if (error instanceof ControllerClientError && error.code === "CONTROLLER_NOT_RUNNING")
|
|
99
|
+
return undefined;
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
const identity = value;
|
|
103
|
+
if (!Number.isSafeInteger(identity.pid) || identity.pid < 1
|
|
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) {
|
|
107
|
+
throw runtimeError("Cannot capture the exact Controller for a reversible upgrade preflight.");
|
|
108
|
+
}
|
|
109
|
+
return { pid: identity.pid, identity: {
|
|
110
|
+
executablePath: identity.executablePath, args: identity.args, version: identity.version
|
|
111
|
+
} };
|
|
112
|
+
}
|
|
73
113
|
async function runUpdateOwnedUpgrade(home, environment) {
|
|
74
114
|
const ownerText = environment.YUI_UPDATE_HANDOVER_OWNER_PID;
|
|
75
115
|
const ownerPid = ownerText === undefined ? Number.NaN : Number(ownerText);
|