opencode-swarm 7.54.0 → 7.55.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.55.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),
@@ -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<{