@sapiom/harness 0.8.9 → 0.9.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.
- package/CHANGELOG.md +63 -0
- package/README.md +11 -1
- package/dist/core/agent-project-discovery.d.ts +235 -1
- package/dist/core/agent-project-discovery.d.ts.map +1 -1
- package/dist/core/agent-project-discovery.js +303 -1
- package/dist/core/agent-project-discovery.js.map +1 -1
- package/dist/core/canvas-interconnections.d.ts +31 -0
- package/dist/core/canvas-interconnections.d.ts.map +1 -1
- package/dist/core/canvas-interconnections.js +294 -21
- package/dist/core/canvas-interconnections.js.map +1 -1
- package/dist/core/canvas-render.d.ts +32 -0
- package/dist/core/canvas-render.d.ts.map +1 -1
- package/dist/core/canvas-render.js +50 -41
- package/dist/core/canvas-render.js.map +1 -1
- package/dist/core/definition-name.d.ts +12 -0
- package/dist/core/definition-name.d.ts.map +1 -1
- package/dist/core/definition-name.js +14 -5
- package/dist/core/definition-name.js.map +1 -1
- package/dist/core/system-graph-inventory.d.ts +68 -0
- package/dist/core/system-graph-inventory.d.ts.map +1 -0
- package/dist/core/system-graph-inventory.js +312 -0
- package/dist/core/system-graph-inventory.js.map +1 -0
- package/dist/core/system-graph-relationships.d.ts +41 -0
- package/dist/core/system-graph-relationships.d.ts.map +1 -0
- package/dist/core/system-graph-relationships.js +98 -0
- package/dist/core/system-graph-relationships.js.map +1 -0
- package/dist/core/system-graph-store.d.ts +45 -0
- package/dist/core/system-graph-store.d.ts.map +1 -0
- package/dist/core/system-graph-store.js +222 -0
- package/dist/core/system-graph-store.js.map +1 -0
- package/dist/core/system-graph-watcher.d.ts +51 -0
- package/dist/core/system-graph-watcher.d.ts.map +1 -0
- package/dist/core/system-graph-watcher.js +448 -0
- package/dist/core/system-graph-watcher.js.map +1 -0
- package/dist/core/system-graph.d.ts +43 -0
- package/dist/core/system-graph.d.ts.map +1 -0
- package/dist/core/system-graph.js +321 -0
- package/dist/core/system-graph.js.map +1 -0
- package/dist/core/workflow-registry.d.ts +84 -6
- package/dist/core/workflow-registry.d.ts.map +1 -1
- package/dist/core/workflow-registry.js +191 -91
- package/dist/core/workflow-registry.js.map +1 -1
- package/dist/core/workspace-watcher.d.ts +21 -13
- package/dist/core/workspace-watcher.d.ts.map +1 -1
- package/dist/core/workspace-watcher.js +79 -81
- package/dist/core/workspace-watcher.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/server/agent-move.d.ts +134 -0
- package/dist/server/agent-move.d.ts.map +1 -0
- package/dist/server/agent-move.js +343 -0
- package/dist/server/agent-move.js.map +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +275 -22
- package/dist/server/index.js.map +1 -1
- package/dist/server/rest.d.ts +3 -0
- package/dist/server/rest.d.ts.map +1 -1
- package/dist/server/rest.js +3 -0
- package/dist/server/rest.js.map +1 -1
- package/dist/server/studio-rail.d.ts +76 -0
- package/dist/server/studio-rail.d.ts.map +1 -0
- package/dist/server/studio-rail.js +234 -0
- package/dist/server/studio-rail.js.map +1 -0
- package/dist/server/system-graph.d.ts +14 -0
- package/dist/server/system-graph.d.ts.map +1 -0
- package/dist/server/system-graph.js +40 -0
- package/dist/server/system-graph.js.map +1 -0
- package/dist/server/workflow-graph.d.ts +43 -0
- package/dist/server/workflow-graph.d.ts.map +1 -0
- package/dist/server/workflow-graph.js +200 -0
- package/dist/server/workflow-graph.js.map +1 -0
- package/dist/shared/system-graph.d.ts +59 -0
- package/dist/shared/system-graph.d.ts.map +1 -0
- package/dist/shared/system-graph.js +76 -0
- package/dist/shared/system-graph.js.map +1 -0
- package/dist/shared/types.d.ts +45 -0
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/types.js.map +1 -1
- package/dist/web/assets/index-2Fab6dzf.js +508 -0
- package/dist/web/assets/index-D0doaFvT.css +32 -0
- package/dist/web/index.html +2 -2
- package/package.json +5 -5
- package/dist/web/assets/index-B18B2y5o.js +0 -494
- package/dist/web/assets/index-jGBbXx9R.css +0 -32
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,68 @@
|
|
|
1
1
|
# @sapiom/harness
|
|
2
2
|
|
|
3
|
+
## 0.9.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 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.
|
|
8
|
+
|
|
9
|
+
`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.
|
|
10
|
+
|
|
11
|
+
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):
|
|
12
|
+
|
|
13
|
+
| root | depth 3 | depth 8 | unbounded |
|
|
14
|
+
| ------------------------------- | ---------- | -------------------- | ----------- |
|
|
15
|
+
| a single repo | 119 dirs | 242 dirs · 7 ms | 242 dirs |
|
|
16
|
+
| a monorepo | 758 dirs | 9,016 dirs · 196 ms | 9,195 dirs |
|
|
17
|
+
| a monorepo with worktree copies | 1,298 dirs | 35,489 dirs · 847 ms | 47,544 dirs |
|
|
18
|
+
|
|
19
|
+
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.
|
|
20
|
+
|
|
21
|
+
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".
|
|
22
|
+
|
|
23
|
+
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.
|
|
24
|
+
|
|
25
|
+
- 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.
|
|
26
|
+
- 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.
|
|
27
|
+
|
|
28
|
+
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.
|
|
29
|
+
|
|
30
|
+
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.
|
|
31
|
+
|
|
32
|
+
- 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.
|
|
33
|
+
|
|
34
|
+
**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.
|
|
35
|
+
|
|
36
|
+
**`Remove project`** takes a root off the rail without touching anything on disk.
|
|
37
|
+
|
|
38
|
+
**`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.
|
|
39
|
+
|
|
40
|
+
**New local REST surfaces** on the same `127.0.0.1` boot-token-gated `/api` mount as the rest:
|
|
41
|
+
|
|
42
|
+
- `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.
|
|
43
|
+
- `GET /api/studio-rail/launch-edges` — which agents launch which, across every registered agent, used to seed groups.
|
|
44
|
+
- `POST /api/agents/move` — above.
|
|
45
|
+
|
|
46
|
+
**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.
|
|
47
|
+
|
|
48
|
+
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.
|
|
49
|
+
|
|
50
|
+
- 37ab85b: A canvas board can now be read by agent path, with no session involved: `GET /api/workflows/:path/graph`.
|
|
51
|
+
|
|
52
|
+
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.
|
|
53
|
+
|
|
54
|
+
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.
|
|
55
|
+
|
|
56
|
+
`: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>`.
|
|
57
|
+
|
|
58
|
+
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.
|
|
59
|
+
|
|
60
|
+
Full contract: `packages/harness/docs/agent-canvas-graph.md`.
|
|
61
|
+
|
|
62
|
+
- 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.
|
|
63
|
+
- 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.
|
|
64
|
+
- 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.
|
|
65
|
+
|
|
3
66
|
## 0.8.9
|
|
4
67
|
|
|
5
68
|
### 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
|
-
|
|
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":"
|
|
1
|
+
{"version":3,"file":"agent-project-discovery.d.ts","sourceRoot":"","sources":["../../src/core/agent-project-discovery.ts"],"names":[],"mappings":"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"}
|