@deepstrike/sdk 0.2.38 → 0.2.40

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.
Files changed (102) hide show
  1. package/README.md +61 -31
  2. package/dist/collaboration/contract.d.ts +2 -2
  3. package/dist/collaboration/contract.js +2 -2
  4. package/dist/collaboration/handoff.d.ts +2 -14
  5. package/dist/collaboration/handoff.js +1 -17
  6. package/dist/collaboration/harness.d.ts +16 -47
  7. package/dist/collaboration/harness.js +57 -158
  8. package/dist/collaboration/index.d.ts +2 -2
  9. package/dist/collaboration/index.js +1 -2
  10. package/dist/collaboration/modes/creator-verifier.js +42 -6
  11. package/dist/collaboration/pool.d.ts +8 -0
  12. package/dist/collaboration/pool.js +56 -3
  13. package/dist/harness/harness.d.ts +97 -97
  14. package/dist/harness/harness.js +190 -144
  15. package/dist/harness/judge.d.ts +3 -2
  16. package/dist/harness/judge.js +7 -3
  17. package/dist/harness/public.d.ts +5 -3
  18. package/dist/harness/public.js +3 -2
  19. package/dist/index.d.ts +22 -7
  20. package/dist/index.js +8 -1
  21. package/dist/kernel.d.ts +12 -39
  22. package/dist/memory/agent.d.ts +8 -41
  23. package/dist/memory/agent.js +34 -117
  24. package/dist/memory/extraction.d.ts +4 -0
  25. package/dist/memory/extraction.js +79 -0
  26. package/dist/memory/in-memory-store.d.ts +28 -15
  27. package/dist/memory/in-memory-store.js +74 -25
  28. package/dist/memory/protocols.d.ts +56 -56
  29. package/dist/memory/protocols.js +1 -0
  30. package/dist/memory/public.d.ts +3 -1
  31. package/dist/memory/public.js +1 -0
  32. package/dist/memory/ranking.d.ts +33 -0
  33. package/dist/memory/ranking.js +77 -0
  34. package/dist/memory/retention.d.ts +17 -0
  35. package/dist/memory/retention.js +54 -0
  36. package/dist/os/public.d.ts +3 -3
  37. package/dist/os/public.js +1 -1
  38. package/dist/providers/base.d.ts +8 -0
  39. package/dist/providers/base.js +0 -0
  40. package/dist/providers/gemini.js +12 -0
  41. package/dist/providers/ollama.js +3 -1
  42. package/dist/providers/openai-responses.js +9 -2
  43. package/dist/providers/profiles.d.ts +5 -3
  44. package/dist/providers/profiles.js +3 -3
  45. package/dist/runtime/context-policy.d.ts +35 -0
  46. package/dist/runtime/context-policy.js +66 -0
  47. package/dist/runtime/eval.d.ts +6 -2
  48. package/dist/runtime/eval.js +2 -2
  49. package/dist/runtime/event-stream.d.ts +9 -0
  50. package/dist/runtime/event-stream.js +25 -11
  51. package/dist/runtime/execution-plane.d.ts +5 -1
  52. package/dist/runtime/execution-plane.js +9 -3
  53. package/dist/runtime/facade.js +2 -1
  54. package/dist/runtime/kernel-event-log.d.ts +0 -2
  55. package/dist/runtime/kernel-event-log.js +61 -13
  56. package/dist/runtime/kernel-primitives-dashboard.js +1 -1
  57. package/dist/runtime/kernel-rebuild.d.ts +13 -0
  58. package/dist/runtime/kernel-rebuild.js +75 -0
  59. package/dist/runtime/kernel-step.d.ts +157 -8
  60. package/dist/runtime/kernel-step.js +220 -7
  61. package/dist/runtime/kernel-transaction-log.d.ts +61 -0
  62. package/dist/runtime/kernel-transaction-log.js +149 -0
  63. package/dist/runtime/large-result-spool.d.ts +3 -1
  64. package/dist/runtime/large-result-spool.js +24 -5
  65. package/dist/runtime/loop-driver.d.ts +1 -1
  66. package/dist/runtime/loop-driver.js +2 -7
  67. package/dist/runtime/mcp-proxy-plane.d.ts +1 -0
  68. package/dist/runtime/mcp-proxy-plane.js +23 -6
  69. package/dist/runtime/os-profile.d.ts +9 -10
  70. package/dist/runtime/os-profile.js +14 -10
  71. package/dist/runtime/os-snapshot.d.ts +19 -0
  72. package/dist/runtime/os-snapshot.js +33 -3
  73. package/dist/runtime/process-sandbox-plane.js +16 -11
  74. package/dist/runtime/reaction-checkpoint.d.ts +51 -0
  75. package/dist/runtime/reaction-checkpoint.js +83 -0
  76. package/dist/runtime/reactive-session.d.ts +9 -3
  77. package/dist/runtime/reactive-session.js +44 -14
  78. package/dist/runtime/reliability.d.ts +48 -0
  79. package/dist/runtime/reliability.js +86 -0
  80. package/dist/runtime/remote-vpc-plane.js +4 -3
  81. package/dist/runtime/run-group.d.ts +38 -35
  82. package/dist/runtime/run-group.js +97 -54
  83. package/dist/runtime/runner.d.ts +117 -78
  84. package/dist/runtime/runner.js +1516 -1197
  85. package/dist/runtime/session-log.d.ts +71 -12
  86. package/dist/runtime/session-log.js +192 -32
  87. package/dist/runtime/session-repair.d.ts +11 -7
  88. package/dist/runtime/session-repair.js +11 -8
  89. package/dist/runtime/sub-agent-orchestrator.d.ts +6 -2
  90. package/dist/runtime/sub-agent-orchestrator.js +45 -18
  91. package/dist/signals/gateway.d.ts +23 -10
  92. package/dist/signals/gateway.js +81 -17
  93. package/dist/signals/scheduled.js +0 -1
  94. package/dist/signals/types.d.ts +21 -13
  95. package/dist/skills/loader.js +12 -2
  96. package/dist/tools/index.d.ts +2 -0
  97. package/dist/types/agent.d.ts +40 -1
  98. package/dist/types/agent.js +61 -1
  99. package/dist/types.d.ts +6 -1
  100. package/dist/workflow/public.d.ts +2 -3
  101. package/dist/workflow/public.js +0 -1
  102. package/package.json +2 -2
@@ -19,67 +19,73 @@ export interface SessionData {
19
19
  createdAtMs: number;
20
20
  updatedAtMs: number;
21
21
  }
22
- export interface MemoryEntry {
23
- text: string;
24
- score: number;
25
- metadata: unknown;
26
- }
27
- export interface CurationStats {
28
- insightsProcessed: number;
29
- duplicatesRemoved: number;
30
- conflictsResolved: number;
31
- entriesAdded: number;
32
- }
33
- export interface CurationResult {
34
- toAdd: MemoryEntry[];
35
- /** Indices into the `existingMemories` array passed to `DreamStore.loadMemories`. */
36
- toRemoveIndices: number[];
37
- stats: CurationStats;
22
+ export type MemoryKind = "user" | "feedback" | "project" | "reference";
23
+ export type MemoryAuthor = "model" | "host" | "extraction";
24
+ export type MemoryTrustLevel = "untrusted" | "user_asserted" | "host_verified";
25
+ export interface MemoryScope {
26
+ tenant_id: string;
27
+ namespace: string;
38
28
  }
39
- export interface DreamStore {
40
- loadSessions(agentId: string): Promise<SessionData[]>;
41
- loadMemories(agentId: string): Promise<MemoryEntry[]>;
42
- commit(agentId: string, result: CurationResult, existing: MemoryEntry[]): Promise<void>;
43
- /** Semantic search over the agent's long-term memories. Called on demand during a run. */
44
- search(agentId: string, query: string, topK?: number): Promise<MemoryEntry[]>;
45
- /** Persist a completed session for future consolidation via `Agent.dream()`. */
46
- saveSession(data: SessionData): Promise<void>;
29
+ export interface MemoryProvenance {
30
+ session_id?: string;
31
+ author: MemoryAuthor;
32
+ trust: MemoryTrustLevel;
33
+ evidence_refs: string[];
47
34
  }
48
- /** Memory kind (4 types, mirroring Claude Code). */
49
- export type MemoryKind = "user" | "feedback" | "project" | "reference";
50
- /** Memory metadata (kernel stores, SDK provides full content). */
51
- export interface MemoryMetadata {
35
+ export interface MemoryRecord {
36
+ record_id: string;
37
+ scope: MemoryScope;
52
38
  name: string;
39
+ kind: MemoryKind;
40
+ content: string;
53
41
  description: string;
54
- kind?: MemoryKind;
42
+ provenance: MemoryProvenance;
55
43
  created_at: number;
56
44
  updated_at: number;
57
- session_id?: string;
58
- user_role?: string;
59
- expertise_level?: string;
60
- preference_rule?: string;
61
- approved_pattern?: string;
62
- project_phase?: string;
63
- relative_date?: string;
64
- external_url?: string;
65
- ticket_ref?: string;
45
+ last_recalled_at?: number;
46
+ recall_count: number;
47
+ confidence: number;
48
+ links: string[];
49
+ pinned: boolean;
50
+ ttl_days?: number;
66
51
  }
67
- /** Memory write request (SDK → kernel). */
68
- export interface MemoryWriteRequest {
69
- metadata: MemoryMetadata;
70
- content: string;
52
+ export interface MemoryRecall {
53
+ record: MemoryRecord;
54
+ score: number;
55
+ why: string;
56
+ }
57
+ /** One record's recall lifecycle, mirrored from the kernel's `memory_recalled` observation. */
58
+ export interface MemoryRecallLifecycle {
59
+ record_id: string;
60
+ recall_count: number;
61
+ last_recalled_at: number;
62
+ }
63
+ export interface DreamStore {
64
+ /** The only durable memory mutation. Callers must reach this through the kernel WriteMemory gate. */
65
+ upsert(agentId: string, record: MemoryRecord): Promise<void>;
66
+ /** Semantic search over the agent's long-term memories. Called on demand during a run. */
67
+ search(agentId: string, query: MemoryQuery): Promise<MemoryRecall[]>;
68
+ /** Persist the completed session before the runner performs its one extraction pass. */
69
+ saveSession(data: SessionData): Promise<void>;
70
+ /**
71
+ * M3: mirror the kernel's journaled recall lifecycle into the durable store so recall history
72
+ * (count + last-recalled turn) survives across sessions. Optional: a store that does not track
73
+ * recall history omits it. The runner calls it when it observes `memory_recalled`.
74
+ */
75
+ recordRecall?(agentId: string, recalls: MemoryRecallLifecycle[]): Promise<void>;
76
+ /**
77
+ * M4: set (or clear) a record's pin. Pinned records are exempt from the store's retention
78
+ * eviction. Optional. The runner calls it when the host/model acts on a promotion suggestion.
79
+ */
80
+ setPinned?(agentId: string, recordId: string, pinned: boolean): Promise<void>;
71
81
  }
72
82
  /** Memory query request (kernel → SDK). */
73
83
  export interface MemoryQuery {
74
- current_context: string;
75
- active_tools: string[];
76
- already_surfaced: string[];
84
+ scope: MemoryScope;
85
+ query: string;
77
86
  top_k: number;
78
- }
79
- /** Memory retrieval response (SDK → kernel). */
80
- export interface MemoryRetrieval {
81
- selected_memory_ids: string[];
82
- selection_rationale: string;
87
+ kinds: MemoryKind[];
88
+ min_score?: number;
83
89
  }
84
90
  /** Memory validation error (mirroring kernel MemoryValidationError). */
85
91
  export type MemoryValidationError = {
@@ -102,9 +108,3 @@ export type MemoryValidationError = {
102
108
  limit: number;
103
109
  };
104
110
  /** Durable transcript storage for same-session conversational continuity. */
105
- export interface DreamResult {
106
- sessionsProcessed: number;
107
- insightsExtracted: number;
108
- entriesAdded: number;
109
- entriesRemoved: number;
110
- }
@@ -1 +1,2 @@
1
1
  export {};
2
+ /** Durable transcript storage for same-session conversational continuity. */
@@ -1,4 +1,6 @@
1
1
  export { WorkingMemory } from "./working.js";
2
2
  export { InMemoryDreamStore } from "./in-memory-store.js";
3
- export type { DreamStore, DreamResult, SessionData, SessionMessage, MemoryEntry, CurationResult, CurationStats, MemoryWriteRequest, MemoryQuery, MemoryRetrieval, MemoryMetadata, MemoryKind, } from "./protocols.js";
3
+ export type { InMemoryDreamStoreOptions } from "./in-memory-store.js";
4
+ export { memoryRetentionScore } from "./retention.js";
5
+ export type { DreamStore, SessionData, SessionMessage, MemoryRecord, MemoryRecall, MemoryQuery, MemoryScope, MemoryProvenance, MemoryRecallLifecycle, MemoryKind, MemoryAuthor, MemoryTrustLevel, } from "./protocols.js";
4
6
  export type { KnowledgeSource } from "../knowledge/source.js";
@@ -1,3 +1,4 @@
1
1
  // `@deepstrike/sdk/memory` — long-term (dream) and working memory, plus the knowledge-source interface.
2
2
  export { WorkingMemory } from "./working.js";
3
3
  export { InMemoryDreamStore } from "./in-memory-store.js";
4
+ export { memoryRetentionScore } from "./retention.js";
@@ -0,0 +1,33 @@
1
+ interface RankableMemory<T> {
2
+ value: T;
3
+ searchableText: string;
4
+ updatedAt: number;
5
+ /** Times this record has been recalled — a proven-useful record ranks slightly higher. */
6
+ recallCount: number;
7
+ /** Optional day-based TTL. Records past it are discounted, not dropped (host owns hard deletion). */
8
+ ttlDays?: number;
9
+ insertionIndex: number;
10
+ }
11
+ /** One ranked hit with a genuine relevance score in [0,1] and a human-readable rationale. */
12
+ export interface RankedMemory<T> {
13
+ value: T;
14
+ score: number;
15
+ why: string;
16
+ }
17
+ export interface RankOptions {
18
+ /** Wall-clock reference for the staleness discount. Omit to disable TTL/staleness scoring. */
19
+ nowMs?: number;
20
+ /** Age (days) past which a record starts losing relevance. */
21
+ staleWarningDays?: number;
22
+ }
23
+ /**
24
+ * Rank memories without embeddings or provider calls, returning a genuine relevance score in [0,1].
25
+ *
26
+ * The score is lexical overlap (distinct query terms present, as a fraction) as the dominant term,
27
+ * lifted slightly by recall history and lowered by TTL/staleness. Recency and insertion order break
28
+ * ties. A non-empty query never returns unrelated entries (lexical overlap zero ⇒ filtered out).
29
+ *
30
+ * The score is relevance, deliberately distinct from the record's stored confidence.
31
+ */
32
+ export declare function rankMemories<T>(query: string, candidates: Array<RankableMemory<T>>, topK: number, opts?: RankOptions): Array<RankedMemory<T>>;
33
+ export {};
@@ -0,0 +1,77 @@
1
+ function terms(text) {
2
+ const result = new Set();
3
+ for (const segment of text.toLocaleLowerCase().match(/[\p{L}\p{N}]+/gu) ?? []) {
4
+ result.add(segment);
5
+ if (/\p{Script=Han}/u.test(segment)) {
6
+ const characters = [...segment];
7
+ for (let index = 0; index + 1 < characters.length; index++) {
8
+ result.add(characters[index] + characters[index + 1]);
9
+ }
10
+ }
11
+ }
12
+ return result;
13
+ }
14
+ const DAY_MS = 86_400_000;
15
+ /** Staleness discount in [0,1): grows once a record is older than the warning window, and jumps
16
+ * past its TTL. Clock-based, so it lives host-side (the kernel owns no wall clock). */
17
+ function stalenessPenalty(updatedAt, ttlDays, opts) {
18
+ if (opts.nowMs === undefined)
19
+ return 0;
20
+ const ageDays = Math.max(0, (opts.nowMs - updatedAt) / DAY_MS);
21
+ let penalty = 0;
22
+ if (opts.staleWarningDays !== undefined && ageDays > opts.staleWarningDays) {
23
+ penalty += Math.min(0.3, 0.05 * (ageDays - opts.staleWarningDays));
24
+ }
25
+ if (ttlDays !== undefined && ageDays > ttlDays) {
26
+ penalty += 0.4;
27
+ }
28
+ return Math.min(0.9, penalty);
29
+ }
30
+ /** Small proven-usefulness boost from recall history: log-shaped, capped so it never overturns a
31
+ * clearly more lexically relevant record. */
32
+ function recallBoost(recallCount) {
33
+ if (recallCount <= 0)
34
+ return 0;
35
+ return Math.min(0.15, 0.05 * Math.log2(1 + recallCount));
36
+ }
37
+ /**
38
+ * Rank memories without embeddings or provider calls, returning a genuine relevance score in [0,1].
39
+ *
40
+ * The score is lexical overlap (distinct query terms present, as a fraction) as the dominant term,
41
+ * lifted slightly by recall history and lowered by TTL/staleness. Recency and insertion order break
42
+ * ties. A non-empty query never returns unrelated entries (lexical overlap zero ⇒ filtered out).
43
+ *
44
+ * The score is relevance, deliberately distinct from the record's stored confidence.
45
+ */
46
+ export function rankMemories(query, candidates, topK, opts = {}) {
47
+ const queryTerms = terms(query);
48
+ const limit = Math.max(0, Math.floor(topK));
49
+ if (limit === 0)
50
+ return [];
51
+ return candidates
52
+ .map(candidate => {
53
+ const candidateTerms = terms(candidate.searchableText);
54
+ let lexicalMatches = 0;
55
+ for (const term of queryTerms) {
56
+ if (candidateTerms.has(term))
57
+ lexicalMatches++;
58
+ }
59
+ const lexicalFraction = queryTerms.size === 0 ? 0 : lexicalMatches / queryTerms.size;
60
+ const penalty = stalenessPenalty(candidate.updatedAt, candidate.ttlDays, opts);
61
+ const boost = recallBoost(candidate.recallCount);
62
+ const score = Math.max(0, Math.min(1, lexicalFraction * 0.85 + boost - penalty));
63
+ const why = queryTerms.size === 0
64
+ ? "no query terms; insertion order"
65
+ : `lexical ${lexicalMatches}/${queryTerms.size}`
66
+ + (boost > 0 ? `, recall×${candidate.recallCount}` : "")
67
+ + (penalty > 0 ? `, stale −${penalty.toFixed(2)}` : "");
68
+ return { ...candidate, lexicalMatches, score, why };
69
+ })
70
+ .filter(candidate => queryTerms.size === 0 || candidate.lexicalMatches > 0)
71
+ .sort((a, b) => b.score - a.score
72
+ || b.lexicalMatches - a.lexicalMatches
73
+ || b.updatedAt - a.updatedAt
74
+ || a.insertionIndex - b.insertionIndex)
75
+ .slice(0, limit)
76
+ .map(candidate => ({ value: candidate.value, score: candidate.score, why: candidate.why }));
77
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Host-side mirror of the kernel's shared retention vocabulary (`crates/.../mm/value.rs` +
3
+ * `memory_retention_score`). The durable store owns the full cross-session record set, so it — not
4
+ * the kernel — enforces the capacity bound; it must rank by the same integer "value" definition so
5
+ * memory and context knowledge agree on what is worth keeping.
6
+ *
7
+ * Integer terms (magnitudes well within 2^53, so JS number math is exact). The turn-based recency
8
+ * term is omitted here: the host store has no turn counter, so a record's recall_count, kind,
9
+ * confidence, size, and (clock-based) staleness drive the ordering. `parity` tests pin this against
10
+ * the Rust reference for the terms both compute.
11
+ */
12
+ import type { MemoryRecord } from "./protocols.js";
13
+ /**
14
+ * Deterministic retention score for a durable record. Higher is retained first;
15
+ * `Number.POSITIVE_INFINITY` is reserved for pins (the kernel uses `i64::MAX`).
16
+ */
17
+ export declare function memoryRetentionScore(record: MemoryRecord, nowMs: number, staleWarningDays: number): number;
@@ -0,0 +1,54 @@
1
+ const DAY_MS = 86_400_000;
2
+ const KIND_WEIGHT = {
3
+ user: 1_600,
4
+ feedback: 1_800,
5
+ project: 1_400,
6
+ reference: 1_200,
7
+ };
8
+ /** floor(log2(1 + n)) — the kernel's stable ln(1+n) proxy for usage. */
9
+ function usageBucket(useCount) {
10
+ if (useCount <= 0)
11
+ return 0;
12
+ return Math.floor(Math.log2(useCount + 1));
13
+ }
14
+ /** Day-based staleness discount in parts-per-million, matching the store's recall-ranking penalty
15
+ * band but expressed on the kernel's ppm scale. */
16
+ function staleDiscountPpm(updatedAt, ttlDays, nowMs, staleWarningDays) {
17
+ const ageDays = Math.max(0, (nowMs - updatedAt) / DAY_MS);
18
+ let ppm = 0;
19
+ if (ageDays > staleWarningDays) {
20
+ ppm += Math.min(300_000, 50_000 * (ageDays - staleWarningDays));
21
+ }
22
+ if (ttlDays !== undefined && ageDays > ttlDays) {
23
+ ppm += 400_000;
24
+ }
25
+ return Math.min(1_000_000, Math.floor(ppm));
26
+ }
27
+ /**
28
+ * Deterministic retention score for a durable record. Higher is retained first;
29
+ * `Number.POSITIVE_INFINITY` is reserved for pins (the kernel uses `i64::MAX`).
30
+ */
31
+ export function memoryRetentionScore(record, nowMs, staleWarningDays) {
32
+ if (record.pinned)
33
+ return Number.POSITIVE_INFINITY;
34
+ const usage = usageBucket(record.recall_count) * 8_192;
35
+ // Recency (turn-based in the kernel) is unavailable host-side; omitted deterministically.
36
+ const kind = KIND_WEIGHT[record.kind] ?? 0;
37
+ const confidencePpm = Math.min(1_000_000, Math.floor(Math.max(0, Math.min(1, record.confidence)) * 1_000_000));
38
+ const confidence = Math.floor(confidencePpm / 250);
39
+ const stalePpm = staleDiscountPpm(record.updated_at, record.ttl_days, nowMs, staleWarningDays);
40
+ const staleness = Math.floor(stalePpm / 125);
41
+ // 4-bytes-per-token proxy, matching the kernel's content.len()/4.
42
+ const tokens = Math.min(0xffffffff, Math.floor(byteLength(record.content) / 4));
43
+ const size = tokens * 4;
44
+ return usage + kind + confidence - staleness - size;
45
+ }
46
+ function byteLength(text) {
47
+ // Match Rust's content.len() (UTF-8 bytes) without pulling in Buffer for portability.
48
+ let bytes = 0;
49
+ for (const ch of text) {
50
+ const code = ch.codePointAt(0);
51
+ bytes += code <= 0x7f ? 1 : code <= 0x7ff ? 2 : code <= 0xffff ? 3 : 4;
52
+ }
53
+ return bytes;
54
+ }
@@ -1,11 +1,11 @@
1
- export { DEFAULT_NATIVE_ATTENTION_POLICY, DEFAULT_NATIVE_GOVERNANCE_POLICY, assertNativeProfile, osProfile, } from "../runtime/os-profile.js";
2
- export type { NativeOsProfile, OsProfileId } from "../runtime/os-profile.js";
1
+ export { DEFAULT_NATIVE_SIGNAL_POLICY, DEFAULT_NATIVE_GOVERNANCE_POLICY, assertNativeProfile, osProfile, } from "../runtime/os-profile.js";
2
+ export type { NativeOsProfile, OsProfileId, SignalPolicy } from "../runtime/os-profile.js";
3
3
  export { rebuildOsSnapshotFromSessionEvents } from "../runtime/os-snapshot.js";
4
4
  export type { OsSnapshot } from "../runtime/os-snapshot.js";
5
5
  export type { KernelEventCategory } from "../runtime/kernel-event-log.js";
6
6
  export { KernelPrimitivesDashboard } from "../runtime/kernel-primitives-dashboard.js";
7
7
  export type { MemoryPolicy, MemoryWriteRateLimit, ResourceQuota } from "../kernel.js";
8
- export type { SchedulerBudget } from "../runtime/runner.js";
8
+ export type { PromptBudget, SchedulerPolicy } from "../runtime/runner.js";
9
9
  export { ScheduledPrompt } from "../signals/scheduled.js";
10
10
  export { SignalGateway } from "../signals/gateway.js";
11
11
  export { PermissionManager, PermissionMode } from "../safety/permissions.js";
package/dist/os/public.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // `@deepstrike/sdk/os` — Agent-OS diagnostics, profiles, signal/permission machinery, replay-testing,
2
2
  // and the scheduler/quota/policy types referenced by advanced `RuntimeOptions` fields.
3
- export { DEFAULT_NATIVE_ATTENTION_POLICY, DEFAULT_NATIVE_GOVERNANCE_POLICY, assertNativeProfile, osProfile, } from "../runtime/os-profile.js";
3
+ export { DEFAULT_NATIVE_SIGNAL_POLICY, DEFAULT_NATIVE_GOVERNANCE_POLICY, assertNativeProfile, osProfile, } from "../runtime/os-profile.js";
4
4
  export { rebuildOsSnapshotFromSessionEvents } from "../runtime/os-snapshot.js";
5
5
  export { KernelPrimitivesDashboard } from "../runtime/kernel-primitives-dashboard.js";
6
6
  // Signals + SDK-side permissions.
@@ -48,6 +48,11 @@ export declare function normalizeToolCall(id: string, name: string, args: unknow
48
48
  name: string;
49
49
  arguments: string;
50
50
  } | null;
51
+ export declare class UnsupportedModalityError extends Error {
52
+ readonly modality: string;
53
+ readonly provider: string;
54
+ constructor(modality: string, provider: string);
55
+ }
51
56
  export declare function toAnthropicContent(msg: Message): string | Array<Record<string, unknown>>;
52
57
  /**
53
58
  * History turns with the volatile State turn appended as the latest turn, for
@@ -63,6 +68,9 @@ export declare function turnsWithStateAppended(context: RenderedContext): Messag
63
68
  /** Convert RenderedContext.turns to Anthropic messages array.
64
69
  * `turns` contains only user / assistant / tool roles — no system filtering needed. */
65
70
  export declare function toAnthropicMessages(turns: Message[], nativeReplay?: (message: Message) => Array<Record<string, unknown>> | undefined): Array<Record<string, unknown>>;
71
+ /** Map an audio MIME type to OpenAI's `input_audio.format` (accepts "mp3" | "wav").
72
+ * `audio/mpeg` must become "mp3", not the raw "mpeg" subtype. */
73
+ export declare function openaiAudioFormat(mediaType: string | undefined): string;
66
74
  export declare function toOpenAIContent(msg: Message): string | Array<Record<string, unknown>>;
67
75
  /** Build the full OpenAI messages array from a RenderedContext.
68
76
  * Prepends systemText as the first system message, then converts turns. */
Binary file
@@ -2,6 +2,7 @@ import { GoogleGenerativeAI } from "@google/generative-ai";
2
2
  import { withServerRuntimeGuard } from "../runtime/server.js";
3
3
  import { CircuitBreaker, normalizeToolCall, turnsWithStateAppended } from "./base.js";
4
4
  import { endpointProfiles } from "./profiles.js";
5
+ import { UnsupportedModalityError } from "./base.js";
5
6
  const GEMINI_BASE = endpointProfiles["gemini.google"].baseURL;
6
7
  const GEMINI_POLICIES = {
7
8
  "gemini-3-pro-preview": { maxTurns: 50 },
@@ -68,6 +69,17 @@ export function buildContents(turns) {
68
69
  else if (p.url)
69
70
  parts.push({ fileData: { mimeType: p.mediaType ?? "image/png", fileUri: p.url } });
70
71
  }
72
+ else if (p.type === "audio") {
73
+ if (!p.data)
74
+ throw new UnsupportedModalityError("audio", "gemini");
75
+ parts.push({ inlineData: { mimeType: p.mediaType ?? "audio/wav", data: p.data } });
76
+ }
77
+ else if (p.type === "tool_result") {
78
+ // tool results are handled via functionResponse on tool role messages
79
+ }
80
+ else {
81
+ throw new UnsupportedModalityError(String(p.type ?? "unknown"), "gemini");
82
+ }
71
83
  }
72
84
  }
73
85
  else if (msg.content) {
@@ -1,4 +1,4 @@
1
- import { normalizeToolCall, omitExtensionKeys, turnsWithStateAppended } from "./base.js";
1
+ import { normalizeToolCall, omitExtensionKeys, turnsWithStateAppended, UnsupportedModalityError } from "./base.js";
2
2
  // Prefix-based policy for local models (first match wins)
3
3
  const OLLAMA_PREFIX_POLICIES = [
4
4
  ["deepseek-r1", { maxTurns: 40 }],
@@ -38,6 +38,8 @@ export class OllamaProvider {
38
38
  for (const p of m.contentParts) {
39
39
  if (p.type === "image" && p.data)
40
40
  images.push(p.data);
41
+ else if (p.type === "audio")
42
+ throw new UnsupportedModalityError("audio", "ollama");
41
43
  }
42
44
  }
43
45
  result.push({ role: m.role, content: m.content, ...(images.length ? { images } : {}) });
@@ -2,6 +2,7 @@ import OpenAI from "openai";
2
2
  import { withServerRuntimeGuard } from "../runtime/server.js";
3
3
  import { CircuitBreaker, omitExtensionKeys } from "./base.js";
4
4
  import { normalizeToolCall } from "./base.js";
5
+ import { UnsupportedModalityError } from "./base.js";
5
6
  export class OpenAIResponsesAdapter {
6
7
  buildTools(tools) {
7
8
  return tools.map(t => ({
@@ -111,8 +112,10 @@ export class OpenAIResponsesAdapter {
111
112
  continue;
112
113
  }
113
114
  if (part.type === "image") {
114
- const imageUrl = part.url ?? (part.data && part.mediaType
115
- ? `data:${part.mediaType};base64,${part.data}`
115
+ // Default the MIME type (like every other serializer) so a data-only image is
116
+ // not silently dropped; only a part with neither url nor data yields undefined.
117
+ const imageUrl = part.url ?? (part.data
118
+ ? `data:${part.mediaType ?? "image/png"};base64,${part.data}`
116
119
  : undefined);
117
120
  if (imageUrl)
118
121
  content.push({
@@ -120,6 +123,10 @@ export class OpenAIResponsesAdapter {
120
123
  detail: part.detail ?? "auto",
121
124
  image_url: imageUrl,
122
125
  });
126
+ continue;
127
+ }
128
+ if (part.type === "audio") {
129
+ throw new UnsupportedModalityError("audio", "openai-responses");
123
130
  }
124
131
  }
125
132
  return content;
@@ -11,6 +11,8 @@ export interface ModelProfile {
11
11
  providerId: ProviderId;
12
12
  defaultEndpointId: string;
13
13
  contextWindow?: number;
14
+ /** Agent-message modalities the ContentPart → provider serializer path can deliver.
15
+ * Do not advertise video/pdf until ContentPart gains those variants. */
14
16
  modalities: {
15
17
  input: Array<"text" | "image" | "audio" | "video" | "pdf">;
16
18
  output: Array<"text" | "embedding">;
@@ -1448,7 +1450,7 @@ export declare const modelProfiles: {
1448
1450
  readonly defaultEndpointId: "qwen.dashscope.multimodal-embeddings";
1449
1451
  readonly contextWindow: 32000;
1450
1452
  readonly modalities: {
1451
- readonly input: ["text", "image", "video"];
1453
+ readonly input: ["text", "image"];
1452
1454
  readonly output: ["embedding"];
1453
1455
  };
1454
1456
  readonly tools: {
@@ -1465,7 +1467,7 @@ export declare const modelProfiles: {
1465
1467
  readonly defaultEndpointId: "qwen.dashscope.multimodal-embeddings";
1466
1468
  readonly contextWindow: 32000;
1467
1469
  readonly modalities: {
1468
- readonly input: ["text", "image", "video"];
1470
+ readonly input: ["text", "image"];
1469
1471
  readonly output: ["embedding"];
1470
1472
  };
1471
1473
  readonly tools: {
@@ -1679,7 +1681,7 @@ export declare const modelProfiles: {
1679
1681
  readonly defaultEndpointId: "gemini.google.embeddings";
1680
1682
  readonly contextWindow: 8192;
1681
1683
  readonly modalities: {
1682
- readonly input: ["text", "image", "audio", "video", "pdf"];
1684
+ readonly input: ["text", "image", "audio"];
1683
1685
  readonly output: ["embedding"];
1684
1686
  };
1685
1687
  readonly tools: {
@@ -605,13 +605,13 @@ export const modelProfiles = {
605
605
  "qwen/qwen3-vl-embedding": {
606
606
  id: "qwen/qwen3-vl-embedding", providerId: "qwen", defaultEndpointId: "qwen.dashscope.multimodal-embeddings",
607
607
  contextWindow: 32_000,
608
- modalities: { input: ["text", "image", "video"], output: ["embedding"] },
608
+ modalities: { input: ["text", "image"], output: ["embedding"] },
609
609
  tools: { supported: false }, reasoning: { supported: false, preserveAcrossToolTurns: false },
610
610
  },
611
611
  "qwen/qwen2.5-vl-embedding": {
612
612
  id: "qwen/qwen2.5-vl-embedding", providerId: "qwen", defaultEndpointId: "qwen.dashscope.multimodal-embeddings",
613
613
  contextWindow: 32_000,
614
- modalities: { input: ["text", "image", "video"], output: ["embedding"] },
614
+ modalities: { input: ["text", "image"], output: ["embedding"] },
615
615
  tools: { supported: false }, reasoning: { supported: false, preserveAcrossToolTurns: false },
616
616
  },
617
617
  // ── Gemini ─────────────────────────────────────────────────────────────────
@@ -687,7 +687,7 @@ export const modelProfiles = {
687
687
  "gemini/gemini-embedding-2": {
688
688
  id: "gemini/gemini-embedding-2", providerId: "gemini", defaultEndpointId: "gemini.google.embeddings",
689
689
  contextWindow: 8_192,
690
- modalities: { input: ["text", "image", "audio", "video", "pdf"], output: ["embedding"] },
690
+ modalities: { input: ["text", "image", "audio"], output: ["embedding"] },
691
691
  tools: { supported: false }, reasoning: { supported: false, preserveAcrossToolTurns: false },
692
692
  },
693
693
  // ── GLM ────────────────────────────────────────────────────────────────────
@@ -0,0 +1,35 @@
1
+ export declare const CONTEXT_POLICY_VERSION: 1;
2
+ export declare const PPM_SCALE: 1000000;
3
+ export interface ContextPressureThresholdsV1 {
4
+ snip: number;
5
+ micro: number;
6
+ collapse: number;
7
+ auto: number;
8
+ renewal: number;
9
+ }
10
+ export interface ContextPolicyV1 {
11
+ pressureThresholds: ContextPressureThresholdsV1;
12
+ targetAfterCompress: number;
13
+ preserveRecentTurns: number;
14
+ renewalCarryover: number;
15
+ collapseOldAssistantNarration: boolean;
16
+ idleMicroCompactMinutes: number;
17
+ }
18
+ export interface ContextPolicyWireV1 {
19
+ version: typeof CONTEXT_POLICY_VERSION;
20
+ pressure_thresholds_ppm: ContextPressureThresholdsV1;
21
+ target_after_compress_ppm: number;
22
+ preserve_recent_turns: number;
23
+ renewal_carryover_ppm: number;
24
+ collapse_old_assistant_narration: boolean;
25
+ idle_micro_compact_minutes: number;
26
+ }
27
+ export interface ContextPolicyOverridesV1 extends Partial<Omit<ContextPolicyV1, "pressureThresholds">> {
28
+ pressureThresholds?: Partial<ContextPressureThresholdsV1>;
29
+ }
30
+ export declare const DEFAULT_CONTEXT_POLICY_V1: Readonly<ContextPolicyV1>;
31
+ /** Resolve ergonomic partial SDK options into one complete, atomically validated policy. */
32
+ export declare function contextPolicyV1(overrides?: ContextPolicyOverridesV1): ContextPolicyV1;
33
+ /** Convert the public ratio-based policy to the canonical integer-only ABI wire shape. */
34
+ export declare function normalizeContextPolicyV1(policy: ContextPolicyV1): ContextPolicyWireV1;
35
+ export declare function ratioToPpm(value: number, field?: string): number;
@@ -0,0 +1,66 @@
1
+ export const CONTEXT_POLICY_VERSION = 1;
2
+ export const PPM_SCALE = 1_000_000;
3
+ export const DEFAULT_CONTEXT_POLICY_V1 = Object.freeze({
4
+ pressureThresholds: Object.freeze({ snip: 0.70, micro: 0.80, collapse: 0.90, auto: 0.95, renewal: 0.98 }),
5
+ targetAfterCompress: 0.65,
6
+ preserveRecentTurns: 2,
7
+ renewalCarryover: 0.05,
8
+ collapseOldAssistantNarration: true,
9
+ idleMicroCompactMinutes: 60,
10
+ });
11
+ /** Resolve ergonomic partial SDK options into one complete, atomically validated policy. */
12
+ export function contextPolicyV1(overrides = {}) {
13
+ const policy = {
14
+ ...DEFAULT_CONTEXT_POLICY_V1,
15
+ ...overrides,
16
+ pressureThresholds: {
17
+ ...DEFAULT_CONTEXT_POLICY_V1.pressureThresholds,
18
+ ...overrides.pressureThresholds,
19
+ },
20
+ };
21
+ normalizeContextPolicyV1(policy);
22
+ return policy;
23
+ }
24
+ /** Convert the public ratio-based policy to the canonical integer-only ABI wire shape. */
25
+ export function normalizeContextPolicyV1(policy) {
26
+ const pressure_thresholds_ppm = {
27
+ snip: ratioToPpm(policy.pressureThresholds.snip, "pressureThresholds.snip"),
28
+ micro: ratioToPpm(policy.pressureThresholds.micro, "pressureThresholds.micro"),
29
+ collapse: ratioToPpm(policy.pressureThresholds.collapse, "pressureThresholds.collapse"),
30
+ auto: ratioToPpm(policy.pressureThresholds.auto, "pressureThresholds.auto"),
31
+ renewal: ratioToPpm(policy.pressureThresholds.renewal, "pressureThresholds.renewal"),
32
+ };
33
+ const ordered = Object.values(pressure_thresholds_ppm);
34
+ if (!ordered.every((value, index) => index === 0 || ordered[index - 1] < value)) {
35
+ throw new RangeError("context pressure thresholds must satisfy snip < micro < collapse < auto < renewal");
36
+ }
37
+ const target_after_compress_ppm = ratioToPpm(policy.targetAfterCompress, "targetAfterCompress");
38
+ if (target_after_compress_ppm >= pressure_thresholds_ppm.snip) {
39
+ throw new RangeError("targetAfterCompress must be lower than the snip threshold");
40
+ }
41
+ assertIntegerAtLeast(policy.preserveRecentTurns, 1, "preserveRecentTurns");
42
+ assertIntegerAtLeast(policy.idleMicroCompactMinutes, 0, "idleMicroCompactMinutes");
43
+ if (typeof policy.collapseOldAssistantNarration !== "boolean") {
44
+ throw new TypeError("collapseOldAssistantNarration must be boolean");
45
+ }
46
+ return {
47
+ version: CONTEXT_POLICY_VERSION,
48
+ pressure_thresholds_ppm,
49
+ target_after_compress_ppm,
50
+ preserve_recent_turns: policy.preserveRecentTurns,
51
+ renewal_carryover_ppm: ratioToPpm(policy.renewalCarryover, "renewalCarryover"),
52
+ collapse_old_assistant_narration: policy.collapseOldAssistantNarration,
53
+ idle_micro_compact_minutes: policy.idleMicroCompactMinutes,
54
+ };
55
+ }
56
+ export function ratioToPpm(value, field = "ratio") {
57
+ if (!Number.isFinite(value) || value < 0 || value > 1) {
58
+ throw new RangeError(`${field} must be a finite number between 0 and 1`);
59
+ }
60
+ return Math.floor(value * PPM_SCALE + 0.5);
61
+ }
62
+ function assertIntegerAtLeast(value, minimum, field) {
63
+ if (!Number.isSafeInteger(value) || value < minimum) {
64
+ throw new RangeError(`${field} must be a safe integer >= ${minimum}`);
65
+ }
66
+ }