@abaigram/abaikit 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +15 -2
  3. package/abai/identity.md +1 -0
  4. package/abai/learnabai.md +474 -0
  5. package/abai/use.md +1 -0
  6. package/abaigram.yaml +208 -0
  7. package/dist/abaify.d.ts +68 -0
  8. package/dist/abaify.d.ts.map +1 -0
  9. package/dist/abaify.js +310 -0
  10. package/dist/abaify.js.map +1 -0
  11. package/dist/abaigram.d.ts +18 -0
  12. package/dist/abaigram.d.ts.map +1 -0
  13. package/dist/abaigram.js +153 -0
  14. package/dist/abaigram.js.map +1 -0
  15. package/dist/app.d.ts +19 -0
  16. package/dist/app.d.ts.map +1 -0
  17. package/dist/app.js +29 -0
  18. package/dist/app.js.map +1 -0
  19. package/dist/bin.d.ts +45 -0
  20. package/dist/bin.d.ts.map +1 -0
  21. package/dist/bin.js +274 -0
  22. package/dist/bin.js.map +1 -0
  23. package/dist/check.d.ts +119 -0
  24. package/dist/check.d.ts.map +1 -0
  25. package/dist/check.js +416 -0
  26. package/dist/check.js.map +1 -0
  27. package/dist/command.d.ts +41 -0
  28. package/dist/command.d.ts.map +1 -0
  29. package/dist/command.js +179 -0
  30. package/dist/command.js.map +1 -0
  31. package/dist/index.d.ts +5 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +5 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/io.d.ts +12 -0
  36. package/dist/io.d.ts.map +1 -0
  37. package/dist/io.js +20 -0
  38. package/dist/io.js.map +1 -0
  39. package/dist/learnabai.d.ts +60 -0
  40. package/dist/learnabai.d.ts.map +1 -0
  41. package/dist/learnabai.js +197 -0
  42. package/dist/learnabai.js.map +1 -0
  43. package/dist/new.d.ts +26 -0
  44. package/dist/new.d.ts.map +1 -0
  45. package/dist/new.js +87 -0
  46. package/dist/new.js.map +1 -0
  47. package/dist/repo.d.ts +99 -0
  48. package/dist/repo.d.ts.map +1 -0
  49. package/dist/repo.js +482 -0
  50. package/dist/repo.js.map +1 -0
  51. package/dist/run.d.ts +6 -0
  52. package/dist/run.d.ts.map +1 -0
  53. package/dist/run.js +47 -0
  54. package/dist/run.js.map +1 -0
  55. package/dist/subject.d.ts +15 -0
  56. package/dist/subject.d.ts.map +1 -0
  57. package/dist/subject.js +53 -0
  58. package/dist/subject.js.map +1 -0
  59. package/dist/template-ts.d.ts +21 -0
  60. package/dist/template-ts.d.ts.map +1 -0
  61. package/dist/template-ts.js +322 -0
  62. package/dist/template-ts.js.map +1 -0
  63. package/dist/version.d.ts +3 -0
  64. package/dist/version.d.ts.map +1 -0
  65. package/dist/version.js +3 -0
  66. package/dist/version.js.map +1 -0
  67. package/examples/hello/abai/identity.md +1 -0
  68. package/examples/hello/abaigram.yaml +24 -0
  69. package/examples/hello/hello.ts +18 -0
  70. package/package.json +40 -5
package/dist/check.js ADDED
@@ -0,0 +1,416 @@
1
+ // abaikit check: conformance as section 12 defines it, with no model.
2
+ // Level 1 is reached through the subject's entry alone (what `manifest`
3
+ // prints, byte for byte the checked-in file, and `doctor --offline --json`); levels 2 to 4 through its `check.run`
4
+ // under the exercise null harness, once per declared mode, read from the
5
+ // report the stub prints at exit; the overhead of 11.6 by the same command
6
+ // under `ABAI_HARNESS=off` and `ABAI_HARNESS=null`, interleaved, side by
7
+ // side. The subject's environment is constructed, never inherited: each
8
+ // run gets exactly the `ABAI_HARNESS` and `ABAI_MODE` it means, the entry
9
+ // runs get neither. A level is the one reached under every checked mode,
10
+ // and passing means that level with no violation.
11
+ import { readFileSync } from "node:fs";
12
+ import { parseManifestYaml, validateManifest } from "@abaigram/abai-stub";
13
+ import { jsonDocument, lastJsonLine, lastPlainLine, median, runCommand, subjectEnv, tailOf, } from "./command.js";
14
+ import { errorMessage, UsageError } from "./io.js";
15
+ import { entryCommand, loadSubject } from "./subject.js";
16
+ export const DEFAULT_RUNS = 3;
17
+ export const DEFAULT_TIMEOUT_MS = 120_000;
18
+ /** How long an entry verb (manifest, doctor) may take. */
19
+ export const ENTRY_TIMEOUT_MS = 60_000;
20
+ /** Below this median for the off run, startup noise is most of the number. */
21
+ export const NOISE_FLOOR_MS = 500;
22
+ /** Characters of a run's output the JSON report carries. */
23
+ const REPORT_TAIL_CHARS = 4096;
24
+ export const LEVEL_NAMES = ["none", "reachable", "observable", "steerable", "deliberative"];
25
+ function parseOverhead(limit) {
26
+ if (limit === undefined)
27
+ return undefined;
28
+ const match = /^(\d+(?:\.\d+)?)\s*%$/.exec(limit.trim());
29
+ return match ? Number(match[1]) / 100 : undefined;
30
+ }
31
+ function firstDifferingLine(a, b) {
32
+ const al = a.split("\n");
33
+ const bl = b.split("\n");
34
+ for (let i = 0; i < Math.max(al.length, bl.length); i++)
35
+ if (al[i] !== bl[i])
36
+ return i + 1;
37
+ return 0;
38
+ }
39
+ function isExerciseReport(value) {
40
+ return value !== undefined && value.form === "exercise" && Array.isArray(value.unexercised);
41
+ }
42
+ function describeRun(run) {
43
+ if (run.timedOut)
44
+ return "did not finish within the timeout and was ended";
45
+ if (run.code !== 0)
46
+ return `exited ${run.code ?? run.signal ?? "?"}`;
47
+ return "exited 0";
48
+ }
49
+ function lastWords(run) {
50
+ return lastPlainLine(run.stderr) || lastPlainLine(run.stdout) || "no output";
51
+ }
52
+ /** A run as the JSON report carries it: the newest part of each stream, not a whole log. */
53
+ function trimmed(run) {
54
+ return { ...run, stdout: tailOf(run.stdout, REPORT_TAIL_CHARS), stderr: tailOf(run.stderr, REPORT_TAIL_CHARS) };
55
+ }
56
+ /** Level 1 through the entry: the effective manifest, the checked-in file, the offline doctor. */
57
+ async function checkLevelOne(subject, entry, env, violations) {
58
+ const manifest = subject.manifest;
59
+ const fileText = readFileSync(subject.manifestPath, "utf8");
60
+ let manifestAgrees = false;
61
+ const details = [];
62
+ const ran = [];
63
+ // what the entry prints is the manifest: byte for byte the checked-in file (section 12), and valid
64
+ const manifestCommand = entryCommand(manifest, "manifest");
65
+ ran.push(manifestCommand);
66
+ const shown = await runCommand(manifestCommand, { cwd: subject.root, env, timeoutMs: ENTRY_TIMEOUT_MS });
67
+ if (shown.code !== 0) {
68
+ violations.push({ where: manifestCommand, what: `${describeRun(shown)}: ${lastWords(shown)}` });
69
+ }
70
+ else {
71
+ let effective;
72
+ try {
73
+ effective = parseManifestYaml(shown.stdout);
74
+ }
75
+ catch (error) {
76
+ violations.push({ where: manifestCommand, what: `did not print a manifest: ${errorMessage(error)}` });
77
+ }
78
+ if (effective !== undefined) {
79
+ const problems = validateManifest(effective);
80
+ if (problems.length > 0) {
81
+ violations.push({
82
+ where: manifestCommand,
83
+ what: `the printed manifest does not validate: ${problems.join("; ")}`,
84
+ });
85
+ }
86
+ else if (effective.name !== manifest.name) {
87
+ violations.push({
88
+ where: subject.manifestPath,
89
+ what: `names ${manifest.name}, but the entry's program is ${effective.name}`,
90
+ });
91
+ }
92
+ else if (shown.stdout === fileText) {
93
+ manifestAgrees = true;
94
+ details.push("abaigram.yaml is what the entry prints");
95
+ }
96
+ else {
97
+ violations.push({
98
+ where: subject.manifestPath,
99
+ what: `is stale: first difference at line ${firstDifferingLine(fileText, shown.stdout)}; run: ${entry} manifest > abaigram.yaml`,
100
+ });
101
+ }
102
+ }
103
+ }
104
+ let doctor = [];
105
+ const doctorCommand = entryCommand(manifest, "doctor", "--offline", "--json");
106
+ ran.push(doctorCommand);
107
+ const examined = await runCommand(doctorCommand, { cwd: subject.root, env, timeoutMs: ENTRY_TIMEOUT_MS });
108
+ const parsed = jsonDocument(examined.stdout);
109
+ if (parsed && Array.isArray(parsed.checks)) {
110
+ doctor = parsed.checks;
111
+ for (const check of doctor) {
112
+ // the manifest file is judged above, with the hint for this entry; the doctor's word on it would repeat it
113
+ if (check.status === "fail" && check.name !== "abaigram.yaml") {
114
+ violations.push({ where: `doctor: ${check.name}`, what: check.detail });
115
+ }
116
+ }
117
+ const failed = doctor.filter((c) => c.status === "fail").length;
118
+ details.push(`doctor: ${doctor.filter((c) => c.status === "ok").length} ok, ${failed} failed`);
119
+ }
120
+ else {
121
+ violations.push({
122
+ where: doctorCommand,
123
+ what: `${describeRun(examined)} and printed no report: ${lastWords(examined)}`,
124
+ });
125
+ }
126
+ const ok = manifestAgrees && doctor.length > 0 && doctor.every((c) => c.status !== "fail");
127
+ return { ok, manifestAgrees, doctor, ran, detail: details.join("; ") };
128
+ }
129
+ function spread(values) {
130
+ return { min: Math.min(...values), max: Math.max(...values) };
131
+ }
132
+ /** Levels 2 to 4 and the measurement, under one mode. */
133
+ async function checkMode(subject, mode, options, violations, unexercised, notes) {
134
+ const manifest = subject.manifest;
135
+ const check = manifest.check;
136
+ const result = { mode, level: 1, evidence: { l2: "", l3: "", l4: "" } };
137
+ const where = (harness) => `check.run under ${mode} with ABAI_HARNESS=${harness}`;
138
+ const run = await runCommand(check.run, {
139
+ cwd: subject.root,
140
+ env: subjectEnv(options.env, { ABAI_HARNESS: "exercise", ABAI_MODE: mode }),
141
+ timeoutMs: options.timeoutMs,
142
+ });
143
+ result.exercise = { run: trimmed(run) };
144
+ // the report is the stub's exit hook: it is there even when the program failed, and says what was reached
145
+ const report = lastJsonLine(run.stderr, (v) => v.form === "exercise");
146
+ if (isExerciseReport(report)) {
147
+ result.exercise.report = report;
148
+ for (const u of report.unexercised)
149
+ unexercised.push({ mode, ...u });
150
+ for (const cell of manifest.cells) {
151
+ if (cell.access === "read" && !report.cellsWritten.includes(cell.name)) {
152
+ unexercised.push({ mode, kind: "cell", name: cell.name, why: "not written during the exercise run" });
153
+ }
154
+ }
155
+ if (report.eventsEmitted) {
156
+ for (const event of manifest.events) {
157
+ if (!report.eventsEmitted.includes(event.name)) {
158
+ unexercised.push({ mode, kind: "event", name: event.name, why: "not emitted during the exercise run" });
159
+ }
160
+ }
161
+ }
162
+ }
163
+ if (run.timedOut || run.code !== 0) {
164
+ violations.push({ where: where("exercise"), what: `${describeRun(run)}: ${lastWords(run)}` });
165
+ return result;
166
+ }
167
+ if (!isExerciseReport(report)) {
168
+ violations.push({
169
+ where: where("exercise"),
170
+ what: `printed no exercise report at exit${run.dropped.stderr > 0 ? ` in the ${run.stderr.length} newest bytes of stderr kept` : ""}: the stub did not attach its null harness (is the entry hook in main, and the program built?)`,
171
+ });
172
+ return result;
173
+ }
174
+ // the program's own events, by name; a report that names none (an older stub) shows none: the library's do not count
175
+ const emitted = report.eventsEmitted ?? [];
176
+ const l2 = report.cellsWritten.length > 0 || emitted.length > 0;
177
+ result.evidence.l2 = l2
178
+ ? `cells written: ${report.cellsWritten.join(", ") || "none"}; events: ${emitted.join(", ") || "none"}`
179
+ : "no cell written and no event emitted during the exercise run";
180
+ const l3 = report.capabilitiesCalled.length > 0 || report.knobsWritten.length > 0;
181
+ result.evidence.l3 = l3
182
+ ? `capabilities called: ${report.capabilitiesCalled.join(", ") || "none"}; knobs landed: ${report.knobsWritten.join(", ") || "none"}`
183
+ : "no capability called and no knob landed";
184
+ const l4 = report.decisions > 0;
185
+ result.evidence.l4 = l4 ? `decisions reached: ${report.decisions}` : "no decision reached";
186
+ result.level = l2 ? (l3 ? (l4 ? 4 : 3) : 2) : 1;
187
+ // the measurement (11.6): off and null, interleaved so drift lands on both, K runs each, the medians side by side
188
+ const measurement = {
189
+ runs: options.runs,
190
+ off: [],
191
+ null: [],
192
+ offMedianMs: Number.NaN,
193
+ nullMedianMs: Number.NaN,
194
+ slowdown: Number.NaN,
195
+ failed: [],
196
+ withinNoise: false,
197
+ };
198
+ for (let i = 0; i < options.runs; i++) {
199
+ for (const harness of ["off", "null"]) {
200
+ const timed = await runCommand(check.run, {
201
+ cwd: subject.root,
202
+ env: subjectEnv(options.env, { ABAI_HARNESS: harness, ABAI_MODE: mode }),
203
+ timeoutMs: options.timeoutMs,
204
+ });
205
+ if (timed.timedOut || timed.code !== 0)
206
+ measurement.failed.push(`${harness}: ${describeRun(timed)}`);
207
+ else
208
+ measurement[harness].push(timed.ms);
209
+ }
210
+ }
211
+ if (measurement.failed.length > 0) {
212
+ violations.push({
213
+ where: `check.run under ${mode} with ABAI_HARNESS=off and null`,
214
+ what: `measurement runs failed: ${measurement.failed.join("; ")}`,
215
+ });
216
+ }
217
+ if (measurement.off.length > 0 && measurement.null.length > 0) {
218
+ measurement.offMedianMs = median(measurement.off);
219
+ measurement.nullMedianMs = median(measurement.null);
220
+ measurement.slowdown =
221
+ measurement.offMedianMs > 0 ? measurement.nullMedianMs / measurement.offMedianMs - 1 : Number.NaN;
222
+ const off = spread(measurement.off);
223
+ const nul = spread(measurement.null);
224
+ measurement.withinNoise = measurement.offMedianMs < NOISE_FLOOR_MS || (nul.min <= off.max && off.min <= nul.max);
225
+ if (measurement.withinNoise) {
226
+ notes.push(`under ${mode} the overhead numbers are within noise (off ${off.min.toFixed(0)}..${off.max.toFixed(0)} ms, null ${nul.min.toFixed(0)}..${nul.max.toFixed(0)} ms, ${options.runs} runs each): raise --runs or give check.run more work before reading the slowdown`);
227
+ }
228
+ const limit = parseOverhead(check.overhead);
229
+ if (check.overhead !== undefined && limit === undefined) {
230
+ violations.push({
231
+ where: "check.overhead",
232
+ what: `${JSON.stringify(check.overhead)} is not a percentage such as "5%"`,
233
+ });
234
+ }
235
+ else if (limit !== undefined) {
236
+ measurement.limit = limit;
237
+ if (measurement.slowdown > limit) {
238
+ violations.push({
239
+ where: where("null"),
240
+ what: `the null-harness run is ${(measurement.slowdown * 100).toFixed(1)}% slower than the off run (medians of ${options.runs}${measurement.withinNoise ? ", within noise" : ""}); check.overhead allows ${check.overhead}`,
241
+ });
242
+ }
243
+ }
244
+ }
245
+ result.measurement = measurement;
246
+ return result;
247
+ }
248
+ export async function checkRepository(options) {
249
+ const violations = [];
250
+ const unexercised = [];
251
+ const notes = [];
252
+ const subject = loadSubject(options.repo, options.cwd);
253
+ const manifest = subject.manifest;
254
+ const entry = options.entry ?? manifest.entry;
255
+ const subjectForEntry = options.entry
256
+ ? { ...subject, manifest: { ...manifest, entry: options.entry } }
257
+ : subject;
258
+ const runs = options.runs ?? DEFAULT_RUNS;
259
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
260
+ const l1 = await checkLevelOne(subjectForEntry, entry, subjectEnv(options.env), violations);
261
+ let checkedModes = manifest.modes;
262
+ if (options.mode !== undefined) {
263
+ if (!manifest.modes.includes(options.mode)) {
264
+ violations.push({
265
+ where: "--mode",
266
+ what: `${manifest.name} declares ${manifest.modes.join(", ")}, not ${options.mode}`,
267
+ });
268
+ checkedModes = [];
269
+ }
270
+ else
271
+ checkedModes = [options.mode];
272
+ }
273
+ if (!/\babai$/.test(entry.trim())) {
274
+ notes.push(`the entry "${entry}" does not end in the word abai: the stub's runEntry answers only "abai ...", so the verbs reach the program only if it translates them itself`);
275
+ }
276
+ if (checkedModes.includes("sidecar") && manifest.check?.run) {
277
+ notes.push("under sidecar this instantiation keeps the null harness in the program's process and opens no socket for it, so the measured overhead leaves out the serialization sidecar observation costs (a deviation every manifest it writes declares)");
278
+ }
279
+ const perMode = [];
280
+ if (l1.ok && manifest.check?.run) {
281
+ for (const mode of checkedModes) {
282
+ perMode.push(await checkMode(subject, mode, { runs, timeoutMs, env: options.env }, violations, unexercised, notes));
283
+ }
284
+ }
285
+ else if (l1.ok) {
286
+ notes.push("check.run is not declared, so nothing above level 1 can be shown and nothing is measured (section 12)");
287
+ }
288
+ let level = 0;
289
+ if (l1.ok)
290
+ level = perMode.length > 0 ? Math.min(...perMode.map((m) => m.level)) : 1;
291
+ const requested = options.level;
292
+ const passed = level >= (requested ?? 1) && violations.length === 0;
293
+ return {
294
+ root: subject.root,
295
+ name: manifest.name,
296
+ abaigrammar: manifest.abaigrammar,
297
+ entry,
298
+ declaredModes: manifest.modes,
299
+ checkedModes,
300
+ checkRun: manifest.check?.run,
301
+ overheadLimit: manifest.check?.overhead,
302
+ compiledOut: "not offered",
303
+ built: "no",
304
+ l1,
305
+ perMode,
306
+ level,
307
+ requested,
308
+ passed,
309
+ unexercised,
310
+ violations,
311
+ notes,
312
+ };
313
+ }
314
+ function pct(value) {
315
+ return Number.isNaN(value) ? "n/a" : `${value >= 0 ? "+" : ""}${(value * 100).toFixed(1)}%`;
316
+ }
317
+ function range(values) {
318
+ if (values.length === 0)
319
+ return "failed";
320
+ const { min, max } = spread(values);
321
+ return `${median(values).toFixed(0)} (${min.toFixed(0)}..${max.toFixed(0)})`;
322
+ }
323
+ export function renderReport(report) {
324
+ const lines = [];
325
+ lines.push(`abaikit check: ${report.name} at ${report.root} (abaigrammar ${report.abaigrammar}, entry: ${report.entry})`);
326
+ let verdict;
327
+ if (report.requested === undefined)
328
+ verdict = report.violations.length === 0 ? "no violations" : "with violations";
329
+ else if (report.level < report.requested)
330
+ verdict = `requested ${report.requested}: not reached`;
331
+ else
332
+ verdict =
333
+ report.violations.length === 0
334
+ ? `requested ${report.requested}: passed`
335
+ : `requested ${report.requested}: reached, with violations`;
336
+ lines.push(report.level === 0
337
+ ? `level: 0 (L1 not reached; ${verdict})`
338
+ : `level: ${report.level} ${LEVEL_NAMES[report.level]} (${verdict})`);
339
+ lines.push(`modes: ${report.declaredModes.join(", ")}${report.checkedModes.length === report.declaredModes.length ? "" : `; checked: ${report.checkedModes.join(", ") || "none"}`}`);
340
+ lines.push(`L1 reachable: ${report.l1.ok ? "ok" : "not reached"}${report.l1.detail ? ` (${report.l1.detail})` : ""}; ran: ${report.l1.ran.join("; ")}`);
341
+ for (const n of [2, 3, 4]) {
342
+ const key = `l${n}`;
343
+ const parts = report.perMode.map((m) => {
344
+ if (m.exercise && !m.exercise.report)
345
+ return `${m.mode}: exercise run failed`;
346
+ if (!m.evidence[key])
347
+ return `${m.mode}: not run`;
348
+ return `${m.mode}: ${m.level >= n ? "ok" : "no"}, ${m.evidence[key]}`;
349
+ });
350
+ lines.push(`L${n} ${LEVEL_NAMES[n]}: ${parts.length > 0 ? parts.join("; ") : "not shown"}`);
351
+ }
352
+ if (report.checkRun !== undefined && report.perMode.some((m) => m.measurement)) {
353
+ lines.push(`overhead (check.run: ${report.checkRun}; built: ${report.built}, the command runs as is; compiled out: ${report.compiledOut}; median ms (min..max) of each run set):`);
354
+ lines.push(" mode runs off null slowdown limit");
355
+ for (const m of report.perMode) {
356
+ const x = m.measurement;
357
+ if (!x)
358
+ continue;
359
+ const limit = x.limit === undefined ? "none" : `${(x.limit * 100).toFixed(1)}%`;
360
+ lines.push(` ${m.mode.padEnd(9)} ${String(x.runs).padEnd(5)} ${range(x.off).padEnd(18)} ${range(x.null).padEnd(18)} ${pct(x.slowdown).padEnd(10)} ${limit}${x.withinNoise ? " (within noise)" : ""}`);
361
+ }
362
+ }
363
+ lines.push(report.unexercised.length === 0
364
+ ? "unexercised: none"
365
+ : `unexercised:\n${report.unexercised.map((u) => ` ${u.mode}: ${u.kind} ${u.name}: ${u.why}`).join("\n")}`);
366
+ lines.push(report.violations.length === 0
367
+ ? "violations: none"
368
+ : `violations:\n${report.violations.map((v) => ` ${v.where}: ${v.what}`).join("\n")}`);
369
+ for (const note of report.notes)
370
+ lines.push(`note: ${note}`);
371
+ return `${lines.join("\n")}\n`;
372
+ }
373
+ /** abaikit's own channels see every check: two cells and one event, for the agent fused into abaikit. */
374
+ export function recordCheck(app, report) {
375
+ if (!app)
376
+ return;
377
+ const runs = app.cell("check.runs");
378
+ runs.set((runs.get() ?? 0) + 1);
379
+ app.cell("check.last_level").set(report.level);
380
+ app.event("check.completed").emit({ repo: report.root, level: report.level });
381
+ }
382
+ /** The `check` capability: the same check, its report as text, for the agent on any front end. */
383
+ export function checkHandler(app, io) {
384
+ return async (params) => {
385
+ if (typeof params.repo !== "string" || params.repo === "")
386
+ throw new UsageError("check needs a repo");
387
+ const level = typeof params.level === "number" ? params.level : undefined;
388
+ if (level !== undefined && (!Number.isInteger(level) || level < 1 || level > 4)) {
389
+ throw new UsageError("level must be 1 to 4");
390
+ }
391
+ const mode = typeof params.mode === "string" && params.mode !== "" ? params.mode : undefined;
392
+ if (mode !== undefined && mode !== "turns" && mode !== "sidecar")
393
+ throw new UsageError("mode must be turns or sidecar");
394
+ const entry = typeof params.entry === "string" && params.entry !== "" ? params.entry : undefined;
395
+ const runs = typeof params.runs === "number" && Number.isInteger(params.runs) && params.runs > 0 ? params.runs : undefined;
396
+ const report = await checkRepository({ repo: params.repo, cwd: io.cwd, env: io.env, level, mode, entry, runs });
397
+ recordCheck(app, report);
398
+ return { level: report.level, passed: report.passed, report: renderReport(report) };
399
+ };
400
+ }
401
+ export async function runCheck(parsed, io, app) {
402
+ const report = await checkRepository({
403
+ repo: parsed.repo,
404
+ cwd: io.cwd,
405
+ env: io.env,
406
+ level: parsed.level,
407
+ mode: parsed.mode,
408
+ entry: parsed.entry,
409
+ runs: parsed.runs,
410
+ timeoutMs: parsed.timeoutS === undefined ? undefined : parsed.timeoutS * 1000,
411
+ });
412
+ recordCheck(app, report);
413
+ io.out(parsed.json ? `${JSON.stringify(report, null, 2)}\n` : renderReport(report));
414
+ return report.passed ? 0 : 1;
415
+ }
416
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"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","sourcesContent":["// abaikit check: conformance as section 12 defines it, with no model.\n// Level 1 is reached through the subject's entry alone (what `manifest`\n// prints, byte for byte the checked-in file, and `doctor --offline --json`); levels 2 to 4 through its `check.run`\n// under the exercise null harness, once per declared mode, read from the\n// report the stub prints at exit; the overhead of 11.6 by the same command\n// under `ABAI_HARNESS=off` and `ABAI_HARNESS=null`, interleaved, side by\n// side. The subject's environment is constructed, never inherited: each\n// run gets exactly the `ABAI_HARNESS` and `ABAI_MODE` it means, the entry\n// runs get neither. A level is the one reached under every checked mode,\n// and passing means that level with no violation.\nimport { readFileSync } from \"node:fs\";\nimport type { Abaigram, DoctorCheck, Manifest, Mode } from \"@abaigram/abai-stub\";\nimport { parseManifestYaml, validateManifest } from \"@abaigram/abai-stub\";\nimport type { ParsedArgs } from \"./bin.ts\";\nimport {\n\ttype CommandRun,\n\tjsonDocument,\n\tlastJsonLine,\n\tlastPlainLine,\n\tmedian,\n\trunCommand,\n\tsubjectEnv,\n\ttailOf,\n} from \"./command.ts\";\nimport { type AbaikitIo, errorMessage, UsageError } from \"./io.ts\";\nimport { entryCommand, loadSubject, type Subject } from \"./subject.ts\";\n\nexport const DEFAULT_RUNS = 3;\nexport const DEFAULT_TIMEOUT_MS = 120_000;\n/** How long an entry verb (manifest, doctor) may take. */\nexport const ENTRY_TIMEOUT_MS = 60_000;\n/** Below this median for the off run, startup noise is most of the number. */\nexport const NOISE_FLOOR_MS = 500;\n/** Characters of a run's output the JSON report carries. */\nconst REPORT_TAIL_CHARS = 4096;\nexport const LEVEL_NAMES = [\"none\", \"reachable\", \"observable\", \"steerable\", \"deliberative\"] as const;\n\nexport interface CheckOptions {\n\trepo: string;\n\tcwd: string;\n\tenv: NodeJS.ProcessEnv;\n\tlevel?: number;\n\tmode?: Mode;\n\tentry?: string;\n\truns?: number;\n\ttimeoutMs?: number;\n}\n\n/** What the stub's exercise harness reports at exit (abai's NullHarnessReport, as JSON on stderr). */\nexport interface ExerciseReport {\n\tform: string;\n\tevents: number;\n\tdropped: number;\n\tdecisions: number;\n\tturns: number;\n\ttouchpoints: number;\n\tcellsWritten: string[];\n\t/** The program's own events, by name; absent in a report from an older stub. */\n\teventsEmitted?: string[];\n\tknobsWritten: string[];\n\tcapabilitiesCalled: string[];\n\tunexercised: Array<{ kind: string; name: string; why: string }>;\n}\n\nexport interface Measurement {\n\truns: number;\n\toff: number[];\n\tnull: number[];\n\toffMedianMs: number;\n\tnullMedianMs: number;\n\t/** null over off, minus one; NaN when either side has no run. */\n\tslowdown: number;\n\tlimit?: number;\n\t/** The runs that did not exit 0 or timed out, by harness. */\n\tfailed: string[];\n\t/** The numbers are within startup noise or the two ranges overlap. */\n\twithinNoise: boolean;\n}\n\nexport interface ModeResult {\n\tmode: Mode;\n\t/** The highest level this mode demonstrated, 1 to 4. */\n\tlevel: number;\n\texercise?: { run: CommandRun; report?: ExerciseReport };\n\tevidence: { l2: string; l3: string; l4: string };\n\tmeasurement?: Measurement;\n}\n\nexport interface Violation {\n\twhere: string;\n\twhat: string;\n}\n\nexport interface CheckReport {\n\troot: string;\n\tname: string;\n\tabaigrammar: string;\n\tentry: string;\n\tdeclaredModes: Mode[];\n\tcheckedModes: Mode[];\n\tcheckRun?: string;\n\toverheadLimit?: string;\n\t/** This instantiation offers no build with the stub compiled out, and builds nothing: the command runs as is. */\n\tcompiledOut: \"not offered\";\n\tbuilt: \"no\";\n\tl1: { ok: boolean; manifestAgrees: boolean; doctor: DoctorCheck[]; ran: string[]; detail: string };\n\tperMode: ModeResult[];\n\tlevel: number;\n\trequested?: number;\n\tpassed: boolean;\n\tunexercised: Array<{ mode: Mode; kind: string; name: string; why: string }>;\n\tviolations: Violation[];\n\tnotes: string[];\n}\n\nfunction parseOverhead(limit: string | undefined): number | undefined {\n\tif (limit === undefined) return undefined;\n\tconst match = /^(\\d+(?:\\.\\d+)?)\\s*%$/.exec(limit.trim());\n\treturn match ? Number(match[1]) / 100 : undefined;\n}\n\nfunction firstDifferingLine(a: string, b: string): number {\n\tconst al = a.split(\"\\n\");\n\tconst bl = b.split(\"\\n\");\n\tfor (let i = 0; i < Math.max(al.length, bl.length); i++) if (al[i] !== bl[i]) return i + 1;\n\treturn 0;\n}\n\nfunction isExerciseReport(\n\tvalue: Record<string, unknown> | undefined,\n): value is ExerciseReport & Record<string, unknown> {\n\treturn value !== undefined && value.form === \"exercise\" && Array.isArray(value.unexercised);\n}\n\nfunction describeRun(run: CommandRun): string {\n\tif (run.timedOut) return \"did not finish within the timeout and was ended\";\n\tif (run.code !== 0) return `exited ${run.code ?? run.signal ?? \"?\"}`;\n\treturn \"exited 0\";\n}\n\nfunction lastWords(run: CommandRun): string {\n\treturn lastPlainLine(run.stderr) || lastPlainLine(run.stdout) || \"no output\";\n}\n\n/** A run as the JSON report carries it: the newest part of each stream, not a whole log. */\nfunction trimmed(run: CommandRun): CommandRun {\n\treturn { ...run, stdout: tailOf(run.stdout, REPORT_TAIL_CHARS), stderr: tailOf(run.stderr, REPORT_TAIL_CHARS) };\n}\n\n/** Level 1 through the entry: the effective manifest, the checked-in file, the offline doctor. */\nasync function checkLevelOne(\n\tsubject: Subject,\n\tentry: string,\n\tenv: NodeJS.ProcessEnv,\n\tviolations: Violation[],\n): Promise<CheckReport[\"l1\"]> {\n\tconst manifest = subject.manifest;\n\tconst fileText = readFileSync(subject.manifestPath, \"utf8\");\n\tlet manifestAgrees = false;\n\tconst details: string[] = [];\n\tconst ran: string[] = [];\n\t// what the entry prints is the manifest: byte for byte the checked-in file (section 12), and valid\n\tconst manifestCommand = entryCommand(manifest, \"manifest\");\n\tran.push(manifestCommand);\n\tconst shown = await runCommand(manifestCommand, { cwd: subject.root, env, timeoutMs: ENTRY_TIMEOUT_MS });\n\tif (shown.code !== 0) {\n\t\tviolations.push({ where: manifestCommand, what: `${describeRun(shown)}: ${lastWords(shown)}` });\n\t} else {\n\t\tlet effective: unknown;\n\t\ttry {\n\t\t\teffective = parseManifestYaml(shown.stdout);\n\t\t} catch (error) {\n\t\t\tviolations.push({ where: manifestCommand, what: `did not print a manifest: ${errorMessage(error)}` });\n\t\t}\n\t\tif (effective !== undefined) {\n\t\t\tconst problems = validateManifest(effective);\n\t\t\tif (problems.length > 0) {\n\t\t\t\tviolations.push({\n\t\t\t\t\twhere: manifestCommand,\n\t\t\t\t\twhat: `the printed manifest does not validate: ${problems.join(\"; \")}`,\n\t\t\t\t});\n\t\t\t} else if ((effective as Manifest).name !== manifest.name) {\n\t\t\t\tviolations.push({\n\t\t\t\t\twhere: subject.manifestPath,\n\t\t\t\t\twhat: `names ${manifest.name}, but the entry's program is ${(effective as Manifest).name}`,\n\t\t\t\t});\n\t\t\t} else if (shown.stdout === fileText) {\n\t\t\t\tmanifestAgrees = true;\n\t\t\t\tdetails.push(\"abaigram.yaml is what the entry prints\");\n\t\t\t} else {\n\t\t\t\tviolations.push({\n\t\t\t\t\twhere: subject.manifestPath,\n\t\t\t\t\twhat: `is stale: first difference at line ${firstDifferingLine(fileText, shown.stdout)}; run: ${entry} manifest > abaigram.yaml`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\tlet doctor: DoctorCheck[] = [];\n\tconst doctorCommand = entryCommand(manifest, \"doctor\", \"--offline\", \"--json\");\n\tran.push(doctorCommand);\n\tconst examined = await runCommand(doctorCommand, { cwd: subject.root, env, timeoutMs: ENTRY_TIMEOUT_MS });\n\tconst parsed = jsonDocument(examined.stdout);\n\tif (parsed && Array.isArray(parsed.checks)) {\n\t\tdoctor = parsed.checks as DoctorCheck[];\n\t\tfor (const check of doctor) {\n\t\t\t// the manifest file is judged above, with the hint for this entry; the doctor's word on it would repeat it\n\t\t\tif (check.status === \"fail\" && check.name !== \"abaigram.yaml\") {\n\t\t\t\tviolations.push({ where: `doctor: ${check.name}`, what: check.detail });\n\t\t\t}\n\t\t}\n\t\tconst failed = doctor.filter((c) => c.status === \"fail\").length;\n\t\tdetails.push(`doctor: ${doctor.filter((c) => c.status === \"ok\").length} ok, ${failed} failed`);\n\t} else {\n\t\tviolations.push({\n\t\t\twhere: doctorCommand,\n\t\t\twhat: `${describeRun(examined)} and printed no report: ${lastWords(examined)}`,\n\t\t});\n\t}\n\tconst ok = manifestAgrees && doctor.length > 0 && doctor.every((c) => c.status !== \"fail\");\n\treturn { ok, manifestAgrees, doctor, ran, detail: details.join(\"; \") };\n}\n\nfunction spread(values: number[]): { min: number; max: number } {\n\treturn { min: Math.min(...values), max: Math.max(...values) };\n}\n\n/** Levels 2 to 4 and the measurement, under one mode. */\nasync function checkMode(\n\tsubject: Subject,\n\tmode: Mode,\n\toptions: { runs: number; timeoutMs: number; env: NodeJS.ProcessEnv },\n\tviolations: Violation[],\n\tunexercised: CheckReport[\"unexercised\"],\n\tnotes: string[],\n): Promise<ModeResult> {\n\tconst manifest = subject.manifest;\n\tconst check = manifest.check as { run: string; overhead?: string };\n\tconst result: ModeResult = { mode, level: 1, evidence: { l2: \"\", l3: \"\", l4: \"\" } };\n\tconst where = (harness: string) => `check.run under ${mode} with ABAI_HARNESS=${harness}`;\n\tconst run = await runCommand(check.run, {\n\t\tcwd: subject.root,\n\t\tenv: subjectEnv(options.env, { ABAI_HARNESS: \"exercise\", ABAI_MODE: mode }),\n\t\ttimeoutMs: options.timeoutMs,\n\t});\n\tresult.exercise = { run: trimmed(run) };\n\t// the report is the stub's exit hook: it is there even when the program failed, and says what was reached\n\tconst report = lastJsonLine(run.stderr, (v) => v.form === \"exercise\");\n\tif (isExerciseReport(report)) {\n\t\tresult.exercise.report = report;\n\t\tfor (const u of report.unexercised) unexercised.push({ mode, ...u });\n\t\tfor (const cell of manifest.cells) {\n\t\t\tif (cell.access === \"read\" && !report.cellsWritten.includes(cell.name)) {\n\t\t\t\tunexercised.push({ mode, kind: \"cell\", name: cell.name, why: \"not written during the exercise run\" });\n\t\t\t}\n\t\t}\n\t\tif (report.eventsEmitted) {\n\t\t\tfor (const event of manifest.events) {\n\t\t\t\tif (!report.eventsEmitted.includes(event.name)) {\n\t\t\t\t\tunexercised.push({ mode, kind: \"event\", name: event.name, why: \"not emitted during the exercise run\" });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif (run.timedOut || run.code !== 0) {\n\t\tviolations.push({ where: where(\"exercise\"), what: `${describeRun(run)}: ${lastWords(run)}` });\n\t\treturn result;\n\t}\n\tif (!isExerciseReport(report)) {\n\t\tviolations.push({\n\t\t\twhere: where(\"exercise\"),\n\t\t\twhat: `printed no exercise report at exit${run.dropped.stderr > 0 ? ` in the ${run.stderr.length} newest bytes of stderr kept` : \"\"}: the stub did not attach its null harness (is the entry hook in main, and the program built?)`,\n\t\t});\n\t\treturn result;\n\t}\n\t// the program's own events, by name; a report that names none (an older stub) shows none: the library's do not count\n\tconst emitted = report.eventsEmitted ?? [];\n\tconst l2 = report.cellsWritten.length > 0 || emitted.length > 0;\n\tresult.evidence.l2 = l2\n\t\t? `cells written: ${report.cellsWritten.join(\", \") || \"none\"}; events: ${emitted.join(\", \") || \"none\"}`\n\t\t: \"no cell written and no event emitted during the exercise run\";\n\tconst l3 = report.capabilitiesCalled.length > 0 || report.knobsWritten.length > 0;\n\tresult.evidence.l3 = l3\n\t\t? `capabilities called: ${report.capabilitiesCalled.join(\", \") || \"none\"}; knobs landed: ${report.knobsWritten.join(\", \") || \"none\"}`\n\t\t: \"no capability called and no knob landed\";\n\tconst l4 = report.decisions > 0;\n\tresult.evidence.l4 = l4 ? `decisions reached: ${report.decisions}` : \"no decision reached\";\n\tresult.level = l2 ? (l3 ? (l4 ? 4 : 3) : 2) : 1;\n\n\t// the measurement (11.6): off and null, interleaved so drift lands on both, K runs each, the medians side by side\n\tconst measurement: Measurement = {\n\t\truns: options.runs,\n\t\toff: [],\n\t\tnull: [],\n\t\toffMedianMs: Number.NaN,\n\t\tnullMedianMs: Number.NaN,\n\t\tslowdown: Number.NaN,\n\t\tfailed: [],\n\t\twithinNoise: false,\n\t};\n\tfor (let i = 0; i < options.runs; i++) {\n\t\tfor (const harness of [\"off\", \"null\"] as const) {\n\t\t\tconst timed = await runCommand(check.run, {\n\t\t\t\tcwd: subject.root,\n\t\t\t\tenv: subjectEnv(options.env, { ABAI_HARNESS: harness, ABAI_MODE: mode }),\n\t\t\t\ttimeoutMs: options.timeoutMs,\n\t\t\t});\n\t\t\tif (timed.timedOut || timed.code !== 0) measurement.failed.push(`${harness}: ${describeRun(timed)}`);\n\t\t\telse measurement[harness].push(timed.ms);\n\t\t}\n\t}\n\tif (measurement.failed.length > 0) {\n\t\tviolations.push({\n\t\t\twhere: `check.run under ${mode} with ABAI_HARNESS=off and null`,\n\t\t\twhat: `measurement runs failed: ${measurement.failed.join(\"; \")}`,\n\t\t});\n\t}\n\tif (measurement.off.length > 0 && measurement.null.length > 0) {\n\t\tmeasurement.offMedianMs = median(measurement.off);\n\t\tmeasurement.nullMedianMs = median(measurement.null);\n\t\tmeasurement.slowdown =\n\t\t\tmeasurement.offMedianMs > 0 ? measurement.nullMedianMs / measurement.offMedianMs - 1 : Number.NaN;\n\t\tconst off = spread(measurement.off);\n\t\tconst nul = spread(measurement.null);\n\t\tmeasurement.withinNoise = measurement.offMedianMs < NOISE_FLOOR_MS || (nul.min <= off.max && off.min <= nul.max);\n\t\tif (measurement.withinNoise) {\n\t\t\tnotes.push(\n\t\t\t\t`under ${mode} the overhead numbers are within noise (off ${off.min.toFixed(0)}..${off.max.toFixed(0)} ms, null ${nul.min.toFixed(0)}..${nul.max.toFixed(0)} ms, ${options.runs} runs each): raise --runs or give check.run more work before reading the slowdown`,\n\t\t\t);\n\t\t}\n\t\tconst limit = parseOverhead(check.overhead);\n\t\tif (check.overhead !== undefined && limit === undefined) {\n\t\t\tviolations.push({\n\t\t\t\twhere: \"check.overhead\",\n\t\t\t\twhat: `${JSON.stringify(check.overhead)} is not a percentage such as \"5%\"`,\n\t\t\t});\n\t\t} else if (limit !== undefined) {\n\t\t\tmeasurement.limit = limit;\n\t\t\tif (measurement.slowdown > limit) {\n\t\t\t\tviolations.push({\n\t\t\t\t\twhere: where(\"null\"),\n\t\t\t\t\twhat: `the null-harness run is ${(measurement.slowdown * 100).toFixed(1)}% slower than the off run (medians of ${options.runs}${measurement.withinNoise ? \", within noise\" : \"\"}); check.overhead allows ${check.overhead}`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\tresult.measurement = measurement;\n\treturn result;\n}\n\nexport async function checkRepository(options: CheckOptions): Promise<CheckReport> {\n\tconst violations: Violation[] = [];\n\tconst unexercised: CheckReport[\"unexercised\"] = [];\n\tconst notes: string[] = [];\n\tconst subject = loadSubject(options.repo, options.cwd);\n\tconst manifest = subject.manifest;\n\tconst entry = options.entry ?? manifest.entry;\n\tconst subjectForEntry: Subject = options.entry\n\t\t? { ...subject, manifest: { ...manifest, entry: options.entry } }\n\t\t: subject;\n\tconst runs = options.runs ?? DEFAULT_RUNS;\n\tconst timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n\tconst l1 = await checkLevelOne(subjectForEntry, entry, subjectEnv(options.env), violations);\n\n\tlet checkedModes: Mode[] = manifest.modes;\n\tif (options.mode !== undefined) {\n\t\tif (!manifest.modes.includes(options.mode)) {\n\t\t\tviolations.push({\n\t\t\t\twhere: \"--mode\",\n\t\t\t\twhat: `${manifest.name} declares ${manifest.modes.join(\", \")}, not ${options.mode}`,\n\t\t\t});\n\t\t\tcheckedModes = [];\n\t\t} else checkedModes = [options.mode];\n\t}\n\tif (!/\\babai$/.test(entry.trim())) {\n\t\tnotes.push(\n\t\t\t`the entry \"${entry}\" does not end in the word abai: the stub's runEntry answers only \"abai ...\", so the verbs reach the program only if it translates them itself`,\n\t\t);\n\t}\n\tif (checkedModes.includes(\"sidecar\") && manifest.check?.run) {\n\t\tnotes.push(\n\t\t\t\"under sidecar this instantiation keeps the null harness in the program's process and opens no socket for it, so the measured overhead leaves out the serialization sidecar observation costs (a deviation every manifest it writes declares)\",\n\t\t);\n\t}\n\n\tconst perMode: ModeResult[] = [];\n\tif (l1.ok && manifest.check?.run) {\n\t\tfor (const mode of checkedModes) {\n\t\t\tperMode.push(\n\t\t\t\tawait checkMode(subject, mode, { runs, timeoutMs, env: options.env }, violations, unexercised, notes),\n\t\t\t);\n\t\t}\n\t} else if (l1.ok) {\n\t\tnotes.push(\n\t\t\t\"check.run is not declared, so nothing above level 1 can be shown and nothing is measured (section 12)\",\n\t\t);\n\t}\n\n\tlet level = 0;\n\tif (l1.ok) level = perMode.length > 0 ? Math.min(...perMode.map((m) => m.level)) : 1;\n\tconst requested = options.level;\n\tconst passed = level >= (requested ?? 1) && violations.length === 0;\n\treturn {\n\t\troot: subject.root,\n\t\tname: manifest.name,\n\t\tabaigrammar: manifest.abaigrammar,\n\t\tentry,\n\t\tdeclaredModes: manifest.modes,\n\t\tcheckedModes,\n\t\tcheckRun: manifest.check?.run,\n\t\toverheadLimit: manifest.check?.overhead,\n\t\tcompiledOut: \"not offered\",\n\t\tbuilt: \"no\",\n\t\tl1,\n\t\tperMode,\n\t\tlevel,\n\t\trequested,\n\t\tpassed,\n\t\tunexercised,\n\t\tviolations,\n\t\tnotes,\n\t};\n}\n\nfunction pct(value: number): string {\n\treturn Number.isNaN(value) ? \"n/a\" : `${value >= 0 ? \"+\" : \"\"}${(value * 100).toFixed(1)}%`;\n}\n\nfunction range(values: number[]): string {\n\tif (values.length === 0) return \"failed\";\n\tconst { min, max } = spread(values);\n\treturn `${median(values).toFixed(0)} (${min.toFixed(0)}..${max.toFixed(0)})`;\n}\n\nexport function renderReport(report: CheckReport): string {\n\tconst lines: string[] = [];\n\tlines.push(\n\t\t`abaikit check: ${report.name} at ${report.root} (abaigrammar ${report.abaigrammar}, entry: ${report.entry})`,\n\t);\n\tlet verdict: string;\n\tif (report.requested === undefined) verdict = report.violations.length === 0 ? \"no violations\" : \"with violations\";\n\telse if (report.level < report.requested) verdict = `requested ${report.requested}: not reached`;\n\telse\n\t\tverdict =\n\t\t\treport.violations.length === 0\n\t\t\t\t? `requested ${report.requested}: passed`\n\t\t\t\t: `requested ${report.requested}: reached, with violations`;\n\tlines.push(\n\t\treport.level === 0\n\t\t\t? `level: 0 (L1 not reached; ${verdict})`\n\t\t\t: `level: ${report.level} ${LEVEL_NAMES[report.level]} (${verdict})`,\n\t);\n\tlines.push(\n\t\t`modes: ${report.declaredModes.join(\", \")}${report.checkedModes.length === report.declaredModes.length ? \"\" : `; checked: ${report.checkedModes.join(\", \") || \"none\"}`}`,\n\t);\n\tlines.push(\n\t\t`L1 reachable: ${report.l1.ok ? \"ok\" : \"not reached\"}${report.l1.detail ? ` (${report.l1.detail})` : \"\"}; ran: ${report.l1.ran.join(\"; \")}`,\n\t);\n\tfor (const n of [2, 3, 4] as const) {\n\t\tconst key = `l${n}` as const;\n\t\tconst parts = report.perMode.map((m) => {\n\t\t\tif (m.exercise && !m.exercise.report) return `${m.mode}: exercise run failed`;\n\t\t\tif (!m.evidence[key]) return `${m.mode}: not run`;\n\t\t\treturn `${m.mode}: ${m.level >= n ? \"ok\" : \"no\"}, ${m.evidence[key]}`;\n\t\t});\n\t\tlines.push(`L${n} ${LEVEL_NAMES[n]}: ${parts.length > 0 ? parts.join(\"; \") : \"not shown\"}`);\n\t}\n\tif (report.checkRun !== undefined && report.perMode.some((m) => m.measurement)) {\n\t\tlines.push(\n\t\t\t`overhead (check.run: ${report.checkRun}; built: ${report.built}, the command runs as is; compiled out: ${report.compiledOut}; median ms (min..max) of each run set):`,\n\t\t);\n\t\tlines.push(\" mode runs off null slowdown limit\");\n\t\tfor (const m of report.perMode) {\n\t\t\tconst x = m.measurement;\n\t\t\tif (!x) continue;\n\t\t\tconst limit = x.limit === undefined ? \"none\" : `${(x.limit * 100).toFixed(1)}%`;\n\t\t\tlines.push(\n\t\t\t\t` ${m.mode.padEnd(9)} ${String(x.runs).padEnd(5)} ${range(x.off).padEnd(18)} ${range(x.null).padEnd(18)} ${pct(x.slowdown).padEnd(10)} ${limit}${x.withinNoise ? \" (within noise)\" : \"\"}`,\n\t\t\t);\n\t\t}\n\t}\n\tlines.push(\n\t\treport.unexercised.length === 0\n\t\t\t? \"unexercised: none\"\n\t\t\t: `unexercised:\\n${report.unexercised.map((u) => ` ${u.mode}: ${u.kind} ${u.name}: ${u.why}`).join(\"\\n\")}`,\n\t);\n\tlines.push(\n\t\treport.violations.length === 0\n\t\t\t? \"violations: none\"\n\t\t\t: `violations:\\n${report.violations.map((v) => ` ${v.where}: ${v.what}`).join(\"\\n\")}`,\n\t);\n\tfor (const note of report.notes) lines.push(`note: ${note}`);\n\treturn `${lines.join(\"\\n\")}\\n`;\n}\n\n/** abaikit's own channels see every check: two cells and one event, for the agent fused into abaikit. */\nexport function recordCheck(app: Abaigram | undefined, report: CheckReport): void {\n\tif (!app) return;\n\tconst runs = app.cell<number>(\"check.runs\");\n\truns.set((runs.get() ?? 0) + 1);\n\tapp.cell<number>(\"check.last_level\").set(report.level);\n\tapp.event(\"check.completed\").emit({ repo: report.root, level: report.level });\n}\n\n/** The `check` capability: the same check, its report as text, for the agent on any front end. */\nexport function checkHandler(app: Abaigram, io: Pick<AbaikitIo, \"cwd\" | \"env\">) {\n\treturn async (params: Record<string, unknown>): Promise<{ level: number; passed: boolean; report: string }> => {\n\t\tif (typeof params.repo !== \"string\" || params.repo === \"\") throw new UsageError(\"check needs a repo\");\n\t\tconst level = typeof params.level === \"number\" ? params.level : undefined;\n\t\tif (level !== undefined && (!Number.isInteger(level) || level < 1 || level > 4)) {\n\t\t\tthrow new UsageError(\"level must be 1 to 4\");\n\t\t}\n\t\tconst mode = typeof params.mode === \"string\" && params.mode !== \"\" ? params.mode : undefined;\n\t\tif (mode !== undefined && mode !== \"turns\" && mode !== \"sidecar\")\n\t\t\tthrow new UsageError(\"mode must be turns or sidecar\");\n\t\tconst entry = typeof params.entry === \"string\" && params.entry !== \"\" ? params.entry : undefined;\n\t\tconst runs =\n\t\t\ttypeof params.runs === \"number\" && Number.isInteger(params.runs) && params.runs > 0 ? params.runs : undefined;\n\t\tconst report = await checkRepository({ repo: params.repo, cwd: io.cwd, env: io.env, level, mode, entry, runs });\n\t\trecordCheck(app, report);\n\t\treturn { level: report.level, passed: report.passed, report: renderReport(report) };\n\t};\n}\n\nexport async function runCheck(\n\tparsed: Extract<ParsedArgs, { verb: \"check\" }>,\n\tio: AbaikitIo,\n\tapp?: Abaigram,\n): Promise<number> {\n\tconst report = await checkRepository({\n\t\trepo: parsed.repo,\n\t\tcwd: io.cwd,\n\t\tenv: io.env,\n\t\tlevel: parsed.level,\n\t\tmode: parsed.mode,\n\t\tentry: parsed.entry,\n\t\truns: parsed.runs,\n\t\ttimeoutMs: parsed.timeoutS === undefined ? undefined : parsed.timeoutS * 1000,\n\t});\n\trecordCheck(app, report);\n\tio.out(parsed.json ? `${JSON.stringify(report, null, 2)}\\n` : renderReport(report));\n\treturn report.passed ? 0 : 1;\n}\n"]}
@@ -0,0 +1,41 @@
1
+ export interface CommandRun {
2
+ command: string;
3
+ code: number | null;
4
+ signal: NodeJS.Signals | null;
5
+ /** The last `maxOutputBytes` of each stream; what came before is counted in `dropped`. */
6
+ stdout: string;
7
+ stderr: string;
8
+ dropped: {
9
+ stdout: number;
10
+ stderr: number;
11
+ };
12
+ /** Wall clock, from spawn to exit. */
13
+ ms: number;
14
+ timedOut: boolean;
15
+ }
16
+ export interface CommandOptions {
17
+ cwd: string;
18
+ env: NodeJS.ProcessEnv;
19
+ timeoutMs: number;
20
+ /** Bytes of each stream kept, the newest; default 4 MiB. */
21
+ maxOutputBytes?: number;
22
+ }
23
+ /** Variables that tell the stub what to do; a subject inherits only where things are (`ABAI_HOME`, `ABAI_RUN_DIR`, the harness locations). */
24
+ export declare const BEHAVIOUR_VARIABLES: string[];
25
+ /** The environment a subject's run gets: ours without the behaviour variables, plus exactly what this run means. */
26
+ export declare function subjectEnv(base: NodeJS.ProcessEnv, set?: Record<string, string>): NodeJS.ProcessEnv;
27
+ /** After the timeout's SIGTERM, how long before the group is killed. */
28
+ export declare const FORCE_AFTER_MS = 2000;
29
+ /** After the shell has exited, how long a survivor may hold the pipes before the group is killed and the run resolves. */
30
+ export declare const CLOSE_GRACE_MS = 1500;
31
+ export declare function runCommand(command: string, options: CommandOptions): Promise<CommandRun>;
32
+ /** A stream that is one JSON object, pretty or not, else its last line that is one. */
33
+ export declare function jsonDocument(text: string): Record<string, unknown> | undefined;
34
+ /** The last line of a stream that parses as a JSON object and satisfies `accept`, or undefined. */
35
+ export declare function lastJsonLine(text: string, accept?: (value: Record<string, unknown>) => boolean): Record<string, unknown> | undefined;
36
+ /** The last non-empty line that is not a JSON object, for saying how a run failed. */
37
+ export declare function lastPlainLine(text: string): string;
38
+ /** The newest `bytes` of a text, for a report that should not carry a whole log. */
39
+ export declare function tailOf(text: string, bytes: number): string;
40
+ export declare function median(values: number[]): number;
41
+ //# sourceMappingURL=command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,UAAU;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;IAC9B,0FAA0F;IAC1F,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,sCAAsC;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,8IAA8I;AAC9I,eAAO,MAAM,mBAAmB,UAM/B,CAAC;AAEF,oHAAoH;AACpH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,EAAE,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAAG,MAAM,CAAC,UAAU,CAMvG;AAED,wEAAwE;AACxE,eAAO,MAAM,cAAc,OAAO,CAAC;AACnC,0HAA0H;AAC1H,eAAO,MAAM,cAAc,OAAO,CAAC;AAyBnC,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,CAuExF;AAED,uFAAuF;AACvF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAS9E;AAED,mGAAmG;AACnG,wBAAgB,YAAY,CAC3B,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAoB,GAC9D,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAoBrC;AAED,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGlD;AAED,oFAAoF;AACpF,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAI/C","sourcesContent":["// Running a subject's commands: its entry's verbs and its check.run, by\n// the shell in its root, with a constructed environment and a bound on\n// time. The command runs in a process group of its own, so a compound\n// command's children are ended with it: a run that outlives its bound is\n// ended by that group, SIGTERM then SIGKILL, and abaikit's own end ends\n// it too. Nothing on the machine is looked for by name.\nimport { spawn } from \"node:child_process\";\nimport { performance } from \"node:perf_hooks\";\n\nexport interface CommandRun {\n\tcommand: string;\n\tcode: number | null;\n\tsignal: NodeJS.Signals | null;\n\t/** The last `maxOutputBytes` of each stream; what came before is counted in `dropped`. */\n\tstdout: string;\n\tstderr: string;\n\tdropped: { stdout: number; stderr: number };\n\t/** Wall clock, from spawn to exit. */\n\tms: number;\n\ttimedOut: boolean;\n}\n\nexport interface CommandOptions {\n\tcwd: string;\n\tenv: NodeJS.ProcessEnv;\n\ttimeoutMs: number;\n\t/** Bytes of each stream kept, the newest; default 4 MiB. */\n\tmaxOutputBytes?: number;\n}\n\n/** Variables that tell the stub what to do; a subject inherits only where things are (`ABAI_HOME`, `ABAI_RUN_DIR`, the harness locations). */\nexport const BEHAVIOUR_VARIABLES = [\n\t\"ABAI_MODE\",\n\t\"ABAI_HARNESS\",\n\t\"ABAI_LAUNCH_SOCKET\",\n\t\"ABAI_CHANNEL\",\n\t\"ABAI_ATTACH_WAIT_MS\",\n];\n\n/** The environment a subject's run gets: ours without the behaviour variables, plus exactly what this run means. */\nexport function subjectEnv(base: NodeJS.ProcessEnv, set: Record<string, string> = {}): NodeJS.ProcessEnv {\n\tconst env: NodeJS.ProcessEnv = {};\n\tfor (const [key, value] of Object.entries(base)) {\n\t\tif (!BEHAVIOUR_VARIABLES.includes(key)) env[key] = value;\n\t}\n\treturn { ...env, ...set };\n}\n\n/** After the timeout's SIGTERM, how long before the group is killed. */\nexport const FORCE_AFTER_MS = 2000;\n/** After the shell has exited, how long a survivor may hold the pipes before the group is killed and the run resolves. */\nexport const CLOSE_GRACE_MS = 1500;\n\n/** The newest `max` bytes of a stream, as chunks arrive. */\nclass Tail {\n\tprivate readonly chunks: Buffer[] = [];\n\tprivate readonly max: number;\n\tprivate kept = 0;\n\tdropped = 0;\n\tconstructor(max: number) {\n\t\tthis.max = max;\n\t}\n\tpush(chunk: Buffer): void {\n\t\tthis.chunks.push(chunk);\n\t\tthis.kept += chunk.length;\n\t\twhile (this.kept > this.max && this.chunks.length > 1) {\n\t\t\tconst oldest = this.chunks.shift() as Buffer;\n\t\t\tthis.kept -= oldest.length;\n\t\t\tthis.dropped += oldest.length;\n\t\t}\n\t}\n\ttext(): string {\n\t\treturn Buffer.concat(this.chunks).toString(\"utf8\");\n\t}\n}\n\nexport function runCommand(command: string, options: CommandOptions): Promise<CommandRun> {\n\tconst max = options.maxOutputBytes ?? 4 * 1024 * 1024;\n\treturn new Promise((resolve) => {\n\t\tconst started = performance.now();\n\t\tconst child = spawn(\"sh\", [\"-c\", command], {\n\t\t\tcwd: options.cwd,\n\t\t\tenv: options.env,\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\tdetached: true,\n\t\t});\n\t\tconst out = new Tail(max);\n\t\tconst err = new Tail(max);\n\t\tchild.stdout?.on(\"data\", (chunk: Buffer) => out.push(chunk));\n\t\tchild.stderr?.on(\"data\", (chunk: Buffer) => err.push(chunk));\n\t\t// the whole group, by the pid we were given; the child alone when the group is already gone\n\t\tconst signalGroup = (signal: NodeJS.Signals): void => {\n\t\t\tconst pid = child.pid;\n\t\t\tif (pid === undefined) return;\n\t\t\ttry {\n\t\t\t\tprocess.kill(-pid, signal);\n\t\t\t} catch {\n\t\t\t\ttry {\n\t\t\t\t\tchild.kill(signal);\n\t\t\t\t} catch {\n\t\t\t\t\t// already gone\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tlet timedOut = false;\n\t\tlet settled = false;\n\t\tlet exited: { code: number | null; signal: NodeJS.Signals | null } | undefined;\n\t\tlet closeGrace: NodeJS.Timeout | undefined;\n\t\tconst timer = setTimeout(() => {\n\t\t\ttimedOut = true;\n\t\t\tsignalGroup(\"SIGTERM\");\n\t\t}, options.timeoutMs);\n\t\tconst force = setTimeout(() => signalGroup(\"SIGKILL\"), options.timeoutMs + FORCE_AFTER_MS);\n\t\tforce.unref();\n\t\t// a group of its own is outside the terminal's foreground group: our end has to reach it\n\t\tconst forward = (signal: NodeJS.Signals): void => signalGroup(signal);\n\t\tconst forwarded: NodeJS.Signals[] = [\"SIGINT\", \"SIGTERM\", \"SIGHUP\"];\n\t\tfor (const signal of forwarded) process.on(signal, forward);\n\t\tconst finish = (code: number | null, signal: NodeJS.Signals | null, failure?: Error): void => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timer);\n\t\t\tclearTimeout(force);\n\t\t\tif (closeGrace !== undefined) clearTimeout(closeGrace);\n\t\t\tfor (const s of forwarded) process.off(s, forward);\n\t\t\tresolve({\n\t\t\t\tcommand,\n\t\t\t\tcode,\n\t\t\t\tsignal,\n\t\t\t\tstdout: out.text(),\n\t\t\t\tstderr: err.text() + (failure ? `${failure.message}\\n` : \"\"),\n\t\t\t\tdropped: { stdout: out.dropped, stderr: err.dropped },\n\t\t\t\tms: performance.now() - started,\n\t\t\t\ttimedOut,\n\t\t\t});\n\t\t};\n\t\tchild.once(\"error\", (error) => finish(null, null, error));\n\t\tchild.once(\"exit\", (code, signal) => {\n\t\t\texited = { code, signal };\n\t\t\t// the pipes may be held by a survivor of the group: a bounded wait for close, then the group is ended\n\t\t\tcloseGrace = setTimeout(() => {\n\t\t\t\tsignalGroup(\"SIGKILL\");\n\t\t\t\tfinish(code, signal);\n\t\t\t}, CLOSE_GRACE_MS);\n\t\t});\n\t\tchild.once(\"close\", (code, signal) => finish(exited?.code ?? code, exited?.signal ?? signal));\n\t});\n}\n\n/** A stream that is one JSON object, pretty or not, else its last line that is one. */\nexport function jsonDocument(text: string): Record<string, unknown> | undefined {\n\ttry {\n\t\tconst parsed = JSON.parse(text) as unknown;\n\t\tif (typeof parsed === \"object\" && parsed !== null && !Array.isArray(parsed))\n\t\t\treturn parsed as Record<string, unknown>;\n\t} catch {\n\t\t// not one document\n\t}\n\treturn lastJsonLine(text);\n}\n\n/** The last line of a stream that parses as a JSON object and satisfies `accept`, or undefined. */\nexport function lastJsonLine(\n\ttext: string,\n\taccept: (value: Record<string, unknown>) => boolean = () => true,\n): Record<string, unknown> | undefined {\n\tconst lines = text.split(\"\\n\").filter((l) => l.trim() !== \"\");\n\tfor (let i = lines.length - 1; i >= 0; i--) {\n\t\tconst line = lines[i].trim();\n\t\tif (!line.startsWith(\"{\")) continue;\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(line) as unknown;\n\t\t\tif (\n\t\t\t\ttypeof parsed === \"object\" &&\n\t\t\t\tparsed !== null &&\n\t\t\t\t!Array.isArray(parsed) &&\n\t\t\t\taccept(parsed as Record<string, unknown>)\n\t\t\t) {\n\t\t\t\treturn parsed as Record<string, unknown>;\n\t\t\t}\n\t\t} catch {\n\t\t\t// not this line\n\t\t}\n\t}\n\treturn undefined;\n}\n\n/** The last non-empty line that is not a JSON object, for saying how a run failed. */\nexport function lastPlainLine(text: string): string {\n\tconst lines = text.split(\"\\n\").filter((l) => l.trim() !== \"\" && !l.trim().startsWith(\"{\"));\n\treturn lines.at(-1)?.trim() ?? \"\";\n}\n\n/** The newest `bytes` of a text, for a report that should not carry a whole log. */\nexport function tailOf(text: string, bytes: number): string {\n\treturn text.length <= bytes ? text : `[${text.length - bytes} earlier characters omitted]\\n${text.slice(-bytes)}`;\n}\n\nexport function median(values: number[]): number {\n\tconst sorted = [...values].sort((a, b) => a - b);\n\tconst mid = Math.floor(sorted.length / 2);\n\treturn sorted.length % 2 === 1 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;\n}\n"]}