@zq-silk/yui 0.13.9 → 0.14.0
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 +69 -428
- package/README.md +108 -298
- package/dist/agent/managedRuntimeEnvironment.js +2 -2
- package/dist/cli/commandCatalog.js +39 -77
- package/dist/cli/interactionCandidates.js +4 -4
- package/dist/cli/interactionPolicy.js +7 -10
- package/dist/cli/invocationRouter.js +1 -1
- package/dist/cli/updateCommand.js +6 -27
- package/dist/cli/updateOrchestrator.js +63 -484
- package/dist/cli/updatePorts.js +15 -333
- package/dist/cli/upgradeCommand.js +8 -65
- package/dist/cli.js +57 -242
- package/dist/commands/configCommands.js +11 -62
- package/dist/commands/executionAuditCommands.js +23 -23
- package/dist/commands/globalRoleCommands.js +2 -7
- package/dist/commands/jobCommands.js +1 -4
- package/dist/commands/projectCommands.js +5 -5
- package/dist/commands/releaseCommands.js +3 -10
- package/dist/commands/resourcesCommands.js +2 -2
- package/dist/commands/sessionCommands.js +2 -2
- package/dist/commands/taskActor.js +31 -39
- package/dist/commands/taskChangeSetCommands.js +5 -12
- package/dist/commands/taskCommands.js +464 -1249
- package/dist/commands/taskContextCommand.js +35 -36
- package/dist/commands/taskExecutionCommands.js +33 -46
- package/dist/commands/taskInputCommands.js +20 -42
- package/dist/commands/taskNextActionCommand.js +9 -9
- package/dist/commands/taskOverviewCommand.js +56 -95
- package/dist/commands/taskRoleRuntimeStatus.js +77 -107
- package/dist/commands/taskUpstreamCommands.js +3 -3
- package/dist/commands/taskWorkspaceCommands.js +7 -79
- package/dist/commands/telemetryCommands.js +32 -124
- package/dist/commands/workflowCommands.js +3 -3
- package/dist/completion/fileCompletionManager.js +1 -1
- package/dist/config/configCatalog.js +4 -6
- package/dist/config/yuiConfig.js +6 -43
- package/dist/context/dispatchContext.js +14 -30
- package/dist/context/roleSessionContext.js +2 -2
- package/dist/context/sessionBootstrapManifest.js +2 -2
- package/dist/context/{runContextPack.js → turnContextPack.js} +58 -73
- package/dist/context/turnInputContract.js +204 -0
- package/dist/context/wakeNotification.js +2 -2
- package/dist/controller/agentRuntimeObserver.js +18 -18
- package/dist/controller/clientRuntime.js +13 -20
- package/dist/controller/controller.js +89 -116
- package/dist/controller/fileSchedulerStoreAdapter.js +511 -742
- package/dist/controller/jobControl.js +17 -17
- package/dist/controller/resourceInventory.js +6 -6
- package/dist/controller/resourceInventoryLinux.js +38 -59
- package/dist/controller/resourceInventoryRpc.js +3 -3
- package/dist/controller/resourceInventoryWorker.js +1 -1
- package/dist/controller/runtime.js +46 -53
- package/dist/controller/runtimeEventInbox.js +23 -31
- package/dist/controller/runtimeEventProcessor.js +15 -15
- package/dist/controller/{runtimeHookRunFence.js → runtimeHookTurnFence.js} +87 -81
- package/dist/controller/runtimeLaunchCoordinator.js +3 -3
- package/dist/controller/runtimeObservationHook.js +11 -8
- package/dist/controller/sessionNotify.js +11 -11
- package/dist/controller/structuredProviderObservation.js +89 -16
- package/dist/coordination/workMailbox.js +309 -579
- package/dist/coordination/workMailboxQueue.js +5 -6
- package/dist/core/controllerClient.js +1 -91
- package/dist/core/controllerServer.js +3 -6
- package/dist/doctor/doctor.js +51 -314
- package/dist/execution/candidateConvergence.js +3 -3
- package/dist/execution/executionGroup.js +32 -33
- package/dist/execution/executionHealth.js +36 -38
- package/dist/execution/resourceBroker.js +12 -12
- package/dist/executor/agentExecutor.js +30 -265
- package/dist/executor/effectiveLaunch.js +9 -19
- package/dist/executor/executorRegistry.js +43 -17
- package/dist/executor/fileRoleLaunchPlanner.js +56 -56
- package/dist/executor/launchPlan.js +3 -3
- package/dist/executor/workspacePreflightClassification.js +16 -16
- package/dist/grant/capabilityGrant.js +20 -25
- package/dist/input/inputRequest.js +12 -12
- package/dist/integration/changeSet.js +9 -13
- package/dist/integration/changeSetManifest.js +1 -1
- package/dist/integration/gitIntegrationService.js +2 -2
- package/dist/integration/integrationQueueService.js +5 -21
- package/dist/integration/overlapDiagnostics.js +2 -2
- package/dist/interaction/operatorPresentation.js +1 -1
- package/dist/job/jobRunner.js +1 -1
- package/dist/lifecycle/canonicalLifecycleEvent.js +25 -25
- package/dist/lifecycle/{exactRunTerminalization.js → exactTurnTerminalization.js} +116 -232
- package/dist/message/message.js +6 -6
- package/dist/observability/executionAudit.js +74 -86
- package/dist/observability/faultClassification.js +7 -24
- package/dist/observability/orchestrationMetrics.js +5 -5
- package/dist/observability/runtimeIdentity.js +34 -101
- package/dist/profile/agentProfile.js +1 -1
- package/dist/release/cliHomeReleaseFence.js +3 -3
- package/dist/release/releaseWorkflowEngine.js +6 -10
- package/dist/release/releaseWorkflowPorts.js +2 -2
- package/dist/release/workflowFileLock.js +1 -1
- package/dist/repository/checkoutSwap.js +1 -1
- package/dist/repository/gitWorkspace.js +0 -13
- package/dist/repository/project.js +1 -1
- package/dist/repository/projectMaintenanceLock.js +1 -2
- package/dist/repository/taskBaseFreshness.js +13 -13
- package/dist/repository/taskWorkspaceCoordinator.js +21 -25
- package/dist/repository/taskWorkspaceIdentity.js +8 -21
- package/dist/repository/taskWorkspacePreparer.js +63 -620
- package/dist/resources/autoResourceGc.js +3 -3
- package/dist/resources/liveReferences.js +1 -1
- package/dist/resources/resourceGc.js +1 -1
- package/dist/resources/sqliteResourceRegistry.js +1 -1
- package/dist/review/reviewDecision.js +7 -7
- package/dist/review/reviewFindingLedger.js +1 -1
- package/dist/review/reviewOutcomeClassifier.js +35 -50
- package/dist/review/reviewRound.js +14 -25
- package/dist/review/reviewerAvailability.js +4 -4
- package/dist/review/taskFinalReviewContractResolution.js +41 -0
- package/dist/role/role.js +6 -3
- package/dist/runtime/agentDriver.js +4 -0
- package/dist/runtime/agentHost.js +151 -36
- package/dist/runtime/builtinAgentDrivers.js +79 -2
- package/dist/runtime/codexAppServerRuntime.js +122 -0
- package/dist/runtime/continuationManager.js +3 -5
- package/dist/runtime/exactControlPlane.js +17 -42
- package/dist/runtime/firstProgressAdvisory.js +1 -1
- package/dist/runtime/index.js +2 -2
- package/dist/runtime/launchDiagnostics.js +2 -2
- package/dist/runtime/lifecycleReservation.js +4 -4
- package/dist/runtime/processExitObservation.js +1 -1
- package/dist/runtime/processExitOutbox.js +0 -32
- package/dist/runtime/promptEnvelope.js +9 -9
- package/dist/runtime/providerContinuation.js +2 -2
- package/dist/runtime/providerContinuationReconciliationService.js +2 -2
- package/dist/runtime/providerRuntimeIdentity.js +76 -24
- package/dist/runtime/recentTurnIds.js +38 -0
- package/dist/runtime/runtimeContinuationProjection.js +2 -2
- package/dist/runtime/runtimeHealthPolicy.js +1 -1
- package/dist/runtime/runtimeObservation.js +58 -21
- package/dist/runtime/runtimeProjection.js +19 -27
- package/dist/runtime/sessionLaunchRequest.js +2 -2
- package/dist/runtime/structuredProviderHost.js +177 -12
- package/dist/runtime/tmuxAdapters.js +46 -11
- package/dist/scheduler/activeRoleTurnDelivery.js +245 -0
- package/dist/scheduler/leaderWakeupProcessor.js +147 -445
- package/dist/scheduler/operatorEvent.js +4 -6
- package/dist/scheduler/resourceQueueProjection.js +71 -0
- package/dist/scheduler/{roleRunLiveness.js → roleTurnLiveness.js} +24 -28
- package/dist/scheduler/{roleRunStall.js → roleTurnStall.js} +162 -247
- package/dist/scheduler/taskExecutionProjection.js +99 -99
- package/dist/scheduler/taskObservabilityProjection.js +10 -10
- package/dist/scheduler/taskWake.js +4 -4
- package/dist/scheduler/wakeReason.js +6 -10
- package/dist/scheduler/wakeupQueue.js +0 -9
- package/dist/setup/setupCommand.js +2 -2
- package/dist/storage/currentTaskStore.js +46 -0
- package/dist/storage/persistenceWorker.js +1 -1
- package/dist/storage/sqliteSchema.js +81 -485
- package/dist/storage/sqliteStore.js +206 -365
- package/dist/storage/storageSchema.js +1 -15
- package/dist/storage/storageVersions.js +8 -9
- package/dist/storage/storeRpc.js +10 -34
- package/dist/storage/taskStore.js +63 -3415
- package/dist/storage/upgrade/recordVersions.js +55 -149
- package/dist/storage/upgrade/upgradeOrchestrator.js +89 -1752
- package/dist/task/completionReadiness.js +5 -5
- package/dist/task/deliveryGuard.js +10 -10
- package/dist/task/nextAction.js +74 -77
- package/dist/task/task.js +2 -4
- package/dist/task/taskRecordReference.js +3 -3
- package/dist/task/taskRecordRetirement.js +1 -1
- package/dist/telemetry/sqliteTelemetryStore.js +53 -53
- package/dist/telemetry/telemetryConfig.js +14 -14
- package/dist/telemetry/telemetryWiring.js +8 -14
- package/dist/turn/turn.js +356 -0
- package/dist/{run/runIdentity.js → turn/turnIdentity.js} +4 -4
- package/dist/verification/gateArtifactStore.js +2 -2
- package/dist/web/assets/client/app.js +1 -1
- package/dist/web/assets/client/components.js +29 -54
- package/dist/web/assets/client/i18n.js +17 -37
- package/dist/web/assets/client/view.js +37 -37
- package/dist/web/assets/shell.js +1 -1
- package/dist/web/assets/styles/cards.js +5 -5
- package/dist/web/assets/styles/widgets.js +1 -2
- package/dist/web/webSnapshot.js +65 -60
- package/dist/workItem/workItem.js +11 -11
- package/dist/workspace/workItemChangeSetManager.js +1 -1
- package/docs/task-local-identity.md +5 -5
- package/i18n/README.zh-CN.md +41 -54
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +85 -83
- package/skills/yui-operator/SKILL.md +16 -16
- package/skills/yui-reviewer/SKILL.md +10 -10
- package/skills/yui-runtime/SKILL.md +17 -18
- package/skills/yui-worker/SKILL.md +16 -18
- package/dist/context/runContextContract.js +0 -162
- package/dist/executor/turnCompletion.js +0 -126
- package/dist/review/taskFinalReviewContractEvent.js +0 -1
- package/dist/review/taskFinalReviewContractRebind.js +0 -367
- package/dist/run/agentRun.js +0 -356
- package/dist/run/rejectedYieldAttempt.js +0 -221
- package/dist/run/runControlRequest.js +0 -50
- package/dist/run/yieldReceipt.js +0 -65
- package/dist/runtime/turnCompletion.js +0 -3
- package/dist/scheduler/actionability.js +0 -323
- package/dist/scheduler/activeRoleRunDelivery.js +0 -868
- package/dist/scheduler/activeTaskProgress.js +0 -126
- package/dist/storage/compatibleTaskStore.js +0 -248
- package/dist/storage/migration/baseline.js +0 -78
- package/dist/storage/migration/classifier.js +0 -51
- package/dist/storage/migration/compatibleCodec.js +0 -53
- package/dist/storage/migration/engine.js +0 -147
- package/dist/storage/migration/index.js +0 -33
- package/dist/storage/migration/planner.js +0 -154
- package/dist/storage/migration/productionRegistry.js +0 -3931
- package/dist/storage/migration/registry.js +0 -169
- package/dist/storage/migration/report.js +0 -54
- package/dist/storage/migration/types.js +0 -31
- package/dist/storage/upgrade/homeClassification.js +0 -353
- package/dist/storage/upgrade/homeMigrationTarget.js +0 -595
- package/dist/storage/upgrade/migrationReceipt.js +0 -67
- package/dist/storage/upgrade/offlineUpgradeInventory.js +0 -453
- package/dist/storage/upgrade/productionMigrationRegistry.js +0 -6
- package/dist/storage/upgrade/pseudoLayoutRepair.js +0 -241
- package/dist/storage/upgrade/recordVersionScan.js +0 -176
- package/dist/storage/upgrade/sqliteFileSet.js +0 -50
- package/dist/storage/upgrade/sqliteMigrationTarget.js +0 -375
- package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +0 -305
- package/dist/storage/upgrade/sqliteStateMigration.js +0 -918
- package/dist/storage/upgrade/switchProgress.js +0 -80
- package/dist/storage/upgrade/upgradeReceipt.js +0 -161
- package/dist/storage/upgradeCoordination.js +0 -187
- package/dist/storage/upgradeFence.js +0 -366
- package/dist/telemetry/telemetryCompaction.js +0 -252
|
@@ -1,89 +1,17 @@
|
|
|
1
1
|
import { usageError } from "../errors/cliError.js";
|
|
2
2
|
import { createMilestone } from "../milestone/milestone.js";
|
|
3
|
-
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
4
3
|
import { runTaskCommand } from "./taskCommands.js";
|
|
5
4
|
/**
|
|
6
|
-
* Task workspace lifecycle
|
|
7
|
-
* surface because
|
|
8
|
-
*
|
|
9
|
-
* replacement-creating path for terminal Tasks.
|
|
5
|
+
* Task workspace lifecycle command that sits outside the sync task command
|
|
6
|
+
* surface because replacement preserves the terminal Task and creates a new
|
|
7
|
+
* current-contract Task.
|
|
10
8
|
*/
|
|
11
9
|
export async function runTaskWorkspaceCommand(args, store, preparer, options = {}) {
|
|
12
10
|
const [command, ...rest] = args;
|
|
13
|
-
|
|
14
|
-
return rebuildTaskCommand(rest, preparer);
|
|
15
|
-
if (command === "history")
|
|
16
|
-
return historyCommand(rest, preparer);
|
|
11
|
+
void preparer;
|
|
17
12
|
if (command === "replace")
|
|
18
13
|
return replaceTaskCommand(rest, store, options);
|
|
19
|
-
throw usageError("Unknown task workspace command. Available: yui task
|
|
20
|
-
+ "yui task history list|archive [task], yui task replace <task>.");
|
|
21
|
-
}
|
|
22
|
-
async function rebuildTaskCommand(args, preparer) {
|
|
23
|
-
const usage = "Task rebuild usage: yui task rebuild <task> [--latest].";
|
|
24
|
-
const taskId = args[0];
|
|
25
|
-
const options = new Set(args.slice(1));
|
|
26
|
-
if (taskId === undefined
|
|
27
|
-
|| options.size !== args.length - 1
|
|
28
|
-
|| [...options].some((option) => option !== "--latest")) {
|
|
29
|
-
throw usageError(usage);
|
|
30
|
-
}
|
|
31
|
-
const result = await preparer.rebuildTaskWorkspace(taskId, {
|
|
32
|
-
latestRemote: options.has("--latest")
|
|
33
|
-
});
|
|
34
|
-
const archived = result.archived.length === 0
|
|
35
|
-
? "no legacy refs"
|
|
36
|
-
: `${result.archived.length} legacy ref(s) archived`;
|
|
37
|
-
const resumed = result.resumed ? " (resumed pending cleanup)" : "";
|
|
38
|
-
return {
|
|
39
|
-
output: `Rebuilt Task workspace ${result.task.id} at ${result.task.cwd ?? "its configured workspace"}: `
|
|
40
|
-
+ `${archived}${resumed}.`,
|
|
41
|
-
data: {
|
|
42
|
-
taskId: result.task.id,
|
|
43
|
-
archived: result.archived,
|
|
44
|
-
resumed: result.resumed,
|
|
45
|
-
latestRemote: options.has("--latest")
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
async function historyCommand(args, preparer) {
|
|
50
|
-
const usage = "Task history usage: yui task history list|archive [task].";
|
|
51
|
-
const action = args[0];
|
|
52
|
-
if (action !== "list" && action !== "archive")
|
|
53
|
-
throw usageError(usage);
|
|
54
|
-
const taskId = args[1];
|
|
55
|
-
if (args.length > 2)
|
|
56
|
-
throw usageError(usage);
|
|
57
|
-
if (action === "list") {
|
|
58
|
-
const refs = await preparer.listLegacyTaskRefs(taskId);
|
|
59
|
-
if (refs.length === 0) {
|
|
60
|
-
return { output: "No legacy Task refs.", data: { refs: [] } };
|
|
61
|
-
}
|
|
62
|
-
return {
|
|
63
|
-
output: renderTable("Legacy Task refs", [
|
|
64
|
-
{ header: "Project", minWidth: 8, maxWidth: 24 },
|
|
65
|
-
{ header: "Task", minWidth: 8, maxWidth: 16 },
|
|
66
|
-
{ header: "Legacy ref", minWidth: 16, maxWidth: 64 }
|
|
67
|
-
], refs.map((entry) => [entry.projectId, entry.taskId, entry.ref]), defaultTableWidth()),
|
|
68
|
-
data: { refs }
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
const result = await preparer.archiveLegacyTaskRefs(taskId);
|
|
72
|
-
const lines = [];
|
|
73
|
-
if (result.archived.length > 0) {
|
|
74
|
-
lines.push(`Archived ${result.archived.length} legacy ref(s):`);
|
|
75
|
-
lines.push(...result.archived.map((entry) => ` ${entry}`));
|
|
76
|
-
}
|
|
77
|
-
if (result.refused.length > 0) {
|
|
78
|
-
lines.push(`Refused ${result.refused.length} live ref(s) owned by an open Task:`);
|
|
79
|
-
lines.push(...result.refused.map((entry) => ` ${entry}`));
|
|
80
|
-
}
|
|
81
|
-
if (lines.length === 0)
|
|
82
|
-
lines.push("No legacy Task refs to archive.");
|
|
83
|
-
return {
|
|
84
|
-
output: lines.join("\n"),
|
|
85
|
-
data: { archived: result.archived, refused: result.refused }
|
|
86
|
-
};
|
|
14
|
+
throw usageError("Unknown task workspace command. Available: yui task replace <task>.");
|
|
87
15
|
}
|
|
88
16
|
/**
|
|
89
17
|
* A terminal (retired/completed/archived) Task is never rewritten in place.
|
|
@@ -100,10 +28,10 @@ function replaceTaskCommand(args, store, options) {
|
|
|
100
28
|
if (old === null)
|
|
101
29
|
throw usageError(`Task not found: ${oldId}.`);
|
|
102
30
|
if (old.status === "draft") {
|
|
103
|
-
throw usageError(`Draft Task ${old.id} owns no
|
|
31
|
+
throw usageError(`Draft Task ${old.id} owns no completed history to replace; activate it or retire it and create a new Draft.`);
|
|
104
32
|
}
|
|
105
33
|
if (old.status === "active") {
|
|
106
|
-
throw usageError(`Active Task
|
|
34
|
+
throw usageError(`Active Task cannot be replaced: ${old.id}.`);
|
|
107
35
|
}
|
|
108
36
|
let title = `Replaces ${old.id}: ${old.title}`;
|
|
109
37
|
for (let index = 1; index < args.length; index += 1) {
|
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
1
|
import { join } from "node:path";
|
|
3
2
|
import { usageError } from "../errors/cliError.js";
|
|
4
|
-
import { STORAGE_SCHEMA_FILE } from "../storage/storageSchema.js";
|
|
5
3
|
import { CURRENT_CONFIG_SCHEMA_VERSION } from "../storage/taskStore.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { DEFAULT_RUN_CAP, DEFAULT_TERMINAL_KEEP, resolveRunCap, resolveTerminalKeep } from "../telemetry/telemetryConfig.js";
|
|
4
|
+
import { CURRENT_DATABASE_FILENAME as COMMITTED_DATABASE_FILENAME } from "../storage/currentTaskStore.js";
|
|
5
|
+
import { DEFAULT_TERMINAL_KEEP, resolveTurnCap, resolveTerminalKeep } from "../telemetry/telemetryConfig.js";
|
|
9
6
|
import { resolveTelemetryEnabled } from "../config/yuiConfig.js";
|
|
10
|
-
import { applyTelemetryCompaction, planTelemetryCompaction } from "../telemetry/telemetryCompaction.js";
|
|
11
7
|
import { SqliteTelemetryStore } from "../telemetry/sqliteTelemetryStore.js";
|
|
12
8
|
/** Read the durable config, falling back to defaults when no store is available. */
|
|
13
9
|
function storeConfig(options) {
|
|
@@ -22,16 +18,13 @@ export async function runTelemetryCommand(args, options) {
|
|
|
22
18
|
return telemetryStatus(rest, options);
|
|
23
19
|
if (command === "prune")
|
|
24
20
|
return telemetryPrune(rest, options);
|
|
25
|
-
if (command === "compact")
|
|
26
|
-
return telemetryCompact(rest, options);
|
|
27
21
|
if (command === "read")
|
|
28
22
|
return telemetryRead(rest, options);
|
|
29
23
|
throw usageError(command === undefined
|
|
30
|
-
? "Telemetry command is required: status, prune,
|
|
24
|
+
? "Telemetry command is required: status, prune, or read."
|
|
31
25
|
: `Unknown command: telemetry ${command}`, "yui telemetry status [--json]\n"
|
|
32
26
|
+ "yui telemetry prune [--task <id>] [--keep <n>] [--dry-run] [--json]\n"
|
|
33
|
-
+ "yui telemetry
|
|
34
|
-
+ "yui telemetry read --task <id> [--run <id>] [--aggregate] [--limit <n>] [--offset <n>]");
|
|
27
|
+
+ "yui telemetry read --task <id> [--turn <id>] [--aggregate] [--limit <n>] [--offset <n>]");
|
|
35
28
|
}
|
|
36
29
|
// -- status ---------------------------------------------------------------------
|
|
37
30
|
function telemetryStatus(args, options) {
|
|
@@ -40,7 +33,7 @@ function telemetryStatus(args, options) {
|
|
|
40
33
|
const telemetry = new SqliteTelemetryStore(options.home, {
|
|
41
34
|
mode,
|
|
42
35
|
terminalKeep: resolveTerminalKeep(storeConfig(options).telemetryTerminalKeep),
|
|
43
|
-
|
|
36
|
+
turnCap: resolveTurnCap(storeConfig(options).telemetryTurnCap)
|
|
44
37
|
});
|
|
45
38
|
try {
|
|
46
39
|
const health = telemetry.health();
|
|
@@ -50,7 +43,7 @@ function telemetryStatus(args, options) {
|
|
|
50
43
|
: store.listTasks().map((task) => ({
|
|
51
44
|
taskId: task.id,
|
|
52
45
|
rows: telemetry.count(task.id),
|
|
53
|
-
|
|
46
|
+
turns: telemetry.listTurnAggregates(task.id).length
|
|
54
47
|
}));
|
|
55
48
|
const report = {
|
|
56
49
|
mode,
|
|
@@ -61,7 +54,7 @@ function telemetryStatus(args, options) {
|
|
|
61
54
|
lastError: health.lastError,
|
|
62
55
|
totalRows: health.rows,
|
|
63
56
|
terminalKeep: resolveTerminalKeep(storeConfig(options).telemetryTerminalKeep),
|
|
64
|
-
|
|
57
|
+
turnCap: resolveTurnCap(storeConfig(options).telemetryTurnCap),
|
|
65
58
|
tasks: perTask
|
|
66
59
|
};
|
|
67
60
|
if (options.json || flags.has("json"))
|
|
@@ -75,10 +68,10 @@ function telemetryStatus(args, options) {
|
|
|
75
68
|
`Coalesced: ${health.coalesced}`,
|
|
76
69
|
...(health.lastError === null ? [] : [`Last error: ${health.lastError}`]),
|
|
77
70
|
`Terminal keep: ${report.terminalKeep}`,
|
|
78
|
-
`
|
|
71
|
+
`Turn cap: ${report.turnCap}`
|
|
79
72
|
];
|
|
80
73
|
for (const task of perTask) {
|
|
81
|
-
lines.push(` ${task.taskId}: ${task.rows} rows across ${task.
|
|
74
|
+
lines.push(` ${task.taskId}: ${task.rows} rows across ${task.turns} Turn generation(s)`);
|
|
82
75
|
}
|
|
83
76
|
return lines.join("\n");
|
|
84
77
|
}
|
|
@@ -92,12 +85,12 @@ function telemetryPrune(args, options) {
|
|
|
92
85
|
const taskId = stringOption(args, "--task");
|
|
93
86
|
const keep = integerOption(args, "--keep", DEFAULT_TERMINAL_KEEP);
|
|
94
87
|
const dryRun = flags.has("dry-run");
|
|
95
|
-
const cap =
|
|
88
|
+
const cap = resolveTurnCap(storeConfig(options).telemetryTurnCap);
|
|
96
89
|
const store = requireStore(options);
|
|
97
90
|
const telemetry = new SqliteTelemetryStore(options.home, {
|
|
98
91
|
mode: telemetryMode(storeConfig(options)),
|
|
99
92
|
terminalKeep: keep,
|
|
100
|
-
|
|
93
|
+
turnCap: cap
|
|
101
94
|
});
|
|
102
95
|
try {
|
|
103
96
|
const tasks = taskId === undefined
|
|
@@ -105,35 +98,35 @@ function telemetryPrune(args, options) {
|
|
|
105
98
|
: [taskId];
|
|
106
99
|
const report = [];
|
|
107
100
|
for (const id of tasks) {
|
|
108
|
-
const
|
|
101
|
+
const terminalTurns = new Set(store.listTurns(id)
|
|
109
102
|
.filter((run) => run.status !== "active")
|
|
110
103
|
.map((run) => run.id));
|
|
111
104
|
const entry = { taskId: id, terminalPruned: 0, activeCapped: 0, generations: [] };
|
|
112
|
-
for (const aggregate of telemetry.
|
|
113
|
-
if (
|
|
114
|
-
const before = telemetry.count(id, aggregate.
|
|
105
|
+
for (const aggregate of telemetry.listTurnAggregates(id)) {
|
|
106
|
+
if (terminalTurns.has(aggregate.turnId)) {
|
|
107
|
+
const before = telemetry.count(id, aggregate.turnId);
|
|
115
108
|
const deleted = dryRun
|
|
116
109
|
? Math.max(0, before - keep)
|
|
117
|
-
: telemetry.pruneGeneration(aggregate.taskId, aggregate.roleName, aggregate.
|
|
110
|
+
: telemetry.pruneGeneration(aggregate.taskId, aggregate.roleName, aggregate.turnId, aggregate.generation, keep);
|
|
118
111
|
entry.terminalPruned += deleted;
|
|
119
112
|
entry.generations.push({
|
|
120
|
-
|
|
113
|
+
turnId: aggregate.turnId,
|
|
121
114
|
generation: aggregate.generation,
|
|
122
115
|
kept: Math.min(before, keep),
|
|
123
116
|
deleted
|
|
124
117
|
});
|
|
125
118
|
}
|
|
126
119
|
}
|
|
127
|
-
for (const run of store.
|
|
120
|
+
for (const run of store.listTurns(id)) {
|
|
128
121
|
if (run.status !== "active")
|
|
129
122
|
continue;
|
|
130
123
|
const before = telemetry.count(id, run.id);
|
|
131
124
|
if (before <= cap)
|
|
132
125
|
continue;
|
|
133
|
-
const deleted = dryRun ? before - cap : telemetry.
|
|
126
|
+
const deleted = dryRun ? before - cap : telemetry.capTurn(id, run.id, cap);
|
|
134
127
|
entry.activeCapped += deleted;
|
|
135
128
|
entry.generations.push({
|
|
136
|
-
|
|
129
|
+
turnId: run.id,
|
|
137
130
|
generation: "*",
|
|
138
131
|
kept: Math.min(before, cap),
|
|
139
132
|
deleted
|
|
@@ -151,14 +144,14 @@ function telemetryPrune(args, options) {
|
|
|
151
144
|
const lines = [
|
|
152
145
|
`Telemetry prune (${dryRun ? "dry-run" : "applied"}):`,
|
|
153
146
|
` terminal generations pruned: ${totals.terminalPruned} row(s) (keep ${keep})`,
|
|
154
|
-
` active
|
|
147
|
+
` active Turns capped: ${totals.activeCapped} row(s) (cap ${cap})`
|
|
155
148
|
];
|
|
156
149
|
for (const entry of report) {
|
|
157
150
|
if (entry.generations.length === 0)
|
|
158
151
|
continue;
|
|
159
152
|
lines.push(` ${entry.taskId}:`);
|
|
160
153
|
for (const generation of entry.generations) {
|
|
161
|
-
lines.push(` ${generation.
|
|
154
|
+
lines.push(` ${generation.turnId}/${generation.generation}: kept ${generation.kept}, deleted ${generation.deleted}`);
|
|
162
155
|
}
|
|
163
156
|
}
|
|
164
157
|
return lines.join("\n");
|
|
@@ -167,105 +160,38 @@ function telemetryPrune(args, options) {
|
|
|
167
160
|
void telemetry.close();
|
|
168
161
|
}
|
|
169
162
|
}
|
|
170
|
-
// -- compact --------------------------------------------------------------------
|
|
171
|
-
function telemetryCompact(args, options) {
|
|
172
|
-
const flags = parseFlags(args, new Set(["dry-run", "json"]));
|
|
173
|
-
const from = stringOption(args, "--from");
|
|
174
|
-
const staged = stringOption(args, "--staged");
|
|
175
|
-
const keep = integerOption(args, "--keep", DEFAULT_TERMINAL_KEEP);
|
|
176
|
-
const dryRun = flags.has("dry-run");
|
|
177
|
-
if (from === undefined || staged === undefined) {
|
|
178
|
-
throw usageError("telemetry compact requires --from <home> and --staged <dir>.", "yui telemetry compact --from <home> --staged <dir> [--keep <n>] [--dry-run]");
|
|
179
|
-
}
|
|
180
|
-
const resolvedFrom = resolvePath(from);
|
|
181
|
-
const resolvedStaged = resolvePath(staged);
|
|
182
|
-
if (resolvedFrom === resolvedStaged) {
|
|
183
|
-
throw usageError("--from and --staged must be different directories.");
|
|
184
|
-
}
|
|
185
|
-
if (!existsSync(join(resolvedFrom, STORAGE_SCHEMA_FILE))) {
|
|
186
|
-
throw usageError(`--from is not a Yui Home (missing ${STORAGE_SCHEMA_FILE}): ${resolvedFrom}`);
|
|
187
|
-
}
|
|
188
|
-
if (existsSync(resolvedStaged) && dryRun === false) {
|
|
189
|
-
throw usageError(`--staged already exists; refuse to overwrite: ${resolvedStaged}`);
|
|
190
|
-
}
|
|
191
|
-
// Database-only direction: compaction folds semantic progress into the
|
|
192
|
-
// telemetry tables inside `yui.db`. A Home without a database has not
|
|
193
|
-
// reached SQLite storage yet; migrate it first (yui upgrade) instead
|
|
194
|
-
// of silently compacting a file-only copy that telemetry cannot join.
|
|
195
|
-
if (!existsSync(join(resolvedFrom, COMMITTED_DATABASE_FILENAME))) {
|
|
196
|
-
throw usageError(`--from is not a SQLite Home (missing ${COMMITTED_DATABASE_FILENAME}): ${resolvedFrom}. `
|
|
197
|
-
+ "Migrate it to the database backend before compaction.");
|
|
198
|
-
}
|
|
199
|
-
const backend = "sqlite";
|
|
200
|
-
mkdirSync(resolvedStaged, { recursive: true });
|
|
201
|
-
copyStoreFiles(resolvedFrom, resolvedStaged);
|
|
202
|
-
const store = openTaskStore(resolvedStaged, backend);
|
|
203
|
-
const telemetry = new SqliteTelemetryStore(resolvedStaged, {
|
|
204
|
-
mode: "on",
|
|
205
|
-
terminalKeep: keep,
|
|
206
|
-
runCap: DEFAULT_RUN_CAP
|
|
207
|
-
});
|
|
208
|
-
try {
|
|
209
|
-
const plan = planTelemetryCompaction(store, { terminalKeep: keep });
|
|
210
|
-
const receipt = applyTelemetryCompaction(store, telemetry, plan, {
|
|
211
|
-
dryRun,
|
|
212
|
-
source: resolvedFrom,
|
|
213
|
-
terminalKeep: keep
|
|
214
|
-
});
|
|
215
|
-
writeFileSync(join(resolvedStaged, "telemetry-compaction.json"), JSON.stringify(receipt, null, 2) + "\n");
|
|
216
|
-
if (options.json || flags.has("json"))
|
|
217
|
-
return JSON.stringify({ staged: resolvedStaged, receipt }, null, 2);
|
|
218
|
-
return [
|
|
219
|
-
`Telemetry compaction (${dryRun ? "dry-run" : "applied"}) on staged copy:`,
|
|
220
|
-
` staged: ${resolvedStaged}`,
|
|
221
|
-
` backend: ${backend}`,
|
|
222
|
-
` tasks: ${receipt.totals.tasks}`,
|
|
223
|
-
` progress: ${receipt.totals.progressEvents} event(s) across ${receipt.totals.generations} generation(s)`,
|
|
224
|
-
` sidecar rows: ${receipt.totals.telemetryRows} (window ${keep}/generation)`,
|
|
225
|
-
` receipt: ${join(resolvedStaged, "telemetry-compaction.json")}`,
|
|
226
|
-
dryRun
|
|
227
|
-
? "Dry-run made no changes. Re-run without --dry-run, then promote the staged Home after verifying the receipt."
|
|
228
|
-
: "Applied. Promote the staged Home only after verifying the receipt; the original Home is unchanged."
|
|
229
|
-
].join("\n");
|
|
230
|
-
}
|
|
231
|
-
finally {
|
|
232
|
-
void telemetry.close();
|
|
233
|
-
if (store instanceof SqliteTaskStore)
|
|
234
|
-
store.close();
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
163
|
// -- read -----------------------------------------------------------------------
|
|
238
164
|
function telemetryRead(args, options) {
|
|
239
165
|
const flags = parseFlags(args, new Set(["aggregate", "json"]));
|
|
240
166
|
const taskId = stringOption(args, "--task");
|
|
241
167
|
if (taskId === undefined) {
|
|
242
|
-
throw usageError("telemetry read requires --task <id>.", "yui telemetry read --task <id> [--
|
|
168
|
+
throw usageError("telemetry read requires --task <id>.", "yui telemetry read --task <id> [--turn <id>] [--aggregate]");
|
|
243
169
|
}
|
|
244
|
-
const
|
|
170
|
+
const turnId = stringOption(args, "--turn");
|
|
245
171
|
const limit = integerOption(args, "--limit", 100);
|
|
246
172
|
const offset = integerOption(args, "--offset", 0);
|
|
247
173
|
const telemetry = new SqliteTelemetryStore(options.home, {
|
|
248
174
|
mode: telemetryMode(storeConfig(options)),
|
|
249
175
|
terminalKeep: resolveTerminalKeep(storeConfig(options).telemetryTerminalKeep),
|
|
250
|
-
|
|
176
|
+
turnCap: resolveTurnCap(storeConfig(options).telemetryTurnCap)
|
|
251
177
|
});
|
|
252
178
|
try {
|
|
253
179
|
if (flags.has("aggregate")) {
|
|
254
|
-
if (
|
|
255
|
-
const aggregates = telemetry.
|
|
180
|
+
if (turnId === undefined) {
|
|
181
|
+
const aggregates = telemetry.listTurnAggregates(taskId);
|
|
256
182
|
if (options.json || flags.has("json"))
|
|
257
183
|
return JSON.stringify(aggregates, null, 2);
|
|
258
184
|
return aggregates.map((aggregate) => formatAggregate(aggregate)).join("\n") || "(no telemetry)";
|
|
259
185
|
}
|
|
260
|
-
const aggregate = telemetry.aggregate(taskId,
|
|
186
|
+
const aggregate = telemetry.aggregate(taskId, turnId);
|
|
261
187
|
if (options.json || flags.has("json"))
|
|
262
188
|
return JSON.stringify(aggregate, null, 2);
|
|
263
|
-
return aggregate === null ? "(no telemetry for this
|
|
189
|
+
return aggregate === null ? "(no telemetry for this Turn)" : formatAggregate(aggregate);
|
|
264
190
|
}
|
|
265
|
-
const page = telemetry.list(taskId,
|
|
191
|
+
const page = telemetry.list(taskId, turnId, { limit, offset });
|
|
266
192
|
if (options.json || flags.has("json"))
|
|
267
193
|
return JSON.stringify(page, null, 2);
|
|
268
|
-
const lines = page.items.map((entry) => `${entry.receivedAt} ${entry.
|
|
194
|
+
const lines = page.items.map((entry) => `${entry.receivedAt} ${entry.turnId}/${entry.generation}/${entry.progressId}`
|
|
269
195
|
+ `${entry.sequence === undefined ? "" : ` seq=${entry.sequence}`}`);
|
|
270
196
|
if (page.nextOffset !== null)
|
|
271
197
|
lines.push(`(next offset: ${page.nextOffset})`);
|
|
@@ -282,24 +208,6 @@ function requireStore(options) {
|
|
|
282
208
|
}
|
|
283
209
|
return options.store;
|
|
284
210
|
}
|
|
285
|
-
function resolvePath(value) {
|
|
286
|
-
return value.startsWith("/") ? value : join(process.cwd(), value);
|
|
287
|
-
}
|
|
288
|
-
function copyStoreFiles(from, staged) {
|
|
289
|
-
for (const filename of [
|
|
290
|
-
COMMITTED_DATABASE_FILENAME,
|
|
291
|
-
`${COMMITTED_DATABASE_FILENAME}-wal`,
|
|
292
|
-
`${COMMITTED_DATABASE_FILENAME}-shm`
|
|
293
|
-
]) {
|
|
294
|
-
const source = join(from, filename);
|
|
295
|
-
if (existsSync(source))
|
|
296
|
-
copyFile(source, join(staged, filename));
|
|
297
|
-
}
|
|
298
|
-
copyFile(join(from, STORAGE_SCHEMA_FILE), join(staged, STORAGE_SCHEMA_FILE));
|
|
299
|
-
}
|
|
300
|
-
function copyFile(source, destination) {
|
|
301
|
-
writeFileSync(destination, readFileSync(source));
|
|
302
|
-
}
|
|
303
211
|
function parseFlags(args, known) {
|
|
304
212
|
const flags = new Set();
|
|
305
213
|
for (const arg of args) {
|
|
@@ -332,5 +240,5 @@ function integerOption(args, name, fallback) {
|
|
|
332
240
|
return value;
|
|
333
241
|
}
|
|
334
242
|
function formatAggregate(aggregate) {
|
|
335
|
-
return `${aggregate.
|
|
243
|
+
return `${aggregate.turnId}/${aggregate.generation}: count=${aggregate.count} first=${aggregate.firstAt} last=${aggregate.lastAt} maxSequence=${aggregate.maxSequence === null ? "-" : aggregate.maxSequence} errors=${aggregate.errorCount}`;
|
|
336
244
|
}
|
|
@@ -61,9 +61,9 @@ export async function runWorkflowCommandAsync(args, store, options) {
|
|
|
61
61
|
// first transition or calls an external system. A run that is killed,
|
|
62
62
|
// crashes, or whose engine rethrows after this point can never leave
|
|
63
63
|
// mutation or effect evidence without an audit record saying the run was
|
|
64
|
-
// attempted.
|
|
65
|
-
//
|
|
66
|
-
// what makes the trail crash-safe across the whole
|
|
64
|
+
// attempted. The async engine run cannot join this synchronous transaction;
|
|
65
|
+
// committing the intent first is
|
|
66
|
+
// what makes the trail crash-safe across the whole turn.
|
|
67
67
|
const intentNow = clock(options);
|
|
68
68
|
store.transaction((tx) => {
|
|
69
69
|
recordTaskEvent(tx, taskId, "release-workflow.run-started", {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { installCompletion } from "./completionInstaller.js";
|
|
2
2
|
import { COMPLETION_SHELLS, completionActivationIsCurrent, completionScriptIsCurrent, currentCompletionShell, suggestedCompletionInstallation } from "./completionState.js";
|
|
3
|
-
/**
|
|
3
|
+
/** Durable completion port used by the interactive CLI wizard. */
|
|
4
4
|
export class FileCompletionManager {
|
|
5
5
|
#store;
|
|
6
6
|
#env;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { MAX_CONTEXT_BUDGET_TOKENS, MIN_CONTEXT_BUDGET_TOKENS } from "./yuiConfig.js";
|
|
1
|
+
import { MAX_TURN_CAP } from "../telemetry/telemetryConfig.js";
|
|
3
2
|
export const CONFIG_DOMAINS = ["system", "runtime", "workflow", "resources", "tools"];
|
|
4
3
|
/**
|
|
5
4
|
* Public durable configuration contract. CLI help, completion, config show,
|
|
@@ -16,8 +15,7 @@ export const CONFIG_DEFINITIONS = Object.freeze([
|
|
|
16
15
|
{ 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." },
|
|
17
16
|
{ 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." },
|
|
18
17
|
{ 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." },
|
|
19
|
-
{ key: "leader-semantic-budget-turns", domain: "workflow", property: "leaderSemanticBudgetTurns", label: "Leader semantic budget", summary: "Consecutive
|
|
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." },
|
|
18
|
+
{ key: "leader-semantic-budget-turns", domain: "workflow", property: "leaderSemanticBudgetTurns", label: "Leader semantic budget", summary: "Consecutive completed Leader turns without durable delivery progress before warning, 1-20 (default: 3).", takesEffect: "The next Leader delivery guard evaluation." },
|
|
21
19
|
{ 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." },
|
|
22
20
|
{ 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." },
|
|
23
21
|
{ 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." },
|
|
@@ -25,8 +23,8 @@ export const CONFIG_DEFINITIONS = Object.freeze([
|
|
|
25
23
|
{ key: "tmux-bin", domain: "tools", property: "tmuxBin", label: "Tmux bin", summary: "Command or path to the tmux binary (default: tmux).", takesEffect: "The next CLI-owned tmux invocation; restart the Controller for Controller-owned launches." },
|
|
26
24
|
{ key: "tmux-history-limit", domain: "tools", property: "tmuxHistoryLimit", label: "Tmux history limit", summary: "History lines retained for newly created tmux sessions, 1000-1000000 (default: 100000).", takesEffect: "New tmux sessions; existing sessions report drift until recreated." },
|
|
27
25
|
{ key: "telemetry-enabled", domain: "tools", property: "telemetryEnabled", label: "Diagnostic telemetry", summary: "Enable the optional SQLite diagnostic telemetry projection (default: false); requires a SQLite Home.", takesEffect: "Standalone telemetry commands immediately; scheduler writes after Controller restart." },
|
|
28
|
-
{ key: "telemetry-terminal-keep", domain: "tools", property: "telemetryTerminalKeep", label: "Telemetry terminal keep", summary: "Positive-integer rows retained per terminal
|
|
29
|
-
{ key: "telemetry-
|
|
26
|
+
{ key: "telemetry-terminal-keep", domain: "tools", property: "telemetryTerminalKeep", label: "Telemetry terminal keep", summary: "Positive-integer rows retained per terminal Turn generation (default: 200).", takesEffect: "Explicit retention immediately; automatic retention after the Controller restarts." },
|
|
27
|
+
{ key: "telemetry-turn-cap", domain: "tools", property: "telemetryTurnCap", label: "Telemetry Turn cap", summary: `Positive-integer maximum rows retained per Turn, at most ${MAX_TURN_CAP} (default: 50000).`, takesEffect: "Explicit retention immediately; automatic write-time capping after the Controller restarts." }
|
|
30
28
|
]);
|
|
31
29
|
export const CONFIG_KEYS = Object.freeze(CONFIG_DEFINITIONS.map(({ key }) => key));
|
|
32
30
|
export function configDefinitionsForDomain(domain) {
|
package/dist/config/yuiConfig.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DEFAULT_TURN_CAP, DEFAULT_TERMINAL_KEEP, MAX_TURN_CAP } from "../telemetry/telemetryConfig.js";
|
|
2
2
|
import { DEFAULT_RUNTIME_HEALTH_POLICY } from "../runtime/runtimeHealthPolicy.js";
|
|
3
3
|
export const DEFAULT_RECONCILIATION_INTERVAL_SECONDS = 120;
|
|
4
4
|
export const MIN_RECONCILIATION_INTERVAL_SECONDS = 5;
|
|
@@ -99,14 +99,14 @@ export function resolveTelemetryTerminalKeep(value) {
|
|
|
99
99
|
}
|
|
100
100
|
return value;
|
|
101
101
|
}
|
|
102
|
-
export function
|
|
102
|
+
export function resolveTelemetryTurnCap(value) {
|
|
103
103
|
if (value === undefined || value === null)
|
|
104
|
-
return
|
|
104
|
+
return DEFAULT_TURN_CAP;
|
|
105
105
|
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1) {
|
|
106
|
-
throw new TypeError("
|
|
106
|
+
throw new TypeError("telemetryTurnCap must be a positive integer.");
|
|
107
107
|
}
|
|
108
|
-
if (value >
|
|
109
|
-
throw new TypeError(`
|
|
108
|
+
if (value > MAX_TURN_CAP) {
|
|
109
|
+
throw new TypeError(`telemetryTurnCap must not exceed ${MAX_TURN_CAP.toLocaleString("en-US")} (retention cannot be disabled).`);
|
|
110
110
|
}
|
|
111
111
|
return value;
|
|
112
112
|
}
|
|
@@ -147,43 +147,6 @@ export function resolveLeaderSemanticBudgetTurns(value) {
|
|
|
147
147
|
export function resolveTmuxHistoryLimit(value) {
|
|
148
148
|
return resolveBoundedPositiveInteger(value, DEFAULT_TMUX_HISTORY_LIMIT, 1_000, 1_000_000, "tmuxHistoryLimit");
|
|
149
149
|
}
|
|
150
|
-
/**
|
|
151
|
-
* Compatibility parser for the retired context-budget setting. Existing
|
|
152
|
-
* Homes may retain these values, but no runtime, scheduler, or lifecycle path
|
|
153
|
-
* consumes them. Session Token metrics are an independent read-only view.
|
|
154
|
-
*/
|
|
155
|
-
export const DEFAULT_CONTEXT_SOFT_TOKENS = 100_000;
|
|
156
|
-
export const DEFAULT_CONTEXT_HARD_TOKENS = 120_000;
|
|
157
|
-
export const MIN_CONTEXT_BUDGET_TOKENS = 1_000;
|
|
158
|
-
export const MAX_CONTEXT_BUDGET_TOKENS = 1_000_000;
|
|
159
|
-
function resolveContextBudgetToken(value, label) {
|
|
160
|
-
if (typeof value !== "number" || !Number.isSafeInteger(value)) {
|
|
161
|
-
throw new TypeError(`${label} must be a safe integer.`);
|
|
162
|
-
}
|
|
163
|
-
if (value < MIN_CONTEXT_BUDGET_TOKENS || value > MAX_CONTEXT_BUDGET_TOKENS) {
|
|
164
|
-
throw new TypeError(`${label} must be between ${MIN_CONTEXT_BUDGET_TOKENS} and ${MAX_CONTEXT_BUDGET_TOKENS}.`);
|
|
165
|
-
}
|
|
166
|
-
return value;
|
|
167
|
-
}
|
|
168
|
-
export function resolveContextBudget(configured) {
|
|
169
|
-
if (configured === undefined || configured === null) {
|
|
170
|
-
return { softTokens: DEFAULT_CONTEXT_SOFT_TOKENS, hardTokens: DEFAULT_CONTEXT_HARD_TOKENS };
|
|
171
|
-
}
|
|
172
|
-
if (typeof configured !== "object" || Array.isArray(configured)) {
|
|
173
|
-
throw new TypeError("contextBudget must be an object.");
|
|
174
|
-
}
|
|
175
|
-
const record = configured;
|
|
176
|
-
const softTokens = record.softTokens === undefined
|
|
177
|
-
? DEFAULT_CONTEXT_SOFT_TOKENS
|
|
178
|
-
: resolveContextBudgetToken(record.softTokens, "contextBudget.softTokens");
|
|
179
|
-
const hardTokens = record.hardTokens === undefined
|
|
180
|
-
? DEFAULT_CONTEXT_HARD_TOKENS
|
|
181
|
-
: resolveContextBudgetToken(record.hardTokens, "contextBudget.hardTokens");
|
|
182
|
-
if (softTokens >= hardTokens) {
|
|
183
|
-
throw new TypeError("contextBudget.softTokens must be smaller than contextBudget.hardTokens.");
|
|
184
|
-
}
|
|
185
|
-
return { softTokens, hardTokens };
|
|
186
|
-
}
|
|
187
150
|
function resolveBoundedPositiveInteger(value, fallback, minimum, maximum, label) {
|
|
188
151
|
if (value === undefined || value === null)
|
|
189
152
|
return fallback;
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/** Compatibility entry point used by the restored Task workflow. */
|
|
2
|
-
export function compileDispatchInput(_store, taskId, role, input, workContext = {}) {
|
|
3
|
-
return buildRoleContext({ taskId, role, input, ...workContext });
|
|
4
|
-
}
|
|
5
1
|
export function buildRoleContext(context) {
|
|
6
2
|
return context.role.name === "leader"
|
|
7
3
|
? buildLeaderContext(context)
|
|
@@ -13,40 +9,28 @@ export function buildLeaderContext(context) {
|
|
|
13
9
|
export function buildWorkerContext(context) {
|
|
14
10
|
return renderDispatchContext("worker", context);
|
|
15
11
|
}
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
+ "
|
|
21
|
-
+ "followed by the outcome, evidence, and a closing `YUI_SUMMARY` line, "
|
|
22
|
-
+ "replacing the placeholder with that ID.",
|
|
12
|
+
const WORKER_TURN_COMPLETION_MARKER = "Yui Role Turn result requirement:";
|
|
13
|
+
const WORKER_TURN_COMPLETION_REQUIREMENT = [
|
|
14
|
+
WORKER_TURN_COMPLETION_MARKER,
|
|
15
|
+
"End the Provider turn with a truthful final report. Yui records that native turn result "
|
|
16
|
+
+ "for the current Turn automatically; no completion command is required.",
|
|
23
17
|
"If you cannot finally determine success, failure, completeness, or the correct "
|
|
24
18
|
+ "disposition, do not guess, silently stop, or hide uncertainty behind a success "
|
|
25
|
-
+ "summary.
|
|
19
|
+
+ "summary. Label the final report uncertain, incomplete, "
|
|
26
20
|
+ "blocked, or requiring Leader judgment.",
|
|
27
|
-
"Report the most complete truthful evidence available and, when applicable: exact
|
|
21
|
+
"Report the most complete truthful evidence available and, when applicable: exact Turn, "
|
|
28
22
|
+ "WorkItem, and native Session identity; actions actually performed; changed paths "
|
|
29
23
|
+ "and commit/worktree state; checks actually run and their outcomes; provider, runtime, "
|
|
30
24
|
+ "or permission errors; the last confirmed lifecycle boundary; work not performed; "
|
|
31
25
|
+ "unresolved assumptions or decisions; residual risks; confidence; and bounded next options.",
|
|
32
|
-
"
|
|
33
|
-
+ "
|
|
34
|
-
+ "
|
|
35
|
-
+ "Leader decides disposition.",
|
|
36
|
-
"This exact control-plane permission does not grant repository writes, broad Bash, "
|
|
37
|
-
+ "external effects, or cross-Run control.",
|
|
38
|
-
"If the exact yield is denied, do not retry, broaden permissions, use a wrapper, mutate "
|
|
39
|
-
+ "Yui state, or invent delivery evidence. Truthfully surface the blocker through the "
|
|
40
|
-
+ "supported provider failure boundary; there is no fallback protocol.",
|
|
41
|
-
"Yielding closes the Run and hands the WorkItem to the Leader for acceptance; "
|
|
42
|
-
+ "a final response alone does neither.",
|
|
43
|
-
"The yield command must be your final tool action. After it succeeds, stop immediately: "
|
|
44
|
-
+ "do not inspect, poll, accept, or perform any further work in the same native turn."
|
|
26
|
+
"The Provider turn ending closes only this Turn. It does not imply Leader acceptance, "
|
|
27
|
+
+ "WorkItem completion, ChangeSet capture, Integration, or Task completion. Review Turns "
|
|
28
|
+
+ "report findings, verification gaps, and limits; the Leader decides disposition."
|
|
45
29
|
].join("\n");
|
|
46
|
-
export function
|
|
47
|
-
return input.endsWith(`\n\n${
|
|
30
|
+
export function ensureWorkerTurnCompletionRequirement(input) {
|
|
31
|
+
return input.endsWith(`\n\n${WORKER_TURN_COMPLETION_REQUIREMENT}`)
|
|
48
32
|
? input
|
|
49
|
-
: `${input}\n\n${
|
|
33
|
+
: `${input}\n\n${WORKER_TURN_COMPLETION_REQUIREMENT}`;
|
|
50
34
|
}
|
|
51
35
|
function renderDispatchContext(kind, context) {
|
|
52
36
|
const profile = context.role;
|
|
@@ -74,7 +58,7 @@ function renderDispatchContext(kind, context) {
|
|
|
74
58
|
].filter((section) => section !== null && section.length > 0)
|
|
75
59
|
.join("\n\n");
|
|
76
60
|
return kind === "worker"
|
|
77
|
-
?
|
|
61
|
+
? ensureWorkerTurnCompletionRequirement(rendered)
|
|
78
62
|
: rendered;
|
|
79
63
|
}
|
|
80
64
|
function renderContextLayers(context, kind) {
|
|
@@ -12,7 +12,7 @@ const BUILTIN_YUI_SKILLS = new Set([
|
|
|
12
12
|
]);
|
|
13
13
|
/**
|
|
14
14
|
* Compiles stable Role policy for an Agent-native instruction channel.
|
|
15
|
-
* Dynamic wakeups and
|
|
15
|
+
* Dynamic wakeups and Turn assignments deliberately remain outside this value.
|
|
16
16
|
*/
|
|
17
17
|
export function compileRoleSessionContext(yuiHome, role, owner, options = {}) {
|
|
18
18
|
const kind = roleSessionKind(role, owner, options.purpose ?? "execution");
|
|
@@ -72,7 +72,7 @@ function renderRoleCore(kind, role, owner) {
|
|
|
72
72
|
case "reviewer":
|
|
73
73
|
return [
|
|
74
74
|
`You are Yui review Role ${role.name} for Task ${owner.scope === "task" ? owner.taskId : role.name}.`,
|
|
75
|
-
"Review only the exact frozen ReviewRound scope assigned to this
|
|
75
|
+
"Review only the exact frozen ReviewRound scope assigned to this Turn.",
|
|
76
76
|
"Follow the injected yui-reviewer Skill while reviewing Yui work."
|
|
77
77
|
];
|
|
78
78
|
case "worker":
|
|
@@ -133,8 +133,8 @@ export function materializeSessionBootstrap(input) {
|
|
|
133
133
|
loadCommand: "\"$YUI_SESSION_CLI\" session context \"$YUI_ROLE\" --json"
|
|
134
134
|
}
|
|
135
135
|
: {
|
|
136
|
-
loadCommand: "\"$YUI_SESSION_CLI\" task
|
|
137
|
-
expandCommand: "\"$YUI_SESSION_CLI\" task
|
|
136
|
+
loadCommand: "\"$YUI_SESSION_CLI\" task turn context \"$YUI_TASK_ID/<turn-id>\" --json",
|
|
137
|
+
expandCommand: "\"$YUI_SESSION_CLI\" task turn context expand \"$YUI_TASK_ID/<turn-id>\" <ref-id> --store <store> --mode full --json"
|
|
138
138
|
}
|
|
139
139
|
};
|
|
140
140
|
const manifest = Object.freeze({ ...body, digest: digest(body) });
|