@deepstrike/sdk 0.1.1 → 0.1.4

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.
@@ -44,3 +44,37 @@ export function normalizeToolCall(id, name, args) {
44
44
  }
45
45
  return { id: String(id ?? ""), name: n, arguments: JSON.stringify(parsed) };
46
46
  }
47
+ export function toAnthropicContent(msg) {
48
+ if (!msg.contentParts?.length)
49
+ return msg.content;
50
+ return msg.contentParts.map(p => {
51
+ if (p.type === "text")
52
+ return { type: "text", text: p.text };
53
+ if (p.type === "image") {
54
+ if (p.data) {
55
+ return { type: "image", source: { type: "base64", media_type: p.mediaType ?? "image/png", data: p.data } };
56
+ }
57
+ return { type: "image", source: { type: "url", url: p.url } };
58
+ }
59
+ if (p.type === "audio") {
60
+ return { type: "text", text: `[audio: ${p.mediaType}]` };
61
+ }
62
+ return { type: "text", text: "" };
63
+ });
64
+ }
65
+ export function toOpenAIContent(msg) {
66
+ if (!msg.contentParts?.length)
67
+ return msg.content;
68
+ return msg.contentParts.map(p => {
69
+ if (p.type === "text")
70
+ return { type: "text", text: p.text };
71
+ if (p.type === "image") {
72
+ const url = p.data ? `data:${p.mediaType ?? "image/png"};base64,${p.data}` : p.url;
73
+ return { type: "image_url", image_url: { url, ...(p.detail ? { detail: p.detail } : {}) } };
74
+ }
75
+ if (p.type === "audio") {
76
+ return { type: "input_audio", input_audio: { data: p.data, format: p.mediaType?.split("/")[1] ?? "wav" } };
77
+ }
78
+ return { type: "text", text: "" };
79
+ });
80
+ }
@@ -7,7 +7,16 @@ export class OllamaProvider {
7
7
  this.baseUrl = baseUrl;
8
8
  }
9
9
  toOllamaMessages(messages) {
10
- return messages.map(m => ({ role: m.role, content: m.content }));
10
+ return messages.map(m => {
11
+ const images = [];
12
+ if (m.contentParts?.length) {
13
+ for (const p of m.contentParts) {
14
+ if (p.type === "image" && p.data)
15
+ images.push(p.data);
16
+ }
17
+ }
18
+ return { role: m.role, content: m.content, ...(images.length ? { images } : {}) };
19
+ });
11
20
  }
12
21
  async complete(messages, tools) {
13
22
  const resp = await fetch(`${this.baseUrl}/api/chat`, {
@@ -27,6 +27,7 @@ export declare class QwenProvider extends OpenAIProvider {
27
27
  maxRetries: number;
28
28
  baseDelay: number;
29
29
  });
30
+ stream(messages: Message[], tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
30
31
  }
31
32
  export declare class DeepSeekProvider extends OpenAIProvider {
32
33
  constructor(apiKey: string, model?: string, retry?: {
@@ -42,3 +43,9 @@ export declare class MiniMaxProvider extends OpenAIProvider {
42
43
  });
43
44
  stream(messages: Message[], tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
44
45
  }
46
+ export declare class KimiProvider extends OpenAIProvider {
47
+ constructor(apiKey: string, model?: string, retry?: {
48
+ maxRetries: number;
49
+ baseDelay: number;
50
+ });
51
+ }
@@ -1,5 +1,5 @@
1
1
  import OpenAI from "openai";
2
- import { CircuitBreaker, normalizeToolCall } from "./base.js";
2
+ import { CircuitBreaker, normalizeToolCall, toOpenAIContent } from "./base.js";
3
3
  export class OpenAIProvider {
4
4
  model;
5
5
  client;
@@ -22,7 +22,7 @@ export class OpenAIProvider {
22
22
  async complete(messages, tools) {
23
23
  if (this.circuit.isOpen())
24
24
  throw new Error("Circuit breaker open");
25
- const msgs = messages.map(m => ({ role: m.role, content: m.content }));
25
+ const msgs = messages.map(m => ({ role: m.role, content: toOpenAIContent(m) }));
26
26
  let lastErr;
27
27
  for (let i = 0; i < this.maxRetries; i++) {
28
28
  try {
@@ -46,15 +46,21 @@ export class OpenAIProvider {
46
46
  throw lastErr;
47
47
  }
48
48
  async *stream(messages, tools, extensions) {
49
- const msgs = messages.map(m => ({ role: m.role, content: m.content }));
49
+ const msgs = messages.map(m => ({ role: m.role, content: toOpenAIContent(m) }));
50
50
  const toolCallBufs = {};
51
51
  const stream = await this.client.chat.completions.create({
52
52
  model: this.model,
53
53
  messages: msgs,
54
54
  ...(tools.length ? { tools: this.buildTools(tools) } : {}),
55
55
  stream: true,
56
+ stream_options: { include_usage: true },
56
57
  });
58
+ let totalTokens = 0;
57
59
  for await (const chunk of stream) {
60
+ if (chunk.usage) {
61
+ totalTokens = chunk.usage.total_tokens;
62
+ continue;
63
+ }
58
64
  const choice = chunk.choices[0];
59
65
  if (!choice)
60
66
  continue;
@@ -82,17 +88,63 @@ export class OpenAIProvider {
82
88
  }
83
89
  }
84
90
  }
91
+ if (totalTokens > 0)
92
+ yield { type: "usage", totalTokens };
85
93
  }
86
94
  }
87
95
  const DASHSCOPE_BASE = "https://dashscope.aliyuncs.com/compatible-mode/v1";
88
96
  const DEEPSEEK_BASE = "https://api.deepseek.com/v1";
89
97
  const MINIMAX_BASE = "https://api.minimax.chat/v1";
98
+ const MOONSHOT_BASE = "https://api.moonshot.cn/v1";
90
99
  const DEEPSEEK_REASONERS = new Set(["deepseek-reasoner", "deepseek-r1"]);
91
100
  const MINIMAX_REASONERS = new Set(["MiniMax-M1", "minimax-m1"]);
92
101
  export class QwenProvider extends OpenAIProvider {
93
- constructor(apiKey, model = "qwen-plus", retry) {
102
+ constructor(apiKey, model = "qwen-max", retry) {
94
103
  super(apiKey, model, retry, DASHSCOPE_BASE);
95
104
  }
105
+ async *stream(messages, tools, extensions) {
106
+ const enableThinking = Boolean(extensions?.enableThinking);
107
+ const thinkingBudget = extensions?.thinkingBudget;
108
+ const msgs = messages.map(m => ({ role: m.role, content: toOpenAIContent(m) }));
109
+ const toolCallBufs = {};
110
+ const stream = await this.client.chat.completions.create({
111
+ model: this.model,
112
+ messages: msgs,
113
+ ...(tools.length ? { tools: this.buildTools(tools) } : {}),
114
+ stream: true,
115
+ ...(enableThinking ? { extra_body: { enable_thinking: true, ...(thinkingBudget ? { thinking_budget: thinkingBudget } : {}) } } : {}),
116
+ });
117
+ for await (const chunk of stream) {
118
+ const choice = chunk.choices[0];
119
+ if (!choice)
120
+ continue;
121
+ const delta = choice.delta;
122
+ if (delta.reasoning_content)
123
+ yield { type: "thinking_delta", delta: delta.reasoning_content };
124
+ if (delta.content)
125
+ yield { type: "text_delta", delta: delta.content };
126
+ for (const tc of delta.tool_calls ?? []) {
127
+ const idx = tc.index;
128
+ if (!toolCallBufs[idx])
129
+ toolCallBufs[idx] = { id: tc.id ?? "", name: "", argsBuf: "" };
130
+ if (tc.function?.name)
131
+ toolCallBufs[idx].name += tc.function.name;
132
+ toolCallBufs[idx].argsBuf += tc.function?.arguments ?? "";
133
+ }
134
+ if (choice.finish_reason === "tool_calls") {
135
+ for (const tb of Object.values(toolCallBufs)) {
136
+ let args = {};
137
+ try {
138
+ args = JSON.parse(tb.argsBuf || "{}");
139
+ }
140
+ catch {
141
+ args = {};
142
+ }
143
+ yield { type: "tool_call", id: tb.id, name: tb.name, arguments: args };
144
+ }
145
+ }
146
+ }
147
+ }
96
148
  }
97
149
  export class DeepSeekProvider extends OpenAIProvider {
98
150
  constructor(apiKey, model = "deepseek-chat", retry) {
@@ -100,7 +152,7 @@ export class DeepSeekProvider extends OpenAIProvider {
100
152
  }
101
153
  async *stream(messages, tools, extensions) {
102
154
  const exposeReasoning = extensions?.exposeReasoning ?? false;
103
- const msgs = messages.map(m => ({ role: m.role, content: m.content }));
155
+ const msgs = messages.map(m => ({ role: m.role, content: toOpenAIContent(m) }));
104
156
  const isReasoner = DEEPSEEK_REASONERS.has(this.model);
105
157
  const stream = await this.client.chat.completions.create({
106
158
  model: this.model,
@@ -124,7 +176,7 @@ export class MiniMaxProvider extends OpenAIProvider {
124
176
  }
125
177
  async *stream(messages, tools, extensions) {
126
178
  const exposeReasoning = extensions?.exposeReasoning ?? false;
127
- const msgs = messages.map(m => ({ role: m.role, content: m.content }));
179
+ const msgs = messages.map(m => ({ role: m.role, content: toOpenAIContent(m) }));
128
180
  const isReasoner = MINIMAX_REASONERS.has(this.model);
129
181
  const stream = await this.client.chat.completions.create({
130
182
  model: this.model,
@@ -142,3 +194,8 @@ export class MiniMaxProvider extends OpenAIProvider {
142
194
  }
143
195
  }
144
196
  }
197
+ export class KimiProvider extends OpenAIProvider {
198
+ constructor(apiKey, model = "moonshot-v1-8k", retry) {
199
+ super(apiKey, model, retry, MOONSHOT_BASE);
200
+ }
201
+ }
@@ -3,15 +3,28 @@ export declare enum PermissionMode {
3
3
  PLAN = "PLAN",
4
4
  AUTO = "AUTO"
5
5
  }
6
+ export interface Permission {
7
+ tool: string;
8
+ action: string;
9
+ allowed: boolean;
10
+ requiresApproval: boolean;
11
+ note: string;
12
+ }
6
13
  export interface PermissionDecision {
7
14
  allowed: boolean;
8
15
  reason: string;
16
+ requiresApproval?: boolean;
17
+ matchedPermission?: Permission;
9
18
  }
10
19
  export declare class PermissionManager {
11
20
  private mode;
12
- private grants;
21
+ private permissions;
13
22
  constructor(mode?: PermissionMode);
14
- grant(resource: string, action: string): this;
15
- revoke(resource: string, action: string): this;
23
+ grant(resource: string, action: string, options?: {
24
+ requiresApproval?: boolean;
25
+ note?: string;
26
+ }): this;
27
+ revoke(resource: string, action: string, note?: string): this;
28
+ private matchPermission;
16
29
  evaluate(resource: string, action: string): PermissionDecision;
17
30
  }
@@ -6,27 +6,44 @@ export var PermissionMode;
6
6
  })(PermissionMode || (PermissionMode = {}));
7
7
  export class PermissionManager {
8
8
  mode;
9
- grants = new Map();
9
+ permissions = new Map();
10
10
  constructor(mode = PermissionMode.DEFAULT) {
11
11
  this.mode = mode;
12
12
  }
13
- grant(resource, action) {
14
- if (!this.grants.has(resource))
15
- this.grants.set(resource, new Set());
16
- this.grants.get(resource).add(action);
13
+ grant(resource, action, options = {}) {
14
+ const key = `${resource}:${action}`;
15
+ this.permissions.set(key, {
16
+ tool: resource, action, allowed: true,
17
+ requiresApproval: options.requiresApproval ?? false,
18
+ note: options.note ?? "",
19
+ });
17
20
  return this;
18
21
  }
19
- revoke(resource, action) {
20
- this.grants.get(resource)?.delete(action);
22
+ revoke(resource, action, note = "") {
23
+ const key = `${resource}:${action}`;
24
+ this.permissions.set(key, { tool: resource, action, allowed: false, requiresApproval: false, note });
21
25
  return this;
22
26
  }
27
+ matchPermission(tool, action) {
28
+ for (const key of [`${tool}:${action}`, `${tool}:*`, `*:${action}`, `*:*`]) {
29
+ const p = this.permissions.get(key);
30
+ if (p)
31
+ return p;
32
+ }
33
+ return undefined;
34
+ }
23
35
  evaluate(resource, action) {
24
36
  if (this.mode === PermissionMode.AUTO)
25
37
  return { allowed: true, reason: "AUTO mode" };
26
38
  if (this.mode === PermissionMode.PLAN)
27
39
  return { allowed: false, reason: "PLAN mode blocks all" };
28
- const actions = this.grants.get(resource);
29
- const allowed = !!(actions && (actions.has(action) || actions.has("*")));
30
- return { allowed, reason: allowed ? "granted" : "not granted" };
40
+ const perm = this.matchPermission(resource, action);
41
+ if (!perm)
42
+ return { allowed: false, reason: "not granted" };
43
+ if (!perm.allowed)
44
+ return { allowed: false, reason: perm.note || "permission denied", matchedPermission: perm };
45
+ if (perm.requiresApproval)
46
+ return { allowed: false, reason: perm.note || "requires approval", requiresApproval: true, matchedPermission: perm };
47
+ return { allowed: true, reason: "granted", matchedPermission: perm };
31
48
  }
32
49
  }
@@ -1,30 +1,34 @@
1
- import type { RuntimeSignal } from "./types.js";
1
+ import type { RuntimeSignal, SignalSource } from "./types.js";
2
2
  import type { ScheduledPrompt } from "./scheduled.js";
3
3
  /**
4
- * SignalGateway — the entry point for all external signals into the agent.
4
+ * SignalGateway — entry point for all external signals into the agent.
5
5
  *
6
- * Responsibilities:
7
- * - Cron scheduling: fires ScheduledPrompts at the right time (deduplicated)
8
- * - Webhook ingestion: converts raw external payloads to RuntimeSignal
9
- * - Routes all signals through the kernel SignalRouter for priority + dedup
6
+ * Implements `SignalSource` so it can be passed directly to `AgentOptions.signalSource`.
7
+ * The gateway maintains an internal FIFO queue; `nextSignal()` drains it one entry at a time
8
+ * on each agent turn.
10
9
  *
11
- * Usage:
12
- * const gateway = new SignalGateway()
13
- * gateway.schedule(new ScheduledPrompt("summarize", Date.now() + 60_000))
14
- * gateway.onSignal(sig => agent.ingestSignal(sig))
10
+ * Responsibilities:
11
+ * - Cron scheduling: fires ScheduledPrompts at the right wall-clock time (idempotent by goal+time)
12
+ * - Webhook / push ingestion: external code calls `ingest()` to push a signal in
13
+ * - Listener API: `onSignal()` for side-channel observers that don't need the pull interface
15
14
  */
16
- export declare class SignalGateway {
15
+ export declare class SignalGateway implements SignalSource {
17
16
  private timers;
17
+ private queue;
18
18
  private listeners;
19
- private router;
20
- init(): Promise<void>;
19
+ /** Called by the agent loop each turn. Returns the oldest queued signal or null. */
20
+ nextSignal(): Promise<RuntimeSignal | null>;
21
+ /** Register a listener that is called synchronously whenever a signal is emitted. */
21
22
  onSignal(listener: (sig: RuntimeSignal) => void): void;
22
- /** Schedule a ScheduledPrompt to fire at its runAtMs. Idempotent by goal. */
23
+ /** Schedule a ScheduledPrompt to fire at its `runAtMs`. Idempotent by goal+time. */
23
24
  schedule(prompt: ScheduledPrompt): void;
24
- /** Cancel a scheduled prompt. */
25
+ /** Cancel a scheduled prompt before it fires. */
25
26
  cancel(goal: string, runAtMs: number): void;
26
- /** Ingest a raw external signal (e.g. from a webhook handler). */
27
+ /** Push a raw signal directly (e.g. from a webhook handler). */
27
28
  ingest(sig: RuntimeSignal): void;
28
- private emit;
29
+ /** Number of signals currently buffered in the queue. */
30
+ get depth(): number;
31
+ /** Clear all pending timers. Call when shutting down to avoid process leaks. */
29
32
  destroy(): void;
33
+ private emit;
30
34
  }
@@ -1,37 +1,34 @@
1
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2
- async function loadKernel() {
3
- return import("@deepstrike/core");
4
- }
5
1
  /**
6
- * SignalGateway — the entry point for all external signals into the agent.
2
+ * SignalGateway — entry point for all external signals into the agent.
7
3
  *
8
- * Responsibilities:
9
- * - Cron scheduling: fires ScheduledPrompts at the right time (deduplicated)
10
- * - Webhook ingestion: converts raw external payloads to RuntimeSignal
11
- * - Routes all signals through the kernel SignalRouter for priority + dedup
4
+ * Implements `SignalSource` so it can be passed directly to `AgentOptions.signalSource`.
5
+ * The gateway maintains an internal FIFO queue; `nextSignal()` drains it one entry at a time
6
+ * on each agent turn.
12
7
  *
13
- * Usage:
14
- * const gateway = new SignalGateway()
15
- * gateway.schedule(new ScheduledPrompt("summarize", Date.now() + 60_000))
16
- * gateway.onSignal(sig => agent.ingestSignal(sig))
8
+ * Responsibilities:
9
+ * - Cron scheduling: fires ScheduledPrompts at the right wall-clock time (idempotent by goal+time)
10
+ * - Webhook / push ingestion: external code calls `ingest()` to push a signal in
11
+ * - Listener API: `onSignal()` for side-channel observers that don't need the pull interface
17
12
  */
18
13
  export class SignalGateway {
19
14
  timers = new Map();
15
+ queue = [];
20
16
  listeners = [];
21
- router = null;
22
- async init() {
23
- const kernel = await loadKernel();
24
- this.router = new kernel.SignalRouter(1024);
17
+ // ── SignalSource interface (pull model) ─────────────────────────────────────
18
+ /** Called by the agent loop each turn. Returns the oldest queued signal or null. */
19
+ async nextSignal() {
20
+ return this.queue.shift() ?? null;
25
21
  }
22
+ // ── Push API ────────────────────────────────────────────────────────────────
23
+ /** Register a listener that is called synchronously whenever a signal is emitted. */
26
24
  onSignal(listener) {
27
25
  this.listeners.push(listener);
28
26
  }
29
- /** Schedule a ScheduledPrompt to fire at its runAtMs. Idempotent by goal. */
27
+ /** Schedule a ScheduledPrompt to fire at its `runAtMs`. Idempotent by goal+time. */
30
28
  schedule(prompt) {
31
29
  const key = `cron:${prompt.goal}:${prompt.runAtMs}`;
32
30
  if (this.timers.has(key))
33
- return; // already scheduled
34
- const delay = prompt.runAtMs - Date.now();
31
+ return;
35
32
  const fire = () => {
36
33
  this.timers.delete(key);
37
34
  this.emit({
@@ -39,6 +36,7 @@ export class SignalGateway {
39
36
  payload: { goal: prompt.goal, criteria: prompt.criteria, runAtMs: prompt.runAtMs, ...prompt.metadata },
40
37
  });
41
38
  };
39
+ const delay = prompt.runAtMs - Date.now();
42
40
  if (delay <= 0) {
43
41
  fire();
44
42
  }
@@ -46,7 +44,7 @@ export class SignalGateway {
46
44
  this.timers.set(key, setTimeout(fire, delay));
47
45
  }
48
46
  }
49
- /** Cancel a scheduled prompt. */
47
+ /** Cancel a scheduled prompt before it fires. */
50
48
  cancel(goal, runAtMs) {
51
49
  const key = `cron:${goal}:${runAtMs}`;
52
50
  const t = this.timers.get(key);
@@ -55,17 +53,25 @@ export class SignalGateway {
55
53
  this.timers.delete(key);
56
54
  }
57
55
  }
58
- /** Ingest a raw external signal (e.g. from a webhook handler). */
56
+ /** Push a raw signal directly (e.g. from a webhook handler). */
59
57
  ingest(sig) {
60
58
  this.emit(sig);
61
59
  }
62
- emit(sig) {
63
- for (const l of this.listeners)
64
- l(sig);
60
+ /** Number of signals currently buffered in the queue. */
61
+ get depth() {
62
+ return this.queue.length;
65
63
  }
64
+ /** Clear all pending timers. Call when shutting down to avoid process leaks. */
66
65
  destroy() {
67
66
  for (const t of this.timers.values())
68
67
  clearTimeout(t);
69
68
  this.timers.clear();
69
+ this.queue.length = 0;
70
+ }
71
+ // ── Internal ────────────────────────────────────────────────────────────────
72
+ emit(sig) {
73
+ this.queue.push(sig);
74
+ for (const l of this.listeners)
75
+ l(sig);
70
76
  }
71
77
  }
@@ -5,7 +5,7 @@ export interface SkillMetadata {
5
5
  effort?: number;
6
6
  estimatedTokens?: number;
7
7
  }
8
- /** Read one skill file and return its full markdown content. */
8
+ /** Read one skill file and return its body (frontmatter stripped). */
9
9
  export declare function readSkillFile(skillDir: string, name: string): Promise<string | null>;
10
10
  /** Scan a skill directory and return frontmatter-only metadata for all `.md` files. */
11
11
  export declare function scanSkillDir(skillDir: string): Promise<SkillMetadata[]>;
@@ -12,10 +12,11 @@ function parseFrontmatter(content) {
12
12
  }
13
13
  return { meta, body: match[2] };
14
14
  }
15
- /** Read one skill file and return its full markdown content. */
15
+ /** Read one skill file and return its body (frontmatter stripped). */
16
16
  export async function readSkillFile(skillDir, name) {
17
17
  try {
18
- return await readFile(path.join(skillDir, `${name}.md`), "utf8");
18
+ const raw = await readFile(path.join(skillDir, `${name}.md`), "utf8");
19
+ return parseFrontmatter(raw).body;
19
20
  }
20
21
  catch {
21
22
  return null;
package/dist/types.d.ts CHANGED
@@ -1,6 +1,32 @@
1
+ export interface TextPart {
2
+ type: "text";
3
+ text: string;
4
+ }
5
+ export interface ImagePart {
6
+ type: "image";
7
+ /** Remote image URL (mutually exclusive with `data`). */
8
+ url?: string;
9
+ /** Raw base64-encoded image bytes (mutually exclusive with `url`). */
10
+ data?: string;
11
+ /** MIME type, e.g. `"image/png"`. Required when `data` is set. */
12
+ mediaType?: string;
13
+ /** OpenAI vision detail level. */
14
+ detail?: "auto" | "low" | "high";
15
+ }
16
+ export interface AudioPart {
17
+ type: "audio";
18
+ /** Raw base64-encoded audio bytes. */
19
+ data: string;
20
+ /** MIME type, e.g. `"audio/wav"`. */
21
+ mediaType: string;
22
+ }
23
+ export type ContentPart = TextPart | ImagePart | AudioPart;
1
24
  export interface Message {
2
25
  role: "system" | "user" | "assistant" | "tool";
26
+ /** Plain-text content. When `contentParts` is present, this holds only the text segments. */
3
27
  content: string;
28
+ /** Structured multimodal content. When present, takes precedence over `content` for provider calls. */
29
+ contentParts?: ContentPart[];
4
30
  tokenCount?: number;
5
31
  toolCalls?: ToolCall[];
6
32
  }
@@ -61,6 +87,16 @@ export interface PermissionRequestEvent extends StreamEvent {
61
87
  arguments: string;
62
88
  reason: string;
63
89
  }
90
+ export interface TokenUsage {
91
+ inputTokens: number;
92
+ outputTokens: number;
93
+ totalTokens: number;
94
+ }
95
+ export interface ProviderToolSpec {
96
+ name: string;
97
+ description: string;
98
+ parameters: Record<string, unknown>;
99
+ }
64
100
  export interface RetryConfig {
65
101
  maxRetries?: number;
66
102
  baseDelay?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepstrike/sdk",
3
- "version": "0.1.1",
3
+ "version": "0.1.4",
4
4
  "description": "DeepStrike Node.js SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "@anthropic-ai/sdk": "^0.39.0",
15
- "@deepstrike/core": "0.1.0",
15
+ "@deepstrike/core": "0.1.4",
16
16
  "openai": "^4.77.0"
17
17
  },
18
18
  "devDependencies": {