opencode-swarm 7.136.4 → 7.136.5

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 (40) hide show
  1. package/dist/cli/{config-doctor-qwp5y9dk.js → config-doctor-n3hatm9d.js} +2 -2
  2. package/dist/cli/{curation-policy-8tsbaa4q.js → curation-policy-c4g08bg1.js} +2 -2
  3. package/dist/cli/{curator-763smew7.js → curator-jc5cc980.js} +12 -12
  4. package/dist/cli/{curator-llm-factory-dg8mmzy7.js → curator-llm-factory-atfnhbn6.js} +12 -12
  5. package/dist/cli/{guardrail-explain-6jc012g3.js → guardrail-explain-v6f8efve.js} +13 -13
  6. package/dist/cli/{guardrail-log-86aw7fne.js → guardrail-log-0mrq14g9.js} +3 -3
  7. package/dist/cli/{hive-promoter-en786ja4.js → hive-promoter-wyejhz8b.js} +12 -12
  8. package/dist/cli/{index-94pvh7hc.js → index-6kd0ezgd.js} +1 -1
  9. package/dist/cli/{index-06htxvzq.js → index-84se71v1.js} +1 -1
  10. package/dist/cli/{index-fhnbzz75.js → index-8f4346kf.js} +1 -1
  11. package/dist/cli/{index-qv1xc6rd.js → index-akc6rp3x.js} +2 -2
  12. package/dist/cli/{index-rkp6jvc3.js → index-bqan4y2y.js} +2 -2
  13. package/dist/cli/{index-pq8ge6fe.js → index-eeb35dnx.js} +1 -1
  14. package/dist/cli/{index-89fpahtg.js → index-f5qs217h.js} +6 -1
  15. package/dist/cli/{index-t1s4b5ha.js → index-hbzh7yyz.js} +669 -618
  16. package/dist/cli/{index-gjs5g97v.js → index-j7ja83v5.js} +1 -1
  17. package/dist/cli/{index-1hef020t.js → index-pyz1p8qv.js} +1 -1
  18. package/dist/cli/{index-bb3ks0v3.js → index-qhbr4h5n.js} +4 -4
  19. package/dist/cli/{index-wy2f83j5.js → index-tr0ctbhq.js} +5 -5
  20. package/dist/cli/{index-fkdxfdbx.js → index-vm05set2.js} +2 -2
  21. package/dist/cli/{index-g0sdhyqk.js → index-xp2pfbpq.js} +1 -1
  22. package/dist/cli/{index-et8b70c8.js → index-xvcctb2t.js} +14 -14
  23. package/dist/cli/{index-4cv991ra.js → index-yj61bped.js} +3 -3
  24. package/dist/cli/index.js +12 -12
  25. package/dist/cli/{knowledge-escalator-e815bveb.js → knowledge-escalator-2xe8z24p.js} +3 -3
  26. package/dist/cli/{knowledge-events-dz2tyhpw.js → knowledge-events-wecrshsx.js} +1 -1
  27. package/dist/cli/{knowledge-store-97qr7m9k.js → knowledge-store-9f74whxw.js} +1 -1
  28. package/dist/cli/{knowledge-validator-xsetvy4v.js → knowledge-validator-4ng7x9dr.js} +4 -4
  29. package/dist/cli/{scan-cursor-129fwf7e.js → scan-cursor-yzj9n225.js} +2 -2
  30. package/dist/cli/{schema-3xdza5gg.js → schema-cr1nr1w0.js} +1 -1
  31. package/dist/cli/{skill-generator-8zhprasg.js → skill-generator-ybncj9et.js} +5 -5
  32. package/dist/hooks/guardrails/file-authority.d.ts +20 -1
  33. package/dist/index.js +283 -284
  34. package/dist/memory/index.d.ts +1 -1
  35. package/dist/memory/local-jsonl-provider.d.ts +4 -1
  36. package/dist/memory/redaction.d.ts +91 -0
  37. package/dist/memory/sqlite-provider.d.ts +5 -1
  38. package/dist/utils/arg-hash.d.ts +113 -0
  39. package/dist/utils/stable-stringify.d.ts +47 -2
  40. package/package.json +1 -1
@@ -12,7 +12,7 @@ export { invalidateMemoryStoreDirCache, isMemoryLinked, MEMORY_LINK_POINTER_FILE
12
12
  export { buildRecallPromptBlock } from './prompt-block';
13
13
  export type { MemoryCompactOptions, MemoryCompactResult, MemoryProposalStore, MemoryProvider, MemoryRecallUsageEvent, MemoryRecallUsageFilter, } from './provider';
14
14
  export { buildMemoryRecallPlan, type MemoryRecallPlan, type MemoryRecallPlannerInput, } from './recall-planner';
15
- export { buildMemoryCohortFingerprintInput, computeMemoryCohortFingerprint, computeRedactionPolicyVersion, findSecrets, redactSecrets, } from './redaction';
15
+ export { buildMemoryCohortFingerprintInput, computeMemoryCohortFingerprint, computeRedactionPolicyVersion, FINGERPRINT_ALGORITHM_VERSION, findSecrets, redactSecrets, } from './redaction';
16
16
  export { MEMORY_RECALL_PROFILES, type MemoryRecallProfile, normalizeMemoryAgentRole, resolveMemoryRecallProfile, } from './role-profiles';
17
17
  export { appendMemoryRunLog, sanitizeRunId } from './run-log';
18
18
  export { computeMemoryContentHash, createBundleId, createMemoryId, createProposalId, isExpired, normalizeMemoryText, validateCuratorMemoryDecision, validateMemoryProposal, validateMemoryRecordRules, } from './schema';
@@ -55,7 +55,10 @@ export declare class LocalJsonlMemoryProvider implements MemoryProvider, MemoryP
55
55
  /**
56
56
  * #1850 (KC-001 fix): mirror the SQLite provider's fingerprint check. Reads
57
57
  * `memory-cohort-config.json` and throws on mismatch. Absent/malformed file
58
- * is permissive (fail-open never strands memory).
58
+ * is permissive (fail-open never strands memory). #2062 F-012: a file whose
59
+ * `algorithm_version` is not the current one — or is present but
60
+ * uninterpretable — is also permissive, because the digests are not
61
+ * comparable; both warn with a re-link instruction.
59
62
  */
60
63
  private assertCohortConfigFingerprint;
61
64
  private activeMemory;
@@ -20,6 +20,97 @@ export interface MemoryCohortFingerprintInput {
20
20
  embedding_dimension: number;
21
21
  embedding_version: string;
22
22
  }
23
+ /**
24
+ * #2062 F-012: version of the cohort-fingerprint ALGORITHM (canonicalization +
25
+ * hash + truncation), persisted alongside the fingerprint in
26
+ * `memory-cohort-config.json` and read back by every consumer.
27
+ *
28
+ * Version 1 is the algorithm below (sorted-key canonical JSON -> sha256 -> first
29
+ * 12 hex chars). Files written before this field existed are treated as
30
+ * version 1, which is exactly correct: they were produced by this same
31
+ * algorithm, so they keep validating with no forced re-link.
32
+ *
33
+ * BUMP THIS whenever a change would alter the digest for an unchanged input —
34
+ * e.g. a different hash, a different truncation length, a change to
35
+ * `stableCanonicalStringify`'s output for a reachable input, or a change to the
36
+ * `MemoryCohortFingerprintInput` shape. Readers compare versions BEFORE
37
+ * comparing fingerprints, so a bump surfaces as "re-run `/swarm memory link`"
38
+ * instead of being misreported as a real provider/embedding config mismatch.
39
+ */
40
+ export declare const FINGERPRINT_ALGORITHM_VERSION = 1;
41
+ /**
42
+ * #2062 F-012 (R3 fix): the algorithm version implied by a persisted cohort
43
+ * config that has NO `algorithm_version` field. Every file written before the
44
+ * field existed was produced by algorithm version 1, so that is what an absent
45
+ * field means — permanently, whatever `FINGERPRINT_ALGORITHM_VERSION` later
46
+ * becomes.
47
+ *
48
+ * This MUST stay a standalone literal and must never be re-expressed in terms
49
+ * of `FINGERPRINT_ALGORITHM_VERSION`. If it tracked the current version, then
50
+ * the moment that constant is bumped every legacy file already on disk would
51
+ * report itself as current, the version gate could never fire for the very
52
+ * files it exists to protect, and a v1 digest would be byte-compared against a
53
+ * v2 expected value — which `SQLiteMemoryProvider` turns into a hard
54
+ * "config differs" throw. Defaulting to the current version is self-defeating
55
+ * at exactly the moment the mechanism is supposed to help.
56
+ *
57
+ * The explicit `: 1` literal type is the enforcement, not decoration — and it
58
+ * enforces only for as long as it is actually there. No runtime test can catch
59
+ * the aliasing refactor today (while `FINGERPRINT_ALGORITHM_VERSION` is still
60
+ * 1, an alias evaluates to 1 and every assertion passes), so the guard has to
61
+ * fire at the only moment the mistake becomes real: the bump.
62
+ *
63
+ * Both halves of that were measured against this repo with the constant
64
+ * temporarily set to 2, running `bunx tsc --noEmit`:
65
+ *
66
+ * - `LEGACY_…: 1 = FINGERPRINT_ALGORITHM_VERSION` (annotation KEPT)
67
+ * -> exit 2, `TS2322: Type '2' is not assignable to type '1'`. Guard fires.
68
+ * - `LEGACY_… = FINGERPRINT_ALGORITHM_VERSION` (annotation DROPPED)
69
+ * -> exit 0. Guard does NOT fire, and every legacy cohort file is stranded
70
+ * silently.
71
+ *
72
+ * The second form is the one a refactorer actually types, so do NOT rely on
73
+ * `tsc` to catch removal of this annotation — the annotation is the thing
74
+ * being protected, not merely the mechanism. A legitimate bump that leaves
75
+ * this literal alone typechecks cleanly.
76
+ */
77
+ export declare const LEGACY_FINGERPRINT_ALGORITHM_VERSION: 1;
78
+ /**
79
+ * DI seam for testability (repo convention, cf. `src/utils/logger.ts`). Lets a
80
+ * test simulate a FUTURE bump of `FINGERPRINT_ALGORITHM_VERSION` without
81
+ * hand-editing the constant — the only way to exercise the legacy-file version
82
+ * gate before a real bump ever happens. Production code never mutates this.
83
+ */
84
+ export declare const _internals: {
85
+ currentAlgorithmVersion: number;
86
+ };
87
+ /**
88
+ * Result of interpreting a persisted `algorithm_version` field:
89
+ * - `comparable` — stored digest was produced by the current algorithm, so a
90
+ * byte comparison is meaningful.
91
+ * - `mismatch` — a known but different algorithm produced it; the digests are
92
+ * not comparable, so callers skip the compare and fail OPEN with a re-link
93
+ * instruction rather than reporting a config difference that does not exist.
94
+ * - `unknown` — the field is present but not a finite number, so the digest
95
+ * cannot be attributed to any algorithm. Callers skip the compare too, but
96
+ * must NOT assume it is current (that would byte-compare on a guess).
97
+ */
98
+ export type StoredFingerprintAlgorithmVersion = {
99
+ status: 'comparable';
100
+ } | {
101
+ status: 'mismatch';
102
+ storedVersion: number;
103
+ currentVersion: number;
104
+ } | {
105
+ status: 'unknown';
106
+ };
107
+ /**
108
+ * Single source of truth for the version gate shared by all four readers
109
+ * (SQLite provider, local-jsonl provider, status service, knowledge
110
+ * diagnostics). Keeping it here means a future bump changes one file, and the
111
+ * absent-field semantics cannot silently drift apart across call sites.
112
+ */
113
+ export declare function classifyStoredFingerprintAlgorithmVersion(rawStoredVersion: unknown, currentVersion?: number): StoredFingerprintAlgorithmVersion;
23
114
  export declare function computeMemoryCohortFingerprint(input: MemoryCohortFingerprintInput): string;
24
115
  /** Convenience: build the fingerprint input from a memory config subset. */
25
116
  export declare function buildMemoryCohortFingerprintInput(config: {
@@ -148,7 +148,11 @@ export declare class SQLiteMemoryProvider implements MemoryProvider, MemoryPropo
148
148
  * with this worktree's config (acceptance #10). Absent file = first link
149
149
  * (permissive — the linker writes it immediately after migration). A
150
150
  * malformed file is also permissive (fail-open never strands memory, but
151
- * a mismatch is a hard error).
151
+ * a mismatch is a hard error). #2062 F-012: a file whose `algorithm_version`
152
+ * is not the current one — or is present but uninterpretable — is permissive
153
+ * too, because cross-algorithm digests are not comparable. Both warn with a
154
+ * re-link instruction rather than reporting a config mismatch that does not
155
+ * exist.
152
156
  */
153
157
  private assertCohortConfigFingerprint;
154
158
  private writeMemoryFts;
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Shared primitives for hashing tool-call arguments (issue #2060 follow-ups
3
+ * F-009 / F-010).
4
+ *
5
+ * # Why this exists
6
+ *
7
+ * Two subsystems hash tool arguments to detect "the agent is repeating
8
+ * itself": `hashArgsForSpiral` in `hooks/adversarial-detector.ts` (spiral
9
+ * advisory) and `hashArgs` in `hooks/guardrails/file-authority.ts` (which
10
+ * feeds the consecutive-repetition circuit breaker in
11
+ * `hooks/guardrails/tool-before.ts` — a path that THROWS, not warns).
12
+ *
13
+ * Both need the same two primitives. They live here rather than as two inline
14
+ * copies for the same reason `src/utils/stable-stringify.ts` was extracted: a
15
+ * bug class that exists at two call sites must be fixed once, in one place,
16
+ * and tested once. Keep them here; do not re-inline a copy at a call site.
17
+ *
18
+ * The two call sites differ only in the OUTPUT SHAPE they need — the detector
19
+ * wants a compact base-36 string, file-authority wants a `number` — so
20
+ * `boundedBunHash` returns `bigint` (matching `bunHash`) and each caller
21
+ * formats it.
22
+ */
23
+ /**
24
+ * Cap on the amount of input fed into `bunHash`.
25
+ *
26
+ * Both call sites run synchronously on a per-tool-call hot path, and payloads
27
+ * (patch bodies, file writes) can reach ~1 MB. Node's `bunHash` fallback
28
+ * (djb2, see `bun-compat.ts`) is genuinely reachable in production — the
29
+ * OpenCode plugin host and the Desktop sidecar can run under Node — and is
30
+ * O(n): ~141 ms for a 2 MB input versus ~5 ms for 64 KB. The threat model is
31
+ * "same tool, same args, repeatedly", not adversarial collision resistance,
32
+ * so bounding the hashed input keeps worst-case per-call cost flat.
33
+ *
34
+ * NOTE ON UNITS: this bounds `String.prototype.length`, i.e. UTF-16 code
35
+ * units, not encoded bytes. It is a cost bound, not a byte-exact limit; a
36
+ * non-BMP-heavy string can encode to more bytes than the name suggests. That
37
+ * is fine — the point is a fixed ceiling, and the ceiling is fixed.
38
+ */
39
+ export declare const HASH_INPUT_CAP_BYTES: number;
40
+ /**
41
+ * Reduces an arbitrarily long string to a bounded, length-prefixed
42
+ * head-and-tail sample suitable as hash input.
43
+ *
44
+ * ## Why not a bare prefix
45
+ *
46
+ * The obvious bound — `input.slice(0, CAP)` — makes every pair of inputs that
47
+ * share a `CAP`-length prefix hash identically. For the circuit-breaker call
48
+ * site that is a false-positive engine: ten consecutive large writes that
49
+ * share a boilerplate header but differ in their (appended) bodies look like
50
+ * ten identical calls and the breaker throws. Sampling the head AND the tail
51
+ * removes the whole append-collision class, and sampling the head removes the
52
+ * prepend-collision class, at exactly the same bounded cost.
53
+ *
54
+ * ## Why the length prefix is load-bearing
55
+ *
56
+ * It is not decoration. Without it the two branches below can collide across
57
+ * the cap boundary: an input of exactly `CAP` characters passes through
58
+ * untransformed, while a longer input produces a `CAP`-character head+tail
59
+ * concatenation — two different inputs, one identical hash input. Prefixing
60
+ * the true length separates the classes. It also makes the under-cap branch
61
+ * injective, so this transform adds ZERO collisions for inputs at or below
62
+ * the cap.
63
+ *
64
+ * ## Residual, accepted lossiness
65
+ *
66
+ * Two inputs longer than the cap collide only if they have the SAME total
67
+ * length AND the same first `CAP/2` characters AND the same last `CAP/2`
68
+ * characters, differing only in the discarded middle. That is unavoidable for
69
+ * any fixed-cost sampler and is the deliberate trade documented on
70
+ * `HASH_INPUT_CAP_BYTES`.
71
+ *
72
+ * Cannot throw: `String.prototype.slice` and template interpolation of a
73
+ * number are total.
74
+ */
75
+ export declare function sampleForHash(input: string): string;
76
+ /**
77
+ * `bunHash` over a bounded head+tail sample of `input`.
78
+ *
79
+ * Returns `bigint` (the `bunHash` shape) so each caller can format it for its
80
+ * own storage: the detector uses `.toString(36)`, file-authority uses
81
+ * `Number(...)`.
82
+ *
83
+ * Cannot throw for a string input: `sampleForHash` is total and `bunHash`
84
+ * on a string is `TextEncoder.encode` plus BigInt arithmetic (or `Bun.hash`)
85
+ * — no throwing path. Callers rely on this: both use it inside a `catch`
86
+ * block, where a second throw would escape into a hook.
87
+ */
88
+ export declare function boundedBunHash(input: string): bigint;
89
+ /**
90
+ * Shallow, non-recursive structural summary of a value's own enumerable keys,
91
+ * used only as a fallback discriminator when `stableCanonicalStringify`
92
+ * throws (issue #2060 follow-up F-009).
93
+ *
94
+ * Before this existed, both call sites answered an unstringifiable argument
95
+ * with a CONSTANT ('h:fallback' / `0`). That made every distinct-but-
96
+ * unserializable argument collide, so N consecutive calls with genuinely
97
+ * different arguments looked identical and fired a false positive — a spiral
98
+ * advisory in one case, a thrown circuit breaker in the other.
99
+ *
100
+ * Deliberately shallow (no recursion into nested values) so it cannot itself
101
+ * throw on the very inputs that broke `stableCanonicalStringify`: recursing
102
+ * into a cyclic reference would revisit the cycle, and BigInt values are
103
+ * handled fine by `typeof` / `String()` (unlike `JSON.stringify`, which
104
+ * throws on them). `Object.keys` and `typeof` cannot throw for ordinary
105
+ * objects; the outer try/catch guards only against exotic Proxy traps, and
106
+ * even then identical arguments still collide, so true-positive repetition
107
+ * detection is preserved.
108
+ *
109
+ * This is a DISCRIMINATOR, not an identity function. It is only ever used to
110
+ * break up false collisions on a detection path — never as a fail-closed
111
+ * identity (see the caller guidance on `stableCanonicalStringify`).
112
+ */
113
+ export declare function coarseObjectDiscriminator(args: unknown): string;
@@ -25,6 +25,11 @@
25
25
  * Originally introduced for the adversarial-detector spiral hash
26
26
  * (issue #2060) and shared with `file-authority.hashArgs` so both code paths
27
27
  * use one correct implementation.
28
+ *
29
+ * This is a canonicalizer for HASH INPUT, not a drop-in `JSON.stringify`
30
+ * replacement — see the "Serialization limits" section on
31
+ * `stableCanonicalStringify` before using it anywhere the output is parsed or
32
+ * shown to a user.
28
33
  */
29
34
  /**
30
35
  * Recursively produces a canonical JSON string with object keys sorted at
@@ -39,8 +44,48 @@
39
44
  * function exists to eliminate. Sorting must be done by rebuilding each object
40
45
  * with sorted keys before serialization.
41
46
  *
47
+ * ## Serialization limits (#2062 F-008 — deliberate, verified, do not "fix"
48
+ * without re-reading this)
49
+ *
50
+ * - **No `toJSON` support.** `toJSON` is never consulted. A `Date` therefore
51
+ * collapses to `{}` (it has no own enumerable keys) — a genuine divergence
52
+ * from `JSON.stringify`, which emits the ISO string.
53
+ * - **`Map`/`Set` collapse to `{}`.** This MATCHES `JSON.stringify` (neither
54
+ * exposes own enumerable string keys), so it is lossy for both rather than a
55
+ * divergence between them.
56
+ * - **`undefined` serializes as `null`** in object-property AND array-element
57
+ * position, so no bare `undefined` token or sparse-array hole is emitted for
58
+ * it. `JSON.stringify` instead omits an `undefined`-valued property; keeping
59
+ * the key here is deliberate — two objects differing only by a
60
+ * present-but-undefined key must not hash equal. True sparse array *holes*
61
+ * (`[1,,3]`) are NOT covered: `Array.prototype.map` skips holes, so they
62
+ * still render as `[1,,3]`.
63
+ * - **Function and symbol values still emit a bare `undefined` token**, which
64
+ * is not valid JSON. It is deterministic, so it is safe as hash input, but
65
+ * the result is not always `JSON.parse`-able.
66
+ *
67
+ * These limits are safe at every current call site: the cohort fingerprint
68
+ * input is a flat all-primitive struct, and the hook call sites hash tool-call
69
+ * arguments that have already round-tripped through JSON transport, so
70
+ * `Date`/`Map`/`Set`/function values cannot reach them.
71
+ *
72
+ * ## Throwing, and how callers must (and must not) handle it
73
+ *
42
74
  * Throws on cyclic structures (infinite recursion) and on values that
43
- * `JSON.stringify` cannot represent (BigInt); callers should wrap in try/catch
44
- * and fall back to a stable coarse hash.
75
+ * `JSON.stringify` cannot represent (BigInt). Correct caller handling depends
76
+ * on what the hash is FOR:
77
+ *
78
+ * - **Repetition / spiral-detection callers** (`hashArgsForSpiral` in
79
+ * `hooks/adversarial-detector.ts`, `hashArgs` in
80
+ * `hooks/guardrails/file-authority.ts`) SHOULD wrap in try/catch and fall
81
+ * back to a stable coarse hash. There, a fallback only degrades detection
82
+ * sensitivity for the one call that failed.
83
+ * - **Identity / fail-closed comparison callers** (e.g.
84
+ * `computeMemoryCohortFingerprint` in `memory/redaction.ts`) MUST NOT do
85
+ * that. A constant fallback would collapse every failing input to the same
86
+ * fingerprint, so two genuinely incompatible cohort members would compare
87
+ * equal and the fail-closed coherence check would be silently defeated. Let
88
+ * the throw propagate, or rethrow with added context — never return a
89
+ * constant.
45
90
  */
46
91
  export declare function stableCanonicalStringify(value: unknown): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "7.136.4",
3
+ "version": "7.136.5",
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",