@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
package/dist/provider.js
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { CircuitBreaker } from './circuit-breaker.js';
|
|
2
|
+
import { ProviderError } from './types.js';
|
|
3
|
+
const TRANSIENT_STATUS_CODES = new Set([429, 500, 502, 503, 504, 529]);
|
|
4
|
+
const NON_TRANSIENT_STATUS_CODES = new Set([400, 401, 403, 422]);
|
|
5
|
+
const MODEL_DATE_SUFFIX_PATTERN = /-\d{4,8}[a-zA-Z]*$/;
|
|
6
|
+
const DEFAULT_TIER_TIMEOUTS = {
|
|
7
|
+
cheap: 120000,
|
|
8
|
+
medium: 300000,
|
|
9
|
+
strong: 600000,
|
|
10
|
+
reader: 300000,
|
|
11
|
+
};
|
|
12
|
+
const providerCircuitBreaker = new CircuitBreaker();
|
|
13
|
+
function getTierTimeoutMs(tier, config) {
|
|
14
|
+
const timeoutConfig = config.timeout?.api_timeout_seconds;
|
|
15
|
+
const tierTimeoutOverride = config.defaults.api_timeout_seconds;
|
|
16
|
+
if (timeoutConfig && typeof timeoutConfig === 'object') {
|
|
17
|
+
const tierMs = timeoutConfig[tier];
|
|
18
|
+
if (typeof tierMs === 'number' && tierMs > 0) {
|
|
19
|
+
return tierMs;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const defaultMs = DEFAULT_TIER_TIMEOUTS[tier];
|
|
23
|
+
if (typeof defaultMs === 'number') {
|
|
24
|
+
return defaultMs;
|
|
25
|
+
}
|
|
26
|
+
return tierTimeoutOverride ? tierTimeoutOverride * 1000 : defaultMs;
|
|
27
|
+
}
|
|
28
|
+
export { getTierTimeoutMs };
|
|
29
|
+
function getApiKey(envVar) {
|
|
30
|
+
const key = process.env[envVar];
|
|
31
|
+
if (!key) {
|
|
32
|
+
throw new ProviderError(`API key environment variable "${envVar}" is not set`, false);
|
|
33
|
+
}
|
|
34
|
+
return key;
|
|
35
|
+
}
|
|
36
|
+
function buildRequestBody(request) {
|
|
37
|
+
const messages = request.system_prompt
|
|
38
|
+
? [{ role: 'system', content: request.system_prompt }, ...request.messages]
|
|
39
|
+
: request.messages;
|
|
40
|
+
const body = {
|
|
41
|
+
model: request.model,
|
|
42
|
+
messages,
|
|
43
|
+
max_tokens: request.max_tokens,
|
|
44
|
+
temperature: request.temperature,
|
|
45
|
+
};
|
|
46
|
+
if (request.response_format === 'json') {
|
|
47
|
+
body.response_format = { type: 'json_object' };
|
|
48
|
+
}
|
|
49
|
+
return body;
|
|
50
|
+
}
|
|
51
|
+
function logTimeoutWarning(provider, model, timeoutMs) {
|
|
52
|
+
const warning = {
|
|
53
|
+
event: 'provider_timeout',
|
|
54
|
+
provider,
|
|
55
|
+
model,
|
|
56
|
+
timeout_ms: timeoutMs,
|
|
57
|
+
};
|
|
58
|
+
console.warn(JSON.stringify(warning));
|
|
59
|
+
}
|
|
60
|
+
function createTimeoutError() {
|
|
61
|
+
return new ProviderError('Provider timeout exceeded', true, undefined, 'timeout');
|
|
62
|
+
}
|
|
63
|
+
function isAbortError(err) {
|
|
64
|
+
return err instanceof Error && err.name === 'AbortError';
|
|
65
|
+
}
|
|
66
|
+
async function doFetch(url, apiKey, body, signal) {
|
|
67
|
+
try {
|
|
68
|
+
return await fetch(url, {
|
|
69
|
+
method: 'POST',
|
|
70
|
+
headers: {
|
|
71
|
+
'Content-Type': 'application/json',
|
|
72
|
+
Authorization: `Bearer ${apiKey}`,
|
|
73
|
+
},
|
|
74
|
+
body: JSON.stringify(body),
|
|
75
|
+
signal,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
if (isAbortError(err)) {
|
|
80
|
+
throw createTimeoutError();
|
|
81
|
+
}
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async function readJsonWithTimeout(response, signal) {
|
|
86
|
+
try {
|
|
87
|
+
return (await Promise.race([
|
|
88
|
+
response.json(),
|
|
89
|
+
new Promise((_, reject) => {
|
|
90
|
+
signal.addEventListener('abort', () => reject(createTimeoutError()), { once: true });
|
|
91
|
+
}),
|
|
92
|
+
]));
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
if (err instanceof ProviderError) {
|
|
96
|
+
throw err;
|
|
97
|
+
}
|
|
98
|
+
if (isAbortError(err)) {
|
|
99
|
+
throw createTimeoutError();
|
|
100
|
+
}
|
|
101
|
+
throw err;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function sleep(ms) {
|
|
105
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
106
|
+
}
|
|
107
|
+
function isTransientErrorStatus(statusCode) {
|
|
108
|
+
if (NON_TRANSIENT_STATUS_CODES.has(statusCode))
|
|
109
|
+
return false;
|
|
110
|
+
return TRANSIENT_STATUS_CODES.has(statusCode);
|
|
111
|
+
}
|
|
112
|
+
function isTransientError(error) {
|
|
113
|
+
if (error instanceof ProviderError) {
|
|
114
|
+
if (error.statusCode !== undefined) {
|
|
115
|
+
return isTransientErrorStatus(error.statusCode);
|
|
116
|
+
}
|
|
117
|
+
return error.retryable;
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
function preprocessModelName(name) {
|
|
122
|
+
return name.replace(MODEL_DATE_SUFFIX_PATTERN, '');
|
|
123
|
+
}
|
|
124
|
+
function buildModelNormalizationTable(configuredModels) {
|
|
125
|
+
return configuredModels.map((canonical) => {
|
|
126
|
+
const preprocessedCanonical = preprocessModelName(canonical);
|
|
127
|
+
const aliases = new Set([canonical, preprocessedCanonical]);
|
|
128
|
+
const canonicalParts = preprocessedCanonical.split('/');
|
|
129
|
+
const modelName = canonicalParts[1];
|
|
130
|
+
if (modelName !== undefined && canonical.startsWith('anthropic/')) {
|
|
131
|
+
aliases.add(modelName);
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
canonical,
|
|
135
|
+
aliases: [...aliases],
|
|
136
|
+
};
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
export function normalizeModelName(rawName, configuredModels) {
|
|
140
|
+
const preprocessedRawName = preprocessModelName(rawName);
|
|
141
|
+
const normalizationTable = buildModelNormalizationTable(configuredModels);
|
|
142
|
+
const matchingConfiguredModels = normalizationTable
|
|
143
|
+
.filter((entry) => entry.aliases.some((alias) => alias === rawName || preprocessModelName(alias) === preprocessedRawName))
|
|
144
|
+
.map((entry) => entry.canonical);
|
|
145
|
+
if (matchingConfiguredModels.length > 1) {
|
|
146
|
+
// Multiple matches — deduplicate (same canonical can appear via pricing + models registry)
|
|
147
|
+
const unique = [...new Set(matchingConfiguredModels)];
|
|
148
|
+
if (unique.length === 1) {
|
|
149
|
+
return unique[0];
|
|
150
|
+
}
|
|
151
|
+
// True collision — pick first match but warn
|
|
152
|
+
console.warn(`Model name collision detected for "${rawName}": multiple configured models match preprocessed name "${preprocessedRawName}"`);
|
|
153
|
+
return unique[0] ?? rawName;
|
|
154
|
+
}
|
|
155
|
+
if (matchingConfiguredModels.length === 1) {
|
|
156
|
+
const [matchedModel] = matchingConfiguredModels;
|
|
157
|
+
if (matchedModel !== undefined) {
|
|
158
|
+
return matchedModel;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
console.warn(`Model name not found in configured models: "${rawName}"`);
|
|
162
|
+
return rawName;
|
|
163
|
+
}
|
|
164
|
+
export function isProviderCircuitAvailable(provider, tier) {
|
|
165
|
+
return providerCircuitBreaker.isAvailable(provider, tier);
|
|
166
|
+
}
|
|
167
|
+
export function recordProviderCircuitSuccess(provider, tier) {
|
|
168
|
+
providerCircuitBreaker.recordSuccess(provider, tier);
|
|
169
|
+
}
|
|
170
|
+
export function recordProviderCircuitFailure(provider, tier) {
|
|
171
|
+
providerCircuitBreaker.recordFailure(provider, tier);
|
|
172
|
+
}
|
|
173
|
+
export function createCircuitsOpenPause(run) {
|
|
174
|
+
return {
|
|
175
|
+
...run,
|
|
176
|
+
status: 'paused',
|
|
177
|
+
pause_reason: 'circuits_open',
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
export async function callProvider(request, config, defaults, tier = 'strong', fullConfig) {
|
|
181
|
+
if (!providerCircuitBreaker.isAvailable(config.base_url, tier)) {
|
|
182
|
+
throw new ProviderError('All circuits open', true);
|
|
183
|
+
}
|
|
184
|
+
const apiKey = getApiKey(config.api_key_env);
|
|
185
|
+
const url = `${config.base_url.replace(/\/+$/, '')}/chat/completions`;
|
|
186
|
+
const body = buildRequestBody(request);
|
|
187
|
+
const timeoutMs = request.timeout_ms || (fullConfig ? getTierTimeoutMs(tier, fullConfig) : defaults.api_timeout_seconds * 1000);
|
|
188
|
+
const maxRetries = defaults.max_retries;
|
|
189
|
+
let lastError;
|
|
190
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
191
|
+
if (attempt > 0) {
|
|
192
|
+
const backoffMs = Math.min(1000 * 2 ** (attempt - 1), 30000);
|
|
193
|
+
await sleep(backoffMs);
|
|
194
|
+
}
|
|
195
|
+
const startTime = Date.now();
|
|
196
|
+
const controller = new AbortController();
|
|
197
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
198
|
+
const cancelTimeout = () => clearTimeout(timer);
|
|
199
|
+
let response;
|
|
200
|
+
try {
|
|
201
|
+
response = await doFetch(url, apiKey, body, controller.signal);
|
|
202
|
+
}
|
|
203
|
+
catch (err) {
|
|
204
|
+
cancelTimeout();
|
|
205
|
+
if (err instanceof ProviderError) {
|
|
206
|
+
lastError = err;
|
|
207
|
+
if (err.code === 'timeout') {
|
|
208
|
+
logTimeoutWarning(config.base_url, request.model, timeoutMs);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
213
|
+
lastError = new ProviderError(`Network error: ${message}`, true);
|
|
214
|
+
}
|
|
215
|
+
if (isTransientError(lastError)) {
|
|
216
|
+
providerCircuitBreaker.recordFailure(config.base_url, tier);
|
|
217
|
+
}
|
|
218
|
+
if (attempt < maxRetries && isTransientError(lastError))
|
|
219
|
+
continue;
|
|
220
|
+
throw lastError;
|
|
221
|
+
}
|
|
222
|
+
if (!response.ok) {
|
|
223
|
+
const retryable = isTransientErrorStatus(response.status);
|
|
224
|
+
const responseText = await response.text().catch(() => '');
|
|
225
|
+
cancelTimeout();
|
|
226
|
+
lastError = new ProviderError(`HTTP ${response.status}: ${responseText}`, retryable, response.status);
|
|
227
|
+
if (isTransientError(lastError)) {
|
|
228
|
+
providerCircuitBreaker.recordFailure(config.base_url, tier);
|
|
229
|
+
}
|
|
230
|
+
if (!retryable)
|
|
231
|
+
throw lastError;
|
|
232
|
+
if (attempt < maxRetries)
|
|
233
|
+
continue;
|
|
234
|
+
throw lastError;
|
|
235
|
+
}
|
|
236
|
+
let data;
|
|
237
|
+
try {
|
|
238
|
+
data = await readJsonWithTimeout(response, controller.signal);
|
|
239
|
+
}
|
|
240
|
+
catch (err) {
|
|
241
|
+
cancelTimeout();
|
|
242
|
+
if (err instanceof ProviderError) {
|
|
243
|
+
lastError = err;
|
|
244
|
+
if (err.code === 'timeout') {
|
|
245
|
+
logTimeoutWarning(config.base_url, request.model, timeoutMs);
|
|
246
|
+
}
|
|
247
|
+
if (isTransientError(lastError)) {
|
|
248
|
+
providerCircuitBreaker.recordFailure(config.base_url, tier);
|
|
249
|
+
}
|
|
250
|
+
if (attempt < maxRetries && isTransientError(lastError))
|
|
251
|
+
continue;
|
|
252
|
+
throw lastError;
|
|
253
|
+
}
|
|
254
|
+
throw err;
|
|
255
|
+
}
|
|
256
|
+
cancelTimeout();
|
|
257
|
+
const choice = data.choices?.[0];
|
|
258
|
+
if (!choice) {
|
|
259
|
+
throw new ProviderError('No choices in response', false);
|
|
260
|
+
}
|
|
261
|
+
providerCircuitBreaker.recordSuccess(config.base_url, tier);
|
|
262
|
+
const configuredModels = Object.values(config.models).filter((model) => typeof model === 'string');
|
|
263
|
+
const normalizedModel = normalizeModelName(data.model, configuredModels);
|
|
264
|
+
return {
|
|
265
|
+
content: choice.message.content,
|
|
266
|
+
model: normalizedModel,
|
|
267
|
+
provider: config.base_url,
|
|
268
|
+
usage: {
|
|
269
|
+
input_tokens: data.usage.prompt_tokens,
|
|
270
|
+
output_tokens: data.usage.completion_tokens,
|
|
271
|
+
},
|
|
272
|
+
latency_ms: Date.now() - startTime,
|
|
273
|
+
finish_reason: choice.finish_reason,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
throw lastError ?? new ProviderError('Unexpected: no attempts made', false);
|
|
277
|
+
}
|
|
278
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Task, TaskState, StwConfig } from './types.js';
|
|
2
|
+
import type { Logger } from './logger.js';
|
|
3
|
+
export interface WorkerQuestionResponse {
|
|
4
|
+
status: 'question';
|
|
5
|
+
question: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function readWorkerQuestionResponse(taskDir: string): WorkerQuestionResponse | null;
|
|
8
|
+
export declare function handleWorkerQuestion(input: {
|
|
9
|
+
questionResponse: WorkerQuestionResponse;
|
|
10
|
+
task: Task;
|
|
11
|
+
config: StwConfig;
|
|
12
|
+
taskDir: string;
|
|
13
|
+
stwRoot?: string;
|
|
14
|
+
runId?: string;
|
|
15
|
+
log?: Logger;
|
|
16
|
+
tryTransition: (stwRoot: string, runId: string, taskId: string, state: TaskState, updatedBy: string) => void;
|
|
17
|
+
}): 'auto_answered' | 'waiting_for_human';
|
|
18
|
+
export declare function loadPersistedQuestionAnswers(taskDir: string, taskId: string): Record<number, string>;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { existsSync, readdirSync } from 'node:fs';
|
|
3
|
+
import { writeRunState } from './status.js';
|
|
4
|
+
import { readText, writeJson } from './storage.js';
|
|
5
|
+
import { triageQuestion } from './question-triage.js';
|
|
6
|
+
export function readWorkerQuestionResponse(taskDir) {
|
|
7
|
+
const rawPath = join(taskDir, 'response_raw.txt');
|
|
8
|
+
if (!existsSync(rawPath)) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
const raw = readText(rawPath).trim();
|
|
13
|
+
if (raw.length === 0) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const parsed = JSON.parse(raw);
|
|
17
|
+
if (parsed.status === 'question' && typeof parsed.question === 'string' && parsed.question.trim().length > 0) {
|
|
18
|
+
return {
|
|
19
|
+
status: 'question',
|
|
20
|
+
question: parsed.question.trim(),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
export function handleWorkerQuestion(input) {
|
|
30
|
+
const { questionResponse, task, config, taskDir, stwRoot, runId, log, tryTransition } = input;
|
|
31
|
+
const questionNumber = getNextQuestionNumber(taskDir);
|
|
32
|
+
const questionArtifact = {
|
|
33
|
+
schema_version: 1,
|
|
34
|
+
task_id: task.id,
|
|
35
|
+
status: 'question',
|
|
36
|
+
question: questionResponse.question,
|
|
37
|
+
question_number: questionNumber,
|
|
38
|
+
created_at: new Date().toISOString(),
|
|
39
|
+
};
|
|
40
|
+
writeJson(join(taskDir, 'question.json'), questionArtifact);
|
|
41
|
+
if (stwRoot && runId) {
|
|
42
|
+
tryTransition(stwRoot, runId, task.id, 'question_pending', 'task-executor');
|
|
43
|
+
}
|
|
44
|
+
const maxAutoAnswers = config.defaults.max_auto_answers ?? 2;
|
|
45
|
+
const triage = triageQuestion({
|
|
46
|
+
question: questionResponse.question,
|
|
47
|
+
task,
|
|
48
|
+
config,
|
|
49
|
+
taskDir,
|
|
50
|
+
evidence: buildAutoAnswerEvidence(task, questionResponse.question),
|
|
51
|
+
confidence: 0.95,
|
|
52
|
+
questionNumber,
|
|
53
|
+
maxAutoAnswers,
|
|
54
|
+
});
|
|
55
|
+
if (triage.decision === 'auto_answer' && triage.answer) {
|
|
56
|
+
const answerArtifact = {
|
|
57
|
+
schema_version: 1,
|
|
58
|
+
task_id: task.id,
|
|
59
|
+
question_number: questionNumber,
|
|
60
|
+
answer: triage.answer,
|
|
61
|
+
answered_at: new Date().toISOString(),
|
|
62
|
+
answered_by: 'task-executor',
|
|
63
|
+
};
|
|
64
|
+
writeJson(join(taskDir, `answer-${questionNumber}.json`), answerArtifact);
|
|
65
|
+
if (stwRoot && runId) {
|
|
66
|
+
tryTransition(stwRoot, runId, task.id, 'working', 'task-executor');
|
|
67
|
+
}
|
|
68
|
+
log?.info('Auto-answered worker question', {
|
|
69
|
+
question_number: questionNumber,
|
|
70
|
+
question_thread_id: triage.question_thread_id,
|
|
71
|
+
});
|
|
72
|
+
return 'auto_answered';
|
|
73
|
+
}
|
|
74
|
+
if (stwRoot && runId) {
|
|
75
|
+
tryTransition(stwRoot, runId, task.id, 'waiting_for_human', 'task-executor');
|
|
76
|
+
try {
|
|
77
|
+
writeRunState(stwRoot, runId, 'paused', {
|
|
78
|
+
pause_reason: 'human_review',
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
console.warn(`[question] Failed to pause run for human review: ${err instanceof Error ? err.message : String(err)}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
log?.info('Worker question requires human review', {
|
|
86
|
+
question_number: questionNumber,
|
|
87
|
+
question_thread_id: triage.question_thread_id,
|
|
88
|
+
});
|
|
89
|
+
return 'waiting_for_human';
|
|
90
|
+
}
|
|
91
|
+
function getNextQuestionNumber(taskDir) {
|
|
92
|
+
const questionPath = join(taskDir, 'question.json');
|
|
93
|
+
if (existsSync(questionPath)) {
|
|
94
|
+
try {
|
|
95
|
+
const existing = JSON.parse(readText(questionPath));
|
|
96
|
+
if (typeof existing.question_number === 'number' && Number.isFinite(existing.question_number)) {
|
|
97
|
+
return existing.question_number + 1;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// Corrupt question.json — fall through to default
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
let maxAnswerNumber = 0;
|
|
105
|
+
try {
|
|
106
|
+
for (const entry of readdirSync(taskDir, { withFileTypes: true })) {
|
|
107
|
+
if (!entry.isFile()) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const match = entry.name.match(/^answer-(\d+)\.json$/);
|
|
111
|
+
if (!match) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const num = Number(match[1]);
|
|
115
|
+
if (Number.isFinite(num) && num > maxAnswerNumber) {
|
|
116
|
+
maxAnswerNumber = num;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch { }
|
|
121
|
+
return maxAnswerNumber + 1;
|
|
122
|
+
}
|
|
123
|
+
export function loadPersistedQuestionAnswers(taskDir, taskId) {
|
|
124
|
+
const answers = {};
|
|
125
|
+
try {
|
|
126
|
+
for (const entry of readdirSync(taskDir, { withFileTypes: true })) {
|
|
127
|
+
if (!entry.isFile()) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
const match = entry.name.match(/^answer-(\d+)\.json$/);
|
|
131
|
+
if (!match) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const filePath = join(taskDir, entry.name);
|
|
135
|
+
try {
|
|
136
|
+
const artifact = JSON.parse(readText(filePath));
|
|
137
|
+
if (artifact.task_id !== taskId) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (typeof artifact.question_number !== 'number' || typeof artifact.answer !== 'string') {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
answers[artifact.question_number] = artifact.answer;
|
|
144
|
+
}
|
|
145
|
+
catch { }
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch { }
|
|
149
|
+
return answers;
|
|
150
|
+
}
|
|
151
|
+
function buildAutoAnswerEvidence(task, question) {
|
|
152
|
+
return `Use the task description and declared scope as the source of truth. Task: ${task.description}. Question: ${question}`;
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=question-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"question-handler.js","sourceRoot":"","sources":["../src/question-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Task, StwConfig } from './types.js';
|
|
2
|
+
export type TriageDecision = 'auto_answer' | 'ask_human';
|
|
3
|
+
export interface TriageResult {
|
|
4
|
+
decision: TriageDecision;
|
|
5
|
+
answer?: string;
|
|
6
|
+
confidence?: number;
|
|
7
|
+
question_thread_id: string;
|
|
8
|
+
reason: string;
|
|
9
|
+
}
|
|
10
|
+
export interface QuestionTriageInput {
|
|
11
|
+
question: string;
|
|
12
|
+
task: Task;
|
|
13
|
+
config: StwConfig;
|
|
14
|
+
taskDir: string;
|
|
15
|
+
evidence?: string;
|
|
16
|
+
confidence?: number;
|
|
17
|
+
questionNumber?: number;
|
|
18
|
+
maxAutoAnswers?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Normalizes question text for deterministic hashing.
|
|
22
|
+
* Lowercases, collapses whitespace, strips punctuation at boundaries.
|
|
23
|
+
*/
|
|
24
|
+
export declare function normalizeQuestionText(text: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Computes a deterministic question_thread_id from normalized question text.
|
|
27
|
+
* Uses SHA-256, returns first 16 hex chars prefixed with 'qt_'.
|
|
28
|
+
*/
|
|
29
|
+
export declare function computeQuestionThreadId(question: string): string;
|
|
30
|
+
export declare function triageDecisionPath(taskDir: string): string;
|
|
31
|
+
export declare function triageQuestion(input: QuestionTriageInput): TriageResult;
|