@sapiom/harness 0.8.9 → 0.10.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 (85) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/README.md +11 -1
  3. package/dist/core/agent-project-discovery.d.ts +235 -1
  4. package/dist/core/agent-project-discovery.d.ts.map +1 -1
  5. package/dist/core/agent-project-discovery.js +303 -1
  6. package/dist/core/agent-project-discovery.js.map +1 -1
  7. package/dist/core/canvas-interconnections.d.ts +31 -0
  8. package/dist/core/canvas-interconnections.d.ts.map +1 -1
  9. package/dist/core/canvas-interconnections.js +294 -21
  10. package/dist/core/canvas-interconnections.js.map +1 -1
  11. package/dist/core/canvas-render.d.ts +32 -0
  12. package/dist/core/canvas-render.d.ts.map +1 -1
  13. package/dist/core/canvas-render.js +50 -41
  14. package/dist/core/canvas-render.js.map +1 -1
  15. package/dist/core/definition-name.d.ts +12 -0
  16. package/dist/core/definition-name.d.ts.map +1 -1
  17. package/dist/core/definition-name.js +14 -5
  18. package/dist/core/definition-name.js.map +1 -1
  19. package/dist/core/system-graph-inventory.d.ts +68 -0
  20. package/dist/core/system-graph-inventory.d.ts.map +1 -0
  21. package/dist/core/system-graph-inventory.js +312 -0
  22. package/dist/core/system-graph-inventory.js.map +1 -0
  23. package/dist/core/system-graph-relationships.d.ts +41 -0
  24. package/dist/core/system-graph-relationships.d.ts.map +1 -0
  25. package/dist/core/system-graph-relationships.js +98 -0
  26. package/dist/core/system-graph-relationships.js.map +1 -0
  27. package/dist/core/system-graph-store.d.ts +45 -0
  28. package/dist/core/system-graph-store.d.ts.map +1 -0
  29. package/dist/core/system-graph-store.js +222 -0
  30. package/dist/core/system-graph-store.js.map +1 -0
  31. package/dist/core/system-graph-watcher.d.ts +51 -0
  32. package/dist/core/system-graph-watcher.d.ts.map +1 -0
  33. package/dist/core/system-graph-watcher.js +448 -0
  34. package/dist/core/system-graph-watcher.js.map +1 -0
  35. package/dist/core/system-graph.d.ts +43 -0
  36. package/dist/core/system-graph.d.ts.map +1 -0
  37. package/dist/core/system-graph.js +321 -0
  38. package/dist/core/system-graph.js.map +1 -0
  39. package/dist/core/workflow-registry.d.ts +84 -6
  40. package/dist/core/workflow-registry.d.ts.map +1 -1
  41. package/dist/core/workflow-registry.js +191 -91
  42. package/dist/core/workflow-registry.js.map +1 -1
  43. package/dist/core/workspace-watcher.d.ts +21 -13
  44. package/dist/core/workspace-watcher.d.ts.map +1 -1
  45. package/dist/core/workspace-watcher.js +79 -81
  46. package/dist/core/workspace-watcher.js.map +1 -1
  47. package/dist/index.d.ts +1 -0
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js.map +1 -1
  50. package/dist/server/agent-move.d.ts +134 -0
  51. package/dist/server/agent-move.d.ts.map +1 -0
  52. package/dist/server/agent-move.js +343 -0
  53. package/dist/server/agent-move.js.map +1 -0
  54. package/dist/server/index.d.ts.map +1 -1
  55. package/dist/server/index.js +275 -22
  56. package/dist/server/index.js.map +1 -1
  57. package/dist/server/rest.d.ts +3 -0
  58. package/dist/server/rest.d.ts.map +1 -1
  59. package/dist/server/rest.js +3 -0
  60. package/dist/server/rest.js.map +1 -1
  61. package/dist/server/studio-rail.d.ts +76 -0
  62. package/dist/server/studio-rail.d.ts.map +1 -0
  63. package/dist/server/studio-rail.js +234 -0
  64. package/dist/server/studio-rail.js.map +1 -0
  65. package/dist/server/system-graph.d.ts +14 -0
  66. package/dist/server/system-graph.d.ts.map +1 -0
  67. package/dist/server/system-graph.js +40 -0
  68. package/dist/server/system-graph.js.map +1 -0
  69. package/dist/server/workflow-graph.d.ts +43 -0
  70. package/dist/server/workflow-graph.d.ts.map +1 -0
  71. package/dist/server/workflow-graph.js +200 -0
  72. package/dist/server/workflow-graph.js.map +1 -0
  73. package/dist/shared/system-graph.d.ts +59 -0
  74. package/dist/shared/system-graph.d.ts.map +1 -0
  75. package/dist/shared/system-graph.js +76 -0
  76. package/dist/shared/system-graph.js.map +1 -0
  77. package/dist/shared/types.d.ts +45 -0
  78. package/dist/shared/types.d.ts.map +1 -1
  79. package/dist/shared/types.js.map +1 -1
  80. package/dist/web/assets/index-BLrCZ-6M.css +32 -0
  81. package/dist/web/assets/index-CzCPIbq9.js +513 -0
  82. package/dist/web/index.html +2 -2
  83. package/package.json +3 -3
  84. package/dist/web/assets/index-B18B2y5o.js +0 -494
  85. package/dist/web/assets/index-jGBbXx9R.css +0 -32
package/CHANGELOG.md CHANGED
@@ -1,5 +1,104 @@
1
1
  # @sapiom/harness
2
2
 
3
+ ## 0.10.0
4
+
5
+ ### Minor Changes
6
+
7
+ - f26d2cd: Studio rail: an agent is never a project, and a row that is both opens the agent.
8
+
9
+ **Rows you had may disappear on upgrade, and no files are touched.** The rail now
10
+ derives its project list from one rule: a project is a directory you chose that
11
+ holds agents. Two kinds of row stop being drawn:
12
+
13
+ - A remembered folder that is an agent's **own** directory. It is dropped when
14
+ another project already shows that agent, and otherwise replaced by the folder
15
+ that holds it. These were residue of a fixed bug (a session used to root at the
16
+ agent's folder, and that folder got remembered), and they rendered the same
17
+ agent twice: once nested under its project and once again at top level. On one
18
+ real install this cut 42 project rows to 3 with all 89 agents still visible.
19
+ - A folder known only because a session ran there and holding no agent, unless a
20
+ session is live in it.
21
+
22
+ Nothing is deleted: `recentDirs` is untouched and any folder is one **Add a
23
+ project** away from coming back. Opening an agent's own folder now opens the
24
+ folder that holds it, rather than doing nothing.
25
+
26
+ Also: a project row whose root is an agent now opens that **agent** on click
27
+ instead of a dependency graph with one node in it; the graph moves to its own
28
+ control on the row. Every project row gains a hover **+** that creates an agent in
29
+ that folder. The rail header reads "Projects" on both axes, the primary call to
30
+ action reads "Create new agent", and two rail rows that painted a solid brand
31
+ green (the "New group" row on hover, and the armed "Reset to detected groups"
32
+ row) are a neutral hover and a red wash.
33
+
34
+ ### Patch Changes
35
+
36
+ - Updated dependencies [03da8e2]
37
+ - @sapiom/mcp@0.13.1
38
+
39
+ ## 0.9.0
40
+
41
+ ### Minor Changes
42
+
43
+ - 37ab85b: Agents nested deep under a project root are discovered again: the scan now reaches 8 levels, bounded by a node budget rather than by depth alone.
44
+
45
+ `AGENT_PROJECT_SCAN_MAX_DEPTH` was 3, which predated the project-rooted rail — it assumed the directory you opened was roughly the agent's own folder. Under a root you _choose_, depth is ordinary: `<root>/backend/src/agents/<agent>` is four segments down and `<root>/apps/<app>/src/features/<x>/agents/<agent>` is six. Those agents were not found at all and landed in "No workspace" — on a measured root, well over a third of the rail.
46
+
47
+ Raising the depth alone would not have been affordable, and the numbers are why. Measured against real roots (macOS/APFS, warm cache, ~22-25 µs per directory entered):
48
+
49
+ | root | depth 3 | depth 8 | unbounded |
50
+ | ------------------------------- | ---------- | -------------------- | ----------- |
51
+ | a single repo | 119 dirs | 242 dirs · 7 ms | 242 dirs |
52
+ | a monorepo | 758 dirs | 9,016 dirs · 196 ms | 9,195 dirs |
53
+ | a monorepo with worktree copies | 1,298 dirs | 35,489 dirs · 847 ms | 47,544 dirs |
54
+
55
+ So cost is linear and predictable in _directories entered_, and that is now what is bounded: `AGENT_PROJECT_SCAN_MAX_NODES` (10,000) for the registry scan, and a tighter `AGENT_PROJECT_WATCH_MAX_NODES` (2,500) for the workspace watcher's fingerprint, which is synchronous and re-runs on a debounce after every save. Pruning harder was the other candidate and does not pay — extending the ignored-directory list with the usual suspects removed 0.5–11% of the directories on those roots.
56
+
57
+ The registry and the watcher now share one traversal in `core/agent-project-discovery.ts`, and it is **breadth-first**, which is what makes the budget safe: every level shallower than the cut is complete, so a truncated scan degrades by depth exactly as the fixed cap did — just at a depth the tree's real width chooses instead of one guessed in advance. A scan reports how far it got (`AgentProjectScanBudget.envelopeDepth`) and the registry reconciles only within that, so a bounded scan never mistakes "I did not look there" for "it is gone".
58
+
59
+ Termination on a pathological tree does not depend on the cap: subdirectories are filtered on raw dirent type, so a symlink — including one closing a cycle — is never descended into. That is now asserted directly, and `src/core/agent-project-scan.perf.test.ts` measures the cost of both bounds on a deep monorepo fixture next to the depth-3 baseline.
60
+
61
+ - b66ff0e: Show every registry-known agent contained by an Agent Studio Project in that Project's graph, including local-only and disconnected agents, while preserving partial inventory with path-free warnings.
62
+ - b66ff0e: Detect literal direct agent `run` and `launch` relationships in Agent Studio Project graphs, distinguish blocking and asynchronous modes, and report dynamic targets without drawing misleading connectors.
63
+
64
+ The syntax-only detector recognizes the exact `ctx.sapiom.agents` form plus proven named `agents` aliases and legacy `orchestrations.launch` imports from `@sapiom/tools`. Unlike the previous text match, unrelated local objects, custom context names, destructured namespaces, namespace imports, and optional chains are not inferred. The existing per-agent Canvas remains launch-only, so blocking `agents.run` calls retain their existing capability chip there until that Canvas supports blocking relationship nodes.
65
+
66
+ TypeScript is now a Harness runtime dependency, constrained to the tested 5.9 compiler-API band, because published Harness and desktop servers execute the syntax parser locally.
67
+
68
+ - 37ab85b: Agent Studio's left rail is rebuilt around where an agent **is** and what it is **related to**, and it can now move an agent's directory on disk.
69
+
70
+ **Two axes, chosen from the rail's own `Group by` control.** _Project_ files every agent under the project roots that contain it — the folder you opened, a folder you have a session in — with the branching directories between them as rows. An agent inside two roots you both opened appears under both; they are two contexts, and the old longest-prefix rule made the shallower project silently lose agents it plainly contained. _Group_ is the arrangement you make yourself: named groups per project root, seeded from the launch edges between agents and then yours to edit. The **Deployment axis is retired** — an agent's deploy state is a badge on its row, not a place it lives.
71
+
72
+ **`Remove project`** takes a root off the rail without touching anything on disk.
73
+
74
+ **`POST /api/agents/move` renames a directory in your working tree.** This is the Project axis's drag, and it is a real filesystem move, not a display preference: dropping an agent on a folder relocates that agent's directory there — `git mv` when the directory is tracked in a git repo, a plain rename otherwise. Nothing inside `sapiom.json` is rewritten, live sessions whose cwd sat inside the moved tree follow it, and the endpoint refuses on its own findings (a destination that exists, a destination inside the source, a `from` that is not a registered agent, or a destination outside the folders the rail shows) rather than trusting the caller.
75
+
76
+ **New local REST surfaces** on the same `127.0.0.1` boot-token-gated `/api` mount as the rest:
77
+
78
+ - `GET`/`PUT`/`DELETE /api/studio-rail` — the stored Group-axis arrangement, one `.sapiom/studio-rail.json` per project root. It is a committable file, so a team can share an arrangement. Writable roots are exactly the roots the rail can show.
79
+ - `GET /api/studio-rail/launch-edges` — which agents launch which, across every registered agent, used to seed groups.
80
+ - `POST /api/agents/move` — above.
81
+
82
+ **Stored UI state resets once on upgrade, deliberately.** The rail's preferences moved to keys that can name a project, a directory or a group rather than only a cwd: `collapsedCwds` → `collapsedKeys` (namespaced `project:` / `dir:` / `group:`) and `railGrouping` → `railAxis`. The old values are not migrated — they describe a rail that no longer exists — so after upgrading, every fold is open and the axis is back to _Project_. Set them again once and they stick.
83
+
84
+ There is no migration for project roots either: every directory already in your recents, and every live session's cwd, becomes a project row. Nothing is discarded, and `Remove project` is how the list gets shorter.
85
+
86
+ - 37ab85b: A canvas board can now be read by agent path, with no session involved: `GET /api/workflows/:path/graph`.
87
+
88
+ The canvas was reachable only at `/canvas/:harnessSessionId/`, resolved by the session's current binding — so an agent that had never hosted a session had no board, and you could not look at agent F's board while working in agent B's session.
89
+
90
+ This adds a second, session-free entry point onto the _same_ derivation. `deriveWorkflowCanvas` is extracted out of the render-file write path and shared by both, so the document this route returns is byte-identical to the render a bound session's canvas serves for the same workflow. Nothing is written to disk.
91
+
92
+ `:path` is the agent's absolute directory, URI-encoded into one segment (`encodeURIComponent(agentPath)`), matching `/api/workflows/:id/input-contract`. It sits behind the usual `/api` boot-token middleware, so it is a `fetch` target rather than an `<iframe src>`.
93
+
94
+ Failure modes are deliberately distinct: `400` for a blank, relative or `..`-carrying path (and for a `sapiom.json` symlinked out of the project), `404` only for a path that is not a registered workflow, and `200` with `status: "empty" | "preparing" | "error" | "ok"` for everything else — a missing `sapiom.json` is an empty board, never a missing route.
95
+
96
+ Full contract: `packages/harness/docs/agent-canvas-graph.md`.
97
+
98
+ - b66ff0e: Keep Agent Studio Project dependency graphs current with revisioned lifecycle snapshots, per-agent relationship caching, and last-good stale or degraded presentation during refresh failures. Opening a Project graph arms one additional session-independent recursive watcher for that Project; inventory fingerprints and the polling fallback run asynchronously so wide Projects do not block the Studio server loop.
99
+ - b66ff0e: Render Project dependency graphs as full-main Agent Studio destinations with deterministic non-linear layout, routed invocation modes, stable per-Project pan and zoom controls, and exact agent-card navigation from both rail axes.
100
+ - b66ff0e: Add a cached local Project dependency graph to Agent Studio. Project labels on both the Project and Group axes open the graph; use the dedicated chevron to expand or collapse the Project's agents.
101
+
3
102
  ## 0.8.9
4
103
 
5
104
  ### Patch Changes
package/README.md CHANGED
@@ -21,7 +21,9 @@ system prompt, in whatever project directory you choose.
21
21
  Agent Studio only configures it. Multiple sessions, resumable chat history.
22
22
  - **Agents rail** — agent projects (`sapiom.json`) discovered and
23
23
  tracked, with one-click local test run, deploy, production run, and
24
- open-in-Sapiom actions.
24
+ open-in-Sapiom actions. How that discovery is rooted and bounded, how a
25
+ newly-created agent gets registered, and how a stale entry leaves:
26
+ [docs/agent-discovery.md](docs/agent-discovery.md).
25
27
  - **Canvas** — a live pane that renders static HTML your agent writes to
26
28
  `.sapiom/canvas/` (visualize your agent, your docs, anything), plus a
27
29
  preview mode for dev servers the agent starts.
@@ -49,6 +51,14 @@ Architecture: a single Node process (Express + ws + node-pty) serves the built
49
51
  SPA, a small REST API, terminal WebSocket streams, and the local telemetry
50
52
  ingest endpoint. The interface contract lives in `src/shared/types.ts`.
51
53
 
54
+ HTTP contracts that need more than a type to use are written up under `docs/`:
55
+
56
+ - [`docs/agent-canvas-graph.md`](docs/agent-canvas-graph.md) — the session-free
57
+ `GET /api/workflows/:path/graph` Canvas route keyed by an agent's path.
58
+ - [`docs/workspace-system-graph.md`](docs/workspace-system-graph.md) — the
59
+ Project dependency-graph endpoints, lifecycle states, cache signal, warnings,
60
+ and `system-graph.changed` event.
61
+
52
62
  ## Testing
53
63
 
54
64
  Three tiers — run whatever fits your change:
@@ -1,4 +1,144 @@
1
- export declare const AGENT_PROJECT_SCAN_MAX_DEPTH = 3;
1
+ /**
2
+ * One discovery contract for Agent Studio's registry, live workspace watcher,
3
+ * and folder picker. Keeping marker parsing and traversal policy here prevents
4
+ * the three surfaces from disagreeing about whether a directory is an agent
5
+ * project.
6
+ */
7
+ import * as fs from "node:fs";
8
+ /**
9
+ * How deep a scan looks for agent projects beneath a chosen root.
10
+ *
11
+ * This was 3, which predated the project-rooted rail: it assumed the root the
12
+ * user opened was more or less the agent's own folder. Under a chosen project
13
+ * root, depth is normal — `<root>/backend/src/agents/<agent>` is four segments
14
+ * down and `<root>/apps/<app>/src/features/<x>/agents/<agent>` is six. At 3
15
+ * those agents are not found at all and file under "No workspace"; on a
16
+ * measured root that was well over a third of the rail.
17
+ *
18
+ * 8 leaves headroom above the deepest realistic layout without becoming a walk
19
+ * of the user's home directory. It is NOT what keeps the scan cheap — see
20
+ * AGENT_PROJECT_SCAN_MAX_NODES. What depth still buys is (a) a pathological
21
+ * deep chain terminating in 8 steps rather than by exhausting a node budget,
22
+ * and (b) a *deterministic* outer envelope, which reconciliation needs: the
23
+ * registry may only forget a project it can prove it would have looked for.
24
+ */
25
+ export declare const AGENT_PROJECT_SCAN_MAX_DEPTH = 8;
26
+ /**
27
+ * The bound that actually governs scan cost: how many directories one walk may
28
+ * enter. Depth alone cannot do this job, and the numbers are why.
29
+ *
30
+ * Measured on this machine (macOS/APFS) against real roots on a real install,
31
+ * per directory entered = one marker `lstat` + one `readdir`, warm cache
32
+ * ~22-25 us/dir at every depth:
33
+ *
34
+ * root depth 3 depth 8 unbounded
35
+ * a single repo 119 dirs 242 dirs 7 ms 242 dirs
36
+ * ~/sapiom/sapiom-js 758 dirs 9,016 dirs 196 ms 9,195 dirs
37
+ * ~/sapiom/Sapiom 1,298 dirs 35,489 dirs 847 ms 47,544 dirs
38
+ *
39
+ * So raising the depth cap alone makes a 32 ms scan an 847 ms one on a root a
40
+ * user would plausibly open, and the watcher below pays that synchronously on
41
+ * a debounce. Cost is linear and predictable in directories entered, so that
42
+ * is what we bound.
43
+ *
44
+ * Pruning harder by NAME was the other candidate and does not pay: extending
45
+ * the ignored-directory list with the usual suspects (`.venv`, `target`,
46
+ * `vendor`, `coverage`, `.turbo`, `__pycache__`, `out`, ...) removed 0.5-11% of
47
+ * the dirs on the roots above. What is actually there is source directories
48
+ * and, on the biggest root, 42,163 of 47,544 dirs in git worktree copies of the
49
+ * repo itself.
50
+ *
51
+ * That second half IS now reachable, and not by name: see
52
+ * {@link isForeignRepositoryRoot}. A walk that stops at a foreign checkout does
53
+ * not enter those 42,163 directories at all, which is why the node budget is no
54
+ * longer what ends a scan of a real root — the repository is. The budget stays
55
+ * as the backstop for a tree that is genuinely one enormous checkout.
56
+ *
57
+ * **The tradeoff, stated plainly:** past the budget a scan is incomplete, and
58
+ * an agent below the cut is not discovered on that pass. The walk is therefore
59
+ * breadth-first, so the budget degrades by *depth* — every level below the cut
60
+ * is complete, which is exactly the guarantee the old fixed cap gave, just at a
61
+ * depth chosen by the tree's real width instead of guessed in advance. Callers
62
+ * learn how far they may trust the result from
63
+ * {@link AgentProjectScanBudget.envelopeDepth}; nothing beyond it is reconciled
64
+ * away as missing.
65
+ *
66
+ * 10,000 dirs ~= 240 ms warm for the async registry scan, which runs when a
67
+ * session is created or the user asks — and covers a real monorepo project root
68
+ * outright (sapiom-js: 9,016).
69
+ */
70
+ export declare const AGENT_PROJECT_SCAN_MAX_NODES = 10000;
71
+ /**
72
+ * The watcher fingerprint's budget, deliberately tighter than the scan's.
73
+ *
74
+ * `snapshotWorkspaceWorkflows` is synchronous and re-runs on a 250 ms debounce
75
+ * after any file change under the session's cwd, so its cost lands on the event
76
+ * loop while the user is typing. 2,500 dirs ~= 60 ms warm, which is the same
77
+ * order as what depth-3 cost on the widest real root measured (1,298 dirs /
78
+ * 32 ms): the watcher gets the full depth on an ordinary project and gets no
79
+ * slower on a huge one. A truncated fingerprint means a *deep* structural
80
+ * change may not arm a rescan until some shallower event does; discovery depth
81
+ * itself is the registry scan's budget, not this one.
82
+ */
83
+ export declare const AGENT_PROJECT_WATCH_MAX_NODES = 2500;
84
+ /**
85
+ * The entry name that marks a directory as its own repository checkout: a
86
+ * `.git` DIRECTORY in an ordinary clone, a `.git` FILE in a git worktree or a
87
+ * submodule. Both forms are one entry called `.git`, which is all this policy
88
+ * needs to know.
89
+ */
90
+ export declare const REPOSITORY_MARKER = ".git";
91
+ /**
92
+ * Whether a directory we just listed is the root of a repository other than
93
+ * the one the scan started in — the boundary a walk does not cross.
94
+ *
95
+ * **Why breadth, not depth, was the real bound.** The scan that produced this
96
+ * install's 88-row registry was rooted one level too high, at `~/sapiom`. From
97
+ * there the walk crossed into twelve sibling checkouts nobody had opened, and
98
+ * kept going until the node budget cut it off:
99
+ *
100
+ * Measured on that install (macOS/APFS, warm cache) at the shipped 10,000-node
101
+ * budget. Each cell is agents registered / distinct names among them /
102
+ * directories entered / wall clock:
103
+ *
104
+ * root today with the boundary
105
+ * ~/sapiom/wf-demo-testing 10 / 10, 17 dirs, 0 ms 10 / 10, 17 dirs, 0 ms
106
+ * ~/sapiom 88 / 65, 10,000 dirs, 239 ms 68 / 64, 408 dirs, 8 ms
107
+ * TRUNCATED at depth 5 complete
108
+ * ~/sapiom/sapiom-js 25 / 2, 9,016 dirs, 233 ms 2 / 2, 444 dirs, 8 ms
109
+ * ~/sapiom/Sapiom 0 / 0, 10,000 dirs, 200 ms 0 / 0, 5,408 dirs, 107 ms
110
+ * TRUNCATED at depth 5 complete
111
+ *
112
+ * The `sapiom-js` row is the whole argument in one line. Of the 25 agents a
113
+ * scan of that repo used to register, 24 were the SAME agent — one e2e fixture
114
+ * — reachable once per git worktree under `.trees/`. Two were real. Read the
115
+ * name counts down the column: the rule barely changes how many *distinct*
116
+ * agents a scan finds, and collapses how many *rows* it writes.
117
+ *
118
+ * Letting the node budget off its leash widens the gap rather than closing it:
119
+ * uncapped, `~/sapiom` is 141 agents across 83,969 directories in 6.8 s — and
120
+ * still only 73 distinct names. Depth was never what was wrong.
121
+ *
122
+ * **The cost, stated plainly.** Across that pair, 5 distinct agents are lost at
123
+ * `~/sapiom` (73 names uncapped vs 68 bounded), and `~/sapiom/Sapiom` loses the
124
+ * single agent it has. Every one of them lives inside a checkout below the scan
125
+ * root, and every one is registered the moment that checkout is itself the root
126
+ * — opened as a project, hosting a session, or named to
127
+ * `POST /api/workflows/scan`. That is the trade the user asked for in as many
128
+ * words: "It's okay if we don't fully scan."
129
+ *
130
+ * It also costs nothing to evaluate: the walk has already listed the directory
131
+ * to find its subdirectories, so the check reads a `Dirent[]` already in hand —
132
+ * no extra syscall.
133
+ *
134
+ * **What it deliberately does NOT do.** A nested checkout that IS an agent (its
135
+ * own `sapiom.json` at the top) is still registered: the marker is inspected
136
+ * before the boundary is considered, and a marker stops the walk anyway. Only
137
+ * a checkout that merely *contains* agents is left alone. So `~/agents/foo`
138
+ * (one repo per agent) is found; `~/src/some-other-monorepo/**` is not, unless
139
+ * the user points a scan at that monorepo.
140
+ */
141
+ export declare function isForeignRepositoryRoot(entries: fs.Dirent[]): boolean;
2
142
  export interface AgentProjectMarker {
3
143
  definitionId?: number | null;
4
144
  /** The agent's `defineAgent({ name })`, cached by `link`. */
@@ -29,4 +169,98 @@ export declare function readAgentProjectMarkerSync(dir: string): AgentProjectMar
29
169
  export declare function inspectAgentProjectMarkerSync(dir: string): AgentProjectMarkerInspection;
30
170
  export declare function readAgentProjectMarker(dir: string): Promise<AgentProjectMarker | null>;
31
171
  export declare function inspectAgentProjectMarker(dir: string): Promise<AgentProjectMarkerInspection>;
172
+ /** Overridable halves of the traversal policy — tests and benchmarks vary these. */
173
+ export interface AgentProjectScanLimits {
174
+ maxDepth: number;
175
+ maxNodes: number;
176
+ }
177
+ /**
178
+ * One walk's traversal allowance, and its report on what it managed to cover.
179
+ *
180
+ * A budget is single-use: it accumulates `visited` as the walk runs, so pass a
181
+ * fresh one per walk (the walkers below default to one). Callers that care
182
+ * about cost or completeness construct it themselves and read it afterwards —
183
+ * that is also how the perf benchmark measures nodes visited without the
184
+ * scanner growing a second, parallel counting path.
185
+ */
186
+ export declare class AgentProjectScanBudget implements AgentProjectScanLimits {
187
+ readonly maxDepth: number;
188
+ readonly maxNodes: number;
189
+ /** Directories entered (marker inspected), root included. */
190
+ visited: number;
191
+ /** The depth at which `maxNodes` stopped the walk, or null if it never did. */
192
+ truncatedAtDepth: number | null;
193
+ /**
194
+ * Foreign repository roots the walk stopped at rather than entering.
195
+ *
196
+ * Rides on the budget for the same reason `visited` does: a caller holding the
197
+ * budget can read what the walk actually did without every intermediate
198
+ * signature having to forward it. Load-bearing for the UI — a scan of a
199
+ * non-repo folder full of clones finds nothing, and only this list can tell
200
+ * "there is nothing here" apart from "I did not look in there".
201
+ */
202
+ repositoryBoundaries: string[];
203
+ constructor(limits?: Partial<AgentProjectScanLimits>);
204
+ get truncated(): boolean;
205
+ /**
206
+ * The deepest level this walk enumerated *in full* — how deep its results may
207
+ * be trusted as complete, and therefore how deep a caller may reconcile a
208
+ * previously-known project away as gone. `maxDepth` for a walk that finished;
209
+ * one level above the cut for a walk the node budget stopped, because that
210
+ * level is the only incomplete one (the walk is breadth-first).
211
+ */
212
+ get envelopeDepth(): number;
213
+ /** Charges one directory. False (and records the cut) once spent. */
214
+ admit(depth: number): boolean;
215
+ }
216
+ /** What the walk should do with a directory it just entered. */
217
+ export type AgentProjectWalkAction =
218
+ /** Record it and go no deeper — it is a project, or an opaque subtree. */
219
+ "stop"
220
+ /** Not a project: enumerate its subdirectories and keep going. */
221
+ | "descend";
222
+ export interface AgentProjectWalkVisitor<Action = AgentProjectWalkAction | Promise<AgentProjectWalkAction>> {
223
+ /** Every directory entered, root first, shallowest level first. */
224
+ onDirectory(dir: string, depth: number): Action;
225
+ /** `dir`'s entries could not be listed, and not because it is gone. */
226
+ onUnreadable?(dir: string, depth: number): void;
227
+ /**
228
+ * `dir` is a repository checkout of its own and was not descended into — see
229
+ * {@link isForeignRepositoryRoot}. Reported so a caller can say what it
230
+ * declined to look inside rather than silently returning a short list.
231
+ */
232
+ onRepositoryBoundary?(dir: string, depth: number): void;
233
+ }
234
+ /** Traversal policy a caller may vary; the defaults are what ships. */
235
+ export interface AgentProjectWalkOptions {
236
+ /**
237
+ * Descend into nested repository checkouts as if they were ordinary
238
+ * directories — the pre-boundary behaviour. Off by default, and only ever
239
+ * turned on to MEASURE the difference (see agent-project-scan.perf.test.ts).
240
+ * Turning it on in a product path re-opens the accumulation this boundary
241
+ * exists to stop.
242
+ */
243
+ crossRepositoryBoundaries?: boolean;
244
+ }
245
+ /**
246
+ * Breadth-first, depth- and node-bounded traversal beneath `root` — the one
247
+ * traversal policy the registry and the workspace watcher both run, so they
248
+ * cannot disagree about which directories a scan of a given root covers.
249
+ *
250
+ * Breadth-first is load-bearing rather than incidental: it is what makes the
251
+ * node budget degrade by depth (see AGENT_PROJECT_SCAN_MAX_NODES). Every level
252
+ * shallower than `budget.envelopeDepth` is complete no matter where the budget
253
+ * ran out, which keeps the generalized bound a superset of the fixed depth cap
254
+ * it replaces.
255
+ *
256
+ * Returns the budget so a caller can read `visited` / `envelopeDepth` off it.
257
+ */
258
+ export declare function walkAgentProjectTree(root: string, visitor: AgentProjectWalkVisitor<AgentProjectWalkAction>, budget?: AgentProjectScanBudget, options?: AgentProjectWalkOptions): AgentProjectScanBudget;
259
+ /**
260
+ * Async twin of {@link walkAgentProjectTree}, for callers that must not block
261
+ * the event loop on a wide tree. Same order, same bounds, same decisions — so
262
+ * the two produce identical results on the same tree with the same budget,
263
+ * which the watcher's sync/async fingerprint pair depends on.
264
+ */
265
+ export declare function walkAgentProjectTreeAsync(root: string, visitor: AgentProjectWalkVisitor<AgentProjectWalkAction | Promise<AgentProjectWalkAction>>, budget?: AgentProjectScanBudget, options?: AgentProjectWalkOptions): Promise<AgentProjectScanBudget>;
32
266
  //# sourceMappingURL=agent-project-discovery.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-project-discovery.d.ts","sourceRoot":"","sources":["../../src/core/agent-project-discovery.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAW9C,MAAM,WAAW,kBAAkB;IACjC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oFAAoF;IACpF,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;;;;GAKG;AACH,MAAM,MAAM,4BAA4B,GACpC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,kBAAkB,CAAA;CAAE,GAC/C;IAAE,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,YAAY,CAAA;CAAE,CAAC;AAEpD,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElE;AAED,4EAA4E;AAC5E,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,MAAM,GACV,kBAAkB,GAAG,IAAI,CAS3B;AA6BD,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,MAAM,GACV,kBAAkB,GAAG,IAAI,CAG3B;AAED,wBAAgB,6BAA6B,CAC3C,GAAG,EAAE,MAAM,GACV,4BAA4B,CAqB9B;AAED,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAGpC;AAED,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,4BAA4B,CAAC,CAuBvC"}
1
+ {"version":3,"file":"agent-project-discovery.d.ts","sourceRoot":"","sources":["../../src/core/agent-project-discovery.ts"],"names":[],"mappings":"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"}