agent-nuvira 3.5.7 → 3.5.9

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 (101) hide show
  1. package/dist/agents/agents/package-agent.d.ts +22 -0
  2. package/dist/agents/agents/package-agent.d.ts.map +1 -1
  3. package/dist/agents/agents/package-agent.js +92 -4
  4. package/dist/agents/agents/package-agent.js.map +1 -1
  5. package/dist/agents/release-preflight.d.ts +42 -5
  6. package/dist/agents/release-preflight.d.ts.map +1 -1
  7. package/dist/agents/release-preflight.js +82 -5
  8. package/dist/agents/release-preflight.js.map +1 -1
  9. package/dist/cli/chat.d.ts.map +1 -1
  10. package/dist/cli/chat.js +99 -9
  11. package/dist/cli/chat.js.map +1 -1
  12. package/dist/cli/code-map.d.ts +51 -0
  13. package/dist/cli/code-map.d.ts.map +1 -1
  14. package/dist/cli/code-map.js +174 -2
  15. package/dist/cli/code-map.js.map +1 -1
  16. package/dist/cli/credentials.d.ts.map +1 -1
  17. package/dist/cli/credentials.js +4 -1
  18. package/dist/cli/credentials.js.map +1 -1
  19. package/dist/cli/execute.js +5 -5
  20. package/dist/cli/execute.js.map +1 -1
  21. package/dist/cli/loop-executor.d.ts.map +1 -1
  22. package/dist/cli/loop-executor.js +76 -13
  23. package/dist/cli/loop-executor.js.map +1 -1
  24. package/dist/config/process-env.d.ts.map +1 -1
  25. package/dist/config/process-env.js +2 -2
  26. package/dist/config/process-env.js.map +1 -1
  27. package/dist/editing/types.d.ts +23 -1
  28. package/dist/editing/types.d.ts.map +1 -1
  29. package/dist/editing/types.js +51 -7
  30. package/dist/editing/types.js.map +1 -1
  31. package/dist/inference/route-resolver.d.ts +39 -0
  32. package/dist/inference/route-resolver.d.ts.map +1 -1
  33. package/dist/inference/route-resolver.js +51 -2
  34. package/dist/inference/route-resolver.js.map +1 -1
  35. package/dist/inference/thinking-replay.d.ts +86 -0
  36. package/dist/inference/thinking-replay.d.ts.map +1 -0
  37. package/dist/inference/thinking-replay.js +110 -0
  38. package/dist/inference/thinking-replay.js.map +1 -0
  39. package/dist/inference/tools.d.ts +9 -0
  40. package/dist/inference/tools.d.ts.map +1 -1
  41. package/dist/inference/tools.js +12 -1
  42. package/dist/inference/tools.js.map +1 -1
  43. package/dist/learning/assessment-protocol.d.ts +120 -0
  44. package/dist/learning/assessment-protocol.d.ts.map +1 -0
  45. package/dist/learning/assessment-protocol.js +236 -0
  46. package/dist/learning/assessment-protocol.js.map +1 -0
  47. package/dist/learning/assessment-turn-context.d.ts +54 -0
  48. package/dist/learning/assessment-turn-context.d.ts.map +1 -0
  49. package/dist/learning/assessment-turn-context.js +71 -0
  50. package/dist/learning/assessment-turn-context.js.map +1 -0
  51. package/dist/learning/exhaustion-report.d.ts +24 -1
  52. package/dist/learning/exhaustion-report.d.ts.map +1 -1
  53. package/dist/learning/exhaustion-report.js +18 -6
  54. package/dist/learning/exhaustion-report.js.map +1 -1
  55. package/dist/learning/perf-probe.d.ts +140 -0
  56. package/dist/learning/perf-probe.d.ts.map +1 -0
  57. package/dist/learning/perf-probe.js +316 -0
  58. package/dist/learning/perf-probe.js.map +1 -0
  59. package/dist/learning/self-check-discovery.d.ts +117 -0
  60. package/dist/learning/self-check-discovery.d.ts.map +1 -0
  61. package/dist/learning/self-check-discovery.js +334 -0
  62. package/dist/learning/self-check-discovery.js.map +1 -0
  63. package/dist/tools/child-agent-runtime.js +19 -1
  64. package/dist/tools/child-agent-runtime.js.map +1 -1
  65. package/dist/tools/credentials-tool.d.ts.map +1 -1
  66. package/dist/tools/credentials-tool.js +4 -1
  67. package/dist/tools/credentials-tool.js.map +1 -1
  68. package/dist/tools/loop-project-context.d.ts +7 -0
  69. package/dist/tools/loop-project-context.d.ts.map +1 -1
  70. package/dist/tools/loop-project-context.js +44 -0
  71. package/dist/tools/loop-project-context.js.map +1 -1
  72. package/dist/tools/pipeline-tool.d.ts.map +1 -1
  73. package/dist/tools/pipeline-tool.js +19 -1
  74. package/dist/tools/pipeline-tool.js.map +1 -1
  75. package/dist/tools/plan-store.d.ts +90 -6
  76. package/dist/tools/plan-store.d.ts.map +1 -1
  77. package/dist/tools/plan-store.js +140 -13
  78. package/dist/tools/plan-store.js.map +1 -1
  79. package/dist/tools/publish-tool.d.ts.map +1 -1
  80. package/dist/tools/publish-tool.js +9 -0
  81. package/dist/tools/publish-tool.js.map +1 -1
  82. package/dist/tools/registry.d.ts.map +1 -1
  83. package/dist/tools/registry.js +25 -3
  84. package/dist/tools/registry.js.map +1 -1
  85. package/dist/tools/tool-loop.d.ts +17 -0
  86. package/dist/tools/tool-loop.d.ts.map +1 -1
  87. package/dist/tools/tool-loop.js +4 -1
  88. package/dist/tools/tool-loop.js.map +1 -1
  89. package/dist/web-dashboard/chat-console.d.ts.map +1 -1
  90. package/dist/web-dashboard/chat-console.js +11 -1
  91. package/dist/web-dashboard/chat-console.js.map +1 -1
  92. package/dist/web-dashboard/project-context.js +6 -0
  93. package/dist/web-dashboard/project-context.js.map +1 -1
  94. package/dist/web-dashboard/server.d.ts.map +1 -1
  95. package/dist/web-dashboard/server.js +25 -3
  96. package/dist/web-dashboard/server.js.map +1 -1
  97. package/package.json +1 -1
  98. package/src/web-dashboard/public/assets/{index-V0rIqVbg.js → index-CGYIlLpO.js} +3 -3
  99. package/src/web-dashboard/public/assets/index-CGYIlLpO.js.map +1 -0
  100. package/src/web-dashboard/public/index.html +1 -1
  101. package/src/web-dashboard/public/assets/index-V0rIqVbg.js.map +0 -1
@@ -0,0 +1,140 @@
1
+ /**
2
+ * PERF PROBE — name the three measurements an assessment is allowed to be silent
3
+ * about, and the exact bounded command for each.
4
+ *
5
+ * WHY THIS EXISTS (R12, the head-to-head audit, 2026-10-16). Startup time, memory
6
+ * and throughput were the ONE class of claim NOBODY measured: the report that
7
+ * compared the two agents lists them as UNMEASURED on both sides, and on the
8
+ * repository under review every finding about them was an inference. A gap that
9
+ * both sides share is not a fair comparison — it is a blind spot in the rubric,
10
+ * and it is the cheapest one to close because the measurements are mechanical.
11
+ *
12
+ * THE THREE MEASUREMENTS, and why these three:
13
+ *
14
+ * 1. IMPORT / COLD-START COST — the import profiler, which names the individual
15
+ * expensive imports rather than one aggregate number. For a desktop app this
16
+ * is the user-visible "it takes N seconds to open".
17
+ * 2. PEAK ALLOCATION — `tracemalloc`. Stated honestly: it traces PYTHON
18
+ * allocations and does NOT see a native/CUDA/driver allocation, so a probe
19
+ * reporting 40 MB for a model loader is not a bug in the probe — it is the
20
+ * boundary of what this measurement can see, and the block says so.
21
+ * 3. WALL CLOCK OF THE HEAVIEST REALISTIC OPERATION — the one number a user
22
+ * actually feels. Deliberately NOT templated with a guessed input: the
23
+ * command is the project's own entry point or e2e script, because a
24
+ * synthetic input measures the synthetic input.
25
+ *
26
+ * EVERY TEMPLATE HERE WAS MEASURED AGAINST A REAL MACHINE, and two of the first
27
+ * drafts were wrong because of it:
28
+ *
29
+ * - `timeout` is ABSENT on macOS and `gtimeout` is not installed by default, so
30
+ * a template that assumed GNU `timeout` emitted commands that fail on the
31
+ * machine this was written on. The bound is therefore chosen from what the
32
+ * host HAS: the coreutils wrapper when present, else the interpreter's own
33
+ * `signal.alarm` — which is a real bound, not a comment (measured: the process
34
+ * is killed, and nothing after the alarm prints).
35
+ * - `python` is ABSENT on macOS too (only `python3` exists), so the interpreter
36
+ * name is detected rather than assumed. A probe block whose commands do not
37
+ * run is worse than no probe block, because it reads as one that ran.
38
+ *
39
+ * THE IMPORT PROFILER IS REACHED TWO WAYS for the same reason: `-X importtime`
40
+ * when the wrapper can bound the whole command, and `PYTHONPROFILEIMPORTTIME=1`
41
+ * when the bound has to come from inside the interpreter (so the `-X` flag has to
42
+ * give way to the env var). Measured: the env var produces the same profile.
43
+ *
44
+ * HARD TIMEOUTS, OR AN HONEST REFUSAL. When neither mechanism exists (Windows, in
45
+ * practice) the command is emitted UNBOUNDED and the block states plainly that the
46
+ * bound must come from the operator and that an unbound number is tagged
47
+ * UNMEASURED — the discipline rules 3 and 9 already apply to hang claims, moved
48
+ * one step earlier, to the place the numbers come from.
49
+ *
50
+ * WHY IT ONLY NAMES COMMANDS AND NEVER RUNS THEM — same reason as
51
+ * `self-check-discovery.ts`: these run the project's own code, which is exactly
52
+ * the thing that must pass the normal tool gate. Naming the command keeps the run
53
+ * as one consented tool call with a stated timeout; executing it here would hand
54
+ * any cloned repository arbitrary code execution at prompt-assembly time. This
55
+ * module is READ-ONLY over the filesystem, by design.
56
+ *
57
+ * DETERMINISTIC AND LLM-FREE, like everything else in `learning/`: no model
58
+ * decides what a project's startup cost is. Detection that finds nothing to probe
59
+ * yields an EMPTY string, so that turn's prompt is byte-identical to a build
60
+ * without this module.
61
+ */
62
+ import { type MachineFacts } from './machine-facts.js';
63
+ /** Which runtime to probe. `none` means nothing detectable — emit nothing. */
64
+ export type PerfRuntime = 'python' | 'node' | 'none';
65
+ /** Everything the renderer needs, so it can be tested with no filesystem at all. */
66
+ export interface PerfProbeInput {
67
+ runtime: PerfRuntime;
68
+ /** Importable module/package name to time, e.g. `ai_voice_studio`. */
69
+ pythonModule?: string;
70
+ /** The python executable THIS machine has (`python3` on macOS, `python` on Windows). */
71
+ pythonCommand?: string;
72
+ /** Entry file to require/run for a cold-start number, repo-relative. */
73
+ nodeEntry?: string;
74
+ /** Timeout wrapper present on this machine (`timeout`, `gtimeout`), or ''. */
75
+ timeoutCommand?: string;
76
+ /** Host platform — decides whether the runtime's own alarm is available. */
77
+ platform?: string;
78
+ /** Which `/usr/bin/time` flavour exists, for the RSS probe. */
79
+ nativePerfTool?: 'bsd' | 'gnu' | 'none';
80
+ }
81
+ /** Seconds every emitted probe is bounded to. Long enough for a real import. */
82
+ export declare const PROBE_TIMEOUT_SECONDS = 60;
83
+ /** Hard cap on rendered probe lines — the block shares the context budget. */
84
+ export declare const MAX_PROBE_LINES = 26;
85
+ /**
86
+ * A module name that is safe to place inside a shell template. The name is
87
+ * derived from the REPOSITORY (a `pyproject.toml` field or a directory name), and
88
+ * a template that interpolates repo-controlled text into a shell command is an
89
+ * injection the harness would be performing on the user's behalf. Anything outside
90
+ * the identifier charset is dropped; an empty result means "no probe".
91
+ */
92
+ export declare function safeIdentifier(name: string): string;
93
+ /**
94
+ * A repo-relative path safe inside a shell template — same reasoning as
95
+ * {@link safeIdentifier}, with `/` allowed. Quotes, backticks, `$` and newlines
96
+ * are refused outright rather than escaped, because a legitimate entry path never
97
+ * contains them.
98
+ */
99
+ export declare function safeRelativePath(p: string): string;
100
+ /** Does this executable exist anywhere on PATH? (Never spawns a process.) */
101
+ export declare function executableOnPath(name: string, env?: NodeJS.ProcessEnv): boolean;
102
+ /**
103
+ * A python one-liner, bounded by the best mechanism this host has.
104
+ *
105
+ * Returns `bounded: false` only when there is genuinely no way to bound it, and
106
+ * the caller then refuses to present the probe as if it were safe.
107
+ */
108
+ export declare function pythonProbe(input: PerfProbeInput, program: string): {
109
+ command: string;
110
+ bounded: boolean;
111
+ };
112
+ /**
113
+ * The import-cost probe: `-X importtime` under the coreutils wrapper, or the
114
+ * equivalent env var when the bound has to come from inside the interpreter.
115
+ * Measured: both produce the same `import time: ... imported package` profile.
116
+ */
117
+ export declare function pythonImportProbe(input: PerfProbeInput, moduleName: string): {
118
+ command: string;
119
+ bounded: boolean;
120
+ };
121
+ /**
122
+ * Render the probe section for the prompt. Returns '' when there is nothing to
123
+ * probe, which is what keeps such a turn byte-identical to a build without this.
124
+ *
125
+ * Deliberately SHORT and imperative: three measurements, the boundary of each, and
126
+ * the rule for a platform that forbids one. It is not a methodology essay — the
127
+ * model already has rules 1, 3 and 9 for how to report.
128
+ */
129
+ export declare function buildPerfProbeBlock(input: PerfProbeInput): string;
130
+ /**
131
+ * Detect what this project can be probed for.
132
+ *
133
+ * Deliberately shallow and cheap: two files read, one directory listing. The
134
+ * Python module name comes from `pyproject.toml`, else from a top-level package
135
+ * directory that has an `__init__.py` (the importable shape). The Node entry comes
136
+ * from `package.json`'s `main`, else its first `bin`. Best-effort by contract: any
137
+ * failure yields `runtime: 'none'`.
138
+ */
139
+ export declare function detectPerfProbeInput(dir: string, facts?: MachineFacts): PerfProbeInput;
140
+ //# sourceMappingURL=perf-probe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"perf-probe.d.ts","sourceRoot":"","sources":["../../src/learning/perf-probe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AAKH,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAE3E,8EAA8E;AAC9E,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAErD,oFAAoF;AACpF,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,WAAW,CAAC;IACrB,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wFAAwF;IACxF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,cAAc,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;CACzC;AAED,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC,8EAA8E;AAC9E,eAAO,MAAM,eAAe,KAAK,CAAC;AAMlC;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAGlD;AAED,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAW5F;AASD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAWzG;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,cAAc,EACrB,UAAU,EAAE,MAAM,GACjB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAgBvC;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CA+DjE;AAaD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,YAAY,GAAG,cAAc,CA4FtF"}
@@ -0,0 +1,316 @@
1
+ /**
2
+ * PERF PROBE — name the three measurements an assessment is allowed to be silent
3
+ * about, and the exact bounded command for each.
4
+ *
5
+ * WHY THIS EXISTS (R12, the head-to-head audit, 2026-10-16). Startup time, memory
6
+ * and throughput were the ONE class of claim NOBODY measured: the report that
7
+ * compared the two agents lists them as UNMEASURED on both sides, and on the
8
+ * repository under review every finding about them was an inference. A gap that
9
+ * both sides share is not a fair comparison — it is a blind spot in the rubric,
10
+ * and it is the cheapest one to close because the measurements are mechanical.
11
+ *
12
+ * THE THREE MEASUREMENTS, and why these three:
13
+ *
14
+ * 1. IMPORT / COLD-START COST — the import profiler, which names the individual
15
+ * expensive imports rather than one aggregate number. For a desktop app this
16
+ * is the user-visible "it takes N seconds to open".
17
+ * 2. PEAK ALLOCATION — `tracemalloc`. Stated honestly: it traces PYTHON
18
+ * allocations and does NOT see a native/CUDA/driver allocation, so a probe
19
+ * reporting 40 MB for a model loader is not a bug in the probe — it is the
20
+ * boundary of what this measurement can see, and the block says so.
21
+ * 3. WALL CLOCK OF THE HEAVIEST REALISTIC OPERATION — the one number a user
22
+ * actually feels. Deliberately NOT templated with a guessed input: the
23
+ * command is the project's own entry point or e2e script, because a
24
+ * synthetic input measures the synthetic input.
25
+ *
26
+ * EVERY TEMPLATE HERE WAS MEASURED AGAINST A REAL MACHINE, and two of the first
27
+ * drafts were wrong because of it:
28
+ *
29
+ * - `timeout` is ABSENT on macOS and `gtimeout` is not installed by default, so
30
+ * a template that assumed GNU `timeout` emitted commands that fail on the
31
+ * machine this was written on. The bound is therefore chosen from what the
32
+ * host HAS: the coreutils wrapper when present, else the interpreter's own
33
+ * `signal.alarm` — which is a real bound, not a comment (measured: the process
34
+ * is killed, and nothing after the alarm prints).
35
+ * - `python` is ABSENT on macOS too (only `python3` exists), so the interpreter
36
+ * name is detected rather than assumed. A probe block whose commands do not
37
+ * run is worse than no probe block, because it reads as one that ran.
38
+ *
39
+ * THE IMPORT PROFILER IS REACHED TWO WAYS for the same reason: `-X importtime`
40
+ * when the wrapper can bound the whole command, and `PYTHONPROFILEIMPORTTIME=1`
41
+ * when the bound has to come from inside the interpreter (so the `-X` flag has to
42
+ * give way to the env var). Measured: the env var produces the same profile.
43
+ *
44
+ * HARD TIMEOUTS, OR AN HONEST REFUSAL. When neither mechanism exists (Windows, in
45
+ * practice) the command is emitted UNBOUNDED and the block states plainly that the
46
+ * bound must come from the operator and that an unbound number is tagged
47
+ * UNMEASURED — the discipline rules 3 and 9 already apply to hang claims, moved
48
+ * one step earlier, to the place the numbers come from.
49
+ *
50
+ * WHY IT ONLY NAMES COMMANDS AND NEVER RUNS THEM — same reason as
51
+ * `self-check-discovery.ts`: these run the project's own code, which is exactly
52
+ * the thing that must pass the normal tool gate. Naming the command keeps the run
53
+ * as one consented tool call with a stated timeout; executing it here would hand
54
+ * any cloned repository arbitrary code execution at prompt-assembly time. This
55
+ * module is READ-ONLY over the filesystem, by design.
56
+ *
57
+ * DETERMINISTIC AND LLM-FREE, like everything else in `learning/`: no model
58
+ * decides what a project's startup cost is. Detection that finds nothing to probe
59
+ * yields an EMPTY string, so that turn's prompt is byte-identical to a build
60
+ * without this module.
61
+ */
62
+ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
63
+ import { delimiter, join } from 'node:path';
64
+ import { detectMachineFacts } from './machine-facts.js';
65
+ /** Seconds every emitted probe is bounded to. Long enough for a real import. */
66
+ export const PROBE_TIMEOUT_SECONDS = 60;
67
+ /** Hard cap on rendered probe lines — the block shares the context budget. */
68
+ export const MAX_PROBE_LINES = 26;
69
+ /** The one-line honesty rule, repeated in the block: no bound, no number. */
70
+ const UNMEASURED_RULE = 'If a number cannot be produced on this machine, tag it `UNMEASURED` and name the platform constraint — never estimate it.';
71
+ /**
72
+ * A module name that is safe to place inside a shell template. The name is
73
+ * derived from the REPOSITORY (a `pyproject.toml` field or a directory name), and
74
+ * a template that interpolates repo-controlled text into a shell command is an
75
+ * injection the harness would be performing on the user's behalf. Anything outside
76
+ * the identifier charset is dropped; an empty result means "no probe".
77
+ */
78
+ export function safeIdentifier(name) {
79
+ return String(name ?? '').replace(/[^A-Za-z0-9_.-]/g, '');
80
+ }
81
+ /**
82
+ * A repo-relative path safe inside a shell template — same reasoning as
83
+ * {@link safeIdentifier}, with `/` allowed. Quotes, backticks, `$` and newlines
84
+ * are refused outright rather than escaped, because a legitimate entry path never
85
+ * contains them.
86
+ */
87
+ export function safeRelativePath(p) {
88
+ const value = String(p ?? '').trim().replace(/^\.\//, '');
89
+ return /^[A-Za-z0-9_./@\- ]+$/.test(value) ? value : '';
90
+ }
91
+ /** Does this executable exist anywhere on PATH? (Never spawns a process.) */
92
+ export function executableOnPath(name, env = process.env) {
93
+ if (!name)
94
+ return false;
95
+ const dirs = (env.PATH || '').split(delimiter).filter(Boolean);
96
+ for (const dir of dirs) {
97
+ try {
98
+ if (existsSync(join(dir, name)))
99
+ return true;
100
+ }
101
+ catch {
102
+ // An unreadable PATH entry tells us nothing — keep looking.
103
+ }
104
+ }
105
+ return false;
106
+ }
107
+ /** `[project] name` from a pyproject.toml, without a TOML parser. */
108
+ function pythonNameFromToml(text) {
109
+ const section = /\[project\]([\s\S]*?)(?:\n\[|$)/.exec(text)?.[1] ?? '';
110
+ const name = /^\s*name\s*=\s*["']([^"']+)["']/m.exec(section)?.[1] ?? '';
111
+ return name.trim();
112
+ }
113
+ /**
114
+ * A python one-liner, bounded by the best mechanism this host has.
115
+ *
116
+ * Returns `bounded: false` only when there is genuinely no way to bound it, and
117
+ * the caller then refuses to present the probe as if it were safe.
118
+ */
119
+ export function pythonProbe(input, program) {
120
+ const py = safeIdentifier(input.pythonCommand || 'python');
121
+ if (input.timeoutCommand) {
122
+ return { command: `${input.timeoutCommand} ${PROBE_TIMEOUT_SECONDS} ${py} -c "${program}"`, bounded: true };
123
+ }
124
+ if (input.platform !== 'win32') {
125
+ // The interpreter's own alarm — a real bound, and the reason the probe works
126
+ // at all on a machine without coreutils (measured on macOS: it kills the run).
127
+ return { command: `${py} -c "import signal; signal.alarm(${PROBE_TIMEOUT_SECONDS}); ${program}"`, bounded: true };
128
+ }
129
+ return { command: `${py} -c "${program}"`, bounded: false };
130
+ }
131
+ /**
132
+ * The import-cost probe: `-X importtime` under the coreutils wrapper, or the
133
+ * equivalent env var when the bound has to come from inside the interpreter.
134
+ * Measured: both produce the same `import time: ... imported package` profile.
135
+ */
136
+ export function pythonImportProbe(input, moduleName) {
137
+ const py = safeIdentifier(input.pythonCommand || 'python');
138
+ const mod = safeIdentifier(moduleName);
139
+ if (input.timeoutCommand) {
140
+ return {
141
+ command: `${input.timeoutCommand} ${PROBE_TIMEOUT_SECONDS} ${py} -X importtime -c "import ${mod}" 2>&1 | tail -15`,
142
+ bounded: true,
143
+ };
144
+ }
145
+ if (input.platform !== 'win32') {
146
+ return {
147
+ command: `PYTHONPROFILEIMPORTTIME=1 ${py} -c "import signal; signal.alarm(${PROBE_TIMEOUT_SECONDS}); import ${mod}" 2>&1 | tail -15`,
148
+ bounded: true,
149
+ };
150
+ }
151
+ return { command: `${py} -X importtime -c "import ${mod}" 2>&1 | tail -15`, bounded: false };
152
+ }
153
+ /**
154
+ * Render the probe section for the prompt. Returns '' when there is nothing to
155
+ * probe, which is what keeps such a turn byte-identical to a build without this.
156
+ *
157
+ * Deliberately SHORT and imperative: three measurements, the boundary of each, and
158
+ * the rule for a platform that forbids one. It is not a methodology essay — the
159
+ * model already has rules 1, 3 and 9 for how to report.
160
+ */
161
+ export function buildPerfProbeBlock(input) {
162
+ if (input.runtime === 'none')
163
+ return '';
164
+ const lines = [
165
+ '',
166
+ '## Performance probes — this class of claim went UNMEASURED last time',
167
+ 'Every performance claim ("slow to start", "eats memory", "takes forever")',
168
+ 'needs one of these numbers, produced by the command beside it:',
169
+ '',
170
+ ];
171
+ let unbounded = false;
172
+ if (input.runtime === 'python') {
173
+ const mod = safeIdentifier(input.pythonModule || '');
174
+ const named = mod ? `\`${mod}\`` : 'the project\'s top-level package';
175
+ const importProbe = pythonImportProbe(input, mod || 'PACKAGE');
176
+ const memProbe = pythonProbe(input, `import tracemalloc; tracemalloc.start(); import ${mod || 'PACKAGE'}; cur, peak = tracemalloc.get_traced_memory(); print(f'peak {peak/1e6:.1f} MB python-alloc')`);
177
+ unbounded = !importProbe.bounded || !memProbe.bounded;
178
+ lines.push(`- Cold import cost of ${named}: \`${importProbe.command}\``, `- Peak PYTHON allocation: \`${memProbe.command}\``, ' (tracemalloc sees Python allocations only — a native/CUDA allocation is', ' outside it. Report that boundary rather than calling the number total.)', input.timeoutCommand
179
+ ? `- Wall clock of the heaviest realistic operation: the project's own entry point or e2e script, run as \`${input.timeoutCommand} ${PROBE_TIMEOUT_SECONDS} python <its-own-entry>\`.`
180
+ : `- Wall clock of the heaviest realistic operation: the project's own entry point or e2e script, and bound it yourself.`);
181
+ }
182
+ else {
183
+ const entry = safeRelativePath(input.nodeEntry || '');
184
+ const named = entry ? `\`${entry}\`` : 'the project\'s own main entry';
185
+ const startProbe = entry
186
+ ? pythonProbeStyleNode(input, entry)
187
+ : { command: 'node <the project\'s entry>', bounded: false };
188
+ unbounded = !startProbe.bounded;
189
+ const wrap = input.timeoutCommand ? `${input.timeoutCommand} ${PROBE_TIMEOUT_SECONDS} ` : '';
190
+ const rss = input.nativePerfTool === 'bsd'
191
+ ? `\`${wrap}/usr/bin/time -l node ${entry || '<entry>'} 2>&1 | grep -i "maximum resident" | tail -2\``
192
+ : input.nativePerfTool === 'gnu'
193
+ ? `\`${wrap}/usr/bin/time -v node ${entry || '<entry>'} 2>&1 | grep -i "maximum resident" | tail -2\``
194
+ : 'no `/usr/bin/time` on this platform — tag it `UNMEASURED`.';
195
+ lines.push(`- Cold require / startup of ${named}: \`${startProbe.command}\``, `- Peak RSS: ${rss}`, '- Wall clock of the heaviest realistic operation: the project\'s own largest', ' command or test, bounded the same way.');
196
+ }
197
+ if (unbounded) {
198
+ lines.push('', `No \`timeout\`/\`gtimeout\` on this machine and no interpreter-side bound`, `available, so the commands above are bounded only by you: if one has not`, `finished in ~${PROBE_TIMEOUT_SECONDS} s, interrupt it and tag the measurement UNMEASURED.`, `Do not report a number you had to kill to get.`);
199
+ }
200
+ lines.push('', UNMEASURED_RULE, 'Report each number next to the command that produced it.');
201
+ return lines.slice(0, MAX_PROBE_LINES).join('\n');
202
+ }
203
+ /** The node cold-require one-liner, bounded where the host allows (no Windows path). */
204
+ function pythonProbeStyleNode(input, entry) {
205
+ const program = `const t = Date.now(); require('./${entry}'); console.log((Date.now() - t) + ' ms cold require')`;
206
+ if (input.timeoutCommand) {
207
+ return { command: `${input.timeoutCommand} ${PROBE_TIMEOUT_SECONDS} node -e "${program}"`, bounded: true };
208
+ }
209
+ // No coreutils and no interpreter alarm for node: node's own `--eval` has no
210
+ // watchdog flag, so the bound is the caller's. Stated, not hidden.
211
+ return { command: `node -e "${program}"`, bounded: false };
212
+ }
213
+ /**
214
+ * Detect what this project can be probed for.
215
+ *
216
+ * Deliberately shallow and cheap: two files read, one directory listing. The
217
+ * Python module name comes from `pyproject.toml`, else from a top-level package
218
+ * directory that has an `__init__.py` (the importable shape). The Node entry comes
219
+ * from `package.json`'s `main`, else its first `bin`. Best-effort by contract: any
220
+ * failure yields `runtime: 'none'`.
221
+ */
222
+ export function detectPerfProbeInput(dir, facts) {
223
+ const machine = facts ?? detectMachineFacts();
224
+ const platform = machine.platform;
225
+ const timeoutCommand = executableOnPath('timeout') ? 'timeout' : executableOnPath('gtimeout') ? 'gtimeout' : '';
226
+ const pythonCommand = executableOnPath('python') ? 'python' : executableOnPath('python3') ? 'python3' : 'python';
227
+ const nativePerfTool = platform === 'darwin' ? 'bsd' : platform === 'linux' && existsSync('/usr/bin/time') ? 'gnu' : 'none';
228
+ const base = { timeoutCommand, platform, pythonCommand, nativePerfTool };
229
+ if (!dir)
230
+ return { runtime: 'none', ...base };
231
+ try {
232
+ if (!existsSync(dir) || !statSync(dir).isDirectory())
233
+ return { runtime: 'none', ...base };
234
+ // ── Python ────────────────────────────────────────────────────────────────
235
+ let pythonModule = '';
236
+ const pyproject = join(dir, 'pyproject.toml');
237
+ if (existsSync(pyproject)) {
238
+ try {
239
+ pythonModule = pythonNameFromToml(readFileSync(pyproject, 'utf8'));
240
+ }
241
+ catch {
242
+ // Unreadable pyproject — fall through to the directory heuristic.
243
+ }
244
+ }
245
+ if (!pythonModule) {
246
+ try {
247
+ const packages = readdirSync(dir, { withFileTypes: true })
248
+ .filter((e) => e.isDirectory() &&
249
+ !e.name.startsWith('.') &&
250
+ !/^(tests?|docs?|build|dist|node_modules|venv|\.venv)$/i.test(e.name))
251
+ .map((e) => e.name)
252
+ .filter((name) => existsSync(join(dir, name, '__init__.py')))
253
+ .sort((a, b) => a.localeCompare(b));
254
+ if (packages.length > 0)
255
+ pythonModule = packages[0];
256
+ }
257
+ catch {
258
+ // Unreadable directory — no python module.
259
+ }
260
+ }
261
+ // ── Node ─────────────────────────────────────────────────────────────────
262
+ let nodeEntry = '';
263
+ const pkgPath = join(dir, 'package.json');
264
+ if (existsSync(pkgPath)) {
265
+ try {
266
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
267
+ if (typeof pkg.main === 'string' && pkg.main.trim())
268
+ nodeEntry = pkg.main.trim();
269
+ else if (typeof pkg.bin === 'string' && pkg.bin.trim())
270
+ nodeEntry = pkg.bin.trim();
271
+ else if (pkg.bin && typeof pkg.bin === 'object') {
272
+ const first = Object.keys(pkg.bin).sort((a, b) => a.localeCompare(b))[0];
273
+ if (first)
274
+ nodeEntry = String(pkg.bin[first]);
275
+ }
276
+ }
277
+ catch {
278
+ // Malformed package.json — not a probe candidate.
279
+ }
280
+ }
281
+ // WHICH RUNTIME IS THIS PROJECT'S. A top-level directory holding an
282
+ // `__init__.py` is NOT enough on its own: measured on this very repository,
283
+ // a stray fixture directory (`addon/__init__.py`) made a TypeScript project
284
+ // probe as Python, and the block would have told the model to time the import
285
+ // of a directory that is not the shipped program. Python therefore needs a
286
+ // PROJECT MARKER behind it — the same discipline `self-check-discovery.ts`
287
+ // applies before it calls something a check. A Python project that also has
288
+ // Node tooling is probed as Python: the runtime whose startup a user waits on
289
+ // is what matters, and a desktop app's is Python.
290
+ let hasPythonMarker = false;
291
+ for (const marker of ['pyproject.toml', 'setup.py', 'setup.cfg', 'Pipfile']) {
292
+ if (existsSync(join(dir, marker)))
293
+ hasPythonMarker = true;
294
+ }
295
+ if (!hasPythonMarker) {
296
+ try {
297
+ hasPythonMarker = readdirSync(dir).some((n) => /^requirements.*\.txt$/i.test(n));
298
+ }
299
+ catch {
300
+ // Unreadable directory — no marker.
301
+ }
302
+ }
303
+ const safeModule = safeIdentifier(pythonModule);
304
+ const safeEntry = safeRelativePath(nodeEntry);
305
+ if (safeModule && (hasPythonMarker || !safeEntry)) {
306
+ return { runtime: 'python', pythonModule: safeModule, ...base };
307
+ }
308
+ if (safeEntry)
309
+ return { runtime: 'node', nodeEntry: safeEntry, ...base };
310
+ return { runtime: 'none', ...base };
311
+ }
312
+ catch {
313
+ return { runtime: 'none', ...base };
314
+ }
315
+ }
316
+ //# sourceMappingURL=perf-probe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"perf-probe.js","sourceRoot":"","sources":["../../src/learning/perf-probe.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * SELF-CHECK DISCOVERY — name the project's OWN checks on an assessment turn.
3
+ *
4
+ * WHY THIS EXISTS (the side-by-side audit, 2026-10-16). On an audit of a Python
5
+ * desktop app (`/Users/dheeraj/Documents/anuj_voice`, 60 modules) this CLI read
6
+ * the right modules, measured two real bugs, and still missed three findings
7
+ * that the other agent found — because it never RAN the repository's own
8
+ * tooling. The tree contained `tools/dead_code_audit.py` (an unreachable-code
9
+ * auditor, clean) and `tools/winget_validate.py` (a packaging-compliance
10
+ * checker, which is what reported the shipped manifest defect its test suite
11
+ * had been failing on). This CLI's plan mentioned `tools/` in its notes and
12
+ * never executed either one.
13
+ *
14
+ * That is a pure capability gap, and it is the cheapest kind to close: the
15
+ * project has already declared its own checks in files the agent can read, and
16
+ * running a check the repository itself ships produces evidence whose
17
+ * PROVENANCE the agent cannot fabricate. A finding that says "the project's own
18
+ * auditor reports X" costs one tool call and cannot be a hallucination.
19
+ *
20
+ * WHY THIS ONLY NAMES COMMANDS AND NEVER RUNS THEM. A repository's declared
21
+ * scripts are arbitrary code. Executing them because a prompt block suggested
22
+ * it would hand a cloned repository's `package.json` the ability to run
23
+ * anything of its choosing at audit time, with no gate and no consent — the
24
+ * exact class of surprise the write/shell gates exist to prevent. Naming the
25
+ * command keeps the decision where it belongs: the model proposes the run
26
+ * through the normal terminal tool, which keeps its own confirmation and
27
+ * sandbox rules. This module is READ-ONLY over the filesystem, by design.
28
+ *
29
+ * WHY IT IS BOUNDED, AND WHY THE CAP IS RANKED RATHER THAN ALPHABETICAL. The
30
+ * output is prompt text that competes with the actual file tree for the context
31
+ * budget, so it is capped (MAX_CHECKS) and the walk is depth-1 over known
32
+ * directories rather than a recursive scan. But a purely alphabetical cap is
33
+ * WRONG, and the audited project proves it: its `tools/` directory holds ~30
34
+ * scripts whose names start `a11y_`, so an alphabetical list of ten filled up
35
+ * before it ever reached `tools/winget_validate.py` — the checker whose output
36
+ * WAS the release defect the assessment missed. Ordering is therefore by
37
+ * RELEVANCE first (a name that says check/audit/validate/verify/lint/test…, and
38
+ * the project's own test suite) and alphabetical only as the tie-break inside
39
+ * each tier. Deterministic still, and stable across turns; just not arbitrary.
40
+ *
41
+ * WHY THE SUITE OUTRANKS LOOSE SCRIPTS. `pytest` / `npm test` is the check the
42
+ * project itself runs before shipping, and a directory of helper scripts is not.
43
+ * A cap that lets thirty utility scripts displace the test suite would give the
44
+ * agent the weakest evidence first.
45
+ *
46
+ * WHY IT IS DETERMINISTICALLY ORDERED. Same reason the tree walk is: a block
47
+ * whose contents reorder between turns breaks the prompt cache, and the whole
48
+ * point of an ambient block is that it is stable across the turns of one task.
49
+ *
50
+ * DETERMINISTIC AND LLM-FREE, like everything else in `learning/`: it decides
51
+ * from the filesystem, not from a model's idea of what a project "usually"
52
+ * has. A project with no discoverable checks yields an EMPTY string, so its
53
+ * turn is byte-identical to one from a build without this module.
54
+ */
55
+ /** One command the project itself declares, and where it was declared. */
56
+ export interface SelfCheck {
57
+ /** The command to run, exactly as the project declares or ships it. */
58
+ command: string;
59
+ /** Where it came from (`package.json scripts`, `tools/`, CI…). */
60
+ source: string;
61
+ }
62
+ /**
63
+ * Everything the discovery needs, so the matching rules can be unit-tested
64
+ * with no filesystem at all. {@link readProjectSelfChecks} is the thin I/O
65
+ * wrapper that fills this in from a real directory.
66
+ */
67
+ export interface SelfCheckInput {
68
+ /** `package.json` `scripts`, as declared. */
69
+ packageScripts?: Record<string, string>;
70
+ /** The package manager the repo uses — decides `npm run` vs `pnpm run` … */
71
+ packageManager?: string;
72
+ /** Repo-relative paths of the project's own tooling files (`tools/audit.py`). */
73
+ toolFiles?: string[];
74
+ /** Repo-relative paths of CI workflow files. */
75
+ ciWorkflows?: string[];
76
+ /** Repo-relative paths of top-level runner files (`Makefile`, `justfile`). */
77
+ markerFiles?: string[];
78
+ /** Makefile target names, already parsed (see {@link parseMakeTargets}). */
79
+ makeTargets?: string[];
80
+ /** Does a `tests/` tree with `test_*.py` files exist? */
81
+ hasPythonTests?: boolean;
82
+ }
83
+ /** Hard cap on the rendered list — the block shares the context budget. */
84
+ export declare const MAX_CHECKS = 10;
85
+ /**
86
+ * Makefile target names — the `name:` lines at the start of a line, excluding
87
+ * the special targets (`%`, `.PHONY`) and any name carrying a `=`/`$` (a
88
+ * variable assignment, not a target).
89
+ */
90
+ export declare function parseMakeTargets(text: string): string[];
91
+ /**
92
+ * Turn a project's declared facts into an ordered, capped list of checks.
93
+ *
94
+ * Order is by SOURCE (scripts, then Make targets, then the shipped tools, then
95
+ * CI) and then alphabetically inside each source — stable across turns, and
96
+ * "the things the project officially declares" ahead of "the loose scripts in
97
+ * a folder", because the declared ones are the ones its own CI actually runs.
98
+ */
99
+ export declare function discoverSelfChecks(input: SelfCheckInput): SelfCheck[];
100
+ /**
101
+ * Read a directory and discover its checks. Best-effort by contract: any
102
+ * failure (unreadable directory, malformed `package.json`, a race with a
103
+ * concurrent write) yields an empty list, never a throw — an ambient context
104
+ * must not be able to break a turn.
105
+ */
106
+ export declare function readProjectSelfChecks(dir: string): SelfCheck[];
107
+ /**
108
+ * Render the checks as the prompt section the assessment protocol appends.
109
+ * Returns '' for an empty list, which is what keeps a project with no
110
+ * discoverable checks byte-identical to a build without this module.
111
+ *
112
+ * The wording carries the two rules the audit proved were missing: run these
113
+ * BEFORE concluding, and treat their output as the strongest evidence in the
114
+ * report — with the provenance named, so a reader can re-run it.
115
+ */
116
+ export declare function buildSelfCheckBlock(checks: SelfCheck[]): string;
117
+ //# sourceMappingURL=self-check-discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"self-check-discovery.d.ts","sourceRoot":"","sources":["../../src/learning/self-check-discovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAKH,0EAA0E;AAC1E,MAAM,WAAW,SAAS;IACxB,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,6CAA6C;IAC7C,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,yDAAyD;IACzD,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAwCD,2EAA2E;AAC3E,eAAO,MAAM,UAAU,KAAK,CAAC;AAuB7B;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAQvD;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,cAAc,GAAG,SAAS,EAAE,CA0DrE;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,CAqG9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAkB/D"}