@deepstrike/sdk 0.2.42 → 0.2.44
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.
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { RuntimeRunner } from "../runtime/runner.js";
|
|
2
2
|
import type { SessionEvent } from "../runtime/session-log.js";
|
|
3
|
+
import type { ContentPart } from "../types.js";
|
|
3
4
|
import type { WorkflowNodeSpec } from "../types/agent.js";
|
|
4
5
|
import type { Criterion, Verdict } from "../runtime/eval.js";
|
|
5
6
|
import type { AttemptJudge, JudgeResult } from "./judge.js";
|
|
@@ -8,6 +9,12 @@ export interface AttemptRequest {
|
|
|
8
9
|
sessionId?: string;
|
|
9
10
|
goal: string;
|
|
10
11
|
criteria?: Criterion[];
|
|
12
|
+
/**
|
|
13
|
+
* Multimodal inputs (images / audio) attached to the task. Forwarded to every attempt
|
|
14
|
+
* unconditionally; the runner seeds them per session idempotently, so fresh-session carries
|
|
15
|
+
* re-seed while same-session carries do not double.
|
|
16
|
+
*/
|
|
17
|
+
attachments?: ContentPart[];
|
|
11
18
|
extensions?: Record<string, unknown>;
|
|
12
19
|
/** Parent transcript inherited by the first attempt only. */
|
|
13
20
|
inheritEvents?: Array<{
|
package/dist/harness/harness.js
CHANGED
|
@@ -14,6 +14,7 @@ export class RuntimeAttemptBody {
|
|
|
14
14
|
sessionId: context.sessionId,
|
|
15
15
|
goal: context.goal,
|
|
16
16
|
criteria: (context.criteria ?? []).map(criterion => criterion.text),
|
|
17
|
+
...(context.attachments?.length ? { attachments: context.attachments } : {}),
|
|
17
18
|
extensions: context.extensions,
|
|
18
19
|
...(context.attempt === 1 && context.inheritEvents
|
|
19
20
|
? { inheritEvents: context.inheritEvents }
|
|
@@ -19,7 +19,6 @@ export function restoreKernelRuntime(runtime, snapshot) {
|
|
|
19
19
|
}, 1);
|
|
20
20
|
kernelWireStates.set(runtime, { operationId, nextEventSequence });
|
|
21
21
|
}
|
|
22
|
-
let nextOperationSequence = 1;
|
|
23
22
|
const kernelWireStates = new WeakMap();
|
|
24
23
|
function tryParseJson(s) {
|
|
25
24
|
try {
|
|
@@ -370,8 +369,12 @@ function mapKernelAction(raw) {
|
|
|
370
369
|
function stepInput(runtime, event) {
|
|
371
370
|
let state = kernelWireStates.get(runtime);
|
|
372
371
|
if (!state) {
|
|
372
|
+
// Globally unique, never a process-local counter: durable session logs key the kernel
|
|
373
|
+
// genesis/transaction chains by (sessionId, operationId) and outlive this process, so a
|
|
374
|
+
// counter that restarts at 1 collides with yesterday's chain on the same session (genesis
|
|
375
|
+
// digest conflict, or step_seq successor violation when the policy digest happens to match).
|
|
373
376
|
state = {
|
|
374
|
-
operationId: `node-operation-${
|
|
377
|
+
operationId: `node-operation-${crypto.randomUUID()}`,
|
|
375
378
|
nextEventSequence: 1,
|
|
376
379
|
};
|
|
377
380
|
kernelWireStates.set(runtime, state);
|
|
@@ -74,7 +74,7 @@ export declare class LargeResultSpool {
|
|
|
74
74
|
/**
|
|
75
75
|
* Persist a kernel-spooled tool output to disk. Returns the on-disk path ref.
|
|
76
76
|
*/
|
|
77
|
-
persistOutput(callId: string, content: string): Promise<string>;
|
|
77
|
+
persistOutput(sessionId: string, callId: string, content: string): Promise<string>;
|
|
78
78
|
/**
|
|
79
79
|
* Read a spooled result back from disk.
|
|
80
80
|
*/
|
|
@@ -85,7 +85,7 @@ export declare class LargeResultSpool {
|
|
|
85
85
|
* for the hashed call-key prefix; returns `undefined` if nothing was ever spooled
|
|
86
86
|
* for that call (e.g. it never actually exceeded the threshold, or the spool dir was cleaned up).
|
|
87
87
|
*/
|
|
88
|
-
findByCallId(callId: string): Promise<string | undefined>;
|
|
88
|
+
findByCallId(sessionId: string, callId: string): Promise<string | undefined>;
|
|
89
89
|
/**
|
|
90
90
|
* Clean up old spool files (optional maintenance).
|
|
91
91
|
*/
|
|
@@ -46,8 +46,11 @@ export class LargeResultSpool {
|
|
|
46
46
|
getSpoolPath(hash) {
|
|
47
47
|
return path.join(this.spoolDir, `${hash}.txt`);
|
|
48
48
|
}
|
|
49
|
-
callKey(callId) {
|
|
50
|
-
|
|
49
|
+
callKey(sessionId, callId) {
|
|
50
|
+
// Session-scoped: the spool dir is shared across sessions and outlives runs, while vendor
|
|
51
|
+
// call ids can be index-style ("call_0") and repeat — an unscoped key lets read_result in
|
|
52
|
+
// one session fetch another session's spooled output.
|
|
53
|
+
return this.hashContent(`${sessionId}\u0000${callId}`).slice(0, 32);
|
|
51
54
|
}
|
|
52
55
|
async atomicWrite(spoolPath, content) {
|
|
53
56
|
const tempPath = `${spoolPath}.${process.pid}.${crypto.randomUUID()}.tmp`;
|
|
@@ -128,9 +131,9 @@ omitted: ${omitted} chars
|
|
|
128
131
|
/**
|
|
129
132
|
* Persist a kernel-spooled tool output to disk. Returns the on-disk path ref.
|
|
130
133
|
*/
|
|
131
|
-
async persistOutput(callId, content) {
|
|
134
|
+
async persistOutput(sessionId, callId, content) {
|
|
132
135
|
const hash = this.hashContent(content);
|
|
133
|
-
const spoolPath = this.getSpoolPath(`${this.callKey(callId)}-${hash.slice(0, 16)}`);
|
|
136
|
+
const spoolPath = this.getSpoolPath(`${this.callKey(sessionId, callId)}-${hash.slice(0, 16)}`);
|
|
134
137
|
let promise = this.activeWrites.get(spoolPath);
|
|
135
138
|
if (!promise) {
|
|
136
139
|
promise = (async () => {
|
|
@@ -165,7 +168,7 @@ omitted: ${omitted} chars
|
|
|
165
168
|
* for the hashed call-key prefix; returns `undefined` if nothing was ever spooled
|
|
166
169
|
* for that call (e.g. it never actually exceeded the threshold, or the spool dir was cleaned up).
|
|
167
170
|
*/
|
|
168
|
-
async findByCallId(callId) {
|
|
171
|
+
async findByCallId(sessionId, callId) {
|
|
169
172
|
let files;
|
|
170
173
|
try {
|
|
171
174
|
files = await fs.readdir(this.spoolDir);
|
|
@@ -173,7 +176,7 @@ omitted: ${omitted} chars
|
|
|
173
176
|
catch {
|
|
174
177
|
return undefined;
|
|
175
178
|
}
|
|
176
|
-
const prefix = `${this.callKey(callId)}-`;
|
|
179
|
+
const prefix = `${this.callKey(sessionId, callId)}-`;
|
|
177
180
|
const match = files.find(f => f.startsWith(prefix) && f.endsWith('.txt'));
|
|
178
181
|
if (!match)
|
|
179
182
|
return undefined;
|
package/dist/runtime/runner.js
CHANGED
|
@@ -1208,6 +1208,12 @@ export class RuntimeRunner {
|
|
|
1208
1208
|
const runId = midRun && resumedStart?.event.kind === "run_started"
|
|
1209
1209
|
? resumedStart.event.run_id
|
|
1210
1210
|
: crypto.randomUUID();
|
|
1211
|
+
// Idempotent per session: an earlier run's `run_started` already carries these attachments
|
|
1212
|
+
// (same-session retry attempt), so replay reconstructs them — recording and seeding again
|
|
1213
|
+
// would double them in history. Deduping at the append keeps live and replay in agreement.
|
|
1214
|
+
const attachments = req.attachments?.length && !attachmentsAlreadySeeded(prior, req.attachments)
|
|
1215
|
+
? req.attachments
|
|
1216
|
+
: undefined;
|
|
1211
1217
|
if (!midRun) {
|
|
1212
1218
|
await this.opts.sessionLog.append(req.sessionId, {
|
|
1213
1219
|
kind: "run_started",
|
|
@@ -1216,10 +1222,10 @@ export class RuntimeRunner {
|
|
|
1216
1222
|
criteria: req.criteria ?? [],
|
|
1217
1223
|
agent_id: this.opts.agentId,
|
|
1218
1224
|
system_prompt: this.opts.systemPrompt,
|
|
1219
|
-
...(
|
|
1225
|
+
...(attachments ? { attachments } : {}),
|
|
1220
1226
|
});
|
|
1221
1227
|
}
|
|
1222
|
-
yield* this.execute(req.sessionId, req.goal, req.criteria ?? [], req.extensions, prior.length > 0 ? prior : undefined, midRun,
|
|
1228
|
+
yield* this.execute(req.sessionId, req.goal, req.criteria ?? [], req.extensions, prior.length > 0 ? prior : undefined, midRun, attachments, runId);
|
|
1223
1229
|
}
|
|
1224
1230
|
async *wake(sessionId, extensions) {
|
|
1225
1231
|
const events = await this.opts.sessionLog.read(sessionId);
|
|
@@ -1334,7 +1340,7 @@ export class RuntimeRunner {
|
|
|
1334
1340
|
let full;
|
|
1335
1341
|
const spool = this.opts.resultSpool ?? new LargeResultSpool();
|
|
1336
1342
|
try {
|
|
1337
|
-
full = await spool.findByCallId(callId);
|
|
1343
|
+
full = await spool.findByCallId(sessionId, callId);
|
|
1338
1344
|
}
|
|
1339
1345
|
catch {
|
|
1340
1346
|
full = undefined;
|
|
@@ -1850,7 +1856,7 @@ export class RuntimeRunner {
|
|
|
1850
1856
|
let spoolRef;
|
|
1851
1857
|
let error;
|
|
1852
1858
|
try {
|
|
1853
|
-
spoolRef = await spool.persistOutput(action.callId, action.output);
|
|
1859
|
+
spoolRef = await spool.persistOutput(sessionId, action.callId, action.output);
|
|
1854
1860
|
}
|
|
1855
1861
|
catch (cause) {
|
|
1856
1862
|
error = formatToolError(cause);
|
|
@@ -2467,6 +2473,15 @@ function isMidRun(events) {
|
|
|
2467
2473
|
}
|
|
2468
2474
|
return lastStarted >= 0 && lastStarted > lastTerminal;
|
|
2469
2475
|
}
|
|
2476
|
+
/**
|
|
2477
|
+
* True when an earlier run in this session already seeded the same attachments. Replay
|
|
2478
|
+
* reconstructs the attachment message from that run's `run_started`, so recording and
|
|
2479
|
+
* live-seeding them again (a same-session retry attempt) would double them in history.
|
|
2480
|
+
*/
|
|
2481
|
+
function attachmentsAlreadySeeded(prior, attachments) {
|
|
2482
|
+
const wanted = JSON.stringify(attachments);
|
|
2483
|
+
return prior.some(({ event }) => event.kind === "run_started" && JSON.stringify(event.attachments ?? []) === wanted);
|
|
2484
|
+
}
|
|
2470
2485
|
/**
|
|
2471
2486
|
* Build a kernel `add_history_message` payload from user attachments: a `user`
|
|
2472
2487
|
* message whose content is the multimodal parts in the kernel's serde shape
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepstrike/sdk",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.44",
|
|
4
4
|
"description": "DeepStrike Node.js SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
},
|
|
73
73
|
"dependencies": {
|
|
74
74
|
"@anthropic-ai/sdk": "^0.99.0",
|
|
75
|
-
"@deepstrike/core": "0.2.
|
|
75
|
+
"@deepstrike/core": "0.2.44",
|
|
76
76
|
"@google/generative-ai": "^0.24.1",
|
|
77
77
|
"openai": "^5.23.2"
|
|
78
78
|
},
|