@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.
Files changed (281) hide show
  1. package/ARCHITECTURE.md +192 -399
  2. package/README.md +127 -1149
  3. package/dist/agent/adapterCatalog.js +15 -2
  4. package/dist/agent/agent.js +23 -3
  5. package/dist/agent/argumentPolicy.js +7 -1
  6. package/dist/agent/connectionPlan.js +62 -0
  7. package/dist/agent/executionComponents.js +158 -0
  8. package/dist/agent/launchEnvironment.js +31 -3
  9. package/dist/agent/managedRuntimeEnvironment.js +3 -5
  10. package/dist/{turn/turn.js → agentRun/agentRun.js} +166 -109
  11. package/dist/{turn/turnIdentity.js → agentRun/runIdentity.js} +4 -4
  12. package/dist/brief/taskBrief.js +12 -0
  13. package/dist/cli/agentConfigurationPicker.js +13 -0
  14. package/dist/cli/commandCatalog.js +157 -70
  15. package/dist/cli/interactionCandidates.js +5 -5
  16. package/dist/cli/interactionPolicy.js +38 -8
  17. package/dist/cli/invocationRouter.js +1 -1
  18. package/dist/cli/managedDiagnostics.js +28 -0
  19. package/dist/cli/operatorWizard.js +1 -7
  20. package/dist/cli/roleOptionOrder.js +27 -0
  21. package/dist/cli/roleWizard.js +50 -14
  22. package/dist/cli/updateOrchestrator.js +1 -1
  23. package/dist/cli/updatePorts.js +3 -4
  24. package/dist/cli.js +178 -95
  25. package/dist/commands/agentCommands.js +72 -14
  26. package/dist/commands/capabilityCommands.js +9 -6
  27. package/dist/commands/configCommands.js +20 -20
  28. package/dist/commands/deliveryGuardPreflight.js +2 -2
  29. package/dist/commands/executionAuditCommands.js +24 -24
  30. package/dist/commands/globalRoleCommands.js +1 -1
  31. package/dist/commands/grantCommands.js +4 -4
  32. package/dist/commands/operatorCommands.js +1 -7
  33. package/dist/commands/projectCommands.js +4 -4
  34. package/dist/commands/resourcesCommands.js +2 -2
  35. package/dist/commands/roleConfiguration.js +25 -5
  36. package/dist/commands/roleRuntimeGuard.js +4 -5
  37. package/dist/commands/sessionCommands.js +3 -7
  38. package/dist/commands/taskActivationCommands.js +259 -0
  39. package/dist/commands/taskActor.js +28 -49
  40. package/dist/commands/taskCommands.js +1188 -710
  41. package/dist/commands/taskContextCommand.js +39 -583
  42. package/dist/commands/taskExecutionCommands.js +32 -32
  43. package/dist/commands/taskInputCommands.js +40 -104
  44. package/dist/commands/taskIntegrationCommands.js +3 -2
  45. package/dist/commands/taskIntegrationQueueCommands.js +1 -1
  46. package/dist/commands/taskNextActionCommand.js +8 -8
  47. package/dist/commands/taskOverviewCommand.js +33 -45
  48. package/dist/commands/taskRemoteDeliveryCommand.js +2 -2
  49. package/dist/commands/taskRoleRuntimeStatus.js +133 -102
  50. package/dist/commands/telemetryCommands.js +36 -38
  51. package/dist/config/configCatalog.js +4 -4
  52. package/dist/config/yuiConfig.js +8 -8
  53. package/dist/context/contextSnapshot.js +10 -10
  54. package/dist/context/dispatchContext.js +11 -11
  55. package/dist/context/roleSessionContext.js +6 -3
  56. package/dist/context/{turnContextPack.js → runContextPack.js} +146 -81
  57. package/dist/context/{turnInputContract.js → runInputContract.js} +73 -60
  58. package/dist/context/sessionBootstrapManifest.js +21 -2
  59. package/dist/context/sourceRunContext.js +30 -0
  60. package/dist/context/taskContext.js +458 -0
  61. package/dist/context/wakeNotification.js +27 -27
  62. package/dist/controller/agentRuntimeObserver.js +21 -24
  63. package/dist/controller/capabilityBridge.js +17 -6
  64. package/dist/controller/clientRuntime.js +65 -92
  65. package/dist/controller/controller.js +114 -188
  66. package/dist/controller/fileSchedulerStoreAdapter.js +787 -887
  67. package/dist/controller/jobControl.js +54 -85
  68. package/dist/controller/resourceInventory.js +8 -27
  69. package/dist/controller/resourceInventoryLinux.js +12 -13
  70. package/dist/controller/runtime.js +529 -479
  71. package/dist/controller/runtimeEventInbox.js +55 -25
  72. package/dist/controller/runtimeEventProcessor.js +22 -31
  73. package/dist/controller/{runtimeHookTurnFence.js → runtimeHookRunFence.js} +91 -115
  74. package/dist/controller/runtimeLaunchCoordinator.js +80 -426
  75. package/dist/controller/runtimeObservationHook.js +14 -18
  76. package/dist/controller/sessionNotify.js +16 -24
  77. package/dist/controller/sessionOwnerReconciliation.js +168 -50
  78. package/dist/controller/structuredProviderObservation.js +138 -99
  79. package/dist/coordination/workMailbox.js +3 -3
  80. package/dist/coordination/workMailboxQueue.js +36 -33
  81. package/dist/core/boundedRpc.js +8 -1
  82. package/dist/core/controllerClient.js +20 -1
  83. package/dist/core/controllerServer.js +4 -4
  84. package/dist/doctor/doctor.js +13 -2
  85. package/dist/domain/agentResultTransport.js +9 -9
  86. package/dist/execution/codexThreadNaming.js +2 -8
  87. package/dist/execution/executionHealth.js +51 -63
  88. package/dist/execution/reviewMainRun.js +137 -0
  89. package/dist/execution/workItemExecution.js +28 -29
  90. package/dist/execution/workItemExecutionProjection.js +99 -107
  91. package/dist/execution/workItemMainRun.js +141 -0
  92. package/dist/executor/agentAdapter.js +227 -20
  93. package/dist/executor/agentConfigurationCatalog.js +126 -4
  94. package/dist/executor/agentConfigurationProbe.js +162 -4
  95. package/dist/executor/agentExecutor.js +79 -78
  96. package/dist/executor/effectiveLaunch.js +105 -18
  97. package/dist/executor/executorRegistry.js +29 -44
  98. package/dist/executor/fileRoleLaunchPlanner.js +229 -154
  99. package/dist/executor/workspacePreflightClassification.js +16 -16
  100. package/dist/grant/capabilityGrant.js +6 -3
  101. package/dist/input/inputRequest.js +12 -10
  102. package/dist/integration/gitIntegrationService.js +4 -11
  103. package/dist/integration/integrationQueueService.js +4 -4
  104. package/dist/interaction/operatorPresentation.js +1 -1
  105. package/dist/kernel/builtinCapabilities.js +247 -12
  106. package/dist/kernel/capabilityRegistry.js +64 -18
  107. package/dist/kernel/instanceHost.js +12 -1
  108. package/dist/kernel/kernelPorts.js +2 -2
  109. package/dist/lifecycle/canonicalLifecycleEvent.js +44 -49
  110. package/dist/lifecycle/exactRunTerminalization.js +449 -0
  111. package/dist/message/message.js +62 -6
  112. package/dist/message/messageContinuation.js +204 -0
  113. package/dist/observability/executionAudit.js +70 -72
  114. package/dist/observability/faultClassification.js +2 -2
  115. package/dist/observability/orchestrationMetrics.js +8 -8
  116. package/dist/operator/operatorSessionHistory.js +1 -7
  117. package/dist/output/agentConfigurationPresentation.js +8 -3
  118. package/dist/output/agentRunConfigurationPresentation.js +128 -0
  119. package/dist/output/rolePresentation.js +54 -3
  120. package/dist/plugins/pluginChild.js +104 -0
  121. package/dist/plugins/pluginIntent.js +26 -0
  122. package/dist/plugins/pluginInterpreter.js +43 -0
  123. package/dist/plugins/pluginPackage.js +101 -0
  124. package/dist/plugins/pluginProcess.js +112 -0
  125. package/dist/plugins/pluginService.js +380 -0
  126. package/dist/profile/agentProfile.js +1 -1
  127. package/dist/repository/gitWorkspace.js +26 -4
  128. package/dist/repository/project.js +19 -4
  129. package/dist/repository/taskBaseFreshness.js +13 -13
  130. package/dist/repository/taskWorkspaceCoordinator.js +20 -27
  131. package/dist/repository/taskWorkspacePreparer.js +344 -83
  132. package/dist/resources/autoResourceGc.js +3 -3
  133. package/dist/resources/liveReferences.js +3 -3
  134. package/dist/resources/projectResource.js +123 -0
  135. package/dist/resources/projectResourceService.js +421 -0
  136. package/dist/resources/resourceDiscovery.js +6 -6
  137. package/dist/resources/resourceGc.js +1 -1
  138. package/dist/resources/resourceRegistrar.js +1 -1
  139. package/dist/resources/resourceTypes.js +1 -1
  140. package/dist/review/deltaRecheck.js +3 -3
  141. package/dist/review/reviewAcceptance.js +16 -16
  142. package/dist/review/reviewDecision.js +7 -7
  143. package/dist/review/reviewRound.js +21 -20
  144. package/dist/review/reviewerAvailability.js +2 -2
  145. package/dist/role/role.js +51 -7
  146. package/dist/role/taskRoleUpdate.js +30 -0
  147. package/dist/runtime/acpProtocol.js +425 -0
  148. package/dist/runtime/acpSession.js +731 -0
  149. package/dist/runtime/acpSessionConfiguration.js +260 -0
  150. package/dist/runtime/agentDriver.js +30 -11
  151. package/dist/runtime/agentEndpoint.js +278 -0
  152. package/dist/runtime/agentEndpointIdentity.js +86 -0
  153. package/dist/runtime/agentEndpointOwnership.js +239 -0
  154. package/dist/runtime/agentError.js +2 -10
  155. package/dist/runtime/agentHost.js +565 -314
  156. package/dist/runtime/agentRunConfiguration.js +258 -0
  157. package/dist/runtime/builtinAgentDrivers.js +134 -18
  158. package/dist/runtime/builtinAgentErrorMappers.js +55 -3
  159. package/dist/runtime/builtinTranscriptUsage.js +1 -1
  160. package/dist/runtime/claude-process-owner +0 -0
  161. package/dist/runtime/codexAppServerRuntime.js +38 -30
  162. package/dist/runtime/codexInteractiveHost.js +41 -6
  163. package/dist/runtime/continuationManager.js +2 -6
  164. package/dist/runtime/executionEnvironment.js +30 -0
  165. package/dist/runtime/firstProgressAdvisory.js +11 -11
  166. package/dist/runtime/index.js +4 -3
  167. package/dist/runtime/jsonLineChannel.js +109 -0
  168. package/dist/runtime/launchBroker.js +91 -16
  169. package/dist/runtime/launchDiagnostics.js +2 -2
  170. package/dist/runtime/lifecycleReservation.js +10 -18
  171. package/dist/runtime/managedCaller.js +61 -17
  172. package/dist/runtime/nativeSessionControl.js +102 -0
  173. package/dist/runtime/ports.js +6 -21
  174. package/dist/runtime/processExitObservation.js +8 -7
  175. package/dist/runtime/promptEnvelope.js +17 -6
  176. package/dist/runtime/providerContinuation.js +3 -9
  177. package/dist/runtime/providerContinuationReconciliationService.js +4 -13
  178. package/dist/runtime/providerControl.js +2 -7
  179. package/dist/runtime/providerRuntimeIdentity.js +110 -222
  180. package/dist/runtime/providerRuntimeReconciler.js +5 -9
  181. package/dist/runtime/runtimeBinding.js +0 -1
  182. package/dist/runtime/runtimeContinuationProjection.js +4 -7
  183. package/dist/runtime/runtimeDeadlines.js +9 -0
  184. package/dist/runtime/runtimeHealthPolicy.js +1 -1
  185. package/dist/runtime/runtimeObservation.js +29 -65
  186. package/dist/runtime/runtimeProjection.js +43 -51
  187. package/dist/runtime/runtimeSessionCandidate.js +1 -3
  188. package/dist/runtime/sessionLaunchRequest.js +3 -7
  189. package/dist/runtime/sessionOwnerIdentity.js +7 -54
  190. package/dist/runtime/sessionOwnerRegistry.js +22 -17
  191. package/dist/runtime/sessionReconciliation.js +4 -8
  192. package/dist/runtime/sessionTerminationGuard.js +70 -259
  193. package/dist/runtime/sessionTokenMetrics.js +5 -16
  194. package/dist/runtime/structuredProviderHost.js +237 -117
  195. package/dist/runtime/taskRuntimeIsolation.js +39 -122
  196. package/dist/runtime/tmuxAdapters.js +39 -86
  197. package/dist/scheduler/activeRoleRunDelivery.js +354 -0
  198. package/dist/scheduler/leaderWakeupProcessor.js +75 -266
  199. package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
  200. package/dist/scheduler/ports.js +80 -9
  201. package/dist/scheduler/{roleTurnLiveness.js → roleRunLiveness.js} +26 -30
  202. package/dist/scheduler/{roleTurnStall.js → roleRunStall.js} +128 -139
  203. package/dist/scheduler/taskExecutionProjection.js +120 -124
  204. package/dist/scheduler/taskObservabilityProjection.js +29 -29
  205. package/dist/scheduler/taskWake.js +11 -4
  206. package/dist/scheduler/wakeReason.js +9 -1
  207. package/dist/setup/setupCommand.js +1 -0
  208. package/dist/storage/migrations/agentRunContract.js +159 -0
  209. package/dist/storage/migrations/removeRuntimeGeneration.js +207 -0
  210. package/dist/storage/sqliteSchema.js +431 -5
  211. package/dist/storage/sqliteStore.js +375 -220
  212. package/dist/storage/storageVersions.js +1 -1
  213. package/dist/storage/storeRpc.js +10 -5
  214. package/dist/storage/taskStore.js +13 -11
  215. package/dist/storage/upgrade/upgradeOrchestrator.js +5 -7
  216. package/dist/surface/surfaceContributions.js +102 -0
  217. package/dist/task/completionReadiness.js +32 -6
  218. package/dist/task/deliveryGuard.js +16 -16
  219. package/dist/task/draftPlan.js +72 -12
  220. package/dist/task/nextAction.js +144 -128
  221. package/dist/task/remoteDelivery.js +6 -6
  222. package/dist/task/task.js +184 -18
  223. package/dist/task/taskActivation.js +301 -0
  224. package/dist/task/taskActivationService.js +392 -0
  225. package/dist/task/taskRecordReference.js +5 -4
  226. package/dist/task/taskRecordRetirement.js +1 -1
  227. package/dist/telemetry/sqliteTelemetryStore.js +55 -68
  228. package/dist/telemetry/telemetryConfig.js +14 -14
  229. package/dist/telemetry/telemetryWiring.js +2 -2
  230. package/dist/web/assets/assetManifest.js +2 -0
  231. package/dist/web/assets/client/app.js +120 -20
  232. package/dist/web/assets/client/components.js +87 -54
  233. package/dist/web/assets/client/i18n.js +83 -41
  234. package/dist/web/assets/client/markdown.js +1 -1
  235. package/dist/web/assets/client/taskSurface.js +353 -0
  236. package/dist/web/assets/client/view.js +49 -44
  237. package/dist/web/assets/shell.js +1 -1
  238. package/dist/web/assets/styles/cards.js +22 -4
  239. package/dist/web/controllerWeb.js +60 -0
  240. package/dist/web/webMutation.js +28 -0
  241. package/dist/web/webServer.js +118 -8
  242. package/dist/web/webSnapshot.js +81 -74
  243. package/dist/web/webTaskSurface.js +64 -0
  244. package/dist/workItem/dependencyGate.js +1 -1
  245. package/dist/workItem/workItem.js +80 -48
  246. package/dist/workspace/workItemChangeSetManager.js +16 -9
  247. package/docs/agent-result-consumption.md +94 -0
  248. package/docs/agent-runtime-drivers.md +91 -0
  249. package/docs/architecture/README.md +38 -0
  250. package/docs/architecture/capabilities-and-resources.md +79 -0
  251. package/docs/managed-turn-and-session-runtime.md +222 -0
  252. package/docs/observability/README.md +81 -0
  253. package/docs/plugin-sdk.md +290 -0
  254. package/docs/provider-runtime.md +163 -0
  255. package/docs/release-workflow.md +303 -0
  256. package/docs/roles-and-configuration.md +113 -0
  257. package/docs/sqlite-control-plane-design.md +76 -0
  258. package/docs/task-dag-semantics.md +57 -0
  259. package/docs/task-delivery.md +103 -0
  260. package/docs/task-local-identity.md +6 -6
  261. package/docs/testing/verification-levels.md +86 -0
  262. package/i18n/README.zh-CN.md +98 -739
  263. package/package.json +2 -2
  264. package/skills/yui-leader/SKILL.md +130 -103
  265. package/skills/yui-leader/references/integration.md +39 -0
  266. package/skills/yui-leader/references/replicated-execution.md +42 -0
  267. package/skills/yui-leader/references/task-plugins.md +33 -0
  268. package/skills/yui-operator/SKILL.md +30 -59
  269. package/skills/yui-reviewer/SKILL.md +35 -36
  270. package/skills/yui-runtime/SKILL.md +88 -24
  271. package/skills/yui-runtime/references/publication.md +22 -0
  272. package/skills/yui-runtime/references/recovery.md +64 -0
  273. package/skills/yui-worker/SKILL.md +37 -39
  274. package/dist/cli/roleOptionCatalog.js +0 -68
  275. package/dist/context/sourceTurnContext.js +0 -30
  276. package/dist/execution/reviewMainTurn.js +0 -161
  277. package/dist/execution/workItemMainTurn.js +0 -164
  278. package/dist/lifecycle/exactTurnTerminalization.js +0 -407
  279. package/dist/runtime/preallocatedNativeSession.js +0 -13
  280. package/dist/runtime/runtimeStopReceipt.js +0 -42
  281. 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
+ }