opencode-swarm 7.114.7 → 7.114.9

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 (50) hide show
  1. package/.opencode/skills/ci-fix-monitor/SKILL.md +8 -13
  2. package/.opencode/skills/commit-pr/SKILL.md +4 -0
  3. package/.opencode/skills/engineering-conventions/SKILL.md +33 -0
  4. package/.opencode/skills/execute/SKILL.md +2 -0
  5. package/.opencode/skills/swarm-ci-monitor/SKILL.md +5 -0
  6. package/.opencode/skills/swarm-pr-feedback/SKILL.md +2 -0
  7. package/.opencode/skills/writing-tests/SKILL.md +2 -0
  8. package/README.md +16 -14
  9. package/dist/agents/template.d.ts +40 -20
  10. package/dist/cli/{curator-qshyr2tc.js → curator-5xt89z05.js} +7 -7
  11. package/dist/cli/{curator-llm-factory-2xgznxyw.js → curator-llm-factory-pnhvf3s3.js} +7 -7
  12. package/dist/cli/{evidence-summary-service-8g594znj.js → evidence-summary-service-fehwj116.js} +1 -1
  13. package/dist/cli/{guardrail-explain-4t8fnysh.js → guardrail-explain-7aztgtyh.js} +8 -8
  14. package/dist/cli/{hive-promoter-5ngbmnrw.js → hive-promoter-q899q2m6.js} +7 -7
  15. package/dist/cli/{index-z01r5483.js → index-54sevh81.js} +8 -8
  16. package/dist/cli/{index-7stsmndb.js → index-6f6y2rbp.js} +3 -3
  17. package/dist/cli/{index-kjbfry6m.js → index-8f2270hc.js} +1 -1
  18. package/dist/cli/{index-gmjrwz34.js → index-m9m7p49w.js} +1 -1
  19. package/dist/cli/{index-qrnhvhmg.js → index-n18yy0z1.js} +26 -2
  20. package/dist/cli/{index-91qesget.js → index-rk6qhyng.js} +2 -2
  21. package/dist/cli/{index-6ctejje2.js → index-typ408f4.js} +710 -630
  22. package/dist/cli/{index-vjvdvjd3.js → index-x12hvfpf.js} +3 -3
  23. package/dist/cli/{index-f6y341yk.js → index-yrrqs4b0.js} +82 -21
  24. package/dist/cli/index.d.ts +34 -0
  25. package/dist/cli/index.js +105 -28
  26. package/dist/cli/{knowledge-escalator-qn7ew687.js → knowledge-escalator-5gwp1ar8.js} +3 -3
  27. package/dist/cli/{knowledge-events-k2xsz5bh.js → knowledge-events-m304swh6.js} +1 -1
  28. package/dist/cli/{knowledge-store-ksa1dr2z.js → knowledge-store-9babt8rd.js} +1 -1
  29. package/dist/cli/{knowledge-validator-2knz0d2t.js → knowledge-validator-t9sym59g.js} +4 -2
  30. package/dist/cli/{skill-generator-w6qd9mde.js → skill-generator-tav44xpm.js} +4 -4
  31. package/dist/commands/ci-monitor.d.ts +18 -0
  32. package/dist/commands/registry.d.ts +8 -0
  33. package/dist/config/skill-mirrors.d.ts +22 -1
  34. package/dist/hooks/adversarial-detector.d.ts +6 -0
  35. package/dist/hooks/delegation-gate.d.ts +19 -0
  36. package/dist/hooks/guardrails/index.d.ts +2 -2
  37. package/dist/hooks/knowledge-validator.d.ts +22 -0
  38. package/dist/index.d.ts +7 -0
  39. package/dist/index.js +194 -169
  40. package/dist/plan/ledger.d.ts +83 -20
  41. package/dist/types/delegation.d.ts +24 -0
  42. package/package.json +1 -1
  43. package/dist/graph/graph-builder.d.ts +0 -39
  44. package/dist/graph/graph-query.d.ts +0 -42
  45. package/dist/graph/graph-store.d.ts +0 -27
  46. package/dist/graph/import-extractor.d.ts +0 -44
  47. package/dist/graph/index.d.ts +0 -16
  48. package/dist/graph/symbol-extractor.d.ts +0 -17
  49. package/dist/graph/types.d.ts +0 -84
  50. package/dist/sandbox/win32/restricted-token-executor.d.ts +0 -9
@@ -5,21 +5,8 @@ description: >
5
5
  Monitor CI on a PR, diagnose failures, fix them, and re-push until green.
6
6
  Covers reading CI logs, classifying failure types (check-title, package-check,
7
7
  test failures, lint), determining the correct fix, and re-pushing.
8
- triggers:
9
- - ci check failure
10
- - fix ci
11
- - monitor ci
12
- generated_from_knowledge: []
13
- source_knowledge_ids: []
14
- generated_at: 2026-07-02T02:35:49.945Z
15
- confidence: 0.60
16
- status: active
17
- version: 6
18
- skill_origin: generated
19
8
  ---
20
9
 
21
- <!-- generated by opencode-swarm skill-generator. Do not edit by hand; edits will be preserved on regeneration only with controlled update mode. -->
22
-
23
10
  # CI Fix & Monitor Protocol
24
11
 
25
12
  Activates when the user asks to monitor CI, fix CI failures, or resolve red
@@ -201,3 +188,11 @@ the stall state to the user rather than waiting indefinitely.
201
188
  Do NOT declare victory until ALL required checks pass. A check in `skipped`
202
189
  state is acceptable only if the same check was skipped on the base branch
203
190
  (i.e. the workflow gates on a path filter). Confirm this explicitly.
191
+
192
+ ## Standalone retry bound
193
+
194
+ When this skill is invoked directly (not composed via `swarm-ci-monitor`'s
195
+ own 5-iteration counter), cap fix-push cycles at 5 iterations. If the PR is
196
+ still not green after 5 fix-push cycles, stop and escalate to the user with
197
+ the last failing check and a short log excerpt rather than looping
198
+ indefinitely.
@@ -79,6 +79,8 @@ git status --short
79
79
 
80
80
  On Windows, prefer temporary save branches over `git stash`. If you must stash, use `git stash push --include-untracked` and verify the stash contents.
81
81
 
82
+ If this changeset edited any SKILL.md file's wording, also run `file:.swarm/bundled-skills/skill-edit-validation/SKILL.md`'s content-assertion sweep before committing, to catch stale test assertions.
83
+
82
84
  ## Step 1 - Commit and PR titles
83
85
 
84
86
  Use `<type>(<scope>): <description>` exactly.
@@ -549,6 +551,8 @@ checks to rerun. Re-check before claiming final green or merge-readiness.
549
551
 
550
552
  ### Merge queue (current-base validation)
551
553
 
554
+ Read `file:.swarm/bundled-skills/merge-queue-readiness/SKILL.md` for the full pre-queue merge-group CI simulation protocol.
555
+
552
556
  When `main` has a GitHub **merge queue** enabled, do not rebase or force-push a PR
553
557
  *solely because `main` advanced*. Once required checks and review are green, add the
554
558
  PR to the merge queue; GitHub re-runs the required workflows against the queued
@@ -52,6 +52,39 @@ The OpenCode `test_runner` tool is for **targeted agent validation** with explic
52
52
  - For repo validation, run the shell commands in `contributing.md` / `TESTING.md` directly (per-file isolation loops + tier orchestration).
53
53
  - `scope: 'all'` is gated behind the `SWARM_ALLOW_FULL_SUITE=1` env var (intended for opt-in CI mirrors only); there is no `allow_full_suite` arg. Default to `files: [...]` instead.
54
54
 
55
+ ## Agent prompt strings — escaping pitfalls
56
+
57
+ Agent prompts in `src/agents/*.ts` are large TypeScript template literals. They frequently contain characters that have special meaning inside template literals and cause silent parse errors if unescaped:
58
+
59
+ | Character | Inside template literal | Correct escape |
60
+ |-----------|------------------------|----------------|
61
+ | Backtick `` ` `` | Terminates the literal | `` \` `` (single backslash — renders as `` ` `` in output) |
62
+ | `${` | Starts an interpolation | `\${` (single backslash) |
63
+ | Literal backslash `\` | Consumed by escape processing | `\\` (double backslash renders as `\` in output) |
64
+
65
+ **The most common failure pattern:** A coder adds an inline code example containing backticks to an agent prompt string. The unescaped backtick silently terminates the template literal, producing a `SyntaxError: Unexpected identifier` or `Unexpected token` at the character *after* the backtick — which appears unrelated to the actual cause.
66
+
67
+ ```typescript
68
+ // WRONG — unescaped backtick terminates the template literal
69
+ const PROMPT = `
70
+ Use `bun:test` for all tests. // ← bare backtick before "bun" closes the literal
71
+ `;
72
+
73
+ // CORRECT — single backslash before each backtick; renders as Use `bun:test` in output
74
+ const PROMPT = `
75
+ Use \`bun:test\` for all tests.
76
+ `;
77
+
78
+ // OVER-ESCAPED (also wrong) — triple backslash produces literal \` in the rendered prompt
79
+ const PROMPT = `
80
+ Use \\\`bun:test\\\` for all tests. // renders as: Use \`bun:test\` (backslashes visible)
81
+ `;
82
+ ```
83
+
84
+ **Detection:** If `bun run build` or `bun --smol test` reports a parse error at a line number that seems far from any recent change, search the surrounding lines for an unescaped backtick inside a template literal.
85
+
86
+ **Prevention:** After adding any inline code example to an agent prompt, run `bun run build` immediately — the TypeScript compiler catches unescaped backticks as a syntax error before any tests run.
87
+
55
88
  ## The invariant-audit gate (PR-time)
56
89
 
57
90
  Every PR that touches a relevant area must include an `## Invariant audit` section in its description. The format is in `AGENTS.md` ("Invariant audit required in PRs"). The `commit-pr` skill enforces this gate before push/PR — load it before committing.
@@ -111,6 +111,8 @@ Treating pre_check_batch as a substitute for the active swarm's reviewer agent i
111
111
 
112
112
  When `council_mode` is OFF, the standard Stage B flow (steps 5j-5l: reviewer + test_engineer) runs as normal.
113
113
 
114
+ For set-dispatch reviewer/test_engineer verdict rows that must be attributed to plan tasks, read `file:.swarm/bundled-skills/gate-attribution/SKILL.md`. Before re-dispatching a coder for a task that already has a lane (any prior dispatch status), read `file:.swarm/bundled-skills/worktree-retry-cleanup/SKILL.md`.
115
+
114
116
  5j. the active swarm's reviewer agent - General review. REJECTED before the configured QA retry limit → coder retry. REJECTED at the configured QA retry limit → escalate.
115
117
  → REQUIRED: Print "reviewer: [APPROVED | REJECTED — reason]"
116
118
  5k. Security gate: if change matches TIER 3 criteria OR content contains SECURITY_KEYWORDS OR secretscan has ANY findings OR sast_scan has ANY findings at or above threshold → MUST delegate the active swarm's reviewer agent security-only review. REJECTED before the configured QA retry limit → coder retry. REJECTED at the configured QA retry limit → escalate to user.
@@ -241,6 +241,11 @@ already ran once in this invocation.
241
241
  directly per Step 5 as an "un-approval" terminal, do not loop back to
242
242
  Step 2.
243
243
 
244
+ Optional pre-queue simulation: `file:.swarm/bundled-skills/merge-queue-readiness/SKILL.md`
245
+ covers running `/swarm ci-simulate` before entering a merge queue. It is a
246
+ fast local signal, not a substitute for this step's live re-checks above —
247
+ run it in addition to, never instead of, Step 3.
248
+
244
249
  ## Step 4 — Merge
245
250
 
246
251
  ### 4a. Execute the merge
@@ -239,6 +239,8 @@ branches or prior work sessions.
239
239
 
240
240
  ## Batch Collection (mandatory before any fix)
241
241
 
242
+ Read `file:.swarm/bundled-skills/ci-failure-batching/SKILL.md` for the full batch collection and fix protocol before proceeding.
243
+
242
244
  Issue #1746: 8+ push cycles where 3–4 would have sufficed with batching.
243
245
  The anti-pattern: iterating check-by-check, proposing a fix for one failure,
244
246
  pushing, waiting for CI, then discovering the next failure. Each cycle costs
@@ -617,6 +617,8 @@ authority checks:
617
617
 
618
618
  `scripts/check-test-file-cap.sh` enforces the **500-line cap** per test file (FR-006) as a **diff-scoped ratchet**: new test files over 500 lines and existing over-cap files that grew fail the quality gate and block PR merge. Pre-existing over-cap files not touched by the PR are non-blocking. Escape hatch: `TEST_CAP_ENFORCE=0` soft-warns (use only for a deliberate growth PR).
619
619
 
620
+ For the full splitting protocol (describe-block extraction, shared helper management, pure-function extraction, mock isolation verification, cascading-split detection), read `file:.swarm/bundled-skills/test-file-split/SKILL.md`.
621
+
620
622
  ### Checking file length
621
623
 
622
624
  ```bash
package/README.md CHANGED
@@ -39,7 +39,7 @@ Most AI coding tools let one model write code and ask that same model whether th
39
39
  - 🔄 **Phase completion gates** — completion-verify and drift verifier gates enforced before phase completion
40
40
  - 🔁 **Resumable sessions** — all state saved to `.swarm/`; pick up any project any day
41
41
  - 🖥️ **PR Monitor** — GitHub PR subscription and background polling via `gh` CLI; delivers real-time CI, review, and merge status updates via the AutomationEventBus (FR-001, opt-in via `pr_monitor.enabled: true`). Subscribe with `/swarm pr subscribe <pr-url|owner/repo#N|N>`; unsubscribe with `/swarm pr unsubscribe <pr-url|owner/repo#N|N>`; check status with `/swarm pr status`. Enable `auto_pr_feedback: true` in `pr_monitor` config to inject `[MODE: PR_FEEDBACK pr="URL"]` on CI failures and merge conflicts automatically.
42
- - 🌐 **12 full language profiles** (TypeScript, Python, Go, Rust, Java, Kotlin, C/C++, C#, Ruby, Swift, Dart, PHP) with **tree-sitter parse validation across 20 grammars** (adds JavaScript, CSS, Bash, PowerShell, INI, Regex — and `.tsx` / `.c` aliases) — extending: see [docs/adding-a-language.md](docs/adding-a-language.md)
42
+ - 🌐 **13 full language profiles** (TypeScript, JavaScript, Python, Go, Rust, Java, Kotlin, C/C++, C#, Ruby, Swift, Dart, PHP) with **tree-sitter parse validation across 20 grammars** (adds CSS, Bash, PowerShell, INI, Regex — and `.tsx` / `.c` aliases) — extending: see [docs/adding-a-language.md](docs/adding-a-language.md)
43
43
  - 🛡️ **Built-in security** — SAST, secrets scanning, dependency audit per task
44
44
  - 🔒 **Scope enforcement** — Validates write targets against declared scope with cross-process persistence, TTL expiry, and scope-aware destructive command blocking. **Handles both single-string and array-based path arguments** (`files[]`, `paths[]`, `targetFiles[]`) to prevent scope bypass via multi-file tool calls.
45
45
  - 📝 **Shell write detection** — Static analysis of POSIX/PowerShell/cmd commands to detect file writes (redirects, builtins, in-place editors, network downloads, archive extraction, git destructive ops) before execution
@@ -550,14 +550,14 @@ These quality gates run locally. No Docker is required. Config-gated features su
550
550
 
551
551
  ### Context Budget Guard
552
552
 
553
- The Context Budget Guard monitors how much context Swarm is injecting into the conversation. It helps prevent context overflow before it becomes a problem.
553
+ The Context Budget Guard monitors how full the model's context window is getting across the whole conversation. It helps prevent context overflow before it becomes a problem.
554
554
 
555
555
  ### Default Behavior
556
556
 
557
557
  - **Enabled automatically** — No setup required. Swarm starts tracking context usage right away.
558
- - **What it measures** — Only the context that Swarm injects (plan, context, evidence, retrospectives). It does **not** count your chat history or the model's responses.
559
- - **Warning threshold (0.7 ratio)** — When swarm-injected context reaches ~2800 tokens (70% of 4000), the architect receives a one-time advisory warning. This is informational — execution continues normally.
560
- - **Critical threshold (0.9 ratio)** — When context reaches ~3600 tokens (90% of 4000), the architect receives a critical alert with a recommendation to run `/swarm handoff`. This is also one-time only.
558
+ - **What it measures** — The estimated total tokens across **all** messages in the conversation (every text part of every message — your prompts, the model's responses, and injected content alike), divided by the model's context window. The window comes from `context_budget.model_limits` (default `128000`), resolved per model/provider. It does **not** measure only swarm-injected content, and it does **not** read `max_injection_tokens`.
559
+ - **Warning threshold (0.7 ratio)** — When total conversation tokens reach 70% of the model context window (e.g. ~89,600 of 128,000), the architect receives a one-time advisory warning. This is informational — execution continues normally.
560
+ - **Critical threshold (0.9 ratio)** — When total conversation tokens reach 90% of the model context window (e.g. ~115,200 of 128,000), the architect receives a critical alert with a recommendation to run `/swarm handoff`. This is also one-time only.
561
561
  - **Non-nagging** — Alerts fire once per session, not repeatedly. You won't be pestered every turn.
562
562
  - **Who sees warnings** — Only the architect receives these warnings. Other agents are unaware of the budget.
563
563
 
@@ -588,17 +588,18 @@ Swarm includes an intelligent skill propagation system that tracks, validates, a
588
588
 
589
589
  ```json
590
590
  {
591
- "skill_propagation": {
591
+ "skillPropagation": {
592
592
  "enabled": true,
593
593
  "enforce": false,
594
- "scoring": {
595
- "threshold": 0.5,
596
- "max_recommendations": 5
597
- }
594
+ "audiences": []
598
595
  }
599
596
  }
600
597
  ```
601
598
 
599
+ - **`enabled`** (boolean, default `true`) — turns skill-propagation scoring, recommendations, and warnings on or off. The mandatory explicit-skill-reference integrity check still runs even when this is `false`.
600
+ - **`enforce`** (boolean, default `false`) — when `true`, blocks delegations that omit the `SKILLS:` field instead of only warning.
601
+ - **`audiences`** (string array, default `[]`, max 16 entries, de-duplicated) — extra audience tokens to broaden which skills are considered in scope for this project, in addition to the active runner's own audience. Each entry must be a lowercase domain token matching `^[a-z0-9]+(?:[._-][a-z0-9]+)*$`; the reserved value `swarm-plugin` and any `runner:*` prefix are rejected.
602
+
602
603
  **Skill routing file format** (`.opencode/skill-routing.yaml`):
603
604
 
604
605
  ```yaml
@@ -695,9 +696,9 @@ Every candidate passes a 3-gate pipeline before entering quarantine:
695
696
  | Key | Type | Default | Description |
696
697
  |-----|------|---------|-------------|
697
698
  | `context_budget.enabled` | boolean | `true` | Enable or disable the context budget guard entirely |
698
- | `context_budget.max_injection_tokens` | number | `4000` | Token budget for swarm-injected context per turn. This is NOT the model's context window it's the swarm plugin's own contribution |
699
- | `context_budget.warn_threshold` | number | `0.7` | Ratio (0.0-1.0) of `max_injection_tokens` that triggers a warning advisory |
700
- | `context_budget.critical_threshold` | number | `0.9` | Ratio (0.0-1.0) of `max_injection_tokens` that triggers a critical alert with handoff recommendation |
699
+ | `context_budget.max_injection_tokens` | number | `4000` | Separate per-turn cap on system-enhancer injection. It is NOT the guard's budget — the guard measures total conversation tokens against `model_limits`, not this value |
700
+ | `context_budget.warn_threshold` | number | `0.7` | Ratio (0.0-1.0) of the model context window (`model_limits`, default `128000`) at which total conversation tokens trigger a warning advisory |
701
+ | `context_budget.critical_threshold` | number | `0.9` | Ratio (0.0-1.0) of the model context window (`model_limits`, default `128000`) at which total conversation tokens trigger a critical alert with handoff recommendation |
701
702
  | `context_budget.enforce` | boolean | `true` | When true, enforces budget limits and may trigger handoffs |
702
703
  | `context_budget.prune_target` | number | `0.7` | Ratio (0.0-1.0) of context to preserve when pruning occurs |
703
704
  | `context_budget.preserve_last_n_turns` | number | `4` | Number of recent turns to preserve when pruning |
@@ -847,7 +848,7 @@ Swarm uses file locking to protect shared state files from concurrent write corr
847
848
 
848
849
  ### Locking Implementation
849
850
 
850
- - **Library**: `proper-lockfile` with `retries: 0` (fail-fast no polling retries)
851
+ - **Library**: `proper-lockfile` with automatic retries (F-09): 5 retries with exponential backoff (10ms→500ms, factor 2); a held lock is treated as stale after `LOCK_TIMEOUT_MS` (5 minutes)
851
852
  - **Scope**: Each tool acquires an exclusive lock on the target file before writing
852
853
  - **Agents**: Lock is tagged with the current agent name and task context for diagnostics
853
854
 
@@ -1114,6 +1115,7 @@ Control how tool outputs are summarized for LLM context.
1114
1115
  | `/swarm issue <issue-url\|owner/repo#N\|N> [--plan] [--trace] [--no-repro]` | Ingest a GitHub issue for localization and resolution |
1115
1116
  | `/swarm pr-review <pr-url\|owner/repo#N\|N> [--council] [instructions...]` | Structured deep PR review with parallel lanes, reviewer confirmation, and critic challenge |
1116
1117
  | `/swarm pr-feedback [<pr-url\|owner/repo#N\|N>] [instructions...]` | Ingest and close known PR feedback (review comments, CI failures, conflicts) without a fresh review |
1118
+ | `/swarm ci-monitor <pr-url\|owner/repo#N\|N>` | Drive an already-reviewed, approved PR to green and merged (monitor CI, fix, merge; max 5 fix cycles) |
1117
1119
  | `/swarm pr subscribe <pr-url\|owner/repo#N\|N>` | Subscribe current session to PR monitoring (session-scoped); requires `pr_monitor.enabled: true` |
1118
1120
  | `/swarm pr unsubscribe <pr-url\|owner/repo#N\|N>` | Remove session's subscription to a PR |
1119
1121
  | `/swarm pr status` | List active PR subscriptions for current session with relative timestamps (the `bunx opencode-swarm run pr status` CLI has no session context, so it lists subscriptions across all sessions) |
@@ -1,22 +1,29 @@
1
1
  /**
2
- * Agent prompt template renderer.
2
+ * Agent prompt template helpers.
3
3
  *
4
- * Replaces `{{KEY}}` placeholders in agent prompt strings with values from
5
- * a `ProjectContext` resolved at session-init time. Strict by design:
6
- * unknown placeholders raise (caught at build time by
7
- * `tests/unit/agents/template-substitution.test.ts`), so a typo never
8
- * leaks to the model.
4
+ * `{{KEY}}` placeholders in agent prompt strings are substituted with values
5
+ * from a `ProjectContext` resolved at session-init time by the hand-rolled
6
+ * `.replace()` chains in `src/agents/index.ts` (Chain B) and
7
+ * `src/agents/architect.ts` (Chain A). `assertNoUnresolvedPlaceholders` is the
8
+ * post-substitution safety net that guarantees a typo never leaks a raw
9
+ * `{{KEY}}` to the model — it runs over each agent's FINAL prompt inside the
10
+ * `createSwarmAgents` loop (`src/agents/index.ts`), after every substitution
11
+ * chain has run, before the agents are returned.
9
12
  *
10
- * Phase 4b of language-agnostic plugin work. Pinned call site is
11
- * `src/index.ts:initializeOpenCodeSwarm` immediately before
12
- * `getAgentConfigs(...)` see the `withTimeout(2000ms)` wrapping there
13
- * to honor invariant 1 (plugin init bounded + fail-open).
13
+ * Invariant 1 (fail-open): the assertion itself throws (unit tests depend on
14
+ * it), but its production call site in `createSwarmAgents` wraps it in a
15
+ * try/catch that downgrades a leftover placeholder to a deferred warning and
16
+ * registers the agent anyway a user-authored custom prompt may legitimately
17
+ * contain a literal `{{UPPER_KEY}}` in prose, and plugin init must never abort.
18
+ * Built-in-prompt regressions stay caught in CI by a test that drains the
19
+ * warning buffer after `getAgentConfigs()` over the default prompts.
20
+ *
21
+ * Phase 4b of language-agnostic plugin work.
14
22
  */
15
23
  /**
16
24
  * Variables available for substitution into agent prompts. Every prompt's
17
- * `{{KEY}}` placeholders must be a key of this interface; the renderer
18
- * rejects unknown placeholders. New variables go here AND in
19
- * `buildProjectContext` in `src/index.ts`.
25
+ * `{{KEY}}` placeholders must be a key of this interface. New variables go
26
+ * here AND in `buildProjectContext` in `src/index.ts`.
20
27
  */
21
28
  export interface ProjectContext {
22
29
  PROJECT_LANGUAGE: string;
@@ -68,15 +75,28 @@ export declare function emptyProjectContext(): ProjectContext;
68
75
  */
69
76
  export declare function escapeForTemplate(s: string): string;
70
77
  /**
71
- * Render `prompt` with `vars`. Replaces every `{{KEY}}` whose KEY is a
72
- * documented `ProjectContext` field. Unknown placeholders raise.
78
+ * Post-substitution safety net for the hand-rolled `.replace()` chains that
79
+ * assemble each agent's FINAL system prompt (Chain A in
80
+ * `src/agents/architect.ts` and Chain B in `src/agents/index.ts`). Neither
81
+ * chain validated for leftover placeholders, so a renamed, mistyped, or
82
+ * newly-added `{{KEY}}` could leak raw template text straight to the model
83
+ * with no error. This asserts — over the fully-substituted prompt — that no
84
+ * `{{KEY}}` survived, applied at the reachable production call site (see
85
+ * `src/agents/index.ts`, immediately before `getAgentConfigs`).
86
+ *
87
+ * The scan uses the character class `[A-Z_]+`. It MUST NOT be broadened to
88
+ * `[^}]+`, `.*?`, or similar: `src/agents/architect.ts` contains a literal
89
+ * `` `{{...}}` `` in instructional prose (teaching the architect what an
90
+ * unresolved field looks like). The dots in `...` are not in `[A-Z_]`, so this
91
+ * guard never matches that prose. A broader class WOULD match it and
92
+ * false-throw during agent init. The trailing `g` flag only enumerates every
93
+ * offender for the error message; it does not widen what counts as a
94
+ * placeholder.
73
95
  *
74
- * The renderer is intentionally simple — single-pass, no nesting, no
75
- * conditionals, no loops. Agent prompts that need conditional sections
76
- * should pre-compute a string variable in `buildProjectContext` and
77
- * substitute it as a single placeholder.
96
+ * @throws if any `{{KEY}}` placeholder remains, naming the offending key(s)
97
+ * and the agent whose prompt they were found in.
78
98
  */
79
- export declare function renderPrompt(prompt: string, vars: ProjectContext): string;
99
+ export declare function assertNoUnresolvedPlaceholders(prompt: string, agentName: string): void;
80
100
  /**
81
101
  * Convert an array of constraint strings into a bulleted block ready for
82
102
  * inclusion in an agent prompt via `{{CODER_CONSTRAINTS}}` etc.
@@ -14,20 +14,20 @@ import {
14
14
  runCuratorInit,
15
15
  runCuratorPhase,
16
16
  writeCuratorSummary
17
- } from "./index-6ctejje2.js";
17
+ } from "./index-typ408f4.js";
18
18
  import"./index-yfedche9.js";
19
19
  import"./index-c8s9a3zh.js";
20
20
  import"./index-9bsmzfk3.js";
21
21
  import"./index-a45jq4b7.js";
22
- import"./index-f6y341yk.js";
22
+ import"./index-yrrqs4b0.js";
23
23
  import"./index-scww5b77.js";
24
24
  import"./index-ga4ta3cr.js";
25
25
  import"./index-80jkseqw.js";
26
- import"./index-vjvdvjd3.js";
27
- import"./index-qrnhvhmg.js";
28
- import"./index-7stsmndb.js";
29
- import"./index-kjbfry6m.js";
30
- import"./index-91qesget.js";
26
+ import"./index-x12hvfpf.js";
27
+ import"./index-n18yy0z1.js";
28
+ import"./index-6f6y2rbp.js";
29
+ import"./index-8f2270hc.js";
30
+ import"./index-rk6qhyng.js";
31
31
  import"./index-bfwt3abw.js";
32
32
  import"./index-g40r5d08.js";
33
33
  import"./index-vxv732ex.js";
@@ -1,20 +1,20 @@
1
1
  // @bun
2
2
  import {
3
3
  createCuratorLLMDelegate
4
- } from "./index-6ctejje2.js";
4
+ } from "./index-typ408f4.js";
5
5
  import"./index-yfedche9.js";
6
6
  import"./index-c8s9a3zh.js";
7
7
  import"./index-9bsmzfk3.js";
8
8
  import"./index-a45jq4b7.js";
9
- import"./index-f6y341yk.js";
9
+ import"./index-yrrqs4b0.js";
10
10
  import"./index-scww5b77.js";
11
11
  import"./index-ga4ta3cr.js";
12
12
  import"./index-80jkseqw.js";
13
- import"./index-vjvdvjd3.js";
14
- import"./index-qrnhvhmg.js";
15
- import"./index-7stsmndb.js";
16
- import"./index-kjbfry6m.js";
17
- import"./index-91qesget.js";
13
+ import"./index-x12hvfpf.js";
14
+ import"./index-n18yy0z1.js";
15
+ import"./index-6f6y2rbp.js";
16
+ import"./index-8f2270hc.js";
17
+ import"./index-rk6qhyng.js";
18
18
  import"./index-bfwt3abw.js";
19
19
  import"./index-g40r5d08.js";
20
20
  import"./index-vxv732ex.js";
@@ -6,7 +6,7 @@ import {
6
6
  loadPlanJsonOnly,
7
7
  mergeDurableGateEntriesFromEvidence,
8
8
  readDurableGateEvidence
9
- } from "./index-f6y341yk.js";
9
+ } from "./index-yrrqs4b0.js";
10
10
  import"./index-scww5b77.js";
11
11
  import"./index-q1exe2b3.js";
12
12
  import"./index-wh8949ef.js";
@@ -1,21 +1,21 @@
1
1
  // @bun
2
2
  import {
3
3
  handleGuardrailExplain
4
- } from "./index-gmjrwz34.js";
5
- import"./index-6ctejje2.js";
4
+ } from "./index-m9m7p49w.js";
5
+ import"./index-typ408f4.js";
6
6
  import"./index-yfedche9.js";
7
7
  import"./index-c8s9a3zh.js";
8
8
  import"./index-9bsmzfk3.js";
9
9
  import"./index-a45jq4b7.js";
10
- import"./index-f6y341yk.js";
10
+ import"./index-yrrqs4b0.js";
11
11
  import"./index-scww5b77.js";
12
12
  import"./index-ga4ta3cr.js";
13
13
  import"./index-80jkseqw.js";
14
- import"./index-vjvdvjd3.js";
15
- import"./index-qrnhvhmg.js";
16
- import"./index-7stsmndb.js";
17
- import"./index-kjbfry6m.js";
18
- import"./index-91qesget.js";
14
+ import"./index-x12hvfpf.js";
15
+ import"./index-n18yy0z1.js";
16
+ import"./index-6f6y2rbp.js";
17
+ import"./index-8f2270hc.js";
18
+ import"./index-rk6qhyng.js";
19
19
  import"./index-bfwt3abw.js";
20
20
  import"./index-g40r5d08.js";
21
21
  import"./index-vxv732ex.js";
@@ -6,20 +6,20 @@ import {
6
6
  isHiveEligible,
7
7
  promoteFromSwarm,
8
8
  promoteToHive
9
- } from "./index-6ctejje2.js";
9
+ } from "./index-typ408f4.js";
10
10
  import"./index-yfedche9.js";
11
11
  import"./index-c8s9a3zh.js";
12
12
  import"./index-9bsmzfk3.js";
13
13
  import"./index-a45jq4b7.js";
14
- import"./index-f6y341yk.js";
14
+ import"./index-yrrqs4b0.js";
15
15
  import"./index-scww5b77.js";
16
16
  import"./index-ga4ta3cr.js";
17
17
  import"./index-80jkseqw.js";
18
- import"./index-vjvdvjd3.js";
19
- import"./index-qrnhvhmg.js";
20
- import"./index-7stsmndb.js";
21
- import"./index-kjbfry6m.js";
22
- import"./index-91qesget.js";
18
+ import"./index-x12hvfpf.js";
19
+ import"./index-n18yy0z1.js";
20
+ import"./index-6f6y2rbp.js";
21
+ import"./index-8f2270hc.js";
22
+ import"./index-rk6qhyng.js";
23
23
  import"./index-bfwt3abw.js";
24
24
  import"./index-g40r5d08.js";
25
25
  import"./index-vxv732ex.js";
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  handleGuardrailExplain
4
- } from "./index-gmjrwz34.js";
4
+ } from "./index-m9m7p49w.js";
5
5
  import {
6
6
  handleGuardrailLog
7
7
  } from "./index-7jmrbp68.js";
@@ -81,7 +81,7 @@ import {
81
81
  handleWriteRetroCommand,
82
82
  normalizeSwarmCommandInput,
83
83
  resolveCommand
84
- } from "./index-6ctejje2.js";
84
+ } from "./index-typ408f4.js";
85
85
  import"./index-yfedche9.js";
86
86
  import"./index-c8s9a3zh.js";
87
87
  import"./index-9bsmzfk3.js";
@@ -90,15 +90,15 @@ import {
90
90
  ORCHESTRATOR_NAME,
91
91
  stripKnownSwarmPrefix
92
92
  } from "./index-a45jq4b7.js";
93
- import"./index-f6y341yk.js";
93
+ import"./index-yrrqs4b0.js";
94
94
  import"./index-scww5b77.js";
95
95
  import"./index-ga4ta3cr.js";
96
96
  import"./index-80jkseqw.js";
97
- import"./index-vjvdvjd3.js";
98
- import"./index-qrnhvhmg.js";
99
- import"./index-7stsmndb.js";
100
- import"./index-kjbfry6m.js";
101
- import"./index-91qesget.js";
97
+ import"./index-x12hvfpf.js";
98
+ import"./index-n18yy0z1.js";
99
+ import"./index-6f6y2rbp.js";
100
+ import"./index-8f2270hc.js";
101
+ import"./index-rk6qhyng.js";
102
102
  import"./index-bfwt3abw.js";
103
103
  import"./index-g40r5d08.js";
104
104
  import"./index-vxv732ex.js";
@@ -4,7 +4,7 @@ import {
4
4
  countEntryViolationsInWindow,
5
5
  readKnowledgeEvents,
6
6
  recordKnowledgeEvent
7
- } from "./index-kjbfry6m.js";
7
+ } from "./index-8f2270hc.js";
8
8
  import {
9
9
  jaccardBigram,
10
10
  readKnowledge,
@@ -12,7 +12,7 @@ import {
12
12
  resolveSwarmKnowledgePath,
13
13
  transactKnowledge,
14
14
  wordBigrams
15
- } from "./index-91qesget.js";
15
+ } from "./index-rk6qhyng.js";
16
16
  import {
17
17
  isActiveStatus
18
18
  } from "./index-bfwt3abw.js";
@@ -188,7 +188,7 @@ async function maybeQuarantineOnContradiction(directory, entryId, threshold, win
188
188
  alreadyInactive: true
189
189
  };
190
190
  }
191
- const { quarantineEntry } = await import("./knowledge-validator-2knz0d2t.js");
191
+ const { quarantineEntry } = await import("./knowledge-validator-t9sym59g.js");
192
192
  await quarantineEntry(directory, entryId, `repeat_contradiction: ${count} contradicted events in ${windowDays}d`, "auto");
193
193
  return { quarantined: true, entryId, contradictionsInWindow: count };
194
194
  } catch {
@@ -594,7 +594,7 @@ async function applyKnowledgeVerdictFeedback(directory, options) {
594
594
  deltas.push({ id, delta });
595
595
  }
596
596
  if (deltas.length > 0) {
597
- const { bumpKnowledgeConfidenceBatch } = await import("./knowledge-store-ksa1dr2z.js");
597
+ const { bumpKnowledgeConfidenceBatch } = await import("./knowledge-store-9babt8rd.js");
598
598
  await bumpKnowledgeConfidenceBatch(directory, deltas, options?.floorOptions);
599
599
  }
600
600
  return {
@@ -12,7 +12,7 @@ import {
12
12
  detectPosixWrites,
13
13
  detectWindowsWrites,
14
14
  resolveWriteTargets
15
- } from "./index-6ctejje2.js";
15
+ } from "./index-typ408f4.js";
16
16
  import {
17
17
  checkFileAuthority,
18
18
  classifyFile,
@@ -6,7 +6,7 @@ import {
6
6
  resolveSwarmKnowledgePath,
7
7
  resolveSwarmRejectedPath,
8
8
  transactKnowledge
9
- } from "./index-91qesget.js";
9
+ } from "./index-rk6qhyng.js";
10
10
  import {
11
11
  resolveKnowledgeStoreDir
12
12
  } from "./index-g40r5d08.js";
@@ -75,6 +75,25 @@ var INJECTION_PATTERNS = [
75
75
  /\bconstructor\[/,
76
76
  /\.prototype\[/
77
77
  ];
78
+ function scanContentSafety(text) {
79
+ if (typeof text !== "string" || text.length === 0)
80
+ return null;
81
+ const normalized = text.normalize("NFKC").replace(INVISIBLE_FORMAT_CHARS, " ").replace(/\s+/g, " ").toLowerCase();
82
+ for (const pattern of DANGEROUS_COMMAND_ERROR_PATTERNS) {
83
+ if (pattern.test(normalized))
84
+ return "dangerous command pattern detected";
85
+ }
86
+ for (const pattern of SECURITY_DEGRADING_PATTERNS) {
87
+ if (pattern.test(normalized)) {
88
+ return "security-degrading instruction detected";
89
+ }
90
+ }
91
+ for (const pattern of INJECTION_PATTERNS) {
92
+ if (pattern.test(text))
93
+ return "injection pattern detected";
94
+ }
95
+ return null;
96
+ }
78
97
  var VALID_CATEGORIES = new Set([
79
98
  "process",
80
99
  "architecture",
@@ -412,6 +431,11 @@ function validateActionableFields(fields) {
412
431
  errors.push(`${name} contains invalid string`);
413
432
  return;
414
433
  }
434
+ const unsafe = scanContentSafety(item);
435
+ if (unsafe) {
436
+ errors.push(`${name} contains unsafe content: ${unsafe}`);
437
+ return;
438
+ }
415
439
  }
416
440
  }
417
441
  function checkNameList(name, list) {
@@ -758,4 +782,4 @@ var _internals = {
758
782
  hasSignificantOverlap
759
783
  };
760
784
 
761
- export { DANGEROUS_COMMAND_ERROR_PATTERNS, DANGEROUS_COMMAND_WARNING_PATTERNS, DANGEROUS_COMMAND_PATTERNS, SECURITY_DEGRADING_PATTERNS, INVISIBLE_FORMAT_CHARS, INJECTION_PATTERNS, validateLesson, ACTIONABLE_STRING_MAX, ACTIONABLE_LIST_MAX, RETIRED_SKILL_HISTORY_MAX, ALLOWED_SKILL_PATH_PREFIXES, validateSkillPath, validateSkillCandidatePath, validateActionableFields, validateActionability, resolveUnactionablePath, appendUnactionable, auditEntryHealth, quarantineEntry, restoreEntry, unarchiveEntry, _internals };
785
+ export { DANGEROUS_COMMAND_ERROR_PATTERNS, DANGEROUS_COMMAND_WARNING_PATTERNS, DANGEROUS_COMMAND_PATTERNS, SECURITY_DEGRADING_PATTERNS, INVISIBLE_FORMAT_CHARS, INJECTION_PATTERNS, scanContentSafety, validateLesson, ACTIONABLE_STRING_MAX, ACTIONABLE_LIST_MAX, RETIRED_SKILL_HISTORY_MAX, ALLOWED_SKILL_PATH_PREFIXES, validateSkillPath, validateSkillCandidatePath, validateActionableFields, validateActionability, resolveUnactionablePath, appendUnactionable, auditEntryHealth, quarantineEntry, restoreEntry, unarchiveEntry, _internals };
@@ -595,7 +595,7 @@ async function applyConfidenceFloorAction(directory, touched, options) {
595
595
  const recovered = touched.filter((e) => e.confidence > CONFIDENCE_FLOOR + FLOOR_EPSILON && e.confidence_floor_demoted);
596
596
  if (atFloor.length === 0 && recovered.length === 0)
597
597
  return;
598
- const { readKnowledgeCounterRollups, effectiveRetrievalOutcomes } = await import("./knowledge-events-k2xsz5bh.js");
598
+ const { readKnowledgeCounterRollups, effectiveRetrievalOutcomes } = await import("./knowledge-events-m304swh6.js");
599
599
  const rollups = await readKnowledgeCounterRollups(directory);
600
600
  const flagIds = new Set;
601
601
  for (const e of atFloor) {
@@ -662,7 +662,7 @@ async function applyConfidenceFloorAction(directory, touched, options) {
662
662
  });
663
663
  }
664
664
  if (action === "quarantine" && toQuarantine.length > 0) {
665
- const { quarantineEntry } = await import("./knowledge-validator-2knz0d2t.js");
665
+ const { quarantineEntry } = await import("./knowledge-validator-t9sym59g.js");
666
666
  for (const { id } of toQuarantine) {
667
667
  await quarantineEntry(directory, id, "confidence_floor_negative_outcome", "auto").catch((err) => {
668
668
  log("[knowledge-store] confidence-floor quarantine failed (best-effort):", err instanceof Error ? err.message : String(err));