@zq-silk/yui 0.11.3 → 0.12.1
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 +23 -4
- package/README.md +44 -22
- package/dist/cli/commandCatalog.js +13 -10
- package/dist/cli/interactionPolicy.js +5 -6
- package/dist/cli/updateCommand.js +3 -1
- package/dist/cli/updateOrchestrator.js +173 -28
- package/dist/cli/updatePorts.js +137 -8
- package/dist/cli/upgradeCommand.js +19 -9
- package/dist/cli.js +156 -57
- package/dist/commands/configCommands.js +13 -46
- package/dist/commands/executionAuditCommands.js +3 -2
- package/dist/commands/jobCommands.js +4 -10
- package/dist/commands/taskCommands.js +234 -200
- package/dist/commands/taskCompletionGate.js +29 -54
- package/dist/commands/taskContextCommand.js +61 -13
- package/dist/commands/taskInputCommands.js +8 -5
- package/dist/commands/taskNextActionCommand.js +56 -6
- package/dist/commands/taskOverviewCommand.js +17 -31
- package/dist/commands/taskRoleRuntimeStatus.js +55 -7
- package/dist/commands/taskWorkspaceCommands.js +5 -2
- package/dist/config/configCatalog.js +3 -3
- package/dist/config/yuiConfig.js +3 -7
- package/dist/context/wakeNotification.js +20 -5
- package/dist/controller/agentRuntimeObserver.js +247 -51
- package/dist/controller/clientRuntime.js +39 -3
- package/dist/controller/controller.js +31 -27
- package/dist/controller/fileSchedulerStoreAdapter.js +390 -332
- package/dist/controller/runtime.js +58 -3
- package/dist/controller/runtimeEventInbox.js +17 -7
- package/dist/controller/runtimeEventProcessor.js +12 -19
- package/dist/controller/runtimeHookRunFence.js +19 -4
- package/dist/controller/runtimeObservationHook.js +45 -0
- package/dist/controller/structuredProviderObservation.js +20 -3
- package/dist/core/controllerClient.js +20 -2
- package/dist/core/controllerServer.js +1 -0
- package/dist/execution/resourceBroker.js +5 -4
- package/dist/executor/agentExecutor.js +52 -50
- package/dist/executor/effectiveLaunch.js +8 -48
- package/dist/executor/fileRoleLaunchPlanner.js +129 -45
- package/dist/executor/workspacePreflightClassification.js +23 -2
- package/dist/interaction/operatorPresentation.js +33 -89
- package/dist/lifecycle/exactRunTerminalization.js +69 -5
- package/dist/observability/executionAudit.js +5 -0
- package/dist/observability/orchestrationMetrics.js +8 -3
- package/dist/profile/agentProfile.js +1 -1
- package/dist/release/cliHomeReleaseFence.js +123 -0
- package/dist/release/runtimeRelease.js +20 -0
- package/dist/repository/taskWorkspacePreparer.js +176 -60
- package/dist/resources/sqliteResourceRegistry.js +1 -1
- package/dist/review/deltaRecheck.js +12 -51
- package/dist/review/reviewAcceptance.js +26 -0
- package/dist/review/reviewConfig.js +0 -31
- package/dist/review/reviewDecision.js +113 -0
- package/dist/review/reviewOutcomeClassifier.js +1 -1
- package/dist/review/reviewRound.js +1 -1
- package/dist/review/reviewerAvailability.js +69 -0
- package/dist/run/recoveryProjection.js +45 -6
- package/dist/runtime/agentDriverObservation.js +24 -10
- package/dist/runtime/agentHost.js +159 -85
- package/dist/runtime/builtinAgentDrivers.js +4 -3
- package/dist/runtime/builtinTranscriptObserver.js +301 -64
- package/dist/runtime/builtinTranscriptUsage.js +9 -7
- package/dist/runtime/conversationSwitch.js +277 -0
- package/dist/runtime/index.js +2 -1
- package/dist/runtime/launchBroker.js +12 -0
- package/dist/runtime/processExitOutbox.js +88 -0
- package/dist/runtime/providerRuntimeIdentity.js +29 -1
- package/dist/runtime/runtimeHealthPolicy.js +5 -5
- package/dist/runtime/runtimeObservation.js +28 -0
- package/dist/runtime/runtimeProjection.js +22 -32
- package/dist/runtime/sessionTokenMetrics.js +181 -0
- package/dist/runtime/structuredProviderHost.js +7 -1
- package/dist/runtime/tmuxAdapters.js +4 -1
- package/dist/scheduler/activeRoleRunDelivery.js +34 -199
- package/dist/scheduler/activeTaskProgress.js +7 -6
- package/dist/scheduler/leaderWakeupProcessor.js +42 -138
- package/dist/scheduler/operatorEvent.js +34 -0
- package/dist/scheduler/operatorInputNotificationProcessor.js +54 -94
- package/dist/scheduler/roleRunStall.js +53 -31
- package/dist/scheduler/taskExecutionProjection.js +97 -76
- package/dist/scheduler/taskObservabilityProjection.js +10 -11
- package/dist/storage/migration/productionRegistry.js +379 -0
- package/dist/storage/sqliteSchema.js +66 -23
- package/dist/storage/sqliteStore.js +46 -23
- package/dist/storage/storeRpc.js +0 -1
- package/dist/storage/taskStore.js +15 -30
- package/dist/storage/upgrade/homeClassification.js +52 -0
- package/dist/storage/upgrade/offlineUpgradeInventory.js +145 -7
- package/dist/storage/upgrade/recordVersions.js +3 -2
- package/dist/storage/upgrade/sqliteMigrationTarget.js +30 -8
- package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +23 -11
- package/dist/storage/upgrade/sqliteStateMigration.js +66 -8
- package/dist/storage/upgrade/upgradeOrchestrator.js +333 -12
- package/dist/task/completionReadiness.js +10 -6
- package/dist/task/nextAction.js +82 -58
- package/dist/telemetry/sqliteTelemetryStore.js +1 -1
- package/dist/web/assets/client/components.js +13 -3
- package/dist/web/assets/client/i18n.js +6 -2
- package/dist/web/webSnapshot.js +7 -1
- package/i18n/README.zh-CN.md +2 -2
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +60 -20
- package/skills/yui-operator/SKILL.md +13 -4
- package/skills/yui-reviewer/SKILL.md +35 -11
- package/dist/context/sessionContextBudget.js +0 -71
- package/dist/lifecycle/contextBudgetRollover.js +0 -81
- package/dist/lifecycle/taskRoleSessionReset.js +0 -126
- package/dist/scheduler/operatorNotification.js +0 -59
|
@@ -8,6 +8,8 @@ import { latestRunDurableProgressAt } from "../scheduler/roleRunStall.js";
|
|
|
8
8
|
import { resolveRuntimeHealth } from "../config/yuiConfig.js";
|
|
9
9
|
import { builtinDriverIdForAdapter } from "../runtime/builtinAgentDrivers.js";
|
|
10
10
|
import { operationalTaskRecords } from "../task/taskRecordRetirement.js";
|
|
11
|
+
import { projectSessionTokenMetrics, resolveSessionTokenIdentity } from "../runtime/sessionTokenMetrics.js";
|
|
12
|
+
import { freshConversationLaunchBlockers, projectConversationSwitch } from "../runtime/conversationSwitch.js";
|
|
11
13
|
export function inspectTaskRoleRuntimeStatuses(taskId, roles, store, panes, now = new Date()) {
|
|
12
14
|
const taskOpenInputRequestCount = store.listInputRequests(taskId)
|
|
13
15
|
.filter((request) => request.status === "open").length;
|
|
@@ -65,6 +67,12 @@ export function renderTaskRoleRuntimeStatus(status) {
|
|
|
65
67
|
? ""
|
|
66
68
|
: ` (${status.runtime.observerDetail})`}`
|
|
67
69
|
].filter((value) => value !== undefined).join("; ");
|
|
70
|
+
const sessionTokens = [
|
|
71
|
+
`total=${sessionCumulativeTokenLabel(status.sessionTokens)}`,
|
|
72
|
+
`max-request-input=${status.sessionTokens.maximumRequestInput.status === "observed"
|
|
73
|
+
? status.sessionTokens.maximumRequestInput.inputTokens
|
|
74
|
+
: "unobserved"}`
|
|
75
|
+
].join("; ");
|
|
68
76
|
return [
|
|
69
77
|
`Task Role status: ${status.taskId}/${status.roleName}`,
|
|
70
78
|
"",
|
|
@@ -86,9 +94,15 @@ export function renderTaskRoleRuntimeStatus(status) {
|
|
|
86
94
|
? `needs-attention (${status.stall.kind ?? "workflow-not-progressing"}; no workflow progress since ${status.stall.progressAt ?? "unknown"})`
|
|
87
95
|
: "none"}`,
|
|
88
96
|
` Native session ${nativeSession}`,
|
|
97
|
+
` Session switch ${status.conversationSwitch === null
|
|
98
|
+
? "none"
|
|
99
|
+
: `${status.conversationSwitch.status} (${status.conversationSwitch.requestId}; ${status.conversationSwitch.reason})`}`,
|
|
89
100
|
` Agent runtime ${runtime}`,
|
|
101
|
+
` Session tokens ${sessionTokens}`,
|
|
90
102
|
` Runtime cleanup ${status.runtimeCleanupPending ? "pending" : "none"}`,
|
|
91
|
-
` Fresh launch ${status.freshLaunchAllowed
|
|
103
|
+
` Fresh launch ${status.freshLaunchAllowed
|
|
104
|
+
? "allowed"
|
|
105
|
+
: `blocked (${status.freshLaunchBlockers.join(", ")})`}`,
|
|
92
106
|
` tmux pane ${tmux}`,
|
|
93
107
|
` Workspace ${status.workspace.managed ? status.workspace.root : status.workspace.path}`,
|
|
94
108
|
...workspaceDetails
|
|
@@ -109,19 +123,30 @@ export function taskRoleLastRunLabel(status) {
|
|
|
109
123
|
}
|
|
110
124
|
export function taskRoleNativeSessionLabel(status) {
|
|
111
125
|
if (status.runtimeCleanupPending && status.nativeSession === null)
|
|
112
|
-
return "
|
|
126
|
+
return "unbound (cleanup-pending)";
|
|
113
127
|
return status.nativeSession?.status ?? "unbound";
|
|
114
128
|
}
|
|
115
129
|
export function inspectTaskRoleSessionRecovery(taskId, roleName, store) {
|
|
116
130
|
const sessions = store.getTaskRoleSessionSet(taskId, roleName);
|
|
117
131
|
const target = runtimeLifecycleTarget({ scope: "task", taskId, roleName });
|
|
118
132
|
const runtimeMailbox = store.getWorkMailbox(target);
|
|
133
|
+
const roleMailbox = store.getWorkMailbox({ kind: "role", taskId, roleName });
|
|
134
|
+
const switchBlockers = freshConversationLaunchBlockers({
|
|
135
|
+
sessions,
|
|
136
|
+
events: store.listEvents(taskId),
|
|
137
|
+
mailbox: roleMailbox,
|
|
138
|
+
roleName
|
|
139
|
+
});
|
|
140
|
+
const freshLaunchBlockers = [
|
|
141
|
+
...switchBlockers,
|
|
142
|
+
...(hasRuntimeLifecycleWork(runtimeMailbox) ? ["runtime-lifecycle-busy"] : [])
|
|
143
|
+
];
|
|
119
144
|
return {
|
|
120
145
|
taskId,
|
|
121
146
|
roleName,
|
|
122
147
|
runtimeCleanupPending: hasRuntimeCleanupObligation(runtimeMailbox),
|
|
123
|
-
freshLaunchAllowed:
|
|
124
|
-
|
|
148
|
+
freshLaunchAllowed: freshLaunchBlockers.length === 0,
|
|
149
|
+
freshLaunchBlockers
|
|
125
150
|
};
|
|
126
151
|
}
|
|
127
152
|
export function taskRoleOpenInputLabel(status) {
|
|
@@ -178,6 +203,10 @@ function inspectTaskRoleRuntimeStatus(taskId, role, store, pane, openInputReques
|
|
|
178
203
|
? { managed: false, path: role.workspace }
|
|
179
204
|
: { ...managedWorkspace, managed: true };
|
|
180
205
|
const events = store.listEvents(taskId);
|
|
206
|
+
const conversationSwitch = projectConversationSwitch(events, role.name, sessions);
|
|
207
|
+
const sessionTokens = projectSessionTokenMetrics(events, resolveSessionTokenIdentity(nativeSession === null
|
|
208
|
+
? null
|
|
209
|
+
: { taskId, roleName: role.name, ...nativeSession }));
|
|
181
210
|
const runtime = projectTaskRoleRuntime(activeRun, nativeSession, tmux, events, store.getWorkMailbox({ kind: "role", taskId, roleName: role.name }), store, taskId, role.name, now);
|
|
182
211
|
const stalled = activeRun !== null && isRoleRunStalled(events, activeRun.id);
|
|
183
212
|
const stallProgressAt = activeRun === null
|
|
@@ -213,13 +242,29 @@ function inspectTaskRoleRuntimeStatus(taskId, role, store, pane, openInputReques
|
|
|
213
242
|
nativeSession,
|
|
214
243
|
tmux,
|
|
215
244
|
workspace,
|
|
245
|
+
sessionTokens,
|
|
246
|
+
conversationSwitch,
|
|
216
247
|
runtime,
|
|
217
248
|
stall
|
|
218
249
|
};
|
|
219
250
|
}
|
|
251
|
+
function sessionCumulativeTokenLabel(metrics) {
|
|
252
|
+
const total = metrics.cumulativeTotal;
|
|
253
|
+
if (total.status === "unobserved")
|
|
254
|
+
return "unobserved";
|
|
255
|
+
const breakdown = [
|
|
256
|
+
`input=${total.inputTokens}`,
|
|
257
|
+
`output=${total.outputTokens}`,
|
|
258
|
+
...(total.cachedInputTokens === undefined ? [] : [`cached-input=${total.cachedInputTokens}`]),
|
|
259
|
+
...(total.reasoningTokens === undefined ? [] : [`reasoning=${total.reasoningTokens}`])
|
|
260
|
+
];
|
|
261
|
+
return `${total.totalTokens} (${breakdown.join(", ")})`;
|
|
262
|
+
}
|
|
220
263
|
function latestStallKind(events, runId) {
|
|
221
264
|
const event = [...events]
|
|
222
|
-
.filter((candidate) => candidate.type === "run.stalled"
|
|
265
|
+
.filter((candidate) => candidate.type === "run.stalled"
|
|
266
|
+
&& candidate.payload.runId === runId
|
|
267
|
+
&& candidate.payload.status !== "diagnostic-only")
|
|
223
268
|
.sort((left, right) => Date.parse(right.createdAt) - Date.parse(left.createdAt))[0];
|
|
224
269
|
return event?.payload.kind === "delivery-stalled" || event?.payload.kind === "workflow-not-progressing"
|
|
225
270
|
? event.payload.kind
|
|
@@ -229,7 +274,7 @@ function calculateHealth(role, activeRun, lastRun, nativeSession, runtimeCleanup
|
|
|
229
274
|
if (runtimeCleanupPending && nativeSession === null) {
|
|
230
275
|
return {
|
|
231
276
|
health: "needs-attention",
|
|
232
|
-
healthReason: "the native Session
|
|
277
|
+
healthReason: "the native Session is unbound; verified runtime cleanup is pending"
|
|
233
278
|
};
|
|
234
279
|
}
|
|
235
280
|
if (role.status === "failed" || role.status === "exited") {
|
|
@@ -291,12 +336,15 @@ function calculateHealth(role, activeRun, lastRun, nativeSession, runtimeCleanup
|
|
|
291
336
|
case "broken":
|
|
292
337
|
return { health: "failed", healthReason: runtime.healthReason };
|
|
293
338
|
case "stopped":
|
|
294
|
-
case "diagnostic-needed":
|
|
295
339
|
case "ready":
|
|
296
340
|
case "awaiting-provider-acceptance":
|
|
297
341
|
case "runtime-unobservable":
|
|
298
342
|
case "starting":
|
|
299
343
|
return { health: "needs-attention", healthReason: runtime.healthReason };
|
|
344
|
+
case "diagnostic-needed":
|
|
345
|
+
return runtime.observerStatus === "degraded" || runtime.observerStatus === "unavailable"
|
|
346
|
+
? { health: "needs-attention", healthReason: runtime.healthReason }
|
|
347
|
+
: { health: "running", healthReason: runtime.healthReason };
|
|
300
348
|
case "waiting-user":
|
|
301
349
|
return { health: "blocked-input", healthReason: runtime.healthReason };
|
|
302
350
|
case "waiting-permission":
|
|
@@ -99,8 +99,11 @@ function replaceTaskCommand(args, store, options) {
|
|
|
99
99
|
const old = store.getTask(oldId);
|
|
100
100
|
if (old === null)
|
|
101
101
|
throw usageError(`Task not found: ${oldId}.`);
|
|
102
|
-
if (old.status === "draft"
|
|
103
|
-
throw usageError(`Task
|
|
102
|
+
if (old.status === "draft") {
|
|
103
|
+
throw usageError(`Draft Task ${old.id} owns no workspace to replace or rebuild; activate it or retire it and create a new Draft.`);
|
|
104
|
+
}
|
|
105
|
+
if (old.status === "active") {
|
|
106
|
+
throw usageError(`Active Task can be rebuilt instead of replaced: ${old.id}.`);
|
|
104
107
|
}
|
|
105
108
|
let title = `Replaces ${old.id}: ${old.title}`;
|
|
106
109
|
for (let index = 1; index < args.length; index += 1) {
|
|
@@ -13,7 +13,7 @@ export const CONFIG_DEFINITIONS = Object.freeze([
|
|
|
13
13
|
{ key: "time-zone", domain: "system", property: "timeZone", label: "Time zone", summary: "IANA timezone for human-facing timestamps (default: Asia/Shanghai).", takesEffect: "The next human-readable timestamp rendering; durable JSON remains UTC." },
|
|
14
14
|
{ key: "reconciliation-interval-seconds", domain: "runtime", property: "reconciliationIntervalSeconds", label: "Reconciliation interval", summary: "Recovery reconciliation interval, 5-300 seconds (default: 120).", takesEffect: "The running Controller is refreshed after the value is saved." },
|
|
15
15
|
{ key: "controller-task-concurrency", domain: "runtime", property: "controllerTaskConcurrency", label: "Controller Task concurrency", summary: "Maximum Tasks reconciled concurrently, 1-32 (default: 4).", takesEffect: "After the Controller restarts." },
|
|
16
|
-
{ key: "runtime-health", domain: "runtime", property: "runtimeHealth", label: "Runtime health thresholds", summary: "Quiet,
|
|
16
|
+
{ key: "runtime-health", domain: "runtime", property: "runtimeHealth", label: "Runtime health thresholds", summary: "Quiet, checkpoint-overdue, and read-only diagnostic thresholds in seconds; values must be strictly increasing (defaults: 300/900/1800).", takesEffect: "CLI and Web projections immediately; scheduler diagnostics after the Controller restarts." },
|
|
17
17
|
{ key: "agent-launch-inactivity-timeout-seconds", domain: "runtime", property: "agentLaunchInactivityTimeoutSeconds", label: "Agent launch inactivity timeout", summary: "Maximum launch silence before startup fails, 15-3600 seconds (default: 300).", takesEffect: "After the Controller restarts." },
|
|
18
18
|
{ key: "delivery-timeout-seconds", domain: "runtime", property: "deliveryTimeoutSeconds", label: "Delivery timeout", summary: "Total control-plane delivery retry budget, 5-600 seconds (default: 120).", takesEffect: "After the Controller restarts; internal retry cadence remains automatic." },
|
|
19
19
|
{ key: "provider-retry-mode", domain: "runtime", property: "providerRetryMode", label: "Provider retry mode", summary: "Provider retry mode: off, shadow, or enforce (default: enforce).", takesEffect: "The next eligible provider-failure decision." },
|
|
@@ -22,8 +22,8 @@ export const CONFIG_DEFINITIONS = Object.freeze([
|
|
|
22
22
|
{ key: "provider-retry-max-window-seconds", domain: "runtime", property: "providerRetryMaxWindowSeconds", label: "Provider retry max window", summary: "Positive-integer total retry budget per Run lineage in seconds (default: 600).", takesEffect: "The next retry episode; an active episode keeps its existing deadline." },
|
|
23
23
|
{ key: "leader-next-action", domain: "workflow", property: "leaderNextActionMode", label: "Leader next-action mode", summary: "Leader next-action mode: display, warn, or enforce (default: display).", takesEffect: "The next Leader next-action projection or gate." },
|
|
24
24
|
{ key: "leader-semantic-budget-turns", domain: "workflow", property: "leaderSemanticBudgetTurns", label: "Leader semantic budget", summary: "Consecutive yielded Leader turns without durable delivery progress before warning, 1-20 (default: 3).", takesEffect: "The next Leader delivery guard evaluation." },
|
|
25
|
-
{ key: "context-budget", domain: "workflow", property: "contextBudget", label: "
|
|
26
|
-
{ key: "review", domain: "workflow", property: "review", label: "Review", summary: "Optional global WorkItem review rule: a configured Role plus always, leader, or final trigger
|
|
25
|
+
{ key: "context-budget", domain: "workflow", property: "contextBudget", label: "Legacy context budget (inactive)", summary: `Compatibility-only soft and hard values in ${MIN_CONTEXT_BUDGET_TOKENS}-${MAX_CONTEXT_BUDGET_TOKENS}; they are parsed but never control Session or workflow behavior.`, takesEffect: "No runtime effect; Session Token metrics are read-only observations." },
|
|
26
|
+
{ key: "review", domain: "workflow", property: "review", label: "Review", summary: "Optional global WorkItem review rule: a configured Role plus always, leader, or final trigger and optional finding-ledger behavior. Setup leaves it disabled so the Leader may review directly or delegate selectively.", takesEffect: "The next Candidate snapshot; in-flight Candidates and ReviewRounds keep their policy." },
|
|
27
27
|
{ key: "resources-gc-mode", domain: "resources", property: "resourcesGcMode", label: "Resources GC mode", summary: "Resource GC mode: report or quarantine (default: report).", takesEffect: "The next resource GC operation." },
|
|
28
28
|
{ key: "resources-gc-auto-quarantine", domain: "resources", property: "resourcesGcAutoQuarantine", label: "Resources GC auto-quarantine", summary: "Automatically quarantine eligible terminal Task resources (default: false).", takesEffect: "The next eligible automatic resource GC operation." },
|
|
29
29
|
{ key: "resources-quarantine-ttl-hours", domain: "resources", property: "resourcesQuarantineTtlHours", label: "Resource quarantine TTL", summary: "Default observation window before purge, 1-720 hours (default: 24).", takesEffect: "The next resource GC command without an explicit TTL override." },
|
package/dist/config/yuiConfig.js
CHANGED
|
@@ -227,13 +227,9 @@ export function resolveTmuxHistoryLimit(value) {
|
|
|
227
227
|
return resolveBoundedPositiveInteger(value, DEFAULT_TMUX_HISTORY_LIMIT, 1_000, 1_000_000, "tmuxHistoryLimit");
|
|
228
228
|
}
|
|
229
229
|
/**
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
* advisory; when it crosses the hard threshold the scheduler retires the
|
|
234
|
-
* generation and starts a fresh one instead of waiting for provider-side
|
|
235
|
-
* auto-compaction. The fields are additive and optional — Homes without them
|
|
236
|
-
* keep these defaults, so no config migration is required.
|
|
230
|
+
* Compatibility parser for the retired context-budget setting. Existing
|
|
231
|
+
* Homes may retain these values, but no runtime, scheduler, or lifecycle path
|
|
232
|
+
* consumes them. Session Token metrics are an independent read-only view.
|
|
237
233
|
*/
|
|
238
234
|
export const DEFAULT_CONTEXT_SOFT_TOKENS = 100_000;
|
|
239
235
|
export const DEFAULT_CONTEXT_HARD_TOKENS = 120_000;
|
|
@@ -4,17 +4,18 @@ import { operationalTaskRecords } from "../task/taskRecordRetirement.js";
|
|
|
4
4
|
* Issue 04 (context token budget) — long-term design:
|
|
5
5
|
*
|
|
6
6
|
* A Leader wake is a NOTIFICATION, not a context dump. The wake envelope
|
|
7
|
-
* carries only what the Agent
|
|
8
|
-
*
|
|
9
|
-
* reads
|
|
10
|
-
* the full projection with
|
|
7
|
+
* carries only what the Agent needs for immediate orientation: the wake id,
|
|
8
|
+
* aggregated reason tags, delta window, and a bounded list of active frozen
|
|
9
|
+
* Task Reviews. The Agent reads delta content on demand with
|
|
10
|
+
* `yui task wake show <wake-id>` and the full projection with
|
|
11
|
+
* `yui task context <task>`.
|
|
11
12
|
*
|
|
12
13
|
* The envelope is mode-agnostic: fresh generations and resumed generations
|
|
13
14
|
* receive the same minimal text. The native Session is a disposable cache of
|
|
14
15
|
* working context; Yui's durable Task records (including the wake ledger) are
|
|
15
16
|
* the checkpoint.
|
|
16
17
|
*/
|
|
17
|
-
/** Structural guardrail: the envelope
|
|
18
|
+
/** Structural guardrail: the envelope stays far below a normal model context window. */
|
|
18
19
|
export const WAKE_ENVELOPE_HARD_BYTES = 2_000;
|
|
19
20
|
/** Maximum reason tags rendered before elision to a count. */
|
|
20
21
|
const REASON_DISPLAY_LIMIT = 6;
|
|
@@ -35,11 +36,25 @@ export function buildTaskWakeEnvelope(reader, request) {
|
|
|
35
36
|
runs: operationalTaskRecords(reader.listAgentRuns(request.taskId), events, "agent-run")
|
|
36
37
|
.filter((record) => Date.parse(record.createdAt) > fromTime).length
|
|
37
38
|
};
|
|
39
|
+
const activeReviews = reader.listReviewRounds(request.taskId).filter((round) => ((round.scope ?? "work-item") === "task"
|
|
40
|
+
&& (round.status === "pending" || round.status === "running")));
|
|
41
|
+
const reviewOrientation = activeReviews.slice(0, 3).map((round) => {
|
|
42
|
+
const projects = round.taskCandidate?.projects ?? [];
|
|
43
|
+
const heads = projects.slice(0, 2).map(({ projectId, commit }) => (`${projectId}@${commit.slice(0, 12)}`)).join("+");
|
|
44
|
+
return `${round.id}/${round.reviewerRoleName}`
|
|
45
|
+
+ `/${round.deltaRecheck === undefined ? "full" : "delta"}`
|
|
46
|
+
+ `[${round.status}]`
|
|
47
|
+
+ `@${heads || round.reviewBaseCommit.slice(0, 12)}`
|
|
48
|
+
+ `${projects.length > 2 ? `+${projects.length - 2}` : ""}`;
|
|
49
|
+
}).join(", ");
|
|
38
50
|
const lines = [
|
|
39
51
|
`Wake: ${request.wakeId} — delta since ${request.fromCursor}`,
|
|
40
52
|
` Reasons: ${renderReasons(request.reasons)}`,
|
|
41
53
|
` Changed: ${counts.events} events, ${counts.messages} messages, ${counts.runs} runs`
|
|
42
54
|
+ ` → yui task wake show ${request.taskId} ${request.wakeId}`,
|
|
55
|
+
` Active Task Reviews: ${activeReviews.length === 0
|
|
56
|
+
? "none"
|
|
57
|
+
: `${reviewOrientation}${activeReviews.length > 3 ? `, … (+${activeReviews.length - 3})` : ""}`}`,
|
|
43
58
|
`Full context: yui task context ${request.taskId}`
|
|
44
59
|
];
|
|
45
60
|
const body = lines.join("\n");
|