@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
package/ARCHITECTURE.md
CHANGED
|
@@ -1,9 +1,33 @@
|
|
|
1
1
|
# Yui Architecture
|
|
2
2
|
|
|
3
|
-
Yui is a local control plane for durable work across
|
|
4
|
-
runtimes. The user talks to one Operator. The Operator
|
|
5
|
-
the right Project and Task; that Task's Leader owns
|
|
6
|
-
choice, review, integration, and completion.
|
|
3
|
+
Yui is a local control plane for intelligent Agents doing durable work across
|
|
4
|
+
Projects and native runtimes. The user talks to one Operator. The Operator
|
|
5
|
+
routes each request to the right Project and Task; that Task's Leader owns
|
|
6
|
+
decomposition, execution choice, review, integration, and completion.
|
|
7
|
+
|
|
8
|
+
## Design principles
|
|
9
|
+
|
|
10
|
+
- **Agents own judgment.** Yui exposes current durable context and atomic
|
|
11
|
+
capabilities; the Operator, Leader, and Workers choose plans, execution
|
|
12
|
+
topology, sequencing, retry, and recovery from that context.
|
|
13
|
+
- **Core provides primitives, not a prescribed workflow.** Reads, messages,
|
|
14
|
+
bounded record transitions, workspace ownership, Session lifecycle, and
|
|
15
|
+
acceptance are composable operations. Project Skills and Knowledge provide
|
|
16
|
+
project-specific policy without adding core branches.
|
|
17
|
+
- **Durable intent outranks runtime continuity.** Tasks, WorkItems, Messages,
|
|
18
|
+
Decisions, results, Project Knowledge, and managed workspaces are authority.
|
|
19
|
+
Provider Sessions, transcripts, processes, and observations are execution
|
|
20
|
+
aids that may be resumed or replaced.
|
|
21
|
+
- **Trust explicit Agent actions.** Once identity, authority, and scope are
|
|
22
|
+
established, a valid Agent command is a semantic declaration. Core should not
|
|
23
|
+
reconstruct the same judgment through another status protocol.
|
|
24
|
+
- **Fail visibly and let the Agent adapt.** Preserve pending intent and return
|
|
25
|
+
actionable state. Add automated retry, recovery, leases, or fallback only for
|
|
26
|
+
a normal product path, a hard safety or data-integrity boundary, or a proven
|
|
27
|
+
failure whose cost justifies the machinery.
|
|
28
|
+
- **One question has one authority.** Projections and indexes may summarize
|
|
29
|
+
state, but scheduling and lifecycle decisions must not depend on independently
|
|
30
|
+
writable copies of the same fact.
|
|
7
31
|
|
|
8
32
|
## One outcome, Leader-chosen execution topology
|
|
9
33
|
|
|
@@ -254,6 +278,18 @@ Yui's current attachment, not exclusive ownership of the Provider thread. One
|
|
|
254
278
|
Turn identifies one provider-native execution. Yui's authority epoch fences
|
|
255
279
|
only Yui's own submissions and retries.
|
|
256
280
|
|
|
281
|
+
`AgentRun` is the single durable scheduling authority for a Role. Provider
|
|
282
|
+
runtime persistence has no independently writable current-Run field; a Turn's
|
|
283
|
+
Run id is correlation evidence for receipts and terminal observations only.
|
|
284
|
+
`TaskRole` likewise stores configuration and identity, not a writable runtime
|
|
285
|
+
status. CLI and Web status views derive activity from the active AgentRun and
|
|
286
|
+
add Session/Driver facts only as lifecycle and diagnostic detail.
|
|
287
|
+
`AgentHost` is the serialized consumer: while a native Turn is active, the next
|
|
288
|
+
AgentRun and mailbox batch remain durable and unsubmitted. When that Turn ends,
|
|
289
|
+
the Host makes the Conversation ready and the retained delivery continues.
|
|
290
|
+
This remains true when the Agent declared the old Run's semantic outcome before
|
|
291
|
+
the Provider emitted its terminal event.
|
|
292
|
+
|
|
257
293
|
Codex Task threads remain ordinary native sessions and can be opened and used
|
|
258
294
|
directly in Desktop. If a direct user Turn is active, Yui keeps its pending
|
|
259
295
|
Run/message until that Turn settles. Global interactive entry remains a native
|
package/README.md
CHANGED
|
@@ -2,7 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
# Yui
|
|
4
4
|
|
|
5
|
-
Yui is a local control plane for
|
|
5
|
+
Yui is a local control plane for intelligent Codex and Claude Agents. It keeps
|
|
6
|
+
user intent, Project knowledge, Tasks, handoffs, and results durable and
|
|
7
|
+
inspectable, while exposing small atomic capabilities for context, messaging,
|
|
8
|
+
delegation, workspaces, Sessions, review, and integration. Agents compose those
|
|
9
|
+
capabilities and decide how to plan, sequence, delegate, retry, and recover.
|
|
10
|
+
|
|
11
|
+
Yui deliberately does not turn Agent judgment into a deterministic workflow
|
|
12
|
+
engine. Its core owns durable identity, user authority, workspace isolation,
|
|
13
|
+
and atomic state changes. Provider Sessions and runtime observations support
|
|
14
|
+
execution and continuity, but they are not competing sources of Task truth.
|
|
6
15
|
|
|
7
16
|
The current implementation restores the useful Role/Agent/session and CLI framework without restoring the later data-maintenance, lease, schedule, and recovery-ledger systems.
|
|
8
17
|
|
|
@@ -39,7 +48,7 @@ behavior, then apply only changes the user confirms.
|
|
|
39
48
|
|
|
40
49
|
Durable settings are grouped by responsibility: `config system` for Home
|
|
41
50
|
defaults and presentation, `config runtime` for Controller health, concurrency,
|
|
42
|
-
launch,
|
|
51
|
+
launch, and delivery mechanics, `config workflow` for Leader/context/review
|
|
43
52
|
policy, `config resources` for quarantine and GC, and `config tools` for tmux
|
|
44
53
|
and diagnostic telemetry. Configured Agents, global Roles, Profiles, and shell
|
|
45
54
|
completion remain the sibling `config agent|role|profile|completion` domains.
|
|
@@ -883,8 +892,9 @@ creates a fresh proxy attachment.
|
|
|
883
892
|
If the proxy disconnects, the Host may attach a bounded replacement client and
|
|
884
893
|
reconcile the exact owned Turn from native history. A failed fresh attachment
|
|
885
894
|
is released instead of becoming a cleanup prerequisite for later Runs.
|
|
886
|
-
Claude Code keeps its independent stream-json process
|
|
887
|
-
|
|
895
|
+
Claude Code keeps its independent stream-json process. Agent Host is the sole
|
|
896
|
+
writer to that process, so a completed stream write accepts the Turn; the
|
|
897
|
+
later provider `result` event settles it. An uncertain write becomes
|
|
888
898
|
`delivery-unknown` and is never automatically retried.
|
|
889
899
|
|
|
890
900
|
Task Role observation and takeover are explicit:
|
|
@@ -900,6 +910,15 @@ supported human-control boundary. A Codex Role uses an ordinary shared thread
|
|
|
900
910
|
and may be operated directly in Desktop; an active Desktop Turn creates bounded
|
|
901
911
|
backpressure for Yui rather than a failed Run.
|
|
902
912
|
|
|
913
|
+
AgentRun is the only durable Role scheduling state. Conversation state does not
|
|
914
|
+
carry a second current-Run pointer; each Provider Turn records a Run id only to
|
|
915
|
+
correlate its receipt and terminal event. If an Agent finishes a Yui Run before
|
|
916
|
+
the native Turn terminal arrives, the next mailbox intent remains pending until
|
|
917
|
+
that Turn settles. Yui then claims the new AgentRun and submits it through the
|
|
918
|
+
same Session. TaskRole itself stores identity and desired launch configuration,
|
|
919
|
+
not runtime status; Role status shown by CLI/Web is derived from the active
|
|
920
|
+
AgentRun plus Session/Driver lifecycle facts.
|
|
921
|
+
|
|
903
922
|
Global Operator and global Role sessions remain native interactive CLIs:
|
|
904
923
|
|
|
905
924
|
```sh
|
|
@@ -48,7 +48,7 @@ const CONFIG_KEY_VALUES = CONFIG_DEFINITIONS.map((definition) => ({
|
|
|
48
48
|
}));
|
|
49
49
|
const CONFIG_DOMAIN_SUMMARIES = {
|
|
50
50
|
system: "Configure Home-wide defaults and human-facing presentation.",
|
|
51
|
-
runtime: "Configure Controller recovery, concurrency, health, launch,
|
|
51
|
+
runtime: "Configure Controller recovery, concurrency, health, launch, and delivery mechanics.",
|
|
52
52
|
workflow: "Configure Leader convergence, context, and optional review policy.",
|
|
53
53
|
resources: "Configure resource garbage collection and quarantine policy.",
|
|
54
54
|
tools: "Configure tmux and optional diagnostic telemetry."
|
|
@@ -559,7 +559,7 @@ const taskChildren = [
|
|
|
559
559
|
summary: "Manage Roles within a Task.",
|
|
560
560
|
sections: [{ id: "manage", title: "Commands", entries: [
|
|
561
561
|
"add", "list", "status", "show", "update", "remove", "bind", "unbind",
|
|
562
|
-
"view", "takeover", "release"
|
|
562
|
+
"session", "view", "takeover", "release"
|
|
563
563
|
] }],
|
|
564
564
|
children: [
|
|
565
565
|
{
|
|
@@ -587,6 +587,25 @@ const taskChildren = [
|
|
|
587
587
|
{ name: "remove", summary: "Remove a Task Role.", usage: "yui task role remove <task> <role>" },
|
|
588
588
|
{ name: "bind", summary: "Bind and activate an Agent for a Task Role.", usage: "yui task role bind <task> <role> <agent-id>" },
|
|
589
589
|
{ name: "unbind", summary: "Unbind a dormant Agent from a Task Role.", usage: "yui task role unbind <task> <role> <agent-id>" },
|
|
590
|
+
{
|
|
591
|
+
name: "session",
|
|
592
|
+
summary: "Inspect or stop one Task Role's native Session.",
|
|
593
|
+
executable: true,
|
|
594
|
+
sections: [{ id: "manage", title: "Commands", entries: ["inspect", "stop"] }],
|
|
595
|
+
children: [
|
|
596
|
+
{
|
|
597
|
+
name: "inspect",
|
|
598
|
+
summary: "Read the current Session, Host activation, and Turn facts.",
|
|
599
|
+
usage: "yui task role session inspect <task> <role>"
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
name: "stop",
|
|
603
|
+
summary: "Stop one idle Session and its exact Host activation.",
|
|
604
|
+
usage: "yui task role session stop <task> <role> --reason <text>",
|
|
605
|
+
options: ["--reason"]
|
|
606
|
+
}
|
|
607
|
+
]
|
|
608
|
+
},
|
|
590
609
|
{
|
|
591
610
|
name: "view",
|
|
592
611
|
summary: "Attach read-only to an independent Provider presentation surface.",
|
package/dist/cli/updatePorts.js
CHANGED
|
@@ -226,10 +226,10 @@ export function createUpdatePorts(environment, spawn = spawnSync, stagingRoot =
|
|
|
226
226
|
+ `but the staged/verified artifact was ${staged.version}. Refusing to trust a `
|
|
227
227
|
+ "different build than the one that passed preflight.");
|
|
228
228
|
}
|
|
229
|
-
// Existing managed Sessions may have been created by
|
|
230
|
-
//
|
|
231
|
-
//
|
|
232
|
-
//
|
|
229
|
+
// Existing managed Sessions may have been created by an earlier release.
|
|
230
|
+
// Retarget those authenticated, Manifest-referenced wrappers to the
|
|
231
|
+
// activated control plane before the replacement Controller starts so
|
|
232
|
+
// the update cannot strand a live Session.
|
|
233
233
|
const sessionCliRefresh = run(activeBinary, ["--json", "internal", "session-cli-refresh"], { cwd: process.cwd(), env: { ...environment, YUI_HOME: home }, shell: false });
|
|
234
234
|
assertSpawnOk(sessionCliRefresh, "refresh managed Session CLI wrappers");
|
|
235
235
|
// Retain the exact path used by both doctor and version verification. The
|
package/dist/cli.js
CHANGED
|
@@ -348,7 +348,7 @@ export async function main() {
|
|
|
348
348
|
|| (process.env.YUI_SESSION_SCOPE === "global" && process.env.YUI_ROLE !== "operator")) {
|
|
349
349
|
throw usageError("Managed Session CLI refresh may be run only by the user or global Operator.");
|
|
350
350
|
}
|
|
351
|
-
const result = refreshManagedSessionCliWrappers(home);
|
|
351
|
+
const result = refreshManagedSessionCliWrappers(home, currentInvocationControlPlane(home));
|
|
352
352
|
emit(`Refreshed ${result.refreshed} legacy Session CLI wrapper(s); `
|
|
353
353
|
+ `${result.current} already current, ${result.skipped} skipped.`, false, result);
|
|
354
354
|
return;
|
|
@@ -366,7 +366,16 @@ export async function main() {
|
|
|
366
366
|
return;
|
|
367
367
|
}
|
|
368
368
|
if (args[1] === "runtime-hook" && args.length === 2) {
|
|
369
|
-
|
|
369
|
+
// Provider lifecycle Hooks are observation channels, never execution
|
|
370
|
+
// gates. A late/stale Hook must not make Claude reject an otherwise
|
|
371
|
+
// valid Session or Turn; its exact fence is revalidated before any
|
|
372
|
+
// inbox fact is written, so dropping an invalid observation is safe.
|
|
373
|
+
try {
|
|
374
|
+
await runRuntimeObservationHookCommand(readFileSync(0, "utf8"), process.env);
|
|
375
|
+
}
|
|
376
|
+
catch {
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
370
379
|
return;
|
|
371
380
|
}
|
|
372
381
|
throw usageError("Internal lifecycle callback usage is invalid.");
|
|
@@ -1402,6 +1411,30 @@ export async function main() {
|
|
|
1402
1411
|
if (jsonOutput) {
|
|
1403
1412
|
throw usageError("Task Role view/takeover requires an interactive terminal.");
|
|
1404
1413
|
}
|
|
1414
|
+
if (result.kind === "session-stop") {
|
|
1415
|
+
await ensureFileTaskController(home, { environment: process.env });
|
|
1416
|
+
try {
|
|
1417
|
+
await runtime.stopExactTaskRoleSession({
|
|
1418
|
+
taskId: result.taskId,
|
|
1419
|
+
roleName: result.roleName,
|
|
1420
|
+
agentId: result.agentId,
|
|
1421
|
+
adapterId: result.adapterId,
|
|
1422
|
+
nativeSessionId: result.nativeSessionId,
|
|
1423
|
+
...(result.launchId === undefined ? {} : { launchId: result.launchId }),
|
|
1424
|
+
sessionUpdatedAt: result.sessionUpdatedAt
|
|
1425
|
+
});
|
|
1426
|
+
}
|
|
1427
|
+
catch (error) {
|
|
1428
|
+
throw runtimeError(`Session stop was requested but physical Host cleanup did not complete: ${error instanceof Error ? error.message : String(error)}`);
|
|
1429
|
+
}
|
|
1430
|
+
emit(result.output, false, {
|
|
1431
|
+
taskId: result.taskId,
|
|
1432
|
+
roleName: result.roleName,
|
|
1433
|
+
stopped: true,
|
|
1434
|
+
reason: result.reason
|
|
1435
|
+
});
|
|
1436
|
+
return;
|
|
1437
|
+
}
|
|
1405
1438
|
if (result.kind === "view") {
|
|
1406
1439
|
if (result.output !== undefined)
|
|
1407
1440
|
emit(result.output);
|
|
@@ -1604,17 +1637,14 @@ async function preflightManagedTaskControlPlane() {
|
|
|
1604
1637
|
&& process.env.YUI_DRIVER_ID !== undefined
|
|
1605
1638
|
? builtinAgentDriverRegistry().require(process.env.YUI_DRIVER_ID)
|
|
1606
1639
|
: undefined;
|
|
1607
|
-
const preallocatedDriverCallback = runtimeDriverCallback
|
|
1608
|
-
?.capabilities.observation.sessionBootstrap === "preallocated";
|
|
1609
1640
|
const verifiedStore = openCompatibleFileTaskStore(control.yuiHome);
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
: {});
|
|
1641
|
+
// Runtime Hooks have their own event-aware fence, including the valid case
|
|
1642
|
+
// where a Provider terminal arrives after its Yui Run has yielded and a
|
|
1643
|
+
// later wake is pending. Ordinary managed commands still require the exact
|
|
1644
|
+
// current mutable runtime before routing.
|
|
1645
|
+
if (runtimeDriverCallback === undefined) {
|
|
1646
|
+
assertExactTaskRuntimeState(runtime, verifiedStore);
|
|
1647
|
+
}
|
|
1618
1648
|
const request = taskFinalReviewInvocation.request;
|
|
1619
1649
|
if (request === undefined) {
|
|
1620
1650
|
return {
|
|
@@ -2334,8 +2364,7 @@ async function executeOperatorSessionControl(control, home, store, runtime, tmux
|
|
|
2334
2364
|
await ensureFileTaskController(home, { environment: process.env });
|
|
2335
2365
|
if (paneRunning
|
|
2336
2366
|
|| (active !== undefined
|
|
2337
|
-
&& active.status
|
|
2338
|
-
&& active.status !== "broken")) {
|
|
2367
|
+
&& active.status === "active")) {
|
|
2339
2368
|
await runtime.stopGlobalRoleSession(role.name);
|
|
2340
2369
|
}
|
|
2341
2370
|
applyOperatorSessionControl(control, store);
|
|
@@ -244,7 +244,7 @@ function findNonStoppedSessionReference(store, agentId) {
|
|
|
244
244
|
}
|
|
245
245
|
function sessionReference(set, agentId, reference) {
|
|
246
246
|
const session = set.sessions[agentId];
|
|
247
|
-
if (session === undefined || session.status === "
|
|
247
|
+
if (session === undefined || session.status === "ended")
|
|
248
248
|
return null;
|
|
249
249
|
return { ...reference, status: session.status };
|
|
250
250
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mkdirSync, realpathSync } from "node:fs";
|
|
2
2
|
import { isAbsolute, relative, resolve } from "node:path";
|
|
3
3
|
import { usageError } from "../errors/cliError.js";
|
|
4
|
-
import { DEFAULT_AGENT_LAUNCH_INACTIVITY_TIMEOUT_SECONDS, DEFAULT_CONTROLLER_TASK_CONCURRENCY, DEFAULT_DELIVERY_TIMEOUT_SECONDS, DEFAULT_LEADER_NEXT_ACTION_MODE, DEFAULT_LEADER_SEMANTIC_BUDGET_TURNS,
|
|
4
|
+
import { DEFAULT_AGENT_LAUNCH_INACTIVITY_TIMEOUT_SECONDS, DEFAULT_CONTROLLER_TASK_CONCURRENCY, DEFAULT_DELIVERY_TIMEOUT_SECONDS, DEFAULT_LEADER_NEXT_ACTION_MODE, DEFAULT_LEADER_SEMANTIC_BUDGET_TURNS, DEFAULT_RECONCILIATION_INTERVAL_SECONDS, DEFAULT_RESOURCES_GC_MODE, DEFAULT_RESOURCES_QUARANTINE_TTL_HOURS, DEFAULT_TMUX_HISTORY_LIMIT, LEADER_NEXT_ACTION_MODES, reconciliationIntervalMilliseconds, resolveAgentLaunchInactivityTimeoutSeconds, resolveControllerTaskConcurrency, resolveContextBudget, resolveDeliveryTimeoutSeconds, resolveLeaderNextActionMode, resolveLeaderSemanticBudgetTurns, resolveResourcesGcAutoQuarantine, resolveResourcesGcMode, resolveResourcesQuarantineTtlHours, resolveRuntimeHealth, resolveTelemetryEnabled, resolveTelemetryRunCap, resolveTelemetryTerminalKeep, resolveTmuxBin, resolveTmuxHistoryLimit } from "../config/yuiConfig.js";
|
|
5
5
|
import { CONFIG_DEFINITIONS, CONFIG_KEYS, configDefinition, configDefinitionsForDomain } from "../config/configCatalog.js";
|
|
6
6
|
import { resolveTimeZone } from "../output/timePresentation.js";
|
|
7
7
|
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
@@ -73,10 +73,6 @@ export function effectiveConfigData(config, domain) {
|
|
|
73
73
|
resourcesGcMode: resolveResourcesGcMode(config.resourcesGcMode),
|
|
74
74
|
resourcesGcAutoQuarantine: resolveResourcesGcAutoQuarantine(config.resourcesGcAutoQuarantine),
|
|
75
75
|
resourcesQuarantineTtlHours: resolveResourcesQuarantineTtlHours(config.resourcesQuarantineTtlHours),
|
|
76
|
-
providerRetryMode: resolveProviderRetryMode(config.providerRetryMode),
|
|
77
|
-
providerRetryAdapters: resolveProviderRetryAdapters(config.providerRetryAdapters),
|
|
78
|
-
providerRetryDelaysSeconds: resolveProviderRetryDelaysSeconds(config.providerRetryDelaysSeconds),
|
|
79
|
-
providerRetryMaxWindowSeconds: resolveProviderRetryMaxWindowSeconds(config.providerRetryMaxWindowSeconds),
|
|
80
76
|
runtimeHealth: {
|
|
81
77
|
quietAfterSeconds: health.quietAfterMs / 1_000,
|
|
82
78
|
diagnosticAfterSeconds: health.diagnosticAfterMs / 1_000,
|
|
@@ -498,87 +494,6 @@ const CONFIG_KEY_HANDLERS = [
|
|
|
498
494
|
return `Delivery timeout reset to ${DEFAULT_DELIVERY_TIMEOUT_SECONDS} seconds\n`;
|
|
499
495
|
}
|
|
500
496
|
},
|
|
501
|
-
{
|
|
502
|
-
key: "provider-retry-mode",
|
|
503
|
-
showLabel: "Provider retry mode",
|
|
504
|
-
showValue: (config) => resolveProviderRetryMode(config.providerRetryMode),
|
|
505
|
-
set(args, store) {
|
|
506
|
-
if (args.length !== 1)
|
|
507
|
-
throw usageError(`Runtime config set usage: yui config runtime set provider-retry-mode <${PROVIDER_RETRY_MODES.join("|")}>.`);
|
|
508
|
-
const mode = validatedConfigValue(() => resolveProviderRetryMode(args[0]), `Runtime config set usage: yui config runtime set provider-retry-mode <${PROVIDER_RETRY_MODES.join("|")}>.`);
|
|
509
|
-
saveConfigKey(store, (config) => ({ ...config, providerRetryMode: mode }));
|
|
510
|
-
return `Provider retry mode set to ${mode}\n`;
|
|
511
|
-
},
|
|
512
|
-
clear(store) {
|
|
513
|
-
saveConfigKey(store, (config) => {
|
|
514
|
-
const { providerRetryMode: _removed, ...rest } = config;
|
|
515
|
-
return rest;
|
|
516
|
-
});
|
|
517
|
-
return `Provider retry mode reset to ${DEFAULT_PROVIDER_RETRY_MODE}\n`;
|
|
518
|
-
}
|
|
519
|
-
},
|
|
520
|
-
{
|
|
521
|
-
key: "provider-retry-adapters",
|
|
522
|
-
showLabel: "Provider retry adapters",
|
|
523
|
-
showValue: (config) => resolveProviderRetryAdapters(config.providerRetryAdapters).join(", ") || "none",
|
|
524
|
-
set(args, store) {
|
|
525
|
-
if (args.length !== 1)
|
|
526
|
-
throw usageError("Runtime config set usage: yui config runtime set provider-retry-adapters <all|claude,codex|off>.");
|
|
527
|
-
const raw = args[0].trim().toLowerCase();
|
|
528
|
-
const adapters = raw === "off" || raw === "" || raw === "0"
|
|
529
|
-
? []
|
|
530
|
-
: validatedConfigValue(() => resolveProviderRetryAdapters(raw.split(",")), "Runtime config set usage: yui config runtime set provider-retry-adapters <all|claude,codex|off>.");
|
|
531
|
-
saveConfigKey(store, (config) => ({ ...config, providerRetryAdapters: adapters }));
|
|
532
|
-
return `Provider retry adapters set to ${adapters.join(", ") || "none"}\n`;
|
|
533
|
-
},
|
|
534
|
-
clear(store) {
|
|
535
|
-
saveConfigKey(store, (config) => {
|
|
536
|
-
const { providerRetryAdapters: _removed, ...rest } = config;
|
|
537
|
-
return rest;
|
|
538
|
-
});
|
|
539
|
-
return "Provider retry adapters reset to all supported\n";
|
|
540
|
-
}
|
|
541
|
-
},
|
|
542
|
-
{
|
|
543
|
-
key: "provider-retry-delays-seconds",
|
|
544
|
-
showLabel: "Provider retry delays",
|
|
545
|
-
showValue: (config) => `${resolveProviderRetryDelaysSeconds(config.providerRetryDelaysSeconds).join(", ")} seconds`,
|
|
546
|
-
set(args, store) {
|
|
547
|
-
const usage = "Runtime config set usage: yui config runtime set provider-retry-delays-seconds <comma-separated-seconds>.";
|
|
548
|
-
if (args.length !== 1)
|
|
549
|
-
throw usageError(usage);
|
|
550
|
-
const providerRetryDelaysSeconds = validatedConfigValue(() => resolveProviderRetryDelaysSeconds(args[0].split(",").map(Number)), usage);
|
|
551
|
-
saveConfigKey(store, (config) => ({ ...config, providerRetryDelaysSeconds }));
|
|
552
|
-
return `Provider retry delays set to ${providerRetryDelaysSeconds.join(", ")} seconds\n`;
|
|
553
|
-
},
|
|
554
|
-
clear(store) {
|
|
555
|
-
saveConfigKey(store, (config) => {
|
|
556
|
-
const { providerRetryDelaysSeconds: _removed, ...rest } = config;
|
|
557
|
-
return rest;
|
|
558
|
-
});
|
|
559
|
-
return `Provider retry delays reset to ${DEFAULT_PROVIDER_RETRY_DELAYS_SECONDS.join(", ")} seconds\n`;
|
|
560
|
-
}
|
|
561
|
-
},
|
|
562
|
-
{
|
|
563
|
-
key: "provider-retry-max-window-seconds",
|
|
564
|
-
showLabel: "Provider retry max window",
|
|
565
|
-
showValue: (config) => `${resolveProviderRetryMaxWindowSeconds(config.providerRetryMaxWindowSeconds)} seconds`,
|
|
566
|
-
set(args, store) {
|
|
567
|
-
const usage = "Runtime config set usage: yui config runtime set provider-retry-max-window-seconds <positive-seconds>.";
|
|
568
|
-
if (args.length !== 1)
|
|
569
|
-
throw usageError(usage);
|
|
570
|
-
const providerRetryMaxWindowSeconds = validatedConfigValue(() => resolveProviderRetryMaxWindowSeconds(Number(args[0])), usage);
|
|
571
|
-
saveConfigKey(store, (config) => ({ ...config, providerRetryMaxWindowSeconds }));
|
|
572
|
-
return `Provider retry max window set to ${providerRetryMaxWindowSeconds} seconds\n`;
|
|
573
|
-
},
|
|
574
|
-
clear(store) {
|
|
575
|
-
saveConfigKey(store, (config) => {
|
|
576
|
-
const { providerRetryMaxWindowSeconds: _removed, ...rest } = config;
|
|
577
|
-
return rest;
|
|
578
|
-
});
|
|
579
|
-
return `Provider retry max window reset to ${DEFAULT_PROVIDER_RETRY_MAX_WINDOW_SECONDS} seconds\n`;
|
|
580
|
-
}
|
|
581
|
-
},
|
|
582
497
|
{
|
|
583
498
|
key: "tmux-bin",
|
|
584
499
|
showLabel: "Tmux bin",
|
|
@@ -183,29 +183,30 @@ export function renderExecutionAudit(report, width = defaultTableWidth()) {
|
|
|
183
183
|
else {
|
|
184
184
|
lines.push("", ...sectionError("events", report));
|
|
185
185
|
}
|
|
186
|
-
if (report.
|
|
187
|
-
const
|
|
188
|
-
if (
|
|
189
|
-
lines.push("", `
|
|
190
|
-
|
|
186
|
+
if (report.agentErrors.status === "ok" && report.agentErrors.data !== undefined) {
|
|
187
|
+
const errors = report.agentErrors.data;
|
|
188
|
+
if (errors.total > 0) {
|
|
189
|
+
lines.push("", `Agent errors: ${errors.total} · ${Object.entries(errors.byCategory)
|
|
190
|
+
.map(([category, count]) => `${category}:${count}`).join(", ")}`);
|
|
191
|
+
lines.push(renderTable("Agent errors", [
|
|
191
192
|
{ header: "Task", minWidth: 8, maxWidth: 14 },
|
|
192
193
|
{ header: "Run", minWidth: 14, maxWidth: 24 },
|
|
193
194
|
{ header: "Role", minWidth: 8, maxWidth: 12 },
|
|
194
|
-
{ header: "
|
|
195
|
-
{ header: "
|
|
196
|
-
{ header: "
|
|
197
|
-
],
|
|
195
|
+
{ header: "Category", minWidth: 12, maxWidth: 20 },
|
|
196
|
+
{ header: "Code", minWidth: 16, maxWidth: 32 },
|
|
197
|
+
{ header: "Session", minWidth: 12, maxWidth: 16 }
|
|
198
|
+
], errors.entries.map((entry) => [
|
|
198
199
|
entry.taskId,
|
|
199
200
|
entry.runId,
|
|
200
201
|
entry.roleName,
|
|
201
|
-
|
|
202
|
-
entry.
|
|
203
|
-
entry.
|
|
202
|
+
entry.category,
|
|
203
|
+
entry.code,
|
|
204
|
+
entry.sessionDisposition
|
|
204
205
|
]), width));
|
|
205
206
|
}
|
|
206
207
|
}
|
|
207
208
|
else {
|
|
208
|
-
lines.push("", ...sectionError("
|
|
209
|
+
lines.push("", ...sectionError("agentErrors", report));
|
|
209
210
|
}
|
|
210
211
|
if (report.workItems.status === "ok" && report.workItems.data !== undefined) {
|
|
211
212
|
const items = report.workItems.data;
|
|
@@ -257,8 +258,8 @@ export function renderExecutionAudit(report, width = defaultTableWidth()) {
|
|
|
257
258
|
const versions = Object.entries(runtime.contextProtocolVersions)
|
|
258
259
|
.map(([version, count]) => `${version}:${count}`)
|
|
259
260
|
.join(", ") || "none";
|
|
260
|
-
const
|
|
261
|
-
.map(([
|
|
261
|
+
const errorCategories = Object.entries(runtime.agentErrorCategories)
|
|
262
|
+
.map(([category, count]) => `${category}:${count}`)
|
|
262
263
|
.join(", ") || "none";
|
|
263
264
|
const exits = Object.entries(runtime.processExitClassifications)
|
|
264
265
|
.map(([classification, count]) => `${classification}:${count}`)
|
|
@@ -266,7 +267,7 @@ export function renderExecutionAudit(report, width = defaultTableWidth()) {
|
|
|
266
267
|
const usage = Object.entries(runtime.usageSemantics)
|
|
267
268
|
.map(([semantics, count]) => `${semantics}:${count}`)
|
|
268
269
|
.join(", ") || "none";
|
|
269
|
-
lines.push("", `Runtime protocol: context versions ${versions} · manifest compatibility identities ${runtime.manifestCompatibilityDigests}`, `
|
|
270
|
+
lines.push("", `Runtime protocol: context versions ${versions} · manifest compatibility identities ${runtime.manifestCompatibilityDigests}`, `Agent errors: ${runtime.agentErrors} [${errorCategories}]`, `Process exits: ${runtime.processExitObservations} [${exits}] · capacity failures ${runtime.contextCapacityFailures}`, `Context telemetry: usage [${usage}] · native compaction events ${runtime.compactionEvents}`);
|
|
270
271
|
}
|
|
271
272
|
else {
|
|
272
273
|
lines.push("", ...sectionError("runtimeProtocol", report));
|
|
@@ -247,7 +247,7 @@ function bindRole(args, store) {
|
|
|
247
247
|
const switched = switchActiveRoleAgent(withBinding, existingSet ?? createRoleSessionSet({ scope: "global", roleName: name }, role.activeAgentId, now), agentId, {
|
|
248
248
|
activeRun: false,
|
|
249
249
|
nativeProcessRunning: activeSession !== undefined
|
|
250
|
-
&& activeSession.status
|
|
250
|
+
&& activeSession.status === "active"
|
|
251
251
|
}, now);
|
|
252
252
|
tx.saveGlobalRoleWithSessionSet(switched.role, switched.sessions);
|
|
253
253
|
return { message: `Bound role ${name} to ${agentId}`, role: switched.role };
|
|
@@ -271,7 +271,7 @@ function removeRole(args, store) {
|
|
|
271
271
|
roleName: role.name
|
|
272
272
|
}, "removal");
|
|
273
273
|
const sessions = tx.getGlobalRoleSessionSet(name);
|
|
274
|
-
if (Object.values(sessions?.sessions ?? {}).some(({ status }) => status
|
|
274
|
+
if (Object.values(sessions?.sessions ?? {}).some(({ status }) => status === "active")) {
|
|
275
275
|
throw usageError(`GlobalRole is active and cannot be removed: ${name}.`);
|
|
276
276
|
}
|
|
277
277
|
if (!tx.removeGlobalRole(name))
|
|
@@ -336,7 +336,7 @@ function roleSession(args, store, options) {
|
|
|
336
336
|
adapterId: binding.adapterId,
|
|
337
337
|
nativeSessionId,
|
|
338
338
|
policy: "fixed",
|
|
339
|
-
status:
|
|
339
|
+
status: "active",
|
|
340
340
|
effective: resolveEffectiveLaunch({ role, purpose: "execution" })
|
|
341
341
|
};
|
|
342
342
|
if (command === "record") {
|
|
@@ -349,7 +349,7 @@ function roleSession(args, store, options) {
|
|
|
349
349
|
if (existing === null) {
|
|
350
350
|
throw usageError("Native session replacement requires an existing native session.");
|
|
351
351
|
}
|
|
352
|
-
if (existing.status !== "
|
|
352
|
+
if (existing.status !== "ended") {
|
|
353
353
|
throw usageError("Native session replacement is blocked while the native Agent process is running.");
|
|
354
354
|
}
|
|
355
355
|
if (existing.nativeSessionId === nativeSessionId) {
|
|
@@ -75,16 +75,12 @@ function blockedSessions(input) {
|
|
|
75
75
|
return [];
|
|
76
76
|
return [{
|
|
77
77
|
session,
|
|
78
|
-
reason:
|
|
79
|
-
? "running"
|
|
80
|
-
: "runtime-work-pending"
|
|
78
|
+
reason: "runtime-work-pending"
|
|
81
79
|
}];
|
|
82
80
|
});
|
|
83
81
|
}
|
|
84
82
|
function blockedStopResult(blocked) {
|
|
85
|
-
const details = blocked.map(({ session
|
|
86
|
-
? "a Turn or Run is still running"
|
|
87
|
-
: "an active Run or lifecycle operation is still pending"})`));
|
|
83
|
+
const details = blocked.map(({ session }) => (`- ${renderSessionOwner(session)} (an active Run or lifecycle operation is still pending)`));
|
|
88
84
|
return {
|
|
89
85
|
output: [
|
|
90
86
|
`Cannot stop managed Sessions: ${blocked.length} Session(s) are still busy.`,
|
|
@@ -207,8 +207,7 @@ export function taskLeaderActionRunId(store, taskId, environment, yuiHome) {
|
|
|
207
207
|
if (session === undefined
|
|
208
208
|
|| identity(session.nativeSessionId) === undefined
|
|
209
209
|
|| identity(session.launchId) === undefined
|
|
210
|
-
|| session.status === "
|
|
211
|
-
|| session.status === "broken"
|
|
210
|
+
|| session.status === "ended"
|
|
212
211
|
|| session.adapterId !== adapterId
|
|
213
212
|
|| (explicitAssertion === undefined && session.launchId !== launchId))
|
|
214
213
|
return undefined;
|