@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.
- package/README.md +150 -167
- package/dist/agent.d.ts +37 -13
- package/dist/agent.js +121 -62
- package/dist/harness/harness.d.ts +24 -10
- package/dist/harness/harness.js +42 -42
- package/dist/index.d.ts +7 -5
- package/dist/index.js +9 -2
- package/dist/knowledge/source.d.ts +2 -0
- package/dist/memory/protocols.d.ts +2 -0
- package/dist/providers/anthropic.js +3 -3
- package/dist/providers/base.d.ts +3 -0
- package/dist/providers/base.js +34 -0
- package/dist/providers/ollama.js +10 -1
- package/dist/providers/openai.d.ts +7 -0
- package/dist/providers/openai.js +63 -6
- package/dist/safety/permissions.d.ts +16 -3
- package/dist/safety/permissions.js +27 -10
- package/dist/signals/gateway.d.ts +21 -17
- package/dist/signals/gateway.js +31 -25
- package/dist/skills/loader.d.ts +1 -1
- package/dist/skills/loader.js +3 -2
- package/dist/types.d.ts +36 -0
- package/package.json +2 -2
package/dist/providers/base.js
CHANGED
|
@@ -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
|
+
}
|
package/dist/providers/ollama.js
CHANGED
|
@@ -7,7 +7,16 @@ export class OllamaProvider {
|
|
|
7
7
|
this.baseUrl = baseUrl;
|
|
8
8
|
}
|
|
9
9
|
toOllamaMessages(messages) {
|
|
10
|
-
return messages.map(m =>
|
|
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
|
+
}
|
package/dist/providers/openai.js
CHANGED
|
@@ -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
|
|
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
|
|
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-
|
|
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
|
|
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
|
|
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
|
|
21
|
+
private permissions;
|
|
13
22
|
constructor(mode?: PermissionMode);
|
|
14
|
-
grant(resource: string, action: string
|
|
15
|
-
|
|
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
|
-
|
|
9
|
+
permissions = new Map();
|
|
10
10
|
constructor(mode = PermissionMode.DEFAULT) {
|
|
11
11
|
this.mode = mode;
|
|
12
12
|
}
|
|
13
|
-
grant(resource, action) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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
|
|
29
|
-
|
|
30
|
-
|
|
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 —
|
|
4
|
+
* SignalGateway — entry point for all external signals into the agent.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
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
|
-
|
|
20
|
-
|
|
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
|
|
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
|
-
/**
|
|
27
|
+
/** Push a raw signal directly (e.g. from a webhook handler). */
|
|
27
28
|
ingest(sig: RuntimeSignal): void;
|
|
28
|
-
|
|
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
|
}
|
package/dist/signals/gateway.js
CHANGED
|
@@ -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 —
|
|
2
|
+
* SignalGateway — entry point for all external signals into the agent.
|
|
7
3
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
this.
|
|
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
|
|
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;
|
|
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
|
-
/**
|
|
56
|
+
/** Push a raw signal directly (e.g. from a webhook handler). */
|
|
59
57
|
ingest(sig) {
|
|
60
58
|
this.emit(sig);
|
|
61
59
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
}
|
package/dist/skills/loader.d.ts
CHANGED
|
@@ -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
|
|
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[]>;
|
package/dist/skills/loader.js
CHANGED
|
@@ -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
|
|
15
|
+
/** Read one skill file and return its body (frontmatter stripped). */
|
|
16
16
|
export async function readSkillFile(skillDir, name) {
|
|
17
17
|
try {
|
|
18
|
-
|
|
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.
|
|
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.
|
|
15
|
+
"@deepstrike/core": "0.1.4",
|
|
16
16
|
"openai": "^4.77.0"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|