@yagni-app/code-staging 0.1.0-staging.997.1 → 0.2.0-staging.1025.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -9
- package/dist/claudeCompat.d.ts +36 -5
- package/dist/claudeCompat.js +85 -23
- package/dist/claudePlugins.d.ts +109 -0
- package/dist/claudePlugins.js +336 -0
- package/dist/cli.js +14 -4
- package/dist/crashReport.d.ts +135 -0
- package/dist/crashReport.js +291 -0
- package/dist/doctor.d.ts +21 -0
- package/dist/doctor.js +52 -0
- package/dist/extension/askAdvisorTool.js +7 -1
- package/dist/extension/bless.js +16 -3
- package/dist/extension/boostCommand.d.ts +144 -0
- package/dist/extension/boostCommand.js +263 -0
- package/dist/extension/branding.d.ts +31 -0
- package/dist/extension/branding.js +37 -0
- package/dist/extension/chipEditor.js +7 -3
- package/dist/extension/claudeRules.d.ts +54 -0
- package/dist/extension/claudeRules.js +180 -0
- package/dist/extension/config.d.ts +61 -0
- package/dist/extension/config.js +86 -0
- package/dist/extension/costHud.d.ts +128 -15
- package/dist/extension/costHud.js +189 -19
- package/dist/extension/crashReport.d.ts +89 -0
- package/dist/extension/crashReport.js +241 -0
- package/dist/extension/index.d.ts +43 -4
- package/dist/extension/index.js +241 -32
- package/dist/extension/initPass.d.ts +65 -47
- package/dist/extension/initPass.js +145 -145
- package/dist/extension/mcpTools.d.ts +57 -0
- package/dist/extension/mcpTools.js +132 -0
- package/dist/extension/pipeline/eval.d.ts +42 -5
- package/dist/extension/pipeline/eval.js +44 -0
- package/dist/extension/pipeline/goCommand.d.ts +18 -0
- package/dist/extension/pipeline/goCommand.js +139 -26
- package/dist/extension/pipeline/goCompareCommand.d.ts +18 -8
- package/dist/extension/pipeline/goCompareCommand.js +42 -23
- package/dist/extension/pipeline/orchestrator.js +9 -0
- package/dist/extension/pipeline/runCostTable.d.ts +37 -0
- package/dist/extension/pipeline/runCostTable.js +165 -0
- package/dist/extension/pipeline/runState.d.ts +19 -0
- package/dist/extension/pipeline/runState.js +11 -0
- package/dist/extension/pipeline/runner.d.ts +19 -0
- package/dist/extension/pipeline/runner.js +13 -1
- package/dist/extension/pipeline/scrubSecrets.js +2 -2
- package/dist/extension/pipeline/stages.d.ts +3 -1
- package/dist/extension/pipeline/stages.js +3 -1
- package/dist/extension/pipeline/types.d.ts +7 -4
- package/dist/extension/pipeline/verify.js +6 -1
- package/dist/extension/pipeline/worktree.js +3 -1
- package/dist/extension/provider.d.ts +7 -1
- package/dist/extension/provider.js +8 -1
- package/dist/extension/recall.js +5 -2
- package/dist/extension/rerouteNotice.d.ts +42 -0
- package/dist/extension/rerouteNotice.js +67 -0
- package/dist/extension/sessionRuns.d.ts +45 -0
- package/dist/extension/sessionRuns.js +77 -0
- package/dist/extension/subagents.d.ts +17 -7
- package/dist/extension/subagents.js +52 -7
- package/dist/launch.d.ts +17 -3
- package/dist/launch.js +22 -9
- package/dist/login.d.ts +7 -0
- package/dist/login.js +3 -1
- package/package.json +2 -2
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crash reporting for the pi extension: a sanitized, fire-and-forget POST to
|
|
3
|
+
* the YAGNI backend's /api/yagni-code/crash. No third-party telemetry SDK —
|
|
4
|
+
* crash reports are the only thing that phones home, they contain no code and
|
|
5
|
+
* no prompts, and `YAGNI_DISABLE_CRASH_REPORTS=1` turns them off.
|
|
6
|
+
*
|
|
7
|
+
* Two report paths use this module:
|
|
8
|
+
* - `uncaughtExceptionMonitor` (installed by index.ts): the MONITOR hook is
|
|
9
|
+
* chosen deliberately — unlike an `uncaughtException` listener it never
|
|
10
|
+
* changes crash semantics in pi's process (which YAGNI does not own); it
|
|
11
|
+
* only observes the crash that is already happening. This is also the
|
|
12
|
+
* "tool error that indicates a crash, not a user error" discriminator:
|
|
13
|
+
* a user-visible tool failure is caught and rendered by pi, while
|
|
14
|
+
* anything reaching the monitor was about to kill the session.
|
|
15
|
+
* - the /go pipeline's terminal catch (goCommand.ts), where a thrown error
|
|
16
|
+
* — not a verdict-shaped failure — ends the run.
|
|
17
|
+
*
|
|
18
|
+
* Everything is fail-soft: never throws, never retries, single short-timeout
|
|
19
|
+
* attempt, and reporting can never affect the user's session.
|
|
20
|
+
*
|
|
21
|
+
* The sanitizer (env-value redaction + path collapse + caps around the shared
|
|
22
|
+
* scrubSecrets patterns) is kept in sync with
|
|
23
|
+
* `yagni-code-cli/src/crashReport.ts`; the backend re-runs its own copy in
|
|
24
|
+
* `backend/src/yagniCode/crashReports.ts`. Spec:
|
|
25
|
+
* docs/superpowers/specs/2026-08-08-crash-reporting-design.md
|
|
26
|
+
*/
|
|
27
|
+
export declare const CRASH_REPORT_DISABLE_ENV = "YAGNI_DISABLE_CRASH_REPORTS";
|
|
28
|
+
export declare const CRASH_REPORT_TIMEOUT_MS = 1500;
|
|
29
|
+
export declare const MAX_CRASH_MESSAGE = 512;
|
|
30
|
+
export declare const MAX_CRASH_ERROR_CLASS = 128;
|
|
31
|
+
export declare const MAX_CRASH_STACK = 8000;
|
|
32
|
+
export declare const MAX_CRASH_STACK_FRAMES = 40;
|
|
33
|
+
export declare const MAX_CRASH_PAYLOAD_BYTES = 16384;
|
|
34
|
+
/** Same truthiness rule as the CLI's YAGNI_DISABLE_* family. */
|
|
35
|
+
export declare function crashReportsDisabled(env?: NodeJS.ProcessEnv): boolean;
|
|
36
|
+
export interface SanitizeCrashOptions {
|
|
37
|
+
env?: NodeJS.ProcessEnv;
|
|
38
|
+
repoRoot?: string;
|
|
39
|
+
}
|
|
40
|
+
/** Env values → [ENV:NAME], scrubSecrets patterns, ~-collapse, repo-relative, basename. */
|
|
41
|
+
export declare function sanitizeCrashText(text: string, opts?: SanitizeCrashOptions): string;
|
|
42
|
+
export interface SanitizedCrash {
|
|
43
|
+
errorClass: string;
|
|
44
|
+
message: string;
|
|
45
|
+
stack?: string;
|
|
46
|
+
}
|
|
47
|
+
/** Reads ONLY name/message/stack; custom error fields are never touched. */
|
|
48
|
+
export declare function sanitizeCrashError(err: unknown, opts?: SanitizeCrashOptions): SanitizedCrash;
|
|
49
|
+
export interface CrashReporterOpts {
|
|
50
|
+
baseUrl: string;
|
|
51
|
+
getToken: () => string | undefined;
|
|
52
|
+
fetchImpl?: typeof fetch;
|
|
53
|
+
env?: NodeJS.ProcessEnv;
|
|
54
|
+
timeoutMs?: number;
|
|
55
|
+
}
|
|
56
|
+
export type CrashReporter = (error: unknown, context?: string, repoRoot?: string) => Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Build the fail-soft reporter. The extension runs inside pi's process, so
|
|
59
|
+
* the client label follows the surface: `desktop` under the desktop shell
|
|
60
|
+
* (YAGNI_SURFACE=desktop), `cli` in the terminal. Version comes from
|
|
61
|
+
* `YAGNI_CODE_VERSION` (forwarded by the launcher; the bundled extension has
|
|
62
|
+
* no package.json on disk). Resolves on every outcome; never throws.
|
|
63
|
+
*/
|
|
64
|
+
export declare function makeCrashReporter(opts: CrashReporterOpts): CrashReporter;
|
|
65
|
+
export type SpawnLike = (command: string, args: string[], options: {
|
|
66
|
+
detached: boolean;
|
|
67
|
+
stdio: "ignore";
|
|
68
|
+
env: Record<string, string | undefined>;
|
|
69
|
+
}) => {
|
|
70
|
+
unref: () => void;
|
|
71
|
+
};
|
|
72
|
+
export interface FatalCrashOpts extends CrashReporterOpts {
|
|
73
|
+
/** Spawn seam for tests (defaults to node:child_process spawn). */
|
|
74
|
+
spawnImpl?: SpawnLike;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Deliver a crash report from a process that is about to die: build the
|
|
78
|
+
* sanitized payload in-process (cheap, synchronous), then spawn a detached
|
|
79
|
+
* one-shot sender that outlives the crash. Never throws.
|
|
80
|
+
*/
|
|
81
|
+
export declare function reportFatalCrash(error: unknown, opts: FatalCrashOpts, context?: string): void;
|
|
82
|
+
/**
|
|
83
|
+
* Observe (never alter) a fatal crash in pi's process. Uses
|
|
84
|
+
* `uncaughtExceptionMonitor`, which fires before the process dies without
|
|
85
|
+
* installing a handler that would change node's default crash behavior —
|
|
86
|
+
* YAGNI does not own pi's process, so crash semantics must stay untouched.
|
|
87
|
+
*/
|
|
88
|
+
export declare function installUncaughtExceptionMonitor(opts: FatalCrashOpts, proc?: Pick<NodeJS.Process, "on">): void;
|
|
89
|
+
//# sourceMappingURL=crashReport.d.ts.map
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crash reporting for the pi extension: a sanitized, fire-and-forget POST to
|
|
3
|
+
* the YAGNI backend's /api/yagni-code/crash. No third-party telemetry SDK —
|
|
4
|
+
* crash reports are the only thing that phones home, they contain no code and
|
|
5
|
+
* no prompts, and `YAGNI_DISABLE_CRASH_REPORTS=1` turns them off.
|
|
6
|
+
*
|
|
7
|
+
* Two report paths use this module:
|
|
8
|
+
* - `uncaughtExceptionMonitor` (installed by index.ts): the MONITOR hook is
|
|
9
|
+
* chosen deliberately — unlike an `uncaughtException` listener it never
|
|
10
|
+
* changes crash semantics in pi's process (which YAGNI does not own); it
|
|
11
|
+
* only observes the crash that is already happening. This is also the
|
|
12
|
+
* "tool error that indicates a crash, not a user error" discriminator:
|
|
13
|
+
* a user-visible tool failure is caught and rendered by pi, while
|
|
14
|
+
* anything reaching the monitor was about to kill the session.
|
|
15
|
+
* - the /go pipeline's terminal catch (goCommand.ts), where a thrown error
|
|
16
|
+
* — not a verdict-shaped failure — ends the run.
|
|
17
|
+
*
|
|
18
|
+
* Everything is fail-soft: never throws, never retries, single short-timeout
|
|
19
|
+
* attempt, and reporting can never affect the user's session.
|
|
20
|
+
*
|
|
21
|
+
* The sanitizer (env-value redaction + path collapse + caps around the shared
|
|
22
|
+
* scrubSecrets patterns) is kept in sync with
|
|
23
|
+
* `yagni-code-cli/src/crashReport.ts`; the backend re-runs its own copy in
|
|
24
|
+
* `backend/src/yagniCode/crashReports.ts`. Spec:
|
|
25
|
+
* docs/superpowers/specs/2026-08-08-crash-reporting-design.md
|
|
26
|
+
*/
|
|
27
|
+
import { spawn } from "node:child_process";
|
|
28
|
+
import { scrubSecrets } from "./pipeline/scrubSecrets.js";
|
|
29
|
+
import { isDesktopSurface } from "./surface.js";
|
|
30
|
+
const defaultSpawn = (command, args, options) => spawn(command, args, options);
|
|
31
|
+
export const CRASH_REPORT_DISABLE_ENV = "YAGNI_DISABLE_CRASH_REPORTS";
|
|
32
|
+
export const CRASH_REPORT_TIMEOUT_MS = 1_500;
|
|
33
|
+
export const MAX_CRASH_MESSAGE = 512;
|
|
34
|
+
export const MAX_CRASH_ERROR_CLASS = 128;
|
|
35
|
+
export const MAX_CRASH_STACK = 8_000;
|
|
36
|
+
export const MAX_CRASH_STACK_FRAMES = 40;
|
|
37
|
+
export const MAX_CRASH_PAYLOAD_BYTES = 16_384;
|
|
38
|
+
/** Same truthiness rule as the CLI's YAGNI_DISABLE_* family. */
|
|
39
|
+
export function crashReportsDisabled(env = process.env) {
|
|
40
|
+
const value = env[CRASH_REPORT_DISABLE_ENV];
|
|
41
|
+
return value !== undefined && value !== "" && value !== "0";
|
|
42
|
+
}
|
|
43
|
+
const HOME_DIR_RE = /(?:\/(?:Users|home)\/|[A-Za-z]:\\Users\\)[^\s/\\]+/g;
|
|
44
|
+
// Spaces deliberately allowed inside the token (real directories contain
|
|
45
|
+
// them); prose after a path may fold into the kept basename — over-redacts
|
|
46
|
+
// rather than under-redacts. Keep in sync with the CLI copy.
|
|
47
|
+
const PATH_TOKEN_RE = /(?<![\w.~/\\])(?:~\/|\/|[A-Za-z]:\\)[^\n():'",]+/g;
|
|
48
|
+
const MIN_ENV_VALUE_LENGTH = 8;
|
|
49
|
+
function isPathLikeValue(value) {
|
|
50
|
+
return value.startsWith("/") || value.startsWith("~") || /^[A-Za-z]:\\/.test(value);
|
|
51
|
+
}
|
|
52
|
+
function collapsePathToken(token) {
|
|
53
|
+
const nm = token.lastIndexOf("node_modules/");
|
|
54
|
+
if (nm >= 0)
|
|
55
|
+
return token.slice(nm);
|
|
56
|
+
const cut = Math.max(token.lastIndexOf("/"), token.lastIndexOf("\\"));
|
|
57
|
+
const base = cut >= 0 ? token.slice(cut + 1) : token;
|
|
58
|
+
return base.length > 0 ? base : token;
|
|
59
|
+
}
|
|
60
|
+
/** Env values → [ENV:NAME], scrubSecrets patterns, ~-collapse, repo-relative, basename. */
|
|
61
|
+
export function sanitizeCrashText(text, opts = {}) {
|
|
62
|
+
let out = text;
|
|
63
|
+
const env = opts.env ?? process.env;
|
|
64
|
+
const entries = Object.entries(env)
|
|
65
|
+
.filter((entry) => typeof entry[1] === "string" &&
|
|
66
|
+
entry[1].length >= MIN_ENV_VALUE_LENGTH &&
|
|
67
|
+
!isPathLikeValue(entry[1]))
|
|
68
|
+
.sort((a, b) => b[1].length - a[1].length);
|
|
69
|
+
for (const [name, value] of entries) {
|
|
70
|
+
if (out.includes(value))
|
|
71
|
+
out = out.split(value).join(`[ENV:${name}]`);
|
|
72
|
+
}
|
|
73
|
+
out = scrubSecrets(out);
|
|
74
|
+
out = out.replace(HOME_DIR_RE, "~");
|
|
75
|
+
if (opts.repoRoot) {
|
|
76
|
+
const root = opts.repoRoot.replace(HOME_DIR_RE, "~").replace(/\/+$/, "");
|
|
77
|
+
if (root.length > 1)
|
|
78
|
+
out = out.split(`${root}/`).join("");
|
|
79
|
+
}
|
|
80
|
+
out = out.replace(PATH_TOKEN_RE, collapsePathToken);
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
/** Reads ONLY name/message/stack; custom error fields are never touched. */
|
|
84
|
+
export function sanitizeCrashError(err, opts = {}) {
|
|
85
|
+
let errorClass = "Error";
|
|
86
|
+
let message;
|
|
87
|
+
let stack;
|
|
88
|
+
if (err instanceof Error) {
|
|
89
|
+
errorClass = err.name || "Error";
|
|
90
|
+
message = err.message;
|
|
91
|
+
stack = typeof err.stack === "string" ? err.stack : undefined;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
try {
|
|
95
|
+
message = String(err);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
message = "unstringifiable thrown value";
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const cappedStack = stack !== undefined
|
|
102
|
+
? sanitizeCrashText(stack, opts)
|
|
103
|
+
.split("\n")
|
|
104
|
+
.slice(0, MAX_CRASH_STACK_FRAMES)
|
|
105
|
+
.join("\n")
|
|
106
|
+
.slice(0, MAX_CRASH_STACK)
|
|
107
|
+
: undefined;
|
|
108
|
+
return {
|
|
109
|
+
errorClass: sanitizeCrashText(errorClass, opts).slice(0, MAX_CRASH_ERROR_CLASS),
|
|
110
|
+
message: sanitizeCrashText(message, opts).slice(0, MAX_CRASH_MESSAGE),
|
|
111
|
+
...(cappedStack !== undefined ? { stack: cappedStack } : {}),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Build the fail-soft reporter. The extension runs inside pi's process, so
|
|
116
|
+
* the client label follows the surface: `desktop` under the desktop shell
|
|
117
|
+
* (YAGNI_SURFACE=desktop), `cli` in the terminal. Version comes from
|
|
118
|
+
* `YAGNI_CODE_VERSION` (forwarded by the launcher; the bundled extension has
|
|
119
|
+
* no package.json on disk). Resolves on every outcome; never throws.
|
|
120
|
+
*/
|
|
121
|
+
export function makeCrashReporter(opts) {
|
|
122
|
+
return async (error, context, repoRoot) => {
|
|
123
|
+
try {
|
|
124
|
+
const env = opts.env ?? process.env;
|
|
125
|
+
if (crashReportsDisabled(env))
|
|
126
|
+
return;
|
|
127
|
+
const token = opts.getToken();
|
|
128
|
+
if (!token)
|
|
129
|
+
return;
|
|
130
|
+
const sanitized = sanitizeCrashError(error, { env, repoRoot });
|
|
131
|
+
const payload = {
|
|
132
|
+
client: isDesktopSurface() ? "desktop" : "cli",
|
|
133
|
+
clientVersion: env.YAGNI_CODE_VERSION?.trim() || "unknown",
|
|
134
|
+
platform: `${process.platform} ${process.arch}`,
|
|
135
|
+
...sanitized,
|
|
136
|
+
...(context !== undefined ? { context } : {}),
|
|
137
|
+
timestamp: new Date().toISOString(),
|
|
138
|
+
};
|
|
139
|
+
if (JSON.stringify(payload).length > MAX_CRASH_PAYLOAD_BYTES) {
|
|
140
|
+
delete payload.stack;
|
|
141
|
+
}
|
|
142
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
143
|
+
const controller = new AbortController();
|
|
144
|
+
const timer = setTimeout(() => controller.abort(), opts.timeoutMs ?? CRASH_REPORT_TIMEOUT_MS);
|
|
145
|
+
timer.unref?.();
|
|
146
|
+
try {
|
|
147
|
+
await fetchImpl(`${opts.baseUrl.replace(/\/$/, "")}/api/yagni-code/crash`, {
|
|
148
|
+
method: "POST",
|
|
149
|
+
headers: {
|
|
150
|
+
"content-type": "application/json",
|
|
151
|
+
authorization: `Bearer ${token}`,
|
|
152
|
+
},
|
|
153
|
+
body: JSON.stringify(payload),
|
|
154
|
+
signal: controller.signal,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
clearTimeout(timer);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
// fail-soft by contract
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* One-shot sender source for the detached delivery child. A fatal uncaught
|
|
168
|
+
* exception kills pi's process before any in-process fetch could connect, so
|
|
169
|
+
* the monitor path hands the (already sanitized) payload to a detached
|
|
170
|
+
* `node -e` child that survives the parent's death, POSTs once with a hard
|
|
171
|
+
* timeout, and exits. No retries, nothing written to disk. The payload, URL
|
|
172
|
+
* and token travel via the child's env — the same channel the launcher
|
|
173
|
+
* already uses to hand pi its YAGNI_TOKEN.
|
|
174
|
+
*/
|
|
175
|
+
const DETACHED_SENDER_SRC = [
|
|
176
|
+
"const done = () => process.exit(0);",
|
|
177
|
+
"setTimeout(done, 5000).unref?.();",
|
|
178
|
+
"fetch(process.env.YAGNI_CRASH_URL, {",
|
|
179
|
+
" method: 'POST',",
|
|
180
|
+
" headers: {",
|
|
181
|
+
" 'content-type': 'application/json',",
|
|
182
|
+
" authorization: 'Bearer ' + process.env.YAGNI_CRASH_TOKEN,",
|
|
183
|
+
" },",
|
|
184
|
+
" body: process.env.YAGNI_CRASH_PAYLOAD,",
|
|
185
|
+
" signal: AbortSignal.timeout(4000),",
|
|
186
|
+
"}).catch(() => {}).finally(done);",
|
|
187
|
+
].join("\n");
|
|
188
|
+
/**
|
|
189
|
+
* Deliver a crash report from a process that is about to die: build the
|
|
190
|
+
* sanitized payload in-process (cheap, synchronous), then spawn a detached
|
|
191
|
+
* one-shot sender that outlives the crash. Never throws.
|
|
192
|
+
*/
|
|
193
|
+
export function reportFatalCrash(error, opts, context) {
|
|
194
|
+
try {
|
|
195
|
+
const env = opts.env ?? process.env;
|
|
196
|
+
if (crashReportsDisabled(env))
|
|
197
|
+
return;
|
|
198
|
+
const token = opts.getToken();
|
|
199
|
+
if (!token)
|
|
200
|
+
return;
|
|
201
|
+
const sanitized = sanitizeCrashError(error, { env });
|
|
202
|
+
const payload = {
|
|
203
|
+
client: isDesktopSurface() ? "desktop" : "cli",
|
|
204
|
+
clientVersion: env.YAGNI_CODE_VERSION?.trim() || "unknown",
|
|
205
|
+
platform: `${process.platform} ${process.arch}`,
|
|
206
|
+
...sanitized,
|
|
207
|
+
...(context !== undefined ? { context } : {}),
|
|
208
|
+
timestamp: new Date().toISOString(),
|
|
209
|
+
};
|
|
210
|
+
if (JSON.stringify(payload).length > MAX_CRASH_PAYLOAD_BYTES) {
|
|
211
|
+
delete payload.stack;
|
|
212
|
+
}
|
|
213
|
+
const spawnImpl = opts.spawnImpl ?? defaultSpawn;
|
|
214
|
+
const child = spawnImpl(process.execPath, ["-e", DETACHED_SENDER_SRC], {
|
|
215
|
+
detached: true,
|
|
216
|
+
stdio: "ignore",
|
|
217
|
+
env: {
|
|
218
|
+
// Minimal env: the sender needs nothing from the parent beyond these.
|
|
219
|
+
YAGNI_CRASH_URL: `${opts.baseUrl.replace(/\/$/, "")}/api/yagni-code/crash`,
|
|
220
|
+
YAGNI_CRASH_TOKEN: token,
|
|
221
|
+
YAGNI_CRASH_PAYLOAD: JSON.stringify(payload),
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
child.unref();
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
// a crash reporter must never add its own crash
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Observe (never alter) a fatal crash in pi's process. Uses
|
|
232
|
+
* `uncaughtExceptionMonitor`, which fires before the process dies without
|
|
233
|
+
* installing a handler that would change node's default crash behavior —
|
|
234
|
+
* YAGNI does not own pi's process, so crash semantics must stay untouched.
|
|
235
|
+
*/
|
|
236
|
+
export function installUncaughtExceptionMonitor(opts, proc = process) {
|
|
237
|
+
proc.on("uncaughtExceptionMonitor", (err) => {
|
|
238
|
+
reportFatalCrash(err, opts, "uncaught-exception");
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
//# sourceMappingURL=crashReport.js.map
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { type SpendResponse } from "./costHud.js";
|
|
2
3
|
import { runInitPass as defaultRunInitPass } from "./initPass.js";
|
|
4
|
+
import { fetchMcpServers as defaultFetchMcpServers } from "./mcpTools.js";
|
|
3
5
|
import { type FlushOutcome, type SpoolClientOpts } from "./spool.js";
|
|
4
6
|
import { type TokenProvider } from "./tokenProvider.js";
|
|
5
7
|
import { type ContextBrief, type ModelEntry } from "./config.js";
|
|
@@ -35,6 +37,12 @@ export interface RegisterYagniDeps {
|
|
|
35
37
|
getToken: () => string | undefined;
|
|
36
38
|
fetchImpl?: typeof fetch;
|
|
37
39
|
}) => Promise<ContextBrief | null>;
|
|
40
|
+
/**
|
|
41
|
+
* Workspace MCP server list seam (YAG-446). The default hits
|
|
42
|
+
* `/api/yagni-code/mcp/servers` and is fail-soft: older backends and
|
|
43
|
+
* un-rescoped tokens yield `[]`, never a startup error.
|
|
44
|
+
*/
|
|
45
|
+
fetchMcpServers?: typeof defaultFetchMcpServers;
|
|
38
46
|
/**
|
|
39
47
|
* The CLI init pass (Onramp Door B). Injectable so tests assert it fires on a
|
|
40
48
|
* fresh-workspace first-run and is skipped otherwise, without a network or disk.
|
|
@@ -57,6 +65,35 @@ export interface RegisterYagniDeps {
|
|
|
57
65
|
flushSpool?: (opts: SpoolClientOpts) => Promise<FlushOutcome>;
|
|
58
66
|
env?: NodeJS.ProcessEnv;
|
|
59
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Build GET /api/yagni-code/spend's URL. Pure (no fetch), exported for tests.
|
|
70
|
+
*
|
|
71
|
+
* YAG-383: a `/go` run's dispatches bill under its OWN run id
|
|
72
|
+
* (`llm_usage.session_id` holds the run id, not the driver session id — see
|
|
73
|
+
* attributionHeaders in config.ts), so `sessionId=` alone misses every run
|
|
74
|
+
* this session launched. `runIds` (comma-separated) widens the same query to
|
|
75
|
+
* include them; empty is omitted entirely rather than sent as `runIds=`.
|
|
76
|
+
*/
|
|
77
|
+
export declare function buildSpendUrl(baseUrl: string, sessionId: string, runIds: readonly string[]): string;
|
|
78
|
+
/**
|
|
79
|
+
* Build GET /api/yagni-code/spend's URL for ONE `/go` run's own spend
|
|
80
|
+
* (`?runId=`, the sibling of {@link buildSpendUrl}'s session-wide
|
|
81
|
+
* `?sessionId=&runIds=`). Pure (no fetch), exported for tests. The backend
|
|
82
|
+
* route treats `runId` and `sessionId` as aliases into the same lookup (see
|
|
83
|
+
* routes/yagniCode.ts's `/spend` handler), so this is just the more honest
|
|
84
|
+
* query param name for "this one run" rather than reusing `buildSpendUrl`
|
|
85
|
+
* with the run id awkwardly standing in for a session id.
|
|
86
|
+
*/
|
|
87
|
+
export declare function buildRunSpendUrl(baseUrl: string, runId: string): string;
|
|
88
|
+
/**
|
|
89
|
+
* Validate + narrow a `GET /api/yagni-code/spend` response body before
|
|
90
|
+
* trusting it as a {@link SpendResponse}. A network response is untyped at
|
|
91
|
+
* runtime, so this is the ONE shared guard both the session-wide `/cost`
|
|
92
|
+
* fetch and the per-run `/go` fetch rely on (drift-risk fix: they used to
|
|
93
|
+
* each carry their own copy of this same check). Pure, exported for tests.
|
|
94
|
+
* Returns null on anything that doesn't look like the real shape.
|
|
95
|
+
*/
|
|
96
|
+
export declare function parseSpendResponse(data: unknown): SpendResponse | null;
|
|
60
97
|
export declare function registerYagni(pi: ExtensionAPI, deps?: RegisterYagniDeps): Promise<void>;
|
|
61
98
|
export default function (pi: ExtensionAPI): Promise<void>;
|
|
62
99
|
export { makeAskYagniTool } from "./askYagniTool.js";
|
|
@@ -73,11 +110,11 @@ export { makeSuggestNextWorkTool, defaultNextAction } from "./nextWorkTool.js";
|
|
|
73
110
|
export type { MakeNextWorkToolOptions, DefaultNextAction, NextActionOption, } from "./nextWorkTool.js";
|
|
74
111
|
export { recordDecision } from "./recordDecisionTool.js";
|
|
75
112
|
export type { RecordDecisionParams } from "./recordDecisionTool.js";
|
|
76
|
-
export { runInitPass, isFreshWorkspace, readRepoIntake, draftEngineering, summarizeDraft, FRESH_BRIEF_MIN_CHARS, } from "./initPass.js";
|
|
77
|
-
export type { RunInitPassDeps, InitPassOutcome, RepoIntake, RepoIntakeFs,
|
|
113
|
+
export { runInitPass, runTeamSetup, registerTeamSetupCommand, isFreshWorkspace, readRepoIntake, draftEngineering, summarizeDraft, FRESH_BRIEF_MIN_CHARS, } from "./initPass.js";
|
|
114
|
+
export type { RunInitPassDeps, InitPassOutcome, RunTeamSetupDeps, TeamSetupOutcome, RepoIntake, RepoIntakeFs, EngineeringDraft, DraftTeam, } from "./initPass.js";
|
|
78
115
|
export { isInitDone, markInitDone, initDoneMarkerFile, _setInitDoneHomeForTest } from "./initDone.js";
|
|
79
|
-
export { brandSystemPrompt, YAGNI_IDENTITY, BRAND_NAME } from "./branding.js";
|
|
80
|
-
export { fetchCatalog, getToken, getWorkspaceId, resolveBaseUrl } from "./config.js";
|
|
116
|
+
export { brandSystemPrompt, YAGNI_IDENTITY, YAGNI_IDENTITY_DRIVER, BRAND_NAME } from "./branding.js";
|
|
117
|
+
export { attributionHeaders, isDriverCaller, fetchCatalog, getToken, getWorkspaceId, resolveBaseUrl, sanitizeCallerSegment, } from "./config.js";
|
|
81
118
|
export type { FetchCatalogOptions, ModelEntry } from "./config.js";
|
|
82
119
|
export { buildYagniProvider } from "./provider.js";
|
|
83
120
|
export { registerGoCommand } from "./pipeline/goCommand.js";
|
|
@@ -113,4 +150,6 @@ export { makeTokenProvider, makeAuthedFetch, persistRotationToProfile, PROACTIVE
|
|
|
113
150
|
export type { TokenProvider, TokenProviderDeps, ScheduleFn } from "./tokenProvider.js";
|
|
114
151
|
export { appendToSpool, loadSpool, flushSpool, sendOrSpool, spoolFile, MAX_SPOOL_AGE_MS, _setSpoolHomeForTest, } from "./spool.js";
|
|
115
152
|
export type { SpoolEntry, SpoolClientOpts, FlushOutcome, JudgmentWriteOutcome } from "./spool.js";
|
|
153
|
+
export { crashReportsDisabled, installUncaughtExceptionMonitor, makeCrashReporter, reportFatalCrash, sanitizeCrashError, sanitizeCrashText, } from "./crashReport.js";
|
|
154
|
+
export type { CrashReporter, CrashReporterOpts, FatalCrashOpts, SanitizedCrash } from "./crashReport.js";
|
|
116
155
|
//# sourceMappingURL=index.d.ts.map
|