@zq-silk/yui 2.21.0 → 2.23.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 +5 -0
- package/dist/cli/commandCatalog.js +87 -27
- package/dist/cli/dynamicCompletion.js +4 -1
- package/dist/cli/helpRenderer.js +12 -1
- package/dist/cli/interactionPolicy.js +2 -0
- package/dist/cli/invocationRouter.js +5 -5
- package/dist/cli/roleWizard.js +5 -1
- package/dist/cli/updateCommand.js +20 -3
- package/dist/cli/updateOrchestrator.js +13 -0
- package/dist/cli/updatePorts.js +27 -0
- package/dist/commands/globalRoleCommands.js +33 -8
- package/dist/commands/inboxCommands.js +39 -12
- package/dist/commands/operatorCommands.js +2 -5
- package/dist/commands/releaseCommands.js +16 -2
- package/dist/commands/roleConfiguration.js +25 -2
- package/dist/commands/sessionCommands.js +5 -2
- package/dist/commands/taskCommands.js +217 -64
- package/dist/commands/taskContextCommand.js +26 -4
- package/dist/commands/taskFactCommands.js +15 -2
- package/dist/commands/taskIntegrationCommands.js +3 -3
- package/dist/commands/taskRoleRuntimeStatus.js +19 -5
- package/dist/commands/textInput.js +2 -2
- package/dist/context/contextFamilies.js +10 -0
- package/dist/context/currentSkills.js +37 -4
- package/dist/context/runContextPack.js +44 -5
- package/dist/context/runInputContract.js +1 -1
- package/dist/context/sessionBootstrapManifest.js +21 -6
- package/dist/context/sessionSkillNotice.js +74 -0
- package/dist/context/taskContext.js +100 -16
- package/dist/controlPlaneCli.js +17 -7
- package/dist/controller/agentRuntimeObserver.js +57 -12
- package/dist/controller/clientRuntime.js +66 -18
- package/dist/controller/controller.js +19 -2
- package/dist/controller/diagnosticJournal.js +10 -2
- package/dist/controller/fileSchedulerStoreAdapter.js +74 -24
- package/dist/controller/hostLifecycle.js +153 -0
- package/dist/controller/hostLifecycleChecks.js +27 -0
- package/dist/controller/hostUpgrade.js +344 -0
- package/dist/controller/messageHealth.js +4 -3
- package/dist/controller/providerRetryAdmission.js +15 -1
- package/dist/controller/providerRetryDelivery.js +18 -2
- package/dist/controller/runtime.js +82 -21
- package/dist/controller/runtimeEventInbox.js +107 -13
- package/dist/controller/runtimeEventProcessor.js +3 -3
- package/dist/controller/sessionNotify.js +5 -46
- package/dist/controller/taskRunRecovery.js +153 -0
- package/dist/core/controllerClient.js +16 -2
- package/dist/core/controllerServer.js +14 -5
- package/dist/core/controllerTelemetry.js +51 -5
- package/dist/errors/cliFailure.js +14 -5
- package/dist/execution/codexThreadNaming.js +54 -43
- package/dist/executor/agentAdapter.js +32 -4
- package/dist/executor/agentConfigurationFields.js +2 -0
- package/dist/executor/fileRoleLaunchPlanner.js +18 -3
- package/dist/integration/gitIntegrationService.js +3 -2
- package/dist/integration/integrationSourceApplication.js +12 -1
- package/dist/interaction/notificationPresentation.js +11 -16
- package/dist/interaction/operatorPresentation.js +1 -1
- package/dist/message/messageContinuation.js +3 -1
- package/dist/output/boundedRead.js +36 -7
- package/dist/output/rolePresentation.js +3 -1
- package/dist/runtime/agentEndpoint.js +11 -1
- package/dist/runtime/agentEndpointIdentity.js +2 -0
- package/dist/runtime/agentHost.js +174 -40
- package/dist/runtime/agentHostImplementation.js +3 -1
- package/dist/runtime/codexAppServerRuntime.js +113 -24
- package/dist/runtime/codexInteractiveHost.js +102 -11
- package/dist/runtime/codexNativeAccess.js +1 -1
- package/dist/runtime/contextRecovery.js +15 -0
- package/dist/runtime/hostSessionLifecycle.js +83 -0
- package/dist/runtime/hostUpgradeGate.js +43 -0
- package/dist/runtime/lifecycleChecks.js +47 -0
- package/dist/runtime/lifecycleReservation.js +9 -1
- package/dist/runtime/nativeTitleReceipt.js +31 -0
- package/dist/runtime/nativeTitleSync.js +192 -0
- package/dist/runtime/providerRetry.js +9 -6
- package/dist/runtime/providerRuntimeIdentity.js +1 -1
- package/dist/runtime/structuredProviderHost.js +65 -35
- package/dist/scheduler/wakeupQueue.js +10 -4
- package/dist/storage/sqliteSchema.js +29 -0
- package/dist/storage/storageVersions.js +1 -1
- package/dist/tmux/tmuxManager.js +4 -2
- package/dist/web/webSettings.js +2 -0
- package/docs/authorization.md +18 -3
- package/docs/cli-information-contract.md +228 -6
- package/docs/cli-information-contract.zh-CN.md +95 -5
- package/docs/managed-turn-and-session-runtime.md +177 -21
- package/docs/managed-turn-and-session-runtime.zh-CN.md +126 -14
- package/docs/provider-retry.md +11 -0
- package/docs/release-workflow.md +65 -0
- package/docs/release-workflow.zh-CN.md +48 -0
- package/docs/roles-and-configuration.md +28 -0
- package/docs/roles-and-configuration.zh-CN.md +21 -0
- package/docs/storage-baseline.md +13 -1
- package/docs/storage-baseline.zh-CN.md +5 -1
- package/docs/task-delivery.md +7 -0
- package/docs/task-delivery.zh-CN.md +5 -0
- package/docs/testing/review-evaluation.md +7 -7
- package/docs/testing/review-evaluation.zh-CN.md +7 -7
- package/docs/testing/verification-levels.md +45 -2
- package/docs/testing/verification-levels.zh-CN.md +21 -2
- package/i18n/README.zh-CN.md +6 -0
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +98 -193
- package/skills/yui-leader/VERSION +1 -1
- package/skills/yui-leader/references/artifacts.md +41 -0
- package/skills/yui-leader/references/assignment-execution.md +140 -0
- package/skills/yui-leader/references/completion.md +79 -0
- package/skills/yui-leader/references/delivery-scope.md +26 -0
- package/skills/yui-leader/references/execution.md +16 -487
- package/skills/yui-leader/references/implementation.md +81 -0
- package/skills/yui-leader/references/planning.md +7 -45
- package/skills/yui-leader/references/review.md +90 -0
- package/skills/yui-leader/references/task-progress.md +23 -0
- package/skills/yui-leader/references/work-allocation.md +118 -0
- package/skills/yui-operator/SKILL.md +77 -333
- package/skills/yui-operator/VERSION +1 -1
- package/skills/yui-operator/references/body-transport.md +62 -0
- package/skills/yui-operator/references/configuration.md +47 -0
- package/skills/yui-operator/references/lifecycle.md +41 -0
- package/skills/yui-operator/references/progress.md +39 -0
- package/skills/yui-operator/references/routing.md +57 -0
- package/skills/yui-operator/references/submission.md +56 -0
- package/skills/yui-operator/references/task-delivery.md +1 -1
- package/skills/yui-operator/references/web-lifecycle.md +19 -0
- package/skills/yui-operator/scripts/verify-submission.mjs +37 -0
- package/skills/yui-runtime/SKILL.md +182 -531
- package/skills/yui-runtime/VERSION +1 -1
- package/skills/yui-runtime/references/archive.md +51 -0
- package/skills/yui-runtime/references/command-discovery.md +41 -0
- package/skills/yui-runtime/references/completed-followup.md +65 -0
- package/skills/yui-runtime/references/context-ordering.md +3 -34
- package/skills/yui-runtime/references/context-reads.md +61 -0
- package/skills/yui-runtime/references/dispatched-run.md +61 -0
- package/skills/yui-runtime/references/execution-failure.md +105 -0
- package/skills/yui-runtime/references/global-context.md +18 -0
- package/skills/yui-runtime/references/global-input-controls.md +44 -0
- package/skills/yui-runtime/references/host-custody.md +30 -0
- package/skills/yui-runtime/references/inbox-recovery.md +77 -0
- package/skills/yui-runtime/references/incremental-context.md +40 -0
- package/skills/yui-runtime/references/publication.md +4 -111
- package/skills/yui-runtime/references/recovery.md +32 -192
- package/skills/yui-runtime/references/session-identity.md +18 -0
- package/skills/yui-runtime/references/session-replacement.md +45 -0
- package/skills/yui-runtime/references/uncertain-execution.md +29 -0
|
@@ -19,6 +19,10 @@ export function taskRoleHostDiagnostic(host) {
|
|
|
19
19
|
const delivery = snapshot.eventDelivery;
|
|
20
20
|
return [
|
|
21
21
|
`Provider Host=${snapshot.state}`,
|
|
22
|
+
`loaded Host=${snapshot.hostImplementation?.generation ?? "unreported"}`,
|
|
23
|
+
`loaded Endpoint=${snapshot.endpointImplementation?.generation ?? "unreported"}`,
|
|
24
|
+
snapshot.hostUpgrade === undefined ? undefined
|
|
25
|
+
: `Host upgrade=${snapshot.hostUpgrade.state} (${snapshot.hostUpgrade.requestId})${snapshot.hostUpgrade.detail ? `: ${snapshot.hostUpgrade.detail}` : ""}`,
|
|
22
26
|
`terminal reconciliation=${snapshot.controlCapabilities?.includes("reconcile-turn") ? "supported" : "unreported/unsupported"}`,
|
|
23
27
|
delivery === undefined ? "event delivery=unreported" :
|
|
24
28
|
`pending facts=${delivery.pending}; native terminals awaiting acknowledgement=${delivery.pendingTerminals}`,
|
|
@@ -49,6 +53,16 @@ export function withTaskRoleHostObservation(status, host) {
|
|
|
49
53
|
};
|
|
50
54
|
}
|
|
51
55
|
export function inspectTaskRoleRuntimeStatuses(taskId, roles, store, panes, now = new Date()) {
|
|
56
|
+
if (roles.length === 0)
|
|
57
|
+
return [];
|
|
58
|
+
// One inspection shares the same Task history across Roles. This is local
|
|
59
|
+
// read reuse, not a cache of runtime health or provider observations.
|
|
60
|
+
const events = store.listEvents(taskId);
|
|
61
|
+
const history = {
|
|
62
|
+
events,
|
|
63
|
+
runs: operationalTaskRecords(store.listRuns(taskId), events, "run"),
|
|
64
|
+
workspaces: store.listManagedWorkspaces(taskId)
|
|
65
|
+
};
|
|
52
66
|
const taskOpenInputRequestCount = store.listInputRequests(taskId)
|
|
53
67
|
.filter((request) => request.status === "open").length;
|
|
54
68
|
const panesByRole = new Map();
|
|
@@ -57,7 +71,7 @@ export function inspectTaskRoleRuntimeStatuses(taskId, roles, store, panes, now
|
|
|
57
71
|
if (current === undefined || current.dead && !pane.dead)
|
|
58
72
|
panesByRole.set(pane.roleName, pane);
|
|
59
73
|
}
|
|
60
|
-
return roles.map((role) => inspectTaskRoleRuntimeStatus(taskId, role, store, panesByRole.get(role.name), role.name === "leader" ? taskOpenInputRequestCount : 0, now));
|
|
74
|
+
return roles.map((role) => inspectTaskRoleRuntimeStatus(taskId, role, store, panesByRole.get(role.name), role.name === "leader" ? taskOpenInputRequestCount : 0, now, history));
|
|
61
75
|
}
|
|
62
76
|
export function renderTaskRoleRuntimeStatus(status) {
|
|
63
77
|
const activeRun = status.activeRun === null
|
|
@@ -185,12 +199,12 @@ export function taskRoleTmuxLabel(status) {
|
|
|
185
199
|
? "running"
|
|
186
200
|
: `running (${status.tmux.currentCommand})`;
|
|
187
201
|
}
|
|
188
|
-
function inspectTaskRoleRuntimeStatus(taskId, role, store, pane, openInputRequestCount, now) {
|
|
202
|
+
function inspectTaskRoleRuntimeStatus(taskId, role, store, pane, openInputRequestCount, now, history) {
|
|
189
203
|
const activeRun = store.getActiveRun(taskId, role.name);
|
|
190
204
|
// The last AgentRun outcome is a separate axis from the Session lifecycle. A
|
|
191
205
|
// Session that stops after its AgentRun completed must not retroactively turn
|
|
192
206
|
// that AgentRun into a failure; the status display keeps both visible.
|
|
193
|
-
const lastRun =
|
|
207
|
+
const lastRun = history.runs
|
|
194
208
|
.filter((candidate) => candidate.roleName === role.name)
|
|
195
209
|
.sort((left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt))[0]
|
|
196
210
|
?? null;
|
|
@@ -221,12 +235,12 @@ function inspectTaskRoleRuntimeStatus(taskId, role, store, pane, openInputReques
|
|
|
221
235
|
// distinct from the WorkItem Develop workspace.
|
|
222
236
|
const actualWorkspaceRoot = effectiveLaunch?.workspace.root ?? role.workspace;
|
|
223
237
|
const managedWorkspace = activeRun?.workspace
|
|
224
|
-
??
|
|
238
|
+
?? history.workspaces.find(({ root }) => root === actualWorkspaceRoot)
|
|
225
239
|
?? null;
|
|
226
240
|
const workspace = managedWorkspace === null
|
|
227
241
|
? { managed: false, path: actualWorkspaceRoot }
|
|
228
242
|
: { ...managedWorkspace, managed: true };
|
|
229
|
-
const events =
|
|
243
|
+
const { events } = history;
|
|
230
244
|
const sessionTokens = projectSessionTokenMetrics(events, resolveSessionTokenIdentity(nativeSession === null
|
|
231
245
|
? null
|
|
232
246
|
: { taskId, roleName: role.name, ...nativeSession }));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { usageError } from "../errors/cliError.js";
|
|
3
|
-
export function readCommandText(inline, file, label, usage) {
|
|
3
|
+
export function readCommandText(inline, file, label, usage, options = {}) {
|
|
4
4
|
if ((inline === undefined) === (file === undefined)) {
|
|
5
5
|
throw usageError(`Specify exactly one of ${label} or ${label}-file.`, usage);
|
|
6
6
|
}
|
|
@@ -11,5 +11,5 @@ export function readCommandText(inline, file, label, usage) {
|
|
|
11
11
|
if (normalized.length === 0 || normalized.includes("\0")) {
|
|
12
12
|
throw usageError(`${label} is required.`, usage);
|
|
13
13
|
}
|
|
14
|
-
return normalized;
|
|
14
|
+
return file !== undefined && options.preserveFileWhitespace ? value : normalized;
|
|
15
15
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Public current-Context family names, shared by reads and CLI discovery.
|
|
2
|
+
* Listing a family grants no permission to read its records.
|
|
3
|
+
*/
|
|
4
|
+
export const CONTEXT_FAMILIES = Object.freeze([
|
|
5
|
+
"task", "task-brief", "remote-delivery", "role", "role-profile",
|
|
6
|
+
"project-policy", "project-knowledge", "input-request", "work-item",
|
|
7
|
+
"accepted-work-item", "candidate", "environment-preparation", "managed-workspace",
|
|
8
|
+
"change-set", "task-message", "run", "source-run", "review-round", "mailbox",
|
|
9
|
+
"task-decision", "task-milestone", "job", "publication", "task-event"
|
|
10
|
+
]);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { accessSync, constants, lstatSync, readFileSync, realpathSync } from "node:fs";
|
|
2
|
+
import { access, lstat, readFile, realpath } from "node:fs/promises";
|
|
2
3
|
import { join, resolve } from "node:path";
|
|
3
4
|
import { fileURLToPath } from "node:url";
|
|
4
5
|
export const BUILTIN_YUI_SKILLS = new Set([
|
|
@@ -41,8 +42,40 @@ export function checkCurrentSkills(home, ids) {
|
|
|
41
42
|
}
|
|
42
43
|
});
|
|
43
44
|
}
|
|
45
|
+
/** Advisory lifecycle observation uses asynchronous filesystem I/O; it neither
|
|
46
|
+
* blocks a state transition nor substitutes for the exact per-input check. */
|
|
47
|
+
export async function checkCurrentSkillsAsync(home, ids, signal) {
|
|
48
|
+
const skills = [];
|
|
49
|
+
for (const id of new Set(ids)) {
|
|
50
|
+
signal.throwIfAborted();
|
|
51
|
+
const path = currentSkillPath(home, id);
|
|
52
|
+
const versionPath = join(path, "VERSION");
|
|
53
|
+
try {
|
|
54
|
+
for (const file of [join(path, "SKILL.md"), versionPath]) {
|
|
55
|
+
if (await realpath(file) !== resolve(file) || !(await lstat(file)).isFile()) {
|
|
56
|
+
throw new Error(`Skill files must be regular files without symlinks: ${file}.`);
|
|
57
|
+
}
|
|
58
|
+
await access(file, constants.R_OK);
|
|
59
|
+
}
|
|
60
|
+
if ((await lstat(versionPath)).size > 128)
|
|
61
|
+
throw new Error("VERSION exceeds 128 bytes.");
|
|
62
|
+
const version = (await readFile(versionPath, { encoding: "utf8", signal })).trim();
|
|
63
|
+
if (!/^[1-9][0-9]{0,8}$/u.test(version))
|
|
64
|
+
throw new Error("VERSION must contain a positive internal integer.");
|
|
65
|
+
skills.push({ id, path, version, versionPath });
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
throw new Error(`Current Skill ${id} is missing, unreadable or invalid at ${path}. `
|
|
69
|
+
+ `${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return skills;
|
|
73
|
+
}
|
|
44
74
|
export const SKILL_CHECK_COMMAND = "yui session skills --json";
|
|
45
|
-
export const SKILL_REUSE_RULE = "
|
|
46
|
-
+ "
|
|
47
|
-
+ "or
|
|
48
|
-
+ "
|
|
75
|
+
export const SKILL_REUSE_RULE = "The managed Host checks current Skill versions/availability before each input; "
|
|
76
|
+
+ "unchanged inputs carry no notice or marker, and do not require a duplicate check. "
|
|
77
|
+
+ "For unmediated clients or uncertain current input routing, check through the Manifest's command; conversation reuse alone does not establish Host mediation. Keep routine checks internal, not user-facing progress. "
|
|
78
|
+
+ "Reuse unchanged full instructions in context; messages alone require no reload. "
|
|
79
|
+
+ "Read affected entries/references fully on change or localized loss. On first entry, recovery or lost context, "
|
|
80
|
+
+ "rebuild the current working set from applicable Skill entry points and action-relevant references, not all past procedures. "
|
|
81
|
+
+ "Metadata proves neither loading, memory nor authority.";
|
|
@@ -371,9 +371,11 @@ export function runContextEntry(pack) {
|
|
|
371
371
|
const { skillPackages: _packages, pointers: _pointers, deltaRefs, liveTaskState, input, ...identity } = pack;
|
|
372
372
|
const { directive, ...inputIdentity } = input;
|
|
373
373
|
const { items, ...pointerPage } = runContextPointerPage(pack, { limit: 16 });
|
|
374
|
+
const directiveSummary = directive === undefined ? undefined : summaryText(directive);
|
|
374
375
|
return {
|
|
375
376
|
...identity, input: { ...inputIdentity, ...(directive === undefined ? {} : {
|
|
376
|
-
directiveSummary
|
|
377
|
+
...(directiveSummary === directive ? { directive, directiveOmitted: false }
|
|
378
|
+
: { directiveSummary, directiveOmitted: true })
|
|
377
379
|
}) }, pointers: items, pointerPage,
|
|
378
380
|
deltaRefs: deltaRefs.slice(0, 16), deltaRefsTotal: deltaRefs.length,
|
|
379
381
|
inventory: { ...pack.inventory, complete: pointerPage.complete },
|
|
@@ -386,9 +388,16 @@ export function runContextEntry(pack) {
|
|
|
386
388
|
},
|
|
387
389
|
reads: {
|
|
388
390
|
inbox: "yui inbox list --view current --json",
|
|
389
|
-
|
|
391
|
+
...(items.length > 0 ? {
|
|
392
|
+
expand: `yui task run context expand ${pack.identity.taskId}/${pack.identity.runId} --context-ref <store/refId> [--context-ref <store/refId> ...] --mode full --json`
|
|
393
|
+
} : {}),
|
|
394
|
+
...(!pointerPage.complete ? {
|
|
395
|
+
refs: `yui task run context ${pack.identity.taskId}/${pack.identity.runId} --view refs`
|
|
396
|
+
} : {}),
|
|
390
397
|
full: `yui task run context ${pack.identity.taskId}/${pack.identity.runId} --view full`,
|
|
391
|
-
|
|
398
|
+
...(directiveSummary !== directive ? {
|
|
399
|
+
input: `yui task run context ${pack.identity.taskId}/${pack.identity.runId} --view input`
|
|
400
|
+
} : {}),
|
|
392
401
|
live: `yui task context ${pack.identity.taskId}`
|
|
393
402
|
}
|
|
394
403
|
};
|
|
@@ -413,7 +422,7 @@ function readLiveTaskState(store, taskId) {
|
|
|
413
422
|
}));
|
|
414
423
|
return Object.freeze({
|
|
415
424
|
providerRetries: Object.freeze(store.listRoleSessionSets(taskId).flatMap(set => set.providerBinding?.retry === undefined
|
|
416
|
-
? [] : [{ roleName: set.owner.roleName, retry: providerRetrySummary(set.providerBinding) }])),
|
|
425
|
+
? [] : [{ roleName: set.owner.roleName, retry: providerRetrySummary(set.providerBinding, `yui task role session retry ${taskId} ${set.owner.roleName} show --json`) }])),
|
|
417
426
|
activeRuns: Object.freeze(activeRuns),
|
|
418
427
|
activeTaskReviews: Object.freeze(activeTaskReviews)
|
|
419
428
|
});
|
|
@@ -422,6 +431,34 @@ export function expandRunContextRef(store, taskId, runId, refId, refStore) {
|
|
|
422
431
|
requireIdentity(refStore, "Context ref store");
|
|
423
432
|
const run = requireExactRun(store, taskId, runId);
|
|
424
433
|
const snapshot = readRunContextSnapshot(store, run);
|
|
434
|
+
return expandSnapshotRef(snapshot, refId, refStore);
|
|
435
|
+
}
|
|
436
|
+
/** Explicit originals from one already-authorized frozen Run, not live Task
|
|
437
|
+
* discovery. Read the snapshot once and reject the entire selection on drift. */
|
|
438
|
+
export function expandRunContextRefs(store, taskId, runId, selectors) {
|
|
439
|
+
if (selectors.length < 1 || selectors.length > 8)
|
|
440
|
+
throw new Error("Run Context batch requires 1..8 exact references.");
|
|
441
|
+
const seen = new Set();
|
|
442
|
+
for (const ref of selectors) {
|
|
443
|
+
requireIdentity(ref.store, "Context ref store");
|
|
444
|
+
requireIdentity(ref.refId, "Context ref id");
|
|
445
|
+
if (ref.digest !== undefined && !/^[a-f0-9]{64}$/.test(ref.digest))
|
|
446
|
+
throw new Error("An explicit Run Context digest must be exact.");
|
|
447
|
+
const key = JSON.stringify([ref.store, ref.refId]);
|
|
448
|
+
if (seen.has(key))
|
|
449
|
+
throw new Error("Duplicate Run Context batch reference.");
|
|
450
|
+
seen.add(key);
|
|
451
|
+
}
|
|
452
|
+
const snapshot = readRunContextSnapshot(store, requireExactRun(store, taskId, runId));
|
|
453
|
+
const items = selectors.map(ref => {
|
|
454
|
+
const expanded = expandSnapshotRef(snapshot, ref.refId, ref.store);
|
|
455
|
+
if (ref.digest !== undefined && expanded.ref.digest !== ref.digest)
|
|
456
|
+
throw new Error(`Run Context reference digest does not match its frozen Snapshot: ${ref.store}/${ref.refId}.`);
|
|
457
|
+
return expanded;
|
|
458
|
+
});
|
|
459
|
+
return { taskId, runId, items };
|
|
460
|
+
}
|
|
461
|
+
function expandSnapshotRef(snapshot, refId, refStore) {
|
|
425
462
|
const authorized = snapshot.refs.filter((ref) => (ref.refId === refId && ref.store === refStore));
|
|
426
463
|
const selector = `${refStore}/${refId}`;
|
|
427
464
|
if (authorized.length !== 1) {
|
|
@@ -770,7 +807,9 @@ function materialize(layer, store, refId, value) {
|
|
|
770
807
|
summary: (store === "project-knowledge"
|
|
771
808
|
? [title, `version: ${record.version}`, ...(record.scope === undefined ? [] : [`scope: ${record.scope}`]),
|
|
772
809
|
...(record.expiresWhen === undefined ? [] : [`expiresWhen: ${record.expiresWhen}`])].join("; ")
|
|
773
|
-
:
|
|
810
|
+
: store === "task-message" && typeof record.body === "string"
|
|
811
|
+
? summaryText(`Excerpt: ${record.body}`, 160)
|
|
812
|
+
: title).slice(0, 400)
|
|
774
813
|
}),
|
|
775
814
|
value
|
|
776
815
|
});
|
|
@@ -79,7 +79,7 @@ export function serializeRunInputEnvelope(value) {
|
|
|
79
79
|
.join(",");
|
|
80
80
|
const snapshot = normalized.contextSnapshotRef;
|
|
81
81
|
const lines = [
|
|
82
|
-
"Yui AgentRun input.
|
|
82
|
+
"Yui AgentRun input. Follow the Session Manifest profile and current Skills; use the Host check when supplied.",
|
|
83
83
|
`${normalized.subject.taskId === undefined ? "" : `task=${normalized.subject.taskId} `}run=${normalized.runId} role=${normalized.roleName}`,
|
|
84
84
|
`source=${normalized.source.type}/${normalized.source.channel} purpose=${normalized.purpose} subject=${subject || "global"} snapshot=${snapshot === undefined ? "none" : `${snapshot.id}@${snapshot.digest}`}`,
|
|
85
85
|
normalized.deltaRefIds.length === 0
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { chmodSync, readFileSync } from "node:fs";
|
|
3
|
+
import { readFile } from "node:fs/promises";
|
|
3
4
|
import { join, resolve } from "node:path";
|
|
4
5
|
import { writeTextFileAtomically } from "../storage/durableFile.js";
|
|
5
6
|
import { validateSkillPackageRef } from "./skillPackage.js";
|
|
6
|
-
import { checkCurrentSkills, SKILL_CHECK_COMMAND, SKILL_REUSE_RULE } from "./currentSkills.js";
|
|
7
|
+
import { checkCurrentSkills, checkCurrentSkillsAsync, SKILL_CHECK_COMMAND, SKILL_REUSE_RULE } from "./currentSkills.js";
|
|
7
8
|
import { SESSION_BOOTSTRAP_MANIFEST_SCHEMA_VERSION, SESSION_CONTEXT_PROTOCOL, sessionManifestCompatibilityDigest } from "./sessionProtocolIdentity.js";
|
|
8
9
|
export { SESSION_BOOTSTRAP_MANIFEST_SCHEMA_VERSION, SESSION_CONTEXT_PROTOCOL, sessionManifestCompatibilityDigest } from "./sessionProtocolIdentity.js";
|
|
9
10
|
/** Portable read pointers accompany every managed input, including the first
|
|
@@ -18,13 +19,15 @@ export function withSessionContextPointer(text, environment) {
|
|
|
18
19
|
text
|
|
19
20
|
].join("\n");
|
|
20
21
|
}
|
|
21
|
-
/**
|
|
22
|
+
/** Host metadata checks do not prove instructions survived context compaction.
|
|
23
|
+
* Unmediated/native clients retain the explicit diagnostic fallback. */
|
|
22
24
|
export function sessionContextInstructions(manifestPath, digest) {
|
|
23
25
|
return `Yui Session Manifest: ${manifestPath}${digest === undefined ? "" : ` (digest ${digest})`}. `
|
|
24
|
-
+
|
|
25
|
-
+ `(
|
|
26
|
-
+ "
|
|
27
|
-
+
|
|
26
|
+
+ "The managed Host checks Skills internally and injects guidance only when loading is needed; absence of a notice does not require another check. "
|
|
27
|
+
+ `For unmediated clients or uncertain current input routing, run its contextProtocol.skillCheckCommand (fallback: ${SKILL_CHECK_COMMAND}); conversation reuse alone does not establish Host mediation. `
|
|
28
|
+
+ "On first entry or lost instructions, read the current applicable Skill entry points, then only references needed for the current action. "
|
|
29
|
+
+ "On change reload affected instructions. Compaction does not require restoring every previously used procedure. "
|
|
30
|
+
+ "Preserve the Manifest Role profile; metadata never proves loading or memory. Keep routine role, version and empty-inbox checks internal.";
|
|
28
31
|
}
|
|
29
32
|
/** The caller must already pass the ordinary managed Session/CLI/Home checks.
|
|
30
33
|
* Old manifests remain immutable audit records, while this read resolves their
|
|
@@ -38,6 +41,15 @@ export function checkSessionSkills(home, manifestPath) {
|
|
|
38
41
|
historicalPackagesAreAuditOnly: true
|
|
39
42
|
};
|
|
40
43
|
}
|
|
44
|
+
export async function checkSessionSkillsAsync(home, manifestPath, signal) {
|
|
45
|
+
const manifest = validateSessionBootstrapManifest(JSON.parse(await readFile(resolve(manifestPath), { encoding: "utf8", signal })));
|
|
46
|
+
return {
|
|
47
|
+
skills: await checkCurrentSkillsAsync(home, manifest.skills.map(skill => skill.id), signal),
|
|
48
|
+
roleProfileRef: manifest.roleProfileRef,
|
|
49
|
+
rule: SKILL_REUSE_RULE,
|
|
50
|
+
historicalPackagesAreAuditOnly: true
|
|
51
|
+
};
|
|
52
|
+
}
|
|
41
53
|
/** Read back one immutable Session Manifest and verify its content digest. */
|
|
42
54
|
export function readSessionBootstrapManifest(path) {
|
|
43
55
|
const source = resolve(path);
|
|
@@ -48,6 +60,9 @@ export function readSessionBootstrapManifest(path) {
|
|
|
48
60
|
catch (error) {
|
|
49
61
|
throw new Error(`Session Manifest is unreadable: ${source}.`, { cause: error });
|
|
50
62
|
}
|
|
63
|
+
return validateSessionBootstrapManifest(parsed);
|
|
64
|
+
}
|
|
65
|
+
function validateSessionBootstrapManifest(parsed) {
|
|
51
66
|
if (parsed === null || typeof parsed !== "object") {
|
|
52
67
|
throw new Error("Session Manifest is invalid.");
|
|
53
68
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { checkSessionSkills, checkSessionSkillsAsync } from "./sessionBootstrapManifest.js";
|
|
2
|
+
/** Early advisory checks do not commit notices or assert model memory. The
|
|
3
|
+
* next input still validates its exact metadata and emits only its own delta.
|
|
4
|
+
* Invalid metadata may reject that input, never a Session state transition. */
|
|
5
|
+
export function createSessionSkillLifecycleCheck(home, environment) {
|
|
6
|
+
return {
|
|
7
|
+
id: "session-skills",
|
|
8
|
+
run: async (transition, signal) => {
|
|
9
|
+
if (!["connected", "interactive-connected", "settled"].includes(transition.event.type))
|
|
10
|
+
return;
|
|
11
|
+
const manifest = environment().YUI_SESSION_MANIFEST;
|
|
12
|
+
if (!manifest || signal.aborted)
|
|
13
|
+
return;
|
|
14
|
+
try {
|
|
15
|
+
await checkSessionSkillsAsync(home, manifest, signal);
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
if (!signal.aborted)
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** Host-local last accepted metadata, never a model loading/memory receipt.
|
|
25
|
+
* A new Host/attachment starts cold. No durable schema or input replay is added. */
|
|
26
|
+
export function createSessionSkillNotice(home) {
|
|
27
|
+
let previous;
|
|
28
|
+
let generation = 0;
|
|
29
|
+
const invalidate = () => { previous = undefined; generation += 1; };
|
|
30
|
+
return {
|
|
31
|
+
invalidate,
|
|
32
|
+
prepare(text, environment, sessionKey, options = {}) {
|
|
33
|
+
const manifest = environment.YUI_SESSION_MANIFEST;
|
|
34
|
+
if (manifest === undefined)
|
|
35
|
+
return { text, inputText: text, commit() { } };
|
|
36
|
+
let current;
|
|
37
|
+
try {
|
|
38
|
+
current = checkSessionSkills(home, manifest);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
invalidate();
|
|
42
|
+
throw error; // Do not submit business input with an invalid current installation.
|
|
43
|
+
}
|
|
44
|
+
const key = JSON.stringify([sessionKey, manifest, current.roleProfileRef, options.contextEpoch]);
|
|
45
|
+
const skills = new Map(current.skills.map(skill => [skill.id, JSON.stringify({
|
|
46
|
+
id: skill.id, path: skill.path, version: skill.version
|
|
47
|
+
})]));
|
|
48
|
+
const cold = previous?.key !== key;
|
|
49
|
+
const changed = [...skills].filter(([id, value]) => cold || previous.skills.get(id) !== value)
|
|
50
|
+
.map(([, value]) => value);
|
|
51
|
+
const removed = !cold && [...previous.skills.keys()].some(id => !skills.has(id));
|
|
52
|
+
// Every input is checked, but only a lifecycle/metadata event needs an
|
|
53
|
+
// instruction-loading notice. Unchanged input is passed through exactly.
|
|
54
|
+
const lines = [];
|
|
55
|
+
if (cold || changed.length > 0 || removed) {
|
|
56
|
+
lines.push(`Yui Session Manifest: ${manifest}`, "Host-managed Skill checks are active; unchanged input has no notice. Detached/unmediated clients must use the Manifest check fallback.", cold
|
|
57
|
+
? `Read the exact Role profile at ${current.roleProfileRef.path} (not a Task projection) and all current Skill entry points below. Follow references only for the current action.`
|
|
58
|
+
: "Skill metadata changed: load affected entries and applicable references; retain other complete instructions.", `Current Skill pointers: [${(removed ? [...skills.values()] : changed).join(",")}]`, "Read required files through EOF. Metadata is not loaded instructions. Keep routine checks internal.");
|
|
59
|
+
}
|
|
60
|
+
const preparedGeneration = generation;
|
|
61
|
+
return {
|
|
62
|
+
text: lines.length === 0 ? text : `${lines.join("\n")}\n\n${text}`,
|
|
63
|
+
inputText: text,
|
|
64
|
+
...(lines.length === 0 ? {} : { context: lines.join("\n") }),
|
|
65
|
+
commit() {
|
|
66
|
+
if (generation !== preparedGeneration)
|
|
67
|
+
return;
|
|
68
|
+
previous = { key, skills };
|
|
69
|
+
generation += 1;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -2,6 +2,7 @@ import { runExecutionObservation } from "../agentRun/agentRun.js";
|
|
|
2
2
|
import { isGitArtifactRefString, parseGitArtifactRef } from "../artifacts/gitArtifactRef.js";
|
|
3
3
|
import { taskNotFound, usageError } from "../errors/cliError.js";
|
|
4
4
|
import { expandTaskMessageResult } from "../message/message.js";
|
|
5
|
+
import { taskMessagePresentation } from "../interaction/notificationPresentation.js";
|
|
5
6
|
import { currentManagedRuntime, resolveManagedTaskReader } from "../runtime/managedCaller.js";
|
|
6
7
|
import { hasManagedIdentity } from "../runtime/managedIdentity.js";
|
|
7
8
|
import { providerRetrySummary } from "../runtime/providerRetry.js";
|
|
@@ -10,13 +11,21 @@ import { retiredTaskRecordIds, TASK_RECORD_RETIRED_EVENT } from "../task/taskRec
|
|
|
10
11
|
import { managedWorkspaceKey } from "../worktree/managedWorkspace.js";
|
|
11
12
|
import { contextContentDigest } from "./contextSnapshot.js";
|
|
12
13
|
import { sourceRunContextValue } from "./sourceRunContext.js";
|
|
13
|
-
import { boundedDocument, fitsReadBudget, summaryText, recordPage } from "../output/boundedRead.js";
|
|
14
|
+
import { boundedDocument, fitsReadBudget, summaryText, recordPage, readDigest } from "../output/boundedRead.js";
|
|
14
15
|
import { taskBriefGuard, taskPhase } from "./taskPhase.js";
|
|
16
|
+
import { CONTEXT_FAMILIES } from "./contextFamilies.js";
|
|
15
17
|
const MAX_RECORDS = 64;
|
|
16
18
|
const MAX_VALUE_BYTES = 2048;
|
|
17
19
|
const MAX_PAGE_BYTES = 24 * 1024;
|
|
18
20
|
const MAX_EVENTS = 100;
|
|
19
21
|
const MAX_ATTENTION_REFS = 8;
|
|
22
|
+
// The current read is a working index, not a replay of configuration, workspace
|
|
23
|
+
// inventories and requirements on every wake. Exact bodies use the same scoped,
|
|
24
|
+
// digest-checked inspect path regardless of how small a record happens to be.
|
|
25
|
+
const INDEX_ONLY_FAMILIES = new Set([
|
|
26
|
+
"task", "task-brief", "remote-delivery", "role", "project-policy",
|
|
27
|
+
"managed-workspace", "task-decision", "task-message", "run", "work-item", "project-knowledge"
|
|
28
|
+
]);
|
|
20
29
|
/** A current read model, not a second snapshot store or a delivery receipt.
|
|
21
30
|
* Every public path checks the same scope before reading, counting or paging.
|
|
22
31
|
*/
|
|
@@ -44,8 +53,8 @@ export function readTaskContext(store, taskId, environment = {}) {
|
|
|
44
53
|
if (entry.ref.store === "run")
|
|
45
54
|
runtimeEvents ??= reader.listEventsByType(taskId, ["runtime.agent-error"]);
|
|
46
55
|
const valueBytes = Buffer.byteLength(JSON.stringify(entry.value));
|
|
47
|
-
const content = valueBytes > MAX_VALUE_BYTES ||
|
|
48
|
-
? { ref: entry.ref, summary:
|
|
56
|
+
const content = valueBytes > MAX_VALUE_BYTES || INDEX_ONLY_FAMILIES.has(entry.ref.store)
|
|
57
|
+
? { ref: entry.ref, summary: summarizeEntry(reader, entry), omitted: true }
|
|
49
58
|
: { ...entry, omitted: false };
|
|
50
59
|
const revisedAfterActivation = entry.ref.store === "task-message" && phaseSource?.ref !== undefined
|
|
51
60
|
&& reader.queryContextRecords(taskId, {
|
|
@@ -61,9 +70,9 @@ export function readTaskContext(store, taskId, environment = {}) {
|
|
|
61
70
|
? "recorded-before-activation-request" : "recorded-after-activation-request"
|
|
62
71
|
} : {}),
|
|
63
72
|
...(entry.ref.store === "run" ? {
|
|
64
|
-
execution:
|
|
73
|
+
execution: currentRunExecution(reader, entry.value, runtimeEvents)
|
|
65
74
|
} : {}), ...(entry.ref.store === "role" ? {
|
|
66
|
-
providerRetry: providerRetrySummary(reader.getTaskRoleSessionSet(taskId, entry.value.name)?.providerBinding)
|
|
75
|
+
providerRetry: providerRetrySummary(reader.getTaskRoleSessionSet(taskId, entry.value.name)?.providerBinding, `yui task role session retry ${taskId} ${entry.value.name} show --json`)
|
|
67
76
|
} : {}) };
|
|
68
77
|
const size = Buffer.byteLength(JSON.stringify(record));
|
|
69
78
|
if (bytes + size > MAX_PAGE_BYTES)
|
|
@@ -117,6 +126,15 @@ export function readTaskContext(store, taskId, environment = {}) {
|
|
|
117
126
|
return result;
|
|
118
127
|
});
|
|
119
128
|
}
|
|
129
|
+
/** Keep live delivery/attempt facts, but do not replay retry diagnostics and
|
|
130
|
+
* history into the working index. Exact Run inspect below retains the full
|
|
131
|
+
* observation, under the same authorization and document-pagination contract.
|
|
132
|
+
*/
|
|
133
|
+
function currentRunExecution(store, run, events) {
|
|
134
|
+
const binding = store.getTaskRoleSessionSet(run.taskId, run.roleName)?.providerBinding;
|
|
135
|
+
const execution = runExecutionObservation(run, binding, events);
|
|
136
|
+
return { ...execution, retry: execution.retry === null ? null : providerRetrySummary(binding, `yui task role session retry ${run.taskId} ${run.roleName} show --json`) };
|
|
137
|
+
}
|
|
120
138
|
function summarizeAttention(store, taskId) {
|
|
121
139
|
const group = () => ({ count: 0, refs: [], omittedRefs: 0 });
|
|
122
140
|
const attention = {
|
|
@@ -208,6 +226,43 @@ export function inspectTaskContext(store, taskId, selector, environment = {}) {
|
|
|
208
226
|
const response = readTaskContextResource(store, taskId, selector, environment);
|
|
209
227
|
return boundedDocument(response, `task:${taskId}/${selector.store}/${selector.refId}`, selector.cursor);
|
|
210
228
|
}
|
|
229
|
+
/** CLI spelling shared by explicit dispatch selection and grouped reading. */
|
|
230
|
+
export function parseExactContextRef(selector) {
|
|
231
|
+
const match = /^([^/]+)\/(.+)@([0-9a-f]{64})$/u.exec(selector);
|
|
232
|
+
if (match === null)
|
|
233
|
+
throw usageError(`Invalid required Context reference: ${selector}. Use store/refId@digest from the applicable Context pointers or list/inspect.`);
|
|
234
|
+
return { store: match[1], refId: match[2], digest: match[3] };
|
|
235
|
+
}
|
|
236
|
+
/** Explicit independent originals, one shared transport budget. Reuse the
|
|
237
|
+
* single-reference authorization/digest checks on every page. No implicit
|
|
238
|
+
* discovery, partial success, cached values or read/handling receipts.
|
|
239
|
+
*/
|
|
240
|
+
export function inspectTaskContextBatch(store, taskId, selectors, cursor, environment = {}) {
|
|
241
|
+
if (!Array.isArray(selectors) || selectors.length < 1 || selectors.length > 8)
|
|
242
|
+
throw usageError("Context batch requires 1..8 exact references.");
|
|
243
|
+
const seen = new Set();
|
|
244
|
+
const refs = selectors.map((value) => {
|
|
245
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
246
|
+
throw usageError("Context batch reference requires store, refId and digest.");
|
|
247
|
+
const ref = value;
|
|
248
|
+
if (Object.keys(ref).some(key => !["store", "refId", "digest", "revision"].includes(key))
|
|
249
|
+
|| typeof ref.store !== "string" || !ref.store
|
|
250
|
+
|| typeof ref.refId !== "string" || !ref.refId
|
|
251
|
+
|| typeof ref.digest !== "string" || !/^[a-f0-9]{64}$/.test(ref.digest)
|
|
252
|
+
|| ref.revision !== undefined && typeof ref.revision !== "string")
|
|
253
|
+
throw usageError("Context batch reference requires exact store, refId and digest; revision is optional metadata.");
|
|
254
|
+
const key = JSON.stringify([ref.store, ref.refId]);
|
|
255
|
+
if (seen.has(key))
|
|
256
|
+
throw usageError("Duplicate Context batch reference.");
|
|
257
|
+
seen.add(key);
|
|
258
|
+
return { store: ref.store, refId: ref.refId, digest: ref.digest };
|
|
259
|
+
});
|
|
260
|
+
// All-or-error: a stale/forbidden source cannot be silently skipped. Each
|
|
261
|
+
// source is independently digest-pinned; this is not a new cross-record
|
|
262
|
+
// snapshot. A continuation is additionally bound to the complete response.
|
|
263
|
+
const items = refs.map(ref => readTaskContextResource(store, taskId, ref, environment));
|
|
264
|
+
return boundedDocument({ taskId, items }, `task:${taskId}/context-batch:${readDigest(refs)}`, cursor);
|
|
265
|
+
}
|
|
211
266
|
/** The same authorized read as inspect, before transport pagination. */
|
|
212
267
|
export function readTaskContextResource(store, taskId, selector, environment = {}) {
|
|
213
268
|
return store.readTransaction((reader) => {
|
|
@@ -257,7 +312,7 @@ export function listTaskContext(store, taskId, family, options = {}, environment
|
|
|
257
312
|
|| options.workItemId !== undefined && value.workItemId !== options.workItemId
|
|
258
313
|
|| options.after !== undefined && Date.parse(String(value.createdAt)) <= Date.parse(options.after))
|
|
259
314
|
continue;
|
|
260
|
-
records.push({ ref: entry.ref, summary:
|
|
315
|
+
records.push({ ref: entry.ref, summary: summarizeEntry(reader, entry),
|
|
261
316
|
...(retired === undefined ? {} : { history: retired.has(entry.ref.refId) ? "retired" : "active" }),
|
|
262
317
|
...Object.fromEntries(["status", "kind", "type", "createdAt", "roleName", "assignee", "purpose", "workItemId", "reviewRoundId"]
|
|
263
318
|
.filter(key => typeof value[key] === "string").map(key => [key, value[key]])) });
|
|
@@ -269,13 +324,6 @@ export function listTaskContext(store, taskId, family, options = {}, environment
|
|
|
269
324
|
})}`, options);
|
|
270
325
|
});
|
|
271
326
|
}
|
|
272
|
-
export const CONTEXT_FAMILIES = [
|
|
273
|
-
"task", "task-brief", "remote-delivery", "role", "role-profile",
|
|
274
|
-
"project-policy", "project-knowledge", "input-request", "work-item",
|
|
275
|
-
"accepted-work-item", "candidate", "environment-preparation", "managed-workspace",
|
|
276
|
-
"change-set", "task-message", "run", "source-run", "review-round", "mailbox",
|
|
277
|
-
"task-decision", "task-milestone", "job", "publication", "task-event"
|
|
278
|
-
];
|
|
279
327
|
export function listContextMessages(store, taskId, environment = {}) {
|
|
280
328
|
return store.readTransaction((reader) => {
|
|
281
329
|
authorizeContext(reader, taskId, environment);
|
|
@@ -478,11 +526,21 @@ function authorizedSources(store, { task, caller }, current = false, requestedFa
|
|
|
478
526
|
return;
|
|
479
527
|
const selection = { ...options, family };
|
|
480
528
|
const { count } = store.queryContextRecords(taskId, { ...selection, limit: 0 });
|
|
529
|
+
// The bounded current index must not bury Task main behind alphabetically
|
|
530
|
+
// earlier integration/review history. Full discovery keeps its stable order.
|
|
531
|
+
const taskWorkspaceKey = managedWorkspaceKey({ type: "task", taskId });
|
|
532
|
+
const preferredWorkspace = current && !discovery && family === "managed-workspace"
|
|
533
|
+
? store.getManagedWorkspace({ type: "task", taskId }) : null;
|
|
481
534
|
sources.push({ family, count, *read(offset = 0) {
|
|
535
|
+
if (preferredWorkspace !== null)
|
|
536
|
+
yield materialize(family, taskWorkspaceKey, preferredWorkspace);
|
|
482
537
|
for (const { id, value } of store.queryContextRecords(taskId, {
|
|
483
538
|
...selection, offset, limit: current && !discovery ? 8 : MAX_RECORDS
|
|
484
|
-
}).records)
|
|
539
|
+
}).records) {
|
|
540
|
+
if (preferredWorkspace !== null && id === taskWorkspaceKey)
|
|
541
|
+
continue;
|
|
485
542
|
yield materialize(family, id, value);
|
|
543
|
+
}
|
|
486
544
|
} });
|
|
487
545
|
};
|
|
488
546
|
add("task", taskId, task);
|
|
@@ -517,10 +575,13 @@ function authorizedSources(store, { task, caller }, current = false, requestedFa
|
|
|
517
575
|
// artifacts remain reachable as commit-pinned pointers under their Candidate.
|
|
518
576
|
if (!current)
|
|
519
577
|
query("environment-preparation");
|
|
520
|
-
|
|
578
|
+
if (!current || discovery)
|
|
579
|
+
query("managed-workspace");
|
|
521
580
|
if (caller?.taskId === taskId && caller.currentRunId !== undefined) {
|
|
522
581
|
add("managed-workspace", `${taskId}/${caller.roleName}`, store.getRun(taskId, caller.currentRunId)?.workspace ?? null);
|
|
523
582
|
}
|
|
583
|
+
if (current && !discovery)
|
|
584
|
+
query("managed-workspace");
|
|
524
585
|
if (!current)
|
|
525
586
|
query("change-set");
|
|
526
587
|
query("task-message", { descending: true, unhandledMessages: current });
|
|
@@ -594,8 +655,31 @@ function freeze(value) {
|
|
|
594
655
|
}
|
|
595
656
|
function summarize(value) {
|
|
596
657
|
const record = value;
|
|
597
|
-
return summaryText(["title", "name", "activeAgentId", "scope", "expiresWhen", "version", "objective", "question", "summary", "body"
|
|
658
|
+
return summaryText(["title", "name", "status", "type", "currentFocus", "root", "activeAgentId", "scope", "expiresWhen", "version", "objective", "question", "summary", "body"]
|
|
598
659
|
.flatMap((key) => typeof record[key] === "string" || (key === "version" && typeof record[key] === "number")
|
|
599
660
|
? [`${key}: ${record[key]}`] : [])
|
|
600
661
|
.join("; "));
|
|
601
662
|
}
|
|
663
|
+
/** Reuse producer-authored navigation, never generate a verdict or copy a
|
|
664
|
+
* report. The authorized Message already permits its matching terminal result
|
|
665
|
+
* through exact inspect; unrelated/malformed links cannot supply summaries.
|
|
666
|
+
*/
|
|
667
|
+
function summarizeEntry(store, entry) {
|
|
668
|
+
if (entry.ref.store === "task-message") {
|
|
669
|
+
const message = entry.value;
|
|
670
|
+
const presentation = taskMessagePresentation(message, (taskId, runId) => {
|
|
671
|
+
const run = store.getRun(taskId, runId);
|
|
672
|
+
return run !== null && run.taskId === message.taskId
|
|
673
|
+
&& message.author.type === "role" && run.roleName === message.author.roleName
|
|
674
|
+
&& run.status !== "active" && run.result !== undefined ? run : null;
|
|
675
|
+
});
|
|
676
|
+
if (presentation.summary !== undefined) {
|
|
677
|
+
// Include attribution in the existing budget without clipping qualifiers.
|
|
678
|
+
const label = `Author summary (${presentation.summarySource})`;
|
|
679
|
+
const text = `${label}: ${presentation.summary}`;
|
|
680
|
+
return Buffer.byteLength(text) <= 512 ? text
|
|
681
|
+
: `${label} omitted: exceeds discovery budget; read original.`;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
return summarize(entry.value);
|
|
685
|
+
}
|