@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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { providerRetrySummary } from "../runtime/providerRetry.js";
|
|
2
2
|
import { operationalTaskRecords } from "../task/taskRecordRetirement.js";
|
|
3
3
|
import { TASK_COMPLETION_PUBLISHED_TREE_AUTHORIZED_EVENT } from "../task/publicationReference.js";
|
|
4
4
|
import { RUN_INPUT_MAX_DELTAS, requireRunContextSnapshotRef } from "./runInputContract.js";
|
|
@@ -9,6 +9,7 @@ import { formatGitArtifactRef, validateGitArtifactRef } from "../artifacts/gitAr
|
|
|
9
9
|
import { managedWorkspaceKey } from "../worktree/managedWorkspace.js";
|
|
10
10
|
import { contextContentDigest, contextSnapshotRef, createContextSnapshot, validateContextSnapshot } from "./contextSnapshot.js";
|
|
11
11
|
import { MAX_SYNTHESIS_SOURCE_RUNS, sourceRunContextValue } from "./sourceRunContext.js";
|
|
12
|
+
import { recordPage, summaryText } from "../output/boundedRead.js";
|
|
12
13
|
export const RUN_CONTEXT_PACK_SCHEMA_VERSION = 1;
|
|
13
14
|
function mergeRequiredContext(automatic, required, requiredPolicy = "current") {
|
|
14
15
|
const entries = new Map(automatic.map(entry => [`${entry.ref.store}/${entry.ref.refId}`, entry]));
|
|
@@ -344,6 +345,8 @@ export function buildRunContextPack(store, taskId, runId) {
|
|
|
344
345
|
const preliminaryBytes = Buffer.byteLength(JSON.stringify({ ...body, digest }), "utf8");
|
|
345
346
|
const pack = Object.freeze({
|
|
346
347
|
...body,
|
|
348
|
+
// Current loading guidance is not part of the frozen Assignment digest.
|
|
349
|
+
skillCheckCommand: "yui session skills --json",
|
|
347
350
|
inventory: Object.freeze({
|
|
348
351
|
refs: pointers.length,
|
|
349
352
|
frozenBytes: preliminaryBytes,
|
|
@@ -354,6 +357,41 @@ export function buildRunContextPack(store, taskId, runId) {
|
|
|
354
357
|
});
|
|
355
358
|
return pack;
|
|
356
359
|
}
|
|
360
|
+
/** The frozen pack remains authority. Entry is a bounded discovery projection;
|
|
361
|
+
* historical Skill packages and complete inventories are explicit reads only. */
|
|
362
|
+
export function runContextPointerPage(pack, options = {}) {
|
|
363
|
+
const priority = ["task", "work-item", "review-round", "role-profile"];
|
|
364
|
+
const pointers = [...pack.pointers].sort((a, b) => {
|
|
365
|
+
const rank = (store) => priority.includes(store) ? priority.indexOf(store) : priority.length;
|
|
366
|
+
return rank(a.store) - rank(b.store);
|
|
367
|
+
}).map(pointer => ({ ...pointer, summary: summaryText(pointer.summary ?? "") }));
|
|
368
|
+
return recordPage(pointers, `run:${pack.identity.taskId}/${pack.identity.runId}/refs/${pack.snapshot.digest}`, options);
|
|
369
|
+
}
|
|
370
|
+
export function runContextEntry(pack) {
|
|
371
|
+
const { skillPackages: _packages, pointers: _pointers, deltaRefs, liveTaskState, input, ...identity } = pack;
|
|
372
|
+
const { directive, ...inputIdentity } = input;
|
|
373
|
+
const { items, ...pointerPage } = runContextPointerPage(pack, { limit: 16 });
|
|
374
|
+
return {
|
|
375
|
+
...identity, input: { ...inputIdentity, ...(directive === undefined ? {} : {
|
|
376
|
+
directiveSummary: summaryText(directive), directiveOmitted: true
|
|
377
|
+
}) }, pointers: items, pointerPage,
|
|
378
|
+
deltaRefs: deltaRefs.slice(0, 16), deltaRefsTotal: deltaRefs.length,
|
|
379
|
+
inventory: { ...pack.inventory, complete: pointerPage.complete },
|
|
380
|
+
liveTaskState: {
|
|
381
|
+
providerRetries: liveTaskState.providerRetries.slice(0, 8),
|
|
382
|
+
activeRuns: liveTaskState.activeRuns.slice(0, 8),
|
|
383
|
+
activeTaskReviews: liveTaskState.activeTaskReviews.slice(0, 8),
|
|
384
|
+
totals: { providerRetries: liveTaskState.providerRetries.length,
|
|
385
|
+
activeRuns: liveTaskState.activeRuns.length, activeTaskReviews: liveTaskState.activeTaskReviews.length }
|
|
386
|
+
},
|
|
387
|
+
reads: {
|
|
388
|
+
refs: `yui task run context ${pack.identity.taskId}/${pack.identity.runId} --view refs`,
|
|
389
|
+
full: `yui task run context ${pack.identity.taskId}/${pack.identity.runId} --view full`,
|
|
390
|
+
input: `yui task run context ${pack.identity.taskId}/${pack.identity.runId} --view input`,
|
|
391
|
+
live: `yui task context ${pack.identity.taskId}`
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
}
|
|
357
395
|
/** Reads the Task's current in-flight execution, outside the frozen contract. */
|
|
358
396
|
function readLiveTaskState(store, taskId) {
|
|
359
397
|
const activeRuns = store.listActiveRuns(taskId)
|
|
@@ -374,7 +412,7 @@ function readLiveTaskState(store, taskId) {
|
|
|
374
412
|
}));
|
|
375
413
|
return Object.freeze({
|
|
376
414
|
providerRetries: Object.freeze(store.listRoleSessionSets(taskId).flatMap(set => set.providerBinding?.retry === undefined
|
|
377
|
-
? [] : [{ roleName: set.owner.roleName, retry:
|
|
415
|
+
? [] : [{ roleName: set.owner.roleName, retry: providerRetrySummary(set.providerBinding) }])),
|
|
378
416
|
activeRuns: Object.freeze(activeRuns),
|
|
379
417
|
activeTaskReviews: Object.freeze(activeTaskReviews)
|
|
380
418
|
});
|
|
@@ -445,9 +483,18 @@ function collectAuthorizedContext(store, run) {
|
|
|
445
483
|
const view = contextView(run);
|
|
446
484
|
const result = [materialize("L2", "task", task.id, task)];
|
|
447
485
|
const brief = store.getTaskBrief(task.id);
|
|
448
|
-
if (brief !== null && view === "leader") {
|
|
486
|
+
if (brief !== null && (view === "leader" || view === "reviewer")) {
|
|
449
487
|
result.push(materialize("L2", "task-brief", task.id, brief));
|
|
450
488
|
}
|
|
489
|
+
// Reviewers need the current material index and effective decisions, not
|
|
490
|
+
// only the original Task description. Freeze them with the candidate so
|
|
491
|
+
// later edits cannot silently change the review contract. Artifact bodies
|
|
492
|
+
// remain on-demand, commit-pinned reads; do not crawl prose or Task history.
|
|
493
|
+
if (view === "reviewer") {
|
|
494
|
+
for (const decision of store.listDecisions(task.id).filter(({ status }) => status === "active")) {
|
|
495
|
+
result.push(materialize("L2", "task-decision", decision.id, decision));
|
|
496
|
+
}
|
|
497
|
+
}
|
|
451
498
|
result.push(...collectRunContextOverlays(store, run));
|
|
452
499
|
result.push(...collectSourceRunContext(store, run));
|
|
453
500
|
if (run.workItemId !== undefined) {
|
|
@@ -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. Follow the Session Manifest and
|
|
82
|
+
"Yui AgentRun input. Check current Skills: yui session skills --json. Follow the Session Manifest profile and current Skills.",
|
|
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
|
|
@@ -3,6 +3,7 @@ import { chmodSync, readFileSync } from "node:fs";
|
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
4
|
import { writeTextFileAtomically } from "../storage/durableFile.js";
|
|
5
5
|
import { validateSkillPackageRef } from "./skillPackage.js";
|
|
6
|
+
import { checkCurrentSkills, SKILL_CHECK_COMMAND, SKILL_REUSE_RULE } from "./currentSkills.js";
|
|
6
7
|
import { SESSION_BOOTSTRAP_MANIFEST_SCHEMA_VERSION, SESSION_CONTEXT_PROTOCOL, sessionManifestCompatibilityDigest } from "./sessionProtocolIdentity.js";
|
|
7
8
|
export { SESSION_BOOTSTRAP_MANIFEST_SCHEMA_VERSION, SESSION_CONTEXT_PROTOCOL, sessionManifestCompatibilityDigest } from "./sessionProtocolIdentity.js";
|
|
8
9
|
/** Portable read pointers accompany every managed input, including the first
|
|
@@ -20,9 +21,22 @@ export function withSessionContextPointer(text, environment) {
|
|
|
20
21
|
/** No remembered/sent flag can prove instructions survived context compaction. */
|
|
21
22
|
export function sessionContextInstructions(manifestPath, digest) {
|
|
22
23
|
return `Yui Session Manifest: ${manifestPath}${digest === undefined ? "" : ` (digest ${digest})`}. `
|
|
23
|
-
+
|
|
24
|
-
+
|
|
25
|
-
+ "
|
|
24
|
+
+ `Before work on every turn run the Manifest's contextProtocol.skillCheckCommand `
|
|
25
|
+
+ `(older Manifests: ${SKILL_CHECK_COMMAND}; Global loadCommand also checks current Skills). `
|
|
26
|
+
+ "Use its current stable Skill paths instead of historical frozen-package paths; preserve the Manifest's Role profile. "
|
|
27
|
+
+ SKILL_REUSE_RULE;
|
|
28
|
+
}
|
|
29
|
+
/** The caller must already pass the ordinary managed Session/CLI/Home checks.
|
|
30
|
+
* Old manifests remain immutable audit records, while this read resolves their
|
|
31
|
+
* Skill IDs against the currently executing installation and Home. */
|
|
32
|
+
export function checkSessionSkills(home, manifestPath) {
|
|
33
|
+
const manifest = readSessionBootstrapManifest(manifestPath);
|
|
34
|
+
return {
|
|
35
|
+
skills: checkCurrentSkills(home, manifest.skills.map(skill => skill.id)),
|
|
36
|
+
roleProfileRef: manifest.roleProfileRef,
|
|
37
|
+
rule: SKILL_REUSE_RULE,
|
|
38
|
+
historicalPackagesAreAuditOnly: true
|
|
39
|
+
};
|
|
26
40
|
}
|
|
27
41
|
/** Read back one immutable Session Manifest and verify its content digest. */
|
|
28
42
|
export function readSessionBootstrapManifest(path) {
|
|
@@ -76,6 +90,10 @@ export function readSessionBootstrapManifest(path) {
|
|
|
76
90
|
requireText(entry.id, "Session Manifest Skill id");
|
|
77
91
|
requireText(entry.path, "Session Manifest Skill path");
|
|
78
92
|
requireDigest(entry.digest, "Session Manifest Skill digest");
|
|
93
|
+
if (entry.version !== undefined) {
|
|
94
|
+
requireText(entry.version, "Session Manifest Skill version");
|
|
95
|
+
requireText(entry.versionPath, "Session Manifest Skill version path");
|
|
96
|
+
}
|
|
79
97
|
if (entry.package !== undefined) {
|
|
80
98
|
const ref = entry.package;
|
|
81
99
|
validateSkillPackageRef(ref);
|
|
@@ -121,6 +139,7 @@ export function materializeSessionBootstrap(input) {
|
|
|
121
139
|
id: skill.id,
|
|
122
140
|
path: skill.path,
|
|
123
141
|
digest: skill.package?.digest ?? digest(skill.content),
|
|
142
|
+
...(skill.version === undefined ? {} : { version: skill.version, versionPath: skill.versionPath }),
|
|
124
143
|
...(skill.package === undefined ? {} : { package: skill.package })
|
|
125
144
|
})),
|
|
126
145
|
roleProfileRef: { digest: profileDigest, path: roleProfilePath },
|
|
@@ -129,11 +148,15 @@ export function materializeSessionBootstrap(input) {
|
|
|
129
148
|
// A Global Session may move between Yui's remote TUI and Desktop.
|
|
130
149
|
// Carry its read entry in the Thread-visible Manifest instead of
|
|
131
150
|
// depending on the client process that happened to create it.
|
|
132
|
-
loadCommand: renderGlobalContextCommand(input.entryPoint, home, input.role.name)
|
|
151
|
+
loadCommand: renderGlobalContextCommand(input.entryPoint, home, input.role.name),
|
|
152
|
+
// Same self-contained read remains usable from Desktop without the
|
|
153
|
+
// launching shell's managed environment or PATH.
|
|
154
|
+
skillCheckCommand: renderGlobalContextCommand(input.entryPoint, home, input.role.name)
|
|
133
155
|
}
|
|
134
156
|
: {
|
|
135
157
|
loadCommand: `yui task run context ${quoteShellWord(`${input.owner.taskId}/<run-id>`)} --json`,
|
|
136
158
|
expandCommand: `yui task run context expand ${quoteShellWord(`${input.owner.taskId}/<run-id>`)} <ref-id> --store <store> --mode full --json`,
|
|
159
|
+
skillCheckCommand: SKILL_CHECK_COMMAND,
|
|
137
160
|
...(input.role.name === "leader" ? {
|
|
138
161
|
currentCommand: `yui task context ${quoteShellWord(input.owner.taskId)} --json`
|
|
139
162
|
} : {})
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { lstatSync, readFileSync, readdirSync, realpathSync } from "node:fs";
|
|
3
3
|
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
4
|
import { verifySkillPackage } from "./skillPackage.js";
|
|
6
|
-
import { BUILTIN_YUI_SKILLS } from "./
|
|
5
|
+
import { BUILTIN_YUI_SKILLS, currentSkillPath } from "./currentSkills.js";
|
|
7
6
|
const SAFE_ID = /^[A-Za-z0-9][A-Za-z0-9_-]*$/u;
|
|
8
7
|
const MAX_TEXT = 256 * 1024;
|
|
9
8
|
const digest = (data) => createHash("sha256").update(data).digest("hex");
|
|
@@ -13,7 +12,7 @@ export function skillSource(home, id) {
|
|
|
13
12
|
throw new Error("Invalid Skill id.");
|
|
14
13
|
const builtin = BUILTIN_YUI_SKILLS.has(id);
|
|
15
14
|
return { kind: builtin ? "builtin" : "configured",
|
|
16
|
-
path:
|
|
15
|
+
path: currentSkillPath(home, id) };
|
|
17
16
|
}
|
|
18
17
|
function physical(path) {
|
|
19
18
|
if (realpathSync(path) !== resolve(path))
|
|
@@ -4,14 +4,14 @@ import { taskNotFound, usageError } from "../errors/cliError.js";
|
|
|
4
4
|
import { expandTaskMessageResult } from "../message/message.js";
|
|
5
5
|
import { resolveManagedTaskReader } from "../runtime/managedCaller.js";
|
|
6
6
|
import { hasManagedIdentity } from "../runtime/managedIdentity.js";
|
|
7
|
-
import {
|
|
7
|
+
import { providerRetrySummary } from "../runtime/providerRetry.js";
|
|
8
8
|
import { projectTaskRemoteDeliveryFromStore } from "../task/remoteDeliveryService.js";
|
|
9
9
|
import { retiredTaskRecordIds, TASK_RECORD_RETIRED_EVENT } from "../task/taskRecordRetirement.js";
|
|
10
10
|
import { managedWorkspaceKey } from "../worktree/managedWorkspace.js";
|
|
11
11
|
import { contextContentDigest } from "./contextSnapshot.js";
|
|
12
12
|
import { buildRunContextPack } from "./runContextPack.js";
|
|
13
13
|
import { sourceRunContextValue } from "./sourceRunContext.js";
|
|
14
|
-
import { boundedDocument, recordPage } from "../output/boundedRead.js";
|
|
14
|
+
import { boundedDocument, fitsReadBudget, summaryText, recordPage } from "../output/boundedRead.js";
|
|
15
15
|
const MAX_RECORDS = 64;
|
|
16
16
|
const MAX_VALUE_BYTES = 2048;
|
|
17
17
|
const MAX_PAGE_BYTES = 24 * 1024;
|
|
@@ -41,13 +41,13 @@ export function readTaskContext(store, taskId, environment = {}) {
|
|
|
41
41
|
if (entry.ref.store === "run")
|
|
42
42
|
runtimeEvents ??= reader.listEventsByType(taskId, ["runtime.agent-error"]);
|
|
43
43
|
const valueBytes = Buffer.byteLength(JSON.stringify(entry.value));
|
|
44
|
-
const content = valueBytes > MAX_VALUE_BYTES || ["task-message", "run", "work-item", "project-knowledge"].includes(entry.ref.store)
|
|
44
|
+
const content = valueBytes > MAX_VALUE_BYTES || ["role", "task-message", "run", "work-item", "project-knowledge"].includes(entry.ref.store)
|
|
45
45
|
? { ref: entry.ref, summary: summarize(entry.value), omitted: true }
|
|
46
46
|
: { ...entry, omitted: false };
|
|
47
47
|
const record = { ...content, ...(entry.ref.store === "run" ? {
|
|
48
48
|
execution: runExecutionObservation(entry.value, reader.getTaskRoleSessionSet(taskId, entry.value.roleName)?.providerBinding, runtimeEvents)
|
|
49
49
|
} : {}), ...(entry.ref.store === "role" ? {
|
|
50
|
-
providerRetry:
|
|
50
|
+
providerRetry: providerRetrySummary(reader.getTaskRoleSessionSet(taskId, entry.value.name)?.providerBinding)
|
|
51
51
|
} : {}) };
|
|
52
52
|
const size = Buffer.byteLength(JSON.stringify(record));
|
|
53
53
|
if (bytes + size > MAX_PAGE_BYTES)
|
|
@@ -55,7 +55,7 @@ export function readTaskContext(store, taskId, environment = {}) {
|
|
|
55
55
|
records.push(record);
|
|
56
56
|
bytes += size;
|
|
57
57
|
}
|
|
58
|
-
|
|
58
|
+
const result = {
|
|
59
59
|
taskId, coreCursor: encode(cursor), throughCursor: encode(cursor),
|
|
60
60
|
view: "current",
|
|
61
61
|
collections: Object.entries(sources.reduce((counts, source) => {
|
|
@@ -67,6 +67,8 @@ export function readTaskContext(store, taskId, environment = {}) {
|
|
|
67
67
|
discovery: `task context list ${taskId} --store <store>`,
|
|
68
68
|
commandDiscovery: {
|
|
69
69
|
help: "yui help", describe: "yui --json help",
|
|
70
|
+
skillCheck: "yui session skills --json",
|
|
71
|
+
ownInbox: "yui inbox list --json",
|
|
70
72
|
note: "Help and completion share the current caller's offline command view. Follow that view for operation-specific discovery; execution always revalidates authority."
|
|
71
73
|
},
|
|
72
74
|
detail: `task context inspect ${taskId} --store <store> --ref <refId> --digest <digest>`,
|
|
@@ -77,6 +79,18 @@ export function readTaskContext(store, taskId, environment = {}) {
|
|
|
77
79
|
limits: { records: MAX_RECORDS, valueBytes: MAX_VALUE_BYTES, pageBytes: 32 * 1024, recordBytes: MAX_PAGE_BYTES,
|
|
78
80
|
attentionRefsPerCategory: MAX_ATTENTION_REFS, attentionRefBytesPerCategory: MAX_VALUE_BYTES }
|
|
79
81
|
};
|
|
82
|
+
// Account for nested retry facts, collection counts, commands and JSON
|
|
83
|
+
// structure together. Exact omitted records stay discoverable by family.
|
|
84
|
+
while (!fitsReadBudget(result) && records.length > 0) {
|
|
85
|
+
records.pop();
|
|
86
|
+
result.collections.forEach(collection => {
|
|
87
|
+
collection.returned = records.filter(record => record.ref.store === collection.store).length;
|
|
88
|
+
});
|
|
89
|
+
result.omitted = { records: count - records.length, values: records.filter(record => record.omitted).length };
|
|
90
|
+
}
|
|
91
|
+
if (!fitsReadBudget(result))
|
|
92
|
+
throw usageError("Context identities exceed the response budget; use scoped discovery.");
|
|
93
|
+
return result;
|
|
80
94
|
});
|
|
81
95
|
}
|
|
82
96
|
function summarizeAttention(store, taskId, allow) {
|
|
@@ -91,7 +105,8 @@ function summarizeAttention(store, taskId, allow) {
|
|
|
91
105
|
["messages", "task-message", undefined]
|
|
92
106
|
]) {
|
|
93
107
|
const page = store.queryContextRecords(taskId, {
|
|
94
|
-
family, statuses, descending: category === "messages",
|
|
108
|
+
family, statuses, descending: category === "messages",
|
|
109
|
+
unhandledMessages: category === "messages", ids: allowedIds(allow, family), limit: MAX_ATTENTION_REFS
|
|
95
110
|
});
|
|
96
111
|
const summary = attention[category];
|
|
97
112
|
summary.count = page.count;
|
|
@@ -260,11 +275,11 @@ export async function withContextObservations(core, providers, timeoutMs = 250)
|
|
|
260
275
|
if (!["known", "partial", "unknown"].includes(value.coverage)
|
|
261
276
|
|| Buffer.byteLength(JSON.stringify(value)) > MAX_VALUE_BYTES)
|
|
262
277
|
throw new Error("unbounded observation");
|
|
263
|
-
return { source: provider.source
|
|
278
|
+
return { source: summaryText(provider.source, 200), observedAt: new Date().toISOString(),
|
|
264
279
|
coverage: value.coverage, status: "available", value: JSON.parse(JSON.stringify(value.value)) };
|
|
265
280
|
}
|
|
266
281
|
catch {
|
|
267
|
-
return { source: provider.source
|
|
282
|
+
return { source: summaryText(provider.source, 200), observedAt: new Date().toISOString(),
|
|
268
283
|
coverage: "unknown", status: "unavailable" };
|
|
269
284
|
}
|
|
270
285
|
finally {
|
|
@@ -273,7 +288,18 @@ export async function withContextObservations(core, providers, timeoutMs = 250)
|
|
|
273
288
|
controller.abort();
|
|
274
289
|
}
|
|
275
290
|
}));
|
|
276
|
-
|
|
291
|
+
const result = { ...core, observations: [] };
|
|
292
|
+
for (const observation of observations) {
|
|
293
|
+
result.observations.push(observation);
|
|
294
|
+
if (!fitsReadBudget(result)) {
|
|
295
|
+
result.observations.pop();
|
|
296
|
+
const { value: _value, ...identity } = observation;
|
|
297
|
+
result.observations.push({ ...identity, omitted: true });
|
|
298
|
+
if (!fitsReadBudget(result))
|
|
299
|
+
result.observations.pop();
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return { ...result, omittedObservations: observations.length - result.observations.length };
|
|
277
303
|
}
|
|
278
304
|
/** Shared current/historical Session read identity; discovery may only narrow it. */
|
|
279
305
|
export function resolveContextReader(store, environment) {
|
|
@@ -511,7 +537,7 @@ function authorizedSources(store, { task, allow, caller }, current = false, requ
|
|
|
511
537
|
}
|
|
512
538
|
if (!current)
|
|
513
539
|
query("change-set");
|
|
514
|
-
query("task-message", { descending: true });
|
|
540
|
+
query("task-message", { descending: true, unhandledMessages: current });
|
|
515
541
|
query("run", { descending: true, ...(current ? { statuses: ["active"] } : {}) });
|
|
516
542
|
for (const id of allowedIds(allow, "source-turn") ?? []) {
|
|
517
543
|
const run = store.getRun(taskId, id);
|
|
@@ -583,8 +609,8 @@ function freeze(value) {
|
|
|
583
609
|
}
|
|
584
610
|
function summarize(value) {
|
|
585
611
|
const record = value;
|
|
586
|
-
return ["title", "scope", "expiresWhen", "version", "objective", "question", "summary", "body", "status", "type"]
|
|
612
|
+
return summaryText(["title", "name", "activeAgentId", "scope", "expiresWhen", "version", "objective", "question", "summary", "body", "status", "type"]
|
|
587
613
|
.flatMap((key) => typeof record[key] === "string" || (key === "version" && typeof record[key] === "number")
|
|
588
614
|
? [`${key}: ${record[key]}`] : [])
|
|
589
|
-
.join("; ")
|
|
615
|
+
.join("; "));
|
|
590
616
|
}
|
package/dist/controlPlaneCli.js
CHANGED
|
@@ -22,7 +22,10 @@ import { allowsInteractiveSelection, resolveInteractiveArguments } from "./cli/i
|
|
|
22
22
|
import { routeInvocation } from "./cli/invocationRouter.js";
|
|
23
23
|
import { operatorOfflineCommand, taskDiagnosticTarget } from "./cli/managedDiagnostics.js";
|
|
24
24
|
import { assertConfigurationAuthority, assertTaskInvocationScope } from "./cli/invocationAuthority.js";
|
|
25
|
-
import { requireManagedGlobalCaller, ManagedRuntimeDriftError } from "./runtime/managedCaller.js";
|
|
25
|
+
import { requireManagedGlobalCaller, requireManagedGlobalReader, ManagedRuntimeDriftError } from "./runtime/managedCaller.js";
|
|
26
|
+
import { sessionCallerEnvironment, requireManagedCaller } from "./runtime/sessionCaller.js";
|
|
27
|
+
import { checkCurrentSkills, SKILL_REUSE_RULE } from "./context/currentSkills.js";
|
|
28
|
+
import { roleSkillIds } from "./context/roleSessionContext.js";
|
|
26
29
|
import { hasManagedIdentity } from "./runtime/managedIdentity.js";
|
|
27
30
|
import { resolveOperatorWizardArguments } from "./cli/operatorWizard.js";
|
|
28
31
|
import { resolveGlobalRoleAgentConfigurationArguments, resolveRoleWizardArguments } from "./cli/roleWizard.js";
|
|
@@ -38,6 +41,7 @@ import { runDurableJobCommand } from "./commands/durableJobCommands.js";
|
|
|
38
41
|
import { parseExecutionAuditOptions, runExecutionAuditCommand } from "./commands/executionAuditCommands.js";
|
|
39
42
|
import { runGlobalRoleCommand, previewGlobalRoleAgentConfigurationMutation } from "./commands/globalRoleCommands.js";
|
|
40
43
|
import { runJobCommand } from "./commands/jobCommands.js";
|
|
44
|
+
import { runInboxCommand } from "./commands/inboxCommands.js";
|
|
41
45
|
import { applyOperatorSessionControl, runOperatorCommand } from "./commands/operatorCommands.js";
|
|
42
46
|
import { previewProfileAgentConfigurationMutation, runProfileCommand } from "./commands/profileCommands.js";
|
|
43
47
|
import { runProjectCommand } from "./commands/projectCommands.js";
|
|
@@ -61,7 +65,8 @@ import { runWorkflowCommandAsync } from "./commands/workflowCommands.js";
|
|
|
61
65
|
import { FileCompletionManager } from "./completion/fileCompletionManager.js";
|
|
62
66
|
import { CONFIG_DOMAINS } from "./config/configCatalog.js";
|
|
63
67
|
import { resolveTmuxBin, resolveTmuxHistoryLimit } from "./config/yuiConfig.js";
|
|
64
|
-
import { readSessionBootstrapManifest } from "./context/sessionBootstrapManifest.js";
|
|
68
|
+
import { checkSessionSkills, readSessionBootstrapManifest } from "./context/sessionBootstrapManifest.js";
|
|
69
|
+
import { boundedDocument, readOptions } from "./output/boundedRead.js";
|
|
65
70
|
import { assertFileTaskControllerStorageCompatible, ensureFileTaskController, FileTaskWorkflowRuntime, refreshRunningFileTaskControllerConfiguration, refreshRunningFileTaskControllerEnvironment, restartFileTaskController, stopFileTaskController } from "./controller/clientRuntime.js";
|
|
66
71
|
import { FileSchedulerStoreAdapter } from "./controller/fileSchedulerStoreAdapter.js";
|
|
67
72
|
import { createControllerIntegrationJobPort } from "./controller/jobClient.js";
|
|
@@ -632,6 +637,11 @@ export async function main() {
|
|
|
632
637
|
return;
|
|
633
638
|
}
|
|
634
639
|
assertTaskInvocationScope(resolved, process.env);
|
|
640
|
+
if (resolved[0] === "inbox") {
|
|
641
|
+
const result = runInboxCommand(resolved.slice(1), store, process.env);
|
|
642
|
+
emit(JSON.stringify(result, null, 2), false, result);
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
635
645
|
if (resolved[0] === "task" && resolved[1] === "role" && resolved[2] === "capabilities") {
|
|
636
646
|
const query = parseTaskAgentCapabilityQuery(resolved.slice(3));
|
|
637
647
|
const result = await callController(home, "task.role-capabilities", {
|
|
@@ -789,6 +799,26 @@ export async function main() {
|
|
|
789
799
|
return;
|
|
790
800
|
}
|
|
791
801
|
if (resolved[0] === "session") {
|
|
802
|
+
if (resolved[1] === "skills") {
|
|
803
|
+
const parsed = readOptions(resolved.slice(2), ["--cursor"]);
|
|
804
|
+
if (parsed.positionals.length !== 0)
|
|
805
|
+
throw usageError("Usage: yui session skills [--cursor <cursor>] --json");
|
|
806
|
+
const path = process.env.YUI_SESSION_MANIFEST;
|
|
807
|
+
const caller = requireManagedCaller(store, process.env);
|
|
808
|
+
const set = caller.scope === "global" ? store.getGlobalRoleSessionSet(caller.roleName)
|
|
809
|
+
: store.getTaskRoleSessionSet(caller.taskId, caller.roleName);
|
|
810
|
+
const effective = set.sessions[caller.agentId].effective;
|
|
811
|
+
const kind = caller.scope === "global" ? caller.roleName === "operator" ? "operator" : "global"
|
|
812
|
+
: effective.reviewRoundId !== undefined ? "reviewer" : caller.roleName === "leader" ? "leader" : "worker";
|
|
813
|
+
const checked = path === undefined ? {
|
|
814
|
+
skills: checkCurrentSkills(home, roleSkillIds(effective.context, kind)),
|
|
815
|
+
profile: effective.context, identity: caller, rule: SKILL_REUSE_RULE,
|
|
816
|
+
historicalPackagesAreAuditOnly: true
|
|
817
|
+
} : checkSessionSkills(home, path);
|
|
818
|
+
const data = boundedDocument(checked, "session:current-skills", parsed.cursor);
|
|
819
|
+
emit(JSON.stringify(data, null, 2), false, data);
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
792
822
|
if (resolved[1] === "stop") {
|
|
793
823
|
const options = parseSessionStopOptions(resolved.slice(2));
|
|
794
824
|
const result = await runSessionStopCommand({
|
|
@@ -1969,6 +1999,19 @@ async function preflightManagedTaskControlPlane() {
|
|
|
1969
1999
|
if (taskFinalReviewInvocation.error !== undefined) {
|
|
1970
2000
|
throw new Error(taskFinalReviewInvocation.error);
|
|
1971
2001
|
}
|
|
2002
|
+
// Desktop clients may carry only their provider-native identity. Resolve
|
|
2003
|
+
// command-local hints before any public/Task/Global routing. This reads this
|
|
2004
|
+
// Home only, and never creates a Session or mutates its registration.
|
|
2005
|
+
if ((process.env.CODEX_THREAD_ID !== undefined || process.env.YUI_NATIVE_SESSION_ID !== undefined)
|
|
2006
|
+
&& (process.env.YUI_SESSION_SCOPE === undefined || process.env.YUI_ROLE === undefined)) {
|
|
2007
|
+
const registry = openCurrentTaskStore(resolveYuiHome(process.env));
|
|
2008
|
+
try {
|
|
2009
|
+
Object.assign(process.env, sessionCallerEnvironment(registry, process.env));
|
|
2010
|
+
}
|
|
2011
|
+
finally {
|
|
2012
|
+
registry.close();
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
1972
2015
|
if (process.env.YUI_SESSION_SCOPE !== "task") {
|
|
1973
2016
|
if (process.env.YUI_SESSION_SCOPE === "global") {
|
|
1974
2017
|
return await preflightManagedGlobalControlPlane();
|
|
@@ -2047,8 +2090,8 @@ async function preflightManagedGlobalControlPlane() {
|
|
|
2047
2090
|
const home = resolveYuiHome(process.env);
|
|
2048
2091
|
const manifest = assertManagedSessionManifest(home, "global");
|
|
2049
2092
|
const expectedRoleKind = process.env.YUI_ROLE === "operator" ? "operator" : "global";
|
|
2050
|
-
if (manifest.owner.scope !== "global"
|
|
2051
|
-
|| manifest.roleKind !== expectedRoleKind) {
|
|
2093
|
+
if (manifest !== undefined && (manifest.owner.scope !== "global"
|
|
2094
|
+
|| manifest.roleKind !== expectedRoleKind)) {
|
|
2052
2095
|
throw new ManagedRuntimeDriftError("Managed global invocation does not match its Session Manifest.");
|
|
2053
2096
|
}
|
|
2054
2097
|
if (expectedRoleKind === "operator" && ["doctor", "upgrade", "update"].includes(args[0] ?? "")) {
|
|
@@ -2062,9 +2105,15 @@ async function preflightManagedGlobalControlPlane() {
|
|
|
2062
2105
|
const verifiedStore = openCurrentTaskStore(home);
|
|
2063
2106
|
const internalCallback = args[0] === "internal"
|
|
2064
2107
|
&& ["agent-host", "session-notify", "runtime-hook"].includes(args[1] ?? "");
|
|
2065
|
-
if (!internalCallback
|
|
2108
|
+
if (!internalCallback) {
|
|
2109
|
+
const selfRead = args[0] === "session" && args[1] === "context" && args[2] === process.env.YUI_ROLE
|
|
2110
|
+
|| args[0] === "role" && args[1] === "message" && ["list", "show"].includes(args[2] ?? "")
|
|
2111
|
+
&& args[3] === process.env.YUI_ROLE;
|
|
2066
2112
|
try {
|
|
2067
|
-
|
|
2113
|
+
if (selfRead)
|
|
2114
|
+
requireManagedGlobalReader(verifiedStore, process.env);
|
|
2115
|
+
else
|
|
2116
|
+
requireManagedGlobalCaller(verifiedStore, process.env);
|
|
2068
2117
|
}
|
|
2069
2118
|
catch (error) {
|
|
2070
2119
|
verifiedStore.close();
|
|
@@ -2076,7 +2125,9 @@ async function preflightManagedGlobalControlPlane() {
|
|
|
2076
2125
|
function assertManagedSessionManifest(home, scope) {
|
|
2077
2126
|
const manifestPath = process.env.YUI_SESSION_MANIFEST;
|
|
2078
2127
|
if (manifestPath === undefined) {
|
|
2079
|
-
|
|
2128
|
+
// A Manifest is a context pointer, not an authority credential. Native
|
|
2129
|
+
// identity is validated against the current Home immediately below.
|
|
2130
|
+
return undefined;
|
|
2080
2131
|
}
|
|
2081
2132
|
let manifest;
|
|
2082
2133
|
try {
|
|
@@ -692,6 +692,7 @@ export class FileTaskController {
|
|
|
692
692
|
#globalInputDelivery;
|
|
693
693
|
#providerRetry;
|
|
694
694
|
#continuationReconciler;
|
|
695
|
+
#hostTerminalRecovery;
|
|
695
696
|
#current;
|
|
696
697
|
#operatorCurrent;
|
|
697
698
|
#runtimeObserverCurrent;
|
|
@@ -741,6 +742,7 @@ export class FileTaskController {
|
|
|
741
742
|
this.#leaderWakeFence = options.leaderWakeFence;
|
|
742
743
|
this.#jobSupervisor = options.jobSupervisor;
|
|
743
744
|
this.#continuationReconciler = options.continuationReconciler;
|
|
745
|
+
this.#hostTerminalRecovery = options.hostTerminalRecovery;
|
|
744
746
|
this.#globalInputDelivery = options.globalInputDelivery;
|
|
745
747
|
this.#providerRetry = options.providerRetry;
|
|
746
748
|
this.#signalScheduler = new MailboxScheduler(async (keys) => { await this.#requestPass({ kind: "dirty", keys }); }, {
|
|
@@ -995,8 +997,11 @@ export class FileTaskController {
|
|
|
995
997
|
}
|
|
996
998
|
}
|
|
997
999
|
const firstRuntimeDrain = await this.#drainRuntimeEvents();
|
|
1000
|
+
if (scope.kind === "full" && this.#hostTerminalRecovery !== undefined) {
|
|
1001
|
+
await this.#observeMaintenance("Host terminal recovery", this.#hostTerminalRecovery);
|
|
1002
|
+
}
|
|
998
1003
|
await this.#providerRetry?.reconcile();
|
|
999
|
-
await this.#globalInputDelivery?.();
|
|
1004
|
+
await this.#globalInputDelivery?.({ reconcile: scope.kind === "full", now: this.#now() });
|
|
1000
1005
|
for (const taskId of runtimeTaskFailureIds(firstRuntimeDrain)) {
|
|
1001
1006
|
runtimeFailedTaskIds.add(taskId);
|
|
1002
1007
|
}
|
|
@@ -1076,7 +1081,8 @@ export class FileTaskController {
|
|
|
1076
1081
|
if (scope.kind === "full"
|
|
1077
1082
|
|| scope.keys.some((key) => key !== "operator")
|
|
1078
1083
|
|| runtimeAdvanced) {
|
|
1079
|
-
this.#signalOperatorMailbox(runtimeAdvanced
|
|
1084
|
+
this.#signalOperatorMailbox(runtimeAdvanced || scope.kind === "full"
|
|
1085
|
+
|| scope.kind === "dirty" && scope.keys.some(key => parseMailboxKey(key).kind === "global-role"));
|
|
1080
1086
|
}
|
|
1081
1087
|
}
|
|
1082
1088
|
catch (error) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { isDeepStrictEqual } from "node:util";
|
|
3
|
+
import { deliveredMessageWake, pinMessageRevisions, taskMessageInboxReceipt, unfinishedLeaderInbox } from "../message/taskInboxOwnership.js";
|
|
3
4
|
import { contextSnapshotRef } from "../context/contextSnapshot.js";
|
|
4
5
|
import { freezeRunContextSnapshot } from "../context/runContextPack.js";
|
|
5
6
|
import { taskNotificationPresentation } from "../interaction/notificationPresentation.js";
|
|
@@ -28,8 +29,9 @@ import { activeRoleAgentBinding } from "../role/role.js";
|
|
|
28
29
|
import { SYSTEM_OPERATOR_ROLE } from "../role/systemRoles.js";
|
|
29
30
|
import { standardAgentError } from "../runtime/agentError.js";
|
|
30
31
|
import { recordTaskAgentError } from "./taskAgentError.js";
|
|
31
|
-
import { recordGlobalRuntimeAttention, recordGlobalRuntimeFailure } from "./globalRuntimeAttention.js";
|
|
32
|
+
import { recordGlobalRuntimeAttention, recordGlobalRuntimeFailure, recordGlobalRuntimeRecovery } from "./globalRuntimeAttention.js";
|
|
32
33
|
import { queueOperatorNotification } from "./operatorNotification.js";
|
|
34
|
+
import { claimOperatorAttention } from "../scheduler/operatorEvent.js";
|
|
33
35
|
import { builtinAgentDriverRegistry } from "../runtime/builtinAgentDrivers.js";
|
|
34
36
|
import { RUNTIME_CLEANUP_REQUIRED_REASON, RUNTIME_HOST_DETACH_REQUIRED_REASON, RUNTIME_LIFECYCLE_OWNER, hasRuntimeCleanupObligation, hasRuntimeLifecycleWork, isRuntimeCleanupReason, runtimeCleanupDisposition, runtimeLifecycleTarget } from "../runtime/lifecycleReservation.js";
|
|
35
37
|
import { classifyRuntimeProcessExit, validateRuntimeProcessExitObservation } from "../runtime/processExitObservation.js";
|
|
@@ -448,6 +450,10 @@ export class FileSchedulerStoreAdapter {
|
|
|
448
450
|
binding = settleProviderTurn(binding, { attemptId: receipt, nativeTurnId: fence.nativeTurnId,
|
|
449
451
|
status: input.kind === "turn.completed" ? "completed" : input.kind === "turn.cancelled" ? "cancelled" : "failed",
|
|
450
452
|
settledAt: at });
|
|
453
|
+
if (input.kind === "turn.completed")
|
|
454
|
+
recordGlobalRuntimeRecovery(store, fence.roleName, input.eventId, {
|
|
455
|
+
nativeSessionId: fence.nativeSessionId, evidence: `provider-structured:${receipt}`
|
|
456
|
+
}, now);
|
|
451
457
|
if (fence.nativeTurnId !== undefined)
|
|
452
458
|
sessions = rememberRoleAgentCompletedTurn(sessions, fence.agentId, fence.nativeSessionId, fence.nativeTurnId, new Date(at));
|
|
453
459
|
}
|
|
@@ -882,6 +888,11 @@ export class FileSchedulerStoreAdapter {
|
|
|
882
888
|
return [...this.store.listPendingActivationRequestTaskIds()].sort((left, right) => (left.localeCompare(right, undefined, { numeric: true })));
|
|
883
889
|
}
|
|
884
890
|
getTask(taskId) { return this.store.getTask(taskId); }
|
|
891
|
+
getEvent(taskId, eventId) {
|
|
892
|
+
return this.store.queryContextRecords(taskId, {
|
|
893
|
+
family: "task-event", ids: [eventId], limit: 1
|
|
894
|
+
}).records[0]?.value ?? null;
|
|
895
|
+
}
|
|
885
896
|
getTaskWorkspace(taskId) { return this.store.getTaskWorkspace(taskId); }
|
|
886
897
|
getTaskBrief(taskId) { return this.store.getTaskBrief(taskId); }
|
|
887
898
|
listDecisions(taskId) { return this.store.listDecisions(taskId); }
|
|
@@ -1146,7 +1157,8 @@ export class FileSchedulerStoreAdapter {
|
|
|
1146
1157
|
throw new AgentHostProviderSessionBusyError("The next Global input belongs to an explicit then handoff.");
|
|
1147
1158
|
}
|
|
1148
1159
|
if (message !== undefined) {
|
|
1149
|
-
if (
|
|
1160
|
+
if (store.queryGlobalRoleMessages(input.roleName, { handling: true, limit: 0 }).count > 0
|
|
1161
|
+
|| message.inbox !== undefined || message.delivery !== undefined || message.notDelivered !== undefined
|
|
1150
1162
|
|| message.control !== undefined && message.control.outcome !== "rejected"
|
|
1151
1163
|
|| message.deliveryTarget?.agentId !== input.agentId
|
|
1152
1164
|
|| message.deliveryTarget?.nativeSessionId !== input.nativeSessionId) {
|
|
@@ -1567,7 +1579,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
1567
1579
|
if (mailbox.processing !== null) {
|
|
1568
1580
|
return { status: "processing", processing: mailbox.processing };
|
|
1569
1581
|
}
|
|
1570
|
-
let claimed = claimPending(mailbox, {
|
|
1582
|
+
let claimed = (input.target.kind === "operator" ? claimOperatorAttention : claimPending)(mailbox, {
|
|
1571
1583
|
batchId: input.batchId,
|
|
1572
1584
|
owner: input.owner,
|
|
1573
1585
|
startedAt: input.now.toISOString()
|
|
@@ -1737,7 +1749,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
1737
1749
|
clearPendingWakeup(taskId) { this.store.clearPendingWakeup(taskId); }
|
|
1738
1750
|
getLeaderFailure(taskId) { return this.store.getLeaderFailure(taskId); }
|
|
1739
1751
|
prepareMessageContinuations(taskId, now) {
|
|
1740
|
-
const roles = new Set(this.store.listMessages(taskId).flatMap((message) => message.recipient?.ownerRunId !== undefined && message.continuation?.runId === undefined ? [message.recipient.roleName] : []));
|
|
1752
|
+
const roles = new Set(this.store.listMessages(taskId).flatMap((message) => message.inbox === undefined && message.recipient?.ownerRunId !== undefined && message.continuation?.runId === undefined ? [message.recipient.roleName] : []));
|
|
1741
1753
|
for (const roleName of roles) {
|
|
1742
1754
|
try {
|
|
1743
1755
|
this.store.transaction((store) => prepareMessageContinuations(store, taskId, now, roleName));
|
|
@@ -1746,7 +1758,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
1746
1758
|
const reason = `preparation-failed: ${error instanceof Error ? error.message : String(error)}`.slice(0, 1000);
|
|
1747
1759
|
this.store.transaction((store) => {
|
|
1748
1760
|
for (const message of store.listMessages(taskId)) {
|
|
1749
|
-
if (message.recipient?.roleName === roleName && message.recipient.ownerRunId !== undefined && message.continuation?.runId === undefined
|
|
1761
|
+
if (message.inbox === undefined && message.recipient?.roleName === roleName && message.recipient.ownerRunId !== undefined && message.continuation?.runId === undefined
|
|
1750
1762
|
&& message.continuation?.notDeliveredReason !== reason) {
|
|
1751
1763
|
store.updateMessage(taskId, { ...message, continuation: { notDeliveredReason: reason } });
|
|
1752
1764
|
}
|
|
@@ -1855,6 +1867,8 @@ export class FileSchedulerStoreAdapter {
|
|
|
1855
1867
|
return { wakeId, attemptId: nextAttempt, disposition: "submit",
|
|
1856
1868
|
presentation: taskNotificationPresentation(wake, store) };
|
|
1857
1869
|
}
|
|
1870
|
+
if (unfinishedLeaderInbox(store, taskId))
|
|
1871
|
+
return null;
|
|
1858
1872
|
if (mailbox.pending === null || (provider !== null && provider !== undefined
|
|
1859
1873
|
&& ["submitting", "accepted", "delivery-unknown"].includes(provider.status)))
|
|
1860
1874
|
return null;
|
|
@@ -1864,6 +1878,12 @@ export class FileSchedulerStoreAdapter {
|
|
|
1864
1878
|
const sessions = store.getTaskRoleSessionSet(taskId, "leader");
|
|
1865
1879
|
const session = sessions?.sessions[sessions.activeAgentId];
|
|
1866
1880
|
const obsoleteIds = new Set();
|
|
1881
|
+
for (const message of store.listMessages(taskId)) {
|
|
1882
|
+
if (pendingMessageIds.has(message.id) && (taskMessageInboxReceipt(store, message) !== undefined
|
|
1883
|
+
|| deliveredMessageWake(store, message) !== undefined)) {
|
|
1884
|
+
obsoleteIds.add(message.id);
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1867
1887
|
for (const message of store.listMessages(taskId)) {
|
|
1868
1888
|
if (!pendingMessageIds.has(message.id) || message.inputControl?.expectedSessionId === undefined)
|
|
1869
1889
|
continue;
|
|
@@ -1910,7 +1930,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
1910
1930
|
const wakeId = store.nextTaskWakeId(taskId);
|
|
1911
1931
|
const wake = createTaskWake({
|
|
1912
1932
|
id: wakeId, taskId, reasons: mailbox.pending.reasons,
|
|
1913
|
-
refs: selectedRefs,
|
|
1933
|
+
refs: pinMessageRevisions(store, taskId, selectedRefs),
|
|
1914
1934
|
fromCursor: latest?.toCursor ?? task.createdAt,
|
|
1915
1935
|
toCursor: new Date(Math.max(now.getTime(), Date.parse(mailbox.pending.lastQueuedAt))).toISOString(), now
|
|
1916
1936
|
});
|