@zq-silk/yui 0.13.9 → 0.14.0

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 (229) hide show
  1. package/ARCHITECTURE.md +69 -428
  2. package/README.md +108 -298
  3. package/dist/agent/managedRuntimeEnvironment.js +2 -2
  4. package/dist/cli/commandCatalog.js +39 -77
  5. package/dist/cli/interactionCandidates.js +4 -4
  6. package/dist/cli/interactionPolicy.js +7 -10
  7. package/dist/cli/invocationRouter.js +1 -1
  8. package/dist/cli/updateCommand.js +6 -27
  9. package/dist/cli/updateOrchestrator.js +63 -484
  10. package/dist/cli/updatePorts.js +15 -333
  11. package/dist/cli/upgradeCommand.js +8 -65
  12. package/dist/cli.js +57 -242
  13. package/dist/commands/configCommands.js +11 -62
  14. package/dist/commands/executionAuditCommands.js +23 -23
  15. package/dist/commands/globalRoleCommands.js +2 -7
  16. package/dist/commands/jobCommands.js +1 -4
  17. package/dist/commands/projectCommands.js +5 -5
  18. package/dist/commands/releaseCommands.js +3 -10
  19. package/dist/commands/resourcesCommands.js +2 -2
  20. package/dist/commands/sessionCommands.js +2 -2
  21. package/dist/commands/taskActor.js +31 -39
  22. package/dist/commands/taskChangeSetCommands.js +5 -12
  23. package/dist/commands/taskCommands.js +464 -1249
  24. package/dist/commands/taskContextCommand.js +35 -36
  25. package/dist/commands/taskExecutionCommands.js +33 -46
  26. package/dist/commands/taskInputCommands.js +20 -42
  27. package/dist/commands/taskNextActionCommand.js +9 -9
  28. package/dist/commands/taskOverviewCommand.js +56 -95
  29. package/dist/commands/taskRoleRuntimeStatus.js +77 -107
  30. package/dist/commands/taskUpstreamCommands.js +3 -3
  31. package/dist/commands/taskWorkspaceCommands.js +7 -79
  32. package/dist/commands/telemetryCommands.js +32 -124
  33. package/dist/commands/workflowCommands.js +3 -3
  34. package/dist/completion/fileCompletionManager.js +1 -1
  35. package/dist/config/configCatalog.js +4 -6
  36. package/dist/config/yuiConfig.js +6 -43
  37. package/dist/context/dispatchContext.js +14 -30
  38. package/dist/context/roleSessionContext.js +2 -2
  39. package/dist/context/sessionBootstrapManifest.js +2 -2
  40. package/dist/context/{runContextPack.js → turnContextPack.js} +58 -73
  41. package/dist/context/turnInputContract.js +204 -0
  42. package/dist/context/wakeNotification.js +2 -2
  43. package/dist/controller/agentRuntimeObserver.js +18 -18
  44. package/dist/controller/clientRuntime.js +13 -20
  45. package/dist/controller/controller.js +89 -116
  46. package/dist/controller/fileSchedulerStoreAdapter.js +511 -742
  47. package/dist/controller/jobControl.js +17 -17
  48. package/dist/controller/resourceInventory.js +6 -6
  49. package/dist/controller/resourceInventoryLinux.js +38 -59
  50. package/dist/controller/resourceInventoryRpc.js +3 -3
  51. package/dist/controller/resourceInventoryWorker.js +1 -1
  52. package/dist/controller/runtime.js +46 -53
  53. package/dist/controller/runtimeEventInbox.js +23 -31
  54. package/dist/controller/runtimeEventProcessor.js +15 -15
  55. package/dist/controller/{runtimeHookRunFence.js → runtimeHookTurnFence.js} +87 -81
  56. package/dist/controller/runtimeLaunchCoordinator.js +3 -3
  57. package/dist/controller/runtimeObservationHook.js +11 -8
  58. package/dist/controller/sessionNotify.js +11 -11
  59. package/dist/controller/structuredProviderObservation.js +89 -16
  60. package/dist/coordination/workMailbox.js +309 -579
  61. package/dist/coordination/workMailboxQueue.js +5 -6
  62. package/dist/core/controllerClient.js +1 -91
  63. package/dist/core/controllerServer.js +3 -6
  64. package/dist/doctor/doctor.js +51 -314
  65. package/dist/execution/candidateConvergence.js +3 -3
  66. package/dist/execution/executionGroup.js +32 -33
  67. package/dist/execution/executionHealth.js +36 -38
  68. package/dist/execution/resourceBroker.js +12 -12
  69. package/dist/executor/agentExecutor.js +30 -265
  70. package/dist/executor/effectiveLaunch.js +9 -19
  71. package/dist/executor/executorRegistry.js +43 -17
  72. package/dist/executor/fileRoleLaunchPlanner.js +56 -56
  73. package/dist/executor/launchPlan.js +3 -3
  74. package/dist/executor/workspacePreflightClassification.js +16 -16
  75. package/dist/grant/capabilityGrant.js +20 -25
  76. package/dist/input/inputRequest.js +12 -12
  77. package/dist/integration/changeSet.js +9 -13
  78. package/dist/integration/changeSetManifest.js +1 -1
  79. package/dist/integration/gitIntegrationService.js +2 -2
  80. package/dist/integration/integrationQueueService.js +5 -21
  81. package/dist/integration/overlapDiagnostics.js +2 -2
  82. package/dist/interaction/operatorPresentation.js +1 -1
  83. package/dist/job/jobRunner.js +1 -1
  84. package/dist/lifecycle/canonicalLifecycleEvent.js +25 -25
  85. package/dist/lifecycle/{exactRunTerminalization.js → exactTurnTerminalization.js} +116 -232
  86. package/dist/message/message.js +6 -6
  87. package/dist/observability/executionAudit.js +74 -86
  88. package/dist/observability/faultClassification.js +7 -24
  89. package/dist/observability/orchestrationMetrics.js +5 -5
  90. package/dist/observability/runtimeIdentity.js +34 -101
  91. package/dist/profile/agentProfile.js +1 -1
  92. package/dist/release/cliHomeReleaseFence.js +3 -3
  93. package/dist/release/releaseWorkflowEngine.js +6 -10
  94. package/dist/release/releaseWorkflowPorts.js +2 -2
  95. package/dist/release/workflowFileLock.js +1 -1
  96. package/dist/repository/checkoutSwap.js +1 -1
  97. package/dist/repository/gitWorkspace.js +0 -13
  98. package/dist/repository/project.js +1 -1
  99. package/dist/repository/projectMaintenanceLock.js +1 -2
  100. package/dist/repository/taskBaseFreshness.js +13 -13
  101. package/dist/repository/taskWorkspaceCoordinator.js +21 -25
  102. package/dist/repository/taskWorkspaceIdentity.js +8 -21
  103. package/dist/repository/taskWorkspacePreparer.js +63 -620
  104. package/dist/resources/autoResourceGc.js +3 -3
  105. package/dist/resources/liveReferences.js +1 -1
  106. package/dist/resources/resourceGc.js +1 -1
  107. package/dist/resources/sqliteResourceRegistry.js +1 -1
  108. package/dist/review/reviewDecision.js +7 -7
  109. package/dist/review/reviewFindingLedger.js +1 -1
  110. package/dist/review/reviewOutcomeClassifier.js +35 -50
  111. package/dist/review/reviewRound.js +14 -25
  112. package/dist/review/reviewerAvailability.js +4 -4
  113. package/dist/review/taskFinalReviewContractResolution.js +41 -0
  114. package/dist/role/role.js +6 -3
  115. package/dist/runtime/agentDriver.js +4 -0
  116. package/dist/runtime/agentHost.js +151 -36
  117. package/dist/runtime/builtinAgentDrivers.js +79 -2
  118. package/dist/runtime/codexAppServerRuntime.js +122 -0
  119. package/dist/runtime/continuationManager.js +3 -5
  120. package/dist/runtime/exactControlPlane.js +17 -42
  121. package/dist/runtime/firstProgressAdvisory.js +1 -1
  122. package/dist/runtime/index.js +2 -2
  123. package/dist/runtime/launchDiagnostics.js +2 -2
  124. package/dist/runtime/lifecycleReservation.js +4 -4
  125. package/dist/runtime/processExitObservation.js +1 -1
  126. package/dist/runtime/processExitOutbox.js +0 -32
  127. package/dist/runtime/promptEnvelope.js +9 -9
  128. package/dist/runtime/providerContinuation.js +2 -2
  129. package/dist/runtime/providerContinuationReconciliationService.js +2 -2
  130. package/dist/runtime/providerRuntimeIdentity.js +76 -24
  131. package/dist/runtime/recentTurnIds.js +38 -0
  132. package/dist/runtime/runtimeContinuationProjection.js +2 -2
  133. package/dist/runtime/runtimeHealthPolicy.js +1 -1
  134. package/dist/runtime/runtimeObservation.js +58 -21
  135. package/dist/runtime/runtimeProjection.js +19 -27
  136. package/dist/runtime/sessionLaunchRequest.js +2 -2
  137. package/dist/runtime/structuredProviderHost.js +177 -12
  138. package/dist/runtime/tmuxAdapters.js +46 -11
  139. package/dist/scheduler/activeRoleTurnDelivery.js +245 -0
  140. package/dist/scheduler/leaderWakeupProcessor.js +147 -445
  141. package/dist/scheduler/operatorEvent.js +4 -6
  142. package/dist/scheduler/resourceQueueProjection.js +71 -0
  143. package/dist/scheduler/{roleRunLiveness.js → roleTurnLiveness.js} +24 -28
  144. package/dist/scheduler/{roleRunStall.js → roleTurnStall.js} +162 -247
  145. package/dist/scheduler/taskExecutionProjection.js +99 -99
  146. package/dist/scheduler/taskObservabilityProjection.js +10 -10
  147. package/dist/scheduler/taskWake.js +4 -4
  148. package/dist/scheduler/wakeReason.js +6 -10
  149. package/dist/scheduler/wakeupQueue.js +0 -9
  150. package/dist/setup/setupCommand.js +2 -2
  151. package/dist/storage/currentTaskStore.js +46 -0
  152. package/dist/storage/persistenceWorker.js +1 -1
  153. package/dist/storage/sqliteSchema.js +81 -485
  154. package/dist/storage/sqliteStore.js +206 -365
  155. package/dist/storage/storageSchema.js +1 -15
  156. package/dist/storage/storageVersions.js +8 -9
  157. package/dist/storage/storeRpc.js +10 -34
  158. package/dist/storage/taskStore.js +63 -3415
  159. package/dist/storage/upgrade/recordVersions.js +55 -149
  160. package/dist/storage/upgrade/upgradeOrchestrator.js +89 -1752
  161. package/dist/task/completionReadiness.js +5 -5
  162. package/dist/task/deliveryGuard.js +10 -10
  163. package/dist/task/nextAction.js +74 -77
  164. package/dist/task/task.js +2 -4
  165. package/dist/task/taskRecordReference.js +3 -3
  166. package/dist/task/taskRecordRetirement.js +1 -1
  167. package/dist/telemetry/sqliteTelemetryStore.js +53 -53
  168. package/dist/telemetry/telemetryConfig.js +14 -14
  169. package/dist/telemetry/telemetryWiring.js +8 -14
  170. package/dist/turn/turn.js +356 -0
  171. package/dist/{run/runIdentity.js → turn/turnIdentity.js} +4 -4
  172. package/dist/verification/gateArtifactStore.js +2 -2
  173. package/dist/web/assets/client/app.js +1 -1
  174. package/dist/web/assets/client/components.js +29 -54
  175. package/dist/web/assets/client/i18n.js +17 -37
  176. package/dist/web/assets/client/view.js +37 -37
  177. package/dist/web/assets/shell.js +1 -1
  178. package/dist/web/assets/styles/cards.js +5 -5
  179. package/dist/web/assets/styles/widgets.js +1 -2
  180. package/dist/web/webSnapshot.js +65 -60
  181. package/dist/workItem/workItem.js +11 -11
  182. package/dist/workspace/workItemChangeSetManager.js +1 -1
  183. package/docs/task-local-identity.md +5 -5
  184. package/i18n/README.zh-CN.md +41 -54
  185. package/package.json +1 -1
  186. package/skills/yui-leader/SKILL.md +85 -83
  187. package/skills/yui-operator/SKILL.md +16 -16
  188. package/skills/yui-reviewer/SKILL.md +10 -10
  189. package/skills/yui-runtime/SKILL.md +17 -18
  190. package/skills/yui-worker/SKILL.md +16 -18
  191. package/dist/context/runContextContract.js +0 -162
  192. package/dist/executor/turnCompletion.js +0 -126
  193. package/dist/review/taskFinalReviewContractEvent.js +0 -1
  194. package/dist/review/taskFinalReviewContractRebind.js +0 -367
  195. package/dist/run/agentRun.js +0 -356
  196. package/dist/run/rejectedYieldAttempt.js +0 -221
  197. package/dist/run/runControlRequest.js +0 -50
  198. package/dist/run/yieldReceipt.js +0 -65
  199. package/dist/runtime/turnCompletion.js +0 -3
  200. package/dist/scheduler/actionability.js +0 -323
  201. package/dist/scheduler/activeRoleRunDelivery.js +0 -868
  202. package/dist/scheduler/activeTaskProgress.js +0 -126
  203. package/dist/storage/compatibleTaskStore.js +0 -248
  204. package/dist/storage/migration/baseline.js +0 -78
  205. package/dist/storage/migration/classifier.js +0 -51
  206. package/dist/storage/migration/compatibleCodec.js +0 -53
  207. package/dist/storage/migration/engine.js +0 -147
  208. package/dist/storage/migration/index.js +0 -33
  209. package/dist/storage/migration/planner.js +0 -154
  210. package/dist/storage/migration/productionRegistry.js +0 -3931
  211. package/dist/storage/migration/registry.js +0 -169
  212. package/dist/storage/migration/report.js +0 -54
  213. package/dist/storage/migration/types.js +0 -31
  214. package/dist/storage/upgrade/homeClassification.js +0 -353
  215. package/dist/storage/upgrade/homeMigrationTarget.js +0 -595
  216. package/dist/storage/upgrade/migrationReceipt.js +0 -67
  217. package/dist/storage/upgrade/offlineUpgradeInventory.js +0 -453
  218. package/dist/storage/upgrade/productionMigrationRegistry.js +0 -6
  219. package/dist/storage/upgrade/pseudoLayoutRepair.js +0 -241
  220. package/dist/storage/upgrade/recordVersionScan.js +0 -176
  221. package/dist/storage/upgrade/sqliteFileSet.js +0 -50
  222. package/dist/storage/upgrade/sqliteMigrationTarget.js +0 -375
  223. package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +0 -305
  224. package/dist/storage/upgrade/sqliteStateMigration.js +0 -918
  225. package/dist/storage/upgrade/switchProgress.js +0 -80
  226. package/dist/storage/upgrade/upgradeReceipt.js +0 -161
  227. package/dist/storage/upgradeCoordination.js +0 -187
  228. package/dist/storage/upgradeFence.js +0 -366
  229. package/dist/telemetry/telemetryCompaction.js +0 -252
@@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { Duplex } from "node:stream";
4
4
  import WebSocket from "ws";
5
- import { CodexAppServerRequestError, CodexAppServerRuntime, codexAppServerErrorIsMissing } from "./codexAppServerRuntime.js";
5
+ import { CodexAppServerRequestError, CodexAppServerRuntime, codexGoalNotification, codexAppServerErrorIsMissing, codexTurnInput, codexTurnSummary } from "./codexAppServerRuntime.js";
6
6
  import { serializeAgentErrorRaw } from "./agentError.js";
7
7
  import { PROVIDER_ACCEPT_TIMEOUT_MS } from "./runtimeDeadlines.js";
8
8
  import { YUI_VERSION } from "../version.js";
@@ -61,15 +61,16 @@ export async function startStructuredProviderSession(payload, input = {}) {
61
61
  const exit = childExit(child, processInstanceId);
62
62
  try {
63
63
  if (control.adapterId === "codex") {
64
- const opened = await CodexStructuredProviderSession.open(child, exit, processInstanceId, payload, control, input.onTerminal, mirror);
64
+ const opened = await CodexStructuredProviderSession.open(child, exit, processInstanceId, payload, control, input.onStarted, input.onTerminal, input.onGoal, mirror);
65
65
  return Object.freeze({
66
66
  session: opened.session,
67
67
  ...(opened.recoveredTerminal === undefined
68
68
  ? {}
69
- : { recoveredTerminal: opened.recoveredTerminal })
69
+ : { recoveredTerminal: opened.recoveredTerminal }),
70
+ goal: opened.goal
70
71
  });
71
72
  }
72
- const session = await ClaudeStructuredProviderSession.open(child, exit, processInstanceId, control, input.onTerminal, mirror);
73
+ const session = await ClaudeStructuredProviderSession.open(child, exit, processInstanceId, control, input.onTerminal, input.onGoal, mirror);
73
74
  return Object.freeze({ session });
74
75
  }
75
76
  catch (error) {
@@ -339,21 +340,26 @@ class CodexStructuredProviderSession {
339
340
  processInstanceId;
340
341
  conversationId;
341
342
  runtime;
343
+ onStarted;
342
344
  onTerminal;
345
+ onGoal;
343
346
  adapterId = "codex";
344
347
  #activeTurnId;
345
348
  #clientOwnedTurnId;
346
349
  #submissionPending = false;
350
+ #bufferedStarts = [];
347
351
  #bufferedTerminals = [];
348
- constructor(child, exit, processInstanceId, conversationId, runtime, onTerminal) {
352
+ constructor(child, exit, processInstanceId, conversationId, runtime, onStarted, onTerminal, onGoal) {
349
353
  this.child = child;
350
354
  this.exit = exit;
351
355
  this.processInstanceId = processInstanceId;
352
356
  this.conversationId = conversationId;
353
357
  this.runtime = runtime;
358
+ this.onStarted = onStarted;
354
359
  this.onTerminal = onTerminal;
360
+ this.onGoal = onGoal;
355
361
  }
356
- static async open(child, exit, processInstanceId, payload, control, onTerminal, mirror) {
362
+ static async open(child, exit, processInstanceId, payload, control, onStarted, onTerminal, onGoal, mirror) {
357
363
  const channel = await CodexProxyWebSocketChannel.connect(child, mirror);
358
364
  const openingMessages = [];
359
365
  const stopOpeningBuffer = channel.onMessage((message) => openingMessages.push(message));
@@ -398,7 +404,7 @@ class CodexStructuredProviderSession {
398
404
  name: control.sessionTitle
399
405
  });
400
406
  }
401
- const session = new CodexStructuredProviderSession(child, exit, processInstanceId, conversationId, runtime, onTerminal);
407
+ const session = new CodexStructuredProviderSession(child, exit, processInstanceId, conversationId, runtime, onStarted, onTerminal, onGoal);
402
408
  session.#activeTurnId = resumedActiveTurnId;
403
409
  const ownedTurn = control.kind === "restore" ? control.ownedTurn : undefined;
404
410
  session.#clientOwnedTurnId = ownedTurn?.turnId;
@@ -427,6 +433,7 @@ class CodexStructuredProviderSession {
427
433
  ? "failed"
428
434
  : recovered.status === "interrupted" ? "cancelled" : "completed",
429
435
  observedAt: new Date().toISOString(),
436
+ ...(recovered.summary === undefined ? {} : { summary: recovered.summary }),
430
437
  ...(recovered.status !== "failed"
431
438
  ? {}
432
439
  : {
@@ -440,9 +447,11 @@ class CodexStructuredProviderSession {
440
447
  throw new ProviderDeliveryUnknownError(`Codex resume could not recover the persisted Yui Turn ${ownedTurn.turnId}.`, ownedTurn.attemptId);
441
448
  }
442
449
  }
450
+ const goal = await runtime.readGoal(conversationId);
443
451
  return Object.freeze({
444
452
  session,
445
- ...(recoveredTerminal === undefined ? {} : { recoveredTerminal })
453
+ ...(recoveredTerminal === undefined ? {} : { recoveredTerminal }),
454
+ goal
446
455
  });
447
456
  }
448
457
  #observeMessage(message, emit) {
@@ -450,10 +459,30 @@ class CodexStructuredProviderSession {
450
459
  const params = object(message.params) ?? {};
451
460
  if (optionalId(params.threadId) !== this.conversationId)
452
461
  return undefined;
462
+ const goal = codexGoalNotification({ method, params });
463
+ if (goal !== undefined) {
464
+ if (emit)
465
+ this.onGoal?.(goal);
466
+ return undefined;
467
+ }
453
468
  if (method === "turn/started") {
454
469
  const turnId = nestedId(params, "turn") ?? optionalId(params.turnId);
455
- if (turnId !== undefined)
470
+ if (turnId !== undefined) {
456
471
  this.#activeTurnId = turnId;
472
+ const turn = object(params.turn) ?? {};
473
+ const input = codexTurnInput(turn);
474
+ const started = {
475
+ conversationId: this.conversationId,
476
+ nativeSessionId: this.conversationId,
477
+ nativeTurnId: turnId,
478
+ observedAt: new Date().toISOString(),
479
+ ...(input === undefined ? {} : { input })
480
+ };
481
+ if (this.#submissionPending && emit)
482
+ this.#bufferedStarts.push(started);
483
+ else if (emit)
484
+ this.#emitStarted(started);
485
+ }
457
486
  return undefined;
458
487
  }
459
488
  if (method !== "turn/completed")
@@ -468,12 +497,16 @@ class CodexStructuredProviderSession {
468
497
  const failure = status === "failed"
469
498
  ? providerErrorEvidence(turn.error)
470
499
  : undefined;
500
+ const summary = codexTurnSummary(turn);
501
+ const input = codexTurnInput(turn);
471
502
  const terminal = {
472
503
  conversationId: this.conversationId,
473
504
  nativeSessionId: this.conversationId,
474
505
  nativeTurnId,
475
506
  status,
476
507
  observedAt: new Date().toISOString(),
508
+ ...(input === undefined ? {} : { input }),
509
+ ...(summary === undefined ? {} : { summary }),
477
510
  ...(failure === undefined ? {} : failure)
478
511
  };
479
512
  if (this.#submissionPending && emit) {
@@ -521,14 +554,48 @@ class CodexStructuredProviderSession {
521
554
  }
522
555
  finally {
523
556
  this.#submissionPending = false;
557
+ const starts = this.#bufferedStarts.splice(0);
558
+ for (const started of starts)
559
+ this.#emitStarted(started);
524
560
  const buffered = this.#bufferedTerminals.splice(0);
525
561
  for (const terminal of buffered)
526
562
  this.#emitTerminal(terminal);
527
563
  }
528
564
  }
565
+ async steerTurn(turn) {
566
+ const nativeTurnId = this.#activeTurnId;
567
+ if (nativeTurnId === undefined) {
568
+ throw new ProviderTurnRejectedError("Provider Conversation has no active Turn to steer.", turn.attemptId);
569
+ }
570
+ const acceptance = await this.runtime.steerTurn({
571
+ conversationId: this.conversationId,
572
+ expectedTurnId: nativeTurnId,
573
+ text: turn.boundedText,
574
+ clientUserMessageId: turn.attemptId
575
+ });
576
+ if (acceptance.status === "unknown") {
577
+ throw new ProviderDeliveryUnknownError(acceptance.reason, turn.attemptId);
578
+ }
579
+ if (acceptance.status !== "accepted") {
580
+ throw new ProviderTurnRejectedError(acceptance.reason, turn.attemptId);
581
+ }
582
+ return Object.freeze({
583
+ attemptId: turn.attemptId,
584
+ conversationId: this.conversationId,
585
+ nativeSessionId: this.conversationId,
586
+ nativeTurnId,
587
+ acceptedAt: new Date().toISOString()
588
+ });
589
+ }
529
590
  #emitTerminal(terminal) {
530
591
  this.#completeTerminal(terminal, true);
531
592
  }
593
+ #emitStarted(started) {
594
+ this.onStarted?.({
595
+ ...started,
596
+ clientOwned: started.nativeTurnId === this.#clientOwnedTurnId
597
+ });
598
+ }
532
599
  #completeTerminal(terminal, emit) {
533
600
  const clientOwned = terminal.nativeTurnId === this.#clientOwnedTurnId;
534
601
  if (terminal.nativeTurnId === this.#activeTurnId)
@@ -553,22 +620,25 @@ class ClaudeStructuredProviderSession {
553
620
  processInstanceId;
554
621
  conversationId;
555
622
  channel;
623
+ onGoal;
556
624
  adapterId = "claude";
557
625
  #activeTurnId;
558
- constructor(child, exit, processInstanceId, conversationId, channel) {
626
+ #lastGoalKey;
627
+ constructor(child, exit, processInstanceId, conversationId, channel, onGoal) {
559
628
  this.child = child;
560
629
  this.exit = exit;
561
630
  this.processInstanceId = processInstanceId;
562
631
  this.conversationId = conversationId;
563
632
  this.channel = channel;
633
+ this.onGoal = onGoal;
564
634
  }
565
- static async open(child, exit, processInstanceId, control, onTerminal, mirror) {
635
+ static async open(child, exit, processInstanceId, control, onTerminal, onGoal, mirror) {
566
636
  const channel = new JsonLineChannel(child, mirror);
567
637
  const nativeSessionId = control.nativeSessionId;
568
638
  if (nativeSessionId === undefined) {
569
639
  throw new Error("Managed Claude launch requires a preallocated native Session id.");
570
640
  }
571
- const session = new ClaudeStructuredProviderSession(child, exit, processInstanceId, nativeSessionId, channel);
641
+ const session = new ClaudeStructuredProviderSession(child, exit, processInstanceId, nativeSessionId, channel, onGoal);
572
642
  channel.onMessage((message) => session.#receive(message, onTerminal));
573
643
  return session;
574
644
  }
@@ -609,6 +679,31 @@ class ClaudeStructuredProviderSession {
609
679
  acceptedAt: new Date().toISOString()
610
680
  });
611
681
  }
682
+ async steerTurn(turn) {
683
+ const nativeTurnId = this.#activeTurnId;
684
+ if (nativeTurnId === undefined) {
685
+ throw new ProviderTurnRejectedError("Provider Conversation has no active Turn to steer.", turn.attemptId);
686
+ }
687
+ try {
688
+ await this.channel.send({
689
+ type: "user",
690
+ message: {
691
+ role: "user",
692
+ content: [{ type: "text", text: turn.boundedText }]
693
+ }
694
+ });
695
+ }
696
+ catch (error) {
697
+ throw new ProviderDeliveryUnknownError(`Claude steer write did not complete: ${error instanceof Error ? error.message : String(error)}`, turn.attemptId);
698
+ }
699
+ return Object.freeze({
700
+ attemptId: turn.attemptId,
701
+ conversationId: this.conversationId,
702
+ nativeSessionId: this.conversationId,
703
+ nativeTurnId,
704
+ acceptedAt: new Date().toISOString()
705
+ });
706
+ }
612
707
  waitForExit() {
613
708
  return this.exit;
614
709
  }
@@ -616,6 +711,18 @@ class ClaudeStructuredProviderSession {
616
711
  terminateProcessGroup(this.child, signal);
617
712
  }
618
713
  #receive(message, onTerminal) {
714
+ const goal = claudeActiveGoal(message, this.conversationId);
715
+ if (goal !== undefined) {
716
+ const key = JSON.stringify(goal === null ? null : {
717
+ status: goal.status,
718
+ objective: goal.objective,
719
+ tokenBudget: goal.tokenBudget
720
+ });
721
+ if (key !== this.#lastGoalKey) {
722
+ this.#lastGoalKey = key;
723
+ this.onGoal?.(goal);
724
+ }
725
+ }
619
726
  if (message.type === "user")
620
727
  return;
621
728
  if (message.type !== "result" || this.#activeTurnId === undefined
@@ -642,6 +749,64 @@ class ClaudeStructuredProviderSession {
642
749
  });
643
750
  }
644
751
  }
752
+ function claudeActiveGoal(message, conversationId) {
753
+ if (!Object.hasOwn(message, "active_goal"))
754
+ return undefined;
755
+ if (message.active_goal === null)
756
+ return null;
757
+ const raw = object(message.active_goal);
758
+ if (raw === null)
759
+ return undefined;
760
+ const objective = typeof raw.objective === "string" ? raw.objective.trim() : "";
761
+ if (objective.length === 0)
762
+ return undefined;
763
+ const status = providerGoalStatus(raw.status);
764
+ if (status === undefined)
765
+ return undefined;
766
+ const tokenBudget = positiveOptionalInteger(raw.token_budget ?? raw.tokenBudget);
767
+ return Object.freeze({
768
+ conversationId,
769
+ status,
770
+ objective,
771
+ updatedAt: providerTimestamp(raw.updated_at ?? raw.updatedAt),
772
+ ...(optionalId(raw.turn_id ?? raw.turnId) === undefined
773
+ ? {}
774
+ : { nativeTurnId: optionalId(raw.turn_id ?? raw.turnId) }),
775
+ ...(tokenBudget === undefined ? {} : { tokenBudget })
776
+ });
777
+ }
778
+ function providerGoalStatus(value) {
779
+ switch (value) {
780
+ case "active":
781
+ case "paused":
782
+ case "blocked":
783
+ case "complete":
784
+ return value;
785
+ case "usageLimited":
786
+ case "usage_limited":
787
+ case "usage-limited":
788
+ return "usage-limited";
789
+ case "budgetLimited":
790
+ case "budget_limited":
791
+ case "budget-limited":
792
+ return "budget-limited";
793
+ default:
794
+ return undefined;
795
+ }
796
+ }
797
+ function providerTimestamp(value) {
798
+ if (typeof value === "string" && Number.isFinite(Date.parse(value))) {
799
+ return new Date(value).toISOString();
800
+ }
801
+ if (Number.isSafeInteger(value) && value >= 0) {
802
+ const epoch = value;
803
+ return new Date(epoch < 1_000_000_000_000 ? epoch * 1_000 : epoch).toISOString();
804
+ }
805
+ return new Date().toISOString();
806
+ }
807
+ function positiveOptionalInteger(value) {
808
+ return Number.isSafeInteger(value) && value > 0 ? value : undefined;
809
+ }
645
810
  function childExit(child, processInstanceId) {
646
811
  return new Promise((resolvePromise, reject) => {
647
812
  child.once("error", reject);
@@ -8,7 +8,7 @@ import { toRuntimeLaunchFailure } from "./launchDiagnostics.js";
8
8
  import { requireSafeIdentity } from "./validation.js";
9
9
  import { YUI_CONTROL_PLANE_DESCRIPTOR, YUI_TASK_RUNTIME_DESCRIPTOR } from "./exactControlPlane.js";
10
10
  import { launchBrokerForHome } from "./launchBroker.js";
11
- import { AGENT_HOST_CONTROL_PROTOCOL, sendAgentHostLaunchControl, sendAgentHostTurnControl, waitForAgentHostLaunchAck } from "./agentHost.js";
11
+ import { AGENT_HOST_CONTROL_PROTOCOL, sendAgentHostLaunchControl, sendAgentHostTurnControl, sendAgentHostSteerControl, waitForAgentHostLaunchAck } from "./agentHost.js";
12
12
  const DEFAULT_INACTIVITY_TIMEOUT_MS = 300_000;
13
13
  /**
14
14
  * Runtime lifecycle adapter for the current tmux host. The returned hostRef is
@@ -160,7 +160,7 @@ export class TmuxSessionHost {
160
160
  // Validate generated identity before starting an external process.
161
161
  const bindingId = requireSafeIdentity(this.#createBindingId(), "Runtime binding id");
162
162
  const writableHumanAttached = request.owner.scope === "task"
163
- && request.runId !== undefined
163
+ && request.turnId !== undefined
164
164
  && (this.tmux.hasWritableClientAsync !== undefined
165
165
  ? await this.tmux.hasWritableClientAsync(hostId, request.owner.roleName)
166
166
  : this.tmux.hasWritableClient?.(hostId, request.owner.roleName) === true);
@@ -174,7 +174,7 @@ export class TmuxSessionHost {
174
174
  effective: request.effective,
175
175
  launchId: request.launchId,
176
176
  mode: request.mode,
177
- ...(request.runId === undefined ? {} : { runId: request.runId }),
177
+ ...(request.turnId === undefined ? {} : { turnId: request.turnId }),
178
178
  ...(request.runtimeIsolation === undefined
179
179
  ? {}
180
180
  : { runtimeIsolation: request.runtimeIsolation }),
@@ -221,7 +221,7 @@ export class TmuxSessionHost {
221
221
  beforeHostStart?.({
222
222
  owner: request.owner,
223
223
  launchId: request.launchId,
224
- ...(request.runId === undefined ? {} : { runId: request.runId }),
224
+ ...(request.turnId === undefined ? {} : { turnId: request.turnId }),
225
225
  agentId: request.agentId,
226
226
  adapterId: request.adapterId,
227
227
  effective: request.effective,
@@ -235,12 +235,12 @@ export class TmuxSessionHost {
235
235
  // Interactive/global Roles remain native TUIs even when their Driver
236
236
  // advertises a persistent child lifecycle. Provider control metadata is
237
237
  // the discriminator for the structured Agent Host path. A managed Task
238
- // Run has no terminal-write fallback and must expose that contract.
238
+ // Turn has no terminal-write fallback and must expose that contract.
239
239
  if (yuiHome === undefined
240
240
  || childLifecycle === undefined
241
241
  || planned.launch.providerControl === undefined) {
242
- if (request.owner.scope === "task" && request.runId !== undefined) {
243
- throw new Error("Managed Task Run is missing its structured Agent Host contract.");
242
+ if (request.owner.scope === "task" && request.turnId !== undefined) {
243
+ throw new Error("Managed Task Turn is missing its structured Agent Host contract.");
244
244
  }
245
245
  let hostCreated;
246
246
  try {
@@ -270,7 +270,7 @@ export class TmuxSessionHost {
270
270
  }
271
271
  if (request.mode === "new"
272
272
  && request.owner.scope === "task"
273
- && request.runId !== undefined
273
+ && request.turnId !== undefined
274
274
  && this.#waitForNativeSession !== undefined) {
275
275
  binding = createRuntimeBinding({
276
276
  ...binding,
@@ -286,7 +286,7 @@ export class TmuxSessionHost {
286
286
  const sessionManifest = planned.launch.env.YUI_SESSION_MANIFEST;
287
287
  const frozenControlPlane = planned.launch.env[YUI_CONTROL_PLANE_DESCRIPTOR];
288
288
  const frozenTaskRuntime = planned.launch.env[YUI_TASK_RUNTIME_DESCRIPTOR];
289
- if (request.owner.scope === "task" && request.runId !== undefined
289
+ if (request.owner.scope === "task" && request.turnId !== undefined
290
290
  && (sessionManifest === undefined
291
291
  || frozenControlPlane === undefined
292
292
  || frozenTaskRuntime === undefined)) {
@@ -507,7 +507,7 @@ async function deadHostLaunchFailure(tmux, hostId, roleName, pane, context) {
507
507
  ...(stderrTail === undefined || stderrTail.length === 0 ? {} : { stderrTail })
508
508
  });
509
509
  }
510
- /** Structured managed-Run input; tmux remains presentation/liveness only. */
510
+ /** Structured managed-Turn input; tmux remains presentation/liveness only. */
511
511
  export class AgentHostPromptPushAdapter {
512
512
  home;
513
513
  constructor(home) {
@@ -531,7 +531,7 @@ export class AgentHostPromptPushAdapter {
531
531
  type: "submit-turn",
532
532
  launchId: request.binding.launchId,
533
533
  nativeSessionId: request.binding.nativeSessionId,
534
- runId: request.envelope.source.localId,
534
+ turnId: request.envelope.source.localId,
535
535
  authority: request.binding.providerAuthority,
536
536
  turn: {
537
537
  attemptId: request.envelope.id,
@@ -563,6 +563,41 @@ export class AgentHostPromptPushAdapter {
563
563
  : "delivery-unknown";
564
564
  }
565
565
  }
566
+ async trySteer(request) {
567
+ try {
568
+ const result = await sendAgentHostSteerControl({
569
+ home: this.home,
570
+ scope: "task",
571
+ taskId: request.owner.taskId,
572
+ roleName: request.owner.roleName,
573
+ control: {
574
+ protocol: AGENT_HOST_CONTROL_PROTOCOL,
575
+ type: "steer-turn",
576
+ launchId: request.launchId,
577
+ nativeSessionId: request.nativeSessionId,
578
+ nativeTurnId: request.nativeTurnId,
579
+ authority: request.providerAuthority,
580
+ turn: {
581
+ attemptId: request.envelope.id,
582
+ boundedText: request.envelope.text
583
+ }
584
+ }
585
+ });
586
+ if (result.outcome === "accepted")
587
+ return "delivered";
588
+ if (result.snapshot.state === "delivery-unknown")
589
+ return "delivery-unknown";
590
+ if (result.snapshot.state === "busy")
591
+ return "busy";
592
+ return result.outcome === "rejected" ? "rejected" : "unavailable";
593
+ }
594
+ catch (error) {
595
+ const code = error.code;
596
+ return code === "ENOENT" || code === "ECONNREFUSED"
597
+ ? "unavailable"
598
+ : "delivery-unknown";
599
+ }
600
+ }
566
601
  }
567
602
  async function ensureRoleWindow(tmux, hostId, role, launch) {
568
603
  return tmux.ensureRoleWindowAsync === undefined