@cydm/happy-elves 0.1.0-beta.80 → 0.1.0-beta.81
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/apps/cli/dist/commands/daemon.js +3 -3
- package/apps/cli/dist/commands/lib/bootstrap.js +5 -2
- package/apps/cli/dist/commands/lib/exit.js +11 -0
- package/apps/cli/dist/commands/lib/session-output.d.ts +3 -1
- package/apps/cli/dist/commands/lib/session-output.js +8 -4
- package/apps/cli/dist/commands/lib/session-view.d.ts +6 -0
- package/apps/cli/dist/commands/lib/session-view.js +9 -1
- package/apps/cli/dist/commands/lib/usage.js +2 -2
- package/apps/cli/dist/commands/orchestrator.js +118 -14
- package/apps/cli/dist/commands/session.js +10 -3
- package/apps/cli/dist/commands/skill.js +14 -0
- package/apps/cli/dist/commands/turn.js +26 -16
- package/apps/cli/dist/index.js +8 -1
- package/apps/cli/dist/skills/auto-align.d.ts +21 -0
- package/apps/cli/dist/skills/auto-align.js +134 -0
- package/apps/cli/dist/skills/registry.d.ts +1 -1
- package/apps/cli/dist/skills/registry.js +8 -5
- package/apps/daemon/dist/gateway/runtime.js +7 -7
- package/apps/daemon/dist/lifecycle/detached-helper-bundle.mjs +50 -6
- package/apps/daemon/dist/loop/runner.js +19 -12
- package/apps/daemon/dist/memory/config.d.ts +7 -0
- package/apps/daemon/dist/memory/config.js +9 -0
- package/apps/daemon/dist/memory/flush.d.ts +11 -7
- package/apps/daemon/dist/memory/flush.js +16 -470
- package/apps/daemon/dist/paths.js +1 -1
- package/apps/daemon/dist/relay/register.js +1 -0
- package/apps/daemon/dist/runtime/catalog.d.ts +29 -7
- package/apps/daemon/dist/runtime/catalog.js +169 -60
- package/apps/daemon/dist/runtime/external-provider.js +1 -0
- package/apps/daemon/dist/runtime/selection.d.ts +2 -1
- package/apps/daemon/dist/runtime/selection.js +11 -3
- package/apps/daemon/dist/runtime/turn-projection.d.ts +14 -1
- package/apps/daemon/dist/runtime/turn-projection.js +22 -1
- package/apps/daemon/dist/runtime.d.ts +1 -1
- package/apps/daemon/dist/runtime.js +2 -2
- package/apps/daemon/dist/session/create-session.js +5 -1
- package/apps/daemon/dist/session/lifecycle.js +1 -0
- package/apps/daemon/dist/session/prompt.js +8 -7
- package/apps/daemon/dist/session/reconcile.js +20 -12
- package/apps/daemon/dist/session/retry-recovery.js +2 -2
- package/apps/daemon/dist/start.js +1 -1
- package/apps/daemon/package.json +1 -1
- package/apps/relay/dist/controller-handlers.d.ts +1 -0
- package/apps/relay/dist/controller-handlers.js +600 -201
- package/apps/relay/dist/db.js +263 -16
- package/apps/relay/dist/dispatch-outbox.d.ts +66 -0
- package/apps/relay/dist/dispatch-outbox.js +74 -0
- package/apps/relay/dist/event-visibility.d.ts +9 -1
- package/apps/relay/dist/event-visibility.js +42 -18
- package/apps/relay/dist/http-routes.js +44 -56
- package/apps/relay/dist/legacy-recovery.d.ts +94 -0
- package/apps/relay/dist/legacy-recovery.js +380 -0
- package/apps/relay/dist/machine-command-result-handlers.js +58 -3
- package/apps/relay/dist/machine-handler-context.d.ts +0 -6
- package/apps/relay/dist/projection-repository.d.ts +19 -0
- package/apps/relay/dist/projection-repository.js +144 -10
- package/apps/relay/dist/projections.js +11 -1
- package/apps/relay/dist/relay-context.d.ts +0 -6
- package/apps/relay/dist/relay-context.js +93 -100
- package/apps/relay/dist/repositories.js +46 -5
- package/apps/relay/dist/session-projection-reducer.js +253 -18
- package/apps/relay/dist/turn-repository.js +27 -0
- package/apps/relay/dist/types.d.ts +2 -0
- package/apps/relay/dist/websocket.js +1 -1
- package/build-identity.json +3 -3
- package/npm-shrinkwrap.json +18 -18
- package/package.json +1 -1
- package/packages/client/dist/client.js +23 -7
- package/packages/client/dist/parsers.js +2 -0
- package/packages/client/dist/recovery-context.d.ts +2 -2
- package/packages/client/dist/transport.js +20 -2
- package/packages/client/dist/types.d.ts +10 -1
- package/packages/runtime/dist/index.d.ts +20 -0
- package/packages/runtime-cli/dist/codex-app-server.d.ts +11 -0
- package/packages/runtime-cli/dist/codex-app-server.js +41 -12
- package/packages/runtime-cli/dist/codex-json-rpc.d.ts +3 -0
- package/packages/runtime-cli/dist/codex-json-rpc.js +29 -1
- package/packages/runtime-cli/dist/command-resolver.d.ts +10 -0
- package/packages/runtime-cli/dist/command-resolver.js +58 -0
- package/packages/runtime-cli/dist/index.d.ts +1 -1
- package/packages/runtime-cli/dist/index.js +93 -18
- package/packages/runtime-cli/dist/session-store.d.ts +5 -1
- package/packages/runtime-cli/dist/session-store.js +28 -6
- package/packages/shared/dist/protocol-schemas.d.ts +76 -3
- package/packages/shared/dist/protocol-schemas.js +33 -1
- package/packages/shared/dist/protocol-types.d.ts +22 -1
- package/packages/shared/dist/protocol.d.ts +6 -2
- package/packages/shared/dist/protocol.js +4 -2
- package/packages/shared/dist/turn-state.d.ts +16 -2
- package/packages/shared/dist/turn-state.js +12 -2
- package/packages/shared/dist/ui-probe.d.ts +2 -2
|
@@ -2,7 +2,7 @@ import { createHash, timingSafeEqual } from "node:crypto";
|
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { daemonLifecycleRequestIdV1Schema, } from "../../../../packages/shared/dist/index.js";
|
|
5
|
-
import { assertLocalDaemonRuntimeMatchesConfig, assertLocalDaemonVerified, assertNoPendingEnrollmentAuthority, canonicalRelayUrl, cliErrorFromFetch, CliError, compactText, ControllerClient, configDir, configPath, daemonPidPath, deriveOrchestration, diagnoseInstallation, emitDoctorResult, ensureDaemonRunning, localDaemonStatus, machineAvailabilityLabel, machineIsAvailable, ok, parsePairingClaimResponse, randomId, readConfig, readDaemonConfig, readDaemonProjectionBaseline, readLocalAuditLog, readRelayJson, redactedDaemonConfig, requirePositional, requireRelayUrl, requireString,
|
|
5
|
+
import { assertLocalDaemonRuntimeMatchesConfig, assertLocalDaemonVerified, assertNoPendingEnrollmentAuthority, canonicalRelayUrl, cliErrorFromFetch, CliError, compactText, ControllerClient, configDir, configPath, daemonPidPath, deriveOrchestration, diagnoseInstallation, emitDoctorResult, ensureDaemonRunning, localDaemonStatus, machineAvailabilityLabel, machineIsAvailable, ok, parsePairingClaimResponse, randomId, readConfig, readDaemonConfig, readDaemonProjectionBaseline, readLocalAuditLog, readRelayJson, redactedDaemonConfig, requirePositional, requireRelayUrl, requireString, showMachine, startDaemonTimeoutMs, stopLocalDaemon, stopSpawnedDaemon, throwRelayHttpError, waitForDaemonReadiness, waitForDaemonSelfReadiness, wantsJson, withAccountConfigTransaction } from "./lib/index.js";
|
|
6
6
|
function positiveLimit(flags, fallback) {
|
|
7
7
|
const value = flags.limit;
|
|
8
8
|
if (value === undefined)
|
|
@@ -328,8 +328,8 @@ export async function handleDaemon({ domain, action, flags }) {
|
|
|
328
328
|
const baseline = client
|
|
329
329
|
? await readDaemonProjectionBaseline(client, config.machineId, { timeoutMs: startDaemonTimeoutMs })
|
|
330
330
|
: undefined;
|
|
331
|
-
const daemon = await
|
|
332
|
-
const readiness = await waitForLocalDaemonReadiness(client, config, baseline, daemon,
|
|
331
|
+
const daemon = await ensureDaemonRunning(config);
|
|
332
|
+
const readiness = await waitForLocalDaemonReadiness(client, config, baseline, daemon, daemon.started);
|
|
333
333
|
return {
|
|
334
334
|
...readiness.local,
|
|
335
335
|
started: daemon.started,
|
|
@@ -10,7 +10,7 @@ import { assertLocalDaemonRuntimeMatchesConfig, assertLocalDaemonVerified, inspe
|
|
|
10
10
|
import { redactedRelayUrl, withAccountConfigTransaction, } from "./config.js";
|
|
11
11
|
import { cliErrorFromFetch } from "./relay-http.js";
|
|
12
12
|
import { controllerBaseUrl, ensureControllerConfig, ensureDaemonPaired, readStartConfigPreflight } from "./bootstrap-config.js";
|
|
13
|
-
import {
|
|
13
|
+
import { ensureDaemonRunning, readDaemonProjectionBaseline, startFailureMayUseCommittedConfig, waitForDaemonReadiness, } from "./bootstrap-daemon.js";
|
|
14
14
|
import { openUrl, writeHumanStartOutput } from "./bootstrap-output.js";
|
|
15
15
|
import { startJoinBootstrap } from "./bootstrap-join.js";
|
|
16
16
|
import { reconcileSatisfiedDeviceRecoveryPending, startDeviceRecoveryBootstrap } from "./bootstrap-device-recovery.js";
|
|
@@ -194,7 +194,10 @@ export async function startBootstrap(flags) {
|
|
|
194
194
|
daemonProcessNeedsAttention,
|
|
195
195
|
};
|
|
196
196
|
}
|
|
197
|
-
const daemon =
|
|
197
|
+
const daemon = {
|
|
198
|
+
...await ensureDaemonRunning(daemonState.config, cwd),
|
|
199
|
+
restarted: false,
|
|
200
|
+
};
|
|
198
201
|
startedHandle = daemon.startedHandle;
|
|
199
202
|
committedConfigMayBeInUse = daemon.committedConfigMayBeInUse === true;
|
|
200
203
|
const readiness = await waitForDaemonReadiness(client, daemonState.config.machineId, {
|
|
@@ -146,6 +146,16 @@ export function renderCliError(error) {
|
|
|
146
146
|
}
|
|
147
147
|
return 1;
|
|
148
148
|
}
|
|
149
|
+
const recoveryCommand = code === "RECOVERY_CONFIRMATION_REQUIRED" && enriched.sessionId && enriched.requestId
|
|
150
|
+
? `happy-elves turn retry-unknown ${enriched.sessionId} --request-id ${enriched.requestId} --confirm-risk --json`
|
|
151
|
+
: undefined;
|
|
152
|
+
if (recoveryCommand && !wantsJsonOutput()) {
|
|
153
|
+
console.error(`Error: ${message}`);
|
|
154
|
+
console.error("");
|
|
155
|
+
console.error("Only the newly persisted continuation will run in a separate Recovery Session; the unknown prompt will not be replayed.");
|
|
156
|
+
printRunCommands([recoveryCommand]);
|
|
157
|
+
return 1;
|
|
158
|
+
}
|
|
149
159
|
writeError(code, message, {
|
|
150
160
|
requestId: enriched.requestId,
|
|
151
161
|
machineId: enriched.machineId,
|
|
@@ -160,6 +170,7 @@ export function renderCliError(error) {
|
|
|
160
170
|
staleRunningTurnId: enriched.staleRunningTurnId,
|
|
161
171
|
lastTransientError,
|
|
162
172
|
retryCount: enriched.retryCount,
|
|
173
|
+
diagnostics: recoveryCommand ? { recoveryCommand } : undefined,
|
|
163
174
|
}, enriched.capability, enriched.retryable, detailCode);
|
|
164
175
|
return 1;
|
|
165
176
|
}
|
|
@@ -43,6 +43,8 @@ export type StructuredCommandOutput = {
|
|
|
43
43
|
sessionId: string;
|
|
44
44
|
turnId?: string;
|
|
45
45
|
status?: string;
|
|
46
|
+
admission?: "accepted" | "replayed";
|
|
47
|
+
dispatch?: "queued" | "delivered";
|
|
46
48
|
finalOutput?: string;
|
|
47
49
|
error?: string;
|
|
48
50
|
artifactPaths?: {
|
|
@@ -56,7 +58,7 @@ export declare function readStoredTurnOutput(sessionId: string, turnId?: string)
|
|
|
56
58
|
export declare function summarizeTurns(events: DecodedSessionEvent[]): TurnSummary[];
|
|
57
59
|
export declare function structuredOutputForTurn(sessionId: string, turn: TurnSummary): StructuredCommandOutput;
|
|
58
60
|
export declare function structuredOutputForStoredTurn(output: StoredTurnOutput): StructuredCommandOutput;
|
|
59
|
-
export declare function structuredOutputForRunningTurn(sessionId: string, turnId: string): StructuredCommandOutput;
|
|
61
|
+
export declare function structuredOutputForRunningTurn(sessionId: string, turnId: string, dispatch?: "queued" | "delivered"): StructuredCommandOutput;
|
|
60
62
|
export declare function collectExactTurnEvents(client: ControllerClient, sessionId: string, turnId: string, limit?: number): Promise<DecodedSessionEvent[]>;
|
|
61
63
|
export declare function rebuildTurnOutput(client: ControllerClient, sessionId: string, turnId?: string): Promise<StoredTurnOutput>;
|
|
62
64
|
export declare function conciseEvents(events: DecodedSessionEvent[]): Array<{
|
|
@@ -294,12 +294,13 @@ export function structuredOutputForStoredTurn(output) {
|
|
|
294
294
|
},
|
|
295
295
|
};
|
|
296
296
|
}
|
|
297
|
-
export function structuredOutputForRunningTurn(sessionId, turnId) {
|
|
297
|
+
export function structuredOutputForRunningTurn(sessionId, turnId, dispatch = "delivered") {
|
|
298
|
+
const status = dispatch === "queued" ? "queued" : "running";
|
|
298
299
|
return {
|
|
299
|
-
state:
|
|
300
|
+
state: status,
|
|
300
301
|
sessionId,
|
|
301
302
|
turnId,
|
|
302
|
-
status
|
|
303
|
+
status,
|
|
303
304
|
};
|
|
304
305
|
}
|
|
305
306
|
function foldAliasedRuntimeEvents(events) {
|
|
@@ -393,7 +394,10 @@ export async function rebuildTurnOutput(client, sessionId, turnId) {
|
|
|
393
394
|
if (exactTurnId) {
|
|
394
395
|
events = await collectExactTurnEvents(client, sessionId, exactTurnId, 1000);
|
|
395
396
|
folded = foldAliasedRuntimeEvents(events);
|
|
396
|
-
|
|
397
|
+
const canonicalExactTurnId = events[0]?.turnId ?? exactTurnId;
|
|
398
|
+
resolvedTurnId = turnId
|
|
399
|
+
? (folded.runtimeToRequest.get(turnId) ?? canonicalExactTurnId)
|
|
400
|
+
: canonicalExactTurnId;
|
|
397
401
|
turns = summarizeTurns(events);
|
|
398
402
|
latestCompleted = turns.find((turn) => turn.status === "completed") ?? latestCompleted;
|
|
399
403
|
summary = resolvedTurnId ? turns.find((turn) => turn.turnId === resolvedTurnId) : turns[0];
|
|
@@ -25,6 +25,12 @@ export declare function structuredSessionProjection(client: ControllerClient, se
|
|
|
25
25
|
lastTurnId?: string;
|
|
26
26
|
sourceSessionId?: string;
|
|
27
27
|
sourceCheckpointId?: string;
|
|
28
|
+
daemon: SessionSnapshot["daemon"];
|
|
29
|
+
connection: SessionSnapshot["connection"];
|
|
30
|
+
supervision: SessionSnapshot["supervision"];
|
|
31
|
+
execution: SessionSnapshot["execution"];
|
|
32
|
+
admission: SessionSnapshot["admission"];
|
|
33
|
+
dispatch: SessionSnapshot["dispatch"];
|
|
28
34
|
capabilities: SessionSnapshot["capabilities"];
|
|
29
35
|
orchestration: OrchestrationSummary;
|
|
30
36
|
}>;
|
|
@@ -33,6 +33,8 @@ function orchestrationStateFromSnapshot(session) {
|
|
|
33
33
|
return "blocked";
|
|
34
34
|
if (session.execution.status === "failed")
|
|
35
35
|
return "blocked";
|
|
36
|
+
if (session.execution.status === "idle" && (session.dispatch === "queued" || session.dispatch === "delivered"))
|
|
37
|
+
return "pending";
|
|
36
38
|
if (session.execution.status === "active" || session.supervision.status === "watching")
|
|
37
39
|
return "active";
|
|
38
40
|
if (session.supervision.status === "reconciling" || session.supervision.status === "settling")
|
|
@@ -45,7 +47,7 @@ function orchestrationStateFromSnapshot(session) {
|
|
|
45
47
|
}
|
|
46
48
|
function canTrustVisibleRunningTurn(session) {
|
|
47
49
|
return session.status !== "closed" &&
|
|
48
|
-
(session.execution.status === "active" || session.
|
|
50
|
+
(session.execution.status === "active" || session.dispatch === "started");
|
|
49
51
|
}
|
|
50
52
|
function operationalProjection(session) {
|
|
51
53
|
return {
|
|
@@ -138,6 +140,12 @@ export async function structuredSessionProjection(client, session, events = [])
|
|
|
138
140
|
lastTurnId: session.lastTurnId,
|
|
139
141
|
sourceSessionId: session.sourceSessionId,
|
|
140
142
|
sourceCheckpointId: session.sourceCheckpointId,
|
|
143
|
+
daemon: session.daemon,
|
|
144
|
+
connection: session.connection,
|
|
145
|
+
supervision: session.supervision,
|
|
146
|
+
execution: session.execution,
|
|
147
|
+
admission: session.admission,
|
|
148
|
+
dispatch: session.dispatch,
|
|
141
149
|
capabilities: session.capabilities,
|
|
142
150
|
orchestration,
|
|
143
151
|
};
|
|
@@ -129,7 +129,7 @@ Session management:
|
|
|
129
129
|
happy-elves turn wait <sessionId> <turnId> [--timeout 1000s] --json
|
|
130
130
|
happy-elves turn cancel <sessionId> <turnId> [--reason <reason>] --json
|
|
131
131
|
happy-elves turn reconcile <sessionId> <turnId> [--request-id <stable-id>] --json
|
|
132
|
-
happy-elves turn retry-unknown <sessionId>
|
|
132
|
+
happy-elves turn retry-unknown <sessionId> [turnId] --request-id <original-continuation-request-id> --confirm-risk [--name <recovery-name>] --json
|
|
133
133
|
happy-elves turn rewind <sessionId> <turn-or-checkpoint-id> --json
|
|
134
134
|
happy-elves turn fork <sessionId> <turn-or-checkpoint-id> --name <name> --json
|
|
135
135
|
|
|
@@ -283,7 +283,7 @@ Core commands:
|
|
|
283
283
|
turn result <sessionId> [<turnId>] [--json]
|
|
284
284
|
turn cancel <sessionId> <turnId> [--reason <reason>] --json
|
|
285
285
|
turn reconcile <sessionId> <turnId> [--request-id <id>] --json
|
|
286
|
-
turn retry-unknown <sessionId>
|
|
286
|
+
turn retry-unknown <sessionId> [turnId] --request-id <original-continuation-request-id> --confirm-risk [--name <name>] --json
|
|
287
287
|
turn rewind <sessionId> <turn-or-checkpoint-id> --json
|
|
288
288
|
turn fork <sessionId> <turn-or-checkpoint-id> --name <name> --json
|
|
289
289
|
|
|
@@ -505,14 +505,15 @@ async function readExactTurn(client, threadId, turnId, metadata) {
|
|
|
505
505
|
collectExactTurnEvents(client, threadId, turnId, defaultTurnHistoryLimit),
|
|
506
506
|
client.getTurn(threadId, turnId),
|
|
507
507
|
]);
|
|
508
|
-
const
|
|
508
|
+
const canonicalTurnId = turnSnapshot?.turnId ?? events[0]?.turnId ?? turnId;
|
|
509
|
+
const exact = summarizeTurns(events).find((turn) => turn.turnId === canonicalTurnId);
|
|
509
510
|
const projectedExact = applyAuthoritativeTurnSnapshot(exact, turnSnapshot, turnId);
|
|
510
511
|
if (projectedExact && projectedExact.status !== "running")
|
|
511
512
|
return projectedExact;
|
|
512
513
|
if (!metadata)
|
|
513
514
|
return projectedExact;
|
|
514
515
|
const allEvents = await client.history(threadId, defaultTurnHistoryLimit);
|
|
515
|
-
const folded = summarizeOrchestratorTurns(allEvents, metadata).find((turn) => turn.turnId ===
|
|
516
|
+
const folded = summarizeOrchestratorTurns(allEvents, metadata).find((turn) => turn.turnId === canonicalTurnId);
|
|
516
517
|
return applyAuthoritativeTurnSnapshot(folded ?? projectedExact, turnSnapshot, turnId);
|
|
517
518
|
}
|
|
518
519
|
async function readExactTurnOrThrow(client, threadId, turnId, metadata) {
|
|
@@ -538,12 +539,13 @@ async function rebuildOutputForTurn(client, threadId, turn) {
|
|
|
538
539
|
}
|
|
539
540
|
async function rebuildRuntimeTurnOutput(client, threadId, runtimeTurnId) {
|
|
540
541
|
const events = await collectExactTurnEvents(client, threadId, runtimeTurnId, defaultTurnHistoryLimit);
|
|
541
|
-
const
|
|
542
|
-
const
|
|
542
|
+
const canonicalTurnId = events[0]?.turnId ?? runtimeTurnId;
|
|
543
|
+
const exact = events.filter((event) => event.turnId === canonicalTurnId);
|
|
544
|
+
const exactSummary = summarizeTurns(exact).find((turn) => turn.turnId === canonicalTurnId);
|
|
543
545
|
if (exactSummary?.output)
|
|
544
|
-
return await writeTurnOutput(threadId,
|
|
546
|
+
return await writeTurnOutput(threadId, canonicalTurnId, exact);
|
|
545
547
|
const history = await client.history(threadId, defaultTurnHistoryLimit);
|
|
546
|
-
return await writeTurnOutput(threadId,
|
|
548
|
+
return await writeTurnOutput(threadId, canonicalTurnId, history.filter((event) => event.turnId === canonicalTurnId));
|
|
547
549
|
}
|
|
548
550
|
async function readTurnDetails(client, threadId, turnId, metadata) {
|
|
549
551
|
const turn = await readExactTurnOrThrow(client, threadId, turnId, metadata);
|
|
@@ -827,7 +829,20 @@ async function waitForTurn(client, threadId, turnId, timeoutMs, metadata) {
|
|
|
827
829
|
if (turn?.status === "running") {
|
|
828
830
|
const session = await client.getSession(threadId).catch(() => undefined);
|
|
829
831
|
if (session && classifyRunningTurn(session, [], turn).state === "stale") {
|
|
830
|
-
|
|
832
|
+
// The exact turn and Session projections are fetched independently.
|
|
833
|
+
// A provider terminal may commit between those reads, making the first
|
|
834
|
+
// turn snapshot look running while the Session already references the
|
|
835
|
+
// same completed execution. Re-read the immutable turn before calling
|
|
836
|
+
// it orphaned, and let that same-turn convergence window retry.
|
|
837
|
+
const refreshedTurn = await readExactTurn(client, threadId, turnId, metadata);
|
|
838
|
+
if (refreshedTurn && refreshedTurn.status !== "running")
|
|
839
|
+
return refreshedTurn;
|
|
840
|
+
const canonicalTurnId = refreshedTurn?.turnId ?? turn.turnId;
|
|
841
|
+
const sameTerminalExecution = session.execution.turnId === canonicalTurnId &&
|
|
842
|
+
["completed", "failed", "cancelled"].includes(session.execution.status);
|
|
843
|
+
if (!sameTerminalExecution) {
|
|
844
|
+
throw turnOrphaned(threadId, turnId, turn.status, operationalSessionStatus(session));
|
|
845
|
+
}
|
|
831
846
|
}
|
|
832
847
|
}
|
|
833
848
|
if (Date.now() - startedAt >= timeoutMs) {
|
|
@@ -876,11 +891,20 @@ async function sendOrchestratorMessage(client, flags, input) {
|
|
|
876
891
|
model,
|
|
877
892
|
thoughtLevel,
|
|
878
893
|
});
|
|
879
|
-
const self = await
|
|
894
|
+
const [self, directSession] = await Promise.all([
|
|
895
|
+
client.tokenSelf(),
|
|
896
|
+
client.getSession(input.threadId),
|
|
897
|
+
]);
|
|
880
898
|
if (!self.full) {
|
|
881
899
|
throw new OrchestratorError("orchestrator send-message requires a full controller token.", "SCOPED_TOKEN_FORBIDDEN", { retryable: false, sessionId: input.threadId, turnId });
|
|
882
900
|
}
|
|
883
|
-
|
|
901
|
+
if (!directSession) {
|
|
902
|
+
throw new OrchestratorError(`Thread not found: ${input.threadId}`, "THREAD_NOT_FOUND", {
|
|
903
|
+
sessionId: input.threadId,
|
|
904
|
+
retryable: false,
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
const session = directSession;
|
|
884
908
|
const metadata = await client.decodeSessionMetadata(session);
|
|
885
909
|
const resolvedPermission = resolvePermissionMode(requestedPermissionMode, metadata);
|
|
886
910
|
let activePermissionMode = resolvedPermission.permissionMode;
|
|
@@ -1053,6 +1077,85 @@ async function sendOrchestratorMessage(client, flags, input) {
|
|
|
1053
1077
|
includeOutput: includeContextOutput,
|
|
1054
1078
|
},
|
|
1055
1079
|
} : {};
|
|
1080
|
+
// Contract 7 makes the immutable Relay admission the sole idempotency
|
|
1081
|
+
// authority. Older orchestrator versions stored a second request ledger in
|
|
1082
|
+
// encrypted Session metadata and performed several read/CAS round trips
|
|
1083
|
+
// before and after dispatch. Besides being redundant, that made a detached
|
|
1084
|
+
// send take tens of seconds on a high-latency Relay. Dispatch directly with
|
|
1085
|
+
// the stable request/turn identity; the Relay returns only after admission
|
|
1086
|
+
// and its delivery outbox are durable.
|
|
1087
|
+
if (!existingRequest) {
|
|
1088
|
+
const inactivityTimeoutMs = parseDurationMs(flags["inactivity-timeout"], DEFAULT_COMMAND_TIMEOUT_MS);
|
|
1089
|
+
let admissionResult;
|
|
1090
|
+
try {
|
|
1091
|
+
admissionResult = await client.run(input.threadId, text, {
|
|
1092
|
+
wait: false,
|
|
1093
|
+
permissionMode: activePermissionMode,
|
|
1094
|
+
requestId: relayRequestId,
|
|
1095
|
+
turnId,
|
|
1096
|
+
model,
|
|
1097
|
+
thoughtLevel,
|
|
1098
|
+
ackTimeoutMs: 15_000,
|
|
1099
|
+
inactivityTimeoutMs,
|
|
1100
|
+
});
|
|
1101
|
+
}
|
|
1102
|
+
catch (error) {
|
|
1103
|
+
const enriched = error;
|
|
1104
|
+
if (enriched.code === "REQUEST_ID_CONFLICT" || enriched.code === "TURN_ADMISSION_CONFLICT") {
|
|
1105
|
+
throw requestConflict(input.threadId, requestId, turnId);
|
|
1106
|
+
}
|
|
1107
|
+
if (enriched.code === "SESSION_BUSY") {
|
|
1108
|
+
const activeTurnId = enriched.activeTurnId ?? session.execution.turnId;
|
|
1109
|
+
throw new OrchestratorError(enriched.message, "THREAD_BUSY", {
|
|
1110
|
+
retryable: true,
|
|
1111
|
+
sessionId: input.threadId,
|
|
1112
|
+
turnId: activeTurnId ?? turnId,
|
|
1113
|
+
activeTurnId,
|
|
1114
|
+
latestCompletedTurnId: enriched.latestCompletedTurnId,
|
|
1115
|
+
rawStatus: "running",
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
throw error;
|
|
1119
|
+
}
|
|
1120
|
+
if (admissionResult.admission === "replayed") {
|
|
1121
|
+
const replayed = await readExactTurn(client, input.threadId, turnId, metadata);
|
|
1122
|
+
if (replayed && replayed.status !== "running") {
|
|
1123
|
+
const output = await rebuildOutputForTurn(client, input.threadId, replayed);
|
|
1124
|
+
ok(input.responseType, {
|
|
1125
|
+
threadId: input.threadId,
|
|
1126
|
+
sessionId: input.threadId,
|
|
1127
|
+
turnId,
|
|
1128
|
+
requestId,
|
|
1129
|
+
admission: "replayed",
|
|
1130
|
+
dispatch: admissionResult.dispatch ?? "delivered",
|
|
1131
|
+
status: terminalStatusForTurn(replayed),
|
|
1132
|
+
rawStatus: replayed.status,
|
|
1133
|
+
finalOutput: output.output || undefined,
|
|
1134
|
+
artifactPaths: {
|
|
1135
|
+
output: output.outputPath,
|
|
1136
|
+
latest: output.latestPath,
|
|
1137
|
+
metadata: output.metadataPath,
|
|
1138
|
+
},
|
|
1139
|
+
...permissionEvidence(requestedPermissionMode, undefined, resolvedPermission),
|
|
1140
|
+
...contextData,
|
|
1141
|
+
}, { requestId, sessionId: input.threadId, turnId });
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
ok(input.responseType, {
|
|
1146
|
+
threadId: input.threadId,
|
|
1147
|
+
sessionId: input.threadId,
|
|
1148
|
+
turnId,
|
|
1149
|
+
requestId,
|
|
1150
|
+
admission: admissionResult.admission ?? "accepted",
|
|
1151
|
+
dispatch: admissionResult.dispatch ?? "queued",
|
|
1152
|
+
status: "inProgress",
|
|
1153
|
+
rawStatus: admissionResult.dispatch ?? "queued",
|
|
1154
|
+
...permissionEvidence(requestedPermissionMode, undefined, resolvedPermission),
|
|
1155
|
+
...contextData,
|
|
1156
|
+
}, { requestId, sessionId: input.threadId, turnId });
|
|
1157
|
+
return;
|
|
1158
|
+
}
|
|
1056
1159
|
const events = await client.history(input.threadId, defaultTurnHistoryLimit);
|
|
1057
1160
|
const turns = summarizeOrchestratorTurns(events, metadata);
|
|
1058
1161
|
const existingTurn = await readExactTurn(client, input.threadId, turnId, metadata);
|
|
@@ -1134,11 +1237,12 @@ async function sendOrchestratorMessage(client, flags, input) {
|
|
|
1134
1237
|
if (enriched.code === "REQUEST_ID_CONFLICT")
|
|
1135
1238
|
throw requestConflict(input.threadId, requestId, turnId);
|
|
1136
1239
|
if (enriched.code === "SESSION_BUSY") {
|
|
1240
|
+
const activeTurnId = enriched.activeTurnId ?? session.execution.turnId;
|
|
1137
1241
|
throw new OrchestratorError(enriched.message, "THREAD_BUSY", {
|
|
1138
1242
|
retryable: true,
|
|
1139
1243
|
sessionId: input.threadId,
|
|
1140
|
-
turnId,
|
|
1141
|
-
activeTurnId
|
|
1244
|
+
turnId: activeTurnId ?? turnId,
|
|
1245
|
+
activeTurnId,
|
|
1142
1246
|
latestCompletedTurnId: enriched.latestCompletedTurnId,
|
|
1143
1247
|
rawStatus: "running",
|
|
1144
1248
|
});
|
|
@@ -1256,7 +1360,7 @@ export async function handleOrchestrator({ domain, action, positional, flags })
|
|
|
1256
1360
|
ok("orchestrator.readTurn", {
|
|
1257
1361
|
thread: await threadSummary(client, session, machines, { focusTurn: details.turn }),
|
|
1258
1362
|
turn: projectTurn(threadId, details, includeOutput),
|
|
1259
|
-
}, { sessionId: threadId, turnId });
|
|
1363
|
+
}, { sessionId: threadId, turnId: details.turn.turnId });
|
|
1260
1364
|
return true;
|
|
1261
1365
|
}
|
|
1262
1366
|
if (action === "send-message") {
|
|
@@ -1306,7 +1410,7 @@ export async function handleOrchestrator({ domain, action, positional, flags })
|
|
|
1306
1410
|
ok("orchestrator.waitThread", {
|
|
1307
1411
|
threadId,
|
|
1308
1412
|
sessionId: threadId,
|
|
1309
|
-
turnId,
|
|
1413
|
+
turnId: turn.turnId,
|
|
1310
1414
|
status: terminalStatusForTurn(turn),
|
|
1311
1415
|
rawStatus: turn.status,
|
|
1312
1416
|
finalOutput: output.output || undefined,
|
|
@@ -1315,7 +1419,7 @@ export async function handleOrchestrator({ domain, action, positional, flags })
|
|
|
1315
1419
|
latest: output.latestPath,
|
|
1316
1420
|
metadata: output.metadataPath,
|
|
1317
1421
|
},
|
|
1318
|
-
}, { sessionId: threadId, turnId });
|
|
1422
|
+
}, { sessionId: threadId, turnId: turn.turnId });
|
|
1319
1423
|
return true;
|
|
1320
1424
|
}
|
|
1321
1425
|
if (action === "set-thread-tags") {
|
|
@@ -506,7 +506,11 @@ export async function handleSession({ domain, action, positional, flags }) {
|
|
|
506
506
|
// silently run in the controller's local repository.
|
|
507
507
|
const cwd = requireString(flags, "cwd");
|
|
508
508
|
const requestedRuntimeName = typeof flags.name === "string" ? flags.name : undefined;
|
|
509
|
-
|
|
509
|
+
// Relay admission is now the create command's completion boundary. Keep
|
|
510
|
+
// the requested product-facing name in that durable projection instead
|
|
511
|
+
// of publishing a transient "main" name and relying on a later,
|
|
512
|
+
// blocking repair rename after provider readiness.
|
|
513
|
+
const name = requestedRuntimeName ?? "main";
|
|
510
514
|
const permissionMode = parsePermissionMode(flags["permission-mode"] ?? flags.permission);
|
|
511
515
|
const model = parseRuntimeSelection(flags.model, "model");
|
|
512
516
|
const thoughtLevel = parseThoughtLevel(flags);
|
|
@@ -591,9 +595,10 @@ export async function handleSession({ domain, action, positional, flags }) {
|
|
|
591
595
|
session.execution.status === "unknown" ||
|
|
592
596
|
session.execution.status === "legacy-unresolved" ||
|
|
593
597
|
machine?.archivedAt !== undefined;
|
|
598
|
+
const verboseSession = wantsVerbose(flags) ? await showSession(client, session) : undefined;
|
|
594
599
|
return {
|
|
595
|
-
...(
|
|
596
|
-
?
|
|
600
|
+
...(verboseSession
|
|
601
|
+
? { ...verboseSession, state: verboseSession.orchestration.state }
|
|
597
602
|
: await compactSessionListItem(client, session, {
|
|
598
603
|
includeMachine,
|
|
599
604
|
includeCwd,
|
|
@@ -652,6 +657,8 @@ export async function handleSession({ domain, action, positional, flags }) {
|
|
|
652
657
|
connection: session.connection,
|
|
653
658
|
supervision: session.supervision,
|
|
654
659
|
execution: session.execution,
|
|
660
|
+
admission: session.admission,
|
|
661
|
+
dispatch: session.dispatch,
|
|
655
662
|
activeTurnId,
|
|
656
663
|
latestCompletedTurnId: latestCompletedTurn?.turnId,
|
|
657
664
|
latestTerminalTurnId: latestTerminalTurn?.turnId,
|
|
@@ -6,6 +6,7 @@ import { pathToFileURL } from "node:url";
|
|
|
6
6
|
import { ControllerClient, buildIdentity, CliExit, ok, machineIsAvailable, readConfig, redactedRelayUrl, requireString, } from "./lib/index.js";
|
|
7
7
|
import { CliError } from "../errors.js";
|
|
8
8
|
import { ORCHESTRATOR_SKILL_NAME, builtinSkillNames, resolveBuiltinSkill } from "../skills/registry.js";
|
|
9
|
+
import { readSkillAutoAlignmentReport } from "../skills/auto-align.js";
|
|
9
10
|
function optionalString(flags, name) {
|
|
10
11
|
const value = flags[name];
|
|
11
12
|
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
@@ -303,6 +304,19 @@ async function skillDoctor(flags) {
|
|
|
303
304
|
detail: String(skill.contractVersion),
|
|
304
305
|
});
|
|
305
306
|
await addInstalledSkillCheck(checks, warnings, flags, name, skill);
|
|
307
|
+
if (skill.doctorKind === "orchestrator") {
|
|
308
|
+
const alignment = await readSkillAutoAlignmentReport();
|
|
309
|
+
if (alignment?.contractVersion === skill.contractVersion) {
|
|
310
|
+
checks.push({
|
|
311
|
+
name: "skill-reload",
|
|
312
|
+
status: "warning",
|
|
313
|
+
compatible: true,
|
|
314
|
+
aligned: true,
|
|
315
|
+
detail: `Official skill was aligned at ${new Date(alignment.alignedAt).toISOString()}; reload Codex or open a fresh task to use it`,
|
|
316
|
+
});
|
|
317
|
+
warnings.push("The official orchestrator skill was updated. Reload Codex or open a fresh task before relying on the new guidance.");
|
|
318
|
+
}
|
|
319
|
+
}
|
|
306
320
|
if (skill.doctorKind === "orchestrator") {
|
|
307
321
|
await addOrchestratorChecks(checks, warnings, flags);
|
|
308
322
|
await addGitCommitCapabilityCheck(checks, warnings);
|
|
@@ -50,7 +50,8 @@ async function readTurnSummary(client, sessionId, turnId) {
|
|
|
50
50
|
client.history(sessionId, 1000),
|
|
51
51
|
client.getTurn(sessionId, turnId),
|
|
52
52
|
]);
|
|
53
|
-
const
|
|
53
|
+
const canonicalTurnId = turnSnapshot?.turnId ?? events[0]?.turnId ?? turnId;
|
|
54
|
+
const summary = summarizeTurns(events).find((turn) => turn.turnId === canonicalTurnId);
|
|
54
55
|
return applyAuthoritativeTurnSnapshot(summary, turnSnapshot, turnId);
|
|
55
56
|
}
|
|
56
57
|
async function readExactTurnSummary(client, sessionId, turnId) {
|
|
@@ -58,7 +59,8 @@ async function readExactTurnSummary(client, sessionId, turnId) {
|
|
|
58
59
|
collectExactTurnEvents(client, sessionId, turnId, 1000),
|
|
59
60
|
client.getTurn(sessionId, turnId),
|
|
60
61
|
]);
|
|
61
|
-
const
|
|
62
|
+
const canonicalTurnId = turnSnapshot?.turnId ?? events[0]?.turnId ?? turnId;
|
|
63
|
+
const summary = summarizeTurns(events).find((turn) => turn.turnId === canonicalTurnId);
|
|
62
64
|
return applyAuthoritativeTurnSnapshot(summary, turnSnapshot, turnId);
|
|
63
65
|
}
|
|
64
66
|
async function resolveTurnOrCheckpointId(client, sessionId, turnOrCheckpointId) {
|
|
@@ -134,7 +136,7 @@ export async function handleTurn({ domain, action, positional, flags }) {
|
|
|
134
136
|
const turn = await readTurnSummary(client, sessionId, turnId);
|
|
135
137
|
if (!turn)
|
|
136
138
|
throw new CliError(`Turn not found: ${turnId}`, "TURN_NOT_FOUND");
|
|
137
|
-
ok("turn.status", wantsVerbose(flags) ? { sessionId, turn } : { sessionId, ...compactTurn(turn) }, { sessionId, turnId });
|
|
139
|
+
ok("turn.status", wantsVerbose(flags) ? { sessionId, turn } : { sessionId, ...compactTurn(turn) }, { sessionId, turnId: turn.turnId });
|
|
138
140
|
return true;
|
|
139
141
|
}
|
|
140
142
|
if (action === "run") {
|
|
@@ -175,10 +177,12 @@ export async function handleTurn({ domain, action, positional, flags }) {
|
|
|
175
177
|
const structured = output
|
|
176
178
|
? structuredOutputForStoredTurn(output)
|
|
177
179
|
: result.turnId
|
|
178
|
-
? structuredOutputForRunningTurn(sessionId, result.turnId)
|
|
180
|
+
? structuredOutputForRunningTurn(sessionId, result.turnId, result.dispatch)
|
|
179
181
|
: { state: "accepted", sessionId };
|
|
180
182
|
ok("turn.run", {
|
|
181
183
|
...structured,
|
|
184
|
+
admission: result.admission ?? "accepted",
|
|
185
|
+
dispatch: result.dispatch ?? "delivered",
|
|
182
186
|
effectivePermissionMode: permissionMode,
|
|
183
187
|
permissionSource,
|
|
184
188
|
...(wantsVerbose(flags) ? { events, output } : {}),
|
|
@@ -207,7 +211,7 @@ export async function handleTurn({ domain, action, positional, flags }) {
|
|
|
207
211
|
...structuredOutputForTurn(sessionId, turn),
|
|
208
212
|
until: "turn",
|
|
209
213
|
...(wantsVerbose(flags) ? { turn } : {}),
|
|
210
|
-
}, { sessionId, turnId });
|
|
214
|
+
}, { sessionId, turnId: turn.turnId });
|
|
211
215
|
}
|
|
212
216
|
else {
|
|
213
217
|
console.log(`Turn ${turn.turnId} ${turn.status}`);
|
|
@@ -270,7 +274,6 @@ export async function handleTurn({ domain, action, positional, flags }) {
|
|
|
270
274
|
}
|
|
271
275
|
if (action === "retry-unknown") {
|
|
272
276
|
const sessionId = requirePositional(positional[0], "sessionId");
|
|
273
|
-
const turnId = requirePositional(positional[1], "turnId");
|
|
274
277
|
if (flags["confirm-risk"] !== true) {
|
|
275
278
|
throw new CliError("Retrying an unknown execution can duplicate external side effects. Re-run with --confirm-risk to create a Recovery Session.", "CONFIRMATION_REQUIRED");
|
|
276
279
|
}
|
|
@@ -278,15 +281,22 @@ export async function handleTurn({ domain, action, positional, flags }) {
|
|
|
278
281
|
const session = await client.getSession(sessionId);
|
|
279
282
|
if (!session)
|
|
280
283
|
throw new CliError("Session not found", "SESSION_NOT_FOUND");
|
|
281
|
-
|
|
282
|
-
|
|
284
|
+
const sourceTurnId = positional[1]?.trim() ||
|
|
285
|
+
session.execution.turnId ||
|
|
286
|
+
session.supervision.turnId ||
|
|
287
|
+
session.historyRecovery?.turnId;
|
|
288
|
+
const legacyRiskRequired = session.historyRecovery?.state === "risk-required" ||
|
|
289
|
+
session.historyRecovery?.state === "uninspectable";
|
|
290
|
+
if (!legacyRiskRequired && (session.supervision.status !== "failed" || session.execution.status !== "unknown")) {
|
|
291
|
+
throw new CliError("Unknown-turn retry requires failed supervision with unknown execution, or an uninspectable history recovery", "TURN_NOT_RETRYABLE");
|
|
283
292
|
}
|
|
284
|
-
const
|
|
285
|
-
const
|
|
293
|
+
const recoveryIdentity = sourceTurnId ?? "legacy-uninspectable";
|
|
294
|
+
const recoverySessionId = deterministicRecoverySessionId(sessionId, recoveryIdentity, requestId);
|
|
295
|
+
const recoveryTurnId = deterministicRecoveryTurnId(sessionId, recoveryIdentity, requestId);
|
|
286
296
|
const sourceCheckpointId = session.currentHead && isProviderCheckpoint(session.currentHead) ? session.currentHead : undefined;
|
|
287
297
|
const name = typeof flags.name === "string" && flags.name.trim() ? flags.name.trim() : `Recovery: ${session.name}`;
|
|
288
298
|
const result = await client.retryUnknownTurn(sessionId, {
|
|
289
|
-
turnId,
|
|
299
|
+
...(sourceTurnId ? { turnId: sourceTurnId } : {}),
|
|
290
300
|
requestId,
|
|
291
301
|
riskAcknowledged: true,
|
|
292
302
|
recoverySessionId,
|
|
@@ -295,7 +305,7 @@ export async function handleTurn({ domain, action, positional, flags }) {
|
|
|
295
305
|
...(sourceCheckpointId ? { sourceCheckpointId } : {}),
|
|
296
306
|
context: {
|
|
297
307
|
sourceSessionId: sessionId,
|
|
298
|
-
sourceTurnId:
|
|
308
|
+
...(sourceTurnId ? { sourceTurnId } : {}),
|
|
299
309
|
sourceExecution: "unknown",
|
|
300
310
|
riskAcknowledged: true,
|
|
301
311
|
checkpointAvailable: Boolean(sourceCheckpointId),
|
|
@@ -303,20 +313,20 @@ export async function handleTurn({ domain, action, positional, flags }) {
|
|
|
303
313
|
metadata: {
|
|
304
314
|
name,
|
|
305
315
|
recoverySourceSessionId: sessionId,
|
|
306
|
-
recoverySourceTurnId:
|
|
316
|
+
...(sourceTurnId ? { recoverySourceTurnId: sourceTurnId } : {}),
|
|
307
317
|
recoveryRiskAcknowledged: true,
|
|
308
318
|
},
|
|
309
319
|
});
|
|
310
320
|
ok("turn.retryUnknown", {
|
|
311
321
|
sourceSessionId: sessionId,
|
|
312
|
-
sourceTurnId
|
|
322
|
+
sourceTurnId,
|
|
313
323
|
sessionId: result.sessionId ?? recoverySessionId,
|
|
314
324
|
recoverySessionId: result.sessionId ?? recoverySessionId,
|
|
315
|
-
recoveryTurnId,
|
|
325
|
+
recoveryTurnId: result.turnId ?? recoveryTurnId,
|
|
316
326
|
sourceCheckpointId,
|
|
317
327
|
status: "accepted",
|
|
318
328
|
riskAcknowledged: true,
|
|
319
|
-
}, { requestId: result.requestId, sessionId: result.sessionId ?? recoverySessionId, turnId: recoveryTurnId });
|
|
329
|
+
}, { requestId: result.requestId, sessionId: result.sessionId ?? recoverySessionId, turnId: result.turnId ?? recoveryTurnId });
|
|
320
330
|
return true;
|
|
321
331
|
}
|
|
322
332
|
if (action === "rewind") {
|
package/apps/cli/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { main } from "./commands/app.js";
|
|
3
3
|
import { renderCliError } from "./commands/lib/exit.js";
|
|
4
4
|
import { isCliExit } from "./commands/lib/cli-exit.js";
|
|
5
|
+
import { autoAlignOfficialOrchestratorSkill } from "./skills/auto-align.js";
|
|
5
6
|
async function flushWritable(stream) {
|
|
6
7
|
await new Promise((resolve) => {
|
|
7
8
|
stream.write("", () => resolve());
|
|
@@ -11,4 +12,10 @@ async function exitAfterFlush(code) {
|
|
|
11
12
|
await Promise.all([flushWritable(process.stdout), flushWritable(process.stderr)]);
|
|
12
13
|
process.exit(code);
|
|
13
14
|
}
|
|
14
|
-
void
|
|
15
|
+
void autoAlignOfficialOrchestratorSkill().then((report) => {
|
|
16
|
+
if (report && process.stderr.isTTY && !process.argv.includes("--json")) {
|
|
17
|
+
const backups = report.actions.flatMap((action) => action.backupPath ? [action.backupPath] : []);
|
|
18
|
+
console.error(`Happy Elves aligned the official orchestrator skill to contract ${report.contractVersion}. Reload Codex to use it.${backups.length > 0 ? ` Modified copies were backed up at: ${backups.join(", ")}` : ""}`);
|
|
19
|
+
}
|
|
20
|
+
return main();
|
|
21
|
+
}).then(() => exitAfterFlush(0), (error) => exitAfterFlush(isCliExit(error) ? error.exitCode : renderCliError(error)));
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
type SkillAutoAlignmentAction = {
|
|
2
|
+
path: string;
|
|
3
|
+
action: "installed" | "updated" | "updated-with-backup";
|
|
4
|
+
backupPath?: string;
|
|
5
|
+
};
|
|
6
|
+
export type SkillAutoAlignmentReport = {
|
|
7
|
+
version: 1;
|
|
8
|
+
cliVersion: string;
|
|
9
|
+
contractVersion: number;
|
|
10
|
+
alignedAt: number;
|
|
11
|
+
reloadRequired: boolean;
|
|
12
|
+
actions: SkillAutoAlignmentAction[];
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Keeps only the official orchestrator skill aligned with the installed CLI.
|
|
16
|
+
* Other skill directories are never enumerated or modified. The operation is
|
|
17
|
+
* idempotent and each replacement is committed with an atomic rename.
|
|
18
|
+
*/
|
|
19
|
+
export declare function autoAlignOfficialOrchestratorSkill(): Promise<SkillAutoAlignmentReport | undefined>;
|
|
20
|
+
export declare function readSkillAutoAlignmentReport(): Promise<SkillAutoAlignmentReport | undefined>;
|
|
21
|
+
export {};
|