@zq-silk/yui 0.13.8 → 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/README.md +9 -8
- package/dist/cli/commandCatalog.js +21 -2
- package/dist/cli.js +42 -13
- 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 +92 -9
- package/dist/commands/taskRoleRuntimeStatus.js +3 -3
- package/dist/config/configCatalog.js +1 -6
- package/dist/config/yuiConfig.js +0 -79
- package/dist/controller/clientRuntime.js +40 -3
- package/dist/controller/controller.js +29 -52
- package/dist/controller/fileSchedulerStoreAdapter.js +305 -1056
- 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 +92 -68
- package/dist/executor/executorRegistry.js +8 -20
- package/dist/executor/fileRoleLaunchPlanner.js +24 -90
- package/dist/executor/turnCompletion.js +5 -5
- package/dist/lifecycle/exactRunTerminalization.js +1 -1
- package/dist/observability/executionAudit.js +40 -94
- package/dist/operator/operatorSessionHistory.js +7 -5
- package/dist/role/role.js +1 -1
- 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 +0 -1
- package/dist/runtime/launchBroker.js +5 -19
- package/dist/runtime/lifecycleReservation.js +20 -4
- package/dist/runtime/providerRuntimeIdentity.js +3 -2
- 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 +218 -168
- package/dist/scheduler/leaderWakeupProcessor.js +126 -86
- package/dist/scheduler/roleRunLiveness.js +4 -1
- package/dist/scheduler/roleRunStall.js +7 -11
- package/dist/scheduler/wakeReason.js +4 -0
- package/dist/storage/migration/productionRegistry.js +332 -0
- package/dist/storage/sqliteSchema.js +54 -2
- package/dist/storage/sqliteStore.js +11 -44
- package/dist/storage/taskStore.js +7 -35
- 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 +6 -4
- 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/README.md
CHANGED
|
@@ -48,7 +48,7 @@ behavior, then apply only changes the user confirms.
|
|
|
48
48
|
|
|
49
49
|
Durable settings are grouped by responsibility: `config system` for Home
|
|
50
50
|
defaults and presentation, `config runtime` for Controller health, concurrency,
|
|
51
|
-
launch,
|
|
51
|
+
launch, and delivery mechanics, `config workflow` for Leader/context/review
|
|
52
52
|
policy, `config resources` for quarantine and GC, and `config tools` for tmux
|
|
53
53
|
and diagnostic telemetry. Configured Agents, global Roles, Profiles, and shell
|
|
54
54
|
completion remain the sibling `config agent|role|profile|completion` domains.
|
|
@@ -892,8 +892,9 @@ creates a fresh proxy attachment.
|
|
|
892
892
|
If the proxy disconnects, the Host may attach a bounded replacement client and
|
|
893
893
|
reconcile the exact owned Turn from native history. A failed fresh attachment
|
|
894
894
|
is released instead of becoming a cleanup prerequisite for later Runs.
|
|
895
|
-
Claude Code keeps its independent stream-json process
|
|
896
|
-
|
|
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
|
|
897
898
|
`delivery-unknown` and is never automatically retried.
|
|
898
899
|
|
|
899
900
|
Task Role observation and takeover are explicit:
|
|
@@ -912,11 +913,11 @@ backpressure for Yui rather than a failed Run.
|
|
|
912
913
|
AgentRun is the only durable Role scheduling state. Conversation state does not
|
|
913
914
|
carry a second current-Run pointer; each Provider Turn records a Run id only to
|
|
914
915
|
correlate its receipt and terminal event. If an Agent finishes a Yui Run before
|
|
915
|
-
the native Turn terminal arrives, the next mailbox
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
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.
|
|
920
921
|
|
|
921
922
|
Global Operator and global Role sessions remain native interactive CLIs:
|
|
922
923
|
|
|
@@ -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.js
CHANGED
|
@@ -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;
|
|
@@ -1276,6 +1276,8 @@ function taskRoleCommand(args, store, options) {
|
|
|
1276
1276
|
return output(bindTaskRole(rest, store, options));
|
|
1277
1277
|
if (command === "unbind")
|
|
1278
1278
|
return output(unbindTaskRole(rest, store, options));
|
|
1279
|
+
if (command === "session")
|
|
1280
|
+
return taskRoleSessionCommand(rest, store, options);
|
|
1279
1281
|
if (command === "view")
|
|
1280
1282
|
return viewTaskRole(rest, store);
|
|
1281
1283
|
if (command === "takeover")
|
|
@@ -1286,6 +1288,87 @@ function taskRoleCommand(args, store, options) {
|
|
|
1286
1288
|
? "Task role command is required."
|
|
1287
1289
|
: `Unknown command: task role ${command}`);
|
|
1288
1290
|
}
|
|
1291
|
+
function taskRoleSessionCommand(args, store, options) {
|
|
1292
|
+
const [command, ...rest] = args;
|
|
1293
|
+
if (command === "inspect") {
|
|
1294
|
+
exactPositionals(rest, 2, "Task Role Session inspect usage: yui task role session inspect <task> <role>.");
|
|
1295
|
+
const task = requireTask(store, rest[0]);
|
|
1296
|
+
const role = requireRole(store, task.id, rest[1]);
|
|
1297
|
+
taskActor(store, options, task.id);
|
|
1298
|
+
const sessions = store.getTaskRoleSessionSet(task.id, role.name);
|
|
1299
|
+
const active = sessions?.sessions[sessions.activeAgentId] ?? null;
|
|
1300
|
+
const binding = sessions?.providerBinding ?? null;
|
|
1301
|
+
return output(active === null
|
|
1302
|
+
? `No Session exists for ${task.id}/${role.name}.\n`
|
|
1303
|
+
: [
|
|
1304
|
+
`Session ${task.id}/${role.name}`,
|
|
1305
|
+
`Agent: ${active.agentId}/${active.adapterId}`,
|
|
1306
|
+
`Native id: ${active.nativeSessionId}`,
|
|
1307
|
+
`Host activation: ${active.launchId ?? "none"}`,
|
|
1308
|
+
`Session: ${active.status}${active.endReason === undefined ? "" : `/${active.endReason}`}`,
|
|
1309
|
+
`Turn: ${binding?.turn?.status ?? "none"}`
|
|
1310
|
+
].join("\n") + "\n", { task, role, session: active, providerBinding: binding });
|
|
1311
|
+
}
|
|
1312
|
+
if (command === "stop") {
|
|
1313
|
+
const usage = "Task Role Session stop usage: yui task role session stop <task> <role> --reason <text>.";
|
|
1314
|
+
const parsed = parseTail(rest, new Set(["--reason"]), usage);
|
|
1315
|
+
exactPositionals(parsed.positionals, 2, usage);
|
|
1316
|
+
const reason = requiredOption(parsed.options, "--reason");
|
|
1317
|
+
const now = clock(options);
|
|
1318
|
+
const request = store.transaction((tx) => {
|
|
1319
|
+
const task = requireTask(tx, parsed.positionals[0]);
|
|
1320
|
+
assertTaskOpen(task);
|
|
1321
|
+
const actor = taskActor(tx, options, task.id);
|
|
1322
|
+
const role = requireRole(tx, task.id, parsed.positionals[1]);
|
|
1323
|
+
if (actor === "leader" && role.name === LEADER_ROLE) {
|
|
1324
|
+
throw usageError("A Leader cannot stop the Session executing its own current command.", usage);
|
|
1325
|
+
}
|
|
1326
|
+
if (tx.getActiveAgentRun(task.id, role.name) !== null) {
|
|
1327
|
+
throw usageError(`Task Role has an active Run; settle or retire it before stopping the Session: ${task.id}/${role.name}.`, usage);
|
|
1328
|
+
}
|
|
1329
|
+
const sessions = tx.getTaskRoleSessionSet(task.id, role.name);
|
|
1330
|
+
const session = sessions?.sessions[sessions.activeAgentId];
|
|
1331
|
+
if (session === undefined || session.status === "ended") {
|
|
1332
|
+
throw usageError(`Task Role has no active Session: ${task.id}/${role.name}.`, usage);
|
|
1333
|
+
}
|
|
1334
|
+
const lifecycle = tx.getWorkMailbox(runtimeLifecycleTarget({
|
|
1335
|
+
scope: "task",
|
|
1336
|
+
taskId: task.id,
|
|
1337
|
+
roleName: role.name
|
|
1338
|
+
}));
|
|
1339
|
+
if (lifecycle !== null && workMailboxHasWork(lifecycle)) {
|
|
1340
|
+
throw usageError(`Task Role Session lifecycle is busy: ${task.id}/${role.name}.`, usage);
|
|
1341
|
+
}
|
|
1342
|
+
recordTaskEvent(tx, task.id, "runtime.session-stop-requested", {
|
|
1343
|
+
roleName: role.name,
|
|
1344
|
+
agentId: session.agentId,
|
|
1345
|
+
adapterId: session.adapterId,
|
|
1346
|
+
nativeSessionId: session.nativeSessionId,
|
|
1347
|
+
launchId: session.launchId ?? "",
|
|
1348
|
+
reason,
|
|
1349
|
+
requestedBy: actor
|
|
1350
|
+
}, now);
|
|
1351
|
+
return {
|
|
1352
|
+
taskId: task.id,
|
|
1353
|
+
roleName: role.name,
|
|
1354
|
+
agentId: session.agentId,
|
|
1355
|
+
adapterId: session.adapterId,
|
|
1356
|
+
nativeSessionId: session.nativeSessionId,
|
|
1357
|
+
...(session.launchId === undefined ? {} : { launchId: session.launchId }),
|
|
1358
|
+
sessionUpdatedAt: session.updatedAt
|
|
1359
|
+
};
|
|
1360
|
+
});
|
|
1361
|
+
return {
|
|
1362
|
+
kind: "session-stop",
|
|
1363
|
+
...request,
|
|
1364
|
+
reason,
|
|
1365
|
+
output: `Stopped Session ${request.taskId}/${request.roleName}: ${reason}\n`
|
|
1366
|
+
};
|
|
1367
|
+
}
|
|
1368
|
+
throw usageError(command === undefined
|
|
1369
|
+
? "Task Role Session command is required."
|
|
1370
|
+
: `Unknown command: task role session ${command}`);
|
|
1371
|
+
}
|
|
1289
1372
|
function addTaskRole(args, store, options) {
|
|
1290
1373
|
const usage = "Task role add usage: yui task role add <task> <name> [Role and Agent settings].";
|
|
1291
1374
|
const [taskId, roleName, ...tail] = args;
|
|
@@ -1483,7 +1566,7 @@ function removeTaskRole(args, store, options) {
|
|
|
1483
1566
|
throw usageError(`Task Role has an active Run and cannot be removed: ${task.id}/${role.name}.`);
|
|
1484
1567
|
}
|
|
1485
1568
|
const sessions = tx.getTaskRoleSessionSet(task.id, role.name);
|
|
1486
|
-
if (Object.values(sessions?.sessions ?? {}).some(({ status }) => status
|
|
1569
|
+
if (Object.values(sessions?.sessions ?? {}).some(({ status }) => status === "active")) {
|
|
1487
1570
|
throw usageError(`Task Role has a running native Agent and cannot be removed: ${task.id}/${role.name}.`);
|
|
1488
1571
|
}
|
|
1489
1572
|
if (!tx.removeTaskRole(task.id, role.name))
|
|
@@ -1531,7 +1614,7 @@ function bindTaskRole(args, store, options) {
|
|
|
1531
1614
|
return switchActiveRoleAgent(bound, existing, agent.id, {
|
|
1532
1615
|
activeRun: tx.getActiveAgentRun(task.id, role.name) !== null,
|
|
1533
1616
|
nativeProcessRunning: currentSession !== undefined
|
|
1534
|
-
&& currentSession.status
|
|
1617
|
+
&& currentSession.status === "active"
|
|
1535
1618
|
}, now);
|
|
1536
1619
|
}
|
|
1537
1620
|
catch (error) {
|
|
@@ -1585,7 +1668,7 @@ function viewTaskRole(args, store) {
|
|
|
1585
1668
|
}
|
|
1586
1669
|
const role = requireRole(store, task.id, args[1]);
|
|
1587
1670
|
const session = store.getRoleSession(task.id, role.name);
|
|
1588
|
-
if (session === null || session.status === "
|
|
1671
|
+
if (session === null || session.status === "ended") {
|
|
1589
1672
|
throw usageError(`Task Role has no live Provider view: ${task.id}/${role.name}.`);
|
|
1590
1673
|
}
|
|
1591
1674
|
return {
|
|
@@ -1614,7 +1697,7 @@ function transferTaskRoleAuthority(args, store, options, action) {
|
|
|
1614
1697
|
if (sessions === null || sessions === undefined || session === undefined
|
|
1615
1698
|
|| binding === null || binding === undefined
|
|
1616
1699
|
|| session.launchId === undefined
|
|
1617
|
-
|| session.status === "
|
|
1700
|
+
|| session.status === "ended") {
|
|
1618
1701
|
throw new Error(`Task Role has no live managed Provider: ${task.id}/${role.name}.`);
|
|
1619
1702
|
}
|
|
1620
1703
|
const activation = currentProviderActivation(binding);
|
|
@@ -3059,7 +3142,7 @@ function rebindTaskFinalReviewContract(args, store, options) {
|
|
|
3059
3142
|
if (leaderSessions.inFlight !== null) {
|
|
3060
3143
|
throw usageError("Task final-review contract cannot rebind while the Leader runtime has unsettled Run state.");
|
|
3061
3144
|
}
|
|
3062
|
-
const liveLeaderSession = Object.values(leaderSessions.sessions).find(({ status }) => status
|
|
3145
|
+
const liveLeaderSession = Object.values(leaderSessions.sessions).find(({ status }) => status === "active");
|
|
3063
3146
|
if (liveLeaderSession !== undefined) {
|
|
3064
3147
|
throw usageError(`Task final-review contract cannot rebind while Leader Session ${liveLeaderSession.agentId} is ${liveLeaderSession.status}.`);
|
|
3065
3148
|
}
|
|
@@ -7162,8 +7245,8 @@ function settleTaskExecutionForCompletion(store, taskId, roles, summary, now) {
|
|
|
7162
7245
|
sessions = terminalizeTaskRoleRunSession(sessions, sessions.inFlight, now);
|
|
7163
7246
|
}
|
|
7164
7247
|
const current = sessions.sessions[sessions.activeAgentId];
|
|
7165
|
-
if (current !== undefined && current.status
|
|
7166
|
-
sessions = updateRoleAgentSessionStatus(sessions, sessions.activeAgentId, "
|
|
7248
|
+
if (current !== undefined && current.status === "active") {
|
|
7249
|
+
sessions = updateRoleAgentSessionStatus(sessions, sessions.activeAgentId, "ended", now, "stopped");
|
|
7167
7250
|
}
|
|
7168
7251
|
store.saveTaskRoleSessionSet(sessions);
|
|
7169
7252
|
}
|
|
@@ -7180,14 +7263,14 @@ function settleTaskExecutionForCompletion(store, taskId, roles, summary, now) {
|
|
|
7180
7263
|
return targets;
|
|
7181
7264
|
}
|
|
7182
7265
|
function ownedRuntimeSessionRequiresCleanup(session) {
|
|
7183
|
-
if (session === undefined || session.status === "
|
|
7266
|
+
if (session === undefined || session.status === "ended") {
|
|
7184
7267
|
return false;
|
|
7185
7268
|
}
|
|
7186
7269
|
// Older synthetic session fixtures can represent a ready native session
|
|
7187
7270
|
// without a lifecycle generation. Do not manufacture a cleanup obligation
|
|
7188
7271
|
// for those records; coordinated launches always carry launchId, while a
|
|
7189
7272
|
// running session remains actionable even when an older record lacks it.
|
|
7190
|
-
return session.
|
|
7273
|
+
return session.launchId !== undefined;
|
|
7191
7274
|
}
|
|
7192
7275
|
function roleMailbox(taskId, roleName) {
|
|
7193
7276
|
return { kind: "role", taskId, roleName };
|
|
@@ -253,7 +253,7 @@ function calculateHealth(role, activeRun, lastRun, nativeSession, runtimeCleanup
|
|
|
253
253
|
healthReason: "the native Session is unbound; verified runtime cleanup is pending"
|
|
254
254
|
};
|
|
255
255
|
}
|
|
256
|
-
if (nativeSession?.status === "
|
|
256
|
+
if (nativeSession?.status === "ended" && nativeSession.endReason === "failed") {
|
|
257
257
|
// Issue 09: a broken Session only fails a live Run. When the last Run
|
|
258
258
|
// already yielded, the Session death is a lifecycle event, not a Run
|
|
259
259
|
// failure — surface it as attention with the persisted Run outcome.
|
|
@@ -329,10 +329,10 @@ function calculateHealth(role, activeRun, lastRun, nativeSession, runtimeCleanup
|
|
|
329
329
|
}
|
|
330
330
|
}
|
|
331
331
|
}
|
|
332
|
-
if (nativeSession?.status === "
|
|
332
|
+
if (nativeSession?.status === "active" && tmux.state !== "running") {
|
|
333
333
|
return { health: "needs-attention", healthReason: "the native session is running without a live tmux pane" };
|
|
334
334
|
}
|
|
335
|
-
if (nativeSession?.status === "
|
|
335
|
+
if (nativeSession?.status === "ended" && tmux.state === "running") {
|
|
336
336
|
return { health: "needs-attention", healthReason: "a stopped native session has a live tmux pane" };
|
|
337
337
|
}
|
|
338
338
|
if (role.name === "leader" && openInputRequestCount > 0) {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { supportedAgentAdapterIds } from "../agent/adapterCatalog.js";
|
|
2
1
|
import { MAX_RUN_CAP } from "../telemetry/telemetryConfig.js";
|
|
3
|
-
import { MAX_CONTEXT_BUDGET_TOKENS,
|
|
2
|
+
import { MAX_CONTEXT_BUDGET_TOKENS, MIN_CONTEXT_BUDGET_TOKENS } from "./yuiConfig.js";
|
|
4
3
|
export const CONFIG_DOMAINS = ["system", "runtime", "workflow", "resources", "tools"];
|
|
5
4
|
/**
|
|
6
5
|
* Public durable configuration contract. CLI help, completion, config show,
|
|
@@ -16,10 +15,6 @@ export const CONFIG_DEFINITIONS = Object.freeze([
|
|
|
16
15
|
{ 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
16
|
{ 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
17
|
{ 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
|
-
{ 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." },
|
|
20
|
-
{ key: "provider-retry-adapters", domain: "runtime", property: "providerRetryAdapters", label: "Provider retry adapters", summary: `Adapters with in-place retry: all, off, or a comma-separated subset of ${supportedAgentAdapterIds().join(", ")} (default: all).`, takesEffect: "The next eligible provider-failure decision." },
|
|
21
|
-
{ key: "provider-retry-delays-seconds", domain: "runtime", property: "providerRetryDelaysSeconds", label: "Provider retry delays", summary: `Ordered comma-separated retry delays of 1-600 seconds, with 1-${MAX_PROVIDER_RETRY_ATTEMPTS} attempts (default: 2,5,15).`, takesEffect: "The next eligible provider-failure decision; attempt count is the list length." },
|
|
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
18
|
{ 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
19
|
{ 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
20
|
{ 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." },
|