substrate-ai 0.20.63 → 0.20.65

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.
Files changed (36) hide show
  1. package/dist/adapter-registry-BbVWH3Yv.js +4 -0
  2. package/dist/cli/index.js +93 -23
  3. package/dist/decision-router-DblHY8se.js +97 -0
  4. package/dist/{decisions-4F91LrVD.js → decisions-DilHo99V.js} +2 -2
  5. package/dist/{dist-W2emvN3F.js → dist-K_RRWnBX.js} +2 -2
  6. package/dist/{errors-CKFu8YI9.js → errors-pSiZbn6e.js} +2 -2
  7. package/dist/{experimenter-DxxwicpK.js → experimenter-DT9v2Pto.js} +1 -1
  8. package/dist/health-DC3y-sR6.js +1715 -0
  9. package/dist/health-qhtWYh49.js +8 -0
  10. package/dist/index-c924O9mj.d.ts +1432 -0
  11. package/dist/index.d.ts +132 -736
  12. package/dist/index.js +2 -2
  13. package/dist/interactive-prompt-C7wpE4z4.js +183 -0
  14. package/dist/{health-C-KOZrFJ.js → manifest-read-DDkXC3L_.js} +130 -2013
  15. package/dist/modules/interactive-prompt/index.d.ts +86 -0
  16. package/dist/modules/interactive-prompt/index.js +6 -0
  17. package/dist/recovery-engine-BKGBeBnW.js +281 -0
  18. package/dist/{routing-0ykvBl_4.js → routing-CzF0p6lI.js} +2 -2
  19. package/dist/run-DX95j4_D.js +14 -0
  20. package/dist/{run-CHUFlRbH.js → run-DzB4rgkj.js} +391 -31
  21. package/dist/src/modules/decision-router/index.d.ts +88 -0
  22. package/dist/src/modules/decision-router/index.js +3 -0
  23. package/dist/src/modules/recovery-engine/index.d.ts +1101 -0
  24. package/dist/src/modules/recovery-engine/index.js +5 -0
  25. package/dist/{upgrade-C8LAnB6W.js → upgrade-DxzQ1nss.js} +3 -3
  26. package/dist/{upgrade-CAqLkNUP.js → upgrade-MP9XzrI6.js} +2 -2
  27. package/dist/version-manager-impl-GZDUBt0Q.js +4 -0
  28. package/dist/work-graph-repository-DZyJv5pV.js +265 -0
  29. package/package.json +1 -1
  30. package/dist/adapter-registry-k7ZX3Bz6.js +0 -4
  31. package/dist/health-CJqd1FzY.js +0 -6
  32. package/dist/run-Z_-caE_i.js +0 -9
  33. package/dist/version-manager-impl-DaA_ALYK.js +0 -4
  34. /package/dist/{decisions-C0pz9Clx.js → decisions-CzSIEeGP.js} +0 -0
  35. /package/dist/{routing-CcBOCuC9.js → routing-DFxoKHDt.js} +0 -0
  36. /package/dist/{version-manager-impl-BmOWu8ml.js → version-manager-impl-qFBiO4Eh.js} +0 -0
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { childLogger, createLogger, logger } from "./logger-KeHncl-f.js";
2
2
  import { assertDefined, createEventBus, createTuiApp, deepClone, formatDuration, generateId, isPlainObject, isTuiCapable, printNonTtyWarning, sleep, withRetry } from "./helpers-CElYrONe.js";
3
- import { AdapterRegistry, AdtError, ClaudeCodeAdapter, CodexCLIAdapter, ConfigError, ConfigIncompatibleFormatError, GeminiCLIAdapter } from "./dist-W2emvN3F.js";
3
+ import { AdapterRegistry$1 as AdapterRegistry, AdtError$1 as AdtError, ClaudeCodeAdapter$1 as ClaudeCodeAdapter, CodexCLIAdapter$1 as CodexCLIAdapter, ConfigError$1 as ConfigError, ConfigIncompatibleFormatError$1 as ConfigIncompatibleFormatError, GeminiCLIAdapter$1 as GeminiCLIAdapter } from "./dist-K_RRWnBX.js";
4
4
  import "./adapter-registry-DXLMTmfD.js";
5
- import { BudgetExceededError, GitError, RecoveryError, TaskConfigError, TaskGraphCycleError, TaskGraphError, TaskGraphIncompatibleFormatError, WorkerError, WorkerNotFoundError } from "./errors-CKFu8YI9.js";
5
+ import { BudgetExceededError, GitError, RecoveryError, TaskConfigError, TaskGraphCycleError, TaskGraphError, TaskGraphIncompatibleFormatError, WorkerError, WorkerNotFoundError } from "./errors-pSiZbn6e.js";
6
6
 
7
7
  //#region src/core/di.ts
8
8
  /**
@@ -0,0 +1,183 @@
1
+ import { createLogger } from "./logger-KeHncl-f.js";
2
+ import { readCurrentRunId, resolveMainRepoRoot } from "./manifest-read-DDkXC3L_.js";
3
+ import { join } from "node:path";
4
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
5
+ import * as readline from "node:readline";
6
+
7
+ //#region src/modules/interactive-prompt/index.ts
8
+ const logger = createLogger("interactive-prompt");
9
+ const SEPARATOR = "─────────────────────────────────────────────────";
10
+ /**
11
+ * Write the numbered-choice prompt to stdout (AC2).
12
+ * Called before readline to ensure the separator appears before stdin read.
13
+ */
14
+ function renderPrompt(ctx) {
15
+ process.stdout.write(`${SEPARATOR}\n`);
16
+ process.stdout.write(`⚠ Halt: ${ctx.decisionType} (${ctx.severity})\n`);
17
+ process.stdout.write(`${SEPARATOR}\n`);
18
+ process.stdout.write(`${ctx.summary}\n\n`);
19
+ process.stdout.write(`1) Accept default: ${ctx.defaultAction}\n`);
20
+ process.stdout.write(`2) Retry with custom context\n`);
21
+ process.stdout.write(`3) Propose re-scope\n`);
22
+ process.stdout.write(`4) Abort run\n`);
23
+ process.stdout.write(`\nChoice [1]: `);
24
+ }
25
+ /**
26
+ * Sanitize an ISO timestamp string for use in a filename (replace colons and dots).
27
+ */
28
+ function sanitizeTimestampForFilename(iso) {
29
+ return iso.replace(/:/g, "-").replace(/\./g, "-");
30
+ }
31
+ /**
32
+ * Resolve the notifications directory (AC8 — use resolveMainRepoRoot).
33
+ */
34
+ async function resolveNotificationsDir() {
35
+ const repoRoot = await resolveMainRepoRoot();
36
+ return join(repoRoot, ".substrate", "notifications");
37
+ }
38
+ /**
39
+ * Write the initial notification file BEFORE prompting (AC5).
40
+ * Returns the file path so we can update operatorChoice after the prompt.
41
+ */
42
+ async function writeNotificationFile(runId, ctx, timestamp) {
43
+ const notifDir = await resolveNotificationsDir();
44
+ await mkdir(notifDir, { recursive: true });
45
+ const safeTs = sanitizeTimestampForFilename(timestamp);
46
+ const filePath = join(notifDir, `${runId}-${safeTs}.json`);
47
+ const notification = {
48
+ runId,
49
+ timestamp,
50
+ decisionType: ctx.decisionType,
51
+ severity: ctx.severity,
52
+ context: {
53
+ summary: ctx.summary,
54
+ defaultAction: ctx.defaultAction
55
+ },
56
+ choices: ctx.choices,
57
+ operatorChoice: null
58
+ };
59
+ await writeFile(filePath, JSON.stringify(notification, null, 2), "utf8");
60
+ logger.debug({ filePath }, "notification file written");
61
+ return filePath;
62
+ }
63
+ /**
64
+ * Update operatorChoice in the notification file after the operator responds (AC5).
65
+ * If the file was deleted by an external monitor, swallow ENOENT and continue (AC7).
66
+ */
67
+ async function updateNotificationChoice(filePath, operatorChoice) {
68
+ try {
69
+ const raw = await readFile(filePath, "utf8");
70
+ const parsed = JSON.parse(raw);
71
+ parsed.operatorChoice = operatorChoice;
72
+ await writeFile(filePath, JSON.stringify(parsed, null, 2), "utf8");
73
+ logger.debug({
74
+ filePath,
75
+ operatorChoice
76
+ }, "notification file updated with operator choice");
77
+ } catch (err) {
78
+ if (err.code === "ENOENT") {
79
+ logger.debug({ filePath }, "notification file already deleted by external monitor — continuing");
80
+ return;
81
+ }
82
+ logger.warn({
83
+ err,
84
+ filePath
85
+ }, "failed to update notification file — continuing");
86
+ }
87
+ }
88
+ /**
89
+ * Parse a raw stdin line into a 1–4 integer choice, defaulting to 1 on failure.
90
+ */
91
+ function parseChoice(raw) {
92
+ const trimmed = raw.trim();
93
+ if (trimmed === "") return 1;
94
+ const n = parseInt(trimmed, 10);
95
+ if (isNaN(n) || n < 1 || n > 4) return 1;
96
+ return n;
97
+ }
98
+ /**
99
+ * Map a parsed choice integer to the corresponding operator action string.
100
+ */
101
+ function mapChoiceToAction(choice, defaultAction) {
102
+ switch (choice) {
103
+ case 1: return defaultAction;
104
+ case 2: return "retry-with-custom-context";
105
+ case 3: return "propose-re-scope";
106
+ case 4: return "abort-run";
107
+ }
108
+ }
109
+ /**
110
+ * Run the interactive prompt for a Decision Router halt.
111
+ *
112
+ * Workflow:
113
+ * 1. Resolve run ID (from context or manifest-read.ts).
114
+ * 2. Write notification file BEFORE prompting (AC5).
115
+ * 3. If non-interactive mode, emit halt-skipped event and return default (AC4).
116
+ * 4. If stdin is not a TTY, log warning and treat as non-interactive (defensive).
117
+ * 5. Render the numbered-choice prompt (AC2).
118
+ * 6. Read one line from stdin via readline.createInterface (AC3).
119
+ * 7. Parse and return the chosen action.
120
+ * 8. Update notification file with the operator's choice (AC5).
121
+ *
122
+ * @param decisionContext - Decision context from the Decision Router halt.
123
+ * @returns The operator's chosen action string.
124
+ */
125
+ async function runInteractivePrompt(decisionContext) {
126
+ let runId = decisionContext.runId ?? null;
127
+ if (!runId) try {
128
+ const repoRoot = await resolveMainRepoRoot();
129
+ runId = await readCurrentRunId(repoRoot);
130
+ } catch {}
131
+ if (!runId) runId = "unknown";
132
+ const timestamp = new Date().toISOString();
133
+ let notifPath = null;
134
+ try {
135
+ notifPath = await writeNotificationFile(runId, decisionContext, timestamp);
136
+ } catch (err) {
137
+ logger.warn({ err }, "failed to write notification file — continuing without it");
138
+ }
139
+ const isNonInteractive = process.env["SUBSTRATE_NON_INTERACTIVE"] === "true" || decisionContext.nonInteractive === true;
140
+ if (isNonInteractive) {
141
+ const haltSkippedPayload = {
142
+ runId,
143
+ decisionType: decisionContext.decisionType,
144
+ severity: decisionContext.severity,
145
+ defaultAction: decisionContext.defaultAction,
146
+ reason: "non-interactive: stdin prompt suppressed"
147
+ };
148
+ decisionContext.onHaltSkipped?.(haltSkippedPayload);
149
+ logger.debug({
150
+ runId,
151
+ decisionType: decisionContext.decisionType
152
+ }, "non-interactive mode: returning default action");
153
+ return decisionContext.defaultAction;
154
+ }
155
+ if (!process.stdin.isTTY) {
156
+ logger.warn({
157
+ runId,
158
+ decisionType: decisionContext.decisionType
159
+ }, "stdin is not a TTY — treating as non-interactive and returning default action");
160
+ return decisionContext.defaultAction;
161
+ }
162
+ renderPrompt(decisionContext);
163
+ const chosenAction = await new Promise((resolve$1) => {
164
+ const rl = readline.createInterface({
165
+ input: process.stdin,
166
+ output: process.stdout
167
+ });
168
+ rl.once("line", (line) => {
169
+ rl.close();
170
+ const choice = parseChoice(line);
171
+ resolve$1(mapChoiceToAction(choice, decisionContext.defaultAction));
172
+ });
173
+ rl.once("close", () => {
174
+ resolve$1(decisionContext.defaultAction);
175
+ });
176
+ });
177
+ if (notifPath) await updateNotificationChoice(notifPath, chosenAction);
178
+ return chosenAction;
179
+ }
180
+
181
+ //#endregion
182
+ export { runInteractivePrompt };
183
+ //# sourceMappingURL=interactive-prompt-C7wpE4z4.js.map