@tangle-network/agent-runtime 0.69.0 → 0.70.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 (78) hide show
  1. package/README.md +81 -13
  2. package/dist/agent.d.ts +4 -5
  3. package/dist/agent.js +3 -6
  4. package/dist/agent.js.map +1 -1
  5. package/dist/chunk-BYZCXQHF.js +474 -0
  6. package/dist/chunk-BYZCXQHF.js.map +1 -0
  7. package/dist/{chunk-G6U3GVK2.js → chunk-EDCVUZZC.js} +2 -2
  8. package/dist/{chunk-RTDI2DHD.js → chunk-L5ZFBVT6.js} +6 -6
  9. package/dist/chunk-L5ZFBVT6.js.map +1 -0
  10. package/dist/{chunk-NLRA6434.js → chunk-QXWGSDAQ.js} +716 -535
  11. package/dist/chunk-QXWGSDAQ.js.map +1 -0
  12. package/dist/{chunk-QZ6FA5LM.js → chunk-ZNQVMMR5.js} +3 -3
  13. package/dist/{coordination-Curpzeyc.d.ts → coordination-C7WxwHXq.d.ts} +8 -1
  14. package/dist/{delegates-CLFNAKyi.d.ts → delegates-DqAgo32T.d.ts} +105 -10
  15. package/dist/{improvement-adapter-BC4HhuAR.d.ts → improvement-adapter-BVuMragr.d.ts} +1 -1
  16. package/dist/index.d.ts +439 -100
  17. package/dist/index.js +583 -125
  18. package/dist/index.js.map +1 -1
  19. package/dist/intelligence.d.ts +2 -3
  20. package/dist/{loop-runner-bin-B6dzNZC8.d.ts → loop-runner-bin-B0NeLTRd.d.ts} +4 -4
  21. package/dist/loop-runner-bin.d.ts +7 -10
  22. package/dist/loop-runner-bin.js +4 -7
  23. package/dist/loops.d.ts +3149 -16
  24. package/dist/loops.js +34 -63
  25. package/dist/mcp/bin.js +14 -16
  26. package/dist/mcp/bin.js.map +1 -1
  27. package/dist/mcp/index.d.ts +8 -12
  28. package/dist/mcp/index.js +16 -19
  29. package/dist/mcp/index.js.map +1 -1
  30. package/dist/{openai-tools-CA2N3-Ak.d.ts → openai-tools-DPx9Gzvn.d.ts} +1 -1
  31. package/dist/profiles.d.ts +94 -6
  32. package/dist/profiles.js +320 -9
  33. package/dist/profiles.js.map +1 -1
  34. package/dist/{router-client-30Y_pca8.d.ts → router-client-C7kp_ECN.d.ts} +37 -1
  35. package/dist/{substrate-CUgk7F7s.d.ts → substrate-BoRXgvka.d.ts} +52 -1
  36. package/dist/{types-Crxftafi.d.ts → types-BYa2ZOAx.d.ts} +83 -2
  37. package/dist/{types-p8dWBIXL.d.ts → types-DJu6TBGp.d.ts} +2 -16
  38. package/dist/{worktree-fanout-DUiKPApb.d.ts → worktree-fanout-gNfl0Byj.d.ts} +9 -37
  39. package/package.json +9 -39
  40. package/dist/analyst-loop.d.ts +0 -52
  41. package/dist/analyst-loop.js +0 -11
  42. package/dist/analyst-loop.js.map +0 -1
  43. package/dist/audit.d.ts +0 -93
  44. package/dist/audit.js +0 -312
  45. package/dist/audit.js.map +0 -1
  46. package/dist/chunk-4B6U4CVQ.js +0 -15
  47. package/dist/chunk-4B6U4CVQ.js.map +0 -1
  48. package/dist/chunk-K4FQSEXQ.js +0 -214
  49. package/dist/chunk-K4FQSEXQ.js.map +0 -1
  50. package/dist/chunk-NLRA6434.js.map +0 -1
  51. package/dist/chunk-O2UPHN7X.js +0 -114
  52. package/dist/chunk-O2UPHN7X.js.map +0 -1
  53. package/dist/chunk-P5OKDSLB.js +0 -580
  54. package/dist/chunk-P5OKDSLB.js.map +0 -1
  55. package/dist/chunk-RTDI2DHD.js.map +0 -1
  56. package/dist/chunk-VLF5RHEQ.js +0 -143
  57. package/dist/chunk-VLF5RHEQ.js.map +0 -1
  58. package/dist/coder-2leJPOvC.d.ts +0 -52
  59. package/dist/improvement.d.ts +0 -208
  60. package/dist/improvement.js +0 -343
  61. package/dist/improvement.js.map +0 -1
  62. package/dist/local-harness-BE_h8szs.d.ts +0 -93
  63. package/dist/platform.d.ts +0 -255
  64. package/dist/platform.js +0 -229
  65. package/dist/platform.js.map +0 -1
  66. package/dist/run-loop-D3PwlG7J.d.ts +0 -112
  67. package/dist/runtime-hooks-C7JwKb9E.d.ts +0 -70
  68. package/dist/runtime.d.ts +0 -3120
  69. package/dist/runtime.js +0 -263
  70. package/dist/runtime.js.map +0 -1
  71. package/dist/topology.d.ts +0 -126
  72. package/dist/topology.js +0 -333
  73. package/dist/topology.js.map +0 -1
  74. package/dist/workflow.d.ts +0 -551
  75. package/dist/workflow.js +0 -1781
  76. package/dist/workflow.js.map +0 -1
  77. /package/dist/{chunk-G6U3GVK2.js.map → chunk-EDCVUZZC.js.map} +0 -0
  78. /package/dist/{chunk-QZ6FA5LM.js.map → chunk-ZNQVMMR5.js.map} +0 -0
@@ -1,10 +1,8 @@
1
1
  import { AgentProfile } from '@tangle-network/agent-interface';
2
2
  import { AnalystFinding, DefaultVerdict } from '@tangle-network/agent-eval';
3
- import { h as AgentSpec, E as ExecutorRegistry, B as Budget, A as Agent, i as SpawnJournal, g as ResultBlobStore, j as RootHandle, k as SupervisedResult, N as NodeId, l as Settled, m as Spend, S as Scope, n as Executor, G as GitRunner } from './delegates-CLFNAKyi.js';
4
- import { I as Iteration } from './types-Crxftafi.js';
3
+ import { h as AgentSpec, E as ExecutorRegistry, B as Budget, A as Agent, i as SpawnJournal, g as ResultBlobStore, j as RootHandle, k as SupervisedResult, N as NodeId, l as Settled, m as Spend, S as Scope, n as Executor, L as LocalHarness, G as GitRunner, r as runLocalHarness } from './delegates-DqAgo32T.js';
4
+ import { b as RuntimeHooks, I as Iteration } from './types-BYa2ZOAx.js';
5
5
  import { BackendType } from '@tangle-network/sandbox';
6
- import { R as RuntimeHooks } from './runtime-hooks-C7JwKb9E.js';
7
- import { L as LocalHarness, r as runLocalHarness } from './local-harness-BE_h8szs.js';
8
6
 
9
7
  /**
10
8
  * @experimental
@@ -787,6 +785,12 @@ type EqualKOnCost = (arms: ReadonlyArray<EqualKArm>, options?: EqualKOnCostOptio
787
785
  * fail-closed (not delivered), never a crash.
788
786
  */
789
787
 
788
+ /**
789
+ * The deployable completion oracle passed to {@link gateOnDeliverable}: a `check` that
790
+ * decides DELIVERED (settles `valid` ⟺ it resolves true) plus an optional `describe` of
791
+ * what the spawn was supposed to produce. The check reads the child's output — never the
792
+ * model judging itself.
793
+ */
790
794
  interface DeliverableSpec<Out = unknown> {
791
795
  /** The deployable check that decides DELIVERED. `settled.valid ⟺ this resolves true`. */
792
796
  check: (out: Out) => boolean | Promise<boolean>;
@@ -813,15 +817,6 @@ declare function gateOnDeliverable<Out>(inner: Executor<Out>, deliverable: Deliv
813
817
  * (`isNonEmptyPatch` / `touchesSecretPath`) for reuse outside the full scorer.
814
818
  */
815
819
 
816
- /** @experimental The patch + its derived PASS signals the mechanical gate decides on. */
817
- interface CoderCheckInput {
818
- /** The unified diff produced by the run. */
819
- patch: string;
820
- /** Did `testCmd` exit clean? */
821
- testsPassed: boolean;
822
- /** Did `typecheckCmd` exit clean? */
823
- typecheckPassed: boolean;
824
- }
825
820
  /** @experimental The per-task constraints the mechanical gate enforces. */
826
821
  interface CoderCheckConstraints {
827
822
  /** Default 400. Hard cap; gate fails when exceeded. */
@@ -829,29 +824,6 @@ interface CoderCheckConstraints {
829
824
  /** Literal path prefixes the patch must not touch. */
830
825
  forbiddenPaths?: string[];
831
826
  }
832
- /** The unified-diff paths the patch touches — the `+++`/`---` headers, de-`a/`/`b/`-prefixed,
833
- * with `/dev/null` (a delete's other side) dropped. */
834
- declare function touchedPathsFromPatch(patch: string): string[];
835
- /** Count the added/removed content lines in a unified diff (excludes the `+++`/`---` headers). */
836
- declare function countDiffLines(patch: string): number;
837
- /** True when the patch actually changes something — at least one touched path AND non-blank body.
838
- * An empty patch can trivially "pass" tests/typecheck (nothing changed) yet does no work. */
839
- declare function isNonEmptyPatch(patch: string): boolean;
840
- /** The credential-shaped paths the patch touches (always-on floor, independent of `forbiddenPaths`). */
841
- declare function touchesSecretPath(patch: string): string[];
842
- /**
843
- * @experimental
844
- *
845
- * The pure mechanical gate — the SINGLE source of the no-op / always-on secret-path floor /
846
- * diff-size / forbidden-path / test / typecheck checks. No I/O: it scores a patch + its
847
- * already-derived pass signals.
848
- *
849
- * Checks in order: (1) no-op rejection, (2) always-on secret-path floor (independent of
850
- * `forbiddenPaths`), (3) forbidden-path, (4) diff-size cap, (5) tests, (6) typecheck.
851
- * Aggregate score: `0.5*tests + 0.3*typecheck + 0.2*(1 - diffLines/maxDiff)`; `valid` is the
852
- * conjunction of all six.
853
- */
854
- declare function runCoderChecks(input: CoderCheckInput, constraints?: CoderCheckConstraints): DefaultVerdict;
855
827
 
856
828
  /**
857
829
  * @experimental
@@ -1096,4 +1068,4 @@ interface WorktreeFanoutOptions extends PatchDeliverableOptions {
1096
1068
  */
1097
1069
  declare function worktreeFanout<Task>(options: WorktreeFanoutOptions): CombinatorShape<Task, WorktreePatchArtifact>;
1098
1070
 
1099
- export { type TrajectoryNode as $, type AuthoredHarness as A, type FlatWidenGate as B, type CorpusRecord as C, type DefinePersonaInput as D, type EqualKArm as E, type FanoutOptions as F, type LoopUntil as G, type LoopUntilState as H, type Panel as I, type PanelJudge as J, type PanelVerdict as K, type LoopUntilSpec as L, type PatchDeliverableOptions as M, type PersonaContext as N, type Outcome as O, type PanelSpec as P, type PersonaExecutors as Q, type RenderCorpusToInstructionsOptions as R, type ScopeAnalyzeInput as S, type TrajectoryReportOptions as T, type Pipeline as U, type VerifySpec as V, type WinnerStrategy as W, type RenderCorpusToInstructions as X, type RunPersonified as Y, type ShapeBudget as Z, type ShapeContext as _, type WorktreeFanoutOptions as a, type TrajectoryReportFn as a0, type Verify as a1, type Widen as a2, type WidenDecision as a3, type WidenLineage as a4, type WorktreeCliExecutorOptions as a5, type WorktreeCommandResult as a6, countDiffLines as a7, createWorktreeCliExecutor as a8, gateOnDeliverable as a9, isNonEmptyPatch as aa, patchDelivered as ab, runCoderChecks as ac, touchedPathsFromPatch as ad, touchesSecretPath as ae, worktreeFanout as af, type WorktreePatchArtifact as b, type Corpus as c, type AssertTraceDerivedFindings as d, type SteerContext as e, type ScopeAnalyst as f, type CombinatorShape as g, type ScopeWidenGate as h, type PipelineStage as i, type FanoutWinnerSelector as j, type WidenSpec as k, type CorpusFilter as l, type Persona as m, type RunPersonifiedOptions as n, type ShapeRegistry as o, type LoopShape as p, type EqualKOnCostOptions as q, type EqualKVerdict as r, type TrajectoryReport as s, type DeliverableSpec as t, type CoderCheckConstraints as u, type CoderCheckInput as v, type DefinePersona as w, type EqualKOnCost as x, type Fanout as y, type FanoutSynthesis as z };
1071
+ export { type Verify as $, type AuthoredHarness as A, type LoopUntilState as B, type CorpusRecord as C, type DefinePersonaInput as D, type EqualKArm as E, type FanoutOptions as F, type Panel as G, type PanelJudge as H, type PanelVerdict as I, type PatchDeliverableOptions as J, type PersonaContext as K, type LoopUntilSpec as L, type PersonaExecutors as M, type Pipeline as N, type Outcome as O, type PanelSpec as P, type RenderCorpusToInstructions as Q, type RenderCorpusToInstructionsOptions as R, type ScopeAnalyzeInput as S, type TrajectoryReportOptions as T, type RunPersonified as U, type VerifySpec as V, type WinnerStrategy as W, type ShapeBudget as X, type ShapeContext as Y, type TrajectoryNode as Z, type TrajectoryReportFn as _, type WorktreeFanoutOptions as a, type Widen as a0, type WidenDecision as a1, type WidenLineage as a2, type WorktreeCliExecutorOptions as a3, type WorktreeCommandResult as a4, createWorktreeCliExecutor as a5, gateOnDeliverable as a6, patchDelivered as a7, worktreeFanout as a8, type WorktreePatchArtifact as b, type Corpus as c, type AssertTraceDerivedFindings as d, type SteerContext as e, type ScopeAnalyst as f, type CombinatorShape as g, type ScopeWidenGate as h, type PipelineStage as i, type FanoutWinnerSelector as j, type WidenSpec as k, type CorpusFilter as l, type Persona as m, type RunPersonifiedOptions as n, type ShapeRegistry as o, type LoopShape as p, type EqualKOnCostOptions as q, type EqualKVerdict as r, type TrajectoryReport as s, type DeliverableSpec as t, type DefinePersona as u, type EqualKOnCost as v, type Fanout as w, type FanoutSynthesis as x, type FlatWidenGate as y, type LoopUntil as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-runtime",
3
- "version": "0.69.0",
3
+ "version": "0.70.0",
4
4
  "description": "Shared task-lifecycle skeleton for agents: a recursive loop kernel for chat turns, one-shot tasks, and multi-attempt loops, with trace capture and eval-gated self-improvement. Domain behavior lives in adapters; scoring and ship-gates in @tangle-network/agent-eval.",
5
5
  "homepage": "https://github.com/tangle-network/agent-runtime#readme",
6
6
  "repository": {
@@ -19,21 +19,6 @@
19
19
  "import": "./dist/index.js",
20
20
  "default": "./dist/index.js"
21
21
  },
22
- "./platform": {
23
- "types": "./dist/platform.d.ts",
24
- "import": "./dist/platform.js",
25
- "default": "./dist/platform.js"
26
- },
27
- "./analyst-loop": {
28
- "types": "./dist/analyst-loop.d.ts",
29
- "import": "./dist/analyst-loop.js",
30
- "default": "./dist/analyst-loop.js"
31
- },
32
- "./improvement": {
33
- "types": "./dist/improvement.d.ts",
34
- "import": "./dist/improvement.js",
35
- "default": "./dist/improvement.js"
36
- },
37
22
  "./agent": {
38
23
  "types": "./dist/agent.d.ts",
39
24
  "import": "./dist/agent.js",
@@ -44,36 +29,16 @@
44
29
  "import": "./dist/intelligence.js",
45
30
  "default": "./dist/intelligence.js"
46
31
  },
47
- "./runtime": {
48
- "types": "./dist/runtime.d.ts",
49
- "import": "./dist/runtime.js",
50
- "default": "./dist/runtime.js"
51
- },
52
32
  "./loops": {
53
33
  "types": "./dist/loops.d.ts",
54
34
  "import": "./dist/loops.js",
55
35
  "default": "./dist/loops.js"
56
36
  },
57
- "./topology": {
58
- "types": "./dist/topology.d.ts",
59
- "import": "./dist/topology.js",
60
- "default": "./dist/topology.js"
61
- },
62
- "./workflow": {
63
- "types": "./dist/workflow.d.ts",
64
- "import": "./dist/workflow.js",
65
- "default": "./dist/workflow.js"
66
- },
67
37
  "./profiles": {
68
38
  "types": "./dist/profiles.d.ts",
69
39
  "import": "./dist/profiles.js",
70
40
  "default": "./dist/profiles.js"
71
41
  },
72
- "./audit": {
73
- "types": "./dist/audit.d.ts",
74
- "import": "./dist/audit.js",
75
- "default": "./dist/audit.js"
76
- },
77
42
  "./mcp": {
78
43
  "types": "./dist/mcp/index.d.ts",
79
44
  "import": "./dist/mcp/index.js",
@@ -102,16 +67,21 @@
102
67
  "lint:fix": "biome check --write src tests examples",
103
68
  "typecheck": "tsc --noEmit && pnpm run typecheck:examples",
104
69
  "typecheck:examples": "tsc --noEmit -p tsconfig.examples.json",
105
- "verify:package": "node scripts/verify-package-exports.mjs"
70
+ "verify:package": "node scripts/verify-package-exports.mjs",
71
+ "docs:api": "typedoc",
72
+ "docs:freshness": "node scripts/check-docs-freshness.mjs",
73
+ "docs:check": "pnpm run docs:api && git diff --exit-code -- docs/api && pnpm run docs:freshness"
106
74
  },
107
75
  "devDependencies": {
108
76
  "@biomejs/biome": "^2.4.15",
109
- "@tangle-network/agent-eval": ">=0.93.0 <1.0.0",
77
+ "@tangle-network/agent-eval": ">=0.95.0 <1.0.0",
110
78
  "@tangle-network/agent-interface": ">=0.10.0 <1.0.0",
111
79
  "@tangle-network/sandbox": ">=0.8.0 <1.0.0",
112
80
  "@types/node": "^25.9.3",
113
81
  "playwright": "^1.61.0",
114
82
  "tsup": "^8.0.0",
83
+ "typedoc": "0.28.19",
84
+ "typedoc-plugin-markdown": "4.12.0",
115
85
  "typescript": "^5.7.0",
116
86
  "vitest": "^3.0.0"
117
87
  },
@@ -132,7 +102,7 @@
132
102
  "license": "MIT",
133
103
  "packageManager": "pnpm@10.28.0",
134
104
  "peerDependencies": {
135
- "@tangle-network/agent-eval": ">=0.93.0 <1.0.0",
105
+ "@tangle-network/agent-eval": ">=0.95.0 <1.0.0",
136
106
  "@tangle-network/agent-interface": ">=0.10.0 <1.0.0",
137
107
  "@tangle-network/agent-knowledge": ">=1.7.0 <2.0.0",
138
108
  "@tangle-network/sandbox": ">=0.8.0 <1.0.0",
@@ -1,52 +0,0 @@
1
- import { TraceAnalystByteBudgets, TraceAnalysisStore } from '@tangle-network/agent-eval';
2
- import { I as Iteration } from './types-Crxftafi.js';
3
- import { R as RunAnalystLoopOpts, a as RunAnalystLoopResult } from './types-p8dWBIXL.js';
4
- export { A as AnalystLoopEvent, b as AnalystRegistryLike, c as AnalystRegistryStreamingLike, d as AutoApplyPolicy, F as FindingsStoreLike, I as ImprovementAdapter, e as ImprovementEditBatch, f as ImprovementReport, K as KnowledgeAdapter, g as KnowledgeProposalBatch, h as KnowledgeReport } from './types-p8dWBIXL.js';
5
- import '@tangle-network/sandbox';
6
- import './runtime-hooks-C7JwKb9E.js';
7
-
8
- /**
9
- * @experimental
10
- *
11
- * The read seam that closes the autonomous loop: project a round's `Iteration[]`
12
- * (each carrying its raw `SandboxEvent[]`) into an in-memory `TraceAnalysisStore`, the
13
- * read interface the trace analysts query. `runAnalystLoop` has had zero consumers
14
- * because nothing turned a loop's iterations into a store — this is that bridge.
15
- *
16
- * One iteration → one trace. The iteration is the root AGENT span; each `SandboxEvent`
17
- * becomes a child span (LLM for llm_call events, TOOL for tool events, else SPAN), so an
18
- * analyst can walk a shot's trace, cluster its errors, and emit findings the driver
19
- * steers on. Projection is best-effort over the FLAT SandboxEvent shape (no per-event
20
- * lineage yet — that's the richer-trace gap); it never fabricates — an errored iteration
21
- * surfaces a real ERROR span carrying the real message.
22
- */
23
-
24
- /**
25
- * Build an in-memory `TraceAnalysisStore` over a loop round's iterations. Fail-loud on an
26
- * empty round — there is nothing for an analyst to read, and a silent empty store would
27
- * mask a broken capture path.
28
- */
29
- declare function iterationsToTraceStore<Task, Output>(iterations: ReadonlyArray<Iteration<Task, Output>>, budgets?: TraceAnalystByteBudgets): TraceAnalysisStore;
30
-
31
- /**
32
- * `runAnalystLoop` — the one call agent apps reach for to close the
33
- * recursive-self-improvement loop.
34
- *
35
- * 1. Load baseline findings (last run, or the slice the caller specifies)
36
- * 2. Run the analyst registry with priorFindings injected
37
- * 3. Persist the new run's findings to the ledger
38
- * 4. Diff the new run against the baseline
39
- * 5. Hand the findings to the knowledge adapter → proposals (and
40
- * optionally apply them) → wiki edits
41
- * 6. Hand the findings to the improvement adapter → prompt / tool /
42
- * scaffolding edits (review-only by default)
43
- * 7. Return a single report the consumer renders / persists / acts on.
44
- *
45
- * Adapters are optional: the loop works as a "run + diff + report"
46
- * primitive when no adapters are wired; it closes end-to-end when
47
- * both adapters are wired.
48
- */
49
-
50
- declare function runAnalystLoop<TProposal = unknown, TEdit = unknown>(opts: RunAnalystLoopOpts): Promise<RunAnalystLoopResult<TProposal, TEdit>>;
51
-
52
- export { RunAnalystLoopOpts, RunAnalystLoopResult, iterationsToTraceStore, runAnalystLoop };
@@ -1,11 +0,0 @@
1
- import {
2
- iterationsToTraceStore,
3
- runAnalystLoop
4
- } from "./chunk-P5OKDSLB.js";
5
- import "./chunk-VLF5RHEQ.js";
6
- import "./chunk-DGUM43GV.js";
7
- export {
8
- iterationsToTraceStore,
9
- runAnalystLoop
10
- };
11
- //# sourceMappingURL=analyst-loop.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/audit.d.ts DELETED
@@ -1,93 +0,0 @@
1
- import { U as UiFinding, a as UiLens } from './substrate-CUgk7F7s.js';
2
-
3
- /**
4
- * @experimental
5
- *
6
- * UI-audit issue writer — pure I/O. Takes a workspace dir + `UiFinding[]`
7
- * and emits:
8
- * - `<workspace>/issues/NNN--<lens>--<slug>.md` — one self-contained
9
- * GitHub-issue-ready Markdown per finding, with embedded screenshot
10
- * references.
11
- * - `<workspace>/registry.json` — finding index for dedup and audit
12
- * resume across iterations.
13
- * - `<workspace>/index.md` — human-readable rollup (severity / lens /
14
- * route counts plus a sorted finding list).
15
- *
16
- * The writer is deterministic, idempotent for `appendFindings()`, and
17
- * never invokes an LLM. It assigns the next monotonic id to a finding the
18
- * caller did not pre-id.
19
- */
20
-
21
- /** @experimental */
22
- interface AuditRegistry {
23
- schemaVersion: 1;
24
- findings: UiFinding[];
25
- /** Route → URL + captures sidecar; preserved across runs. */
26
- routes: Record<string, {
27
- url?: string;
28
- captures: AuditRegistryCapture[];
29
- }>;
30
- }
31
- /** @experimental */
32
- interface AuditRegistryCapture {
33
- file: string;
34
- viewport?: string;
35
- fullPage?: boolean;
36
- elementSelector?: string;
37
- capturedAt: string;
38
- }
39
- /** @experimental */
40
- declare function initAuditWorkspace(workspaceDir: string): Promise<void>;
41
- /** @experimental */
42
- declare function readAuditRegistry(workspaceDir: string): Promise<AuditRegistry>;
43
- /** @experimental */
44
- interface AppendFindingsResult {
45
- /** Findings with id + createdAt assigned, in input order. */
46
- written: UiFinding[];
47
- /** Workspace-relative path to each issue Markdown file, in input order. */
48
- files: string[];
49
- }
50
- /**
51
- * Append findings to a workspace, writing one Markdown file per finding
52
- * and updating registry.json. Assigns monotonically increasing ids to
53
- * findings that arrived without one.
54
- *
55
- * Findings already carrying an id that collides with the registry are
56
- * rejected — callers must either freshly mint findings (id undefined) or
57
- * use a separate update path. This protects against accidental overwrite.
58
- *
59
- * @experimental
60
- */
61
- declare function appendFindings(workspaceDir: string, findings: readonly UiFinding[]): Promise<AppendFindingsResult>;
62
- /** @experimental */
63
- interface RegisterCapturesOptions {
64
- route: string;
65
- url?: string;
66
- captures: readonly AuditRegistryCapture[];
67
- }
68
- /**
69
- * Record screenshots taken for a route in the registry, without filing a
70
- * finding. Useful when the auditor wants to remember which captures
71
- * exist for resume / dedup purposes.
72
- *
73
- * @experimental
74
- */
75
- declare function registerCaptures(workspaceDir: string, options: RegisterCapturesOptions): Promise<void>;
76
- /** @experimental */
77
- interface AuditIndex {
78
- /** Total findings in the workspace. */
79
- total: number;
80
- bySeverity: Record<UiFinding['severity'], number>;
81
- byLens: Partial<Record<UiLens, number>>;
82
- byRoute: Record<string, number>;
83
- }
84
- /** @experimental */
85
- declare function summarizeRegistry(reg: AuditRegistry): AuditIndex;
86
- /**
87
- * Regenerate `<workspace>/index.md` from registry.json.
88
- *
89
- * @experimental
90
- */
91
- declare function writeAuditIndex(workspaceDir: string): Promise<string>;
92
-
93
- export { type AppendFindingsResult, type AuditIndex, type AuditRegistry, type AuditRegistryCapture, type RegisterCapturesOptions, appendFindings, initAuditWorkspace, readAuditRegistry, registerCaptures, summarizeRegistry, writeAuditIndex };
package/dist/audit.js DELETED
@@ -1,312 +0,0 @@
1
- import {
2
- slugify
3
- } from "./chunk-4B6U4CVQ.js";
4
- import {
5
- UI_FINDING_SEVERITIES,
6
- UI_LENSES
7
- } from "./chunk-WIR4HOOJ.js";
8
- import "./chunk-DGUM43GV.js";
9
-
10
- // src/audit/issue-writer.ts
11
- import { promises as fs } from "fs";
12
- import path from "path";
13
- var SEVERITY_ORDER = {
14
- critical: 0,
15
- high: 1,
16
- med: 2,
17
- low: 3
18
- };
19
- function assertValidWorkspaceDir(dir) {
20
- if (typeof dir !== "string" || dir.length === 0) {
21
- throw new Error(
22
- `audit-writer: workspaceDir must be a non-empty string (got ${JSON.stringify(dir)})`
23
- );
24
- }
25
- if (!path.isAbsolute(dir)) {
26
- throw new Error(
27
- `audit-writer: workspaceDir must be an absolute path (got ${JSON.stringify(dir)})`
28
- );
29
- }
30
- if (dir.split(path.sep).includes("..")) {
31
- throw new Error(
32
- `audit-writer: workspaceDir must not contain '..' segments (got ${JSON.stringify(dir)})`
33
- );
34
- }
35
- }
36
- async function initAuditWorkspace(workspaceDir) {
37
- assertValidWorkspaceDir(workspaceDir);
38
- await fs.mkdir(path.join(workspaceDir, "issues"), { recursive: true });
39
- await fs.mkdir(path.join(workspaceDir, "screenshots"), { recursive: true });
40
- const regPath = path.join(workspaceDir, "registry.json");
41
- try {
42
- await fs.access(regPath);
43
- } catch {
44
- const fresh = { schemaVersion: 1, findings: [], routes: {} };
45
- await fs.writeFile(regPath, JSON.stringify(fresh, null, 2));
46
- }
47
- }
48
- async function readAuditRegistry(workspaceDir) {
49
- assertValidWorkspaceDir(workspaceDir);
50
- const regPath = path.join(workspaceDir, "registry.json");
51
- const text = await fs.readFile(regPath, "utf8");
52
- const parsed = JSON.parse(text);
53
- if (!parsed || typeof parsed !== "object") {
54
- throw new Error(`audit-writer: registry.json at ${regPath} is not a JSON object`);
55
- }
56
- const reg = parsed;
57
- if (reg.schemaVersion !== 1 || !Array.isArray(reg.findings) || typeof reg.routes !== "object") {
58
- throw new Error(`audit-writer: registry.json at ${regPath} has an unrecognized shape`);
59
- }
60
- return reg;
61
- }
62
- async function writeAuditRegistry(workspaceDir, reg) {
63
- const regPath = path.join(workspaceDir, "registry.json");
64
- const tmpPath = `${regPath}.tmp-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
65
- await fs.writeFile(tmpPath, JSON.stringify(reg, null, 2));
66
- await fs.rename(tmpPath, regPath);
67
- }
68
- var workspaceLocks = /* @__PURE__ */ new Map();
69
- async function withWorkspaceLock(workspaceDir, fn) {
70
- const key = path.resolve(workspaceDir);
71
- const prev = workspaceLocks.get(key) ?? Promise.resolve();
72
- let signalDone;
73
- const done = new Promise((resolve) => {
74
- signalDone = resolve;
75
- });
76
- workspaceLocks.set(key, done);
77
- try {
78
- await prev;
79
- return await fn();
80
- } finally {
81
- signalDone();
82
- if (workspaceLocks.get(key) === done) workspaceLocks.delete(key);
83
- }
84
- }
85
- function assertFindingShape(f, index) {
86
- const where = `audit-writer: findings[${index}]`;
87
- if (!UI_LENSES.includes(f.lens)) {
88
- throw new Error(
89
- `${where}: invalid lens ${JSON.stringify(f.lens)}; one of ${UI_LENSES.join("|")}`
90
- );
91
- }
92
- if (!UI_FINDING_SEVERITIES.includes(f.severity)) {
93
- throw new Error(
94
- `${where}: invalid severity ${JSON.stringify(f.severity)}; one of ${UI_FINDING_SEVERITIES.join("|")}`
95
- );
96
- }
97
- for (const field of ["title", "route", "observation", "impact", "suggestedFix"]) {
98
- const v = f[field];
99
- if (typeof v !== "string" || v.trim().length === 0) {
100
- throw new Error(`${where}: ${field} must be a non-empty string`);
101
- }
102
- }
103
- if (!Array.isArray(f.screenshots) || f.screenshots.length === 0) {
104
- throw new Error(`${where}: screenshots must be a non-empty array`);
105
- }
106
- for (let i = 0; i < f.screenshots.length; i += 1) {
107
- const s = f.screenshots[i];
108
- if (!s || typeof s.path !== "string" || s.path.length === 0) {
109
- throw new Error(`${where}.screenshots[${i}].path must be a non-empty string`);
110
- }
111
- if (s.path.split(/[/\\]/).includes("..")) {
112
- throw new Error(`${where}.screenshots[${i}].path must not contain '..' segments`);
113
- }
114
- }
115
- }
116
- function nextFindingId(reg) {
117
- let max = 0;
118
- for (const f of reg.findings) {
119
- if (typeof f.id === "number" && f.id > max) max = f.id;
120
- }
121
- return max + 1;
122
- }
123
- function slugifyTitle(title) {
124
- return slugify(title, "title");
125
- }
126
- function renderFinding(finding) {
127
- if (!finding.id) {
128
- throw new Error("audit-writer: cannot render a finding without an assigned id");
129
- }
130
- if (finding.screenshots.length === 0) {
131
- throw new Error(`audit-writer: finding #${finding.id} has no screenshots`);
132
- }
133
- const id = finding.id;
134
- const numStr = String(id).padStart(3, "0");
135
- const metaLines = [
136
- `> **Issue #${numStr}** \xB7 **Severity:** ${finding.severity} \xB7 **Lens:** ${finding.lens}`,
137
- `> **Route:** \`${finding.route}\`${finding.url ? ` \xB7 **URL:** ${finding.url}` : ""}`
138
- ];
139
- if (finding.viewport) metaLines.push(`> **Viewport:** ${finding.viewport}`);
140
- if (finding.selector) metaLines.push(`> **Selector:** \`${finding.selector}\``);
141
- if (finding.tags && finding.tags.length > 0) {
142
- metaLines.push(`> **Tags:** ${finding.tags.map((t) => `\`${t}\``).join(", ")}`);
143
- }
144
- if (finding.similarTo && finding.similarTo.length > 0) {
145
- metaLines.push(
146
- `> **Possible duplicates:** ${finding.similarTo.map((n) => `#${String(n).padStart(3, "0")}`).join(", ")}`
147
- );
148
- }
149
- metaLines.push(`> _Generated: ${finding.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()}_`);
150
- const reproSteps = finding.reproSteps ?? `1. Open \`${finding.route}\`${finding.url ? ` (${finding.url})` : ""}
151
- 2. Observe the area shown in the screenshot${finding.selector ? ` (selector: \`${finding.selector}\`)` : ""}.`;
152
- const evidence = finding.screenshots.map((s) => `![${path.basename(s.path)}](../${s.path})`).join("\n\n");
153
- return [
154
- `# [UI] ${finding.title}`,
155
- "",
156
- metaLines.join("\n"),
157
- "",
158
- "## Observation",
159
- finding.observation.trim(),
160
- "",
161
- "## Why it matters",
162
- finding.impact.trim(),
163
- "",
164
- "## Steps to reproduce",
165
- reproSteps,
166
- "",
167
- "## Suggested fix",
168
- finding.suggestedFix.trim(),
169
- "",
170
- "## Evidence",
171
- evidence,
172
- "",
173
- "---",
174
- `<sub>Generated by agent-runtime/ui-auditor \xB7 lens=\`${finding.lens}\` \xB7 severity=\`${finding.severity}\`</sub>`,
175
- ""
176
- ].join("\n");
177
- }
178
- async function appendFindings(workspaceDir, findings) {
179
- assertValidWorkspaceDir(workspaceDir);
180
- for (let i = 0; i < findings.length; i += 1) {
181
- const f = findings[i];
182
- if (!f) throw new Error(`audit-writer: findings[${i}] is undefined`);
183
- assertFindingShape(f, i);
184
- }
185
- return withWorkspaceLock(workspaceDir, async () => {
186
- await initAuditWorkspace(workspaceDir);
187
- const reg = await readAuditRegistry(workspaceDir);
188
- const usedIds = /* @__PURE__ */ new Set();
189
- for (const f of reg.findings) {
190
- if (typeof f.id === "number") usedIds.add(f.id);
191
- }
192
- const written = [];
193
- const files = [];
194
- let nextId = nextFindingId(reg);
195
- for (const incoming of findings) {
196
- let id = incoming.id;
197
- if (id !== void 0) {
198
- if (usedIds.has(id)) {
199
- throw new Error(
200
- `audit-writer: incoming finding id ${id} (title=${JSON.stringify(incoming.title)}) collides with an existing registry entry`
201
- );
202
- }
203
- } else {
204
- id = nextId;
205
- while (usedIds.has(id)) id += 1;
206
- nextId = id + 1;
207
- }
208
- usedIds.add(id);
209
- const createdAt = incoming.createdAt ?? (/* @__PURE__ */ new Date()).toISOString();
210
- const persisted = { ...incoming, id, createdAt };
211
- const slug = slugifyTitle(persisted.title);
212
- const fileName = `${String(id).padStart(3, "0")}--${persisted.lens}--${slug}.md`;
213
- const filePathAbs = path.join(workspaceDir, "issues", fileName);
214
- const filePathRel = `issues/${fileName}`;
215
- await fs.writeFile(filePathAbs, renderFinding(persisted));
216
- reg.findings.push(persisted);
217
- written.push(persisted);
218
- files.push(filePathRel);
219
- }
220
- await writeAuditRegistry(workspaceDir, reg);
221
- return { written, files };
222
- });
223
- }
224
- async function registerCaptures(workspaceDir, options) {
225
- assertValidWorkspaceDir(workspaceDir);
226
- if (typeof options.route !== "string" || options.route.trim().length === 0) {
227
- throw new Error("audit-writer: registerCaptures: route must be a non-empty string");
228
- }
229
- return withWorkspaceLock(workspaceDir, async () => {
230
- await initAuditWorkspace(workspaceDir);
231
- const reg = await readAuditRegistry(workspaceDir);
232
- const slot = reg.routes[options.route] ?? { captures: [] };
233
- if (options.url) slot.url = options.url;
234
- slot.captures = [...slot.captures, ...options.captures];
235
- reg.routes[options.route] = slot;
236
- await writeAuditRegistry(workspaceDir, reg);
237
- });
238
- }
239
- function summarizeRegistry(reg) {
240
- const bySeverity = {
241
- critical: 0,
242
- high: 0,
243
- med: 0,
244
- low: 0
245
- };
246
- const byLens = {};
247
- const byRoute = {};
248
- for (const f of reg.findings) {
249
- bySeverity[f.severity] += 1;
250
- byLens[f.lens] = (byLens[f.lens] ?? 0) + 1;
251
- byRoute[f.route] = (byRoute[f.route] ?? 0) + 1;
252
- }
253
- return { total: reg.findings.length, bySeverity, byLens, byRoute };
254
- }
255
- async function writeAuditIndex(workspaceDir) {
256
- assertValidWorkspaceDir(workspaceDir);
257
- const reg = await readAuditRegistry(workspaceDir);
258
- const summary = summarizeRegistry(reg);
259
- const sorted = [...reg.findings].sort((a, b) => {
260
- const sa = SEVERITY_ORDER[a.severity];
261
- const sb = SEVERITY_ORDER[b.severity];
262
- if (sa !== sb) return sa - sb;
263
- return (a.id ?? 0) - (b.id ?? 0);
264
- });
265
- const lines = [];
266
- lines.push("# UI audit index", "");
267
- lines.push(`_Generated: ${(/* @__PURE__ */ new Date()).toISOString()}_`, "");
268
- lines.push(`**Total findings:** ${summary.total}`, "");
269
- lines.push("## By severity");
270
- for (const s of ["critical", "high", "med", "low"]) {
271
- const n = summary.bySeverity[s] ?? 0;
272
- if (n > 0) lines.push(`- **${s}** \u2014 ${n}`);
273
- }
274
- lines.push("", "## By lens");
275
- const lensEntries = Object.entries(summary.byLens).map(([k, v]) => [
276
- k,
277
- v ?? 0
278
- ]);
279
- for (const [lens, n] of lensEntries.sort((a, b) => b[1] - a[1])) {
280
- lines.push(`- **${lens}** \u2014 ${n}`);
281
- }
282
- lines.push("", "## By route");
283
- const routeEntries = Object.entries(summary.byRoute).map(([k, v]) => [
284
- k,
285
- v ?? 0
286
- ]);
287
- for (const [route, n] of routeEntries.sort((a, b) => b[1] - a[1])) {
288
- lines.push(`- \`${route}\` \u2014 ${n}`);
289
- }
290
- lines.push("", "## Findings", "");
291
- for (const f of sorted) {
292
- if (typeof f.id !== "number") continue;
293
- const num = String(f.id).padStart(3, "0");
294
- const slug = slugifyTitle(f.title);
295
- const file = `issues/${num}--${f.lens}--${slug}.md`;
296
- lines.push(`- [#${num} \`${f.severity}\` \`${f.lens}\` \`${f.route}\` \u2014 ${f.title}](${file})`);
297
- }
298
- lines.push("");
299
- const out = path.join(workspaceDir, "index.md");
300
- const body = lines.join("\n");
301
- await fs.writeFile(out, body);
302
- return out;
303
- }
304
- export {
305
- appendFindings,
306
- initAuditWorkspace,
307
- readAuditRegistry,
308
- registerCaptures,
309
- summarizeRegistry,
310
- writeAuditIndex
311
- };
312
- //# sourceMappingURL=audit.js.map