@stigmer/server 3.18.0-dev.20260918103812 → 3.18.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 (75) hide show
  1. package/dist/boot/compose.d.ts +12 -0
  2. package/dist/boot/compose.d.ts.map +1 -1
  3. package/dist/boot/compose.js +4 -2
  4. package/dist/boot/compose.js.map +1 -1
  5. package/dist/domain/agentexecution/lifecycle.js +1 -1
  6. package/dist/domain/agentexecution/lifecycle.js.map +1 -1
  7. package/dist/domain/iampolicy/constants.d.ts +3 -3
  8. package/dist/domain/iampolicy/constants.js +3 -3
  9. package/dist/domain/oauthapp/refresolution/refresolution.js +1 -1
  10. package/dist/domain/oauthapp/refresolution.js +1 -1
  11. package/dist/domain/platform/controller.d.ts +8 -1
  12. package/dist/domain/platform/controller.d.ts.map +1 -1
  13. package/dist/domain/platform/controller.js +2 -3
  14. package/dist/domain/platform/controller.js.map +1 -1
  15. package/dist/domain/plugin/constants.d.ts +7 -1
  16. package/dist/domain/plugin/constants.d.ts.map +1 -1
  17. package/dist/domain/plugin/constants.js +7 -1
  18. package/dist/domain/plugin/constants.js.map +1 -1
  19. package/dist/domain/plugin/materialize/plan.d.ts.map +1 -1
  20. package/dist/domain/plugin/materialize/plan.js +16 -1
  21. package/dist/domain/plugin/materialize/plan.js.map +1 -1
  22. package/dist/domain/plugin/members.d.ts +46 -0
  23. package/dist/domain/plugin/members.d.ts.map +1 -1
  24. package/dist/domain/plugin/members.js +31 -8
  25. package/dist/domain/plugin/members.js.map +1 -1
  26. package/dist/domain/plugin/push.d.ts +5 -3
  27. package/dist/domain/plugin/push.d.ts.map +1 -1
  28. package/dist/domain/plugin/push.js +58 -14
  29. package/dist/domain/plugin/push.js.map +1 -1
  30. package/dist/domain/workflowexecution/lifecycle.js +1 -1
  31. package/dist/domain/workflowexecution/lifecycle.js.map +1 -1
  32. package/dist/pipeline/apiresource-labels.d.ts +20 -1
  33. package/dist/pipeline/apiresource-labels.d.ts.map +1 -1
  34. package/dist/pipeline/apiresource-labels.js +17 -0
  35. package/dist/pipeline/apiresource-labels.js.map +1 -1
  36. package/dist/sandbox/kubernetes.d.ts.map +1 -1
  37. package/dist/sandbox/kubernetes.js +1 -8
  38. package/dist/sandbox/kubernetes.js.map +1 -1
  39. package/dist/sandbox/provisioner.d.ts +5 -20
  40. package/dist/sandbox/provisioner.d.ts.map +1 -1
  41. package/dist/sandbox/provisioner.js.map +1 -1
  42. package/dist/sandbox/steps.d.ts +8 -22
  43. package/dist/sandbox/steps.d.ts.map +1 -1
  44. package/dist/sandbox/steps.js +12 -17
  45. package/dist/sandbox/steps.js.map +1 -1
  46. package/dist/store/interface.d.ts +6 -2
  47. package/dist/store/interface.d.ts.map +1 -1
  48. package/dist/store/interface.js.map +1 -1
  49. package/package.json +5 -5
  50. package/src/boot/compose.ts +16 -2
  51. package/src/domain/agentexecution/lifecycle.ts +1 -1
  52. package/src/domain/iampolicy/constants.ts +3 -3
  53. package/src/domain/oauthapp/__tests__/oauthapp.test.ts +2 -2
  54. package/src/domain/oauthapp/__tests__/refresolution.test.ts +2 -2
  55. package/src/domain/oauthapp/refresolution/refresolution.ts +1 -1
  56. package/src/domain/oauthapp/refresolution.ts +1 -1
  57. package/src/domain/platform/__tests__/platform.test.ts +47 -0
  58. package/src/domain/platform/controller.ts +10 -4
  59. package/src/domain/plugin/__tests__/materialize.test.ts +42 -47
  60. package/src/domain/plugin/__tests__/members.test.ts +149 -0
  61. package/src/domain/plugin/__tests__/plugin.test.ts +219 -8
  62. package/src/domain/plugin/constants.ts +7 -1
  63. package/src/domain/plugin/materialize/plan.ts +20 -1
  64. package/src/domain/plugin/members.ts +76 -7
  65. package/src/domain/plugin/push.ts +80 -20
  66. package/src/domain/workflowexecution/lifecycle.ts +1 -1
  67. package/src/pipeline/apiresource-labels.ts +26 -1
  68. package/src/sandbox/__tests__/docker.integration.test.ts +0 -2
  69. package/src/sandbox/__tests__/kubernetes.test.ts +2 -10
  70. package/src/sandbox/__tests__/local-process.integration.test.ts +0 -3
  71. package/src/sandbox/__tests__/steps.test.ts +19 -72
  72. package/src/sandbox/kubernetes.ts +1 -8
  73. package/src/sandbox/provisioner.ts +5 -20
  74. package/src/sandbox/steps.ts +12 -29
  75. package/src/store/interface.ts +6 -2
@@ -44,9 +44,15 @@ export type MaterializedKindName = (typeof MATERIALIZATION_ORDER)[number];
44
44
  export const TRANSFER_LANE_NOT_CONFIGURED =
45
45
  "plugin artifact transfer lane is not configured on this server";
46
46
 
47
- /** Warning kinds the SERVER adds to the library's; the wire carries them as strings. */
47
+ /**
48
+ * Warning kinds the SERVER adds to the library's; the wire carries them as
49
+ * strings, and the list in PluginWarning.kind's comment (plugin/v1/status.proto)
50
+ * is kept equal to this one because the SDK docs are generated from it.
51
+ */
48
52
  export const SERVER_WARNING_KINDS = {
49
53
  componentIgnored: "component-ignored",
54
+ /** A system-content row the plugin took over in place (members.ts, judgeSlug). */
55
+ memberAdopted: "member-adopted",
50
56
  modelHintUnresolved: "model-hint-unresolved",
51
57
  subAgentNameBuiltin: "sub-agent-name-builtin",
52
58
  versionNotTaggable: "version-not-taggable",
@@ -19,6 +19,11 @@ import { PluginWarningSchema } from "@stigmer/protos/ai/stigmer/agentic/plugin/v
19
19
  import type { PluginWarning } from "@stigmer/protos/ai/stigmer/agentic/plugin/v1/status_pb";
20
20
  import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
21
21
 
22
+ import {
23
+ RESERVED_LABEL_TRUE,
24
+ SYSTEM_LABEL,
25
+ isSystemContent,
26
+ } from "../../../pipeline/apiresource-labels.js";
22
27
  import { SERVER_WARNING_KINDS, VERSION_TAG_PATTERN } from "../constants.js";
23
28
  import type { PlannedMember } from "../members.js";
24
29
  import type { ParsedOverlays } from "../overlay/documents.js";
@@ -105,24 +110,38 @@ export function planMaterialization(
105
110
  );
106
111
  const workflows = planWorkflows(overlays.workflows, identity);
107
112
 
113
+ // `system` is read from the labels each member will be written with (the
114
+ // overlay author's, the plugin's two merged over them). A skill's request
115
+ // carries only the plugin's two, so a skill never declares system content
116
+ // and can never adopt a row; the overlay kinds can.
108
117
  const members: PlannedMember[] = [
109
118
  ...skills.map((skill) => ({
110
119
  kind: ApiResourceKind.skill,
111
120
  slug: skill.slug,
112
121
  name: skill.name,
122
+ system: skill.request.labels[SYSTEM_LABEL] === RESERVED_LABEL_TRUE,
113
123
  })),
114
124
  ...mcpServers.map((server) => ({
115
125
  kind: ApiResourceKind.mcp_server,
116
126
  slug: server.slug,
117
127
  name: server.name,
128
+ system: isSystemContent(server.resource.metadata),
118
129
  })),
119
130
  ...(agent === undefined
120
131
  ? []
121
- : [{ kind: ApiResourceKind.agent, slug: agent.slug, name: agent.name }]),
132
+ : [
133
+ {
134
+ kind: ApiResourceKind.agent,
135
+ slug: agent.slug,
136
+ name: agent.name,
137
+ system: isSystemContent(agent.resource.metadata),
138
+ },
139
+ ]),
122
140
  ...workflows.map((workflow) => ({
123
141
  kind: ApiResourceKind.workflow,
124
142
  slug: workflow.slug,
125
143
  name: workflow.name,
144
+ system: isSystemContent(workflow.resource.metadata),
126
145
  })),
127
146
  ];
128
147
 
@@ -8,14 +8,17 @@
8
8
  * pattern.
9
9
  *
10
10
  * The same module answers the two questions a push asks before any write:
11
- * which planned slugs are already held in the organization, and by whom
12
- * (this plugin: an upgrade; another plugin or an unmanaged resource: a
13
- * refusal naming the holder); and whether the plugin's members already
14
- * converge on the head's digest (every planned member present, each
15
- * carrying `stigmer.ai/plugin-version` equal to it), which is what lets a
16
- * re-push of the same archive return without a child write.
11
+ * what to do about each planned slug in the organization (`judgeSlug`:
12
+ * free; ours, an upgrade; system content the plugin replaces, adopted;
13
+ * another plugin's or a user's, refused naming the holder); and whether the
14
+ * plugin's members already converge on the head's digest (every planned
15
+ * member present, each carrying `stigmer.ai/plugin-version` equal to it),
16
+ * which is what lets a re-push of the same archive return without a child
17
+ * write.
17
18
  *
18
- * Proven by __tests__/members.test.ts and the conformance suite's
19
+ * Proven by __tests__/members.test.ts (the slug decision on plain values,
20
+ * convergence, the dropped set), __tests__/plugin.test.ts (adoption and
21
+ * refusal through a composed server) and the conformance suite's
19
22
  * listMembers, collision and reconcile arms.
20
23
  */
21
24
  import { fromBinary } from "@bufbuild/protobuf";
@@ -31,6 +34,7 @@ import type { ApiResourceMetadata } from "@stigmer/protos/ai/stigmer/commons/api
31
34
  import {
32
35
  PLUGIN_LABEL,
33
36
  PLUGIN_VERSION_LABEL,
37
+ isSystemContent,
34
38
  pluginIdOf,
35
39
  } from "../../pipeline/apiresource-labels.js";
36
40
  import { findResourceBySlug } from "../../pipeline/steps/helpers.js";
@@ -56,6 +60,8 @@ export interface Member {
56
60
  readonly name: string;
57
61
  /** The `stigmer.ai/plugin-version` value it was last materialised from. */
58
62
  readonly version: string;
63
+ /** Whether the stored row carries `stigmer.ai/system` (isSystemContent). */
64
+ readonly system: boolean;
59
65
  }
60
66
 
61
67
  /** A member the plan intends to exist after this push. */
@@ -63,6 +69,8 @@ export interface PlannedMember {
63
69
  readonly kind: ApiResourceKind;
64
70
  readonly slug: string;
65
71
  readonly name: string;
72
+ /** Whether the member the plugin brings declares `stigmer.ai/system`. */
73
+ readonly system: boolean;
66
74
  }
67
75
 
68
76
  function memberOf(
@@ -75,6 +83,7 @@ function memberOf(
75
83
  slug: metadata.slug,
76
84
  name: metadata.name,
77
85
  version: metadata.labels[PLUGIN_VERSION_LABEL] ?? "",
86
+ system: isSystemContent(metadata),
78
87
  };
79
88
  }
80
89
 
@@ -145,6 +154,66 @@ export async function slugHolder(
145
154
  };
146
155
  }
147
156
 
157
+ /**
158
+ * What a push does about one planned slug, decided before any write.
159
+ *
160
+ * free nothing holds it; the member is created
161
+ * ours this plugin's own member; the push is an upgrade
162
+ * adopt system content no plugin manages, and the plugin
163
+ * brings system content for the same slug: the child's
164
+ * apply (upsert-by-slug) rewrites the row in place, so
165
+ * its id, its default instance, every personal instance
166
+ * and every session bound to them survive
167
+ * held-unmanaged a resource no plugin manages and the rule above does
168
+ * not admit; refused naming it
169
+ * held-by-plugin another plugin's member; refused naming that plugin
170
+ *
171
+ * Adoption is deliberately narrow, "system replaces system": the held row's
172
+ * label narrows which rows are eligible (a user's row never carries it on
173
+ * an edition that enforces reserved labels), and the plugin's own claim to
174
+ * the label was charged to `can_write_reserved_labels` by the sanitiser
175
+ * moments before, so the grant is the caller's live permission and never
176
+ * the label alone (pipeline/apiresource-labels.ts). It never transfers a
177
+ * member between plugins: another plugin's slug is refused whatever labels
178
+ * either side carries. The one case it exists for is the platform re-homing
179
+ * the content it seeded before plugins existed (the default agent; the
180
+ * platform's own server) without orphaning a single conversation.
181
+ */
182
+ export type SlugDecision =
183
+ | { readonly kind: "free" }
184
+ | { readonly kind: "ours"; readonly holder: Member }
185
+ | { readonly kind: "adopt"; readonly holder: Member }
186
+ | { readonly kind: "held-unmanaged"; readonly holder: Member }
187
+ | {
188
+ readonly kind: "held-by-plugin";
189
+ readonly holder: Member;
190
+ readonly pluginId: string;
191
+ };
192
+
193
+ export function judgeSlug(
194
+ holder: SlugHolder,
195
+ planned: PlannedMember,
196
+ pluginId: string,
197
+ ): SlugDecision {
198
+ if (!holder.held) {
199
+ return { kind: "free" };
200
+ }
201
+ if (holder.byPlugin === pluginId) {
202
+ return { kind: "ours", holder: holder.holder };
203
+ }
204
+ if (holder.byPlugin !== undefined) {
205
+ return {
206
+ kind: "held-by-plugin",
207
+ holder: holder.holder,
208
+ pluginId: holder.byPlugin,
209
+ };
210
+ }
211
+ if (holder.holder.system && planned.system) {
212
+ return { kind: "adopt", holder: holder.holder };
213
+ }
214
+ return { kind: "held-unmanaged", holder: holder.holder };
215
+ }
216
+
148
217
  /**
149
218
  * Whether the stored members already ARE the plan at this digest: every
150
219
  * planned member present, none extra, each stamped with the digest.
@@ -19,8 +19,10 @@
19
19
  * digest, READY, every member present and stamped with it) the controller
20
20
  * returns the head and the install chain never runs, so a re-push writes
21
21
  * nothing. Everything that can refuse — the archive, the package, the
22
- * overlay, a reserved label, a held slug, a missing permission, the level
23
- * — refuses in the plan chain BEFORE any write; the install chain's own
22
+ * overlay, a reserved label, a held slug (unless it is system content the
23
+ * plugin replaces, which is adopted in place: members.ts, judgeSlug), a
24
+ * missing permission, the level — refuses in the plan chain BEFORE any
25
+ * write; the install chain's own
24
26
  * failures are a child's, wrapped with the plugin's name, and leave a head
25
27
  * that says FAILED so the next push of the same archive converges.
26
28
  *
@@ -52,6 +54,7 @@ import {
52
54
  PluginStatusSchema,
53
55
  PluginWarningSchema,
54
56
  } from "@stigmer/protos/ai/stigmer/agentic/plugin/v1/status_pb";
57
+ import type { PluginWarning } from "@stigmer/protos/ai/stigmer/agentic/plugin/v1/status_pb";
55
58
  import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
56
59
  import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
57
60
  import {
@@ -95,7 +98,7 @@ import { requireOperatorMaySetPublic } from "../../pipeline/steps/visibility-gat
95
98
  import { ResourceNotFoundError } from "../../store/interface.js";
96
99
  import type { Store } from "../../store/interface.js";
97
100
  import { openPluginArchive } from "./archive.js";
98
- import { VERSION_TAG_PATTERN } from "./constants.js";
101
+ import { SERVER_WARNING_KINDS, VERSION_TAG_PATTERN } from "./constants.js";
99
102
  import type { OpenedPluginArchive } from "./archive.js";
100
103
  import type { PluginIdentity } from "./materialize/identity.js";
101
104
  import { McpServerOverlayError } from "./materialize/mcp-servers.js";
@@ -105,10 +108,11 @@ import type { PluginMaterializerProvider } from "./materialize/ports.js";
105
108
  import {
106
109
  droppedMembers,
107
110
  findMembers,
111
+ judgeSlug,
108
112
  membersConverge,
109
113
  slugHolder,
110
114
  } from "./members.js";
111
- import type { Member } from "./members.js";
115
+ import type { Member, PlannedMember } from "./members.js";
112
116
  import { parseOverlays } from "./overlay/documents.js";
113
117
  import type { ParsedOverlays } from "./overlay/documents.js";
114
118
  import { OverlayParseError } from "./overlay/parse.js";
@@ -339,9 +343,11 @@ export function requestedVisibility(
339
343
 
340
344
  /**
341
345
  * PlanMaterialization — everything the push will write, checked whole:
342
- * every child slug against the organization (free, ours, or refused
343
- * naming the holder), the caller's permission for every member kind, and
344
- * the plugin's current members for the convergence and drop decisions.
346
+ * every child slug against the organization (free, ours, adopted system
347
+ * content, or refused naming the holder), the caller's permission for
348
+ * every member kind, and the plugin's current members for the convergence
349
+ * and drop decisions. An adopted slug is recorded as a warning on the plan,
350
+ * so the install receipt says which rows the plugin took over.
345
351
  */
346
352
  export function newPlanMaterializationStep(
347
353
  store: Store,
@@ -376,6 +382,7 @@ export function newPlanMaterializationStep(
376
382
  throw error;
377
383
  }
378
384
 
385
+ const adopted: PluginWarning[] = [];
379
386
  for (const member of plan.members) {
380
387
  let holder;
381
388
  try {
@@ -383,20 +390,41 @@ export function newPlanMaterializationStep(
383
390
  } catch (error) {
384
391
  throw internalError(error, "failed to check a member slug");
385
392
  }
386
- if (!holder.held || holder.byPlugin === identity.id) {
387
- continue;
388
- }
393
+ const decision = judgeSlug(holder, member, identity.id);
389
394
  const noun = memberNoun(member.kind);
390
- if (holder.byPlugin === undefined) {
391
- throw alreadyExistsError(
392
- noun,
393
- `'${member.slug}' exists in org '${identity.org}' and is not managed by a plugin; rename or delete it first`,
394
- );
395
+ switch (decision.kind) {
396
+ case "free":
397
+ case "ours":
398
+ break;
399
+ case "adopt":
400
+ adopted.push(
401
+ create(PluginWarningSchema, {
402
+ kind: SERVER_WARNING_KINDS.memberAdopted,
403
+ path: declaringDocumentOf(overlays, member),
404
+ message:
405
+ `${noun} '${member.slug}' (${decision.holder.id}) was seeded by the platform before this plugin and is now managed by it; ` +
406
+ "its definition is the plugin's, and its instances and conversations continue",
407
+ }),
408
+ );
409
+ break;
410
+ case "held-unmanaged":
411
+ throw alreadyExistsError(
412
+ noun,
413
+ `'${member.slug}' exists in org '${identity.org}' and is not managed by a plugin; rename or delete it first`,
414
+ );
415
+ case "held-by-plugin":
416
+ throw alreadyExistsError(
417
+ noun,
418
+ `'${member.slug}' is held by plugin '${await pluginSlugOf(store, decision.pluginId)}'`,
419
+ );
420
+ default: {
421
+ const exhaustive: never = decision;
422
+ throw internalError(
423
+ new Error(`unknown slug decision ${JSON.stringify(exhaustive)}`),
424
+ "failed to check a member slug",
425
+ );
426
+ }
395
427
  }
396
- throw alreadyExistsError(
397
- noun,
398
- `'${member.slug}' is held by plugin '${await pluginSlugOf(store, holder.byPlugin)}'`,
399
- );
400
428
  }
401
429
 
402
430
  const missing: string[] = [];
@@ -452,7 +480,12 @@ export function newPlanMaterializationStep(
452
480
  } catch (error) {
453
481
  throw internalError(error, "failed to list plugin members");
454
482
  }
455
- ctx.set(PLUGIN_PLAN_KEY, plan);
483
+ // An adoption is something this push noticed and did not refuse, so it
484
+ // rides the plan's warnings onto the install receipt like the rest.
485
+ ctx.set(PLUGIN_PLAN_KEY, {
486
+ ...plan,
487
+ warnings: [...plan.warnings, ...adopted],
488
+ } satisfies MaterializationPlan);
456
489
  ctx.set(EXISTING_MEMBERS_KEY, members);
457
490
  },
458
491
  };
@@ -986,6 +1019,33 @@ function memberNoun(kind: ApiResourceKind): string {
986
1019
  return MEMBER_NOUNS.get(kind) ?? ApiResourceKind[kind] ?? String(kind);
987
1020
  }
988
1021
 
1022
+ /**
1023
+ * The overlay document that declared an adopted member, for the warning's
1024
+ * path: only an overlay can carry the system label, so a skill (whose
1025
+ * request carries the plugin's labels alone) never reaches this.
1026
+ */
1027
+ function declaringDocumentOf(
1028
+ overlays: ParsedOverlays,
1029
+ member: PlannedMember,
1030
+ ): string {
1031
+ switch (member.kind) {
1032
+ case ApiResourceKind.agent:
1033
+ return overlays.agent?.path ?? "";
1034
+ case ApiResourceKind.mcp_server:
1035
+ return (
1036
+ overlays.mcpServers.find((document) => document.server === member.name)
1037
+ ?.path ?? ""
1038
+ );
1039
+ case ApiResourceKind.workflow:
1040
+ return (
1041
+ overlays.workflows.find((document) => document.name === member.name)
1042
+ ?.path ?? ""
1043
+ );
1044
+ default:
1045
+ return "";
1046
+ }
1047
+ }
1048
+
989
1049
  /** The holding plugin's slug for a refusal; a dangling id names itself. */
990
1050
  async function pluginSlugOf(store: Store, pluginId: string): Promise<string> {
991
1051
  try {
@@ -934,7 +934,7 @@ export async function recoverExecution(
934
934
  temporalConfig: deps.temporalConfig,
935
935
  },
936
936
  loadedExecution(ctx),
937
- ctx.callerIdentity,
937
+ ctx.callerIdentity.identityId,
938
938
  );
939
939
  },
940
940
  },
@@ -13,7 +13,11 @@
13
13
  * authorizer's permissive default ALLOWS reserved-label writes by design
14
14
  * (the self-hosted operator owns the store), which is why a restrict-shaped
15
15
  * predicate here still pairs the label with server-owned state: the
16
- * plugin-managed guard refuses only beside an existing plugin row.
16
+ * plugin-managed guard refuses only beside an existing plugin row, and a
17
+ * plugin adopts a system-content row only when its own claim to the same
18
+ * label was just charged to `can_write_reserved_labels` by the sanitiser
19
+ * (domain/plugin/members.ts) — the label narrows which rows are eligible,
20
+ * the live authorization is what grants.
17
21
  */
18
22
  import type { ApiResourceMetadata } from "@stigmer/protos/ai/stigmer/commons/apiresource/metadata_pb";
19
23
 
@@ -39,6 +43,20 @@ export const DEFAULT_INSTANCE_LABEL = `${RESERVED_LABEL_PREFIX}default-instance`
39
43
  */
40
44
  export const SYSTEM_MANAGED_LABEL = `${RESERVED_LABEL_PREFIX}system-managed`;
41
45
 
46
+ /**
47
+ * Marks system content: a resource the platform itself seeded rather than a
48
+ * user authored — the system organization, the default agent, the
49
+ * platform's own MCP servers. The label predates this module (the retired
50
+ * seedpack wrote it on every row it applied; the official plugins' overlays
51
+ * carry it on the resources they replace) and has one server-side meaning:
52
+ * a plugin push may adopt a system-content row that holds a slug the plugin
53
+ * needs, keeping the row's id and everything bound to it, instead of
54
+ * refusing the slug as taken. It never marks a user's resource, so a user's
55
+ * row is never adopted (domain/plugin/members.ts, judgeSlug). The CLI names
56
+ * the same bytes as SYSTEM_ORG_LABEL for the organization it ensures.
57
+ */
58
+ export const SYSTEM_LABEL = `${RESERVED_LABEL_PREFIX}system`;
59
+
42
60
  /**
43
61
  * The only value that activates a reserved marker label; any other value is
44
62
  * inert (matching cloud's "true".equals(...)). Stamped by the flows that
@@ -86,3 +104,10 @@ export function isDefaultInstance(
86
104
  ): boolean {
87
105
  return metadata?.labels[DEFAULT_INSTANCE_LABEL] === RESERVED_LABEL_TRUE;
88
106
  }
107
+
108
+ /** Whether the metadata carries the SYSTEM_LABEL marker (undefined-safe). */
109
+ export function isSystemContent(
110
+ metadata: ApiResourceMetadata | undefined,
111
+ ): boolean {
112
+ return metadata?.labels[SYSTEM_LABEL] === RESERVED_LABEL_TRUE;
113
+ }
@@ -68,7 +68,6 @@ describe.skipIf(!optedIn || !dockerAnswers())(
68
68
  await driver.ensureSessionSandbox(sessionId, {
69
69
  taskQueue: `session:${sessionId}`,
70
70
  stigmerToken: "tok-docker-smoke",
71
- callerClass: "user",
72
71
  });
73
72
  expect(await driver.probe("session", sessionId)).toBe("running");
74
73
 
@@ -92,7 +91,6 @@ describe.skipIf(!optedIn || !dockerAnswers())(
92
91
  await driver.ensureSessionSandbox(sessionId, {
93
92
  taskQueue: `session:${sessionId}`,
94
93
  stigmerToken: "tok-docker-smoke",
95
- callerClass: "user",
96
94
  });
97
95
  expect(await driver.probe("session", sessionId)).toBe("running");
98
96
 
@@ -89,11 +89,7 @@ function fakeGateway(
89
89
  };
90
90
  }
91
91
 
92
- const env = {
93
- taskQueue: "session:ses_1",
94
- stigmerToken: "tok-1",
95
- callerClass: "user",
96
- };
92
+ const env = { taskQueue: "session:ses_1", stigmerToken: "tok-1" };
97
93
 
98
94
  describe("the ensure state machine", () => {
99
95
  it("absent → Secret, PVC, Deployment — in that order, no readiness wait", async () => {
@@ -215,11 +211,7 @@ describe("the manifest shapes (the Java SandboxManifestFactory pins)", () => {
215
211
  });
216
212
 
217
213
  it("a token-less sandbox omits the token env but keeps the (empty) Secret", () => {
218
- const tokenless = {
219
- taskQueue: "session:ses_1",
220
- stigmerToken: "",
221
- callerClass: "user",
222
- };
214
+ const tokenless = { taskQueue: "session:ses_1", stigmerToken: "" };
223
215
  const deployment = buildSandboxDeployment(
224
216
  "session",
225
217
  "ses_1",
@@ -81,7 +81,6 @@ describe.skipIf(process.platform === "win32")(
81
81
  await driver.ensureSessionSandbox("ses_smoke", {
82
82
  taskQueue: "session:ses_smoke",
83
83
  stigmerToken: "tok-smoke",
84
- callerClass: "user",
85
84
  });
86
85
  await vi.waitFor(() => expect(spawnCount()).toBe(1), {
87
86
  timeout: CHILD_SETTLE_TIMEOUT_MS,
@@ -92,7 +91,6 @@ describe.skipIf(process.platform === "win32")(
92
91
  await driver.ensureSessionSandbox("ses_smoke", {
93
92
  taskQueue: "session:ses_smoke",
94
93
  stigmerToken: "tok-smoke",
95
- callerClass: "user",
96
94
  });
97
95
  expect(spawnCount()).toBe(1);
98
96
 
@@ -126,7 +124,6 @@ describe.skipIf(process.platform === "win32")(
126
124
  await driver.ensureSessionSandbox("ses_smoke", {
127
125
  taskQueue: "session:ses_smoke",
128
126
  stigmerToken: "",
129
- callerClass: "user",
130
127
  });
131
128
  await vi.waitFor(() => expect(spawnCount()).toBe(2), {
132
129
  timeout: CHILD_SETTLE_TIMEOUT_MS,