@deepstrike/wasm 0.2.6 → 0.2.8
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/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/providers/anthropic.d.ts +2 -1
- package/dist/providers/anthropic.js +45 -0
- package/dist/runtime/kernel-event-log.js +20 -0
- package/dist/runtime/kernel-step.d.ts +10 -0
- package/dist/runtime/provider-replay.d.ts +7 -1
- package/dist/runtime/provider-replay.js +29 -4
- package/dist/runtime/runner.d.ts +21 -1
- package/dist/runtime/runner.js +64 -2
- package/dist/runtime/session-log.d.ts +22 -0
- package/dist/runtime/session-repair.d.ts +23 -2
- package/dist/runtime/session-repair.js +30 -31
- package/dist/runtime/types/agent.d.ts +44 -0
- package/dist/runtime/types/agent.js +108 -0
- package/dist/types.d.ts +23 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,8 @@ export type { NativeOsProfile, OsProfileId, MemoryPolicy, MemoryWriteRateLimit,
|
|
|
3
3
|
export { FilteredExecutionPlane } from "./runtime/filtered-plane.js";
|
|
4
4
|
export { SubAgentOrchestrator, defaultSubAgentOrchestrator, spawnStandalone } from "./runtime/sub-agent-orchestrator.js";
|
|
5
5
|
export type { SubAgentRunContext } from "./runtime/sub-agent-orchestrator.js";
|
|
6
|
-
export type { AgentCapabilityFilter, AgentIdentity, AgentIsolation, AgentRunSpec, AgentProcessChangedObservation, ContextInheritance, KernelAgentRole, LoopResult, MilestoneCheckResult, MilestoneContract, MilestonePhase, MilestonePolicy, SubAgentResult, TerminationReason, } from "./runtime/types/agent.js";
|
|
6
|
+
export type { AgentCapabilityFilter, AgentIdentity, AgentIsolation, AgentRunSpec, AgentProcessChangedObservation, ContextInheritance, KernelAgentRole, LoopResult, MilestoneCheckResult, MilestoneContract, MilestonePhase, MilestonePolicy, SubAgentResult, TerminationReason, WorkflowSpec, WorkflowNodeSpec, WorkflowTaskSpec, WorkflowSpawnInfo, } from "./runtime/types/agent.js";
|
|
7
|
+
export { workflowSpecToKernel, fanoutSynthesize, generateAndFilter, verifyRules } from "./runtime/types/agent.js";
|
|
7
8
|
export { Governance } from "./governance.js";
|
|
8
9
|
export type { GovernanceVerdict } from "./governance.js";
|
|
9
10
|
export { AnthropicProvider } from "./providers/anthropic.js";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { RuntimeRunner, collectText, InMemorySessionLog, LocalExecutionPlane, DEFAULT_NATIVE_ATTENTION_POLICY, DEFAULT_NATIVE_GOVERNANCE_POLICY, DEFAULT_SANDBOX_POLICY, assertNativeProfile, osProfile, validateDeclarativePolicy, } from "./runtime/index.js";
|
|
2
2
|
export { FilteredExecutionPlane } from "./runtime/filtered-plane.js";
|
|
3
3
|
export { SubAgentOrchestrator, defaultSubAgentOrchestrator, spawnStandalone } from "./runtime/sub-agent-orchestrator.js";
|
|
4
|
+
export { workflowSpecToKernel, fanoutSynthesize, generateAndFilter, verifyRules } from "./runtime/types/agent.js";
|
|
4
5
|
export { Governance } from "./governance.js";
|
|
5
6
|
export { AnthropicProvider } from "./providers/anthropic.js";
|
|
6
7
|
export { OpenAIProvider, QwenProvider, DeepSeekProvider, MiniMaxProvider, KimiProvider } from "./providers/openai.js";
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import type { RenderedContext, ToolSchema, StreamEvent, LLMProvider, Message, ProviderReplay } from "../types.js";
|
|
1
|
+
import type { RenderedContext, ToolSchema, StreamEvent, LLMProvider, Message, ProviderDescriptor, ProviderReplay } from "../types.js";
|
|
2
2
|
export declare class AnthropicProvider implements LLMProvider {
|
|
3
3
|
private readonly apiKey;
|
|
4
4
|
private readonly model;
|
|
5
5
|
private readonly maxTokens;
|
|
6
6
|
private nativeAssistantBlocks;
|
|
7
7
|
constructor(apiKey: string, model?: string, maxTokens?: number);
|
|
8
|
+
descriptor(): ProviderDescriptor;
|
|
8
9
|
peekProviderReplay(message: Pick<Message, "content" | "toolCalls">): ProviderReplay | undefined;
|
|
9
10
|
seedProviderReplay(message: Pick<Message, "content" | "toolCalls">, replay: ProviderReplay): void;
|
|
10
11
|
complete(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): Promise<Message>;
|
|
@@ -17,6 +17,22 @@ export class AnthropicProvider {
|
|
|
17
17
|
this.model = model;
|
|
18
18
|
this.maxTokens = maxTokens;
|
|
19
19
|
}
|
|
20
|
+
descriptor() {
|
|
21
|
+
return {
|
|
22
|
+
provider: "anthropic",
|
|
23
|
+
protocol: "anthropic-messages",
|
|
24
|
+
model: this.model,
|
|
25
|
+
reasoning: {
|
|
26
|
+
supported: true,
|
|
27
|
+
preserveAcrossToolTurns: true,
|
|
28
|
+
requiresReplayForToolTurns: true,
|
|
29
|
+
},
|
|
30
|
+
toolCalls: {
|
|
31
|
+
supported: true,
|
|
32
|
+
requiresStrictPairing: true,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
20
36
|
peekProviderReplay(message) {
|
|
21
37
|
const blocks = this.nativeAssistantBlocks.get(assistantReplayKey(message));
|
|
22
38
|
return blocks?.length ? { native_blocks: blocks } : undefined;
|
|
@@ -24,7 +40,13 @@ export class AnthropicProvider {
|
|
|
24
40
|
seedProviderReplay(message, replay) {
|
|
25
41
|
if (replay.native_blocks?.length) {
|
|
26
42
|
this.nativeAssistantBlocks.set(assistantReplayKey(message), replay.native_blocks);
|
|
43
|
+
return;
|
|
27
44
|
}
|
|
45
|
+
// Legacy log without persisted native blocks: reconstruct neutral
|
|
46
|
+
// text + tool_use blocks so a tool-use turn can be replayed.
|
|
47
|
+
const blocks = reconstructAnthropicBlocks(message);
|
|
48
|
+
if (blocks.length)
|
|
49
|
+
this.nativeAssistantBlocks.set(assistantReplayKey(message), blocks);
|
|
28
50
|
}
|
|
29
51
|
async complete(context, tools, extensions) {
|
|
30
52
|
return collectStreamMessage(this.stream(context, tools, extensions));
|
|
@@ -154,3 +176,26 @@ export class AnthropicProvider {
|
|
|
154
176
|
this.nativeAssistantBlocks.set(assistantReplayKey(message), blocks);
|
|
155
177
|
}
|
|
156
178
|
}
|
|
179
|
+
/**
|
|
180
|
+
* Reconstruct Anthropic assistant content blocks from a neutral transcript when
|
|
181
|
+
* no provider replay was persisted. Only meaningful for tool-use turns.
|
|
182
|
+
*/
|
|
183
|
+
function reconstructAnthropicBlocks(message) {
|
|
184
|
+
const toolCalls = message.toolCalls ?? [];
|
|
185
|
+
if (!toolCalls.length)
|
|
186
|
+
return [];
|
|
187
|
+
const blocks = [];
|
|
188
|
+
if (message.content)
|
|
189
|
+
blocks.push({ type: "text", text: message.content });
|
|
190
|
+
for (const tc of toolCalls) {
|
|
191
|
+
let input = {};
|
|
192
|
+
try {
|
|
193
|
+
input = JSON.parse(tc.arguments || "{}");
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
input = {};
|
|
197
|
+
}
|
|
198
|
+
blocks.push({ type: "tool_use", id: tc.id, name: tc.name, input });
|
|
199
|
+
}
|
|
200
|
+
return blocks;
|
|
201
|
+
}
|
|
@@ -198,6 +198,26 @@ export function kernelObservationToSessionEvent(obs, turn, opts = {}) {
|
|
|
198
198
|
memory_id: obs.memory_id ?? "",
|
|
199
199
|
error: obs.error ?? "",
|
|
200
200
|
});
|
|
201
|
+
case "workflow_batch_spawned": {
|
|
202
|
+
const nodes = obs.nodes ?? [];
|
|
203
|
+
return withCategory({
|
|
204
|
+
kind: "workflow_batch_spawned",
|
|
205
|
+
turn: t,
|
|
206
|
+
node_count: nodes.length,
|
|
207
|
+
node_ids: nodes.map((n) => n.agent_id ?? ""),
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
case "workflow_completed": {
|
|
211
|
+
const completed = obs.completed ?? [];
|
|
212
|
+
const failed = obs.failed ?? [];
|
|
213
|
+
return withCategory({
|
|
214
|
+
kind: "workflow_completed",
|
|
215
|
+
turn: t,
|
|
216
|
+
completed,
|
|
217
|
+
failed,
|
|
218
|
+
total_nodes: completed.length + failed.length,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
201
221
|
default:
|
|
202
222
|
return null;
|
|
203
223
|
}
|
|
@@ -93,6 +93,16 @@ export interface KernelObservation {
|
|
|
93
93
|
requires_async_response?: boolean;
|
|
94
94
|
/** memory_validation_failed (Phase 7). */
|
|
95
95
|
error?: string;
|
|
96
|
+
nodes?: Array<{
|
|
97
|
+
agent_id: string;
|
|
98
|
+
goal: string;
|
|
99
|
+
role: string;
|
|
100
|
+
isolation: string;
|
|
101
|
+
context_inheritance: string;
|
|
102
|
+
model_hint?: string;
|
|
103
|
+
}>;
|
|
104
|
+
completed?: string[];
|
|
105
|
+
failed?: string[];
|
|
96
106
|
}
|
|
97
107
|
export declare function toolSchemaToKernel(schema: ToolSchema): Record<string, unknown>;
|
|
98
108
|
export declare function skillMetadataToKernel(skill: SkillMetadata): Record<string, unknown>;
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import type { LLMProvider, Message, ProviderReplay, ToolCall } from "../types.js";
|
|
1
|
+
import type { LLMProvider, Message, ProviderDescriptor, ProviderReplay, ToolCall } from "../types.js";
|
|
2
2
|
import type { SessionEvent } from "./session-log.js";
|
|
3
3
|
export declare function assistantReplayKey(message: Pick<Message, "content" | "toolCalls">): string;
|
|
4
|
+
/**
|
|
5
|
+
* A stored replay may only be seeded into a provider speaking the same wire
|
|
6
|
+
* protocol; on a cross-protocol fallback the incompatible envelope is skipped so
|
|
7
|
+
* the new provider re-serializes neutral context instead.
|
|
8
|
+
*/
|
|
9
|
+
export declare function isReplayCompatibleWithProvider(replay: ProviderReplay, descriptor: ProviderDescriptor | undefined): boolean;
|
|
4
10
|
export declare function seedProviderReplayFromEvents(provider: LLMProvider, events: Array<{
|
|
5
11
|
event: SessionEvent;
|
|
6
12
|
}>): void;
|
|
@@ -1,21 +1,46 @@
|
|
|
1
|
-
import { effectiveProviderReplay } from "./session-repair.js";
|
|
2
1
|
export function assistantReplayKey(message) {
|
|
3
2
|
return JSON.stringify({
|
|
4
3
|
content: message.content,
|
|
5
4
|
toolCalls: message.toolCalls ?? [],
|
|
6
5
|
});
|
|
7
6
|
}
|
|
7
|
+
/** Infer the wire protocol a stored replay envelope belongs to (explicit, or by shape for legacy logs). */
|
|
8
|
+
function replayProtocol(replay) {
|
|
9
|
+
if (replay.protocol)
|
|
10
|
+
return replay.protocol;
|
|
11
|
+
if (replay.native_blocks?.length)
|
|
12
|
+
return "anthropic-messages";
|
|
13
|
+
if (replay.reasoning_content != null || replay.reasoning_details !== undefined)
|
|
14
|
+
return "openai-chat";
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A stored replay may only be seeded into a provider speaking the same wire
|
|
19
|
+
* protocol; on a cross-protocol fallback the incompatible envelope is skipped so
|
|
20
|
+
* the new provider re-serializes neutral context instead.
|
|
21
|
+
*/
|
|
22
|
+
export function isReplayCompatibleWithProvider(replay, descriptor) {
|
|
23
|
+
if (!descriptor)
|
|
24
|
+
return true;
|
|
25
|
+
const protocol = replayProtocol(replay);
|
|
26
|
+
if (!protocol)
|
|
27
|
+
return true;
|
|
28
|
+
return protocol === descriptor.protocol;
|
|
29
|
+
}
|
|
8
30
|
export function seedProviderReplayFromEvents(provider, events) {
|
|
9
31
|
if (!provider.seedProviderReplay)
|
|
10
32
|
return;
|
|
33
|
+
const descriptor = provider.descriptor?.();
|
|
11
34
|
for (const { event } of events) {
|
|
12
35
|
if (event.kind !== "llm_completed")
|
|
13
36
|
continue;
|
|
14
37
|
const toolCalls = event.tool_calls ?? [];
|
|
15
|
-
const
|
|
16
|
-
if (!
|
|
38
|
+
const stored = event.provider_replay;
|
|
39
|
+
if (stored && !isReplayCompatibleWithProvider(stored, descriptor))
|
|
17
40
|
continue;
|
|
18
|
-
|
|
41
|
+
// Pass the message even with no persisted replay: a provider may reconstruct
|
|
42
|
+
// a legacy replay (e.g. Anthropic native_blocks) from the neutral transcript.
|
|
43
|
+
provider.seedProviderReplay({ content: event.content, toolCalls }, stored ?? {});
|
|
19
44
|
}
|
|
20
45
|
}
|
|
21
46
|
export function peekProviderReplay(provider, content, toolCalls) {
|
package/dist/runtime/runner.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { SignalSource } from "../signals/index.js";
|
|
|
6
6
|
import type { SessionLog, SessionEvent } from "./session-log.js";
|
|
7
7
|
import type { ExecutionPlane } from "./execution-plane.js";
|
|
8
8
|
import { type GovernancePolicy } from "../governance.js";
|
|
9
|
-
import type { AgentRunSpec, SubAgentResult, MilestonePolicy, MilestoneContract, MilestoneCheckResult } from "./types/agent.js";
|
|
9
|
+
import type { AgentRunSpec, SubAgentResult, MilestonePolicy, MilestoneContract, MilestoneCheckResult, WorkflowSpec } from "./types/agent.js";
|
|
10
10
|
import { type SubAgentOrchestrator } from "./sub-agent-orchestrator.js";
|
|
11
11
|
import { type NativeOsProfile, type OsProfileId } from "./os-profile.js";
|
|
12
12
|
import { LargeResultSpool } from "./large-result-spool.js";
|
|
@@ -113,6 +113,26 @@ export declare class RuntimeRunner {
|
|
|
113
113
|
dream(agentId: string, nowMs?: number): Promise<DreamResult>;
|
|
114
114
|
private execute;
|
|
115
115
|
spawnSubAgent(spec: AgentRunSpec): Promise<SubAgentResult>;
|
|
116
|
+
/**
|
|
117
|
+
* W0-ABI: run a declarative workflow DAG. The kernel owns the DAG and gates every node spawn
|
|
118
|
+
* through the syscall trap; this driver runs each kernel-emitted batch of nodes in parallel,
|
|
119
|
+
* feeds their results back, and loops until the kernel reports the workflow complete.
|
|
120
|
+
*/
|
|
121
|
+
runWorkflow(spec: WorkflowSpec, opts?: {
|
|
122
|
+
resumedCompleted?: string[];
|
|
123
|
+
}): Promise<{
|
|
124
|
+
completed: string[];
|
|
125
|
+
failed: string[];
|
|
126
|
+
}>;
|
|
127
|
+
/**
|
|
128
|
+
* Resume a workflow from the parent session's completed nodes.
|
|
129
|
+
* Reads the session log, extracts completed workflow node agent_ids, and
|
|
130
|
+
* calls runWorkflow with resumedCompleted so the kernel skips those nodes.
|
|
131
|
+
*/
|
|
132
|
+
resumeWorkflow(spec: WorkflowSpec): Promise<{
|
|
133
|
+
completed: string[];
|
|
134
|
+
failed: string[];
|
|
135
|
+
}>;
|
|
116
136
|
private appendObservations;
|
|
117
137
|
private archiveSemanticPageOut;
|
|
118
138
|
}
|
package/dist/runtime/runner.js
CHANGED
|
@@ -3,9 +3,9 @@ import { governancePolicyToKernelEvent } from "../governance.js";
|
|
|
3
3
|
import { getKernel } from "./kernel.js";
|
|
4
4
|
import { peekProviderReplay, seedProviderReplayFromEvents } from "./provider-replay.js";
|
|
5
5
|
import { sanitizeReplayText } from "./replay-sanitize.js";
|
|
6
|
-
import { buildLlmCompletedEvent, buildRunTerminalEvent, repairEventsForRecovery } from "./session-repair.js";
|
|
6
|
+
import { buildLlmCompletedEvent, buildRunTerminalEvent, buildWorkflowNodeCompletedEvent, recoverCompletedWorkflowNodes, repairEventsForRecovery, } from "./session-repair.js";
|
|
7
7
|
import { forceCompact, kernelAction, kernelApply, kernelMaybeAction, messageToKernelMessage, skillMetadataToKernel, toolResultToKernel, toolSchemaToKernel, } from "./kernel-step.js";
|
|
8
|
-
import { agentRunSpecToKernel, findSpawnProcessObservation, milestoneCheckPass, milestoneCheckResultToKernel, spawnObservationToManifest, subAgentResultToKernel, } from "./types/agent.js";
|
|
8
|
+
import { agentRunSpecToKernel, findSpawnProcessObservation, milestoneCheckPass, milestoneCheckResultToKernel, spawnObservationToManifest, subAgentResultToKernel, workflowNodeToManifest, workflowNodeToSpec, workflowSpecToKernel, } from "./types/agent.js";
|
|
9
9
|
import { defaultSubAgentOrchestrator } from "./sub-agent-orchestrator.js";
|
|
10
10
|
import { kernelObservationToSessionEvent, withCategory } from "./kernel-event-log.js";
|
|
11
11
|
import { assertNativeProfile } from "./os-profile.js";
|
|
@@ -706,6 +706,68 @@ export class RuntimeRunner {
|
|
|
706
706
|
});
|
|
707
707
|
return result;
|
|
708
708
|
}
|
|
709
|
+
/**
|
|
710
|
+
* W0-ABI: run a declarative workflow DAG. The kernel owns the DAG and gates every node spawn
|
|
711
|
+
* through the syscall trap; this driver runs each kernel-emitted batch of nodes in parallel,
|
|
712
|
+
* feeds their results back, and loops until the kernel reports the workflow complete.
|
|
713
|
+
*/
|
|
714
|
+
async runWorkflow(spec, opts) {
|
|
715
|
+
if (!this.activeKernel || !this.currentSessionId) {
|
|
716
|
+
throw new Error("runWorkflow requires an active parent run");
|
|
717
|
+
}
|
|
718
|
+
const parentSessionId = this.currentSessionId;
|
|
719
|
+
const runtime = this.activeKernel;
|
|
720
|
+
const orchestrator = this.opts.subAgentOrchestrator ?? defaultSubAgentOrchestrator;
|
|
721
|
+
let observations = kernelApply(runtime, this.pendingObservations, {
|
|
722
|
+
kind: "load_workflow",
|
|
723
|
+
spec: workflowSpecToKernel(spec),
|
|
724
|
+
parent_session_id: parentSessionId,
|
|
725
|
+
// W0-ABI resume: skip nodes already completed before an interruption.
|
|
726
|
+
...(opts?.resumedCompleted && opts.resumedCompleted.length ? { resumed_completed: opts.resumedCompleted } : {}),
|
|
727
|
+
});
|
|
728
|
+
for (;;) {
|
|
729
|
+
const done = observations.find(o => o.kind === "workflow_completed");
|
|
730
|
+
if (done)
|
|
731
|
+
return { completed: done.completed ?? [], failed: done.failed ?? [] };
|
|
732
|
+
const batch = observations.find(o => o.kind === "workflow_batch_spawned");
|
|
733
|
+
const nodes = batch?.nodes ?? [];
|
|
734
|
+
if (nodes.length === 0)
|
|
735
|
+
return { completed: [], failed: [] };
|
|
736
|
+
const results = await Promise.all(nodes.map(node => orchestrator.run({
|
|
737
|
+
parentOpts: this.opts,
|
|
738
|
+
parentSessionId,
|
|
739
|
+
spec: workflowNodeToSpec(node, parentSessionId),
|
|
740
|
+
manifest: workflowNodeToManifest(node, parentSessionId),
|
|
741
|
+
sessionLog: this.opts.sessionLog,
|
|
742
|
+
})));
|
|
743
|
+
observations = [];
|
|
744
|
+
for (const result of results) {
|
|
745
|
+
observations = kernelApply(runtime, this.pendingObservations, {
|
|
746
|
+
kind: "sub_agent_completed",
|
|
747
|
+
result: subAgentResultToKernel(result),
|
|
748
|
+
});
|
|
749
|
+
// Persist node completion for resume recovery.
|
|
750
|
+
await this.opts.sessionLog.append(parentSessionId, buildWorkflowNodeCompletedEvent({
|
|
751
|
+
turn: runtime.turn(),
|
|
752
|
+
agentId: result.agentId,
|
|
753
|
+
termination: result.result.termination,
|
|
754
|
+
}));
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Resume a workflow from the parent session's completed nodes.
|
|
760
|
+
* Reads the session log, extracts completed workflow node agent_ids, and
|
|
761
|
+
* calls runWorkflow with resumedCompleted so the kernel skips those nodes.
|
|
762
|
+
*/
|
|
763
|
+
async resumeWorkflow(spec) {
|
|
764
|
+
if (!this.currentSessionId) {
|
|
765
|
+
throw new Error("resumeWorkflow requires an active parent run");
|
|
766
|
+
}
|
|
767
|
+
const events = await this.opts.sessionLog.read(this.currentSessionId);
|
|
768
|
+
const resumedCompleted = recoverCompletedWorkflowNodes(events);
|
|
769
|
+
return this.runWorkflow(spec, { resumedCompleted });
|
|
770
|
+
}
|
|
709
771
|
async appendObservations(sessionId, runtime, nextArchiveStart) {
|
|
710
772
|
const turn = runtime.turn();
|
|
711
773
|
const preservedRefs = runtime.preservedRefs();
|
|
@@ -232,6 +232,28 @@ export type SessionEvent = {
|
|
|
232
232
|
primitive?: KernelPrimitive;
|
|
233
233
|
memory_id: string;
|
|
234
234
|
error: string;
|
|
235
|
+
} | {
|
|
236
|
+
kind: "workflow_node_completed";
|
|
237
|
+
turn: number;
|
|
238
|
+
category?: KernelEventCategory;
|
|
239
|
+
primitive?: KernelPrimitive;
|
|
240
|
+
agent_id: string;
|
|
241
|
+
termination: string;
|
|
242
|
+
} | {
|
|
243
|
+
kind: "workflow_batch_spawned";
|
|
244
|
+
turn: number;
|
|
245
|
+
category?: KernelEventCategory;
|
|
246
|
+
primitive?: KernelPrimitive;
|
|
247
|
+
node_count: number;
|
|
248
|
+
node_ids: string[];
|
|
249
|
+
} | {
|
|
250
|
+
kind: "workflow_completed";
|
|
251
|
+
turn: number;
|
|
252
|
+
category?: KernelEventCategory;
|
|
253
|
+
primitive?: KernelPrimitive;
|
|
254
|
+
completed: string[];
|
|
255
|
+
failed: string[];
|
|
256
|
+
total_nodes: number;
|
|
235
257
|
} | {
|
|
236
258
|
kind: "run_terminal";
|
|
237
259
|
reason: string;
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import type { ProviderReplay, ToolCall } from "../types.js";
|
|
2
2
|
import type { SessionEvent } from "./session-log.js";
|
|
3
3
|
export { REPLAY_CONTENT_MAX_BYTES as RECOVERY_CONTENT_MAX_BYTES } from "./replay-sanitize.js";
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Normalize a persisted llm_completed event for recovery. Content is sanitized
|
|
6
|
+
* and token_count backfilled, but the stored `provider_replay` envelope is
|
|
7
|
+
* passed through verbatim — this layer is provider-neutral and never
|
|
8
|
+
* synthesizes protocol-specific replay shapes. Legacy reconstruction is the
|
|
9
|
+
* responsibility of the target provider's `seedProviderReplay`.
|
|
10
|
+
*/
|
|
6
11
|
export declare function normalizeLlmCompleted(event: Extract<SessionEvent, {
|
|
7
12
|
kind: "llm_completed";
|
|
8
13
|
}>, maxBytes?: number): Extract<SessionEvent, {
|
|
@@ -31,3 +36,19 @@ export declare function buildRunTerminalEvent(input: {
|
|
|
31
36
|
}): Extract<SessionEvent, {
|
|
32
37
|
kind: "run_terminal";
|
|
33
38
|
}>;
|
|
39
|
+
export declare function buildWorkflowNodeCompletedEvent(input: {
|
|
40
|
+
turn: number;
|
|
41
|
+
agentId: string;
|
|
42
|
+
termination: string;
|
|
43
|
+
}): Extract<SessionEvent, {
|
|
44
|
+
kind: "workflow_node_completed";
|
|
45
|
+
}>;
|
|
46
|
+
/**
|
|
47
|
+
* Recover completed workflow node agent_ids from a session event stream.
|
|
48
|
+
* Scans for workflow_node_completed events and returns the agent_ids whose
|
|
49
|
+
* termination was "completed". Used to rebuild resumedCompleted for resumeWorkflow.
|
|
50
|
+
*/
|
|
51
|
+
export declare function recoverCompletedWorkflowNodes(events: Array<{
|
|
52
|
+
seq: number;
|
|
53
|
+
event: SessionEvent;
|
|
54
|
+
}>): string[];
|
|
@@ -3,40 +3,17 @@ export { REPLAY_CONTENT_MAX_BYTES as RECOVERY_CONTENT_MAX_BYTES } from "./replay
|
|
|
3
3
|
function estimateTokenCount(text) {
|
|
4
4
|
return Math.max(1, Math.ceil(text.length / 4));
|
|
5
5
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
export function synthesizeProviderReplay(content, toolCalls) {
|
|
15
|
-
if (!toolCalls.length)
|
|
16
|
-
return undefined;
|
|
17
|
-
const blocks = [];
|
|
18
|
-
if (content)
|
|
19
|
-
blocks.push({ type: "text", text: content });
|
|
20
|
-
for (const tc of toolCalls) {
|
|
21
|
-
blocks.push({
|
|
22
|
-
type: "tool_use",
|
|
23
|
-
id: tc.id,
|
|
24
|
-
name: tc.name,
|
|
25
|
-
input: parseToolInput(tc.arguments),
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
return { native_blocks: blocks };
|
|
29
|
-
}
|
|
30
|
-
export function effectiveProviderReplay(content, toolCalls, stored) {
|
|
31
|
-
if (stored?.native_blocks?.length || stored?.reasoning_content != null) {
|
|
32
|
-
return stored;
|
|
33
|
-
}
|
|
34
|
-
return synthesizeProviderReplay(content, toolCalls);
|
|
35
|
-
}
|
|
6
|
+
/**
|
|
7
|
+
* Normalize a persisted llm_completed event for recovery. Content is sanitized
|
|
8
|
+
* and token_count backfilled, but the stored `provider_replay` envelope is
|
|
9
|
+
* passed through verbatim — this layer is provider-neutral and never
|
|
10
|
+
* synthesizes protocol-specific replay shapes. Legacy reconstruction is the
|
|
11
|
+
* responsibility of the target provider's `seedProviderReplay`.
|
|
12
|
+
*/
|
|
36
13
|
export function normalizeLlmCompleted(event, maxBytes) {
|
|
37
14
|
const content = sanitizeReplayText(event.content ?? "", maxBytes);
|
|
38
15
|
const toolCalls = event.tool_calls ?? [];
|
|
39
|
-
const providerReplay =
|
|
16
|
+
const providerReplay = event.provider_replay;
|
|
40
17
|
return {
|
|
41
18
|
kind: "llm_completed",
|
|
42
19
|
turn: event.turn,
|
|
@@ -71,3 +48,25 @@ export function buildRunTerminalEvent(input) {
|
|
|
71
48
|
total_tokens: Math.max(0, input.totalTokens),
|
|
72
49
|
};
|
|
73
50
|
}
|
|
51
|
+
export function buildWorkflowNodeCompletedEvent(input) {
|
|
52
|
+
return {
|
|
53
|
+
kind: "workflow_node_completed",
|
|
54
|
+
turn: input.turn,
|
|
55
|
+
agent_id: input.agentId,
|
|
56
|
+
termination: input.termination,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Recover completed workflow node agent_ids from a session event stream.
|
|
61
|
+
* Scans for workflow_node_completed events and returns the agent_ids whose
|
|
62
|
+
* termination was "completed". Used to rebuild resumedCompleted for resumeWorkflow.
|
|
63
|
+
*/
|
|
64
|
+
export function recoverCompletedWorkflowNodes(events) {
|
|
65
|
+
const completed = [];
|
|
66
|
+
for (const { event } of events) {
|
|
67
|
+
if (event.kind === "workflow_node_completed" && event.termination === "completed") {
|
|
68
|
+
completed.push(event.agent_id);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return completed;
|
|
72
|
+
}
|
|
@@ -75,3 +75,47 @@ export declare function milestoneCheckResultToKernel(result: MilestoneCheckResul
|
|
|
75
75
|
export declare function subAgentResultToKernel(result: SubAgentResult): Record<string, unknown>;
|
|
76
76
|
export declare function milestoneCheckPass(phaseId: string): MilestoneCheckResult;
|
|
77
77
|
export declare function milestoneCheckFail(phaseId: string, reason: string): MilestoneCheckResult;
|
|
78
|
+
/** A task for a workflow node: a full object, or a bare goal string. */
|
|
79
|
+
export type WorkflowTaskSpec = {
|
|
80
|
+
goal: string;
|
|
81
|
+
criteria?: string[];
|
|
82
|
+
lane?: string;
|
|
83
|
+
} | string;
|
|
84
|
+
/** One node in a declarative workflow DAG (camelCase host shape). */
|
|
85
|
+
export interface WorkflowNodeSpec {
|
|
86
|
+
task: WorkflowTaskSpec;
|
|
87
|
+
role: KernelAgentRole;
|
|
88
|
+
isolation?: AgentIsolation;
|
|
89
|
+
contextInheritance?: ContextInheritance;
|
|
90
|
+
modelHint?: string;
|
|
91
|
+
/** Indices of nodes this node depends on. */
|
|
92
|
+
dependsOn?: number[];
|
|
93
|
+
}
|
|
94
|
+
/** A declarative workflow DAG the kernel runs node-by-node, gating each spawn. */
|
|
95
|
+
export interface WorkflowSpec {
|
|
96
|
+
nodes: WorkflowNodeSpec[];
|
|
97
|
+
}
|
|
98
|
+
/** Per-node spawn descriptor carried in the `workflow_batch_spawned` observation. */
|
|
99
|
+
export interface WorkflowSpawnInfo {
|
|
100
|
+
agent_id: string;
|
|
101
|
+
goal: string;
|
|
102
|
+
role: string;
|
|
103
|
+
isolation: string;
|
|
104
|
+
context_inheritance: string;
|
|
105
|
+
model_hint?: string;
|
|
106
|
+
}
|
|
107
|
+
/** Map a host `WorkflowSpec` to the snake_case kernel JSON (`load_workflow.spec`). */
|
|
108
|
+
export declare function workflowSpecToKernel(spec: WorkflowSpec): Record<string, unknown>;
|
|
109
|
+
/** Build a sub-agent run spec for a kernel-generated workflow node. */
|
|
110
|
+
export declare function workflowNodeToSpec(node: WorkflowSpawnInfo, parentSessionId: string): AgentRunSpec;
|
|
111
|
+
/** Build the host manifest for a kernel-generated workflow node. */
|
|
112
|
+
export declare function workflowNodeToManifest(node: WorkflowSpawnInfo, parentSessionId: string): AgentProcessChangedObservation;
|
|
113
|
+
/** N parallel read-only Explore workers feeding a single Plan synthesizer (barrier). */
|
|
114
|
+
export declare function fanoutSynthesize(workers: WorkflowTaskSpec[], synthesize: WorkflowTaskSpec): WorkflowSpec;
|
|
115
|
+
/** N parallel Implement generators feeding a single Verify filter/dedupe step (barrier). */
|
|
116
|
+
export declare function generateAndFilter(generators: WorkflowTaskSpec[], filter: WorkflowTaskSpec): WorkflowSpec;
|
|
117
|
+
/**
|
|
118
|
+
* One fresh-context verifier per rule/claim (parallel) + optional skeptic that depends on all and
|
|
119
|
+
* re-checks flags. Verifiers run read-only with no inherited author context (bias-resistant).
|
|
120
|
+
*/
|
|
121
|
+
export declare function verifyRules(rules: WorkflowTaskSpec[], skeptic?: WorkflowTaskSpec): WorkflowSpec;
|
|
@@ -94,3 +94,111 @@ export function milestoneCheckPass(phaseId) {
|
|
|
94
94
|
export function milestoneCheckFail(phaseId, reason) {
|
|
95
95
|
return { phaseId, passed: false, reason };
|
|
96
96
|
}
|
|
97
|
+
/** Map a host `WorkflowSpec` to the snake_case kernel JSON (`load_workflow.spec`). */
|
|
98
|
+
export function workflowSpecToKernel(spec) {
|
|
99
|
+
return {
|
|
100
|
+
nodes: spec.nodes.map(n => {
|
|
101
|
+
const task = typeof n.task === "string" ? { goal: n.task } : n.task;
|
|
102
|
+
return {
|
|
103
|
+
task: {
|
|
104
|
+
goal: task.goal,
|
|
105
|
+
// `criteria` is required by the kernel's RuntimeTask serde (no default).
|
|
106
|
+
criteria: task.criteria ?? [],
|
|
107
|
+
...(task.lane ? { lane: task.lane } : {}),
|
|
108
|
+
},
|
|
109
|
+
role: n.role,
|
|
110
|
+
isolation: n.isolation ?? "shared",
|
|
111
|
+
context_inheritance: n.contextInheritance ?? "none",
|
|
112
|
+
...(n.modelHint ? { model_hint: n.modelHint } : {}),
|
|
113
|
+
...(n.dependsOn && n.dependsOn.length ? { depends_on: n.dependsOn } : {}),
|
|
114
|
+
};
|
|
115
|
+
}),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/** Build a sub-agent run spec for a kernel-generated workflow node. */
|
|
119
|
+
export function workflowNodeToSpec(node, parentSessionId) {
|
|
120
|
+
return {
|
|
121
|
+
identity: {
|
|
122
|
+
agentId: node.agent_id,
|
|
123
|
+
sessionId: `${parentSessionId}-${node.agent_id}`,
|
|
124
|
+
isSubAgent: true,
|
|
125
|
+
parentSessionId,
|
|
126
|
+
},
|
|
127
|
+
role: node.role,
|
|
128
|
+
isolation: node.isolation,
|
|
129
|
+
goal: node.goal,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/** Build the host manifest for a kernel-generated workflow node. */
|
|
133
|
+
export function workflowNodeToManifest(node, parentSessionId) {
|
|
134
|
+
return {
|
|
135
|
+
kind: "agent_process_changed",
|
|
136
|
+
agent_id: node.agent_id,
|
|
137
|
+
parent_session_id: parentSessionId,
|
|
138
|
+
role: node.role,
|
|
139
|
+
isolation: node.isolation,
|
|
140
|
+
context_inheritance: node.context_inheritance,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
// ─── W1/W2 workflow templates (the six patterns as one-liners) ───
|
|
144
|
+
// Roles carry the kernel's role_defaults isolation/inheritance so host-built specs match the
|
|
145
|
+
// core `orchestration::workflow` constructors (e.g. verifiers stay bias-resistant).
|
|
146
|
+
function asTask(t) {
|
|
147
|
+
return typeof t === "string" ? { goal: t } : t;
|
|
148
|
+
}
|
|
149
|
+
/** N parallel read-only Explore workers feeding a single Plan synthesizer (barrier). */
|
|
150
|
+
export function fanoutSynthesize(workers, synthesize) {
|
|
151
|
+
const nodes = workers.map(t => ({
|
|
152
|
+
task: asTask(t),
|
|
153
|
+
role: "explore",
|
|
154
|
+
isolation: "read_only",
|
|
155
|
+
contextInheritance: "system_only",
|
|
156
|
+
}));
|
|
157
|
+
nodes.push({
|
|
158
|
+
task: asTask(synthesize),
|
|
159
|
+
role: "plan",
|
|
160
|
+
isolation: "shared",
|
|
161
|
+
contextInheritance: "full",
|
|
162
|
+
dependsOn: workers.map((_, i) => i),
|
|
163
|
+
});
|
|
164
|
+
return { nodes };
|
|
165
|
+
}
|
|
166
|
+
/** N parallel Implement generators feeding a single Verify filter/dedupe step (barrier). */
|
|
167
|
+
export function generateAndFilter(generators, filter) {
|
|
168
|
+
const nodes = generators.map(t => ({
|
|
169
|
+
task: asTask(t),
|
|
170
|
+
role: "implement",
|
|
171
|
+
isolation: "worktree",
|
|
172
|
+
contextInheritance: "full",
|
|
173
|
+
}));
|
|
174
|
+
nodes.push({
|
|
175
|
+
task: asTask(filter),
|
|
176
|
+
role: "verify",
|
|
177
|
+
isolation: "read_only",
|
|
178
|
+
contextInheritance: "none",
|
|
179
|
+
dependsOn: generators.map((_, i) => i),
|
|
180
|
+
});
|
|
181
|
+
return { nodes };
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* One fresh-context verifier per rule/claim (parallel) + optional skeptic that depends on all and
|
|
185
|
+
* re-checks flags. Verifiers run read-only with no inherited author context (bias-resistant).
|
|
186
|
+
*/
|
|
187
|
+
export function verifyRules(rules, skeptic) {
|
|
188
|
+
const nodes = rules.map(t => ({
|
|
189
|
+
task: asTask(t),
|
|
190
|
+
role: "verify",
|
|
191
|
+
isolation: "read_only",
|
|
192
|
+
contextInheritance: "none",
|
|
193
|
+
}));
|
|
194
|
+
if (skeptic !== undefined) {
|
|
195
|
+
nodes.push({
|
|
196
|
+
task: asTask(skeptic),
|
|
197
|
+
role: "verify",
|
|
198
|
+
isolation: "read_only",
|
|
199
|
+
contextInheritance: "none",
|
|
200
|
+
dependsOn: rules.map((_, i) => i),
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
return { nodes };
|
|
204
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -112,9 +112,31 @@ export interface ToolArgumentRepairedEvent extends StreamEvent {
|
|
|
112
112
|
* The framework creates and threads this object; providers may read/write it.
|
|
113
113
|
*/
|
|
114
114
|
export type ProviderRunState = Record<string, unknown>;
|
|
115
|
+
export type ProviderProtocol = "anthropic-messages" | "openai-chat" | "openai-responses" | "gemini";
|
|
116
|
+
export interface ProviderDescriptor {
|
|
117
|
+
provider: string;
|
|
118
|
+
protocol: ProviderProtocol;
|
|
119
|
+
model: string;
|
|
120
|
+
reasoning: {
|
|
121
|
+
supported: boolean;
|
|
122
|
+
preserveAcrossToolTurns: boolean;
|
|
123
|
+
requiresReplayForToolTurns?: boolean;
|
|
124
|
+
};
|
|
125
|
+
toolCalls: {
|
|
126
|
+
supported: boolean;
|
|
127
|
+
requiresStrictPairing: boolean;
|
|
128
|
+
};
|
|
129
|
+
}
|
|
115
130
|
export interface ProviderReplay {
|
|
131
|
+
schema_version?: 1 | 2;
|
|
132
|
+
provider?: string;
|
|
133
|
+
protocol?: ProviderProtocol;
|
|
134
|
+
model?: string;
|
|
116
135
|
native_blocks?: Array<Record<string, unknown>>;
|
|
117
136
|
reasoning_content?: string;
|
|
137
|
+
reasoning_details?: unknown;
|
|
138
|
+
native_message?: unknown;
|
|
139
|
+
tool_calls?: unknown[];
|
|
118
140
|
}
|
|
119
141
|
export interface LLMProvider {
|
|
120
142
|
createRunState?(): ProviderRunState;
|
|
@@ -122,6 +144,7 @@ export interface LLMProvider {
|
|
|
122
144
|
maxTurns?: number;
|
|
123
145
|
timeoutMs?: number;
|
|
124
146
|
};
|
|
147
|
+
descriptor?(): ProviderDescriptor;
|
|
125
148
|
peekProviderReplay?(message: Pick<Message, "content" | "toolCalls">): ProviderReplay | undefined;
|
|
126
149
|
seedProviderReplay?(message: Pick<Message, "content" | "toolCalls">, replay: ProviderReplay): void;
|
|
127
150
|
complete(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): Promise<Message>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepstrike/wasm",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"description": "DeepStrike WASM SDK — browser, Cloudflare Workers, Deno Deploy",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"test": "node --experimental-vm-modules node_modules/.bin/jest"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@deepstrike/wasm-kernel": "0.2.
|
|
18
|
+
"@deepstrike/wasm-kernel": "0.2.8"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@types/jest": "^30.0.0",
|