@stigmer/server 3.20.0 → 3.22.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 (257) hide show
  1. package/dist/authorization/evaluator.d.ts +7 -1
  2. package/dist/authorization/evaluator.d.ts.map +1 -1
  3. package/dist/authorization/evaluator.js.map +1 -1
  4. package/dist/authorization/list-read-scope.d.ts.map +1 -1
  5. package/dist/authorization/list-read-scope.js +19 -11
  6. package/dist/authorization/list-read-scope.js.map +1 -1
  7. package/dist/authorization/model/organization.d.ts.map +1 -1
  8. package/dist/authorization/model/organization.js +2 -0
  9. package/dist/authorization/model/organization.js.map +1 -1
  10. package/dist/boot/compose.d.ts.map +1 -1
  11. package/dist/boot/compose.js +0 -1
  12. package/dist/boot/compose.js.map +1 -1
  13. package/dist/boot/inprocess.d.ts +0 -2
  14. package/dist/boot/inprocess.d.ts.map +1 -1
  15. package/dist/boot/inprocess.js +0 -6
  16. package/dist/boot/inprocess.js.map +1 -1
  17. package/dist/domain/agent/controller.d.ts +2 -2
  18. package/dist/domain/agent/controller.d.ts.map +1 -1
  19. package/dist/domain/agent/controller.js +3 -17
  20. package/dist/domain/agent/controller.js.map +1 -1
  21. package/dist/domain/agent/steps.d.ts +0 -1
  22. package/dist/domain/agent/steps.d.ts.map +1 -1
  23. package/dist/domain/agent/steps.js +1 -40
  24. package/dist/domain/agent/steps.js.map +1 -1
  25. package/dist/domain/agentchannel/controller.d.ts.map +1 -1
  26. package/dist/domain/agentchannel/controller.js +6 -1
  27. package/dist/domain/agentchannel/controller.js.map +1 -1
  28. package/dist/domain/agentchannel/steps.d.ts +19 -0
  29. package/dist/domain/agentchannel/steps.d.ts.map +1 -1
  30. package/dist/domain/agentchannel/steps.js +32 -0
  31. package/dist/domain/agentchannel/steps.js.map +1 -1
  32. package/dist/domain/agentexecution/controller.d.ts.map +1 -1
  33. package/dist/domain/agentexecution/controller.js +7 -9
  34. package/dist/domain/agentexecution/controller.js.map +1 -1
  35. package/dist/domain/agentexecution/create-execution-context-step.d.ts +0 -2
  36. package/dist/domain/agentexecution/create-execution-context-step.d.ts.map +1 -1
  37. package/dist/domain/agentexecution/create-execution-context-step.js +179 -60
  38. package/dist/domain/agentexecution/create-execution-context-step.js.map +1 -1
  39. package/dist/domain/agentexecution/create-steps.d.ts +16 -31
  40. package/dist/domain/agentexecution/create-steps.d.ts.map +1 -1
  41. package/dist/domain/agentexecution/create-steps.js +36 -102
  42. package/dist/domain/agentexecution/create-steps.js.map +1 -1
  43. package/dist/domain/agentexecution/run-target.d.ts +7 -3
  44. package/dist/domain/agentexecution/run-target.d.ts.map +1 -1
  45. package/dist/domain/agentexecution/run-target.js.map +1 -1
  46. package/dist/domain/agentinstance/controller.d.ts +5 -5
  47. package/dist/domain/agentinstance/controller.d.ts.map +1 -1
  48. package/dist/domain/agentinstance/controller.js +6 -1
  49. package/dist/domain/agentinstance/controller.js.map +1 -1
  50. package/dist/domain/agentinstance/steps.d.ts +43 -8
  51. package/dist/domain/agentinstance/steps.d.ts.map +1 -1
  52. package/dist/domain/agentinstance/steps.js +71 -8
  53. package/dist/domain/agentinstance/steps.js.map +1 -1
  54. package/dist/domain/agentshare/controller.d.ts.map +1 -1
  55. package/dist/domain/agentshare/controller.js +7 -1
  56. package/dist/domain/agentshare/controller.js.map +1 -1
  57. package/dist/domain/agentshare/steps.d.ts +58 -0
  58. package/dist/domain/agentshare/steps.d.ts.map +1 -1
  59. package/dist/domain/agentshare/steps.js +109 -4
  60. package/dist/domain/agentshare/steps.js.map +1 -1
  61. package/dist/domain/artifact/controller.js +21 -25
  62. package/dist/domain/artifact/controller.js.map +1 -1
  63. package/dist/domain/channelapp/controller.d.ts.map +1 -1
  64. package/dist/domain/channelapp/controller.js +2 -0
  65. package/dist/domain/channelapp/controller.js.map +1 -1
  66. package/dist/domain/environment/controller.d.ts.map +1 -1
  67. package/dist/domain/environment/controller.js +2 -0
  68. package/dist/domain/environment/controller.js.map +1 -1
  69. package/dist/domain/environment/personal.d.ts +42 -0
  70. package/dist/domain/environment/personal.d.ts.map +1 -0
  71. package/dist/domain/environment/personal.js +96 -0
  72. package/dist/domain/environment/personal.js.map +1 -0
  73. package/dist/domain/executioncontext/controller.d.ts.map +1 -1
  74. package/dist/domain/executioncontext/controller.js +2 -0
  75. package/dist/domain/executioncontext/controller.js.map +1 -1
  76. package/dist/domain/executioncontext/steps.d.ts.map +1 -1
  77. package/dist/domain/executioncontext/steps.js +2 -1
  78. package/dist/domain/executioncontext/steps.js.map +1 -1
  79. package/dist/domain/mcpserver/connect.d.ts +0 -2
  80. package/dist/domain/mcpserver/connect.d.ts.map +1 -1
  81. package/dist/domain/mcpserver/connect.js +24 -79
  82. package/dist/domain/mcpserver/connect.js.map +1 -1
  83. package/dist/domain/mcpserver/controller.d.ts.map +1 -1
  84. package/dist/domain/mcpserver/controller.js +2 -0
  85. package/dist/domain/mcpserver/controller.js.map +1 -1
  86. package/dist/domain/mcpserver/start-connect.d.ts.map +1 -1
  87. package/dist/domain/mcpserver/start-connect.js +8 -7
  88. package/dist/domain/mcpserver/start-connect.js.map +1 -1
  89. package/dist/domain/memory/controller.d.ts.map +1 -1
  90. package/dist/domain/memory/controller.js +6 -1
  91. package/dist/domain/memory/controller.js.map +1 -1
  92. package/dist/domain/memory/steps.d.ts +23 -0
  93. package/dist/domain/memory/steps.d.ts.map +1 -1
  94. package/dist/domain/memory/steps.js +35 -0
  95. package/dist/domain/memory/steps.js.map +1 -1
  96. package/dist/domain/oauthapp/controller.d.ts.map +1 -1
  97. package/dist/domain/oauthapp/controller.js +2 -0
  98. package/dist/domain/oauthapp/controller.js.map +1 -1
  99. package/dist/domain/plugin/controller.d.ts.map +1 -1
  100. package/dist/domain/plugin/controller.js +5 -24
  101. package/dist/domain/plugin/controller.js.map +1 -1
  102. package/dist/domain/plugin/overlay/sanitize.js +2 -2
  103. package/dist/domain/schedule/controller.d.ts.map +1 -1
  104. package/dist/domain/schedule/controller.js +7 -1
  105. package/dist/domain/schedule/controller.js.map +1 -1
  106. package/dist/domain/schedule/steps.d.ts +19 -0
  107. package/dist/domain/schedule/steps.d.ts.map +1 -1
  108. package/dist/domain/schedule/steps.js +32 -0
  109. package/dist/domain/schedule/steps.js.map +1 -1
  110. package/dist/domain/session/controller.d.ts +0 -7
  111. package/dist/domain/session/controller.d.ts.map +1 -1
  112. package/dist/domain/session/controller.js +11 -16
  113. package/dist/domain/session/controller.js.map +1 -1
  114. package/dist/domain/session/run-target.d.ts +9 -8
  115. package/dist/domain/session/run-target.d.ts.map +1 -1
  116. package/dist/domain/session/run-target.js.map +1 -1
  117. package/dist/domain/session/steps.d.ts +8 -36
  118. package/dist/domain/session/steps.d.ts.map +1 -1
  119. package/dist/domain/session/steps.js +27 -172
  120. package/dist/domain/session/steps.js.map +1 -1
  121. package/dist/domain/skill/controller.d.ts.map +1 -1
  122. package/dist/domain/skill/controller.js +8 -17
  123. package/dist/domain/skill/controller.js.map +1 -1
  124. package/dist/domain/workflow/controller.d.ts.map +1 -1
  125. package/dist/domain/workflow/controller.js +30 -107
  126. package/dist/domain/workflow/controller.js.map +1 -1
  127. package/dist/domain/workflow/version-resolution.d.ts +5 -0
  128. package/dist/domain/workflow/version-resolution.d.ts.map +1 -0
  129. package/dist/domain/workflow/version-resolution.js +113 -0
  130. package/dist/domain/workflow/version-resolution.js.map +1 -0
  131. package/dist/domain/workflowexecution/create-steps.d.ts +4 -3
  132. package/dist/domain/workflowexecution/create-steps.d.ts.map +1 -1
  133. package/dist/domain/workflowexecution/create-steps.js +4 -3
  134. package/dist/domain/workflowexecution/create-steps.js.map +1 -1
  135. package/dist/domain/workflowinstance/controller.d.ts.map +1 -1
  136. package/dist/domain/workflowinstance/controller.js +6 -1
  137. package/dist/domain/workflowinstance/controller.js.map +1 -1
  138. package/dist/domain/workflowinstance/steps.d.ts +20 -0
  139. package/dist/domain/workflowinstance/steps.d.ts.map +1 -1
  140. package/dist/domain/workflowinstance/steps.js +39 -0
  141. package/dist/domain/workflowinstance/steps.js.map +1 -1
  142. package/dist/extensions/identity.d.ts +19 -0
  143. package/dist/extensions/identity.d.ts.map +1 -1
  144. package/dist/extensions/identity.js +22 -3
  145. package/dist/extensions/identity.js.map +1 -1
  146. package/dist/extensions/list-read-scope.d.ts +41 -7
  147. package/dist/extensions/list-read-scope.d.ts.map +1 -1
  148. package/dist/extensions/list-read-scope.js +13 -2
  149. package/dist/extensions/list-read-scope.js.map +1 -1
  150. package/dist/pipeline/apiresource-labels.d.ts +2 -2
  151. package/dist/pipeline/apiresource-labels.js +2 -2
  152. package/dist/pipeline/errors.d.ts +2 -2
  153. package/dist/pipeline/errors.js +2 -2
  154. package/dist/pipeline/steps/authorize-resolved-target.d.ts +88 -0
  155. package/dist/pipeline/steps/authorize-resolved-target.d.ts.map +1 -0
  156. package/dist/pipeline/steps/authorize-resolved-target.js +36 -0
  157. package/dist/pipeline/steps/authorize-resolved-target.js.map +1 -0
  158. package/dist/pipeline/steps/authorize.d.ts +27 -0
  159. package/dist/pipeline/steps/authorize.d.ts.map +1 -1
  160. package/dist/pipeline/steps/authorize.js +49 -10
  161. package/dist/pipeline/steps/authorize.js.map +1 -1
  162. package/dist/pipeline/steps/guard-memory-capture.d.ts.map +1 -1
  163. package/dist/pipeline/steps/guard-memory-capture.js +2 -1
  164. package/dist/pipeline/steps/guard-memory-capture.js.map +1 -1
  165. package/dist/pipeline/steps/guard-reserved-labels.d.ts.map +1 -1
  166. package/dist/pipeline/steps/guard-reserved-labels.js +12 -12
  167. package/dist/pipeline/steps/guard-reserved-labels.js.map +1 -1
  168. package/dist/pipeline/steps/helpers.d.ts +9 -5
  169. package/dist/pipeline/steps/helpers.d.ts.map +1 -1
  170. package/dist/pipeline/steps/helpers.js +11 -15
  171. package/dist/pipeline/steps/helpers.js.map +1 -1
  172. package/dist/pipeline/steps/version-history.d.ts +11 -0
  173. package/dist/pipeline/steps/version-history.d.ts.map +1 -1
  174. package/dist/pipeline/steps/version-history.js +26 -0
  175. package/dist/pipeline/steps/version-history.js.map +1 -1
  176. package/package.json +6 -6
  177. package/src/authorization/__tests__/list-read-scope.test.ts +23 -12
  178. package/src/authorization/__tests__/skip-lane-inventory.test.ts +160 -0
  179. package/src/authorization/__tests__/wire-permissions.test.ts +5 -5
  180. package/src/authorization/evaluator.ts +7 -0
  181. package/src/authorization/list-read-scope.ts +22 -11
  182. package/src/authorization/model/__tests__/registry.test.ts +2 -0
  183. package/src/authorization/model/organization.ts +2 -0
  184. package/src/boot/compose.ts +0 -1
  185. package/src/boot/inprocess.ts +0 -9
  186. package/src/domain/agent/__tests__/agent.test.ts +0 -68
  187. package/src/domain/agent/controller.ts +11 -36
  188. package/src/domain/agent/steps.ts +1 -60
  189. package/src/domain/agentchannel/__tests__/create-authorization.test.ts +58 -0
  190. package/src/domain/agentchannel/controller.ts +19 -0
  191. package/src/domain/agentchannel/steps.ts +40 -0
  192. package/src/domain/agentexecution/__tests__/create-execution-context-step.test.ts +216 -0
  193. package/src/domain/agentexecution/__tests__/create-steps.test.ts +70 -145
  194. package/src/domain/agentexecution/controller.ts +6 -10
  195. package/src/domain/agentexecution/create-execution-context-step.ts +271 -83
  196. package/src/domain/agentexecution/create-steps.ts +38 -138
  197. package/src/domain/agentexecution/run-target.ts +7 -3
  198. package/src/domain/agentinstance/__tests__/create-authorization.composed.test.ts +243 -0
  199. package/src/domain/agentinstance/__tests__/create-authorization.test.ts +144 -0
  200. package/src/domain/agentinstance/controller.ts +24 -5
  201. package/src/domain/agentinstance/steps.ts +82 -8
  202. package/src/domain/agentshare/__tests__/create-authorization.test.ts +86 -0
  203. package/src/domain/agentshare/__tests__/member-audience.test.ts +113 -0
  204. package/src/domain/agentshare/controller.ts +21 -0
  205. package/src/domain/agentshare/steps.ts +181 -15
  206. package/src/domain/artifact/controller.ts +18 -22
  207. package/src/domain/channelapp/controller.ts +10 -0
  208. package/src/domain/environment/__tests__/personal.test.ts +143 -0
  209. package/src/domain/environment/controller.ts +10 -0
  210. package/src/domain/environment/personal.ts +141 -0
  211. package/src/domain/executioncontext/__tests__/executioncontext.test.ts +1 -0
  212. package/src/domain/executioncontext/controller.ts +10 -0
  213. package/src/domain/executioncontext/steps.ts +2 -1
  214. package/src/domain/mcpserver/connect.ts +32 -92
  215. package/src/domain/mcpserver/controller.ts +13 -1
  216. package/src/domain/mcpserver/start-connect.ts +17 -11
  217. package/src/domain/memory/__tests__/create-authorization.test.ts +59 -0
  218. package/src/domain/memory/controller.ts +11 -0
  219. package/src/domain/memory/steps.ts +41 -0
  220. package/src/domain/oauthapp/controller.ts +10 -0
  221. package/src/domain/plugin/controller.ts +22 -36
  222. package/src/domain/plugin/overlay/sanitize.ts +2 -2
  223. package/src/domain/schedule/__tests__/create-authorization.test.ts +58 -0
  224. package/src/domain/schedule/controller.ts +20 -0
  225. package/src/domain/schedule/steps.ts +45 -2
  226. package/src/domain/session/__tests__/run-gate.test.ts +19 -52
  227. package/src/domain/session/__tests__/session.test.ts +27 -203
  228. package/src/domain/session/controller.ts +10 -30
  229. package/src/domain/session/run-target.ts +9 -8
  230. package/src/domain/session/steps.ts +33 -267
  231. package/src/domain/skill/controller.ts +24 -23
  232. package/src/domain/workflow/controller.ts +58 -145
  233. package/src/domain/workflow/version-resolution.ts +166 -0
  234. package/src/domain/workflowexecution/create-steps.ts +4 -3
  235. package/src/domain/workflowinstance/__tests__/create-authorization.test.ts +109 -0
  236. package/src/domain/workflowinstance/controller.ts +19 -0
  237. package/src/domain/workflowinstance/steps.ts +49 -1
  238. package/src/extensions/__tests__/composed-support.ts +38 -4
  239. package/src/extensions/__tests__/identity.test.ts +44 -1
  240. package/src/extensions/__tests__/list-read-scope-composed.test.ts +87 -1
  241. package/src/extensions/__tests__/list-read-scope.test.ts +63 -0
  242. package/src/extensions/identity.ts +23 -5
  243. package/src/extensions/list-read-scope.ts +47 -7
  244. package/src/pipeline/apiresource-labels.ts +2 -2
  245. package/src/pipeline/errors.ts +2 -2
  246. package/src/pipeline/steps/__tests__/authorize-resolved-target.test.ts +285 -0
  247. package/src/pipeline/steps/authorize-resolved-target.ts +129 -0
  248. package/src/pipeline/steps/authorize.ts +70 -13
  249. package/src/pipeline/steps/guard-memory-capture.ts +2 -1
  250. package/src/pipeline/steps/guard-reserved-labels.ts +12 -14
  251. package/src/pipeline/steps/helpers.ts +11 -15
  252. package/src/pipeline/steps/version-history.ts +34 -0
  253. package/dist/domain/agent/defaultagent.d.ts +0 -37
  254. package/dist/domain/agent/defaultagent.d.ts.map +0 -1
  255. package/dist/domain/agent/defaultagent.js +0 -104
  256. package/dist/domain/agent/defaultagent.js.map +0 -1
  257. package/src/domain/agent/defaultagent.ts +0 -135
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Pins the coverage inventory's own rule for `is_skip_authorization` lanes
3
+ * (docs/authorization-coverage.md, "How to read the tables"): a skip lane
4
+ * reaches no Authorizer in either edition, so what guards it is whatever
5
+ * its Handler cell names — and a cell that names nothing is a lane open to
6
+ * every authenticated caller that nobody has said so about. Two things are
7
+ * held here, so the doc cannot drift from the server it describes:
8
+ *
9
+ * 1. The set of skip-annotated methods the open-source server SERVES
10
+ * (the empty composition's routes replayed into a recorder, a method
11
+ * counted only when its service's implementation map carries it — a
12
+ * partially implemented service lists more on its descriptor than it
13
+ * serves) equals the set of skip rows in the doc, service by method.
14
+ * A new skip lane without a row, or a row for a lane that is no
15
+ * longer a skip, fails here.
16
+ * 2. Every skip row's Handler cell carries one of the three markers the
17
+ * doc defines — `guard: <Name>`, `driver: <Seam>`,
18
+ * `by design: <reason>` — and every `guard:` names a pipeline step or
19
+ * a handler's guard function that exists by that name in `src/`
20
+ * outside the tests, so a renamed or removed guard cannot leave the
21
+ * doc pointing at nothing.
22
+ *
23
+ * The rule lives in the doc; this test only enforces it. The doc is the
24
+ * one inventory (the package guide mandates updating it with every
25
+ * annotation change), and a second list here would be the drift this
26
+ * test exists to prevent.
27
+ */
28
+ import {
29
+ mkdtempSync,
30
+ readFileSync,
31
+ readdirSync,
32
+ rmSync,
33
+ statSync,
34
+ } from "node:fs";
35
+ import { tmpdir } from "node:os";
36
+ import path from "node:path";
37
+ import { fileURLToPath } from "node:url";
38
+
39
+ import { getOption } from "@bufbuild/protobuf";
40
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
41
+
42
+ import { is_skip_authorization } from "@stigmer/protos/ai/stigmer/commons/rpc/method_options_pb";
43
+
44
+ import { loadConfig } from "../../boot/config.js";
45
+ import { composeServer } from "../../boot/compose.js";
46
+ import type { ComposedServer } from "../../boot/compose.js";
47
+ import {
48
+ baseConfig,
49
+ servedMethods,
50
+ silentLogger,
51
+ } from "../../extensions/__tests__/composed-support.js";
52
+
53
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
54
+ const PACKAGE_ROOT = path.resolve(HERE, "../../..");
55
+ const COVERAGE_DOC = path.join(PACKAGE_ROOT, "docs/authorization-coverage.md");
56
+ const SRC = path.join(PACKAGE_ROOT, "src");
57
+
58
+ /** A skip row: `| Service.method | is_skip_authorization | <handler cell> |`. */
59
+ const SKIP_ROW =
60
+ /^\| ([A-Za-z]+)\.([A-Za-z]+) \| is_skip_authorization \| (.*) \|$/;
61
+
62
+ const MARKERS = ["guard:", "driver:", "by design:"] as const;
63
+
64
+ interface SkipRow {
65
+ readonly method: string;
66
+ readonly handler: string;
67
+ }
68
+
69
+ function skipRowsOf(doc: string): SkipRow[] {
70
+ const rows: SkipRow[] = [];
71
+ for (const line of doc.split("\n")) {
72
+ const m = SKIP_ROW.exec(line);
73
+ if (m !== null) {
74
+ rows.push({ method: `${m[1]}.${m[2]}`, handler: m[3] ?? "" });
75
+ }
76
+ }
77
+ return rows;
78
+ }
79
+
80
+ /** Every `guard: <Name>` a handler cell names. */
81
+ function guardsOf(handler: string): string[] {
82
+ return [...handler.matchAll(/guard: ([A-Za-z]+)/g)].map((m) => m[1] ?? "");
83
+ }
84
+
85
+ /** Every non-test `.ts` source under `src/`, read once. */
86
+ function sourceText(): string {
87
+ const parts: string[] = [];
88
+ const walk = (dir: string): void => {
89
+ for (const entry of readdirSync(dir)) {
90
+ const full = path.join(dir, entry);
91
+ if (statSync(full).isDirectory()) {
92
+ if (entry !== "__tests__") {
93
+ walk(full);
94
+ }
95
+ } else if (full.endsWith(".ts") && !full.endsWith(".test.ts")) {
96
+ parts.push(readFileSync(full, "utf8"));
97
+ }
98
+ }
99
+ };
100
+ walk(SRC);
101
+ return parts.join("\n");
102
+ }
103
+
104
+ describe("the skip-lane inventory (docs/authorization-coverage.md) is true to the served server", () => {
105
+ let dir: string;
106
+ let server: ComposedServer;
107
+
108
+ beforeAll(async () => {
109
+ dir = mkdtempSync(path.join(tmpdir(), "skip-lane-inventory-test-"));
110
+ server = await composeServer({
111
+ config: loadConfig(baseConfig(dir)),
112
+ logger: silentLogger,
113
+ portOverride: 0,
114
+ host: "127.0.0.1",
115
+ });
116
+ });
117
+
118
+ afterAll(async () => {
119
+ await server.shutdown();
120
+ rmSync(dir, { recursive: true, force: true });
121
+ });
122
+
123
+ it("the doc's skip rows are exactly the skip-annotated methods the server serves", () => {
124
+ const served = new Set<string>();
125
+ for (const method of servedMethods(server.routes)) {
126
+ if (getOption(method, is_skip_authorization)) {
127
+ const serviceName = method.parent.typeName.split(".").at(-1) ?? "";
128
+ served.add(`${serviceName}.${method.name}`);
129
+ }
130
+ }
131
+ const documented = new Set(
132
+ skipRowsOf(readFileSync(COVERAGE_DOC, "utf8")).map((r) => r.method),
133
+ );
134
+ expect([...served].sort()).toEqual([...documented].sort());
135
+ });
136
+
137
+ it("every skip row names its guard, its driver or says the lane is open by design", () => {
138
+ const bare = skipRowsOf(readFileSync(COVERAGE_DOC, "utf8"))
139
+ .filter((r) => !MARKERS.some((marker) => r.handler.includes(marker)))
140
+ .map((r) => r.method);
141
+ expect(bare).toEqual([]);
142
+ });
143
+
144
+ it("every guard a skip row names is a step or a guard function that exists by that name in src/", () => {
145
+ const source = sourceText();
146
+ const dangling: string[] = [];
147
+ for (const row of skipRowsOf(readFileSync(COVERAGE_DOC, "utf8"))) {
148
+ for (const guard of guardsOf(row.handler)) {
149
+ // A step is registered by its quoted name; a guard function by its
150
+ // declaration.
151
+ const isStep = source.includes(`"${guard}"`);
152
+ const isFunction = new RegExp(`function ${guard}\\(`).test(source);
153
+ if (!isStep && !isFunction) {
154
+ dangling.push(`${row.method} names guard ${guard}`);
155
+ }
156
+ }
157
+ }
158
+ expect(dangling).toEqual([]);
159
+ });
160
+ });
@@ -117,12 +117,11 @@ describe("every static (kind, permission) the served RPCs ask about is a declare
117
117
  expect(undeclared).toEqual([]);
118
118
  });
119
119
 
120
- it("asks about every served kind the model declares except the two no RPC targets statically", () => {
120
+ it("asks about every served kind the model declares except the one no RPC targets statically", () => {
121
121
  // iam_policy's RPCs target the policy's RESOURCE (`resource_kind_path`
122
- // or the load-then-authorize `get`); execution_context's RPCs are
123
- // `is_skip_authorization` lanes (or unannotated) whose handlers gate on
124
- // the runner's execution-scoped token. Every other declared kind is
125
- // asked about by at least one static annotation.
122
+ // or the load-then-authorize `get`). Every other declared kind is asked
123
+ // about by at least one static annotation — execution_context through
124
+ // its `get` and `delete`; its runner-token lane stays a skip.
126
125
  const asked = new Set(wireQuestions().map((q) => kindEnumName(q.kind)));
127
126
  expect([...asked].sort()).toEqual(
128
127
  [
@@ -135,6 +134,7 @@ describe("every static (kind, permission) the served RPCs ask about is a declare
135
134
  "artifact",
136
135
  "channel_app",
137
136
  "environment",
137
+ "execution_context",
138
138
  "identity_account",
139
139
  "mcp_server",
140
140
  "memory",
@@ -67,8 +67,15 @@ import {
67
67
  */
68
68
  export const MAX_RESOLUTION_DEPTH = 25;
69
69
 
70
+ /**
71
+ * The faults an evaluation can raise. Two are consumer bugs by the seam
72
+ * contracts (a kind the model does not declare; the `internal` class
73
+ * offered to the list scope's restrict verb, which the shared helper
74
+ * answers before any driver); the rest are the model's own.
75
+ */
70
76
  export type EvaluationFault =
71
77
  | "undeclared-target-kind"
78
+ | "internal-caller-offered"
72
79
  | "resolution-depth-exceeded"
73
80
  | "resolution-cycle"
74
81
  | "unknown-condition";
@@ -38,17 +38,25 @@
38
38
  * lane can show it either.
39
39
  *
40
40
  * The `internal` class — the server acting as itself over the in-process
41
- * transport — keeps every offered id and enumerates every id of the kind:
42
- * the in-process authorization skip (pipeline/interceptors/auth.ts, ruling
43
- * Q4) applied to a list answer, as the built-in directory applies it to a
44
- * directory answer. No in-process edge reaches a list lane today; the arm
45
- * is pinned so a future one meets the same rule every other lane keeps.
41
+ * transport — is answered by the seam, not here: `restrictListByReadScope`
42
+ * returns the org-narrowed rows for the class before any driver is asked
43
+ * (extensions/list-read-scope.ts, the header's contract line), the
44
+ * Authorize step's trust-domain rule applied to a list answer. So this
45
+ * driver's `restrictListEntries` REFUSES the class (`internal-caller-offered`):
46
+ * being offered it means a caller reached the driver around the one
47
+ * consumption idiom, and evaluating the server as a person would return
48
+ * a quiet short list — the failure shape stigmer#1207 fixed. The
49
+ * enumeration verb has no helper in front of it and keeps its own arm:
50
+ * every id of the kind, as the built-in directory answers the class with
51
+ * every organization. No in-process edge reaches an enumeration lane
52
+ * today; the arm is pinned so a future one meets the rule.
46
53
  *
47
54
  * Faults THROW and are never an empty answer (the seam: an empty set means
48
55
  * "authorized to see nothing"; an outage is the pipeline's sanitized
49
- * INTERNAL). A kind the model does not declare is a consumer bug by the
50
- * seam's contract and faults through the evaluator, loud; the model
51
- * declares exactly the open-source tier, so no served lane can reach it.
56
+ * INTERNAL). A kind the model does not declare, like the internal class
57
+ * above, is a consumer bug by the seam's contract and faults through the
58
+ * evaluator, loud; the model declares exactly the open-source tier, so no
59
+ * served lane can reach it.
52
60
  *
53
61
  * Logging. One debug line per call — kind, offered, kept, elapsed — what
54
62
  * an operator needs to answer "why is Alice's list short" beside the
@@ -188,6 +196,12 @@ export function newBuiltInListReadScope(
188
196
  entries: ReadonlyArray<ListEntryMeta>,
189
197
  ): Promise<ReadonlySet<string>> {
190
198
  requireDeclared(kind);
199
+ if (caller.callerClass === "internal") {
200
+ throw new AuthorizationEvaluationError(
201
+ "internal-caller-offered",
202
+ `the internal caller class was offered to the list scope for kind '${kindEnumName(kind)}' — the shared helper answers that class before any driver; a list lane reached the driver around it`,
203
+ );
204
+ }
191
205
  // One candidate per unique id: a lane offers each row once, and a
192
206
  // duplicate would only spend a walk to learn the same answer.
193
207
  const unique = new Map<string, RowFacts>();
@@ -196,9 +210,6 @@ export function newBuiltInListReadScope(
196
210
  unique.set(entry.id, rowFactsOfEntry(kind, entry));
197
211
  }
198
212
  }
199
- if (caller.callerClass === "internal") {
200
- return new Set(unique.keys());
201
- }
202
213
  return visibleAmong(caller, kind, [...unique.values()]);
203
214
  },
204
215
 
@@ -104,6 +104,7 @@ const WIRE_PERMISSIONS_BY_TYPE: Readonly<
104
104
  "can_create_environment",
105
105
  "can_create_skill",
106
106
  "can_create_plugin",
107
+ "can_create_mcp_server",
107
108
  "can_create_idp",
108
109
  "can_create_identity_account",
109
110
  "can_create_oauth_app",
@@ -111,6 +112,7 @@ const WIRE_PERMISSIONS_BY_TYPE: Readonly<
111
112
  "can_create_channel_app",
112
113
  "can_create_execution_in",
113
114
  "can_create_agent_share",
115
+ "can_create_agent_instance",
114
116
  "can_grant_access",
115
117
  "can_view_access",
116
118
  "can_view_billing",
@@ -39,6 +39,7 @@ export const organizationDeclaration = declareKind({
39
39
  ["can_create_environment", computed("member")],
40
40
  ["can_create_skill", computed("admin")],
41
41
  ["can_create_plugin", computed("admin")],
42
+ ["can_create_mcp_server", computed("admin")],
42
43
  ["can_create_idp", computed("admin")],
43
44
  ["can_create_identity_account", computed("admin")],
44
45
  ["can_create_oauth_app", computed("admin")],
@@ -46,6 +47,7 @@ export const organizationDeclaration = declareKind({
46
47
  ["can_create_channel_app", computed("admin")],
47
48
  ["can_create_execution_in", union(computed("member"), computed("guest"))],
48
49
  ["can_create_agent_share", computed("admin")],
50
+ ["can_create_agent_instance", computed("member")],
49
51
  ["can_grant_access", computed("admin")],
50
52
  ["can_view_access", computed("viewer")],
51
53
  ["can_view_billing", computed("viewer")],
@@ -1269,7 +1269,6 @@ export async function composeServer(
1269
1269
  logger,
1270
1270
  authorizer,
1271
1271
  temporalConfig,
1272
- agentInstanceCreator: () => requireInProcess().agentInstanceCreator,
1273
1272
  gateSteps: extensions.gateSteps,
1274
1273
  sandboxLane,
1275
1274
  authorizationLifecycle,
@@ -60,7 +60,6 @@ import type {
60
60
  } from "../domain/agentexecution/create-execution-context-step.js";
61
61
  import type { ConnectExecutionContextClient } from "../domain/mcpserver/connect.js";
62
62
  import type { ManagedEnvironmentClient } from "../domain/mcpserver/oauth/managed-env.js";
63
- import type { AgentInstanceCreator } from "../domain/session/steps.js";
64
63
  import type { WorkflowInstanceCreator } from "../domain/workflow/steps.js";
65
64
  import type { ExecutionWorkflowInstanceCreator } from "../domain/workflowexecution/create-steps.js";
66
65
  import type {
@@ -89,7 +88,6 @@ import type { CallOptions } from "@connectrpc/connect";
89
88
  /** The narrow in-process surfaces the domains consume (DD-002). */
90
89
  export interface InProcessClients {
91
90
  readonly agentInstanceApplier: AgentInstanceApplier;
92
- readonly agentInstanceCreator: AgentInstanceCreator;
93
91
  readonly parentAgentLoader: ParentAgentLoader;
94
92
  readonly workflowInstanceCreator: WorkflowInstanceCreator;
95
93
  readonly parentWorkflowLoader: ParentWorkflowLoader;
@@ -254,13 +252,6 @@ export function createInProcessClients(
254
252
  applyAsCaller: (instance, caller) =>
255
253
  agentInstanceCommand.apply(instance, asCaller(caller)),
256
254
  },
257
- // CREATE (not apply) so a duplicate default-instance slug surfaces
258
- // as AlreadyExists instead of silently updating — as the original
259
- // caller (ruling R5).
260
- agentInstanceCreator: {
261
- createAsCaller: (instance, caller) =>
262
- agentInstanceCommand.create(instance, asCaller(caller)),
263
- },
264
255
  parentAgentLoader: {
265
256
  get: (agentId) =>
266
257
  agentQuery.get(create(AgentIdSchema, { value: agentId })),
@@ -6,11 +6,6 @@
6
6
  * the router transport, traversing the whole chain).
7
7
  *
8
8
  * The load-bearing pins:
9
- * - default-agent resolution (stigmer/stigmer#356): NotFound copy when
10
- * nothing is labeled; FailedPrecondition copy when labeled agents
11
- * exist but none is public; incumbent-wins (LOWEST metadata.id) among
12
- * public labeled candidates — an older non-public labeled agent never
13
- * wins;
14
9
  * - MCP env-merge semantics: agent-declared entries win, among servers
15
10
  * first-encountered wins, only declaration fields are copied;
16
11
  * - enabled-tools validation (#402): byte-pinned INVALID_ARGUMENT copy
@@ -46,10 +41,6 @@ import { composeServer } from "../../../boot/compose.js";
46
41
  import type { ComposedServer } from "../../../boot/compose.js";
47
42
  import { createLogger } from "../../../boot/logger.js";
48
43
  import { ResourceNotFoundError } from "../../../store/interface.js";
49
- import {
50
- DEFAULT_AGENT_LABEL,
51
- DEFAULT_AGENT_LABEL_VALUE,
52
- } from "../defaultagent.js";
53
44
 
54
45
  const silentLogger = createLogger({
55
46
  level: "error",
@@ -197,65 +188,6 @@ async function grpcError(run: () => Promise<unknown>): Promise<ConnectError> {
197
188
  }
198
189
  }
199
190
 
200
- // The getDefault tests are order-dependent within this describe: the
201
- // platform default is a GLOBAL singleton, so the empty state must be
202
- // asserted before any labeled agent exists.
203
- describe("agent getDefault (platform default resolution, #356)", () => {
204
- it("answers NotFound with the configured-hint copy when nothing carries the label", async () => {
205
- // org is required for authorization scoping only; resolution is global.
206
- const error = await grpcError(() => query.getDefault({ org: ORG }));
207
- expect(error.code).toBe(Code.NotFound);
208
- expect(error.rawMessage).toBe(
209
- "No default agent available. Ensure an agent with label " +
210
- "stigmer.ai/default-agent=true and visibility_public exists: " +
211
- "no agent labeled stigmer.ai/default-agent=true",
212
- );
213
- });
214
-
215
- it("answers FailedPrecondition when labeled agents exist but none is public", async () => {
216
- await command.create(
217
- agentInput({
218
- name: "Labeled But Org Visible",
219
- labels: { [DEFAULT_AGENT_LABEL]: DEFAULT_AGENT_LABEL_VALUE },
220
- }),
221
- );
222
-
223
- const error = await grpcError(() => query.getDefault({ org: ORG }));
224
- expect(error.code).toBe(Code.FailedPrecondition);
225
- expect(error.rawMessage).toBe(
226
- "Default agent exists but is not visibility_public: agents labeled " +
227
- "stigmer.ai/default-agent=true exist but none is visibility_public",
228
- );
229
- });
230
-
231
- it("serves the incumbent — the LOWEST metadata.id among PUBLIC labeled candidates", async () => {
232
- const first = await command.create(
233
- agentInput({
234
- name: "Public Default One",
235
- visibility: ApiResourceVisibility.visibility_public,
236
- labels: { [DEFAULT_AGENT_LABEL]: DEFAULT_AGENT_LABEL_VALUE },
237
- }),
238
- );
239
- const second = await command.create(
240
- agentInput({
241
- name: "Public Default Two",
242
- visibility: ApiResourceVisibility.visibility_public,
243
- labels: { [DEFAULT_AGENT_LABEL]: DEFAULT_AGENT_LABEL_VALUE },
244
- }),
245
- );
246
-
247
- const firstId = first.metadata!.id;
248
- const secondId = second.metadata!.id;
249
- const incumbentId = firstId < secondId ? firstId : secondId;
250
-
251
- // The older NON-public labeled agent (previous test) has a lower id
252
- // than both public candidates; incumbent-wins must skip it.
253
- // A DIFFERENT org resolves the same singleton — resolution is global.
254
- const resolved = await query.getDefault({ org: "globex" });
255
- expect(resolved.metadata?.id).toBe(incumbentId);
256
- });
257
- });
258
-
259
191
  describe("agent MCP env merge (merge_mcp_env_specs)", () => {
260
192
  it("merges declarations: agent-declared wins, first-encountered server wins, declaration fields copied", async () => {
261
193
  await seedMcpServer({
@@ -4,8 +4,8 @@
4
4
  * default instance through the in-process agentinstance client (the
5
5
  * agent↔agentinstance mutual edge, wired as a lazy provider in the
6
6
  * composition root — sub-project DD-002); delete cascades ALL instances
7
- * (oss#611) and same-org shares before the agent row; GetDefault serves
8
- * the platform default-agent resolution (defaultagent.ts).
7
+ * (oss#611) and same-org shares before the agent row. There is no default
8
+ * agent to serve: a session with no agent runs the built-in assistant.
9
9
  *
10
10
  * Pipeline per RPC mirrors the Go step chains character-for-character.
11
11
  * Proven by agent.conformance.test.ts (CONFORMANCE_TARGET=local) and
@@ -20,10 +20,7 @@ import { AgentCommandController } from "@stigmer/protos/ai/stigmer/agentic/agent
20
20
  import { AgentQueryController } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/query_pb";
21
21
  import { AgentSchema } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
22
22
  import type { Agent } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
23
- import type {
24
- AgentId,
25
- GetDefaultAgentRequest,
26
- } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/io_pb";
23
+ import type { AgentId } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/io_pb";
27
24
  import type {
28
25
  ApiResourceReference,
29
26
  UpdateVisibilityInput,
@@ -40,6 +37,10 @@ import type { PipelineStep } from "../../pipeline/pipeline.js";
40
37
  import { callerIdentityOf } from "../../pipeline/interceptors/auth.js";
41
38
  import { RequestContext } from "../../pipeline/request-context.js";
42
39
  import { newAuthorizeStep } from "../../pipeline/steps/authorize.js";
40
+ import {
41
+ loadedTargetAsMethod,
42
+ newAuthorizeResolvedTargetStep,
43
+ } from "../../pipeline/steps/authorize-resolved-target.js";
43
44
  import { newGuardReservedLabelsStep } from "../../pipeline/steps/guard-reserved-labels.js";
44
45
  import {
45
46
  newAuthorizeVisibilityTransitionStep,
@@ -98,7 +99,6 @@ import {
98
99
  newCascadeDeleteInstancesStep,
99
100
  newCascadeDeleteSharesStep,
100
101
  newCreateDefaultInstanceStep,
101
- newLoadDefaultAgentStep,
102
102
  newMergeMcpServerEnvSpecsStep,
103
103
  newUpdateAgentStatusWithDefaultInstanceStep,
104
104
  newValidateEnabledToolsStep,
@@ -136,7 +136,6 @@ export function registerAgentServices(
136
136
  router.service(AgentQueryController, {
137
137
  get: (id, ctx) => get(deps, id, ctx),
138
138
  getByReference: (ref, ctx) => getByReference(deps, ref, ctx),
139
- getDefault: (req, ctx) => getDefault(deps, req, ctx),
140
139
  });
141
140
  }
142
141
 
@@ -532,36 +531,12 @@ async function getByReference(
532
531
  )
533
532
  .addStep(newValidateProtoStep())
534
533
  .addStep(newLoadByReferenceStep(deps.store, AgentSchema))
535
- .build()
536
- .execute(reqCtx);
537
- return reqCtx.get(TARGET_RESOURCE_KEY) as Agent;
538
- }
539
-
540
- /**
541
- * GetDefault — the platform default agent (label + visibility_public,
542
- * incumbent-wins). Used by frontends for the session-first UX where users
543
- * start a conversation without explicitly selecting an agent.
544
- */
545
- async function getDefault(
546
- deps: AgentControllerDeps,
547
- req: GetDefaultAgentRequest,
548
- ctx: HandlerContext,
549
- ): Promise<Agent> {
550
- const reqCtx = new RequestContext(
551
- AgentQueryController.method.getDefault.input,
552
- req,
553
- callerIdentityOf(ctx),
554
- kindOf(ctx),
555
- );
556
- await newPipeline<typeof AgentQueryController.method.getDefault.input>(
557
- "agent-get-default",
558
- deps.logger,
559
- )
560
534
  .addStep(
561
- newAuthorizeStep(AgentQueryController.method.getDefault, deps.authorizer),
535
+ newAuthorizeResolvedTargetStep(
536
+ deps.authorizer,
537
+ loadedTargetAsMethod(AgentQueryController.method.get),
538
+ ),
562
539
  )
563
- .addStep(newValidateProtoStep())
564
- .addStep(newLoadDefaultAgentStep(deps.store, deps.logger))
565
540
  .build()
566
541
  .execute(reqCtx);
567
542
  return reqCtx.get(TARGET_RESOURCE_KEY) as Agent;
@@ -6,7 +6,7 @@
6
6
  * embody agent-specific contracts: the default-instance choreography, the
7
7
  * cascade rules, MCP env merging, and enabled-tools validation.
8
8
  */
9
- import { Code, ConnectError } from "@connectrpc/connect";
9
+ import { ConnectError } from "@connectrpc/connect";
10
10
  import { create, fromBinary } from "@bufbuild/protobuf";
11
11
  import type { DescMessage } from "@bufbuild/protobuf";
12
12
 
@@ -43,15 +43,9 @@ import { notifyDefaultInstanceLinked } from "../../pipeline/steps/authorization-
43
43
  import type { PipelineStep } from "../../pipeline/pipeline.js";
44
44
  import type { RequestContext } from "../../pipeline/request-context.js";
45
45
  import { EXISTING_RESOURCE_KEY } from "../../pipeline/steps/load-existing.js";
46
- import { TARGET_RESOURCE_KEY } from "../../pipeline/steps/load-target.js";
47
46
  import { findResourceBySlug } from "../../pipeline/steps/helpers.js";
48
47
  import type { Store } from "../../store/interface.js";
49
48
  import { buildDefaultInstanceRequest } from "../agentinstance/defaultinstance.js";
50
- import {
51
- DefaultAgentNotConfiguredError,
52
- DefaultAgentNotPublicError,
53
- findDefaultAgent,
54
- } from "./defaultagent.js";
55
49
 
56
50
  /** Context key carrying the applied default instance's id (Go DefaultInstanceIDKey). */
57
51
  const DEFAULT_INSTANCE_ID_KEY = "default_instance_id";
@@ -636,56 +630,3 @@ export function newCascadeDeleteSharesStep<Desc extends DescMessage>(
636
630
  },
637
631
  };
638
632
  }
639
-
640
- // ---------------------------------------------------------------------------
641
- // LoadDefaultAgent — get_default.go's step: resolve via defaultagent and
642
- // map its sentinels to the wire contract. Go builds the wire message with
643
- // WrapError ("%s: %v"), so the sentinel's text RIDES the message — both
644
- // strings are cross-edition conformance surface.
645
- // ---------------------------------------------------------------------------
646
-
647
- export function newLoadDefaultAgentStep<Desc extends DescMessage>(
648
- store: Store,
649
- logger: Logger,
650
- ): PipelineStep<Desc> {
651
- return {
652
- name: "LoadDefaultAgent",
653
- async execute(ctx: RequestContext<Desc>): Promise<void> {
654
- logger.info("Resolving platform default agent");
655
-
656
- let agent: Agent;
657
- try {
658
- agent = await findDefaultAgent(store, logger);
659
- } catch (error) {
660
- if (error instanceof DefaultAgentNotConfiguredError) {
661
- throw new ConnectError(
662
- `No default agent available. Ensure an agent with label stigmer.ai/default-agent=true and visibility_public exists: ${error.message}`,
663
- Code.NotFound,
664
- );
665
- }
666
- if (error instanceof DefaultAgentNotPublicError) {
667
- throw new ConnectError(
668
- `Default agent exists but is not visibility_public: ${error.message}`,
669
- Code.FailedPrecondition,
670
- );
671
- }
672
- // Store/decode failure — an internal fault, not "no default agent".
673
- // InternalError keeps the cause off the wire (stigmer/stigmer#478).
674
- logger.error("Failed to resolve platform default agent", {
675
- error: error instanceof Error ? error.message : String(error),
676
- });
677
- throw internalError(
678
- error,
679
- "failed to resolve the platform default agent",
680
- );
681
- }
682
-
683
- logger.info("Resolved platform default agent", {
684
- agentId: agent.metadata?.id ?? "",
685
- agentName: agent.metadata?.name ?? "",
686
- });
687
-
688
- ctx.set(TARGET_RESOURCE_KEY, agent);
689
- },
690
- };
691
- }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Pins the create lane's authorization question (steps.ts,
3
+ * resolveChannelCreateTargets): can_edit on the REFERENCED AGENT with the
4
+ * Java handler's copy, and nothing on the organization (the same-org
5
+ * invariant already binds the channel to the agent's); a missing
6
+ * referenced agent throws.
7
+ */
8
+ import { describe, expect, it } from "vitest";
9
+ import { create } from "@bufbuild/protobuf";
10
+
11
+ import { AgentSchema } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
12
+ import { AgentChannelSchema } from "@stigmer/protos/ai/stigmer/agentic/agentchannel/v1/api_pb";
13
+ import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
14
+ import { IamPermission } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
15
+
16
+ import { testCallerIdentity } from "../../../pipeline/__tests__/support.js";
17
+ import { RequestContext } from "../../../pipeline/request-context.js";
18
+ import {
19
+ CHANNEL_CREATE_DENIED_MESSAGE,
20
+ REFERENCED_AGENT_KEY,
21
+ resolveChannelCreateTargets,
22
+ } from "../steps.js";
23
+
24
+ const agent = create(AgentSchema, {
25
+ metadata: { id: "agt_01target", name: "helper", org: "acme" },
26
+ });
27
+
28
+ function ctxFor(stash = true) {
29
+ const ctx = new RequestContext(
30
+ AgentChannelSchema,
31
+ create(AgentChannelSchema, { metadata: { name: "support", org: "acme" } }),
32
+ testCallerIdentity(),
33
+ ApiResourceKind.agent_channel,
34
+ );
35
+ if (stash) {
36
+ ctx.set(REFERENCED_AGENT_KEY, agent);
37
+ }
38
+ return ctx;
39
+ }
40
+
41
+ describe("resolveChannelCreateTargets", () => {
42
+ it("asks can_edit on the referenced agent and nothing else", () => {
43
+ expect(resolveChannelCreateTargets(ctxFor())).toEqual([
44
+ {
45
+ permission: IamPermission.can_edit,
46
+ resourceKind: ApiResourceKind.agent,
47
+ resourceId: "agt_01target",
48
+ deniedMessage: CHANNEL_CREATE_DENIED_MESSAGE,
49
+ },
50
+ ]);
51
+ });
52
+
53
+ it("a missing referenced agent is a broken chain invariant and throws", () => {
54
+ expect(() => resolveChannelCreateTargets(ctxFor(false))).toThrow(
55
+ "referenced agent not found in context",
56
+ );
57
+ });
58
+ });