@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,425 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Client Protocol (ACP) v1 codec.
|
|
3
|
+
*
|
|
4
|
+
* This module owns the protocol only. It knows nothing about which product is
|
|
5
|
+
* on the other end of the pipe: every product-specific fact (executable,
|
|
6
|
+
* arguments, version, authentication context) belongs to a product descriptor.
|
|
7
|
+
* A second ACP product must be reachable by adding a descriptor alone.
|
|
8
|
+
*
|
|
9
|
+
* Field names and semantics follow the published v1 specification
|
|
10
|
+
* (agentclientprotocol.com/protocol/v1). Where the specification leaves a
|
|
11
|
+
* behavior optional, this codec reports the negotiated fact rather than
|
|
12
|
+
* assuming the capability exists.
|
|
13
|
+
*/
|
|
14
|
+
export const ACP_PROTOCOL_VERSION = 1;
|
|
15
|
+
/** JSON-RPC "Request Cancelled", used by ACP for `$/cancel_request`. */
|
|
16
|
+
export const ACP_REQUEST_CANCELLED_CODE = -32800;
|
|
17
|
+
export const ACP_METHOD_NOT_FOUND_CODE = -32601;
|
|
18
|
+
/**
|
|
19
|
+
* Capabilities Yui advertises as an ACP client. The filesystem and terminal
|
|
20
|
+
* entries are deliberately false: Yui does not expose either to the Agent
|
|
21
|
+
* through this transport, so the Agent must not call `fs/*` or `terminal/*`.
|
|
22
|
+
* Declaring a capability Yui does not implement would invite calls it must then
|
|
23
|
+
* refuse.
|
|
24
|
+
*
|
|
25
|
+
* `session.configOptions.boolean` is likewise absent. ACP gates boolean config
|
|
26
|
+
* options behind that capability, and Yui's Role configuration expresses model,
|
|
27
|
+
* effort and permission mode as named values rather than flags. Advertising it
|
|
28
|
+
* would invite boolean options Yui has nothing to bind them to; staying silent
|
|
29
|
+
* means the Agent sends only the `select` options this client can actually act
|
|
30
|
+
* on, which the specification requires it to support unconditionally.
|
|
31
|
+
*/
|
|
32
|
+
export const YUI_CLIENT_CAPABILITIES = Object.freeze({
|
|
33
|
+
fs: Object.freeze({ readTextFile: false, writeTextFile: false }),
|
|
34
|
+
terminal: false
|
|
35
|
+
});
|
|
36
|
+
export function acpInitializeRequest(clientVersion) {
|
|
37
|
+
return {
|
|
38
|
+
protocolVersion: ACP_PROTOCOL_VERSION,
|
|
39
|
+
clientCapabilities: YUI_CLIENT_CAPABILITIES,
|
|
40
|
+
clientInfo: { name: "yui", title: "Yui", version: clientVersion }
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Read an `initialize` result. The specification allows the Agent to answer
|
|
45
|
+
* with the newest version it supports; a client that cannot use that version
|
|
46
|
+
* must stop rather than continue on a guessed dialect.
|
|
47
|
+
*/
|
|
48
|
+
export function readAcpInitializeResult(value) {
|
|
49
|
+
const result = asObject(value);
|
|
50
|
+
if (result === null)
|
|
51
|
+
throw new Error("ACP initialize result is not an object.");
|
|
52
|
+
const negotiated = result.protocolVersion;
|
|
53
|
+
if (typeof negotiated !== "number" || !Number.isSafeInteger(negotiated)) {
|
|
54
|
+
throw new Error("ACP initialize result is missing an integer protocolVersion.");
|
|
55
|
+
}
|
|
56
|
+
if (negotiated !== ACP_PROTOCOL_VERSION) {
|
|
57
|
+
throw new Error(`ACP Agent negotiated protocol version ${negotiated}; Yui implements version `
|
|
58
|
+
+ `${ACP_PROTOCOL_VERSION}. Select an Agent build that speaks this version.`);
|
|
59
|
+
}
|
|
60
|
+
const capabilities = asObject(result.agentCapabilities) ?? {};
|
|
61
|
+
const prompt = asObject(capabilities.promptCapabilities) ?? {};
|
|
62
|
+
const session = asObject(capabilities.sessionCapabilities) ?? {};
|
|
63
|
+
const info = asObject(result.agentInfo) ?? {};
|
|
64
|
+
return Object.freeze({
|
|
65
|
+
protocolVersion: negotiated,
|
|
66
|
+
capabilities: Object.freeze({
|
|
67
|
+
loadSession: capabilities.loadSession === true,
|
|
68
|
+
// A capability object being present (even empty) is how ACP signals
|
|
69
|
+
// support for these; absence or false means unsupported.
|
|
70
|
+
resumeSession: isCapabilityPresent(session.resume),
|
|
71
|
+
closeSession: isCapabilityPresent(session.close),
|
|
72
|
+
additionalDirectories: isCapabilityPresent(session.additionalDirectories),
|
|
73
|
+
promptImage: prompt.image === true,
|
|
74
|
+
promptAudio: prompt.audio === true,
|
|
75
|
+
promptEmbeddedContext: prompt.embeddedContext === true
|
|
76
|
+
}),
|
|
77
|
+
authMethods: Object.freeze(readAuthMethods(result.authMethods)),
|
|
78
|
+
...(optionalText(info.name) === undefined ? {} : { agentName: optionalText(info.name) }),
|
|
79
|
+
...(optionalText(info.version) === undefined
|
|
80
|
+
? {}
|
|
81
|
+
: { agentVersion: optionalText(info.version) })
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
function isCapabilityPresent(value) {
|
|
85
|
+
return value === true || (value !== null && typeof value === "object" && !Array.isArray(value));
|
|
86
|
+
}
|
|
87
|
+
function readAuthMethods(value) {
|
|
88
|
+
if (!Array.isArray(value))
|
|
89
|
+
return [];
|
|
90
|
+
const methods = [];
|
|
91
|
+
for (const entry of value) {
|
|
92
|
+
const method = asObject(entry);
|
|
93
|
+
const id = method === null ? undefined : optionalText(method.id);
|
|
94
|
+
if (method === null || id === undefined)
|
|
95
|
+
continue;
|
|
96
|
+
methods.push(Object.freeze({
|
|
97
|
+
id,
|
|
98
|
+
...(optionalText(method.name) === undefined ? {} : { name: optionalText(method.name) }),
|
|
99
|
+
...(optionalText(method.description) === undefined
|
|
100
|
+
? {}
|
|
101
|
+
: { description: optionalText(method.description) })
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
return methods;
|
|
105
|
+
}
|
|
106
|
+
export function acpNewSessionRequest(cwd, additionalDirectories = []) {
|
|
107
|
+
// `mcpServers` is required by the specification; Yui exposes none over ACP.
|
|
108
|
+
return {
|
|
109
|
+
cwd,
|
|
110
|
+
mcpServers: [],
|
|
111
|
+
// Clients MUST only send `additionalDirectories` when the Agent advertised
|
|
112
|
+
// `sessionCapabilities.additionalDirectories`, so the caller gates this and
|
|
113
|
+
// passes an empty list for an Agent that never offered the field. Omitting
|
|
114
|
+
// the key entirely — rather than sending `[]` — keeps the request byte-wise
|
|
115
|
+
// identical to what an Agent without the capability expects.
|
|
116
|
+
...(additionalDirectories.length === 0
|
|
117
|
+
? {}
|
|
118
|
+
: { additionalDirectories: [...additionalDirectories] })
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
export function acpPromptRequest(sessionId, text) {
|
|
122
|
+
// Text is the baseline ContentBlock every ACP Agent must accept, so a prompt
|
|
123
|
+
// never depends on a negotiated prompt capability.
|
|
124
|
+
return { sessionId, prompt: [{ type: "text", text }] };
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Read a `configOptions` array from a session setup or set-option result.
|
|
128
|
+
*
|
|
129
|
+
* Returns undefined when the field is absent, which is how an Agent that
|
|
130
|
+
* predates config options answers; that is a different fact from an Agent
|
|
131
|
+
* answering with an empty list, which offers nothing configurable. Callers must
|
|
132
|
+
* be able to tell those apart, so the absence is not flattened to `[]`.
|
|
133
|
+
*/
|
|
134
|
+
export function readAcpConfigOptions(value) {
|
|
135
|
+
if (!Array.isArray(value))
|
|
136
|
+
return undefined;
|
|
137
|
+
const options = [];
|
|
138
|
+
for (const entry of value) {
|
|
139
|
+
const option = asObject(entry);
|
|
140
|
+
if (option === null)
|
|
141
|
+
continue;
|
|
142
|
+
const id = optionalText(option.id);
|
|
143
|
+
// A select option's current value and choices are what make it settable.
|
|
144
|
+
// An entry missing either cannot be acted on, and inventing a default here
|
|
145
|
+
// would let Yui report a selection the Agent never offered.
|
|
146
|
+
const currentValue = optionalText(option.currentValue);
|
|
147
|
+
if (id === undefined || currentValue === undefined)
|
|
148
|
+
continue;
|
|
149
|
+
if (option.type !== undefined && option.type !== "select")
|
|
150
|
+
continue;
|
|
151
|
+
if (!Array.isArray(option.options))
|
|
152
|
+
continue;
|
|
153
|
+
const values = [];
|
|
154
|
+
for (const candidate of option.options) {
|
|
155
|
+
const choice = asObject(candidate);
|
|
156
|
+
const choiceValue = choice === null ? undefined : optionalText(choice.value);
|
|
157
|
+
if (choice === null || choiceValue === undefined)
|
|
158
|
+
continue;
|
|
159
|
+
values.push(Object.freeze({
|
|
160
|
+
value: choiceValue,
|
|
161
|
+
name: optionalText(choice.name) ?? choiceValue,
|
|
162
|
+
...(optionalText(choice.description) === undefined
|
|
163
|
+
? {}
|
|
164
|
+
: { description: optionalText(choice.description) })
|
|
165
|
+
}));
|
|
166
|
+
}
|
|
167
|
+
options.push(Object.freeze({
|
|
168
|
+
id,
|
|
169
|
+
name: optionalText(option.name) ?? id,
|
|
170
|
+
...(optionalText(option.description) === undefined
|
|
171
|
+
? {}
|
|
172
|
+
: { description: optionalText(option.description) }),
|
|
173
|
+
...(optionalText(option.category) === undefined
|
|
174
|
+
? {}
|
|
175
|
+
: { category: optionalText(option.category) }),
|
|
176
|
+
currentValue,
|
|
177
|
+
options: Object.freeze(values)
|
|
178
|
+
}));
|
|
179
|
+
}
|
|
180
|
+
return Object.freeze(options);
|
|
181
|
+
}
|
|
182
|
+
/** The config option id ACP uses for a session's permission mode. */
|
|
183
|
+
export const ACP_MODE_CONFIG_ID = "mode";
|
|
184
|
+
export function readAcpSessionConfiguration(value) {
|
|
185
|
+
const result = asObject(value);
|
|
186
|
+
const modern = readAcpConfigOptions(result?.configOptions);
|
|
187
|
+
if (modern !== undefined) {
|
|
188
|
+
return Object.freeze({ options: modern, legacyModes: false });
|
|
189
|
+
}
|
|
190
|
+
const legacy = readAcpSessionModes(result?.modes);
|
|
191
|
+
return Object.freeze({
|
|
192
|
+
options: legacy === undefined ? Object.freeze([]) : Object.freeze([legacy]),
|
|
193
|
+
legacyModes: legacy !== undefined
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Convert the legacy `modes` field into the one config option it describes.
|
|
198
|
+
*
|
|
199
|
+
* The shape is fixed by ACP: `currentModeId` plus `availableModes`. Presenting
|
|
200
|
+
* it as a config option keeps mode selection in one vocabulary without
|
|
201
|
+
* pretending the Agent supports the newer method.
|
|
202
|
+
*/
|
|
203
|
+
function readAcpSessionModes(value) {
|
|
204
|
+
const modes = asObject(value);
|
|
205
|
+
const currentValue = modes === null ? undefined : optionalText(modes.currentModeId);
|
|
206
|
+
if (modes === null || currentValue === undefined)
|
|
207
|
+
return undefined;
|
|
208
|
+
const values = [];
|
|
209
|
+
if (Array.isArray(modes.availableModes)) {
|
|
210
|
+
for (const entry of modes.availableModes) {
|
|
211
|
+
const mode = asObject(entry);
|
|
212
|
+
const id = mode === null ? undefined : optionalText(mode.id);
|
|
213
|
+
if (mode === null || id === undefined)
|
|
214
|
+
continue;
|
|
215
|
+
values.push(Object.freeze({
|
|
216
|
+
value: id,
|
|
217
|
+
name: optionalText(mode.name) ?? id,
|
|
218
|
+
...(optionalText(mode.description) === undefined
|
|
219
|
+
? {}
|
|
220
|
+
: { description: optionalText(mode.description) })
|
|
221
|
+
}));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return Object.freeze({
|
|
225
|
+
id: ACP_MODE_CONFIG_ID,
|
|
226
|
+
name: "Mode",
|
|
227
|
+
category: "mode",
|
|
228
|
+
currentValue,
|
|
229
|
+
options: Object.freeze(values)
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
export function acpSetConfigOptionRequest(sessionId, configId, value) {
|
|
233
|
+
return { sessionId, configId, value };
|
|
234
|
+
}
|
|
235
|
+
export function acpSetModeRequest(sessionId, modeId) {
|
|
236
|
+
return { sessionId, modeId };
|
|
237
|
+
}
|
|
238
|
+
export function acpCancelNotification(sessionId) {
|
|
239
|
+
return { sessionId };
|
|
240
|
+
}
|
|
241
|
+
const STOP_REASONS = [
|
|
242
|
+
"end_turn",
|
|
243
|
+
"max_tokens",
|
|
244
|
+
"max_turn_requests",
|
|
245
|
+
"refusal",
|
|
246
|
+
"cancelled"
|
|
247
|
+
];
|
|
248
|
+
export function readAcpStopReason(value) {
|
|
249
|
+
const result = asObject(value);
|
|
250
|
+
const reason = result?.stopReason;
|
|
251
|
+
return STOP_REASONS.find((candidate) => candidate === reason);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Map a stop reason onto Yui's terminal vocabulary. Only `end_turn` is a
|
|
255
|
+
* completed Turn: a token or request ceiling and a refusal each ended the Turn
|
|
256
|
+
* without delivering the requested work, and reporting them as success would
|
|
257
|
+
* hide a real outcome from the Task record.
|
|
258
|
+
*/
|
|
259
|
+
export function acpTerminalStatus(reason) {
|
|
260
|
+
switch (reason) {
|
|
261
|
+
case "end_turn":
|
|
262
|
+
return "completed";
|
|
263
|
+
case "cancelled":
|
|
264
|
+
return "cancelled";
|
|
265
|
+
default:
|
|
266
|
+
return "failed";
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
export function acpStopReasonDetail(reason) {
|
|
270
|
+
switch (reason) {
|
|
271
|
+
case "max_tokens":
|
|
272
|
+
return "ACP Agent stopped at its token limit (stopReason=max_tokens).";
|
|
273
|
+
case "max_turn_requests":
|
|
274
|
+
return "ACP Agent stopped at its per-Turn model request limit "
|
|
275
|
+
+ "(stopReason=max_turn_requests).";
|
|
276
|
+
case "refusal":
|
|
277
|
+
return "ACP Agent refused to continue the Turn (stopReason=refusal).";
|
|
278
|
+
case "cancelled":
|
|
279
|
+
return "ACP Agent reported the Turn as cancelled (stopReason=cancelled).";
|
|
280
|
+
case "end_turn":
|
|
281
|
+
return "ACP Agent completed the Turn (stopReason=end_turn).";
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/** Decode a `session/update` notification payload for the given session. */
|
|
285
|
+
export function readAcpSessionUpdate(params, sessionId) {
|
|
286
|
+
const value = asObject(params);
|
|
287
|
+
if (value === null || optionalText(value.sessionId) !== sessionId)
|
|
288
|
+
return undefined;
|
|
289
|
+
const update = asObject(value.update);
|
|
290
|
+
const kind = update === null ? undefined : optionalText(update.sessionUpdate);
|
|
291
|
+
if (update === null || kind === undefined)
|
|
292
|
+
return undefined;
|
|
293
|
+
switch (kind) {
|
|
294
|
+
case "agent_message_chunk": {
|
|
295
|
+
const text = readContentText(update.content);
|
|
296
|
+
return text === undefined ? undefined : Object.freeze({ kind: "agent-message", text });
|
|
297
|
+
}
|
|
298
|
+
case "tool_call": {
|
|
299
|
+
const toolCallId = optionalText(update.toolCallId);
|
|
300
|
+
if (toolCallId === undefined)
|
|
301
|
+
return undefined;
|
|
302
|
+
return Object.freeze({
|
|
303
|
+
kind: "tool-call",
|
|
304
|
+
toolCallId,
|
|
305
|
+
...(optionalText(update.title) === undefined
|
|
306
|
+
? {}
|
|
307
|
+
: { title: optionalText(update.title) }),
|
|
308
|
+
...(optionalText(update.status) === undefined
|
|
309
|
+
? {}
|
|
310
|
+
: { status: optionalText(update.status) })
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
case "tool_call_update": {
|
|
314
|
+
const toolCallId = optionalText(update.toolCallId);
|
|
315
|
+
if (toolCallId === undefined)
|
|
316
|
+
return undefined;
|
|
317
|
+
return Object.freeze({
|
|
318
|
+
kind: "tool-call-update",
|
|
319
|
+
toolCallId,
|
|
320
|
+
...(optionalText(update.status) === undefined
|
|
321
|
+
? {}
|
|
322
|
+
: { status: optionalText(update.status) })
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
case "plan":
|
|
326
|
+
return Object.freeze({ kind: "plan" });
|
|
327
|
+
case "config_option_update": {
|
|
328
|
+
const options = readAcpConfigOptions(update.configOptions);
|
|
329
|
+
return options === undefined
|
|
330
|
+
? undefined
|
|
331
|
+
: Object.freeze({ kind: "config-options", options });
|
|
332
|
+
}
|
|
333
|
+
case "current_mode_update": {
|
|
334
|
+
const modeId = optionalText(update.currentModeId);
|
|
335
|
+
return modeId === undefined ? undefined : Object.freeze({ kind: "mode", modeId });
|
|
336
|
+
}
|
|
337
|
+
case "usage_update":
|
|
338
|
+
return Object.freeze({
|
|
339
|
+
kind: "usage",
|
|
340
|
+
...(safeCount(update.used) === undefined ? {} : { used: safeCount(update.used) }),
|
|
341
|
+
...(safeCount(update.size) === undefined ? {} : { size: safeCount(update.size) })
|
|
342
|
+
});
|
|
343
|
+
default:
|
|
344
|
+
return Object.freeze({ kind: "other", sessionUpdate: kind });
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
function readContentText(value) {
|
|
348
|
+
const content = asObject(value);
|
|
349
|
+
if (content === null || content.type !== "text")
|
|
350
|
+
return undefined;
|
|
351
|
+
return typeof content.text === "string" ? content.text : undefined;
|
|
352
|
+
}
|
|
353
|
+
export function readAcpPermissionRequest(params) {
|
|
354
|
+
const value = asObject(params);
|
|
355
|
+
const sessionId = value === null ? undefined : optionalText(value.sessionId);
|
|
356
|
+
if (value === null || sessionId === undefined)
|
|
357
|
+
return undefined;
|
|
358
|
+
const toolCall = asObject(value.toolCall);
|
|
359
|
+
const options = [];
|
|
360
|
+
if (Array.isArray(value.options)) {
|
|
361
|
+
for (const entry of value.options) {
|
|
362
|
+
const option = asObject(entry);
|
|
363
|
+
const optionId = option === null ? undefined : optionalText(option.optionId);
|
|
364
|
+
const kind = option === null ? undefined : optionalText(option.kind);
|
|
365
|
+
if (option === null || optionId === undefined || !isPermissionOptionKind(kind))
|
|
366
|
+
continue;
|
|
367
|
+
options.push(Object.freeze({
|
|
368
|
+
optionId,
|
|
369
|
+
name: optionalText(option.name) ?? optionId,
|
|
370
|
+
kind
|
|
371
|
+
}));
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
const toolCallId = toolCall === null ? undefined : optionalText(toolCall.toolCallId);
|
|
375
|
+
return Object.freeze({
|
|
376
|
+
sessionId,
|
|
377
|
+
...(toolCallId === undefined ? {} : { toolCallId }),
|
|
378
|
+
options: Object.freeze(options)
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
function isPermissionOptionKind(value) {
|
|
382
|
+
return value === "allow_once" || value === "allow_always"
|
|
383
|
+
|| value === "reject_once" || value === "reject_always";
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Choose a permission response without ever granting authority on the user's
|
|
387
|
+
* behalf. Yui holds no interactive consent on this transport, so an Agent's
|
|
388
|
+
* request to act is declined: an explicit reject option when the Agent offered
|
|
389
|
+
* one, otherwise the `cancelled` outcome the specification always permits.
|
|
390
|
+
* Escalating to an allow option here would silently convert an absent user
|
|
391
|
+
* decision into a granted one.
|
|
392
|
+
*/
|
|
393
|
+
export function acpDeclinePermission(request) {
|
|
394
|
+
const reject = request.options.find((option) => option.kind === "reject_once")
|
|
395
|
+
?? request.options.find((option) => option.kind === "reject_always");
|
|
396
|
+
return reject === undefined
|
|
397
|
+
? Object.freeze({ outcome: "cancelled" })
|
|
398
|
+
: Object.freeze({ outcome: "selected", optionId: reject.optionId, kind: reject.kind });
|
|
399
|
+
}
|
|
400
|
+
export function acpPermissionResult(decision) {
|
|
401
|
+
return decision.outcome === "selected"
|
|
402
|
+
? { outcome: { outcome: "selected", optionId: decision.optionId } }
|
|
403
|
+
: { outcome: { outcome: "cancelled" } };
|
|
404
|
+
}
|
|
405
|
+
export function acpPermissionSummary(request, decision) {
|
|
406
|
+
const target = request.toolCallId === undefined
|
|
407
|
+
? "an operation"
|
|
408
|
+
: `tool call ${request.toolCallId}`;
|
|
409
|
+
return decision.outcome === "selected"
|
|
410
|
+
? `Yui declined ACP permission for ${target} using option ${decision.optionId}.`
|
|
411
|
+
: `Yui returned the cancelled outcome for an ACP permission request covering ${target}.`;
|
|
412
|
+
}
|
|
413
|
+
export function asObject(value) {
|
|
414
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
415
|
+
? value
|
|
416
|
+
: null;
|
|
417
|
+
}
|
|
418
|
+
export function optionalText(value) {
|
|
419
|
+
return typeof value === "string" && value.trim().length > 0 && !value.includes("\0")
|
|
420
|
+
? value.trim()
|
|
421
|
+
: undefined;
|
|
422
|
+
}
|
|
423
|
+
function safeCount(value) {
|
|
424
|
+
return Number.isSafeInteger(value) && value >= 0 ? value : undefined;
|
|
425
|
+
}
|