@zq-silk/yui 0.15.7 → 0.15.8
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 +192 -399
- package/README.md +127 -1149
- package/dist/agent/adapterCatalog.js +15 -2
- package/dist/agent/agent.js +23 -3
- package/dist/agent/argumentPolicy.js +7 -1
- package/dist/agent/connectionPlan.js +62 -0
- package/dist/agent/executionComponents.js +158 -0
- package/dist/agent/launchEnvironment.js +31 -3
- package/dist/agent/managedRuntimeEnvironment.js +3 -5
- package/dist/{turn/turn.js → agentRun/agentRun.js} +166 -109
- package/dist/{turn/turnIdentity.js → agentRun/runIdentity.js} +4 -4
- package/dist/brief/taskBrief.js +12 -0
- package/dist/cli/agentConfigurationPicker.js +13 -0
- package/dist/cli/commandCatalog.js +157 -70
- package/dist/cli/interactionCandidates.js +5 -5
- package/dist/cli/interactionPolicy.js +38 -8
- package/dist/cli/invocationRouter.js +1 -1
- package/dist/cli/managedDiagnostics.js +28 -0
- package/dist/cli/operatorWizard.js +1 -7
- package/dist/cli/roleOptionOrder.js +27 -0
- package/dist/cli/roleWizard.js +50 -14
- package/dist/cli/updateOrchestrator.js +1 -1
- package/dist/cli/updatePorts.js +3 -4
- package/dist/cli.js +178 -95
- package/dist/commands/agentCommands.js +72 -14
- package/dist/commands/capabilityCommands.js +9 -6
- package/dist/commands/configCommands.js +20 -20
- package/dist/commands/deliveryGuardPreflight.js +2 -2
- package/dist/commands/executionAuditCommands.js +24 -24
- package/dist/commands/globalRoleCommands.js +1 -1
- package/dist/commands/grantCommands.js +4 -4
- package/dist/commands/operatorCommands.js +1 -7
- package/dist/commands/projectCommands.js +4 -4
- package/dist/commands/resourcesCommands.js +2 -2
- package/dist/commands/roleConfiguration.js +25 -5
- package/dist/commands/roleRuntimeGuard.js +4 -5
- package/dist/commands/sessionCommands.js +3 -7
- package/dist/commands/taskActivationCommands.js +259 -0
- package/dist/commands/taskActor.js +28 -49
- package/dist/commands/taskCommands.js +1188 -710
- package/dist/commands/taskContextCommand.js +39 -583
- package/dist/commands/taskExecutionCommands.js +32 -32
- package/dist/commands/taskInputCommands.js +40 -104
- package/dist/commands/taskIntegrationCommands.js +3 -2
- package/dist/commands/taskIntegrationQueueCommands.js +1 -1
- package/dist/commands/taskNextActionCommand.js +8 -8
- package/dist/commands/taskOverviewCommand.js +33 -45
- package/dist/commands/taskRemoteDeliveryCommand.js +2 -2
- package/dist/commands/taskRoleRuntimeStatus.js +133 -102
- package/dist/commands/telemetryCommands.js +36 -38
- package/dist/config/configCatalog.js +4 -4
- package/dist/config/yuiConfig.js +8 -8
- package/dist/context/contextSnapshot.js +10 -10
- package/dist/context/dispatchContext.js +11 -11
- package/dist/context/roleSessionContext.js +6 -3
- package/dist/context/{turnContextPack.js → runContextPack.js} +146 -81
- package/dist/context/{turnInputContract.js → runInputContract.js} +73 -60
- package/dist/context/sessionBootstrapManifest.js +21 -2
- package/dist/context/sourceRunContext.js +30 -0
- package/dist/context/taskContext.js +458 -0
- package/dist/context/wakeNotification.js +27 -27
- package/dist/controller/agentRuntimeObserver.js +21 -24
- package/dist/controller/capabilityBridge.js +17 -6
- package/dist/controller/clientRuntime.js +65 -92
- package/dist/controller/controller.js +114 -188
- package/dist/controller/fileSchedulerStoreAdapter.js +787 -887
- package/dist/controller/jobControl.js +54 -85
- package/dist/controller/resourceInventory.js +8 -27
- package/dist/controller/resourceInventoryLinux.js +12 -13
- package/dist/controller/runtime.js +529 -479
- package/dist/controller/runtimeEventInbox.js +55 -25
- package/dist/controller/runtimeEventProcessor.js +22 -31
- package/dist/controller/{runtimeHookTurnFence.js → runtimeHookRunFence.js} +91 -115
- package/dist/controller/runtimeLaunchCoordinator.js +80 -426
- package/dist/controller/runtimeObservationHook.js +14 -18
- package/dist/controller/sessionNotify.js +16 -24
- package/dist/controller/sessionOwnerReconciliation.js +168 -50
- package/dist/controller/structuredProviderObservation.js +138 -99
- package/dist/coordination/workMailbox.js +3 -3
- package/dist/coordination/workMailboxQueue.js +36 -33
- package/dist/core/boundedRpc.js +8 -1
- package/dist/core/controllerClient.js +20 -1
- package/dist/core/controllerServer.js +4 -4
- package/dist/doctor/doctor.js +13 -2
- package/dist/domain/agentResultTransport.js +9 -9
- package/dist/execution/codexThreadNaming.js +2 -8
- package/dist/execution/executionHealth.js +51 -63
- package/dist/execution/reviewMainRun.js +137 -0
- package/dist/execution/workItemExecution.js +28 -29
- package/dist/execution/workItemExecutionProjection.js +99 -107
- package/dist/execution/workItemMainRun.js +141 -0
- package/dist/executor/agentAdapter.js +227 -20
- package/dist/executor/agentConfigurationCatalog.js +126 -4
- package/dist/executor/agentConfigurationProbe.js +162 -4
- package/dist/executor/agentExecutor.js +79 -78
- package/dist/executor/effectiveLaunch.js +105 -18
- package/dist/executor/executorRegistry.js +29 -44
- package/dist/executor/fileRoleLaunchPlanner.js +229 -154
- package/dist/executor/workspacePreflightClassification.js +16 -16
- package/dist/grant/capabilityGrant.js +6 -3
- package/dist/input/inputRequest.js +12 -10
- package/dist/integration/gitIntegrationService.js +4 -11
- package/dist/integration/integrationQueueService.js +4 -4
- package/dist/interaction/operatorPresentation.js +1 -1
- package/dist/kernel/builtinCapabilities.js +247 -12
- package/dist/kernel/capabilityRegistry.js +64 -18
- package/dist/kernel/instanceHost.js +12 -1
- package/dist/kernel/kernelPorts.js +2 -2
- package/dist/lifecycle/canonicalLifecycleEvent.js +44 -49
- package/dist/lifecycle/exactRunTerminalization.js +449 -0
- package/dist/message/message.js +62 -6
- package/dist/message/messageContinuation.js +204 -0
- package/dist/observability/executionAudit.js +70 -72
- package/dist/observability/faultClassification.js +2 -2
- package/dist/observability/orchestrationMetrics.js +8 -8
- package/dist/operator/operatorSessionHistory.js +1 -7
- package/dist/output/agentConfigurationPresentation.js +8 -3
- package/dist/output/agentRunConfigurationPresentation.js +128 -0
- package/dist/output/rolePresentation.js +54 -3
- package/dist/plugins/pluginChild.js +104 -0
- package/dist/plugins/pluginIntent.js +26 -0
- package/dist/plugins/pluginInterpreter.js +43 -0
- package/dist/plugins/pluginPackage.js +101 -0
- package/dist/plugins/pluginProcess.js +112 -0
- package/dist/plugins/pluginService.js +380 -0
- package/dist/profile/agentProfile.js +1 -1
- package/dist/repository/gitWorkspace.js +26 -4
- package/dist/repository/project.js +19 -4
- package/dist/repository/taskBaseFreshness.js +13 -13
- package/dist/repository/taskWorkspaceCoordinator.js +20 -27
- package/dist/repository/taskWorkspacePreparer.js +344 -83
- package/dist/resources/autoResourceGc.js +3 -3
- package/dist/resources/liveReferences.js +3 -3
- package/dist/resources/projectResource.js +123 -0
- package/dist/resources/projectResourceService.js +421 -0
- package/dist/resources/resourceDiscovery.js +6 -6
- package/dist/resources/resourceGc.js +1 -1
- package/dist/resources/resourceRegistrar.js +1 -1
- package/dist/resources/resourceTypes.js +1 -1
- package/dist/review/deltaRecheck.js +3 -3
- package/dist/review/reviewAcceptance.js +16 -16
- package/dist/review/reviewDecision.js +7 -7
- package/dist/review/reviewRound.js +21 -20
- package/dist/review/reviewerAvailability.js +2 -2
- package/dist/role/role.js +51 -7
- package/dist/role/taskRoleUpdate.js +30 -0
- package/dist/runtime/acpProtocol.js +425 -0
- package/dist/runtime/acpSession.js +731 -0
- package/dist/runtime/acpSessionConfiguration.js +260 -0
- package/dist/runtime/agentDriver.js +30 -11
- package/dist/runtime/agentEndpoint.js +278 -0
- package/dist/runtime/agentEndpointIdentity.js +86 -0
- package/dist/runtime/agentEndpointOwnership.js +239 -0
- package/dist/runtime/agentError.js +2 -10
- package/dist/runtime/agentHost.js +565 -314
- package/dist/runtime/agentRunConfiguration.js +258 -0
- package/dist/runtime/builtinAgentDrivers.js +134 -18
- package/dist/runtime/builtinAgentErrorMappers.js +55 -3
- package/dist/runtime/builtinTranscriptUsage.js +1 -1
- package/dist/runtime/claude-process-owner +0 -0
- package/dist/runtime/codexAppServerRuntime.js +38 -30
- package/dist/runtime/codexInteractiveHost.js +41 -6
- package/dist/runtime/continuationManager.js +2 -6
- package/dist/runtime/executionEnvironment.js +30 -0
- package/dist/runtime/firstProgressAdvisory.js +11 -11
- package/dist/runtime/index.js +4 -3
- package/dist/runtime/jsonLineChannel.js +109 -0
- package/dist/runtime/launchBroker.js +91 -16
- package/dist/runtime/launchDiagnostics.js +2 -2
- package/dist/runtime/lifecycleReservation.js +10 -18
- package/dist/runtime/managedCaller.js +61 -17
- package/dist/runtime/nativeSessionControl.js +102 -0
- package/dist/runtime/ports.js +6 -21
- package/dist/runtime/processExitObservation.js +8 -7
- package/dist/runtime/promptEnvelope.js +17 -6
- package/dist/runtime/providerContinuation.js +3 -9
- package/dist/runtime/providerContinuationReconciliationService.js +4 -13
- package/dist/runtime/providerControl.js +2 -7
- package/dist/runtime/providerRuntimeIdentity.js +110 -222
- package/dist/runtime/providerRuntimeReconciler.js +5 -9
- package/dist/runtime/runtimeBinding.js +0 -1
- package/dist/runtime/runtimeContinuationProjection.js +4 -7
- package/dist/runtime/runtimeDeadlines.js +9 -0
- package/dist/runtime/runtimeHealthPolicy.js +1 -1
- package/dist/runtime/runtimeObservation.js +29 -65
- package/dist/runtime/runtimeProjection.js +43 -51
- package/dist/runtime/runtimeSessionCandidate.js +1 -3
- package/dist/runtime/sessionLaunchRequest.js +3 -7
- package/dist/runtime/sessionOwnerIdentity.js +7 -54
- package/dist/runtime/sessionOwnerRegistry.js +22 -17
- package/dist/runtime/sessionReconciliation.js +4 -8
- package/dist/runtime/sessionTerminationGuard.js +70 -259
- package/dist/runtime/sessionTokenMetrics.js +5 -16
- package/dist/runtime/structuredProviderHost.js +237 -117
- package/dist/runtime/taskRuntimeIsolation.js +39 -122
- package/dist/runtime/tmuxAdapters.js +39 -86
- package/dist/scheduler/activeRoleRunDelivery.js +354 -0
- package/dist/scheduler/leaderWakeupProcessor.js +75 -266
- package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
- package/dist/scheduler/ports.js +80 -9
- package/dist/scheduler/{roleTurnLiveness.js → roleRunLiveness.js} +26 -30
- package/dist/scheduler/{roleTurnStall.js → roleRunStall.js} +128 -139
- package/dist/scheduler/taskExecutionProjection.js +120 -124
- package/dist/scheduler/taskObservabilityProjection.js +29 -29
- package/dist/scheduler/taskWake.js +11 -4
- package/dist/scheduler/wakeReason.js +9 -1
- package/dist/setup/setupCommand.js +1 -0
- package/dist/storage/migrations/agentRunContract.js +159 -0
- package/dist/storage/migrations/removeRuntimeGeneration.js +207 -0
- package/dist/storage/sqliteSchema.js +431 -5
- package/dist/storage/sqliteStore.js +375 -220
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +10 -5
- package/dist/storage/taskStore.js +13 -11
- package/dist/storage/upgrade/upgradeOrchestrator.js +5 -7
- package/dist/surface/surfaceContributions.js +102 -0
- package/dist/task/completionReadiness.js +32 -6
- package/dist/task/deliveryGuard.js +16 -16
- package/dist/task/draftPlan.js +72 -12
- package/dist/task/nextAction.js +144 -128
- package/dist/task/remoteDelivery.js +6 -6
- package/dist/task/task.js +184 -18
- package/dist/task/taskActivation.js +301 -0
- package/dist/task/taskActivationService.js +392 -0
- package/dist/task/taskRecordReference.js +5 -4
- package/dist/task/taskRecordRetirement.js +1 -1
- package/dist/telemetry/sqliteTelemetryStore.js +55 -68
- package/dist/telemetry/telemetryConfig.js +14 -14
- package/dist/telemetry/telemetryWiring.js +2 -2
- package/dist/web/assets/assetManifest.js +2 -0
- package/dist/web/assets/client/app.js +120 -20
- package/dist/web/assets/client/components.js +87 -54
- package/dist/web/assets/client/i18n.js +83 -41
- package/dist/web/assets/client/markdown.js +1 -1
- package/dist/web/assets/client/taskSurface.js +353 -0
- package/dist/web/assets/client/view.js +49 -44
- package/dist/web/assets/shell.js +1 -1
- package/dist/web/assets/styles/cards.js +22 -4
- package/dist/web/controllerWeb.js +60 -0
- package/dist/web/webMutation.js +28 -0
- package/dist/web/webServer.js +118 -8
- package/dist/web/webSnapshot.js +81 -74
- package/dist/web/webTaskSurface.js +64 -0
- package/dist/workItem/dependencyGate.js +1 -1
- package/dist/workItem/workItem.js +80 -48
- package/dist/workspace/workItemChangeSetManager.js +16 -9
- package/docs/agent-result-consumption.md +94 -0
- package/docs/agent-runtime-drivers.md +91 -0
- package/docs/architecture/README.md +38 -0
- package/docs/architecture/capabilities-and-resources.md +79 -0
- package/docs/managed-turn-and-session-runtime.md +222 -0
- package/docs/observability/README.md +81 -0
- package/docs/plugin-sdk.md +290 -0
- package/docs/provider-runtime.md +163 -0
- package/docs/release-workflow.md +303 -0
- package/docs/roles-and-configuration.md +113 -0
- package/docs/sqlite-control-plane-design.md +76 -0
- package/docs/task-dag-semantics.md +57 -0
- package/docs/task-delivery.md +103 -0
- package/docs/task-local-identity.md +6 -6
- package/docs/testing/verification-levels.md +86 -0
- package/i18n/README.zh-CN.md +98 -739
- package/package.json +2 -2
- package/skills/yui-leader/SKILL.md +130 -103
- package/skills/yui-leader/references/integration.md +39 -0
- package/skills/yui-leader/references/replicated-execution.md +42 -0
- package/skills/yui-leader/references/task-plugins.md +33 -0
- package/skills/yui-operator/SKILL.md +30 -59
- package/skills/yui-reviewer/SKILL.md +35 -36
- package/skills/yui-runtime/SKILL.md +88 -24
- package/skills/yui-runtime/references/publication.md +22 -0
- package/skills/yui-runtime/references/recovery.md +64 -0
- package/skills/yui-worker/SKILL.md +37 -39
- package/dist/cli/roleOptionCatalog.js +0 -68
- package/dist/context/sourceTurnContext.js +0 -30
- package/dist/execution/reviewMainTurn.js +0 -161
- package/dist/execution/workItemMainTurn.js +0 -164
- package/dist/lifecycle/exactTurnTerminalization.js +0 -407
- package/dist/runtime/preallocatedNativeSession.js +0 -13
- package/dist/runtime/runtimeStopReceipt.js +0 -42
- package/dist/scheduler/activeRoleTurnDelivery.js +0 -315
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { detectRunningRelease } from "../release/runtimeRelease.js";
|
|
5
|
+
import { isAgentAdapterId } from "../agent/adapterCatalog.js";
|
|
6
|
+
/**
|
|
7
|
+
* The generation of the Endpoint code this process is actually executing.
|
|
8
|
+
*
|
|
9
|
+
* Resolved once, at module load, from the immutable release the running module
|
|
10
|
+
* belongs to — the same idiom the Controller, the handover candidate and the CLI
|
|
11
|
+
* Home fence already use. A release directory is content-addressed
|
|
12
|
+
* (`<version>-<packageDigest>`) and byte-verified on install, so its digest
|
|
13
|
+
* covers every shipped file rather than a hand-picked few, and cannot drift
|
|
14
|
+
* under a live process: publishing a new release writes a new directory instead
|
|
15
|
+
* of mutating this one.
|
|
16
|
+
*
|
|
17
|
+
* Resolving at load rather than on first use is the point. A digest computed
|
|
18
|
+
* lazily would read whatever is on disk when the first Session happens to pin,
|
|
19
|
+
* and could label already-loaded code A with the identity of a newer B.
|
|
20
|
+
*
|
|
21
|
+
* A development checkout has no immutable release, and mutable files have no
|
|
22
|
+
* honest generation: editing a module in place would keep claiming the previous
|
|
23
|
+
* bytes. So a checkout gets a digest of its loaded Endpoint modules, explicitly
|
|
24
|
+
* marked `checkout-`, which never compares equal to a release generation. It is
|
|
25
|
+
* a development identity and is not claimed to be reproducible from a release.
|
|
26
|
+
*/
|
|
27
|
+
const ENDPOINT_CHECKOUT_MODULES = Object.freeze([
|
|
28
|
+
"agentEndpoint.js",
|
|
29
|
+
"agentEndpointIdentity.js",
|
|
30
|
+
"structuredProviderHost.js",
|
|
31
|
+
"claude-process-owner",
|
|
32
|
+
"codexAppServerRuntime.js",
|
|
33
|
+
"acpSession.js",
|
|
34
|
+
"acpProtocol.js",
|
|
35
|
+
"acpSessionConfiguration.js",
|
|
36
|
+
"agentRunConfiguration.js",
|
|
37
|
+
"jsonLineChannel.js"
|
|
38
|
+
]);
|
|
39
|
+
function resolveEndpointGeneration() {
|
|
40
|
+
const running = detectRunningRelease(fileURLToPath(import.meta.url));
|
|
41
|
+
// The release digest already covers these modules' bytes, so an installed
|
|
42
|
+
// release needs no separate file list to be exact.
|
|
43
|
+
if (running !== null)
|
|
44
|
+
return running.manifest.packageDigest.slice(0, 32);
|
|
45
|
+
const hash = createHash("sha256");
|
|
46
|
+
for (const module of ENDPOINT_CHECKOUT_MODULES) {
|
|
47
|
+
// An unreadable module is a broken installation, not a reason to fall back
|
|
48
|
+
// to an identity that would silently match a different build.
|
|
49
|
+
hash.update(module).update("\0").update(readFileSync(new URL(module, import.meta.url))).update("\0");
|
|
50
|
+
}
|
|
51
|
+
return `checkout-${hash.digest("hex").slice(0, 23)}`;
|
|
52
|
+
}
|
|
53
|
+
const ENDPOINT_GENERATION = resolveEndpointGeneration();
|
|
54
|
+
/**
|
|
55
|
+
* Built-in Endpoint identity under the existing ImplementationRef contract.
|
|
56
|
+
* Uses the adopted release/checkout identity; matching it does not by itself
|
|
57
|
+
* prove that the Provider can recover its native Session.
|
|
58
|
+
*/
|
|
59
|
+
export function builtinAgentEndpointImplementation(adapterId) {
|
|
60
|
+
// Every catalogued adapter has a managed Endpoint. Re-listing the adapter
|
|
61
|
+
// names here would let the two lists drift, so the catalog is the only source.
|
|
62
|
+
if (!isAgentAdapterId(adapterId)) {
|
|
63
|
+
throw new Error(`No managed Endpoint implementation for adapter: ${adapterId}.`);
|
|
64
|
+
}
|
|
65
|
+
return Object.freeze({
|
|
66
|
+
id: `yui.agent-endpoint.${adapterId}`,
|
|
67
|
+
generation: ENDPOINT_GENERATION
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
export function validateAgentEndpointImplementation(ref) {
|
|
71
|
+
if (ref === null || typeof ref !== "object"
|
|
72
|
+
|| typeof ref.id !== "string" || !ref.id.trim() || ref.id.includes("\0")
|
|
73
|
+
|| typeof ref.generation !== "string" || !ref.generation.trim() || ref.generation.includes("\0")) {
|
|
74
|
+
throw new Error("Session Endpoint implementation identity is invalid.");
|
|
75
|
+
}
|
|
76
|
+
return ref;
|
|
77
|
+
}
|
|
78
|
+
/** Missing old code is an explicit inability to resume, never a Provider fallback. */
|
|
79
|
+
export function requireBuiltinAgentEndpointImplementation(adapterId, ref) {
|
|
80
|
+
validateAgentEndpointImplementation(ref);
|
|
81
|
+
const current = builtinAgentEndpointImplementation(adapterId);
|
|
82
|
+
if (ref.id !== current.id || ref.generation !== current.generation) {
|
|
83
|
+
throw new Error(`Session Endpoint ${ref.id}@${ref.generation} is unavailable; explicitly select a new Session.`);
|
|
84
|
+
}
|
|
85
|
+
return ref;
|
|
86
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { InstanceHost } from "../kernel/instanceHost.js";
|
|
2
|
+
import { builtinAgentEndpointImplementation, requireBuiltinAgentEndpointImplementation } from "./agentEndpointIdentity.js";
|
|
3
|
+
import { createAgentEndpointFactory } from "./agentEndpoint.js";
|
|
4
|
+
/**
|
|
5
|
+
* Process-local ownership of the Endpoint implementation this Agent Host is
|
|
6
|
+
* executing. Reuses the one Instance Host primitive; it is not a second
|
|
7
|
+
* registry, a capability directory or a durable reference table. The Controller
|
|
8
|
+
* owns its own Host in its own process and never holds an Endpoint client, so
|
|
9
|
+
* the reference facts for a Session live where the Session actually runs.
|
|
10
|
+
*
|
|
11
|
+
* Only the running code can be attached here. Selecting between generations is
|
|
12
|
+
* the launch decision recorded in the durable Session; this owner proves the
|
|
13
|
+
* chosen generation is really held and really released.
|
|
14
|
+
*/
|
|
15
|
+
export function createAgentEndpointOwner(factory = createAgentEndpointFactory()) {
|
|
16
|
+
const host = new InstanceHost();
|
|
17
|
+
const attached = new Map();
|
|
18
|
+
const live = new Map();
|
|
19
|
+
const implementationFor = (adapterId) => {
|
|
20
|
+
const current = builtinAgentEndpointImplementation(adapterId);
|
|
21
|
+
const existing = attached.get(current.id);
|
|
22
|
+
if (existing !== undefined)
|
|
23
|
+
return existing;
|
|
24
|
+
// The disposer owns the opener, not the clients: a Session's client is
|
|
25
|
+
// terminated by that Session's detach. Dropping the opener stops a leaked
|
|
26
|
+
// reference from starting new work on a generation that is already gone.
|
|
27
|
+
const ref = host.attach(current, factory, [
|
|
28
|
+
() => { live.delete(current.id); }
|
|
29
|
+
]);
|
|
30
|
+
attached.set(current.id, ref);
|
|
31
|
+
live.set(current.id, new Set());
|
|
32
|
+
return ref;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Hand the reference back only when the Session asked to end *and* it has no
|
|
36
|
+
* client running *and* no handshake in flight. A client that never proves exit
|
|
37
|
+
* keeps this hold, so the drain below reports a real dependency instead of an
|
|
38
|
+
* empty list; so does an opener that has not answered yet, whose client may
|
|
39
|
+
* still be about to attach.
|
|
40
|
+
*/
|
|
41
|
+
const settle = async (holders, holder) => {
|
|
42
|
+
if (holder.settled || !holder.releaseRequested)
|
|
43
|
+
return;
|
|
44
|
+
if (holder.current !== undefined || holder.opening > 0)
|
|
45
|
+
return;
|
|
46
|
+
holder.settled = true;
|
|
47
|
+
holders.delete(holder);
|
|
48
|
+
await holder.handle.release();
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Pin the implementation for one Session. A pinned reference from the durable
|
|
52
|
+
* Session must name this exact code; a mismatch is an explicit inability to
|
|
53
|
+
* resume, never a silent start on a different generation.
|
|
54
|
+
*/
|
|
55
|
+
const pin = (adapterId, pinned) => {
|
|
56
|
+
if (pinned !== undefined)
|
|
57
|
+
requireBuiltinAgentEndpointImplementation(adapterId, pinned);
|
|
58
|
+
const ref = implementationFor(adapterId);
|
|
59
|
+
const handle = host.acquire(ref);
|
|
60
|
+
const holders = live.get(ref.id);
|
|
61
|
+
// One entry per Session hold, reporting the client it currently has. A
|
|
62
|
+
// Session may reattach a replacement client for the same conversation; that
|
|
63
|
+
// succession is one hold, not two, so a drain never double-counts it.
|
|
64
|
+
const holder = { handle, opening: 0, releaseRequested: false, settled: false };
|
|
65
|
+
holders.add(holder);
|
|
66
|
+
const track = async (opened) => {
|
|
67
|
+
// Counted before the handshake is awaited, so a stop that lands while the
|
|
68
|
+
// client is still coming up sees this Session as a real holder.
|
|
69
|
+
holder.opening += 1;
|
|
70
|
+
let result;
|
|
71
|
+
try {
|
|
72
|
+
result = await opened;
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
// A handshake that failed owes nothing: no client attached, so the hold
|
|
76
|
+
// ends on the real dependency rather than waiting for an exit that can
|
|
77
|
+
// never come.
|
|
78
|
+
holder.opening -= 1;
|
|
79
|
+
void settle(holders, holder).catch(() => undefined);
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
holder.current = result.session;
|
|
83
|
+
holder.opening -= 1;
|
|
84
|
+
// Only a resolved exit proves the client stopped. An exit that never
|
|
85
|
+
// settles, or that fails, leaves this hold in place on purpose: the drain
|
|
86
|
+
// must be able to say what is still running.
|
|
87
|
+
void result.session.waitForExit().then(() => {
|
|
88
|
+
if (holder.current !== result.session)
|
|
89
|
+
return;
|
|
90
|
+
holder.current = undefined;
|
|
91
|
+
void settle(holders, holder).catch(() => undefined);
|
|
92
|
+
}, () => { });
|
|
93
|
+
// A stop may already have been requested while this handshake was in
|
|
94
|
+
// flight. The client is real and attached, so it enters the same controlled
|
|
95
|
+
// exit tracking instead of being handed back as an untracked leak: ask it to
|
|
96
|
+
// exit and let its proven exit release the hold.
|
|
97
|
+
if (holder.releaseRequested)
|
|
98
|
+
result.session.detach();
|
|
99
|
+
return result;
|
|
100
|
+
};
|
|
101
|
+
const start = (call) => {
|
|
102
|
+
// An acquired handle keeps its captured opener even after the generation is
|
|
103
|
+
// disposed, so a released lease must refuse to start new work rather than
|
|
104
|
+
// run on code the Session has already given up.
|
|
105
|
+
if (holder.releaseRequested) {
|
|
106
|
+
return Promise.reject(new Error("Session Endpoint lease is released; it cannot start new work."));
|
|
107
|
+
}
|
|
108
|
+
return track(call(handle.value));
|
|
109
|
+
};
|
|
110
|
+
return Object.freeze({
|
|
111
|
+
implementation: handle.implementation,
|
|
112
|
+
open: (payload) => start((value) => value.open(payload)),
|
|
113
|
+
resume: (payload) => start((value) => value.resume(payload)),
|
|
114
|
+
release: async () => {
|
|
115
|
+
// Idempotent, and never blocking: a client that has not exited keeps the
|
|
116
|
+
// reference, and `stop` is what bounds and reports that wait. A hold that
|
|
117
|
+
// never opened a client (a failed start) owes nothing and settles here.
|
|
118
|
+
holder.releaseRequested = true;
|
|
119
|
+
await settle(holders, holder);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
/** Actual reference and request records, never a status label. */
|
|
124
|
+
const inspectImplementation = (implementationId, waited) => {
|
|
125
|
+
const instances = host.inspect(implementationId);
|
|
126
|
+
const references = instances.reduce((total, instance) => total + instance.references, 0);
|
|
127
|
+
const holders = [...live.get(implementationId) ?? []];
|
|
128
|
+
const opening = holders.reduce((total, holder) => total + holder.opening, 0);
|
|
129
|
+
const sessions = holders.flatMap((holder) => {
|
|
130
|
+
const endpoint = holder.current;
|
|
131
|
+
// A hold between clients has nothing attached to report. Its hold is
|
|
132
|
+
// still counted in `references`, so it cannot be mistaken for quiescence.
|
|
133
|
+
if (endpoint === undefined)
|
|
134
|
+
return [];
|
|
135
|
+
const state = endpoint.inspect();
|
|
136
|
+
return [Object.freeze({
|
|
137
|
+
nativeSessionId: endpoint.nativeSessionId,
|
|
138
|
+
processInstanceId: endpoint.processInstanceId,
|
|
139
|
+
attachment: state.attachment,
|
|
140
|
+
cancellation: state.cancellation,
|
|
141
|
+
resources: state.resources,
|
|
142
|
+
releaseAwaitingExit: holder.releaseRequested,
|
|
143
|
+
pending: Object.freeze(state.submissions
|
|
144
|
+
.filter((submission) => submission.disposition.status === "pending"
|
|
145
|
+
|| submission.disposition.status === "unknown")
|
|
146
|
+
.map((submission) => Object.freeze({
|
|
147
|
+
attemptId: submission.attemptId,
|
|
148
|
+
inputRef: submission.inputRef,
|
|
149
|
+
status: submission.disposition.status
|
|
150
|
+
})))
|
|
151
|
+
})];
|
|
152
|
+
});
|
|
153
|
+
return Object.freeze({
|
|
154
|
+
// Quiescent means nothing holds the implementation, no handshake is still
|
|
155
|
+
// in flight, and no owned client is still attached. Holds are dropped by
|
|
156
|
+
// proven exit, so an unreleased Session, an opening client or a client that
|
|
157
|
+
// never exited keeps this false.
|
|
158
|
+
quiescent: references === 0
|
|
159
|
+
&& opening === 0
|
|
160
|
+
&& sessions.every((session) => session.attachment === "exited" && session.pending.length === 0),
|
|
161
|
+
references,
|
|
162
|
+
opening,
|
|
163
|
+
waitedMs: waited.waitedMs,
|
|
164
|
+
timedOut: waited.timedOut,
|
|
165
|
+
sessions: Object.freeze(sessions)
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
const inspectWith = (adapterId, waited) => inspectImplementation(builtinAgentEndpointImplementation(adapterId).id, waited);
|
|
169
|
+
const inspect = (adapterId) => inspectWith(adapterId, { waitedMs: 0, timedOut: false });
|
|
170
|
+
/**
|
|
171
|
+
* Wait a bounded time for one already-detached drain, and report whether the
|
|
172
|
+
* bound ran out. The timer keeps the process alive for the window, or a caller
|
|
173
|
+
* with nothing else pending would exit before reporting what is still in use,
|
|
174
|
+
* and is cleared as soon as the drain settles so a released implementation
|
|
175
|
+
* never delays shutdown until the deadline.
|
|
176
|
+
*/
|
|
177
|
+
const awaitDrained = async (drained, timeoutMs) => {
|
|
178
|
+
const startedAt = Date.now();
|
|
179
|
+
let timedOut = false;
|
|
180
|
+
if (timeoutMs > 0) {
|
|
181
|
+
let expire;
|
|
182
|
+
try {
|
|
183
|
+
timedOut = !await Promise.race([
|
|
184
|
+
drained,
|
|
185
|
+
new Promise((resolve) => { expire = setTimeout(() => resolve(false), timeoutMs); })
|
|
186
|
+
]);
|
|
187
|
+
}
|
|
188
|
+
finally {
|
|
189
|
+
if (expire !== undefined)
|
|
190
|
+
clearTimeout(expire);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
await Promise.resolve();
|
|
195
|
+
}
|
|
196
|
+
return Object.freeze({ waitedMs: Date.now() - startedAt, timedOut });
|
|
197
|
+
};
|
|
198
|
+
/**
|
|
199
|
+
* Ordinary stop: no new independent acquisition, while an already-acquired
|
|
200
|
+
* Session ends bounded. Resolves with the drain facts once every reference is
|
|
201
|
+
* released, or with what is still in use when the wait runs out.
|
|
202
|
+
*/
|
|
203
|
+
const stop = async (adapterId, timeoutMs = 0) => {
|
|
204
|
+
const ref = attached.get(builtinAgentEndpointImplementation(adapterId).id);
|
|
205
|
+
if (ref === undefined) {
|
|
206
|
+
return Object.freeze({
|
|
207
|
+
quiescent: true, references: 0, opening: 0, waitedMs: 0, timedOut: false, sessions: []
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
// A disposer failure still ends acquisition; the drain facts below, not the
|
|
211
|
+
// settle reason, decide whether this is actually quiescent.
|
|
212
|
+
const drained = host.detach(ref).then(() => true, () => true);
|
|
213
|
+
return inspectWith(adapterId, await awaitDrained(drained, timeoutMs));
|
|
214
|
+
};
|
|
215
|
+
/**
|
|
216
|
+
* Final cleanup, bounded by construction. Every attached implementation is
|
|
217
|
+
* detached so nothing can acquire again, and the wait for their drains shares
|
|
218
|
+
* one deadline. `InstanceHost.close()` resolves only after every reference is
|
|
219
|
+
* handed back, which a client that never proves exit will never do; awaiting it
|
|
220
|
+
* here would block the Host's own shutdown after a bounded `stop` already
|
|
221
|
+
* reported that dependency, leaving the control socket unclosed.
|
|
222
|
+
*
|
|
223
|
+
* So this returns on the deadline and says what is still held, rather than
|
|
224
|
+
* releasing a reference it cannot prove or killing a client it does not own.
|
|
225
|
+
* The still-live implementations stay detached and undisposed on purpose: their
|
|
226
|
+
* facts remain readable, and the caller decides what to do about them.
|
|
227
|
+
*/
|
|
228
|
+
const close = async (timeoutMs = 0) => {
|
|
229
|
+
const drains = [...attached.values()].map((ref) => Object.freeze({
|
|
230
|
+
implementationId: ref.id,
|
|
231
|
+
// Detaching cannot throw here: these refs are exactly what was attached,
|
|
232
|
+
// and detach is idempotent for an already-detached instance.
|
|
233
|
+
drained: host.detach(ref).then(() => true, () => true)
|
|
234
|
+
}));
|
|
235
|
+
const waited = await awaitDrained(Promise.all(drains.map((entry) => entry.drained)).then(() => true, () => true), timeoutMs);
|
|
236
|
+
return Object.freeze(drains.map((entry) => inspectImplementation(entry.implementationId, waited)));
|
|
237
|
+
};
|
|
238
|
+
return Object.freeze({ pin, stop, inspect, close });
|
|
239
|
+
}
|
|
@@ -69,8 +69,6 @@ export function providerDeliveryFailureFacts(failure) {
|
|
|
69
69
|
...(failure.errorName === undefined ? {} : { errorName: failure.errorName }),
|
|
70
70
|
...(failure.causeName === undefined ? {} : { causeName: failure.causeName }),
|
|
71
71
|
...(failure.hostState === undefined ? {} : { hostState: failure.hostState }),
|
|
72
|
-
...(failure.expectedRuntimeGenerationId === undefined ? {} : { expectedRuntimeGenerationId: failure.expectedRuntimeGenerationId }),
|
|
73
|
-
...(failure.observedRuntimeGenerationId === undefined ? {} : { observedRuntimeGenerationId: failure.observedRuntimeGenerationId }),
|
|
74
72
|
...(failure.attemptId === undefined ? {} : { attemptId: failure.attemptId })
|
|
75
73
|
};
|
|
76
74
|
}
|
|
@@ -111,7 +109,7 @@ function errorMessageText(error) {
|
|
|
111
109
|
return String(error ?? "unknown error");
|
|
112
110
|
}
|
|
113
111
|
/**
|
|
114
|
-
* Renders a delivery failure as one readable line for a
|
|
112
|
+
* Renders a delivery failure as one readable line for a AgentRun summary. The
|
|
115
113
|
* complete record stays available on `runtime.agent-error`; this is the
|
|
116
114
|
* bounded projection, never a replacement for the original cause.
|
|
117
115
|
*/
|
|
@@ -126,12 +124,6 @@ export function formatProviderDeliveryFailure(failure) {
|
|
|
126
124
|
fields.push(`error=${failure.errorName}`);
|
|
127
125
|
if (failure.causeName !== undefined)
|
|
128
126
|
fields.push(`cause=${failure.causeName}`);
|
|
129
|
-
if (failure.expectedRuntimeGenerationId !== undefined) {
|
|
130
|
-
fields.push(`expectedGeneration=${failure.expectedRuntimeGenerationId}`);
|
|
131
|
-
}
|
|
132
|
-
if (failure.observedRuntimeGenerationId !== undefined) {
|
|
133
|
-
fields.push(`observedGeneration=${failure.observedRuntimeGenerationId}`);
|
|
134
|
-
}
|
|
135
127
|
if (failure.attemptId !== undefined)
|
|
136
128
|
fields.push(`attemptId=${failure.attemptId}`);
|
|
137
129
|
if (failure.registrationDisposition !== undefined) {
|
|
@@ -225,7 +217,7 @@ function rawPayloadText(value) {
|
|
|
225
217
|
try {
|
|
226
218
|
const seen = new WeakSet();
|
|
227
219
|
const serialized = JSON.stringify(value, (key, member) => {
|
|
228
|
-
//
|
|
220
|
+
// AgentRun input is the Agent's prompt: Task context, quoted files, whatever
|
|
229
221
|
// the Role was told. It is not a failure fact, and it reaches the same
|
|
230
222
|
// durable, publicly-readable record as the rest of this payload. A
|
|
231
223
|
// transport client that echoes its own request into the thrown error
|