@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.
- package/ARCHITECTURE.md +69 -428
- package/README.md +108 -298
- package/dist/agent/managedRuntimeEnvironment.js +2 -2
- package/dist/cli/commandCatalog.js +39 -77
- package/dist/cli/interactionCandidates.js +4 -4
- package/dist/cli/interactionPolicy.js +7 -10
- package/dist/cli/invocationRouter.js +1 -1
- package/dist/cli/updateCommand.js +6 -27
- package/dist/cli/updateOrchestrator.js +63 -484
- package/dist/cli/updatePorts.js +15 -333
- package/dist/cli/upgradeCommand.js +8 -65
- package/dist/cli.js +57 -242
- package/dist/commands/configCommands.js +11 -62
- package/dist/commands/executionAuditCommands.js +23 -23
- package/dist/commands/globalRoleCommands.js +2 -7
- package/dist/commands/jobCommands.js +1 -4
- package/dist/commands/projectCommands.js +5 -5
- package/dist/commands/releaseCommands.js +3 -10
- package/dist/commands/resourcesCommands.js +2 -2
- package/dist/commands/sessionCommands.js +2 -2
- package/dist/commands/taskActor.js +31 -39
- package/dist/commands/taskChangeSetCommands.js +5 -12
- package/dist/commands/taskCommands.js +464 -1249
- package/dist/commands/taskContextCommand.js +35 -36
- package/dist/commands/taskExecutionCommands.js +33 -46
- package/dist/commands/taskInputCommands.js +20 -42
- package/dist/commands/taskNextActionCommand.js +9 -9
- package/dist/commands/taskOverviewCommand.js +56 -95
- package/dist/commands/taskRoleRuntimeStatus.js +77 -107
- package/dist/commands/taskUpstreamCommands.js +3 -3
- package/dist/commands/taskWorkspaceCommands.js +7 -79
- package/dist/commands/telemetryCommands.js +32 -124
- package/dist/commands/workflowCommands.js +3 -3
- package/dist/completion/fileCompletionManager.js +1 -1
- package/dist/config/configCatalog.js +4 -6
- package/dist/config/yuiConfig.js +6 -43
- package/dist/context/dispatchContext.js +14 -30
- package/dist/context/roleSessionContext.js +2 -2
- package/dist/context/sessionBootstrapManifest.js +2 -2
- package/dist/context/{runContextPack.js → turnContextPack.js} +58 -73
- package/dist/context/turnInputContract.js +204 -0
- package/dist/context/wakeNotification.js +2 -2
- package/dist/controller/agentRuntimeObserver.js +18 -18
- package/dist/controller/clientRuntime.js +13 -20
- package/dist/controller/controller.js +89 -116
- package/dist/controller/fileSchedulerStoreAdapter.js +511 -742
- package/dist/controller/jobControl.js +17 -17
- package/dist/controller/resourceInventory.js +6 -6
- package/dist/controller/resourceInventoryLinux.js +38 -59
- package/dist/controller/resourceInventoryRpc.js +3 -3
- package/dist/controller/resourceInventoryWorker.js +1 -1
- package/dist/controller/runtime.js +46 -53
- package/dist/controller/runtimeEventInbox.js +23 -31
- package/dist/controller/runtimeEventProcessor.js +15 -15
- package/dist/controller/{runtimeHookRunFence.js → runtimeHookTurnFence.js} +87 -81
- package/dist/controller/runtimeLaunchCoordinator.js +3 -3
- package/dist/controller/runtimeObservationHook.js +11 -8
- package/dist/controller/sessionNotify.js +11 -11
- package/dist/controller/structuredProviderObservation.js +89 -16
- package/dist/coordination/workMailbox.js +309 -579
- package/dist/coordination/workMailboxQueue.js +5 -6
- package/dist/core/controllerClient.js +1 -91
- package/dist/core/controllerServer.js +3 -6
- package/dist/doctor/doctor.js +51 -314
- package/dist/execution/candidateConvergence.js +3 -3
- package/dist/execution/executionGroup.js +32 -33
- package/dist/execution/executionHealth.js +36 -38
- package/dist/execution/resourceBroker.js +12 -12
- package/dist/executor/agentExecutor.js +30 -265
- package/dist/executor/effectiveLaunch.js +9 -19
- package/dist/executor/executorRegistry.js +43 -17
- package/dist/executor/fileRoleLaunchPlanner.js +56 -56
- package/dist/executor/launchPlan.js +3 -3
- package/dist/executor/workspacePreflightClassification.js +16 -16
- package/dist/grant/capabilityGrant.js +20 -25
- package/dist/input/inputRequest.js +12 -12
- package/dist/integration/changeSet.js +9 -13
- package/dist/integration/changeSetManifest.js +1 -1
- package/dist/integration/gitIntegrationService.js +2 -2
- package/dist/integration/integrationQueueService.js +5 -21
- package/dist/integration/overlapDiagnostics.js +2 -2
- package/dist/interaction/operatorPresentation.js +1 -1
- package/dist/job/jobRunner.js +1 -1
- package/dist/lifecycle/canonicalLifecycleEvent.js +25 -25
- package/dist/lifecycle/{exactRunTerminalization.js → exactTurnTerminalization.js} +116 -232
- package/dist/message/message.js +6 -6
- package/dist/observability/executionAudit.js +74 -86
- package/dist/observability/faultClassification.js +7 -24
- package/dist/observability/orchestrationMetrics.js +5 -5
- package/dist/observability/runtimeIdentity.js +34 -101
- package/dist/profile/agentProfile.js +1 -1
- package/dist/release/cliHomeReleaseFence.js +3 -3
- package/dist/release/releaseWorkflowEngine.js +6 -10
- package/dist/release/releaseWorkflowPorts.js +2 -2
- package/dist/release/workflowFileLock.js +1 -1
- package/dist/repository/checkoutSwap.js +1 -1
- package/dist/repository/gitWorkspace.js +0 -13
- package/dist/repository/project.js +1 -1
- package/dist/repository/projectMaintenanceLock.js +1 -2
- package/dist/repository/taskBaseFreshness.js +13 -13
- package/dist/repository/taskWorkspaceCoordinator.js +21 -25
- package/dist/repository/taskWorkspaceIdentity.js +8 -21
- package/dist/repository/taskWorkspacePreparer.js +63 -620
- package/dist/resources/autoResourceGc.js +3 -3
- package/dist/resources/liveReferences.js +1 -1
- package/dist/resources/resourceGc.js +1 -1
- package/dist/resources/sqliteResourceRegistry.js +1 -1
- package/dist/review/reviewDecision.js +7 -7
- package/dist/review/reviewFindingLedger.js +1 -1
- package/dist/review/reviewOutcomeClassifier.js +35 -50
- package/dist/review/reviewRound.js +14 -25
- package/dist/review/reviewerAvailability.js +4 -4
- package/dist/review/taskFinalReviewContractResolution.js +41 -0
- package/dist/role/role.js +6 -3
- package/dist/runtime/agentDriver.js +4 -0
- package/dist/runtime/agentHost.js +151 -36
- package/dist/runtime/builtinAgentDrivers.js +79 -2
- package/dist/runtime/codexAppServerRuntime.js +122 -0
- package/dist/runtime/continuationManager.js +3 -5
- package/dist/runtime/exactControlPlane.js +17 -42
- package/dist/runtime/firstProgressAdvisory.js +1 -1
- package/dist/runtime/index.js +2 -2
- package/dist/runtime/launchDiagnostics.js +2 -2
- package/dist/runtime/lifecycleReservation.js +4 -4
- package/dist/runtime/processExitObservation.js +1 -1
- package/dist/runtime/processExitOutbox.js +0 -32
- package/dist/runtime/promptEnvelope.js +9 -9
- package/dist/runtime/providerContinuation.js +2 -2
- package/dist/runtime/providerContinuationReconciliationService.js +2 -2
- package/dist/runtime/providerRuntimeIdentity.js +76 -24
- package/dist/runtime/recentTurnIds.js +38 -0
- package/dist/runtime/runtimeContinuationProjection.js +2 -2
- package/dist/runtime/runtimeHealthPolicy.js +1 -1
- package/dist/runtime/runtimeObservation.js +58 -21
- package/dist/runtime/runtimeProjection.js +19 -27
- package/dist/runtime/sessionLaunchRequest.js +2 -2
- package/dist/runtime/structuredProviderHost.js +177 -12
- package/dist/runtime/tmuxAdapters.js +46 -11
- package/dist/scheduler/activeRoleTurnDelivery.js +245 -0
- package/dist/scheduler/leaderWakeupProcessor.js +147 -445
- package/dist/scheduler/operatorEvent.js +4 -6
- package/dist/scheduler/resourceQueueProjection.js +71 -0
- package/dist/scheduler/{roleRunLiveness.js → roleTurnLiveness.js} +24 -28
- package/dist/scheduler/{roleRunStall.js → roleTurnStall.js} +162 -247
- package/dist/scheduler/taskExecutionProjection.js +99 -99
- package/dist/scheduler/taskObservabilityProjection.js +10 -10
- package/dist/scheduler/taskWake.js +4 -4
- package/dist/scheduler/wakeReason.js +6 -10
- package/dist/scheduler/wakeupQueue.js +0 -9
- package/dist/setup/setupCommand.js +2 -2
- package/dist/storage/currentTaskStore.js +46 -0
- package/dist/storage/persistenceWorker.js +1 -1
- package/dist/storage/sqliteSchema.js +81 -485
- package/dist/storage/sqliteStore.js +206 -365
- package/dist/storage/storageSchema.js +1 -15
- package/dist/storage/storageVersions.js +8 -9
- package/dist/storage/storeRpc.js +10 -34
- package/dist/storage/taskStore.js +63 -3415
- package/dist/storage/upgrade/recordVersions.js +55 -149
- package/dist/storage/upgrade/upgradeOrchestrator.js +89 -1752
- package/dist/task/completionReadiness.js +5 -5
- package/dist/task/deliveryGuard.js +10 -10
- package/dist/task/nextAction.js +74 -77
- package/dist/task/task.js +2 -4
- package/dist/task/taskRecordReference.js +3 -3
- package/dist/task/taskRecordRetirement.js +1 -1
- package/dist/telemetry/sqliteTelemetryStore.js +53 -53
- package/dist/telemetry/telemetryConfig.js +14 -14
- package/dist/telemetry/telemetryWiring.js +8 -14
- package/dist/turn/turn.js +356 -0
- package/dist/{run/runIdentity.js → turn/turnIdentity.js} +4 -4
- package/dist/verification/gateArtifactStore.js +2 -2
- package/dist/web/assets/client/app.js +1 -1
- package/dist/web/assets/client/components.js +29 -54
- package/dist/web/assets/client/i18n.js +17 -37
- package/dist/web/assets/client/view.js +37 -37
- package/dist/web/assets/shell.js +1 -1
- package/dist/web/assets/styles/cards.js +5 -5
- package/dist/web/assets/styles/widgets.js +1 -2
- package/dist/web/webSnapshot.js +65 -60
- package/dist/workItem/workItem.js +11 -11
- package/dist/workspace/workItemChangeSetManager.js +1 -1
- package/docs/task-local-identity.md +5 -5
- package/i18n/README.zh-CN.md +41 -54
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +85 -83
- package/skills/yui-operator/SKILL.md +16 -16
- package/skills/yui-reviewer/SKILL.md +10 -10
- package/skills/yui-runtime/SKILL.md +17 -18
- package/skills/yui-worker/SKILL.md +16 -18
- package/dist/context/runContextContract.js +0 -162
- package/dist/executor/turnCompletion.js +0 -126
- package/dist/review/taskFinalReviewContractEvent.js +0 -1
- package/dist/review/taskFinalReviewContractRebind.js +0 -367
- package/dist/run/agentRun.js +0 -356
- package/dist/run/rejectedYieldAttempt.js +0 -221
- package/dist/run/runControlRequest.js +0 -50
- package/dist/run/yieldReceipt.js +0 -65
- package/dist/runtime/turnCompletion.js +0 -3
- package/dist/scheduler/actionability.js +0 -323
- package/dist/scheduler/activeRoleRunDelivery.js +0 -868
- package/dist/scheduler/activeTaskProgress.js +0 -126
- package/dist/storage/compatibleTaskStore.js +0 -248
- package/dist/storage/migration/baseline.js +0 -78
- package/dist/storage/migration/classifier.js +0 -51
- package/dist/storage/migration/compatibleCodec.js +0 -53
- package/dist/storage/migration/engine.js +0 -147
- package/dist/storage/migration/index.js +0 -33
- package/dist/storage/migration/planner.js +0 -154
- package/dist/storage/migration/productionRegistry.js +0 -3931
- package/dist/storage/migration/registry.js +0 -169
- package/dist/storage/migration/report.js +0 -54
- package/dist/storage/migration/types.js +0 -31
- package/dist/storage/upgrade/homeClassification.js +0 -353
- package/dist/storage/upgrade/homeMigrationTarget.js +0 -595
- package/dist/storage/upgrade/migrationReceipt.js +0 -67
- package/dist/storage/upgrade/offlineUpgradeInventory.js +0 -453
- package/dist/storage/upgrade/productionMigrationRegistry.js +0 -6
- package/dist/storage/upgrade/pseudoLayoutRepair.js +0 -241
- package/dist/storage/upgrade/recordVersionScan.js +0 -176
- package/dist/storage/upgrade/sqliteFileSet.js +0 -50
- package/dist/storage/upgrade/sqliteMigrationTarget.js +0 -375
- package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +0 -305
- package/dist/storage/upgrade/sqliteStateMigration.js +0 -918
- package/dist/storage/upgrade/switchProgress.js +0 -80
- package/dist/storage/upgrade/upgradeReceipt.js +0 -161
- package/dist/storage/upgradeCoordination.js +0 -187
- package/dist/storage/upgradeFence.js +0 -366
- 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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
//
|
|
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.
|
|
243
|
-
throw new Error("Managed Task
|
|
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.
|
|
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.
|
|
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-
|
|
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
|
-
|
|
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
|