opencode-swarm 7.54.0 → 7.56.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.
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Background subagent completion OBSERVER (issue #1151, PR 2 Stage A).
3
+ *
4
+ * Registers as a swarm `event` hook to watch for the upstream background-completion signal:
5
+ * a message part with `synthetic === true` whose text is a task envelope with
6
+ * `state="completed"` or `state="error"`. When such a part correlates to a durable pending
7
+ * background-delegation record, it is logged (debug-gated) as the empirical confirmation
8
+ * instrument operators use to verify the runtime signal in a real environment.
9
+ *
10
+ * Stage A is strictly READ-ONLY: this observer NEVER advances workflow gates, records gate
11
+ * evidence, or mutates the durable store. Gate-affecting completion ingestion is Stage B,
12
+ * gated on runtime confirmation produced by exactly this observer.
13
+ *
14
+ * The `synthetic` flag is the trust gate (set by OpenCode, not the model/user). Non-synthetic
15
+ * text that merely looks like an envelope is ignored. The observer is fail-open: any error is
16
+ * swallowed so it can never block event delivery or plugin load (Invariant 1/10).
17
+ */
18
+ interface ObserverConfig {
19
+ enabled: boolean;
20
+ }
21
+ /**
22
+ * Build the Stage A completion observer. Returns an `event` handler suitable for the
23
+ * OpenCode plugin `event` hook. No-op (cheap early return) when the feature is disabled.
24
+ */
25
+ export declare function createBackgroundCompletionObserver(opts: {
26
+ config: ObserverConfig;
27
+ directory: string;
28
+ }): {
29
+ event: (input: {
30
+ event: unknown;
31
+ }) => Promise<void>;
32
+ };
33
+ export {};
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Durable pending background-delegation store (issue #1151, PR 2 Stage A).
3
+ *
4
+ * Append-only JSONL event log under project-root `.swarm/background-delegations.jsonl`.
5
+ * Each line is a full record snapshot; readers fold to the latest snapshot per
6
+ * `correlationId`. This tracks background swarm `Task` dispatches so a later (Stage B)
7
+ * trusted completion can be correlated to a real dispatch. The stale sweep bounds the
8
+ * number of permanently-running (unresolved) entries by transitioning them to `stale`, so
9
+ * the folded in-memory view stays bounded by distinct correlationIds. Note: the on-disk
10
+ * log itself is append-only and is NOT compacted in Stage A — each dispatch leaves a small,
11
+ * fixed number of lines; on-disk compaction of dropped/stale records is a future stage.
12
+ *
13
+ * Stage A scope: dispatch records a `pending` snapshot and the stale sweep records
14
+ * `stale` snapshots. There is NO gate advancement and NO completion mutation here — the
15
+ * completion observer (Stage A) is read-only. Gate-affecting completion ingestion is
16
+ * Stage B, gated on runtime confirmation of the upstream completion signal.
17
+ *
18
+ * Concurrency: all writes (append, sweep) run under a single project-scoped lock via
19
+ * `withEvidenceLock`, so concurrent dispatches/sweeps cannot interleave appends. Reads are
20
+ * lock-free (line-oriented; partial trailing lines are skipped defensively).
21
+ *
22
+ * Containment: the path is validated with `validateSwarmPath`, so it can never escape
23
+ * `.swarm/` (Invariant 4).
24
+ */
25
+ export declare const BACKGROUND_DELEGATIONS_FILE = "background-delegations.jsonl";
26
+ export type BackgroundDelegationStatus = 'pending' | 'completed' | 'error' | 'stale';
27
+ export interface BackgroundDelegationRecord {
28
+ schemaVersion: 1;
29
+ /** Subagent session id from the dispatch envelope — the correlation key. */
30
+ correlationId: string;
31
+ /** Structured jobId from dispatch metadata when available, else null. */
32
+ jobId: string | null;
33
+ /** Subagent session id (== correlationId; kept explicit for clarity/forward-compat). */
34
+ subagentSessionId: string;
35
+ /** Parent (dispatching) session id. */
36
+ parentSessionId: string;
37
+ /** Tool callID of the dispatching Task call. */
38
+ callID: string;
39
+ /** Canonical swarm role (e.g. "reviewer", "test_engineer"). */
40
+ normalizedAgent: string;
41
+ /** Raw, possibly swarm-prefixed agent name (e.g. "mega_reviewer"). */
42
+ swarmPrefixedAgent: string;
43
+ /** Plan/evidence task id resolved at dispatch, or null. */
44
+ planTaskId: string | null;
45
+ evidenceTaskId: string | null;
46
+ status: BackgroundDelegationStatus;
47
+ createdAt: number;
48
+ updatedAt: number;
49
+ }
50
+ /**
51
+ * Read and fold the store to the latest snapshot per correlationId. Lock-free and
52
+ * defensive: a missing file yields an empty list, and malformed/partial lines are skipped
53
+ * (never throws). Records are returned in first-seen correlationId order.
54
+ *
55
+ * Cost: O(lines on disk) per call — a full read + parse + fold with no in-memory cache.
56
+ * This is intentionally simple and acceptable at Stage A volumes (a swarm has few concurrent
57
+ * background delegations, and the on-disk log is small). If sustained high-throughput
58
+ * background load ever makes this hot, a future stage should add a stat-invalidated in-memory
59
+ * cache and/or JSONL compaction (the same future-compaction work noted in the module header).
60
+ */
61
+ export declare function readDelegations(directory: string): BackgroundDelegationRecord[];
62
+ /** Returns the folded record for a correlationId, or null. Lock-free read. */
63
+ export declare function findByCorrelationId(directory: string, correlationId: string): BackgroundDelegationRecord | null;
64
+ export interface RecordPendingInput {
65
+ correlationId: string;
66
+ jobId: string | null;
67
+ subagentSessionId: string;
68
+ parentSessionId: string;
69
+ callID: string;
70
+ normalizedAgent: string;
71
+ swarmPrefixedAgent: string;
72
+ planTaskId: string | null;
73
+ evidenceTaskId: string | null;
74
+ }
75
+ /**
76
+ * Record a `pending` background delegation. Runs the stale sweep first (lazy maintenance,
77
+ * no plugin-init cost), then appends the pending snapshot — all under one lock acquisition
78
+ * so concurrent dispatches cannot interleave. Best-effort: returns null on lock timeout or
79
+ * write failure (Stage A has no gate effects, so a missed record is non-fatal).
80
+ */
81
+ export declare function recordPendingDelegation(directory: string, input: RecordPendingInput, options?: {
82
+ staleTimeoutMs?: number;
83
+ }): Promise<BackgroundDelegationRecord | null>;
84
+ /**
85
+ * Public stale sweep: acquires the store lock and marks overdue pendings as `stale`.
86
+ * Best-effort; returns the number swept (0 on lock timeout / error).
87
+ */
88
+ export declare function sweepStaleDelegations(directory: string, timeoutMs: number): Promise<number>;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Background subagent task-envelope parsing (issue #1151, PR 2 Stage A).
3
+ *
4
+ * OpenCode background subagents (v1.16.2) render task dispatch/completion as a stable
5
+ * XML-ish envelope via the upstream `renderOutput`:
6
+ *
7
+ * <task id="<subagentSessionID>" state="running|completed|error">
8
+ * <summary>...</summary>
9
+ * <task_result>...</task_result> (or <task_error> for state="error")
10
+ * </task>
11
+ *
12
+ * - The dispatch tool result carries `state="running"` and the subagent session id.
13
+ * - The deferred completion arrives as a synthetic parent message part whose text is the
14
+ * same envelope with `state="completed"` or `state="error"`.
15
+ *
16
+ * These parsers are intentionally pure and defensive — they never throw — so they can be
17
+ * used both at dispatch (tool.execute.after output) and at completion observation time.
18
+ */
19
+ export type TaskEnvelopeState = 'running' | 'completed' | 'error';
20
+ export interface TaskEnvelope {
21
+ /** The subagent session id from `<task id="...">` — the cross-event correlation key. */
22
+ sessionId: string;
23
+ state: TaskEnvelopeState;
24
+ }
25
+ /**
26
+ * Parse a task envelope from arbitrary text. Returns null when the text does not contain
27
+ * a well-formed opening `<task id="..." state="...">` tag. Never throws.
28
+ */
29
+ export declare function parseTaskEnvelope(text: unknown): TaskEnvelope | null;
30
+ /**
31
+ * Extract the subagent session id and (optional) jobId from a background `Task` dispatch
32
+ * result (the `tool.execute.after` output object `{ title, output, metadata }`).
33
+ *
34
+ * - `subagentSessionId` is parsed from the rendered `output` envelope `<task id="...">`.
35
+ * - `jobId` is read defensively from `metadata.jobId` (the installed plugin SDK types this
36
+ * field as `any`; upstream sets `{ background: true, jobId }`). Absent → null.
37
+ *
38
+ * Both are best-effort; either may be null.
39
+ */
40
+ export declare function extractDispatchIds(output: unknown): {
41
+ subagentSessionId: string | null;
42
+ jobId: string | null;
43
+ };
package/dist/cli/index.js CHANGED
@@ -52,7 +52,7 @@ var package_default;
52
52
  var init_package = __esm(() => {
53
53
  package_default = {
54
54
  name: "opencode-swarm",
55
- version: "7.54.0",
55
+ version: "7.56.0",
56
56
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
57
57
  main: "dist/index.js",
58
58
  types: "dist/index.d.ts",
@@ -17302,7 +17302,9 @@ var init_schema = __esm(() => {
17302
17302
  delegation_tracker: exports_external.boolean().default(false),
17303
17303
  agent_awareness_max_chars: exports_external.number().min(50).max(2000).default(300),
17304
17304
  delegation_gate: exports_external.boolean().default(true),
17305
- delegation_max_chars: exports_external.number().min(500).max(20000).default(4000)
17305
+ delegation_max_chars: exports_external.number().min(500).max(20000).default(4000),
17306
+ background_subagents: exports_external.boolean().default(false),
17307
+ background_pending_timeout_minutes: exports_external.number().int().min(1).max(1440).default(30)
17306
17308
  });
17307
17309
  ScoringWeightsSchema = exports_external.object({
17308
17310
  phase: exports_external.number().min(0).max(5).default(1),
@@ -36809,7 +36811,7 @@ function clusterEntries(entries) {
36809
36811
  function uniqueStrings(arr) {
36810
36812
  return [...new Set(arr.filter((s) => typeof s === "string" && s.length > 0))];
36811
36813
  }
36812
- function renderSkillMarkdown(cluster, mode = "active") {
36814
+ function renderSkillMarkdown(cluster, mode = "active", generatedAt = new Date().toISOString()) {
36813
36815
  const description = cluster.title.length > 200 ? `${cluster.title.slice(0, 197)}\u2026` : cluster.title;
36814
36816
  const ids = cluster.entries.map((e) => ` - ${e.id}`).join(`
36815
36817
  `);
@@ -36819,6 +36821,9 @@ function renderSkillMarkdown(cluster, mode = "active") {
36819
36821
  lines.push(`description: ${escapeYaml(description)}`);
36820
36822
  lines.push("generated_from_knowledge:");
36821
36823
  lines.push(ids);
36824
+ lines.push("source_knowledge_ids:");
36825
+ lines.push(ids);
36826
+ lines.push(`generated_at: ${generatedAt}`);
36822
36827
  lines.push(`confidence: ${cluster.avgConfidence.toFixed(2)}`);
36823
36828
  lines.push(`status: ${mode === "active" ? "active" : "draft"}`);
36824
36829
  lines.push("---");
@@ -37007,6 +37012,62 @@ async function stampSourceEntries(directory, slug, ids) {
37007
37012
  if (touchedHive)
37008
37013
  await rewriteKnowledge(hivePath, hive);
37009
37014
  }
37015
+ function parseDraftFrontmatter(content) {
37016
+ const stripped = content.charCodeAt(0) === 65279 ? content.slice(1) : content;
37017
+ const openFence = stripped.match(/^---[ \t]*\r?\n/);
37018
+ if (!openFence)
37019
+ return null;
37020
+ const fenceLen = openFence[0].length;
37021
+ const closeFence = stripped.slice(fenceLen).match(/\n---[ \t]*(\r?\n|$)/);
37022
+ if (!closeFence)
37023
+ return null;
37024
+ const closeStart = fenceLen + (closeFence.index ?? 0);
37025
+ const body = stripped.slice(fenceLen, closeStart).replace(/\r\n/g, `
37026
+ `);
37027
+ const lines = body.split(`
37028
+ `);
37029
+ const out = {
37030
+ sourceKnowledgeIds: []
37031
+ };
37032
+ let inLegacyIdsList = false;
37033
+ let inSourceIdsList = false;
37034
+ for (const raw of lines) {
37035
+ const line = raw;
37036
+ if (inLegacyIdsList || inSourceIdsList) {
37037
+ const m = line.match(/^\s+-\s+(\S{1,64})\s*$/);
37038
+ if (m) {
37039
+ out.sourceKnowledgeIds.push(m[1]);
37040
+ continue;
37041
+ }
37042
+ inLegacyIdsList = false;
37043
+ inSourceIdsList = false;
37044
+ }
37045
+ const nm = line.match(/^name:\s*(\S+)\s*$/);
37046
+ if (nm) {
37047
+ out.name = nm[1];
37048
+ continue;
37049
+ }
37050
+ const st = line.match(/^status:\s*(\S+)\s*$/);
37051
+ if (st) {
37052
+ out.status = st[1];
37053
+ continue;
37054
+ }
37055
+ const ga = line.match(/^generated_at:\s*(\S+)\s*$/);
37056
+ if (ga) {
37057
+ out.generatedAt = ga[1];
37058
+ continue;
37059
+ }
37060
+ if (/^generated_from_knowledge:\s*$/.test(line)) {
37061
+ inLegacyIdsList = true;
37062
+ continue;
37063
+ }
37064
+ if (/^source_knowledge_ids:\s*$/.test(line)) {
37065
+ out.sourceKnowledgeIds = [];
37066
+ inSourceIdsList = true;
37067
+ }
37068
+ }
37069
+ return out;
37070
+ }
37010
37071
  async function listSkills(directory) {
37011
37072
  const result = {
37012
37073
  proposals: [],
@@ -38336,7 +38397,7 @@ var init_skill_improver_quota = __esm(() => {
38336
38397
 
38337
38398
  // src/services/skill-improver.ts
38338
38399
  import { existsSync as existsSync12 } from "fs";
38339
- import { mkdir as mkdir7, rename as rename5, writeFile as writeFile6 } from "fs/promises";
38400
+ import { mkdir as mkdir7, readFile as readFile7, rename as rename5, writeFile as writeFile6 } from "fs/promises";
38340
38401
  import * as path18 from "path";
38341
38402
  function timestampSlug(d) {
38342
38403
  return d.toISOString().replace(/[:.]/g, "-");
@@ -38353,15 +38414,63 @@ async function gatherInventory(directory) {
38353
38414
  const hive = existsSync12(hivePath) ? await readKnowledge(hivePath) : [];
38354
38415
  const archived = [...swarm, ...hive].filter((e) => e.status === "archived").length;
38355
38416
  const skills = await listSkills(directory);
38417
+ const knowledgeById = new Map([...swarm, ...hive].map((entry) => [entry.id, entry]));
38418
+ const staleActiveSkills = [];
38419
+ let metadataReadable = 0;
38420
+ for (const skill of skills.active) {
38421
+ let content;
38422
+ try {
38423
+ content = await readFile7(skill.path, "utf-8");
38424
+ } catch {
38425
+ continue;
38426
+ }
38427
+ const fm = parseDraftFrontmatter(content);
38428
+ if (!fm)
38429
+ continue;
38430
+ metadataReadable += 1;
38431
+ const reasons = [];
38432
+ if (fm.sourceKnowledgeIds.length === 0) {
38433
+ reasons.push("missing_source_knowledge_ids");
38434
+ }
38435
+ if (!fm.generatedAt) {
38436
+ reasons.push("missing_generated_at");
38437
+ } else {
38438
+ const generatedAtMs = Date.parse(fm.generatedAt);
38439
+ if (!Number.isFinite(generatedAtMs)) {
38440
+ reasons.push("invalid_generated_at");
38441
+ } else {
38442
+ for (const id of fm.sourceKnowledgeIds) {
38443
+ const source = knowledgeById.get(id);
38444
+ if (!source) {
38445
+ reasons.push(`missing_source:${id}`);
38446
+ continue;
38447
+ }
38448
+ const updatedAtMs = Date.parse(source.updated_at);
38449
+ if (Number.isFinite(updatedAtMs) && updatedAtMs > generatedAtMs) {
38450
+ reasons.push(`updated_after_generation:${id}`);
38451
+ }
38452
+ }
38453
+ }
38454
+ }
38455
+ if (reasons.length > 0) {
38456
+ staleActiveSkills.push({
38457
+ slug: skill.slug,
38458
+ reasons: reasons.slice(0, 6)
38459
+ });
38460
+ }
38461
+ }
38356
38462
  const matureCandidates = swarm.concat(hive).filter((e) => e.status !== "archived" && e.confidence >= 0.85 && !e.generated_skill_slug && (e.confirmed_by ?? []).length >= 2);
38357
38463
  return {
38358
38464
  knowledge: { swarm: swarm.length, hive: hive.length, archived },
38359
38465
  skills: {
38360
38466
  proposals: skills.proposals.length,
38361
- active: skills.active.length
38467
+ active: skills.active.length,
38468
+ stale: staleActiveSkills.length,
38469
+ metadataReadable
38362
38470
  },
38363
38471
  highConfidenceClusters: matureCandidates.length,
38364
- matureCandidates
38472
+ matureCandidates,
38473
+ staleActiveSkills
38365
38474
  };
38366
38475
  }
38367
38476
  function buildSystemPrompt(targets, cfg) {
@@ -38385,10 +38494,16 @@ hive_entries: ${inv.knowledge.hive}
38385
38494
  archived: ${inv.knowledge.archived}
38386
38495
  draft_skills: ${inv.skills.proposals}
38387
38496
  active_skills: ${inv.skills.active}
38497
+ active_skills_with_readable_metadata: ${inv.skills.metadataReadable}
38498
+ stale_active_skills: ${inv.skills.stale}
38388
38499
  mature_uncompiled_clusters: ${inv.highConfidenceClusters}
38389
38500
 
38390
38501
  TOP MATURE CANDIDATES (first 25):
38391
38502
  ${matureRows || "(none)"}
38503
+
38504
+ STALE ACTIVE SKILLS (first 10):
38505
+ ${inv.staleActiveSkills.slice(0, 10).map((s) => `- ${s.slug} | ${s.reasons.join(", ")}`).join(`
38506
+ `) || "(none)"}
38392
38507
  `;
38393
38508
  }
38394
38509
  function isAbortError(err) {
@@ -38417,7 +38532,11 @@ function buildDeterministicProposal(args) {
38417
38532
  lines.push("## Inventory snapshot");
38418
38533
  lines.push(`- Knowledge entries: swarm=${args.inventory.knowledge.swarm}, hive=${args.inventory.knowledge.hive}, archived=${args.inventory.knowledge.archived}`);
38419
38534
  lines.push(`- Generated skills: proposals=${args.inventory.skills.proposals}, active=${args.inventory.skills.active}`);
38535
+ lines.push(`- Active skills with readable metadata: ${args.inventory.skills.metadataReadable} (stale=${args.inventory.skills.stale})`);
38420
38536
  lines.push(`- High-confidence un-skill'd clusters: ${args.inventory.highConfidenceClusters}`);
38537
+ if (args.inventory.staleActiveSkills.length > 0) {
38538
+ lines.push(`- Stale active skills: ${args.inventory.staleActiveSkills.map((s) => s.slug).join(", ")}`);
38539
+ }
38421
38540
  lines.push("");
38422
38541
  lines.push("## Recommendations");
38423
38542
  if (args.inventory.highConfidenceClusters > 0) {
@@ -39912,7 +40031,7 @@ var init_curate = __esm(() => {
39912
40031
  // src/tools/co-change-analyzer.ts
39913
40032
  import * as child_process3 from "child_process";
39914
40033
  import { randomUUID as randomUUID2 } from "crypto";
39915
- import { readdir, readFile as readFile7, stat as stat2 } from "fs/promises";
40034
+ import { readdir, readFile as readFile8, stat as stat2 } from "fs/promises";
39916
40035
  import * as path21 from "path";
39917
40036
  import { promisify } from "util";
39918
40037
  function getExecFileAsync() {
@@ -40039,7 +40158,7 @@ async function getStaticEdges(directory) {
40039
40158
  const sourceFiles = await scanSourceFiles(directory);
40040
40159
  for (const sourceFile of sourceFiles) {
40041
40160
  try {
40042
- const content = await readFile7(sourceFile, "utf-8");
40161
+ const content = await readFile8(sourceFile, "utf-8");
40043
40162
  const importRegex = /(?:import|require)\s*(?:\(?\s*['"`]|.*?from\s+['"`])([^'"`]+)['"`]/g;
40044
40163
  for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
40045
40164
  const importPath = match[1].trim();
@@ -40823,11 +40942,11 @@ var init_version_check = __esm(() => {
40823
40942
 
40824
40943
  // src/services/knowledge-diagnostics.ts
40825
40944
  import { existsSync as existsSync14 } from "fs";
40826
- import { readFile as readFile8 } from "fs/promises";
40945
+ import { readFile as readFile9 } from "fs/promises";
40827
40946
  async function readRawLines(filePath) {
40828
40947
  if (!existsSync14(filePath))
40829
40948
  return { entries: [], corrupt: 0 };
40830
- const content = await readFile8(filePath, "utf-8");
40949
+ const content = await readFile9(filePath, "utf-8");
40831
40950
  const entries = [];
40832
40951
  let corrupt = 0;
40833
40952
  for (const line of content.split(`
@@ -46047,7 +46166,7 @@ var KNOWLEDGE_SCHEMA_VERSION = 2;
46047
46166
  // src/hooks/knowledge-migrator.ts
46048
46167
  import { randomUUID as randomUUID3 } from "crypto";
46049
46168
  import { existsSync as existsSync19, readFileSync as readFileSync13 } from "fs";
46050
- import { mkdir as mkdir8, readFile as readFile9, writeFile as writeFile7 } from "fs/promises";
46169
+ import { mkdir as mkdir8, readFile as readFile10, writeFile as writeFile7 } from "fs/promises";
46051
46170
  import * as path29 from "path";
46052
46171
  async function migrateKnowledgeToExternal(_directory, _config) {
46053
46172
  return {
@@ -46080,7 +46199,7 @@ async function migrateContextToKnowledge(directory, config3) {
46080
46199
  skippedReason: "no-context-file"
46081
46200
  };
46082
46201
  }
46083
- const contextContent = await readFile9(contextPath, "utf-8");
46202
+ const contextContent = await readFile10(contextPath, "utf-8");
46084
46203
  if (contextContent.trim().length === 0) {
46085
46204
  return {
46086
46205
  migrated: false,
@@ -47329,7 +47448,7 @@ import { existsSync as existsSync20 } from "fs";
47329
47448
  import {
47330
47449
  appendFile as appendFile5,
47331
47450
  mkdir as mkdir9,
47332
- readFile as readFile10,
47451
+ readFile as readFile11,
47333
47452
  rename as rename6,
47334
47453
  writeFile as writeFile8
47335
47454
  } from "fs/promises";
@@ -47678,7 +47797,7 @@ function validateLoadedProposals(values, config3) {
47678
47797
  async function readJsonl(filePath) {
47679
47798
  if (!existsSync20(filePath))
47680
47799
  return [];
47681
- const content = await readFile10(filePath, "utf-8");
47800
+ const content = await readFile11(filePath, "utf-8");
47682
47801
  const records = [];
47683
47802
  for (const line of content.split(`
47684
47803
  `)) {
@@ -47763,7 +47882,7 @@ var init_prompt_block = __esm(() => {
47763
47882
 
47764
47883
  // src/memory/jsonl-migration.ts
47765
47884
  import { existsSync as existsSync21 } from "fs";
47766
- import { copyFile, mkdir as mkdir10, readFile as readFile11, stat as stat3, writeFile as writeFile9 } from "fs/promises";
47885
+ import { copyFile, mkdir as mkdir10, readFile as readFile12, stat as stat3, writeFile as writeFile9 } from "fs/promises";
47767
47886
  import * as path31 from "path";
47768
47887
  function resolveMemoryStorageDir(rootDirectory, config3 = {}) {
47769
47888
  const resolved = resolveConfig(config3);
@@ -47827,7 +47946,7 @@ async function readMigrationReport(rootDirectory, config3 = {}) {
47827
47946
  if (!existsSync21(reportPath))
47828
47947
  return null;
47829
47948
  try {
47830
- return JSON.parse(await readFile11(reportPath, "utf-8"));
47949
+ return JSON.parse(await readFile12(reportPath, "utf-8"));
47831
47950
  } catch {
47832
47951
  return null;
47833
47952
  }
@@ -47928,7 +48047,7 @@ async function readJsonlRows(filePath) {
47928
48047
  if (!existsSync21(filePath)) {
47929
48048
  return { rows: [], invalidRows: [], totalRows: 0 };
47930
48049
  }
47931
- const content = await readFile11(filePath, "utf-8");
48050
+ const content = await readFile12(filePath, "utf-8");
47932
48051
  const rows = [];
47933
48052
  const invalidRows = [];
47934
48053
  let totalRows = 0;
@@ -93,6 +93,8 @@ export declare const HooksConfigSchema: z.ZodObject<{
93
93
  agent_awareness_max_chars: z.ZodDefault<z.ZodNumber>;
94
94
  delegation_gate: z.ZodDefault<z.ZodBoolean>;
95
95
  delegation_max_chars: z.ZodDefault<z.ZodNumber>;
96
+ background_subagents: z.ZodDefault<z.ZodBoolean>;
97
+ background_pending_timeout_minutes: z.ZodDefault<z.ZodNumber>;
96
98
  }, z.core.$strip>;
97
99
  export type HooksConfig = z.infer<typeof HooksConfigSchema>;
98
100
  export declare const ScoringWeightsSchema: z.ZodObject<{
@@ -958,6 +960,8 @@ export declare const PluginConfigSchema: z.ZodObject<{
958
960
  agent_awareness_max_chars: z.ZodDefault<z.ZodNumber>;
959
961
  delegation_gate: z.ZodDefault<z.ZodBoolean>;
960
962
  delegation_max_chars: z.ZodDefault<z.ZodNumber>;
963
+ background_subagents: z.ZodDefault<z.ZodBoolean>;
964
+ background_pending_timeout_minutes: z.ZodDefault<z.ZodNumber>;
961
965
  }, z.core.$strip>>;
962
966
  gates: z.ZodOptional<z.ZodObject<{
963
967
  syntax_check: z.ZodDefault<z.ZodObject<{