@synergenius/flow-weaver-pack-weaver 0.8.3 → 0.9.3
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/dist/bot/ai-client.d.ts +22 -2
- package/dist/bot/ai-client.d.ts.map +1 -1
- package/dist/bot/ai-client.js +168 -20
- package/dist/bot/ai-client.js.map +1 -1
- package/dist/bot/assistant-core.d.ts +25 -0
- package/dist/bot/assistant-core.d.ts.map +1 -0
- package/dist/bot/assistant-core.js +265 -0
- package/dist/bot/assistant-core.js.map +1 -0
- package/dist/bot/assistant-tools.d.ts +9 -0
- package/dist/bot/assistant-tools.d.ts.map +1 -0
- package/dist/bot/assistant-tools.js +602 -0
- package/dist/bot/assistant-tools.js.map +1 -0
- package/dist/bot/audit-logger.d.ts.map +1 -1
- package/dist/bot/audit-logger.js +9 -5
- package/dist/bot/audit-logger.js.map +1 -1
- package/dist/bot/audit-store.d.ts.map +1 -1
- package/dist/bot/audit-store.js +3 -11
- package/dist/bot/audit-store.js.map +1 -1
- package/dist/bot/bot-manager.d.ts +49 -0
- package/dist/bot/bot-manager.d.ts.map +1 -0
- package/dist/bot/bot-manager.js +279 -0
- package/dist/bot/bot-manager.js.map +1 -0
- package/dist/bot/child-process-tracker.d.ts +6 -0
- package/dist/bot/child-process-tracker.d.ts.map +1 -0
- package/dist/bot/child-process-tracker.js +35 -0
- package/dist/bot/child-process-tracker.js.map +1 -0
- package/dist/bot/cli-provider.d.ts.map +1 -1
- package/dist/bot/cli-provider.js +13 -8
- package/dist/bot/cli-provider.js.map +1 -1
- package/dist/bot/conversation-store.d.ts +40 -0
- package/dist/bot/conversation-store.d.ts.map +1 -0
- package/dist/bot/conversation-store.js +182 -0
- package/dist/bot/conversation-store.js.map +1 -0
- package/dist/bot/cost-store.d.ts.map +1 -1
- package/dist/bot/cost-store.js +10 -14
- package/dist/bot/cost-store.js.map +1 -1
- package/dist/bot/error-guide.d.ts +10 -0
- package/dist/bot/error-guide.d.ts.map +1 -0
- package/dist/bot/error-guide.js +34 -0
- package/dist/bot/error-guide.js.map +1 -0
- package/dist/bot/genesis-store.d.ts.map +1 -1
- package/dist/bot/genesis-store.js +11 -20
- package/dist/bot/genesis-store.js.map +1 -1
- package/dist/bot/index.d.ts +3 -0
- package/dist/bot/index.d.ts.map +1 -1
- package/dist/bot/index.js +3 -0
- package/dist/bot/index.js.map +1 -1
- package/dist/bot/knowledge-store.d.ts +17 -0
- package/dist/bot/knowledge-store.d.ts.map +1 -0
- package/dist/bot/knowledge-store.js +53 -0
- package/dist/bot/knowledge-store.js.map +1 -0
- package/dist/bot/pipeline-runner.d.ts.map +1 -1
- package/dist/bot/pipeline-runner.js +8 -1
- package/dist/bot/pipeline-runner.js.map +1 -1
- package/dist/bot/retry-utils.d.ts +19 -0
- package/dist/bot/retry-utils.d.ts.map +1 -0
- package/dist/bot/retry-utils.js +64 -0
- package/dist/bot/retry-utils.js.map +1 -0
- package/dist/bot/run-store.d.ts.map +1 -1
- package/dist/bot/run-store.js +2 -10
- package/dist/bot/run-store.js.map +1 -1
- package/dist/bot/runner.d.ts.map +1 -1
- package/dist/bot/runner.js +24 -3
- package/dist/bot/runner.js.map +1 -1
- package/dist/bot/safe-json.d.ts +32 -0
- package/dist/bot/safe-json.d.ts.map +1 -0
- package/dist/bot/safe-json.js +56 -0
- package/dist/bot/safe-json.js.map +1 -0
- package/dist/bot/safe-path.d.ts +18 -0
- package/dist/bot/safe-path.d.ts.map +1 -0
- package/dist/bot/safe-path.js +40 -0
- package/dist/bot/safe-path.js.map +1 -0
- package/dist/bot/session-state.d.ts.map +1 -1
- package/dist/bot/session-state.js +3 -1
- package/dist/bot/session-state.js.map +1 -1
- package/dist/bot/steering.js +1 -1
- package/dist/bot/steering.js.map +1 -1
- package/dist/bot/step-executor.d.ts +10 -5
- package/dist/bot/step-executor.d.ts.map +1 -1
- package/dist/bot/step-executor.js +252 -3
- package/dist/bot/step-executor.js.map +1 -1
- package/dist/bot/system-prompt.d.ts +1 -1
- package/dist/bot/system-prompt.d.ts.map +1 -1
- package/dist/bot/system-prompt.js +69 -43
- package/dist/bot/system-prompt.js.map +1 -1
- package/dist/bot/task-decomposer.d.ts +24 -0
- package/dist/bot/task-decomposer.d.ts.map +1 -0
- package/dist/bot/task-decomposer.js +75 -0
- package/dist/bot/task-decomposer.js.map +1 -0
- package/dist/bot/task-queue.d.ts +17 -4
- package/dist/bot/task-queue.d.ts.map +1 -1
- package/dist/bot/task-queue.js +102 -14
- package/dist/bot/task-queue.js.map +1 -1
- package/dist/bot/terminal-renderer.d.ts +60 -0
- package/dist/bot/terminal-renderer.d.ts.map +1 -0
- package/dist/bot/terminal-renderer.js +205 -0
- package/dist/bot/terminal-renderer.js.map +1 -0
- package/dist/bot/types.d.ts +7 -0
- package/dist/bot/types.d.ts.map +1 -1
- package/dist/bot/weaver-tools.d.ts +18 -0
- package/dist/bot/weaver-tools.d.ts.map +1 -0
- package/dist/bot/weaver-tools.js +215 -0
- package/dist/bot/weaver-tools.js.map +1 -0
- package/dist/cli-bridge.d.ts.map +1 -1
- package/dist/cli-bridge.js +10 -3
- package/dist/cli-bridge.js.map +1 -1
- package/dist/cli-handlers.d.ts +15 -1
- package/dist/cli-handlers.d.ts.map +1 -1
- package/dist/cli-handlers.js +742 -28
- package/dist/cli-handlers.js.map +1 -1
- package/dist/handlers/on-bot-completed.d.ts +21 -0
- package/dist/handlers/on-bot-completed.d.ts.map +1 -0
- package/dist/handlers/on-bot-completed.js +28 -0
- package/dist/handlers/on-bot-completed.js.map +1 -0
- package/dist/handlers/on-execution-failure.d.ts +23 -0
- package/dist/handlers/on-execution-failure.d.ts.map +1 -0
- package/dist/handlers/on-execution-failure.js +28 -0
- package/dist/handlers/on-execution-failure.js.map +1 -0
- package/dist/handlers/scheduled-run.d.ts +24 -0
- package/dist/handlers/scheduled-run.d.ts.map +1 -0
- package/dist/handlers/scheduled-run.js +25 -0
- package/dist/handlers/scheduled-run.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-tools.js +2 -2
- package/dist/mcp-tools.js.map +1 -1
- package/dist/node-types/abort-task.d.ts.map +1 -1
- package/dist/node-types/abort-task.js +4 -3
- package/dist/node-types/abort-task.js.map +1 -1
- package/dist/node-types/agent-execute.d.ts +38 -0
- package/dist/node-types/agent-execute.d.ts.map +1 -0
- package/dist/node-types/agent-execute.js +256 -0
- package/dist/node-types/agent-execute.js.map +1 -0
- package/dist/node-types/bot-report.d.ts +5 -3
- package/dist/node-types/bot-report.d.ts.map +1 -1
- package/dist/node-types/bot-report.js +39 -7
- package/dist/node-types/bot-report.js.map +1 -1
- package/dist/node-types/build-context.d.ts +3 -3
- package/dist/node-types/build-context.d.ts.map +1 -1
- package/dist/node-types/build-context.js +108 -24
- package/dist/node-types/build-context.js.map +1 -1
- package/dist/node-types/detect-provider.d.ts +2 -2
- package/dist/node-types/detect-provider.d.ts.map +1 -1
- package/dist/node-types/detect-provider.js +3 -1
- package/dist/node-types/detect-provider.js.map +1 -1
- package/dist/node-types/exec-validate-retry.d.ts.map +1 -1
- package/dist/node-types/exec-validate-retry.js +43 -6
- package/dist/node-types/exec-validate-retry.js.map +1 -1
- package/dist/node-types/execute-plan.d.ts.map +1 -1
- package/dist/node-types/execute-plan.js +31 -8
- package/dist/node-types/execute-plan.js.map +1 -1
- package/dist/node-types/execute-target.d.ts.map +1 -1
- package/dist/node-types/execute-target.js +3 -1
- package/dist/node-types/execute-target.js.map +1 -1
- package/dist/node-types/fix-errors.d.ts.map +1 -1
- package/dist/node-types/fix-errors.js +21 -5
- package/dist/node-types/fix-errors.js.map +1 -1
- package/dist/node-types/genesis-observe.d.ts.map +1 -1
- package/dist/node-types/genesis-observe.js +3 -1
- package/dist/node-types/genesis-observe.js.map +1 -1
- package/dist/node-types/genesis-report.js +4 -1
- package/dist/node-types/genesis-report.js.map +1 -1
- package/dist/node-types/git-ops.d.ts.map +1 -1
- package/dist/node-types/git-ops.js +98 -4
- package/dist/node-types/git-ops.js.map +1 -1
- package/dist/node-types/index.d.ts +2 -0
- package/dist/node-types/index.d.ts.map +1 -1
- package/dist/node-types/index.js +2 -0
- package/dist/node-types/index.js.map +1 -1
- package/dist/node-types/load-config.d.ts +2 -2
- package/dist/node-types/load-config.d.ts.map +1 -1
- package/dist/node-types/load-config.js.map +1 -1
- package/dist/node-types/plan-task.d.ts.map +1 -1
- package/dist/node-types/plan-task.js +14 -2
- package/dist/node-types/plan-task.js.map +1 -1
- package/dist/node-types/read-workflow.js +8 -2
- package/dist/node-types/read-workflow.js.map +1 -1
- package/dist/node-types/receive-task.d.ts.map +1 -1
- package/dist/node-types/receive-task.js +35 -26
- package/dist/node-types/receive-task.js.map +1 -1
- package/dist/node-types/send-notify.js +2 -1
- package/dist/node-types/send-notify.js.map +1 -1
- package/dist/node-types/validate-gate.d.ts +18 -0
- package/dist/node-types/validate-gate.d.ts.map +1 -0
- package/dist/node-types/validate-gate.js +96 -0
- package/dist/node-types/validate-gate.js.map +1 -0
- package/dist/workflows/genesis-task.d.ts +20 -12
- package/dist/workflows/genesis-task.d.ts.map +1 -1
- package/dist/workflows/genesis-task.js +20 -12
- package/dist/workflows/genesis-task.js.map +1 -1
- package/dist/workflows/weaver-agent.d.ts +35 -0
- package/dist/workflows/weaver-agent.d.ts.map +1 -0
- package/dist/workflows/weaver-agent.js +777 -0
- package/dist/workflows/weaver-agent.js.map +1 -0
- package/dist/workflows/weaver-bot-batch.d.ts +19 -26
- package/dist/workflows/weaver-bot-batch.d.ts.map +1 -1
- package/dist/workflows/weaver-bot-batch.js +1043 -27
- package/dist/workflows/weaver-bot-batch.js.map +1 -1
- package/dist/workflows/weaver-bot.d.ts +21 -35
- package/dist/workflows/weaver-bot.d.ts.map +1 -1
- package/dist/workflows/weaver-bot.js +1119 -36
- package/dist/workflows/weaver-bot.js.map +1 -1
- package/flowweaver.manifest.json +113 -2
- package/package.json +5 -2
- package/src/bot/ai-client.ts +180 -19
- package/src/bot/assistant-core.ts +306 -0
- package/src/bot/assistant-tools.ts +605 -0
- package/src/bot/audit-logger.ts +6 -5
- package/src/bot/audit-store.ts +3 -12
- package/src/bot/bot-manager.ts +293 -0
- package/src/bot/child-process-tracker.ts +40 -0
- package/src/bot/cli-provider.ts +13 -8
- package/src/bot/conversation-store.ts +222 -0
- package/src/bot/cost-store.ts +11 -12
- package/src/bot/error-guide.ts +34 -0
- package/src/bot/genesis-store.ts +11 -17
- package/src/bot/index.ts +5 -0
- package/src/bot/knowledge-store.ts +59 -0
- package/src/bot/pipeline-runner.ts +7 -1
- package/src/bot/retry-utils.ts +76 -0
- package/src/bot/run-store.ts +2 -11
- package/src/bot/runner.ts +26 -3
- package/src/bot/safe-json.ts +76 -0
- package/src/bot/safe-path.ts +44 -0
- package/src/bot/session-state.ts +2 -1
- package/src/bot/steering.ts +1 -1
- package/src/bot/step-executor.ts +313 -5
- package/src/bot/system-prompt.ts +70 -47
- package/src/bot/task-decomposer.ts +100 -0
- package/src/bot/task-queue.ts +119 -15
- package/src/bot/terminal-renderer.ts +241 -0
- package/src/bot/types.ts +8 -0
- package/src/bot/weaver-tools.ts +225 -0
- package/src/cli-bridge.ts +14 -3
- package/src/cli-handlers.ts +760 -29
- package/src/handlers/on-bot-completed.ts +48 -0
- package/src/handlers/on-execution-failure.ts +42 -0
- package/src/handlers/scheduled-run.ts +42 -0
- package/src/index.ts +5 -0
- package/src/mcp-tools.ts +2 -2
- package/src/node-types/abort-task.ts +5 -4
- package/src/node-types/agent-execute.ts +306 -0
- package/src/node-types/bot-report.ts +40 -9
- package/src/node-types/build-context.ts +112 -25
- package/src/node-types/detect-provider.ts +4 -3
- package/src/node-types/exec-validate-retry.ts +47 -8
- package/src/node-types/execute-plan.ts +32 -8
- package/src/node-types/execute-target.ts +2 -1
- package/src/node-types/fix-errors.ts +20 -5
- package/src/node-types/genesis-observe.ts +2 -1
- package/src/node-types/genesis-report.ts +1 -1
- package/src/node-types/git-ops.ts +93 -4
- package/src/node-types/index.ts +2 -0
- package/src/node-types/load-config.ts +3 -3
- package/src/node-types/plan-task.ts +15 -3
- package/src/node-types/read-workflow.ts +2 -2
- package/src/node-types/receive-task.ts +31 -26
- package/src/node-types/send-notify.ts +1 -1
- package/src/node-types/validate-gate.ts +112 -0
- package/src/workflows/genesis-task.ts +20 -12
- package/src/workflows/weaver-agent.ts +799 -0
- package/src/workflows/weaver-bot-batch.ts +1049 -27
- package/src/workflows/weaver-bot.ts +1123 -36
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Actionable error guidance — maps cryptic error messages
|
|
3
|
+
* to human-readable explanations with fix suggestions.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const GUIDES: Array<{ pattern: RegExp; guidance: string }> = [
|
|
7
|
+
{ pattern: /ETIMEDOUT/i, guidance: 'Network timeout. Check internet connection or increase timeout with --timeout.' },
|
|
8
|
+
{ pattern: /ECONNRESET/i, guidance: 'Connection reset. The server closed the connection. Retry in a few seconds.' },
|
|
9
|
+
{ pattern: /ECONNREFUSED/i, guidance: 'Connection refused. Is the service running? Check the provider URL.' },
|
|
10
|
+
{ pattern: /401|authentication|invalid.*key/i, guidance: 'Authentication failed. Check ANTHROPIC_API_KEY or run "weaver init" to reconfigure.' },
|
|
11
|
+
{ pattern: /403|forbidden/i, guidance: 'Access denied. Your API key may not have permission for this model.' },
|
|
12
|
+
{ pattern: /429|rate.?limit|too many requests/i, guidance: 'Rate limited. Wait a few minutes or reduce --parallel.' },
|
|
13
|
+
{ pattern: /502|bad gateway/i, guidance: 'Server error (502). The API is temporarily unavailable. Will auto-retry.' },
|
|
14
|
+
{ pattern: /503|service unavailable|overloaded/i, guidance: 'Service overloaded. Will auto-retry with backoff.' },
|
|
15
|
+
{ pattern: /exit code 143/i, guidance: 'Process was killed (SIGTERM). Likely our timeout or Ctrl+C.' },
|
|
16
|
+
{ pattern: /exit code 137/i, guidance: 'Process was killed (OOM or SIGKILL). System may be low on memory.' },
|
|
17
|
+
{ pattern: /ENOMEM/i, guidance: 'Out of memory. Close other applications or increase available RAM.' },
|
|
18
|
+
{ pattern: /ENOSPC/i, guidance: 'Disk full. Free up disk space.' },
|
|
19
|
+
{ pattern: /lock.*retries|failed to acquire.*lock/i, guidance: 'File lock contention. Another weaver process may be running. Check with "ps aux | grep weaver".' },
|
|
20
|
+
{ pattern: /not a workflow|No @flowWeaver/i, guidance: 'File is not a Flow Weaver workflow. Ensure it has @flowWeaver annotations.' },
|
|
21
|
+
{ pattern: /parse.*json|unexpected token/i, guidance: 'JSON parse error. The AI may have returned malformed output. Retry the task.' },
|
|
22
|
+
{ pattern: /Queue full/i, guidance: 'Too many pending tasks (200 max). Process or clear existing tasks first.' },
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get actionable guidance for an error message.
|
|
27
|
+
* Returns null if no guidance is available.
|
|
28
|
+
*/
|
|
29
|
+
export function getErrorGuidance(msg: string): string | null {
|
|
30
|
+
for (const { pattern, guidance } of GUIDES) {
|
|
31
|
+
if (pattern.test(msg)) return guidance;
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
package/src/bot/genesis-store.ts
CHANGED
|
@@ -2,6 +2,7 @@ import * as fs from 'node:fs';
|
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import * as crypto from 'node:crypto';
|
|
4
4
|
import type { GenesisConfig, GenesisHistory, GenesisCycleRecord, GenesisFingerprint, EscrowToken, GenesisSelfMigrationRecord } from './types.js';
|
|
5
|
+
import { jsonParseOr } from './safe-json.js';
|
|
5
6
|
|
|
6
7
|
const DEFAULT_CONFIG: GenesisConfig = {
|
|
7
8
|
intent: 'Improve workflow reliability and efficiency',
|
|
@@ -32,7 +33,8 @@ export class GenesisStore {
|
|
|
32
33
|
fs.writeFileSync(configPath, JSON.stringify(DEFAULT_CONFIG, null, 2), 'utf-8');
|
|
33
34
|
return { ...DEFAULT_CONFIG };
|
|
34
35
|
}
|
|
35
|
-
const
|
|
36
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
37
|
+
const raw = jsonParseOr(content, {} as Record<string, unknown>, 'genesis config');
|
|
36
38
|
return { ...DEFAULT_CONFIG, ...raw };
|
|
37
39
|
}
|
|
38
40
|
|
|
@@ -46,7 +48,8 @@ export class GenesisStore {
|
|
|
46
48
|
if (!fs.existsSync(historyPath)) {
|
|
47
49
|
return { configHash: '', cycles: [] };
|
|
48
50
|
}
|
|
49
|
-
|
|
51
|
+
const content = fs.readFileSync(historyPath, 'utf-8');
|
|
52
|
+
return jsonParseOr<GenesisHistory>(content, { configHash: '', cycles: [] }, 'genesis history');
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
appendCycle(cycle: GenesisCycleRecord): void {
|
|
@@ -79,11 +82,8 @@ export class GenesisStore {
|
|
|
79
82
|
getLastFingerprint(): GenesisFingerprint | null {
|
|
80
83
|
const fpPath = path.join(this.genesisDir, 'fingerprint.json');
|
|
81
84
|
if (!fs.existsSync(fpPath)) return null;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
} catch {
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
85
|
+
const content = fs.readFileSync(fpPath, 'utf-8');
|
|
86
|
+
return jsonParseOr<GenesisFingerprint | null>(content, null, 'genesis fingerprint');
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
getRecentOutcomes(count: number): string[] {
|
|
@@ -109,11 +109,8 @@ export class GenesisStore {
|
|
|
109
109
|
loadEscrowToken(): EscrowToken | null {
|
|
110
110
|
const tokenPath = path.join(this.genesisDir, 'escrow', 'token.json');
|
|
111
111
|
if (!fs.existsSync(tokenPath)) return null;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
} catch {
|
|
115
|
-
return null;
|
|
116
|
-
}
|
|
112
|
+
const content = fs.readFileSync(tokenPath, 'utf-8');
|
|
113
|
+
return jsonParseOr<EscrowToken | null>(content, null, 'escrow token');
|
|
117
114
|
}
|
|
118
115
|
|
|
119
116
|
saveEscrowToken(token: EscrowToken): void {
|
|
@@ -145,11 +142,8 @@ export class GenesisStore {
|
|
|
145
142
|
loadSelfHistory(): GenesisSelfMigrationRecord[] {
|
|
146
143
|
const histPath = path.join(this.genesisDir, 'self-history.json');
|
|
147
144
|
if (!fs.existsSync(histPath)) return [];
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
} catch {
|
|
151
|
-
return [];
|
|
152
|
-
}
|
|
145
|
+
const content = fs.readFileSync(histPath, 'utf-8');
|
|
146
|
+
return jsonParseOr<GenesisSelfMigrationRecord[]>(content, [], 'genesis self-history');
|
|
153
147
|
}
|
|
154
148
|
|
|
155
149
|
appendSelfMigration(record: GenesisSelfMigrationRecord): void {
|
package/src/bot/index.ts
CHANGED
|
@@ -112,6 +112,11 @@ export { GenesisStore } from './genesis-store.js';
|
|
|
112
112
|
export { withFileLock } from './file-lock.js';
|
|
113
113
|
export type { FileLockOptions } from './file-lock.js';
|
|
114
114
|
|
|
115
|
+
// Safe utilities
|
|
116
|
+
export { safeJsonParse, jsonParseOr, parseNdjson } from './safe-json.js';
|
|
117
|
+
export type { SafeParseResult } from './safe-json.js';
|
|
118
|
+
export { safePath, safePathOrThrow } from './safe-path.js';
|
|
119
|
+
|
|
115
120
|
// Shared modules
|
|
116
121
|
export { callCli, callApi, parseJsonResponse } from './ai-client.js';
|
|
117
122
|
export { executeStep } from './step-executor.js';
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import * as os from 'node:os';
|
|
4
|
+
import * as crypto from 'node:crypto';
|
|
5
|
+
|
|
6
|
+
export interface KnowledgeEntry {
|
|
7
|
+
key: string;
|
|
8
|
+
value: string;
|
|
9
|
+
source: string;
|
|
10
|
+
createdAt: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class KnowledgeStore {
|
|
14
|
+
private filePath: string;
|
|
15
|
+
|
|
16
|
+
constructor(projectDir?: string) {
|
|
17
|
+
const dir = projectDir
|
|
18
|
+
? path.join(os.homedir(), '.weaver', 'projects', crypto.createHash('sha256').update(projectDir).digest('hex').slice(0, 8))
|
|
19
|
+
: path.join(os.homedir(), '.weaver');
|
|
20
|
+
this.filePath = path.join(dir, 'knowledge.ndjson');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
learn(key: string, value: string, source: string): void {
|
|
24
|
+
// Append entry to NDJSON file. If key exists, update it.
|
|
25
|
+
const entries = this.readAll().filter(e => e.key !== key);
|
|
26
|
+
entries.push({ key, value, source, createdAt: Date.now() });
|
|
27
|
+
this.writeAll(entries);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
recall(query: string): KnowledgeEntry[] {
|
|
31
|
+
// Fuzzy match: return entries whose key contains the query (case-insensitive)
|
|
32
|
+
const lower = query.toLowerCase();
|
|
33
|
+
return this.readAll().filter(e => e.key.toLowerCase().includes(lower) || e.value.toLowerCase().includes(lower));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
forget(key: string): void {
|
|
37
|
+
const entries = this.readAll().filter(e => e.key !== key);
|
|
38
|
+
this.writeAll(entries);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
list(): KnowledgeEntry[] {
|
|
42
|
+
return this.readAll();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private readAll(): KnowledgeEntry[] {
|
|
46
|
+
if (!fs.existsSync(this.filePath)) return [];
|
|
47
|
+
const content = fs.readFileSync(this.filePath, 'utf-8').trim();
|
|
48
|
+
if (!content) return [];
|
|
49
|
+
return content.split('\n').filter(Boolean).map(line => {
|
|
50
|
+
try { return JSON.parse(line); } catch { return null; }
|
|
51
|
+
}).filter(Boolean) as KnowledgeEntry[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private writeAll(entries: KnowledgeEntry[]): void {
|
|
55
|
+
const dir = path.dirname(this.filePath);
|
|
56
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
57
|
+
fs.writeFileSync(this.filePath, entries.map(e => JSON.stringify(e)).join('\n') + (entries.length > 0 ? '\n' : ''));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -31,7 +31,13 @@ export class PipelineRunner {
|
|
|
31
31
|
throw new Error(`Pipeline config not found: ${absPath}`);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
let raw: PipelineConfig;
|
|
35
|
+
try {
|
|
36
|
+
raw = JSON.parse(fs.readFileSync(absPath, 'utf-8')) as PipelineConfig;
|
|
37
|
+
} catch (err) {
|
|
38
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
39
|
+
throw new Error(`Invalid JSON in pipeline config: ${absPath}\n ${msg}`);
|
|
40
|
+
}
|
|
35
41
|
const configDir = path.dirname(absPath);
|
|
36
42
|
|
|
37
43
|
// Resolve workflow paths relative to config file
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry utilities for transient error handling with exponential backoff.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const TRANSIENT_STATUS_CODES = [429, 502, 503, 504];
|
|
6
|
+
const TRANSIENT_ERROR_CODES = ['ETIMEDOUT', 'ECONNRESET', 'ECONNREFUSED', 'EPIPE', 'ENOTFOUND'];
|
|
7
|
+
const TRANSIENT_MESSAGES = ['rate limit', 'too many requests', 'overloaded', 'bad gateway', 'service unavailable'];
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Check if an error is transient (retriable) vs permanent.
|
|
11
|
+
* Transient: network issues, rate limits, server errors.
|
|
12
|
+
* Permanent: auth failures, parse errors, validation errors.
|
|
13
|
+
*/
|
|
14
|
+
export function isTransientError(err: unknown): boolean {
|
|
15
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
16
|
+
const lower = msg.toLowerCase();
|
|
17
|
+
|
|
18
|
+
// Check for HTTP status codes in message
|
|
19
|
+
for (const code of TRANSIENT_STATUS_CODES) {
|
|
20
|
+
if (msg.includes(String(code))) return true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Check for Node.js error codes
|
|
24
|
+
if (err instanceof Error && 'code' in err) {
|
|
25
|
+
const code = (err as NodeJS.ErrnoException).code;
|
|
26
|
+
if (code && TRANSIENT_ERROR_CODES.includes(code)) return true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Also check message for error code strings (e.g. "ETIMEDOUT" in message)
|
|
30
|
+
for (const code of TRANSIENT_ERROR_CODES) {
|
|
31
|
+
if (msg.includes(code)) return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Check for rate limit / overload messages
|
|
35
|
+
for (const phrase of TRANSIENT_MESSAGES) {
|
|
36
|
+
if (lower.includes(phrase)) return true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Check for exit code 143 (SIGTERM — likely our timeout killed the process)
|
|
40
|
+
if (msg.includes('exit code 143') || msg.includes('exited with code 143')) return true;
|
|
41
|
+
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Run a function with exponential backoff retry on transient errors.
|
|
47
|
+
*/
|
|
48
|
+
export async function withRetry<T>(
|
|
49
|
+
fn: () => Promise<T>,
|
|
50
|
+
options?: {
|
|
51
|
+
maxRetries?: number;
|
|
52
|
+
baseDelayMs?: number;
|
|
53
|
+
multiplier?: number;
|
|
54
|
+
onRetry?: (attempt: number, delay: number, err: Error) => void;
|
|
55
|
+
},
|
|
56
|
+
): Promise<T> {
|
|
57
|
+
const maxRetries = options?.maxRetries ?? 3;
|
|
58
|
+
const baseDelay = options?.baseDelayMs ?? 5_000;
|
|
59
|
+
const multiplier = options?.multiplier ?? 3;
|
|
60
|
+
|
|
61
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
62
|
+
try {
|
|
63
|
+
return await fn();
|
|
64
|
+
} catch (err: unknown) {
|
|
65
|
+
const isLast = attempt >= maxRetries;
|
|
66
|
+
if (isLast || !isTransientError(err)) throw err;
|
|
67
|
+
|
|
68
|
+
const delay = baseDelay * Math.pow(multiplier, attempt);
|
|
69
|
+
options?.onRetry?.(attempt + 1, delay, err instanceof Error ? err : new Error(String(err)));
|
|
70
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Unreachable, but TypeScript needs it
|
|
75
|
+
throw new Error('withRetry: exhausted retries');
|
|
76
|
+
}
|
package/src/bot/run-store.ts
CHANGED
|
@@ -3,6 +3,7 @@ import * as path from 'node:path';
|
|
|
3
3
|
import * as crypto from 'node:crypto';
|
|
4
4
|
import * as os from 'node:os';
|
|
5
5
|
import type { RunRecord, RunFilter, RetentionPolicy } from './types.js';
|
|
6
|
+
import { parseNdjson } from './safe-json.js';
|
|
6
7
|
|
|
7
8
|
export class RunStore {
|
|
8
9
|
private readonly dir: string;
|
|
@@ -153,17 +154,7 @@ export class RunStore {
|
|
|
153
154
|
if (!fs.existsSync(this.filePath)) return [];
|
|
154
155
|
|
|
155
156
|
const content = fs.readFileSync(this.filePath, 'utf-8');
|
|
156
|
-
const
|
|
157
|
-
const records: RunRecord[] = [];
|
|
158
|
-
|
|
159
|
-
for (const line of lines) {
|
|
160
|
-
try {
|
|
161
|
-
records.push(JSON.parse(line) as RunRecord);
|
|
162
|
-
} catch {
|
|
163
|
-
console.error('[weaver] Skipping corrupt history line');
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
157
|
+
const { records } = parseNdjson<RunRecord>(content, 'history');
|
|
167
158
|
return records;
|
|
168
159
|
}
|
|
169
160
|
}
|
package/src/bot/runner.ts
CHANGED
|
@@ -58,17 +58,29 @@ function resolveWeaverConfig(
|
|
|
58
58
|
const dir = path.dirname(filePath);
|
|
59
59
|
const localConfig = path.join(dir, '.weaver.json');
|
|
60
60
|
if (fs.existsSync(localConfig)) {
|
|
61
|
-
return
|
|
61
|
+
return parseConfigFile(localConfig);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
const cwdConfig = path.join(process.cwd(), '.weaver.json');
|
|
65
65
|
if (fs.existsSync(cwdConfig)) {
|
|
66
|
-
return
|
|
66
|
+
return parseConfigFile(cwdConfig);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
return { provider: 'auto' };
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
function parseConfigFile(configPath: string): WeaverConfig {
|
|
73
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
74
|
+
try {
|
|
75
|
+
return JSON.parse(content) as WeaverConfig;
|
|
76
|
+
} catch {
|
|
77
|
+
throw new Error(
|
|
78
|
+
`Invalid JSON in config file: ${configPath}\n` +
|
|
79
|
+
` Fix the file or delete it to use defaults.`,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
72
84
|
function buildSummary(result: unknown): string {
|
|
73
85
|
if (!result || typeof result !== 'object') return String(result);
|
|
74
86
|
|
|
@@ -212,6 +224,16 @@ export async function runWorkflow(
|
|
|
212
224
|
const summary = buildSummary(result);
|
|
213
225
|
const outcome = success ? 'completed' : 'failed';
|
|
214
226
|
|
|
227
|
+
// Extract stepLog from WeaverContext if available
|
|
228
|
+
let stepLog: import('./types.js').StepLogEntry[] | undefined;
|
|
229
|
+
try {
|
|
230
|
+
const ctxStr = result?.ctx as string | undefined;
|
|
231
|
+
if (ctxStr) {
|
|
232
|
+
const ctx = JSON.parse(ctxStr);
|
|
233
|
+
if (ctx.stepLogJson) stepLog = JSON.parse(ctx.stepLogJson);
|
|
234
|
+
}
|
|
235
|
+
} catch { /* stepLog extraction is best-effort */ }
|
|
236
|
+
|
|
215
237
|
await notifier({
|
|
216
238
|
type: 'workflow-complete',
|
|
217
239
|
workflowFile: absPath,
|
|
@@ -225,7 +247,7 @@ export async function runWorkflow(
|
|
|
225
247
|
recordRun(store, {
|
|
226
248
|
id: runId, workflowFile: absPath, startedAt, success, outcome: outcome as RunOutcome, summary,
|
|
227
249
|
functionName: execResult.functionName, executionTime: execResult.executionTime,
|
|
228
|
-
dryRun: false, provider: providerConfig.name, params: options?.params,
|
|
250
|
+
dryRun: false, provider: providerConfig.name, params: options?.params, stepLog,
|
|
229
251
|
}, verbose);
|
|
230
252
|
|
|
231
253
|
auditEmit('run-complete', { success, outcome, summary });
|
|
@@ -270,6 +292,7 @@ function recordRun(
|
|
|
270
292
|
outcome: RunOutcome; summary: string; functionName?: string;
|
|
271
293
|
executionTime?: number; dryRun: boolean; provider?: string;
|
|
272
294
|
params?: Record<string, unknown>;
|
|
295
|
+
stepLog?: import('./types.js').StepLogEntry[];
|
|
273
296
|
},
|
|
274
297
|
verbose: boolean,
|
|
275
298
|
): void {
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safe JSON parsing utilities.
|
|
3
|
+
*
|
|
4
|
+
* Wraps JSON.parse with proper error handling and optional context for
|
|
5
|
+
* meaningful error messages. Prevents crashes from malformed config files,
|
|
6
|
+
* corrupt NDJSON lines, or unexpected AI output.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type SafeParseResult<T> = {
|
|
10
|
+
ok: true;
|
|
11
|
+
value: T;
|
|
12
|
+
} | {
|
|
13
|
+
ok: false;
|
|
14
|
+
error: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Parse JSON safely, returning a discriminated result instead of throwing.
|
|
19
|
+
*/
|
|
20
|
+
export function safeJsonParse<T = unknown>(
|
|
21
|
+
text: string,
|
|
22
|
+
context?: string,
|
|
23
|
+
): SafeParseResult<T> {
|
|
24
|
+
try {
|
|
25
|
+
return { ok: true, value: JSON.parse(text) as T };
|
|
26
|
+
} catch (err: unknown) {
|
|
27
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
28
|
+
const prefix = context ? `${context}: ` : '';
|
|
29
|
+
return { ok: false, error: `${prefix}Invalid JSON — ${msg}` };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Parse JSON or return a fallback value on failure.
|
|
35
|
+
* Optionally logs a warning when parsing fails.
|
|
36
|
+
*/
|
|
37
|
+
export function jsonParseOr<T>(
|
|
38
|
+
text: string,
|
|
39
|
+
fallback: T,
|
|
40
|
+
context?: string,
|
|
41
|
+
): T {
|
|
42
|
+
const result = safeJsonParse<T>(text, context);
|
|
43
|
+
if (result.ok) return result.value;
|
|
44
|
+
if (context) {
|
|
45
|
+
console.error(`[weaver] ${result.error}`);
|
|
46
|
+
}
|
|
47
|
+
return fallback;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Parse NDJSON (newline-delimited JSON) safely.
|
|
52
|
+
* Skips corrupt lines and optionally reports them.
|
|
53
|
+
*/
|
|
54
|
+
export function parseNdjson<T>(
|
|
55
|
+
content: string,
|
|
56
|
+
context?: string,
|
|
57
|
+
): { records: T[]; errors: number } {
|
|
58
|
+
const lines = content.split('\n').filter((line) => line.trim().length > 0);
|
|
59
|
+
const records: T[] = [];
|
|
60
|
+
let errors = 0;
|
|
61
|
+
|
|
62
|
+
for (const line of lines) {
|
|
63
|
+
const result = safeJsonParse<T>(line);
|
|
64
|
+
if (result.ok) {
|
|
65
|
+
records.push(result.value);
|
|
66
|
+
} else {
|
|
67
|
+
errors++;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (errors > 0 && context) {
|
|
72
|
+
console.error(`[weaver] ${context}: skipped ${errors} corrupt line(s)`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { records, errors };
|
|
76
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path safety utilities.
|
|
3
|
+
*
|
|
4
|
+
* Prevents path traversal attacks and ensures file operations stay within
|
|
5
|
+
* expected boundaries. Critical for any code that constructs paths from
|
|
6
|
+
* user input, AI output, or external configuration.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Validate that a relative path does not escape the base directory.
|
|
13
|
+
* Returns the resolved absolute path if safe, or null if the path
|
|
14
|
+
* attempts traversal.
|
|
15
|
+
*/
|
|
16
|
+
export function safePath(baseDir: string, relativePath: string): string | null {
|
|
17
|
+
const normalized = path.normalize(relativePath);
|
|
18
|
+
|
|
19
|
+
// Reject absolute paths and explicit traversal
|
|
20
|
+
if (path.isAbsolute(normalized)) return null;
|
|
21
|
+
if (normalized.startsWith('..')) return null;
|
|
22
|
+
|
|
23
|
+
const resolved = path.resolve(baseDir, normalized);
|
|
24
|
+
const resolvedBase = path.resolve(baseDir);
|
|
25
|
+
|
|
26
|
+
// Ensure resolved path is within baseDir
|
|
27
|
+
if (!resolved.startsWith(resolvedBase + path.sep) && resolved !== resolvedBase) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return resolved;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Validate and resolve a path, throwing a descriptive error on traversal.
|
|
36
|
+
*/
|
|
37
|
+
export function safePathOrThrow(baseDir: string, relativePath: string, context?: string): string {
|
|
38
|
+
const resolved = safePath(baseDir, relativePath);
|
|
39
|
+
if (resolved === null) {
|
|
40
|
+
const prefix = context ? `${context}: ` : '';
|
|
41
|
+
throw new Error(`${prefix}Unsafe file path rejected: "${relativePath}"`);
|
|
42
|
+
}
|
|
43
|
+
return resolved;
|
|
44
|
+
}
|
package/src/bot/session-state.ts
CHANGED
|
@@ -40,7 +40,8 @@ export class SessionStore {
|
|
|
40
40
|
try {
|
|
41
41
|
if (!fs.existsSync(this.filePath)) return null;
|
|
42
42
|
return JSON.parse(fs.readFileSync(this.filePath, 'utf-8')) as SessionState;
|
|
43
|
-
} catch {
|
|
43
|
+
} catch (err) {
|
|
44
|
+
if (process.env.WEAVER_VERBOSE) process.stderr.write(`[weaver] session state load failed: ${err}\n`);
|
|
44
45
|
return null;
|
|
45
46
|
}
|
|
46
47
|
}
|
package/src/bot/steering.ts
CHANGED
|
@@ -13,7 +13,7 @@ export class SteeringController {
|
|
|
13
13
|
private controlPath: string;
|
|
14
14
|
|
|
15
15
|
constructor(controlDir?: string) {
|
|
16
|
-
const dir = controlDir ?? path.join(os.homedir(), '.weaver');
|
|
16
|
+
const dir = controlDir ?? process.env.WEAVER_STEERING_DIR ?? path.join(os.homedir(), '.weaver');
|
|
17
17
|
this.controlPath = path.join(dir, 'control.json');
|
|
18
18
|
}
|
|
19
19
|
|