@deepstrike/sdk 0.2.62 → 0.2.64

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.
@@ -6,6 +6,65 @@ import { primitiveForKind } from "./kernel-event-log.js";
6
6
  import { KeyedSerialExecutor } from "./reliability.js";
7
7
  import { FileKernelJournal, InMemoryKernelJournal } from "./kernel-journal.js";
8
8
  import { decodeDurableContent } from "./durable-content.js";
9
+ /**
10
+ * The registered session-event vocabulary (F9 / S3, P7-S4). This list is the single authority
11
+ * the cross-SDK manifest fixture pins: a kind added here without the same-commit update to
12
+ * `tests/fixtures/sdk-conformance/canonical/session-event-vocabulary.json` and the python/wasm
13
+ * vocabularies turns cross-SDK conformance red. Declared in `SessionEvent` union order.
14
+ */
15
+ export const SESSION_EVENT_KINDS = [
16
+ "run_started",
17
+ "llm_completed",
18
+ "prompt_measured",
19
+ "provider_attempt",
20
+ "tool_requested",
21
+ "tool_completed",
22
+ "tool_argument_repaired",
23
+ "tool_denied",
24
+ "permission_requested",
25
+ "permission_resolved",
26
+ "compressed",
27
+ "page_out",
28
+ "semantic_archive_pending",
29
+ "semantic_archive_completed",
30
+ "semantic_archive_failed",
31
+ "page_in",
32
+ "rollbacked",
33
+ "capability_changed",
34
+ "context_renewed",
35
+ "suspended",
36
+ "resumed",
37
+ "tool_gated",
38
+ "signal_delivery_disposed",
39
+ "budget_exceeded",
40
+ "budget_usage_reported",
41
+ "operation_cancelled",
42
+ "milestone_advanced",
43
+ "milestone_blocked",
44
+ "checkpoint_taken",
45
+ "entropy_sample",
46
+ "entropy_alert",
47
+ "agent_process_changed",
48
+ "memory_written",
49
+ "memory_queried",
50
+ "memory_validation_failed",
51
+ "memory_write_failed",
52
+ "memory_query_failed",
53
+ "memory_retrieval_result",
54
+ "workflow_node_completed",
55
+ "workflow_nodes_submitted",
56
+ "workflow_batch_spawned",
57
+ "workflow_completed",
58
+ "kernel_observation",
59
+ "run_terminal",
60
+ "summary_upgraded",
61
+ "group_member_joined",
62
+ "group_budget_charged",
63
+ "round_started",
64
+ "round_paced",
65
+ ];
66
+ const _vocabularyCoversUnion = true;
67
+ void _vocabularyCoversUnion;
9
68
  /**
10
69
  * **Single-process dev/test implementation** of both capabilities (spec §9.4: one class may
11
70
  * implement several capabilities; the *interfaces* stay separate). Its `KernelJournal` half is
@@ -158,6 +217,15 @@ function decodePersistedSessionRecord(value) {
158
217
  if (event.kind === "llm_completed" && event.provider_replay !== undefined) {
159
218
  assertCanonicalProviderReplay(event.provider_replay);
160
219
  }
220
+ if (event.kind === "llm_completed" && event.wire_evidence !== undefined) {
221
+ assertCanonicalWireEvidence(event.wire_evidence);
222
+ }
223
+ if (event.kind === "run_started" && event.route !== undefined) {
224
+ assertCanonicalRoute(event.route);
225
+ }
226
+ if (event.kind === "provider_attempt") {
227
+ assertCanonicalProviderAttempt(event);
228
+ }
161
229
  if (event.kind === "tool_completed") {
162
230
  if (!Array.isArray(event.results))
163
231
  throw new Error("tool_completed results must be an array");
@@ -183,3 +251,79 @@ function assertCanonicalProviderReplay(value) {
183
251
  if (typeof replay.protocol !== "string" || replay.protocol.length === 0)
184
252
  throw new Error("provider replay protocol is required");
185
253
  }
254
+ const WIRE_EVIDENCE_KEYS = new Set(["protocol", "request_fingerprint", "response_id", "raw_usage", "replay_state"]);
255
+ /** P3 §3.3: raw_usage carries BoundedJson semantics — the 4KB cap is enforced at the boundary. */
256
+ const WIRE_EVIDENCE_RAW_USAGE_MAX_BYTES = 4096;
257
+ function assertCanonicalWireEvidence(value) {
258
+ if (!value || typeof value !== "object" || Array.isArray(value))
259
+ throw new Error("wire evidence must be an object");
260
+ const evidence = value;
261
+ for (const key of Object.keys(evidence))
262
+ if (!WIRE_EVIDENCE_KEYS.has(key))
263
+ throw new Error(`wire evidence has unknown field ${key}`);
264
+ if (typeof evidence.protocol !== "string" || evidence.protocol.length === 0)
265
+ throw new Error("wire evidence protocol is required");
266
+ // G2: the fingerprint is mandatory non-empty — it is what binds this evidence to a request plan.
267
+ if (typeof evidence.request_fingerprint !== "string" || evidence.request_fingerprint.length === 0) {
268
+ throw new Error("wire evidence request_fingerprint is required");
269
+ }
270
+ if (evidence.response_id !== undefined && typeof evidence.response_id !== "string") {
271
+ throw new Error("wire evidence response_id must be a string");
272
+ }
273
+ if (evidence.raw_usage !== undefined
274
+ && new TextEncoder().encode(JSON.stringify(evidence.raw_usage)).byteLength > WIRE_EVIDENCE_RAW_USAGE_MAX_BYTES) {
275
+ throw new Error("wire evidence raw_usage exceeds the 4KB BoundedJson cap");
276
+ }
277
+ if (evidence.replay_state !== undefined)
278
+ assertCanonicalProviderReplay(evidence.replay_state);
279
+ }
280
+ function assertCanonicalRoute(value) {
281
+ if (!value || typeof value !== "object" || Array.isArray(value))
282
+ throw new Error("route must be an object");
283
+ const route = value;
284
+ for (const field of ["routeId", "provider", "protocol", "model", "adapterVersion", "capabilitiesRef"]) {
285
+ if (typeof route[field] !== "string" || route[field].length === 0) {
286
+ throw new Error(`route ${field} must be a non-empty string`);
287
+ }
288
+ }
289
+ if (!route.endpoint || typeof route.endpoint !== "object" || Array.isArray(route.endpoint)) {
290
+ throw new Error("route endpoint must be an object");
291
+ }
292
+ }
293
+ const PROVIDER_ATTEMPT_KEYS = new Set([
294
+ "kind", "effect_id", "attempt_seq", "route", "request_fingerprint", "status", "transport_rungs",
295
+ "last_error_class", "started_at_ms", "finished_at_ms", "usage", "wire_evidence", "accounting_policy_id",
296
+ ]);
297
+ const PROVIDER_ATTEMPT_STATUSES = new Set(["success", "transport_exhausted", "aborted", "rejected"]);
298
+ function assertCanonicalProviderAttempt(event) {
299
+ for (const key of Object.keys(event))
300
+ if (!PROVIDER_ATTEMPT_KEYS.has(key))
301
+ throw new Error(`provider_attempt has unknown field ${key}`);
302
+ if (typeof event.effect_id !== "string" || event.effect_id.length === 0)
303
+ throw new Error("provider_attempt effect_id is required");
304
+ if (!Number.isInteger(event.attempt_seq) || event.attempt_seq < 1)
305
+ throw new Error("provider_attempt attempt_seq must be a positive integer");
306
+ assertCanonicalRoute(event.route);
307
+ if (typeof event.request_fingerprint !== "string" || event.request_fingerprint.length === 0) {
308
+ throw new Error("provider_attempt request_fingerprint is required");
309
+ }
310
+ if (typeof event.status !== "string" || !PROVIDER_ATTEMPT_STATUSES.has(event.status)) {
311
+ throw new Error("provider_attempt status must be success|transport_exhausted|aborted|rejected");
312
+ }
313
+ if (!Number.isInteger(event.transport_rungs) || event.transport_rungs < 0) {
314
+ throw new Error("provider_attempt transport_rungs must be a non-negative integer");
315
+ }
316
+ if (event.last_error_class !== undefined && typeof event.last_error_class !== "string") {
317
+ throw new Error("provider_attempt last_error_class must be a string");
318
+ }
319
+ for (const field of ["started_at_ms", "finished_at_ms"]) {
320
+ if (typeof event[field] !== "number" || !Number.isFinite(event[field])) {
321
+ throw new Error(`provider_attempt ${field} must be a finite number`);
322
+ }
323
+ }
324
+ if (event.wire_evidence !== undefined)
325
+ assertCanonicalWireEvidence(event.wire_evidence);
326
+ if (event.accounting_policy_id !== undefined && typeof event.accounting_policy_id !== "string") {
327
+ throw new Error("provider_attempt accounting_policy_id must be a string");
328
+ }
329
+ }
@@ -1,4 +1,4 @@
1
- import type { Message, ProviderReplay, ToolCall } from "../types.js";
1
+ import type { Message, ProviderReplay, ProviderWireEvidence, ToolCall } from "../types.js";
2
2
  import type { SessionEvent } from "./session-log.js";
3
3
  import type { WorkflowNodeStatus } from "../types/agent.js";
4
4
  export { REPLAY_CONTENT_MAX_BYTES as RECOVERY_CONTENT_MAX_BYTES } from "./replay-sanitize.js";
@@ -10,6 +10,10 @@ export { REPLAY_CONTENT_MAX_BYTES as RECOVERY_CONTENT_MAX_BYTES } from "./replay
10
10
  * provider-neutral and must never synthesize protocol-specific replay shapes
11
11
  * (e.g. Anthropic `native_blocks`). Canonical replay seeding for a given protocol
12
12
  * is the responsibility of that provider's `seedProviderReplay`.
13
+ *
14
+ * The P3-S2/P4-S1 evidence fields (`effect_id`, `invocation_id`, `wire_evidence`)
15
+ * likewise pass through verbatim: they are host evidence joining this projection to
16
+ * the journal effect chain, and recovery must not rewrite evidence.
13
17
  */
14
18
  export declare function normalizeLlmCompleted(event: Extract<SessionEvent, {
15
19
  kind: "llm_completed";
@@ -31,6 +35,9 @@ export declare function buildLlmCompletedEvent(input: {
31
35
  tokenCount?: number;
32
36
  toolCalls: ToolCall[];
33
37
  providerReplay?: ProviderReplay;
38
+ effectId?: string;
39
+ invocationId?: string;
40
+ wireEvidence?: ProviderWireEvidence;
34
41
  }): Extract<SessionEvent, {
35
42
  kind: "llm_completed";
36
43
  }>;
@@ -11,6 +11,10 @@ function estimateTokenCount(text) {
11
11
  * provider-neutral and must never synthesize protocol-specific replay shapes
12
12
  * (e.g. Anthropic `native_blocks`). Canonical replay seeding for a given protocol
13
13
  * is the responsibility of that provider's `seedProviderReplay`.
14
+ *
15
+ * The P3-S2/P4-S1 evidence fields (`effect_id`, `invocation_id`, `wire_evidence`)
16
+ * likewise pass through verbatim: they are host evidence joining this projection to
17
+ * the journal effect chain, and recovery must not rewrite evidence.
14
18
  */
15
19
  export function normalizeLlmCompleted(event, maxBytes) {
16
20
  const content = sanitizeReplayText(event.content ?? "", maxBytes);
@@ -23,6 +27,9 @@ export function normalizeLlmCompleted(event, maxBytes) {
23
27
  tool_calls: toolCalls,
24
28
  token_count: event.token_count ?? estimateTokenCount(content),
25
29
  ...(providerReplay ? { provider_replay: providerReplay } : {}),
30
+ ...(event.effect_id !== undefined ? { effect_id: event.effect_id } : {}),
31
+ ...(event.invocation_id !== undefined ? { invocation_id: event.invocation_id } : {}),
32
+ ...(event.wire_evidence !== undefined ? { wire_evidence: event.wire_evidence } : {}),
26
33
  };
27
34
  }
28
35
  /** Repair event log for recovery minimum set before preload/wake. */
@@ -42,6 +49,9 @@ export function buildLlmCompletedEvent(input) {
42
49
  tool_calls: input.toolCalls ?? [],
43
50
  token_count: input.tokenCount,
44
51
  provider_replay: input.providerReplay,
52
+ ...(input.effectId !== undefined ? { effect_id: input.effectId } : {}),
53
+ ...(input.invocationId !== undefined ? { invocation_id: input.invocationId } : {}),
54
+ ...(input.wireEvidence !== undefined ? { wire_evidence: input.wireEvidence } : {}),
45
55
  });
46
56
  }
47
57
  /** Build run_terminal with required recovery fields. */
package/dist/types.d.ts CHANGED
@@ -445,6 +445,31 @@ export interface ProviderReplay {
445
445
  native_message?: unknown;
446
446
  tool_calls?: unknown[];
447
447
  }
448
+ /**
449
+ * P3 §3.3 (D1): the wire-level evidence bundle for one provider call — what was actually
450
+ * asked (fingerprint) and what came back (response id, raw usage). SessionLog evidence only;
451
+ * nothing here ever crosses into kernel state (B1/B4).
452
+ */
453
+ export interface ProviderWireEvidence {
454
+ protocol: import("./providers/protocol-capabilities.js").GenerationProtocol;
455
+ /** → ProviderRequestPlan.fingerprint. Mandatory non-empty (G2). */
456
+ request_fingerprint: string;
457
+ /** G3: OpenAI response id / Anthropic message id / protocol equivalent, when the wire exposes one. */
458
+ response_id?: string;
459
+ /** BoundedJson semantics: producers truncate to ≤4KB before landing it here. */
460
+ raw_usage?: unknown;
461
+ /** The former `llm_completed.provider_replay` field, carried verbatim (P3-S2 migration). */
462
+ replay_state?: ProviderReplay;
463
+ }
464
+ /**
465
+ * P4 §1.2: host-observed transport facts for one provider execution. `rungs` counts the HTTP
466
+ * attempts inside the provider's transport ladder (1 when the call path never retries, e.g.
467
+ * streaming); rung-level detail stays in adapter debug logs, never in SessionLog.
468
+ */
469
+ export interface ProviderTransportTelemetry {
470
+ rungs: number;
471
+ responseId?: string;
472
+ }
448
473
  /** Result of a pre-flight reasoning-replay assessment for a target provider. */
449
474
  export interface ReplayabilityAssessment {
450
475
  /** True when every reasoning-requiring tool-call turn has replay available. */
@@ -506,6 +531,13 @@ export interface LLMProvider {
506
531
  runtimePolicy?(): RuntimePolicy;
507
532
  /** Read provider-native replay fields captured after the most recent assistant turn. */
508
533
  peekProviderReplay?(message: Pick<Message, "content" | "toolCalls">): ProviderReplay | undefined;
534
+ /**
535
+ * P4-S1: read the transport facts captured during the most recent execution (HTTP rung count,
536
+ * wire response id). Optional — a provider without it simply omits the telemetry and the
537
+ * runner falls back to `rungs: 1` with no response id. Pure host evidence (B7); the values
538
+ * never feed kernel input.
539
+ */
540
+ peekTransportTelemetry?(): ProviderTransportTelemetry | undefined;
509
541
  /** Restore provider-native replay fields when rebuilding history from SessionLog. */
510
542
  seedProviderReplay?(message: Pick<Message, "content" | "toolCalls">, replay: ProviderReplay): void;
511
543
  /**
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@deepstrike/sdk",
3
- "version": "0.2.62",
3
+ "version": "0.2.64",
4
4
  "description": "DeepStrike Node.js SDK",
5
+ "license": "SEE LICENSE IN LICENSE",
5
6
  "type": "module",
6
7
  "main": "dist/index.js",
7
8
  "types": "dist/index.d.ts",
@@ -72,7 +73,7 @@
72
73
  },
73
74
  "dependencies": {
74
75
  "@anthropic-ai/sdk": "^0.99.0",
75
- "@deepstrike/core": "0.2.62",
76
+ "@deepstrike/core": "0.2.64",
76
77
  "@google/generative-ai": "^0.24.1",
77
78
  "openai": "^7.5.0"
78
79
  },