opencode-swarm 7.116.0 → 7.117.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 (52) hide show
  1. package/.opencode/skills/commit-pr/SKILL.md +60 -579
  2. package/dist/cli/{curator-drift-c0by7063.js → curator-drift-7p2ky1th.js} +2 -2
  3. package/dist/cli/{curator-7s5yw6hw.js → curator-fr2m5hna.js} +13 -13
  4. package/dist/cli/{curator-llm-factory-x96dr0zf.js → curator-llm-factory-m0kvna8n.js} +13 -13
  5. package/dist/cli/{evidence-summary-service-ef6kgtev.js → evidence-summary-service-bwt6drb3.js} +5 -5
  6. package/dist/cli/{gate-evidence-thqt0beq.js → gate-evidence-j2m4cd85.js} +3 -2
  7. package/dist/cli/{guardrail-explain-kwqfkwch.js → guardrail-explain-xjgk822d.js} +14 -14
  8. package/dist/cli/{hive-promoter-rkkx40qv.js → hive-promoter-zpk5jmx0.js} +13 -13
  9. package/dist/cli/{index-bbsaxnj5.js → index-0pfqcjp6.js} +4 -0
  10. package/dist/cli/{index-qgeeyy75.js → index-4f4zv8pb.js} +29 -7
  11. package/dist/cli/{index-a6f8b0vn.js → index-5bcrp3bc.js} +1 -1
  12. package/dist/cli/{index-81vzywh3.js → index-abjjn4yh.js} +1 -1
  13. package/dist/cli/{index-8bvh0qfa.js → index-af94ke83.js} +3 -3
  14. package/dist/cli/{index-k5ey429j.js → index-aws8yhwq.js} +7 -7
  15. package/dist/cli/{index-jyetmzcn.js → index-c9m5skgs.js} +3 -3
  16. package/dist/cli/{index-3yh67t1h.js → index-dc3j6jmk.js} +5 -4
  17. package/dist/cli/{index-jtqkh8jf.js → index-fsrp8wp3.js} +10 -1
  18. package/dist/cli/{index-s385axt1.js → index-hskmya46.js} +3 -3
  19. package/dist/cli/{index-wh8949ef.js → index-kzj3fp7c.js} +3 -3
  20. package/dist/cli/{index-tvgvb5pa.js → index-n3sm9ewn.js} +2 -2
  21. package/dist/cli/{index-3b8n3dbd.js → index-pgtqxat4.js} +14 -14
  22. package/dist/cli/{index-me46ffm2.js → index-vkdr12ek.js} +1700 -936
  23. package/dist/cli/{index-cnrhj56z.js → index-xdsd2hma.js} +6 -6
  24. package/dist/cli/index.js +13 -13
  25. package/dist/cli/{knowledge-escalator-0bq62d34.js → knowledge-escalator-bmk725p4.js} +6 -6
  26. package/dist/cli/{knowledge-events-cwwky393.js → knowledge-events-px14n75v.js} +4 -3
  27. package/dist/cli/{knowledge-store-q8x4h82m.js → knowledge-store-z9znvtn2.js} +4 -4
  28. package/dist/cli/{knowledge-validator-zr0djm02.js → knowledge-validator-7r5t821g.js} +5 -5
  29. package/dist/cli/{pending-delegations-rr9x47rd.js → pending-delegations-9cn17983.js} +2 -2
  30. package/dist/cli/{pr-subscriptions-cny2jz2d.js → pr-subscriptions-8a6jwek2.js} +3 -3
  31. package/dist/cli/{skill-generator-mz267btk.js → skill-generator-4d3w8sqt.js} +7 -7
  32. package/dist/commands/close.d.ts +12 -0
  33. package/dist/commands/link.d.ts +7 -4
  34. package/dist/commands/registry.d.ts +6 -6
  35. package/dist/commands/reset-backup.d.ts +32 -0
  36. package/dist/commands/reset-session.d.ts +2 -0
  37. package/dist/commands/reset.d.ts +9 -0
  38. package/dist/commands/unlink.d.ts +12 -5
  39. package/dist/evidence/task-file.d.ts +7 -0
  40. package/dist/hooks/knowledge-events.d.ts +2 -0
  41. package/dist/hooks/knowledge-link.d.ts +31 -4
  42. package/dist/index.js +240 -229
  43. package/dist/knowledge/cohort-identity.d.ts +103 -0
  44. package/dist/knowledge/config-fingerprint.d.ts +57 -0
  45. package/dist/knowledge/family-manifest.d.ts +50 -0
  46. package/dist/knowledge/family-migration.d.ts +109 -0
  47. package/dist/knowledge/identity.d.ts +12 -2
  48. package/dist/knowledge/index.d.ts +1 -0
  49. package/dist/services/knowledge-diagnostics.d.ts +27 -0
  50. package/dist/services/status-service.d.ts +13 -0
  51. package/dist/utils/swarm-artifact-cache.d.ts +13 -0
  52. package/package.json +1 -1
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Canonical cohort-identity resolver for the linked-swarm knowledge system.
3
+ *
4
+ * Problem (issue #1846): the legacy `deriveProjectHash` (in `./identity.ts`)
5
+ * fed the *raw* `git remote get-url origin` string into SHA-256 with only a
6
+ * `.trim()`. Equivalent remote spellings (SSH vs scp vs HTTPS, optional `.git`,
7
+ * slash direction, host/path case, percent-encoding, NFC/NFD) therefore hashed
8
+ * to *different* cohort ids, fragmenting the linked store. When no origin was
9
+ * present it hashed the absolute worktree path, so sibling worktrees of the
10
+ * same repository became unrelated cohorts.
11
+ *
12
+ * This module is the single canonical identity resolver used by linking (and,
13
+ * going forward, status, diagnostics, cache keys, hive provenance, and worktree
14
+ * suggestions). Resolution order (issue #1846 §1):
15
+ *
16
+ * 1. Normalize a configured Git remote into a provider-neutral host/path
17
+ * identity so equivalent spellings converge.
18
+ * 2. If no usable remote exists, derive identity from a repository-stable Git
19
+ * identity shared by sibling worktrees (`git rev-parse --git-common-dir`).
20
+ * This fallback is machine-local (not portable across machines), so it is
21
+ * flagged `degraded: true` with a visible warning.
22
+ * 3. Only then fall back to a normalized absolute path (also `degraded`).
23
+ *
24
+ * Subprocess contract (AGENTS.md invariant 3): array-form `execFile('git', …)`,
25
+ * explicit `git -C <dir>`, `stdin` closed immediately, bounded `maxBuffer`, and
26
+ * a `timeout` — Node's `execFile` sends SIGTERM (then SIGKILL) on timeout, so
27
+ * the child is terminated without a manual `kill()`. A defensive `child.on('error')`
28
+ * guard resolves the promise rather than hanging on a spawn failure. This
29
+ * mirrors the compliant precedent in `src/session/worktree-link-suggestion.ts`.
30
+ *
31
+ * This module performs NO writes and holds NO module-level state (invariant 8).
32
+ * It is NOT imported on the plugin-init path (invariant 1); the only caller is
33
+ * `/swarm link` and, lazily, diagnostics — never `server()` resolution.
34
+ */
35
+ export type CohortIdentitySource = 'remote' | 'git-common-dir' | 'path';
36
+ export interface CohortIdentity {
37
+ /** 12-hex cohort id (SHA-256 prefix). */
38
+ cohortId: string;
39
+ /** How the id was derived. */
40
+ source: CohortIdentitySource;
41
+ /** Normalized remote (host/owner/repo) when source === 'remote'. */
42
+ normalizedRemote?: string;
43
+ /**
44
+ * True when the id is machine-local rather than portable across machines
45
+ * (git-common-dir or path fallback). A degraded cohort is still a strict
46
+ * improvement over per-worktree isolation, but it is NOT a portable cohort
47
+ * identity and must be surfaced as a visible warning (issue #1846 §1.3).
48
+ */
49
+ degraded: boolean;
50
+ }
51
+ /**
52
+ * Normalize an arbitrary Git remote URL into a canonical `host/owner/repo`
53
+ * string. Equivalent SSH/scp/HTTPS spellings, optional `.git`, slash
54
+ * direction, host/scheme case, path case (for known case-insensitive hosts),
55
+ * percent-encoding, default ports, userinfo, and NFC/NFD all converge.
56
+ *
57
+ * Returns `null` when the input cannot be parsed into a host/owner/repo triple.
58
+ */
59
+ export declare function normalizeGitRemote(rawUrl: string): string | null;
60
+ /**
61
+ * Run `git -C <dir> <args...>` and return stdout (trimmed) or null on any
62
+ * failure/timeout. Compliant subprocess contract (invariant 3): array form,
63
+ * explicit cwd via `-C`, stdin closed immediately, bounded `maxBuffer`, and a
64
+ * bounded `timeout` — Node's `execFile` sends SIGTERM (then SIGKILL) on
65
+ * timeout, so the child is terminated without a manual `kill()`. A defensive
66
+ * `child.on('error')` guard resolves the promise rather than hanging on a
67
+ * spawn failure.
68
+ */
69
+ declare function runGit(directory: string, args: string[]): Promise<string | null>;
70
+ /** SHA-256 prefix (12 hex) — matches the legacy `deriveProjectHash` width. */
71
+ declare function cohortHash(input: string): string;
72
+ /**
73
+ * Fold a realpath'd absolute path into a stable cohort-id input. On Windows,
74
+ * path separators and drive-letter case are insignificant, and `realpathSync`
75
+ * can emit an extended-length `\\?\` prefix for one spelling of a path but not
76
+ * another; none of these differences should split a cohort, so we fold them
77
+ * all. A trailing separator is likewise insignificant. This is defence in
78
+ * depth: the primary convergence guarantee comes from asking git for the
79
+ * ABSOLUTE common dir (see `resolveCohortId`), which hands every worktree of a
80
+ * repo one identical string; this folding only removes any residual spelling
81
+ * difference that survives realpath.
82
+ */
83
+ declare function stabilizePath(input: string): string;
84
+ /**
85
+ * Resolve the canonical cohort identity for a worktree directory.
86
+ *
87
+ * Resolution order:
88
+ * 1. Normalized origin remote (portable cohort identity; not degraded).
89
+ * 2. `git rev-parse --path-format=absolute --git-common-dir` — shared by
90
+ * sibling worktrees of the same repo, but machine-local → degraded.
91
+ * 3. Realpath of the directory — last resort, machine-local → degraded.
92
+ *
93
+ * Never throws. The path fallback always succeeds.
94
+ */
95
+ export declare function resolveCohortId(directory: string): Promise<CohortIdentity>;
96
+ export declare const _internals: {
97
+ normalizeGitRemote: typeof normalizeGitRemote;
98
+ runGit: typeof runGit;
99
+ cohortHash: typeof cohortHash;
100
+ stabilizePath: typeof stabilizePath;
101
+ GIT_TIMEOUT_MS: number;
102
+ };
103
+ export {};
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Cohort config fingerprint (issue #1846 §4).
3
+ *
4
+ * @status planned-future-use. The helper and its tests ship in this foundation
5
+ * PR, but it is NOT yet wired into `link`/`unlink` or the diagnostics surfaces.
6
+ * Cohort-agreement enforcement (fail-closed on mismatch for destructive ops)
7
+ * lands with #1847/#1823, which consume the cohort-status source this PR
8
+ * establishes. Shipping the primitive + tests now lets those dependents adopt
9
+ * it without re-deriving the field set.
10
+ *
11
+ * A deterministic hash of the configuration fields that change retrieval,
12
+ * curation, validation, or lifecycle semantics for the swarm knowledge tier.
13
+ * Two linked worktrees SHOULD share the same fingerprint; a mismatch means the
14
+ * cohort members will rank/quarantine/promote lessons differently.
15
+ *
16
+ * Only fields that affect *what the cohort stores and how it behaves* are
17
+ * fingerprinted — not cosmetic or session-local tuning. The set is deliberately
18
+ * conservative: adding a field later only changes the fingerprint when that
19
+ * field's value differs, which is exactly the signal we want.
20
+ */
21
+ /**
22
+ * The config-shape fingerprint input. Callers project their knowledge config
23
+ * into this loose shape; only the keys present are hashed.
24
+ */
25
+ export interface CohortConfigFingerprintInput {
26
+ dedup_threshold?: number;
27
+ scope_filter?: readonly string[];
28
+ validation_enabled?: boolean;
29
+ evergreen_confidence?: number;
30
+ evergreen_utility?: number;
31
+ low_utility_threshold?: number;
32
+ default_max_phases?: number;
33
+ todo_max_phases?: number;
34
+ confidence_floor_action?: string;
35
+ contradiction_threshold_action?: string;
36
+ contradiction_quarantine_threshold?: number;
37
+ directive_min_confidence?: number;
38
+ schema_version?: number;
39
+ swarm_max_entries?: number;
40
+ retrieval?: {
41
+ mmr_lambda?: number;
42
+ cold_start_bonus?: number;
43
+ synonym_min_cooccurrence?: number;
44
+ };
45
+ }
46
+ /**
47
+ * Compute a deterministic 12-hex fingerprint for cohort-agreement checks.
48
+ * Stable key ordering (sorted JSON) ensures equivalent configs hash equally
49
+ * regardless of field insertion order.
50
+ */
51
+ export declare function cohortConfigFingerprint(input: CohortConfigFingerprintInput): string;
52
+ declare function sortKeys(value: unknown): unknown;
53
+ export declare const _internals: {
54
+ cohortConfigFingerprint: typeof cohortConfigFingerprint;
55
+ sortKeys: typeof sortKeys;
56
+ };
57
+ export {};
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Authoritative knowledge-family manifest for the linked-swarm knowledge system.
3
+ *
4
+ * Problem (issue #1846): `link`/`unlink` previously hardcoded a *single* family
5
+ * member (`knowledge.jsonl`) while the link-aware resolvers quietly redirected
6
+ * all seven. The two sources of truth drifted, so pre-link local copies of the
7
+ * other six members were silently orphaned at link time and lost at unlink
8
+ * time. Adding a new family file required editing two commands in lockstep — a
9
+ * classic omission hazard.
10
+ *
11
+ * This manifest is the single source of truth for *which* artifacts participate
12
+ * in link/unlink family migration and *how* each is merged. `link` and `unlink`
13
+ * both iterate it, so a new family member is a one-line edit here.
14
+ *
15
+ * Merge strategies:
16
+ * - `dedup-id-merge`: id-dedup with provenance-preserving near-duplicate merge
17
+ * (union fields, evidence-weighted confidence, preserve losing id in
18
+ * `merged_from`). Used for the active store.
19
+ * - `append-union`: union by the line's stable id field (event id / entry id),
20
+ * appending only lines whose id is not already present on the destination.
21
+ * Idempotent on retry/relink.
22
+ * - `sum-counters`: per-counter field-wise SUM, reusing the existing
23
+ * `mergeRollupInto` primitive (shown/applied/... counts sum; timestamps take
24
+ * the max). Used for the counter baseline JSON.
25
+ *
26
+ * NOT in the manifest (deliberate exceptions, documented in
27
+ * `knowledge-link.ts` "Intentionally NOT redirected"):
28
+ * - `synonym-map.json` — derived state. It is rebuilt deterministically from
29
+ * the linked `knowledge.jsonl` corpus on link rather than migrated, so recall
30
+ * is deterministic across the cohort (issue #1846 §4, critic C4).
31
+ * - `.knowledge-shown.json`, `plan.json`, evidence, session state — per
32
+ * worktree by design.
33
+ */
34
+ export type FamilyRole = 'store' | 'events' | 'rejected' | 'retractions' | 'counters' | 'quarantine' | 'unactionable' | 'application-legacy';
35
+ export type FamilyMergeStrategy = 'dedup-id-merge' | 'append-union' | 'sum-counters';
36
+ export interface KnowledgeFamilyMember {
37
+ /** Filename within the swarm family directory (e.g. `knowledge.jsonl`). */
38
+ readonly filename: string;
39
+ readonly role: FamilyRole;
40
+ readonly mergeStrategy: FamilyMergeStrategy;
41
+ /** True when unlink may copy this member back to the local worktree. */
42
+ readonly reversible: boolean;
43
+ }
44
+ /**
45
+ * The complete knowledge family that participates in cohort linking.
46
+ * Adding a member here automatically includes it in link + unlink migration.
47
+ */
48
+ export declare const KNOWLEDGE_FAMILY: readonly KnowledgeFamilyMember[];
49
+ /** Filenames of every family member, for quick membership checks. */
50
+ export declare const FAMILY_FILENAMES: readonly string[];
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Cohort family-migration engine for the linked-swarm knowledge system.
3
+ *
4
+ * Drives both `/swarm link` and `/swarm unlink` off the single
5
+ * {@link KNOWLEDGE_FAMILY} manifest (issue #1846 §3). Replaces the prior
6
+ * single-file (`knowledge.jsonl`-only) migration that silently orphaned the
7
+ * other six family members.
8
+ *
9
+ * Design (issue #1846, critic-reviewed plan W3/W4):
10
+ * - One manifest drives link + unlink, so a new family member cannot be
11
+ * silently omitted.
12
+ * - Three merge strategies: `dedup-id-merge` (provenance-preserving store
13
+ * merge), `append-union` (id-keyed union for append-only logs),
14
+ * `sum-counters` (per-counter SUM for the baseline JSON, reusing
15
+ * `mergeRollupInto`).
16
+ * - All-or-nothing commit: stage the entire merged family into a sibling
17
+ * staging directory, validate every file, then commit atomically. The
18
+ * pointer is flipped last by the caller.
19
+ * - Lock discipline: acquire the destination store's directory lock first,
20
+ * read source files under a *brief* source-store lock (released before the
21
+ * long merge/validate work), so a long migration cannot have its lock
22
+ * stolen by `stale` expiry and cannot deadlock against the hot path.
23
+ *
24
+ * No writes happen here on the plugin-init path (invariant 1). Locks are
25
+ * `proper-lockfile` directory locks with a bumped `stale` for the migration
26
+ * critical section (invariant 3, critic C9).
27
+ */
28
+ import type { CounterRollup } from '../hooks/knowledge-events.js';
29
+ import type { KnowledgeEntryBase } from '../hooks/knowledge-types.js';
30
+ import { type KnowledgeFamilyMember } from './family-manifest.js';
31
+ export interface FamilyMigrationCounts {
32
+ /** Per-member counts (filename → {merged, skipped}). */
33
+ readonly perMember: ReadonlyArray<{
34
+ filename: string;
35
+ merged: number;
36
+ skipped: number;
37
+ }>;
38
+ }
39
+ /**
40
+ * Provenance-preserving merge of the active store (`dedup-id-merge`).
41
+ *
42
+ * Exact-id duplicates: skip (already present). Near-duplicates (Jaccard bigram
43
+ * similarity ≥ threshold): merge fields rather than drop — union `confirmed_by`,
44
+ * `tags`, `source_refs`; union retrieval outcomes by unique outcome key;
45
+ * confidence becomes an evidence-weighted average; the losing entry's `id` is
46
+ * preserved in `merged_from` for retraction traceability. A `merge` is NOT
47
+ * silent. (Critic C8: this is "provenance-preserving", not "lossless".)
48
+ */
49
+ declare function mergeStoreEntries(destination: KnowledgeEntryBase[], source: KnowledgeEntryBase[]): {
50
+ merged: KnowledgeEntryBase[];
51
+ added: number;
52
+ skipped: number;
53
+ };
54
+ /** Field-level union of `src` into `target` (mutates target). */
55
+ declare function mergeEntryFields(target: KnowledgeEntryBase, src: KnowledgeEntryBase): void;
56
+ /**
57
+ * Append-union: append source lines whose id is not already present on the
58
+ * destination. Lines without an id field are skipped (we cannot dedup them
59
+ * safely, and every family member here is id-keyed).
60
+ */
61
+ declare function appendUnionById<T>(destination: T[], source: T[]): {
62
+ merged: T[];
63
+ added: number;
64
+ skipped: number;
65
+ };
66
+ /**
67
+ * Sum-counters: merge two baseline JSON objects (`Record<id, CounterRollup>`)
68
+ * by per-id field-wise sum, reusing the canonical `mergeRollupInto` primitive.
69
+ */
70
+ declare function sumCounters(destination: Record<string, CounterRollup>, source: Record<string, CounterRollup>): {
71
+ merged: Record<string, CounterRollup>;
72
+ added: number;
73
+ skipped: number;
74
+ };
75
+ /** Serialize a merged family member to its on-disk representation. */
76
+ declare function serialize(member: KnowledgeFamilyMember, data: unknown): string;
77
+ /**
78
+ * Validate a serialized merged member BEFORE commit. This is a real integrity
79
+ * gate, not a vacuous JSON-parseability check: each line must parse AND, for
80
+ * the store member, each entry must carry the required `id`/`lesson` shape so a
81
+ * corrupted merge (e.g. an entry that lost its id) is rejected before it reaches
82
+ * the destination. A failure aborts the migration — the destination stays
83
+ * untouched and the caller never flips the pointer, so a retry is safe.
84
+ */
85
+ declare function validateSerialized(member: KnowledgeFamilyMember, serialized: string): boolean;
86
+ /**
87
+ * Migrate the complete knowledge family from `sourceDir` into `destinationDir`,
88
+ * merging each member according to its manifest strategy. All-or-nothing:
89
+ * stage → validate → commit. The pointer is NOT touched here (caller flips it).
90
+ *
91
+ * Returns per-member merge counts. Throws on validation failure (caller
92
+ * surfaces the error; the destination is left untouched).
93
+ *
94
+ * @param destinationDir the cohort store that absorbs the merge (link → shared;
95
+ * unlink → local `.swarm`).
96
+ * @param sourceDir the store whose family is merged in (link → local `.swarm`;
97
+ * unlink → shared).
98
+ */
99
+ export declare function migrateKnowledgeFamily(destinationDir: string, sourceDir: string): Promise<FamilyMigrationCounts>;
100
+ export declare const _internals: {
101
+ mergeStoreEntries: typeof mergeStoreEntries;
102
+ appendUnionById: typeof appendUnionById;
103
+ sumCounters: typeof sumCounters;
104
+ mergeEntryFields: typeof mergeEntryFields;
105
+ serialize: typeof serialize;
106
+ validateSerialized: typeof validateSerialized;
107
+ MIGRATION_LOCK_STALE_MS: number;
108
+ };
109
+ export {};
@@ -16,11 +16,21 @@ export interface ProjectIdentity {
16
16
  export declare function resolveIdentityPath(projectHash: string): string;
17
17
  /**
18
18
  * Derive a deterministic project hash from a directory.
19
- * Uses git remote URL if available, otherwise falls back to absolute path.
19
+ *
20
+ * @deprecated Since issue #1846. This synchronous helper is retained for
21
+ * backward compatibility (and its existing test coverage) but the canonical,
22
+ * normalization-correct, subprocess-compliant resolver is
23
+ * {@link resolveCohortId} in `./cohort-identity.ts`. New callers MUST use
24
+ * `resolveCohortId`; it normalizes equivalent remotes (SSH/scp/HTTPS, `.git`,
25
+ * case, slashes, percent-encoding, NFC), falls back through
26
+ * `git rev-parse --git-common-dir` before the path, and uses the compliant
27
+ * array-form subprocess contract. This legacy helper is kept only so existing
28
+ * imports keep compiling; it delegates to the legacy un-normalized logic.
20
29
  *
21
30
  * Worktrees of the same repository share a git remote, so they derive the same
22
31
  * hash — which is what lets `/swarm link` (with no name) tie them to one shared
23
- * knowledge store by default.
32
+ * knowledge store by default. (Note: this legacy form does NOT normalize
33
+ * equivalent remote spellings; `resolveCohortId` does.)
24
34
  */
25
35
  export declare function deriveProjectHash(directory: string): string;
26
36
  /**
@@ -1,2 +1,3 @@
1
1
  /** Knowledge system exports for opencode-swarm. */
2
+ export * from './cohort-identity.js';
2
3
  export * from './identity.js';
@@ -46,6 +46,33 @@ export interface KnowledgeDebugMeta {
46
46
  /** Knowledge-event volume bucketed by type (applied/ignored/violated/...). */
47
47
  events_by_type: Record<string, number>;
48
48
  };
49
+ /**
50
+ * Cohort/link health (issue #1846). Makes the linked store and its health
51
+ * obvious to an operator/architect. `null` when link state cannot be read.
52
+ */
53
+ cohort: {
54
+ /** True when this worktree redirects its knowledge family to a shared store. */
55
+ linked: boolean;
56
+ /** Pointer schema version (1 = legacy, 2 = cohort-aware). */
57
+ pointer_version: 1 | 2 | null;
58
+ /** Link id (shared store directory segment). */
59
+ link_id: string | null;
60
+ /** Canonical cohort id (issue #1846 W1), when known. */
61
+ cohort_id: string | null;
62
+ /** How the cohort id was derived. */
63
+ identity_source: 'remote' | 'git-common-dir' | 'path' | null;
64
+ /** True when the cohort id is machine-local (not portable). */
65
+ degraded: boolean;
66
+ /** Resolved shared root, when linked. */
67
+ shared_root: string | null;
68
+ /** Pointer generation (bumped on each link/unlink), when known. */
69
+ generation: number | null;
70
+ /**
71
+ * True when a local knowledge.jsonl coexists with an active link (the
72
+ * pre-link local store was retained). Benign but worth surfacing.
73
+ */
74
+ local_orphaned: boolean;
75
+ };
49
76
  }
50
77
  /**
51
78
  * Compute the debug-metadata block for the knowledge system. Best-effort: never
@@ -78,6 +78,19 @@ export interface StatusData {
78
78
  unactionableQueueDepth?: number;
79
79
  /** #1234 Part 3: pending insight candidates awaiting phase boundary consumption */
80
80
  insightCandidatesPending?: number;
81
+ /**
82
+ * Cohort/link status (issue #1846). Makes the linked knowledge store and its
83
+ * health obvious in `/swarm status`. `undefined` when link state is absent.
84
+ */
85
+ cohort?: {
86
+ linked: boolean;
87
+ linkId?: string;
88
+ cohortId?: string;
89
+ identitySource?: 'remote' | 'git-common-dir' | 'path';
90
+ degraded?: boolean;
91
+ sharedRoot?: string;
92
+ generation?: number;
93
+ };
81
94
  }
82
95
  /**
83
96
  * Get status data from the swarm directory.
@@ -19,5 +19,18 @@ export declare function readCachedTextFileSync(filePath: string, directRead: ()
19
19
  export declare function readCachedTextFile(filePath: string, directRead: () => Promise<string | null>): Promise<string | null>;
20
20
  export declare function readCachedParsedFileSync<T>(filePath: string, namespace: string, readText: () => string | null, parse: (content: string) => T): T | null;
21
21
  export declare function readCachedParsedFile<T>(filePath: string, namespace: string, readText: () => Promise<string | null>, parse: (content: string) => T): Promise<T | null>;
22
+ /**
23
+ * Drop any cached text/parsed entries for `filePath` immediately after a write.
24
+ *
25
+ * The stat-based staleness check (mtimeMs + ctimeMs + size) is a best-effort
26
+ * optimization: a same-size rewrite that lands within one filesystem timestamp
27
+ * tick of a prior cached read can produce an identical stamp, so the very next
28
+ * read-your-own-write can silently return the pre-write value (issue #1729).
29
+ * Writers that read-modify-write the same path twice in quick succession (e.g.
30
+ * a second locked transaction immediately after an atomic write) MUST call
31
+ * this after the write so the next read cannot observe stale content —
32
+ * relying on stat-stamp comparison alone is not sufficient in that window.
33
+ */
34
+ export declare function invalidateCachedArtifact(filePath: string): void;
22
35
  export declare function resetSwarmArtifactCache(): void;
23
36
  export declare function getSwarmArtifactCacheStats(): SwarmArtifactCacheStats;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "7.116.0",
3
+ "version": "7.117.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",