opencode-swarm 7.135.0 → 7.135.2
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/.opencode/skills/swarm-pr-feedback/SKILL.md +24 -21
- package/.opencode/skills/swarm-pr-review/SKILL.md +55 -42
- package/dist/agents/explorer.d.ts +1 -1
- package/dist/background/candidate-contract.d.ts +113 -0
- package/dist/background/candidate-parser.d.ts +8 -29
- package/dist/background/pr-review-trigger-contract.d.ts +197 -0
- package/dist/background/workspace-snapshot.d.ts +26 -0
- package/dist/cli/{config-doctor-j1x63x87.js → config-doctor-pj3yvxdb.js} +2 -2
- package/dist/cli/{core-fvqp2gnn.js → core-4va9z2ng.js} +2 -2
- package/dist/cli/{curation-policy-xg9zw0ep.js → curation-policy-b2caf3v3.js} +5 -5
- package/dist/cli/{curator-drift-9q6f85bm.js → curator-drift-2ta83hss.js} +2 -2
- package/dist/cli/{curator-llm-factory-4mat6cq5.js → curator-llm-factory-9983528d.js} +26 -26
- package/dist/cli/{curator-yc06cmnz.js → curator-r1s2mwf2.js} +26 -26
- package/dist/cli/{dispatch-gxbeb2ps.js → dispatch-wtk6dxky.js} +2 -2
- package/dist/cli/{evidence-summary-service-0cczdmjv.js → evidence-summary-service-feby8ny0.js} +5 -5
- package/dist/cli/{explorer-jp0nen03.js → explorer-15gqzxh8.js} +1 -1
- package/dist/cli/{gate-evidence-8zrwdj69.js → gate-evidence-84b03mb4.js} +3 -3
- package/dist/cli/guardrail-explain-pwxvg5n0.js +57 -0
- package/dist/cli/{guardrail-log-7egxrxap.js → guardrail-log-yzpz8b7t.js} +4 -4
- package/dist/cli/{hive-promoter-7s3gzajy.js → hive-promoter-586dhndj.js} +26 -26
- package/dist/cli/{index-nrvs08pr.js → index-02cqjna3.js} +6 -6
- package/dist/cli/{index-grxkbbc5.js → index-2pxk6qg4.js} +1 -1
- package/dist/cli/{index-crw4n55v.js → index-340chr1e.js} +1612 -166
- package/dist/cli/{index-g0raqavt.js → index-3m7kpnqf.js} +2 -2
- package/dist/cli/{index-z085b4ka.js → index-3xbx305x.js} +1 -1
- package/dist/cli/{index-b4z7s917.js → index-73dfyfhq.js} +1 -1
- package/dist/cli/{index-ens0gt38.js → index-7pnvf04s.js} +8 -8
- package/dist/cli/{index-x88w0cp5.js → index-8gw8p7yh.js} +3 -3
- package/dist/cli/{index-e8w62fgs.js → index-9c3gf2wd.js} +1 -1
- package/dist/cli/{index-vf6e8ame.js → index-b0eva00y.js} +2 -2
- package/dist/cli/{index-2ghkk9ve.js → index-bfd4xfpp.js} +3 -3
- package/dist/cli/{index-6kmmdtdp.js → index-bs4m85v1.js} +2 -2
- package/dist/cli/{index-86wdrskb.js → index-ckdva4rs.js} +5 -5
- package/dist/cli/{index-96b78s7p.js → index-cwdy5vw9.js} +28 -28
- package/dist/cli/{index-44zgqvqh.js → index-d5psymdk.js} +4 -4
- package/dist/cli/{index-vxv732ex.js → index-dg1njnms.js} +7 -4
- package/dist/cli/{index-yhba8swx.js → index-dzyjb33e.js} +1 -1
- package/dist/cli/{index-xg9repnx.js → index-ey29aap6.js} +1 -1
- package/dist/cli/{index-q9mq5tv3.js → index-g3meqrsg.js} +2 -2
- package/dist/cli/{index-7an2qn0s.js → index-hdy2nd5h.js} +4 -4
- package/dist/cli/{index-4yv2rn8q.js → index-hvrrq3f3.js} +1 -1
- package/dist/cli/{index-337r9pkr.js → index-j0kte6gd.js} +1 -1
- package/dist/cli/{index-ctvnb76s.js → index-j92ser8n.js} +1 -1
- package/dist/cli/{index-997d6p42.js → index-jgkjxpdh.js} +4 -4
- package/dist/cli/{index-13xxjfhn.js → index-m0ynxkaa.js} +3 -3
- package/dist/cli/{index-a4972t0f.js → index-qqkw794p.js} +3 -3
- package/dist/cli/{index-b7p0mp2q.js → index-ryqnn3nv.js} +1 -1
- package/dist/cli/{index-ny93srnd.js → index-vg3yx648.js} +1 -1
- package/dist/cli/{index-qe3v54nb.js → index-y111zefa.js} +64 -23
- package/dist/cli/{index-3nasn371.js → index-y6a7gjtj.js} +126 -42
- package/dist/cli/index.js +26 -26
- package/dist/cli/{knowledge-escalator-d76a090n.js → knowledge-escalator-w7by1m8m.js} +6 -6
- package/dist/cli/{knowledge-events-3mcg1a0e.js → knowledge-events-n8xxsay3.js} +4 -4
- package/dist/cli/{knowledge-link-etrfntkd.js → knowledge-link-mm1w967j.js} +3 -3
- package/dist/cli/{knowledge-store-xtt4sede.js → knowledge-store-xzmvfnx3.js} +4 -4
- package/dist/cli/{knowledge-validator-h1k7ymtd.js → knowledge-validator-5q1amsq6.js} +7 -7
- package/dist/cli/{pending-delegations-4z3tkkct.js → pending-delegations-mt5gs1bt.js} +3 -3
- package/dist/cli/{pr-subscriptions-29j794kd.js → pr-subscriptions-v5xd3j6d.js} +3 -3
- package/dist/cli/{scan-cursor-stztfv7d.js → scan-cursor-1q1zmt4b.js} +5 -5
- package/dist/cli/{schema-tbq2srrh.js → schema-8mtsfspm.js} +1 -1
- package/dist/cli/{scope-persistence-7wydjzx8.js → scope-persistence-h2fpgxww.js} +2 -2
- package/dist/cli/{skill-generator-02q1qdf0.js → skill-generator-54xbjgak.js} +8 -8
- package/dist/cli/{workspace-snapshot-9h5za0s6.js → workspace-snapshot-jmyamqnv.js} +6 -1
- package/dist/cli/{worktree-collision-ownership-dtwr0xj4.js → worktree-collision-ownership-15hymf1y.js} +5 -5
- package/dist/commands/registry.d.ts +1 -1
- package/dist/hooks/pr-workflow-gate.d.ts +30 -4
- package/dist/hooks/repo-graph-builder.d.ts +18 -0
- package/dist/hooks/scope-guard.d.ts +11 -4
- package/dist/index.js +395 -391
- package/dist/scope/scope-binding.d.ts +68 -0
- package/dist/session/snapshot-reader.d.ts +6 -0
- package/dist/state.d.ts +118 -0
- package/dist/tools/convene-council.d.ts +3 -3
- package/dist/tools/dispatch-lanes.d.ts +65 -7
- package/dist/tools/repo-graph/builder.d.ts +20 -2
- package/dist/tools/repo-graph/incremental.d.ts +18 -1
- package/dist/tools/repo-graph/ontology.d.ts +8 -0
- package/dist/tools/repo-graph/types.d.ts +62 -1
- package/dist/tools/repo-graph.d.ts +1 -1
- package/dist/tools/submit-phase-council-verdicts.d.ts +3 -3
- package/dist/tools/write-final-council-evidence.d.ts +3 -3
- package/dist/tools/write-pr-review-trigger-eval.d.ts +34 -63
- package/dist/turbo/lean/lane-scope.d.ts +120 -0
- package/dist/turbo/lean/runner.d.ts +66 -4
- package/package.json +1 -1
- package/dist/cli/guardrail-explain-zmbxwh3s.js +0 -57
|
@@ -83,6 +83,66 @@ export declare function getAuthorizedScopeBindingByPlanIdentity(input: {
|
|
|
83
83
|
taskId: string;
|
|
84
84
|
activeSessionId: string;
|
|
85
85
|
}): ScopeBinding | null;
|
|
86
|
+
/**
|
|
87
|
+
* Issue #2002 recurrence guardrail (detection half).
|
|
88
|
+
*
|
|
89
|
+
* The defect class is: *a hook constructed once with the plugin-root
|
|
90
|
+
* `ctx.directory` applies that root to sessions that actually execute in a
|
|
91
|
+
* different root.* Its signature failure is silent — a binding for this exact
|
|
92
|
+
* session exists and is otherwise valid, but `workspaceIdentity` (filter
|
|
93
|
+
* condition #1 in `getAuthorizedScopeBindingByPlanIdentity`) never matches, so
|
|
94
|
+
* the lookup returns null and the caller reports a generic
|
|
95
|
+
* `SCOPE_NOT_DECLARED` that names neither root.
|
|
96
|
+
*
|
|
97
|
+
* This turns that silence into a precise, self-describing diagnostic at the one
|
|
98
|
+
* user-visible failure site (`src/hooks/scope-guard.ts`). It catches the class
|
|
99
|
+
* at ANY call site that resolves a binding with the wrong root — present or
|
|
100
|
+
* future — rather than only at the two gates fixed for this issue.
|
|
101
|
+
*
|
|
102
|
+
* Diagnostic-only: never grants or denies authorization. Callers still fall
|
|
103
|
+
* back to the ordinary `SCOPE_NOT_DECLARED` denial whenever this returns null.
|
|
104
|
+
*
|
|
105
|
+
* The candidate filter mirrors every non-workspace correlation clause
|
|
106
|
+
* `getAuthorizedScopeBindingByPlanIdentity` requires for an otherwise-valid
|
|
107
|
+
* Task-scoped authorization — dispatch correlation (`dispatchCallId` present
|
|
108
|
+
* and equal to `ownerMessageId`/`parentCallId`) plus a genuine parent/child
|
|
109
|
+
* session split — so a stale or malformed "active" leftover that never went
|
|
110
|
+
* through the real claim/derive path cannot be mislabeled as a workspace
|
|
111
|
+
* mismatch just because it happens to share session + task + `activation`.
|
|
112
|
+
* `pr_feedback` bindings are excluded outright: they resolve through an
|
|
113
|
+
* entirely separate authorization path
|
|
114
|
+
* (`getAuthorizedPrFeedbackScopeBinding` / `resolveAuthorizedPrFeedbackScopeBindingFromDisk`)
|
|
115
|
+
* that the caller already attempted before falling into this diagnostic, and
|
|
116
|
+
* a live `pr_feedback` binding otherwise satisfies every correlation clause
|
|
117
|
+
* above.
|
|
118
|
+
*
|
|
119
|
+
* `planId`/`planStructureHash` are deliberately NOT compared here: a
|
|
120
|
+
* plan-drifted binding at the SAME root is already excluded by the
|
|
121
|
+
* `workspaceIdentity` clause below (it would never have reached this
|
|
122
|
+
* diagnostic branch in the first place — the ordinary lookup would have
|
|
123
|
+
* rejected it on plan identity, not returned null for lack of a match at
|
|
124
|
+
* all). Reading the plan at `input.directory` to compare identities would
|
|
125
|
+
* also be unreliable here: `input.directory` is, by construction, the root
|
|
126
|
+
* this diagnostic suspects is wrong, so its `.swarm/plan.json` may not exist
|
|
127
|
+
* or may belong to an unrelated project — silently disabling the guardrail
|
|
128
|
+
* exactly where it matters most.
|
|
129
|
+
*
|
|
130
|
+
* Returns null (no mismatch claim) whenever `input.directory` itself cannot
|
|
131
|
+
* be resolved to a canonical identity (e.g. a deleted lane directory) —
|
|
132
|
+
* otherwise every active binding for the session would trivially satisfy
|
|
133
|
+
* `workspaceIdentity !== null`, misreporting a missing-directory failure as a
|
|
134
|
+
* wrong-root failure.
|
|
135
|
+
*
|
|
136
|
+
* @param input.directory - The root the caller resolved against
|
|
137
|
+
* @param input.activeSessionId - The session attempting the write
|
|
138
|
+
* @param input.taskId - Optional task filter
|
|
139
|
+
* @returns A human-readable mismatch description, or null
|
|
140
|
+
*/
|
|
141
|
+
export declare function describeScopeWorkspaceMismatch(input: {
|
|
142
|
+
directory: string;
|
|
143
|
+
activeSessionId: string;
|
|
144
|
+
taskId?: string | null;
|
|
145
|
+
}): string | null;
|
|
86
146
|
export declare function clearScopeBindings(predicate?: (binding: ScopeBinding) => boolean): ScopeBinding[];
|
|
87
147
|
/**
|
|
88
148
|
* Atomically claim the pending scope for the exact Task call whose upstream
|
|
@@ -103,6 +163,14 @@ export declare function deriveChildScopeBinding(parent: ScopeBinding, input: {
|
|
|
103
163
|
childSessionId: string;
|
|
104
164
|
parentCallId: string;
|
|
105
165
|
}): ScopeBinding;
|
|
166
|
+
/**
|
|
167
|
+
* Containment predicate shared by every scope consumer: a candidate path is
|
|
168
|
+
* covered by a scope entry when it is that entry or lives beneath it. Exported
|
|
169
|
+
* so authority-minting callers (e.g. the Lean Turbo lane publisher) narrow a
|
|
170
|
+
* candidate file set against plan authority using the *same* semantics the
|
|
171
|
+
* write gates later enforce, instead of re-implementing containment.
|
|
172
|
+
*/
|
|
173
|
+
export declare function scopeContains(scope: readonly string[], candidate: string): boolean;
|
|
106
174
|
export declare function resolveCoderScopeSources(input: {
|
|
107
175
|
explicitFiles: readonly string[] | null | undefined;
|
|
108
176
|
planFiles: readonly string[] | null | undefined;
|
|
@@ -7,6 +7,12 @@ import type { SerializedAgentSession, SnapshotData } from './snapshot-writer';
|
|
|
7
7
|
/**
|
|
8
8
|
* Transient session fields that must be reset on rehydration.
|
|
9
9
|
* Centralised here to keep the reset logic DRY and auditable.
|
|
10
|
+
*
|
|
11
|
+
* `workspaceDirectory` (issue #2002) deliberately does NOT belong in this
|
|
12
|
+
* list: it is never part of `SerializedAgentSession` in the first place (see
|
|
13
|
+
* the TRUST BOUNDARY / DELIBERATELY NOT SNAPSHOTTED comment at its field
|
|
14
|
+
* declaration in `src/state.ts`), so `deserializeAgentSession` never restores
|
|
15
|
+
* it and there is nothing here to reset.
|
|
10
16
|
*/
|
|
11
17
|
export declare const TRANSIENT_SESSION_FIELDS: ReadonlyArray<{
|
|
12
18
|
name: string;
|
package/dist/state.d.ts
CHANGED
|
@@ -226,6 +226,72 @@ export interface AgentSessionState {
|
|
|
226
226
|
} | null;
|
|
227
227
|
/** Declared file scope for current coder task (null = no scope declared) */
|
|
228
228
|
declaredCoderScope: string[] | null;
|
|
229
|
+
/**
|
|
230
|
+
* Issue #2002: the root this session actually EXECUTES in, when it differs
|
|
231
|
+
* from the plugin-root `ctx.directory`.
|
|
232
|
+
*
|
|
233
|
+
* Worktree-isolated coder children are created with
|
|
234
|
+
* `session.create({ query: { directory: <lane> } })`, and their scope binding
|
|
235
|
+
* is derived and published against that lane. The write gates are constructed
|
|
236
|
+
* once at plugin init with `ctx.directory` and would otherwise resolve every
|
|
237
|
+
* session's binding, containment, and `.swarm/` reads against the project
|
|
238
|
+
* root — which can never match a lane-rooted binding.
|
|
239
|
+
*
|
|
240
|
+
* TRUST BOUNDARY: written ONLY by `recordSessionWorkspaceRoot`, whose single
|
|
241
|
+
* closed allowlist of production callers each pass `provisionWorktree`'s own
|
|
242
|
+
* output. `ensureAgentSession`'s `directory` argument (which can carry an
|
|
243
|
+
* agent-supplied `working_directory`, e.g. via `declare_scope`) must NEVER
|
|
244
|
+
* write this field.
|
|
245
|
+
*
|
|
246
|
+
* DELIBERATELY NOT SNAPSHOTTED (issue #2002 follow-up). `serializeAgentSession`
|
|
247
|
+
* (`src/session/snapshot-writer.ts`) intentionally omits this field, and it
|
|
248
|
+
* MUST stay omitted. The reason is trust, not oversight: `.swarm/session/state.json`
|
|
249
|
+
* lives under the project-root `.swarm/` directory, which the architect agent
|
|
250
|
+
* can write, so a restored string in that file is not a trusted resolution
|
|
251
|
+
* root — a tampered or stale snapshot could install an arbitrary root, or
|
|
252
|
+
* re-point a session at a *different* lane it never owned (cross-lane
|
|
253
|
+
* containment re-rooting). Persisting it would only be safe if rehydrate could
|
|
254
|
+
* revalidate the restored value against a durable, plugin-owned record of
|
|
255
|
+
* provisioned lanes, and no such record exists:
|
|
256
|
+
* - `worktree-provisioning-owner.ts`'s durable marker
|
|
257
|
+
* (`recordWorktreeProvisioningOwner`) stores no `worktreePath` at all, and
|
|
258
|
+
* its `worktreeSessionId` field is always the *parent* session id
|
|
259
|
+
* (`worktree-isolation.ts` passes `args.parentSessionID`, confirmed by
|
|
260
|
+
* every production and test call site) — it cannot answer "does child
|
|
261
|
+
* session X own lane path Y".
|
|
262
|
+
* - `background-delegations.jsonl` (`src/background/pending-delegations.ts`)
|
|
263
|
+
* does carry a `worktree.worktreePath` keyed by the child session id, but
|
|
264
|
+
* it is written only inside the `isBackgroundTrue(...)` branch of
|
|
265
|
+
* `delegation-gate.ts` (~line 3157) — i.e. only for `background: true` Task
|
|
266
|
+
* dispatches. It is never written for the ordinary synchronous worktree
|
|
267
|
+
* dispatch in `worktree-isolation.ts`, nor for the Lean Turbo dispatch in
|
|
268
|
+
* `src/turbo/lean/lane-scope.ts`, which are the two actual
|
|
269
|
+
* `recordSessionWorkspaceRoot` callers. It does not cover the field it
|
|
270
|
+
* would need to validate.
|
|
271
|
+
* - Re-deriving the expected path from `provisionWorktree`'s own naming
|
|
272
|
+
* scheme (`resolveWorktreeBaseDir(directory, worktreeDir)/parentSessionId/taskId`,
|
|
273
|
+
* or the `os.tmpdir()/swwt/...` Windows-shortened form) does not close the
|
|
274
|
+
* gap either: it requires the child's *parent* session id, which is only
|
|
275
|
+
* available from `delegationChains` in the very same untrusted snapshot —
|
|
276
|
+
* validating one untrusted field with another untrusted field is circular,
|
|
277
|
+
* not a validation. It would also require spawning `git worktree list` per
|
|
278
|
+
* restored session inside `loadSnapshot`, which runs on the plugin-init
|
|
279
|
+
* path under a bounded timeout (AGENTS.md invariant 1 / repro-704).
|
|
280
|
+
* Net effect: a plugin restart mid-lane loses the recorded root, and
|
|
281
|
+
* `resolveSessionWorkspaceDirectory` falls back to the plugin-root directory —
|
|
282
|
+
* fail-closed, byte-identical to pre-#2002 behaviour, never a widened
|
|
283
|
+
* authority (see its own doc comment). The lane coder is blocked with
|
|
284
|
+
* `SCOPE_NOT_DECLARED` (`src/hooks/scope-guard.ts`,
|
|
285
|
+
* `src/hooks/guardrails/tool-before.ts`) until a human/architect re-dispatches
|
|
286
|
+
* it, exactly like the pre-fix bug — not worse. `rehydrateState` (which would
|
|
287
|
+
* apply a restored value) only runs from `loadSnapshot`, itself only called at
|
|
288
|
+
* plugin init (`src/index.ts`), so this window is a real process restart, not
|
|
289
|
+
* something that can strip a live in-session lane coder of its root.
|
|
290
|
+
* Do NOT add this field to `TRANSIENT_SESSION_FIELDS`
|
|
291
|
+
* (`src/session/snapshot-reader.ts`) either — it is never restored in the
|
|
292
|
+
* first place, so there is nothing to reset.
|
|
293
|
+
*/
|
|
294
|
+
workspaceDirectory?: string;
|
|
229
295
|
/** Last scope violation message (null = no violation) */
|
|
230
296
|
lastScopeViolation: string | null;
|
|
231
297
|
/** Flag for one-shot scope violation warning injection in messagesTransform */
|
|
@@ -720,6 +786,58 @@ export declare function getAgentSession(sessionId: string): AgentSessionState |
|
|
|
720
786
|
* @returns The AgentSessionState
|
|
721
787
|
*/
|
|
722
788
|
export declare function ensureAgentSession(sessionId: string, agentName?: string, directory?: string): AgentSessionState;
|
|
789
|
+
/**
|
|
790
|
+
* Issue #2002 — record the root a session actually EXECUTES in, when it
|
|
791
|
+
* differs from the plugin-root `ctx.directory`.
|
|
792
|
+
*
|
|
793
|
+
* TRUST BOUNDARY (security-critical): this function performs NO path
|
|
794
|
+
* validation. `laneRoot` is trusted purely because of WHO calls it, not
|
|
795
|
+
* because of its shape — `provisionWorktree` can legitimately relocate a lane
|
|
796
|
+
* outside the swarm worktree base (e.g. a `os.tmpdir()`-shortened path on
|
|
797
|
+
* Windows when the path-budget check trips), so a shape predicate would
|
|
798
|
+
* refuse a legitimately provisioned lane and silently reintroduce the bug
|
|
799
|
+
* this closes.
|
|
800
|
+
*
|
|
801
|
+
* The only thing standing between this setter and a privilege-escalation bug
|
|
802
|
+
* is a closed allowlist of production callers, each of which passes
|
|
803
|
+
* `provisionWorktree`'s own return value — never a tool argument, never
|
|
804
|
+
* `ensureAgentSession`'s `directory` parameter (which CAN carry an
|
|
805
|
+
* agent-supplied `working_directory`, e.g. via `declare_scope`). That
|
|
806
|
+
* allowlist is mechanically enforced by
|
|
807
|
+
* `tests/unit/state/session-workspace-root-trusted-callers.test.ts`. Adding a
|
|
808
|
+
* new call site is a privilege-escalation review, not a formality: the
|
|
809
|
+
* reviewer must confirm the new caller passes `provisionWorktree` output
|
|
810
|
+
* before adding it to that test's allowlist.
|
|
811
|
+
*
|
|
812
|
+
* A blank or whitespace-only `laneRoot` is ignored (treated as unset) rather
|
|
813
|
+
* than being recorded and later resolving to `path.resolve('')` (cwd).
|
|
814
|
+
*
|
|
815
|
+
* ORDERING IS PART OF THE CONTRACT. This never creates the session — callers
|
|
816
|
+
* MUST register it with its real agent name (`ensureAgentSession(sessionId,
|
|
817
|
+
* 'coder', laneRoot)`) FIRST. Calling this on an unregistered session is a
|
|
818
|
+
* deliberate no-op, so resolution falls back to the plugin root and the write
|
|
819
|
+
* is blocked. Creating the session here instead would register
|
|
820
|
+
* `swarmState.activeAgent` as 'unknown', which fails OPEN rather than closed —
|
|
821
|
+
* see the inline comment in the body for the full chain.
|
|
822
|
+
*
|
|
823
|
+
* @param sessionId - The session identifier
|
|
824
|
+
* @param laneRoot - `provisionWorktree`'s own output; never agent-supplied
|
|
825
|
+
*/
|
|
826
|
+
export declare function recordSessionWorkspaceRoot(sessionId: string, laneRoot: string): void;
|
|
827
|
+
/**
|
|
828
|
+
* Issue #2002 — resolve the root a write gate should evaluate path
|
|
829
|
+
* containment and scope-binding lookups against for a given session.
|
|
830
|
+
*
|
|
831
|
+
* Fail-closed: returns `fallbackDirectory` (the plugin-root `ctx.directory`)
|
|
832
|
+
* whenever no root was recorded for this session, or the recorded value is
|
|
833
|
+
* blank/unset — i.e. byte-identical to pre-#2002 behaviour for every session
|
|
834
|
+
* that was never lane-rooted.
|
|
835
|
+
*
|
|
836
|
+
* @param sessionId - The session identifier
|
|
837
|
+
* @param fallbackDirectory - The plugin-root directory to fall back to
|
|
838
|
+
* @returns the session's recorded workspace root, or `fallbackDirectory`
|
|
839
|
+
*/
|
|
840
|
+
export declare function resolveSessionWorkspaceDirectory(sessionId: string, fallbackDirectory: string): string;
|
|
723
841
|
/**
|
|
724
842
|
* Update only the agent event timestamp (for stale detection).
|
|
725
843
|
* Does NOT change agent name or reset guardrail state.
|
|
@@ -36,10 +36,10 @@ export declare const ArgsSchema: z.ZodObject<{
|
|
|
36
36
|
confidence: z.ZodNumber;
|
|
37
37
|
findings: z.ZodArray<z.ZodObject<{
|
|
38
38
|
severity: z.ZodEnum<{
|
|
39
|
-
CRITICAL: "CRITICAL";
|
|
40
|
-
HIGH: "HIGH";
|
|
41
|
-
MEDIUM: "MEDIUM";
|
|
42
39
|
LOW: "LOW";
|
|
40
|
+
MEDIUM: "MEDIUM";
|
|
41
|
+
HIGH: "HIGH";
|
|
42
|
+
CRITICAL: "CRITICAL";
|
|
43
43
|
}>;
|
|
44
44
|
category: z.ZodString;
|
|
45
45
|
location: z.ZodString;
|
|
@@ -48,11 +48,39 @@ declare const DispatchLanesAsyncArgsSchema: z.ZodObject<{
|
|
|
48
48
|
base_sha: z.ZodOptional<z.ZodString>;
|
|
49
49
|
base_ref: z.ZodOptional<z.ZodString>;
|
|
50
50
|
scope: z.ZodOptional<z.ZodString>;
|
|
51
|
-
trigger_evaluation: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
52
|
-
trigger_id: z.
|
|
51
|
+
trigger_evaluation: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
52
|
+
trigger_id: z.ZodEnum<{
|
|
53
|
+
"auth-identity-secrets": "auth-identity-secrets";
|
|
54
|
+
"untrusted-input-boundaries": "untrusted-input-boundaries";
|
|
55
|
+
"subprocess-platform": "subprocess-platform";
|
|
56
|
+
"concurrency-state": "concurrency-state";
|
|
57
|
+
"dependencies-build-release": "dependencies-build-release";
|
|
58
|
+
"api-schema-migrations": "api-schema-migrations";
|
|
59
|
+
"test-infrastructure": "test-infrastructure";
|
|
60
|
+
"ui-accessibility-i18n": "ui-accessibility-i18n";
|
|
61
|
+
"privacy-observability": "privacy-observability";
|
|
62
|
+
"generated-provenance": "generated-provenance";
|
|
63
|
+
"unclassified-risk": "unclassified-risk";
|
|
64
|
+
}>;
|
|
53
65
|
result: z.ZodLiteral<"MATCHED">;
|
|
54
66
|
evidence: z.ZodString;
|
|
55
|
-
}, z.core.$strict
|
|
67
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
68
|
+
trigger_id: z.ZodEnum<{
|
|
69
|
+
"auth-identity-secrets": "auth-identity-secrets";
|
|
70
|
+
"untrusted-input-boundaries": "untrusted-input-boundaries";
|
|
71
|
+
"subprocess-platform": "subprocess-platform";
|
|
72
|
+
"concurrency-state": "concurrency-state";
|
|
73
|
+
"dependencies-build-release": "dependencies-build-release";
|
|
74
|
+
"api-schema-migrations": "api-schema-migrations";
|
|
75
|
+
"test-infrastructure": "test-infrastructure";
|
|
76
|
+
"ui-accessibility-i18n": "ui-accessibility-i18n";
|
|
77
|
+
"privacy-observability": "privacy-observability";
|
|
78
|
+
"generated-provenance": "generated-provenance";
|
|
79
|
+
"unclassified-risk": "unclassified-risk";
|
|
80
|
+
}>;
|
|
81
|
+
result: z.ZodLiteral<"NOT_TRIGGERED">;
|
|
82
|
+
evidence: z.ZodString;
|
|
83
|
+
}, z.core.$strict>], "result">>>;
|
|
56
84
|
feedback_inventory: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
57
85
|
}, z.core.$strip>;
|
|
58
86
|
declare const CollectLaneResultsArgsSchema: z.ZodObject<{
|
|
@@ -297,11 +325,39 @@ export declare const _test_exports: {
|
|
|
297
325
|
base_sha: z.ZodOptional<z.ZodString>;
|
|
298
326
|
base_ref: z.ZodOptional<z.ZodString>;
|
|
299
327
|
scope: z.ZodOptional<z.ZodString>;
|
|
300
|
-
trigger_evaluation: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
301
|
-
trigger_id: z.
|
|
328
|
+
trigger_evaluation: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
329
|
+
trigger_id: z.ZodEnum<{
|
|
330
|
+
"auth-identity-secrets": "auth-identity-secrets";
|
|
331
|
+
"untrusted-input-boundaries": "untrusted-input-boundaries";
|
|
332
|
+
"subprocess-platform": "subprocess-platform";
|
|
333
|
+
"concurrency-state": "concurrency-state";
|
|
334
|
+
"dependencies-build-release": "dependencies-build-release";
|
|
335
|
+
"api-schema-migrations": "api-schema-migrations";
|
|
336
|
+
"test-infrastructure": "test-infrastructure";
|
|
337
|
+
"ui-accessibility-i18n": "ui-accessibility-i18n";
|
|
338
|
+
"privacy-observability": "privacy-observability";
|
|
339
|
+
"generated-provenance": "generated-provenance";
|
|
340
|
+
"unclassified-risk": "unclassified-risk";
|
|
341
|
+
}>;
|
|
302
342
|
result: z.ZodLiteral<"MATCHED">;
|
|
303
343
|
evidence: z.ZodString;
|
|
304
|
-
}, z.core.$strict
|
|
344
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
345
|
+
trigger_id: z.ZodEnum<{
|
|
346
|
+
"auth-identity-secrets": "auth-identity-secrets";
|
|
347
|
+
"untrusted-input-boundaries": "untrusted-input-boundaries";
|
|
348
|
+
"subprocess-platform": "subprocess-platform";
|
|
349
|
+
"concurrency-state": "concurrency-state";
|
|
350
|
+
"dependencies-build-release": "dependencies-build-release";
|
|
351
|
+
"api-schema-migrations": "api-schema-migrations";
|
|
352
|
+
"test-infrastructure": "test-infrastructure";
|
|
353
|
+
"ui-accessibility-i18n": "ui-accessibility-i18n";
|
|
354
|
+
"privacy-observability": "privacy-observability";
|
|
355
|
+
"generated-provenance": "generated-provenance";
|
|
356
|
+
"unclassified-risk": "unclassified-risk";
|
|
357
|
+
}>;
|
|
358
|
+
result: z.ZodLiteral<"NOT_TRIGGERED">;
|
|
359
|
+
evidence: z.ZodString;
|
|
360
|
+
}, z.core.$strict>], "result">>>;
|
|
305
361
|
feedback_inventory: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
306
362
|
}, z.core.$strip>;
|
|
307
363
|
DEFAULT_TIMEOUT_MS: number;
|
|
@@ -363,7 +419,9 @@ type ApplyCommonPromptResult = {
|
|
|
363
419
|
* prompts when it is. Callers may treat the returned array as their own.
|
|
364
420
|
*/
|
|
365
421
|
declare function applyCommonPrompt(lanes: DispatchLaneSpec[], commonPrompt: string | undefined): ApplyCommonPromptResult;
|
|
366
|
-
declare function applyExplorerFormatSuffix(lanes: DispatchLaneSpec[]
|
|
422
|
+
declare function applyExplorerFormatSuffix(lanes: DispatchLaneSpec[], options?: {
|
|
423
|
+
failClosed?: boolean;
|
|
424
|
+
}): ApplyCommonPromptResult;
|
|
367
425
|
declare function applyPrWorkflowPromptContract(lanes: DispatchLaneSpec[], options: {
|
|
368
426
|
mode?: string;
|
|
369
427
|
prHeadSha?: string;
|
|
@@ -34,6 +34,24 @@ export declare const _internals: {
|
|
|
34
34
|
computeUsedSymbols: typeof computeUsedSymbols;
|
|
35
35
|
extractFileSymbols: typeof extractFileSymbols;
|
|
36
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* Whether `p` is a scannable source file — i.e. one of the extensions the
|
|
39
|
+
* walker turns into a graph node. This is the SINGLE source of truth for "is
|
|
40
|
+
* this a graph-node-able file" (issue #1985, defect A2): the write hook and
|
|
41
|
+
* the incremental updater both route through it so the allowlist can never
|
|
42
|
+
* drift from the builder's `LANGUAGE_REGISTRY`-derived set.
|
|
43
|
+
*/
|
|
44
|
+
export declare function isScannableSourcePath(p: string): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Whether an edge's resolved target is an asset (a real file that never
|
|
47
|
+
* becomes a graph node, e.g. JSON/CSS) rather than a node. Uses the explicit
|
|
48
|
+
* `targetKind` tag on schema >= 1.3.0 graphs and falls back to an extension
|
|
49
|
+
* check on older graphs whose edges are untagged (issue #1985, defect A1).
|
|
50
|
+
* Asset edges only require their source node to exist during incremental
|
|
51
|
+
* validation and are excluded from in-degree ranking / importer / dependent
|
|
52
|
+
* queries.
|
|
53
|
+
*/
|
|
54
|
+
export declare function isAssetEdge(edge: GraphEdge): boolean;
|
|
37
55
|
/**
|
|
38
56
|
* Add or update a node in the graph.
|
|
39
57
|
* @param graph - The graph to modify
|
|
@@ -156,7 +174,7 @@ export interface AsyncScanResult {
|
|
|
156
174
|
* @param maxFileSize - Maximum file size in bytes
|
|
157
175
|
* @returns ScanResult with node and edges
|
|
158
176
|
*/
|
|
159
|
-
export declare function scanFile(filePath: string, absoluteRoot: string, maxFileSize: number): ScanResult;
|
|
177
|
+
export declare function scanFile(filePath: string, absoluteRoot: string, maxFileSize: number, hasManifest?: (relDir: string) => boolean): ScanResult;
|
|
160
178
|
/**
|
|
161
179
|
* Async variant of scanFile that uses tree-sitter symbol extraction
|
|
162
180
|
* (extractFileSymbols) to populate exportRanges and symbolEdges.
|
|
@@ -173,7 +191,7 @@ export declare function scanFile(filePath: string, absoluteRoot: string, maxFile
|
|
|
173
191
|
* @param maxFileSize - Maximum file size in bytes
|
|
174
192
|
* @returns AsyncScanResult with node, edges, and symbolEdges
|
|
175
193
|
*/
|
|
176
|
-
export declare function scanFileAsync(filePath: string, absoluteRoot: string, maxFileSize: number): Promise<AsyncScanResult>;
|
|
194
|
+
export declare function scanFileAsync(filePath: string, absoluteRoot: string, maxFileSize: number, hasManifest?: (relDir: string) => boolean): Promise<AsyncScanResult>;
|
|
177
195
|
/**
|
|
178
196
|
* Build a complete dependency graph for a workspace by scanning all source files.
|
|
179
197
|
*
|
|
@@ -5,8 +5,25 @@
|
|
|
5
5
|
* their nodes and edges in the existing graph, and saves the result. It
|
|
6
6
|
* includes an optimistic concurrency check (mtime comparison) so that
|
|
7
7
|
* concurrent sessions do not overwrite each other's updates — when a race
|
|
8
|
-
* is detected the function
|
|
8
|
+
* is detected the function reloads the freshest on-disk graph and replays
|
|
9
|
+
* the update once before falling back to a full rebuild.
|
|
10
|
+
*
|
|
11
|
+
* Asset edges (issue #1985, defect A1): an edge whose target is a real file
|
|
12
|
+
* that never becomes a graph node (e.g. `import data from './data.json'`) is
|
|
13
|
+
* tagged `targetKind: 'asset'`. Such edges only require their SOURCE node to
|
|
14
|
+
* exist during validation, so a single asset import no longer forces a full
|
|
15
|
+
* rebuild on every incremental update.
|
|
16
|
+
*/
|
|
17
|
+
import { type Stats } from 'node:fs';
|
|
18
|
+
/**
|
|
19
|
+
* DI seam for the optimistic-concurrency stat check (defect A4). Tests override
|
|
20
|
+
* `stat` to inject deterministic mtime shifts between load and the pre-save
|
|
21
|
+
* re-check, exercising the reload-and-replay branch without races. Defaults to
|
|
22
|
+
* the real `fsPromises.stat`. Restore in afterEach.
|
|
9
23
|
*/
|
|
24
|
+
export declare const _internals: {
|
|
25
|
+
stat: (path: string) => Promise<Stats>;
|
|
26
|
+
};
|
|
10
27
|
import type { RepoGraph } from './types';
|
|
11
28
|
/**
|
|
12
29
|
* Incrementally update the graph for a set of changed files.
|
|
@@ -6,5 +6,13 @@ export interface ExtractFileOntologyInput {
|
|
|
6
6
|
language: string;
|
|
7
7
|
exports: string[];
|
|
8
8
|
imports: string[];
|
|
9
|
+
/**
|
|
10
|
+
* Optional callback returning true when a workspace-relative directory
|
|
11
|
+
* contains a package manifest (`package.json`, `Cargo.toml`,
|
|
12
|
+
* `pyproject.toml`, `go.mod`). When provided, the package-boundary rule
|
|
13
|
+
* becomes manifest-driven; otherwise it falls back to the static segment
|
|
14
|
+
* rules (issue #1985, defect A8). The extractor stays pure — no fs I/O.
|
|
15
|
+
*/
|
|
16
|
+
hasManifest?: (relDir: string) => boolean;
|
|
9
17
|
}
|
|
10
18
|
export declare function extractFileOntology(input: ExtractFileOntologyInput): FileOntology;
|
|
@@ -23,11 +23,20 @@ export declare const REPO_GRAPH_FILENAME = "repo-graph.json";
|
|
|
23
23
|
* still load without corruption. New queries may use these fields to provide
|
|
24
24
|
* more precise context-packing and symbol-level navigation.
|
|
25
25
|
*
|
|
26
|
+
* 1.3.0 adds the optional per-edge `targetKind` field (`'node' | 'asset'`)
|
|
27
|
+
* that distinguishes edges whose resolved target is a scannable source file
|
|
28
|
+
* (a graph node) from edges whose target is an asset (JSON/CSS/etc. — a real
|
|
29
|
+
* file that never becomes a node). The field is optional, so 1.0.0–1.2.0
|
|
30
|
+
* graphs still load; `storage.ts` only checks that a version string is
|
|
31
|
+
* present, and feature gating is per-query via {@link isSchemaVersionAtLeast}.
|
|
32
|
+
* For pre-1.3.0 graphs the loader/queries fall back to an extension check
|
|
33
|
+
* (`isScannableSourcePath`) to classify an untagged edge's target kind.
|
|
34
|
+
*
|
|
26
35
|
* Diagnostics are additive and optional on all schema versions. Old graphs
|
|
27
36
|
* without diagnostics remain readable; graph-health queries surface empty
|
|
28
37
|
* diagnostics with an explicit rebuild note.
|
|
29
38
|
*/
|
|
30
|
-
export declare const GRAPH_SCHEMA_VERSION = "1.
|
|
39
|
+
export declare const GRAPH_SCHEMA_VERSION = "1.3.0";
|
|
31
40
|
/**
|
|
32
41
|
* Compare dotted numeric version strings (e.g. '1.1.0' >= '1.1.0').
|
|
33
42
|
* Missing/non-numeric segments are treated as 0. Returns true when `version`
|
|
@@ -150,6 +159,16 @@ export interface GraphEdge {
|
|
|
150
159
|
* imports, where individual symbol usage is not statically resolvable.
|
|
151
160
|
*/
|
|
152
161
|
usedSymbols?: string[];
|
|
162
|
+
/**
|
|
163
|
+
* Whether the resolved target is a graph node or an asset. `'node'` targets
|
|
164
|
+
* are scannable source files that become graph nodes; `'asset'` targets are
|
|
165
|
+
* real files (JSON/CSS/etc.) that never become nodes (schema >= 1.3.0).
|
|
166
|
+
* Absent on older graphs — callers fall back to `isScannableSourcePath` on
|
|
167
|
+
* the target path. Asset edges only require their source node to exist
|
|
168
|
+
* during incremental validation, and are excluded from in-degree ranking /
|
|
169
|
+
* importer / dependent queries.
|
|
170
|
+
*/
|
|
171
|
+
targetKind?: 'node' | 'asset';
|
|
153
172
|
}
|
|
154
173
|
export interface FileReference {
|
|
155
174
|
file: string;
|
|
@@ -269,6 +288,20 @@ export interface RepoGraphDiagnostics {
|
|
|
269
288
|
binaryFiles?: string[];
|
|
270
289
|
unreadableFiles?: string[];
|
|
271
290
|
lowConfidenceEdgeCount?: number;
|
|
291
|
+
/**
|
|
292
|
+
* True when the last workspace walk was truncated by the file cap or wall-
|
|
293
|
+
* clock budget (issue #1985, defect A7). Additive/optional on all schema
|
|
294
|
+
* versions — old graphs without it read as `false`.
|
|
295
|
+
*/
|
|
296
|
+
walkTruncated?: boolean;
|
|
297
|
+
/** Why the walk truncated: hit the wall-clock `'budget'` or the `'cap'`. */
|
|
298
|
+
walkTruncationReason?: 'budget' | 'cap';
|
|
299
|
+
/**
|
|
300
|
+
* Count of incremental-update passes that fell back to a full rebuild
|
|
301
|
+
* (issue #1985, defect A1). Accumulated across incremental runs; reset on
|
|
302
|
+
* a fresh full build.
|
|
303
|
+
*/
|
|
304
|
+
incrementalFallbacks?: number;
|
|
272
305
|
}
|
|
273
306
|
export interface GraphHealthResult {
|
|
274
307
|
schemaVersion: string | null;
|
|
@@ -281,6 +314,9 @@ export interface GraphHealthResult {
|
|
|
281
314
|
binaryFiles: string[];
|
|
282
315
|
unreadableFiles: string[];
|
|
283
316
|
lowConfidenceEdgeCount: number;
|
|
317
|
+
walkTruncated: boolean;
|
|
318
|
+
walkTruncationReason: 'budget' | 'cap' | null;
|
|
319
|
+
incrementalFallbacks: number;
|
|
284
320
|
notes: string[];
|
|
285
321
|
}
|
|
286
322
|
export interface BlastRadiusResult {
|
|
@@ -359,6 +395,31 @@ export interface BuildWorkspaceGraphOptions {
|
|
|
359
395
|
* This ensures the same file produces the same key on Windows, macOS, and Linux.
|
|
360
396
|
*/
|
|
361
397
|
export declare function normalizeGraphPath(filePath: string): string;
|
|
398
|
+
/**
|
|
399
|
+
* Infer the package boundary for a module name using the generic rule shared
|
|
400
|
+
* by ontology extraction and the query-side no-ontology fallback (issue #1985,
|
|
401
|
+
* defect A8). Pure / dependency-free / no fs I/O so it can live in this
|
|
402
|
+
* foundation module and stay callable from both `ontology.ts` and `query.ts`.
|
|
403
|
+
*
|
|
404
|
+
* Rules (in order):
|
|
405
|
+
* - empty → `'.'`
|
|
406
|
+
* - segment 0 ∈ {packages, crates, apps, libs, services} with ≥2 segments → `<seg0>/<seg1>`
|
|
407
|
+
* - segment 0 === `src` with ≥2 segments → `src/<seg1>`
|
|
408
|
+
* - segment 0 === `tests` with ≥2 segments → `tests/<seg1>`
|
|
409
|
+
* - when `hasManifest` is provided and either `<seg0>` or `<seg0>/<seg1>` is a
|
|
410
|
+
* manifest-bearing directory (with ≥2 segments) → `<seg0>/<seg1>`
|
|
411
|
+
* - otherwise → segment 0 (or `'.'` when there are no segments)
|
|
412
|
+
*
|
|
413
|
+
* The previous `src/tools/repo-graph` special case is intentionally removed:
|
|
414
|
+
* user repos should not inherit this project's internal layout.
|
|
415
|
+
*
|
|
416
|
+
* @param moduleName - Workspace-relative module name (forward slashes).
|
|
417
|
+
* @param hasManifest - Optional callback returning true when a workspace-
|
|
418
|
+
* relative directory contains a package manifest (`package.json`,
|
|
419
|
+
* `Cargo.toml`, `pyproject.toml`, `go.mod`). When omitted, only the static
|
|
420
|
+
* segment rules apply.
|
|
421
|
+
*/
|
|
422
|
+
export declare function inferPackageBoundary(moduleName: string, hasManifest?: (relDir: string) => boolean): string;
|
|
362
423
|
/**
|
|
363
424
|
* Create an empty graph for a workspace.
|
|
364
425
|
* @param workspaceRoot - The workspace root directory
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* All existing imports of this module continue to work unchanged.
|
|
16
16
|
*/
|
|
17
17
|
export type { ScanResult } from './repo-graph/builder';
|
|
18
|
-
export { addEdge, buildWorkspaceGraph, buildWorkspaceGraphAsync, resolveModuleSpecifier, upsertNode, } from './repo-graph/builder';
|
|
18
|
+
export { addEdge, buildWorkspaceGraph, buildWorkspaceGraphAsync, isScannableSourcePath, resolveModuleSpecifier, upsertNode, } from './repo-graph/builder';
|
|
19
19
|
export { clearCache, getCachedGraph, getCachedMtime, isDirty, markDirty, setCachedGraph, } from './repo-graph/cache';
|
|
20
20
|
export { updateGraphForFiles } from './repo-graph/incremental';
|
|
21
21
|
export type { ExtractFileOntologyInput } from './repo-graph/ontology';
|
|
@@ -22,10 +22,10 @@ export declare const ArgsSchema: z.ZodObject<{
|
|
|
22
22
|
confidence: z.ZodNumber;
|
|
23
23
|
findings: z.ZodArray<z.ZodObject<{
|
|
24
24
|
severity: z.ZodEnum<{
|
|
25
|
-
CRITICAL: "CRITICAL";
|
|
26
|
-
HIGH: "HIGH";
|
|
27
|
-
MEDIUM: "MEDIUM";
|
|
28
25
|
LOW: "LOW";
|
|
26
|
+
MEDIUM: "MEDIUM";
|
|
27
|
+
HIGH: "HIGH";
|
|
28
|
+
CRITICAL: "CRITICAL";
|
|
29
29
|
}>;
|
|
30
30
|
category: z.ZodString;
|
|
31
31
|
location: z.ZodString;
|
|
@@ -28,10 +28,10 @@ export declare const ArgsSchema: z.ZodObject<{
|
|
|
28
28
|
confidence: z.ZodNumber;
|
|
29
29
|
findings: z.ZodArray<z.ZodObject<{
|
|
30
30
|
severity: z.ZodEnum<{
|
|
31
|
-
CRITICAL: "CRITICAL";
|
|
32
|
-
HIGH: "HIGH";
|
|
33
|
-
MEDIUM: "MEDIUM";
|
|
34
31
|
LOW: "LOW";
|
|
32
|
+
MEDIUM: "MEDIUM";
|
|
33
|
+
HIGH: "HIGH";
|
|
34
|
+
CRITICAL: "CRITICAL";
|
|
35
35
|
}>;
|
|
36
36
|
category: z.ZodString;
|
|
37
37
|
location: z.ZodString;
|