@zq-silk/yui 2.6.0 → 2.8.0
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 +23 -0
- package/dist/cli/commandCatalog.js +34 -7
- package/dist/cli/updateCommand.js +3 -0
- package/dist/commands/configCommands.js +1 -0
- package/dist/commands/globalRoleCommands.js +62 -35
- package/dist/commands/inboxCommands.js +372 -0
- package/dist/commands/roleSkillValidation.js +2 -0
- package/dist/commands/taskCommands.js +39 -7
- package/dist/commands/taskRoleRuntimeStatus.js +2 -0
- package/dist/context/currentSkills.js +48 -0
- package/dist/context/roleSessionContext.js +13 -16
- package/dist/context/runContextPack.js +50 -3
- package/dist/context/runInputContract.js +1 -1
- package/dist/context/sessionBootstrapManifest.js +27 -4
- package/dist/context/skillCatalog.js +2 -3
- package/dist/context/taskContext.js +38 -12
- package/dist/controlPlaneCli.js +58 -7
- package/dist/controller/controller.js +8 -2
- package/dist/controller/fileSchedulerStoreAdapter.js +26 -6
- package/dist/controller/globalInputDelivery.js +86 -16
- package/dist/controller/globalRuntimeAttention.js +136 -6
- package/dist/controller/operatorNotification.js +6 -1
- package/dist/controller/providerRetryDelivery.js +3 -5
- package/dist/controller/runtime.js +6 -2
- package/dist/controller/structuredProviderObservation.js +51 -15
- package/dist/controller/taskHostTerminalRecovery.js +33 -0
- package/dist/doctor/doctor.js +25 -2
- package/dist/executor/effectiveLaunch.js +2 -2
- package/dist/executor/fileRoleLaunchPlanner.js +8 -15
- package/dist/interaction/notificationPresentation.js +2 -2
- package/dist/interaction/operatorPresentation.js +1 -1
- package/dist/message/globalInputBacklog.js +69 -0
- package/dist/message/globalMessageQuery.js +1 -0
- package/dist/message/inboxReceipt.js +62 -0
- package/dist/message/message.js +6 -1
- package/dist/message/messageContinuation.js +34 -13
- package/dist/message/taskInboxOwnership.js +43 -0
- package/dist/models/modelManagement.js +469 -0
- package/dist/models/modelManagementConfig.js +46 -0
- package/dist/models/modelManagementFiles.js +90 -0
- package/dist/models/modelManagementHttp.js +90 -0
- package/dist/models/modelManagementTypes.js +1 -0
- package/dist/nativeAgent/session/backends.js +1 -1
- package/dist/observability/runtimeIdentity.js +1 -1
- package/dist/output/boundedRead.js +43 -14
- package/dist/resources/sqliteResourceRegistry.js +1 -1
- package/dist/runtime/agentEndpointIdentity.js +8 -4
- package/dist/runtime/agentEndpointOwnership.js +5 -6
- package/dist/runtime/agentHost.js +165 -24
- package/dist/runtime/agentHostImplementation.js +18 -0
- package/dist/runtime/managedCaller.js +25 -1
- package/dist/runtime/managedIdentity.js +1 -1
- package/dist/runtime/providerRetry.js +19 -1
- package/dist/runtime/sessionCaller.js +53 -0
- package/dist/scheduler/operatorEvent.js +49 -0
- package/dist/scheduler/operatorInputNotificationProcessor.js +5 -4
- package/dist/scheduler/taskWake.js +5 -0
- package/dist/sqlite/driver.js +108 -0
- package/dist/storage/contextRecords.js +28 -0
- package/dist/storage/sqliteSchema.js +21 -1
- package/dist/storage/sqliteStore.js +64 -1
- package/dist/storage/storageSchema.js +1 -1
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +1 -0
- package/dist/storage/taskStore.js +4 -0
- package/dist/storage/upgrade/upgradeOrchestrator.js +1 -1
- package/dist/task/completionReadiness.js +13 -6
- package/dist/telemetry/sqliteTelemetryStore.js +1 -1
- package/dist/web/assets/assetManifest.js +28 -2
- package/dist/web/assets/client/app/manage.js +6 -0
- package/dist/web/assets/client/app/models.js +20 -0
- package/dist/web/assets/client/app/settings.js +41 -9
- package/dist/web/assets/client/i18n/en.js +180 -2
- package/dist/web/assets/client/i18n/zh-CN.js +178 -2
- package/dist/web/assets/client/index.js +8 -0
- package/dist/web/assets/client/layout/manageShell.js +94 -0
- package/dist/web/assets/client/lib/api.js +13 -1
- package/dist/web/assets/client/lib/i18n.js +5 -3
- package/dist/web/assets/client/views/dock/conversation.js +231 -80
- package/dist/web/assets/client/views/models.js +777 -0
- package/dist/web/assets/client/views/skills.js +22 -15
- package/dist/web/assets/shared/icons.js +2 -0
- package/dist/web/assets/shared/manageNav.js +13 -0
- package/dist/web/assets/shell/manage.js +54 -0
- package/dist/web/assets/shell/models.js +7 -0
- package/dist/web/assets/shell/settings.js +1 -35
- package/dist/web/assets/shell/settingsContent.js +30 -0
- package/dist/web/assets/shell/sidebar.js +1 -0
- package/dist/web/assets/styles/index.js +6 -0
- package/dist/web/assets/styles/views/conversation.js +133 -0
- package/dist/web/assets/styles/views/manage.js +71 -0
- package/dist/web/assets/styles/views/models.js +129 -0
- package/dist/web/assets/styles/views/settings.js +28 -14
- package/dist/web/assets/styles/views/sidebar.js +1 -0
- package/dist/web/webServer.js +59 -9
- package/docs/cli-information-contract.md +42 -11
- package/docs/cli-information-contract.zh-CN.md +28 -7
- package/docs/managed-turn-and-session-runtime.md +127 -5
- package/docs/managed-turn-and-session-runtime.zh-CN.md +83 -3
- package/docs/release-workflow.md +105 -9
- package/docs/release-workflow.zh-CN.md +73 -5
- package/docs/storage-baseline.md +22 -7
- package/docs/storage-baseline.zh-CN.md +14 -5
- package/docs/task-delivery.md +17 -8
- package/docs/task-delivery.zh-CN.md +11 -5
- package/docs/testing/review-evaluation.md +148 -0
- package/docs/testing/review-evaluation.zh-CN.md +117 -0
- package/docs/testing/verification-levels.md +26 -4
- package/docs/testing/verification-levels.zh-CN.md +11 -1
- package/i18n/README.zh-CN.md +19 -0
- package/package.json +5 -2
- package/skills/yui-leader/SKILL.md +28 -11
- package/skills/yui-leader/VERSION +1 -0
- package/skills/yui-leader/references/execution.md +29 -0
- package/skills/yui-leader/references/replicated-execution.md +7 -0
- package/skills/yui-operator/SKILL.md +15 -3
- package/skills/yui-operator/VERSION +1 -0
- package/skills/yui-operator/references/task-delivery.md +2 -0
- package/skills/yui-reviewer/SKILL.md +43 -1
- package/skills/yui-reviewer/VERSION +1 -0
- package/skills/yui-runtime/SKILL.md +144 -15
- package/skills/yui-runtime/VERSION +1 -0
- package/skills/yui-runtime/references/publication.md +16 -5
- package/skills/yui-runtime/references/recovery.md +24 -5
- package/skills/yui-worker/SKILL.md +8 -1
- package/skills/yui-worker/VERSION +1 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { lookup } from "node:dns/promises";
|
|
2
|
+
import { request as httpRequest } from "node:http";
|
|
3
|
+
import { request as httpsRequest } from "node:https";
|
|
4
|
+
import { BlockList, isIP } from "node:net";
|
|
5
|
+
import { modelUrl } from "./modelManagementConfig.js";
|
|
6
|
+
const denied = new BlockList();
|
|
7
|
+
for (const [address, bits] of [
|
|
8
|
+
["0.0.0.0", 8], ["10.0.0.0", 8], ["100.64.0.0", 10], ["169.254.0.0", 16],
|
|
9
|
+
["172.16.0.0", 12], ["192.0.0.0", 24], ["192.168.0.0", 16], ["198.18.0.0", 15],
|
|
10
|
+
["224.0.0.0", 4], ["240.0.0.0", 4]
|
|
11
|
+
])
|
|
12
|
+
denied.addSubnet(address, bits, "ipv4");
|
|
13
|
+
for (const [address, bits] of [["::", 128], ["fc00::", 7], ["fe80::", 10], ["ff00::", 8], ["::ffff:0:0", 96]]) {
|
|
14
|
+
denied.addSubnet(address, bits, "ipv6");
|
|
15
|
+
}
|
|
16
|
+
/** Small fixed-route client: pinned DNS, no redirects/proxies, deadline and byte cap. */
|
|
17
|
+
export async function readModelJson(base, suffix, key, timeoutMs) {
|
|
18
|
+
const url = modelUrl(`${base.replace(/\/+$/, "")}${suffix}`);
|
|
19
|
+
const hostname = url.hostname.replace(/^\[|\]$/g, "");
|
|
20
|
+
const explicitLoopback = ["127.0.0.1", "::1", "localhost"].includes(hostname);
|
|
21
|
+
const abort = new AbortController();
|
|
22
|
+
const timer = setTimeout(() => abort.abort(), timeoutMs);
|
|
23
|
+
try {
|
|
24
|
+
const addresses = await Promise.race([
|
|
25
|
+
isIP(hostname) ? Promise.resolve([{ address: hostname, family: isIP(hostname) }]) : lookup(hostname, { all: true }),
|
|
26
|
+
new Promise((_, reject) => abort.signal.addEventListener("abort", () => reject(new Error("timeout")), { once: true }))
|
|
27
|
+
]);
|
|
28
|
+
if (abort.signal.aborted || !addresses.length || addresses.some(({ address, family }) => {
|
|
29
|
+
const loopback = address === "::1" || address.startsWith("127.");
|
|
30
|
+
return (loopback && !explicitLoopback) || (!loopback && explicitLoopback)
|
|
31
|
+
|| (!loopback && denied.check(address, family === 6 ? "ipv6" : "ipv4"));
|
|
32
|
+
}))
|
|
33
|
+
throw new Error("target-rejected");
|
|
34
|
+
const selected = addresses[0];
|
|
35
|
+
return await new Promise((resolve, reject) => {
|
|
36
|
+
const request = (url.protocol === "https:" ? httpsRequest : httpRequest)(url, {
|
|
37
|
+
method: "GET", signal: abort.signal, agent: false,
|
|
38
|
+
headers: { accept: "application/json", ...(key ? { authorization: `Bearer ${key}` } : {}) },
|
|
39
|
+
lookup: (_name, options, callback) => options.all
|
|
40
|
+
? callback(null, addresses)
|
|
41
|
+
: callback(null, selected.address, selected.family)
|
|
42
|
+
}, response => {
|
|
43
|
+
if (response.statusCode !== 200) {
|
|
44
|
+
response.destroy();
|
|
45
|
+
reject(new Error(response.statusCode === 401 || response.statusCode === 403 ? "authentication-failed"
|
|
46
|
+
: response.statusCode === 404 ? "route-unavailable" : "service-failed"));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
let bytes = 0;
|
|
50
|
+
const chunks = [];
|
|
51
|
+
response.on("data", chunk => {
|
|
52
|
+
bytes += chunk.length;
|
|
53
|
+
if (bytes > 2 * 1024 * 1024) {
|
|
54
|
+
response.destroy();
|
|
55
|
+
reject(new Error("response-too-large"));
|
|
56
|
+
}
|
|
57
|
+
else
|
|
58
|
+
chunks.push(chunk);
|
|
59
|
+
});
|
|
60
|
+
response.on("error", () => reject(new Error("read-failed")));
|
|
61
|
+
response.on("end", () => {
|
|
62
|
+
try {
|
|
63
|
+
// Even schema-allowed upstream strings must not echo our credential.
|
|
64
|
+
const text = Buffer.concat(chunks).toString("utf8");
|
|
65
|
+
const parsed = JSON.parse(text);
|
|
66
|
+
resolve(key ? redact(parsed, key) : parsed);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
reject(new Error("invalid-response"));
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
request.on("error", () => reject(new Error(abort.signal.aborted ? "timeout" : "connection-failed")));
|
|
74
|
+
request.end();
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
clearTimeout(timer);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function redact(value, key) {
|
|
82
|
+
if (typeof value === "string")
|
|
83
|
+
return value.split(key).join("[redacted]");
|
|
84
|
+
if (Array.isArray(value))
|
|
85
|
+
return value.map(item => redact(item, key));
|
|
86
|
+
if (value && typeof value === "object")
|
|
87
|
+
return Object.fromEntries(Object.entries(value)
|
|
88
|
+
.map(([name, item]) => [name.split(key).join("[redacted]"), redact(item, key)]));
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -12,7 +12,7 @@ import { createHash } from "node:crypto";
|
|
|
12
12
|
import { spawnSync } from "node:child_process";
|
|
13
13
|
import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
14
14
|
import { join } from "node:path";
|
|
15
|
-
import Database from "
|
|
15
|
+
import Database from "../sqlite/driver.js";
|
|
16
16
|
import { CURRENT_STORAGE_VERSION, MIN_SUPPORTED_STORAGE_VERSION } from "../storage/storageVersions.js";
|
|
17
17
|
import { inspectStorageSchema } from "../storage/storageSchema.js";
|
|
18
18
|
import { resolveStoreWorkerEnabledForHome } from "../storage/storeRpc.js";
|
|
@@ -2,6 +2,30 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { usageError } from "../errors/cliError.js";
|
|
3
3
|
export const READ_PAGE_BYTES = 32 * 1024;
|
|
4
4
|
export const INLINE_DOCUMENT_BYTES = 16 * 1024;
|
|
5
|
+
/** Compact JSON wire budget, including the CLI envelope and small command
|
|
6
|
+
* wrappers (for example Run Context's `context` field). Never count just items. */
|
|
7
|
+
export function fitsReadBudget(value, bytes = READ_PAGE_BYTES) {
|
|
8
|
+
return Buffer.byteLength(JSON.stringify({ ok: true, data: value })) + 256 <= bytes;
|
|
9
|
+
}
|
|
10
|
+
export function summaryText(text, bytes = 512) {
|
|
11
|
+
if (Buffer.byteLength(text) <= bytes)
|
|
12
|
+
return text;
|
|
13
|
+
let result = "";
|
|
14
|
+
let size = 0;
|
|
15
|
+
for (const character of text) {
|
|
16
|
+
const length = Buffer.byteLength(character);
|
|
17
|
+
if (size + length + 3 > bytes)
|
|
18
|
+
break;
|
|
19
|
+
result += character;
|
|
20
|
+
size += length;
|
|
21
|
+
}
|
|
22
|
+
return result + "…";
|
|
23
|
+
}
|
|
24
|
+
/** List-only nested metadata. The containing entry must expose an exact read. */
|
|
25
|
+
export function summaryMetadata(value, budget = 2048) {
|
|
26
|
+
const bytes = Buffer.byteLength(JSON.stringify(value));
|
|
27
|
+
return bytes <= budget ? value : { omitted: true, digest: readDigest(value), bytes };
|
|
28
|
+
}
|
|
5
29
|
function assertSource(source) {
|
|
6
30
|
if (Buffer.byteLength(source) > 1024)
|
|
7
31
|
throw usageError("Read source identity exceeds its 1024-byte budget.");
|
|
@@ -22,7 +46,7 @@ export function encodeReadCursor(cursor) {
|
|
|
22
46
|
export function decodeReadCursor(raw, source, digest) {
|
|
23
47
|
let cursor;
|
|
24
48
|
try {
|
|
25
|
-
if (raw.length >
|
|
49
|
+
if (raw.length > 16 * 1024)
|
|
26
50
|
throw new Error();
|
|
27
51
|
cursor = JSON.parse(Buffer.from(raw, "base64url").toString("utf8"));
|
|
28
52
|
if (cursor.source !== source || !Number.isSafeInteger(cursor.offset) || cursor.offset < 0
|
|
@@ -46,7 +70,7 @@ export function readLimit(value) {
|
|
|
46
70
|
/** For small in-memory authorized collections. Authorize/filter BEFORE calling.
|
|
47
71
|
* Cursors bind the selected collection, never grant access, and store no state.
|
|
48
72
|
*/
|
|
49
|
-
export function recordPage(records, source, options = {}) {
|
|
73
|
+
export function recordPage(records, source, options = {}, metadata = {}) {
|
|
50
74
|
assertSource(source);
|
|
51
75
|
const limit = readLimit(options.limit);
|
|
52
76
|
const digest = readDigest(records);
|
|
@@ -54,24 +78,29 @@ export function recordPage(records, source, options = {}) {
|
|
|
54
78
|
if (offset > records.length)
|
|
55
79
|
throw usageError("Read cursor exceeds this collection.");
|
|
56
80
|
const items = [];
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
81
|
+
const page = () => {
|
|
82
|
+
const end = offset + items.length;
|
|
83
|
+
return {
|
|
84
|
+
...metadata,
|
|
85
|
+
source, digest, items, total: records.length, complete: end === records.length,
|
|
86
|
+
nextCursor: end < records.length ? encodeReadCursor({ source, digest, offset: end }) : null,
|
|
87
|
+
limits: { items: limit, bytes: READ_PAGE_BYTES }
|
|
88
|
+
};
|
|
89
|
+
};
|
|
60
90
|
for (const record of records.slice(offset, offset + limit)) {
|
|
61
91
|
const size = Buffer.byteLength(JSON.stringify(record)) + 1;
|
|
62
92
|
if (size > READ_PAGE_BYTES / 2)
|
|
63
93
|
throw usageError("List entry exceeds its summary budget; read the exact record.");
|
|
64
|
-
if (bytes + size > READ_PAGE_BYTES - 4096)
|
|
65
|
-
break;
|
|
66
94
|
items.push(record);
|
|
67
|
-
|
|
95
|
+
if (!fitsReadBudget(page())) {
|
|
96
|
+
items.pop();
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
68
99
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
limits: { items: limit, bytes: READ_PAGE_BYTES }
|
|
74
|
-
};
|
|
100
|
+
if (!fitsReadBudget(page()) || items.length === 0 && offset < records.length) {
|
|
101
|
+
throw usageError("List entry and read identity exceed the response budget; read the exact record.");
|
|
102
|
+
}
|
|
103
|
+
return page();
|
|
75
104
|
}
|
|
76
105
|
/** Small exact details stay native JSON. Large details explicitly become a
|
|
77
106
|
* contentPage. Concatenate text in order, then JSON.parse once, never each chunk.
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import { existsSync } from "node:fs";
|
|
14
14
|
import { join } from "node:path";
|
|
15
15
|
import { isDeepStrictEqual } from "node:util";
|
|
16
|
-
import Database from "
|
|
16
|
+
import Database from "../sqlite/driver.js";
|
|
17
17
|
import { validateSqliteSchema } from "../storage/sqliteSchema.js";
|
|
18
18
|
import { emptyResourceRegistry, parseResourceRegistryState } from "./resourceRegistry.js";
|
|
19
19
|
import { RESOURCE_REGISTRY_SCHEMA_VERSION } from "./resourceTypes.js";
|
|
@@ -78,12 +78,16 @@ export function validateAgentEndpointImplementation(ref) {
|
|
|
78
78
|
}
|
|
79
79
|
return ref;
|
|
80
80
|
}
|
|
81
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* The built-in adapter identity is the supported native connection contract.
|
|
83
|
+
* Build generations are provenance, not a requirement to retain old code.
|
|
84
|
+
* Actual native resume still validates the conversation/account/configuration.
|
|
85
|
+
*/
|
|
82
86
|
export function requireBuiltinAgentEndpointImplementation(adapterId, ref) {
|
|
83
87
|
validateAgentEndpointImplementation(ref);
|
|
84
88
|
const current = builtinAgentEndpointImplementation(adapterId);
|
|
85
|
-
if (ref.id !== current.id
|
|
86
|
-
throw new Error(`Session Endpoint ${ref.id}
|
|
89
|
+
if (ref.id !== current.id) {
|
|
90
|
+
throw new Error(`Session Endpoint ${ref.id} is incompatible with adapter ${adapterId}.`);
|
|
87
91
|
}
|
|
88
|
-
return
|
|
92
|
+
return current;
|
|
89
93
|
}
|
|
@@ -8,9 +8,9 @@ import { createAgentEndpointFactory } from "./agentEndpoint.js";
|
|
|
8
8
|
* owns its own Host in its own process and never holds an Endpoint client, so
|
|
9
9
|
* the reference facts for a Session live where the Session actually runs.
|
|
10
10
|
*
|
|
11
|
-
* Only the running code can be attached here.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* Only the running code can be attached here. This owner proves the loaded
|
|
12
|
+
* generation is held and released; historical launch generations do not select
|
|
13
|
+
* old code or block a compatible native resume.
|
|
14
14
|
*/
|
|
15
15
|
export function createAgentEndpointOwner(factory = createAgentEndpointFactory()) {
|
|
16
16
|
const host = new InstanceHost();
|
|
@@ -48,9 +48,8 @@ export function createAgentEndpointOwner(factory = createAgentEndpointFactory())
|
|
|
48
48
|
await holder.handle.release();
|
|
49
49
|
};
|
|
50
50
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* resume, never a silent start on a different generation.
|
|
51
|
+
* Hold the current implementation for one Session. A historical reference
|
|
52
|
+
* must name the same built-in adapter, not the same package build.
|
|
54
53
|
*/
|
|
55
54
|
const pin = (adapterId, pinned) => {
|
|
56
55
|
if (pinned !== undefined)
|
|
@@ -5,7 +5,7 @@ import { homedir, tmpdir } from "node:os";
|
|
|
5
5
|
import { dirname, join, resolve } from "node:path";
|
|
6
6
|
import { createInterface } from "node:readline";
|
|
7
7
|
import { withSessionContextPointer } from "../context/sessionBootstrapManifest.js";
|
|
8
|
-
import { publishStructuredConversationRecoverability, publishStructuredProviderAccepted, publishStructuredProviderActivity, publishStructuredProviderAttachmentExit, publishStructuredProviderConnection, publishStructuredProviderDiagnostic, publishStructuredProviderGoal, publishStructuredProviderInputObserved, publishStructuredProviderInputSettlement, publishStructuredProviderOpened, publishStructuredProviderStarted, publishStructuredProviderTerminal, structuredProviderEventDelivery } from "../controller/structuredProviderObservation.js";
|
|
8
|
+
import { publishStructuredConversationRecoverability, publishStructuredProviderAccepted, publishStructuredProviderActivity, publishStructuredProviderAttachmentExit, publishStructuredProviderConnection, publishStructuredProviderDiagnostic, publishStructuredProviderGoal, publishStructuredProviderInputObserved, publishStructuredProviderInputSettlement, publishStructuredProviderOpened, publishStructuredProviderStarted, publishStructuredProviderTerminal, replayStructuredProviderPersistence, structuredProviderEventDelivery } from "../controller/structuredProviderObservation.js";
|
|
9
9
|
import { callController, controllerCallMayHaveApplied, ControllerClientError } from "../core/controllerClient.js";
|
|
10
10
|
import { readHomeFilesystemId } from "../core/homeFilesystemIdentity.js";
|
|
11
11
|
import { isForeignHandoverLockHeld } from "../release/runtimeRelease.js";
|
|
@@ -14,6 +14,7 @@ import { tmuxSocketDirectory } from "../tmux/tmuxSocketEndpoint.js";
|
|
|
14
14
|
import { createAgentEndpointOwner } from "./agentEndpointOwnership.js";
|
|
15
15
|
import { providerDeliveryFailure, providerDeliveryFailureFrom, redactAgentErrorText, serializeAgentErrorRaw } from "./agentError.js";
|
|
16
16
|
import { AGENT_HOST_CONTROL_PROTOCOL } from "./agentHostProtocol.js";
|
|
17
|
+
import { AGENT_HOST_IMPLEMENTATION } from "./agentHostImplementation.js";
|
|
17
18
|
import { closeAgentHostEndpoints } from "./agentHostCleanup.js";
|
|
18
19
|
import { readAgentRunConfigurationObservation, unknownAgentRunConfiguration } from "./agentRunConfiguration.js";
|
|
19
20
|
import { CodexPreSubmissionError } from "./codexAppServerRuntime.js";
|
|
@@ -93,6 +94,9 @@ export async function runAgentHost(input) {
|
|
|
93
94
|
let activeRunPayload;
|
|
94
95
|
let lastTerminalPayload;
|
|
95
96
|
let lastTerminal;
|
|
97
|
+
// One exact direct Turn witnessed by this Endpoint under its original fence.
|
|
98
|
+
// This is recovery evidence, never ownership of the Desktop client's input.
|
|
99
|
+
let directObservation;
|
|
96
100
|
let activeRunAttemptId;
|
|
97
101
|
let activeNativeTurnId;
|
|
98
102
|
let codexClientAttachedAt;
|
|
@@ -166,6 +170,7 @@ export async function runAgentHost(input) {
|
|
|
166
170
|
const handleStarted = (started, observedPayload = sessionPayload) => {
|
|
167
171
|
if (started.clientOwned || observedPayload === undefined)
|
|
168
172
|
return;
|
|
173
|
+
const observedAuthority = authority === undefined ? undefined : { ...authority };
|
|
169
174
|
updateSnapshot(hostSnapshot("busy", {
|
|
170
175
|
adapterId: observedPayload.providerControl?.adapterId,
|
|
171
176
|
processInstanceId: session?.processInstanceId,
|
|
@@ -175,6 +180,25 @@ export async function runAgentHost(input) {
|
|
|
175
180
|
...authorityFields()
|
|
176
181
|
}));
|
|
177
182
|
void enqueueSerialized(async () => {
|
|
183
|
+
// Desktop can start another Turn before the Controller's periodic pass.
|
|
184
|
+
// Persist the previous exact terminal before replacing its bounded cache
|
|
185
|
+
// or publishing the new start. Never clear the newer native writer.
|
|
186
|
+
const previous = directObservation;
|
|
187
|
+
if (previous?.terminal !== undefined && authority !== undefined
|
|
188
|
+
&& sameProviderAuthorityFence(previous.authority, authority)
|
|
189
|
+
&& previous.started.nativeSessionId === session?.nativeSessionId) {
|
|
190
|
+
await publishStructuredProviderTerminal({
|
|
191
|
+
home: input.home, environment: previous.payload.environment, terminal: previous.terminal
|
|
192
|
+
}).catch(() => {
|
|
193
|
+
// The publisher retains the original unpersisted bytes. Still
|
|
194
|
+
// observe the new start; a failed old write must not hide its writer.
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
if (session !== undefined && started.nativeSessionId === session.nativeSessionId
|
|
198
|
+
&& started.conversationId === session.conversationId && observedAuthority !== undefined
|
|
199
|
+
&& authority !== undefined && sameProviderAuthorityFence(observedAuthority, authority)) {
|
|
200
|
+
directObservation = { started, payload: observedPayload, authority: observedAuthority };
|
|
201
|
+
}
|
|
178
202
|
await publishStructuredProviderStarted({
|
|
179
203
|
home: input.home,
|
|
180
204
|
environment: observedPayload.environment,
|
|
@@ -194,6 +218,14 @@ export async function runAgentHost(input) {
|
|
|
194
218
|
// AgentRun audit record. It has no WorkItem ownership and no managed input
|
|
195
219
|
// fence; the runtime observer records only visible input/output.
|
|
196
220
|
void enqueueSerialized(async () => {
|
|
221
|
+
const direct = directObservation;
|
|
222
|
+
if (direct !== undefined && authority !== undefined
|
|
223
|
+
&& sameProviderAuthorityFence(direct.authority, authority)
|
|
224
|
+
&& direct.started.nativeSessionId === terminal.nativeSessionId
|
|
225
|
+
&& direct.started.conversationId === terminal.conversationId
|
|
226
|
+
&& direct.started.nativeTurnId === terminal.nativeTurnId) {
|
|
227
|
+
direct.terminal = terminal;
|
|
228
|
+
}
|
|
197
229
|
await publishStructuredProviderTerminal({
|
|
198
230
|
home: input.home,
|
|
199
231
|
environment: busyPayload.environment,
|
|
@@ -274,6 +306,7 @@ export async function runAgentHost(input) {
|
|
|
274
306
|
conversationId: terminal.conversationId,
|
|
275
307
|
...authorityFields()
|
|
276
308
|
}));
|
|
309
|
+
signalRoleMailbox(input.home, currentPayload);
|
|
277
310
|
promptHuman();
|
|
278
311
|
}
|
|
279
312
|
catch (error) {
|
|
@@ -565,6 +598,8 @@ export async function runAgentHost(input) {
|
|
|
565
598
|
throw new Error("Agent Host accepts only managed Provider control launches.");
|
|
566
599
|
}
|
|
567
600
|
if (activeRunPayload !== undefined
|
|
601
|
+
|| sessionPayload !== undefined
|
|
602
|
+
&& (structuredProviderEventDelivery(input.home, sessionPayload.environment).unpersisted ?? 0) > 0
|
|
568
603
|
|| ["starting", "ready", "settling", "delivery-unknown"].includes(snapshot.state)) {
|
|
569
604
|
throw new Error("Agent Host still owns an unsettled Provider Turn.");
|
|
570
605
|
}
|
|
@@ -595,8 +630,7 @@ export async function runAgentHost(input) {
|
|
|
595
630
|
|| providerControl.mode !== "resume"
|
|
596
631
|
|| providerControl.nativeSessionId !== session.nativeSessionId
|
|
597
632
|
|| (providerControl.endpointImplementation !== undefined
|
|
598
|
-
&&
|
|
599
|
-
|| providerControl.endpointImplementation.generation !== session.configuration.implementation.generation))) {
|
|
633
|
+
&& providerControl.endpointImplementation.id !== session.configuration.implementation.id)) {
|
|
600
634
|
throw new Error("Agent Host launch does not match its live Provider Conversation.");
|
|
601
635
|
}
|
|
602
636
|
sessionPayload = next;
|
|
@@ -609,9 +643,8 @@ export async function runAgentHost(input) {
|
|
|
609
643
|
}
|
|
610
644
|
// The Controller revalidates the adopted execution environment while
|
|
611
645
|
// redeeming this launch; a persistent Host must not reopen its schema.
|
|
612
|
-
//
|
|
613
|
-
//
|
|
614
|
-
// fails here, rather than silently starting on a different generation.
|
|
646
|
+
// Hold the code actually loaded by this Host. Historical build identity
|
|
647
|
+
// is provenance; native resume and current authority remain mandatory.
|
|
615
648
|
endpointLease ??= endpointOwner.pin(providerControl.adapterId, providerControl.endpointImplementation);
|
|
616
649
|
endpointAdapterId = providerControl.adapterId;
|
|
617
650
|
const started = await (providerControl.mode === "new"
|
|
@@ -621,6 +654,7 @@ export async function runAgentHost(input) {
|
|
|
621
654
|
await recordProviderConnection(input.home, next, started.session);
|
|
622
655
|
lastTerminalPayload = undefined;
|
|
623
656
|
lastTerminal = undefined;
|
|
657
|
+
directObservation = undefined;
|
|
624
658
|
// Recoverable means a later process can rebind this Conversation by
|
|
625
659
|
// its native id. The Endpoint answers that from the Driver capability
|
|
626
660
|
// or, where the protocol settles it per connection, from what this
|
|
@@ -756,6 +790,7 @@ export async function runAgentHost(input) {
|
|
|
756
790
|
throw new Error("Agent Host rejected a stale Provider writer fence.");
|
|
757
791
|
}
|
|
758
792
|
if (activeRunPayload !== undefined || snapshot.state === "settling"
|
|
793
|
+
|| (structuredProviderEventDelivery(input.home, sessionPayload.environment).unpersisted ?? 0) > 0
|
|
759
794
|
|| session.inspect().activeNativeTurnId !== undefined) {
|
|
760
795
|
operation.failure = providerDeliveryFailure({
|
|
761
796
|
detail: "Agent Host still owns an unsettled Provider Turn.",
|
|
@@ -1012,7 +1047,9 @@ export async function runAgentHost(input) {
|
|
|
1012
1047
|
if (session === undefined || request.nativeSessionId !== session.nativeSessionId) {
|
|
1013
1048
|
throw new Error("Agent Host authority targets a different Provider Conversation.");
|
|
1014
1049
|
}
|
|
1015
|
-
if (activeRunPayload !== undefined
|
|
1050
|
+
if (activeRunPayload !== undefined || session.inspect().activeNativeTurnId !== undefined
|
|
1051
|
+
|| sessionPayload !== undefined
|
|
1052
|
+
&& (structuredProviderEventDelivery(input.home, sessionPayload.environment).unpersisted ?? 0) > 0
|
|
1016
1053
|
|| ["starting", "ready", "settling", "delivery-unknown"].includes(snapshot.state)) {
|
|
1017
1054
|
throw new Error("Agent Host authority cannot transfer while a AgentRun is unsettled.");
|
|
1018
1055
|
}
|
|
@@ -1088,7 +1125,9 @@ export async function runAgentHost(input) {
|
|
|
1088
1125
|
return enqueueSerialized(async () => {
|
|
1089
1126
|
const current = session;
|
|
1090
1127
|
if (!current?.nativeAccess || current.nativeSessionId !== request.nativeSessionId
|
|
1091
|
-
|| !sessionPayload ||
|
|
1128
|
+
|| !sessionPayload || activeRunPayload || activeNativeTurnId
|
|
1129
|
+
|| (structuredProviderEventDelivery(input.home, sessionPayload.environment).unpersisted ?? 0) > 0
|
|
1130
|
+
|| authority?.owner !== "controller"
|
|
1092
1131
|
|| (sessionPayload.environment.YUI_SESSION_SCOPE === "task" && sessionPayload.environment.YUI_ROLE !== "leader")
|
|
1093
1132
|
|| (sessionPayload.environment.YUI_SESSION_SCOPE === "global" && sessionPayload.environment.YUI_ROLE !== "operator")) {
|
|
1094
1133
|
throw new Error("Model switching requires the idle current controlled Codex Session, without a native terminal writer.");
|
|
@@ -1157,6 +1196,73 @@ export async function runAgentHost(input) {
|
|
|
1157
1196
|
}
|
|
1158
1197
|
return controlResult("accepted", { ...snapshot, nativeTui: !!nativeTui });
|
|
1159
1198
|
}
|
|
1199
|
+
if (request.type === "reconcile-turn") {
|
|
1200
|
+
if (session === undefined || session.nativeSessionId !== request.nativeSessionId
|
|
1201
|
+
|| authority === undefined || !sameProviderAuthorityFence(authority, request.authority)) {
|
|
1202
|
+
throw new Error("Terminal reconciliation targets a different Session or authority.");
|
|
1203
|
+
}
|
|
1204
|
+
const recovered = await enqueueSerialized(async () => {
|
|
1205
|
+
if (session?.nativeSessionId !== request.nativeSessionId || authority === undefined
|
|
1206
|
+
|| !sameProviderAuthorityFence(authority, request.authority)) {
|
|
1207
|
+
throw new Error("Session changed before original event persistence recovery.");
|
|
1208
|
+
}
|
|
1209
|
+
const persisted = await replayStructuredProviderPersistence(input.home, sessionPayload.environment);
|
|
1210
|
+
if (persisted.length > 0 && activeRunPayload === undefined) {
|
|
1211
|
+
const active = session.inspect().activeNativeTurnId;
|
|
1212
|
+
updateSnapshot(hostSnapshot(active === undefined ? "idle" : "busy", {
|
|
1213
|
+
adapterId: session.adapterId, processInstanceId: session.processInstanceId,
|
|
1214
|
+
nativeSessionId: session.nativeSessionId, conversationId: session.conversationId,
|
|
1215
|
+
nativeTurnId: active, ...authorityFields()
|
|
1216
|
+
}));
|
|
1217
|
+
}
|
|
1218
|
+
return persisted.some(event => ["turn.completed", "turn.failed", "turn.cancelled"].includes(event.kind)
|
|
1219
|
+
&& event.fence.nativeSessionId === request.nativeSessionId
|
|
1220
|
+
&& (request.nativeTurnId === undefined || event.fence.nativeTurnId === request.nativeTurnId)
|
|
1221
|
+
&& (event.fence.receiptId === request.attemptId
|
|
1222
|
+
|| event.fence.receiptId === undefined && request.nativeTurnId !== undefined
|
|
1223
|
+
&& request.attemptId === `direct:${request.nativeTurnId}`));
|
|
1224
|
+
});
|
|
1225
|
+
const direct = directObservation;
|
|
1226
|
+
const directMatch = direct?.terminal !== undefined
|
|
1227
|
+
&& request.nativeTurnId !== undefined
|
|
1228
|
+
&& request.attemptId === `direct:${request.nativeTurnId}`
|
|
1229
|
+
&& direct.started.nativeTurnId === request.nativeTurnId
|
|
1230
|
+
&& direct.started.nativeSessionId === request.nativeSessionId
|
|
1231
|
+
&& sameProviderAuthorityFence(direct.authority, request.authority);
|
|
1232
|
+
const terminal = directMatch ? direct.terminal : lastTerminal;
|
|
1233
|
+
const terminalPayload = directMatch ? direct.payload : lastTerminalPayload;
|
|
1234
|
+
if (terminal === undefined || terminalPayload === undefined
|
|
1235
|
+
|| terminal.nativeSessionId !== request.nativeSessionId
|
|
1236
|
+
|| !directMatch && (terminal.clientOwned !== true || terminal.attemptId !== request.attemptId)
|
|
1237
|
+
|| request.nativeTurnId !== undefined && terminal.nativeTurnId !== request.nativeTurnId) {
|
|
1238
|
+
return { ...controlResult("status", snapshot),
|
|
1239
|
+
reconciliation: recovered ? "terminal-republished" : "unobserved" };
|
|
1240
|
+
}
|
|
1241
|
+
// Serialize with submission/terminal persistence. The original Host and
|
|
1242
|
+
// original launch envelope remain the observer, including the full result.
|
|
1243
|
+
await enqueueSerialized(async () => {
|
|
1244
|
+
if (session?.nativeSessionId !== request.nativeSessionId || authority === undefined
|
|
1245
|
+
|| !sameProviderAuthorityFence(authority, request.authority)
|
|
1246
|
+
|| directMatch && directObservation !== direct) {
|
|
1247
|
+
throw new Error("Session changed before terminal reconciliation.");
|
|
1248
|
+
}
|
|
1249
|
+
await publishStructuredProviderTerminal({
|
|
1250
|
+
home: input.home, environment: terminalPayload.environment, terminal
|
|
1251
|
+
});
|
|
1252
|
+
if (!directMatch && activeRunAttemptId === terminal.attemptId) {
|
|
1253
|
+
activeRunPayload = undefined;
|
|
1254
|
+
activeRunAttemptId = undefined;
|
|
1255
|
+
activeNativeTurnId = undefined;
|
|
1256
|
+
updateSnapshot(hostSnapshot(session.inspect().activeNativeTurnId === undefined ? "idle" : "busy", {
|
|
1257
|
+
adapterId: session.adapterId, processInstanceId: session.processInstanceId,
|
|
1258
|
+
nativeSessionId: session.nativeSessionId, conversationId: session.conversationId,
|
|
1259
|
+
...authorityFields()
|
|
1260
|
+
}));
|
|
1261
|
+
}
|
|
1262
|
+
signalRoleMailbox(input.home, terminalPayload);
|
|
1263
|
+
});
|
|
1264
|
+
return { ...controlResult("status", snapshot), reconciliation: "terminal-republished" };
|
|
1265
|
+
}
|
|
1160
1266
|
if (request.type === "status") {
|
|
1161
1267
|
// Read the Agent's configuration at answer time, not from the stored
|
|
1162
1268
|
// snapshot. The Agent may have changed it since the last state
|
|
@@ -1461,6 +1567,22 @@ export async function inspectAgentHost(input) {
|
|
|
1461
1567
|
});
|
|
1462
1568
|
return result.snapshot;
|
|
1463
1569
|
}
|
|
1570
|
+
export async function reconcileAgentHostTurn(input) {
|
|
1571
|
+
// Periodic observation must not hold the scheduler behind an unavailable Host.
|
|
1572
|
+
// A timeout is only an observation failure, never permission to submit again.
|
|
1573
|
+
const path = agentHostControlSocketPath(input);
|
|
1574
|
+
const observed = await exchangeAgentHostControl(path, {
|
|
1575
|
+
protocol: AGENT_HOST_CONTROL_PROTOCOL, type: "status"
|
|
1576
|
+
}, 1_000);
|
|
1577
|
+
if (observed.outcome === "rejected")
|
|
1578
|
+
return observed;
|
|
1579
|
+
if (!observed.snapshot.controlCapabilities?.includes("reconcile-turn")) {
|
|
1580
|
+
// Older v1 Hosts still serve status and their immutable Inbox remains
|
|
1581
|
+
// consumable. Neither idle nor absent eventDelivery proves a terminal.
|
|
1582
|
+
return { ...observed, reconciliation: "unsupported" };
|
|
1583
|
+
}
|
|
1584
|
+
return exchangeAgentHostControl(path, input.control, 1_000);
|
|
1585
|
+
}
|
|
1464
1586
|
export async function waitForAgentHostLaunchAck(input) {
|
|
1465
1587
|
const deadline = Date.now() + (input.timeoutMs ?? AGENT_HOST_READY_TIMEOUT_MS);
|
|
1466
1588
|
let lastError;
|
|
@@ -1616,7 +1738,12 @@ export async function openAgentHostControl(home, payload, snapshot, dispatch) {
|
|
|
1616
1738
|
...result,
|
|
1617
1739
|
snapshot: {
|
|
1618
1740
|
...result.snapshot,
|
|
1619
|
-
|
|
1741
|
+
hostImplementation: AGENT_HOST_IMPLEMENTATION,
|
|
1742
|
+
controlCapabilities: payload.providerControl === undefined ? ["status"] : [
|
|
1743
|
+
"status", "launch", "submit-turn", "steer-turn", "cancel", "set-authority",
|
|
1744
|
+
"native-open", "native-respond", "native-model", "reconcile-turn"
|
|
1745
|
+
],
|
|
1746
|
+
...(!["task", "global"].includes(payload.environment.YUI_SESSION_SCOPE ?? "") ? {} : {
|
|
1620
1747
|
eventDelivery: structuredProviderEventDelivery(home, payload.environment, result.snapshot.nativeSessionId)
|
|
1621
1748
|
})
|
|
1622
1749
|
}
|
|
@@ -1691,7 +1818,8 @@ function boundControlResponse(result) {
|
|
|
1691
1818
|
// visible through an otherwise successful status request.
|
|
1692
1819
|
result = {
|
|
1693
1820
|
...controlResult(result.outcome, validateSnapshot(result.snapshot), result.failure === undefined ? undefined : providerDeliveryFailure(result.failure)),
|
|
1694
|
-
...(result.cancellation === undefined ? {} : { cancellation: result.cancellation })
|
|
1821
|
+
...(result.cancellation === undefined ? {} : { cancellation: result.cancellation }),
|
|
1822
|
+
...(result.reconciliation === undefined ? {} : { reconciliation: result.reconciliation })
|
|
1695
1823
|
};
|
|
1696
1824
|
if (withinControlBound(result))
|
|
1697
1825
|
return result;
|
|
@@ -1709,29 +1837,29 @@ function boundControlResponse(result) {
|
|
|
1709
1837
|
};
|
|
1710
1838
|
// Never make an omitted option list look like a complete empty enumeration.
|
|
1711
1839
|
if (result.snapshot.runConfiguration?.status === "observed") {
|
|
1712
|
-
result = controlResult(result.outcome, validateSnapshot({
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1840
|
+
result = { ...result, ...controlResult(result.outcome, validateSnapshot({
|
|
1841
|
+
...result.snapshot,
|
|
1842
|
+
runConfiguration: unknownAgentRunConfiguration("The Agent's reported configuration is too large for the Agent Host "
|
|
1843
|
+
+ "control response, so it could not be carried in full and none of it "
|
|
1844
|
+
+ "is shown rather than part of it.")
|
|
1845
|
+
}), result.failure) };
|
|
1718
1846
|
if (withinControlBound(result))
|
|
1719
1847
|
return result;
|
|
1720
1848
|
}
|
|
1721
|
-
let bounded = controlResult(result.outcome, {
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1849
|
+
let bounded = { ...result, ...controlResult(result.outcome, {
|
|
1850
|
+
...result.snapshot,
|
|
1851
|
+
...(result.snapshot.detail === undefined ? {} : { detail: clip(result.snapshot.detail, 1200) })
|
|
1852
|
+
}, result.failure === undefined ? undefined : { ...result.failure, detail: clip(result.failure.detail, 1200) }) };
|
|
1725
1853
|
const raw = bounded.failure?.raw;
|
|
1726
1854
|
if (withinControlBound(bounded) || raw === undefined)
|
|
1727
1855
|
return bounded;
|
|
1728
1856
|
let chars = raw.length;
|
|
1729
1857
|
while (!withinControlBound(bounded) && chars > 0) {
|
|
1730
1858
|
chars = Math.floor(chars / 2);
|
|
1731
|
-
bounded = controlResult(bounded.outcome, bounded.snapshot, {
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1859
|
+
bounded = { ...bounded, ...controlResult(bounded.outcome, bounded.snapshot, {
|
|
1860
|
+
...bounded.failure,
|
|
1861
|
+
raw: clip(raw, chars)
|
|
1862
|
+
}) };
|
|
1735
1863
|
}
|
|
1736
1864
|
return bounded;
|
|
1737
1865
|
}
|
|
@@ -1764,6 +1892,14 @@ function validateControl(control) {
|
|
|
1764
1892
|
}
|
|
1765
1893
|
if (control.type === "status")
|
|
1766
1894
|
return Object.freeze({ ...control });
|
|
1895
|
+
if (control.type === "reconcile-turn") {
|
|
1896
|
+
validateIdentity(control.nativeSessionId, "native Session id");
|
|
1897
|
+
validateIdentity(control.attemptId, "Provider input attempt id");
|
|
1898
|
+
if (control.nativeTurnId !== undefined)
|
|
1899
|
+
validateIdentity(control.nativeTurnId, "native Turn id");
|
|
1900
|
+
validateProviderAuthorityFence(control.authority);
|
|
1901
|
+
return Object.freeze({ ...control });
|
|
1902
|
+
}
|
|
1767
1903
|
if (control.type === "native-open" || control.type === "native-respond" || control.type === "native-model") {
|
|
1768
1904
|
validateIdentity(control.nativeSessionId, "native Session id");
|
|
1769
1905
|
if (control.type === "native-model" && (typeof control.model !== "string"
|
|
@@ -1867,6 +2003,11 @@ function validateSnapshot(snapshot) {
|
|
|
1867
2003
|
&& snapshot.inputAcceptance !== "provider" && snapshot.inputAcceptance !== "transport") {
|
|
1868
2004
|
throw new Error("Agent Host input acceptance evidence is invalid.");
|
|
1869
2005
|
}
|
|
2006
|
+
if (snapshot.controlCapabilities !== undefined && (!Array.isArray(snapshot.controlCapabilities) || snapshot.controlCapabilities.length > 64
|
|
2007
|
+
|| snapshot.controlCapabilities.some(value => typeof value !== "string"
|
|
2008
|
+
|| value.length === 0 || value.length > 64 || value.includes("\0")))) {
|
|
2009
|
+
throw new Error("Agent Host control capability advertisement is invalid.");
|
|
2010
|
+
}
|
|
1870
2011
|
const authorityFields = [
|
|
1871
2012
|
snapshot.authorityEpoch,
|
|
1872
2013
|
snapshot.authorityOwner,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { detectRunningRelease } from "../release/runtimeRelease.js";
|
|
5
|
+
// Capture once when this process loads its Host bridge. Never label a live
|
|
6
|
+
// process with the installation currently on disk at status-query time.
|
|
7
|
+
const release = detectRunningRelease(fileURLToPath(import.meta.url));
|
|
8
|
+
const modules = [
|
|
9
|
+
"agentHost.js", "agentHostProtocol.js", "agentHostImplementation.js",
|
|
10
|
+
"codexInteractiveHost.js", "../controller/structuredProviderObservation.js",
|
|
11
|
+
"../controller/runtimeEventInbox.js"
|
|
12
|
+
];
|
|
13
|
+
export const AGENT_HOST_IMPLEMENTATION = Object.freeze({
|
|
14
|
+
id: "yui.agent-host",
|
|
15
|
+
generation: release?.manifest.packageDigest ?? `checkout-${createHash("sha256")
|
|
16
|
+
.update(modules.map(module => `${module}\0${readFileSync(new URL(module, import.meta.url), "utf8")}`).join("\0"))
|
|
17
|
+
.digest("hex")}`
|
|
18
|
+
});
|