@synergenius/flow-weaver-pack-weaver 0.9.199 → 0.9.201
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/ai-chat-provider.js +5 -5
- package/dist/ai-chat-provider.js.map +1 -1
- package/dist/bot/acceptance-merge.d.ts +21 -0
- package/dist/bot/acceptance-merge.d.ts.map +1 -0
- package/dist/bot/acceptance-merge.js +46 -0
- package/dist/bot/acceptance-merge.js.map +1 -0
- package/dist/bot/ai-client.d.ts +14 -2
- package/dist/bot/ai-client.d.ts.map +1 -1
- package/dist/bot/ai-client.js +71 -24
- package/dist/bot/ai-client.js.map +1 -1
- package/dist/bot/assistant-tools.js +3 -3
- package/dist/bot/assistant-tools.js.map +1 -1
- package/dist/bot/audit-logger.d.ts.map +1 -1
- package/dist/bot/audit-logger.js +34 -14
- package/dist/bot/audit-logger.js.map +1 -1
- package/dist/bot/audit-trail.d.ts +67 -0
- package/dist/bot/audit-trail.d.ts.map +1 -0
- package/dist/bot/audit-trail.js +153 -0
- package/dist/bot/audit-trail.js.map +1 -0
- package/dist/bot/behavior-defaults.d.ts +1 -1
- package/dist/bot/behavior-defaults.d.ts.map +1 -1
- package/dist/bot/behavior-defaults.js +7 -3
- package/dist/bot/behavior-defaults.js.map +1 -1
- package/dist/bot/capability-registry.d.ts +9 -0
- package/dist/bot/capability-registry.d.ts.map +1 -1
- package/dist/bot/capability-registry.js +81 -27
- package/dist/bot/capability-registry.js.map +1 -1
- package/dist/bot/capability-types.d.ts +10 -0
- package/dist/bot/capability-types.d.ts.map +1 -1
- package/dist/bot/cli-provider.d.ts.map +1 -1
- package/dist/bot/cli-provider.js +8 -7
- package/dist/bot/cli-provider.js.map +1 -1
- package/dist/bot/preflight.d.ts +48 -0
- package/dist/bot/preflight.d.ts.map +1 -0
- package/dist/bot/preflight.js +247 -0
- package/dist/bot/preflight.js.map +1 -0
- package/dist/bot/provider-shim.d.ts +74 -0
- package/dist/bot/provider-shim.d.ts.map +1 -0
- package/dist/bot/provider-shim.js +176 -0
- package/dist/bot/provider-shim.js.map +1 -0
- package/dist/bot/runner.d.ts +2 -0
- package/dist/bot/runner.d.ts.map +1 -1
- package/dist/bot/runner.js +60 -17
- package/dist/bot/runner.js.map +1 -1
- package/dist/bot/step-executor.d.ts.map +1 -1
- package/dist/bot/step-executor.js +72 -115
- package/dist/bot/step-executor.js.map +1 -1
- package/dist/bot/swarm-controller.d.ts +2 -0
- package/dist/bot/swarm-controller.d.ts.map +1 -1
- package/dist/bot/swarm-controller.js +92 -20
- package/dist/bot/swarm-controller.js.map +1 -1
- package/dist/bot/task-create-handler.d.ts +37 -0
- package/dist/bot/task-create-handler.d.ts.map +1 -0
- package/dist/bot/task-create-handler.js +124 -0
- package/dist/bot/task-create-handler.js.map +1 -0
- package/dist/bot/task-store.d.ts +1 -0
- package/dist/bot/task-store.d.ts.map +1 -1
- package/dist/bot/task-store.js +67 -0
- package/dist/bot/task-store.js.map +1 -1
- package/dist/bot/types.d.ts +1 -1
- package/dist/bot/types.d.ts.map +1 -1
- package/dist/bot/weaver-tools.d.ts.map +1 -1
- package/dist/bot/weaver-tools.js +7 -39
- package/dist/bot/weaver-tools.js.map +1 -1
- package/dist/node-types/agent-execute.d.ts +25 -8
- package/dist/node-types/agent-execute.d.ts.map +1 -1
- package/dist/node-types/agent-execute.js +89 -23
- package/dist/node-types/agent-execute.js.map +1 -1
- package/dist/node-types/bot-report.d.ts.map +1 -1
- package/dist/node-types/bot-report.js +24 -3
- package/dist/node-types/bot-report.js.map +1 -1
- package/dist/node-types/plan-task.d.ts +8 -17
- package/dist/node-types/plan-task.d.ts.map +1 -1
- package/dist/node-types/plan-task.js +217 -256
- package/dist/node-types/plan-task.js.map +1 -1
- package/dist/node-types/review-result.js +8 -6
- package/dist/node-types/review-result.js.map +1 -1
- package/dist/palindrome.d.ts +9 -0
- package/dist/palindrome.d.ts.map +1 -0
- package/dist/palindrome.js +14 -0
- package/dist/palindrome.js.map +1 -0
- package/dist/ui/approval-card.js +91 -82
- package/dist/ui/bot-activity.js +73 -56
- package/dist/ui/bot-config.js +48 -31
- package/dist/ui/bot-dashboard.js +52 -36
- package/dist/ui/bot-panel.js +230 -228
- package/dist/ui/bot-slot-card.js +100 -90
- package/dist/ui/bot-status.js +37 -15
- package/dist/ui/budget-bar.js +57 -31
- package/dist/ui/capability-editor.js +447 -378
- package/dist/ui/chat-task-result.js +78 -71
- package/dist/ui/decision-log.js +68 -81
- package/dist/ui/genesis-block.js +86 -95
- package/dist/ui/instance-stream-view.js +722 -0
- package/dist/ui/profile-card.js +96 -221
- package/dist/ui/profile-editor.js +532 -575
- package/dist/ui/settings-section.js +41 -45
- package/dist/ui/swarm-controls.js +212 -135
- package/dist/ui/swarm-dashboard.js +3992 -2715
- package/dist/ui/task-detail-view.js +415 -521
- package/dist/ui/task-editor.js +339 -390
- package/dist/ui/task-pool-list.js +60 -55
- package/dist/workflows/src/palindrome.d.ts +11 -0
- package/dist/workflows/src/palindrome.d.ts.map +1 -0
- package/dist/workflows/src/palindrome.js +16 -0
- package/dist/workflows/src/palindrome.js.map +1 -0
- package/dist/workflows/tests/palindrome.test.d.ts +2 -0
- package/dist/workflows/tests/palindrome.test.d.ts.map +1 -0
- package/dist/workflows/tests/palindrome.test.js +41 -0
- package/dist/workflows/tests/palindrome.test.js.map +1 -0
- package/dist/workflows/weaver-bot-batch.js +1 -1
- package/dist/workflows/weaver-bot-batch.js.map +1 -1
- package/dist/workflows/weaver-bot.js +1 -1
- package/dist/workflows/weaver-bot.js.map +1 -1
- package/flowweaver.manifest.json +1 -1
- package/package.json +8 -2
- package/src/ai-chat-provider.ts +5 -5
- package/src/bot/acceptance-merge.ts +62 -0
- package/src/bot/ai-client.ts +77 -21
- package/src/bot/assistant-tools.ts +3 -3
- package/src/bot/audit-logger.ts +42 -14
- package/src/bot/audit-trail.ts +211 -0
- package/src/bot/behavior-defaults.ts +7 -2
- package/src/bot/capability-registry.ts +84 -28
- package/src/bot/capability-types.ts +11 -0
- package/src/bot/cli-provider.ts +8 -7
- package/src/bot/preflight.ts +285 -0
- package/src/bot/provider-shim.ts +218 -0
- package/src/bot/runner.ts +68 -20
- package/src/bot/step-executor.ts +69 -127
- package/src/bot/swarm-controller.ts +94 -20
- package/src/bot/task-create-handler.ts +164 -0
- package/src/bot/task-store.ts +83 -0
- package/src/bot/types.ts +4 -1
- package/src/bot/weaver-tools.ts +7 -45
- package/src/node-types/agent-execute.ts +102 -16
- package/src/node-types/bot-report.ts +24 -3
- package/src/node-types/plan-task.ts +238 -280
- package/src/node-types/review-result.ts +8 -6
- package/src/palindrome.ts +14 -0
- package/src/ui/approval-card.tsx +78 -62
- package/src/ui/bot-activity.tsx +12 -10
- package/src/ui/bot-config.tsx +12 -10
- package/src/ui/bot-dashboard.tsx +13 -11
- package/src/ui/bot-panel.tsx +189 -171
- package/src/ui/bot-slot-card.tsx +125 -70
- package/src/ui/bot-status.tsx +4 -4
- package/src/ui/budget-bar.tsx +86 -25
- package/src/ui/capability-editor.tsx +392 -257
- package/src/ui/chat-task-result.tsx +81 -78
- package/src/ui/decision-log.tsx +76 -73
- package/src/ui/genesis-block.tsx +91 -61
- package/src/ui/instance-stream-view.tsx +861 -0
- package/src/ui/profile-card.tsx +195 -168
- package/src/ui/profile-editor.tsx +453 -370
- package/src/ui/settings-section.tsx +46 -39
- package/src/ui/swarm-controls.tsx +252 -123
- package/src/ui/swarm-dashboard.tsx +999 -466
- package/src/ui/task-detail-view.tsx +485 -428
- package/src/ui/task-editor.tsx +329 -271
- package/src/ui/task-pool-list.tsx +68 -62
- package/src/workflows/src/palindrome.ts +16 -0
- package/src/workflows/tests/palindrome.test.ts +49 -0
- package/src/workflows/weaver-bot-batch.ts +1 -1
- package/src/workflows/weaver-bot.ts +1 -1
- package/dist/ui/bot-constants.d.ts +0 -14
- package/dist/ui/bot-constants.d.ts.map +0 -1
- package/dist/ui/bot-constants.js +0 -189
- package/dist/ui/bot-constants.js.map +0 -1
- package/dist/ui/steer-api.d.ts +0 -7
- package/dist/ui/steer-api.d.ts.map +0 -1
- package/dist/ui/steer-api.js +0 -11
- package/dist/ui/steer-api.js.map +0 -1
- package/dist/ui/trace-to-timeline.d.ts +0 -91
- package/dist/ui/trace-to-timeline.d.ts.map +0 -1
- package/dist/ui/trace-to-timeline.js +0 -116
- package/dist/ui/trace-to-timeline.js.map +0 -1
- package/dist/ui/use-stream-timeline.d.ts +0 -50
- package/dist/ui/use-stream-timeline.d.ts.map +0 -1
- package/dist/ui/use-stream-timeline.js +0 -245
- package/dist/ui/use-stream-timeline.js.map +0 -1
package/src/bot/ai-client.ts
CHANGED
|
@@ -67,7 +67,23 @@ export function extractCliJsonResult(raw: string): string {
|
|
|
67
67
|
return trimmed;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
/** Callback for streaming conversation events to the EventLog. */
|
|
71
|
+
export type StreamEventCallback = (event: { type: string; timestamp: number; data?: Record<string, unknown> }) => void;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Set/clear the global stream event callback. Called by the runner before/after
|
|
75
|
+
* workflow execution so all callAI call sites automatically emit conversation events.
|
|
76
|
+
* Same pattern as __fw_ai_usage_callback__.
|
|
77
|
+
*/
|
|
78
|
+
export function setStreamEventCallback(cb: StreamEventCallback | undefined): void {
|
|
79
|
+
(globalThis as Record<string, unknown>).__fw_stream_event_callback__ = cb;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function getStreamEventCallback(): StreamEventCallback | undefined {
|
|
83
|
+
return (globalThis as Record<string, unknown>).__fw_stream_event_callback__ as StreamEventCallback | undefined;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function callCliAsync(provider: string, prompt: string, model?: string, systemPrompt?: string, cliBinPath?: string, onStreamEvent?: StreamEventCallback): Promise<string> {
|
|
71
87
|
if (provider === 'copilot-cli') {
|
|
72
88
|
return callCli(provider, prompt, model);
|
|
73
89
|
}
|
|
@@ -75,19 +91,21 @@ export async function callCliAsync(provider: string, prompt: string, model?: str
|
|
|
75
91
|
throw new Error(`Unknown CLI provider: ${provider}`);
|
|
76
92
|
}
|
|
77
93
|
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
//
|
|
94
|
+
// Use explicit callback or fall back to global (set by runner)
|
|
95
|
+
const emitStreamEvent = onStreamEvent ?? getStreamEventCallback();
|
|
96
|
+
|
|
97
|
+
// Use centralized CLI config — enforces --tools "" and --strict-mcp-config
|
|
98
|
+
const { getCliBaseArgs } = await import('@synergenius/flow-weaver/agent');
|
|
82
99
|
const args = [
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
100
|
+
...getCliBaseArgs({
|
|
101
|
+
model,
|
|
102
|
+
systemPrompt,
|
|
103
|
+
outputFormat: 'stream-json',
|
|
104
|
+
includePartialMessages: true,
|
|
105
|
+
jsonSchema: PLAN_JSON_SCHEMA,
|
|
106
|
+
}),
|
|
107
|
+
'--verbose',
|
|
88
108
|
];
|
|
89
|
-
if (model) args.push('--model', model);
|
|
90
|
-
if (systemPrompt) args.push('--system-prompt', systemPrompt);
|
|
91
109
|
|
|
92
110
|
const bin = cliBinPath || 'claude';
|
|
93
111
|
const child = spawn(bin, args, {
|
|
@@ -130,28 +148,62 @@ export async function callCliAsync(provider: string, prompt: string, model?: str
|
|
|
130
148
|
inThinkingBlock = true;
|
|
131
149
|
process.stderr.write('\x1b[90m thinking...\x1b[0m');
|
|
132
150
|
}
|
|
133
|
-
// Incremental thinking —
|
|
151
|
+
// Incremental thinking — emit to stream
|
|
134
152
|
else if (event.type === 'content_block_delta' && event.delta?.type === 'thinking_delta' && event.delta?.thinking) {
|
|
135
|
-
|
|
153
|
+
emitStreamEvent?.({ type: 'thinking_delta', timestamp: Date.now(), data: { text: event.delta.thinking } });
|
|
154
|
+
}
|
|
155
|
+
// Thinking block end
|
|
156
|
+
else if (event.type === 'content_block_stop' && inThinkingBlock) {
|
|
157
|
+
process.stderr.write('\n');
|
|
158
|
+
inThinkingBlock = false;
|
|
159
|
+
emitStreamEvent?.({ type: 'thinking_done', timestamp: Date.now() });
|
|
136
160
|
}
|
|
137
161
|
// Text block start
|
|
138
162
|
else if (event.type === 'content_block_start' && event.content_block?.type === 'text') {
|
|
139
163
|
if (inThinkingBlock) {
|
|
140
164
|
process.stderr.write('\n');
|
|
141
165
|
inThinkingBlock = false;
|
|
166
|
+
emitStreamEvent?.({ type: 'thinking_done', timestamp: Date.now() });
|
|
142
167
|
}
|
|
143
168
|
}
|
|
144
|
-
//
|
|
169
|
+
// Tool use block start
|
|
170
|
+
else if (event.type === 'content_block_start' && event.content_block?.type === 'tool_use') {
|
|
171
|
+
emitStreamEvent?.({ type: 'tool_use_start', timestamp: Date.now(), data: {
|
|
172
|
+
id: event.content_block.id, name: event.content_block.name,
|
|
173
|
+
}});
|
|
174
|
+
}
|
|
175
|
+
// Incremental text — stream to stderr + emit to stream
|
|
145
176
|
else if (event.type === 'content_block_delta' && event.delta?.type === 'text_delta' && event.delta?.text) {
|
|
146
177
|
hasStreamedText = true;
|
|
147
178
|
process.stderr.write(`\x1b[36m${event.delta.text}\x1b[0m`);
|
|
179
|
+
emitStreamEvent?.({ type: 'text_delta', timestamp: Date.now(), data: { text: event.delta.text } });
|
|
180
|
+
}
|
|
181
|
+
// Tool result — from partial messages showing tool execution results
|
|
182
|
+
else if (event.type === 'content_block_start' && event.content_block?.type === 'tool_result') {
|
|
183
|
+
emitStreamEvent?.({ type: 'tool_result', timestamp: Date.now(), data: {
|
|
184
|
+
id: event.content_block.tool_use_id,
|
|
185
|
+
result: typeof event.content_block.content === 'string'
|
|
186
|
+
? event.content_block.content
|
|
187
|
+
: JSON.stringify(event.content_block.content ?? ''),
|
|
188
|
+
isError: event.content_block.is_error ?? false,
|
|
189
|
+
}});
|
|
190
|
+
}
|
|
191
|
+
// Subagent tool events — --include-partial-messages may emit these
|
|
192
|
+
else if (event.type === 'tool_use' || event.type === 'tool_start') {
|
|
193
|
+
emitStreamEvent?.({ type: 'tool_use_start', timestamp: Date.now(), data: {
|
|
194
|
+
id: event.id ?? event.tool_use_id, name: event.name ?? event.tool_name ?? 'tool',
|
|
195
|
+
}});
|
|
196
|
+
}
|
|
197
|
+
else if (event.type === 'tool_result') {
|
|
198
|
+
emitStreamEvent?.({ type: 'tool_result', timestamp: Date.now(), data: {
|
|
199
|
+
id: event.tool_use_id ?? event.id,
|
|
200
|
+
result: typeof event.content === 'string' ? event.content : JSON.stringify(event.content ?? ''),
|
|
201
|
+
isError: event.is_error ?? false,
|
|
202
|
+
}});
|
|
148
203
|
}
|
|
149
204
|
// Block boundary — newline
|
|
150
205
|
else if (event.type === 'content_block_stop') {
|
|
151
|
-
|
|
152
|
-
process.stderr.write('\n');
|
|
153
|
-
inThinkingBlock = false;
|
|
154
|
-
}
|
|
206
|
+
// handled above for thinking blocks
|
|
155
207
|
}
|
|
156
208
|
// Result event — contains structured_output with --json-schema
|
|
157
209
|
else if (event.type === 'result') {
|
|
@@ -398,6 +450,7 @@ export async function callAI(
|
|
|
398
450
|
systemPrompt: string,
|
|
399
451
|
userPrompt: string,
|
|
400
452
|
defaultMaxTokens = 4096,
|
|
453
|
+
onStreamEvent?: StreamEventCallback,
|
|
401
454
|
): Promise<string> {
|
|
402
455
|
if (pInfo.type === 'platform') {
|
|
403
456
|
return callPlatform(systemPrompt, userPrompt, pInfo.model, pInfo.maxTokens ?? defaultMaxTokens);
|
|
@@ -414,7 +467,7 @@ export async function callAI(
|
|
|
414
467
|
// Async-only path: uses spawn() so Ctrl+C can kill child processes.
|
|
415
468
|
// Also uses --output-format json + --json-schema to enforce JSON output
|
|
416
469
|
// (eliminates hallucination entirely — no retry needed).
|
|
417
|
-
return callCliAsync(pInfo.type, userPrompt, pInfo.model, systemPrompt, pInfo.cliBinPath);
|
|
470
|
+
return callCliAsync(pInfo.type, userPrompt, pInfo.model, systemPrompt, pInfo.cliBinPath, onStreamEvent);
|
|
418
471
|
}
|
|
419
472
|
|
|
420
473
|
/**
|
|
@@ -456,7 +509,10 @@ export async function callAIWithMessages(
|
|
|
456
509
|
const systemMsg = messages.find(m => m.role === 'system');
|
|
457
510
|
const userMsg = [...messages].reverse().find(m => m.role === 'user');
|
|
458
511
|
const systemPrompt = systemMsg?.content ?? '';
|
|
459
|
-
const
|
|
512
|
+
const rawContent = userMsg?.content;
|
|
513
|
+
const userPrompt = typeof rawContent === 'string' ? rawContent
|
|
514
|
+
: rawContent != null ? JSON.stringify(rawContent)
|
|
515
|
+
: '';
|
|
460
516
|
return callAIWithTools(pInfo, systemPrompt, userPrompt, tools, defaultMaxTokens);
|
|
461
517
|
}
|
|
462
518
|
|
|
@@ -115,13 +115,13 @@ export function createAssistantExecutor(projectDir: string, steeringEngine?: imp
|
|
|
115
115
|
}
|
|
116
116
|
case 'queue_retry': {
|
|
117
117
|
const store = mgr.getTaskStore(String(args.bot));
|
|
118
|
-
const tasks = await store.list({ status: 'cancelled' });
|
|
118
|
+
const tasks = await store.list({ status: ['cancelled', 'done'] });
|
|
119
119
|
let count = 0;
|
|
120
120
|
for (const t of tasks) {
|
|
121
|
-
await store.
|
|
121
|
+
await store.retry(t.id);
|
|
122
122
|
count++;
|
|
123
123
|
}
|
|
124
|
-
return { result: `Reset ${count}
|
|
124
|
+
return { result: `Reset ${count} terminal task(s) to open.`, isError: false };
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
// Flow-weaver tools
|
package/src/bot/audit-logger.ts
CHANGED
|
@@ -1,46 +1,74 @@
|
|
|
1
1
|
import type { AuditEventType, AuditEventCallback } from './types.js';
|
|
2
2
|
import { AuditStore } from './audit-store.js';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// State stored on globalThis to survive dual module instances.
|
|
6
|
+
//
|
|
7
|
+
// When the pack is symlinked (benchmarks, dev), Node.js may load
|
|
8
|
+
// audit-logger.js from two different paths, creating separate module
|
|
9
|
+
// instances with separate module-level variables. The runner calls
|
|
10
|
+
// initAuditLogger on instance A, but plan-task calls auditEmit on
|
|
11
|
+
// instance B — which has currentRunId=null, silently dropping events.
|
|
12
|
+
//
|
|
13
|
+
// Using globalThis ensures both instances share the same state.
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
const GLOBAL_KEY = '__fw_audit_logger__';
|
|
17
|
+
|
|
18
|
+
interface AuditState {
|
|
19
|
+
store: AuditStore | null;
|
|
20
|
+
currentRunId: string | null;
|
|
21
|
+
onEvent: AuditEventCallback | undefined;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getState(): AuditState {
|
|
25
|
+
let state = (globalThis as Record<string, unknown>)[GLOBAL_KEY] as AuditState | undefined;
|
|
26
|
+
if (!state) {
|
|
27
|
+
state = { store: null, currentRunId: null, onEvent: undefined };
|
|
28
|
+
(globalThis as Record<string, unknown>)[GLOBAL_KEY] = state;
|
|
29
|
+
}
|
|
30
|
+
return state;
|
|
31
|
+
}
|
|
7
32
|
|
|
8
33
|
export function initAuditLogger(runId: string, callback?: AuditEventCallback): void {
|
|
34
|
+
const state = getState();
|
|
9
35
|
try {
|
|
10
|
-
store = new AuditStore();
|
|
36
|
+
state.store = new AuditStore();
|
|
11
37
|
} catch (err) {
|
|
12
38
|
if (process.env.WEAVER_VERBOSE) process.stderr.write(`[weaver] audit store init failed: ${err}\n`);
|
|
13
|
-
store = null;
|
|
39
|
+
state.store = null;
|
|
14
40
|
}
|
|
15
|
-
currentRunId = runId;
|
|
16
|
-
onEvent = callback;
|
|
41
|
+
state.currentRunId = runId;
|
|
42
|
+
state.onEvent = callback;
|
|
17
43
|
}
|
|
18
44
|
|
|
19
45
|
export function auditEmit(type: AuditEventType, data?: Record<string, unknown>): void {
|
|
20
|
-
|
|
46
|
+
const state = getState();
|
|
47
|
+
if (!state.currentRunId) return;
|
|
21
48
|
|
|
22
49
|
const event = {
|
|
23
50
|
type,
|
|
24
51
|
timestamp: new Date().toISOString(),
|
|
25
|
-
runId: currentRunId,
|
|
52
|
+
runId: state.currentRunId,
|
|
26
53
|
data,
|
|
27
54
|
};
|
|
28
55
|
|
|
29
56
|
try {
|
|
30
|
-
store?.emit(event);
|
|
57
|
+
state.store?.emit(event);
|
|
31
58
|
} catch (err) {
|
|
32
59
|
if (process.env.WEAVER_VERBOSE) process.stderr.write(`[weaver] audit emit failed: ${err}\n`);
|
|
33
60
|
}
|
|
34
61
|
|
|
35
62
|
try {
|
|
36
|
-
onEvent?.(event);
|
|
63
|
+
state.onEvent?.(event);
|
|
37
64
|
} catch (err) {
|
|
38
65
|
if (process.env.WEAVER_VERBOSE) process.stderr.write(`[weaver] audit callback failed: ${err}\n`);
|
|
39
66
|
}
|
|
40
67
|
}
|
|
41
68
|
|
|
42
69
|
export function teardownAuditLogger(): void {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
70
|
+
const state = getState();
|
|
71
|
+
state.store = null;
|
|
72
|
+
state.currentRunId = null;
|
|
73
|
+
state.onEvent = undefined;
|
|
46
74
|
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit Trail Assembler — reads .weaver/ event files and produces
|
|
3
|
+
* a single structured object: the complete story of a swarm session.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* import { assembleAuditTrail } from './audit-trail.js';
|
|
7
|
+
* const trail = assembleAuditTrail('/path/to/project');
|
|
8
|
+
* fs.writeFileSync('.weaver/audit-trail.json', JSON.stringify(trail, null, 2));
|
|
9
|
+
*
|
|
10
|
+
* The trail includes:
|
|
11
|
+
* - Session metadata (provider, budgets, pack version)
|
|
12
|
+
* - All tasks with final state and acceptance results
|
|
13
|
+
* - Per-run event logs (full prompts, full responses, tool calls)
|
|
14
|
+
* - All swarm events (decisions, claims, releases)
|
|
15
|
+
* - Everything merged and sorted by timestamp
|
|
16
|
+
*
|
|
17
|
+
* Streaming noise (thinking_delta, text_delta) is filtered out —
|
|
18
|
+
* the full thinking/content is already in the audit:ai-response event.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import * as fs from 'node:fs';
|
|
22
|
+
import * as path from 'node:path';
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Types
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
export interface AuditEvent {
|
|
29
|
+
type: string;
|
|
30
|
+
timestamp: number;
|
|
31
|
+
data: Record<string, unknown>;
|
|
32
|
+
source?: string; // 'swarm' | runId
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface AuditRun {
|
|
36
|
+
runId: string;
|
|
37
|
+
events: AuditEvent[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface AuditTask {
|
|
41
|
+
id: string;
|
|
42
|
+
title: string;
|
|
43
|
+
description: string;
|
|
44
|
+
status: string;
|
|
45
|
+
assignedProfile?: string;
|
|
46
|
+
dependsOn: string[];
|
|
47
|
+
tokensUsed: number;
|
|
48
|
+
costUsed: number;
|
|
49
|
+
runs: number;
|
|
50
|
+
acceptance?: {
|
|
51
|
+
met: boolean;
|
|
52
|
+
results: Array<{ name: string; pass: boolean; detail?: string }>;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface AuditSession {
|
|
57
|
+
packVersion?: string;
|
|
58
|
+
status?: string;
|
|
59
|
+
maxConcurrent?: number;
|
|
60
|
+
totalCost?: number;
|
|
61
|
+
totalTokensUsed?: number;
|
|
62
|
+
tasksCompleted?: number;
|
|
63
|
+
budgets?: Record<string, unknown>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface AuditTrail {
|
|
67
|
+
assembledAt: string;
|
|
68
|
+
projectDir: string;
|
|
69
|
+
session: AuditSession;
|
|
70
|
+
tasks: AuditTask[];
|
|
71
|
+
runs: AuditRun[];
|
|
72
|
+
events: AuditEvent[]; // all events merged and sorted by timestamp
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Streaming noise — these are redundant because full content is
|
|
76
|
+
// captured in audit:ai-response. Filtering them keeps the trail readable.
|
|
77
|
+
const NOISE_TYPES = new Set([
|
|
78
|
+
'thinking_delta',
|
|
79
|
+
'text_delta',
|
|
80
|
+
'thinking_done',
|
|
81
|
+
'cost-update', // per-second cost updates — final cost is in swarm/task state
|
|
82
|
+
]);
|
|
83
|
+
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// Assembler
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
export function assembleAuditTrail(projectDir: string): AuditTrail {
|
|
89
|
+
const weaverDir = path.join(projectDir, '.weaver');
|
|
90
|
+
|
|
91
|
+
if (!fs.existsSync(weaverDir)) {
|
|
92
|
+
return emptyTrail(projectDir);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 1. Read session metadata
|
|
96
|
+
const session = readSession(weaverDir);
|
|
97
|
+
|
|
98
|
+
// 2. Read tasks
|
|
99
|
+
const tasks = readTasks(weaverDir);
|
|
100
|
+
|
|
101
|
+
// 3. Read swarm events
|
|
102
|
+
const swarmEvents = readNdjsonSafe(path.join(weaverDir, 'swarm-events.ndjson'))
|
|
103
|
+
.map(e => ({ ...e, source: 'swarm' } as AuditEvent));
|
|
104
|
+
|
|
105
|
+
// 4. Read per-run event files
|
|
106
|
+
const runs: AuditRun[] = [];
|
|
107
|
+
const runEvents: AuditEvent[] = [];
|
|
108
|
+
|
|
109
|
+
const files = fs.readdirSync(weaverDir).filter(f => f.startsWith('events-') && f.endsWith('.ndjson'));
|
|
110
|
+
for (const file of files.sort()) {
|
|
111
|
+
const runId = file.replace('events-', '').replace('.ndjson', '');
|
|
112
|
+
const raw = readNdjsonSafe(path.join(weaverDir, file));
|
|
113
|
+
const filtered = raw.filter(e => !NOISE_TYPES.has(e.type));
|
|
114
|
+
const events = filtered.map(e => ({ ...e, source: runId } as AuditEvent));
|
|
115
|
+
runs.push({ runId, events });
|
|
116
|
+
runEvents.push(...events);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 5. Merge all events sorted by timestamp
|
|
120
|
+
const allEvents = [...swarmEvents, ...runEvents].sort((a, b) => a.timestamp - b.timestamp);
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
assembledAt: new Date().toISOString(),
|
|
124
|
+
projectDir,
|
|
125
|
+
session,
|
|
126
|
+
tasks,
|
|
127
|
+
runs,
|
|
128
|
+
events: allEvents,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ---------------------------------------------------------------------------
|
|
133
|
+
// Helpers
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
|
|
136
|
+
function emptyTrail(projectDir: string): AuditTrail {
|
|
137
|
+
return {
|
|
138
|
+
assembledAt: new Date().toISOString(),
|
|
139
|
+
projectDir,
|
|
140
|
+
session: {},
|
|
141
|
+
tasks: [],
|
|
142
|
+
runs: [],
|
|
143
|
+
events: [],
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function readSession(weaverDir: string): AuditSession {
|
|
148
|
+
const swarmPath = path.join(weaverDir, 'swarm.json');
|
|
149
|
+
if (!fs.existsSync(swarmPath)) return {};
|
|
150
|
+
try {
|
|
151
|
+
const raw = JSON.parse(fs.readFileSync(swarmPath, 'utf-8'));
|
|
152
|
+
return {
|
|
153
|
+
packVersion: raw.packVersion,
|
|
154
|
+
status: raw.status,
|
|
155
|
+
maxConcurrent: raw.maxConcurrent,
|
|
156
|
+
totalCost: raw.totalCost,
|
|
157
|
+
totalTokensUsed: raw.totalTokensUsed,
|
|
158
|
+
tasksCompleted: raw.tasksCompleted,
|
|
159
|
+
budgets: raw.budgets,
|
|
160
|
+
};
|
|
161
|
+
} catch {
|
|
162
|
+
return {};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function readTasks(weaverDir: string): AuditTask[] {
|
|
167
|
+
const tasksPath = path.join(weaverDir, 'tasks.json');
|
|
168
|
+
if (!fs.existsSync(tasksPath)) return [];
|
|
169
|
+
try {
|
|
170
|
+
const raw = JSON.parse(fs.readFileSync(tasksPath, 'utf-8'));
|
|
171
|
+
if (!Array.isArray(raw)) return [];
|
|
172
|
+
return raw.map((t: Record<string, unknown>) => ({
|
|
173
|
+
id: t.id as string,
|
|
174
|
+
title: t.title as string,
|
|
175
|
+
description: (t.description as string) ?? '',
|
|
176
|
+
status: t.status as string,
|
|
177
|
+
assignedProfile: t.assignedProfile as string | undefined,
|
|
178
|
+
dependsOn: (t.dependsOn as string[]) ?? [],
|
|
179
|
+
tokensUsed: (t.tokensUsed as number) ?? 0,
|
|
180
|
+
costUsed: (t.costUsed as number) ?? 0,
|
|
181
|
+
runs: ((t.context as Record<string, unknown>)?.runHistory as unknown[])?.length ?? 0,
|
|
182
|
+
acceptance: t.lastAcceptanceCheck as AuditTask['acceptance'],
|
|
183
|
+
}));
|
|
184
|
+
} catch {
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function readNdjsonSafe(filePath: string): AuditEvent[] {
|
|
190
|
+
if (!fs.existsSync(filePath)) return [];
|
|
191
|
+
try {
|
|
192
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
193
|
+
const events: AuditEvent[] = [];
|
|
194
|
+
for (const line of content.split('\n')) {
|
|
195
|
+
if (!line.trim()) continue;
|
|
196
|
+
try {
|
|
197
|
+
const parsed = JSON.parse(line);
|
|
198
|
+
events.push({
|
|
199
|
+
type: parsed.type ?? 'unknown',
|
|
200
|
+
timestamp: parsed.timestamp ?? 0,
|
|
201
|
+
data: parsed.data ?? parsed,
|
|
202
|
+
});
|
|
203
|
+
} catch {
|
|
204
|
+
// Skip malformed lines
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return events;
|
|
208
|
+
} catch {
|
|
209
|
+
return [];
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -224,6 +224,7 @@ export function buildDefaultBehavior(
|
|
|
224
224
|
export function adjustBehaviorForComplexity(
|
|
225
225
|
behavior: ProfileBehavior,
|
|
226
226
|
complexity: 'trivial' | 'simple' | 'moderate' | 'complex' | undefined,
|
|
227
|
+
profileRole?: string,
|
|
227
228
|
): ProfileBehavior {
|
|
228
229
|
if (!complexity || complexity === 'moderate') {
|
|
229
230
|
return behavior;
|
|
@@ -233,8 +234,12 @@ export function adjustBehaviorForComplexity(
|
|
|
233
234
|
const adjusted: ProfileBehavior = JSON.parse(JSON.stringify(behavior));
|
|
234
235
|
|
|
235
236
|
if (complexity === 'complex') {
|
|
236
|
-
// Complex: upgrade to powerful tier for plan phase (the critical thinking step)
|
|
237
|
-
|
|
237
|
+
// Complex: upgrade to powerful tier for plan phase (the critical thinking step).
|
|
238
|
+
// Exception: orchestrator profile caps at standard — decomposition is
|
|
239
|
+
// pattern matching (break into subtasks), not deep reasoning. Using Opus
|
|
240
|
+
// for orchestration burns 30%+ of budget on a single decomposition call.
|
|
241
|
+
const planTier = profileRole === 'orchestrator' ? 'standard' : 'powerful';
|
|
242
|
+
if (adjusted.phases['plan']?.tier) adjusted.phases['plan'].tier = planTier as ModelTier;
|
|
238
243
|
} else if (complexity === 'trivial') {
|
|
239
244
|
// Trivial: fast tier everywhere, skip review, 1 attempt, no evidence
|
|
240
245
|
for (const phase of Object.values(adjusted.phases)) {
|