@mknightzzz/stw 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +277 -0
- package/dist/agentic-fallback.d.ts +3 -0
- package/dist/agentic-fallback.js +32 -0
- package/dist/agentic-fallback.js.map +1 -0
- package/dist/agentic-prompt.d.ts +2 -0
- package/dist/agentic-prompt.js +68 -0
- package/dist/agentic-prompt.js.map +1 -0
- package/dist/agentic-runtime.d.ts +48 -0
- package/dist/agentic-runtime.js +149 -0
- package/dist/agentic-runtime.js.map +1 -0
- package/dist/agentic-types.d.ts +37 -0
- package/dist/agentic-types.js +2 -0
- package/dist/agentic-types.js.map +1 -0
- package/dist/agents.d.ts +7 -0
- package/dist/agents.js +2 -0
- package/dist/agents.js.map +1 -0
- package/dist/assignments.d.ts +7 -0
- package/dist/assignments.js +125 -0
- package/dist/assignments.js.map +1 -0
- package/dist/checkpoint.d.ts +35 -0
- package/dist/checkpoint.js +78 -0
- package/dist/checkpoint.js.map +1 -0
- package/dist/circuit-breaker.d.ts +17 -0
- package/dist/circuit-breaker.js +65 -0
- package/dist/circuit-breaker.js.map +1 -0
- package/dist/claim.d.ts +6 -0
- package/dist/claim.js +135 -0
- package/dist/claim.js.map +1 -0
- package/dist/clarity-gate.d.ts +12 -0
- package/dist/clarity-gate.js +83 -0
- package/dist/clarity-gate.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +38 -0
- package/dist/cli.js.map +1 -0
- package/dist/command-dispatch.d.ts +45 -0
- package/dist/command-dispatch.js +206 -0
- package/dist/command-dispatch.js.map +1 -0
- package/dist/command-parser.d.ts +11 -0
- package/dist/command-parser.js +101 -0
- package/dist/command-parser.js.map +1 -0
- package/dist/commands/clean.d.ts +10 -0
- package/dist/commands/clean.js +133 -0
- package/dist/commands/clean.js.map +1 -0
- package/dist/commands/execution.d.ts +2 -0
- package/dist/commands/execution.js +327 -0
- package/dist/commands/execution.js.map +1 -0
- package/dist/commands/go.d.ts +2 -0
- package/dist/commands/go.js +197 -0
- package/dist/commands/go.js.map +1 -0
- package/dist/commands/helpers.d.ts +44 -0
- package/dist/commands/helpers.js +231 -0
- package/dist/commands/helpers.js.map +1 -0
- package/dist/commands/idea.d.ts +2 -0
- package/dist/commands/idea.js +89 -0
- package/dist/commands/idea.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +94 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/integration.d.ts +7 -0
- package/dist/commands/integration.js +139 -0
- package/dist/commands/integration.js.map +1 -0
- package/dist/commands/maintenance.d.ts +2 -0
- package/dist/commands/maintenance.js +301 -0
- package/dist/commands/maintenance.js.map +1 -0
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.js +356 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/setup.d.ts +2 -0
- package/dist/commands/setup.js +198 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/spec.d.ts +2 -0
- package/dist/commands/spec.js +35 -0
- package/dist/commands/spec.js.map +1 -0
- package/dist/commands/stats.d.ts +2 -0
- package/dist/commands/stats.js +80 -0
- package/dist/commands/stats.js.map +1 -0
- package/dist/commands/task-ops.d.ts +2 -0
- package/dist/commands/task-ops.js +406 -0
- package/dist/commands/task-ops.js.map +1 -0
- package/dist/config.d.ts +18 -0
- package/dist/config.js +338 -0
- package/dist/config.js.map +1 -0
- package/dist/cost.d.ts +30 -0
- package/dist/cost.js +167 -0
- package/dist/cost.js.map +1 -0
- package/dist/crash-recovery.d.ts +9 -0
- package/dist/crash-recovery.js +42 -0
- package/dist/crash-recovery.js.map +1 -0
- package/dist/diagnostic.d.ts +48 -0
- package/dist/diagnostic.js +328 -0
- package/dist/diagnostic.js.map +1 -0
- package/dist/doctor.d.ts +31 -0
- package/dist/doctor.js +225 -0
- package/dist/doctor.js.map +1 -0
- package/dist/drift.d.ts +11 -0
- package/dist/drift.js +57 -0
- package/dist/drift.js.map +1 -0
- package/dist/git-utils.d.ts +20 -0
- package/dist/git-utils.js +206 -0
- package/dist/git-utils.js.map +1 -0
- package/dist/gitlab.d.ts +54 -0
- package/dist/gitlab.js +101 -0
- package/dist/gitlab.js.map +1 -0
- package/dist/idea.d.ts +35 -0
- package/dist/idea.js +251 -0
- package/dist/idea.js.map +1 -0
- package/dist/import-resolution.d.ts +13 -0
- package/dist/import-resolution.js +111 -0
- package/dist/import-resolution.js.map +1 -0
- package/dist/inbox-renderer.d.ts +2 -0
- package/dist/inbox-renderer.js +67 -0
- package/dist/inbox-renderer.js.map +1 -0
- package/dist/init.d.ts +105 -0
- package/dist/init.js +235 -0
- package/dist/init.js.map +1 -0
- package/dist/llm-reviewer.d.ts +14 -0
- package/dist/llm-reviewer.js +109 -0
- package/dist/llm-reviewer.js.map +1 -0
- package/dist/lock.d.ts +26 -0
- package/dist/lock.js +76 -0
- package/dist/lock.js.map +1 -0
- package/dist/logger.d.ts +24 -0
- package/dist/logger.js +40 -0
- package/dist/logger.js.map +1 -0
- package/dist/math-utils.d.ts +2 -0
- package/dist/math-utils.js +7 -0
- package/dist/math-utils.js.map +1 -0
- package/dist/mechanical-review.d.ts +30 -0
- package/dist/mechanical-review.js +76 -0
- package/dist/mechanical-review.js.map +1 -0
- package/dist/merge.d.ts +83 -0
- package/dist/merge.js +363 -0
- package/dist/merge.js.map +1 -0
- package/dist/parallel.d.ts +35 -0
- package/dist/parallel.js +214 -0
- package/dist/parallel.js.map +1 -0
- package/dist/plan-validation.d.ts +19 -0
- package/dist/plan-validation.js +253 -0
- package/dist/plan-validation.js.map +1 -0
- package/dist/planner-prompt.d.ts +33 -0
- package/dist/planner-prompt.js +244 -0
- package/dist/planner-prompt.js.map +1 -0
- package/dist/planner.d.ts +29 -0
- package/dist/planner.js +511 -0
- package/dist/planner.js.map +1 -0
- package/dist/poller.d.ts +34 -0
- package/dist/poller.js +91 -0
- package/dist/poller.js.map +1 -0
- package/dist/progress.d.ts +34 -0
- package/dist/progress.js +122 -0
- package/dist/progress.js.map +1 -0
- package/dist/prompt-builder.d.ts +51 -0
- package/dist/prompt-builder.js +481 -0
- package/dist/prompt-builder.js.map +1 -0
- package/dist/provider.d.ts +14 -0
- package/dist/provider.js +278 -0
- package/dist/provider.js.map +1 -0
- package/dist/question-handler.d.ts +18 -0
- package/dist/question-handler.js +154 -0
- package/dist/question-handler.js.map +1 -0
- package/dist/question-triage.d.ts +31 -0
- package/dist/question-triage.js +175 -0
- package/dist/question-triage.js.map +1 -0
- package/dist/repo-detection.d.ts +8 -0
- package/dist/repo-detection.js +18 -0
- package/dist/repo-detection.js.map +1 -0
- package/dist/retry-context.d.ts +2 -0
- package/dist/retry-context.js +196 -0
- package/dist/retry-context.js.map +1 -0
- package/dist/router.d.ts +18 -0
- package/dist/router.js +137 -0
- package/dist/router.js.map +1 -0
- package/dist/run-artifact-types.d.ts +43 -0
- package/dist/run-artifact-types.js +2 -0
- package/dist/run-artifact-types.js.map +1 -0
- package/dist/run-summary.d.ts +14 -0
- package/dist/run-summary.js +347 -0
- package/dist/run-summary.js.map +1 -0
- package/dist/run-sync.d.ts +11 -0
- package/dist/run-sync.js +110 -0
- package/dist/run-sync.js.map +1 -0
- package/dist/run.d.ts +26 -0
- package/dist/run.js +150 -0
- package/dist/run.js.map +1 -0
- package/dist/scope-expansion.d.ts +10 -0
- package/dist/scope-expansion.js +117 -0
- package/dist/scope-expansion.js.map +1 -0
- package/dist/scope.d.ts +4 -0
- package/dist/scope.js +37 -0
- package/dist/scope.js.map +1 -0
- package/dist/scorecard.d.ts +18 -0
- package/dist/scorecard.js +128 -0
- package/dist/scorecard.js.map +1 -0
- package/dist/spec-templates.d.ts +2 -0
- package/dist/spec-templates.js +285 -0
- package/dist/spec-templates.js.map +1 -0
- package/dist/spec-validator.d.ts +8 -0
- package/dist/spec-validator.js +144 -0
- package/dist/spec-validator.js.map +1 -0
- package/dist/status.d.ts +68 -0
- package/dist/status.js +261 -0
- package/dist/status.js.map +1 -0
- package/dist/storage.d.ts +9 -0
- package/dist/storage.js +35 -0
- package/dist/storage.js.map +1 -0
- package/dist/task-executor-completion.d.ts +12 -0
- package/dist/task-executor-completion.js +67 -0
- package/dist/task-executor-completion.js.map +1 -0
- package/dist/task-executor-fallback.d.ts +20 -0
- package/dist/task-executor-fallback.js +12 -0
- package/dist/task-executor-fallback.js.map +1 -0
- package/dist/task-executor.d.ts +34 -0
- package/dist/task-executor.js +521 -0
- package/dist/task-executor.js.map +1 -0
- package/dist/task-graph.d.ts +11 -0
- package/dist/task-graph.js +226 -0
- package/dist/task-graph.js.map +1 -0
- package/dist/task-pipeline-helpers.d.ts +45 -0
- package/dist/task-pipeline-helpers.js +160 -0
- package/dist/task-pipeline-helpers.js.map +1 -0
- package/dist/task-review.d.ts +51 -0
- package/dist/task-review.js +410 -0
- package/dist/task-review.js.map +1 -0
- package/dist/transitions.d.ts +13 -0
- package/dist/transitions.js +104 -0
- package/dist/transitions.js.map +1 -0
- package/dist/types.d.ts +405 -0
- package/dist/types.js +101 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +23 -0
- package/dist/utils.js.map +1 -0
- package/dist/validation.d.ts +19 -0
- package/dist/validation.js +73 -0
- package/dist/validation.js.map +1 -0
- package/dist/worker-response.d.ts +12 -0
- package/dist/worker-response.js +60 -0
- package/dist/worker-response.js.map +1 -0
- package/dist/worker-runner.d.ts +19 -0
- package/dist/worker-runner.js +347 -0
- package/dist/worker-runner.js.map +1 -0
- package/dist/worktree-cleanup.d.ts +44 -0
- package/dist/worktree-cleanup.js +325 -0
- package/dist/worktree-cleanup.js.map +1 -0
- package/dist/worktree.d.ts +22 -0
- package/dist/worktree.js +213 -0
- package/dist/worktree.js.map +1 -0
- package/examples/spec.md +58 -0
- package/package.json +66 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentic-types.js","sourceRoot":"","sources":["../src/agentic-types.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,UAAU,EAAE,aAAa,CAAU,CAAC"}
|
package/dist/agents.d.ts
ADDED
package/dist/agents.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents.js","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AssignmentsFile, TaskGraph, StwConfig } from './types.js';
|
|
2
|
+
import type { AgentDefinition } from './agents.js';
|
|
3
|
+
export interface AssignmentsResult {
|
|
4
|
+
assignments: AssignmentsFile;
|
|
5
|
+
warnings: string[];
|
|
6
|
+
}
|
|
7
|
+
export declare function generateAssignments(graph: TaskGraph, agents: AgentDefinition[], config: StwConfig): AssignmentsResult;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { routeTask } from './router.js';
|
|
2
|
+
const AGENT_FALLBACKS = {
|
|
3
|
+
cheap: ['cheap', 'medium', 'strong'],
|
|
4
|
+
medium: ['medium', 'strong'],
|
|
5
|
+
strong: ['strong'],
|
|
6
|
+
reader: ['medium', 'strong'],
|
|
7
|
+
};
|
|
8
|
+
export function generateAssignments(graph, agents, config) {
|
|
9
|
+
const warnings = [...detectScopeOverlaps(graph), ...detectStaleAssignmentsAfterReplan(graph)];
|
|
10
|
+
const assignments = graph.tasks.map((task) => createAssignment(task, agents, config));
|
|
11
|
+
return {
|
|
12
|
+
assignments: {
|
|
13
|
+
schema_version: 1,
|
|
14
|
+
run_id: graph.run_id,
|
|
15
|
+
assignments,
|
|
16
|
+
},
|
|
17
|
+
warnings,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function createAssignment(task, agents, config) {
|
|
21
|
+
const route = routeTask({
|
|
22
|
+
risk: task.risk,
|
|
23
|
+
task_type: task.task_type,
|
|
24
|
+
model_tier: task.model_tier,
|
|
25
|
+
}, config);
|
|
26
|
+
const agent = selectAgent(agents, route.tier);
|
|
27
|
+
const reasonParts = [
|
|
28
|
+
`task_type=${task.task_type}`,
|
|
29
|
+
`risk=${task.risk}`,
|
|
30
|
+
`provider=${route.provider}`,
|
|
31
|
+
`provider_model=${route.model}`,
|
|
32
|
+
];
|
|
33
|
+
if (agent.tier !== route.tier) {
|
|
34
|
+
reasonParts.push(`agent_tier=${agent.tier}`);
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
task_id: task.id,
|
|
38
|
+
agent_id: agent.agent_id,
|
|
39
|
+
tool: agent.tool,
|
|
40
|
+
model: agent.model,
|
|
41
|
+
tier: route.tier,
|
|
42
|
+
reason: reasonParts.join(', '),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function selectAgent(agents, tier) {
|
|
46
|
+
for (const candidateTier of AGENT_FALLBACKS[tier]) {
|
|
47
|
+
const agent = agents.find((item) => item.tier === candidateTier);
|
|
48
|
+
if (agent) {
|
|
49
|
+
return agent;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
throw new Error(`No agent available for tier "${tier}"`);
|
|
53
|
+
}
|
|
54
|
+
function detectScopeOverlaps(graph) {
|
|
55
|
+
const warnings = [];
|
|
56
|
+
const taskById = new Map(graph.tasks.map((task) => [task.id, task]));
|
|
57
|
+
const reachabilityCache = new Map();
|
|
58
|
+
for (let index = 0; index < graph.tasks.length; index += 1) {
|
|
59
|
+
for (let otherIndex = index + 1; otherIndex < graph.tasks.length; otherIndex += 1) {
|
|
60
|
+
const left = graph.tasks[index];
|
|
61
|
+
const right = graph.tasks[otherIndex];
|
|
62
|
+
if (isSerialized(left.id, right.id, taskById, reachabilityCache)) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
const overlap = intersectFiles(left.scope.files, right.scope.files);
|
|
66
|
+
if (overlap.length === 0) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (left.scope.allow_shared_files && right.scope.allow_shared_files) {
|
|
70
|
+
warnings.push(`Shared-file parallelism risk between ${left.id} and ${right.id}: ${overlap.join(', ')}`);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
warnings.push(`Scope overlap between ${left.id} and ${right.id}: ${overlap.join(', ')}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return warnings;
|
|
77
|
+
}
|
|
78
|
+
function detectStaleAssignmentsAfterReplan(graph) {
|
|
79
|
+
const warnings = [];
|
|
80
|
+
const seen = new Set();
|
|
81
|
+
for (const task of graph.tasks) {
|
|
82
|
+
if (seen.has(task.id)) {
|
|
83
|
+
warnings.push(`Stale assignment risk after replan: duplicate task ID ${task.id}`);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
seen.add(task.id);
|
|
87
|
+
}
|
|
88
|
+
return warnings;
|
|
89
|
+
}
|
|
90
|
+
function isSerialized(leftTaskId, rightTaskId, taskById, reachabilityCache) {
|
|
91
|
+
return (hasDependencyPath(leftTaskId, rightTaskId, taskById, reachabilityCache) ||
|
|
92
|
+
hasDependencyPath(rightTaskId, leftTaskId, taskById, reachabilityCache));
|
|
93
|
+
}
|
|
94
|
+
function hasDependencyPath(fromTaskId, toTaskId, taskById, reachabilityCache) {
|
|
95
|
+
return getReachableDependencies(fromTaskId, taskById, reachabilityCache).has(toTaskId);
|
|
96
|
+
}
|
|
97
|
+
function getReachableDependencies(taskId, taskById, reachabilityCache) {
|
|
98
|
+
const cached = reachabilityCache.get(taskId);
|
|
99
|
+
if (cached) {
|
|
100
|
+
return cached;
|
|
101
|
+
}
|
|
102
|
+
const task = taskById.get(taskId);
|
|
103
|
+
const reachable = new Set();
|
|
104
|
+
if (task) {
|
|
105
|
+
for (const dependencyId of task.dependencies) {
|
|
106
|
+
reachable.add(dependencyId);
|
|
107
|
+
for (const nestedDependencyId of getReachableDependencies(dependencyId, taskById, reachabilityCache)) {
|
|
108
|
+
reachable.add(nestedDependencyId);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
reachabilityCache.set(taskId, reachable);
|
|
113
|
+
return reachable;
|
|
114
|
+
}
|
|
115
|
+
function intersectFiles(leftFiles, rightFiles) {
|
|
116
|
+
const rightSet = new Set(rightFiles);
|
|
117
|
+
const overlap = [];
|
|
118
|
+
for (const file of leftFiles) {
|
|
119
|
+
if (rightSet.has(file) && !overlap.includes(file)) {
|
|
120
|
+
overlap.push(file);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return overlap.sort();
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=assignments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assignments.js","sourceRoot":"","sources":["../src/assignments.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export declare const CHECKPOINT_ORDER: readonly ["pre_worker", "post_worker", "post_write", "post_validate", "post_review", "complete"];
|
|
2
|
+
export type CheckpointName = (typeof CHECKPOINT_ORDER)[number];
|
|
3
|
+
export interface ProcessedNotesCheckpoint {
|
|
4
|
+
schema_version: 1;
|
|
5
|
+
mr_iid: number;
|
|
6
|
+
last_seen_at: string;
|
|
7
|
+
last_processed_note_id: number | null;
|
|
8
|
+
processed_note_ids: number[];
|
|
9
|
+
updated_at: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Reads the checkpoint directory and returns the name of the next
|
|
13
|
+
* incomplete checkpoint. Walks the ordered list and returns the first
|
|
14
|
+
* checkpoint whose file does not exist.
|
|
15
|
+
*
|
|
16
|
+
* Returns null if all checkpoints exist (task is fully done).
|
|
17
|
+
*/
|
|
18
|
+
export declare function resumeFromCheckpoint(taskDir: string): CheckpointName | null;
|
|
19
|
+
/**
|
|
20
|
+
* Persists checkpoint data atomically.
|
|
21
|
+
* Uses writeAtomic (write to temp, then rename) for crash safety.
|
|
22
|
+
*/
|
|
23
|
+
export declare function writeCheckpoint(taskDir: string, checkpoint: CheckpointName, data: unknown): void;
|
|
24
|
+
/**
|
|
25
|
+
* Removes checkpoint files from `fromCheckpoint` onwards (inclusive).
|
|
26
|
+
* Used before a retry to prevent stale checkpoint data from a previous
|
|
27
|
+
* attempt being mistaken for current-attempt data on crash recovery.
|
|
28
|
+
*/
|
|
29
|
+
export declare function clearCheckpointsFrom(taskDir: string, fromCheckpoint: CheckpointName): void;
|
|
30
|
+
/**
|
|
31
|
+
* Reads checkpoint data. Returns null if the checkpoint file does not exist.
|
|
32
|
+
*/
|
|
33
|
+
export declare function readCheckpoint<T>(taskDir: string, checkpoint: CheckpointName): T | null;
|
|
34
|
+
export declare function readProcessedNotesCheckpoint(stwRoot: string | undefined, runId: string | undefined, mrIid: number): ProcessedNotesCheckpoint | null;
|
|
35
|
+
export declare function writeProcessedNotesCheckpoint(stwRoot: string | undefined, runId: string | undefined, checkpoint: ProcessedNotesCheckpoint): void;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { existsSync, unlinkSync } from 'node:fs';
|
|
3
|
+
import { writeAtomic } from './storage.js';
|
|
4
|
+
import { readJson } from './storage.js';
|
|
5
|
+
import { fileExists, pollerCheckpointPath, writeJson } from './storage.js';
|
|
6
|
+
export const CHECKPOINT_ORDER = [
|
|
7
|
+
'pre_worker',
|
|
8
|
+
'post_worker',
|
|
9
|
+
'post_write',
|
|
10
|
+
'post_validate',
|
|
11
|
+
'post_review',
|
|
12
|
+
'complete',
|
|
13
|
+
];
|
|
14
|
+
function checkpointPath(taskDir, checkpoint) {
|
|
15
|
+
return join(taskDir, 'checkpoints', `${checkpoint}.json`);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Reads the checkpoint directory and returns the name of the next
|
|
19
|
+
* incomplete checkpoint. Walks the ordered list and returns the first
|
|
20
|
+
* checkpoint whose file does not exist.
|
|
21
|
+
*
|
|
22
|
+
* Returns null if all checkpoints exist (task is fully done).
|
|
23
|
+
*/
|
|
24
|
+
export function resumeFromCheckpoint(taskDir) {
|
|
25
|
+
for (const cp of CHECKPOINT_ORDER) {
|
|
26
|
+
if (!existsSync(checkpointPath(taskDir, cp))) {
|
|
27
|
+
return cp;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// All checkpoints exist — task is fully done
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Persists checkpoint data atomically.
|
|
35
|
+
* Uses writeAtomic (write to temp, then rename) for crash safety.
|
|
36
|
+
*/
|
|
37
|
+
export function writeCheckpoint(taskDir, checkpoint, data) {
|
|
38
|
+
const filePath = checkpointPath(taskDir, checkpoint);
|
|
39
|
+
writeAtomic(filePath, JSON.stringify(data, null, 2) + '\n');
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Removes checkpoint files from `fromCheckpoint` onwards (inclusive).
|
|
43
|
+
* Used before a retry to prevent stale checkpoint data from a previous
|
|
44
|
+
* attempt being mistaken for current-attempt data on crash recovery.
|
|
45
|
+
*/
|
|
46
|
+
export function clearCheckpointsFrom(taskDir, fromCheckpoint) {
|
|
47
|
+
const startIdx = CHECKPOINT_ORDER.indexOf(fromCheckpoint);
|
|
48
|
+
for (let i = startIdx; i < CHECKPOINT_ORDER.length; i++) {
|
|
49
|
+
const filePath = checkpointPath(taskDir, CHECKPOINT_ORDER[i]);
|
|
50
|
+
if (existsSync(filePath)) {
|
|
51
|
+
unlinkSync(filePath);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Reads checkpoint data. Returns null if the checkpoint file does not exist.
|
|
57
|
+
*/
|
|
58
|
+
export function readCheckpoint(taskDir, checkpoint) {
|
|
59
|
+
const filePath = checkpointPath(taskDir, checkpoint);
|
|
60
|
+
if (!existsSync(filePath)) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
return readJson(filePath);
|
|
64
|
+
}
|
|
65
|
+
export function readProcessedNotesCheckpoint(stwRoot, runId, mrIid) {
|
|
66
|
+
if (!stwRoot || !runId)
|
|
67
|
+
return null;
|
|
68
|
+
const filePath = pollerCheckpointPath(stwRoot, runId, mrIid);
|
|
69
|
+
if (!fileExists(filePath))
|
|
70
|
+
return null;
|
|
71
|
+
return readJson(filePath);
|
|
72
|
+
}
|
|
73
|
+
export function writeProcessedNotesCheckpoint(stwRoot, runId, checkpoint) {
|
|
74
|
+
if (!stwRoot || !runId)
|
|
75
|
+
return;
|
|
76
|
+
writeJson(pollerCheckpointPath(stwRoot, runId, checkpoint.mr_iid), checkpoint);
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=checkpoint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpoint.js","sourceRoot":"","sources":["../src/checkpoint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type CircuitState = 'closed' | 'open' | 'half-open';
|
|
2
|
+
export interface CircuitBreakerOptions {
|
|
3
|
+
failureThreshold?: number;
|
|
4
|
+
cooldownMs?: number;
|
|
5
|
+
}
|
|
6
|
+
export declare class CircuitBreaker {
|
|
7
|
+
private readonly circuits;
|
|
8
|
+
private readonly failureThreshold;
|
|
9
|
+
private readonly cooldownMs;
|
|
10
|
+
constructor(options?: CircuitBreakerOptions);
|
|
11
|
+
isAvailable(provider: string, tier?: string): boolean;
|
|
12
|
+
recordSuccess(provider: string, tier?: string): void;
|
|
13
|
+
recordFailure(provider: string, tier?: string): void;
|
|
14
|
+
getState(provider: string, tier?: string): CircuitState;
|
|
15
|
+
reset(provider: string, tier?: string): void;
|
|
16
|
+
private getKey;
|
|
17
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export class CircuitBreaker {
|
|
2
|
+
circuits = new Map();
|
|
3
|
+
failureThreshold;
|
|
4
|
+
cooldownMs;
|
|
5
|
+
constructor(options) {
|
|
6
|
+
this.failureThreshold = options?.failureThreshold ?? 3;
|
|
7
|
+
this.cooldownMs = options?.cooldownMs ?? 60_000;
|
|
8
|
+
}
|
|
9
|
+
isAvailable(provider, tier) {
|
|
10
|
+
const circuit = this.circuits.get(this.getKey(provider, tier));
|
|
11
|
+
if (!circuit || circuit.state === 'closed')
|
|
12
|
+
return true;
|
|
13
|
+
if (circuit.state === 'open') {
|
|
14
|
+
const elapsed = Date.now() - circuit.lastFailureAt;
|
|
15
|
+
if (elapsed >= this.cooldownMs) {
|
|
16
|
+
circuit.state = 'half-open';
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
recordSuccess(provider, tier) {
|
|
24
|
+
const circuit = this.circuits.get(this.getKey(provider, tier));
|
|
25
|
+
if (!circuit)
|
|
26
|
+
return;
|
|
27
|
+
circuit.state = 'closed';
|
|
28
|
+
circuit.consecutiveFailures = 0;
|
|
29
|
+
}
|
|
30
|
+
recordFailure(provider, tier) {
|
|
31
|
+
const key = this.getKey(provider, tier);
|
|
32
|
+
let circuit = this.circuits.get(key);
|
|
33
|
+
if (!circuit) {
|
|
34
|
+
circuit = { state: 'closed', consecutiveFailures: 0, lastFailureAt: 0 };
|
|
35
|
+
this.circuits.set(key, circuit);
|
|
36
|
+
}
|
|
37
|
+
circuit.consecutiveFailures++;
|
|
38
|
+
circuit.lastFailureAt = Date.now();
|
|
39
|
+
if (circuit.state === 'half-open') {
|
|
40
|
+
circuit.state = 'open';
|
|
41
|
+
}
|
|
42
|
+
else if (circuit.consecutiveFailures >= this.failureThreshold) {
|
|
43
|
+
circuit.state = 'open';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
getState(provider, tier) {
|
|
47
|
+
const circuit = this.circuits.get(this.getKey(provider, tier));
|
|
48
|
+
if (!circuit)
|
|
49
|
+
return 'closed';
|
|
50
|
+
if (circuit.state === 'open') {
|
|
51
|
+
const elapsed = Date.now() - circuit.lastFailureAt;
|
|
52
|
+
if (elapsed >= this.cooldownMs) {
|
|
53
|
+
circuit.state = 'half-open';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return circuit.state;
|
|
57
|
+
}
|
|
58
|
+
reset(provider, tier) {
|
|
59
|
+
this.circuits.delete(this.getKey(provider, tier));
|
|
60
|
+
}
|
|
61
|
+
getKey(provider, tier) {
|
|
62
|
+
return tier ? `${provider}:${tier}` : provider;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=circuit-breaker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit-breaker.js","sourceRoot":"","sources":["../src/circuit-breaker.ts"],"names":[],"mappings":"AAaA,MAAM,OAAO,cAAc;IACR,QAAQ,GAAG,IAAI,GAAG,EAA2B,CAAC;IAC9C,gBAAgB,CAAS;IACzB,UAAU,CAAS;IAEpC,YAAY,OAA+B;QACzC,IAAI,CAAC,gBAAgB,GAAG,OAAO,EAAE,gBAAgB,IAAI,CAAC,CAAC;QACvD,IAAI,CAAC,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,MAAM,CAAC;IAClD,CAAC;IAED,WAAW,CAAC,QAAgB,EAAE,IAAa;QACzC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;QAC/D,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,KAAK,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAExD,IAAI,OAAO,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC;YACnD,IAAI,OAAO,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;gBAC/B,OAAO,CAAC,KAAK,GAAG,WAAW,CAAC;gBAC5B,OAAO,IAAI,CAAC;YACd,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED,aAAa,CAAC,QAAgB,EAAE,IAAa;QAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;QAC/D,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,OAAO,CAAC,KAAK,GAAG,QAAQ,CAAC;QACzB,OAAO,CAAC,mBAAmB,GAAG,CAAC,CAAC;IAClC,CAAC;IAED,aAAa,CAAC,QAAgB,EAAE,IAAa;QAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACxC,IAAI,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,mBAAmB,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,CAAC;YACxE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAClC,CAAC;QAED,OAAO,CAAC,mBAAmB,EAAE,CAAC;QAC9B,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEnC,IAAI,OAAO,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;YAClC,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC;QACzB,CAAC;aAAM,IAAI,OAAO,CAAC,mBAAmB,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAChE,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC;QACzB,CAAC;IACH,CAAC;IAED,QAAQ,CAAC,QAAgB,EAAE,IAAa;QACtC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;QAC/D,IAAI,CAAC,OAAO;YAAE,OAAO,QAAQ,CAAC;QAE9B,IAAI,OAAO,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC;YACnD,IAAI,OAAO,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;gBAC/B,OAAO,CAAC,KAAK,GAAG,WAAW,CAAC;YAC9B,CAAC;QACH,CAAC;QAED,OAAO,OAAO,CAAC,KAAK,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,QAAgB,EAAE,IAAa;QACnC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;IACpD,CAAC;IAEO,MAAM,CAAC,QAAgB,EAAE,IAAa;QAC5C,OAAO,IAAI,CAAC,CAAC,CAAC,GAAG,QAAQ,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;IACjD,CAAC;CACF"}
|
package/dist/claim.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export interface ClaimResult {
|
|
2
|
+
claimed: boolean;
|
|
3
|
+
holder?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function claimTask(stwRoot: string, runId: string, taskId: string, agentId: string): Promise<ClaimResult>;
|
|
6
|
+
export declare function releaseClaim(stwRoot: string, runId: string, taskId: string): void;
|
package/dist/claim.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { mkdirSync, readFileSync, writeFileSync, unlinkSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { taskDir } from './storage.js';
|
|
5
|
+
const LOCK_READY = '__stw_claim_locked__';
|
|
6
|
+
const LOCK_COMMAND = `printf '%s\\n' '${LOCK_READY}'; cat >/dev/null`;
|
|
7
|
+
export async function claimTask(stwRoot, runId, taskId, agentId) {
|
|
8
|
+
const dir = taskDir(stwRoot, runId, taskId);
|
|
9
|
+
const lockPath = join(dir, 'claim.lock');
|
|
10
|
+
const statusPath = join(dir, 'status.json');
|
|
11
|
+
mkdirSync(dir, { recursive: true });
|
|
12
|
+
return await withTaskClaimLock(lockPath, () => {
|
|
13
|
+
const existing = readClaimStatus(statusPath);
|
|
14
|
+
if (existing && (existing.status === 'in_progress' || existing.status === 'complete')) {
|
|
15
|
+
return {
|
|
16
|
+
claimed: false,
|
|
17
|
+
holder: existing.agent_id,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const nextStatus = {
|
|
21
|
+
schema_version: 1,
|
|
22
|
+
task_id: taskId,
|
|
23
|
+
agent_id: agentId,
|
|
24
|
+
status: 'in_progress',
|
|
25
|
+
started_at: new Date().toISOString(),
|
|
26
|
+
notes: '',
|
|
27
|
+
};
|
|
28
|
+
writeFileSync(statusPath, JSON.stringify(nextStatus, null, 2) + '\n', 'utf-8');
|
|
29
|
+
return { claimed: true };
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
export function releaseClaim(stwRoot, runId, taskId) {
|
|
33
|
+
const dir = taskDir(stwRoot, runId, taskId);
|
|
34
|
+
const lockPath = join(dir, 'claim.lock');
|
|
35
|
+
try {
|
|
36
|
+
unlinkSync(lockPath);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Lock file may not exist, ignore
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function readClaimStatus(statusPath) {
|
|
43
|
+
try {
|
|
44
|
+
const parsed = JSON.parse(readFileSync(statusPath, 'utf-8'));
|
|
45
|
+
if (typeof parsed?.status !== 'string') {
|
|
46
|
+
throw new Error('Invalid status file: missing status field');
|
|
47
|
+
}
|
|
48
|
+
return parsed;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (isMissingFileError(error)) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async function withTaskClaimLock(lockPath, callback) {
|
|
58
|
+
const child = spawn('flock', ['-x', '-w', '10', lockPath, 'sh', '-c', LOCK_COMMAND], {
|
|
59
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
60
|
+
});
|
|
61
|
+
await waitForLock(child);
|
|
62
|
+
try {
|
|
63
|
+
return await callback();
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
const released = waitForExit(child);
|
|
67
|
+
try {
|
|
68
|
+
child.stdin.end();
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
/* pipe already closed */
|
|
72
|
+
}
|
|
73
|
+
await released;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function waitForLock(child) {
|
|
77
|
+
return new Promise((resolve, reject) => {
|
|
78
|
+
let stdout = '';
|
|
79
|
+
let stderr = '';
|
|
80
|
+
const cleanup = () => {
|
|
81
|
+
child.stdout.off('data', onStdout);
|
|
82
|
+
child.stderr.off('data', onStderr);
|
|
83
|
+
child.off('error', onError);
|
|
84
|
+
child.off('exit', onExit);
|
|
85
|
+
};
|
|
86
|
+
const onStdout = (chunk) => {
|
|
87
|
+
stdout += chunk.toString();
|
|
88
|
+
if (stdout.includes(LOCK_READY)) {
|
|
89
|
+
cleanup();
|
|
90
|
+
resolve();
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
const onStderr = (chunk) => {
|
|
94
|
+
stderr += chunk.toString();
|
|
95
|
+
};
|
|
96
|
+
const onError = (error) => {
|
|
97
|
+
cleanup();
|
|
98
|
+
reject(error);
|
|
99
|
+
};
|
|
100
|
+
const onExit = (code, signal) => {
|
|
101
|
+
cleanup();
|
|
102
|
+
reject(new Error(`flock exited before lock was acquired (code: ${code ?? 'null'}, signal: ${signal ?? 'null'}, stderr: ${stderr.trim()})`));
|
|
103
|
+
};
|
|
104
|
+
child.stdout.on('data', onStdout);
|
|
105
|
+
child.stderr.on('data', onStderr);
|
|
106
|
+
child.on('error', onError);
|
|
107
|
+
child.on('exit', onExit);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function waitForExit(child) {
|
|
111
|
+
return new Promise((resolve, reject) => {
|
|
112
|
+
const onError = (error) => {
|
|
113
|
+
cleanup();
|
|
114
|
+
reject(error);
|
|
115
|
+
};
|
|
116
|
+
const onExit = (code, signal) => {
|
|
117
|
+
cleanup();
|
|
118
|
+
if (code === 0 && signal === null) {
|
|
119
|
+
resolve();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
reject(new Error(`flock exited unexpectedly while releasing lock (code: ${code ?? 'null'}, signal: ${signal ?? 'null'})`));
|
|
123
|
+
};
|
|
124
|
+
const cleanup = () => {
|
|
125
|
+
child.off('error', onError);
|
|
126
|
+
child.off('exit', onExit);
|
|
127
|
+
};
|
|
128
|
+
child.on('error', onError);
|
|
129
|
+
child.on('exit', onExit);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
function isMissingFileError(error) {
|
|
133
|
+
return typeof error === 'object' && error !== null && 'code' in error && error.code === 'ENOENT';
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=claim.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim.js","sourceRoot":"","sources":["../src/claim.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { StwConfig, SeedFileEntry } from './types.js';
|
|
2
|
+
export interface ClarityCheckOptions {
|
|
3
|
+
idea: string;
|
|
4
|
+
seedFiles?: SeedFileEntry[];
|
|
5
|
+
config: StwConfig;
|
|
6
|
+
}
|
|
7
|
+
export interface ClarityResult {
|
|
8
|
+
clear: boolean;
|
|
9
|
+
feedback?: string;
|
|
10
|
+
suggestedIdea?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function checkIdeaClarity(options: ClarityCheckOptions): Promise<ClarityResult>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { callProvider } from './provider.js';
|
|
2
|
+
import { routeTask } from './router.js';
|
|
3
|
+
export async function checkIdeaClarity(options) {
|
|
4
|
+
const { idea, seedFiles, config } = options;
|
|
5
|
+
const seedBlock = seedFiles && seedFiles.length > 0
|
|
6
|
+
? `\n\nSeed files provided:\n${seedFiles.map((f) => `- ${f.relativePath} (${f.sizeHuman}${f.isBinary ? ', binary' : ''})`).join('\n')}`
|
|
7
|
+
: '';
|
|
8
|
+
const prompt = `You evaluate whether a project idea is specific enough to generate a technical spec.
|
|
9
|
+
Be PERMISSIVE — lean toward "clear": true when borderline. Only reject when the idea is genuinely too vague to produce anything useful.
|
|
10
|
+
|
|
11
|
+
Evaluate:
|
|
12
|
+
- Does the idea specify what output is wanted (API, CLI, dashboard, report, library)?
|
|
13
|
+
- Does the idea specify what to do with the data/inputs (transform, analyze, visualize, serve)?
|
|
14
|
+
- Is there at least one actionable verb?
|
|
15
|
+
- If seed files are listed, does the idea reference or imply how to use them?
|
|
16
|
+
|
|
17
|
+
Respond with JSON only: { "clear": true/false, "feedback": "...", "suggested_idea": "..." }
|
|
18
|
+
|
|
19
|
+
Example clear idea: "Build a Python CLI that reads expenses.xlsx, groups spending by category, outputs a summary CSV"
|
|
20
|
+
→ { "clear": true }
|
|
21
|
+
|
|
22
|
+
Example vague idea: "do something with this"
|
|
23
|
+
→ { "clear": false, "feedback": "No output format or processing action specified.", "suggested_idea": "Build a CLI that reads data.csv and outputs a summary report grouped by the first column" }
|
|
24
|
+
|
|
25
|
+
The idea to evaluate:
|
|
26
|
+
"${idea}"${seedBlock}`;
|
|
27
|
+
try {
|
|
28
|
+
const route = routeTask({
|
|
29
|
+
risk: 'low',
|
|
30
|
+
task_type: 'planning',
|
|
31
|
+
model_tier: 'cheap',
|
|
32
|
+
execution_mode: 'conservative',
|
|
33
|
+
planner_eligible: false,
|
|
34
|
+
}, config);
|
|
35
|
+
const providerConfig = config.providers[route.provider];
|
|
36
|
+
if (!providerConfig) {
|
|
37
|
+
// No cheap provider configured — fail-open
|
|
38
|
+
return { clear: true };
|
|
39
|
+
}
|
|
40
|
+
const response = await callProvider({
|
|
41
|
+
model: route.model,
|
|
42
|
+
messages: [{ role: 'user', content: prompt }],
|
|
43
|
+
max_tokens: 512,
|
|
44
|
+
temperature: 0.1,
|
|
45
|
+
timeout_ms: 30_000,
|
|
46
|
+
response_format: 'json',
|
|
47
|
+
}, providerConfig, config.defaults, 'cheap', config);
|
|
48
|
+
return parseResponse(response.content);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Fail-open: infrastructure errors should not block the user
|
|
52
|
+
console.warn('[clarity] Clarity check failed, proceeding anyway');
|
|
53
|
+
return { clear: true };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function stripCodeFences(content) {
|
|
57
|
+
const trimmed = content.trim();
|
|
58
|
+
if (trimmed.startsWith('```')) {
|
|
59
|
+
const firstNewline = trimmed.indexOf('\n');
|
|
60
|
+
const lastFence = trimmed.lastIndexOf('```');
|
|
61
|
+
if (lastFence > firstNewline) {
|
|
62
|
+
return trimmed.slice(firstNewline + 1, lastFence).trim();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return trimmed;
|
|
66
|
+
}
|
|
67
|
+
function parseResponse(content) {
|
|
68
|
+
try {
|
|
69
|
+
const cleaned = stripCodeFences(content);
|
|
70
|
+
const parsed = JSON.parse(cleaned);
|
|
71
|
+
return {
|
|
72
|
+
clear: parsed.clear !== false, // default to clear if field missing
|
|
73
|
+
feedback: parsed.feedback,
|
|
74
|
+
suggestedIdea: parsed.suggested_idea,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Malformed response — fail-open
|
|
79
|
+
console.warn('[clarity] Could not parse clarity response, proceeding anyway');
|
|
80
|
+
return { clear: true };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=clarity-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clarity-gate.js","sourceRoot":"","sources":["../src/clarity-gate.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED