@stigmer/runner 3.12.7 → 3.12.9

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 (79) hide show
  1. package/dist/.build-fingerprint +1 -1
  2. package/dist/activities/execute-cursor/fetch-interceptor.js +29 -16
  3. package/dist/activities/execute-cursor/fetch-interceptor.js.map +1 -1
  4. package/dist/activities/execute-cursor/index.d.ts +14 -0
  5. package/dist/activities/execute-cursor/index.js +76 -5
  6. package/dist/activities/execute-cursor/index.js.map +1 -1
  7. package/dist/activities/execute-cursor/message-translator.d.ts +15 -9
  8. package/dist/activities/execute-cursor/message-translator.js +15 -9
  9. package/dist/activities/execute-cursor/message-translator.js.map +1 -1
  10. package/dist/activities/execute-deep-agent/index.js +9 -0
  11. package/dist/activities/execute-deep-agent/index.js.map +1 -1
  12. package/dist/activities/execute-deep-agent/mcp-gate.d.ts +2 -0
  13. package/dist/activities/execute-deep-agent/mcp-gate.js +2 -1
  14. package/dist/activities/execute-deep-agent/mcp-gate.js.map +1 -1
  15. package/dist/activities/execute-deep-agent/setup.d.ts +10 -1
  16. package/dist/activities/execute-deep-agent/setup.js +45 -3
  17. package/dist/activities/execute-deep-agent/setup.js.map +1 -1
  18. package/dist/payload-codecs.d.ts +1 -1
  19. package/dist/payload-codecs.js +6 -4
  20. package/dist/payload-codecs.js.map +1 -1
  21. package/dist/shared/memory-attachment.d.ts +97 -0
  22. package/dist/shared/memory-attachment.js +136 -0
  23. package/dist/shared/memory-attachment.js.map +1 -0
  24. package/dist/shared/memory-retrieval.d.ts +126 -0
  25. package/dist/shared/memory-retrieval.js +293 -0
  26. package/dist/shared/memory-retrieval.js.map +1 -0
  27. package/dist/shared/runner-credential-keys.js +2 -1
  28. package/dist/shared/runner-credential-keys.js.map +1 -1
  29. package/dist/shared/tool-kind.js +9 -0
  30. package/dist/shared/tool-kind.js.map +1 -1
  31. package/package.json +3 -2
  32. package/src/__tests__/history-encryption-e2e.test.ts +1 -1
  33. package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +40 -1
  34. package/src/activities/execute-cursor/__tests__/fetch-interceptor.test.ts +49 -15
  35. package/src/activities/execute-cursor/fetch-interceptor.ts +37 -16
  36. package/src/activities/execute-cursor/index.ts +86 -5
  37. package/src/activities/execute-cursor/message-translator.ts +15 -9
  38. package/src/activities/execute-deep-agent/__tests__/mcp-gate.test.ts +7 -0
  39. package/src/activities/execute-deep-agent/index.ts +10 -0
  40. package/src/activities/execute-deep-agent/mcp-gate.ts +4 -1
  41. package/src/activities/execute-deep-agent/setup.ts +70 -6
  42. package/src/payload-codecs.ts +12 -9
  43. package/src/shared/__tests__/memory-attachment.test.ts +167 -0
  44. package/src/shared/__tests__/memory-retrieval.test.ts +310 -0
  45. package/src/shared/memory-attachment.ts +167 -0
  46. package/src/shared/memory-retrieval.ts +383 -0
  47. package/src/shared/runner-credential-keys.ts +2 -1
  48. package/src/shared/tool-kind.ts +10 -0
  49. package/dist/claimcheck/compressor.d.ts +0 -2
  50. package/dist/claimcheck/compressor.js +0 -8
  51. package/dist/claimcheck/compressor.js.map +0 -1
  52. package/dist/claimcheck/config.d.ts +0 -7
  53. package/dist/claimcheck/config.js +0 -10
  54. package/dist/claimcheck/config.js.map +0 -1
  55. package/dist/claimcheck/index.d.ts +0 -3
  56. package/dist/claimcheck/index.js +0 -4
  57. package/dist/claimcheck/index.js.map +0 -1
  58. package/dist/claimcheck/payload-codec.d.ts +0 -23
  59. package/dist/claimcheck/payload-codec.js +0 -105
  60. package/dist/claimcheck/payload-codec.js.map +0 -1
  61. package/dist/encryption/config.d.ts +0 -64
  62. package/dist/encryption/config.js +0 -113
  63. package/dist/encryption/config.js.map +0 -1
  64. package/dist/encryption/index.d.ts +0 -3
  65. package/dist/encryption/index.js +0 -3
  66. package/dist/encryption/index.js.map +0 -1
  67. package/dist/encryption/payload-codec.d.ts +0 -41
  68. package/dist/encryption/payload-codec.js +0 -130
  69. package/dist/encryption/payload-codec.js.map +0 -1
  70. package/src/__tests__/claimcheck-codec.test.ts +0 -256
  71. package/src/__tests__/encryption-codec.test.ts +0 -287
  72. package/src/__tests__/fixtures/encrypted-payload-fixture.json +0 -15
  73. package/src/claimcheck/compressor.ts +0 -9
  74. package/src/claimcheck/config.ts +0 -20
  75. package/src/claimcheck/index.ts +0 -3
  76. package/src/claimcheck/payload-codec.ts +0 -139
  77. package/src/encryption/config.ts +0 -161
  78. package/src/encryption/index.ts +0 -3
  79. package/src/encryption/payload-codec.ts +0 -152
@@ -0,0 +1,293 @@
1
+ /**
2
+ * Semantic selection of recalled memories (stigmer/stigmer#293 Phase 3a,
3
+ * DD-008): when a subject's confirmed-fact set outgrows what wholesale
4
+ * injection should carry, select the most relevant facts for THIS
5
+ * execution instead of injecting everything.
6
+ *
7
+ * Sibling of recalled-memories.ts (which owns presentation) — this module
8
+ * owns SELECTION: which subset of the server-composed candidate set
9
+ * (`spec.recalled_memories`, the auditable snapshot both editions' compose
10
+ * steps stamp) actually rides the prompt. The compose steps never change;
11
+ * selection is a runner concern because the runner owns prompt assembly,
12
+ * provider credentials, and the metered proxy lane (DD-008 D1 — the
13
+ * titling-convergence doctrine: control-plane-adjacent LLM work runs once,
14
+ * in the shared runner, for both editions).
15
+ *
16
+ * Mechanism (DD-008 D2/D3): embed-on-read, no stored vectors anywhere.
17
+ * Selection activates ONLY above RETRIEVAL_K candidates; below that,
18
+ * top-k degenerates to wholesale, so no embeddings call is made and the
19
+ * shipped Phase 2 path runs untouched. When active: ONE batched
20
+ * embeddings call (query + all candidates), in-process cosine ranking,
21
+ * top-k by relevance, presented in snapshot order (relevance order would
22
+ * carry no information the model needs and would churn the prompt prefix).
23
+ * The query is the execution's `spec.message` — the current turn.
24
+ *
25
+ * The audit contract (DD-008 D5): the selection outcome is recorded in a
26
+ * runner-owned `RecalledMemoriesReport` on the execution status (the
27
+ * streaming_usage posture — one writer, written at prompt build). A report
28
+ * is returned whenever facts are injected, wholesale or selected; when
29
+ * nothing is injected (recall absent/disabled/empty) there is no report —
30
+ * absent report = wholesale, true by construction, so pre-3a executions
31
+ * read identically.
32
+ *
33
+ * Written-once across re-invocations: the same execution's prompt is
34
+ * rebuilt on approval resume (native) and fresh-agent recovery (cursor).
35
+ * Re-running selection there could pick a DIFFERENT subset mid-execution —
36
+ * so when the loaded execution already carries a report, this module
37
+ * REPLAYS it (selected ids resolved against the snapshot, or wholesale
38
+ * for a selection_active=false report) instead of re-embedding. Selection
39
+ * is computed at most once per execution, by construction.
40
+ *
41
+ * Failure posture: selection is an optimization. ANY failure — no
42
+ * embedder, HTTP error, timeout, malformed response — degrades to
43
+ * wholesale injection with a selection_active=false report, never a
44
+ * failed or degraded execution (DD-008 D3). Deployments with no
45
+ * embeddings-capable credential (Anthropic-only, Cursor-only OSS) run
46
+ * Phase 2 behavior unchanged, forever.
47
+ */
48
+ import { create } from "@bufbuild/protobuf";
49
+ import { RecalledMemoriesReportSchema, } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
50
+ import { resolveProxyBaseUrl, buildProxyHeaders } from "./llm-proxy.js";
51
+ import { checkDirectCredentials } from "./llm-backend.js";
52
+ import { getRunnerSecret } from "./runner-credential-store.js";
53
+ /**
54
+ * Selection activates only when the candidate set EXCEEDS this many facts;
55
+ * at or below it, top-k degenerates to wholesale and no embeddings call is
56
+ * made. 20 is comfortably above the dozens-scale where wholesale is fine
57
+ * (activation is rare) and comfortably below the 100-record cap (activation
58
+ * is meaningful): at 500-char facts, 20 facts ≈ 10KB of prompt. One
59
+ * constant, one place, deliberately not adaptive or per-org configurable
60
+ * in v1 (DD-008 D3).
61
+ */
62
+ export const RETRIEVAL_K = 20;
63
+ /**
64
+ * The v1 embedder (DD-008 D4). OpenAI-only: resolved through the Stigmer
65
+ * proxy when one is configured (cloud — platform key, metered), else the
66
+ * operator's direct OpenAI key (OSS). NOTE: llm-proxy's `inferProvider`
67
+ * does not know the `text-*` prefix — this module never infers; the
68
+ * provider is fixed alongside the model.
69
+ */
70
+ export const EMBEDDING_MODEL = "text-embedding-3-small";
71
+ /**
72
+ * Upper bound on the query text sent to the embedder. Facts are write-time
73
+ * capped at 500 chars, but the query is `spec.message` — unbounded. A giant
74
+ * pasted message would blow the embedder's per-input token limit (8192 for
75
+ * text-embedding-3-small) and 400 the WHOLE batched call, silently forcing
76
+ * wholesale on exactly the executions where selection matters. 20K chars
77
+ * sits safely under the limit at worst-case chars-per-token; selection
78
+ * intent is dominated by the message head, and a truncated query beats no
79
+ * selection.
80
+ */
81
+ export const QUERY_MAX_CHARS = 20_000;
82
+ /**
83
+ * Bound on the single embeddings round trip. Generous relative to the
84
+ * observed 200–400ms typical latency: this exists to keep a hung
85
+ * connection from stalling prompt build, not to race the provider.
86
+ */
87
+ const EMBED_TIMEOUT_MS = 15_000;
88
+ /** The OpenAI SDK-default base, used only in direct (unproxied) mode. */
89
+ const DIRECT_OPENAI_BASE_URL = "https://api.openai.com/v1";
90
+ /**
91
+ * Select the facts to inject for one execution.
92
+ *
93
+ * This is the ONE entry point both harnesses call at prompt build, replacing
94
+ * their direct `readRecalledMemories` reads on the injection path (the read
95
+ * function remains the presentation-side authority; this module reads the
96
+ * proto itself because selection needs `memory_id`, which the render
97
+ * boundary deliberately strips).
98
+ *
99
+ * Never throws: every failure path returns wholesale.
100
+ */
101
+ export async function selectRecalledFacts(recalled, queryText, options) {
102
+ const candidates = readCandidates(recalled);
103
+ if (candidates.length === 0) {
104
+ return { content: undefined, report: undefined };
105
+ }
106
+ // Written-once: a prior invocation of this execution already decided.
107
+ if (options.priorReport !== undefined) {
108
+ const replayed = replayReport(candidates, options.priorReport);
109
+ if (replayed !== undefined) {
110
+ return replayed;
111
+ }
112
+ // Unreplayable (recorded ids missing from the immutable snapshot —
113
+ // structurally impossible, defended anyway): fall through and select
114
+ // fresh rather than inject nothing.
115
+ log(`prior report for execution ${options.executionId} did not resolve ` +
116
+ `against the snapshot; re-selecting`);
117
+ }
118
+ if (candidates.length <= RETRIEVAL_K) {
119
+ return wholesale(candidates);
120
+ }
121
+ const embed = options.embed ?? resolveEmbedder(options);
122
+ if (embed === undefined) {
123
+ // No embeddings-capable credential: the recorded no-embedder posture
124
+ // (DD-008 D4) — Phase 2 behavior, honestly reported.
125
+ return wholesale(candidates);
126
+ }
127
+ try {
128
+ const query = queryText.slice(0, QUERY_MAX_CHARS);
129
+ const vectors = await embed([query, ...candidates.map((c) => c.content)]);
130
+ if (vectors.length !== candidates.length + 1) {
131
+ throw new Error(`embedder returned ${vectors.length} vectors for ` +
132
+ `${candidates.length + 1} inputs`);
133
+ }
134
+ const [queryVector, ...factVectors] = vectors;
135
+ const selected = topKBySimilarity(queryVector, factVectors, RETRIEVAL_K);
136
+ return {
137
+ content: { facts: selected.map((i) => candidates[i].content) },
138
+ report: create(RecalledMemoriesReportSchema, {
139
+ selectionActive: true,
140
+ injectedMemoryIds: selected.map((i) => candidates[i].memoryId),
141
+ embeddingModel: EMBEDDING_MODEL,
142
+ }),
143
+ };
144
+ }
145
+ catch (err) {
146
+ log(`selection failed for execution ${options.executionId}, degrading to ` +
147
+ `wholesale: ${err instanceof Error ? err.message : String(err)}`);
148
+ return wholesale(candidates);
149
+ }
150
+ }
151
+ /**
152
+ * The snapshot's renderable candidates, in snapshot order. Same semantics
153
+ * as readRecalledMemories (disabled → none; blank contents dropped
154
+ * defensively) but keeping `memory_id` — the report's audit link.
155
+ */
156
+ function readCandidates(recalled) {
157
+ if (!recalled?.enabled) {
158
+ return [];
159
+ }
160
+ return (recalled.facts ?? [])
161
+ .map((fact) => ({
162
+ memoryId: fact.memoryId ?? "",
163
+ content: fact.content?.trim() ?? "",
164
+ }))
165
+ .filter((c) => c.content !== "");
166
+ }
167
+ /**
168
+ * Replay a previously recorded outcome so a re-invocation injects exactly
169
+ * what the first invocation did. A selection_active=false report replays
170
+ * as wholesale (including the case where a transient embed failure was
171
+ * recorded — retrying could select a subset and diverge the prompt).
172
+ * Returns undefined when a recorded id no longer resolves.
173
+ */
174
+ function replayReport(candidates, prior) {
175
+ if (!prior.selectionActive) {
176
+ return wholesale(candidates);
177
+ }
178
+ const byId = new Map(candidates.map((c) => [c.memoryId, c.content]));
179
+ const facts = [];
180
+ for (const id of prior.injectedMemoryIds) {
181
+ const content = byId.get(id);
182
+ if (content === undefined) {
183
+ return undefined;
184
+ }
185
+ facts.push(content);
186
+ }
187
+ if (facts.length === 0) {
188
+ return undefined;
189
+ }
190
+ return { content: { facts }, report: prior };
191
+ }
192
+ /** Wholesale injection of the full candidate set, honestly reported. */
193
+ function wholesale(candidates) {
194
+ return {
195
+ content: { facts: candidates.map((c) => c.content) },
196
+ report: create(RecalledMemoriesReportSchema, { selectionActive: false }),
197
+ };
198
+ }
199
+ /**
200
+ * The credential lanes, in the platform's standing precedence (the
201
+ * titling-lane idiom): proxy when configured — the runner authenticates
202
+ * with its Stigmer token and the proxy owns the provider key — else the
203
+ * operator's direct OpenAI key, else no embedder.
204
+ */
205
+ function resolveEmbedder(options) {
206
+ if (options.proxyEndpoint) {
207
+ return fetchEmbedder(resolveProxyBaseUrl(options.proxyEndpoint, "openai"), buildProxyHeaders(options.stigmerToken ?? "", {
208
+ executionId: options.executionId,
209
+ }));
210
+ }
211
+ if (checkDirectCredentials("openai") === null) {
212
+ return fetchEmbedder(DIRECT_OPENAI_BASE_URL, {
213
+ Authorization: `Bearer ${getRunnerSecret("OPENAI_API_KEY") ?? ""}`,
214
+ });
215
+ }
216
+ return undefined;
217
+ }
218
+ /**
219
+ * The real embedder: one POST {base}/embeddings. A plain typed fetch, not
220
+ * LangChain's OpenAIEmbeddings — the SDK wrapper discards the response
221
+ * `usage` block and adds nothing over a single request. Proxy-side
222
+ * metering reads the JSON usage from the relayed body.
223
+ */
224
+ function fetchEmbedder(baseUrl, headers) {
225
+ return async (inputs) => {
226
+ const response = await fetch(`${baseUrl}/embeddings`, {
227
+ method: "POST",
228
+ headers: { ...headers, "Content-Type": "application/json" },
229
+ body: JSON.stringify({ model: EMBEDDING_MODEL, input: inputs }),
230
+ signal: AbortSignal.timeout(EMBED_TIMEOUT_MS),
231
+ });
232
+ if (!response.ok) {
233
+ const body = await response.text().catch(() => "");
234
+ throw new Error(`embeddings request failed: HTTP ${response.status} ${body.slice(0, 300)}`);
235
+ }
236
+ const parsed = (await response.json());
237
+ if (!Array.isArray(parsed.data)) {
238
+ throw new Error("embeddings response carries no data array");
239
+ }
240
+ // Place by the response's own index field — the API documents input
241
+ // order, but the contract names the index as authoritative.
242
+ const vectors = new Array(inputs.length);
243
+ for (const item of parsed.data) {
244
+ const index = item.index ?? -1;
245
+ if (index < 0 || index >= inputs.length || !Array.isArray(item.embedding)) {
246
+ throw new Error("embeddings response entry is malformed");
247
+ }
248
+ vectors[index] = item.embedding;
249
+ }
250
+ if (vectors.some((v) => v === undefined)) {
251
+ throw new Error("embeddings response is missing entries");
252
+ }
253
+ return vectors;
254
+ };
255
+ }
256
+ /**
257
+ * Indices of the top-k facts by cosine similarity to the query, returned
258
+ * ASCENDING — i.e. re-sorted to snapshot order (DD-008 D3: selection is by
259
+ * relevance, presentation stays oldest-first). Ties break toward the lower
260
+ * snapshot index, so equal scores never reorder across invocations.
261
+ */
262
+ function topKBySimilarity(queryVector, factVectors, k) {
263
+ return factVectors
264
+ .map((vector, index) => ({ index, score: cosineSimilarity(queryVector, vector) }))
265
+ .sort((a, b) => b.score - a.score || a.index - b.index)
266
+ .slice(0, k)
267
+ .map((entry) => entry.index)
268
+ .sort((a, b) => a - b);
269
+ }
270
+ /**
271
+ * Plain cosine. OpenAI embeddings arrive unit-normalized (a dot product
272
+ * would suffice today), but normalizing here keeps ranking correct under
273
+ * any future embedder without a silent provider assumption.
274
+ */
275
+ function cosineSimilarity(a, b) {
276
+ let dot = 0;
277
+ let normA = 0;
278
+ let normB = 0;
279
+ const length = Math.min(a.length, b.length);
280
+ for (let i = 0; i < length; i++) {
281
+ dot += a[i] * b[i];
282
+ normA += a[i] * a[i];
283
+ normB += b[i] * b[i];
284
+ }
285
+ if (normA === 0 || normB === 0) {
286
+ return 0;
287
+ }
288
+ return dot / (Math.sqrt(normA) * Math.sqrt(normB));
289
+ }
290
+ function log(msg) {
291
+ console.warn(`[memory-retrieval] ${msg}`);
292
+ }
293
+ //# sourceMappingURL=memory-retrieval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-retrieval.js","sourceRoot":"","sources":["../../src/shared/memory-retrieval.ts"],"names":[],"mappings":"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"}
@@ -62,7 +62,8 @@ export const RUNNER_CREDENTIAL_ENV_KEYS = [
62
62
  * rotation bookkeeping, not secrets.
63
63
  */
64
64
  export const RUNNER_ENCRYPTION_ENV_KEYS = [
65
- // Temporal payload-encryption keys (encryption/config.ts). An agent that
65
+ // Temporal payload-encryption keys (@stigmer/temporal-codecs' encryption
66
+ // config, read through the injected getRunnerSecret). An agent that
66
67
  // reads these could decrypt the runner's Temporal history payloads.
67
68
  "STIGMER_PAYLOAD_ENCRYPTION_KEY",
68
69
  "STIGMER_PAYLOAD_ENCRYPTION_SECONDARY_KEY",
@@ -1 +1 @@
1
- {"version":3,"file":"runner-credential-keys.js","sourceRoot":"","sources":["../../src/shared/runner-credential-keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAsB;IAC3D,wEAAwE;IACxE,iDAAiD;IACjD,4BAA4B;IAC5B,+DAA+D;IAC/D,sEAAsE;IACtE,eAAe;IACf,gEAAgE;IAChE,iDAAiD;IACjD,oBAAoB;IACpB,wEAAwE;IACxE,iCAAiC;IACjC,gBAAgB;IAChB,sEAAsE;IACtE,mBAAmB;IACnB,mEAAmE;IACnE,gBAAgB;IAChB,kDAAkD;IAClD,2BAA2B;IAC3B,iEAAiE;IACjE,0DAA0D;IAC1D,0BAA0B;CAC3B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAsB;IAC3D,yEAAyE;IACzE,oEAAoE;IACpE,gCAAgC;IAChC,0CAA0C;CAC3C,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAsB;IACvD,GAAG,0BAA0B;IAC7B,GAAG,0BAA0B;CAC9B,CAAC"}
1
+ {"version":3,"file":"runner-credential-keys.js","sourceRoot":"","sources":["../../src/shared/runner-credential-keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAsB;IAC3D,wEAAwE;IACxE,iDAAiD;IACjD,4BAA4B;IAC5B,+DAA+D;IAC/D,sEAAsE;IACtE,eAAe;IACf,gEAAgE;IAChE,iDAAiD;IACjD,oBAAoB;IACpB,wEAAwE;IACxE,iCAAiC;IACjC,gBAAgB;IAChB,sEAAsE;IACtE,mBAAmB;IACnB,mEAAmE;IACnE,gBAAgB;IAChB,kDAAkD;IAClD,2BAA2B;IAC3B,iEAAiE;IACjE,0DAA0D;IAC1D,0BAA0B;CAC3B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAsB;IAC3D,yEAAyE;IACzE,oEAAoE;IACpE,oEAAoE;IACpE,gCAAgC;IAChC,0CAA0C;CAC3C,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAsB;IACvD,GAAG,0BAA0B;IAC7B,GAAG,0BAA0B;CAC9B,CAAC"}
@@ -12,6 +12,7 @@
12
12
  * this table and that fixture in lockstep.
13
13
  */
14
14
  import { ToolKind } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
15
+ import { MEMORY_ATTACHMENT_SLUG } from "./memory-attachment.js";
15
16
  // Bare tool name -> ToolKind, covering both harness naming conventions. A name
16
17
  // found here is a built-in and wins over a non-empty mcp_server_slug (an MCP
17
18
  // server is not expected to shadow a built-in name; matching the legacy resolver).
@@ -68,6 +69,14 @@ const TOOL_NAME_TO_KIND = new Map([
68
69
  * back to a name lookup, so this is never worse than no classification).
69
70
  */
70
71
  export function classifyTool(name, mcpServerSlug) {
72
+ // The first-party remember tool (DD-005), slug-scoped on purpose: it is
73
+ // served by the synthesized memory attachment, so only that reserved
74
+ // slug earns the MEMORY kind (and its consent-chip rendering) — a
75
+ // third-party MCP server's coincidental `remember` stays a plain MCP
76
+ // tool, and a bare `remember` with no slug stays unclassified.
77
+ if (name === "remember" && mcpServerSlug === MEMORY_ATTACHMENT_SLUG) {
78
+ return ToolKind.MEMORY;
79
+ }
71
80
  const builtin = TOOL_NAME_TO_KIND.get(name);
72
81
  if (builtin !== undefined) {
73
82
  return builtin;
@@ -1 +1 @@
1
- {"version":3,"file":"tool-kind.js","sourceRoot":"","sources":["../../src/shared/tool-kind.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tool-kind.js","sourceRoot":"","sources":["../../src/shared/tool-kind.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stigmer/runner",
3
- "version": "3.12.7",
3
+ "version": "3.12.9",
4
4
  "description": "Embeddable Temporal worker for the Stigmer AI agent platform — handles agent execution, workflow orchestration, and MCP server management",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -94,7 +94,8 @@
94
94
  "@opentelemetry/sdk-metrics": "^2.0.0",
95
95
  "@opentelemetry/sdk-trace-base": "^2.0.0",
96
96
  "@opentelemetry/sdk-trace-node": "^2.0.0",
97
- "@stigmer/protos": "3.12.7",
97
+ "@stigmer/protos": "3.12.9",
98
+ "@stigmer/temporal-codecs": "3.12.9",
98
99
  "@temporalio/activity": "1.16.2",
99
100
  "@temporalio/common": "1.16.2",
100
101
  "@temporalio/interceptors-opentelemetry": "1.16.2",
@@ -25,7 +25,7 @@ import { join, dirname } from "node:path";
25
25
  import { fileURLToPath } from "node:url";
26
26
  import { loadWorkflowFromYaml } from "../workflow-engine/loader.js";
27
27
  import { evaluateExpressionBatch } from "../workflow-engine/expression.js";
28
- import { EncryptionPayloadCodec } from "../encryption/payload-codec.js";
28
+ import { EncryptionPayloadCodec } from "@stigmer/temporal-codecs";
29
29
  import type { ExecuteServerlessWorkflowInput } from "../workflows/execute-serverless-workflow.js";
30
30
 
31
31
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -12,7 +12,7 @@ import { create } from "@bufbuild/protobuf";
12
12
  import { ApprovalAction, InteractionMode } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
13
13
  import { PendingApprovalSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/approval_pb";
14
14
 
15
- import { appendStructuredOutputDirective, buildPrompt, isHitlReinvocation, primarySendCarriesImages } from "../index.js";
15
+ import { appendStructuredOutputDirective, buildPrompt, isHitlReinvocation, primarySendCarriesImages, promptCarriesStandingContext } from "../index.js";
16
16
  import type { BuildPromptInput } from "../index.js";
17
17
  import { buildReinvocationPrompt, formatInteractionModePrefix, formatImplementPlanSection, formatToolApprovalProtocol, buildToolApprovalRuleFile } from "../prompt-builder.js";
18
18
  import { PLAN_MODE_DIRECTIVE } from "../../../shared/plan-mode-prompt.js";
@@ -330,6 +330,45 @@ describe("buildPrompt", () => {
330
330
  expect(prompt).toContain("APPROVED");
331
331
  });
332
332
 
333
+ it("agrees with promptCarriesStandingContext on every resolution shape — the memory-selection gate can never drift from the routing (DD-008)", () => {
334
+ // The activity gates the (potentially embedding-backed) memory
335
+ // selection on this predicate; buildPrompt routes on the same one.
336
+ // Pin their agreement across the full reason × HITL matrix: memories
337
+ // reach the prompt exactly when the predicate says the prompt carries
338
+ // standing context.
339
+ const reasons: AgentResolutionReason[] = [
340
+ "created_first_execution",
341
+ "created_after_resume_failure",
342
+ "resumed_successfully",
343
+ ];
344
+ const hitlDecisions = new Map<string, ApprovalAction>([
345
+ ["tool-call-1", ApprovalAction.APPROVE],
346
+ ]);
347
+ const hitlApprovals = [
348
+ create(PendingApprovalSchema, {
349
+ toolCallId: "tool-call-1",
350
+ toolName: "Write",
351
+ message: "Write file: gated.txt",
352
+ }),
353
+ ];
354
+
355
+ for (const reason of reasons) {
356
+ for (const hitl of [false, true]) {
357
+ const prompt = buildPrompt(
358
+ input({
359
+ resolution: resolution("local", reason),
360
+ approvalDecisions: hitl ? hitlDecisions : undefined,
361
+ pendingApprovals: hitl ? hitlApprovals : [],
362
+ recalledMemories: { facts: ["Prefers OpenTofu."] },
363
+ }),
364
+ );
365
+ expect(prompt.includes("<recalled_memories>")).toBe(
366
+ promptCarriesStandingContext(reason),
367
+ );
368
+ }
369
+ }
370
+ });
371
+
333
372
  it("uses the reinvocation prompt for a HITL reinvocation (human-meaningful, no opaque ids)", () => {
334
373
  const approvalDecisions = new Map<string, ApprovalAction>([
335
374
  ["tool-call-1", ApprovalAction.APPROVE],
@@ -171,23 +171,26 @@ describe("fetch-interceptor", () => {
171
171
  });
172
172
  });
173
173
 
174
- describe("cursor_models_fetch timing", () => {
174
+ describe("timed REST path timing", () => {
175
175
  beforeEach(() => {
176
176
  installFetchInterceptor({ proxyEndpoint: PROXY_ENDPOINT, stigmerToken: STIGMER_TOKEN });
177
177
  });
178
178
 
179
179
  /**
180
- * Collect emitted `cursor_models_fetch` timing lines from a console.log
181
- * spy, ignoring every other log line (install banner, warnings, other
182
- * timelines).
180
+ * Collect emitted timing lines for one timeline event from a
181
+ * console.log spy, ignoring every other log line (install banner,
182
+ * warnings, other timelines).
183
183
  */
184
- function timingLines(spy: ReturnType<typeof vi.spyOn>): Array<Record<string, unknown>> {
184
+ function timingLines(
185
+ spy: ReturnType<typeof vi.spyOn>,
186
+ event: string,
187
+ ): Array<Record<string, unknown>> {
185
188
  const lines: Array<Record<string, unknown>> = [];
186
189
  for (const call of spy.mock.calls) {
187
190
  if (typeof call[0] !== "string") continue;
188
191
  try {
189
192
  const parsed = JSON.parse(call[0]) as Record<string, unknown>;
190
- if (parsed.stigmer_timing === "cursor_models_fetch") lines.push(parsed);
193
+ if (parsed.stigmer_timing === event) lines.push(parsed);
191
194
  } catch {
192
195
  // Not a JSON log line — ignore.
193
196
  }
@@ -195,7 +198,7 @@ describe("fetch-interceptor", () => {
195
198
  return lines;
196
199
  }
197
200
 
198
- it("emits one timing line for a Cursor-domain /v1/models call, carrying execution_id", async () => {
201
+ it("emits one cursor_models_fetch line for a Cursor-domain /v1/models call, carrying execution_id", async () => {
199
202
  const spy = vi.spyOn(console, "log").mockImplementation(() => {});
200
203
  const executionContext = getExecutionContext();
201
204
 
@@ -203,7 +206,7 @@ describe("fetch-interceptor", () => {
203
206
  await globalThis.fetch("https://api.cursor.com/v1/models", { method: "GET" });
204
207
  });
205
208
 
206
- const lines = timingLines(spy);
209
+ const lines = timingLines(spy, "cursor_models_fetch");
207
210
  expect(lines).toHaveLength(1);
208
211
  expect(lines[0]!.execution_id).toBe("exec-models-1");
209
212
  expect(lines[0]!.http_status).toBe(200);
@@ -212,16 +215,46 @@ describe("fetch-interceptor", () => {
212
215
  expect(segments.map((s) => s.name)).toEqual(["models_fetch"]);
213
216
  });
214
217
 
215
- it("emits for the proxy-endpoint-targeted /v1/models form too", async () => {
218
+ it("emits one cursor_token_exchange line for the SDK's token exchange, carrying execution_id (cloud#484)", async () => {
219
+ const spy = vi.spyOn(console, "log").mockImplementation(() => {});
220
+ const executionContext = getExecutionContext();
221
+
222
+ await executionContext.run({ executionId: "exec-exchange-1" }, async () => {
223
+ await globalThis.fetch(
224
+ "https://api2.cursor.sh/auth/exchange_user_api_key",
225
+ { method: "POST" },
226
+ );
227
+ });
228
+
229
+ const lines = timingLines(spy, "cursor_token_exchange");
230
+ expect(lines).toHaveLength(1);
231
+ expect(lines[0]!.execution_id).toBe("exec-exchange-1");
232
+ expect(lines[0]!.http_status).toBe(200);
233
+ expect(lines[0]!.total_ms).toBeTypeOf("number");
234
+ const segments = lines[0]!.segments as Array<{ name: string }>;
235
+ expect(segments.map((s) => s.name)).toEqual(["token_exchange"]);
236
+ // The exchange emits ONLY its own timeline, never the models one.
237
+ expect(timingLines(spy, "cursor_models_fetch")).toHaveLength(0);
238
+ });
239
+
240
+ it("emits for the proxy-endpoint-targeted forms too", async () => {
216
241
  const spy = vi.spyOn(console, "log").mockImplementation(() => {});
217
242
 
218
243
  await globalThis.fetch(`${PROXY_ENDPOINT}/v1/models`, { method: "GET" });
244
+ await globalThis.fetch(
245
+ `${PROXY_ENDPOINT}/auth/exchange_user_api_key`,
246
+ { method: "POST" },
247
+ );
219
248
 
220
- // The fetch itself was rewritten through the proxy path AND timed.
249
+ // Both fetches were rewritten through the proxy path AND timed.
221
250
  expect(calls[0]!.url).toBe(
222
251
  `${PROXY_ENDPOINT}/v1/proxy/cursor/api.cursor.com/v1/models`,
223
252
  );
224
- expect(timingLines(spy)).toHaveLength(1);
253
+ expect(calls[1]!.url).toBe(
254
+ `${PROXY_ENDPOINT}/v1/proxy/cursor/api2.cursor.sh/auth/exchange_user_api_key`,
255
+ );
256
+ expect(timingLines(spy, "cursor_models_fetch")).toHaveLength(1);
257
+ expect(timingLines(spy, "cursor_token_exchange")).toHaveLength(1);
225
258
  });
226
259
 
227
260
  it("omits execution_id (rather than fabricating one) outside an execution context", async () => {
@@ -229,22 +262,23 @@ describe("fetch-interceptor", () => {
229
262
 
230
263
  await globalThis.fetch("https://api.cursor.com/v1/models", { method: "GET" });
231
264
 
232
- const lines = timingLines(spy);
265
+ const lines = timingLines(spy, "cursor_models_fetch");
233
266
  expect(lines).toHaveLength(1);
234
267
  // undefined context values are dropped by JSON.stringify.
235
268
  expect("execution_id" in lines[0]!).toBe(false);
236
269
  });
237
270
 
238
- it("does NOT emit for other rewritten REST paths", async () => {
271
+ it("does NOT emit for rewritten REST paths outside the timed set", async () => {
239
272
  const spy = vi.spyOn(console, "log").mockImplementation(() => {});
240
273
 
241
274
  await globalThis.fetch(
242
- "https://api2.cursor.sh/auth/exchange_user_api_key",
275
+ "https://api.cursor.com/v1/agents",
243
276
  { method: "POST" },
244
277
  );
245
278
 
246
279
  expect(calls).toHaveLength(1);
247
- expect(timingLines(spy)).toHaveLength(0);
280
+ expect(timingLines(spy, "cursor_models_fetch")).toHaveLength(0);
281
+ expect(timingLines(spy, "cursor_token_exchange")).toHaveLength(0);
248
282
  });
249
283
  });
250
284