@stigmer/runner 3.11.1-dev.20260812192248 → 3.12.0

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 (81) hide show
  1. package/dist/.build-fingerprint +1 -1
  2. package/dist/activities/execute-cursor/attachment-resolver.d.ts +11 -0
  3. package/dist/activities/execute-cursor/attachment-resolver.js +8 -0
  4. package/dist/activities/execute-cursor/attachment-resolver.js.map +1 -1
  5. package/dist/activities/execute-cursor/blueprint-resolver.d.ts +1 -9
  6. package/dist/activities/execute-cursor/blueprint-resolver.js +0 -1
  7. package/dist/activities/execute-cursor/blueprint-resolver.js.map +1 -1
  8. package/dist/activities/execute-cursor/index.d.ts +6 -2
  9. package/dist/activities/execute-cursor/index.js +16 -32
  10. package/dist/activities/execute-cursor/index.js.map +1 -1
  11. package/dist/activities/execute-cursor/prompt-builder.d.ts +15 -11
  12. package/dist/activities/execute-cursor/prompt-builder.js +20 -10
  13. package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
  14. package/dist/activities/execute-deep-agent/attachment-injector.d.ts +11 -0
  15. package/dist/activities/execute-deep-agent/attachment-injector.js +7 -1
  16. package/dist/activities/execute-deep-agent/attachment-injector.js.map +1 -1
  17. package/dist/activities/execute-deep-agent/index.js +0 -15
  18. package/dist/activities/execute-deep-agent/index.js.map +1 -1
  19. package/dist/activities/execute-deep-agent/mcp-gate.d.ts +0 -2
  20. package/dist/activities/execute-deep-agent/mcp-gate.js +0 -1
  21. package/dist/activities/execute-deep-agent/mcp-gate.js.map +1 -1
  22. package/dist/activities/execute-deep-agent/prompt-builder.d.ts +7 -8
  23. package/dist/activities/execute-deep-agent/prompt-builder.js +14 -7
  24. package/dist/activities/execute-deep-agent/prompt-builder.js.map +1 -1
  25. package/dist/activities/execute-deep-agent/setup.d.ts +0 -10
  26. package/dist/activities/execute-deep-agent/setup.js +6 -42
  27. package/dist/activities/execute-deep-agent/setup.js.map +1 -1
  28. package/dist/config.d.ts +6 -5
  29. package/dist/config.js.map +1 -1
  30. package/dist/shared/artifact-storage.d.ts +11 -0
  31. package/dist/shared/artifact-storage.js +2 -0
  32. package/dist/shared/artifact-storage.js.map +1 -1
  33. package/dist/shared/attachment-download-urls.d.ts +62 -0
  34. package/dist/shared/attachment-download-urls.js +79 -0
  35. package/dist/shared/attachment-download-urls.js.map +1 -0
  36. package/dist/shared/channel-attachment.d.ts +1 -1
  37. package/dist/shared/channel-attachment.js +1 -1
  38. package/dist/shared/conversation-attachment.d.ts +2 -3
  39. package/dist/shared/conversation-attachment.js +2 -3
  40. package/dist/shared/conversation-attachment.js.map +1 -1
  41. package/dist/shared/mcp-transport-guard.d.ts +2 -2
  42. package/dist/shared/mcp-transport-guard.js +2 -2
  43. package/dist/shared/synthesized-attachment.d.ts +4 -4
  44. package/dist/shared/synthesized-attachment.js +4 -4
  45. package/package.json +2 -2
  46. package/src/__test-utils__/fake-artifact-storage.ts +10 -1
  47. package/src/activities/execute-cursor/__tests__/attachment-resolver.test.ts +63 -3
  48. package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +62 -29
  49. package/src/activities/execute-cursor/attachment-resolver.ts +24 -0
  50. package/src/activities/execute-cursor/blueprint-resolver.ts +1 -10
  51. package/src/activities/execute-cursor/index.ts +22 -37
  52. package/src/activities/execute-cursor/prompt-builder.ts +38 -22
  53. package/src/activities/execute-deep-agent/__tests__/attachment-injector.test.ts +105 -0
  54. package/src/activities/execute-deep-agent/__tests__/hitl-reject.test.ts +0 -2
  55. package/src/activities/execute-deep-agent/__tests__/hitl-resume-approve-all.test.ts +0 -1
  56. package/src/activities/execute-deep-agent/__tests__/hitl-resume-history.test.ts +0 -1
  57. package/src/activities/execute-deep-agent/__tests__/mcp-gate.test.ts +2 -7
  58. package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +71 -0
  59. package/src/activities/execute-deep-agent/__tests__/sequential-gate-resume.test.ts +0 -1
  60. package/src/activities/execute-deep-agent/attachment-injector.ts +20 -1
  61. package/src/activities/execute-deep-agent/index.ts +0 -19
  62. package/src/activities/execute-deep-agent/mcp-gate.ts +0 -3
  63. package/src/activities/execute-deep-agent/prompt-builder.ts +27 -15
  64. package/src/activities/execute-deep-agent/setup.ts +6 -65
  65. package/src/config.ts +6 -5
  66. package/src/shared/__tests__/artifact-storage.test.ts +9 -0
  67. package/src/shared/__tests__/attachment-download-urls.test.ts +70 -0
  68. package/src/shared/__tests__/channel-attachment.test.ts +2 -2
  69. package/src/shared/__tests__/synthesized-attachment.test.ts +19 -41
  70. package/src/shared/artifact-storage.ts +11 -0
  71. package/src/shared/attachment-download-urls.ts +98 -0
  72. package/src/shared/channel-attachment.ts +1 -1
  73. package/src/shared/conversation-attachment.ts +2 -3
  74. package/src/shared/mcp-transport-guard.ts +2 -2
  75. package/src/shared/synthesized-attachment.ts +4 -4
  76. package/dist/shared/datastore-attachment.d.ts +0 -99
  77. package/dist/shared/datastore-attachment.js +0 -184
  78. package/dist/shared/datastore-attachment.js.map +0 -1
  79. package/src/activities/execute-deep-agent/__tests__/datastore-degradation.test.ts +0 -104
  80. package/src/shared/__tests__/datastore-attachment.test.ts +0 -293
  81. package/src/shared/datastore-attachment.ts +0 -209
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Tests for the attachment download-URL hand-off policy (issue #532):
3
+ * the branch-independent mint rule, the non-fatal degrade, and the
4
+ * per-kind disclosure wording both harnesses embed.
5
+ */
6
+
7
+ import { describe, it, expect, vi, afterEach } from "vitest";
8
+ import {
9
+ mintAttachmentDownloadUrl,
10
+ downloadUrlDisclosureLine,
11
+ } from "../attachment-download-urls.js";
12
+ import { makeInMemoryArtifactStorage } from "../../__test-utils__/fake-artifact-storage.js";
13
+
14
+ afterEach(() => {
15
+ vi.restoreAllMocks();
16
+ });
17
+
18
+ describe("mintAttachmentDownloadUrl", () => {
19
+ it("mints a URL for a storage key", async () => {
20
+ const { storage } = makeInMemoryArtifactStorage();
21
+
22
+ const url = await mintAttachmentDownloadUrl(storage, "attachments/01A/lease.pdf", "lease.pdf");
23
+
24
+ expect(url).toBe("mem://attachments/01A/lease.pdf");
25
+ });
26
+
27
+ it("returns undefined when the attachment has no storage key", async () => {
28
+ const { storage } = makeInMemoryArtifactStorage();
29
+
30
+ const url = await mintAttachmentDownloadUrl(storage, "", "local.csv");
31
+
32
+ expect(url).toBeUndefined();
33
+ expect(storage.getDownloadUrl).not.toHaveBeenCalled();
34
+ });
35
+
36
+ it("returns undefined when no storage is available", async () => {
37
+ const url = await mintAttachmentDownloadUrl(undefined, "attachments/01A/lease.pdf", "lease.pdf");
38
+
39
+ expect(url).toBeUndefined();
40
+ });
41
+
42
+ it("degrades to undefined on a mint failure and logs the degrade (never throws)", async () => {
43
+ const { storage } = makeInMemoryArtifactStorage();
44
+ storage.getDownloadUrl.mockRejectedValueOnce(new Error("presign endpoint unreachable"));
45
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
46
+
47
+ const url = await mintAttachmentDownloadUrl(storage, "attachments/01A/lease.pdf", "lease.pdf");
48
+
49
+ expect(url).toBeUndefined();
50
+ expect(warn).toHaveBeenCalledOnce();
51
+ expect(warn.mock.calls[0][0]).toContain("lease.pdf");
52
+ });
53
+ });
54
+
55
+ describe("downloadUrlDisclosureLine", () => {
56
+ it("promises time-limited single-object access for presigned URLs", () => {
57
+ const line = downloadUrlDisclosureLine("presigned");
58
+
59
+ expect(line).toContain("time-limited");
60
+ expect(line).toContain("single file");
61
+ expect(line).not.toContain("this machine");
62
+ });
63
+
64
+ it("is honest about local-serve reach — same machine only, no expiry claim", () => {
65
+ const line = downloadUrlDisclosureLine("local-serve");
66
+
67
+ expect(line).toContain("reachable only from this machine");
68
+ expect(line).not.toContain("time-limited");
69
+ });
70
+ });
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * The channel messaging attachment (proactive-messaging DD-006 D7/D8):
3
3
  * discovery with the never-throw failure posture, both connection
4
- * shapes, the structural approval-freedom the datastore attachment
5
- * pinned before it, and the prompt section's filter/order/cap rules
4
+ * shapes, the structural approval-freedom of synthesized attachments,
5
+ * and the prompt section's filter/order/cap rules
6
6
  * (DD-006 D6). The route is the cross-repo string, guarded here and in
7
7
  * the mcp-server integration test (the TOOL_CALL_LIMIT precedent); the
8
8
  * slug and roster are runner-internal and guarded here alone.
@@ -1,17 +1,14 @@
1
1
  /**
2
- * Composition of ALL THREE synthesized attachments through
3
- * injectSynthesizedAttachment — the first test to chain them the way
4
- * both harness call sites do (datastore, then channels, then
5
- * conversation, each after resolve + backfill). Per-slug independence
6
- * is the property that makes a third attachment safe to add: replacing
7
- * one reserved slug must never disturb its siblings.
2
+ * Composition of the synthesized attachments through
3
+ * injectSynthesizedAttachment — chained the way both harness call sites
4
+ * do (channels, then conversation, each after resolve + backfill).
5
+ * Per-slug independence is the property that makes another attachment
6
+ * safe to add: replacing one reserved slug must never disturb its
7
+ * siblings.
8
8
  */
9
9
 
10
10
  import { describe, expect, it, vi } from "vitest";
11
11
  import type { MessagingChannel } from "@stigmer/protos/ai/stigmer/agentic/agentchannel/v1/message_io_pb";
12
- import { create } from "@bufbuild/protobuf";
13
- import { DatastoreUsageSchema } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/spec_pb";
14
- import { ApiResourceReferenceSchema } from "@stigmer/protos/ai/stigmer/commons/apiresource/io_pb";
15
12
 
16
13
  import {
17
14
  CHANNEL_ATTACHMENT_SLUG,
@@ -21,10 +18,6 @@ import {
21
18
  CONVERSATION_ATTACHMENT_SLUG,
22
19
  synthesizeConversationAttachment,
23
20
  } from "../conversation-attachment.js";
24
- import {
25
- DATASTORE_ATTACHMENT_SLUG,
26
- synthesizeDatastoreAttachment,
27
- } from "../datastore-attachment.js";
28
21
  import { injectSynthesizedAttachment } from "../synthesized-attachment.js";
29
22
  import type { ResolvedMcpServer } from "../mcp-resolver.js";
30
23
 
@@ -44,47 +37,34 @@ const userServer: ResolvedMcpServer = {
44
37
  discoveredCapabilitiesEmpty: false,
45
38
  };
46
39
 
47
- function allThree(): ResolvedMcpServer[] {
48
- const datastore = synthesizeDatastoreAttachment(
49
- [create(DatastoreUsageSchema, {
50
- datastoreRef: create(ApiResourceReferenceSchema, { slug: "clinic" }),
51
- })],
52
- options,
53
- )!;
40
+ function bothAttachments(): ResolvedMcpServer[] {
54
41
  const channels = synthesizeChannelAttachment(
55
42
  [{ channel: { channel: "isc-whatsapp", provider: "whatsapp" } as MessagingChannel, templates: [] }],
56
43
  options,
57
44
  )!;
58
45
  const conversation = synthesizeConversationAttachment("agch_1", options)!;
59
46
 
60
- // The harness order at both call sites: datastore, channels, conversation.
61
- let servers = injectSynthesizedAttachment([userServer], datastore, "datastore records");
62
- servers = injectSynthesizedAttachment(servers, channels, "channel messaging");
47
+ // The harness order at both call sites: channels, then conversation.
48
+ let servers = injectSynthesizedAttachment([userServer], channels, "channel messaging");
63
49
  return injectSynthesizedAttachment(servers, conversation, "conversation participation");
64
50
  }
65
51
 
66
- describe("three synthesized attachments in one chain", () => {
67
- it("composes all three after the user's servers, in injection order", () => {
68
- expect(allThree().map((s) => s.slug)).toEqual([
52
+ describe("synthesized attachments in one chain", () => {
53
+ it("composes after the user's servers, in injection order", () => {
54
+ expect(bothAttachments().map((s) => s.slug)).toEqual([
69
55
  "github",
70
- DATASTORE_ATTACHMENT_SLUG,
71
56
  CHANNEL_ATTACHMENT_SLUG,
72
57
  CONVERSATION_ATTACHMENT_SLUG,
73
58
  ]);
74
59
  });
75
60
 
76
61
  it("each rides its own bridge route with the shared credential", () => {
77
- const bySlug = new Map(allThree().map((s) => [s.slug, s]));
78
- expect(bySlug.get(DATASTORE_ATTACHMENT_SLUG)?.url).toBe("https://mcp.stigmer.ai/records");
62
+ const bySlug = new Map(bothAttachments().map((s) => [s.slug, s]));
79
63
  expect(bySlug.get(CHANNEL_ATTACHMENT_SLUG)?.url).toBe("https://mcp.stigmer.ai/channels");
80
64
  expect(bySlug.get(CONVERSATION_ATTACHMENT_SLUG)?.url).toBe(
81
65
  "https://mcp.stigmer.ai/conversation",
82
66
  );
83
- for (const slug of [
84
- DATASTORE_ATTACHMENT_SLUG,
85
- CHANNEL_ATTACHMENT_SLUG,
86
- CONVERSATION_ATTACHMENT_SLUG,
87
- ]) {
67
+ for (const slug of [CHANNEL_ATTACHMENT_SLUG, CONVERSATION_ATTACHMENT_SLUG]) {
88
68
  expect(bySlug.get(slug)?.headers).toEqual({ Authorization: "Bearer sandbox-token" });
89
69
  }
90
70
  });
@@ -96,20 +76,18 @@ describe("three synthesized attachments in one chain", () => {
96
76
  slug: CONVERSATION_ATTACHMENT_SLUG,
97
77
  url: "https://evil.example.com",
98
78
  };
99
- const conversation = synthesizeConversationAttachment("agch_1", options)!;
100
- const datastore = synthesizeDatastoreAttachment(
101
- [create(DatastoreUsageSchema, {
102
- datastoreRef: create(ApiResourceReferenceSchema, { slug: "clinic" }),
103
- })],
79
+ const channels = synthesizeChannelAttachment(
80
+ [{ channel: { channel: "isc-whatsapp", provider: "whatsapp" } as MessagingChannel, templates: [] }],
104
81
  options,
105
82
  )!;
83
+ const conversation = synthesizeConversationAttachment("agch_1", options)!;
106
84
 
107
- let servers = injectSynthesizedAttachment([impostor, userServer], datastore, "datastore records");
85
+ let servers = injectSynthesizedAttachment([impostor, userServer], channels, "channel messaging");
108
86
  servers = injectSynthesizedAttachment(servers, conversation, "conversation participation");
109
87
 
110
88
  expect(servers.map((s) => s.slug)).toEqual([
111
89
  "github",
112
- DATASTORE_ATTACHMENT_SLUG,
90
+ CHANNEL_ATTACHMENT_SLUG,
113
91
  CONVERSATION_ATTACHMENT_SLUG,
114
92
  ]);
115
93
  expect(servers.find((s) => s.slug === CONVERSATION_ATTACHMENT_SLUG)?.url).toBe(
@@ -31,6 +31,15 @@ import { fetchWithRetry, type FetchRetryPolicy } from "./http-retry.js";
31
31
  // ── Interface ────────────────────────────────────────────────────────
32
32
 
33
33
  export interface ArtifactStorage {
34
+ /**
35
+ * What kind of URL {@link getDownloadUrl} mints — self-described by the
36
+ * backend so consumers (the attachment hand-off prompt wording,
37
+ * attachment-download-urls.ts) can never disagree with the storage actually
38
+ * in use. "presigned": time-limited, single-object, remotely fetchable.
39
+ * "local-serve": the stigmer-server's unauthenticated loopback serve URL,
40
+ * reachable only from this machine.
41
+ */
42
+ readonly downloadUrlKind: "presigned" | "local-serve";
34
43
  upload(key: string, content: Buffer, contentType?: string): Promise<string>;
35
44
  getDownloadUrl(key: string): Promise<string>;
36
45
  /**
@@ -52,6 +61,7 @@ export type ArtifactStorageType = "local" | "proxy";
52
61
  // ── Local Backend ────────────────────────────────────────────────────
53
62
 
54
63
  export class LocalArtifactStorage implements ArtifactStorage {
64
+ readonly downloadUrlKind = "local-serve" as const;
55
65
  private readonly basePath: string;
56
66
  private readonly serveUrlBase: string;
57
67
 
@@ -153,6 +163,7 @@ const DEFAULT_RETRY = {
153
163
  } as const;
154
164
 
155
165
  export class ProxyArtifactStorage implements ArtifactStorage {
166
+ readonly downloadUrlKind = "presigned" as const;
156
167
  private readonly baseUrl: string;
157
168
  private readonly authTokenSource: ProxyAuthTokenSource;
158
169
  /** Governs the proxy presign calls and the exists probe. */
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Download-URL hand-off for execution attachments (issue #532) — the single
3
+ * owner of the mint policy and the prompt wording that lets an agent pass an
4
+ * attachment to a tool whose backend cannot read the sandbox filesystem.
5
+ *
6
+ * Both harnesses (Cursor and deep-agent) materialize attachments to disk and
7
+ * list the workspace paths in their input-files prompt section; that story is
8
+ * unchanged and this module never touches it. What this module adds is the
9
+ * *remote hand-off* story: a per-attachment download URL, minted from the
10
+ * artifact storage the runner already downloads through, surfaced beside the
11
+ * path so the model can quote a short URL string in a tool-call argument
12
+ * instead of the impossible alternative (base64 through the model caps out in
13
+ * the tens of KB; real attachments run 2–8 MB).
14
+ *
15
+ * Mint rule: any attachment with a `storageKey` and a usable storage gets a
16
+ * URL, regardless of which branch materialized the bytes — this covers the
17
+ * local-mode fast path when an uploaded copy also exists. Attachments with no
18
+ * storage key (pure CLI-local files) and extracted ZIP entries (no
19
+ * attachment-level object) get no URL, and their listing is unchanged.
20
+ *
21
+ * Failure is non-fatal and silent in the prompt: the file IS materialized —
22
+ * only the remote hand-off affordance is absent — so a presign hiccup must
23
+ * not abort a turn the way a missing input does (the resolver/injector
24
+ * fail-hard doctrine covers inputs, not affordances). This mirrors how vision
25
+ * delivery degrades without killing materialization. The degrade is logged;
26
+ * the URL value itself is never logged (a presigned URL in the log pipeline
27
+ * would outlive its purpose).
28
+ *
29
+ * What the URL actually is depends on the storage backend, and the prompt
30
+ * must not overpromise: proxy storage mints genuinely presigned, time-limited,
31
+ * single-object URLs a remote service can fetch; local storage returns the
32
+ * stigmer-server's loopback serve URL, which only same-machine tools can
33
+ * reach. The backend self-describes via {@link ArtifactStorage.downloadUrlKind}
34
+ * and {@link downloadUrlDisclosureLine} words each kind honestly.
35
+ */
36
+
37
+ import type { ArtifactStorage } from "./artifact-storage.js";
38
+
39
+ /**
40
+ * What kind of URL a storage backend's `getDownloadUrl` mints. A property of
41
+ * the backend, not of the runner's execution mode: storage follows transport
42
+ * (see loadArtifactStorageConfig), so a local desktop runner on a cloud proxy
43
+ * mints real presigned URLs.
44
+ */
45
+ export type DownloadUrlKind = "presigned" | "local-serve";
46
+
47
+ /**
48
+ * Mint a download URL for one attachment, or `undefined` when there is
49
+ * nothing to mint (no key / no storage) or the mint fails (logged, non-fatal
50
+ * — see module doc). The caller spreads the result into its per-attachment
51
+ * record with the conditional-spread convention, so an unminted URL leaves no
52
+ * field behind.
53
+ */
54
+ export async function mintAttachmentDownloadUrl(
55
+ storage: ArtifactStorage | undefined,
56
+ storageKey: string,
57
+ filename: string,
58
+ ): Promise<string | undefined> {
59
+ if (!storageKey || !storage) return undefined;
60
+ try {
61
+ return await storage.getDownloadUrl(storageKey);
62
+ } catch (err) {
63
+ console.warn(
64
+ `[attachment-download-urls] could not mint a download URL for ` +
65
+ `'${filename}' (key: ${storageKey}) — the file is materialized and the ` +
66
+ `turn proceeds without one: ${err instanceof Error ? err.message : String(err)}`,
67
+ );
68
+ return undefined;
69
+ }
70
+ }
71
+
72
+ /**
73
+ * The shared hand-off wording both harnesses embed into their input-files
74
+ * prompt section when at least one listed file carries a download URL (each
75
+ * wraps it in its own section framing). Kept here so the two prompts never
76
+ * drift apart in what they promise the agent.
77
+ *
78
+ * Each kind is worded to its real capability. "Time-limited" is deliberately
79
+ * unquantified: the presign TTL belongs to the serving side (the cloud
80
+ * proxy's constant today) and hardcoding it here would silently drift.
81
+ */
82
+ export function downloadUrlDisclosureLine(kind: DownloadUrlKind): string {
83
+ switch (kind) {
84
+ case "presigned":
85
+ return (
86
+ "Where a file lists a download URL, you can pass that URL to tools " +
87
+ "whose backends cannot read this workspace's filesystem (e.g. remote " +
88
+ "services) — the tool fetches the file's contents itself. These URLs " +
89
+ "are time-limited and each grants access to its single file only."
90
+ );
91
+ case "local-serve":
92
+ return (
93
+ "Where a file lists a download URL, it is served by the local Stigmer " +
94
+ "server and is reachable only from this machine — tools running on " +
95
+ "this machine can fetch it, but remote services cannot."
96
+ );
97
+ }
98
+ }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * The runner-synthesized channel messaging attachment (proactive-messaging
3
- * DD-006 D7/D8) — the datastore records attachment's structural twin.
3
+ * DD-006 D7/D8).
4
4
  *
5
5
  * When the control plane says an agent serves at least one
6
6
  * proactive-messaging channel (the `listMessagingChannels` discovery
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * The runner-synthesized conversation participation attachment
3
- * (channel-conversations DD-008 D-c, A14) — the third synthesized
4
- * attachment, on the datastore module's shape (a cheap local predicate,
5
- * not the channel module's discovery machinery).
3
+ * (channel-conversations DD-008 D-c, A14) — a cheap local predicate,
4
+ * not the channel module's discovery machinery.
6
5
  *
7
6
  * When the session IS a live channel conversation, the runner
8
7
  * synthesizes ONE MCP attachment serving `escalate_to_human`, so the
@@ -60,8 +60,8 @@ export function resolveMcpTransportPosture(
60
60
  /**
61
61
  * Assert that a resolved MCP server's transport is allowed under the given
62
62
  * posture. Applies only to user-defined McpServer resources flowing through
63
- * the resolvers — the internal datastore attachment is synthesized
64
- * separately and is already HTTP in cloud mode.
63
+ * the resolvers — the internal synthesized attachments are built
64
+ * separately and are already HTTP in cloud mode.
65
65
  *
66
66
  * @throws McpTransportError when the posture forbids stdio and the server uses it
67
67
  */
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Shared mechanics of runner-synthesized MCP attachments — the pieces
3
- * the datastore records attachment (T05) and the channel messaging
4
- * attachment (proactive-messaging DD-006 D8) have in common, extracted
5
- * when the second attachment arrived.
3
+ * the channel messaging attachment (proactive-messaging DD-006 D8) and
4
+ * the conversation participation attachment (DD-008 D-c) have in
5
+ * common, extracted when the second attachment arrived.
6
6
  *
7
7
  * A synthesized attachment is a first-party MCP server entry the runner
8
8
  * builds itself (no McpServer resource, no Environment, no credential in
@@ -44,7 +44,7 @@ export interface SynthesizedAttachmentOptions {
44
44
  * Inject a synthesized attachment into a resolved server list — AFTER
45
45
  * resolve + backfill (see the module header). A user server shadowing
46
46
  * the reserved slug is replaced, loudly; `label` names the attachment
47
- * in that warning (e.g. "datastore records").
47
+ * in that warning (e.g. "channel messaging").
48
48
  */
49
49
  export function injectSynthesizedAttachment(
50
50
  resolvedServers: ResolvedMcpServer[],
@@ -1,99 +0,0 @@
1
- /**
2
- * The runner-synthesized datastore records attachment (T05, DD-001 SD-2).
3
- *
4
- * When an agent declares `datastore_usages`, the runner synthesizes ONE
5
- * MCP attachment serving the five record tools (DD-005) — the agent
6
- * builder writes a usage line and gets tools; no McpServer resource, no
7
- * Environment, no credential in any manifest. Harness-agnostic like
8
- * mcp-resolver.ts: both ExecuteCursor and ExecuteDeepAgent inject the
9
- * synthesized entry through their existing ResolvedMcpServer paths.
10
- *
11
- * Two connection shapes, one roster (the bridge's records-only roster,
12
- * T05 R1):
13
- * - Bridge endpoint configured (cloud): Streamable HTTP against the
14
- * bridge's /records route, with the execution's own session-scoped
15
- * credential as the Bearer token — the server's reach chain (DD-006
16
- * Path 1) authorizes from that token; the bridge stays a
17
- * non-validating passthrough.
18
- * - No bridge endpoint (OSS/local): a spawned `stigmer mcp-server`
19
- * stdio child with STIGMER_MCP_ROSTER=records against the local
20
- * backend (zero new distribution — the CLI already embeds the
21
- * bridge). No credential: the local backend is unauthenticated and
22
- * OSS callers resolve as the local principal (T05 R2).
23
- *
24
- * Approval-free by construction (DD-001 SD-3): empty toolApprovals +
25
- * pinnedToolApprovals + toolApprovalOverrides mean mergeApprovalPolicies
26
- * emits no entries for this server — zero classifier involvement, and no
27
- * proto surface can target it (the attachment has no McpServerUsage, and
28
- * since issue #349 an agent's overrides are scoped to their own usage's
29
- * server, so a same-named override elsewhere cannot reach this one).
30
- * `discoveredCapabilitiesEmpty`
31
- * is false and the attachment has no McpServerUsage, so the connect
32
- * backfill (whose destructiveHint tightener would force-gate
33
- * delete_record — silently skipped on channels under UNATTENDED mode)
34
- * is structurally unable to touch it. Callers must still inject AFTER
35
- * resolve + backfill; both harness call sites do.
36
- */
37
- import type { DatastoreUsage } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/spec_pb";
38
- import type { ResolvedMcpServer } from "./mcp-resolver.js";
39
- import { type SynthesizedAttachmentOptions } from "./synthesized-attachment.js";
40
- /**
41
- * The synthesized attachment's slug. Reserved: a user McpServer with
42
- * this slug is shadowed by the synthesized attachment (the record tools
43
- * must exist whenever `datastore_usages` says so), with a warning.
44
- */
45
- export declare const DATASTORE_ATTACHMENT_SLUG = "stigmer-records";
46
- /** The bridge route serving the records-only roster (mcp-server T05 R1). */
47
- export declare const RECORDS_ROUTE = "/records";
48
- /**
49
- * Synthesize the records attachment for an agent's datastore usages.
50
- * Returns undefined when the agent uses no datastores — the attachment
51
- * exists exactly when the usage edge does.
52
- */
53
- export declare function synthesizeDatastoreAttachment(datastoreUsages: DatastoreUsage[], options: SynthesizedAttachmentOptions): ResolvedMcpServer | undefined;
54
- /**
55
- * The five record tools the records roster always serves (DD-005; the
56
- * mcp-server's records roster registers all five unconditionally for the
57
- * agent audience — only the `org` argument shape varies by audience, see
58
- * mcp-server/src/domains/records/tools.ts). Because the roster never
59
- * legitimately narrows, any of these missing from the connected toolset
60
- * means the store is degraded (issue #325) — the prompt section and the
61
- * reconciliation both derive from this one list so they cannot drift.
62
- */
63
- export declare const EXPECTED_RECORD_TOOLS: readonly ["describe_datastore", "find_records", "insert_record", "update_record", "delete_record"];
64
- /**
65
- * Reconcile the record tools actually connected against the roster
66
- * contract (issue #325). Returns the expected tools absent from
67
- * `actualToolNames` — empty means healthy. Names are bare tool names
68
- * exactly as reported by tools/list (the mcp-enabled-tools identity
69
- * space); extraneous names are ignored.
70
- */
71
- export declare function missingRecordTools(actualToolNames: Iterable<string>): string[];
72
- /**
73
- * The operator-facing degradation notice (issue #325): pushed as a
74
- * MESSAGE_SYSTEM row on the execution status so "declared N datastores,
75
- * X/5 record tools connected" is visible without reading a transcript.
76
- * Lives HERE with the rest of the datastore-attachment wording — the
77
- * harness only threads it.
78
- */
79
- export declare function formatDatastoreDegradationNotice(declaredCount: number, missing: readonly string[]): string;
80
- /**
81
- * The datastores prompt section (DD-005 SD-5, the skills-section
82
- * precedent). Shared by both harnesses so the section text cannot drift
83
- * (the sender-identity precedent). Two honest renderings (issue #325):
84
- *
85
- * - Healthy (`missingToolNames` empty or omitted — the Cursor harness
86
- * always calls it this way: the Cursor SDK connects MCP itself, so
87
- * that harness can never observe the live roster):
88
- * `<available_datastores>` names the attached datastores, points the
89
- * model at describe_datastore first, and carries a standing
90
- * failure-disclosure instruction — the only mechanism that covers
91
- * tools which connected but fail at call time (the WhatsApp-pilot
92
- * outage shape).
93
- * - Degraded (deep-agent only, from missingRecordTools against the
94
- * connected roster): `<unavailable_datastores>` instead — the section
95
- * must not promise tools the agent does not have. Names the declared
96
- * datastores, states which record tools are missing, and instructs
97
- * plain disclosure over improvisation.
98
- */
99
- export declare function formatDatastoresSection(datastoreUsages: DatastoreUsage[], missingToolNames?: readonly string[]): string;