@deepstrike/sdk 0.1.15 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/collaboration/harness.d.ts +4 -1
- package/dist/collaboration/harness.js +20 -6
- package/dist/collaboration/modes/creator-verifier.d.ts +8 -2
- package/dist/collaboration/modes/creator-verifier.js +9 -3
- package/dist/collaboration/pool.d.ts +24 -3
- package/dist/collaboration/pool.js +65 -5
- package/dist/harness/harness.d.ts +5 -1
- package/dist/harness/harness.js +23 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +5 -0
- package/dist/kernel.d.ts +12 -43
- package/dist/kernel.js +1 -1
- package/dist/providers/anthropic.js +7 -2
- package/dist/providers/base.d.ts +12 -0
- package/dist/providers/base.js +75 -0
- package/dist/providers/catalog.d.ts +3 -2
- package/dist/providers/catalog.js +61 -13
- package/dist/providers/deepseek.js +1 -1
- package/dist/providers/gemini.d.ts +3 -0
- package/dist/providers/gemini.js +26 -6
- package/dist/providers/glm.d.ts +9 -0
- package/dist/providers/glm.js +21 -0
- package/dist/providers/kimi.js +2 -0
- package/dist/providers/minimax.js +5 -0
- package/dist/providers/openai-responses.js +9 -0
- package/dist/providers/openai.js +52 -6
- package/dist/providers/profiles.d.ts +1304 -242
- package/dist/providers/profiles.js +429 -26
- package/dist/providers/qwen.d.ts +3 -1
- package/dist/providers/qwen.js +43 -13
- package/dist/runtime/archive.d.ts +15 -0
- package/dist/runtime/archive.js +35 -0
- package/dist/runtime/execution-plane.js +14 -4
- package/dist/runtime/filtered-plane.d.ts +14 -0
- package/dist/runtime/filtered-plane.js +45 -0
- package/dist/runtime/kernel-step.d.ts +88 -0
- package/dist/runtime/kernel-step.js +262 -0
- package/dist/runtime/process-sandbox-plane.d.ts +4 -4
- package/dist/runtime/process-sandbox-plane.js +5 -5
- package/dist/runtime/provider-replay.js +37 -3
- package/dist/runtime/replay-sanitize.d.ts +5 -0
- package/dist/runtime/replay-sanitize.js +25 -0
- package/dist/runtime/runner.d.ts +60 -4
- package/dist/runtime/runner.js +589 -86
- package/dist/runtime/session-log.d.ts +93 -1
- package/dist/runtime/session-repair.d.ts +37 -0
- package/dist/runtime/session-repair.js +77 -0
- package/dist/runtime/sub-agent-orchestrator.d.ts +19 -0
- package/dist/runtime/sub-agent-orchestrator.js +114 -0
- package/dist/skills/watcher.d.ts +19 -0
- package/dist/skills/watcher.js +38 -0
- package/dist/tools/index.d.ts +4 -1
- package/dist/tools/index.js +82 -11
- package/dist/types/agent.d.ts +68 -0
- package/dist/types/agent.js +78 -0
- package/dist/types.d.ts +27 -0
- package/package.json +2 -2
package/dist/providers/qwen.js
CHANGED
|
@@ -5,16 +5,19 @@ import { OpenAIChatAdapter } from "./openai-chat.js";
|
|
|
5
5
|
import { endpointProfiles } from "./profiles.js";
|
|
6
6
|
const QWEN_BASE = endpointProfiles["qwen.dashscope"].baseURL;
|
|
7
7
|
const QWEN_POLICIES = {
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"qwen3-
|
|
14
|
-
"qwen3-
|
|
15
|
-
"qwen3-
|
|
16
|
-
"qwen3-
|
|
17
|
-
"qwen3-
|
|
8
|
+
"qwen3.7-max-preview": { maxTurns: 45 },
|
|
9
|
+
"qwen3.7-plus-preview": { maxTurns: 40 },
|
|
10
|
+
"qwen3.6-max-preview": { maxTurns: 40 },
|
|
11
|
+
"qwen3.6-plus": { maxTurns: 35 },
|
|
12
|
+
"qwen3.6-flash": { maxTurns: 20 },
|
|
13
|
+
"qwen3.6-35b-a3b": { maxTurns: 25 },
|
|
14
|
+
"qwen3.6-27b": { maxTurns: 25 },
|
|
15
|
+
"qwen3.5-plus": { maxTurns: 35 },
|
|
16
|
+
"qwen3.5-flash": { maxTurns: 20 },
|
|
17
|
+
"qwen3.5-397b-a17b": { maxTurns: 35 },
|
|
18
|
+
"qwen3.5-122b-a10b": { maxTurns: 25 },
|
|
19
|
+
"qwen3.5-35b-a3b": { maxTurns: 20 },
|
|
20
|
+
"qwen3.5-27b": { maxTurns: 20 },
|
|
18
21
|
};
|
|
19
22
|
export class QwenProvider {
|
|
20
23
|
model;
|
|
@@ -23,7 +26,7 @@ export class QwenProvider {
|
|
|
23
26
|
maxRetries;
|
|
24
27
|
baseDelay;
|
|
25
28
|
chat = new OpenAIChatAdapter();
|
|
26
|
-
constructor(apiKey, model = "
|
|
29
|
+
constructor(apiKey, model = "qwen3.6-plus", retry = { maxRetries: 3, baseDelay: 1000 }, baseURL = QWEN_BASE) {
|
|
27
30
|
this.model = model;
|
|
28
31
|
this.client = withServerRuntimeGuard(() => new OpenAI({ apiKey, baseURL }));
|
|
29
32
|
this.circuit = new CircuitBreaker();
|
|
@@ -33,6 +36,17 @@ export class QwenProvider {
|
|
|
33
36
|
runtimePolicy() {
|
|
34
37
|
return QWEN_POLICIES[this.model] ?? {};
|
|
35
38
|
}
|
|
39
|
+
peekProviderReplay(message) {
|
|
40
|
+
const fields = this.chat.peekReplayFields(message);
|
|
41
|
+
if (!fields || !("reasoning_content" in fields))
|
|
42
|
+
return undefined;
|
|
43
|
+
return { reasoning_content: String(fields.reasoning_content ?? "") };
|
|
44
|
+
}
|
|
45
|
+
seedProviderReplay(message, replay) {
|
|
46
|
+
if (replay.reasoning_content !== undefined) {
|
|
47
|
+
this.chat.rememberReplayFields(message, { reasoning_content: replay.reasoning_content });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
36
50
|
async complete(context, tools, extensions) {
|
|
37
51
|
if (this.circuit.isOpen())
|
|
38
52
|
throw new Error("Circuit breaker open");
|
|
@@ -66,6 +80,8 @@ export class QwenProvider {
|
|
|
66
80
|
const msgs = this.chat.buildMessages(context);
|
|
67
81
|
const toolCallBufs = {};
|
|
68
82
|
const emittedToolCallIndexes = new Set();
|
|
83
|
+
let reasoningContent = "";
|
|
84
|
+
let finalText = "";
|
|
69
85
|
const extraBody = this.thinkingExtraBody(extensions);
|
|
70
86
|
const stream = await this.client.chat.completions.create({
|
|
71
87
|
...this.requestExtensions(extensions),
|
|
@@ -86,10 +102,14 @@ export class QwenProvider {
|
|
|
86
102
|
if (!choice)
|
|
87
103
|
continue;
|
|
88
104
|
const delta = choice.delta;
|
|
89
|
-
if (delta.reasoning_content)
|
|
105
|
+
if (delta.reasoning_content) {
|
|
106
|
+
reasoningContent += String(delta.reasoning_content);
|
|
90
107
|
yield { type: "thinking_delta", delta: delta.reasoning_content };
|
|
91
|
-
|
|
108
|
+
}
|
|
109
|
+
if (delta.content) {
|
|
110
|
+
finalText += String(delta.content);
|
|
92
111
|
yield { type: "text_delta", delta: delta.content };
|
|
112
|
+
}
|
|
93
113
|
for (const tc of delta.tool_calls ?? []) {
|
|
94
114
|
const idx = tc.index;
|
|
95
115
|
if (!toolCallBufs[idx])
|
|
@@ -99,6 +119,10 @@ export class QwenProvider {
|
|
|
99
119
|
toolCallBufs[idx].argsBuf += tc.function?.arguments ?? "";
|
|
100
120
|
}
|
|
101
121
|
if (choice.finish_reason === "tool_calls") {
|
|
122
|
+
const toolCalls = Object.values(toolCallBufs).map(tb => ({
|
|
123
|
+
id: tb.id, name: tb.name, arguments: tb.argsBuf || "{}",
|
|
124
|
+
}));
|
|
125
|
+
this.chat.rememberReplayFields({ content: finalText, toolCalls }, { reasoning_content: reasoningContent });
|
|
102
126
|
for (const [index, tb] of Object.entries(toolCallBufs)) {
|
|
103
127
|
const idx = Number(index);
|
|
104
128
|
if (emittedToolCallIndexes.has(idx))
|
|
@@ -115,6 +139,12 @@ export class QwenProvider {
|
|
|
115
139
|
}
|
|
116
140
|
}
|
|
117
141
|
}
|
|
142
|
+
const toolCalls = Object.values(toolCallBufs).map(tb => ({
|
|
143
|
+
id: tb.id, name: tb.name, arguments: tb.argsBuf || "{}",
|
|
144
|
+
}));
|
|
145
|
+
if (toolCalls.length || reasoningContent) {
|
|
146
|
+
this.chat.rememberReplayFields({ content: finalText, toolCalls }, { reasoning_content: reasoningContent });
|
|
147
|
+
}
|
|
118
148
|
for (const [index, tb] of Object.entries(toolCallBufs)) {
|
|
119
149
|
const idx = Number(index);
|
|
120
150
|
if (emittedToolCallIndexes.has(idx))
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Message } from "../types.js";
|
|
2
|
+
export interface ArchiveStore {
|
|
3
|
+
write(sessionId: string, seq: number, messages: Message[]): Promise<string>;
|
|
4
|
+
read(archiveRef: string): Promise<Message[]>;
|
|
5
|
+
}
|
|
6
|
+
export declare class NullArchiveStore implements ArchiveStore {
|
|
7
|
+
write(_sessionId: string, _seq: number, _messages: Message[]): Promise<string>;
|
|
8
|
+
read(_archiveRef: string): Promise<Message[]>;
|
|
9
|
+
}
|
|
10
|
+
export declare class FileArchiveStore implements ArchiveStore {
|
|
11
|
+
private readonly root;
|
|
12
|
+
constructor(root: string);
|
|
13
|
+
write(sessionId: string, seq: number, messages: Message[]): Promise<string>;
|
|
14
|
+
read(archiveRef: string): Promise<Message[]>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { mkdir, writeFile, readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
export class NullArchiveStore {
|
|
4
|
+
async write(_sessionId, _seq, _messages) {
|
|
5
|
+
return "";
|
|
6
|
+
}
|
|
7
|
+
async read(_archiveRef) {
|
|
8
|
+
throw new Error("NullArchiveStore does not store archives");
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export class FileArchiveStore {
|
|
12
|
+
root;
|
|
13
|
+
constructor(root) {
|
|
14
|
+
this.root = root;
|
|
15
|
+
}
|
|
16
|
+
async write(sessionId, seq, messages) {
|
|
17
|
+
const dir = join(this.root, sessionId);
|
|
18
|
+
await mkdir(dir, { recursive: true });
|
|
19
|
+
const filePath = join(dir, `${seq}.jsonl`);
|
|
20
|
+
const lines = messages.map(msg => JSON.stringify(msg)).join("\n") + "\n";
|
|
21
|
+
await writeFile(filePath, lines, "utf8");
|
|
22
|
+
return filePath;
|
|
23
|
+
}
|
|
24
|
+
async read(archiveRef) {
|
|
25
|
+
const content = await readFile(archiveRef, "utf8");
|
|
26
|
+
const lines = content.split("\n");
|
|
27
|
+
const messages = [];
|
|
28
|
+
for (const line of lines) {
|
|
29
|
+
if (!line.trim())
|
|
30
|
+
continue;
|
|
31
|
+
messages.push(JSON.parse(line));
|
|
32
|
+
}
|
|
33
|
+
return messages;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -23,7 +23,7 @@ export class LocalExecutionPlane {
|
|
|
23
23
|
ctx.governance.setTime?.(BigInt(Date.now()));
|
|
24
24
|
const v = ctx.governance.evaluate(c.name, c.arguments);
|
|
25
25
|
if (v.kind === "deny") {
|
|
26
|
-
yield { type: "
|
|
26
|
+
yield { type: "tool_denied", callId: c.id, toolName: c.name, reason: v.reason ?? "" };
|
|
27
27
|
yield { type: "tool_result", callId: c.id, name: c.name, content: `permission denied: ${v.reason ?? ""}`, isError: true };
|
|
28
28
|
continue;
|
|
29
29
|
}
|
|
@@ -96,9 +96,19 @@ export class LocalExecutionPlane {
|
|
|
96
96
|
return { callId: call.id, output: `unknown tool: ${call.name}`, isError: true };
|
|
97
97
|
try {
|
|
98
98
|
const args = JSON.parse(call.arguments || "{}");
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
const originalArgsStr = JSON.stringify(args);
|
|
100
|
+
const validation = validateToolArguments(registered.schema.parameters, args);
|
|
101
|
+
if (validation.error)
|
|
102
|
+
return { callId: call.id, output: `invalid arguments: ${validation.error}`, isError: true };
|
|
103
|
+
if (validation.repaired) {
|
|
104
|
+
yield {
|
|
105
|
+
type: "tool_argument_repaired",
|
|
106
|
+
callId: call.id,
|
|
107
|
+
name: call.name,
|
|
108
|
+
originalArguments: originalArgsStr,
|
|
109
|
+
repairedArguments: JSON.stringify(args),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
102
112
|
const output = await registered.execute(args);
|
|
103
113
|
if (isAsyncIterable(output)) {
|
|
104
114
|
let combined = "";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ToolCall, ToolSchema, StreamEvent } from "../types.js";
|
|
2
|
+
import type { RegisteredTool } from "../tools/index.js";
|
|
3
|
+
import type { ExecutionPlane, RunContext } from "./execution-plane.js";
|
|
4
|
+
/** Wraps an execution plane, allowing only manifest-permitted tool IDs (+ meta-tools). */
|
|
5
|
+
export declare class FilteredExecutionPlane implements ExecutionPlane {
|
|
6
|
+
private readonly inner;
|
|
7
|
+
private readonly permittedIds;
|
|
8
|
+
private readonly metaTools;
|
|
9
|
+
constructor(inner: ExecutionPlane, permittedIds: Set<string>, metaTools?: Set<string>);
|
|
10
|
+
register(...tools: RegisteredTool[]): this;
|
|
11
|
+
unregister(name: string): this;
|
|
12
|
+
schemas(): ToolSchema[];
|
|
13
|
+
executeAll(calls: ToolCall[], ctx: RunContext): AsyncIterable<StreamEvent>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const DEFAULT_META_TOOLS = new Set(["skill", "memory", "knowledge", "update_plan"]);
|
|
2
|
+
/** Wraps an execution plane, allowing only manifest-permitted tool IDs (+ meta-tools). */
|
|
3
|
+
export class FilteredExecutionPlane {
|
|
4
|
+
inner;
|
|
5
|
+
permittedIds;
|
|
6
|
+
metaTools;
|
|
7
|
+
constructor(inner, permittedIds, metaTools = DEFAULT_META_TOOLS) {
|
|
8
|
+
this.inner = inner;
|
|
9
|
+
this.permittedIds = permittedIds;
|
|
10
|
+
this.metaTools = metaTools;
|
|
11
|
+
}
|
|
12
|
+
register(...tools) {
|
|
13
|
+
this.inner.register(...tools);
|
|
14
|
+
return this;
|
|
15
|
+
}
|
|
16
|
+
unregister(name) {
|
|
17
|
+
this.inner.unregister(name);
|
|
18
|
+
return this;
|
|
19
|
+
}
|
|
20
|
+
schemas() {
|
|
21
|
+
return this.inner.schemas().filter(s => this.permittedIds.has(s.name) || this.metaTools.has(s.name));
|
|
22
|
+
}
|
|
23
|
+
async *executeAll(calls, ctx) {
|
|
24
|
+
const permitted = [];
|
|
25
|
+
for (const call of calls) {
|
|
26
|
+
if (this.metaTools.has(call.name) || this.permittedIds.has(call.name)) {
|
|
27
|
+
permitted.push(call);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const reason = `capability not permitted for sub-agent: ${call.name}`;
|
|
31
|
+
yield { type: "tool_denied", callId: call.id, toolName: call.name, reason };
|
|
32
|
+
yield {
|
|
33
|
+
type: "tool_result",
|
|
34
|
+
callId: call.id,
|
|
35
|
+
name: call.name,
|
|
36
|
+
content: reason,
|
|
37
|
+
isError: true,
|
|
38
|
+
errorKind: "governance_denied",
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
if (permitted.length > 0) {
|
|
42
|
+
yield* this.inner.executeAll(permitted, ctx);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { Message, RenderedContext, TaskUpdate, ToolCall, ToolResult, ToolSchema } from "../types.js";
|
|
2
|
+
import type { SkillMetadata } from "../skills/loader.js";
|
|
3
|
+
import type { RollbackReason } from "./session-log.js";
|
|
4
|
+
export declare const KERNEL_ABI_VERSION = 1;
|
|
5
|
+
export interface KernelRuntimeHandle {
|
|
6
|
+
step(inputJson: string): string;
|
|
7
|
+
isTerminal(): boolean;
|
|
8
|
+
turn(): number;
|
|
9
|
+
recoveryContentBytes(): number;
|
|
10
|
+
render(): RenderedContext;
|
|
11
|
+
drainNewMessages(): Message[];
|
|
12
|
+
preservedRefs(): string[];
|
|
13
|
+
}
|
|
14
|
+
export interface KernelLoopResult {
|
|
15
|
+
termination: string;
|
|
16
|
+
turnsUsed: number;
|
|
17
|
+
totalTokensUsed: number;
|
|
18
|
+
}
|
|
19
|
+
export type MilestoneVerifierKind = {
|
|
20
|
+
kind: "machine_check";
|
|
21
|
+
} | {
|
|
22
|
+
kind: "harness_eval";
|
|
23
|
+
} | {
|
|
24
|
+
kind: "llm_judge";
|
|
25
|
+
} | {
|
|
26
|
+
kind: "human_approval";
|
|
27
|
+
} | {
|
|
28
|
+
kind: "external_command";
|
|
29
|
+
cmd: string;
|
|
30
|
+
};
|
|
31
|
+
export type KernelRunnerAction = {
|
|
32
|
+
kind: "call_provider";
|
|
33
|
+
context: RenderedContext;
|
|
34
|
+
tools: ToolSchema[];
|
|
35
|
+
} | {
|
|
36
|
+
kind: "execute_tool";
|
|
37
|
+
calls: ToolCall[];
|
|
38
|
+
} | {
|
|
39
|
+
kind: "evaluate_milestone";
|
|
40
|
+
phaseId: string;
|
|
41
|
+
criteria: string[];
|
|
42
|
+
verifier?: MilestoneVerifierKind;
|
|
43
|
+
requiredEvidence: string[];
|
|
44
|
+
} | {
|
|
45
|
+
kind: "done";
|
|
46
|
+
result: KernelLoopResult;
|
|
47
|
+
};
|
|
48
|
+
export interface KernelObservation {
|
|
49
|
+
kind: string;
|
|
50
|
+
action?: string;
|
|
51
|
+
rho_after?: number;
|
|
52
|
+
sprint?: number;
|
|
53
|
+
summary?: string;
|
|
54
|
+
archived?: Message[];
|
|
55
|
+
turn?: number;
|
|
56
|
+
checkpoint_history_len?: number;
|
|
57
|
+
history_len?: number;
|
|
58
|
+
added?: string[];
|
|
59
|
+
removed?: string[];
|
|
60
|
+
change_kind?: string;
|
|
61
|
+
capability_id?: string;
|
|
62
|
+
version?: string;
|
|
63
|
+
mounted_by?: string;
|
|
64
|
+
mount_reason?: string;
|
|
65
|
+
phase_id?: string;
|
|
66
|
+
capabilities_unlocked?: string[];
|
|
67
|
+
evidence?: string[];
|
|
68
|
+
reason?: RollbackReason;
|
|
69
|
+
agent_id?: string;
|
|
70
|
+
parent_session_id?: string;
|
|
71
|
+
role?: string;
|
|
72
|
+
isolation?: string;
|
|
73
|
+
context_inheritance?: string;
|
|
74
|
+
permitted_capability_ids?: string[];
|
|
75
|
+
}
|
|
76
|
+
export declare function toolSchemaToKernel(schema: ToolSchema): Record<string, unknown>;
|
|
77
|
+
export declare function skillMetadataToKernel(skill: SkillMetadata): Record<string, unknown>;
|
|
78
|
+
export declare function messageToKernelMessage(message: Message): Record<string, unknown>;
|
|
79
|
+
export declare function toolResultToKernel(result: ToolResult): Record<string, unknown>;
|
|
80
|
+
export declare function taskUpdateToKernel(update: TaskUpdate): Record<string, unknown>;
|
|
81
|
+
export declare function capabilityTool(schema: ToolSchema): Record<string, unknown>;
|
|
82
|
+
export declare function capabilitySkill(skill: SkillMetadata): Record<string, unknown>;
|
|
83
|
+
export declare function capabilityMarker(kind: string, id: string, description: string): Record<string, unknown>;
|
|
84
|
+
export declare function capabilityCommandMount(capability: Record<string, unknown>, mountedBy?: string, mountReason?: string): Record<string, unknown>;
|
|
85
|
+
export declare function capabilityCommandUnmount(capabilityKind: string, id: string): Record<string, unknown>;
|
|
86
|
+
export declare function kernelApply(runtime: KernelRuntimeHandle, pending: KernelObservation[], event: Record<string, unknown>): KernelObservation[];
|
|
87
|
+
export declare function kernelAction(runtime: KernelRuntimeHandle, pending: KernelObservation[], event: Record<string, unknown>): KernelRunnerAction;
|
|
88
|
+
export declare function forceCompact(runtime: KernelRuntimeHandle, pending: KernelObservation[]): boolean;
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
export const KERNEL_ABI_VERSION = 1;
|
|
2
|
+
function tryParseJson(s) {
|
|
3
|
+
try {
|
|
4
|
+
return JSON.parse(s);
|
|
5
|
+
}
|
|
6
|
+
catch {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function toolSchemaToKernel(schema) {
|
|
11
|
+
return {
|
|
12
|
+
name: schema.name,
|
|
13
|
+
description: schema.description,
|
|
14
|
+
parameters: tryParseJson(schema.parameters) ?? {},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export function skillMetadataToKernel(skill) {
|
|
18
|
+
const out = {
|
|
19
|
+
name: skill.name,
|
|
20
|
+
description: skill.description,
|
|
21
|
+
estimated_tokens: skill.estimatedTokens ?? 0,
|
|
22
|
+
};
|
|
23
|
+
if (skill.whenToUse)
|
|
24
|
+
out.when_to_use = skill.whenToUse;
|
|
25
|
+
if (skill.effort !== undefined)
|
|
26
|
+
out.effort = skill.effort;
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
export function messageToKernelMessage(message) {
|
|
30
|
+
const out = {
|
|
31
|
+
role: message.role,
|
|
32
|
+
tool_calls: (message.toolCalls ?? []).map(tc => ({
|
|
33
|
+
id: tc.id,
|
|
34
|
+
name: tc.name,
|
|
35
|
+
arguments: tryParseJson(tc.arguments) ?? {},
|
|
36
|
+
})),
|
|
37
|
+
};
|
|
38
|
+
if (message.tokenCount !== undefined) {
|
|
39
|
+
out.token_count = message.tokenCount;
|
|
40
|
+
}
|
|
41
|
+
if (message.contentParts && message.contentParts.length > 0) {
|
|
42
|
+
out.content = message.contentParts.map(part => {
|
|
43
|
+
if (part.type === "text")
|
|
44
|
+
return { type: "text", text: part.text };
|
|
45
|
+
if (part.type === "tool_result") {
|
|
46
|
+
return {
|
|
47
|
+
type: "tool_result",
|
|
48
|
+
call_id: part.callId,
|
|
49
|
+
output: part.output,
|
|
50
|
+
is_error: part.isError,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
if (part.type === "image") {
|
|
54
|
+
return {
|
|
55
|
+
type: "image",
|
|
56
|
+
url: part.url,
|
|
57
|
+
data: part.data,
|
|
58
|
+
media_type: part.mediaType,
|
|
59
|
+
detail: part.detail,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (part.type === "audio") {
|
|
63
|
+
return { type: "audio", data: part.data, media_type: part.mediaType };
|
|
64
|
+
}
|
|
65
|
+
return { type: "text", text: message.content };
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
out.content = message.content;
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
export function toolResultToKernel(result) {
|
|
74
|
+
const out = {
|
|
75
|
+
call_id: result.callId,
|
|
76
|
+
output: result.output,
|
|
77
|
+
is_error: result.isError,
|
|
78
|
+
is_fatal: result.isFatal ?? false,
|
|
79
|
+
token_count: result.tokenCount ?? null,
|
|
80
|
+
};
|
|
81
|
+
if (result.errorKind !== undefined) {
|
|
82
|
+
out.error_kind = result.errorKind;
|
|
83
|
+
}
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
86
|
+
export function taskUpdateToKernel(update) {
|
|
87
|
+
return {
|
|
88
|
+
plan: update.plan,
|
|
89
|
+
current_step: update.currentStep,
|
|
90
|
+
progress: update.progress,
|
|
91
|
+
scratchpad: update.scratchpad,
|
|
92
|
+
blocked_on: update.blockedOn,
|
|
93
|
+
preserved_refs: update.preservedRefs,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
export function capabilityTool(schema) {
|
|
97
|
+
return {
|
|
98
|
+
id: schema.name,
|
|
99
|
+
kind: "tool",
|
|
100
|
+
description: schema.description,
|
|
101
|
+
tool_schema: toolSchemaToKernel(schema),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
export function capabilitySkill(skill) {
|
|
105
|
+
return {
|
|
106
|
+
id: skill.name,
|
|
107
|
+
kind: "skill",
|
|
108
|
+
description: skill.description,
|
|
109
|
+
skill: skillMetadataToKernel(skill),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
export function capabilityMarker(kind, id, description) {
|
|
113
|
+
return { id, kind, description };
|
|
114
|
+
}
|
|
115
|
+
export function capabilityCommandMount(capability, mountedBy = "sdk:runtime", mountReason = "dynamic_register") {
|
|
116
|
+
return {
|
|
117
|
+
kind: "capability_command",
|
|
118
|
+
command: {
|
|
119
|
+
action: "mount",
|
|
120
|
+
capability,
|
|
121
|
+
mounted_by: mountedBy,
|
|
122
|
+
mount_reason: mountReason,
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
export function capabilityCommandUnmount(capabilityKind, id) {
|
|
127
|
+
return {
|
|
128
|
+
kind: "capability_command",
|
|
129
|
+
command: { action: "unmount", kind: capabilityKind, id },
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function parseStep(raw) {
|
|
133
|
+
return JSON.parse(raw);
|
|
134
|
+
}
|
|
135
|
+
function kernelMessageToSdk(raw) {
|
|
136
|
+
const content = raw.content;
|
|
137
|
+
const message = {
|
|
138
|
+
role: raw.role,
|
|
139
|
+
content: typeof content === "string"
|
|
140
|
+
? content
|
|
141
|
+
: Array.isArray(content)
|
|
142
|
+
? content
|
|
143
|
+
.filter((part) => {
|
|
144
|
+
return typeof part === "object" && part !== null && part.type === "text";
|
|
145
|
+
})
|
|
146
|
+
.map(part => String(part.text ?? ""))
|
|
147
|
+
.join("")
|
|
148
|
+
: "",
|
|
149
|
+
toolCalls: (raw.tool_calls ?? []).map(tc => ({
|
|
150
|
+
id: String(tc.id ?? ""),
|
|
151
|
+
name: String(tc.name ?? ""),
|
|
152
|
+
arguments: JSON.stringify(tc.arguments ?? {}),
|
|
153
|
+
})),
|
|
154
|
+
};
|
|
155
|
+
if (typeof raw.token_count === "number") {
|
|
156
|
+
message.tokenCount = raw.token_count;
|
|
157
|
+
}
|
|
158
|
+
if (Array.isArray(content)) {
|
|
159
|
+
message.contentParts = content
|
|
160
|
+
.filter((part) => typeof part === "object" && part !== null)
|
|
161
|
+
.map(part => {
|
|
162
|
+
if (part.type === "text") {
|
|
163
|
+
return { type: "text", text: String(part.text ?? "") };
|
|
164
|
+
}
|
|
165
|
+
if (part.type === "tool_result") {
|
|
166
|
+
return {
|
|
167
|
+
type: "tool_result",
|
|
168
|
+
callId: String(part.call_id ?? ""),
|
|
169
|
+
output: String(part.output ?? ""),
|
|
170
|
+
isError: Boolean(part.is_error),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
if (part.type === "image") {
|
|
174
|
+
return {
|
|
175
|
+
type: "image",
|
|
176
|
+
url: part.url,
|
|
177
|
+
data: part.data,
|
|
178
|
+
mediaType: part.media_type,
|
|
179
|
+
detail: part.detail,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
if (part.type === "audio") {
|
|
183
|
+
return {
|
|
184
|
+
type: "audio",
|
|
185
|
+
data: String(part.data ?? ""),
|
|
186
|
+
mediaType: String(part.media_type ?? "audio/wav"),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
return { type: "text", text: "" };
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
return message;
|
|
193
|
+
}
|
|
194
|
+
function renderedContextToSdk(raw) {
|
|
195
|
+
return {
|
|
196
|
+
systemText: String(raw.system_text ?? raw.systemText ?? ""),
|
|
197
|
+
turns: (raw.turns ?? []).map(kernelMessageToSdk),
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
function mapKernelAction(raw) {
|
|
201
|
+
switch (raw.kind) {
|
|
202
|
+
case "call_provider":
|
|
203
|
+
return {
|
|
204
|
+
kind: "call_provider",
|
|
205
|
+
context: renderedContextToSdk(raw.context ?? {}),
|
|
206
|
+
tools: (raw.tools ?? []).map(t => ({
|
|
207
|
+
name: String(t.name ?? ""),
|
|
208
|
+
description: String(t.description ?? ""),
|
|
209
|
+
parameters: JSON.stringify(t.parameters ?? {}),
|
|
210
|
+
})),
|
|
211
|
+
};
|
|
212
|
+
case "execute_tool":
|
|
213
|
+
return {
|
|
214
|
+
kind: "execute_tool",
|
|
215
|
+
calls: (raw.calls ?? []).map(c => ({
|
|
216
|
+
id: String(c.id ?? ""),
|
|
217
|
+
name: String(c.name ?? ""),
|
|
218
|
+
arguments: JSON.stringify(c.arguments ?? {}),
|
|
219
|
+
})),
|
|
220
|
+
};
|
|
221
|
+
case "evaluate_milestone":
|
|
222
|
+
return {
|
|
223
|
+
kind: "evaluate_milestone",
|
|
224
|
+
phaseId: String(raw.phase_id ?? ""),
|
|
225
|
+
criteria: raw.criteria ?? [],
|
|
226
|
+
verifier: raw.verifier,
|
|
227
|
+
requiredEvidence: raw.required_evidence ?? [],
|
|
228
|
+
};
|
|
229
|
+
case "done": {
|
|
230
|
+
const result = raw.result ?? {};
|
|
231
|
+
return {
|
|
232
|
+
kind: "done",
|
|
233
|
+
result: {
|
|
234
|
+
termination: String(result.termination ?? "error"),
|
|
235
|
+
turnsUsed: Number(result.turns_used ?? 0),
|
|
236
|
+
totalTokensUsed: Number(result.total_tokens_used ?? 0),
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
default:
|
|
241
|
+
throw new Error(`unknown KernelAction kind: ${String(raw.kind)}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function stepInput(event) {
|
|
245
|
+
return JSON.stringify({ version: KERNEL_ABI_VERSION, event });
|
|
246
|
+
}
|
|
247
|
+
export function kernelApply(runtime, pending, event) {
|
|
248
|
+
const step = parseStep(runtime.step(stepInput(event)));
|
|
249
|
+
pending.push(...step.observations);
|
|
250
|
+
return step.observations;
|
|
251
|
+
}
|
|
252
|
+
export function kernelAction(runtime, pending, event) {
|
|
253
|
+
const step = parseStep(runtime.step(stepInput(event)));
|
|
254
|
+
pending.push(...step.observations);
|
|
255
|
+
const raw = step.actions[0];
|
|
256
|
+
if (!raw)
|
|
257
|
+
throw new Error("kernel transition must return one action");
|
|
258
|
+
return mapKernelAction(raw);
|
|
259
|
+
}
|
|
260
|
+
export function forceCompact(runtime, pending) {
|
|
261
|
+
return kernelApply(runtime, pending, { kind: "force_compact" }).some(o => o.kind === "compressed");
|
|
262
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LocalExecutionPlane } from "./execution-plane.js";
|
|
2
2
|
export interface SandboxOptions {
|
|
3
|
-
/** Working directory for all subprocesses.
|
|
3
|
+
/** Working directory for all subprocesses. This is not an OS-enforced filesystem boundary. */
|
|
4
4
|
sandboxDir: string;
|
|
5
5
|
/** Env var names from the host environment to forward into subprocesses. Default: none. */
|
|
6
6
|
allowedEnvKeys?: string[];
|
|
@@ -10,14 +10,14 @@ export interface SandboxOptions {
|
|
|
10
10
|
maxOutputBytes?: number;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
-
* ExecutionPlane that
|
|
13
|
+
* ExecutionPlane that runs subprocesses with a sandbox directory as cwd.
|
|
14
14
|
* Extends LocalExecutionPlane with two built-in tools:
|
|
15
15
|
* - `run_bash` — executes a bash command inside sandboxDir.
|
|
16
16
|
* - `run_node` — evaluates a Node.js script inside sandboxDir.
|
|
17
17
|
*
|
|
18
18
|
* All registered JS tools continue to run in-process (identical to LocalExecutionPlane).
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* Subprocesses are launched with a stripped environment and sandboxDir as cwd;
|
|
20
|
+
* this is execution hygiene, not an OS-enforced filesystem sandbox.
|
|
21
21
|
*/
|
|
22
22
|
export declare class ProcessSandboxPlane extends LocalExecutionPlane {
|
|
23
23
|
private readonly sandboxDir;
|
|
@@ -3,14 +3,14 @@ import { mkdir } from "node:fs/promises";
|
|
|
3
3
|
import { tool } from "../tools/index.js";
|
|
4
4
|
import { LocalExecutionPlane } from "./execution-plane.js";
|
|
5
5
|
/**
|
|
6
|
-
* ExecutionPlane that
|
|
6
|
+
* ExecutionPlane that runs subprocesses with a sandbox directory as cwd.
|
|
7
7
|
* Extends LocalExecutionPlane with two built-in tools:
|
|
8
8
|
* - `run_bash` — executes a bash command inside sandboxDir.
|
|
9
9
|
* - `run_node` — evaluates a Node.js script inside sandboxDir.
|
|
10
10
|
*
|
|
11
11
|
* All registered JS tools continue to run in-process (identical to LocalExecutionPlane).
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* Subprocesses are launched with a stripped environment and sandboxDir as cwd;
|
|
13
|
+
* this is execution hygiene, not an OS-enforced filesystem sandbox.
|
|
14
14
|
*/
|
|
15
15
|
export class ProcessSandboxPlane extends LocalExecutionPlane {
|
|
16
16
|
sandboxDir;
|
|
@@ -77,7 +77,7 @@ export class ProcessSandboxPlane extends LocalExecutionPlane {
|
|
|
77
77
|
});
|
|
78
78
|
}
|
|
79
79
|
makeBashTool() {
|
|
80
|
-
return tool("run_bash", "Run a bash command
|
|
80
|
+
return tool("run_bash", "Run a bash command with the sandbox directory as cwd and a stripped environment. This is not an OS-enforced filesystem sandbox.", {
|
|
81
81
|
type: "object",
|
|
82
82
|
properties: {
|
|
83
83
|
command: { type: "string", description: "The bash command to execute." },
|
|
@@ -92,7 +92,7 @@ export class ProcessSandboxPlane extends LocalExecutionPlane {
|
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
94
|
makeNodeTool() {
|
|
95
|
-
return tool("run_node", "Evaluate a Node.js script
|
|
95
|
+
return tool("run_node", "Evaluate a Node.js script with the sandbox directory as cwd and a stripped environment.", {
|
|
96
96
|
type: "object",
|
|
97
97
|
properties: {
|
|
98
98
|
code: { type: "string", description: "The JavaScript code to evaluate." },
|