@zq-silk/yui 0.15.9 → 0.15.12
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/ARCHITECTURE.md +8 -4
- package/ARCHITECTURE.zh-CN.md +5 -2
- package/README.md +13 -5
- package/dist/agent/launchEnvironment.js +7 -0
- package/dist/agentRun/agentRun.js +3 -0
- package/dist/cli/commandCatalog.js +64 -16
- package/dist/cli/interactionPolicy.js +7 -3
- package/dist/cli/managedDiagnostics.js +1 -1
- package/dist/cli/updateOrchestrator.js +24 -1
- package/dist/cli/updatePorts.js +7 -3
- package/dist/cli/upgradeCommand.js +42 -2
- package/dist/cli.js +381 -107
- package/dist/commands/executionAuditCommands.js +10 -0
- package/dist/commands/globalRoleCommands.js +339 -4
- package/dist/commands/projectCommands.js +50 -22
- package/dist/commands/releaseCommands.js +18 -0
- package/dist/commands/taskActor.js +25 -0
- package/dist/commands/taskCommands.js +586 -96
- package/dist/commands/taskIntegrationCommands.js +19 -39
- package/dist/commands/taskIntegrationQueueCommands.js +1 -1
- package/dist/commands/taskOverviewCommand.js +4 -3
- package/dist/commands/taskPublicationAdoptCommand.js +127 -0
- package/dist/commands/taskPublicationCommands.js +11 -2
- package/dist/commands/taskPublicationVerifyCommand.js +23 -39
- package/dist/commands/taskRemoteDeliveryCommand.js +22 -11
- package/dist/commands/taskRoleRuntimeStatus.js +35 -0
- package/dist/context/runContextPack.js +3 -0
- package/dist/context/taskCatalog.js +187 -0
- package/dist/context/taskContext.js +55 -6
- package/dist/controller/agentHostObservation.js +155 -0
- package/dist/controller/clientRuntime.js +17 -2
- package/dist/controller/controller.js +14 -2
- package/dist/controller/fileSchedulerStoreAdapter.js +519 -25
- package/dist/controller/globalInputDelivery.js +132 -0
- package/dist/controller/jobControl.js +6 -2
- package/dist/controller/providerRetryAdmission.js +100 -0
- package/dist/controller/providerRetryDelivery.js +218 -0
- package/dist/controller/resourceInventory.js +14 -4
- package/dist/controller/resourceInventoryLinux.js +2 -6
- package/dist/controller/runtime.js +117 -7
- package/dist/controller/runtimeEventInbox.js +32 -3
- package/dist/controller/runtimeEventProcessor.js +26 -6
- package/dist/controller/runtimeHookRunFence.js +75 -19
- package/dist/controller/structuredProviderObservation.js +133 -70
- package/dist/coordination/workMailboxQueue.js +5 -0
- package/dist/execution/workItemExecutionProjection.js +1 -1
- package/dist/executor/agentExecutor.js +64 -4
- package/dist/executor/executorRegistry.js +3 -0
- package/dist/executor/fileRoleLaunchPlanner.js +78 -118
- package/dist/integration/deliveryObligation.js +2 -1
- package/dist/integration/gitIntegrationService.js +329 -386
- package/dist/integration/integrationAttempt.js +30 -4
- package/dist/integration/integrationQueueService.js +7 -7
- package/dist/integration/integrationSourceApplication.js +323 -0
- package/dist/lifecycle/exactRunTerminalization.js +4 -1
- package/dist/message/globalInterrupt.js +33 -0
- package/dist/message/globalProviderRetry.js +15 -0
- package/dist/message/inputControlResolution.js +106 -0
- package/dist/message/message.js +367 -0
- package/dist/message/messageContinuation.js +126 -3
- package/dist/message/taskInterrupt.js +34 -0
- package/dist/observability/executionAudit.js +19 -0
- package/dist/observability/orchestrationMetrics.js +1 -1
- package/dist/release/releaseHandover.js +22 -0
- package/dist/release/releaseWorkflowPorts.js +15 -7
- package/dist/repository/gitWorkspace.js +430 -107
- package/dist/repository/projectMaintenanceLock.js +75 -18
- package/dist/repository/taskWorkspaceCoordinator.js +182 -101
- package/dist/repository/taskWorkspacePreparer.js +205 -72
- package/dist/repository/workItemCandidateSnapshot.js +34 -0
- package/dist/repository/workspaceCleanupInspection.js +187 -0
- package/dist/resources/resourceDiscovery.js +3 -2
- package/dist/runtime/agentError.js +5 -3
- package/dist/runtime/agentHost.js +179 -82
- package/dist/runtime/agentHostCompatibility.js +127 -0
- package/dist/runtime/agentHostProtocol.js +53 -0
- package/dist/runtime/builtinAgentErrorMappers.js +91 -0
- package/dist/runtime/codexAppServerRuntime.js +34 -3
- package/dist/runtime/executionEnvironment.js +0 -19
- package/dist/runtime/launchBroker.js +6 -0
- package/dist/runtime/providerControl.js +5 -1
- package/dist/runtime/providerRetry.js +198 -0
- package/dist/runtime/providerRuntimeIdentity.js +28 -2
- package/dist/runtime/sessionReconciliation.js +4 -4
- package/dist/runtime/sessionTokenMetrics.js +15 -5
- package/dist/runtime/structuredProviderHost.js +6 -2
- package/dist/runtime/taskRuntimeIsolation.js +30 -6
- package/dist/runtime/taskUsageMetrics.js +275 -0
- package/dist/runtime/tmuxAdapters.js +5 -3
- package/dist/scheduler/activeRoleRunDelivery.js +12 -0
- package/dist/scheduler/leaderWakeupProcessor.js +5 -0
- package/dist/scheduler/operatorEvent.js +4 -0
- package/dist/scheduler/taskExecutionProjection.js +38 -6
- package/dist/scheduler/taskObservabilityProjection.js +6 -44
- package/dist/scheduler/wakeReason.js +7 -1
- package/dist/scheduler/wakeupQueue.js +2 -0
- package/dist/setup/setupCommand.js +26 -8
- package/dist/storage/homeLayout.js +130 -0
- package/dist/storage/migrations/collapseWorktreeLayout.js +963 -0
- package/dist/storage/migrations/integrationContinuation.js +104 -0
- package/dist/storage/migrations/unifyHomeLayout.js +925 -0
- package/dist/storage/sqliteSchema.js +167 -4
- package/dist/storage/sqliteStore.js +57 -1
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +2 -0
- package/dist/storage/taskCatalog.js +123 -0
- package/dist/storage/taskStore.js +2 -0
- package/dist/storage/upgrade/upgradeOrchestrator.js +95 -2
- package/dist/task/archiveDiagnostics.js +129 -0
- package/dist/task/archivePreflight.js +124 -0
- package/dist/task/nextAction.js +44 -11
- package/dist/task/publicationAdoption.js +56 -0
- package/dist/task/publicationReference.js +10 -0
- package/dist/task/remoteDelivery.js +31 -16
- package/dist/web/assets/client/app.js +147 -17
- package/dist/web/assets/client/components.js +56 -13
- package/dist/web/assets/client/i18n.js +78 -4
- package/dist/web/assets/client/taskSurface.js +108 -1
- package/dist/web/assets/client/view.js +39 -8
- package/dist/web/assets/shell.js +29 -0
- package/dist/web/assets/styles/layout.js +8 -1
- package/dist/web/assets/styles/widgets.js +12 -0
- package/dist/web/webServer.js +131 -4
- package/dist/web/webSnapshot.js +16 -6
- package/dist/web/webTaskSurface.js +222 -5
- package/dist/workspace/cleanupInspection.js +63 -0
- package/dist/workspace/workItemChangeSetManager.js +111 -35
- package/docs/agent-result-consumption.md +4 -0
- package/docs/agent-result-consumption.zh-CN.md +3 -0
- package/docs/agent-runtime-drivers.md +7 -0
- package/docs/agent-runtime-drivers.zh-CN.md +5 -0
- package/docs/architecture/README.md +2 -0
- package/docs/architecture/README.zh-CN.md +3 -1
- package/docs/architecture/capabilities-and-resources.md +30 -5
- package/docs/architecture/capabilities-and-resources.zh-CN.md +23 -3
- package/docs/managed-turn-and-session-runtime.md +47 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +40 -0
- package/docs/observability/README.md +62 -0
- package/docs/observability/README.zh-CN.md +47 -0
- package/docs/project-refresh.md +77 -0
- package/docs/project-refresh.zh-CN.md +59 -0
- package/docs/provider-retry.md +70 -0
- package/docs/release-workflow.md +39 -0
- package/docs/release-workflow.zh-CN.md +29 -0
- package/docs/sqlite-control-plane-design.md +223 -1
- package/docs/task-delivery.md +133 -13
- package/docs/task-delivery.zh-CN.md +99 -10
- package/docs/task-discovery.md +102 -0
- package/docs/task-discovery.zh-CN.md +86 -0
- package/docs/testing/verification-levels.md +40 -0
- package/docs/testing/verification-levels.zh-CN.md +23 -0
- package/i18n/README.zh-CN.md +13 -7
- package/package.json +1 -1
- package/skills/yui-leader/references/execution.md +154 -51
- package/skills/yui-leader/references/integration.md +52 -2
- package/skills/yui-operator/SKILL.md +19 -3
- package/skills/yui-reviewer/SKILL.md +4 -0
- package/skills/yui-runtime/SKILL.md +42 -0
- package/skills/yui-runtime/references/publication.md +42 -0
- package/skills/yui-runtime/references/recovery.md +24 -0
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { reconciliationIntervalMilliseconds, resolveAgentLaunchInactivityTimeoutSeconds, resolveControllerTaskConcurrency, resolveDeliveryTimeoutSeconds, resolveRuntimeHealth, resolveTmuxBin, resolveTmuxHistoryLimit } from "../config/yuiConfig.js";
|
|
2
|
-
import { resolve } from "node:path";
|
|
3
2
|
import { isDeepStrictEqual } from "node:util";
|
|
4
3
|
import { controllerSocketPath } from "../core/controllerEndpoint.js";
|
|
5
4
|
import { AGENT_OPERATIONAL_ENVIRONMENT_NAMES, NATIVE_AGENT_ENVIRONMENT_NAMES, nativeAgentEnvironmentNames, YUI_MANAGED_RUNTIME_ENVIRONMENT_NAMES } from "../agent/launchEnvironment.js";
|
|
@@ -11,6 +10,7 @@ import { AgentConfigurationCatalogService, validateAgentLaunchConfiguration } fr
|
|
|
11
10
|
import { isTaskOwnedWorkspace, sameManagedWorkspaceIdentity } from "../worktree/managedWorkspace.js";
|
|
12
11
|
import { FileRoleLaunchPlanner } from "../executor/fileRoleLaunchPlanner.js";
|
|
13
12
|
import { openCurrentTaskStore } from "../storage/currentTaskStore.js";
|
|
13
|
+
import { managedRuntimeRoot } from "../storage/homeLayout.js";
|
|
14
14
|
import { SqliteTaskStore } from "../storage/sqliteStore.js";
|
|
15
15
|
import { AsyncTaskStoreClient, resolveStoreWorkerEnabledForHome } from "../storage/storeRpc.js";
|
|
16
16
|
import { FileTaskWorkspacePreparer } from "../repository/taskWorkspacePreparer.js";
|
|
@@ -41,9 +41,14 @@ import { createResourceAutoGc } from "../resources/autoResourceGc.js";
|
|
|
41
41
|
import { createRuntimeResourceActivityTracker } from "./resourceInventory.js";
|
|
42
42
|
import { SessionOwnerReconciliation } from "./sessionOwnerReconciliation.js";
|
|
43
43
|
import { launchBrokerForHome } from "../runtime/launchBroker.js";
|
|
44
|
+
import { assertExecutionEnvironmentCurrent } from "../runtime/executionEnvironment.js";
|
|
44
45
|
import { classifyRuntimeProcessExit, validateRuntimeProcessExitObservation } from "../runtime/processExitObservation.js";
|
|
45
46
|
import { replayRuntimeProcessExitOutbox } from "../runtime/processExitOutbox.js";
|
|
46
47
|
import { appendGlobalProcessExitObservation } from "../runtime/globalProcessExitStore.js";
|
|
48
|
+
import { deliverGlobalInputs } from "./globalInputDelivery.js";
|
|
49
|
+
import { providerRetryPrompt } from "../runtime/providerRetry.js";
|
|
50
|
+
import { createProviderRetryHooks } from "./providerRetryDelivery.js";
|
|
51
|
+
import { createGlobalRoleMessage } from "../message/message.js";
|
|
47
52
|
import { builtinAgentDriverRegistry } from "../runtime/builtinAgentDrivers.js";
|
|
48
53
|
import { createRuntimeObservation, runtimeObservationFromTaskEvent } from "../runtime/runtimeObservation.js";
|
|
49
54
|
import { createTaskEvent } from "../event/taskEvent.js";
|
|
@@ -174,11 +179,14 @@ export async function startFileTaskControllerRuntime(home, options = {}) {
|
|
|
174
179
|
?? new AgentHostPromptPushAdapter(home);
|
|
175
180
|
const runtimeIsolation = options.runtimeIsolation
|
|
176
181
|
?? new FileTaskRuntimeIsolation({
|
|
177
|
-
//
|
|
178
|
-
//
|
|
179
|
-
|
|
182
|
+
// Provider data/cache/tmp live in a dedicated Home partition. The
|
|
183
|
+
// control boundary permits runtime roots to overlap only this exact
|
|
184
|
+
// subtree, keeping them out of the database, managed Git workspaces and
|
|
185
|
+
// every other part of the control plane.
|
|
186
|
+
runtimeRoot: managedRuntimeRoot(home),
|
|
180
187
|
controlPlane: {
|
|
181
188
|
yuiHome: home,
|
|
189
|
+
managedRuntimeRoot: managedRuntimeRoot(home),
|
|
182
190
|
controllerSocketPath: controllerSocketPath(homeId),
|
|
183
191
|
tmuxNamespace: yuiTmuxServerName(home),
|
|
184
192
|
globalInstallPaths: [process.execPath]
|
|
@@ -239,6 +247,31 @@ export async function startFileTaskControllerRuntime(home, options = {}) {
|
|
|
239
247
|
sessionHost,
|
|
240
248
|
promptPush,
|
|
241
249
|
launchCoordinator,
|
|
250
|
+
notifyOperatorInputOnce: async (input) => {
|
|
251
|
+
// The original attention mailbox remains pending until this one
|
|
252
|
+
// Global Message has an actual Provider receipt. Never inject keys
|
|
253
|
+
// into a busy Host console and mistake that for accepted attention.
|
|
254
|
+
const message = store.transaction(tx => {
|
|
255
|
+
const requestId = `operator-notice:${encodeURIComponent(input.receiptId)}`;
|
|
256
|
+
const previous = tx.listGlobalRoleMessages("operator").find(entry => (entry.inputControl ?? entry.interruptThen?.reusedInput)?.requestId === requestId);
|
|
257
|
+
if (previous !== undefined)
|
|
258
|
+
return previous;
|
|
259
|
+
const sessions = tx.getGlobalRoleSessionSet("operator");
|
|
260
|
+
const session = sessions?.sessions[sessions.activeAgentId];
|
|
261
|
+
if (session?.status !== "active" || sessions?.providerBinding == null)
|
|
262
|
+
return null;
|
|
263
|
+
const created = { ...createGlobalRoleMessage(tx.nextGlobalRoleMessageId(), "operator", input.text, "system", { type: "system" }, new Date(), { inputControl: { action: "queue", requestId } }),
|
|
264
|
+
deliveryTarget: { agentId: session.agentId, nativeSessionId: session.nativeSessionId } };
|
|
265
|
+
tx.saveGlobalRoleMessage(created);
|
|
266
|
+
return created;
|
|
267
|
+
});
|
|
268
|
+
if (message === null)
|
|
269
|
+
return "unavailable";
|
|
270
|
+
if (message.delivery?.via === "provider")
|
|
271
|
+
return "already-sent";
|
|
272
|
+
runningRuntime?.signal("global-role:operator");
|
|
273
|
+
return "not-ready";
|
|
274
|
+
},
|
|
242
275
|
roleResourceInventory: async (panes, inputs) => {
|
|
243
276
|
const inventory = await scanInventory(panes);
|
|
244
277
|
return inventory.resources.flatMap((resource) => {
|
|
@@ -439,7 +472,30 @@ export async function startFileTaskControllerRuntime(home, options = {}) {
|
|
|
439
472
|
now: options.now,
|
|
440
473
|
onError: options.onError,
|
|
441
474
|
lifecycleHost,
|
|
475
|
+
providerRetry: createProviderRetryHooks(home, store, {
|
|
476
|
+
now: options.now,
|
|
477
|
+
onError: options.onError,
|
|
478
|
+
snapshotTaskCandidate: async (run) => {
|
|
479
|
+
if (run.purpose !== "review" || run.reviewRoundId === undefined)
|
|
480
|
+
return undefined;
|
|
481
|
+
const round = store.getReviewRound(run.taskId, run.reviewRoundId);
|
|
482
|
+
if (round?.scope !== "task")
|
|
483
|
+
return undefined;
|
|
484
|
+
const task = store.getTask(run.taskId);
|
|
485
|
+
const workspace = store.getTaskWorkspace(run.taskId);
|
|
486
|
+
if (task === null || workspace === null || !(workspacePreparer instanceof FileTaskWorkspacePreparer)) {
|
|
487
|
+
throw new Error("Task-final retry requires an observable authoritative Task workspace.");
|
|
488
|
+
}
|
|
489
|
+
const snapshot = await workspacePreparer.snapshotDirectTaskMain(workspace, task.projectBindings.map(p => p.projectId));
|
|
490
|
+
return { schemaVersion: 1, projects: snapshot.projects.map(p => ({
|
|
491
|
+
projectId: p.projectId, commit: p.headCommit
|
|
492
|
+
})) };
|
|
493
|
+
}
|
|
494
|
+
}),
|
|
442
495
|
jobSupervisor,
|
|
496
|
+
globalInputDelivery: () => deliverGlobalInputs(home, store, async (roleName) => {
|
|
497
|
+
await lifecycleDispatcher("runtime.ensure-role-session", { scope: "global", roleName });
|
|
498
|
+
}, options.onError ?? (() => undefined)),
|
|
443
499
|
jobControl,
|
|
444
500
|
capabilityDispatcher: createCapabilityDispatcher(kernel.capabilities),
|
|
445
501
|
...(continuationReconciler === undefined ? {} : { continuationReconciler }),
|
|
@@ -546,6 +602,20 @@ export function createRuntimeLifecycleDispatcher(store, schedulerStore, sessionH
|
|
|
546
602
|
});
|
|
547
603
|
const lifecycleTails = new Map();
|
|
548
604
|
return async (method, params) => {
|
|
605
|
+
if (method === "runtime.host-observation-apply") {
|
|
606
|
+
const id = params.eventId;
|
|
607
|
+
if (typeof id !== "string")
|
|
608
|
+
throw applicationError("INVALID_PARAMS", "Host event id is required.");
|
|
609
|
+
// Eager delivery is only a hint about the exact immutable Inbox file.
|
|
610
|
+
// Missing means the normal drainer has already committed and ACKed it.
|
|
611
|
+
const event = new FileRuntimeEventInbox(store.rootDirectory()).read(id);
|
|
612
|
+
if (event === null)
|
|
613
|
+
return { outcome: "applied" };
|
|
614
|
+
if (event.type !== "runtime-observation" || event.host === undefined) {
|
|
615
|
+
throw applicationError("INVALID_PARAMS", "Expected an Agent Host observation.");
|
|
616
|
+
}
|
|
617
|
+
return { outcome: schedulerStore.observeAgentHostObservation(event, new Date()) };
|
|
618
|
+
}
|
|
549
619
|
if (method === "runtime.observation-apply") {
|
|
550
620
|
try {
|
|
551
621
|
return {
|
|
@@ -569,6 +639,8 @@ export function createRuntimeLifecycleDispatcher(store, schedulerStore, sessionH
|
|
|
569
639
|
authorityEpoch: value.authorityEpoch,
|
|
570
640
|
authorityOwner: value.authorityOwner,
|
|
571
641
|
holderId: value.holderId,
|
|
642
|
+
boundedText: value.boundedText,
|
|
643
|
+
retrySupported: value.retrySupported,
|
|
572
644
|
now: value.now
|
|
573
645
|
});
|
|
574
646
|
}
|
|
@@ -580,7 +652,15 @@ export function createRuntimeLifecycleDispatcher(store, schedulerStore, sessionH
|
|
|
580
652
|
}
|
|
581
653
|
throw error;
|
|
582
654
|
}
|
|
583
|
-
|
|
655
|
+
const binding = (value.taskId === undefined
|
|
656
|
+
? store.getGlobalRoleSessionSet(value.roleName)
|
|
657
|
+
: store.getTaskRoleSessionSet(value.taskId, value.roleName))?.providerBinding;
|
|
658
|
+
const retry = binding?.retry;
|
|
659
|
+
return { recorded: true, ...(retry?.currentAttemptId === value.attemptId && value.boundedText !== undefined
|
|
660
|
+
? { retryInput: {
|
|
661
|
+
text: providerRetryPrompt(retry, value.boundedText),
|
|
662
|
+
...(retry.failedNativeTurnId === undefined ? {} : { expectedFailedNativeTurnId: retry.failedNativeTurnId })
|
|
663
|
+
} } : {}) };
|
|
584
664
|
}
|
|
585
665
|
if (method === "runtime.provider-turn-submission-resolve") {
|
|
586
666
|
const value = providerTurnControlParams(params);
|
|
@@ -600,6 +680,7 @@ export function createRuntimeLifecycleDispatcher(store, schedulerStore, sessionH
|
|
|
600
680
|
status,
|
|
601
681
|
reason,
|
|
602
682
|
raw,
|
|
683
|
+
noProviderWrite: params.providerWrite === "not-issued",
|
|
603
684
|
now: value.now
|
|
604
685
|
});
|
|
605
686
|
return { recorded: true };
|
|
@@ -676,7 +757,34 @@ export function createRuntimeLifecycleDispatcher(store, schedulerStore, sessionH
|
|
|
676
757
|
}
|
|
677
758
|
// The launch payload is validated at reservation time and every member
|
|
678
759
|
// of its discriminated Provider-control union is JSON serializable.
|
|
679
|
-
|
|
760
|
+
const payload = launchBrokerForHome(store.rootDirectory()).redeem(ticket);
|
|
761
|
+
if (payload.executionEnvironment !== undefined) {
|
|
762
|
+
if (payload.environment.YUI_SESSION_SCOPE !== "task"
|
|
763
|
+
|| payload.environment.YUI_TASK_ID !== payload.executionEnvironment.taskId
|
|
764
|
+
|| payload.cwd !== payload.executionEnvironment.directory.path) {
|
|
765
|
+
throw applicationError("INVALID_PARAMS", "Launch does not match its adopted execution environment.");
|
|
766
|
+
}
|
|
767
|
+
assertExecutionEnvironmentCurrent(store, payload.executionEnvironment.taskId, payload.executionEnvironment);
|
|
768
|
+
}
|
|
769
|
+
return payload;
|
|
770
|
+
}
|
|
771
|
+
if (method === "runtime.execution-environment-check") {
|
|
772
|
+
const value = params;
|
|
773
|
+
const { taskId, roleName, agentId, nativeSessionId, workspace } = value;
|
|
774
|
+
if ([taskId, roleName, agentId, nativeSessionId, workspace].some(v => typeof v !== "string")) {
|
|
775
|
+
throw applicationError("INVALID_PARAMS", "Exact Host execution identity is required.");
|
|
776
|
+
}
|
|
777
|
+
const sessions = store.getTaskRoleSessionSet(taskId, roleName);
|
|
778
|
+
const session = sessions?.sessions[agentId];
|
|
779
|
+
if (sessions?.activeAgentId !== agentId || session?.status !== "active"
|
|
780
|
+
|| session.nativeSessionId !== nativeSessionId
|
|
781
|
+
|| (session.effective.executionEnvironment?.directory.path ?? session.effective.workspace.root) !== workspace) {
|
|
782
|
+
throw applicationError("INVALID_PARAMS", "Host no longer owns the current execution environment.");
|
|
783
|
+
}
|
|
784
|
+
if (session.effective.executionEnvironment !== undefined) {
|
|
785
|
+
assertExecutionEnvironmentCurrent(store, taskId, session.effective.executionEnvironment);
|
|
786
|
+
}
|
|
787
|
+
return { current: true };
|
|
680
788
|
}
|
|
681
789
|
if (method === "runtime.replace-agent-environment") {
|
|
682
790
|
if (environmentRefresher === undefined) {
|
|
@@ -1123,7 +1231,7 @@ function providerTurnControlParams(params) {
|
|
|
1123
1231
|
throw applicationError("INVALID_PARAMS", "Provider Turn control fence is invalid.");
|
|
1124
1232
|
}
|
|
1125
1233
|
return {
|
|
1126
|
-
taskId: requiredParam(value.taskId),
|
|
1234
|
+
...(value.taskId === undefined ? {} : { taskId: requiredParam(value.taskId) }),
|
|
1127
1235
|
roleName: requiredParam(value.roleName),
|
|
1128
1236
|
...(value.runId === undefined ? {} : { runId: requiredParam(value.runId) }),
|
|
1129
1237
|
agentId: requiredParam(value.agentId),
|
|
@@ -1132,6 +1240,8 @@ function providerTurnControlParams(params) {
|
|
|
1132
1240
|
authorityEpoch: authorityEpoch,
|
|
1133
1241
|
authorityOwner,
|
|
1134
1242
|
holderId: requiredParam(value.holderId),
|
|
1243
|
+
...(value.boundedText === undefined ? {} : { boundedText: requiredParam(value.boundedText) }),
|
|
1244
|
+
...(value.retrySupport === undefined ? {} : { retrySupported: value.retrySupport === "codex-failed-turn-v1" }),
|
|
1135
1245
|
now: new Date(observedAt)
|
|
1136
1246
|
};
|
|
1137
1247
|
}
|
|
@@ -4,6 +4,7 @@ import { closeSync, constants, existsSync, fchmodSync, fsyncSync, linkSync, lsta
|
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { createRuntimeObservation, isRuntimeTokenEvidence } from "../runtime/runtimeObservation.js";
|
|
6
6
|
import { boundedRunFailureDiagnostic, transportAgentResult } from "../domain/agentResultTransport.js";
|
|
7
|
+
import { readAgentHostObservationSource } from "../runtime/agentHostProtocol.js";
|
|
7
8
|
export const MAX_RUNTIME_EVENT_FILE_BYTES = 16 * 1024 * 1024;
|
|
8
9
|
const RUNTIME_EVENT_DIRECTORY = join("runtime", "inbox");
|
|
9
10
|
const INVALID_RUNTIME_EVENT_DIRECTORY = join("runtime", "inbox-invalid");
|
|
@@ -24,7 +25,7 @@ export class FileRuntimeEventInbox {
|
|
|
24
25
|
this.hooks = hooks;
|
|
25
26
|
this.directory = join(home, RUNTIME_EVENT_DIRECTORY);
|
|
26
27
|
}
|
|
27
|
-
enqueueObservation(input) {
|
|
28
|
+
enqueueObservation(input, host) {
|
|
28
29
|
const observation = createRuntimeObservation(input);
|
|
29
30
|
const scope = observation.fence.taskId === undefined ? "global" : "task";
|
|
30
31
|
const event = Object.freeze({
|
|
@@ -36,7 +37,8 @@ export class FileRuntimeEventInbox {
|
|
|
36
37
|
...(observation.fence.taskId === undefined
|
|
37
38
|
? {}
|
|
38
39
|
: { taskId: observation.fence.taskId }),
|
|
39
|
-
observation
|
|
40
|
+
observation,
|
|
41
|
+
...(host === undefined ? {} : { host: readAgentHostObservationSource(host) })
|
|
40
42
|
});
|
|
41
43
|
return this.publish(event);
|
|
42
44
|
}
|
|
@@ -117,6 +119,20 @@ export class FileRuntimeEventInbox {
|
|
|
117
119
|
}
|
|
118
120
|
return event;
|
|
119
121
|
}
|
|
122
|
+
/** Producer-local delivery diagnostics need only existence, never a read of
|
|
123
|
+
* another version's payload or permission to quarantine another producer. */
|
|
124
|
+
has(id) {
|
|
125
|
+
assertEventId(id);
|
|
126
|
+
try {
|
|
127
|
+
lstatSync(this.eventPath(id));
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
if (isNodeError(error, "ENOENT"))
|
|
132
|
+
return false;
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
120
136
|
acknowledge(id) {
|
|
121
137
|
assertEventId(id);
|
|
122
138
|
try {
|
|
@@ -408,6 +424,7 @@ function parseRuntimeEvent(value) {
|
|
|
408
424
|
function parseRuntimeObservationEvent(value) {
|
|
409
425
|
const expected = [
|
|
410
426
|
"schemaVersion", "id", "type", "receivedAt", "scope", "observation",
|
|
427
|
+
...(value.host === undefined ? [] : ["host"]),
|
|
411
428
|
...(value.taskId === undefined ? [] : ["taskId"])
|
|
412
429
|
];
|
|
413
430
|
if (value.schemaVersion !== 1 || !hasExactKeys(value, expected))
|
|
@@ -426,9 +443,18 @@ function parseRuntimeObservationEvent(value) {
|
|
|
426
443
|
receivedAt: observation.receivedAt,
|
|
427
444
|
scope,
|
|
428
445
|
...(scope === "task" ? { taskId: observation.fence.taskId } : {}),
|
|
429
|
-
observation
|
|
446
|
+
observation,
|
|
447
|
+
...(value.host === undefined ? {} : { host: parseHostSource(value.host) })
|
|
430
448
|
});
|
|
431
449
|
}
|
|
450
|
+
function parseHostSource(value) {
|
|
451
|
+
try {
|
|
452
|
+
return readAgentHostObservationSource(value);
|
|
453
|
+
}
|
|
454
|
+
catch (error) {
|
|
455
|
+
throw invalidEvent(error instanceof Error ? error.message : String(error));
|
|
456
|
+
}
|
|
457
|
+
}
|
|
432
458
|
function parseDurableJobTerminalEvent(value) {
|
|
433
459
|
const expected = [
|
|
434
460
|
"schemaVersion", "id", "type", "receivedAt", "scope", "taskId",
|
|
@@ -520,6 +546,9 @@ function assertEventId(id) {
|
|
|
520
546
|
throw invalidEvent("Runtime event id is invalid.");
|
|
521
547
|
}
|
|
522
548
|
function hasSameIdentity(left, right) {
|
|
549
|
+
if (left.type === "runtime-observation" && right.type === "runtime-observation"
|
|
550
|
+
&& !isDeepStrictEqual(left.host, right.host))
|
|
551
|
+
return false;
|
|
523
552
|
if (left.type === "runtime-observation" && right.type === "runtime-observation"
|
|
524
553
|
&& ["turn.completed", "turn.failed", "turn.cancelled"].includes(left.observation.kind)) {
|
|
525
554
|
return left.id === right.id
|
|
@@ -143,11 +143,17 @@ export class FileRuntimeEventProcessor {
|
|
|
143
143
|
return { candidate, outcome };
|
|
144
144
|
}
|
|
145
145
|
applyRuntimeObservation(event, now) {
|
|
146
|
+
if (event.host !== undefined) {
|
|
147
|
+
return this.observer.observeAgentHostObservation?.(event, now) ?? "deferred";
|
|
148
|
+
}
|
|
146
149
|
const taskId = event.observation.fence.taskId;
|
|
147
150
|
if (taskId !== undefined) {
|
|
148
151
|
const task = this.observer.getTask(taskId);
|
|
152
|
+
if (task?.status === "archived") {
|
|
153
|
+
this.recordObsolete(event, "task-archived", now);
|
|
154
|
+
return "obsolete";
|
|
155
|
+
}
|
|
149
156
|
if (task === null
|
|
150
|
-
|| task.status === "archived"
|
|
151
157
|
|| (!["turn.completed", "turn.failed", "turn.cancelled"].includes(event.observation.kind)
|
|
152
158
|
&& (task.status !== "active" || task.executionGate.state !== "enabled")))
|
|
153
159
|
return "obsolete";
|
|
@@ -247,7 +253,8 @@ export class FileRuntimeEventProcessor {
|
|
|
247
253
|
agentId: fence.agentId,
|
|
248
254
|
...(fence.runId === undefined ? {} : { runId: fence.runId }),
|
|
249
255
|
nativeSessionId: fence.nativeSessionId,
|
|
250
|
-
reason
|
|
256
|
+
reason,
|
|
257
|
+
...(reason === "task-archived" ? { originalEvent: event } : {})
|
|
251
258
|
}, now);
|
|
252
259
|
return;
|
|
253
260
|
}
|
|
@@ -263,7 +270,8 @@ export class FileRuntimeEventProcessor {
|
|
|
263
270
|
agentId: event.agentId,
|
|
264
271
|
...(event.runId === undefined ? {} : { runId: event.runId }),
|
|
265
272
|
nativeSessionId: event.nativeSessionId,
|
|
266
|
-
reason
|
|
273
|
+
reason,
|
|
274
|
+
...(reason === "task-archived" ? { originalEvent: event } : {})
|
|
267
275
|
}, now);
|
|
268
276
|
}
|
|
269
277
|
acknowledge(ids, acknowledged) {
|
|
@@ -394,6 +402,9 @@ function progressStreamKey(event) {
|
|
|
394
402
|
fence.driverId,
|
|
395
403
|
fence.nativeSessionId ?? null,
|
|
396
404
|
fence.runId ?? null,
|
|
405
|
+
fence.receiptId ?? null,
|
|
406
|
+
fence.nativeTurnId ?? null,
|
|
407
|
+
event.host ?? null,
|
|
397
408
|
payload.activity,
|
|
398
409
|
isRuntimeTokenEvidence(event.observation) ? "usage" : "signal"
|
|
399
410
|
]);
|
|
@@ -510,6 +521,7 @@ export function createAsyncRuntimeObserver(invoke) {
|
|
|
510
521
|
const withNow = (method, input, now) => now === undefined ? call(method, [input]) : call(method, [input, now]);
|
|
511
522
|
return {
|
|
512
523
|
getTask: (taskId) => call("getTask", [taskId]),
|
|
524
|
+
observeAgentHostObservation: (input, now) => withNow("observeAgentHostObservation", input, now),
|
|
513
525
|
observeRuntimeObservation: (input, now) => withNow("observeRuntimeObservation", input, now),
|
|
514
526
|
observeRuntimeRunTerminal: (input, now) => withNow("observeRuntimeRunTerminal", input, now),
|
|
515
527
|
observeGlobalRuntimeRunTerminal: (input, now) => withNow("observeGlobalRuntimeRunTerminal", input, now),
|
|
@@ -605,11 +617,17 @@ export class AsyncRuntimeEventProcessor {
|
|
|
605
617
|
};
|
|
606
618
|
}
|
|
607
619
|
async applyRuntimeObservation(event, now) {
|
|
620
|
+
if (event.host !== undefined) {
|
|
621
|
+
return this.observer.observeAgentHostObservation?.(event, now) ?? "deferred";
|
|
622
|
+
}
|
|
608
623
|
const taskId = event.observation.fence.taskId;
|
|
609
624
|
if (taskId !== undefined) {
|
|
610
625
|
const task = await this.observer.getTask(taskId);
|
|
626
|
+
if (task?.status === "archived") {
|
|
627
|
+
await this.recordObsolete(event, "task-archived", now);
|
|
628
|
+
return "obsolete";
|
|
629
|
+
}
|
|
611
630
|
if (task === null
|
|
612
|
-
|| task.status === "archived"
|
|
613
631
|
|| (!["turn.completed", "turn.failed", "turn.cancelled"].includes(event.observation.kind)
|
|
614
632
|
&& (task.status !== "active" || task.executionGate.state !== "enabled")))
|
|
615
633
|
return "obsolete";
|
|
@@ -685,7 +703,8 @@ export class AsyncRuntimeEventProcessor {
|
|
|
685
703
|
agentId: fence.agentId,
|
|
686
704
|
...(fence.runId === undefined ? {} : { runId: fence.runId }),
|
|
687
705
|
nativeSessionId: fence.nativeSessionId,
|
|
688
|
-
reason
|
|
706
|
+
reason,
|
|
707
|
+
...(reason === "task-archived" ? { originalEvent: event } : {})
|
|
689
708
|
}, now);
|
|
690
709
|
return;
|
|
691
710
|
}
|
|
@@ -697,7 +716,8 @@ export class AsyncRuntimeEventProcessor {
|
|
|
697
716
|
agentId: event.agentId,
|
|
698
717
|
...(event.runId === undefined ? {} : { runId: event.runId }),
|
|
699
718
|
nativeSessionId: event.nativeSessionId,
|
|
700
|
-
reason
|
|
719
|
+
reason,
|
|
720
|
+
...(reason === "task-archived" ? { originalEvent: event } : {})
|
|
701
721
|
}, now);
|
|
702
722
|
}
|
|
703
723
|
acknowledge(ids, acknowledged) {
|
|
@@ -5,27 +5,66 @@ import { formatRunReceiptId } from "../task/taskRecordReference.js";
|
|
|
5
5
|
import { currentProviderConversation, managedProviderTurnId } from "../runtime/providerRuntimeIdentity.js";
|
|
6
6
|
import { taskRoleRuntimeIdentity } from "../runtime/managedCaller.js";
|
|
7
7
|
import { runPurposeAdmitsTaskState } from "../agentRun/agentRun.js";
|
|
8
|
+
export class RuntimeHookRunFenceError extends Error {
|
|
9
|
+
}
|
|
8
10
|
/**
|
|
9
11
|
* Resolve observations by Session and exact input identity. A startup Hook
|
|
10
12
|
* may introduce a Session for an explicit new-Session AgentRun.
|
|
11
13
|
*/
|
|
12
|
-
export function resolveRuntimeHookRunFence(environment, adapterId, payloadNativeSessionId, options = {}) {
|
|
14
|
+
export function resolveRuntimeHookRunFence(environment, adapterId, payloadNativeSessionId, options = {}, currentStore) {
|
|
15
|
+
if (currentStore === undefined) {
|
|
16
|
+
const store = openCurrentTaskStore(requireIdentity(environment.YUI_HOME, "YUI_HOME"));
|
|
17
|
+
try {
|
|
18
|
+
return resolveRuntimeHookRunFence(environment, adapterId, payloadNativeSessionId, options, store);
|
|
19
|
+
}
|
|
20
|
+
finally {
|
|
21
|
+
store.close();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (environment.YUI_SESSION_SCOPE === "global") {
|
|
25
|
+
if (environment.YUI_ADAPTER_ID !== adapterId)
|
|
26
|
+
throw new RuntimeHookRunFenceError("Global observation adapter mismatch.");
|
|
27
|
+
const roleName = requireIdentity(environment.YUI_ROLE, "Role name");
|
|
28
|
+
const agentId = requireIdentity(environment.YUI_AGENT_ID, "Agent id");
|
|
29
|
+
const workspace = requireIdentity(environment.YUI_WORKSPACE, "YUI workspace");
|
|
30
|
+
const nativeSessionId = requireIdentity(payloadNativeSessionId, "Provider session id");
|
|
31
|
+
const sessions = currentStore.getGlobalRoleSessionSet(roleName);
|
|
32
|
+
const role = currentStore.getGlobalRole(roleName);
|
|
33
|
+
const session = sessions?.sessions[agentId];
|
|
34
|
+
const turn = sessions?.providerBinding?.run;
|
|
35
|
+
if (role?.activeAgentId !== agentId || sessions?.activeAgentId !== agentId || session?.adapterId !== adapterId
|
|
36
|
+
|| session.status === "ended"
|
|
37
|
+
|| session.nativeSessionId !== nativeSessionId || session.effective.workspace.root !== workspace) {
|
|
38
|
+
throw new RuntimeHookRunFenceError("Global observation Session does not match durable state.");
|
|
39
|
+
}
|
|
40
|
+
if (options.attemptId !== undefined && turn?.attemptId !== options.attemptId
|
|
41
|
+
|| options.nativeTurnId !== undefined && turn?.nativeTurnId !== undefined
|
|
42
|
+
&& turn.nativeTurnId !== options.nativeTurnId) {
|
|
43
|
+
throw new RuntimeHookRunFenceError("Global observation does not match the exact current input.");
|
|
44
|
+
}
|
|
45
|
+
if (options.exactInput && (turn == null
|
|
46
|
+
|| options.attemptId === undefined && (options.nativeTurnId === undefined || turn.nativeTurnId !== options.nativeTurnId)
|
|
47
|
+
|| ["rejected", "deferred"].includes(turn.status))) {
|
|
48
|
+
throw new RuntimeHookRunFenceError("Global observation input has no exact execution binding.");
|
|
49
|
+
}
|
|
50
|
+
return { roleName, agentId, workspace, nativeSessionId,
|
|
51
|
+
...(options.sessionOnly || turn == null ? {} : { receiptId: turn.attemptId }) };
|
|
52
|
+
}
|
|
13
53
|
if (environment.YUI_SESSION_SCOPE !== "task") {
|
|
14
|
-
throw new
|
|
54
|
+
throw new RuntimeHookRunFenceError("Runtime observation Hook requires a Task session scope.");
|
|
15
55
|
}
|
|
16
56
|
if (environment.YUI_ADAPTER_ID !== adapterId) {
|
|
17
|
-
throw new
|
|
57
|
+
throw new RuntimeHookRunFenceError(`Runtime observation Hook requires the ${adapterId} adapter.`);
|
|
18
58
|
}
|
|
19
|
-
const home = requireIdentity(environment.YUI_HOME, "YUI_HOME");
|
|
20
59
|
const taskId = requireIdentity(environment.YUI_TASK_ID, "Task id");
|
|
21
60
|
const roleName = requireIdentity(environment.YUI_ROLE, "Role name");
|
|
22
61
|
const agentId = requireIdentity(environment.YUI_AGENT_ID, "Agent id");
|
|
23
62
|
const workspace = requireIdentity(environment.YUI_WORKSPACE, "YUI workspace");
|
|
24
63
|
const nativeSessionId = requireIdentity(payloadNativeSessionId, "Provider session id");
|
|
25
|
-
const store =
|
|
64
|
+
const store = currentStore;
|
|
26
65
|
const task = store.getTask(taskId);
|
|
27
66
|
if (task === null) {
|
|
28
|
-
throw new
|
|
67
|
+
throw new RuntimeHookRunFenceError("Runtime observation Hook Task does not accept this lifecycle boundary.");
|
|
29
68
|
}
|
|
30
69
|
const role = store.getRole(taskId, roleName);
|
|
31
70
|
const sessions = store.getTaskRoleSessionSet(taskId, roleName);
|
|
@@ -56,6 +95,14 @@ export function resolveRuntimeHookRunFence(environment, adapterId, payloadNative
|
|
|
56
95
|
taskId, roleName, agentId, nativeSessionId,
|
|
57
96
|
continuationId: options.continuationId,
|
|
58
97
|
}));
|
|
98
|
+
if (options.exactInput && acceptedBinding === null && !matchesProviderTurn) {
|
|
99
|
+
throw new RuntimeHookRunFenceError("Runtime observation Hook input has no exact accepted execution binding.");
|
|
100
|
+
}
|
|
101
|
+
if (options.startupRunId !== undefined && options.startupRunId !== activeRun?.id) {
|
|
102
|
+
// A historical Session can report terminal evidence, but its old startup
|
|
103
|
+
// must not bind a new Session under a successor's active Run.
|
|
104
|
+
throw new RuntimeHookRunFenceError("Runtime observation Hook startup does not match its exact launch Run.");
|
|
105
|
+
}
|
|
59
106
|
// Collection of an exactly accepted terminal fact is not a new action by
|
|
60
107
|
// the Role. A successor's active Agent or revoked execution permission
|
|
61
108
|
// cannot erase the original AgentRun's evidence.
|
|
@@ -70,17 +117,26 @@ export function resolveRuntimeHookRunFence(environment, adapterId, payloadNative
|
|
|
70
117
|
&& activeRun.purpose === "planning"
|
|
71
118
|
&& activeRun.roleName === roleName
|
|
72
119
|
&& runPurposeAdmitsTaskState(activeRun.purpose, task);
|
|
120
|
+
// Restoring the Draft Leader's existing planning Session needs no new Run.
|
|
121
|
+
// Admit its Session facts; the shared identity/workspace fence below still
|
|
122
|
+
// requires the exact current Session and never grants delivery authority.
|
|
123
|
+
const planningSessionObservation = options.sessionOnly === true
|
|
124
|
+
&& task.status === "draft" && roleName === "leader"
|
|
125
|
+
&& runPurposeAdmitsTaskState("planning", task)
|
|
126
|
+
&& session?.status === "active"
|
|
127
|
+
&& session.effective.executionAuthority === "planning";
|
|
73
128
|
if (!(task.status === "active" && task.executionGate.state === "enabled")
|
|
74
129
|
&& !(task.status === "completed" && options.sessionOnly === true)
|
|
75
130
|
&& !planningObservation
|
|
131
|
+
&& !planningSessionObservation
|
|
76
132
|
&& !existingExecutionObservation) {
|
|
77
|
-
throw new
|
|
133
|
+
throw new RuntimeHookRunFenceError("Runtime observation Hook Task does not accept this lifecycle boundary.");
|
|
78
134
|
}
|
|
79
135
|
const runtimeIdentity = role === null ? null : taskRoleRuntimeIdentity(role, activeRun);
|
|
80
136
|
if (!existingExecutionObservation && (runtimeIdentity === null || runtimeIdentity.agentId !== agentId
|
|
81
137
|
|| runtimeIdentity.adapterId !== adapterId
|
|
82
138
|
|| (sessions !== null && sessions.activeAgentId !== agentId))) {
|
|
83
|
-
throw new
|
|
139
|
+
throw new RuntimeHookRunFenceError("Runtime observation Hook Role or Agent is not current.");
|
|
84
140
|
}
|
|
85
141
|
const lifecycleMailbox = store.getWorkMailbox(runtimeLifecycleTarget({
|
|
86
142
|
scope: "task",
|
|
@@ -97,7 +153,7 @@ export function resolveRuntimeHookRunFence(environment, adapterId, payloadNative
|
|
|
97
153
|
|| observedSession.adapterId !== adapterId
|
|
98
154
|
|| observedSession.nativeSessionId !== nativeSessionId
|
|
99
155
|
|| observedSession.effective.workspace.root !== workspace) {
|
|
100
|
-
throw new
|
|
156
|
+
throw new RuntimeHookRunFenceError("Runtime observation Hook Session does not match durable state.");
|
|
101
157
|
}
|
|
102
158
|
return {
|
|
103
159
|
taskId,
|
|
@@ -146,7 +202,7 @@ export function resolveRuntimeHookRunFence(environment, adapterId, payloadNative
|
|
|
146
202
|
? managedProviderTurnId(providerTurn) ?? undefined
|
|
147
203
|
: undefined;
|
|
148
204
|
if (options.terminal === true && acceptedBinding === null && registeredRunId === undefined) {
|
|
149
|
-
throw new
|
|
205
|
+
throw new RuntimeHookRunFenceError("Runtime observation Hook terminal has no exact accepted execution binding.");
|
|
150
206
|
}
|
|
151
207
|
const terminalRun = acceptedBinding !== null
|
|
152
208
|
? store.getRun(taskId, acceptedBinding.fence.runId)
|
|
@@ -166,7 +222,7 @@ export function resolveRuntimeHookRunFence(environment, adapterId, payloadNative
|
|
|
166
222
|
&& !exactTerminal
|
|
167
223
|
&& registeredRunId === undefined
|
|
168
224
|
&& acceptedBinding === null) {
|
|
169
|
-
throw new
|
|
225
|
+
throw new RuntimeHookRunFenceError("Runtime observation Hook has no matching durable in-flight AgentRun.");
|
|
170
226
|
}
|
|
171
227
|
const runId = acceptedBinding?.fence.runId
|
|
172
228
|
?? registeredRunId
|
|
@@ -181,21 +237,21 @@ export function resolveRuntimeHookRunFence(environment, adapterId, payloadNative
|
|
|
181
237
|
|| run.roleName !== roleName
|
|
182
238
|
|| run.effective.agentId !== agentId
|
|
183
239
|
|| run.effective.adapterId !== adapterId) {
|
|
184
|
-
throw new
|
|
240
|
+
throw new RuntimeHookRunFenceError("Runtime observation Hook AgentRun does not match durable active state.");
|
|
185
241
|
}
|
|
186
242
|
if (run.effective.workspace.root !== workspace) {
|
|
187
|
-
throw new
|
|
243
|
+
throw new RuntimeHookRunFenceError("Runtime observation Hook workspace does not match the durable AgentRun snapshot.");
|
|
188
244
|
}
|
|
189
245
|
if (session !== undefined && acceptedBinding === null && !matchesProviderTurn && !replacementStartup && !resumedStartup) {
|
|
190
246
|
if (session.adapterId !== adapterId
|
|
191
247
|
|| session.nativeSessionId !== nativeSessionId
|
|
192
248
|
|| session.effective.workspace.root !== workspace) {
|
|
193
|
-
throw new
|
|
249
|
+
throw new RuntimeHookRunFenceError("Runtime observation Hook Session does not match durable state.");
|
|
194
250
|
}
|
|
195
251
|
}
|
|
196
252
|
else if (acceptedBinding === null && !matchesProviderTurn && (session === undefined || replacementStartup)) {
|
|
197
253
|
if (!discoveredStartup && !preallocatedStartup) {
|
|
198
|
-
throw new
|
|
254
|
+
throw new RuntimeHookRunFenceError("Runtime observation Hook has no matching new-Session intent.");
|
|
199
255
|
}
|
|
200
256
|
}
|
|
201
257
|
return {
|
|
@@ -230,7 +286,7 @@ function knownContinuationBinding(events, expected) {
|
|
|
230
286
|
return null;
|
|
231
287
|
if (matches.some((candidate) => candidate.fence.runId !== binding.fence.runId
|
|
232
288
|
|| candidate.fence.receiptId !== binding.fence.receiptId)) {
|
|
233
|
-
throw new
|
|
289
|
+
throw new RuntimeHookRunFenceError("Runtime observation Hook continuation has conflicting durable AgentRun bindings.");
|
|
234
290
|
}
|
|
235
291
|
return binding;
|
|
236
292
|
}
|
|
@@ -260,17 +316,17 @@ function acceptedRunBinding(events, expected) {
|
|
|
260
316
|
&& candidate.fence.nativeTurnId !== expected.nativeTurnId)
|
|
261
317
|
|| candidate.fence.runId !== binding.fence.runId
|
|
262
318
|
|| candidate.fence.receiptId !== binding.fence.receiptId))) {
|
|
263
|
-
throw new
|
|
319
|
+
throw new RuntimeHookRunFenceError("Runtime observation Hook native Turn has conflicting durable AgentRun bindings.");
|
|
264
320
|
}
|
|
265
321
|
return binding;
|
|
266
322
|
}
|
|
267
323
|
function requireIdentity(value, label) {
|
|
268
324
|
if (typeof value !== "string" || value.includes("\0")) {
|
|
269
|
-
throw new
|
|
325
|
+
throw new RuntimeHookRunFenceError(`${label} is required.`);
|
|
270
326
|
}
|
|
271
327
|
const normalized = value.trim();
|
|
272
328
|
if (normalized.length === 0 || normalized.length > 1_024) {
|
|
273
|
-
throw new
|
|
329
|
+
throw new RuntimeHookRunFenceError(`${label} is invalid.`);
|
|
274
330
|
}
|
|
275
331
|
return normalized;
|
|
276
332
|
}
|