opencode-swarm 6.13.1 → 6.13.2
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 +23 -0
- package/dist/config/index.d.ts +2 -2
- package/dist/config/schema.d.ts +48 -0
- package/dist/hooks/adversarial-detector.d.ts +15 -0
- package/dist/index.js +382 -84
- package/dist/state.d.ts +13 -0
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/phase-complete.d.ts +9 -0
- package/dist/tools/tool-names.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -310,6 +310,8 @@ Per-agent overrides:
|
|
|
310
310
|
| build_check | Runs your project's native build/typecheck |
|
|
311
311
|
| quality_budget | Enforces complexity, duplication, and test ratio limits |
|
|
312
312
|
| pre_check_batch | Runs lint, secretscan, SAST, and quality budget in parallel (~15s vs ~60s sequential) |
|
|
313
|
+
| phase_complete | Enforces phase completion, verifies required agents, logs events, and resets state |
|
|
314
|
+
|
|
313
315
|
|
|
314
316
|
All tools run locally. No Docker, no network calls, no external APIs.
|
|
315
317
|
|
|
@@ -586,11 +588,30 @@ The following tools can be assigned to agents via overrides:
|
|
|
586
588
|
| `symbols` | Extract exported symbols |
|
|
587
589
|
| `test_runner` | Run project tests |
|
|
588
590
|
| `todo_extract` | Extract TODO/FIXME comments |
|
|
591
|
+
| `phase_complete` | Enforces phase completion, verifies required agents, logs events, resets state |
|
|
589
592
|
|
|
590
593
|
---
|
|
591
594
|
|
|
592
595
|
## Recent Changes
|
|
593
596
|
|
|
597
|
+
### v6.13.2 — Pipeline Enforcement
|
|
598
|
+
|
|
599
|
+
This release adds enforcement-layer tooling and self-healing guardrails:
|
|
600
|
+
|
|
601
|
+
- **`phase_complete` tool**: Verifies all required agents were dispatched before a phase closes; emits events to `.swarm/events.jsonl`; configurable `enforce`/`warn` policy
|
|
602
|
+
- **Summarization loop fix**: `exempt_tools` config prevents `retrieve_summary` and `task` outputs from being re-summarized (fixes Issue #8)
|
|
603
|
+
- **Same-model adversarial detection**: Warns when coder and reviewer share the same model; `warn`/`gate`/`ignore` policy
|
|
604
|
+
- **Architect test guardrail (HF-1b)**: Prevents architect from running full `bun test` suite — must target specific files one at a time
|
|
605
|
+
- **Docs**: `docs/swarm-briefing.md` (LLM pipeline briefing), Task Field Reference in `docs/planning.md`
|
|
606
|
+
|
|
607
|
+
### v6.13.1 — Consolidation & Defaults Fix
|
|
608
|
+
|
|
609
|
+
- **`consolidateSystemMessages`**: Merges multiple system messages into one at index 0
|
|
610
|
+
- **Test isolation helpers**: `createIsolatedTestEnv` and `assertSafeForWrite`
|
|
611
|
+
- **Coder self-verify guardrail (HF-1)**: Coder and test_engineer agents blocked from running build/test/lint
|
|
612
|
+
- **`/swarm` template fix**: `{{arguments}}` → `$ARGUMENTS`
|
|
613
|
+
- **DEFAULT_MODELS update**: `claude-sonnet-4-5` → `claude-sonnet-4-20250514`, `gemini-2.0-flash` → `gemini-2.5-flash`
|
|
614
|
+
|
|
594
615
|
### v6.13.0 — Context Efficiency
|
|
595
616
|
|
|
596
617
|
This release focuses on reducing context usage and improving mode-conditional behavior:
|
|
@@ -650,6 +671,8 @@ Upcoming: v6.14 focuses on further context optimization and agent coordination i
|
|
|
650
671
|
- [Design Rationale](docs/design-rationale.md)
|
|
651
672
|
- [Installation Guide](docs/installation.md)
|
|
652
673
|
- [Linux + Docker Desktop Install Guide](docs/installation-linux-docker.md)
|
|
674
|
+
- [Pre-Swarm Planning Guide](docs/planning.md)
|
|
675
|
+
- [Swarm Briefing for LLMs](docs/swarm-briefing.md)
|
|
653
676
|
|
|
654
677
|
---
|
|
655
678
|
|
package/dist/config/index.d.ts
CHANGED
|
@@ -5,5 +5,5 @@ export { ApprovalEvidenceSchema, BaseEvidenceSchema, DiffEvidenceSchema, EVIDENC
|
|
|
5
5
|
export { loadAgentPrompt, loadPluginConfig, loadPluginConfigWithMeta, } from './loader';
|
|
6
6
|
export type { MigrationStatus, Phase, PhaseStatus, Plan, Task, TaskSize, TaskStatus, } from './plan-schema';
|
|
7
7
|
export { MigrationStatusSchema, PhaseSchema, PhaseStatusSchema, PlanSchema, TaskSchema, TaskSizeSchema, TaskStatusSchema, } from './plan-schema';
|
|
8
|
-
export type { AgentOverrideConfig, AutomationCapabilities, AutomationConfig, AutomationMode, PipelineConfig, PluginConfig, SwarmConfig, } from './schema';
|
|
9
|
-
export { AgentOverrideConfigSchema, AutomationCapabilitiesSchema, AutomationConfigSchema, AutomationModeSchema, PipelineConfigSchema, PluginConfigSchema, SwarmConfigSchema, } from './schema';
|
|
8
|
+
export type { AgentOverrideConfig, AutomationCapabilities, AutomationConfig, AutomationMode, PhaseCompleteConfig, PipelineConfig, PluginConfig, SwarmConfig, } from './schema';
|
|
9
|
+
export { AgentOverrideConfigSchema, AutomationCapabilitiesSchema, AutomationConfigSchema, AutomationModeSchema, PhaseCompleteConfigSchema, PipelineConfigSchema, PluginConfigSchema, SwarmConfigSchema, } from './schema';
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -190,12 +190,27 @@ export declare const PipelineConfigSchema: z.ZodObject<{
|
|
|
190
190
|
parallel_precheck: z.ZodDefault<z.ZodBoolean>;
|
|
191
191
|
}, z.core.$strip>;
|
|
192
192
|
export type PipelineConfig = z.infer<typeof PipelineConfigSchema>;
|
|
193
|
+
export declare const PhaseCompleteConfigSchema: z.ZodObject<{
|
|
194
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
195
|
+
required_agents: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
196
|
+
reviewer: "reviewer";
|
|
197
|
+
coder: "coder";
|
|
198
|
+
test_engineer: "test_engineer";
|
|
199
|
+
}>>>;
|
|
200
|
+
require_docs: z.ZodDefault<z.ZodBoolean>;
|
|
201
|
+
policy: z.ZodDefault<z.ZodEnum<{
|
|
202
|
+
enforce: "enforce";
|
|
203
|
+
warn: "warn";
|
|
204
|
+
}>>;
|
|
205
|
+
}, z.core.$strip>;
|
|
206
|
+
export type PhaseCompleteConfig = z.infer<typeof PhaseCompleteConfigSchema>;
|
|
193
207
|
export declare const SummaryConfigSchema: z.ZodObject<{
|
|
194
208
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
195
209
|
threshold_bytes: z.ZodDefault<z.ZodNumber>;
|
|
196
210
|
max_summary_chars: z.ZodDefault<z.ZodNumber>;
|
|
197
211
|
max_stored_bytes: z.ZodDefault<z.ZodNumber>;
|
|
198
212
|
retention_days: z.ZodDefault<z.ZodNumber>;
|
|
213
|
+
exempt_tools: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
199
214
|
}, z.core.$strip>;
|
|
200
215
|
export type SummaryConfig = z.infer<typeof SummaryConfigSchema>;
|
|
201
216
|
export declare const ReviewPassesConfigSchema: z.ZodObject<{
|
|
@@ -203,6 +218,16 @@ export declare const ReviewPassesConfigSchema: z.ZodObject<{
|
|
|
203
218
|
security_globs: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
204
219
|
}, z.core.$strip>;
|
|
205
220
|
export type ReviewPassesConfig = z.infer<typeof ReviewPassesConfigSchema>;
|
|
221
|
+
export declare const AdversarialDetectionConfigSchema: z.ZodObject<{
|
|
222
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
223
|
+
policy: z.ZodDefault<z.ZodEnum<{
|
|
224
|
+
warn: "warn";
|
|
225
|
+
gate: "gate";
|
|
226
|
+
ignore: "ignore";
|
|
227
|
+
}>>;
|
|
228
|
+
pairs: z.ZodDefault<z.ZodArray<z.ZodTuple<[z.ZodString, z.ZodString], null>>>;
|
|
229
|
+
}, z.core.$strip>;
|
|
230
|
+
export type AdversarialDetectionConfig = z.infer<typeof AdversarialDetectionConfigSchema>;
|
|
206
231
|
export declare const IntegrationAnalysisConfigSchema: z.ZodObject<{
|
|
207
232
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
208
233
|
}, z.core.$strip>;
|
|
@@ -367,6 +392,19 @@ export declare const PluginConfigSchema: z.ZodObject<{
|
|
|
367
392
|
pipeline: z.ZodOptional<z.ZodObject<{
|
|
368
393
|
parallel_precheck: z.ZodDefault<z.ZodBoolean>;
|
|
369
394
|
}, z.core.$strip>>;
|
|
395
|
+
phase_complete: z.ZodOptional<z.ZodObject<{
|
|
396
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
397
|
+
required_agents: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
398
|
+
reviewer: "reviewer";
|
|
399
|
+
coder: "coder";
|
|
400
|
+
test_engineer: "test_engineer";
|
|
401
|
+
}>>>;
|
|
402
|
+
require_docs: z.ZodDefault<z.ZodBoolean>;
|
|
403
|
+
policy: z.ZodDefault<z.ZodEnum<{
|
|
404
|
+
enforce: "enforce";
|
|
405
|
+
warn: "warn";
|
|
406
|
+
}>>;
|
|
407
|
+
}, z.core.$strip>>;
|
|
370
408
|
qa_retry_limit: z.ZodDefault<z.ZodNumber>;
|
|
371
409
|
inject_phase_reminders: z.ZodDefault<z.ZodBoolean>;
|
|
372
410
|
hooks: z.ZodOptional<z.ZodObject<{
|
|
@@ -479,11 +517,21 @@ export declare const PluginConfigSchema: z.ZodObject<{
|
|
|
479
517
|
max_summary_chars: z.ZodDefault<z.ZodNumber>;
|
|
480
518
|
max_stored_bytes: z.ZodDefault<z.ZodNumber>;
|
|
481
519
|
retention_days: z.ZodDefault<z.ZodNumber>;
|
|
520
|
+
exempt_tools: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
482
521
|
}, z.core.$strip>>;
|
|
483
522
|
review_passes: z.ZodOptional<z.ZodObject<{
|
|
484
523
|
always_security_review: z.ZodDefault<z.ZodBoolean>;
|
|
485
524
|
security_globs: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
486
525
|
}, z.core.$strip>>;
|
|
526
|
+
adversarial_detection: z.ZodOptional<z.ZodObject<{
|
|
527
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
528
|
+
policy: z.ZodDefault<z.ZodEnum<{
|
|
529
|
+
warn: "warn";
|
|
530
|
+
gate: "gate";
|
|
531
|
+
ignore: "ignore";
|
|
532
|
+
}>>;
|
|
533
|
+
pairs: z.ZodDefault<z.ZodArray<z.ZodTuple<[z.ZodString, z.ZodString], null>>>;
|
|
534
|
+
}, z.core.$strip>>;
|
|
487
535
|
integration_analysis: z.ZodOptional<z.ZodObject<{
|
|
488
536
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
489
537
|
}, z.core.$strip>>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PluginConfig } from '../config';
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the model for a given agent by checking config overrides,
|
|
4
|
+
* swarm configurations, and falling back to defaults.
|
|
5
|
+
*/
|
|
6
|
+
export declare function resolveAgentModel(agentName: string, config: PluginConfig): string;
|
|
7
|
+
/**
|
|
8
|
+
* Detect if two agents share the same model (adversarial pair).
|
|
9
|
+
* Returns the shared model string if matched, null otherwise.
|
|
10
|
+
*/
|
|
11
|
+
export declare function detectAdversarialPair(agentA: string, agentB: string, config: PluginConfig): string | null;
|
|
12
|
+
/**
|
|
13
|
+
* Format an adversarial warning message based on policy.
|
|
14
|
+
*/
|
|
15
|
+
export declare function formatAdversarialWarning(agentA: string, agentB: string, sharedModel: string, policy: string): string;
|
package/dist/index.js
CHANGED
|
@@ -31449,7 +31449,8 @@ var TOOL_NAMES = [
|
|
|
31449
31449
|
"detect_domains",
|
|
31450
31450
|
"gitingest",
|
|
31451
31451
|
"retrieve_summary",
|
|
31452
|
-
"extract_code_blocks"
|
|
31452
|
+
"extract_code_blocks",
|
|
31453
|
+
"phase_complete"
|
|
31453
31454
|
];
|
|
31454
31455
|
var TOOL_NAME_SET = new Set(TOOL_NAMES);
|
|
31455
31456
|
|
|
@@ -31785,12 +31786,19 @@ var GateConfigSchema = exports_external.object({
|
|
|
31785
31786
|
var PipelineConfigSchema = exports_external.object({
|
|
31786
31787
|
parallel_precheck: exports_external.boolean().default(true)
|
|
31787
31788
|
});
|
|
31789
|
+
var PhaseCompleteConfigSchema = exports_external.object({
|
|
31790
|
+
enabled: exports_external.boolean().default(true),
|
|
31791
|
+
required_agents: exports_external.array(exports_external.enum(["coder", "reviewer", "test_engineer"])).default(["coder", "reviewer", "test_engineer"]),
|
|
31792
|
+
require_docs: exports_external.boolean().default(true),
|
|
31793
|
+
policy: exports_external.enum(["enforce", "warn"]).default("enforce")
|
|
31794
|
+
});
|
|
31788
31795
|
var SummaryConfigSchema = exports_external.object({
|
|
31789
31796
|
enabled: exports_external.boolean().default(true),
|
|
31790
31797
|
threshold_bytes: exports_external.number().min(1024).max(1048576).default(20480),
|
|
31791
31798
|
max_summary_chars: exports_external.number().min(100).max(5000).default(1000),
|
|
31792
31799
|
max_stored_bytes: exports_external.number().min(10240).max(104857600).default(10485760),
|
|
31793
|
-
retention_days: exports_external.number().min(1).max(365).default(7)
|
|
31800
|
+
retention_days: exports_external.number().min(1).max(365).default(7),
|
|
31801
|
+
exempt_tools: exports_external.array(exports_external.string()).default(["retrieve_summary", "task"])
|
|
31794
31802
|
});
|
|
31795
31803
|
var ReviewPassesConfigSchema = exports_external.object({
|
|
31796
31804
|
always_security_review: exports_external.boolean().default(false),
|
|
@@ -31804,6 +31812,11 @@ var ReviewPassesConfigSchema = exports_external.object({
|
|
|
31804
31812
|
"**/token/**"
|
|
31805
31813
|
])
|
|
31806
31814
|
});
|
|
31815
|
+
var AdversarialDetectionConfigSchema = exports_external.object({
|
|
31816
|
+
enabled: exports_external.boolean().default(true),
|
|
31817
|
+
policy: exports_external.enum(["warn", "gate", "ignore"]).default("warn"),
|
|
31818
|
+
pairs: exports_external.array(exports_external.tuple([exports_external.string(), exports_external.string()])).default([["coder", "reviewer"]])
|
|
31819
|
+
});
|
|
31807
31820
|
var IntegrationAnalysisConfigSchema = exports_external.object({
|
|
31808
31821
|
enabled: exports_external.boolean().default(true)
|
|
31809
31822
|
});
|
|
@@ -32035,6 +32048,7 @@ var PluginConfigSchema = exports_external.object({
|
|
|
32035
32048
|
swarms: exports_external.record(exports_external.string(), SwarmConfigSchema).optional(),
|
|
32036
32049
|
max_iterations: exports_external.number().min(1).max(10).default(5),
|
|
32037
32050
|
pipeline: PipelineConfigSchema.optional(),
|
|
32051
|
+
phase_complete: PhaseCompleteConfigSchema.optional(),
|
|
32038
32052
|
qa_retry_limit: exports_external.number().min(1).max(10).default(3),
|
|
32039
32053
|
inject_phase_reminders: exports_external.boolean().default(true),
|
|
32040
32054
|
hooks: HooksConfigSchema.optional(),
|
|
@@ -32046,6 +32060,7 @@ var PluginConfigSchema = exports_external.object({
|
|
|
32046
32060
|
evidence: EvidenceConfigSchema.optional(),
|
|
32047
32061
|
summaries: SummaryConfigSchema.optional(),
|
|
32048
32062
|
review_passes: ReviewPassesConfigSchema.optional(),
|
|
32063
|
+
adversarial_detection: AdversarialDetectionConfigSchema.optional(),
|
|
32049
32064
|
integration_analysis: IntegrationAnalysisConfigSchema.optional(),
|
|
32050
32065
|
docs: DocsConfigSchema.optional(),
|
|
32051
32066
|
ui_review: UIReviewConfigSchema.optional(),
|
|
@@ -34451,7 +34466,10 @@ function startAgentSession(sessionId, agentName, staleDurationMs = 7200000) {
|
|
|
34451
34466
|
lastGateFailure: null,
|
|
34452
34467
|
partialGateWarningIssued: false,
|
|
34453
34468
|
selfFixAttempted: false,
|
|
34454
|
-
catastrophicPhaseWarnings: new Set
|
|
34469
|
+
catastrophicPhaseWarnings: new Set,
|
|
34470
|
+
lastPhaseCompleteTimestamp: 0,
|
|
34471
|
+
lastPhaseCompletePhase: 0,
|
|
34472
|
+
phaseAgentsDispatched: new Set
|
|
34455
34473
|
};
|
|
34456
34474
|
swarmState.agentSessions.set(sessionId, sessionState);
|
|
34457
34475
|
swarmState.activeAgent.set(sessionId, agentName);
|
|
@@ -34502,6 +34520,15 @@ function ensureAgentSession(sessionId, agentName) {
|
|
|
34502
34520
|
if (!session.catastrophicPhaseWarnings) {
|
|
34503
34521
|
session.catastrophicPhaseWarnings = new Set;
|
|
34504
34522
|
}
|
|
34523
|
+
if (session.lastPhaseCompleteTimestamp === undefined) {
|
|
34524
|
+
session.lastPhaseCompleteTimestamp = 0;
|
|
34525
|
+
}
|
|
34526
|
+
if (session.lastPhaseCompletePhase === undefined) {
|
|
34527
|
+
session.lastPhaseCompletePhase = 0;
|
|
34528
|
+
}
|
|
34529
|
+
if (!session.phaseAgentsDispatched) {
|
|
34530
|
+
session.phaseAgentsDispatched = new Set;
|
|
34531
|
+
}
|
|
34505
34532
|
session.lastToolCallTime = now;
|
|
34506
34533
|
return session;
|
|
34507
34534
|
}
|
|
@@ -34570,6 +34597,17 @@ function pruneOldWindows(sessionId, maxAgeMs = 24 * 60 * 60 * 1000, maxWindows =
|
|
|
34570
34597
|
const toKeep = sorted.slice(0, maxWindows);
|
|
34571
34598
|
session.windows = Object.fromEntries(toKeep);
|
|
34572
34599
|
}
|
|
34600
|
+
function recordPhaseAgentDispatch(sessionId, agentName) {
|
|
34601
|
+
const session = swarmState.agentSessions.get(sessionId);
|
|
34602
|
+
if (!session) {
|
|
34603
|
+
return;
|
|
34604
|
+
}
|
|
34605
|
+
if (!session.phaseAgentsDispatched) {
|
|
34606
|
+
session.phaseAgentsDispatched = new Set;
|
|
34607
|
+
}
|
|
34608
|
+
const normalizedName = stripKnownSwarmPrefix(agentName);
|
|
34609
|
+
session.phaseAgentsDispatched.add(normalizedName);
|
|
34610
|
+
}
|
|
34573
34611
|
|
|
34574
34612
|
// src/commands/benchmark.ts
|
|
34575
34613
|
init_utils();
|
|
@@ -36680,6 +36718,7 @@ function createDelegationTrackerHook(config3, guardrailsEnabled = true) {
|
|
|
36680
36718
|
const isArchitect = strippedAgent === ORCHESTRATOR_NAME;
|
|
36681
36719
|
const session = ensureAgentSession(input.sessionID, agentName);
|
|
36682
36720
|
session.delegationActive = !isArchitect;
|
|
36721
|
+
recordPhaseAgentDispatch(input.sessionID, agentName);
|
|
36683
36722
|
if (!isArchitect && guardrailsEnabled) {
|
|
36684
36723
|
beginInvocation(input.sessionID, agentName);
|
|
36685
36724
|
}
|
|
@@ -37131,8 +37170,8 @@ function hashArgs(args2) {
|
|
|
37131
37170
|
// src/hooks/messages-transform.ts
|
|
37132
37171
|
function consolidateSystemMessages(messages) {
|
|
37133
37172
|
if (messages.length > 0 && messages[0].role === "system" && messages[0].content !== undefined && typeof messages[0].content === "string" && messages[0].content.trim().length > 0) {
|
|
37134
|
-
const
|
|
37135
|
-
if (
|
|
37173
|
+
const totalSystemCount = messages.filter((m) => m.role === "system").length;
|
|
37174
|
+
if (totalSystemCount === 1) {
|
|
37136
37175
|
return [...messages];
|
|
37137
37176
|
}
|
|
37138
37177
|
}
|
|
@@ -37140,34 +37179,31 @@ function consolidateSystemMessages(messages) {
|
|
|
37140
37179
|
const systemContents = [];
|
|
37141
37180
|
for (let i2 = 0;i2 < messages.length; i2++) {
|
|
37142
37181
|
const message = messages[i2];
|
|
37143
|
-
if (message.role !== "system")
|
|
37144
|
-
continue;
|
|
37145
|
-
}
|
|
37146
|
-
if (message.tool_call_id !== undefined || message.name !== undefined) {
|
|
37147
|
-
continue;
|
|
37148
|
-
}
|
|
37149
|
-
if (typeof message.content !== "string") {
|
|
37182
|
+
if (message.role !== "system")
|
|
37150
37183
|
continue;
|
|
37151
|
-
|
|
37152
|
-
const trimmedContent = message.content.trim();
|
|
37153
|
-
if (trimmedContent.length === 0) {
|
|
37184
|
+
if (message.tool_call_id !== undefined || message.name !== undefined)
|
|
37154
37185
|
continue;
|
|
37186
|
+
let textContent = null;
|
|
37187
|
+
if (typeof message.content === "string") {
|
|
37188
|
+
const trimmed = message.content.trim();
|
|
37189
|
+
if (trimmed.length > 0)
|
|
37190
|
+
textContent = trimmed;
|
|
37191
|
+
} else if (Array.isArray(message.content)) {
|
|
37192
|
+
const texts = message.content.filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text.trim()).filter((t) => t.length > 0);
|
|
37193
|
+
if (texts.length > 0)
|
|
37194
|
+
textContent = texts.join(`
|
|
37195
|
+
`);
|
|
37155
37196
|
}
|
|
37156
37197
|
systemMessageIndices.push(i2);
|
|
37157
|
-
|
|
37198
|
+
if (textContent) {
|
|
37199
|
+
systemContents.push(textContent);
|
|
37200
|
+
}
|
|
37158
37201
|
}
|
|
37159
37202
|
if (systemContents.length === 0) {
|
|
37160
|
-
return messages.filter((m) => {
|
|
37161
|
-
if (m.role !== "system")
|
|
37162
|
-
return true;
|
|
37163
|
-
}
|
|
37164
|
-
if (typeof m.content !== "string" || m.name !== undefined || m.tool_call_id !== undefined) {
|
|
37203
|
+
return messages.filter((m, idx) => {
|
|
37204
|
+
if (m.role !== "system")
|
|
37165
37205
|
return true;
|
|
37166
|
-
|
|
37167
|
-
if (m.content.trim().length > 0) {
|
|
37168
|
-
return true;
|
|
37169
|
-
}
|
|
37170
|
-
return false;
|
|
37206
|
+
return idx === 0;
|
|
37171
37207
|
});
|
|
37172
37208
|
}
|
|
37173
37209
|
const mergedSystemContent = systemContents.join(`
|
|
@@ -37178,7 +37214,7 @@ function consolidateSystemMessages(messages) {
|
|
|
37178
37214
|
result.push({
|
|
37179
37215
|
role: "system",
|
|
37180
37216
|
content: mergedSystemContent,
|
|
37181
|
-
...Object.fromEntries(Object.entries(firstSystemMessage).filter(([key]) => key !== "role" && key !== "content"))
|
|
37217
|
+
...Object.fromEntries(Object.entries(firstSystemMessage).filter(([key]) => key !== "role" && key !== "content" && key !== "name" && key !== "tool_call_id"))
|
|
37182
37218
|
});
|
|
37183
37219
|
for (let i2 = 0;i2 < messages.length; i2++) {
|
|
37184
37220
|
const message = messages[i2];
|
|
@@ -37190,7 +37226,11 @@ function consolidateSystemMessages(messages) {
|
|
|
37190
37226
|
}
|
|
37191
37227
|
result.push({ ...message });
|
|
37192
37228
|
}
|
|
37193
|
-
return result
|
|
37229
|
+
return result.filter((msg, idx) => {
|
|
37230
|
+
if (idx === 0)
|
|
37231
|
+
return true;
|
|
37232
|
+
return msg.role !== "system";
|
|
37233
|
+
});
|
|
37194
37234
|
}
|
|
37195
37235
|
// src/hooks/phase-monitor.ts
|
|
37196
37236
|
init_manager2();
|
|
@@ -37585,6 +37625,39 @@ init_preflight_service();
|
|
|
37585
37625
|
// src/hooks/system-enhancer.ts
|
|
37586
37626
|
init_utils();
|
|
37587
37627
|
|
|
37628
|
+
// src/hooks/adversarial-detector.ts
|
|
37629
|
+
function safeGet(obj, key) {
|
|
37630
|
+
if (!obj || !Object.hasOwn(obj, key))
|
|
37631
|
+
return;
|
|
37632
|
+
return obj[key];
|
|
37633
|
+
}
|
|
37634
|
+
function resolveAgentModel(agentName, config3) {
|
|
37635
|
+
const baseName = stripKnownSwarmPrefix(agentName).toLowerCase();
|
|
37636
|
+
const agentOverride = safeGet(config3.agents, baseName)?.model;
|
|
37637
|
+
if (agentOverride)
|
|
37638
|
+
return agentOverride;
|
|
37639
|
+
if (config3.swarms) {
|
|
37640
|
+
for (const swarm of Object.values(config3.swarms)) {
|
|
37641
|
+
const swarmModel = safeGet(swarm.agents, baseName)?.model;
|
|
37642
|
+
if (swarmModel)
|
|
37643
|
+
return swarmModel;
|
|
37644
|
+
}
|
|
37645
|
+
}
|
|
37646
|
+
const defaultModel = safeGet(DEFAULT_MODELS, baseName);
|
|
37647
|
+
return defaultModel ?? DEFAULT_MODELS.default;
|
|
37648
|
+
}
|
|
37649
|
+
function detectAdversarialPair(agentA, agentB, config3) {
|
|
37650
|
+
const modelA = resolveAgentModel(agentA, config3).toLowerCase();
|
|
37651
|
+
const modelB = resolveAgentModel(agentB, config3).toLowerCase();
|
|
37652
|
+
return modelA === modelB ? modelA : null;
|
|
37653
|
+
}
|
|
37654
|
+
function formatAdversarialWarning(agentA, agentB, sharedModel, policy) {
|
|
37655
|
+
if (policy === "gate") {
|
|
37656
|
+
return `\u26A0\uFE0F GATE POLICY: Same-model adversarial pair detected. Agent ${agentA} and checker ${agentB} both use model ${sharedModel}. This requires extra scrutiny \u2014 escalate if issues are found.`;
|
|
37657
|
+
}
|
|
37658
|
+
return `\u26A0\uFE0F Same-model adversarial pair detected. Agent ${agentA} and checker ${agentB} both use model ${sharedModel}. Review may lack independence.`;
|
|
37659
|
+
}
|
|
37660
|
+
|
|
37588
37661
|
// src/hooks/context-scoring.ts
|
|
37589
37662
|
function calculateAgeFactor(ageHours, config3) {
|
|
37590
37663
|
if (ageHours <= 0) {
|
|
@@ -37727,6 +37800,35 @@ function createSystemEnhancerHook(config3, directory) {
|
|
|
37727
37800
|
if (config3.secretscan?.enabled === false) {
|
|
37728
37801
|
tryInject("[SWARM CONFIG] Secretscan gate is DISABLED. Skip secretscan in QA sequence.");
|
|
37729
37802
|
}
|
|
37803
|
+
const activeAgent_hf1 = swarmState.activeAgent.get(_input.sessionID ?? "");
|
|
37804
|
+
const baseRole = activeAgent_hf1 ? stripKnownSwarmPrefix(activeAgent_hf1) : null;
|
|
37805
|
+
if (baseRole === "coder" || baseRole === "test_engineer") {
|
|
37806
|
+
tryInject("[SWARM CONFIG] You must NOT run build, test, lint, or type-check commands (npm run build, bun test, npx tsc, eslint, etc.). Make ONLY the code changes specified in your task. Verification is handled by the reviewer agent \u2014 do not self-verify. If your task explicitly asks you to run a specific command, that is the only exception.");
|
|
37807
|
+
}
|
|
37808
|
+
if (baseRole === "architect" || baseRole === null) {
|
|
37809
|
+
tryInject("[SWARM CONFIG] You must NEVER run the full test suite or batch test files. If you need to verify changes, run ONLY the specific test files for code YOU modified in this session \u2014 one file at a time, strictly serial. Do not run tests from directories or files unrelated to your changes. Do not run bun test without an explicit file path. When possible, delegate test execution to the test_engineer agent instead of running tests yourself.");
|
|
37810
|
+
}
|
|
37811
|
+
if (config3.adversarial_detection?.enabled !== false) {
|
|
37812
|
+
const activeAgent_adv = swarmState.activeAgent.get(_input.sessionID ?? "");
|
|
37813
|
+
if (activeAgent_adv) {
|
|
37814
|
+
const baseRole_adv = stripKnownSwarmPrefix(activeAgent_adv);
|
|
37815
|
+
const pairs_adv = config3.adversarial_detection?.pairs ?? [
|
|
37816
|
+
["coder", "reviewer"]
|
|
37817
|
+
];
|
|
37818
|
+
const policy_adv = config3.adversarial_detection?.policy ?? "warn";
|
|
37819
|
+
for (const [agentA, agentB] of pairs_adv) {
|
|
37820
|
+
if (baseRole_adv === agentB) {
|
|
37821
|
+
const sharedModel = detectAdversarialPair(agentA, agentB, config3);
|
|
37822
|
+
if (sharedModel) {
|
|
37823
|
+
const warningText = formatAdversarialWarning(agentA, agentB, sharedModel, policy_adv);
|
|
37824
|
+
if (policy_adv !== "ignore") {
|
|
37825
|
+
tryInject(`[SWARM CONFIG] ${warningText}`);
|
|
37826
|
+
}
|
|
37827
|
+
}
|
|
37828
|
+
}
|
|
37829
|
+
}
|
|
37830
|
+
}
|
|
37831
|
+
}
|
|
37730
37832
|
if (mode !== "DISCOVER") {
|
|
37731
37833
|
const sessionId_preflight = _input.sessionID;
|
|
37732
37834
|
const activeAgent_preflight = swarmState.activeAgent.get(sessionId_preflight ?? "");
|
|
@@ -37988,6 +38090,34 @@ function createSystemEnhancerHook(config3, directory) {
|
|
|
37988
38090
|
metadata: { contentType: "prose" }
|
|
37989
38091
|
});
|
|
37990
38092
|
}
|
|
38093
|
+
if (config3.adversarial_detection?.enabled !== false) {
|
|
38094
|
+
const activeAgent_adv_b = swarmState.activeAgent.get(_input.sessionID ?? "");
|
|
38095
|
+
if (activeAgent_adv_b) {
|
|
38096
|
+
const baseRole_adv_b = stripKnownSwarmPrefix(activeAgent_adv_b);
|
|
38097
|
+
const pairs_adv_b = config3.adversarial_detection?.pairs ?? [
|
|
38098
|
+
["coder", "reviewer"]
|
|
38099
|
+
];
|
|
38100
|
+
const policy_adv_b = config3.adversarial_detection?.policy ?? "warn";
|
|
38101
|
+
for (const [agentA_b, agentB_b] of pairs_adv_b) {
|
|
38102
|
+
if (baseRole_adv_b === agentB_b) {
|
|
38103
|
+
const sharedModel_b = detectAdversarialPair(agentA_b, agentB_b, config3);
|
|
38104
|
+
if (sharedModel_b) {
|
|
38105
|
+
const warningText_b = formatAdversarialWarning(agentA_b, agentB_b, sharedModel_b, policy_adv_b);
|
|
38106
|
+
if (policy_adv_b !== "ignore") {
|
|
38107
|
+
candidates.push({
|
|
38108
|
+
id: `candidate-${idCounter++}`,
|
|
38109
|
+
kind: "agent_context",
|
|
38110
|
+
text: `[SWARM CONFIG] ${warningText_b}`,
|
|
38111
|
+
tokens: estimateTokens(warningText_b),
|
|
38112
|
+
priority: 2,
|
|
38113
|
+
metadata: { contentType: "prose" }
|
|
38114
|
+
});
|
|
38115
|
+
}
|
|
38116
|
+
}
|
|
38117
|
+
}
|
|
38118
|
+
}
|
|
38119
|
+
}
|
|
38120
|
+
}
|
|
37991
38121
|
const sessionId_preflight_b = _input.sessionID;
|
|
37992
38122
|
const activeAgent_preflight_b = swarmState.activeAgent.get(sessionId_preflight_b ?? "");
|
|
37993
38123
|
const isArchitectForPreflight_b = !activeAgent_preflight_b || stripKnownSwarmPrefix(activeAgent_preflight_b) === "architect";
|
|
@@ -38306,6 +38436,10 @@ function createToolSummarizerHook(config3, directory) {
|
|
|
38306
38436
|
if (typeof output.output !== "string" || output.output.length === 0) {
|
|
38307
38437
|
return;
|
|
38308
38438
|
}
|
|
38439
|
+
const exemptTools = config3.exempt_tools ?? ["retrieve_summary", "task"];
|
|
38440
|
+
if (exemptTools.includes(input.tool)) {
|
|
38441
|
+
return;
|
|
38442
|
+
}
|
|
38309
38443
|
if (!shouldSummarize(output.output, config3.threshold_bytes)) {
|
|
38310
38444
|
return;
|
|
38311
38445
|
}
|
|
@@ -40418,9 +40552,172 @@ var imports = tool({
|
|
|
40418
40552
|
// src/tools/index.ts
|
|
40419
40553
|
init_lint();
|
|
40420
40554
|
|
|
40555
|
+
// src/tools/phase-complete.ts
|
|
40556
|
+
init_tool();
|
|
40557
|
+
import * as fs18 from "fs";
|
|
40558
|
+
init_utils2();
|
|
40559
|
+
function getDelegationsSince(sessionID, sinceTimestamp) {
|
|
40560
|
+
const chain = swarmState.delegationChains.get(sessionID);
|
|
40561
|
+
if (!chain) {
|
|
40562
|
+
return [];
|
|
40563
|
+
}
|
|
40564
|
+
if (sinceTimestamp === 0) {
|
|
40565
|
+
return chain;
|
|
40566
|
+
}
|
|
40567
|
+
return chain.filter((entry) => entry.timestamp > sinceTimestamp);
|
|
40568
|
+
}
|
|
40569
|
+
function normalizeAgentsFromDelegations(delegations) {
|
|
40570
|
+
const agents = new Set;
|
|
40571
|
+
for (const delegation of delegations) {
|
|
40572
|
+
const normalizedFrom = stripKnownSwarmPrefix(delegation.from);
|
|
40573
|
+
const normalizedTo = stripKnownSwarmPrefix(delegation.to);
|
|
40574
|
+
agents.add(normalizedFrom);
|
|
40575
|
+
agents.add(normalizedTo);
|
|
40576
|
+
}
|
|
40577
|
+
return agents;
|
|
40578
|
+
}
|
|
40579
|
+
async function executePhaseComplete(args2) {
|
|
40580
|
+
const phase = Number(args2.phase);
|
|
40581
|
+
const summary = args2.summary;
|
|
40582
|
+
const sessionID = args2.sessionID;
|
|
40583
|
+
if (Number.isNaN(phase) || phase < 1) {
|
|
40584
|
+
return JSON.stringify({
|
|
40585
|
+
success: false,
|
|
40586
|
+
phase,
|
|
40587
|
+
message: "Invalid phase number",
|
|
40588
|
+
agentsDispatched: [],
|
|
40589
|
+
warnings: ["Phase must be a positive number"]
|
|
40590
|
+
}, null, 2);
|
|
40591
|
+
}
|
|
40592
|
+
if (!sessionID) {
|
|
40593
|
+
return JSON.stringify({
|
|
40594
|
+
success: false,
|
|
40595
|
+
phase,
|
|
40596
|
+
message: "Session ID is required",
|
|
40597
|
+
agentsDispatched: [],
|
|
40598
|
+
warnings: [
|
|
40599
|
+
"sessionID parameter is required for phase completion tracking"
|
|
40600
|
+
]
|
|
40601
|
+
}, null, 2);
|
|
40602
|
+
}
|
|
40603
|
+
const session = ensureAgentSession(sessionID);
|
|
40604
|
+
const lastCompletionTimestamp = session.lastPhaseCompleteTimestamp ?? 0;
|
|
40605
|
+
const recentDelegations = getDelegationsSince(sessionID, lastCompletionTimestamp);
|
|
40606
|
+
const delegationAgents = normalizeAgentsFromDelegations(recentDelegations);
|
|
40607
|
+
const trackedAgents = session.phaseAgentsDispatched ?? new Set;
|
|
40608
|
+
const allAgents = new Set([...delegationAgents, ...trackedAgents]);
|
|
40609
|
+
const agentsDispatched = Array.from(allAgents).sort();
|
|
40610
|
+
const directory = process.cwd();
|
|
40611
|
+
const { config: config3 } = loadPluginConfigWithMeta(directory);
|
|
40612
|
+
let phaseCompleteConfig;
|
|
40613
|
+
try {
|
|
40614
|
+
phaseCompleteConfig = PhaseCompleteConfigSchema.parse(config3.phase_complete ?? {});
|
|
40615
|
+
} catch (parseError) {
|
|
40616
|
+
return JSON.stringify({
|
|
40617
|
+
success: false,
|
|
40618
|
+
phase,
|
|
40619
|
+
status: "incomplete",
|
|
40620
|
+
message: `Invalid phase_complete configuration: ${parseError instanceof Error ? parseError.message : "Unknown error"}`,
|
|
40621
|
+
agentsDispatched,
|
|
40622
|
+
agentsMissing: [],
|
|
40623
|
+
warnings: ["Configuration validation failed"]
|
|
40624
|
+
}, null, 2);
|
|
40625
|
+
}
|
|
40626
|
+
if (phaseCompleteConfig.enabled === false) {
|
|
40627
|
+
return JSON.stringify({
|
|
40628
|
+
success: true,
|
|
40629
|
+
phase,
|
|
40630
|
+
status: "disabled",
|
|
40631
|
+
message: `Phase ${phase} complete (enforcement disabled)`,
|
|
40632
|
+
agentsDispatched,
|
|
40633
|
+
agentsMissing: [],
|
|
40634
|
+
warnings: []
|
|
40635
|
+
}, null, 2);
|
|
40636
|
+
}
|
|
40637
|
+
const effectiveRequired = [...phaseCompleteConfig.required_agents];
|
|
40638
|
+
if (phaseCompleteConfig.require_docs && !effectiveRequired.includes("docs")) {
|
|
40639
|
+
effectiveRequired.push("docs");
|
|
40640
|
+
}
|
|
40641
|
+
const agentsMissing = effectiveRequired.filter((req) => !allAgents.has(req));
|
|
40642
|
+
const warnings = [];
|
|
40643
|
+
let success3 = true;
|
|
40644
|
+
let status = "success";
|
|
40645
|
+
const safeSummary = summary?.trim().slice(0, 500);
|
|
40646
|
+
let message = safeSummary ? `Phase ${phase} completed: ${safeSummary}` : `Phase ${phase} completed`;
|
|
40647
|
+
if (agentsMissing.length > 0) {
|
|
40648
|
+
if (phaseCompleteConfig.policy === "enforce") {
|
|
40649
|
+
success3 = false;
|
|
40650
|
+
status = "incomplete";
|
|
40651
|
+
message = `Phase ${phase} incomplete: missing required agents: ${agentsMissing.join(", ")}`;
|
|
40652
|
+
} else {
|
|
40653
|
+
status = "warned";
|
|
40654
|
+
warnings.push(`Warning: phase ${phase} missing required agents: ${agentsMissing.join(", ")}`);
|
|
40655
|
+
}
|
|
40656
|
+
}
|
|
40657
|
+
const now = Date.now();
|
|
40658
|
+
const durationMs = now - lastCompletionTimestamp;
|
|
40659
|
+
const event = {
|
|
40660
|
+
event: "phase_complete",
|
|
40661
|
+
phase,
|
|
40662
|
+
timestamp: new Date(now).toISOString(),
|
|
40663
|
+
agents_dispatched: agentsDispatched,
|
|
40664
|
+
agents_missing: agentsMissing,
|
|
40665
|
+
status,
|
|
40666
|
+
summary: safeSummary ?? null
|
|
40667
|
+
};
|
|
40668
|
+
try {
|
|
40669
|
+
const eventsPath = validateSwarmPath(directory, "events.jsonl");
|
|
40670
|
+
fs18.appendFileSync(eventsPath, `${JSON.stringify(event)}
|
|
40671
|
+
`, "utf-8");
|
|
40672
|
+
} catch (writeError) {
|
|
40673
|
+
warnings.push(`Warning: failed to write phase complete event: ${writeError instanceof Error ? writeError.message : String(writeError)}`);
|
|
40674
|
+
}
|
|
40675
|
+
if (success3) {
|
|
40676
|
+
session.phaseAgentsDispatched = new Set;
|
|
40677
|
+
session.lastPhaseCompleteTimestamp = now;
|
|
40678
|
+
session.lastPhaseCompletePhase = phase;
|
|
40679
|
+
}
|
|
40680
|
+
const result = {
|
|
40681
|
+
success: success3,
|
|
40682
|
+
phase,
|
|
40683
|
+
status,
|
|
40684
|
+
message,
|
|
40685
|
+
agentsDispatched,
|
|
40686
|
+
agentsMissing,
|
|
40687
|
+
warnings
|
|
40688
|
+
};
|
|
40689
|
+
return JSON.stringify({ ...result, timestamp: event.timestamp, duration_ms: durationMs }, null, 2);
|
|
40690
|
+
}
|
|
40691
|
+
var phase_complete = tool({
|
|
40692
|
+
description: "Mark a phase as complete and track which agents were dispatched. " + "Used for phase completion gating and tracking. " + "Accepts phase number and optional summary. Returns list of agents that were dispatched.",
|
|
40693
|
+
args: {
|
|
40694
|
+
phase: tool.schema.number().describe("The phase number being completed (e.g., 1, 2, 3)"),
|
|
40695
|
+
summary: tool.schema.string().optional().describe("Optional summary of what was accomplished in this phase"),
|
|
40696
|
+
sessionID: tool.schema.string().optional().describe("Session ID for tracking state (auto-provided by plugin context)")
|
|
40697
|
+
},
|
|
40698
|
+
execute: async (args2) => {
|
|
40699
|
+
let phaseCompleteArgs;
|
|
40700
|
+
try {
|
|
40701
|
+
phaseCompleteArgs = {
|
|
40702
|
+
phase: Number(args2.phase),
|
|
40703
|
+
summary: args2.summary !== undefined ? String(args2.summary) : undefined,
|
|
40704
|
+
sessionID: args2.sessionID !== undefined ? String(args2.sessionID) : undefined
|
|
40705
|
+
};
|
|
40706
|
+
} catch {
|
|
40707
|
+
return JSON.stringify({
|
|
40708
|
+
success: false,
|
|
40709
|
+
phase: 0,
|
|
40710
|
+
message: "Invalid arguments",
|
|
40711
|
+
agentsDispatched: [],
|
|
40712
|
+
warnings: ["Failed to parse arguments"]
|
|
40713
|
+
}, null, 2);
|
|
40714
|
+
}
|
|
40715
|
+
return executePhaseComplete(phaseCompleteArgs);
|
|
40716
|
+
}
|
|
40717
|
+
});
|
|
40421
40718
|
// src/tools/pkg-audit.ts
|
|
40422
40719
|
init_dist();
|
|
40423
|
-
import * as
|
|
40720
|
+
import * as fs19 from "fs";
|
|
40424
40721
|
import * as path24 from "path";
|
|
40425
40722
|
var MAX_OUTPUT_BYTES5 = 52428800;
|
|
40426
40723
|
var AUDIT_TIMEOUT_MS = 120000;
|
|
@@ -40439,13 +40736,13 @@ function validateArgs3(args2) {
|
|
|
40439
40736
|
function detectEcosystems() {
|
|
40440
40737
|
const ecosystems = [];
|
|
40441
40738
|
const cwd = process.cwd();
|
|
40442
|
-
if (
|
|
40739
|
+
if (fs19.existsSync(path24.join(cwd, "package.json"))) {
|
|
40443
40740
|
ecosystems.push("npm");
|
|
40444
40741
|
}
|
|
40445
|
-
if (
|
|
40742
|
+
if (fs19.existsSync(path24.join(cwd, "pyproject.toml")) || fs19.existsSync(path24.join(cwd, "requirements.txt"))) {
|
|
40446
40743
|
ecosystems.push("pip");
|
|
40447
40744
|
}
|
|
40448
|
-
if (
|
|
40745
|
+
if (fs19.existsSync(path24.join(cwd, "Cargo.toml"))) {
|
|
40449
40746
|
ecosystems.push("cargo");
|
|
40450
40747
|
}
|
|
40451
40748
|
return ecosystems;
|
|
@@ -44337,7 +44634,7 @@ init_lint();
|
|
|
44337
44634
|
init_manager();
|
|
44338
44635
|
|
|
44339
44636
|
// src/quality/metrics.ts
|
|
44340
|
-
import * as
|
|
44637
|
+
import * as fs20 from "fs";
|
|
44341
44638
|
import * as path25 from "path";
|
|
44342
44639
|
var MAX_FILE_SIZE_BYTES5 = 256 * 1024;
|
|
44343
44640
|
var MIN_DUPLICATION_LINES = 10;
|
|
@@ -44376,11 +44673,11 @@ function estimateCyclomaticComplexity(content) {
|
|
|
44376
44673
|
}
|
|
44377
44674
|
function getComplexityForFile2(filePath) {
|
|
44378
44675
|
try {
|
|
44379
|
-
const stat =
|
|
44676
|
+
const stat = fs20.statSync(filePath);
|
|
44380
44677
|
if (stat.size > MAX_FILE_SIZE_BYTES5) {
|
|
44381
44678
|
return null;
|
|
44382
44679
|
}
|
|
44383
|
-
const content =
|
|
44680
|
+
const content = fs20.readFileSync(filePath, "utf-8");
|
|
44384
44681
|
return estimateCyclomaticComplexity(content);
|
|
44385
44682
|
} catch {
|
|
44386
44683
|
return null;
|
|
@@ -44391,7 +44688,7 @@ async function computeComplexityDelta(files, workingDir) {
|
|
|
44391
44688
|
const analyzedFiles = [];
|
|
44392
44689
|
for (const file3 of files) {
|
|
44393
44690
|
const fullPath = path25.isAbsolute(file3) ? file3 : path25.join(workingDir, file3);
|
|
44394
|
-
if (!
|
|
44691
|
+
if (!fs20.existsSync(fullPath)) {
|
|
44395
44692
|
continue;
|
|
44396
44693
|
}
|
|
44397
44694
|
const complexity = getComplexityForFile2(fullPath);
|
|
@@ -44512,7 +44809,7 @@ function countGoExports(content) {
|
|
|
44512
44809
|
}
|
|
44513
44810
|
function getExportCountForFile(filePath) {
|
|
44514
44811
|
try {
|
|
44515
|
-
const content =
|
|
44812
|
+
const content = fs20.readFileSync(filePath, "utf-8");
|
|
44516
44813
|
const ext = path25.extname(filePath).toLowerCase();
|
|
44517
44814
|
switch (ext) {
|
|
44518
44815
|
case ".ts":
|
|
@@ -44540,7 +44837,7 @@ async function computePublicApiDelta(files, workingDir) {
|
|
|
44540
44837
|
const analyzedFiles = [];
|
|
44541
44838
|
for (const file3 of files) {
|
|
44542
44839
|
const fullPath = path25.isAbsolute(file3) ? file3 : path25.join(workingDir, file3);
|
|
44543
|
-
if (!
|
|
44840
|
+
if (!fs20.existsSync(fullPath)) {
|
|
44544
44841
|
continue;
|
|
44545
44842
|
}
|
|
44546
44843
|
const exports = getExportCountForFile(fullPath);
|
|
@@ -44574,15 +44871,15 @@ async function computeDuplicationRatio(files, workingDir) {
|
|
|
44574
44871
|
const analyzedFiles = [];
|
|
44575
44872
|
for (const file3 of files) {
|
|
44576
44873
|
const fullPath = path25.isAbsolute(file3) ? file3 : path25.join(workingDir, file3);
|
|
44577
|
-
if (!
|
|
44874
|
+
if (!fs20.existsSync(fullPath)) {
|
|
44578
44875
|
continue;
|
|
44579
44876
|
}
|
|
44580
44877
|
try {
|
|
44581
|
-
const stat =
|
|
44878
|
+
const stat = fs20.statSync(fullPath);
|
|
44582
44879
|
if (stat.size > MAX_FILE_SIZE_BYTES5) {
|
|
44583
44880
|
continue;
|
|
44584
44881
|
}
|
|
44585
|
-
const content =
|
|
44882
|
+
const content = fs20.readFileSync(fullPath, "utf-8");
|
|
44586
44883
|
const lines = content.split(`
|
|
44587
44884
|
`).filter((line) => line.trim().length > 0);
|
|
44588
44885
|
if (lines.length < MIN_DUPLICATION_LINES) {
|
|
@@ -44650,7 +44947,7 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
|
|
|
44650
44947
|
let testLines = 0;
|
|
44651
44948
|
let codeLines = 0;
|
|
44652
44949
|
const srcDir = path25.join(workingDir, "src");
|
|
44653
|
-
if (
|
|
44950
|
+
if (fs20.existsSync(srcDir)) {
|
|
44654
44951
|
await scanDirectoryForLines(srcDir, enforceGlobs, excludeGlobs, false, (lines) => {
|
|
44655
44952
|
codeLines += lines;
|
|
44656
44953
|
});
|
|
@@ -44658,14 +44955,14 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
|
|
|
44658
44955
|
const possibleSrcDirs = ["lib", "app", "source", "core"];
|
|
44659
44956
|
for (const dir of possibleSrcDirs) {
|
|
44660
44957
|
const dirPath = path25.join(workingDir, dir);
|
|
44661
|
-
if (
|
|
44958
|
+
if (fs20.existsSync(dirPath)) {
|
|
44662
44959
|
await scanDirectoryForLines(dirPath, enforceGlobs, excludeGlobs, false, (lines) => {
|
|
44663
44960
|
codeLines += lines;
|
|
44664
44961
|
});
|
|
44665
44962
|
}
|
|
44666
44963
|
}
|
|
44667
44964
|
const testsDir = path25.join(workingDir, "tests");
|
|
44668
|
-
if (
|
|
44965
|
+
if (fs20.existsSync(testsDir)) {
|
|
44669
44966
|
await scanDirectoryForLines(testsDir, ["**"], ["node_modules", "dist"], true, (lines) => {
|
|
44670
44967
|
testLines += lines;
|
|
44671
44968
|
});
|
|
@@ -44673,7 +44970,7 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
|
|
|
44673
44970
|
const possibleTestDirs = ["test", "__tests__", "specs"];
|
|
44674
44971
|
for (const dir of possibleTestDirs) {
|
|
44675
44972
|
const dirPath = path25.join(workingDir, dir);
|
|
44676
|
-
if (
|
|
44973
|
+
if (fs20.existsSync(dirPath) && dirPath !== testsDir) {
|
|
44677
44974
|
await scanDirectoryForLines(dirPath, ["**"], ["node_modules", "dist"], true, (lines) => {
|
|
44678
44975
|
testLines += lines;
|
|
44679
44976
|
});
|
|
@@ -44685,7 +44982,7 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
|
|
|
44685
44982
|
}
|
|
44686
44983
|
async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTestScan, callback) {
|
|
44687
44984
|
try {
|
|
44688
|
-
const entries =
|
|
44985
|
+
const entries = fs20.readdirSync(dirPath, { withFileTypes: true });
|
|
44689
44986
|
for (const entry of entries) {
|
|
44690
44987
|
const fullPath = path25.join(dirPath, entry.name);
|
|
44691
44988
|
if (entry.isDirectory()) {
|
|
@@ -44733,7 +45030,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
|
|
|
44733
45030
|
continue;
|
|
44734
45031
|
}
|
|
44735
45032
|
try {
|
|
44736
|
-
const content =
|
|
45033
|
+
const content = fs20.readFileSync(fullPath, "utf-8");
|
|
44737
45034
|
const lines = countCodeLines(content);
|
|
44738
45035
|
callback(lines);
|
|
44739
45036
|
} catch {}
|
|
@@ -44949,7 +45246,7 @@ async function qualityBudget(input, directory) {
|
|
|
44949
45246
|
|
|
44950
45247
|
// src/tools/sast-scan.ts
|
|
44951
45248
|
init_manager();
|
|
44952
|
-
import * as
|
|
45249
|
+
import * as fs21 from "fs";
|
|
44953
45250
|
import * as path26 from "path";
|
|
44954
45251
|
import { extname as extname7 } from "path";
|
|
44955
45252
|
|
|
@@ -45813,17 +46110,17 @@ var SEVERITY_ORDER = {
|
|
|
45813
46110
|
};
|
|
45814
46111
|
function shouldSkipFile(filePath) {
|
|
45815
46112
|
try {
|
|
45816
|
-
const stats =
|
|
46113
|
+
const stats = fs21.statSync(filePath);
|
|
45817
46114
|
if (stats.size > MAX_FILE_SIZE_BYTES6) {
|
|
45818
46115
|
return { skip: true, reason: "file too large" };
|
|
45819
46116
|
}
|
|
45820
46117
|
if (stats.size === 0) {
|
|
45821
46118
|
return { skip: true, reason: "empty file" };
|
|
45822
46119
|
}
|
|
45823
|
-
const fd =
|
|
46120
|
+
const fd = fs21.openSync(filePath, "r");
|
|
45824
46121
|
const buffer = Buffer.alloc(8192);
|
|
45825
|
-
const bytesRead =
|
|
45826
|
-
|
|
46122
|
+
const bytesRead = fs21.readSync(fd, buffer, 0, 8192, 0);
|
|
46123
|
+
fs21.closeSync(fd);
|
|
45827
46124
|
if (bytesRead > 0) {
|
|
45828
46125
|
let nullCount = 0;
|
|
45829
46126
|
for (let i2 = 0;i2 < bytesRead; i2++) {
|
|
@@ -45862,7 +46159,7 @@ function countBySeverity(findings) {
|
|
|
45862
46159
|
}
|
|
45863
46160
|
function scanFileWithTierA(filePath, language) {
|
|
45864
46161
|
try {
|
|
45865
|
-
const content =
|
|
46162
|
+
const content = fs21.readFileSync(filePath, "utf-8");
|
|
45866
46163
|
const findings = executeRulesSync(filePath, content, language);
|
|
45867
46164
|
return findings.map((f) => ({
|
|
45868
46165
|
rule_id: f.rule_id,
|
|
@@ -45906,7 +46203,7 @@ async function sastScan(input, directory, config3) {
|
|
|
45906
46203
|
const filesByLanguage = new Map;
|
|
45907
46204
|
for (const filePath of changed_files) {
|
|
45908
46205
|
const resolvedPath = path26.isAbsolute(filePath) ? filePath : path26.resolve(directory, filePath);
|
|
45909
|
-
if (!
|
|
46206
|
+
if (!fs21.existsSync(resolvedPath)) {
|
|
45910
46207
|
_filesSkipped++;
|
|
45911
46208
|
continue;
|
|
45912
46209
|
}
|
|
@@ -46329,7 +46626,7 @@ var retrieve_summary = tool({
|
|
|
46329
46626
|
// src/tools/sbom-generate.ts
|
|
46330
46627
|
init_dist();
|
|
46331
46628
|
init_manager();
|
|
46332
|
-
import * as
|
|
46629
|
+
import * as fs22 from "fs";
|
|
46333
46630
|
import * as path28 from "path";
|
|
46334
46631
|
|
|
46335
46632
|
// src/sbom/detectors/dart.ts
|
|
@@ -47175,7 +47472,7 @@ function findManifestFiles(rootDir) {
|
|
|
47175
47472
|
const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
|
|
47176
47473
|
function searchDir(dir) {
|
|
47177
47474
|
try {
|
|
47178
|
-
const entries =
|
|
47475
|
+
const entries = fs22.readdirSync(dir, { withFileTypes: true });
|
|
47179
47476
|
for (const entry of entries) {
|
|
47180
47477
|
const fullPath = path28.join(dir, entry.name);
|
|
47181
47478
|
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
|
|
@@ -47204,7 +47501,7 @@ function findManifestFilesInDirs(directories, workingDir) {
|
|
|
47204
47501
|
const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
|
|
47205
47502
|
for (const dir of directories) {
|
|
47206
47503
|
try {
|
|
47207
|
-
const entries =
|
|
47504
|
+
const entries = fs22.readdirSync(dir, { withFileTypes: true });
|
|
47208
47505
|
for (const entry of entries) {
|
|
47209
47506
|
const fullPath = path28.join(dir, entry.name);
|
|
47210
47507
|
if (entry.isFile()) {
|
|
@@ -47242,7 +47539,7 @@ function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
|
|
|
47242
47539
|
}
|
|
47243
47540
|
function ensureOutputDir(outputDir) {
|
|
47244
47541
|
try {
|
|
47245
|
-
|
|
47542
|
+
fs22.mkdirSync(outputDir, { recursive: true });
|
|
47246
47543
|
} catch (error93) {
|
|
47247
47544
|
if (!error93 || error93.code !== "EEXIST") {
|
|
47248
47545
|
throw error93;
|
|
@@ -47336,10 +47633,10 @@ var sbom_generate = tool({
|
|
|
47336
47633
|
for (const manifestFile of manifestFiles) {
|
|
47337
47634
|
try {
|
|
47338
47635
|
const fullPath = path28.isAbsolute(manifestFile) ? manifestFile : path28.join(workingDir, manifestFile);
|
|
47339
|
-
if (!
|
|
47636
|
+
if (!fs22.existsSync(fullPath)) {
|
|
47340
47637
|
continue;
|
|
47341
47638
|
}
|
|
47342
|
-
const content =
|
|
47639
|
+
const content = fs22.readFileSync(fullPath, "utf-8");
|
|
47343
47640
|
const components = detectComponents(manifestFile, content);
|
|
47344
47641
|
processedFiles.push(manifestFile);
|
|
47345
47642
|
if (components.length > 0) {
|
|
@@ -47353,7 +47650,7 @@ var sbom_generate = tool({
|
|
|
47353
47650
|
const bomJson = serializeCycloneDX(bom);
|
|
47354
47651
|
const filename = generateSbomFilename();
|
|
47355
47652
|
const outputPath = path28.join(outputDir, filename);
|
|
47356
|
-
|
|
47653
|
+
fs22.writeFileSync(outputPath, bomJson, "utf-8");
|
|
47357
47654
|
const verdict = processedFiles.length > 0 ? "pass" : "pass";
|
|
47358
47655
|
try {
|
|
47359
47656
|
const timestamp = new Date().toISOString();
|
|
@@ -47394,7 +47691,7 @@ var sbom_generate = tool({
|
|
|
47394
47691
|
});
|
|
47395
47692
|
// src/tools/schema-drift.ts
|
|
47396
47693
|
init_dist();
|
|
47397
|
-
import * as
|
|
47694
|
+
import * as fs23 from "fs";
|
|
47398
47695
|
import * as path29 from "path";
|
|
47399
47696
|
var SPEC_CANDIDATES = [
|
|
47400
47697
|
"openapi.json",
|
|
@@ -47436,19 +47733,19 @@ function discoverSpecFile(cwd, specFileArg) {
|
|
|
47436
47733
|
if (!ALLOWED_EXTENSIONS.includes(ext)) {
|
|
47437
47734
|
throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
|
|
47438
47735
|
}
|
|
47439
|
-
const stats =
|
|
47736
|
+
const stats = fs23.statSync(resolvedPath);
|
|
47440
47737
|
if (stats.size > MAX_SPEC_SIZE) {
|
|
47441
47738
|
throw new Error(`Invalid spec_file: file exceeds ${MAX_SPEC_SIZE / 1024 / 1024}MB limit`);
|
|
47442
47739
|
}
|
|
47443
|
-
if (!
|
|
47740
|
+
if (!fs23.existsSync(resolvedPath)) {
|
|
47444
47741
|
throw new Error(`Spec file not found: ${resolvedPath}`);
|
|
47445
47742
|
}
|
|
47446
47743
|
return resolvedPath;
|
|
47447
47744
|
}
|
|
47448
47745
|
for (const candidate of SPEC_CANDIDATES) {
|
|
47449
47746
|
const candidatePath = path29.resolve(cwd, candidate);
|
|
47450
|
-
if (
|
|
47451
|
-
const stats =
|
|
47747
|
+
if (fs23.existsSync(candidatePath)) {
|
|
47748
|
+
const stats = fs23.statSync(candidatePath);
|
|
47452
47749
|
if (stats.size <= MAX_SPEC_SIZE) {
|
|
47453
47750
|
return candidatePath;
|
|
47454
47751
|
}
|
|
@@ -47457,7 +47754,7 @@ function discoverSpecFile(cwd, specFileArg) {
|
|
|
47457
47754
|
return null;
|
|
47458
47755
|
}
|
|
47459
47756
|
function parseSpec(specFile) {
|
|
47460
|
-
const content =
|
|
47757
|
+
const content = fs23.readFileSync(specFile, "utf-8");
|
|
47461
47758
|
const ext = path29.extname(specFile).toLowerCase();
|
|
47462
47759
|
if (ext === ".json") {
|
|
47463
47760
|
return parseJsonSpec(content);
|
|
@@ -47524,7 +47821,7 @@ function extractRoutes(cwd) {
|
|
|
47524
47821
|
function walkDir(dir) {
|
|
47525
47822
|
let entries;
|
|
47526
47823
|
try {
|
|
47527
|
-
entries =
|
|
47824
|
+
entries = fs23.readdirSync(dir, { withFileTypes: true });
|
|
47528
47825
|
} catch {
|
|
47529
47826
|
return;
|
|
47530
47827
|
}
|
|
@@ -47557,7 +47854,7 @@ function extractRoutes(cwd) {
|
|
|
47557
47854
|
}
|
|
47558
47855
|
function extractRoutesFromFile(filePath) {
|
|
47559
47856
|
const routes = [];
|
|
47560
|
-
const content =
|
|
47857
|
+
const content = fs23.readFileSync(filePath, "utf-8");
|
|
47561
47858
|
const lines = content.split(/\r?\n/);
|
|
47562
47859
|
const expressRegex = /(?:app|router|server|express)\.(get|post|put|patch|delete|options|head)\s*\(\s*['"`]([^'"`]+)['"`]/g;
|
|
47563
47860
|
const flaskRegex = /@(?:app|blueprint|bp)\.route\s*\(\s*['"]([^'"]+)['"]/g;
|
|
@@ -47707,7 +48004,7 @@ init_secretscan();
|
|
|
47707
48004
|
|
|
47708
48005
|
// src/tools/symbols.ts
|
|
47709
48006
|
init_tool();
|
|
47710
|
-
import * as
|
|
48007
|
+
import * as fs24 from "fs";
|
|
47711
48008
|
import * as path30 from "path";
|
|
47712
48009
|
var MAX_FILE_SIZE_BYTES7 = 1024 * 1024;
|
|
47713
48010
|
var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
|
|
@@ -47738,8 +48035,8 @@ function containsWindowsAttacks(str) {
|
|
|
47738
48035
|
function isPathInWorkspace(filePath, workspace) {
|
|
47739
48036
|
try {
|
|
47740
48037
|
const resolvedPath = path30.resolve(workspace, filePath);
|
|
47741
|
-
const realWorkspace =
|
|
47742
|
-
const realResolvedPath =
|
|
48038
|
+
const realWorkspace = fs24.realpathSync(workspace);
|
|
48039
|
+
const realResolvedPath = fs24.realpathSync(resolvedPath);
|
|
47743
48040
|
const relativePath = path30.relative(realWorkspace, realResolvedPath);
|
|
47744
48041
|
if (relativePath.startsWith("..") || path30.isAbsolute(relativePath)) {
|
|
47745
48042
|
return false;
|
|
@@ -47759,11 +48056,11 @@ function extractTSSymbols(filePath, cwd) {
|
|
|
47759
48056
|
}
|
|
47760
48057
|
let content;
|
|
47761
48058
|
try {
|
|
47762
|
-
const stats =
|
|
48059
|
+
const stats = fs24.statSync(fullPath);
|
|
47763
48060
|
if (stats.size > MAX_FILE_SIZE_BYTES7) {
|
|
47764
48061
|
throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
|
|
47765
48062
|
}
|
|
47766
|
-
content =
|
|
48063
|
+
content = fs24.readFileSync(fullPath, "utf-8");
|
|
47767
48064
|
} catch {
|
|
47768
48065
|
return [];
|
|
47769
48066
|
}
|
|
@@ -47911,11 +48208,11 @@ function extractPythonSymbols(filePath, cwd) {
|
|
|
47911
48208
|
}
|
|
47912
48209
|
let content;
|
|
47913
48210
|
try {
|
|
47914
|
-
const stats =
|
|
48211
|
+
const stats = fs24.statSync(fullPath);
|
|
47915
48212
|
if (stats.size > MAX_FILE_SIZE_BYTES7) {
|
|
47916
48213
|
throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
|
|
47917
48214
|
}
|
|
47918
|
-
content =
|
|
48215
|
+
content = fs24.readFileSync(fullPath, "utf-8");
|
|
47919
48216
|
} catch {
|
|
47920
48217
|
return [];
|
|
47921
48218
|
}
|
|
@@ -48055,7 +48352,7 @@ init_test_runner();
|
|
|
48055
48352
|
|
|
48056
48353
|
// src/tools/todo-extract.ts
|
|
48057
48354
|
init_dist();
|
|
48058
|
-
import * as
|
|
48355
|
+
import * as fs25 from "fs";
|
|
48059
48356
|
import * as path31 from "path";
|
|
48060
48357
|
var MAX_TEXT_LENGTH = 200;
|
|
48061
48358
|
var MAX_FILE_SIZE_BYTES8 = 1024 * 1024;
|
|
@@ -48151,7 +48448,7 @@ function isSupportedExtension(filePath) {
|
|
|
48151
48448
|
function findSourceFiles3(dir, files = []) {
|
|
48152
48449
|
let entries;
|
|
48153
48450
|
try {
|
|
48154
|
-
entries =
|
|
48451
|
+
entries = fs25.readdirSync(dir);
|
|
48155
48452
|
} catch {
|
|
48156
48453
|
return files;
|
|
48157
48454
|
}
|
|
@@ -48163,7 +48460,7 @@ function findSourceFiles3(dir, files = []) {
|
|
|
48163
48460
|
const fullPath = path31.join(dir, entry);
|
|
48164
48461
|
let stat;
|
|
48165
48462
|
try {
|
|
48166
|
-
stat =
|
|
48463
|
+
stat = fs25.statSync(fullPath);
|
|
48167
48464
|
} catch {
|
|
48168
48465
|
continue;
|
|
48169
48466
|
}
|
|
@@ -48256,7 +48553,7 @@ var todo_extract = tool({
|
|
|
48256
48553
|
return JSON.stringify(errorResult, null, 2);
|
|
48257
48554
|
}
|
|
48258
48555
|
const scanPath = resolvedPath;
|
|
48259
|
-
if (!
|
|
48556
|
+
if (!fs25.existsSync(scanPath)) {
|
|
48260
48557
|
const errorResult = {
|
|
48261
48558
|
error: `path not found: ${pathsInput}`,
|
|
48262
48559
|
total: 0,
|
|
@@ -48266,7 +48563,7 @@ var todo_extract = tool({
|
|
|
48266
48563
|
return JSON.stringify(errorResult, null, 2);
|
|
48267
48564
|
}
|
|
48268
48565
|
const filesToScan = [];
|
|
48269
|
-
const stat =
|
|
48566
|
+
const stat = fs25.statSync(scanPath);
|
|
48270
48567
|
if (stat.isFile()) {
|
|
48271
48568
|
if (isSupportedExtension(scanPath)) {
|
|
48272
48569
|
filesToScan.push(scanPath);
|
|
@@ -48285,11 +48582,11 @@ var todo_extract = tool({
|
|
|
48285
48582
|
const allEntries = [];
|
|
48286
48583
|
for (const filePath of filesToScan) {
|
|
48287
48584
|
try {
|
|
48288
|
-
const fileStat =
|
|
48585
|
+
const fileStat = fs25.statSync(filePath);
|
|
48289
48586
|
if (fileStat.size > MAX_FILE_SIZE_BYTES8) {
|
|
48290
48587
|
continue;
|
|
48291
48588
|
}
|
|
48292
|
-
const content =
|
|
48589
|
+
const content = fs25.readFileSync(filePath, "utf-8");
|
|
48293
48590
|
const entries = parseTodoComments(content, filePath, tagsSet);
|
|
48294
48591
|
allEntries.push(...entries);
|
|
48295
48592
|
} catch {}
|
|
@@ -48490,6 +48787,7 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
48490
48787
|
lint,
|
|
48491
48788
|
diff,
|
|
48492
48789
|
pkg_audit,
|
|
48790
|
+
phase_complete,
|
|
48493
48791
|
pre_check_batch,
|
|
48494
48792
|
retrieve_summary,
|
|
48495
48793
|
schema_drift,
|
|
@@ -48507,7 +48805,7 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
48507
48805
|
opencodeConfig.command = {
|
|
48508
48806
|
...opencodeConfig.command || {},
|
|
48509
48807
|
swarm: {
|
|
48510
|
-
template:
|
|
48808
|
+
template: "/swarm $ARGUMENTS",
|
|
48511
48809
|
description: "Swarm management commands"
|
|
48512
48810
|
}
|
|
48513
48811
|
};
|
package/dist/state.d.ts
CHANGED
|
@@ -75,6 +75,12 @@ export interface AgentSessionState {
|
|
|
75
75
|
selfFixAttempted: boolean;
|
|
76
76
|
/** Phases that have already received a catastrophic zero-reviewer warning */
|
|
77
77
|
catastrophicPhaseWarnings: Set<number>;
|
|
78
|
+
/** Timestamp of most recent phase completion */
|
|
79
|
+
lastPhaseCompleteTimestamp: number;
|
|
80
|
+
/** Phase number of most recent phase completion */
|
|
81
|
+
lastPhaseCompletePhase: number;
|
|
82
|
+
/** Set of agents dispatched in current phase (normalized names) */
|
|
83
|
+
phaseAgentsDispatched: Set<string>;
|
|
78
84
|
}
|
|
79
85
|
/**
|
|
80
86
|
* Represents a single agent invocation window with isolated guardrail budgets.
|
|
@@ -190,3 +196,10 @@ export declare function getActiveWindow(sessionId: string): InvocationWindow | u
|
|
|
190
196
|
* @param maxWindows - Maximum number of windows to keep (default 50)
|
|
191
197
|
*/
|
|
192
198
|
export declare function pruneOldWindows(sessionId: string, maxAgeMs?: number, maxWindows?: number): void;
|
|
199
|
+
/**
|
|
200
|
+
* Record an agent dispatch for phase completion tracking.
|
|
201
|
+
* Normalizes the agent name via stripKnownSwarmPrefix before adding to phaseAgentsDispatched.
|
|
202
|
+
* @param sessionId - Session identifier
|
|
203
|
+
* @param agentName - Agent name to record (will be normalized)
|
|
204
|
+
*/
|
|
205
|
+
export declare function recordPhaseAgentDispatch(sessionId: string, agentName: string): void;
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export { extract_code_blocks } from './file-extractor';
|
|
|
8
8
|
export { fetchGitingest, type GitingestArgs, gitingest } from './gitingest';
|
|
9
9
|
export { imports } from './imports';
|
|
10
10
|
export { lint } from './lint';
|
|
11
|
+
export { phase_complete } from './phase-complete';
|
|
11
12
|
export { pkg_audit } from './pkg-audit';
|
|
12
13
|
export { type PlaceholderFinding, type PlaceholderScanInput, type PlaceholderScanResult, placeholderScan, } from './placeholder-scan';
|
|
13
14
|
export { type PreCheckBatchInput, type PreCheckBatchResult, pre_check_batch, runPreCheckBatch, type ToolResult, } from './pre-check-batch';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase completion tool for tracking and validating phase completion.
|
|
3
|
+
* Core implementation - gathers data, enforces policy, writes event, resets state.
|
|
4
|
+
*/
|
|
5
|
+
import { type ToolDefinition } from '@opencode-ai/plugin/tool';
|
|
6
|
+
/**
|
|
7
|
+
* Tool definition for phase_complete
|
|
8
|
+
*/
|
|
9
|
+
export declare const phase_complete: ToolDefinition;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Used for constants and agent setup references.
|
|
4
4
|
*/
|
|
5
5
|
/** Union type of all valid tool names */
|
|
6
|
-
export type ToolName = 'diff' | 'syntax_check' | 'placeholder_scan' | 'imports' | 'lint' | 'secretscan' | 'sast_scan' | 'build_check' | 'pre_check_batch' | 'quality_budget' | 'symbols' | 'complexity_hotspots' | 'schema_drift' | 'todo_extract' | 'evidence_check' | 'sbom_generate' | 'checkpoint' | 'pkg_audit' | 'test_runner' | 'detect_domains' | 'gitingest' | 'retrieve_summary' | 'extract_code_blocks';
|
|
6
|
+
export type ToolName = 'diff' | 'syntax_check' | 'placeholder_scan' | 'imports' | 'lint' | 'secretscan' | 'sast_scan' | 'build_check' | 'pre_check_batch' | 'quality_budget' | 'symbols' | 'complexity_hotspots' | 'schema_drift' | 'todo_extract' | 'evidence_check' | 'sbom_generate' | 'checkpoint' | 'pkg_audit' | 'test_runner' | 'detect_domains' | 'gitingest' | 'retrieve_summary' | 'extract_code_blocks' | 'phase_complete';
|
|
7
7
|
/** Readonly array of all tool names */
|
|
8
8
|
export declare const TOOL_NAMES: readonly ToolName[];
|
|
9
9
|
/** Set for O(1) tool name validation */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "6.13.
|
|
3
|
+
"version": "6.13.2",
|
|
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",
|