@zq-silk/yui 1.1.0 → 2.0.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 +2 -1
- package/ARCHITECTURE.zh-CN.md +2 -1
- package/README.md +3 -1
- package/dist/cli/commandCatalog.js +66 -54
- package/dist/cli/managedDiagnostics.js +2 -1
- package/dist/cli/updatePorts.js +0 -5
- package/dist/cli.js +113 -107
- package/dist/commands/controllerCommands.js +2 -1
- package/dist/commands/globalRoleCommands.js +84 -43
- package/dist/commands/grantCommands.js +48 -8
- package/dist/commands/taskCommands.js +146 -196
- package/dist/commands/taskContextCommand.js +29 -6
- package/dist/commands/taskFactCommands.js +10 -69
- package/dist/commands/taskInputCommands.js +26 -35
- package/dist/commands/taskPublicationCommands.js +2 -35
- package/dist/commands/taskPublicationVerifyCommand.js +1 -2
- package/dist/commands/taskUpstreamCommands.js +2 -1
- package/dist/context/runContextPack.js +8 -29
- package/dist/context/sessionBootstrapManifest.js +5 -101
- package/dist/context/taskContext.js +98 -39
- package/dist/controller/clientRuntime.js +16 -8
- package/dist/controller/fileSchedulerStoreAdapter.js +24 -0
- package/dist/controller/runtime.js +6 -0
- package/dist/core/controllerClient.js +46 -21
- package/dist/core/protocol.js +12 -7
- package/dist/doctor/doctor.js +6 -3
- package/dist/doctor/ptyProbe.js +74 -0
- package/dist/doctor/ptyProbeChild.js +90 -0
- package/dist/errors/cliError.js +4 -4
- package/dist/errors/cliFailure.js +213 -0
- package/dist/executor/fileRoleLaunchPlanner.js +1 -4
- package/dist/grant/capabilityGrant.js +13 -0
- package/dist/grant/taskAuthorization.js +127 -0
- package/dist/kernel/builtinCapabilities.js +60 -8
- package/dist/kernel/kernelPorts.js +2 -2
- package/dist/output/boundedRead.js +118 -0
- package/dist/release/releaseWorkflowEngine.js +8 -1
- package/dist/runtime/managedCaller.js +2 -2
- package/dist/runtime/runtimeCoherence.js +10 -8
- package/dist/storage/sqliteSchema.js +10 -0
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/taskStore.js +3 -0
- package/dist/task/leaderArchive.js +111 -0
- package/dist/task/leaderArchiveAuthority.js +24 -0
- package/dist/tmux/commandExecutor.js +7 -5
- package/docs/architecture/README.md +1 -0
- package/docs/architecture/README.zh-CN.md +1 -0
- package/docs/cli-information-contract.md +107 -0
- package/docs/cli-information-contract.zh-CN.md +81 -0
- package/docs/managed-turn-and-session-runtime.md +9 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +6 -0
- package/docs/plugin-sdk.md +7 -3
- package/docs/plugin-sdk.zh-CN.md +5 -2
- package/docs/release-workflow.md +19 -9
- package/docs/release-workflow.zh-CN.md +14 -7
- package/docs/storage-baseline.md +5 -0
- package/docs/testing/verification-levels.md +12 -2
- package/docs/testing/verification-levels.zh-CN.md +8 -0
- package/i18n/README.zh-CN.md +3 -1
- package/package.json +3 -3
- package/skills/yui-leader/SKILL.md +8 -1
- package/skills/yui-leader/references/authorization.md +64 -0
- package/skills/yui-leader/references/execution.md +18 -4
- package/skills/yui-leader/references/task-plugins.md +4 -2
- package/skills/yui-operator/SKILL.md +9 -2
- package/skills/yui-runtime/SKILL.md +51 -9
- package/skills/yui-runtime/references/publication.md +3 -1
package/dist/cli.js
CHANGED
|
@@ -20,7 +20,7 @@ import { allowsInteractiveSelection, resolveInteractiveArguments } from "./cli/i
|
|
|
20
20
|
import { routeInvocation } from "./cli/invocationRouter.js";
|
|
21
21
|
import { operatorOfflineCommand, taskDiagnosticTarget } from "./cli/managedDiagnostics.js";
|
|
22
22
|
import { assertConfigurationAuthority, assertTaskInvocationScope } from "./cli/invocationAuthority.js";
|
|
23
|
-
import { requireManagedGlobalCaller } from "./runtime/managedCaller.js";
|
|
23
|
+
import { requireManagedGlobalCaller, ManagedRuntimeDriftError } from "./runtime/managedCaller.js";
|
|
24
24
|
import { resolveOperatorWizardArguments } from "./cli/operatorWizard.js";
|
|
25
25
|
import { resolveGlobalRoleAgentConfigurationArguments, resolveRoleWizardArguments } from "./cli/roleWizard.js";
|
|
26
26
|
import { runUpdateCommand } from "./cli/updateCommand.js";
|
|
@@ -41,7 +41,7 @@ import { runProjectCommand } from "./commands/projectCommands.js";
|
|
|
41
41
|
import { renderReleaseActivateResult, renderReleaseInstallResult, renderReleaseList, resolveReleaseActivationDriver, runReleaseActivate, runReleaseInstall, runReleaseList } from "./commands/releaseCommands.js";
|
|
42
42
|
import { runResourcesCommand } from "./commands/resourcesCommands.js";
|
|
43
43
|
import { parseSessionReconcileOptions, parseSessionStopOptions, runSessionReconcileCommand, runSessionStopCommand } from "./commands/sessionCommands.js";
|
|
44
|
-
import { assertTaskDeliveryAuthority, taskLocalActor } from "./task/taskAuthority.js";
|
|
44
|
+
import { assertTaskDeliveryAuthority, taskLocalActor, resolveJobCaller } from "./task/taskAuthority.js";
|
|
45
45
|
import { runTaskBaseStatusCommand } from "./commands/taskBaseCommands.js";
|
|
46
46
|
import { runTaskChangeSetCommand } from "./commands/taskChangeSetCommands.js";
|
|
47
47
|
import { assertWorkItemDependenciesCompletedForCommand, dispatchPreparedReviewRound, failPendingReviewRound, parseTaskArchiveArguments, parseTaskCompletionRequest, planReplicatedWorkItemLanes, preflightTaskCompletion, preserveReviewRoundWorkspace, previewTaskRoleAgentConfigurationMutation, requireWorkItemAssignee, runTaskCommand, validateTaskArchiveRequest } from "./commands/taskCommands.js";
|
|
@@ -58,7 +58,7 @@ import { runWorkflowCommandAsync } from "./commands/workflowCommands.js";
|
|
|
58
58
|
import { FileCompletionManager } from "./completion/fileCompletionManager.js";
|
|
59
59
|
import { CONFIG_DOMAINS } from "./config/configCatalog.js";
|
|
60
60
|
import { resolveTmuxBin, resolveTmuxHistoryLimit } from "./config/yuiConfig.js";
|
|
61
|
-
import { readSessionBootstrapManifest
|
|
61
|
+
import { readSessionBootstrapManifest } from "./context/sessionBootstrapManifest.js";
|
|
62
62
|
import { assertFileTaskControllerStorageCompatible, ensureFileTaskController, FileTaskWorkflowRuntime, refreshRunningFileTaskControllerConfiguration, refreshRunningFileTaskControllerEnvironment, restartFileTaskController, stopFileTaskController } from "./controller/clientRuntime.js";
|
|
63
63
|
import { FileSchedulerStoreAdapter } from "./controller/fileSchedulerStoreAdapter.js";
|
|
64
64
|
import { createControllerIntegrationJobPort } from "./controller/jobClient.js";
|
|
@@ -70,6 +70,7 @@ import { SessionOwnerReconciliation } from "./controller/sessionOwnerReconciliat
|
|
|
70
70
|
import { callController, ControllerClientError } from "./core/controllerClient.js";
|
|
71
71
|
import { buildDoctorReport, renderDoctor, runDoctorCommand } from "./doctor/doctor.js";
|
|
72
72
|
import { agentNotFound, CliError, runtimeError, usageError } from "./errors/cliError.js";
|
|
73
|
+
import { describeCliFailure, renderCliFailure, sanitizeFailureText, sanitizeFailureDetails } from "./errors/cliFailure.js";
|
|
73
74
|
import { AgentConfigurationCatalogService, validateAgentLaunchConfiguration } from "./executor/agentConfigurationCatalog.js";
|
|
74
75
|
import { FileRoleLaunchPlanner } from "./executor/fileRoleLaunchPlanner.js";
|
|
75
76
|
import { createGitHubCliPublicationVerifier } from "./external/githubPublicationVerifier.js";
|
|
@@ -82,7 +83,7 @@ import { parseTaskAgentCapabilityQuery } from "./commands/taskAgentCapabilities.
|
|
|
82
83
|
import { capabilityReaderIdentity } from "./controller/agentCapabilities.js";
|
|
83
84
|
import { formatTimestamp } from "./output/timePresentation.js";
|
|
84
85
|
import { resolveAgentProfileView } from "./profile/agentProfileRuntime.js";
|
|
85
|
-
import { assertCliHomeReleaseFence
|
|
86
|
+
import { assertCliHomeReleaseFence } from "./release/cliHomeReleaseFence.js";
|
|
86
87
|
import { createReleaseWorkflowPorts } from "./release/releaseWorkflowPorts.js";
|
|
87
88
|
import { acquireHandoverLock, readRuntimeIdentity } from "./release/runtimeRelease.js";
|
|
88
89
|
import { NodeGitWorkspace } from "./repository/gitWorkspace.js";
|
|
@@ -102,7 +103,6 @@ import { requireManagedTaskCaller, resolveManagedTaskReader } from "./runtime/ma
|
|
|
102
103
|
import { assertRuntimeCoherence } from "./runtime/runtimeCoherence.js";
|
|
103
104
|
import { runSetupCommand, validateSetupInvocation } from "./setup/setupCommand.js";
|
|
104
105
|
import { openCurrentTaskStore, validateCurrentTaskStore } from "./storage/currentTaskStore.js";
|
|
105
|
-
import { SqliteSchemaError } from "./storage/sqliteSchema.js";
|
|
106
106
|
import { inspectStorageSchema } from "./storage/storageSchema.js";
|
|
107
107
|
import { resolveYuiHome } from "./storage/taskStore.js";
|
|
108
108
|
import { renderArchiveDiagnostics, taskArchiveDiagnostics } from "./task/archiveDiagnostics.js";
|
|
@@ -123,47 +123,37 @@ const taskFinalReviewInvocation = extractTaskFinalReviewRequest(process.argv.sli
|
|
|
123
123
|
const rawArgs = [...taskFinalReviewInvocation.args];
|
|
124
124
|
const jsonOutput = rawArgs.includes("--json");
|
|
125
125
|
const args = normalizeAliases(jsonOutput ? rawArgs.filter((argument) => argument !== "--json") : rawArgs);
|
|
126
|
+
let invocationHome;
|
|
126
127
|
void main().catch((error) => {
|
|
127
128
|
if (error instanceof CleanupInspectionError) {
|
|
128
129
|
error = cleanupCliError(error, error.checks[0]?.resource ?? "workspace");
|
|
129
130
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
: `${error.code}: ${error.message}${error.helpText === undefined ? "" : `\n\n${error.helpText.trimEnd()}`}`;
|
|
134
|
-
process.stderr.write(`${rendered}\n`);
|
|
135
|
-
process.exitCode = error.exitCode;
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
const message = runtimeFailureMessage(error);
|
|
139
|
-
process.stderr.write(`${jsonOutput
|
|
140
|
-
? JSON.stringify({ ok: false, code: "RUNTIME_ERROR", message, details: {} })
|
|
141
|
-
: `RUNTIME_ERROR: ${message}`}\n`);
|
|
142
|
-
process.exitCode = 5;
|
|
131
|
+
const failure = describeCliFailure(error, failureContext());
|
|
132
|
+
process.stderr.write(`${renderCliFailure(failure, jsonOutput)}\n`);
|
|
133
|
+
process.exitCode = failure.exitCode;
|
|
143
134
|
});
|
|
144
|
-
function
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
return message;
|
|
163
|
-
}
|
|
135
|
+
function failureContext() {
|
|
136
|
+
const route = routeInvocation(args);
|
|
137
|
+
const node = "node" in route ? route.node : route.helpNode;
|
|
138
|
+
const operands = args.slice(node.path.length - 1);
|
|
139
|
+
const publicValues = new Set([
|
|
140
|
+
...Object.values(node.argumentValues).flat(),
|
|
141
|
+
...Object.values(node.optionValues).flat()
|
|
142
|
+
]);
|
|
143
|
+
const recordId = /^(?:task|project|message|run|work|wake|operation|review|input|job|integration|publication)-\d+(?:\/(?:message|run|work|wake|review|input|job|integration|publication)-\d+)?$/;
|
|
144
|
+
return {
|
|
145
|
+
operation: node.path.join(" "),
|
|
146
|
+
targets: operands.filter(value => recordId.test(value)),
|
|
147
|
+
// Do not replay argv: operands include Message bodies, config values and credentials.
|
|
148
|
+
privateValues: operands.filter(value => !/^--[a-z][a-z-]*$/.test(value) && !recordId.test(value) && !publicValues.has(value)),
|
|
149
|
+
home: invocationHome ?? process.env.YUI_HOME ?? "(default Home unresolved)",
|
|
150
|
+
cli: fileURLToPath(import.meta.url),
|
|
151
|
+
environment: process.env
|
|
152
|
+
};
|
|
164
153
|
}
|
|
165
154
|
export async function main() {
|
|
166
155
|
const home = resolveYuiHome(process.env);
|
|
156
|
+
invocationHome = home;
|
|
167
157
|
const routedForFence = args.length === 0 ? undefined : routeInvocation(args);
|
|
168
158
|
const managedInvocation = process.env.YUI_SESSION_SCOPE === "task"
|
|
169
159
|
|| process.env.YUI_SESSION_SCOPE === "global";
|
|
@@ -188,7 +178,7 @@ export async function main() {
|
|
|
188
178
|
stdio: "inherit"
|
|
189
179
|
});
|
|
190
180
|
if (delegated.error !== undefined) {
|
|
191
|
-
throw runtimeError(
|
|
181
|
+
throw runtimeError("Target release activation driver could not start.", { cause: delegated.error });
|
|
192
182
|
}
|
|
193
183
|
if (delegated.signal !== null) {
|
|
194
184
|
throw runtimeError(`Target release activation driver stopped by signal ${delegated.signal}.`);
|
|
@@ -240,7 +230,10 @@ export async function main() {
|
|
|
240
230
|
const subcommand = args[1];
|
|
241
231
|
if (subcommand === "install" && args.length === 3) {
|
|
242
232
|
const result = runReleaseInstall(home, args[2]);
|
|
243
|
-
|
|
233
|
+
if (result.outcome === "aborted")
|
|
234
|
+
emitFailureResult(renderReleaseInstallResult(result), result);
|
|
235
|
+
else
|
|
236
|
+
emit(renderReleaseInstallResult(result), false, result);
|
|
244
237
|
if (result.outcome === "aborted")
|
|
245
238
|
process.exitCode = 5;
|
|
246
239
|
return;
|
|
@@ -259,7 +252,11 @@ export async function main() {
|
|
|
259
252
|
+ "(or activate the active release when exactly one is installed).");
|
|
260
253
|
}
|
|
261
254
|
const result = await runReleaseActivate(home, releaseId);
|
|
262
|
-
|
|
255
|
+
if (result.outcome === "aborted" || result.outcome === "dual-owner") {
|
|
256
|
+
emitFailureResult(renderReleaseActivateResult(result), result);
|
|
257
|
+
}
|
|
258
|
+
else
|
|
259
|
+
emit(renderReleaseActivateResult(result), false, result);
|
|
263
260
|
if (result.outcome === "aborted" || result.outcome === "dual-owner") {
|
|
264
261
|
process.exitCode = 5;
|
|
265
262
|
}
|
|
@@ -356,7 +353,7 @@ export async function main() {
|
|
|
356
353
|
codex = await prepareLocalCodexStartup(startupStore, home, process.env);
|
|
357
354
|
}
|
|
358
355
|
catch (error) {
|
|
359
|
-
throw runtimeError(
|
|
356
|
+
throw runtimeError("Controller is ready, but Codex shared startup failed.", { cause: error });
|
|
360
357
|
}
|
|
361
358
|
const lines = ["Yui Controller is ready."];
|
|
362
359
|
if (codex.daemons.length === 0)
|
|
@@ -398,20 +395,13 @@ export async function main() {
|
|
|
398
395
|
if (args[0] === "upgrade") {
|
|
399
396
|
const result = await runUpgradeCommand(args.slice(1), home);
|
|
400
397
|
process.exitCode = result.exitCode;
|
|
401
|
-
|
|
398
|
+
if (result.exitCode !== 0)
|
|
399
|
+
emitFailureResult(result.output, result.data);
|
|
400
|
+
else
|
|
401
|
+
emit(result.output, false, result.data);
|
|
402
402
|
return;
|
|
403
403
|
}
|
|
404
404
|
if (args[0] === "internal") {
|
|
405
|
-
if (args[1] === "session-cli-refresh" && args.length === 2) {
|
|
406
|
-
if (process.env.YUI_SESSION_SCOPE === "task"
|
|
407
|
-
|| (process.env.YUI_SESSION_SCOPE === "global" && process.env.YUI_ROLE !== "operator")) {
|
|
408
|
-
throw usageError("Managed Session CLI refresh may be run only by the user or global Operator.");
|
|
409
|
-
}
|
|
410
|
-
const result = refreshManagedSessionCliWrappers(home, currentInvocationEntryPoint());
|
|
411
|
-
emit(`Refreshed ${result.refreshed} managed Session CLI wrapper(s); `
|
|
412
|
-
+ `${result.current} already current, ${result.skipped} skipped.`, false, result);
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
405
|
if (args[1] === "agent-host" && args.length === 3) {
|
|
416
406
|
process.exitCode = await runAgentHost({
|
|
417
407
|
home,
|
|
@@ -483,17 +473,7 @@ export async function main() {
|
|
|
483
473
|
catch (error) {
|
|
484
474
|
if (!(error instanceof ControllerClientError))
|
|
485
475
|
throw error;
|
|
486
|
-
|
|
487
|
-
process.stderr.write(`${JSON.stringify({
|
|
488
|
-
ok: false,
|
|
489
|
-
code: error.code,
|
|
490
|
-
message: error.message,
|
|
491
|
-
details: {}
|
|
492
|
-
})}\n`);
|
|
493
|
-
}
|
|
494
|
-
else {
|
|
495
|
-
process.stderr.write(`RUNTIME_ERROR: ${error.message}\n`);
|
|
496
|
-
}
|
|
476
|
+
process.stderr.write(`${renderCliFailure(describeCliFailure(error, failureContext()), jsonOutput, error.code)}\n`);
|
|
497
477
|
process.exitCode = 5;
|
|
498
478
|
}
|
|
499
479
|
return;
|
|
@@ -517,17 +497,7 @@ export async function main() {
|
|
|
517
497
|
// unknown-active block.
|
|
518
498
|
if (!(error instanceof ControllerClientError))
|
|
519
499
|
throw error;
|
|
520
|
-
|
|
521
|
-
process.stderr.write(`${JSON.stringify({
|
|
522
|
-
ok: false,
|
|
523
|
-
code: error.code,
|
|
524
|
-
message: error.message,
|
|
525
|
-
details: {}
|
|
526
|
-
})}\n`);
|
|
527
|
-
}
|
|
528
|
-
else {
|
|
529
|
-
process.stderr.write(`RUNTIME_ERROR: ${error.message}\n`);
|
|
530
|
-
}
|
|
500
|
+
process.stderr.write(`${renderCliFailure(describeCliFailure(error, failureContext()), jsonOutput, error.code)}\n`);
|
|
531
501
|
process.exitCode = 5;
|
|
532
502
|
}
|
|
533
503
|
return;
|
|
@@ -674,7 +644,7 @@ export async function main() {
|
|
|
674
644
|
yuiHome: home,
|
|
675
645
|
historyLimit: resolveTmuxHistoryLimit(store.getConfig().tmuxHistoryLimit),
|
|
676
646
|
terminalInput: process.stdin,
|
|
677
|
-
onWarning: (message) =>
|
|
647
|
+
onWarning: (message) => writeCliWarning(message)
|
|
678
648
|
});
|
|
679
649
|
const telemetry = openSchedulerTelemetry(home, store.getConfig());
|
|
680
650
|
try {
|
|
@@ -684,8 +654,11 @@ export async function main() {
|
|
|
684
654
|
const runtime = new FileTaskWorkflowRuntime(home, store, schedulerStore, planner, tmux, workspacePreparer, {
|
|
685
655
|
environment: process.env,
|
|
686
656
|
onError: (error) => {
|
|
687
|
-
|
|
688
|
-
|
|
657
|
+
// Advisory async failure: preserve the foreground command's exit status.
|
|
658
|
+
const failure = describeCliFailure(error, failureContext());
|
|
659
|
+
process.stderr.write(`${renderCliFailure({
|
|
660
|
+
...failure, details: { ...failure.details, advisory: true }
|
|
661
|
+
}, jsonOutput)}\n`);
|
|
689
662
|
}
|
|
690
663
|
});
|
|
691
664
|
const workspaceCoordinator = new TaskWorkspaceCoordinator(store, workspacePreparer, runtime);
|
|
@@ -845,6 +818,10 @@ export async function main() {
|
|
|
845
818
|
? [resolved[1], ...resolved.slice(2)]
|
|
846
819
|
: ["session", resolved[1] ?? "", ...resolved.slice(2)];
|
|
847
820
|
const result = runGlobalRoleCommand(sessionArgs, store, roleOptions);
|
|
821
|
+
if (typeof result !== "string" && result.kind === "output") {
|
|
822
|
+
emit(result.output, false, result.data);
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
848
825
|
if (typeof result === "string") {
|
|
849
826
|
emit(result);
|
|
850
827
|
return;
|
|
@@ -893,6 +870,14 @@ export async function main() {
|
|
|
893
870
|
emit(result);
|
|
894
871
|
return;
|
|
895
872
|
}
|
|
873
|
+
if (result.kind === "output") {
|
|
874
|
+
if (globalInputFailure !== undefined) {
|
|
875
|
+
emitControlFailure(globalInputFailure.detail, globalInputFailure.code, globalInputFailure.data);
|
|
876
|
+
}
|
|
877
|
+
else
|
|
878
|
+
emit(result.output, false, result.data);
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
896
881
|
if (result.kind === "input-steer") {
|
|
897
882
|
// Core already persisted the durable Global Message and proved target +
|
|
898
883
|
// capability + writer fence from the Global Role's own Session set. This
|
|
@@ -1101,8 +1086,7 @@ export async function main() {
|
|
|
1101
1086
|
finalizeStoppedTaskExecution(result.taskId, store);
|
|
1102
1087
|
}
|
|
1103
1088
|
catch (error) {
|
|
1104
|
-
|
|
1105
|
-
throw runtimeError(`Task execution is stopped and durable progress is preserved, but physical runtime cleanup failed: ${message}`);
|
|
1089
|
+
throw runtimeError("Task execution is stopped and durable progress is preserved, but physical runtime cleanup failed.", { cause: error });
|
|
1106
1090
|
}
|
|
1107
1091
|
emit(result.output, false, result);
|
|
1108
1092
|
return;
|
|
@@ -1317,6 +1301,19 @@ export async function main() {
|
|
|
1317
1301
|
let archiveRemoteDeliveryProof;
|
|
1318
1302
|
let archiveTaskReviewCandidate;
|
|
1319
1303
|
if (resolved[1] === "archive") {
|
|
1304
|
+
const request = parseTaskArchiveArguments(resolved.slice(2));
|
|
1305
|
+
if (request.sourceMessage !== undefined) {
|
|
1306
|
+
if (request.force || request.disposition !== "integrated") {
|
|
1307
|
+
throw usageError("Leader ordinary archive does not authorize --force or --abandon.");
|
|
1308
|
+
}
|
|
1309
|
+
const result = await callController(home, "capability.call", {
|
|
1310
|
+
taskId: request.taskId, caller: resolveJobCaller(process.env, request.taskId),
|
|
1311
|
+
request: { name: "task.archive", requestId: request.requestId,
|
|
1312
|
+
input: { taskId: request.taskId, sourceMessage: request.sourceMessage, purpose: request.purpose } }
|
|
1313
|
+
});
|
|
1314
|
+
emit("Controller archive result; inspect task events if this Session ends before the receipt.\n", false, result);
|
|
1315
|
+
return;
|
|
1316
|
+
}
|
|
1320
1317
|
const { taskId, disposition, force } = validateTaskArchiveRequest(resolved.slice(2), store, {
|
|
1321
1318
|
runtime,
|
|
1322
1319
|
environment: process.env,
|
|
@@ -1497,7 +1494,7 @@ export async function main() {
|
|
|
1497
1494
|
})
|
|
1498
1495
|
});
|
|
1499
1496
|
for (const warning of completionWarnings) {
|
|
1500
|
-
|
|
1497
|
+
writeCliWarning(warning);
|
|
1501
1498
|
}
|
|
1502
1499
|
// Refresh observes remote objects only. Choosing and executing an
|
|
1503
1500
|
// upstream Integration belongs to its explicit command, never completion.
|
|
@@ -2035,7 +2032,7 @@ async function preflightManagedGlobalControlPlane() {
|
|
|
2035
2032
|
const expectedRoleKind = process.env.YUI_ROLE === "operator" ? "operator" : "global";
|
|
2036
2033
|
if (manifest.owner.scope !== "global"
|
|
2037
2034
|
|| manifest.roleKind !== expectedRoleKind) {
|
|
2038
|
-
throw new
|
|
2035
|
+
throw new ManagedRuntimeDriftError("Managed global invocation does not match its Session Manifest.");
|
|
2039
2036
|
}
|
|
2040
2037
|
if (expectedRoleKind === "operator" && ["doctor", "upgrade", "update"].includes(args[0] ?? "")) {
|
|
2041
2038
|
// These commands inspect/adopt the storage contract themselves. Requiring
|
|
@@ -2059,28 +2056,24 @@ async function preflightManagedGlobalControlPlane() {
|
|
|
2059
2056
|
}
|
|
2060
2057
|
return { contract: undefined, verifiedStore };
|
|
2061
2058
|
}
|
|
2062
|
-
/**
|
|
2063
|
-
* The entry point the current invocation resolves to. Only its executable and
|
|
2064
|
-
* CLI path are used, to retarget managed Session wrappers at this installation.
|
|
2065
|
-
*/
|
|
2066
|
-
function currentInvocationEntryPoint() {
|
|
2067
|
-
return {
|
|
2068
|
-
executable: process.execPath,
|
|
2069
|
-
cliEntry: fileURLToPath(import.meta.url)
|
|
2070
|
-
};
|
|
2071
|
-
}
|
|
2072
2059
|
function assertManagedSessionManifest(home, scope) {
|
|
2073
2060
|
const manifestPath = process.env.YUI_SESSION_MANIFEST;
|
|
2074
2061
|
if (manifestPath === undefined) {
|
|
2075
|
-
throw new
|
|
2062
|
+
throw new ManagedRuntimeDriftError("Managed control-plane invocation requires its Session Manifest.");
|
|
2063
|
+
}
|
|
2064
|
+
let manifest;
|
|
2065
|
+
try {
|
|
2066
|
+
manifest = readSessionBootstrapManifest(manifestPath);
|
|
2067
|
+
}
|
|
2068
|
+
catch (error) {
|
|
2069
|
+
throw new ManagedRuntimeDriftError("Managed Session Manifest could not be verified.", { cause: error });
|
|
2076
2070
|
}
|
|
2077
|
-
const manifest = readSessionBootstrapManifest(manifestPath);
|
|
2078
2071
|
if (manifest.owner.scope !== scope) {
|
|
2079
|
-
throw new
|
|
2072
|
+
throw new ManagedRuntimeDriftError("Managed invocation scope does not match its Session Manifest.");
|
|
2080
2073
|
}
|
|
2081
2074
|
if (scope === "task" && (manifest.owner.scope !== "task"
|
|
2082
2075
|
|| manifest.owner.taskId !== process.env.YUI_TASK_ID)) {
|
|
2083
|
-
throw new
|
|
2076
|
+
throw new ManagedRuntimeDriftError("Managed Task invocation does not match its Session Manifest owner.");
|
|
2084
2077
|
}
|
|
2085
2078
|
const expectedPath = resolve(home, "runtime", "session-manifests", `${manifest.digest}.json`);
|
|
2086
2079
|
// YUI_HOME is canonicalized (its macOS /var -> /private/var prefix is a
|
|
@@ -2093,7 +2086,7 @@ function assertManagedSessionManifest(home, scope) {
|
|
|
2093
2086
|
canonicalManifestPath = resolve(manifestPath);
|
|
2094
2087
|
}
|
|
2095
2088
|
if (canonicalManifestPath !== expectedPath) {
|
|
2096
|
-
throw new
|
|
2089
|
+
throw new ManagedRuntimeDriftError("Managed Session Manifest path is outside this YUI_HOME.");
|
|
2097
2090
|
}
|
|
2098
2091
|
return manifest;
|
|
2099
2092
|
}
|
|
@@ -2104,7 +2097,7 @@ function cleanupCliError(error, fallbackResource) {
|
|
|
2104
2097
|
if (error instanceof WorkspaceCleanupBlockedError) {
|
|
2105
2098
|
return usageError(error.message, undefined, cleanupBlockedDetails(error.reason, error.resource, error.retryable));
|
|
2106
2099
|
}
|
|
2107
|
-
return new CliError("RUNTIME_ERROR", error instanceof Error ? error.message : String(error), undefined, cleanupBlockedDetails("cleanup-failed", fallbackResource, true));
|
|
2100
|
+
return new CliError("RUNTIME_ERROR", error instanceof Error ? error.message : String(error), undefined, cleanupBlockedDetails("cleanup-failed", fallbackResource, true), { cause: error });
|
|
2108
2101
|
}
|
|
2109
2102
|
function cleanupBlockedDetails(reason, resource, retryable, checks) {
|
|
2110
2103
|
return {
|
|
@@ -2540,7 +2533,7 @@ async function prepareCodexForCurrentRoles(store, home, targetAgentId) {
|
|
|
2540
2533
|
}
|
|
2541
2534
|
}
|
|
2542
2535
|
catch (error) {
|
|
2543
|
-
const detail = error
|
|
2536
|
+
const detail = describeCliFailure(error, failureContext()).message;
|
|
2544
2537
|
const permissionDenied = isCodexInspectionPermissionError(error);
|
|
2545
2538
|
codexAppNotice(`${permissionDenied
|
|
2546
2539
|
? "Codex sharing could not be prepared or verified from this process"
|
|
@@ -2559,7 +2552,7 @@ function assertHomeStartupAuthority(store) {
|
|
|
2559
2552
|
requireManagedGlobalCaller(store, process.env);
|
|
2560
2553
|
}
|
|
2561
2554
|
function codexAppNotice(message, notification) {
|
|
2562
|
-
|
|
2555
|
+
writeCliWarning(message);
|
|
2563
2556
|
if (process.platform !== "darwin")
|
|
2564
2557
|
return;
|
|
2565
2558
|
// The native TUI clears terminal output on attach, so keep the actionable
|
|
@@ -2569,6 +2562,12 @@ function codexAppNotice(message, notification) {
|
|
|
2569
2562
|
`display notification "${notification}" with title "Yui"`
|
|
2570
2563
|
], { stdio: "ignore", timeout: 3_000 });
|
|
2571
2564
|
}
|
|
2565
|
+
function writeCliWarning(message) {
|
|
2566
|
+
const safe = sanitizeFailureText(message, failureContext());
|
|
2567
|
+
process.stderr.write(`${jsonOutput
|
|
2568
|
+
? JSON.stringify({ ok: false, code: "RUNTIME_ERROR", message: safe, details: { advisory: true } })
|
|
2569
|
+
: `Warning: ${safe}`}\n`);
|
|
2570
|
+
}
|
|
2572
2571
|
function renderControllerResult(method, value) {
|
|
2573
2572
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
2574
2573
|
return JSON.stringify(value);
|
|
@@ -2771,13 +2770,12 @@ async function warmConfigurationMutationCatalog(commandArgs, store, catalogs) {
|
|
|
2771
2770
|
async function liveHostObservationsForTaskCommand(args, store, home) {
|
|
2772
2771
|
if (args[0] !== "task" || args[1] !== "role")
|
|
2773
2772
|
return undefined;
|
|
2774
|
-
const inspect = args[2] === "session" && args[3] === "inspect" && args.length
|
|
2775
|
-
const status = args[2] === "status" && args.length
|
|
2776
|
-
|
|
2777
|
-
if (!inspect && !status && !list)
|
|
2773
|
+
const inspect = args[2] === "session" && args[3] === "inspect" && args.length >= 6;
|
|
2774
|
+
const status = args[2] === "status" && args.length >= 5;
|
|
2775
|
+
if (!inspect && !status)
|
|
2778
2776
|
return undefined;
|
|
2779
2777
|
const taskId = args[inspect ? 4 : 3];
|
|
2780
|
-
const roles =
|
|
2778
|
+
const roles = [args[inspect ? 5 : 4]];
|
|
2781
2779
|
const entries = await Promise.all(roles.map(async (roleName) => [
|
|
2782
2780
|
roleName, await readLiveHostObservation(store, home, taskId, roleName)
|
|
2783
2781
|
]));
|
|
@@ -2954,9 +2952,17 @@ function emit(output, literal = false, data) {
|
|
|
2954
2952
|
}
|
|
2955
2953
|
function emitControlFailure(message, code, details) {
|
|
2956
2954
|
process.exitCode = 2;
|
|
2955
|
+
const context = failureContext();
|
|
2956
|
+
const safeMessage = sanitizeFailureText(message.trim(), context);
|
|
2957
|
+
const safeDetails = sanitizeFailureDetails({ value: details }, context).value;
|
|
2957
2958
|
process.stdout.write(`${jsonOutput
|
|
2958
|
-
? JSON.stringify({ ok: false, code, message:
|
|
2959
|
-
:
|
|
2959
|
+
? JSON.stringify({ ok: false, code, message: safeMessage, details: safeDetails })
|
|
2960
|
+
: safeMessage}\n`);
|
|
2961
|
+
}
|
|
2962
|
+
/** Domain-owned failure reports retain their output envelope and effect receipts. */
|
|
2963
|
+
function emitFailureResult(output, data) {
|
|
2964
|
+
const context = failureContext();
|
|
2965
|
+
emit(sanitizeFailureText(output, context), false, sanitizeFailureDetails({ value: data }, context).value);
|
|
2960
2966
|
}
|
|
2961
2967
|
/**
|
|
2962
2968
|
* The human line for a live steer, corrected to the *actual* live acceptance
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { usageError } from "../errors/cliError.js";
|
|
2
|
+
import { describeCliFailure } from "../errors/cliFailure.js";
|
|
2
3
|
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
3
4
|
import { DEFAULT_RECONCILIATION_INTERVAL_SECONDS } from "../config/yuiConfig.js";
|
|
4
5
|
import { evaluateStorageHealth, UNSUPPORTED } from "../observability/runtimeIdentity.js";
|
|
@@ -280,7 +281,7 @@ export async function runInteractiveControllerCleanup(input) {
|
|
|
280
281
|
catch (error) {
|
|
281
282
|
failed.push({
|
|
282
283
|
id: resource.id,
|
|
283
|
-
message: error
|
|
284
|
+
message: describeCliFailure(error).message
|
|
284
285
|
});
|
|
285
286
|
}
|
|
286
287
|
}
|