opencode-swarm 7.138.4 → 7.139.1
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.
- package/README.md +7 -6
- package/dist/cli/{config-doctor-31yppse4.js → config-doctor-tgnr91q3.js} +2 -2
- package/dist/cli/{core-65c7rvqf.js → core-g2dfh9fq.js} +1 -1
- package/dist/cli/{curation-policy-hs9pc1rp.js → curation-policy-2qnhgdtt.js} +7 -7
- package/dist/cli/{curator-drift-jpap6hv4.js → curator-drift-vfh07mqy.js} +5 -2
- package/dist/cli/{curator-llm-factory-wm27ra25.js → curator-llm-factory-1ftzece0.js} +28 -28
- package/dist/cli/{curator-54n1y4j8.js → curator-nbsag633.js} +28 -28
- package/dist/cli/{evidence-summary-service-hrxcf29a.js → evidence-summary-service-kzxr71yd.js} +9 -9
- package/dist/cli/{gate-evidence-ecx4djaw.js → gate-evidence-z2b1ymnw.js} +5 -5
- package/dist/cli/{guardrail-explain-1729nse8.js → guardrail-explain-vb9k9zwm.js} +29 -29
- package/dist/cli/{guardrail-log-yjj5srkw.js → guardrail-log-0djnvfas.js} +3 -3
- package/dist/cli/{hive-promoter-es2x2b0e.js → hive-promoter-bpt26yew.js} +28 -28
- package/dist/cli/{index-mycqmsz5.js → index-0wp67gqj.js} +452 -159
- package/dist/cli/{index-c60whfgh.js → index-0ymhx85c.js} +2 -2
- package/dist/cli/{index-5q009kc4.js → index-15526t16.js} +8 -3
- package/dist/cli/{index-smsy2p8n.js → index-3eddtgxt.js} +1 -1
- package/dist/cli/{index-4fmztnrq.js → index-4xbyy6aw.js} +2 -2
- package/dist/cli/{index-xybtaph7.js → index-5j6chvj8.js} +1 -1
- package/dist/cli/{index-39k6y018.js → index-5w7ekpy3.js} +2 -2
- package/dist/cli/{index-z7jgvm9b.js → index-5zyk8qgv.js} +2 -2
- package/dist/cli/{index-en4fb04r.js → index-668dpw5e.js} +1 -1
- package/dist/cli/{index-m8txs2a9.js → index-91s653xx.js} +1 -1
- package/dist/cli/{index-y28n2wmq.js → index-a12906mj.js} +1 -1
- package/dist/cli/{index-10mz5js6.js → index-a5eba0t8.js} +2 -2
- package/dist/cli/{index-57stye65.js → index-aymt7hzw.js} +6 -3
- package/dist/cli/{index-1g6xpjk3.js → index-db7wf6ke.js} +7 -7
- package/dist/cli/{index-m1pwkwfe.js → index-dw09fz3w.js} +1 -1
- package/dist/cli/{index-fsrp8wp3.js → index-ed5ykebp.js} +25 -2
- package/dist/cli/{index-jbge0dyh.js → index-en6qq954.js} +3 -3
- package/dist/cli/{index-tqshdzaw.js → index-gvhvz176.js} +2 -2
- package/dist/cli/{index-e1kgxchq.js → index-hgrkhxbk.js} +2 -2
- package/dist/cli/{index-33q2gmgd.js → index-hn0mdcmj.js} +1 -1
- package/dist/cli/{index-a9xny52r.js → index-ht4q3548.js} +2 -2
- package/dist/cli/{index-q344kj84.js → index-prmb5qfp.js} +2 -2
- package/dist/cli/{index-x20cgr2q.js → index-q7ezzgwq.js} +16 -2
- package/dist/cli/{index-ngqxzkqm.js → index-s608d3hw.js} +2 -2
- package/dist/cli/{index-50mwt8fx.js → index-vxbppwvf.js} +5 -5
- package/dist/cli/{index-rrxcfxrd.js → index-wrt6m5ze.js} +1 -1
- package/dist/cli/{index-4qzeef9h.js → index-x9q4bbhd.js} +1 -1
- package/dist/cli/{index-m40ea3zk.js → index-xec724kg.js} +2 -2
- package/dist/cli/{index-k27a4e5g.js → index-xka8s8jw.js} +30 -30
- package/dist/cli/{index-a18hk2fw.js → index-xr07r80d.js} +513 -483
- package/dist/cli/{index-gm3pjfhb.js → index-y1zcsf2a.js} +1 -1
- package/dist/cli/{index-tjwz5tct.js → index-ywysg26s.js} +4 -4
- package/dist/cli/index.js +28 -28
- package/dist/cli/{knowledge-escalator-s6z1x65b.js → knowledge-escalator-pjg2x75c.js} +8 -8
- package/dist/cli/{knowledge-events-nnyn6hst.js → knowledge-events-vxheptk8.js} +6 -6
- package/dist/cli/{knowledge-link-xc6yhxmz.js → knowledge-link-3tq8w6gs.js} +5 -5
- package/dist/cli/{knowledge-store-x4yfc7v3.js → knowledge-store-36mascwz.js} +6 -6
- package/dist/cli/{knowledge-validator-1v047gsa.js → knowledge-validator-anats6rj.js} +9 -9
- package/dist/cli/{pending-delegations-7dbc0j2a.js → pending-delegations-tmyf18p4.js} +5 -5
- package/dist/cli/{pr-subscriptions-m943zn6g.js → pr-subscriptions-hhx6yk24.js} +5 -5
- package/dist/cli/{runner-x1hy047y.js → runner-w30gazew.js} +6 -6
- package/dist/cli/{scan-cursor-jxajj6s7.js → scan-cursor-apbz9eez.js} +7 -7
- package/dist/cli/{schema-fj32eyae.js → schema-x9fr2ey8.js} +3 -1
- package/dist/cli/{scope-persistence-w7zen2wz.js → scope-persistence-3f0d4fwv.js} +4 -4
- package/dist/cli/{skill-generator-rt0jqcsa.js → skill-generator-20czn4kd.js} +10 -10
- package/dist/cli/{telemetry-0yqed3jv.js → telemetry-4tgrfhaz.js} +1 -1
- package/dist/cli/{worktree-collision-ownership-hjrt5hx2.js → worktree-collision-ownership-vexb0b6r.js} +38 -8
- package/dist/config/context-window.d.ts +210 -0
- package/dist/config/schema.d.ts +18 -0
- package/dist/evaluation/ephemeral-agent-dispatcher.d.ts +18 -0
- package/dist/evaluation/model-dispatcher.d.ts +1 -0
- package/dist/hooks/delegate-ack-collector.d.ts +1 -1
- package/dist/hooks/delegation-gate/worktree-collision-ownership.d.ts +7 -0
- package/dist/hooks/delegation-gate/worktree-isolation.d.ts +2 -1
- package/dist/hooks/delegation-gate/worktree-provisioning-owner.d.ts +6 -0
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/init-orphan-recovery.d.ts +4 -1
- package/dist/hooks/knowledge-application-gate.d.ts +2 -1
- package/dist/hooks/knowledge-application.d.ts +5 -4
- package/dist/hooks/knowledge-injector.d.ts +16 -0
- package/dist/hooks/knowledge-types.d.ts +1 -1
- package/dist/hooks/messages-transform.d.ts +18 -0
- package/dist/hooks/model-limits.d.ts +88 -30
- package/dist/index.js +501 -499
- package/dist/memory/injector.d.ts +2 -0
- package/dist/plan/manager.d.ts +2 -0
- package/dist/services/context-budget-service.d.ts +6 -1
- package/dist/services/run-memory.d.ts +53 -5
- package/dist/services/status-service.d.ts +14 -0
- package/dist/state.d.ts +37 -0
- package/dist/tools/context-status.d.ts +3 -1
- package/dist/tools/update-task-status.d.ts +2 -0
- package/dist/turbo/lean/runner.d.ts +10 -0
- package/dist/utils/ephemeral-session-teardown.d.ts +98 -0
- package/dist/utils/path-security.d.ts +95 -1
- package/dist/utils/swarm-artifact-cache.d.ts +13 -0
- package/dist/worktree/index.d.ts +1 -1
- package/dist/worktree/merge.d.ts +36 -3
- package/package.json +1 -1
|
@@ -34,9 +34,11 @@ export interface MemoryLifecycleHooks {
|
|
|
34
34
|
}
|
|
35
35
|
export declare function createMemoryLifecycleHooks(options: MemoryLifecycleHookOptions): MemoryLifecycleHooks;
|
|
36
36
|
declare function messagesContainRecall(messages: unknown[]): boolean;
|
|
37
|
+
declare function recallMessageInsertIndex(messages: unknown[]): number;
|
|
37
38
|
declare function compactText(text: string): string;
|
|
38
39
|
export type { ProposeMemoryInput, MemoryKind };
|
|
39
40
|
export declare const _test_exports: {
|
|
40
41
|
compactText: typeof compactText;
|
|
41
42
|
messagesContainRecall: typeof messagesContainRecall;
|
|
43
|
+
recallMessageInsertIndex: typeof recallMessageInsertIndex;
|
|
42
44
|
};
|
package/dist/plan/manager.d.ts
CHANGED
|
@@ -41,6 +41,7 @@ export interface AcknowledgedRemovals {
|
|
|
41
41
|
import { type Plan, type RuntimePlan, type TaskStatus } from '../config/plan-schema';
|
|
42
42
|
import { isGitRepo } from '../git/branch';
|
|
43
43
|
import { getWorktreeMergeFailure } from '../hooks/delegation-gate/worktree-merge-status';
|
|
44
|
+
import { recordTaskAttempt } from '../services/run-memory.js';
|
|
44
45
|
import { isEpicModeActiveForProject } from '../turbo/epic/state.js';
|
|
45
46
|
import { commitTaskCompletion } from '../turbo/epic/task-commit.js';
|
|
46
47
|
import { readTaskScopes } from '../turbo/lean/conflicts.js';
|
|
@@ -64,6 +65,7 @@ export declare const _internals: {
|
|
|
64
65
|
readTaskScopes: typeof readTaskScopes;
|
|
65
66
|
commitTaskCompletion: typeof commitTaskCompletion;
|
|
66
67
|
getWorktreeMergeFailure: typeof getWorktreeMergeFailure;
|
|
68
|
+
recordTaskAttempt: typeof recordTaskAttempt;
|
|
67
69
|
};
|
|
68
70
|
/** @internal Test seam for snapshot retry helper */
|
|
69
71
|
export declare const _snapshot_test_exports: {
|
|
@@ -42,7 +42,12 @@ export interface ContextBudgetReport {
|
|
|
42
42
|
export interface ContextBudgetConfig {
|
|
43
43
|
/** Enable or disable budget monitoring */
|
|
44
44
|
enabled: boolean;
|
|
45
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* The context-window denominator, in tokens. Production callers derive this
|
|
47
|
+
* per-model via `resolveContextWindowTokens` (`src/config/context-window.ts`)
|
|
48
|
+
* and never pass a constant; `DEFAULT_CONTEXT_BUDGET_CONFIG.budgetTokens`
|
|
49
|
+
* below is only the no-information floor.
|
|
50
|
+
*/
|
|
46
51
|
budgetTokens: number;
|
|
47
52
|
/** Warning threshold percentage (default: 70) */
|
|
48
53
|
warningPct: number;
|
|
@@ -16,7 +16,28 @@ export interface RunMemoryEntry {
|
|
|
16
16
|
taskFingerprint: string;
|
|
17
17
|
/** Which agent executed the task (e.g. "coder") */
|
|
18
18
|
agent: string;
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Outcome of the task execution.
|
|
21
|
+
*
|
|
22
|
+
* PRODUCER COVERAGE (keep this note accurate — directive 2, no unwired code):
|
|
23
|
+
* - `pass` / `fail` are produced today by TWO call sites, deliberately split:
|
|
24
|
+
* 1. `plan/manager.updateTaskStatus` records the TERMINAL outcome after
|
|
25
|
+
* savePlan succeeds — `completed` -> pass, `blocked` -> fail. It lives
|
|
26
|
+
* there, not in the `update_task_status` tool, because the tool is only
|
|
27
|
+
* one of two writers: the council APPROVE fast-path completes tasks via
|
|
28
|
+
* `advanceTaskStateAndPersist` (src/state.ts) with no tool call.
|
|
29
|
+
* 2. `update_task_status` records a `fail` when a council or QA gate
|
|
30
|
+
* BLOCKS a completion. That is a refusal, not a status transition, so
|
|
31
|
+
* it never reaches updateTaskStatus and must be recorded at the tool.
|
|
32
|
+
* - `retry` has a CONSUMER (`summarizeTask` below treats it like `fail`) but
|
|
33
|
+
* no producer. It is deliberately retained for the guardrails
|
|
34
|
+
* transient-retry path (AGENTS.md invariant 9), which owns retry
|
|
35
|
+
* accounting and is out of scope here. Do not add a `retry` producer from
|
|
36
|
+
* the task-status path — a gate block is a real failure, not a retry.
|
|
37
|
+
* - `skip` has neither producer nor consumer (`summarizeTask` ignores it).
|
|
38
|
+
* It is retained only so pre-existing `.swarm/run-memory.jsonl` lines
|
|
39
|
+
* written by hand or by older builds still parse.
|
|
40
|
+
*/
|
|
20
41
|
outcome: 'pass' | 'fail' | 'retry' | 'skip';
|
|
21
42
|
/** 1-indexed attempt number */
|
|
22
43
|
attemptNumber: number;
|
|
@@ -51,12 +72,39 @@ export declare function recordOutcome(directory: string, entry: RunMemoryEntry):
|
|
|
51
72
|
*/
|
|
52
73
|
export declare function getTaskHistory(directory: string, taskId: string): Promise<RunMemoryEntry[]>;
|
|
53
74
|
/**
|
|
54
|
-
*
|
|
75
|
+
* Input for {@link recordTaskAttempt}. `attemptNumber` and `taskFingerprint`
|
|
76
|
+
* are derived, not supplied — callers know the task, not its history.
|
|
77
|
+
*/
|
|
78
|
+
export interface TaskAttemptInput {
|
|
79
|
+
/** Plan.json task ID (e.g. "3.2") */
|
|
80
|
+
taskId: string;
|
|
81
|
+
/** Which agent executed the task (e.g. "coder") */
|
|
82
|
+
agent: string;
|
|
83
|
+
/** Outcome of this attempt */
|
|
84
|
+
outcome: RunMemoryEntry['outcome'];
|
|
85
|
+
/** One-line failure reason — required in practice for fail/retry to be useful */
|
|
86
|
+
failureReason?: string;
|
|
87
|
+
/** Plan `files_touched` for the task; feeds the fingerprint */
|
|
88
|
+
fileTargets?: string[];
|
|
89
|
+
/** Wall-clock time in milliseconds */
|
|
90
|
+
durationMs?: number;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Record one task attempt, deriving the attempt number from prior history.
|
|
94
|
+
*
|
|
95
|
+
* This is the single production producer for `.swarm/run-memory.jsonl`, which
|
|
96
|
+
* `getRunMemorySummary` reads back for cross-turn failure-pattern avoidance.
|
|
97
|
+
*
|
|
98
|
+
* Advisory-only and fail-open by contract: run memory is context enrichment,
|
|
99
|
+
* never a correctness gate, so a write failure must never surface to — or
|
|
100
|
+
* abort — the caller's primary operation (a task status update). The work is
|
|
101
|
+
* still awaited rather than fire-and-forget, so the entry is durable before the
|
|
102
|
+
* caller returns and a test can observe it.
|
|
55
103
|
*
|
|
56
104
|
* @param directory - The swarm workspace directory
|
|
57
|
-
* @
|
|
105
|
+
* @param input - The attempt to record
|
|
58
106
|
*/
|
|
59
|
-
export declare function
|
|
107
|
+
export declare function recordTaskAttempt(directory: string, input: TaskAttemptInput): Promise<void>;
|
|
60
108
|
/**
|
|
61
109
|
* Group entries by taskId
|
|
62
110
|
*/
|
|
@@ -79,8 +127,8 @@ export declare function getRunMemorySummary(directory: string): Promise<string |
|
|
|
79
127
|
export declare const _internals: {
|
|
80
128
|
generateTaskFingerprint: typeof generateTaskFingerprint;
|
|
81
129
|
recordOutcome: typeof recordOutcome;
|
|
130
|
+
recordTaskAttempt: typeof recordTaskAttempt;
|
|
82
131
|
getTaskHistory: typeof getTaskHistory;
|
|
83
|
-
getFailures: typeof getFailures;
|
|
84
132
|
getRunMemorySummary: typeof getRunMemorySummary;
|
|
85
133
|
groupByTaskId: typeof groupByTaskId;
|
|
86
134
|
summarizeTask: typeof summarizeTask;
|
|
@@ -72,6 +72,20 @@ export interface StatusData {
|
|
|
72
72
|
leanPauseReason?: string;
|
|
73
73
|
/** Last known context budget percentage (0-100), or null if not yet measured */
|
|
74
74
|
contextBudgetPct: number | null;
|
|
75
|
+
/**
|
|
76
|
+
* The DENOMINATOR `contextBudgetPct` was measured against, in tokens, or
|
|
77
|
+
* null when no budget report has run. Carried alongside the percentage
|
|
78
|
+
* because the denominator is now derived per-model (`model.limit.context`)
|
|
79
|
+
* rather than being a constant this renderer could assume. The renderer
|
|
80
|
+
* previously back-computed the estimate from
|
|
81
|
+
* `DEFAULT_CONTEXT_BUDGET_CONFIG.budgetTokens`, which was **40000** (not
|
|
82
|
+
* 128000 — that was the schema default for `model_limits.default`, a
|
|
83
|
+
* different constant on a different path). So the status line printed a
|
|
84
|
+
* figure that did not match the percentage beside it for any user whose
|
|
85
|
+
* warnings fired against anything other than 40000 — i.e. every user with a
|
|
86
|
+
* `model_limits` override, and now everyone, since the live window is used.
|
|
87
|
+
*/
|
|
88
|
+
contextBudgetTokens?: number | null;
|
|
75
89
|
/** Number of context compaction events triggered this session */
|
|
76
90
|
compactionCount: number;
|
|
77
91
|
/** ISO timestamp of last compaction snapshot, or null if none */
|
package/dist/state.d.ts
CHANGED
|
@@ -598,6 +598,24 @@ export declare const swarmState: {
|
|
|
598
598
|
generatedAgentNames: string[];
|
|
599
599
|
/** Last known context budget percentage (0-100), updated by system-enhancer */
|
|
600
600
|
lastBudgetPct: number;
|
|
601
|
+
/**
|
|
602
|
+
* The DENOMINATOR `lastBudgetPct` was computed against, in tokens. Written
|
|
603
|
+
* by system-enhancer at the same two statements that write `lastBudgetPct`,
|
|
604
|
+
* because a percentage without its denominator cannot be turned back into a
|
|
605
|
+
* token estimate. `/swarm status` used to reconstruct the estimate from a
|
|
606
|
+
* hardcoded constant, so a user whose real denominator differed saw a token
|
|
607
|
+
* figure that did not match the percentage next to it. 0 means "no budget
|
|
608
|
+
* report has run yet".
|
|
609
|
+
*/
|
|
610
|
+
lastBudgetTokens: number;
|
|
611
|
+
/**
|
|
612
|
+
* Live `model.limit.context` per session — keyed by sessionID, recorded by
|
|
613
|
+
* the `experimental.chat.system.transform` hook (the only hook the host
|
|
614
|
+
* gives a `Model` to) and read by the `experimental.chat.messages.transform`
|
|
615
|
+
* consumers, which receive messages but no model object. Bounded via
|
|
616
|
+
* {@link setLiveContextWindow} (AGENTS.md invariant 8).
|
|
617
|
+
*/
|
|
618
|
+
liveContextWindows: Map<string, number>;
|
|
601
619
|
/** Per-session guardrail state — keyed by sessionID */
|
|
602
620
|
agentSessions: Map<string, AgentSessionState>;
|
|
603
621
|
/** In-flight rehydration promises — awaited by rehydrateState before clearing agentSessions */
|
|
@@ -1142,6 +1160,25 @@ export declare function ensureSessionEnvironment(sessionId: string): Environment
|
|
|
1142
1160
|
export declare const MAX_TRACKED_CRITICAL_SHOWN = 500;
|
|
1143
1161
|
export declare const MAX_TRACKED_KNOWLEDGE_ACKS = 5000;
|
|
1144
1162
|
export declare const MAX_TRACKED_GATE_DENIALS = 500;
|
|
1163
|
+
export declare const MAX_TRACKED_CONTEXT_WINDOWS = 500;
|
|
1164
|
+
/**
|
|
1165
|
+
* Record the live `model.limit.context` the host reported for `sessionID`,
|
|
1166
|
+
* FIFO-evicting the oldest entry past {@link MAX_TRACKED_CONTEXT_WINDOWS}.
|
|
1167
|
+
*
|
|
1168
|
+
* Only accepts a finite number ≥ 1 — the caller (`src/config/context-window.ts`
|
|
1169
|
+
* `isUsableContextWindow`) applies the real plausibility floor, and storing a
|
|
1170
|
+
* junk value here would hand it to every `messages.transform` consumer. A
|
|
1171
|
+
* rejected value leaves any previously recorded window in place rather than
|
|
1172
|
+
* clobbering it, so one malformed turn does not blank a good reading.
|
|
1173
|
+
*/
|
|
1174
|
+
export declare function setLiveContextWindow(sessionID: string | undefined, tokens: unknown): void;
|
|
1175
|
+
/**
|
|
1176
|
+
* Read the live context window recorded for `sessionID`, or `undefined` when
|
|
1177
|
+
* no `system.transform` has run for it yet (first turn of a session, or a
|
|
1178
|
+
* consumer that never sees a sessionID). Callers must degrade to the static
|
|
1179
|
+
* resolution rungs rather than assume a value.
|
|
1180
|
+
*/
|
|
1181
|
+
export declare function getLiveContextWindow(sessionID: string | undefined): number | undefined;
|
|
1145
1182
|
/** Set the critical shown ids for a session, FIFO-evicting the oldest entry
|
|
1146
1183
|
* if the cap is exceeded. Re-setting an existing key keeps insertion order
|
|
1147
1184
|
* fresh for that key (delete-then-set). */
|
|
@@ -79,9 +79,11 @@ export declare const _internals: {
|
|
|
79
79
|
* @param warnThreshold - Usage ratio that triggers 'warn' state (default 0.7)
|
|
80
80
|
* @param criticalThreshold - Usage ratio that triggers 'critical' state (default 0.9)
|
|
81
81
|
* @param modelLimitsConfig - Model-specific limit overrides from config
|
|
82
|
+
* @param liveContextLimit - Live `model.limit.context` recorded for the session
|
|
83
|
+
* by the system.transform hook; `undefined` when none has been seen yet
|
|
82
84
|
* @returns Context status with token usage, limit, and threshold state
|
|
83
85
|
*/
|
|
84
|
-
declare function computeContextHeadroom(messages: ContextMessage[], warnThreshold?: number, criticalThreshold?: number, modelLimitsConfig?: Record<string, number
|
|
86
|
+
declare function computeContextHeadroom(messages: ContextMessage[], warnThreshold?: number, criticalThreshold?: number, modelLimitsConfig?: Record<string, number>, liveContextLimit?: unknown): ContextStatusResult;
|
|
85
87
|
/**
|
|
86
88
|
* context_status tool — read-only context window headroom report.
|
|
87
89
|
*
|
|
@@ -6,6 +6,7 @@ import type { ToolContext, ToolDefinition } from '@opencode-ai/plugin/tool';
|
|
|
6
6
|
import { readTaskEvidenceRaw } from '../gate-evidence.js';
|
|
7
7
|
import { tryAcquireLock } from '../parallel/file-locks.js';
|
|
8
8
|
import { loadPlan, updateTaskStatus } from '../plan/manager';
|
|
9
|
+
import { recordTaskAttempt } from '../services/run-memory.js';
|
|
9
10
|
import { hasActiveLeanTurbo, hasActiveTurboMode } from '../state';
|
|
10
11
|
import { type ReviewerGateEvidenceKind, type ReviewerGateReasonCode } from '../telemetry.js';
|
|
11
12
|
import { verifyLeanTurboTaskCompletion } from '../turbo/lean/task-completion';
|
|
@@ -26,6 +27,7 @@ export declare const _internals: {
|
|
|
26
27
|
verifyLeanTurboTaskCompletion: typeof verifyLeanTurboTaskCompletion;
|
|
27
28
|
hasPassedDurableGateEvidence: typeof hasPassedDurableGateEvidence;
|
|
28
29
|
emitReviewerGateDecision: (sessionId: string, taskId: string, blocked: boolean, reasonCode: ReviewerGateReasonCode, evidenceKind: ReviewerGateEvidenceKind) => void;
|
|
30
|
+
recordTaskAttempt: typeof recordTaskAttempt;
|
|
29
31
|
};
|
|
30
32
|
/**
|
|
31
33
|
* Arguments for the update_task_status tool
|
|
@@ -81,6 +81,16 @@ interface SessionClient {
|
|
|
81
81
|
id: string;
|
|
82
82
|
};
|
|
83
83
|
}): Promise<void>;
|
|
84
|
+
/**
|
|
85
|
+
* Optional graceful abort. Present on the real opencode SDK session at
|
|
86
|
+
* runtime (absent on minimal test fakes). Teardown awaits it before delete
|
|
87
|
+
* so opencode flushes the final part/message (#2123).
|
|
88
|
+
*/
|
|
89
|
+
abort?(options: {
|
|
90
|
+
path: {
|
|
91
|
+
id: string;
|
|
92
|
+
};
|
|
93
|
+
}): Promise<unknown>;
|
|
84
94
|
}
|
|
85
95
|
/**
|
|
86
96
|
* Result of a single lane dispatch (session creation + prompt).
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ephemeral opencode session teardown — abort-then-delete ordering that closes
|
|
3
|
+
* the FOREIGN KEY constraint race described in issue #2123.
|
|
4
|
+
*
|
|
5
|
+
* ## Why this exists
|
|
6
|
+
*
|
|
7
|
+
* opencode writes the final assistant `part`/`message` **asynchronously**, in
|
|
8
|
+
* `SessionProcessor.cleanup`, which runs as a stream-drain finalizer that can
|
|
9
|
+
* settle AFTER `session.prompt()` resolves. The `part.message_id` foreign key
|
|
10
|
+
* is `ON DELETE CASCADE`, so a `session.delete()` that lands before that flush
|
|
11
|
+
* cascade-removes the parent `message` row; opencode's late `updatePart`/
|
|
12
|
+
* `updateMessage` then fails with `SQLiteError: FOREIGN KEY constraint failed`
|
|
13
|
+
* (in opencode's own log — the plugin's `session.delete()` promise resolves
|
|
14
|
+
* fine, which is why a `.catch(() => {})` on the delete cannot prevent it).
|
|
15
|
+
*
|
|
16
|
+
* ## Why awaiting `session.abort()` fixes it
|
|
17
|
+
*
|
|
18
|
+
* `POST /session/{id}/abort` resolves only after `SessionRunState.cancel` →
|
|
19
|
+
* `Runner.cancel` → `Fiber.interrupt(runFiber)`. Effect's `Fiber.interrupt` runs
|
|
20
|
+
* every finalizer on the target fiber before resolving, and the run-loop fiber
|
|
21
|
+
* carries `Effect.ensuring(cleanup())`. Therefore, once `await session.abort()`
|
|
22
|
+
* resolves, opencode has already flushed the final part/message (or the session
|
|
23
|
+
* was already idle, in which case `cleanup` ran during natural completion).
|
|
24
|
+
* Source: opencode v1.18.16 — `packages/opencode/src/session/{run-state,runner,
|
|
25
|
+
* processor}.ts`.
|
|
26
|
+
*
|
|
27
|
+
* Because `cleanup` awaits each pending tool-call deferred with a 250 ms
|
|
28
|
+
* timeout, the abort step can block up to ~250 ms+ under interruption. Its
|
|
29
|
+
* bounded timeout must stay well above that (default 5 s) or it would give up
|
|
30
|
+
* before the flush lands and re-introduce the race.
|
|
31
|
+
*/
|
|
32
|
+
import { log } from './logger.js';
|
|
33
|
+
/** Minimum lifecycle surface for tearing an ephemeral session down. */
|
|
34
|
+
export interface EphemeralSessionLifecycle {
|
|
35
|
+
/** Server-side abort; optional because some session shims (tests, older hosts) lack it. */
|
|
36
|
+
abort?: (args: {
|
|
37
|
+
path: {
|
|
38
|
+
id: string;
|
|
39
|
+
};
|
|
40
|
+
}) => Promise<unknown>;
|
|
41
|
+
/** Hard delete the session and cascade its rows. */
|
|
42
|
+
delete: (args: {
|
|
43
|
+
path: {
|
|
44
|
+
id: string;
|
|
45
|
+
};
|
|
46
|
+
}) => Promise<unknown>;
|
|
47
|
+
}
|
|
48
|
+
export declare const DEFAULT_EPHEMERAL_ABORT_TIMEOUT_MS = 5000;
|
|
49
|
+
export declare const DEFAULT_EPHEMERAL_TEARDOWN_DELETE_TIMEOUT_MS = 2000;
|
|
50
|
+
export interface TeardownEphemeralSessionOptions {
|
|
51
|
+
/** Bounded timeout for the graceful abort step. Must stay > ~500 ms. */
|
|
52
|
+
abortTimeoutMs?: number;
|
|
53
|
+
/** Bounded timeout for the hard delete step. */
|
|
54
|
+
deleteTimeoutMs?: number;
|
|
55
|
+
/**
|
|
56
|
+
* Skip the abort step. Use only for sessions that were never prompted (no
|
|
57
|
+
* final part flush pending) — e.g. a session that timed out during
|
|
58
|
+
* `session.create` before `session.prompt` was ever called.
|
|
59
|
+
*/
|
|
60
|
+
skipAbort?: boolean;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Race a session HTTP call against a bounded timer. The structural lifecycle
|
|
64
|
+
* type carries no `signal` (it must also fit the `SessionOps` shim), so the
|
|
65
|
+
* timer bounds the AWAIT only — it cannot pre-empt the in-flight SDK request,
|
|
66
|
+
* which is allowed to settle in the background. Mirrors `boundedDeleteEphemeralSession`
|
|
67
|
+
* in `src/evaluation/ephemeral-agent-dispatcher.ts`. Best-effort: never throws.
|
|
68
|
+
*/
|
|
69
|
+
declare function boundedSessionCall(call: () => Promise<unknown>, timeoutMs: number, timeoutLabel: string, failureLabel: string, sessionId: string): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Bounded graceful abort. Guarantees opencode has flushed the session's final
|
|
72
|
+
* part/message before resolving (or that the session was already idle). Never
|
|
73
|
+
* throws; logs timeouts/failures via the debug-gated logger.
|
|
74
|
+
*/
|
|
75
|
+
export declare function boundedAbortEphemeralSession(session: EphemeralSessionLifecycle, sessionId: string, timeoutMs?: number): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Bounded hard delete. Never throws; logs timeouts/failures.
|
|
78
|
+
*/
|
|
79
|
+
export declare function boundedDeleteEphemeralSession(session: EphemeralSessionLifecycle, sessionId: string, timeoutMs?: number): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Tear an ephemeral session down without racing opencode's final part/message
|
|
82
|
+
* flush (#2123): a bounded, awaited `session.abort()` (flush) FOLLOWED BY a
|
|
83
|
+
* bounded `session.delete()`. Best-effort: never throws.
|
|
84
|
+
*
|
|
85
|
+
* Callers that need cleanup guaranteed before they return should `await` this.
|
|
86
|
+
* Fire-and-forget callers may `void` it — the abort→delete ordering still holds
|
|
87
|
+
* inside the unit, so the FK race is closed regardless; only the caller's own
|
|
88
|
+
* completion timing is detached (process exit before completion leaks a
|
|
89
|
+
* session, same as the prior fire-and-forget delete, and is harmless).
|
|
90
|
+
*/
|
|
91
|
+
export declare function teardownEphemeralSession(session: EphemeralSessionLifecycle, sessionId: string, options?: TeardownEphemeralSessionOptions): Promise<void>;
|
|
92
|
+
export declare const _internals: {
|
|
93
|
+
boundedSessionCall: typeof boundedSessionCall;
|
|
94
|
+
boundedAbort: typeof boundedAbortEphemeralSession;
|
|
95
|
+
boundedDelete: typeof boundedDeleteEphemeralSession;
|
|
96
|
+
log: typeof log;
|
|
97
|
+
};
|
|
98
|
+
export {};
|
|
@@ -17,14 +17,107 @@ export declare function containsPathTraversal(str: string): boolean;
|
|
|
17
17
|
*/
|
|
18
18
|
export declare function containsControlChars(str: string): boolean;
|
|
19
19
|
/**
|
|
20
|
-
* Validate a
|
|
20
|
+
* Validate a **project/workspace root** for safety.
|
|
21
|
+
*
|
|
22
|
+
* Distinct from {@link validateDirectory}, which additionally rejects absolute
|
|
23
|
+
* paths and is therefore only correct for a *relative sub-path*. A workspace
|
|
24
|
+
* root is absolute by contract — `ctx.directory` injected by `createSwarmTool`
|
|
25
|
+
* is always an absolute project root (AGENTS.md invariant 4) — so applying
|
|
26
|
+
* `validateDirectory` to one rejects 100% of real inputs and silently kills
|
|
27
|
+
* whatever feature depends on it. That is exactly how `.swarm/run-memory.jsonl`
|
|
28
|
+
* came to have no producer and a consumer that threw on every call.
|
|
29
|
+
*
|
|
30
|
+
* This validator keeps the checks that are meaningful for a root (empty,
|
|
31
|
+
* traversal segments, control/bidi characters). Containment of the actual FILE
|
|
32
|
+
* is not this function's job and is unchanged: `validateSwarmPath(directory,
|
|
33
|
+
* filename)` resolves the target under `<directory>/.swarm`, rejects any path
|
|
34
|
+
* that escapes it, and rejects a symlinked `.swarm` base.
|
|
35
|
+
*
|
|
36
|
+
* It ALSO requires the root to be absolute and to not be a filesystem or system
|
|
37
|
+
* location — see `validateProjectDirectory` below, which this function
|
|
38
|
+
* delegates to, for why those two checks are load-bearing rather than
|
|
39
|
+
* defensive. In short: `validateSwarmPath` pins the write *inside* the root, so
|
|
40
|
+
* it cannot help when the root itself is `E:\` or `/etc`, and a RELATIVE root
|
|
41
|
+
* resolves `.swarm/` against the host process cwd — the same invariant-4 hazard
|
|
42
|
+
* as an empty root.
|
|
43
|
+
*
|
|
44
|
+
* @param directory - The workspace root to validate
|
|
45
|
+
* @throws Error if the root is invalid
|
|
46
|
+
*/
|
|
47
|
+
export declare function validateWorkspaceRoot(directory: string): void;
|
|
48
|
+
/**
|
|
49
|
+
* Validate a relative directory path for safety.
|
|
21
50
|
* Rejects empty paths, paths with traversal, control characters, and absolute paths.
|
|
22
51
|
* Throws an Error if the directory is invalid.
|
|
23
52
|
*
|
|
53
|
+
* Do NOT use this on a project/workspace root — see {@link validateWorkspaceRoot}.
|
|
54
|
+
*
|
|
24
55
|
* @param directory - The directory string to validate
|
|
25
56
|
* @throws Error if directory is invalid
|
|
26
57
|
*/
|
|
27
58
|
export declare function validateDirectory(directory: string): void;
|
|
59
|
+
/**
|
|
60
|
+
* Validate a TRUSTED, already-absolute project root directory.
|
|
61
|
+
*
|
|
62
|
+
* This is the trust-model counterpart to `validateDirectory` above, NOT a
|
|
63
|
+
* relaxation of it. `validateDirectory` guards UNTRUSTED, RELATIVE sub-path
|
|
64
|
+
* input and therefore rejects absolute paths by design. A project root that
|
|
65
|
+
* the plugin host injects (`ctx.directory`, or the documented direct-CLI /
|
|
66
|
+
* test `process.cwd()` fallback) is ALWAYS absolute, so handing one to
|
|
67
|
+
* `validateDirectory` throws unconditionally — the misapplication that made
|
|
68
|
+
* the context-budget and run-memory features dead on every real invocation
|
|
69
|
+
* (issue #1619 follow-up).
|
|
70
|
+
*
|
|
71
|
+
* What this still enforces — every check that is meaningful for a root:
|
|
72
|
+
* - Non-empty. An empty root makes `path.resolve('', '.swarm')` land on
|
|
73
|
+
* whatever the host process cwd happens to be, which is an invariant-4
|
|
74
|
+
* (`.swarm/` containment) violation, not a harmless no-op.
|
|
75
|
+
* - No traversal and no control / directional-format characters. A root
|
|
76
|
+
* carrying `..`, a NUL byte, or a bidi override is never a legitimate
|
|
77
|
+
* injected project root.
|
|
78
|
+
* - MUST be absolute. A relative root resolves against the host's process cwd
|
|
79
|
+
* — the same invariant-4 hazard as the empty case, and the reason this is a
|
|
80
|
+
* positive requirement rather than merely "absolute is tolerated".
|
|
81
|
+
*
|
|
82
|
+
* WHAT "ABSOLUTE" MEANS HERE IS PARTLY PLATFORM-DEPENDENT. The check is
|
|
83
|
+
* `path.isAbsolute(directory) || /^[A-Za-z]:[/\\]/.test(directory)`, and
|
|
84
|
+
* `path.isAbsolute` is bound to the host platform. Measured, not assumed
|
|
85
|
+
* (2026-08-10, issue #1619 review round 4, F5):
|
|
86
|
+
*
|
|
87
|
+
* | root | POSIX host | Windows host |
|
|
88
|
+
* | --------------------------- | ---------- | ------------ |
|
|
89
|
+
* | `/srv/app` | accepted | accepted |
|
|
90
|
+
* | `C:/app`, `C:\app` | accepted | accepted |
|
|
91
|
+
* | `//server/share/project` | accepted | accepted |
|
|
92
|
+
* | `\\server\share\project` | REJECTED | accepted |
|
|
93
|
+
* | `app/relative` | rejected | rejected |
|
|
94
|
+
*
|
|
95
|
+
* So the drive-letter fallback is what makes Windows drive roots portable, and
|
|
96
|
+
* a POSIX root is absolute on Windows too (`path.win32.isAbsolute('/srv')` is
|
|
97
|
+
* true — a driveless rooted path passes there, resolving against the current
|
|
98
|
+
* drive). But the BACKSLASH UNC form is not portable: it is win32-absolute and
|
|
99
|
+
* not posix-absolute, and no fallback covers it, so it validates on Windows and
|
|
100
|
+
* throws on a Linux CI runner. Only the forward-slash UNC spelling is accepted
|
|
101
|
+
* on both.
|
|
102
|
+
*
|
|
103
|
+
* UNC paths are intentionally accepted ON WINDOWS: they are absolute there and
|
|
104
|
+
* can be a legitimate project root. Rejecting them would silently re-create the
|
|
105
|
+
* dead-feature class this function exists to fix, because every caller sits
|
|
106
|
+
* behind a debug-gated catch. Nothing is lost by the POSIX rejection — a
|
|
107
|
+
* `\\server\share` root is not a usable path on POSIX in the first place.
|
|
108
|
+
*
|
|
109
|
+
* NOT interchangeable with `validateProjectRoot` (src/evidence/manager.ts).
|
|
110
|
+
* That one is a filesystem-touching, fail-closed check that the directory is
|
|
111
|
+
* the OUTERMOST project root (no ancestor owns a `.swarm/`). It does I/O
|
|
112
|
+
* (realpathSync plus a bounded ancestor walk) on every call and it correctly
|
|
113
|
+
* REJECTS a linked git worktree whose parent checkout has a `.swarm/` — right
|
|
114
|
+
* for a one-shot evidence write, wrong for a per-turn chat-transform hook.
|
|
115
|
+
* Reserve it for writes that must be pinned to the outermost project root.
|
|
116
|
+
*
|
|
117
|
+
* @param directory - the injected, trusted project root
|
|
118
|
+
* @throws Error if the directory is not a usable absolute project root
|
|
119
|
+
*/
|
|
120
|
+
export declare function validateProjectDirectory(directory: string): void;
|
|
28
121
|
/**
|
|
29
122
|
* Validate that a resolved path stays within an allowed root directory.
|
|
30
123
|
* Resolves symlinks via realpathSync for both the target path and the root,
|
|
@@ -105,6 +198,7 @@ export declare const _internals: {
|
|
|
105
198
|
containsPathTraversal: typeof containsPathTraversal;
|
|
106
199
|
containsControlChars: typeof containsControlChars;
|
|
107
200
|
validateDirectory: typeof validateDirectory;
|
|
201
|
+
validateProjectDirectory: typeof validateProjectDirectory;
|
|
108
202
|
validateSymlinkBoundary: typeof validateSymlinkBoundary;
|
|
109
203
|
isCanonicalPathWithinRoot: typeof isCanonicalPathWithinRoot;
|
|
110
204
|
validateTargetWithinRoot: typeof validateTargetWithinRoot;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as fsSync from 'node:fs';
|
|
1
2
|
export interface SwarmArtifactCacheStats {
|
|
2
3
|
textReadCount: number;
|
|
3
4
|
textCacheHitCount: number;
|
|
@@ -14,6 +15,18 @@ export interface SwarmArtifactCacheStats {
|
|
|
14
15
|
textEntryCount: number;
|
|
15
16
|
parsedEntryCount: number;
|
|
16
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Dependency-injection seam for testing. Tests can temporarily replace these
|
|
20
|
+
* to force a specific (e.g. colliding) stat stamp without relying on
|
|
21
|
+
* filesystem/platform-specific ctime semantics (utimesSync cannot set ctime
|
|
22
|
+
* portably — see swarm-artifact-cache.test.ts's documented Windows/POSIX
|
|
23
|
+
* divergence). Restore each entry in afterEach via the saved original
|
|
24
|
+
* reference.
|
|
25
|
+
*/
|
|
26
|
+
export declare const _internals: {
|
|
27
|
+
stat: typeof fsSync.promises.stat;
|
|
28
|
+
statSync: fsSync.StatSyncFn;
|
|
29
|
+
};
|
|
17
30
|
export declare function cloneCachedValue<T>(value: T): T;
|
|
18
31
|
export declare function readCachedTextFileSync(filePath: string, directRead: () => string | null): string | null;
|
|
19
32
|
export declare function readCachedTextFile(filePath: string, directRead: () => Promise<string | null>): Promise<string | null>;
|
package/dist/worktree/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type { AutoCommitSkip, AutoCommitSuccess, CleanCheckFailure, CleanCheckSuccess, CleanFailure, CleanSuccess, ProvisionFailure, ProvisionSuccess, RemoveFailure, RemoveSuccess, } from './core';
|
|
2
2
|
export { _internals as coreInternals, assertCleanWorkingTree, autoCommitDirty, checkPathBudget, cleanUntrackedFiles, isCleanWorktree, isPathUnderSwarmWorktreeBase, makeWorktreeBranchName, provisionWorktree, removeWorktree, resolveWorktreeBaseDir, shortenWorktreePath, } from './core';
|
|
3
3
|
export type { CleanupFailure, CleanupSuccess, ConflictHandlingError, ConflictInfo, DirtyMergeFailure, DirtyMergePartial, DirtyMergeSuccess, MergeConflict, MergeFailure, MergeSuccess, OrphanCleanupResult, StartupRecoveryResult, } from './merge';
|
|
4
|
-
export { _internals as mergeInternals, attemptMergeBackFromDirty, cleanupOrphanedBranches, getMergeStrategy, handleMergeConflict, mergeLaneBranch, postMergeCleanup, startupOrphanRecovery, } from './merge';
|
|
4
|
+
export { _internals as mergeInternals, attemptMergeBackFromDirty, cleanupOrphanedBranches, getMergeStrategy, handleMergeConflict, mergeLaneBranch, postMergeCleanup, pruneStaleWorktreeMetadata, scanRegisteredWorktreeLiveness, startupOrphanRecovery, } from './merge';
|
|
5
5
|
export * from './types';
|
package/dist/worktree/merge.d.ts
CHANGED
|
@@ -130,6 +130,14 @@ export interface OrphanCleanupResult {
|
|
|
130
130
|
* deletions were skipped this pass (fail-safe). */
|
|
131
131
|
recoveryReadError?: boolean;
|
|
132
132
|
}
|
|
133
|
+
export interface OrphanCleanupOptions {
|
|
134
|
+
/**
|
|
135
|
+
* Preserve branches that are not merged into HEAD. Interactive reset flows
|
|
136
|
+
* retain the historical force-delete default; unattended startup recovery
|
|
137
|
+
* sets this to true so a missing worktree cannot erase its only commits.
|
|
138
|
+
*/
|
|
139
|
+
preserveUnmerged?: boolean;
|
|
140
|
+
}
|
|
133
141
|
export interface StartupRecoveryResult {
|
|
134
142
|
prunedWorktrees: boolean;
|
|
135
143
|
remainingBranches: string[];
|
|
@@ -180,6 +188,30 @@ export declare function reconcileLandedMerge(primaryDir: string, provenance: Mer
|
|
|
180
188
|
* @returns Discriminated union: success, partial failure, or full failure.
|
|
181
189
|
*/
|
|
182
190
|
export declare function postMergeCleanup(directory: string, branchName: string): Promise<CleanupSuccess | CleanupFailure>;
|
|
191
|
+
/**
|
|
192
|
+
* Remove only stale Git worktree registration metadata. This deliberately does
|
|
193
|
+
* not delete a lane branch; callers hand branch reconciliation to
|
|
194
|
+
* `provisionWorktree`, which refuses branches with unmerged commits and uses
|
|
195
|
+
* non-forced `git branch -d` for proven-merged stale branches.
|
|
196
|
+
*/
|
|
197
|
+
export declare function pruneStaleWorktreeMetadata(directory: string): Promise<{
|
|
198
|
+
pruned: true;
|
|
199
|
+
} | {
|
|
200
|
+
error: string;
|
|
201
|
+
}>;
|
|
202
|
+
export type RegisteredWorktreeLivenessScan = {
|
|
203
|
+
status: 'ok';
|
|
204
|
+
liveBranches: string[];
|
|
205
|
+
} | {
|
|
206
|
+
status: 'uncertain';
|
|
207
|
+
reason: string;
|
|
208
|
+
};
|
|
209
|
+
/**
|
|
210
|
+
* Enumerate branches whose registered worktree paths still exist. Stale Git
|
|
211
|
+
* metadata with a missing path is deliberately excluded so an expired
|
|
212
|
+
* provisional owner cannot become permanent ownership after a crash.
|
|
213
|
+
*/
|
|
214
|
+
export declare function scanRegisteredWorktreeLiveness(directory: string): Promise<RegisteredWorktreeLivenessScan>;
|
|
183
215
|
/**
|
|
184
216
|
* Handles a merge conflict by listing conflicted files and aborting the
|
|
185
217
|
* in-progress operation to restore the working tree to a clean state.
|
|
@@ -228,14 +260,15 @@ declare function extractSessionId(branchName: string): string | null;
|
|
|
228
260
|
* Cleans up orphaned swarm-lane branches that do not belong to any active session.
|
|
229
261
|
*
|
|
230
262
|
* Lists all swarm-lane/ and swarm/lane/ branches, identifies orphans (branches whose
|
|
231
|
-
* session ID is not in `activeSessionIds`),
|
|
232
|
-
* worktree metadata.
|
|
263
|
+
* session ID is not in `activeSessionIds`), deletes them according to the
|
|
264
|
+
* requested preservation policy, and prunes stale worktree metadata.
|
|
233
265
|
*
|
|
234
266
|
* @param directory - The project root (cwd for all git commands).
|
|
235
267
|
* @param activeSessionIds - Session IDs that are still active; their branches are skipped.
|
|
268
|
+
* @param options - Branch preservation policy for unattended recovery.
|
|
236
269
|
* @returns Result with arrays of removed, skipped, and errored branch names.
|
|
237
270
|
*/
|
|
238
|
-
export declare function cleanupOrphanedBranches(directory: string, activeSessionIds?: string[]): Promise<OrphanCleanupResult>;
|
|
271
|
+
export declare function cleanupOrphanedBranches(directory: string, activeSessionIds?: string[], options?: OrphanCleanupOptions): Promise<OrphanCleanupResult>;
|
|
239
272
|
/**
|
|
240
273
|
* Performs startup orphan recovery: prunes stale worktrees, then identifies
|
|
241
274
|
* any remaining orphaned swarm-lane branches for warning.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.139.1",
|
|
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",
|