opencode-swarm 7.154.2 → 7.155.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 (79) hide show
  1. package/dist/cli/{coder-settlement-xqs49g07.js → coder-settlement-2kdtysrg.js} +7 -7
  2. package/dist/cli/{config-doctor-69eebk90.js → config-doctor-s6p9aqfv.js} +2 -2
  3. package/dist/cli/{core-dyp14hcb.js → core-ev2ebc4v.js} +1 -1
  4. package/dist/cli/{curation-policy-jm9jsxtt.js → curation-policy-jabphe51.js} +4 -4
  5. package/dist/cli/{curator-he8n9z9n.js → curator-adp0dsv4.js} +28 -28
  6. package/dist/cli/{curator-drift-h57z6tm3.js → curator-drift-g6w4ga1q.js} +1 -1
  7. package/dist/cli/{curator-llm-factory-5tq9c4v5.js → curator-llm-factory-kd8v16jj.js} +28 -28
  8. package/dist/cli/{evidence-summary-service-05s5y8d6.js → evidence-summary-service-kpjpd86j.js} +10 -10
  9. package/dist/cli/{gate-evidence-3ptr9af3.js → gate-evidence-etbcms2v.js} +5 -5
  10. package/dist/cli/{guardrail-explain-tebk344k.js → guardrail-explain-wqb5h7kk.js} +29 -29
  11. package/dist/cli/{guardrail-log-x3gz3x17.js → guardrail-log-614g1pta.js} +3 -3
  12. package/dist/cli/{guardrail-reset-716pdyf5.js → guardrail-reset-0wdj53cp.js} +28 -28
  13. package/dist/cli/{hive-promoter-nvwpj2t9.js → hive-promoter-b1wv52fs.js} +28 -28
  14. package/dist/cli/{index-4xqa0sgz.js → index-0q8kywcf.js} +2 -2
  15. package/dist/cli/{index-8v69hfpt.js → index-26cvxrym.js} +3 -3
  16. package/dist/cli/{index-bxvkn0ye.js → index-2gfe61qp.js} +345 -221
  17. package/dist/cli/{index-57t97rtr.js → index-34wn2ztk.js} +5 -2
  18. package/dist/cli/{index-kemxyzc0.js → index-6bqqzfbe.js} +2 -2
  19. package/dist/cli/{index-dm9tc6qb.js → index-79qymsxf.js} +1 -1
  20. package/dist/cli/{index-r0dactw2.js → index-85p1850w.js} +1 -1
  21. package/dist/cli/{index-14bz2t7b.js → index-aqg7c1z9.js} +1 -1
  22. package/dist/cli/{index-xq2v52tt.js → index-c590qxts.js} +1 -1
  23. package/dist/cli/{index-fv9ddfta.js → index-c9nabqzd.js} +1 -1
  24. package/dist/cli/{index-nh4axsxc.js → index-caqe8x14.js} +2 -2
  25. package/dist/cli/{index-n0gqre56.js → index-ctsct3hy.js} +6 -6
  26. package/dist/cli/{index-4cgee5vh.js → index-dtn0pne5.js} +20 -1
  27. package/dist/cli/{index-ynvw5kxc.js → index-dw2q720x.js} +2 -2
  28. package/dist/cli/{index-xt6jx41g.js → index-e90g4dsw.js} +4 -4
  29. package/dist/cli/{index-9avdncr2.js → index-egnpzfe3.js} +5 -5
  30. package/dist/cli/{index-j6q3k628.js → index-ew6fykvv.js} +1 -1
  31. package/dist/cli/{index-6rns7b2m.js → index-fjcm6wwf.js} +2 -2
  32. package/dist/cli/{index-mnxtkt8s.js → index-ketfp4rv.js} +3 -3
  33. package/dist/cli/{index-f6qmar4k.js → index-kgdbtwfm.js} +5 -5
  34. package/dist/cli/{index-399e8aze.js → index-kyc2c7hq.js} +1 -1
  35. package/dist/cli/{index-07n87ctw.js → index-mm7ase9k.js} +1 -1
  36. package/dist/cli/{index-p7ey2w90.js → index-qj9jrjvr.js} +6 -6
  37. package/dist/cli/{index-v2ryvr8k.js → index-qzqpq75z.js} +30 -30
  38. package/dist/cli/{index-v9585wjc.js → index-rb4t72we.js} +2 -2
  39. package/dist/cli/{index-2w6vy9a9.js → index-spbxf9vn.js} +1 -1
  40. package/dist/cli/{index-6qwp46k0.js → index-t3xqpece.js} +3 -3
  41. package/dist/cli/{index-pj2nycpg.js → index-veajr5x7.js} +1 -1
  42. package/dist/cli/{index-vhhdj49x.js → index-wwv3cbv1.js} +8 -9
  43. package/dist/cli/{index-7tw116n6.js → index-x9tbe9v7.js} +2 -2
  44. package/dist/cli/{index-6rcx6vr6.js → index-xn23xnnk.js} +4 -4
  45. package/dist/cli/{index-dsqat5pg.js → index-zvp68qx6.js} +1 -1
  46. package/dist/cli/index.js +28 -28
  47. package/dist/cli/{knowledge-escalator-s0tye368.js → knowledge-escalator-z09aqvfe.js} +10 -10
  48. package/dist/cli/{knowledge-events-eyy9yf6b.js → knowledge-events-03f1rwm8.js} +8 -8
  49. package/dist/cli/{knowledge-link-rb11f06y.js → knowledge-link-4frk38w8.js} +3 -3
  50. package/dist/cli/{knowledge-store-zjq5mz2d.js → knowledge-store-sn28k13f.js} +4 -4
  51. package/dist/cli/{knowledge-validator-yw5qqqpd.js → knowledge-validator-rwrt3yja.js} +6 -6
  52. package/dist/cli/{pending-delegations-rdzjq3nw.js → pending-delegations-qe16vfrj.js} +2 -2
  53. package/dist/cli/{pr-subscriptions-2f5fne6z.js → pr-subscriptions-00evnffe.js} +3 -3
  54. package/dist/cli/{runner-yaqrj388.js → runner-k1rkbjm8.js} +4 -4
  55. package/dist/cli/{scan-cursor-8f0s883y.js → scan-cursor-h3mfzjc9.js} +5 -5
  56. package/dist/cli/{schema-2xw94dh3.js → schema-c37h5xw4.js} +1 -1
  57. package/dist/cli/{scope-persistence-tap2q94x.js → scope-persistence-35z42pj5.js} +5 -5
  58. package/dist/cli/{skill-generator-2xbfryqb.js → skill-generator-60px2mvb.js} +12 -12
  59. package/dist/cli/{worktree-collision-ownership-5gwey8ms.js → worktree-collision-ownership-2pc20ypq.js} +4 -4
  60. package/dist/cli/{worktree-isolation-xsy14t5a.js → worktree-isolation-w4yamc3b.js} +28 -28
  61. package/dist/commands/close.d.ts +2 -0
  62. package/dist/config/schema.d.ts +8 -0
  63. package/dist/hooks/final-context-accounting.d.ts +60 -0
  64. package/dist/hooks/index.d.ts +1 -0
  65. package/dist/hooks/knowledge-injector.d.ts +1 -1
  66. package/dist/hooks/repo-graph-injection.d.ts +40 -0
  67. package/dist/hooks/system-enhancer.d.ts +13 -0
  68. package/dist/hooks/utils.d.ts +11 -0
  69. package/dist/index.js +414 -389
  70. package/dist/memory/reflection-service.d.ts +8 -2
  71. package/dist/memory/schema.d.ts +4 -4
  72. package/dist/services/compaction-service.d.ts +9 -3
  73. package/dist/services/context-budget-service.d.ts +7 -3
  74. package/dist/services/injection-budget.d.ts +103 -26
  75. package/dist/state.d.ts +69 -0
  76. package/dist/summaries/summarizer.d.ts +34 -3
  77. package/dist/tools/repo-graph/indexed-storage.d.ts +136 -0
  78. package/dist/tools/repo-graph/query.d.ts +7 -0
  79. package/package.json +3 -1
@@ -1,7 +1,9 @@
1
+ import { queryNodeByFile, resolveGraphStorageMode } from '../tools/repo-graph/indexed-storage';
2
+ import type { RepoGraph } from '../tools/repo-graph/types';
1
3
  import { type MemoryConfig } from './config';
2
4
  import { type MemoryGateway, type RecordMemoryOutcomeInput } from './gateway';
3
- import { type ReflectionDigest } from './reflection';
4
- import type { MemoryOutcome, MemoryRecord } from './types';
5
+ import { type ReflectionAnchorStatus, type ReflectionDigest } from './reflection';
6
+ import type { MemoryAnchor, MemoryOutcome, MemoryRecord } from './types';
5
7
  interface OutcomeWriteThroughBase {
6
8
  record: MemoryRecord;
7
9
  eventId: string;
@@ -49,12 +51,16 @@ export declare const _test_exports: {
49
51
  isReflectionEligible: typeof isReflectionEligible;
50
52
  prepareWriteThroughEntry: typeof prepareWriteThroughEntry;
51
53
  sanitizeReflectionRecord: typeof sanitizeReflectionRecord;
54
+ createAnchorResolver: typeof createAnchorResolver;
52
55
  };
53
56
  export declare const _internals: {
54
57
  withReflectionLock: typeof withReflectionLock;
55
58
  serializeDigestArtifacts: typeof serializeDigestArtifacts;
56
59
  boundDigest: typeof boundDigest;
60
+ resolveGraphStorageMode: typeof resolveGraphStorageMode;
61
+ queryNodeByFile: typeof queryNodeByFile;
57
62
  };
63
+ declare function createAnchorResolver(directory: string, graph: RepoGraph | null): (anchor: MemoryAnchor) => ReflectionAnchorStatus;
58
64
  declare function withReflectionLock<T>(directory: string, fn: () => Promise<T>): Promise<T>;
59
65
  interface ReflectionArtifactPair {
60
66
  markdown: string;
@@ -10,8 +10,8 @@ export declare const MemoryScopeTypeSchema: z.ZodEnum<{
10
10
  project: "project";
11
11
  workspace: "workspace";
12
12
  run: "run";
13
- global_user: "global_user";
14
13
  repository: "repository";
14
+ global_user: "global_user";
15
15
  cohort: "cohort";
16
16
  }>;
17
17
  export declare const MemoryScopeRefSchema: z.ZodObject<{
@@ -20,8 +20,8 @@ export declare const MemoryScopeRefSchema: z.ZodObject<{
20
20
  project: "project";
21
21
  workspace: "workspace";
22
22
  run: "run";
23
- global_user: "global_user";
24
23
  repository: "repository";
24
+ global_user: "global_user";
25
25
  cohort: "cohort";
26
26
  }>;
27
27
  userId: z.ZodOptional<z.ZodString>;
@@ -77,8 +77,8 @@ export declare const MemoryRecordSchema: z.ZodObject<{
77
77
  project: "project";
78
78
  workspace: "workspace";
79
79
  run: "run";
80
- global_user: "global_user";
81
80
  repository: "repository";
81
+ global_user: "global_user";
82
82
  cohort: "cohort";
83
83
  }>;
84
84
  userId: z.ZodOptional<z.ZodString>;
@@ -171,8 +171,8 @@ export declare const MemoryProposalSchema: z.ZodObject<{
171
171
  project: "project";
172
172
  workspace: "workspace";
173
173
  run: "run";
174
- global_user: "global_user";
175
174
  repository: "repository";
175
+ global_user: "global_user";
176
176
  cohort: "cohort";
177
177
  }>;
178
178
  userId: z.ZodOptional<z.ZodString>;
@@ -7,9 +7,15 @@
7
7
  * - Reflection (default 60%): re-summarise into tighter format
8
8
  * - Emergency (default 80%): hard truncation to system + current task + last N turns
9
9
  *
10
- * Consumes the per-session budget pct recorded by system-enhancer.ts after
11
- * each budget calc (`getSessionBudgetPct`).
12
- * Never throws. Advisory system message injection via callback.
10
+ * Consumes the per-session FINAL PROMPT PRESSURE recorded by the final
11
+ * context-accounting step (#2107 §3) after every injector has run
12
+ * (`getFinalPromptPressure`); before that step has run for a session it falls
13
+ * back to the legacy injection-footprint pct (`getSessionBudgetPct`) so the
14
+ * tiers never go dark.
15
+ * Never throws. Advisory system message injection via callback. These tiers
16
+ * ADVISE the model to compact — they never compact anything themselves and
17
+ * never claim a compaction was performed (only the physical pruning in
18
+ * src/hooks/context-budget.ts actually removes content).
13
19
  */
14
20
  import type { CompactionConfig } from '../config/schema';
15
21
  export type { CompactionConfig };
@@ -74,10 +74,14 @@ export interface BudgetState {
74
74
  */
75
75
  export declare const DEFAULT_CONTEXT_BUDGET_CONFIG: ContextBudgetConfig;
76
76
  /**
77
- * Estimate token count for text using character-based approximation
77
+ * Estimate token count for text using the canonical character-based heuristic.
78
78
  *
79
- * @param text - The text to estimate tokens for
80
- * @returns Estimated token count (ceiling of chars / 3.5)
79
+ * Delegates to `estimateTokens` in src/hooks/utils.ts (issue #1616/#2107). This
80
+ * used to be an independent ÷3.5 formula, which made the user-facing budget
81
+ * report disagree ~15% with the injection-admission path (0.33 tok/char) on
82
+ * the same text. The export is kept for existing importers (capsule-builder,
83
+ * the services barrel); it is a heuristic — provider-reported token usage is
84
+ * authoritative when available.
81
85
  */
82
86
  export declare function estimateTokens(text: string): number;
83
87
  /**
@@ -1,8 +1,10 @@
1
1
  /**
2
2
  * Unified Injection Budget Service (FR-002).
3
3
  *
4
- * Pure, side-effect-free allocation function for the combined
5
- * system-enhancer + knowledge-injector injection ceiling.
4
+ * Two responsibilities: (1) the pure, side-effect-free FR-002 allocation
5
+ * function for the combined system-enhancer + knowledge-injector injection
6
+ * ceiling, and (2) the per-session/per-turn producer ledger (#2107 §2) that
7
+ * every model-visible producer claims from or records emissions into.
6
8
  *
7
9
  * Allocation strategy: proportional share.
8
10
  * - When combined demand fits within the budget, each component receives its
@@ -20,8 +22,8 @@
20
22
  * Priority-based allocation would require an arbitrary component ranking
21
23
  * not specified by the acceptance criteria.
22
24
  *
23
- * Char-to-token conversion uses the project's existing 0.33 tok/char ratio
24
- * (matches `estimateTokens` in src/hooks/utils.ts:200).
25
+ * Char-to-token conversion goes through the canonical estimator
26
+ * (`estimateTokensFromCharCount` in src/hooks/utils.ts — issue #1616/#2107).
25
27
  */
26
28
  /**
27
29
  * Allocation result for a single turn's unified injection budget.
@@ -59,40 +61,115 @@ export interface InjectionBudgetConfig {
59
61
  */
60
62
  export declare function allocateInjectionBudget(systemEnhancerDemandTokens: number, knowledgeInjectorDemandChars: number, config: InjectionBudgetConfig): InjectionBudgetAllocation;
61
63
  /**
62
- * Reset the budget for a session to the full unified ceiling.
63
- * Called by the first component to run for a given turn.
64
+ * Producers that contribute to the model-visible request surface. The
65
+ * `surface` on each accounting entry records WHERE the producer's bytes live:
66
+ * `'system'` entries are pushed to `output.system` in the system.transform
67
+ * chain (invisible to the messages chain and to final accounting's direct
68
+ * measurement of `output.messages` — they MUST be added to the final total via
69
+ * this ledger); `'messages'` entries are spliced into `output.messages` and are
70
+ * therefore already inside the final measurement (attribution only — adding
71
+ * them again would double-count).
64
72
  */
65
- export declare function resetUnifiedBudget(sessionID: string, totalBudget: number): void;
73
+ export type InjectionProducer = 'system-enhancer' | 'knowledge-injector' | 'context-capsule' | 'memory-recall' | 'advisory-queue' | 'swarm-command-banner' | 'linked-cohort-advisory' | 'spec-drift-advisory' | 'final-accounting-warning';
74
+ export type InjectionSurface = 'system' | 'messages';
75
+ export interface ProducerAccounting {
76
+ /** Tokens the producer asked the ledger for this turn (claims only). */
77
+ requested: number;
78
+ /** Tokens the ledger granted under the ceiling + local max (claims only). */
79
+ granted: number;
80
+ /** Tokens actually emitted to the model-visible surface. */
81
+ emitted: number;
82
+ /** Tokens the producer wanted but did not emit (its own pruning). */
83
+ truncated: number;
84
+ surface: InjectionSurface;
85
+ }
86
+ export interface TurnLedgerSummary {
87
+ generation: number;
88
+ totalBudget: number;
89
+ /** Ceiling enforcement is only active when `unified_injection_tokens` is configured. */
90
+ ceilingActive: boolean;
91
+ used: number;
92
+ producers: Array<{
93
+ producer: InjectionProducer;
94
+ } & ProducerAccounting>;
95
+ }
96
+ /**
97
+ * Begin a new turn ledger for a session: reset exactly once at the start of
98
+ * composing that request (the system-enhancer is the first producer and calls
99
+ * this). Mints a fresh generation, so any stale claim from a prior composition
100
+ * is discarded. `ceilingActive` is only true when
101
+ * `context_budget.unified_injection_tokens` is configured; when false the
102
+ * ledger records accounting but never denies a claim (default configs keep
103
+ * their pre-#2107 behavior).
104
+ */
105
+ export declare function beginTurnLedger(sessionID: string, totalBudget: number, ceilingActive: boolean): number;
106
+ /**
107
+ * Claim tokens from the turn ledger. The grant is bounded by the producer's
108
+ * own local maximum (`localMaxTokens`) and, only when the ceiling is active,
109
+ * by what remains of the unified ceiling. When NO ledger exists for the session
110
+ * (system-enhancer never ran this turn — native agent, first turn, or hook
111
+ * disabled), the claim fails open to the local maximum and is not recorded,
112
+ * preserving #1617's fail-open contract; the caller is expected to report that
113
+ * the hard ceiling was unavailable.
114
+ */
115
+ export declare function claimTurnBudget(sessionID: string, producer: InjectionProducer, requestedTokens: number, opts?: {
116
+ localMaxTokens?: number;
117
+ surface?: InjectionSurface;
118
+ }): {
119
+ granted: number;
120
+ ledgerPresent: boolean;
121
+ ceilingActive: boolean;
122
+ };
123
+ /**
124
+ * Record a producer's grant without claiming through `claimTurnBudget`.
125
+ *
126
+ * The system-enhancer and knowledge-injector enforce their split through the
127
+ * pure `allocateInjectionBudget` (FR-002 contract, pinned by tests) rather than
128
+ * sequential claims; this books their allocator-derived grants into the shared
129
+ * ceiling so later claimants (capsule, memory recall) draw from what actually
130
+ * remains. When the ceiling is inactive the grant is recorded but deducts
131
+ * nothing. The deduction is clamped to the remaining budget so `used` can never
132
+ * exceed `totalBudget`.
133
+ */
134
+ export declare function recordProducerGrant(sessionID: string, producer: InjectionProducer, requestedTokens: number, grantedTokens: number, surface: InjectionSurface): void;
66
135
  /**
67
- * Return the remaining unified budget for a session.
136
+ * Record what a producer actually emitted (and pruned itself) this turn.
137
+ * Producers that never claim (fixed/base content: advisory queue, banners,
138
+ * the context-budget warning, the final-accounting warning) still record their
139
+ * emission here so the final accounting can include them.
68
140
  */
69
- export declare function getUnifiedBudgetRemaining(sessionID: string): number;
141
+ export declare function recordProducerEmission(sessionID: string, producer: InjectionProducer, emittedTokens: number, truncatedTokens: number, surface: InjectionSurface): void;
70
142
  /**
71
- * Request a slice of the unified budget. Returns the granted token count.
72
- * If the requester's need exceeds the remaining budget, it gets the remainder
73
- * and the other source is implicitly blocked (remaining drops to 0).
143
+ * Snapshot of the session's current turn ledger (null when absent).
74
144
  */
75
- export declare function requestUnifiedBudget(sessionID: string, requestedTokens: number): number;
76
145
  /**
77
- * Return the total unified budget configured for a session (or 0 if unset).
146
+ * Deduct tokens from a producer's recorded emission (#2107 §3): downstream
147
+ * system-chain mutators (the role filter) REMOVE strings after the producer
148
+ * recorded them, and the final accounting must not count bytes the model
149
+ * never sees. Floors at zero; no-op without a ledger or producer entry.
78
150
  */
79
- export declare function getUnifiedBudgetTotal(sessionID: string): number;
151
+ export declare function deductProducerEmission(sessionID: string, producer: InjectionProducer, removedTokens: number): void;
152
+ export declare function getTurnLedgerSummary(sessionID: string): TurnLedgerSummary | null;
80
153
  /**
81
- * Remove a session's budget entry. Used for cleanup in tests.
154
+ * Emitted tokens recorded for one producer this turn (0 when no ledger or the
155
+ * producer has not run). Replaces the old `getSystemEnhancerDemand` relay: the
156
+ * knowledge-injector reads the system-enhancer's actual emission from here.
82
157
  */
83
- export declare function clearUnifiedBudget(sessionID: string): void;
158
+ export declare function getProducerEmission(sessionID: string, producer: InjectionProducer): number;
84
159
  /**
85
- * Ensure a budget entry exists for the session. Creates one with the full
86
- * budget if none exists; leaves an existing entry untouched.
160
+ * Advance the turn generation for a session: the current ledger is discarded so
161
+ * the next request composition starts from a fresh generation. Called when
162
+ * compaction changes the message surface (`experimental.session.compacting`)
163
+ * and at session teardown.
87
164
  */
88
- export declare function ensureSessionBudget(sessionID: string, totalBudget: number): void;
165
+ export declare function advanceTurnGeneration(sessionID: string): void;
89
166
  /**
90
- * Store the system-enhancer's actual per-turn token demand so that
91
- * knowledge-injector can read it and compute its proportional share.
167
+ * Test/maintenance hook: clear one session's ledger.
92
168
  */
93
- export declare function setSystemEnhancerDemand(sessionID: string, demand: number): void;
169
+ export declare function clearTurnLedger(sessionID: string): void;
94
170
  /**
95
- * Return the system-enhancer's actual per-turn token demand for a session.
96
- * Returns 0 if no budget entry exists or demand was not set.
171
+ * Clear every session's ledger. Called from `resetSwarmState` so the
172
+ * plugin-wide reset also resets per-turn producer accounting (zaxbysauce
173
+ * review on PR #2415: the reset pattern must cover ALL module-scoped maps).
97
174
  */
98
- export declare function getSystemEnhancerDemand(sessionID: string): number;
175
+ export declare function clearAllTurnLedgers(): void;
package/dist/state.d.ts CHANGED
@@ -723,6 +723,34 @@ export declare const swarmState: {
723
723
  pct: number;
724
724
  tokens: number;
725
725
  }>;
726
+ /**
727
+ * Final model-visible prompt pressure per session (#2107 §3) — the ONE
728
+ * truthful measurement taken after every injector has run and the system
729
+ * messages have been consolidated. Unlike `lastBudgetBySession` (the swarm
730
+ * INJECTION FOOTPRINT pct measured from an intermediate file surface),
731
+ * this is estimated total prompt tokens vs the same model limit physical
732
+ * pruning uses. Written by the final-context-accounting step; read by the
733
+ * compaction tiers, the CONTEXT PRESSURE advisory, and `/swarm status`.
734
+ * Bounded via {@link setFinalPromptPressure} (AGENTS.md invariant 8).
735
+ */
736
+ finalPromptPressureBySession: Map<string, {
737
+ pct: number;
738
+ usedTokens: number;
739
+ limitTokens: number;
740
+ estimatorSource: string;
741
+ providerReported: boolean;
742
+ }>;
743
+ /**
744
+ * Per-session final-accounting warning-band latch (#2107 §3): one bounded
745
+ * advisory per session per band (warn / critical). Kept here — not in the
746
+ * hook module — so `resetSwarmState` covers it like every other
747
+ * session-keyed map (AGENTS.md invariant 8). Bounded via
748
+ * {@link setFinalAccountingWarningBand}.
749
+ */
750
+ finalAccountingWarningBandsBySession: Map<string, {
751
+ warn: boolean;
752
+ critical: boolean;
753
+ }>;
726
754
  /**
727
755
  * Live `model.limit.context` per session — keyed by sessionID and bound to
728
756
  * the reporting model/provider identity. Recorded by the
@@ -1382,6 +1410,34 @@ export declare function getLiveContextModelIdentity(sessionID: string | undefine
1382
1410
  * written together so `/swarm status` can never pair a pct from one report
1383
1411
  * with a denominator from another. */
1384
1412
  export declare function setSessionBudget(sessionID: string, pct: number, tokens: number): void;
1413
+ /**
1414
+ * Record this session's final prompt pressure (#2107 §3), FIFO-evicting the
1415
+ * oldest entry past {@link MAX_TRACKED_BUDGET_SESSIONS}. Written once per request
1416
+ * composition by the final-context-accounting step.
1417
+ */
1418
+ export declare function setFinalPromptPressure(sessionID: string, snapshot: {
1419
+ pct: number;
1420
+ usedTokens: number;
1421
+ limitTokens: number;
1422
+ estimatorSource: string;
1423
+ providerReported: boolean;
1424
+ }): void;
1425
+ /** The session's final prompt pressure snapshot, or undefined when the final
1426
+ * accounting step has not run for it (e.g. native-agent sessions). */
1427
+ export declare function getFinalPromptPressure(sessionID: string | undefined): {
1428
+ pct: number;
1429
+ usedTokens: number;
1430
+ limitTokens: number;
1431
+ estimatorSource: string;
1432
+ providerReported: boolean;
1433
+ } | undefined;
1434
+ /** Record that this session's #2107 §3 advisory already fired for a band
1435
+ * (one advisory per session per band), FIFO-evicting past the cap. */
1436
+ export declare function setFinalAccountingWarningBand(sessionID: string, band: 'warn' | 'critical'): void;
1437
+ /** Whether this session already received the band's advisory. */
1438
+ export declare function getFinalAccountingWarningBand(sessionID: string | undefined, band: 'warn' | 'critical'): boolean;
1439
+ /** Clear all band latches (used by resetSwarmState and tests). */
1440
+ export declare function clearFinalAccountingWarningBands(): void;
1385
1441
  /** This session's last budget percentage, or 0 if it has not been measured.
1386
1442
  * Consumers that ACT on a session (compaction, the context-pressure advisory)
1387
1443
  * MUST use this rather than any cross-session aggregate. */
@@ -1397,6 +1453,19 @@ export declare function getDisplayBudget(): {
1397
1453
  pct: number;
1398
1454
  tokens: number;
1399
1455
  } | null;
1456
+ /**
1457
+ * The most-pressured session's FINAL prompt pressure snapshot, for
1458
+ * process-wide DISPLAY only (`/swarm status` has no single session in scope).
1459
+ * The snapshot is returned whole so the pct, used tokens, limit, and estimator
1460
+ * source shown together always come from one measurement (#2107 §3).
1461
+ */
1462
+ export declare function getDisplayFinalPromptPressure(): {
1463
+ pct: number;
1464
+ usedTokens: number;
1465
+ limitTokens: number;
1466
+ estimatorSource: string;
1467
+ providerReported: boolean;
1468
+ } | null;
1400
1469
  /** Set the critical shown ids for a session, FIFO-evicting the oldest entry
1401
1470
  * if the cap is exceeded. Re-setting an existing key keeps insertion order
1402
1471
  * fresh for that key (delete-then-set). */
@@ -44,11 +44,40 @@ declare function summarizeJsonArray(parsed: unknown[]): string;
44
44
  */
45
45
  declare function extractCodeSignatures(code: string): string[];
46
46
  /**
47
- * Build a preview for code output that includes declaration signatures.
47
+ * Codepoint-safe truncation to a UTF-8 byte budget (#2107 §5).
48
+ *
49
+ * Iterates code points (never UTF-16 units) so a multibyte CJK/emoji run can
50
+ * never be split mid-codepoint, and the result is deterministic across Bun and
51
+ * Node (which disagree on `Buffer.byteLength` for unpaired surrogates — per
52
+ * code point, both agree). Returns the kept text and the exact number of bytes
53
+ * omitted.
54
+ */
55
+ export declare function truncateToBytes(text: string, maxBytes: number): {
56
+ text: string;
57
+ omittedBytes: number;
58
+ };
59
+ /**
60
+ * Deterministic bounded head/tail preview (#2107 §5).
61
+ *
62
+ * Head: the leading non-blank identity lines (ceil half of the line budget).
63
+ * Tail: the trailing RAW outcome lines (floor half) — compiler, test, lint,
64
+ * and security verdicts live at the END of tool output, so the old head-only
65
+ * preview discarded exactly the decision-relevant evidence. Lines are never
66
+ * reordered within either segment, no outcome is invented, and every dropped
67
+ * region is disclosed with an accurate omitted-line count. When everything
68
+ * fits, the original text is returned unchanged (modulo per-line caps).
69
+ */
70
+ declare function headTailPreview(output: string, maxLines: number): string;
71
+ /**
72
+ * Build a preview for code output that includes declaration signatures plus
73
+ * the bounded head/tail policy (failure output at the end of a code blob is
74
+ * preserved).
48
75
  */
49
76
  declare function summarizeCode(output: string): string;
50
77
  /**
51
- * Build a preview for plain text output (leading non-blank lines).
78
+ * Build a preview for plain text output using the bounded head/tail policy
79
+ * (see headTailPreview). Previously head-only: the first `maxLines` non-blank
80
+ * lines, which silently discarded trailing failure/exit evidence.
52
81
  */
53
82
  declare function summarizeText(output: string, maxLines: number): string;
54
83
  /**
@@ -56,7 +85,7 @@ declare function summarizeText(output: string, maxLines: number): string;
56
85
  * @param output - The full tool output string
57
86
  * @param toolName - The name of the tool that produced the output
58
87
  * @param summaryId - Unique identifier for this summary
59
- * @param maxSummaryChars - Maximum characters allowed for the preview
88
+ * @param maxSummaryChars - Maximum bytes allowed for the preview
60
89
  * @returns Formatted summary string
61
90
  */
62
91
  export declare function createSummary(output: string, toolName: string, summaryId: string, maxSummaryChars: number): string;
@@ -64,6 +93,8 @@ export declare function createSummary(output: string, toolName: string, summaryI
64
93
  * Internal helpers exposed for testability.
65
94
  */
66
95
  export declare const _internals: {
96
+ truncateToBytes: typeof truncateToBytes;
97
+ headTailPreview: typeof headTailPreview;
67
98
  jsonTypeSignature: typeof jsonTypeSignature;
68
99
  summarizeJsonObject: typeof summarizeJsonObject;
69
100
  summarizeJsonArray: typeof summarizeJsonArray;
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Derived SQLite index for the repo dependency graph (issue #1534).
3
+ *
4
+ * `.swarm/repo-graph.json` stays the single authoritative document in BOTH
5
+ * storage modes. When `repo_graph.storage === 'indexed'`, `saveGraph` also
6
+ * maintains `.swarm/repo-memory.sqlite`, a *derived accelerator* whose only job
7
+ * is to answer "give me the neighbourhood of these files" without parsing the
8
+ * whole JSON document. It returns an ordinary `RepoGraph` value that is fed to
9
+ * the existing, unmodified `getGraphNode` / `getLocalizationContext` /
10
+ * `getBlastRadius`, so no query logic is duplicated here.
11
+ *
12
+ * Every read verifies, before returning anything:
13
+ * (a) the persisted `graph_meta.workspace_root` realpath-matches the ACTIVE
14
+ * workspace (same trust boundary as `bindGraphToWorkspace`,
15
+ * `storage.ts:100-127`), and
16
+ * (b) `graph_meta.source_size` / `source_mtime_ms` equal a live stat of
17
+ * `repo-graph.json`.
18
+ * Any mismatch, absence, or error returns null and the caller uses its
19
+ * existing JSON path. The index is never a second source of truth.
20
+ *
21
+ * Runtime portability (AGENTS.md invariant 2): the SQLite driver is resolved
22
+ * ONLY through `src/db/sqlite-loader.ts`. The `import type` below is erased at
23
+ * build time, so this module contributes no `bun:` runtime resolution.
24
+ * `node:sqlite` is stricter than `bun:sqlite` about bound parameters, so every
25
+ * statement's placeholder count equals its argument count exactly and
26
+ * no-placeholder statements are issued as `db.run(sql)` with no array.
27
+ */
28
+ import type { GraphNode, RepoGraph } from './types';
29
+ /** Filename of the derived index inside `.swarm/`. */
30
+ export declare const REPO_MEMORY_FILENAME = "repo-memory.sqlite";
31
+ /**
32
+ * Validated absolute path to `.swarm/repo-memory.sqlite`.
33
+ * Mirrors `getGraphPath`: validateWorkspace → validateSwarmPath →
34
+ * validateSymlinkBoundary.
35
+ */
36
+ export declare function getRepoMemoryPath(workspace: string): string;
37
+ /**
38
+ * Close the cached connection for a workspace. Called by the `/swarm close`
39
+ * clean stage before unlinking the store (Windows holds a WAL-mode file open,
40
+ * so `unlink` would fail with EBUSY — same guard as
41
+ * `closeProjectDb`, `project-db.ts:232-234`) and from test teardown.
42
+ */
43
+ export declare function closeRepoMemory(workspace: string): void;
44
+ /**
45
+ * Close every cached connection. Test-teardown only: `/swarm close` uses the
46
+ * singular `closeRepoMemory` (src/commands/close.ts); this export has no
47
+ * other production caller.
48
+ */
49
+ export declare function closeAllRepoMemory(): void;
50
+ /**
51
+ * Delete the derived store and its WAL sidecars.
52
+ *
53
+ * The cached handle is closed FIRST on every delete path — a live WAL-mode
54
+ * connection keeps the file open and Windows `unlink` fails with EBUSY
55
+ * otherwise.
56
+ */
57
+ export declare function deleteRepoMemory(workspace: string): void;
58
+ /** Whether a SQLite driver is resolvable in this runtime. */
59
+ export declare function isIndexedStorageAvailable(): boolean;
60
+ /**
61
+ * Effective storage mode for a workspace.
62
+ *
63
+ * Config is the kill switch: flipping `repo_graph.storage` back to `'json'`
64
+ * stops every reader from consulting the index immediately, even while a valid
65
+ * store with a matching stamp is still on disk. Falls back to `'json'` on any
66
+ * config error and whenever no SQLite driver is available.
67
+ */
68
+ export declare function resolveGraphStorageMode(workspace: string): 'json' | 'indexed';
69
+ /**
70
+ * Test seam (invariant 7: DI over mock.module) so the driver-unavailable
71
+ * fallback of {@link resolveGraphStorageMode} is exercisable without patching
72
+ * the module graph. Tests swap and restore the property; production reads it
73
+ * unchanged.
74
+ */
75
+ export declare const _internals: {
76
+ isIndexedStorageAvailable: typeof isIndexedStorageAvailable;
77
+ };
78
+ /**
79
+ * Replace the whole index from `graph` in one transaction.
80
+ *
81
+ * `stamp` MUST be a stat of the TEMP FILE, taken inside the save lock
82
+ * immediately BEFORE this writer's rename publishes it — not a stat of
83
+ * `repo-graph.json` at any point. `rename(2)` leaves the inode's mtime and size
84
+ * untouched, so the temp-file stat identifies THIS writer's own bytes. Any stat
85
+ * of the published path instead describes whatever is at that path by then: a
86
+ * concurrent writer that failed the lock renames unlocked (the JSON write is
87
+ * never lock-gated), so a post-rename stat would let ITS bytes validate an index
88
+ * holding THIS writer's superseded content. See `saveGraph`.
89
+ *
90
+ * Returns false — never throws — on every failure; the JSON write is the
91
+ * durability point and has already succeeded.
92
+ */
93
+ export declare function syncIndexFromGraph(workspace: string, graph: RepoGraph, stamp: {
94
+ size: number;
95
+ mtimeMs: number;
96
+ ino: string;
97
+ }): Promise<boolean>;
98
+ /**
99
+ * Resolve a single file to its `GraphNode` using the index.
100
+ *
101
+ * Implements closure rule steps 1-2 in full, so an anchor whose `moduleName`
102
+ * diverges from `path.relative(root, filePath)` resolves exactly as it would
103
+ * through `getGraphNode` on the full graph. Returns null whenever the index is
104
+ * absent, stale, foreign, or unusable — the caller keeps its existing path.
105
+ */
106
+ export declare function queryNodeByFile(workspace: string, file: string): GraphNode | null;
107
+ /**
108
+ * Build a bounded subgraph covering `files` and their neighbourhood, suitable
109
+ * for feeding the unmodified `getLocalizationContext` / `getBlastRadius`.
110
+ *
111
+ * Closure rule (normative, from the approved plan):
112
+ * 1-2. resolve every requested file the way `getGraphNode` would;
113
+ * 3. `T` = the union of resolved targets across ALL requested files in this
114
+ * ONE call (`getBlastRadius` seeds `visited` with every target at once,
115
+ * query.ts:623-625, so per-file subgraphs merged afterwards would produce
116
+ * a different `totalDependents` and therefore a different `riskLevel`);
117
+ * `N = T ∪ R_1..R_D ∪ F_1`, where `R` is the reverse closure to depth `D`
118
+ * and `F_1` is the 1-hop forward set from the targets only
119
+ * (`getDependencies`, query.ts:398-406);
120
+ * 4. `E = { edge : source ∈ N AND target ∈ N }` — AND, not incidence. An OR
121
+ * rule would admit frontier edges whose counterpart node is absent, and
122
+ * `moduleNameForEdgePath` (query.ts:83-88) silently degrades to a
123
+ * different string for those, changing `importers` / `dependencies` /
124
+ * `directDependents`. No asset/kind filtering happens in SQL:
125
+ * `buildReverseIndex` and `collectExternallyUsedSymbols` re-apply
126
+ * `isAssetEdge` downstream, so over-inclusion is harmless while
127
+ * under-inclusion is fatal;
128
+ * 5. the returned `workspaceRoot` is the realpath-bound ACTIVE workspace,
129
+ * never the persisted `graph_meta.workspace_root`.
130
+ *
131
+ * `depth` is clamped to at least 1: `getLocalizationContext` calls
132
+ * `getImporters` unconditionally, which needs `R_1` even when it passes
133
+ * `maxDepth = 0` to `getBlastRadius`. Over-inclusion of nodes is safe (a
134
+ * `maxDepth = 0` blast radius early-returns, query.ts:613-621).
135
+ */
136
+ export declare function loadSubgraphForFiles(workspace: string, files: string[], depth: number): RepoGraph | null;
@@ -1,6 +1,13 @@
1
1
  import type { FreshnessProbe } from './freshness';
2
2
  import type { BlastRadiusResult, CallerReference, ContextPackResult, ContextPackSourceMode, DeadExportsResult, FileOntology, FileReference, GraphHealthResult, GraphNode, LocalizationBlock, PackageBoundarySummary, RepoGraph, SymbolReference } from './types';
3
3
  export declare function getGraphNode(graph: RepoGraph, input: string): GraphNode | undefined;
4
+ /**
5
+ * Drop every cached query index. Required after any in-place mutation of a
6
+ * graph's `nodes` or `edges` (see the {@link queryIndexCache} contract note),
7
+ * and retained as the test seam for index-staleness assertions. Reassigns a
8
+ * fresh `WeakMap` rather than deleting keys, so it invalidates graphs this
9
+ * module can no longer enumerate.
10
+ */
4
11
  export declare function resetQueryCache(): void;
5
12
  /**
6
13
  * @deprecated Graph age is not a content-freshness signal. Internal consumers
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "7.154.2",
3
+ "version": "7.155.0",
4
4
  "description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -114,6 +114,7 @@
114
114
  "check:trajectory-store": "bun run scripts/check-trajectory-store-usage.ts",
115
115
  "check:mock-cleanup": "bun run scripts/check-mock-cleanup.ts",
116
116
  "check:invariants": "bun run scripts/check-invariants.ts",
117
+ "check:token-formula": "bun run scripts/check-token-formula.ts",
117
118
  "check:cross-contamination": "bun run scripts/check-cross-contamination.ts",
118
119
  "check:test-clock": "bun run scripts/check-test-clock.ts",
119
120
  "check:test-file-cap": "bun run scripts/check-test-file-cap.ts",
@@ -123,6 +124,7 @@
123
124
  "check:gate-portability": "bun run scripts/check-gate-portability.ts",
124
125
  "check:bare-spawn": "bun run scripts/check-bare-executable-spawn.ts",
125
126
  "check:error-channel-discard": "bun run scripts/check-error-channel-discard.ts",
127
+ "check:registry-citations": "bun run scripts/check-registry-citations.ts",
126
128
  "check:memory-recall": "bun run scripts/memory-recall-regression.ts"
127
129
  },
128
130
  "dependencies": {