@zq-silk/yui 0.6.2 → 0.6.3

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 (61) hide show
  1. package/ARCHITECTURE.md +28 -4
  2. package/README.md +62 -24
  3. package/dist/agent/argumentPolicy.js +1 -1
  4. package/dist/agent/managedRuntimeEnvironment.js +1 -0
  5. package/dist/cli/commandCatalog.js +19 -9
  6. package/dist/cli/interactionPolicy.js +4 -2
  7. package/dist/cli.js +77 -32
  8. package/dist/commands/taskCommands.js +46 -11
  9. package/dist/commands/taskContextCommand.js +1 -1
  10. package/dist/commands/taskRoleRuntimeStatus.js +170 -10
  11. package/dist/controller/agentRuntimeObserver.js +210 -0
  12. package/dist/controller/clientRuntime.js +3 -21
  13. package/dist/controller/controller.js +47 -7
  14. package/dist/controller/fileSchedulerStoreAdapter.js +522 -388
  15. package/dist/controller/runtime.js +9 -3
  16. package/dist/controller/runtimeEventInbox.js +49 -295
  17. package/dist/controller/runtimeEventProcessor.js +184 -321
  18. package/dist/controller/runtimeHookRunFence.js +226 -0
  19. package/dist/controller/runtimeLaunchCoordinator.js +91 -26
  20. package/dist/controller/runtimeObservationHook.js +112 -0
  21. package/dist/core/controllerServer.js +5 -0
  22. package/dist/executor/agentAdapter.js +18 -3
  23. package/dist/executor/fileRoleLaunchPlanner.js +64 -15
  24. package/dist/executor/managedClaudeRunner.js +121 -0
  25. package/dist/observability/executionAudit.js +6 -3
  26. package/dist/repository/taskWorkspacePreparer.js +1 -4
  27. package/dist/run/providerRetryConfig.js +8 -3
  28. package/dist/runtime/agentDriver.js +229 -0
  29. package/dist/runtime/agentDriverObservation.js +57 -0
  30. package/dist/runtime/builtinAgentDrivers.js +235 -0
  31. package/dist/runtime/builtinTranscriptObserver.js +290 -0
  32. package/dist/runtime/builtinTranscriptUsage.js +97 -0
  33. package/dist/runtime/exactControlPlane.js +2 -2
  34. package/dist/runtime/index.js +1 -1
  35. package/dist/runtime/ports.js +12 -1
  36. package/dist/runtime/runtimeObservation.js +297 -0
  37. package/dist/runtime/runtimeProjection.js +277 -0
  38. package/dist/runtime/sessionTerminationGuard.js +78 -22
  39. package/dist/runtime/tmuxAdapters.js +35 -0
  40. package/dist/scheduler/activeRoleRunDelivery.js +28 -13
  41. package/dist/scheduler/leaderWakeupProcessor.js +21 -2
  42. package/dist/scheduler/roleRunLiveness.js +2 -2
  43. package/dist/scheduler/roleRunStall.js +62 -114
  44. package/dist/storage/migration/productionRegistry.js +41 -0
  45. package/dist/storage/sqliteStore.js +3 -3
  46. package/dist/storage/storageVersions.js +1 -1
  47. package/dist/telemetry/sqliteTelemetryStore.js +0 -28
  48. package/dist/telemetry/telemetryCompaction.js +1 -0
  49. package/dist/telemetry/telemetryConfig.js +4 -5
  50. package/dist/tmux/tmuxManager.js +136 -22
  51. package/dist/web/assets/client/view.js +1 -1
  52. package/dist/web/tmuxWebTerminal.js +17 -12
  53. package/dist/web/webSnapshot.js +1 -1
  54. package/dist/worktree/managedWorkspace.js +14 -0
  55. package/i18n/README.zh-CN.md +7 -5
  56. package/package.json +1 -1
  57. package/dist/controller/claudeLifecycleHook.js +0 -203
  58. package/dist/controller/codexLifecycleHook.js +0 -108
  59. package/dist/controller/providerHookRunFence.js +0 -156
  60. package/dist/lifecycle/providerLifecycleMapping.js +0 -190
  61. package/dist/telemetry/telemetryRouter.js +0 -32
@@ -0,0 +1,297 @@
1
+ import { requireDriverId } from "./agentDriver.js";
2
+ export const RUNTIME_OBSERVATION_TASK_EVENT = "runtime.observation";
3
+ const KINDS = [
4
+ "host.observed",
5
+ "session.started",
6
+ "session.ready",
7
+ "session.ended",
8
+ "session.failed",
9
+ "turn.accepted",
10
+ "turn.waiting",
11
+ "turn.completed",
12
+ "turn.failed",
13
+ "turn.cancelled",
14
+ "operation.started",
15
+ "operation.completed",
16
+ "operation.failed",
17
+ "activity.observed",
18
+ "observer.health"
19
+ ];
20
+ const AUTHORITIES = [
21
+ "controller",
22
+ "transport",
23
+ "provider-structured",
24
+ "driver-inferred",
25
+ "host",
26
+ "diagnostic"
27
+ ];
28
+ const RUN_SCOPED = new Set([
29
+ "turn.accepted",
30
+ "turn.waiting",
31
+ "turn.completed",
32
+ "turn.failed",
33
+ "turn.cancelled",
34
+ "operation.started",
35
+ "operation.completed",
36
+ "operation.failed",
37
+ "activity.observed",
38
+ "observer.health"
39
+ ]);
40
+ const PROVIDER_STATE = new Set([
41
+ "session.started",
42
+ "session.ready",
43
+ "session.ended",
44
+ "session.failed",
45
+ "turn.accepted",
46
+ "turn.waiting",
47
+ "turn.completed",
48
+ "turn.failed",
49
+ "turn.cancelled",
50
+ "operation.started",
51
+ "operation.completed",
52
+ "operation.failed"
53
+ ]);
54
+ export function createRuntimeObservation(input) {
55
+ if (input.schemaVersion !== 1)
56
+ throw new Error("Runtime observation schemaVersion must be 1.");
57
+ if (!KINDS.includes(input.kind))
58
+ throw new Error("Runtime observation kind is invalid.");
59
+ if (!AUTHORITIES.includes(input.authority))
60
+ throw new Error("Runtime observation authority is invalid.");
61
+ if (input.kind === "host.observed" && input.authority !== "host" && input.authority !== "controller") {
62
+ throw new Error("host.observed requires host or controller authority.");
63
+ }
64
+ if (PROVIDER_STATE.has(input.kind)
65
+ && input.authority !== "provider-structured"
66
+ && input.authority !== "controller") {
67
+ throw new Error(`${input.kind} requires provider-structured or controller authority.`);
68
+ }
69
+ const fence = normalizeFence(input.fence);
70
+ if (RUN_SCOPED.has(input.kind) && fence.runId === undefined) {
71
+ throw new Error(`${input.kind} requires runId.`);
72
+ }
73
+ if (RUN_SCOPED.has(input.kind) && fence.nativeSessionId === undefined) {
74
+ throw new Error(`${input.kind} requires nativeSessionId.`);
75
+ }
76
+ if (RUN_SCOPED.has(input.kind) && fence.nativeTurnId === undefined) {
77
+ throw new Error(`${input.kind} requires nativeTurnId.`);
78
+ }
79
+ const payload = normalizePayload(input.kind, input.payload);
80
+ const sequence = input.sequence;
81
+ if (sequence !== undefined && (!Number.isSafeInteger(sequence) || sequence < 0)) {
82
+ throw new Error("Runtime observation sequence must be a non-negative safe integer.");
83
+ }
84
+ const ordinal = input.ordinal;
85
+ if (ordinal !== undefined && (!Number.isSafeInteger(ordinal) || ordinal < 0)) {
86
+ throw new Error("Runtime observation ordinal must be a non-negative safe integer.");
87
+ }
88
+ return Object.freeze({
89
+ schemaVersion: 1,
90
+ eventId: requireIdentity(input.eventId, "Runtime observation event id"),
91
+ kind: input.kind,
92
+ authority: input.authority,
93
+ receivedAt: requireTimestamp(input.receivedAt, "Runtime observation receivedAt"),
94
+ ...(input.observedAt === undefined
95
+ ? {}
96
+ : { observedAt: requireTimestamp(input.observedAt, "Runtime observation observedAt") }),
97
+ ...(sequence === undefined ? {} : { sequence }),
98
+ ...(ordinal === undefined ? {} : { ordinal }),
99
+ fence,
100
+ payload
101
+ });
102
+ }
103
+ export function runtimeObservationFenceMatches(expected, actual) {
104
+ for (const field of [
105
+ "taskId",
106
+ "roleName",
107
+ "runId",
108
+ "agentId",
109
+ "driverId",
110
+ "launchId",
111
+ "sessionGenerationId",
112
+ "nativeSessionId",
113
+ "nativeTurnId",
114
+ "receiptId"
115
+ ]) {
116
+ if (expected[field] !== actual[field])
117
+ return false;
118
+ }
119
+ return true;
120
+ }
121
+ /** Compact TaskEvent payload used for durable state-boundary observations. */
122
+ export function runtimeObservationTaskEventPayload(input) {
123
+ const observation = createRuntimeObservation(input);
124
+ return Object.freeze({
125
+ eventId: observation.eventId,
126
+ roleName: observation.fence.roleName,
127
+ agentId: observation.fence.agentId,
128
+ driverId: observation.fence.driverId,
129
+ launchId: observation.fence.launchId,
130
+ ...(observation.fence.taskId === undefined ? {} : { taskId: observation.fence.taskId }),
131
+ ...(observation.fence.runId === undefined ? {} : { runId: observation.fence.runId }),
132
+ ...(observation.fence.nativeSessionId === undefined
133
+ ? {}
134
+ : { nativeSessionId: observation.fence.nativeSessionId }),
135
+ kind: observation.kind,
136
+ receivedAt: observation.receivedAt,
137
+ observation: JSON.stringify(observation)
138
+ });
139
+ }
140
+ export function runtimeObservationFromTaskEvent(event) {
141
+ if (event.type !== RUNTIME_OBSERVATION_TASK_EVENT)
142
+ return null;
143
+ try {
144
+ const parsed = JSON.parse(event.payload.observation ?? "");
145
+ return createRuntimeObservation(parsed);
146
+ }
147
+ catch {
148
+ return null;
149
+ }
150
+ }
151
+ function normalizeFence(input) {
152
+ return Object.freeze({
153
+ ...(input.taskId === undefined ? {} : { taskId: requireIdentity(input.taskId, "Task id") }),
154
+ roleName: requireIdentity(input.roleName, "Role name"),
155
+ ...(input.runId === undefined ? {} : { runId: requireIdentity(input.runId, "Run id") }),
156
+ agentId: requireIdentity(input.agentId, "Agent id"),
157
+ driverId: requireDriverId(input.driverId),
158
+ launchId: requireIdentity(input.launchId, "Launch id"),
159
+ sessionGenerationId: requireIdentity(input.sessionGenerationId, "Session generation id"),
160
+ ...(input.nativeSessionId === undefined
161
+ ? {}
162
+ : { nativeSessionId: requireIdentity(input.nativeSessionId, "Native Session id") }),
163
+ ...(input.nativeTurnId === undefined
164
+ ? {}
165
+ : { nativeTurnId: requireIdentity(input.nativeTurnId, "Native Turn id") }),
166
+ ...(input.receiptId === undefined
167
+ ? {}
168
+ : { receiptId: requireIdentity(input.receiptId, "Receipt id") })
169
+ });
170
+ }
171
+ function normalizePayload(kind, input) {
172
+ if (input === null || typeof input !== "object" || Array.isArray(input)) {
173
+ throw new Error("Runtime observation payload must be an object.");
174
+ }
175
+ if (kind === "host.observed" && typeof input.alive !== "boolean") {
176
+ throw new Error("host.observed requires payload.alive.");
177
+ }
178
+ if (kind === "turn.waiting"
179
+ && input.reason !== "user"
180
+ && input.reason !== "permission"
181
+ && input.reason !== "external") {
182
+ throw new Error("turn.waiting requires a supported reason.");
183
+ }
184
+ if (kind === "turn.waiting")
185
+ requireIdentity(input.waitId, "Runtime wait id");
186
+ if (kind.startsWith("operation.")) {
187
+ requireIdentity(input.operationId, "Runtime operation id");
188
+ if (input.operation !== "model" && input.operation !== "tool" && input.operation !== "subagent") {
189
+ throw new Error("Runtime operation kind is invalid.");
190
+ }
191
+ }
192
+ if (kind === "activity.observed") {
193
+ if (!["model", "tool", "subagent", "provider", "resource"].includes(input.activity ?? "")) {
194
+ throw new Error("activity.observed requires an activity kind.");
195
+ }
196
+ if (input.usage !== undefined)
197
+ validateUsage(input.usage);
198
+ }
199
+ if (kind === "observer.health") {
200
+ requireIdentity(input.sourceId, "Runtime observer source id");
201
+ if (!['healthy', 'degraded', 'unavailable'].includes(input.observerStatus ?? "")) {
202
+ throw new Error("observer.health requires a supported status.");
203
+ }
204
+ }
205
+ const observerSource = input.observerSource === undefined
206
+ ? undefined
207
+ : normalizeObserverSource(input.observerSource);
208
+ if (kind === "turn.failed" && input.failure === undefined) {
209
+ throw new Error("turn.failed requires normalized failure evidence.");
210
+ }
211
+ const failure = input.failure === undefined
212
+ ? undefined
213
+ : normalizeFailure(input.failure);
214
+ return Object.freeze({
215
+ ...(input.alive === undefined ? {} : { alive: input.alive }),
216
+ ...(input.reason === undefined ? {} : { reason: input.reason }),
217
+ ...(input.waitId === undefined ? {} : { waitId: requireIdentity(input.waitId, "Runtime wait id") }),
218
+ ...(input.operationId === undefined
219
+ ? {}
220
+ : { operationId: requireIdentity(input.operationId, "Runtime operation id") }),
221
+ ...(input.operation === undefined ? {} : { operation: input.operation }),
222
+ ...(input.activity === undefined ? {} : { activity: input.activity }),
223
+ ...(input.activityId === undefined
224
+ ? {}
225
+ : { activityId: requireIdentity(input.activityId, "Runtime activity id") }),
226
+ ...(input.usage === undefined ? {} : { usage: Object.freeze({ ...input.usage }) }),
227
+ ...(observerSource === undefined ? {} : { observerSource }),
228
+ ...(input.sourceId === undefined
229
+ ? {}
230
+ : { sourceId: requireIdentity(input.sourceId, "Runtime observer source id") }),
231
+ ...(input.observerStatus === undefined ? {} : { observerStatus: input.observerStatus }),
232
+ ...(input.observerDetail === undefined
233
+ ? {}
234
+ : { observerDetail: requireText(input.observerDetail, "Runtime observer detail") }),
235
+ ...(failure === undefined ? {} : { failure }),
236
+ ...(input.summary === undefined ? {} : { summary: requireText(input.summary, "Runtime summary") })
237
+ });
238
+ }
239
+ function normalizeObserverSource(input) {
240
+ if (input === null || typeof input !== "object" || Array.isArray(input)) {
241
+ throw new Error("Runtime observer source must be an object.");
242
+ }
243
+ if (input.schemaVersion !== 1 || input.transport !== "append-only-jsonl") {
244
+ throw new Error("Runtime observer source is invalid.");
245
+ }
246
+ return Object.freeze({
247
+ schemaVersion: 1,
248
+ sourceId: requireIdentity(input.sourceId, "Runtime observer source id"),
249
+ transport: "append-only-jsonl",
250
+ locator: requireText(input.locator, "Runtime observer locator")
251
+ });
252
+ }
253
+ function normalizeFailure(input) {
254
+ if (input === null || typeof input !== "object" || Array.isArray(input)) {
255
+ throw new Error("Runtime failure evidence must be an object.");
256
+ }
257
+ return Object.freeze({
258
+ code: requireText(input.code, "Runtime failure code"),
259
+ ...(input.details === undefined
260
+ ? {}
261
+ : { details: requireText(input.details, "Runtime failure details") }),
262
+ ...(input.lastOutput === undefined
263
+ ? {}
264
+ : { lastOutput: requireText(input.lastOutput, "Runtime failure last output") })
265
+ });
266
+ }
267
+ function validateUsage(input) {
268
+ for (const [name, value] of Object.entries(input)) {
269
+ if (!Number.isSafeInteger(value) || value < 0) {
270
+ throw new Error(`Runtime usage ${name} must be a non-negative safe integer.`);
271
+ }
272
+ }
273
+ if (input.inputTokens === undefined || input.outputTokens === undefined) {
274
+ throw new Error("Runtime usage requires inputTokens and outputTokens.");
275
+ }
276
+ }
277
+ function requireTimestamp(value, label) {
278
+ const timestamp = requireText(value, label);
279
+ if (!Number.isFinite(Date.parse(timestamp)))
280
+ throw new Error(`${label} is invalid.`);
281
+ return timestamp;
282
+ }
283
+ function requireIdentity(value, label) {
284
+ const identity = requireText(value, label);
285
+ if (identity.includes("/../") || identity === "__proto__")
286
+ throw new Error(`${label} is invalid.`);
287
+ return identity;
288
+ }
289
+ function requireText(value, label) {
290
+ if (typeof value !== "string" || value.includes("\0"))
291
+ throw new Error(`${label} is invalid.`);
292
+ const normalized = value.trim();
293
+ if (normalized.length === 0 || normalized.length > 32 * 1024) {
294
+ throw new Error(`${label} is invalid.`);
295
+ }
296
+ return normalized;
297
+ }
@@ -0,0 +1,277 @@
1
+ import { createRuntimeObservation, runtimeObservationFenceMatches, runtimeObservationFromTaskEvent } from "./runtimeObservation.js";
2
+ export function createRuntimeProjection(fence, createdAt) {
3
+ const timestamp = requireTimestamp(createdAt);
4
+ return Object.freeze({
5
+ fence: Object.freeze({ ...fence }),
6
+ host: "unknown",
7
+ session: "unknown",
8
+ turn: "none",
9
+ operations: Object.freeze({}),
10
+ activity: Object.freeze({ kind: "none" }),
11
+ observer: Object.freeze({ status: "unknown" }),
12
+ stateSince: timestamp,
13
+ workflow: Object.freeze({
14
+ lastSemanticProgressAt: timestamp,
15
+ completed: false,
16
+ blocked: false
17
+ })
18
+ });
19
+ }
20
+ export function projectRuntimeTaskEvents(fence, createdAt, events) {
21
+ const observations = events
22
+ .map(runtimeObservationFromTaskEvent)
23
+ .filter((event) => (event !== null && runtimeObservationFenceMatches(fence, event.fence)))
24
+ .sort((left, right) => (left.receivedAt.localeCompare(right.receivedAt)
25
+ || (left.sequence ?? -1) - (right.sequence ?? -1)
26
+ || (left.ordinal ?? -1) - (right.ordinal ?? -1)
27
+ || left.eventId.localeCompare(right.eventId)));
28
+ return observations.reduce(projectRuntimeObservation, createRuntimeProjection(fence, createdAt));
29
+ }
30
+ export function projectRuntimeObservation(current, raw) {
31
+ const event = createRuntimeObservation(raw);
32
+ if (!runtimeObservationFenceMatches(current.fence, event.fence)) {
33
+ throw new Error("Runtime observation fence does not match the projection.");
34
+ }
35
+ const at = event.receivedAt;
36
+ switch (event.kind) {
37
+ case "host.observed":
38
+ return next(current, {
39
+ host: event.payload.alive ? "alive" : "exited",
40
+ stateSince: at
41
+ });
42
+ case "session.started":
43
+ return next(current, { session: "started", stateSince: at });
44
+ case "session.ready":
45
+ return next(current, { session: "ready", stateSince: at });
46
+ case "session.ended":
47
+ return next(current, {
48
+ session: "ended",
49
+ turn: terminalTurn(current.turn),
50
+ operations: Object.freeze({}),
51
+ stateSince: at
52
+ });
53
+ case "session.failed":
54
+ return next(current, {
55
+ session: "failed",
56
+ turn: current.turn === "none" ? "failed" : terminalTurn(current.turn, "failed"),
57
+ operations: Object.freeze({}),
58
+ stateSince: at
59
+ });
60
+ case "turn.accepted":
61
+ return withActivity(next(current, {
62
+ session: "active",
63
+ turn: "accepted",
64
+ waitingReason: undefined,
65
+ waitId: undefined,
66
+ stateSince: at
67
+ }), "provider", at);
68
+ case "turn.waiting":
69
+ return next(current, {
70
+ session: "waiting",
71
+ turn: "waiting",
72
+ waitingReason: event.payload.reason,
73
+ waitId: event.payload.waitId,
74
+ stateSince: at
75
+ });
76
+ case "turn.completed":
77
+ return withActivity(next(current, {
78
+ session: "ready",
79
+ turn: "completed",
80
+ waitingReason: undefined,
81
+ waitId: undefined,
82
+ operations: Object.freeze({}),
83
+ stateSince: at
84
+ }), "provider", at);
85
+ case "turn.failed":
86
+ return withActivity(next(current, {
87
+ session: "ready",
88
+ turn: "failed",
89
+ waitingReason: undefined,
90
+ waitId: undefined,
91
+ operations: Object.freeze({}),
92
+ stateSince: at
93
+ }), "provider", at);
94
+ case "turn.cancelled":
95
+ return withActivity(next(current, {
96
+ session: "ready",
97
+ turn: "cancelled",
98
+ waitingReason: undefined,
99
+ waitId: undefined,
100
+ operations: Object.freeze({}),
101
+ stateSince: at
102
+ }), "provider", at);
103
+ case "operation.started": {
104
+ const operationId = event.payload.operationId;
105
+ const operation = event.payload.operation;
106
+ return withActivity(next(current, {
107
+ session: "active",
108
+ turn: current.turn === "waiting" ? "accepted" : current.turn,
109
+ waitingReason: undefined,
110
+ waitId: undefined,
111
+ operations: Object.freeze({
112
+ ...current.operations,
113
+ [operationId]: Object.freeze({ kind: operation, startedAt: at })
114
+ }),
115
+ stateSince: at
116
+ }), operation, at);
117
+ }
118
+ case "operation.completed":
119
+ case "operation.failed": {
120
+ const operations = { ...current.operations };
121
+ delete operations[event.payload.operationId];
122
+ return withActivity(next(current, {
123
+ session: "active",
124
+ turn: current.turn === "waiting" ? "accepted" : current.turn,
125
+ waitingReason: undefined,
126
+ waitId: undefined,
127
+ operations: Object.freeze(operations),
128
+ stateSince: at
129
+ }), event.payload.operation, at);
130
+ }
131
+ case "activity.observed": {
132
+ const usage = event.payload.usage;
133
+ if (usage !== undefined && !usageAdvanced(current.usage, usage)) {
134
+ return next(current, { usage: Object.freeze({ ...usage }) });
135
+ }
136
+ return withActivity(next(current, {
137
+ ...(usage === undefined ? {} : { usage: Object.freeze({ ...usage }) }),
138
+ session: current.turn === "waiting" ? "active" : current.session,
139
+ turn: current.turn === "waiting" ? "accepted" : current.turn,
140
+ waitingReason: undefined,
141
+ waitId: undefined
142
+ }), event.payload.activity, at);
143
+ }
144
+ case "observer.health":
145
+ return next(current, {
146
+ observer: Object.freeze({
147
+ status: event.payload.observerStatus,
148
+ sourceId: event.payload.sourceId,
149
+ ...(event.payload.observerDetail === undefined
150
+ ? {}
151
+ : { detail: event.payload.observerDetail })
152
+ })
153
+ });
154
+ default:
155
+ return current;
156
+ }
157
+ }
158
+ export function recordRuntimeSemanticProgress(current, progressAt) {
159
+ const at = requireTimestamp(progressAt);
160
+ if (Date.parse(at) <= Date.parse(current.workflow.lastSemanticProgressAt))
161
+ return current;
162
+ return next(current, {
163
+ workflow: Object.freeze({ ...current.workflow, lastSemanticProgressAt: at })
164
+ });
165
+ }
166
+ export function completeRuntimeWorkflow(current, completedAt) {
167
+ const at = requireTimestamp(completedAt);
168
+ return next(current, {
169
+ workflow: Object.freeze({
170
+ lastSemanticProgressAt: maxTimestamp(current.workflow.lastSemanticProgressAt, at),
171
+ completed: true,
172
+ blocked: false
173
+ })
174
+ });
175
+ }
176
+ export function runtimeDisplayStatus(current) {
177
+ if (current.session === "failed")
178
+ return "broken";
179
+ if (current.host === "exited" || current.session === "ended")
180
+ return "stopped";
181
+ const operation = dominantOperation(current.operations);
182
+ if (operation !== null)
183
+ return `${operation}-active`;
184
+ if (current.turn === "waiting")
185
+ return `waiting-${current.waitingReason ?? "external"}`;
186
+ if (current.session === "ready" || current.turn === "completed"
187
+ || current.turn === "failed" || current.turn === "cancelled")
188
+ return "ready";
189
+ if (current.turn === "accepted" || current.session === "active") {
190
+ return current.activity.kind === "model" ? "model-active" : "active-quiet";
191
+ }
192
+ if (current.session === "started")
193
+ return "awaiting-provider-acceptance";
194
+ if (current.host === "alive")
195
+ return "runtime-unobservable";
196
+ return "starting";
197
+ }
198
+ export function evaluateRuntimeAttention(current, now, policy) {
199
+ const display = runtimeDisplayStatus(current);
200
+ const runtimeIdleMs = current.lastRuntimeActivityAt === undefined
201
+ ? Number.POSITIVE_INFINITY
202
+ : now.getTime() - Date.parse(current.lastRuntimeActivityAt);
203
+ const semanticIdleMs = now.getTime() - Date.parse(current.workflow.lastSemanticProgressAt);
204
+ const runtime = display === "broken"
205
+ ? "broken"
206
+ : display === "stopped"
207
+ ? "stopped"
208
+ : display === "runtime-unobservable"
209
+ ? "unobservable"
210
+ : display.startsWith("waiting-")
211
+ ? "waiting"
212
+ : runtimeIdleMs <= policy.runtimeSilenceMs
213
+ ? "healthy"
214
+ : Object.keys(current.operations).length > 0
215
+ ? "active-operation-quiet"
216
+ : "quiet";
217
+ return Object.freeze({
218
+ runtime,
219
+ workflow: current.workflow.completed
220
+ ? "completed"
221
+ : semanticIdleMs > policy.semanticSilenceMs
222
+ ? "not-progressing"
223
+ : "progressing"
224
+ });
225
+ }
226
+ function withActivity(current, kind, at) {
227
+ return next(current, {
228
+ activity: Object.freeze({ kind, observedAt: at }),
229
+ lastRuntimeActivityAt: maxTimestamp(current.lastRuntimeActivityAt, at)
230
+ });
231
+ }
232
+ function next(current, patch) {
233
+ const copy = { ...current, ...patch };
234
+ if (patch.waitingReason === undefined && Object.hasOwn(patch, "waitingReason")) {
235
+ delete copy.waitingReason;
236
+ }
237
+ if (patch.waitId === undefined && Object.hasOwn(patch, "waitId"))
238
+ delete copy.waitId;
239
+ return Object.freeze(copy);
240
+ }
241
+ function usageAdvanced(previous, current) {
242
+ // A first cumulative snapshot may contain history from a resumed native
243
+ // Session. It establishes the generation baseline but cannot prove that
244
+ // tokens were consumed during the current observation window.
245
+ if (previous === undefined)
246
+ return false;
247
+ return usageTotal(current) > usageTotal(previous);
248
+ }
249
+ function usageTotal(usage) {
250
+ // cachedInputTokens and reasoningTokens are breakdowns of the normalized
251
+ // input/output totals, not additional usage.
252
+ return usage.inputTokens + usage.outputTokens;
253
+ }
254
+ function dominantOperation(operations) {
255
+ const kinds = Object.values(operations).map(({ kind }) => kind);
256
+ if (kinds.includes("subagent"))
257
+ return "subagent";
258
+ if (kinds.includes("tool"))
259
+ return "tool";
260
+ if (kinds.includes("model"))
261
+ return "model";
262
+ return null;
263
+ }
264
+ function terminalTurn(current, fallback = "cancelled") {
265
+ return current === "completed" || current === "failed" || current === "cancelled"
266
+ ? current
267
+ : fallback;
268
+ }
269
+ function maxTimestamp(left, right) {
270
+ return left === undefined || Date.parse(right) > Date.parse(left) ? right : left;
271
+ }
272
+ function requireTimestamp(value) {
273
+ if (typeof value !== "string" || !Number.isFinite(Date.parse(value))) {
274
+ throw new Error("Runtime projection timestamp is invalid.");
275
+ }
276
+ return value;
277
+ }