@zq-silk/yui 0.13.7 → 0.13.9
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 +40 -4
- package/README.md +23 -4
- package/dist/cli/commandCatalog.js +21 -2
- package/dist/cli/updatePorts.js +4 -4
- package/dist/cli.js +43 -14
- package/dist/commands/agentCommands.js +1 -1
- package/dist/commands/configCommands.js +1 -86
- package/dist/commands/executionAuditCommands.js +17 -16
- package/dist/commands/globalRoleCommands.js +4 -4
- package/dist/commands/sessionCommands.js +2 -6
- package/dist/commands/taskActor.js +1 -2
- package/dist/commands/taskCommands.js +93 -21
- package/dist/commands/taskContextCommand.js +1 -1
- package/dist/commands/taskExecutionCommands.js +0 -5
- package/dist/commands/taskOverviewCommand.js +5 -1
- package/dist/commands/taskRoleRuntimeStatus.js +3 -16
- package/dist/config/configCatalog.js +1 -6
- package/dist/config/yuiConfig.js +0 -79
- package/dist/context/sessionBootstrapManifest.js +26 -23
- package/dist/controller/clientRuntime.js +40 -3
- package/dist/controller/controller.js +29 -54
- package/dist/controller/fileSchedulerStoreAdapter.js +372 -1117
- package/dist/controller/runtime.js +12 -5
- package/dist/controller/runtimeHookRunFence.js +3 -9
- package/dist/controller/runtimeLaunchCoordinator.js +44 -67
- package/dist/controller/structuredProviderObservation.js +18 -5
- package/dist/coordination/workMailbox.js +4 -4
- package/dist/execution/executionHealth.js +1 -1
- package/dist/executor/agentExecutor.js +103 -93
- package/dist/executor/executorRegistry.js +8 -20
- package/dist/executor/fileRoleLaunchPlanner.js +26 -93
- package/dist/executor/turnCompletion.js +5 -5
- package/dist/lifecycle/exactRunTerminalization.js +2 -4
- package/dist/observability/executionAudit.js +40 -94
- package/dist/operator/operatorSessionHistory.js +7 -5
- package/dist/output/rolePresentation.js +0 -1
- package/dist/repository/taskWorkspacePreparer.js +0 -1
- package/dist/role/role.js +13 -21
- package/dist/run/agentRun.js +4 -54
- package/dist/runtime/agentDriver.js +2 -0
- package/dist/runtime/agentError.js +114 -0
- package/dist/runtime/agentHost.js +55 -82
- package/dist/runtime/builtinAgentDrivers.js +21 -9
- package/dist/runtime/builtinAgentErrorMappers.js +150 -0
- package/dist/runtime/exactControlPlane.js +6 -12
- package/dist/runtime/index.js +1 -2
- package/dist/runtime/launchBroker.js +5 -19
- package/dist/runtime/lifecycleReservation.js +20 -4
- package/dist/runtime/providerRuntimeIdentity.js +11 -19
- package/dist/runtime/runtimeBinding.js +0 -27
- package/dist/runtime/runtimeObservation.js +7 -16
- package/dist/runtime/runtimeSessionCandidate.js +3 -10
- package/dist/runtime/sessionLaunchRequest.js +1 -2
- package/dist/runtime/sessionReconciliation.js +2 -2
- package/dist/runtime/structuredProviderHost.js +44 -79
- package/dist/runtime/taskRuntimeIsolation.js +0 -7
- package/dist/runtime/tmuxAdapters.js +6 -49
- package/dist/scheduler/activeRoleRunDelivery.js +220 -178
- package/dist/scheduler/activeTaskProgress.js +1 -4
- package/dist/scheduler/leaderWakeupProcessor.js +123 -88
- package/dist/scheduler/roleRunLiveness.js +4 -1
- package/dist/scheduler/roleRunStall.js +10 -14
- package/dist/scheduler/wakeReason.js +4 -0
- package/dist/storage/migration/productionRegistry.js +475 -0
- package/dist/storage/sqliteSchema.js +54 -2
- package/dist/storage/sqliteStore.js +11 -44
- package/dist/storage/taskStore.js +8 -36
- package/dist/web/webSnapshot.js +3 -0
- package/i18n/README.zh-CN.md +11 -3
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +30 -8
- package/skills/yui-operator/SKILL.md +14 -6
- package/skills/yui-runtime/SKILL.md +11 -8
- package/dist/lifecycle/providerErrorClass.js +0 -152
- package/dist/run/providerRetry.js +0 -226
- package/dist/run/providerRetryConfig.js +0 -27
- package/dist/runtime/providerErrorCodes.js +0 -278
- package/dist/runtime/providerRecoveryDecision.js +0 -55
|
@@ -10,11 +10,6 @@ import { activeRoleAgentBinding } from "../role/role.js";
|
|
|
10
10
|
import { writeTextFileAtomically } from "../storage/durableFile.js";
|
|
11
11
|
import { compileRoleSessionContext, roleSessionKind } from "../context/roleSessionContext.js";
|
|
12
12
|
import { materializeSessionBootstrap } from "../context/sessionBootstrapManifest.js";
|
|
13
|
-
import { serializeRunBootstrapEnvelope, serializeRunHostRecoveryEnvelope } from "../context/runContextContract.js";
|
|
14
|
-
import { serializeProviderRetryEnvelope } from "../run/providerRetry.js";
|
|
15
|
-
import { serializeWorkflowOutcomeRequestEnvelope } from "../run/runControlRequest.js";
|
|
16
|
-
import { prefixYuiTitleInput } from "../run/runIdentity.js";
|
|
17
|
-
import { agentRunDeliveryReceiptId } from "../run/agentRun.js";
|
|
18
13
|
import { resolveAgentAdapter } from "./agentAdapter.js";
|
|
19
14
|
import { resolveTaskRoleSessionTitle } from "../runtime/sessionTitle.js";
|
|
20
15
|
import { nativeSessionIdForLaunch } from "../runtime/preallocatedNativeSession.js";
|
|
@@ -302,7 +297,6 @@ export class FileRoleLaunchPlanner {
|
|
|
302
297
|
if (runtimeIsolation !== undefined && (owner.scope !== "task"
|
|
303
298
|
|| runtimeIsolation.taskId !== owner.taskId
|
|
304
299
|
|| runtimeIsolation.workspace.root !== effectiveWorkspace
|
|
305
|
-
|| runtimeIsolation.generation.runId !== input.runId
|
|
306
300
|
|| runtimeIsolation.generation.launchId !== input.launchId)) {
|
|
307
301
|
throw new Error("Role launch does not match its Task runtime isolation descriptor.");
|
|
308
302
|
}
|
|
@@ -478,41 +472,31 @@ export class FileRoleLaunchPlanner {
|
|
|
478
472
|
if (owner.scope === "task" && (input.mode === "new" || input.mode === "resume")) {
|
|
479
473
|
jobCallerKey = randomBytes(32).toString("hex");
|
|
480
474
|
}
|
|
481
|
-
//
|
|
482
|
-
//
|
|
483
|
-
//
|
|
484
|
-
// existing without a corresponding Provider Turn.
|
|
475
|
+
// Session lifecycle and Turn submission are separate atomic operations.
|
|
476
|
+
// Planning only starts or restores the exact native Session; delivery
|
|
477
|
+
// submits the Run input after that Session fact is durable.
|
|
485
478
|
if (managedControl && (managedRun === null || managedRun.status !== "active")) {
|
|
486
479
|
throw new Error(`Managed Run is no longer active: ${input.runId}.`);
|
|
487
480
|
}
|
|
488
|
-
const carriesInitialTurn = managedControl && (managedRun.pushedAt === undefined
|
|
489
|
-
|| managedRun.providerRetry?.state === "dispatching"
|
|
490
|
-
|| managedRun.controlRequest?.state === "dispatching");
|
|
491
481
|
const providerAuthority = managedControl
|
|
492
482
|
? this.#providerAuthorityForLaunch(owner.taskId, role.name, input.launchId, input.mode)
|
|
493
483
|
: undefined;
|
|
494
|
-
const providerOwnedTurn = managedControl &&
|
|
484
|
+
const providerOwnedTurn = managedControl && input.mode === "resume"
|
|
495
485
|
? this.#providerOwnedTurnForLaunch(owner.taskId, role.name, input.runId)
|
|
496
486
|
: undefined;
|
|
497
487
|
const providerNativeSessionId = binding.adapterId === "claude"
|
|
498
488
|
? preallocatedNativeSessionId
|
|
499
489
|
: resumeNativeSessionId;
|
|
500
|
-
const initialTurn = carriesInitialTurn
|
|
501
|
-
? {
|
|
502
|
-
attemptId: agentRunDeliveryReceiptId(managedRun),
|
|
503
|
-
boundedText: managedRunLaunchEnvelope(managedRun, input.mode, sessionContext.sessionManifestPath, sessionTitle)
|
|
504
|
-
}
|
|
505
|
-
: undefined;
|
|
506
490
|
const providerControl = !managedControl
|
|
507
491
|
? undefined
|
|
508
|
-
:
|
|
492
|
+
: input.mode === "resume"
|
|
509
493
|
? {
|
|
510
494
|
schemaVersion: 1,
|
|
511
495
|
adapterId: binding.adapterId,
|
|
512
496
|
transport: managedCompiled.transport,
|
|
513
|
-
kind: "
|
|
497
|
+
kind: "restore",
|
|
514
498
|
mode: "resume",
|
|
515
|
-
nativeSessionId: requireText(providerNativeSessionId, "Managed Provider
|
|
499
|
+
nativeSessionId: requireText(providerNativeSessionId, "Managed Provider restore native session id"),
|
|
516
500
|
...(sessionTitle === undefined ? {} : { sessionTitle }),
|
|
517
501
|
...(managedCompiled.codexThread === undefined
|
|
518
502
|
? {}
|
|
@@ -520,44 +504,24 @@ export class FileRoleLaunchPlanner {
|
|
|
520
504
|
...(providerOwnedTurn === undefined ? {} : { ownedTurn: providerOwnedTurn }),
|
|
521
505
|
authority: providerAuthority
|
|
522
506
|
}
|
|
523
|
-
:
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
initialTurn
|
|
539
|
-
}
|
|
540
|
-
: {
|
|
541
|
-
schemaVersion: 1,
|
|
542
|
-
adapterId: binding.adapterId,
|
|
543
|
-
transport: managedCompiled.transport,
|
|
544
|
-
kind: "resume",
|
|
545
|
-
mode: "resume",
|
|
546
|
-
nativeSessionId: requireText(providerNativeSessionId, "Managed Provider resume native session id"),
|
|
547
|
-
...(sessionTitle === undefined ? {} : { sessionTitle }),
|
|
548
|
-
...(managedCompiled.codexThread === undefined
|
|
549
|
-
? {}
|
|
550
|
-
: { codexThread: managedCompiled.codexThread }),
|
|
551
|
-
authority: providerAuthority,
|
|
552
|
-
initialTurn
|
|
553
|
-
};
|
|
507
|
+
: {
|
|
508
|
+
schemaVersion: 1,
|
|
509
|
+
adapterId: binding.adapterId,
|
|
510
|
+
transport: managedCompiled.transport,
|
|
511
|
+
kind: "start",
|
|
512
|
+
mode: "new",
|
|
513
|
+
...(providerNativeSessionId === undefined
|
|
514
|
+
? {}
|
|
515
|
+
: { nativeSessionId: providerNativeSessionId }),
|
|
516
|
+
...(sessionTitle === undefined ? {} : { sessionTitle }),
|
|
517
|
+
...(managedCompiled.codexThread === undefined
|
|
518
|
+
? {}
|
|
519
|
+
: { codexThread: managedCompiled.codexThread }),
|
|
520
|
+
authority: providerAuthority
|
|
521
|
+
};
|
|
554
522
|
const launch = {
|
|
555
523
|
command,
|
|
556
524
|
args,
|
|
557
|
-
...(managedRun?.providerRetry !== undefined
|
|
558
|
-
&& managedRun.providerRetry.state !== "dispatching"
|
|
559
|
-
? { deferProviderStart: true }
|
|
560
|
-
: {}),
|
|
561
525
|
...(providerControl === undefined ? {} : { providerControl }),
|
|
562
526
|
env: {
|
|
563
527
|
...launchEnvironment,
|
|
@@ -608,15 +572,11 @@ export class FileRoleLaunchPlanner {
|
|
|
608
572
|
role: {
|
|
609
573
|
name: role.name,
|
|
610
574
|
workspace: effectiveWorkspace,
|
|
611
|
-
...(agentWorkspace === effectiveWorkspace ? {} : { cwd: agentWorkspace })
|
|
612
|
-
...(owner.scope === "task" ? { status: role.status } : {})
|
|
575
|
+
...(agentWorkspace === effectiveWorkspace ? {} : { cwd: agentWorkspace })
|
|
613
576
|
},
|
|
614
577
|
launch: ordinaryConversationLaunch,
|
|
615
578
|
session,
|
|
616
|
-
...(sessionTitle === undefined ? {} : { sessionTitle })
|
|
617
|
-
...(carriesInitialTurn && input.runId !== undefined
|
|
618
|
-
? { initialTurnRunId: input.runId }
|
|
619
|
-
: {})
|
|
579
|
+
...(sessionTitle === undefined ? {} : { sessionTitle })
|
|
620
580
|
};
|
|
621
581
|
}
|
|
622
582
|
#providerAuthorityForLaunch(taskId, roleName, launchId, mode) {
|
|
@@ -659,7 +619,7 @@ export class FileRoleLaunchPlanner {
|
|
|
659
619
|
}
|
|
660
620
|
#providerOwnedTurnForLaunch(taskId, roleName, runId) {
|
|
661
621
|
const binding = this.store.getTaskRoleSessionSet(taskId, roleName)?.providerBinding;
|
|
662
|
-
if (binding === null || binding === undefined || binding.runId !== runId)
|
|
622
|
+
if (binding === null || binding === undefined || binding.turn?.runId !== runId)
|
|
663
623
|
return undefined;
|
|
664
624
|
const turn = binding.turn;
|
|
665
625
|
if (turn === null || !["accepted", "running"].includes(turn.status))
|
|
@@ -706,33 +666,6 @@ export class FileRoleLaunchPlanner {
|
|
|
706
666
|
return selectEnvironment(source, names);
|
|
707
667
|
}
|
|
708
668
|
}
|
|
709
|
-
function managedRunLaunchEnvelope(run, mode, sessionManifestPath, title) {
|
|
710
|
-
const body = run.providerRetry?.state === "dispatching"
|
|
711
|
-
? serializeProviderRetryEnvelope({
|
|
712
|
-
taskId: run.taskId,
|
|
713
|
-
runId: run.id,
|
|
714
|
-
roleName: run.roleName,
|
|
715
|
-
retry: run.providerRetry
|
|
716
|
-
})
|
|
717
|
-
: run.controlRequest?.state === "dispatching"
|
|
718
|
-
? serializeWorkflowOutcomeRequestEnvelope({
|
|
719
|
-
taskId: run.taskId,
|
|
720
|
-
runId: run.id,
|
|
721
|
-
roleName: run.roleName,
|
|
722
|
-
request: run.controlRequest
|
|
723
|
-
})
|
|
724
|
-
: mode === "resume" && run.pushedAt !== undefined
|
|
725
|
-
? serializeRunHostRecoveryEnvelope(run.bootstrapEnvelope)
|
|
726
|
-
: serializeRunBootstrapEnvelope(run.bootstrapEnvelope);
|
|
727
|
-
const guided = [
|
|
728
|
-
`Yui Session Manifest: ${sessionManifestPath}`,
|
|
729
|
-
"Read the manifest and its matching Role Skill before using the yui CLI for this Task.",
|
|
730
|
-
body
|
|
731
|
-
].join("\n");
|
|
732
|
-
return title === undefined
|
|
733
|
-
? guided
|
|
734
|
-
: prefixYuiTitleInput(guided, title);
|
|
735
|
-
}
|
|
736
669
|
export function nativeAgentWorkspace(workspace) {
|
|
737
670
|
return workspace.entries.length === 1
|
|
738
671
|
? workspace.entries[0].path
|
|
@@ -962,7 +895,7 @@ function readySession(agentId, adapterId, nativeSessionId, effective) {
|
|
|
962
895
|
agentId,
|
|
963
896
|
adapterId,
|
|
964
897
|
nativeSessionId: requireText(nativeSessionId, "Native session id"),
|
|
965
|
-
status: "
|
|
898
|
+
status: "active",
|
|
966
899
|
effective
|
|
967
900
|
};
|
|
968
901
|
}
|
|
@@ -22,7 +22,7 @@ export function createPendingTurnCompletion(input) {
|
|
|
22
22
|
roleName: requireSafeIdentity(input.roleName, "Role name"),
|
|
23
23
|
agentId: requireSafeIdentity(input.agentId, "Agent id"),
|
|
24
24
|
nativeSessionId: requireText(input.nativeSessionId, "Native session id"),
|
|
25
|
-
turnId:
|
|
25
|
+
turnId: requireText(input.turnId, "Turn id"),
|
|
26
26
|
runId: requireSafeIdentity(input.runId, "Run id"),
|
|
27
27
|
summary: requireText(input.summary, "Turn summary"),
|
|
28
28
|
observedAt,
|
|
@@ -44,7 +44,7 @@ export function validatePendingTurnCompletion(value) {
|
|
|
44
44
|
roleName: requireSafeIdentityValue(input.roleName, "Role name"),
|
|
45
45
|
agentId: requireSafeIdentityValue(input.agentId, "Agent id"),
|
|
46
46
|
nativeSessionId: requireTextValue(input.nativeSessionId, "Native session id"),
|
|
47
|
-
turnId:
|
|
47
|
+
turnId: requireTextValue(input.turnId, "Turn id"),
|
|
48
48
|
runId: requireSafeIdentityValue(input.runId, "Run id"),
|
|
49
49
|
summary: requireTextValue(input.summary, "Turn summary"),
|
|
50
50
|
observedAt,
|
|
@@ -58,7 +58,7 @@ export function validateRecentTurnIds(value, limit = DEFAULT_RECENT_TURN_ID_LIMI
|
|
|
58
58
|
if (value.length > normalizedLimit) {
|
|
59
59
|
throw new Error(`Recent Turn ids must not contain more than ${normalizedLimit} entries.`);
|
|
60
60
|
}
|
|
61
|
-
const result = value.map((turnId) =>
|
|
61
|
+
const result = value.map((turnId) => requireTextValue(turnId, "Recent Turn id"));
|
|
62
62
|
if (new Set(result).size !== result.length) {
|
|
63
63
|
throw new Error("Recent Turn ids must not contain duplicates.");
|
|
64
64
|
}
|
|
@@ -67,7 +67,7 @@ export function validateRecentTurnIds(value, limit = DEFAULT_RECENT_TURN_ID_LIMI
|
|
|
67
67
|
export function rememberRecentTurnId(recentTurnIds, turnId, limit = DEFAULT_RECENT_TURN_ID_LIMIT) {
|
|
68
68
|
const normalizedLimit = requireLimit(limit);
|
|
69
69
|
const existing = validateRecentTurnIds(recentTurnIds, normalizedLimit);
|
|
70
|
-
const normalizedTurnId =
|
|
70
|
+
const normalizedTurnId = requireText(turnId, "Recent Turn id");
|
|
71
71
|
return [
|
|
72
72
|
...existing.filter((candidate) => candidate !== normalizedTurnId),
|
|
73
73
|
normalizedTurnId
|
|
@@ -75,7 +75,7 @@ export function rememberRecentTurnId(recentTurnIds, turnId, limit = DEFAULT_RECE
|
|
|
75
75
|
}
|
|
76
76
|
export function hasRecentTurnId(recentTurnIds, turnId) {
|
|
77
77
|
const existing = validateRecentTurnIds(recentTurnIds);
|
|
78
|
-
return existing.includes(
|
|
78
|
+
return existing.includes(requireText(turnId, "Recent Turn id"));
|
|
79
79
|
}
|
|
80
80
|
function requireOrderedTimestamps(observedAt, dueAt) {
|
|
81
81
|
if (Date.parse(dueAt) < Date.parse(observedAt)) {
|
|
@@ -2,7 +2,6 @@ import { isDeepStrictEqual } from "node:util";
|
|
|
2
2
|
import { completeProcessing } from "../coordination/workMailbox.js";
|
|
3
3
|
import { settleExactWorkExecution } from "../coordination/workMailboxQueue.js";
|
|
4
4
|
import { terminalizeTaskRoleRunSession } from "../executor/agentExecutor.js";
|
|
5
|
-
import { updateRoleStatus } from "../role/role.js";
|
|
6
5
|
import { finishReviewRound, updateReviewExecutionGroup } from "../review/reviewRound.js";
|
|
7
6
|
import { reconcileReviewFindingsAfterReview } from "../review/reviewFindingLedger.js";
|
|
8
7
|
import { agentRunDeliveryReceiptId, failAgentRun, withYieldReceipt, yieldAgentRun } from "../run/agentRun.js";
|
|
@@ -265,12 +264,12 @@ export function retireExactActiveAgentRun(store, input, now) {
|
|
|
265
264
|
const session = sessions?.sessions[input.agentId];
|
|
266
265
|
const providerBinding = sessions?.providerBinding;
|
|
267
266
|
const providerTurn = providerBinding?.turn;
|
|
268
|
-
const providerSettled =
|
|
267
|
+
const providerSettled = providerTurn?.runId === current.id
|
|
269
268
|
&& (providerTurn?.status === "completed"
|
|
270
269
|
|| providerTurn?.status === "failed"
|
|
271
270
|
|| providerTurn?.status === "cancelled"
|
|
272
271
|
|| providerTurn?.status === "rejected");
|
|
273
|
-
if (session?.status
|
|
272
|
+
if (session?.status === "active" && !providerSettled) {
|
|
274
273
|
return {
|
|
275
274
|
disposition: "blocked",
|
|
276
275
|
run: current,
|
|
@@ -450,7 +449,6 @@ export function terminalizeExactTaskRun(store, input, now) {
|
|
|
450
449
|
else {
|
|
451
450
|
store.clearActiveAgentRun(input.taskId, input.roleName);
|
|
452
451
|
}
|
|
453
|
-
store.saveRole(input.taskId, updateRoleStatus(role, "idle", now));
|
|
454
452
|
if (sessions !== null) {
|
|
455
453
|
store.saveTaskRoleSessionSet(terminalizeTaskRoleRunSession(sessions, {
|
|
456
454
|
agentId: input.agentId,
|
|
@@ -86,26 +86,6 @@ function roleBucket(roleName) {
|
|
|
86
86
|
}
|
|
87
87
|
return "other";
|
|
88
88
|
}
|
|
89
|
-
/** Maps a provider-retry lifecycle event to its last recorded decision. */
|
|
90
|
-
function providerRetryDecision(event) {
|
|
91
|
-
if (event.type === "runtime.provider-retry-waiting")
|
|
92
|
-
return "waiting";
|
|
93
|
-
if (event.type === "runtime.provider-retry-resume")
|
|
94
|
-
return "resume";
|
|
95
|
-
if (event.type === "runtime.provider-retry-session-dead")
|
|
96
|
-
return "session-dead";
|
|
97
|
-
if (event.type === "runtime.provider-retry-budget-exhausted")
|
|
98
|
-
return "budget-exhausted";
|
|
99
|
-
if (event.payload.note === "native-turn-completion-durable")
|
|
100
|
-
return "suppressed:completion-durable";
|
|
101
|
-
if (event.payload.note === "retry-budget-exhausted")
|
|
102
|
-
return "budget-exhausted";
|
|
103
|
-
if (event.payload.wouldRetry === "true")
|
|
104
|
-
return "retrying";
|
|
105
|
-
if (event.payload.shadow === "true")
|
|
106
|
-
return "shadow";
|
|
107
|
-
return "not-retried";
|
|
108
|
-
}
|
|
109
89
|
function durationMs(run) {
|
|
110
90
|
if (run.endedAt === undefined)
|
|
111
91
|
return 0;
|
|
@@ -205,7 +185,7 @@ export function runExecutionAudit(home, options = {}, ports = createProductionEx
|
|
|
205
185
|
integrations: section,
|
|
206
186
|
publications: section,
|
|
207
187
|
events: section,
|
|
208
|
-
|
|
188
|
+
agentErrors: section,
|
|
209
189
|
workItems: section,
|
|
210
190
|
orchestration: section,
|
|
211
191
|
storage: section,
|
|
@@ -378,13 +358,13 @@ export function runExecutionAudit(home, options = {}, ports = createProductionEx
|
|
|
378
358
|
const history = Array.isArray(set.history) ? set.history : [];
|
|
379
359
|
for (const session of [...history, ...Object.values(set.sessions)]) {
|
|
380
360
|
generations += 1;
|
|
381
|
-
if (session.status === "
|
|
361
|
+
if (session.status === "ended" && session.endReason === "failed")
|
|
382
362
|
broken += 1;
|
|
383
|
-
else if (session.status === "
|
|
363
|
+
else if (session.status === "ended")
|
|
384
364
|
stopped += 1;
|
|
385
365
|
else
|
|
386
366
|
other += 1;
|
|
387
|
-
if (session.status === "
|
|
367
|
+
if (session.status === "ended") {
|
|
388
368
|
classifyTerminalSessionRunRelation(runs, set.owner.roleName, session, terminalByRunRelation);
|
|
389
369
|
}
|
|
390
370
|
}
|
|
@@ -608,51 +588,38 @@ export function runExecutionAudit(home, options = {}, ports = createProductionEx
|
|
|
608
588
|
return failed(error);
|
|
609
589
|
}
|
|
610
590
|
})();
|
|
611
|
-
const
|
|
591
|
+
const agentErrors = (() => {
|
|
612
592
|
try {
|
|
613
|
-
const entries =
|
|
593
|
+
const entries = [];
|
|
594
|
+
const categories = new Map();
|
|
614
595
|
for (const taskId of taskIds) {
|
|
615
596
|
for (const event of store.listEvents(taskId)) {
|
|
616
|
-
if (!inWindow(event.createdAt, options))
|
|
617
|
-
continue;
|
|
618
|
-
if (!event.type.startsWith("runtime.provider-retry-"))
|
|
619
|
-
continue;
|
|
620
|
-
const runId = event.payload.runId;
|
|
621
|
-
if (typeof runId !== "string")
|
|
597
|
+
if (!inWindow(event.createdAt, options) || event.type !== "runtime.agent-error") {
|
|
622
598
|
continue;
|
|
623
|
-
|
|
624
|
-
const
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
const terminal = decision === "session-dead" || decision === "budget-exhausted";
|
|
628
|
-
const next = {
|
|
599
|
+
}
|
|
600
|
+
const category = event.payload.category ?? "unknown";
|
|
601
|
+
categories.set(category, (categories.get(category) ?? 0) + 1);
|
|
602
|
+
entries.push({
|
|
629
603
|
taskId,
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
? { lastBackoffAt: previous.lastBackoffAt }
|
|
644
|
-
: {}),
|
|
645
|
-
decision,
|
|
646
|
-
terminal: (previous?.terminal ?? false) || terminal
|
|
647
|
-
};
|
|
648
|
-
entries.set(key, next);
|
|
604
|
+
eventId: event.id,
|
|
605
|
+
runId: event.payload.runId ?? "",
|
|
606
|
+
roleName: event.payload.roleName ?? "",
|
|
607
|
+
source: event.payload.source ?? "unknown",
|
|
608
|
+
phase: event.payload.phase ?? "unknown",
|
|
609
|
+
category,
|
|
610
|
+
code: event.payload.code ?? "unknown",
|
|
611
|
+
message: event.payload.message ?? "",
|
|
612
|
+
raw: event.payload.raw ?? "",
|
|
613
|
+
inputDisposition: event.payload.inputDisposition ?? "unknown",
|
|
614
|
+
sessionDisposition: event.payload.sessionDisposition ?? "unknown",
|
|
615
|
+
createdAt: event.createdAt
|
|
616
|
+
});
|
|
649
617
|
}
|
|
650
618
|
}
|
|
651
|
-
const list = [...entries.values()];
|
|
652
619
|
return ok({
|
|
653
|
-
total:
|
|
654
|
-
|
|
655
|
-
entries
|
|
620
|
+
total: entries.length,
|
|
621
|
+
byCategory: Object.fromEntries(categories),
|
|
622
|
+
entries
|
|
656
623
|
});
|
|
657
624
|
}
|
|
658
625
|
catch (error) {
|
|
@@ -748,16 +715,10 @@ export function runExecutionAudit(home, options = {}, ports = createProductionEx
|
|
|
748
715
|
try {
|
|
749
716
|
const protocolVersions = new Map();
|
|
750
717
|
const manifestDigests = new Set();
|
|
751
|
-
const
|
|
718
|
+
const agentErrorCategories = new Map();
|
|
752
719
|
const exitClassifications = new Map();
|
|
753
720
|
const usageSemantics = new Map();
|
|
754
|
-
let
|
|
755
|
-
let activeConsecutiveFailures = 0;
|
|
756
|
-
let activeDispatchedRetries = 0;
|
|
757
|
-
let retryClassifiedEvents = 0;
|
|
758
|
-
let retryDispatchedEvents = 0;
|
|
759
|
-
let retryRecoveredEvents = 0;
|
|
760
|
-
let retryExhaustedEvents = 0;
|
|
721
|
+
let agentErrors = 0;
|
|
761
722
|
let contextCapacityFailures = 0;
|
|
762
723
|
let processExitObservations = 0;
|
|
763
724
|
let compactionEvents = 0;
|
|
@@ -770,26 +731,17 @@ export function runExecutionAudit(home, options = {}, ports = createProductionEx
|
|
|
770
731
|
if (run.effective.sessionManifestCompatibilityDigest !== undefined) {
|
|
771
732
|
manifestDigests.add(run.effective.sessionManifestCompatibilityDigest);
|
|
772
733
|
}
|
|
773
|
-
if (run.providerRetry !== undefined) {
|
|
774
|
-
activeRetryEpisodes += 1;
|
|
775
|
-
retryStates.set(run.providerRetry.state, (retryStates.get(run.providerRetry.state) ?? 0) + 1);
|
|
776
|
-
activeConsecutiveFailures += run.providerRetry.consecutiveFailures;
|
|
777
|
-
activeDispatchedRetries += run.providerRetry.dispatchedRetries;
|
|
778
|
-
}
|
|
779
734
|
}
|
|
780
735
|
for (const event of store.listEvents(taskId)) {
|
|
781
736
|
if (!inWindow(event.createdAt, options))
|
|
782
737
|
continue;
|
|
783
|
-
if (event.type === "runtime.
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
retryExhaustedEvents += 1;
|
|
791
|
-
else if (event.type === "runtime.context-capacity-failure")
|
|
792
|
-
contextCapacityFailures += 1;
|
|
738
|
+
if (event.type === "runtime.agent-error") {
|
|
739
|
+
agentErrors += 1;
|
|
740
|
+
const category = event.payload.category ?? "unknown";
|
|
741
|
+
agentErrorCategories.set(category, (agentErrorCategories.get(category) ?? 0) + 1);
|
|
742
|
+
if (category === "context")
|
|
743
|
+
contextCapacityFailures += 1;
|
|
744
|
+
}
|
|
793
745
|
else if (event.type === "runtime.process-exit-observed") {
|
|
794
746
|
processExitObservations += 1;
|
|
795
747
|
const classification = event.payload.classification ?? "unknown";
|
|
@@ -808,14 +760,8 @@ export function runExecutionAudit(home, options = {}, ports = createProductionEx
|
|
|
808
760
|
return ok({
|
|
809
761
|
contextProtocolVersions: Object.fromEntries(protocolVersions),
|
|
810
762
|
manifestCompatibilityDigests: manifestDigests.size,
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
activeConsecutiveFailures,
|
|
814
|
-
activeDispatchedRetries,
|
|
815
|
-
retryClassifiedEvents,
|
|
816
|
-
retryDispatchedEvents,
|
|
817
|
-
retryRecoveredEvents,
|
|
818
|
-
retryExhaustedEvents,
|
|
763
|
+
agentErrors,
|
|
764
|
+
agentErrorCategories: Object.fromEntries(agentErrorCategories),
|
|
819
765
|
contextCapacityFailures,
|
|
820
766
|
processExitObservations,
|
|
821
767
|
processExitClassifications: Object.fromEntries(exitClassifications),
|
|
@@ -870,7 +816,7 @@ export function runExecutionAudit(home, options = {}, ports = createProductionEx
|
|
|
870
816
|
integrations,
|
|
871
817
|
publications,
|
|
872
818
|
events,
|
|
873
|
-
|
|
819
|
+
agentErrors,
|
|
874
820
|
workItems,
|
|
875
821
|
orchestration,
|
|
876
822
|
storage,
|
|
@@ -25,7 +25,7 @@ export function projectOperatorStatus(sessions, activeAgentId, activeAdapterId)
|
|
|
25
25
|
}
|
|
26
26
|
return Object.freeze({
|
|
27
27
|
writer: {
|
|
28
|
-
state: matchingActive.status === "
|
|
28
|
+
state: matchingActive.status === "ended" ? "inactive" : "active",
|
|
29
29
|
agentId: matchingActive.agentId,
|
|
30
30
|
adapterId: matchingActive.adapterId,
|
|
31
31
|
sessionRef: activeRef,
|
|
@@ -42,7 +42,7 @@ export function listOperatorSessions(sessions) {
|
|
|
42
42
|
if (sessions === null)
|
|
43
43
|
return [];
|
|
44
44
|
validateRoleSessionSet(sessions);
|
|
45
|
-
const current = Object.values(sessions.sessions).map((session) => (listItem(session, session.status === "
|
|
45
|
+
const current = Object.values(sessions.sessions).map((session) => (listItem(session, session.status === "ended"
|
|
46
46
|
? "current"
|
|
47
47
|
: "running")));
|
|
48
48
|
const history = Object.values(sessions.history ?? {}).map((session) => (listItem(session, "history")));
|
|
@@ -92,7 +92,8 @@ export function prepareOperatorResumeSession(sessions, ref, now) {
|
|
|
92
92
|
delete history[normalizedRef];
|
|
93
93
|
nextSessions[selected.agentId] = {
|
|
94
94
|
...selected,
|
|
95
|
-
status: "
|
|
95
|
+
status: "ended",
|
|
96
|
+
endReason: "stopped",
|
|
96
97
|
updatedAt: requireDate(now)
|
|
97
98
|
};
|
|
98
99
|
return validateRoleSessionSet({
|
|
@@ -104,12 +105,13 @@ export function prepareOperatorResumeSession(sessions, ref, now) {
|
|
|
104
105
|
});
|
|
105
106
|
}
|
|
106
107
|
function archiveCurrent(history, session) {
|
|
107
|
-
if (session.status !== "
|
|
108
|
+
if (session.status !== "ended") {
|
|
108
109
|
throw new Error(`Cannot replace Operator session while its native process is ${session.status}.`);
|
|
109
110
|
}
|
|
110
111
|
const archived = {
|
|
111
112
|
...session,
|
|
112
|
-
status: "
|
|
113
|
+
status: "ended",
|
|
114
|
+
endReason: session.endReason ?? "stopped"
|
|
113
115
|
};
|
|
114
116
|
return {
|
|
115
117
|
...history,
|
|
@@ -23,7 +23,6 @@ export function renderRoleDetails(title, role, input) {
|
|
|
23
23
|
const overview = [
|
|
24
24
|
` Kind ${input.kind}`,
|
|
25
25
|
` Active Agent ${role.activeAgentId}`,
|
|
26
|
-
...("status" in role ? [` Status ${role.status}`] : []),
|
|
27
26
|
` Workspace ${role.workspace}`,
|
|
28
27
|
` Desired launch r${role.launchRevision}; Profile intent=${role.defaultAccess}`,
|
|
29
28
|
` Effective launch ${effective === undefined
|
|
@@ -2764,7 +2764,6 @@ function retireWorkspaceBoundSession(store, taskId, roleName, now) {
|
|
|
2764
2764
|
}
|
|
2765
2765
|
function canCorrectActiveWorkItemRoleWorkspaceHint(store, taskId, role, item, workspace) {
|
|
2766
2766
|
if (role.taskId !== taskId
|
|
2767
|
-
|| role.status !== "running"
|
|
2768
2767
|
|| item.taskId !== taskId
|
|
2769
2768
|
|| item.assignee !== role.name
|
|
2770
2769
|
|| ["completed", "failed", "retired"].includes(item.status)
|
package/dist/role/role.js
CHANGED
|
@@ -19,19 +19,22 @@ export function createRole(taskId, name, bindings, activeAgentId, workspace, now
|
|
|
19
19
|
const owner = createRoleOwner(name, bindings, activeAgentId, workspace, now, profile, defaultAccess);
|
|
20
20
|
return validateTaskRole({
|
|
21
21
|
...owner,
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
schemaVersion: 4,
|
|
23
|
+
taskId: requireSafeIdentity(taskId, "Task id")
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
26
|
export function createGlobalRole(name, bindings, activeAgentId, workspace, now, profile = {}, defaultAccess = "write") {
|
|
27
|
-
return
|
|
27
|
+
return validateGlobalRole({
|
|
28
|
+
...createRoleOwner(name, bindings, activeAgentId, workspace, now, profile, defaultAccess),
|
|
29
|
+
schemaVersion: 3
|
|
30
|
+
});
|
|
28
31
|
}
|
|
29
32
|
export function copyGlobalRoleToTaskRole(globalRole, taskId, now, name = globalRole.name) {
|
|
30
33
|
validateGlobalRole(globalRole);
|
|
31
34
|
const timestamp = now.toISOString();
|
|
32
35
|
return validateTaskRole({
|
|
33
36
|
...cloneProfile(globalRole),
|
|
34
|
-
schemaVersion:
|
|
37
|
+
schemaVersion: 4,
|
|
35
38
|
launchRevision: 1,
|
|
36
39
|
defaultAccess: globalRole.defaultAccess,
|
|
37
40
|
taskId: requireSafeIdentity(taskId, "Task id"),
|
|
@@ -39,7 +42,6 @@ export function copyGlobalRoleToTaskRole(globalRole, taskId, now, name = globalR
|
|
|
39
42
|
activeAgentId: globalRole.activeAgentId,
|
|
40
43
|
agentBindings: cloneBindings(globalRole.agentBindings),
|
|
41
44
|
workspace: globalRole.workspace,
|
|
42
|
-
status: "idle",
|
|
43
45
|
createdAt: timestamp,
|
|
44
46
|
updatedAt: timestamp
|
|
45
47
|
});
|
|
@@ -76,12 +78,6 @@ export function updateRole(role, patch, now) {
|
|
|
76
78
|
: role.launchRevision + 1;
|
|
77
79
|
return validateTaskRole(updated);
|
|
78
80
|
}
|
|
79
|
-
export function updateRoleStatus(role, status, now) {
|
|
80
|
-
validateTaskRole(role);
|
|
81
|
-
if (!isRoleStatus(status))
|
|
82
|
-
throw new Error(`Role status is invalid: ${status}.`);
|
|
83
|
-
return { ...role, status, updatedAt: now.toISOString() };
|
|
84
|
-
}
|
|
85
81
|
export function switchActiveRoleAgent(role, sessions, targetAgentId, runtime, now) {
|
|
86
82
|
validateRoleOwner(role);
|
|
87
83
|
const normalizedTarget = requireSafeIdentity(targetAgentId, "Target Agent id");
|
|
@@ -134,7 +130,7 @@ export function unbindRoleAgent(role, sessions, agentId, now) {
|
|
|
134
130
|
throw new Error(`Cannot unbind Role Agent with an active Run: ${normalizedAgentId}.`);
|
|
135
131
|
}
|
|
136
132
|
const targetSession = sessions.sessions[normalizedAgentId];
|
|
137
|
-
if (targetSession !== undefined && targetSession.status
|
|
133
|
+
if (targetSession !== undefined && targetSession.status === "active") {
|
|
138
134
|
throw new Error(`Cannot unbind Role Agent while its native session is ${targetSession.status}: `
|
|
139
135
|
+ `${normalizedAgentId}.`);
|
|
140
136
|
}
|
|
@@ -175,8 +171,6 @@ export function validateGlobalRole(role) {
|
|
|
175
171
|
}
|
|
176
172
|
export function validateTaskRole(role) {
|
|
177
173
|
requireSafeIdentity(role.taskId, "Task id");
|
|
178
|
-
if (!isRoleStatus(role.status))
|
|
179
|
-
throw new Error(`Role status is invalid: ${role.status}.`);
|
|
180
174
|
return validateRoleOwner(role);
|
|
181
175
|
}
|
|
182
176
|
function createRoleOwner(name, bindings, activeAgentId, workspace, now, profile, defaultAccess) {
|
|
@@ -189,9 +183,8 @@ function createRoleOwner(name, bindings, activeAgentId, workspace, now, profile,
|
|
|
189
183
|
mappedBindings[binding.agentId] = binding;
|
|
190
184
|
}
|
|
191
185
|
const timestamp = now.toISOString();
|
|
192
|
-
return
|
|
186
|
+
return {
|
|
193
187
|
...cloneProfile(profile),
|
|
194
|
-
schemaVersion: 3,
|
|
195
188
|
launchRevision: 1,
|
|
196
189
|
defaultAccess,
|
|
197
190
|
name: requireSafeIdentity(name, "Role name"),
|
|
@@ -200,11 +193,13 @@ function createRoleOwner(name, bindings, activeAgentId, workspace, now, profile,
|
|
|
200
193
|
workspace: requireText(workspace, "Role workspace"),
|
|
201
194
|
createdAt: timestamp,
|
|
202
195
|
updatedAt: timestamp
|
|
203
|
-
}
|
|
196
|
+
};
|
|
204
197
|
}
|
|
205
198
|
function validateRoleOwner(role) {
|
|
206
|
-
|
|
199
|
+
const expectedSchemaVersion = "taskId" in role ? 4 : 3;
|
|
200
|
+
if (role.schemaVersion !== expectedSchemaVersion) {
|
|
207
201
|
throw new Error("Role schema version is invalid.");
|
|
202
|
+
}
|
|
208
203
|
if (!Number.isSafeInteger(role.launchRevision) || role.launchRevision < 1) {
|
|
209
204
|
throw new Error("Role launch revision must be a positive integer.");
|
|
210
205
|
}
|
|
@@ -333,6 +328,3 @@ function requireText(value, label) {
|
|
|
333
328
|
throw new Error(`${label} is required.`);
|
|
334
329
|
return normalized;
|
|
335
330
|
}
|
|
336
|
-
function isRoleStatus(value) {
|
|
337
|
-
return ["idle", "running", "detached", "exited", "failed"].includes(value);
|
|
338
|
-
}
|