@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
@@ -1,13 +1,23 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { Duplex } from "node:stream";
4
+ import { fileURLToPath } from "node:url";
4
5
  import WebSocket from "ws";
5
- import { CodexAppServerRequestError, CodexAppServerRuntime, codexGoalNotification, codexAppServerErrorIsMissing, codexTurnInput, codexTurnOutput } from "./codexAppServerRuntime.js";
6
+ import { CodexAppServerRequestError, CodexAppServerRuntime, codexClientInitialization, codexGoalNotification, codexAppServerErrorIsMissing, codexTurnInput, codexTurnOutput } from "./codexAppServerRuntime.js";
6
7
  import { serializeAgentErrorRaw } from "./agentError.js";
7
- import { PROVIDER_ACCEPT_TIMEOUT_MS } from "./runtimeDeadlines.js";
8
+ import { AcpStructuredProviderSession } from "./acpSession.js";
9
+ import { acpDesiredSessionConfiguration } from "./acpSessionConfiguration.js";
10
+ import { unsupportedAgentRunConfiguration } from "./agentRunConfiguration.js";
8
11
  import { YUI_VERSION } from "../version.js";
9
- const PROVIDER_MESSAGE_MAX_BYTES = 16 * 1024 * 1024;
12
+ import { JsonLineChannel, PROVIDER_MESSAGE_MAX_BYTES, terminateProcessGroup } from "./jsonLineChannel.js";
13
+ import { PROVIDER_ACCEPT_TIMEOUT_MS } from "./runtimeDeadlines.js";
10
14
  const CODEX_PROXY_HANDSHAKE_TIMEOUT_MS = 10_000;
15
+ /**
16
+ * These Session implementations do not expose run-configuration observations.
17
+ * Do not echo launch flags as Provider confirmation.
18
+ */
19
+ const CODEX_RUN_CONFIGURATION = unsupportedAgentRunConfiguration("The Codex app-server protocol");
20
+ const CLAUDE_RUN_CONFIGURATION = unsupportedAgentRunConfiguration("Claude Code's stream-json interface");
11
21
  export class ProviderDeliveryUnknownError extends Error {
12
22
  attemptId;
13
23
  name = "ProviderDeliveryUnknownError";
@@ -51,7 +61,8 @@ export async function startStructuredProviderSession(payload, input = {}) {
51
61
  if (control === undefined) {
52
62
  throw new Error("Managed Agent Host launch requires Provider control metadata.");
53
63
  }
54
- const child = spawn(payload.command, [...payload.args], {
64
+ const ownedClaude = control.adapterId === "claude" && control.transport !== "acp-stdio";
65
+ const child = spawn(ownedClaude ? fileURLToPath(new URL("./claude-process-owner", import.meta.url)) : payload.command, ownedClaude ? [payload.command, ...payload.args] : [...payload.args], {
55
66
  cwd: payload.cwd,
56
67
  env: { ...payload.environment },
57
68
  stdio: ["pipe", "pipe", "pipe"],
@@ -63,8 +74,40 @@ export async function startStructuredProviderSession(payload, input = {}) {
63
74
  child.stderr.on("data", (chunk) => mirror("stderr", chunk));
64
75
  const exit = childExit(child, processInstanceId);
65
76
  try {
77
+ // Dispatch is on the negotiated transport, not on a product name: every
78
+ // Agent that speaks a given transport is opened by the same code.
79
+ if (control.transport === "acp-stdio") {
80
+ const session = await AcpStructuredProviderSession.open({
81
+ child,
82
+ exit,
83
+ processInstanceId,
84
+ clientVersion: YUI_VERSION,
85
+ cwd: payload.cwd,
86
+ ...(control.acpSession?.additionalDirectories === undefined
87
+ ? {}
88
+ : { additionalDirectories: control.acpSession.additionalDirectories }),
89
+ ...(control.acpSession?.sessionBootstrap === undefined
90
+ ? {}
91
+ : { sessionBootstrap: control.acpSession.sessionBootstrap }),
92
+ ...(control.acpSession?.desiredConfiguration === undefined
93
+ ? {}
94
+ : {
95
+ desiredConfiguration: acpDesiredSessionConfiguration(control.acpSession.desiredConfiguration)
96
+ }),
97
+ // The product identity comes from the Agent binding the launch was
98
+ // compiled from. It is needed for one decision only — which mode value
99
+ // grants bypass — and is never inferred from the command that was run.
100
+ ...(control.component === undefined ? {} : { component: control.component }),
101
+ ...(control.nativeSessionId === undefined
102
+ ? {}
103
+ : { nativeSessionId: control.nativeSessionId }),
104
+ ...(input.onTerminal === undefined ? {} : { onTerminal: input.onTerminal }),
105
+ mirror
106
+ });
107
+ return Object.freeze({ session });
108
+ }
66
109
  if (control.adapterId === "codex") {
67
- const opened = await CodexStructuredProviderSession.open(child, exit, processInstanceId, payload, control, input.onStarted, input.onTerminal, input.onGoal, mirror);
110
+ const opened = await CodexStructuredProviderSession.open(child, exit, processInstanceId, payload, control, input.onStarted, input.onTerminal, input.onGoal, input.onInput, input.onActivity, mirror);
68
111
  return Object.freeze({
69
112
  session: opened.session,
70
113
  ...(opened.recoveredTerminal === undefined
@@ -73,7 +116,7 @@ export async function startStructuredProviderSession(payload, input = {}) {
73
116
  goal: opened.goal
74
117
  });
75
118
  }
76
- const session = await ClaudeStructuredProviderSession.open(child, exit, processInstanceId, control, input.onTerminal, input.onGoal, mirror);
119
+ const session = await ClaudeStructuredProviderSession.open(child, exit, processInstanceId, control, input.onAccepted, input.onTerminal, input.onGoal, input.onActivity, mirror);
77
120
  return Object.freeze({ session });
78
121
  }
79
122
  catch (error) {
@@ -168,7 +211,10 @@ class CodexProxyWebSocketChannel {
168
211
  throw this.#closedError;
169
212
  const id = String(this.#nextId++);
170
213
  const response = new Promise((resolvePromise, reject) => {
171
- const timer = setTimeout(() => {
214
+ // A live turn submission may legitimately outlast the observation
215
+ // deadline. Retain its exact callback until acknowledgement or actual
216
+ // disconnect; a clock alone is not evidence of unknown delivery.
217
+ const timer = method === "turn/start" || method === "turn/steer" ? undefined : setTimeout(() => {
172
218
  this.#pending.delete(id);
173
219
  reject(new Error(`Provider request timed out: ${method}.`));
174
220
  }, PROVIDER_ACCEPT_TIMEOUT_MS);
@@ -305,73 +351,6 @@ class ChildProcessDuplex extends Duplex {
305
351
  return this;
306
352
  }
307
353
  }
308
- class JsonLineChannel {
309
- child;
310
- mirror;
311
- #listeners = new Set();
312
- #buffer = "";
313
- #closedError;
314
- constructor(child, mirror) {
315
- this.child = child;
316
- this.mirror = mirror;
317
- child.stdout.setEncoding("utf8");
318
- child.stdout.on("data", (chunk) => this.#receive(chunk));
319
- child.once("error", (error) => this.#close(error));
320
- child.once("close", (code, signal) => this.#close(new Error(`Provider process exited (code=${code ?? "none"}, signal=${signal ?? "none"}).`)));
321
- }
322
- onMessage(listener) {
323
- this.#listeners.add(listener);
324
- return () => this.#listeners.delete(listener);
325
- }
326
- async send(message) {
327
- if (this.#closedError !== undefined)
328
- throw this.#closedError;
329
- const line = `${JSON.stringify(message)}\n`;
330
- if (Buffer.byteLength(line, "utf8") > PROVIDER_MESSAGE_MAX_BYTES) {
331
- throw new Error("Provider request exceeds its message bound.");
332
- }
333
- await new Promise((resolvePromise, reject) => {
334
- this.child.stdin.write(line, "utf8", (error) => {
335
- if (error === null || error === undefined)
336
- resolvePromise();
337
- else
338
- reject(error);
339
- });
340
- });
341
- }
342
- #receive(chunk) {
343
- this.mirror("stdout", chunk);
344
- this.#buffer += chunk;
345
- if (Buffer.byteLength(this.#buffer, "utf8") > PROVIDER_MESSAGE_MAX_BYTES) {
346
- this.#close(new Error("Provider response line exceeds its message bound."));
347
- terminateProcessGroup(this.child, "SIGTERM");
348
- return;
349
- }
350
- for (;;) {
351
- const newline = this.#buffer.indexOf("\n");
352
- if (newline < 0)
353
- return;
354
- const line = this.#buffer.slice(0, newline).trim();
355
- this.#buffer = this.#buffer.slice(newline + 1);
356
- if (line.length === 0)
357
- continue;
358
- try {
359
- const parsed = JSON.parse(line);
360
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
361
- continue;
362
- for (const listener of this.#listeners)
363
- listener(parsed);
364
- }
365
- catch {
366
- continue;
367
- }
368
- }
369
- }
370
- #close(error) {
371
- if (this.#closedError === undefined)
372
- this.#closedError = error;
373
- }
374
- }
375
354
  class CodexStructuredProviderSession {
376
355
  child;
377
356
  exit;
@@ -381,6 +360,10 @@ class CodexStructuredProviderSession {
381
360
  onStarted;
382
361
  onTerminal;
383
362
  onGoal;
363
+ onInput;
364
+ onActivity;
365
+ nativeAccountHome;
366
+ #turnAttempts = new Map();
384
367
  adapterId = "codex";
385
368
  #activeTurnId;
386
369
  #clientOwnedTurnId;
@@ -388,7 +371,8 @@ class CodexStructuredProviderSession {
388
371
  #submissionPending = false;
389
372
  #bufferedStarts = [];
390
373
  #bufferedTerminals = [];
391
- constructor(child, exit, processInstanceId, conversationId, runtime, onStarted, onTerminal, onGoal) {
374
+ #bufferedActivities = [];
375
+ constructor(child, exit, processInstanceId, conversationId, runtime, onStarted, onTerminal, onGoal, onInput, onActivity, nativeAccountHome) {
392
376
  this.child = child;
393
377
  this.exit = exit;
394
378
  this.processInstanceId = processInstanceId;
@@ -397,18 +381,15 @@ class CodexStructuredProviderSession {
397
381
  this.onStarted = onStarted;
398
382
  this.onTerminal = onTerminal;
399
383
  this.onGoal = onGoal;
384
+ this.onInput = onInput;
385
+ this.onActivity = onActivity;
386
+ this.nativeAccountHome = nativeAccountHome;
400
387
  }
401
- static async open(child, exit, processInstanceId, payload, control, onStarted, onTerminal, onGoal, mirror) {
388
+ static async open(child, exit, processInstanceId, payload, control, onStarted, onTerminal, onGoal, onInput, onActivity, mirror) {
402
389
  const channel = await CodexProxyWebSocketChannel.connect(child, mirror);
403
390
  const openingMessages = [];
404
391
  const stopOpeningBuffer = channel.onMessage((message) => openingMessages.push(message));
405
- await channel.request("initialize", {
406
- clientInfo: { name: "yui", title: "Yui", version: YUI_VERSION },
407
- capabilities: {
408
- experimentalApi: true,
409
- requestAttestation: false
410
- }
411
- });
392
+ const initialized = await channel.request("initialize", codexClientInitialization());
412
393
  await channel.notify("initialized");
413
394
  const runtime = new CodexAppServerRuntime(channel);
414
395
  let conversationId;
@@ -443,11 +424,13 @@ class CodexStructuredProviderSession {
443
424
  name: control.sessionTitle
444
425
  });
445
426
  }
446
- const session = new CodexStructuredProviderSession(child, exit, processInstanceId, conversationId, runtime, onStarted, onTerminal, onGoal);
427
+ const session = new CodexStructuredProviderSession(child, exit, processInstanceId, conversationId, runtime, onStarted, onTerminal, onGoal, onInput, onActivity, optionalId(initialized.codexHome));
447
428
  session.#activeTurnId = resumedActiveTurnId;
448
429
  const ownedTurn = control.kind === "restore" ? control.ownedTurn : undefined;
449
430
  session.#clientOwnedTurnId = ownedTurn?.turnId;
450
431
  session.#clientOwnedAttemptId = ownedTurn?.attemptId;
432
+ if (ownedTurn !== undefined)
433
+ session.#turnAttempts.set(ownedTurn.turnId, ownedTurn.attemptId);
451
434
  stopOpeningBuffer();
452
435
  let recoveredTerminal;
453
436
  for (const message of openingMessages) {
@@ -507,6 +490,38 @@ class CodexStructuredProviderSession {
507
490
  this.onGoal?.(goal);
508
491
  return undefined;
509
492
  }
493
+ if (emit && (method === "item/started" || method === "item/completed"
494
+ || method === "item/agentMessage/delta" || method === "item/reasoning/summaryTextDelta")) {
495
+ const item = object(params.item);
496
+ const id = optionalId(item?.id) ?? optionalId(params.itemId);
497
+ const nativeTurnId = optionalId(params.turnId);
498
+ const tool = ["commandExecution", "mcpToolCall", "dynamicToolCall", "fileChange"].includes(String(item?.type));
499
+ const model = ["agentMessage", "reasoning"].includes(String(item?.type)) || method.endsWith("Delta") || method.endsWith("/delta");
500
+ if (id !== undefined && nativeTurnId !== undefined && (tool || model)) {
501
+ const activity = {
502
+ conversationId: this.conversationId, nativeSessionId: this.conversationId,
503
+ nativeTurnId, id, observedAt: new Date().toISOString(),
504
+ phase: model ? "model" : method === "item/started" ? "started"
505
+ : item?.status === "failed" || (typeof item?.exitCode === "number" && item.exitCode !== 0)
506
+ ? "failed" : "completed"
507
+ };
508
+ if (this.#submissionPending)
509
+ this.#bufferedActivities.push(activity);
510
+ else
511
+ this.#emitActivity(activity);
512
+ }
513
+ }
514
+ if (method === "item/completed") {
515
+ const item = object(params.item);
516
+ const nativeTurnId = optionalId(params.turnId);
517
+ const inputId = optionalId(item?.id);
518
+ const input = item === null ? undefined : codexTurnInput({ items: [item] });
519
+ if (emit && nativeTurnId !== undefined && inputId !== undefined && input !== undefined) {
520
+ this.onInput?.({ conversationId: this.conversationId, nativeSessionId: this.conversationId,
521
+ nativeTurnId, inputId, input, observedAt: new Date().toISOString() });
522
+ }
523
+ return undefined;
524
+ }
510
525
  if (method === "turn/started") {
511
526
  const turnId = nestedId(params, "turn") ?? optionalId(params.turnId);
512
527
  if (turnId !== undefined) {
@@ -563,6 +578,12 @@ class CodexStructuredProviderSession {
563
578
  get activeTurnId() {
564
579
  return this.#activeTurnId;
565
580
  }
581
+ /**
582
+ * This implementation has no run-configuration observation reader.
583
+ */
584
+ get runConfiguration() {
585
+ return CODEX_RUN_CONFIGURATION;
586
+ }
566
587
  async submitTurn(turn) {
567
588
  if (this.#activeTurnId !== undefined) {
568
589
  throw new ProviderTurnBusyError(`Provider Conversation already has active Turn ${this.#activeTurnId}.`, turn.attemptId, this.#activeTurnId);
@@ -587,6 +608,7 @@ class CodexStructuredProviderSession {
587
608
  this.#activeTurnId = acceptance.turnId;
588
609
  this.#clientOwnedTurnId = acceptance.turnId;
589
610
  this.#clientOwnedAttemptId = turn.attemptId;
611
+ this.#turnAttempts.set(acceptance.turnId, turn.attemptId);
590
612
  return Object.freeze({
591
613
  attemptId: turn.attemptId,
592
614
  conversationId: this.conversationId,
@@ -602,6 +624,8 @@ class CodexStructuredProviderSession {
602
624
  for (const started of starts)
603
625
  this.#emitStarted(started);
604
626
  const buffered = this.#bufferedTerminals.splice(0);
627
+ for (const activity of this.#bufferedActivities.splice(0))
628
+ this.#emitActivity(activity);
605
629
  for (const terminal of buffered)
606
630
  this.#emitTerminal(terminal);
607
631
  }
@@ -635,6 +659,11 @@ class CodexStructuredProviderSession {
635
659
  #emitTerminal(terminal) {
636
660
  this.#completeTerminal(terminal, true);
637
661
  }
662
+ #emitActivity(activity) {
663
+ const attemptId = this.#turnAttempts.get(activity.nativeTurnId);
664
+ if (attemptId !== undefined)
665
+ this.onActivity?.({ ...activity, attemptId });
666
+ }
638
667
  #emitStarted(started) {
639
668
  this.onStarted?.({
640
669
  ...started,
@@ -642,12 +671,12 @@ class CodexStructuredProviderSession {
642
671
  });
643
672
  }
644
673
  #completeTerminal(terminal, emit) {
645
- const clientOwned = terminal.nativeTurnId !== undefined
646
- && terminal.nativeTurnId === this.#clientOwnedTurnId;
647
- const attemptId = clientOwned ? this.#clientOwnedAttemptId : undefined;
674
+ const attemptId = terminal.nativeTurnId === undefined ? undefined
675
+ : this.#turnAttempts.get(terminal.nativeTurnId);
676
+ const clientOwned = attemptId !== undefined;
648
677
  if (terminal.nativeTurnId === this.#activeTurnId)
649
678
  this.#activeTurnId = undefined;
650
- if (clientOwned) {
679
+ if (terminal.nativeTurnId === this.#clientOwnedTurnId) {
651
680
  this.#clientOwnedTurnId = undefined;
652
681
  this.#clientOwnedAttemptId = undefined;
653
682
  }
@@ -663,6 +692,16 @@ class CodexStructuredProviderSession {
663
692
  waitForExit() {
664
693
  return this.exit;
665
694
  }
695
+ async cancelTurn(attemptId) {
696
+ if (this.#clientOwnedAttemptId !== attemptId || this.#clientOwnedTurnId === undefined) {
697
+ return this.#submissionPending ? "unknown" : "not-active";
698
+ }
699
+ const result = await this.runtime.interruptTurn({
700
+ conversationId: this.conversationId,
701
+ turnId: this.#clientOwnedTurnId
702
+ });
703
+ return result === "interrupted" ? "requested" : result;
704
+ }
666
705
  terminate(signal) {
667
706
  terminateProcessGroup(this.child, signal);
668
707
  }
@@ -675,7 +714,12 @@ class ClaudeStructuredProviderSession {
675
714
  channel;
676
715
  onGoal;
677
716
  adapterId = "claude";
717
+ get ownedProcessId() { return this.child.pid; }
678
718
  #activeAttemptId;
719
+ #acceptedAttemptId;
720
+ #cancelledAttemptId;
721
+ #seenAssistantIds = new Set();
722
+ #openToolIds = new Set();
679
723
  #resultAttempts = new Map();
680
724
  #lastGoalKey;
681
725
  constructor(child, exit, processInstanceId, conversationId, channel, onGoal) {
@@ -686,14 +730,30 @@ class ClaudeStructuredProviderSession {
686
730
  this.channel = channel;
687
731
  this.onGoal = onGoal;
688
732
  }
689
- static async open(child, exit, processInstanceId, control, onTerminal, onGoal, mirror) {
733
+ static async open(child, exit, processInstanceId, control, onAccepted, onTerminal, onGoal, onActivity, mirror) {
690
734
  const channel = new JsonLineChannel(child, mirror);
691
735
  const nativeSessionId = control.nativeSessionId;
692
736
  if (nativeSessionId === undefined) {
693
737
  throw new Error("Managed Claude launch requires a preallocated native Session id.");
694
738
  }
695
739
  const session = new ClaudeStructuredProviderSession(child, exit, processInstanceId, nativeSessionId, channel, onGoal);
696
- channel.onMessage((message) => session.#receive(message, onTerminal));
740
+ channel.onMessage((message) => session.#receive(message, onTerminal, onAccepted, onActivity));
741
+ // This client owns the whole Claude execution process, unlike a Codex
742
+ // proxy. An exit without a result ends the exact local input as failure,
743
+ // not as continuing work merely because the supervising Host is alive.
744
+ void exit.then(result => {
745
+ const attemptId = session.#activeAttemptId;
746
+ if (attemptId === undefined)
747
+ return;
748
+ session.#activeAttemptId = undefined;
749
+ onTerminal?.({
750
+ conversationId: nativeSessionId, nativeSessionId, attemptId,
751
+ clientOwned: true,
752
+ status: session.#cancelledAttemptId === attemptId ? "cancelled" : "failed",
753
+ observedAt: new Date().toISOString(),
754
+ error: `Owned Provider process exited before a terminal result (code=${result.code}, signal=${result.signal}).`
755
+ });
756
+ });
697
757
  return session;
698
758
  }
699
759
  get nativeSessionId() {
@@ -703,9 +763,15 @@ class ClaudeStructuredProviderSession {
703
763
  // stream-json result.uuid is a message identity, not an execution identity.
704
764
  return undefined;
705
765
  }
766
+ /**
767
+ * This implementation has no run-configuration observation reader.
768
+ */
769
+ get runConfiguration() {
770
+ return CLAUDE_RUN_CONFIGURATION;
771
+ }
706
772
  async submitTurn(turn) {
707
773
  if (this.#activeAttemptId !== undefined) {
708
- throw new ProviderTurnRejectedError("Provider Conversation already has an unsettled Turn.", turn.attemptId);
774
+ throw new ProviderTurnBusyError("Provider Conversation already has an unsettled Turn.", turn.attemptId);
709
775
  }
710
776
  // Reserve before the pipe write: a fast result can precede its callback.
711
777
  // A failed write is ambiguous and must retain the same occupancy.
@@ -723,10 +789,10 @@ class ClaudeStructuredProviderSession {
723
789
  throw new ProviderDeliveryUnknownError(`Claude input write did not complete: ${error instanceof Error ? error.message : String(error)}`, turn.attemptId, { cause: error });
724
790
  }
725
791
  // AgentHost is the sole writer to this dedicated stream-json process.
726
- // A completed pipe write is the smallest reliable acceptance boundary;
727
- // Claude's later `result` is the matching terminal for this serialized
728
- // Turn. Requiring an echoed user-message creates a second, brittle
729
- // protocol without improving delivery safety.
792
+ // A pipe write is transport evidence only. The later result establishes
793
+ // native acceptance and completion through this exact serialized local
794
+ // attempt. A main assistant response can confirm acceptance earlier, without
795
+ // inventing a provider execution id or trusting the echoed user input.
730
796
  return Object.freeze({
731
797
  attemptId: turn.attemptId,
732
798
  conversationId: this.conversationId,
@@ -738,13 +804,20 @@ class ClaudeStructuredProviderSession {
738
804
  async steerTurn(turn) {
739
805
  throw new ProviderTurnRejectedError("Claude stream-json does not expose an exact native Turn identity for steering.", turn.attemptId);
740
806
  }
807
+ async cancelTurn(attemptId) {
808
+ if (this.#activeAttemptId !== attemptId)
809
+ return "not-active";
810
+ this.#cancelledAttemptId = attemptId;
811
+ terminateProcessGroup(this.child, "SIGTERM");
812
+ return "requested";
813
+ }
741
814
  waitForExit() {
742
815
  return this.exit;
743
816
  }
744
817
  terminate(signal) {
745
818
  terminateProcessGroup(this.child, signal);
746
819
  }
747
- #receive(message, onTerminal) {
820
+ #receive(message, onTerminal, onAccepted, onActivity) {
748
821
  const goal = claudeActiveGoal(message, this.conversationId);
749
822
  if (goal !== undefined) {
750
823
  const key = JSON.stringify(goal === null ? null : {
@@ -757,8 +830,64 @@ class ClaudeStructuredProviderSession {
757
830
  this.onGoal?.(goal);
758
831
  }
759
832
  }
760
- if (message.type === "user")
833
+ const activity = (id, phase) => {
834
+ if (this.#activeAttemptId !== undefined)
835
+ onActivity?.({
836
+ conversationId: this.conversationId, nativeSessionId: this.conversationId,
837
+ attemptId: this.#activeAttemptId, id, phase, observedAt: new Date().toISOString()
838
+ });
839
+ };
840
+ if (message.type === "user") {
841
+ if (optionalId(message.session_id) !== this.conversationId || message.parent_tool_use_id != null)
842
+ return;
843
+ const body = object(message.message);
844
+ for (const raw of Array.isArray(body?.content) ? body.content : []) {
845
+ const block = object(raw), id = optionalId(block?.tool_use_id);
846
+ if (block?.type === "tool_result" && id !== undefined && this.#openToolIds.delete(id)) {
847
+ activity(id, block.is_error === true ? "failed" : "completed");
848
+ }
849
+ }
850
+ return;
851
+ }
852
+ if (message.type === "assistant") {
853
+ if (optionalId(message.session_id) !== this.conversationId
854
+ || message.parent_tool_use_id != null
855
+ || object(message.message)?.role !== "assistant")
856
+ return;
857
+ const messageId = optionalId(message.uuid);
858
+ if (messageId === undefined || this.#seenAssistantIds.has(messageId))
859
+ return;
860
+ this.#seenAssistantIds.add(messageId);
861
+ const attemptId = this.#activeAttemptId;
862
+ if (attemptId === undefined)
863
+ return;
864
+ // This dedicated stream has exactly one owned input in flight. Main
865
+ // assistant output proves it is being processed; init, echo and child
866
+ // output do not. Keep the local attempt identity, not the message UUID,
867
+ // as the correlation key (Claude exposes no exact native Turn id).
868
+ if (this.#acceptedAttemptId !== attemptId) {
869
+ this.#acceptedAttemptId = attemptId;
870
+ onAccepted?.({
871
+ attemptId,
872
+ conversationId: this.conversationId,
873
+ nativeSessionId: this.conversationId,
874
+ acceptedAt: new Date().toISOString(),
875
+ acceptance: "provider"
876
+ });
877
+ }
878
+ const body = object(message.message);
879
+ for (const raw of Array.isArray(body?.content) ? body.content : []) {
880
+ const block = object(raw), id = optionalId(block?.id);
881
+ if (block?.type === "tool_use" && id !== undefined && !this.#openToolIds.has(id)) {
882
+ this.#openToolIds.add(id);
883
+ activity(id, "started");
884
+ }
885
+ else if (block?.type === "text" || block?.type === "thinking") {
886
+ activity(messageId, "model");
887
+ }
888
+ }
761
889
  return;
890
+ }
762
891
  if (message.type !== "result"
763
892
  || optionalId(message.session_id) !== this.conversationId)
764
893
  return;
@@ -772,12 +901,14 @@ class ClaudeStructuredProviderSession {
772
901
  return;
773
902
  if (resultId !== undefined)
774
903
  this.#resultAttempts.set(resultId, attemptId);
775
- if (attemptId === this.#activeAttemptId)
904
+ if (attemptId === this.#activeAttemptId) {
776
905
  this.#activeAttemptId = undefined;
906
+ this.#openToolIds.clear();
907
+ }
777
908
  const failed = message.is_error === true || message.subtype === "error_during_execution";
778
909
  const result = typeof message.result === "string" && message.result.length > 0
779
910
  ? message.result
780
- : "Provider Turn failed.";
911
+ : providerErrorEvidence(message).error;
781
912
  onTerminal?.({
782
913
  conversationId: this.conversationId,
783
914
  nativeSessionId: this.conversationId,
@@ -861,21 +992,6 @@ function childExit(child, processInstanceId) {
861
992
  })));
862
993
  });
863
994
  }
864
- function terminateProcessGroup(child, signal) {
865
- if (child.pid === undefined)
866
- return;
867
- // Managed Providers are spawned detached and therefore own a process group.
868
- // Kill that exact group so a CLI helper cannot outlive the Agent Host. The
869
- // direct-child fallback covers embedded runtimes that cannot create setsid.
870
- try {
871
- process.kill(-child.pid, signal);
872
- }
873
- catch (error) {
874
- if (error.code !== "ESRCH")
875
- throw error;
876
- child.kill(signal);
877
- }
878
- }
879
995
  function defaultMirrorOutput(stream, text) {
880
996
  (stream === "stdout" ? process.stdout : process.stderr).write(text);
881
997
  }
@@ -895,10 +1011,14 @@ function nestedId(value, key) {
895
1011
  }
896
1012
  function providerErrorEvidence(value) {
897
1013
  const error = object(value);
1014
+ const errors = Array.isArray(error?.errors)
1015
+ ? error.errors.filter((entry) => typeof entry === "string" && entry.length > 0)
1016
+ : [];
898
1017
  const message = typeof error?.message === "string" && error.message.length > 0
899
1018
  ? error.message
900
- : typeof value === "string" && value.length > 0
901
- ? value
902
- : "Provider Turn failed.";
1019
+ : errors.length > 0 ? errors.join("\n")
1020
+ : typeof value === "string" && value.length > 0
1021
+ ? value
1022
+ : "Provider Turn failed.";
903
1023
  return { error: message, rawError: serializeAgentErrorRaw(value) };
904
1024
  }