@zq-silk/yui 0.15.3 → 0.15.6
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/managedRuntimeEnvironment.js +3 -0
- package/dist/cli.js +35 -127
- package/dist/commands/executionAuditCommands.js +6 -0
- package/dist/commands/taskContextCommand.js +4 -2
- package/dist/context/sessionBootstrapManifest.js +12 -21
- package/dist/controller/clientRuntime.js +1 -1
- package/dist/controller/fileSchedulerStoreAdapter.js +270 -162
- package/dist/controller/runtimeEventInbox.js +8 -0
- package/dist/controller/runtimeEventProcessor.js +31 -4
- package/dist/controller/runtimeHookTurnFence.js +101 -62
- package/dist/controller/runtimeLaunchCoordinator.js +38 -12
- package/dist/controller/runtimeObservationHook.js +17 -1
- package/dist/controller/structuredProviderObservation.js +39 -27
- package/dist/core/controllerClient.js +5 -0
- package/dist/core/controllerServer.js +7 -4
- package/dist/domain/agentResultTransport.js +2 -2
- package/dist/executor/agentExecutor.js +22 -38
- package/dist/executor/executorRegistry.js +16 -5
- package/dist/executor/fileRoleLaunchPlanner.js +22 -28
- package/dist/lifecycle/exactTurnTerminalization.js +3 -3
- package/dist/observability/executionAudit.js +12 -0
- package/dist/repository/executionLaneGitSnapshot.js +4 -3
- package/dist/repository/taskWorkspacePreparer.js +12 -4
- package/dist/review/taskFinalReviewContract.js +13 -32
- package/dist/runtime/agentError.js +299 -12
- package/dist/runtime/agentHost.js +419 -41
- package/dist/runtime/builtinAgentDrivers.js +5 -0
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/ports.js +16 -2
- package/dist/runtime/providerRuntimeIdentity.js +34 -28
- package/dist/runtime/runtimeCoherence.js +91 -0
- package/dist/runtime/runtimeObservation.js +8 -5
- package/dist/runtime/structuredProviderHost.js +53 -44
- package/dist/runtime/tmuxAdapters.js +72 -43
- package/dist/scheduler/activeRoleTurnDelivery.js +59 -11
- package/dist/scheduler/leaderWakeupProcessor.js +61 -7
- package/dist/storage/sqliteSchema.js +9 -0
- package/dist/storage/storageVersions.js +1 -1
- package/dist/turn/turn.js +7 -1
- package/package.json +1 -1
- package/dist/runtime/exactControlPlane.js +0 -232
|
@@ -20,6 +20,9 @@ export const YUI_MANAGED_RUNTIME_ENVIRONMENT_NAMES = Object.freeze([
|
|
|
20
20
|
"YUI_RUNTIME_GENERATION_ID",
|
|
21
21
|
"YUI_NATIVE_SESSION_ROOT",
|
|
22
22
|
"YUI_NATIVE_SESSION_ID",
|
|
23
|
+
// Retired: Yui no longer publishes a frozen control-plane descriptor into a
|
|
24
|
+
// managed process. Sessions launched by an earlier release still carry it, so
|
|
25
|
+
// it stays in this sanitize list.
|
|
23
26
|
"YUI_CONTROL_PLANE_DESCRIPTOR",
|
|
24
27
|
"YUI_TASK_RUNTIME_ISOLATION_DESCRIPTOR",
|
|
25
28
|
"YUI_TASK_RUNTIME_SERVICE_NAMESPACE",
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawnSync } from "node:child_process";
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
|
-
import {
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
5
|
import { createInterface } from "node:readline/promises";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { isDeepStrictEqual } from "node:util";
|
|
@@ -91,7 +91,7 @@ import { TmuxWebTerminalService } from "./web/tmuxWebTerminal.js";
|
|
|
91
91
|
import { listOperatorSessions, operatorSessionRef } from "./operator/operatorSessionHistory.js";
|
|
92
92
|
import { YUI_VERSION, yuiVersionIdentity } from "./version.js";
|
|
93
93
|
import { SqliteSchemaMigrationError } from "./storage/sqliteSchema.js";
|
|
94
|
-
import {
|
|
94
|
+
import { assertRuntimeCoherence, } from "./runtime/runtimeCoherence.js";
|
|
95
95
|
import { requireManagedTaskCaller } from "./runtime/managedCaller.js";
|
|
96
96
|
import { readSessionBootstrapManifest, refreshManagedSessionCliWrappers } from "./context/sessionBootstrapManifest.js";
|
|
97
97
|
import { createTaskFinalReviewContract, extractTaskFinalReviewRequest } from "./review/taskFinalReviewContract.js";
|
|
@@ -101,8 +101,7 @@ import { isCompletedTaskReviewEvidence } from "./review/reviewAcceptance.js";
|
|
|
101
101
|
import { NodeGitWorkspace } from "./repository/gitWorkspace.js";
|
|
102
102
|
import { currentWorkItemExecutionGroup } from "./workItem/workItem.js";
|
|
103
103
|
const VERSION = YUI_VERSION;
|
|
104
|
-
const
|
|
105
|
-
const taskFinalReviewInvocation = extractTaskFinalReviewRequest(exactControlInvocation.args);
|
|
104
|
+
const taskFinalReviewInvocation = extractTaskFinalReviewRequest(process.argv.slice(2));
|
|
106
105
|
const rawArgs = [...taskFinalReviewInvocation.args];
|
|
107
106
|
const jsonOutput = rawArgs.includes("--json");
|
|
108
107
|
const args = normalizeAliases(jsonOutput ? rawArgs.filter((argument) => argument !== "--json") : rawArgs);
|
|
@@ -140,8 +139,7 @@ export async function main() {
|
|
|
140
139
|
const home = resolveYuiHome(process.env);
|
|
141
140
|
const routedForFence = args.length === 0 ? undefined : routeInvocation(args);
|
|
142
141
|
const managedInvocation = process.env.YUI_SESSION_SCOPE === "task"
|
|
143
|
-
|| process.env.YUI_SESSION_SCOPE === "global"
|
|
144
|
-
|| process.env[YUI_CONTROL_PLANE_DESCRIPTOR] !== undefined;
|
|
142
|
+
|| process.env.YUI_SESSION_SCOPE === "global";
|
|
145
143
|
const homeFreeInvocation = args.length === 0
|
|
146
144
|
|| (args[0] === "version" && args.length === 1)
|
|
147
145
|
|| routedForFence?.kind === "help"
|
|
@@ -171,7 +169,7 @@ export async function main() {
|
|
|
171
169
|
process.exitCode = delegated.status ?? 5;
|
|
172
170
|
return;
|
|
173
171
|
}
|
|
174
|
-
const { contract: taskFinalReviewContract, verifiedStore
|
|
172
|
+
const { contract: taskFinalReviewContract, verifiedStore } = await preflightManagedTaskControlPlane();
|
|
175
173
|
if (args.length === 0) {
|
|
176
174
|
emit(renderCommandHelp((await import("./cli/commandCatalog.js")).ROOT_COMMAND, VERSION));
|
|
177
175
|
return;
|
|
@@ -315,7 +313,7 @@ export async function main() {
|
|
|
315
313
|
|| (process.env.YUI_SESSION_SCOPE === "global" && process.env.YUI_ROLE !== "operator")) {
|
|
316
314
|
throw usageError("Managed Session CLI refresh may be run only by the user or global Operator.");
|
|
317
315
|
}
|
|
318
|
-
const result = refreshManagedSessionCliWrappers(home,
|
|
316
|
+
const result = refreshManagedSessionCliWrappers(home, currentInvocationEntryPoint());
|
|
319
317
|
emit(`Refreshed ${result.refreshed} managed Session CLI wrapper(s); `
|
|
320
318
|
+ `${result.current} already current, ${result.skipped} skipped.`, false, result);
|
|
321
319
|
return;
|
|
@@ -1506,74 +1504,31 @@ function explicitReleaseActivationDriver() {
|
|
|
1506
1504
|
return resolveReleaseActivationDriver(resolveYuiHome(process.env), args[2], fileURLToPath(import.meta.url));
|
|
1507
1505
|
}
|
|
1508
1506
|
async function preflightManagedTaskControlPlane() {
|
|
1509
|
-
if (exactControlInvocation.error !== undefined) {
|
|
1510
|
-
throw new Error(exactControlInvocation.error);
|
|
1511
|
-
}
|
|
1512
1507
|
if (taskFinalReviewInvocation.error !== undefined) {
|
|
1513
1508
|
throw new Error(taskFinalReviewInvocation.error);
|
|
1514
1509
|
}
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
throw new Error("Exact control-plane invocation requires its frozen descriptor in a managed Task runtime.");
|
|
1519
|
-
}
|
|
1520
|
-
if (!exactAgentRuntime) {
|
|
1521
|
-
if (exactControlInvocation.digest !== undefined
|
|
1522
|
-
|| process.env.YUI_SESSION_SCOPE === "global") {
|
|
1523
|
-
return await preflightManagedGlobalControlPlane(exactControlInvocation.digest);
|
|
1510
|
+
if (process.env.YUI_SESSION_SCOPE !== "task") {
|
|
1511
|
+
if (process.env.YUI_SESSION_SCOPE === "global") {
|
|
1512
|
+
return await preflightManagedGlobalControlPlane();
|
|
1524
1513
|
}
|
|
1525
1514
|
if (taskFinalReviewInvocation.request !== undefined) {
|
|
1526
|
-
throw new Error("Task final-review contract
|
|
1515
|
+
throw new Error("Task final-review contract may only be established from the Task Leader's managed Session.");
|
|
1527
1516
|
}
|
|
1528
|
-
return { contract: undefined,
|
|
1517
|
+
return { contract: undefined, verifiedStore: undefined };
|
|
1529
1518
|
}
|
|
1530
|
-
if (process.env.YUI_SESSION_SCOPE !== "task") {
|
|
1531
|
-
throw new Error("Exact Task control-plane invocation requires a managed Task runtime.");
|
|
1532
|
-
}
|
|
1533
|
-
if (serializedControl === undefined) {
|
|
1534
|
-
throw new Error("Exact control-plane invocation is required for this managed Task runtime.");
|
|
1535
|
-
}
|
|
1536
|
-
const control = parseExactControlPlaneDescriptor(serializedControl);
|
|
1537
1519
|
const internalCallback = args[0] === "internal"
|
|
1538
1520
|
&& ["agent-host", "session-notify", "runtime-hook"].includes(args[1] ?? "");
|
|
1539
1521
|
const home = resolveYuiHome(process.env);
|
|
1540
|
-
assertManagedSessionManifest(home, "task");
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
const
|
|
1546
|
-
let commandControl;
|
|
1547
|
-
let digest;
|
|
1548
|
-
if (exactCommand) {
|
|
1549
|
-
digest = exactControlInvocation.digest ?? frozenDigest;
|
|
1550
|
-
await assertExactControlPlanePreflight({
|
|
1551
|
-
serializedDescriptor: serializedControl,
|
|
1552
|
-
digest,
|
|
1553
|
-
actualExecutable: process.execPath,
|
|
1554
|
-
actualCliEntry: fileURLToPath(import.meta.url),
|
|
1555
|
-
actualHome: home
|
|
1556
|
-
}, {
|
|
1557
|
-
// Provider callbacks must remain able to append their immutable inbox fact
|
|
1558
|
-
// while the Controller is offline. They still validate executable, CLI,
|
|
1559
|
-
// Home, schema, and the exact Task runtime envelope first.
|
|
1560
|
-
checkController: !internalCallback
|
|
1561
|
-
});
|
|
1562
|
-
commandControl = control;
|
|
1563
|
-
}
|
|
1564
|
-
else {
|
|
1565
|
-
await assertCompatibleControlPlanePreflight({ actualHome: home });
|
|
1566
|
-
// Package/build identity is deliberately not part of ordinary Session
|
|
1567
|
-
// continuity. Keep contracts on the Session's frozen digest while the
|
|
1568
|
-
// current CLI is proven protocol/storage compatible.
|
|
1569
|
-
commandControl = control;
|
|
1570
|
-
digest = frozenDigest;
|
|
1571
|
-
}
|
|
1572
|
-
const verifiedStore = openCurrentTaskStore(control.yuiHome);
|
|
1522
|
+
const manifest = assertManagedSessionManifest(home, "task");
|
|
1523
|
+
// One gate for every managed command: the current CLI, Home, and Controller
|
|
1524
|
+
// must agree. Internal callbacks must still be able to append their immutable
|
|
1525
|
+
// fact while the Controller is offline.
|
|
1526
|
+
await assertRuntimeCoherence({ actualHome: home }, { checkController: !internalCallback });
|
|
1527
|
+
const verifiedStore = openCurrentTaskStore(home);
|
|
1573
1528
|
// One authority for "may this process act as this Task Role?". Yui's own
|
|
1574
|
-
// internal callbacks run
|
|
1575
|
-
//
|
|
1576
|
-
//
|
|
1529
|
+
// internal callbacks run inside the Host process Yui itself launched; an Agent
|
|
1530
|
+
// command proves it is the Role's current runtime with its per-Session caller
|
|
1531
|
+
// key. Nothing here
|
|
1577
1532
|
// gates on the current Turn: which Turn is active is durable state that the
|
|
1578
1533
|
// command needing it reads, never a fact frozen into a process environment.
|
|
1579
1534
|
const runtime = internalCallback
|
|
@@ -1581,14 +1536,10 @@ async function preflightManagedTaskControlPlane() {
|
|
|
1581
1536
|
: requireManagedTaskCaller(verifiedStore, process.env);
|
|
1582
1537
|
const request = taskFinalReviewInvocation.request;
|
|
1583
1538
|
if (request === undefined) {
|
|
1584
|
-
return {
|
|
1585
|
-
contract: undefined,
|
|
1586
|
-
controlPlane: { digest, descriptor: commandControl },
|
|
1587
|
-
verifiedStore
|
|
1588
|
-
};
|
|
1539
|
+
return { contract: undefined, verifiedStore };
|
|
1589
1540
|
}
|
|
1590
1541
|
if (runtime === undefined || runtime.roleName !== "leader") {
|
|
1591
|
-
throw new Error("Only the
|
|
1542
|
+
throw new Error("Only the Task Leader's managed Session may establish a final-review contract.");
|
|
1592
1543
|
}
|
|
1593
1544
|
if (request.taskId !== runtime.taskId) {
|
|
1594
1545
|
throw new Error(`Task final-review contract Task id mismatch: expected ${runtime.taskId}, found ${request.taskId}.`);
|
|
@@ -1602,28 +1553,14 @@ async function preflightManagedTaskControlPlane() {
|
|
|
1602
1553
|
return {
|
|
1603
1554
|
contract: createTaskFinalReviewContract({
|
|
1604
1555
|
taskId: runtime.taskId,
|
|
1605
|
-
reviewerRoleName: request.reviewerRoleName
|
|
1606
|
-
// Once Task evidence establishes a contract, a compatible Session or
|
|
1607
|
-
// CLI replacement presents that same capability. Package/build identity
|
|
1608
|
-
// must not force a release rebind or invalidate delivery evidence.
|
|
1609
|
-
controlPlaneDigest: recordedContract?.controlPlaneDigest ?? digest
|
|
1556
|
+
reviewerRoleName: request.reviewerRoleName
|
|
1610
1557
|
}),
|
|
1611
|
-
controlPlane: { digest, descriptor: commandControl },
|
|
1612
1558
|
verifiedStore
|
|
1613
1559
|
};
|
|
1614
1560
|
}
|
|
1615
|
-
async function preflightManagedGlobalControlPlane(
|
|
1616
|
-
if (process.env.YUI_SESSION_SCOPE !== "global") {
|
|
1617
|
-
throw new Error("A control-plane digest was supplied outside a managed Session runtime. "
|
|
1618
|
-
+ "Run the command through the Role's own Session entry point, or use the "
|
|
1619
|
-
+ "ordinary yui command.");
|
|
1620
|
-
}
|
|
1561
|
+
async function preflightManagedGlobalControlPlane() {
|
|
1621
1562
|
if (taskFinalReviewInvocation.request !== undefined) {
|
|
1622
|
-
throw new Error("Task final-review contract
|
|
1623
|
-
}
|
|
1624
|
-
const manifestPath = process.env.YUI_SESSION_MANIFEST;
|
|
1625
|
-
if (manifestPath === undefined) {
|
|
1626
|
-
throw new Error("Managed global control-plane invocation requires its Session Manifest.");
|
|
1563
|
+
throw new Error("Task final-review contract may only be established from the Task Leader's managed Session.");
|
|
1627
1564
|
}
|
|
1628
1565
|
const home = resolveYuiHome(process.env);
|
|
1629
1566
|
const manifest = assertManagedSessionManifest(home, "global");
|
|
@@ -1632,47 +1569,18 @@ async function preflightManagedGlobalControlPlane(digest) {
|
|
|
1632
1569
|
|| manifest.roleKind !== expectedRoleKind) {
|
|
1633
1570
|
throw new Error("Managed global invocation does not match its Session Manifest.");
|
|
1634
1571
|
}
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
if (resolve(descriptor.yuiHome) !== resolve(home)) {
|
|
1638
|
-
throw new Error("Managed global control-plane descriptor belongs to another YUI_HOME.");
|
|
1639
|
-
}
|
|
1640
|
-
const frozenDigest = exactControlPlaneDigest(descriptor);
|
|
1641
|
-
if (frozenDigest !== manifest.controlPlane.digest
|
|
1642
|
-
|| resolve(manifest.controlPlane.descriptorPath) !== resolve(join(descriptor.yuiHome, "runtime", "control-plane", `${frozenDigest}.json`))
|
|
1643
|
-
|| resolve(manifestPath) !== resolve(join(descriptor.yuiHome, "runtime", "session-manifests", `${manifest.digest}.json`))) {
|
|
1644
|
-
throw new Error("Exact global Session Manifest does not match its control-plane descriptor.");
|
|
1645
|
-
}
|
|
1646
|
-
let commandControl;
|
|
1647
|
-
let commandDigest;
|
|
1648
|
-
if (digest !== undefined) {
|
|
1649
|
-
await assertExactControlPlanePreflight({
|
|
1650
|
-
serializedDescriptor,
|
|
1651
|
-
digest,
|
|
1652
|
-
actualExecutable: process.execPath,
|
|
1653
|
-
actualCliEntry: fileURLToPath(import.meta.url),
|
|
1654
|
-
actualHome: home
|
|
1655
|
-
});
|
|
1656
|
-
commandControl = descriptor;
|
|
1657
|
-
commandDigest = digest;
|
|
1658
|
-
}
|
|
1659
|
-
else {
|
|
1660
|
-
await assertCompatibleControlPlanePreflight({ actualHome: home });
|
|
1661
|
-
commandControl = currentInvocationControlPlane(home);
|
|
1662
|
-
commandDigest = exactControlPlaneDigest(commandControl);
|
|
1663
|
-
}
|
|
1664
|
-
return {
|
|
1665
|
-
contract: undefined,
|
|
1666
|
-
controlPlane: { digest: commandDigest, descriptor: commandControl },
|
|
1667
|
-
verifiedStore: openCurrentTaskStore(home)
|
|
1668
|
-
};
|
|
1572
|
+
await assertRuntimeCoherence({ actualHome: home });
|
|
1573
|
+
return { contract: undefined, verifiedStore: openCurrentTaskStore(home) };
|
|
1669
1574
|
}
|
|
1670
|
-
|
|
1671
|
-
|
|
1575
|
+
/**
|
|
1576
|
+
* The entry point the current invocation resolves to. Only its executable and
|
|
1577
|
+
* CLI path are used, to retarget managed Session wrappers at this installation.
|
|
1578
|
+
*/
|
|
1579
|
+
function currentInvocationEntryPoint() {
|
|
1580
|
+
return {
|
|
1672
1581
|
executable: process.execPath,
|
|
1673
|
-
cliEntry: fileURLToPath(import.meta.url)
|
|
1674
|
-
|
|
1675
|
-
});
|
|
1582
|
+
cliEntry: fileURLToPath(import.meta.url)
|
|
1583
|
+
};
|
|
1676
1584
|
}
|
|
1677
1585
|
function assertManagedSessionManifest(home, scope) {
|
|
1678
1586
|
const manifestPath = process.env.YUI_SESSION_MANIFEST;
|
|
@@ -189,6 +189,8 @@ export function renderExecutionAudit(report, width = defaultTableWidth()) {
|
|
|
189
189
|
{ header: "Role", minWidth: 8, maxWidth: 12 },
|
|
190
190
|
{ header: "Category", minWidth: 12, maxWidth: 20 },
|
|
191
191
|
{ header: "Code", minWidth: 16, maxWidth: 32 },
|
|
192
|
+
{ header: "Input", minWidth: 12, maxWidth: 14 },
|
|
193
|
+
{ header: "Registered", minWidth: 12, maxWidth: 14 },
|
|
192
194
|
{ header: "Session", minWidth: 12, maxWidth: 16 }
|
|
193
195
|
], errors.entries.map((entry) => [
|
|
194
196
|
entry.taskId,
|
|
@@ -196,6 +198,10 @@ export function renderExecutionAudit(report, width = defaultTableWidth()) {
|
|
|
196
198
|
entry.roleName,
|
|
197
199
|
entry.category,
|
|
198
200
|
entry.code,
|
|
201
|
+
entry.inputDisposition,
|
|
202
|
+
// Blank, not "unknown": the Host reports "unknown" as a real
|
|
203
|
+
// observation, and a reader must be able to tell the two apart.
|
|
204
|
+
entry.registrationDisposition ?? "—",
|
|
199
205
|
entry.sessionDisposition
|
|
200
206
|
]), width));
|
|
201
207
|
}
|
|
@@ -300,7 +300,7 @@ export function runTaskContextCommand(args, store, currentTaskReviewCandidate =
|
|
|
300
300
|
` Review policy: ${candidate.reviewPolicy === undefined ? "none" : `${candidate.reviewPolicy.roleName} (${candidate.reviewPolicy.trigger})`}`,
|
|
301
301
|
` Task-final contract: ${candidate.taskFinalReviewContract === undefined
|
|
302
302
|
? "none"
|
|
303
|
-
:
|
|
303
|
+
: `Reviewer ${candidate.taskFinalReviewContract.reviewerRoleName}`}`,
|
|
304
304
|
` Frozen Git: ${candidate.gitSnapshot === undefined
|
|
305
305
|
? "unavailable"
|
|
306
306
|
: `${candidate.gitSnapshot.reviewBaseCommit} (${candidate.gitSnapshot.projects.length} Projects)`}`,
|
|
@@ -442,7 +442,9 @@ function renderReviewRounds(rounds, executionGroupsById = new Map()) {
|
|
|
442
442
|
? [
|
|
443
443
|
` Frozen Task heads: ${latest.taskCandidate?.projects
|
|
444
444
|
.map(({ projectId, commit }) => `${projectId}@${commit}`).join(", ") ?? "unavailable"}`,
|
|
445
|
-
` Task-final contract: ${latest.taskFinalReviewContract
|
|
445
|
+
` Task-final contract: ${latest.taskFinalReviewContract === undefined
|
|
446
|
+
? "global policy"
|
|
447
|
+
: `Reviewer ${latest.taskFinalReviewContract.reviewerRoleName}`}`,
|
|
446
448
|
...(latest.deltaRecheck === undefined
|
|
447
449
|
? [` Review mode: full`]
|
|
448
450
|
: [
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { chmodSync, existsSync, readFileSync, readdirSync } from "node:fs";
|
|
3
3
|
import { dirname, join, resolve } from "node:path";
|
|
4
|
-
import { exactControlPlaneDigest, serializeExactDescriptor } from "../runtime/exactControlPlane.js";
|
|
5
4
|
import { writeTextFileAtomically } from "../storage/durableFile.js";
|
|
6
5
|
import { SESSION_BOOTSTRAP_MANIFEST_SCHEMA_VERSION, SESSION_CONTEXT_PROTOCOL, sessionManifestCompatibilityDigest } from "./sessionProtocolIdentity.js";
|
|
7
6
|
export { SESSION_BOOTSTRAP_MANIFEST_SCHEMA_VERSION, SESSION_CONTEXT_PROTOCOL, sessionManifestCompatibilityDigest } from "./sessionProtocolIdentity.js";
|
|
@@ -13,11 +12,11 @@ const ordinarySessionCli = ["#!/bin/sh", "exec yui \"$@\"", ""].join("\n");
|
|
|
13
12
|
* a Session legitimately outlives it, so embedding it here would turn an
|
|
14
13
|
* ordinary update into a broken Session.
|
|
15
14
|
*/
|
|
16
|
-
function renderSessionCli(
|
|
15
|
+
function renderSessionCli(entryPoint) {
|
|
17
16
|
return [
|
|
18
17
|
"#!/bin/sh",
|
|
19
|
-
`exec ${quoteShellWord(
|
|
20
|
-
+ `${quoteShellWord(
|
|
18
|
+
`exec ${quoteShellWord(entryPoint.executable)} `
|
|
19
|
+
+ `${quoteShellWord(entryPoint.cliEntry)} \"$@\"`,
|
|
21
20
|
""
|
|
22
21
|
].join("\n");
|
|
23
22
|
}
|
|
@@ -68,9 +67,7 @@ export function readSessionBootstrapManifest(path) {
|
|
|
68
67
|
throw new Error("Task Session Manifest owner is invalid.");
|
|
69
68
|
}
|
|
70
69
|
const control = record.controlPlane;
|
|
71
|
-
requireText(control.descriptorPath, "Session Manifest control descriptor path");
|
|
72
70
|
requireText(control.sessionCliPath, "Session Manifest CLI path");
|
|
73
|
-
requireDigest(control.digest, "Session Manifest control-plane digest");
|
|
74
71
|
for (const skill of record.skills) {
|
|
75
72
|
if (skill === null || typeof skill !== "object") {
|
|
76
73
|
throw new Error("Session Manifest Skill entry is invalid.");
|
|
@@ -92,15 +89,12 @@ export function readSessionBootstrapManifest(path) {
|
|
|
92
89
|
}
|
|
93
90
|
export function materializeSessionBootstrap(input) {
|
|
94
91
|
const home = resolve(input.yuiHome);
|
|
95
|
-
const controlDigest = exactControlPlaneDigest(input.controlPlane);
|
|
96
|
-
const descriptorPath = resolve(join(home, "runtime", "control-plane", `${controlDigest}.json`));
|
|
97
|
-
writeImmutableText(descriptorPath, `${serializeExactDescriptor(input.controlPlane)}\n`);
|
|
98
92
|
// Provider command runners may rebuild PATH independently of the managed
|
|
99
93
|
// process environment, so a bare `yui` could resolve to another install or
|
|
100
94
|
// Home. The wrapper pins the resolved entry point instead. Package identity
|
|
101
95
|
// stays out of it: the compatible continuity preflight and the Session's
|
|
102
96
|
// caller key already authorize the command.
|
|
103
|
-
const sessionCliContent = renderSessionCli(input.
|
|
97
|
+
const sessionCliContent = renderSessionCli(input.entryPoint);
|
|
104
98
|
const sessionCliDigest = digest(sessionCliContent);
|
|
105
99
|
const sessionCliPath = resolve(join(home, "runtime", "session-cli", `yui-${sessionCliDigest}.sh`));
|
|
106
100
|
writeImmutableText(sessionCliPath, sessionCliContent);
|
|
@@ -127,9 +121,7 @@ export function materializeSessionBootstrap(input) {
|
|
|
127
121
|
roleKind: input.roleKind,
|
|
128
122
|
compatibilityDigest: sessionManifestCompatibilityDigest(input.role.name, input.roleKind, input.role),
|
|
129
123
|
controlPlane: {
|
|
130
|
-
|
|
131
|
-
sessionCliPath,
|
|
132
|
-
digest: controlDigest
|
|
124
|
+
sessionCliPath
|
|
133
125
|
},
|
|
134
126
|
skills: input.skills.map((skill) => Object.freeze({
|
|
135
127
|
id: skill.id,
|
|
@@ -142,7 +134,7 @@ export function materializeSessionBootstrap(input) {
|
|
|
142
134
|
// A Global Session may move between Yui's remote TUI and Desktop.
|
|
143
135
|
// Carry its read entry in the Thread-visible Manifest instead of
|
|
144
136
|
// depending on the client process that happened to create it.
|
|
145
|
-
loadCommand: renderGlobalContextCommand(input.
|
|
137
|
+
loadCommand: renderGlobalContextCommand(input.entryPoint, home, input.role.name)
|
|
146
138
|
}
|
|
147
139
|
: {
|
|
148
140
|
loadCommand: "\"$YUI_SESSION_CLI\" task turn context \"$YUI_TASK_ID/<turn-id>\" --json",
|
|
@@ -156,15 +148,14 @@ export function materializeSessionBootstrap(input) {
|
|
|
156
148
|
manifest,
|
|
157
149
|
manifestPath,
|
|
158
150
|
sessionCliPath,
|
|
159
|
-
roleProfilePath
|
|
160
|
-
descriptorPath
|
|
151
|
+
roleProfilePath
|
|
161
152
|
});
|
|
162
153
|
}
|
|
163
|
-
function renderGlobalContextCommand(
|
|
154
|
+
function renderGlobalContextCommand(entryPoint, home, roleName) {
|
|
164
155
|
return [
|
|
165
156
|
`YUI_HOME=${quoteShellWord(home)}`,
|
|
166
|
-
quoteShellWord(
|
|
167
|
-
quoteShellWord(
|
|
157
|
+
quoteShellWord(entryPoint.executable),
|
|
158
|
+
quoteShellWord(entryPoint.cliEntry),
|
|
168
159
|
"session",
|
|
169
160
|
"context",
|
|
170
161
|
quoteShellWord(roleName),
|
|
@@ -181,9 +172,9 @@ function quoteShellWord(value) {
|
|
|
181
172
|
* an earlier release may still pin a control-plane digest; retargeting it here
|
|
182
173
|
* is what removes that stale pin from a live Home.
|
|
183
174
|
*/
|
|
184
|
-
export function refreshManagedSessionCliWrappers(homeInput,
|
|
175
|
+
export function refreshManagedSessionCliWrappers(homeInput, entryPoint) {
|
|
185
176
|
const home = resolve(homeInput);
|
|
186
|
-
const currentSessionCli = renderSessionCli(
|
|
177
|
+
const currentSessionCli = renderSessionCli(entryPoint);
|
|
187
178
|
const manifestDirectory = resolve(join(home, "runtime", "session-manifests"));
|
|
188
179
|
const sessionCliDirectory = resolve(join(home, "runtime", "session-cli"));
|
|
189
180
|
if (!existsSync(manifestDirectory)) {
|
|
@@ -7,7 +7,7 @@ import { AGENT_OPERATIONAL_ENVIRONMENT_NAMES, nativeAgentEnvironmentNames, opera
|
|
|
7
7
|
import { openCurrentTaskStore } from "../storage/currentTaskStore.js";
|
|
8
8
|
import { yuiTmuxServerName } from "../tmux/tmuxManager.js";
|
|
9
9
|
import { hasRuntimeLifecycleWork } from "../runtime/lifecycleReservation.js";
|
|
10
|
-
import { assertControllerStatusIdentity } from "../runtime/
|
|
10
|
+
import { assertControllerStatusIdentity } from "../runtime/runtimeCoherence.js";
|
|
11
11
|
import { EPHEMERAL_DOMAIN_ENVIRONMENT_NAMES } from "./domainIdentity.js";
|
|
12
12
|
import { yuiVersionIdentity } from "../version.js";
|
|
13
13
|
import { SessionOwnerReconciliation } from "./sessionOwnerReconciliation.js";
|