@stigmer/server 3.39.0 → 3.40.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 (150) hide show
  1. package/dist/artifactstorage/artifact-storage.d.ts +10 -2
  2. package/dist/artifactstorage/artifact-storage.d.ts.map +1 -1
  3. package/dist/artifactstorage/artifact-storage.js +6 -3
  4. package/dist/artifactstorage/artifact-storage.js.map +1 -1
  5. package/dist/boot/artifact-lane.d.ts +55 -0
  6. package/dist/boot/artifact-lane.d.ts.map +1 -0
  7. package/dist/boot/artifact-lane.js +74 -0
  8. package/dist/boot/artifact-lane.js.map +1 -0
  9. package/dist/boot/compose.d.ts.map +1 -1
  10. package/dist/boot/compose.js +42 -19
  11. package/dist/boot/compose.js.map +1 -1
  12. package/dist/boot/config.d.ts +11 -6
  13. package/dist/boot/config.d.ts.map +1 -1
  14. package/dist/boot/config.js +10 -5
  15. package/dist/boot/config.js.map +1 -1
  16. package/dist/domain/agent/controller.d.ts +5 -2
  17. package/dist/domain/agent/controller.d.ts.map +1 -1
  18. package/dist/domain/agent/controller.js.map +1 -1
  19. package/dist/domain/agentexecution/controller.d.ts +18 -4
  20. package/dist/domain/agentexecution/controller.d.ts.map +1 -1
  21. package/dist/domain/agentexecution/controller.js +2 -2
  22. package/dist/domain/agentexecution/controller.js.map +1 -1
  23. package/dist/domain/agentexecution/create-steps.d.ts +29 -15
  24. package/dist/domain/agentexecution/create-steps.d.ts.map +1 -1
  25. package/dist/domain/agentexecution/create-steps.js +118 -51
  26. package/dist/domain/agentexecution/create-steps.js.map +1 -1
  27. package/dist/domain/agentinstance/controller.d.ts +8 -2
  28. package/dist/domain/agentinstance/controller.d.ts.map +1 -1
  29. package/dist/domain/agentinstance/controller.js.map +1 -1
  30. package/dist/domain/agentinstance/steps.d.ts +3 -1
  31. package/dist/domain/agentinstance/steps.d.ts.map +1 -1
  32. package/dist/domain/agentinstance/steps.js +12 -6
  33. package/dist/domain/agentinstance/steps.js.map +1 -1
  34. package/dist/domain/agentshare/controller.d.ts +9 -4
  35. package/dist/domain/agentshare/controller.d.ts.map +1 -1
  36. package/dist/domain/agentshare/controller.js +3 -3
  37. package/dist/domain/agentshare/controller.js.map +1 -1
  38. package/dist/domain/artifact/file-server.d.ts +2 -1
  39. package/dist/domain/artifact/file-server.d.ts.map +1 -1
  40. package/dist/domain/artifact/file-server.js +6 -3
  41. package/dist/domain/artifact/file-server.js.map +1 -1
  42. package/dist/domain/channelapp/controller.d.ts +8 -3
  43. package/dist/domain/channelapp/controller.d.ts.map +1 -1
  44. package/dist/domain/channelapp/controller.js.map +1 -1
  45. package/dist/domain/environment/controller.d.ts +8 -5
  46. package/dist/domain/environment/controller.d.ts.map +1 -1
  47. package/dist/domain/environment/controller.js +3 -3
  48. package/dist/domain/environment/controller.js.map +1 -1
  49. package/dist/domain/executioncontext/controller.d.ts +7 -4
  50. package/dist/domain/executioncontext/controller.d.ts.map +1 -1
  51. package/dist/domain/executioncontext/controller.js.map +1 -1
  52. package/dist/domain/mcpserver/controller.d.ts.map +1 -1
  53. package/dist/domain/mcpserver/controller.js +6 -2
  54. package/dist/domain/mcpserver/controller.js.map +1 -1
  55. package/dist/domain/memory/constants.d.ts +9 -0
  56. package/dist/domain/memory/constants.d.ts.map +1 -1
  57. package/dist/domain/memory/constants.js +9 -0
  58. package/dist/domain/memory/constants.js.map +1 -1
  59. package/dist/domain/memory/controller.d.ts +18 -6
  60. package/dist/domain/memory/controller.d.ts.map +1 -1
  61. package/dist/domain/memory/controller.js +18 -21
  62. package/dist/domain/memory/controller.js.map +1 -1
  63. package/dist/domain/memory/steps.d.ts +38 -10
  64. package/dist/domain/memory/steps.d.ts.map +1 -1
  65. package/dist/domain/memory/steps.js +87 -17
  66. package/dist/domain/memory/steps.js.map +1 -1
  67. package/dist/domain/organization/controller.d.ts +7 -4
  68. package/dist/domain/organization/controller.d.ts.map +1 -1
  69. package/dist/domain/organization/controller.js.map +1 -1
  70. package/dist/domain/plugin/controller.d.ts.map +1 -1
  71. package/dist/domain/plugin/controller.js +10 -5
  72. package/dist/domain/plugin/controller.js.map +1 -1
  73. package/dist/domain/session/controller.d.ts +7 -3
  74. package/dist/domain/session/controller.d.ts.map +1 -1
  75. package/dist/domain/session/controller.js.map +1 -1
  76. package/dist/domain/skill/controller.d.ts.map +1 -1
  77. package/dist/domain/skill/controller.js +15 -5
  78. package/dist/domain/skill/controller.js.map +1 -1
  79. package/dist/domain/workflow/controller.d.ts +6 -2
  80. package/dist/domain/workflow/controller.d.ts.map +1 -1
  81. package/dist/domain/workflow/controller.js +6 -3
  82. package/dist/domain/workflow/controller.js.map +1 -1
  83. package/dist/domain/workflowexecution/controller.d.ts +8 -4
  84. package/dist/domain/workflowexecution/controller.d.ts.map +1 -1
  85. package/dist/domain/workflowexecution/controller.js.map +1 -1
  86. package/dist/domain/workflowexecution/update-status.d.ts.map +1 -1
  87. package/dist/domain/workflowexecution/update-status.js +3 -2
  88. package/dist/domain/workflowexecution/update-status.js.map +1 -1
  89. package/dist/domain/workflowinstance/controller.d.ts +2 -2
  90. package/dist/domain/workflowinstance/controller.d.ts.map +1 -1
  91. package/dist/domain/workflowinstance/controller.js +10 -5
  92. package/dist/domain/workflowinstance/controller.js.map +1 -1
  93. package/dist/domain/workflowinstance/steps.d.ts +5 -3
  94. package/dist/domain/workflowinstance/steps.d.ts.map +1 -1
  95. package/dist/domain/workflowinstance/steps.js +14 -9
  96. package/dist/domain/workflowinstance/steps.js.map +1 -1
  97. package/dist/extensions/identity.d.ts +28 -14
  98. package/dist/extensions/identity.d.ts.map +1 -1
  99. package/dist/extensions/identity.js +38 -0
  100. package/dist/extensions/identity.js.map +1 -1
  101. package/dist/pipeline/steps/guard-memory-capture.d.ts.map +1 -1
  102. package/dist/pipeline/steps/guard-memory-capture.js +1 -13
  103. package/dist/pipeline/steps/guard-memory-capture.js.map +1 -1
  104. package/package.json +6 -6
  105. package/src/artifactstorage/__tests__/artifact-storage.test.ts +15 -1
  106. package/src/artifactstorage/artifact-storage.ts +12 -4
  107. package/src/boot/__tests__/artifact-lane.test.ts +71 -0
  108. package/src/boot/__tests__/compose.test.ts +73 -3
  109. package/src/boot/__tests__/config.test.ts +19 -0
  110. package/src/boot/artifact-lane.ts +96 -0
  111. package/src/boot/compose.ts +57 -21
  112. package/src/boot/config.ts +25 -15
  113. package/src/domain/agent/controller.ts +5 -2
  114. package/src/domain/agentexecution/__tests__/create-steps.test.ts +230 -2
  115. package/src/domain/agentexecution/controller.ts +32 -6
  116. package/src/domain/agentexecution/create-steps.ts +162 -62
  117. package/src/domain/agentinstance/__tests__/store-faults.test.ts +69 -18
  118. package/src/domain/agentinstance/controller.ts +8 -2
  119. package/src/domain/agentinstance/steps.ts +12 -6
  120. package/src/domain/agentshare/controller.ts +12 -7
  121. package/src/domain/artifact/file-server.ts +8 -4
  122. package/src/domain/channelapp/controller.ts +8 -3
  123. package/src/domain/environment/controller.ts +11 -8
  124. package/src/domain/executioncontext/controller.ts +7 -4
  125. package/src/domain/mcpserver/controller.ts +6 -2
  126. package/src/domain/memory/__tests__/capture-credential.test.ts +6 -6
  127. package/src/domain/memory/__tests__/person-consent-composed.test.ts +187 -0
  128. package/src/domain/memory/__tests__/person-consent.test.ts +274 -0
  129. package/src/domain/memory/constants.ts +11 -0
  130. package/src/domain/memory/controller.ts +36 -27
  131. package/src/domain/memory/steps.ts +112 -17
  132. package/src/domain/organization/controller.ts +7 -4
  133. package/src/domain/plugin/__tests__/plugin.test.ts +8 -0
  134. package/src/domain/plugin/__tests__/store-faults.test.ts +104 -0
  135. package/src/domain/plugin/controller.ts +10 -5
  136. package/src/domain/session/controller.ts +7 -3
  137. package/src/domain/skill/__tests__/skill.test.ts +4 -0
  138. package/src/domain/skill/__tests__/store-faults.test.ts +103 -0
  139. package/src/domain/skill/controller.ts +15 -5
  140. package/src/domain/workflow/__tests__/store-faults.test.ts +107 -0
  141. package/src/domain/workflow/controller.ts +12 -5
  142. package/src/domain/workflowexecution/controller.ts +8 -4
  143. package/src/domain/workflowexecution/update-status.ts +3 -2
  144. package/src/domain/workflowinstance/__tests__/store-faults.test.ts +172 -0
  145. package/src/domain/workflowinstance/controller.ts +12 -7
  146. package/src/domain/workflowinstance/steps.ts +14 -10
  147. package/src/extensions/__tests__/identity.test.ts +82 -0
  148. package/src/extensions/identity.ts +47 -0
  149. package/src/pipeline/__tests__/blind-not-found.test.ts +4 -20
  150. package/src/pipeline/steps/guard-memory-capture.ts +4 -19
@@ -8,7 +8,7 @@
8
8
  * the family test ../workflow/__tests__/workflow.test.ts (the mutual edge
9
9
  * makes the two domains one testable unit).
10
10
  */
11
- import { ConnectError } from "@connectrpc/connect";
11
+ import { Code, ConnectError } from "@connectrpc/connect";
12
12
  import { WorkflowSchema } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/api_pb";
13
13
  import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
14
14
  import { IamPermission } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
@@ -22,9 +22,11 @@ import { ResourceNotFoundError } from "../../store/interface.js";
22
22
  export const PARENT_WORKFLOW_KEY = "parent_workflow";
23
23
  /**
24
24
  * LoadParentWorkflow — create.go: loads the workflow the instance
25
- * materializes and stashes it for the same-org rule. ANY load failure maps
26
- * to NotFound("Workflow", id) — Go folds every client error into that arm
27
- * (capital-W copy is the wire contract).
25
+ * materializes and stashes it for the same-org rule. The workflow is read
26
+ * through the in-process query client, so a missing one arrives as a Connect
27
+ * NotFound and answers NotFound("Workflow", id) (the capital-W copy is the
28
+ * wire contract); any other failure is a fault answered as Internal, never a
29
+ * missing parent.
28
30
  */
29
31
  export function newLoadParentWorkflowStep(loaderProvider, logger) {
30
32
  return {
@@ -37,11 +39,14 @@ export function newLoadParentWorkflowStep(loaderProvider, logger) {
37
39
  parent = await loaderProvider().get(workflowId);
38
40
  }
39
41
  catch (error) {
40
- logger.warn("Parent workflow not found", {
41
- error: error instanceof ConnectError ? error.rawMessage : String(error),
42
- workflowId,
43
- });
44
- throw notFoundError("Workflow", workflowId);
42
+ if (error instanceof ConnectError && error.code === Code.NotFound) {
43
+ logger.warn("Parent workflow not found", {
44
+ error: error.rawMessage,
45
+ workflowId,
46
+ });
47
+ throw notFoundError("Workflow", workflowId);
48
+ }
49
+ throw internalError(error, "failed to load parent workflow");
45
50
  }
46
51
  ctx.set(PARENT_WORKFLOW_KEY, parent);
47
52
  },
@@ -1 +1 @@
1
- {"version":3,"file":"steps.js","sourceRoot":"","sources":["../../../src/domain/workflowinstance/steps.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"steps.js","sourceRoot":"","sources":["../../../src/domain/workflowinstance/steps.ts"],"names":[],"mappings":"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"}
@@ -1,17 +1,3 @@
1
- /**
2
- * Identity extension-point types — the verifier-chain contract of the
3
- * convergence blueprint (20260826.02 blueprint/03 §4, DD-007), carried by
4
- * the extension registry from O1 (20260826.09) and consumed by the
5
- * verifier-chain chassis (O2, 20260827.01 — pipeline/interceptors/auth.ts).
6
- *
7
- * The shapes are transcribed from the ratified design, not invented here:
8
- * a verifier either CLAIMS a token (verifying it fully, throwing on
9
- * signature/expiry/audience failure) or PASSES (returns null → next
10
- * verifier) — the TS rendering of the Java ProviderManager chain. The
11
- * product is one typed value, CallerIdentity, threaded explicitly through
12
- * HandlerContext.values (never ambient state — the every-dependency-
13
- * explicit doctrine).
14
- */
15
1
  /**
16
2
  * The caller-class discriminant. OSS knows user / machine / runner plus
17
3
  * the in-process `internal` class (O2 ruling Q4: the TS rendering of the
@@ -137,4 +123,32 @@ export declare function isPlatformPipelineCaller(caller: CallerIdentity): boolea
137
123
  * instance's provenance) must key on this predicate and never on that one.
138
124
  */
139
125
  export declare function isServerComposedRequest(caller: CallerIdentity): boolean;
126
+ /**
127
+ * Whether `caller` is a FIRST-PARTY HUMAN OPERATOR: a person at the
128
+ * console, the CLI or an SDK, speaking for themselves on the wire — the
129
+ * TS rendering of the Java RequestCallerIdentity.isFirstPartyHumanOperator
130
+ * (not machine, not impersonated, no token_type, not a PlatformClient user
131
+ * token). Stated once for every consumer that must tell the person from a
132
+ * lane acting near them: memory recall and the person's declared
133
+ * preferences on execution create (domain/agentexecution/create-steps.ts).
134
+ *
135
+ * An ALLOW-list, so a lane this module has never heard of is excluded by
136
+ * construction: the class must be `user` (every human verifier stamps it —
137
+ * OIDC, API key, the cloud's direct and federated logins; runners, the
138
+ * cloud's token lanes and the platform's own pipelines each carry their
139
+ * own class), the request must come from the wire (the open-source
140
+ * schedule fire acts as its creator with class `user` but rides the
141
+ * in-process transport, which is exactly what `isServerComposedRequest`
142
+ * reads), and the bearer must not be a PlatformClient user token (a
143
+ * person, but spoken for by a third-party client — DD-002 D4 as amended).
144
+ */
145
+ export declare function isFirstPartyHumanOperator(caller: CallerIdentity): boolean;
146
+ /**
147
+ * Whether the bearer is a platform token naming a PlatformClient. Anything
148
+ * that is not a platform token (no token, an API key, another issuer's
149
+ * JWT) is not one, whatever claims it carries (stigmer#1312). Read from
150
+ * the raw token, never from `platformClientId`, which is audit provenance
151
+ * set by one verifier and not a guard vocabulary (see its field doc).
152
+ */
153
+ export declare function carriesPlatformClientClaim(rawToken: string): boolean;
140
154
  //# sourceMappingURL=identity.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/extensions/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,WAAW,GACnB,MAAM,GACN,SAAS,GACT,QAAQ,GACR,UAAU,GACV,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAElB;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,YAAY,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,uEAAuE;IACvE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,wEAAwE;IACxE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,mEAAmE;IACnE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC;CAC9B;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;CACvD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAExE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAEvE"}
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/extensions/identity.ts"],"names":[],"mappings":"AAoBA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,WAAW,GACnB,MAAM,GACN,SAAS,GACT,QAAQ,GACR,UAAU,GACV,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAElB;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,YAAY,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,uEAAuE;IACvE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,wEAAwE;IACxE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,mEAAmE;IACnE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC;CAC9B;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;CACvD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAExE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAEvE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAMzE;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAMpE"}
@@ -12,6 +12,8 @@
12
12
  * HandlerContext.values (never ambient state — the every-dependency-
13
13
  * explicit doctrine).
14
14
  */
15
+ import { USER_TOKEN_CLAIMS } from "../domain/platformclient/constants.js";
16
+ import { decodeVerifiedPlatformTokenPayload, stringClaim, } from "../platformtoken/envelope.js";
15
17
  /**
16
18
  * Whether `caller` is one of the platform's own pipelines — the admission
17
19
  * rule of the RPCs a person must never reach directly, stated ONCE
@@ -49,4 +51,40 @@ export function isPlatformPipelineCaller(caller) {
49
51
  export function isServerComposedRequest(caller) {
50
52
  return caller.callerClass === "internal" || caller.origin === "in-process";
51
53
  }
54
+ /**
55
+ * Whether `caller` is a FIRST-PARTY HUMAN OPERATOR: a person at the
56
+ * console, the CLI or an SDK, speaking for themselves on the wire — the
57
+ * TS rendering of the Java RequestCallerIdentity.isFirstPartyHumanOperator
58
+ * (not machine, not impersonated, no token_type, not a PlatformClient user
59
+ * token). Stated once for every consumer that must tell the person from a
60
+ * lane acting near them: memory recall and the person's declared
61
+ * preferences on execution create (domain/agentexecution/create-steps.ts).
62
+ *
63
+ * An ALLOW-list, so a lane this module has never heard of is excluded by
64
+ * construction: the class must be `user` (every human verifier stamps it —
65
+ * OIDC, API key, the cloud's direct and federated logins; runners, the
66
+ * cloud's token lanes and the platform's own pipelines each carry their
67
+ * own class), the request must come from the wire (the open-source
68
+ * schedule fire acts as its creator with class `user` but rides the
69
+ * in-process transport, which is exactly what `isServerComposedRequest`
70
+ * reads), and the bearer must not be a PlatformClient user token (a
71
+ * person, but spoken for by a third-party client — DD-002 D4 as amended).
72
+ */
73
+ export function isFirstPartyHumanOperator(caller) {
74
+ return (caller.callerClass === "user" &&
75
+ !isServerComposedRequest(caller) &&
76
+ !carriesPlatformClientClaim(caller.rawToken));
77
+ }
78
+ /**
79
+ * Whether the bearer is a platform token naming a PlatformClient. Anything
80
+ * that is not a platform token (no token, an API key, another issuer's
81
+ * JWT) is not one, whatever claims it carries (stigmer#1312). Read from
82
+ * the raw token, never from `platformClientId`, which is audit provenance
83
+ * set by one verifier and not a guard vocabulary (see its field doc).
84
+ */
85
+ export function carriesPlatformClientClaim(rawToken) {
86
+ const payload = decodeVerifiedPlatformTokenPayload(rawToken);
87
+ return (payload !== undefined &&
88
+ stringClaim(payload, USER_TOKEN_CLAIMS.platformClientId) !== undefined);
89
+ }
52
90
  //# sourceMappingURL=identity.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/extensions/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAuGH;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,wBAAwB,CAAC,MAAsB;IAC7D,OAAO,MAAM,CAAC,WAAW,KAAK,SAAS,IAAI,uBAAuB,CAAC,MAAM,CAAC,CAAC;AAC7E,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAsB;IAC5D,OAAO,MAAM,CAAC,WAAW,KAAK,UAAU,IAAI,MAAM,CAAC,MAAM,KAAK,YAAY,CAAC;AAC7E,CAAC"}
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/extensions/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAC1E,OAAO,EACL,kCAAkC,EAClC,WAAW,GACZ,MAAM,8BAA8B,CAAC;AAuGtC;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,wBAAwB,CAAC,MAAsB;IAC7D,OAAO,MAAM,CAAC,WAAW,KAAK,SAAS,IAAI,uBAAuB,CAAC,MAAM,CAAC,CAAC;AAC7E,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAsB;IAC5D,OAAO,MAAM,CAAC,WAAW,KAAK,UAAU,IAAI,MAAM,CAAC,MAAM,KAAK,YAAY,CAAC;AAC7E,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,yBAAyB,CAAC,MAAsB;IAC9D,OAAO,CACL,MAAM,CAAC,WAAW,KAAK,MAAM;QAC7B,CAAC,uBAAuB,CAAC,MAAM,CAAC;QAChC,CAAC,0BAA0B,CAAC,MAAM,CAAC,QAAQ,CAAC,CAC7C,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,0BAA0B,CAAC,QAAgB;IACzD,MAAM,OAAO,GAAG,kCAAkC,CAAC,QAAQ,CAAC,CAAC;IAC7D,OAAO,CACL,OAAO,KAAK,SAAS;QACrB,WAAW,CAAC,OAAO,EAAE,iBAAiB,CAAC,gBAAgB,CAAC,KAAK,SAAS,CACvE,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"guard-memory-capture.d.ts","sourceRoot":"","sources":["../../../src/pipeline/steps/guard-memory-capture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAKtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAK5D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,gDAAgD,CAAC;AAG/F,wEAAwE;AACxE,eAAO,MAAM,6BAA6B,iEACsB,CAAC;AAEjE;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,4BAA4B,CAAC;AAEvE,wBAAgB,yBAAyB,CAAC,IAAI,SAAS,WAAW,EAChE,QAAQ,CAAC,EAAE,wBAAwB,GAClC,YAAY,CAAC,IAAI,CAAC,CAsDpB;AAED,4EAA4E;AAC5E,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,wBAAwB,EAAE,MAAM,CAAC;IAC1C,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,SAAS,WAAW,EAChE,GAAG,EAAE,cAAc,CAAC,IAAI,CAAC,GACxB,uBAAuB,GAAG,SAAS,CAWrC"}
1
+ {"version":3,"file":"guard-memory-capture.d.ts","sourceRoot":"","sources":["../../../src/pipeline/steps/guard-memory-capture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAOtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,gDAAgD,CAAC;AAG/F,wEAAwE;AACxE,eAAO,MAAM,6BAA6B,iEACsB,CAAC;AAEjE;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,4BAA4B,CAAC;AAEvE,wBAAgB,yBAAyB,CAAC,IAAI,SAAS,WAAW,EAChE,QAAQ,CAAC,EAAE,wBAAwB,GAClC,YAAY,CAAC,IAAI,CAAC,CAsDpB;AAED,4EAA4E;AAC5E,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,wBAAwB,EAAE,MAAM,CAAC;IAC1C,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,SAAS,WAAW,EAChE,GAAG,EAAE,cAAc,CAAC,IAAI,CAAC,GACxB,uBAAuB,GAAG,SAAS,CAWrC"}
@@ -1,7 +1,5 @@
1
1
  import { Code, ConnectError } from "@connectrpc/connect";
2
- import { USER_TOKEN_CLAIMS } from "../../domain/platformclient/constants.js";
3
- import { isServerComposedRequest } from "../../extensions/identity.js";
4
- import { decodeVerifiedPlatformTokenPayload, stringClaim, } from "../../platformtoken/envelope.js";
2
+ import { carriesPlatformClientClaim, isServerComposedRequest, } from "../../extensions/identity.js";
5
3
  import { metadataOf } from "./shapes.js";
6
4
  /** The Java MemoryPolicy.MEMORY_CAPTURE_CALLER_MESSAGE, byte-pinned. */
7
5
  export const MEMORY_CAPTURE_CALLER_MESSAGE = "memory can only be captured for a first-party human operator";
@@ -71,14 +69,4 @@ export function memoryCaptureCredentialOf(ctx) {
71
69
  }
72
70
  return undefined;
73
71
  }
74
- /**
75
- * Whether the bearer is a platform token naming a PlatformClient. Anything
76
- * that is not a platform token (no token, an API key, another issuer's
77
- * JWT) is not one, whatever claims it carries.
78
- */
79
- function carriesPlatformClientClaim(rawToken) {
80
- const payload = decodeVerifiedPlatformTokenPayload(rawToken);
81
- return (payload !== undefined &&
82
- stringClaim(payload, USER_TOKEN_CLAIMS.platformClientId) !== undefined);
83
- }
84
72
  //# sourceMappingURL=guard-memory-capture.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"guard-memory-capture.js","sourceRoot":"","sources":["../../../src/pipeline/steps/guard-memory-capture.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"guard-memory-capture.js","sourceRoot":"","sources":["../../../src/pipeline/steps/guard-memory-capture.ts"],"names":[],"mappings":"AA0CA,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,8BAA8B,CAAC;AAItC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,wEAAwE;AACxE,MAAM,CAAC,MAAM,6BAA6B,GACxC,8DAA8D,CAAC;AAEjE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,yBAAyB,CAAC;AAEvE,MAAM,UAAU,yBAAyB,CACvC,QAAmC;IAEnC,OAAO;QACL,IAAI,EAAE,oBAAoB;QAC1B,KAAK,CAAC,OAAO,CAAC,GAAyB;YACrC,MAAM,MAAM,GAAG,GAAG,CAAC,cAAc,CAAC;YAClC,IAAI,uBAAuB,CAAC,MAAM,CAAC,EAAE,CAAC;gBACpC,6DAA6D;gBAC7D,+DAA+D;gBAC/D,OAAO;YACT,CAAC;YACD,MAAM,MAAM,GAAG,QAAQ,EAAE,sBAAsB,CAAC;YAChD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,gEAAgE;gBAChE,8DAA8D;gBAC9D,8DAA8D;gBAC9D,gEAAgE;gBAChE,2DAA2D;gBAC3D,4DAA4D;gBAC5D,uDAAuD;gBACvD,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC;gBAChD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;gBAC1D,QAAQ,QAAQ,CAAC,OAAO,EAAE,CAAC;oBACzB,KAAK,OAAO;wBACV,GAAG,CAAC,GAAG,CAAC,6BAA6B,EAAE;4BACrC,wBAAwB,EAAE,QAAQ,CAAC,wBAAwB;4BAC3D,eAAe,EAAE,QAAQ,CAAC,eAAe;yBAC1C,CAAC,CAAC;wBACH,OAAO;oBACT,KAAK,QAAQ;wBACX,MAAM,IAAI,YAAY,CACpB,6BAA6B,EAC7B,IAAI,CAAC,gBAAgB,CACtB,CAAC;oBACJ,KAAK,YAAY;wBACf,MAAM;oBACR,OAAO,CAAC,CAAC,CAAC;wBACR,MAAM,UAAU,GAAU,QAAQ,CAAC;wBACnC,MAAM,IAAI,KAAK,CACb,mCAAmC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CAChE,CAAC;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC;YACD,IACE,MAAM,CAAC,WAAW,KAAK,SAAS;gBAChC,0BAA0B,CAAC,MAAM,CAAC,QAAQ,CAAC,EAC3C,CAAC;gBACD,MAAM,IAAI,YAAY,CACpB,6BAA6B,EAC7B,IAAI,CAAC,gBAAgB,CACtB,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAQD;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CACvC,GAAyB;IAEzB,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;IACvD,IACE,OAAO,OAAO,KAAK,QAAQ;QAC3B,OAAO,KAAK,IAAI;QAChB,0BAA0B,IAAI,OAAO;QACrC,iBAAiB,IAAI,OAAO,EAC5B,CAAC;QACD,OAAO,OAAkC,CAAC;IAC5C,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stigmer/server",
3
- "version": "3.39.0",
3
+ "version": "3.40.0",
4
4
  "description": "The Stigmer OSS control plane as a composable library: composeServer, the extension registry and the driver seams a composition builds on. The self-contained deployable of the same server is @stigmer/server-slim.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -63,11 +63,11 @@
63
63
  "@connectrpc/connect-node": "^2.0.0",
64
64
  "@kubernetes/client-node": "2.0.0",
65
65
  "@opentelemetry/api": "^1.9.0",
66
- "@stigmer/outbound": "3.39.0",
67
- "@stigmer/plugin-package": "3.39.0",
68
- "@stigmer/protos": "3.39.0",
69
- "@stigmer/temporal-codecs": "3.39.0",
70
- "@stigmer/zip-structure": "3.39.0",
66
+ "@stigmer/outbound": "3.40.0",
67
+ "@stigmer/plugin-package": "3.40.0",
68
+ "@stigmer/protos": "3.40.0",
69
+ "@stigmer/temporal-codecs": "3.40.0",
70
+ "@stigmer/zip-structure": "3.40.0",
71
71
  "@temporalio/activity": "1.16.2",
72
72
  "@temporalio/client": "1.16.2",
73
73
  "@temporalio/common": "1.16.2",
@@ -2,7 +2,8 @@
2
2
  * Ports storage/local_storage_test.go + disposition_test.go case-for-case:
3
3
  * the containment guard (every escaping key refused on every operation,
4
4
  * contained dot-segment keys allowed), the #285 no-implicit-segment
5
- * layout contract, the local signed-URL shape, and the
5
+ * layout contract, the local signed-URL shape (a fixed serve URL, or one
6
+ * resolved per mint once the file server binds — stigmer#1089), and the
6
7
  * Content-Disposition builder. Adds the factory's r2/unknown refusals
7
8
  * (Go's are boot asserted; here they're the disclosed deferral), and the
8
9
  * O5 widened surface: size, the typed not-found, presigned-PUT over the
@@ -154,6 +155,19 @@ describe("LocalArtifactStorage", () => {
154
155
  expect(exotic).toBe(`http://localhost:7235/${key}?download=a~b%2Ac.md`);
155
156
  });
156
157
 
158
+ it("signed URL: a resolved serve URL is read at every mint, not at construction", async () => {
159
+ let origin = "";
160
+ const resolved = new LocalArtifactStorage(base, () => origin);
161
+ const key = "artifacts/aex_1/plan.md";
162
+ await expect(resolved.getSignedUrl(key, 3600_000, "")).rejects.toThrow(
163
+ "local serve URL not configured",
164
+ );
165
+ origin = "http://localhost:51234";
166
+ expect(await resolved.getSignedUrl(key, 3600_000, "")).toBe(
167
+ `http://localhost:51234/${key}`,
168
+ );
169
+ });
170
+
157
171
  it("delete removes the artifact and prunes empty parents, never the root", async () => {
158
172
  const key = "attachments/01DEL/file.txt";
159
173
  await storage.upload(key, Buffer.from("x"), "text/plain");
@@ -157,7 +157,12 @@ export interface ArtifactStorageConfig {
157
157
  /** "local" or "r2" ("" defaults to local — Go NewArtifactStorage). */
158
158
  readonly type: string;
159
159
  readonly localBasePath: string;
160
- readonly localServeUrl: string;
160
+ /**
161
+ * The download URL base: a fixed string, or a resolver read per mint when
162
+ * the base is only known once the artifact file server binds
163
+ * (boot/artifact-lane.ts). "" = not configured.
164
+ */
165
+ readonly localServeUrl: string | (() => string);
161
166
  /** Cloudflare R2 (S3-compatible) settings — required when type is "r2". */
162
167
  readonly r2Bucket: string;
163
168
  readonly r2Endpoint: string;
@@ -214,7 +219,8 @@ export function newArtifactStorage(
214
219
  export class LocalArtifactStorage implements ArtifactStorage {
215
220
  constructor(
216
221
  private readonly basePath: string,
217
- private readonly serveUrl: string,
222
+ /** Fixed, or resolved per mint (ArtifactStorageConfig.localServeUrl). */
223
+ private readonly serveUrl: string | (() => string),
218
224
  /**
219
225
  * The staged-upload mechanism backing presignPut — optional because
220
226
  * only instances whose root the lane stages into can serve it (the
@@ -287,10 +293,12 @@ export class LocalArtifactStorage implements ArtifactStorage {
287
293
  _expiresInMs: number,
288
294
  downloadFilename: string,
289
295
  ): Promise<string> {
290
- if (this.serveUrl === "") {
296
+ const serveUrl =
297
+ typeof this.serveUrl === "function" ? this.serveUrl() : this.serveUrl;
298
+ if (serveUrl === "") {
291
299
  throw new Error("local serve URL not configured");
292
300
  }
293
- let url = `${this.serveUrl}/${key}`;
301
+ let url = `${serveUrl}/${key}`;
294
302
  if (downloadFilename !== "") {
295
303
  url += `?${LOCAL_DOWNLOAD_QUERY_PARAM}=${goQueryEscape(downloadFilename)}`;
296
304
  }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Pins the artifact lane's placement rules: a configured port wins (an
3
+ * explicit 0 included), an ephemeral unified port makes the lane ephemeral
4
+ * rather than deriving port 1 (stigmer#1089), and otherwise the lane sits at
5
+ * the unified port + 1; a configured serve URL passes through, and the
6
+ * derived one follows the port the lane bound and refuses before the bind.
7
+ */
8
+ import { describe, expect, it } from "vitest";
9
+
10
+ import {
11
+ resolveArtifactLanePort,
12
+ resolveArtifactServeUrl,
13
+ } from "../artifact-lane.js";
14
+
15
+ describe("resolveArtifactLanePort", () => {
16
+ it("keeps a configured port whatever the unified port is", () => {
17
+ expect(
18
+ resolveArtifactLanePort({ configured: 9000, unifiedPort: 7234 }),
19
+ ).toBe(9000);
20
+ expect(resolveArtifactLanePort({ configured: 9000, unifiedPort: 0 })).toBe(
21
+ 9000,
22
+ );
23
+ });
24
+
25
+ it("keeps a configured 0 as an explicit ephemeral bind", () => {
26
+ expect(resolveArtifactLanePort({ configured: 0, unifiedPort: 7234 })).toBe(
27
+ 0,
28
+ );
29
+ });
30
+
31
+ it("binds ephemeral when the unified port is not known until listen, never port 1", () => {
32
+ expect(
33
+ resolveArtifactLanePort({ configured: undefined, unifiedPort: 0 }),
34
+ ).toBe(0);
35
+ });
36
+
37
+ it("takes the unified port + 1 when nothing is configured", () => {
38
+ expect(
39
+ resolveArtifactLanePort({ configured: undefined, unifiedPort: 7234 }),
40
+ ).toBe(7235);
41
+ });
42
+ });
43
+
44
+ describe("resolveArtifactServeUrl", () => {
45
+ it("passes a configured serve URL through as given", () => {
46
+ expect(
47
+ resolveArtifactServeUrl(
48
+ "https://artifacts.stigmer.test",
49
+ () => undefined,
50
+ ),
51
+ ).toBe("https://artifacts.stigmer.test");
52
+ });
53
+
54
+ it("derives the lane's localhost origin from the port it bound", () => {
55
+ let bound: number | undefined;
56
+ const serveUrl = resolveArtifactServeUrl("", () => bound);
57
+ if (typeof serveUrl === "string")
58
+ throw new Error("expected a resolver for an unset serve URL");
59
+ bound = 51234;
60
+ expect(serveUrl()).toBe("http://localhost:51234");
61
+ });
62
+
63
+ it("refuses to mint a URL before the lane bound", () => {
64
+ const serveUrl = resolveArtifactServeUrl("", () => undefined);
65
+ if (typeof serveUrl === "string")
66
+ throw new Error("expected a resolver for an unset serve URL");
67
+ expect(() => serveUrl()).toThrow(
68
+ "before the artifact file server bound its port",
69
+ );
70
+ });
71
+ });
@@ -7,7 +7,12 @@
7
7
  * - start() flips SERVING BEFORE the port binds, so the first probe that
8
8
  * reaches the port already sees a serving server;
9
9
  * - shutdown flips NOT_SERVING FIRST, then drains — and the port stops
10
- * answering.
10
+ * answering;
11
+ * - the artifact download lane binds before SERVING, so a lane that
12
+ * cannot bind fails start() with the address and the setting named and
13
+ * the server never reports itself serving (stigmer#1089), and a server
14
+ * on an ephemeral port gets an ephemeral lane whose minted download
15
+ * URLs reach it.
11
16
  */
12
17
  import { createClient } from "@connectrpc/connect";
13
18
  import { createGrpcTransport } from "@connectrpc/connect-node";
@@ -15,7 +20,13 @@ import {
15
20
  Health,
16
21
  HealthCheckResponse_ServingStatus as ServingStatus,
17
22
  } from "@stigmer/protos/grpc/health/v1/health_pb";
18
- import { connect as netConnect } from "node:net";
23
+ import { ArtifactCommandController } from "@stigmer/protos/ai/stigmer/agentic/artifact/v1/command_pb";
24
+ import { ArtifactQueryController } from "@stigmer/protos/ai/stigmer/agentic/artifact/v1/query_pb";
25
+ import {
26
+ createServer as netCreateServer,
27
+ connect as netConnect,
28
+ } from "node:net";
29
+ import type { AddressInfo } from "node:net";
19
30
  import { describe, expect, it } from "vitest";
20
31
 
21
32
  import { mkdtempSync } from "node:fs";
@@ -32,11 +43,12 @@ const silentLogger = createLogger({
32
43
  write: () => {},
33
44
  });
34
45
 
35
- function compose() {
46
+ function compose(env: NodeJS.ProcessEnv = {}) {
36
47
  // Each composed server gets a throwaway database — the storage stage
37
48
  // opens DB_PATH for real (never the developer's ~/.stigmer).
38
49
  const testDir = mkdtempSync(path.join(tmpdir(), "compose-test-"));
39
50
  const config = loadConfig({
51
+ ...env,
40
52
  STIGMER_MODEL_REGISTRY_REFRESH: "off",
41
53
  // No engine behind composed tests: 127.0.0.1:1 is deterministically
42
54
  // closed, so boots fail the non-fatal connect fast and can never touch
@@ -95,3 +107,61 @@ describe("composition-root boot ordering", () => {
95
107
  expect(refused, "the drained port must refuse new connections").toBe(true);
96
108
  });
97
109
  });
110
+
111
+ describe("the artifact download lane", () => {
112
+ it("fails start() when the lane cannot bind, naming the address and the setting, and never serves", async () => {
113
+ const holder = netCreateServer();
114
+ await new Promise<void>((resolve) =>
115
+ holder.listen(0, "127.0.0.1", resolve),
116
+ );
117
+ const held = (holder.address() as AddressInfo).port;
118
+ try {
119
+ const server = await compose({ ARTIFACT_HTTP_PORT: String(held) });
120
+ try {
121
+ await expect(server.start()).rejects.toThrow(
122
+ `the artifact file server could not bind '127.0.0.1:${held}' (EADDRINUSE): ARTIFACT_HTTP_PORT=${held} cannot be bound; set it to a free port`,
123
+ );
124
+ // SERVING is set before the unified port binds, so a server that is
125
+ // still NOT_SERVING never opened it.
126
+ expect(server.healthState.status("")).toBe(ServingStatus.NOT_SERVING);
127
+ } finally {
128
+ await server.shutdown();
129
+ }
130
+ } finally {
131
+ await new Promise<void>((resolve) => holder.close(() => resolve()));
132
+ }
133
+ });
134
+
135
+ it("binds an ephemeral lane beside an ephemeral unified port, and a minted download URL reaches it", async () => {
136
+ const server = await compose();
137
+ const port = await server.start();
138
+ try {
139
+ const transport = createGrpcTransport({
140
+ baseUrl: `http://127.0.0.1:${port}`,
141
+ });
142
+ const command = createClient(ArtifactCommandController, transport);
143
+ const query = createClient(ArtifactQueryController, transport);
144
+ const content = new TextEncoder().encode("ephemeral lane body\n");
145
+ const created = await command.create({
146
+ spec: {
147
+ displayName: "ephemeral-lane.txt",
148
+ contentType: "text/plain",
149
+ source: { agentExecutionId: "aexec_01ephemerallane" },
150
+ },
151
+ content,
152
+ });
153
+ const download = await query.getDownloadUrl({
154
+ value: created.metadata!.id,
155
+ });
156
+
157
+ // An OS-assigned port, never the privileged port 1 that +1 derived
158
+ // from 0; the bytes coming back prove it is THIS server's lane.
159
+ expect(Number(new URL(download.url).port)).toBeGreaterThan(1024);
160
+ const served = await fetch(download.url);
161
+ expect(served.status).toBe(200);
162
+ expect(new Uint8Array(await served.arrayBuffer())).toEqual(content);
163
+ } finally {
164
+ await server.shutdown();
165
+ }
166
+ });
167
+ });
@@ -71,6 +71,25 @@ describe("loadConfig", () => {
71
71
  );
72
72
  });
73
73
 
74
+ // stigmer#1089: the artifact lane's port and serve URL follow the unified
75
+ // port the composition binds (boot/artifact-lane.ts), so the loader keeps
76
+ // only what the operator set and derives nothing.
77
+ it("leaves an unset or malformed ARTIFACT_HTTP_PORT unset, for the lane to derive", () => {
78
+ expect(loadConfig({}).artifactHttpPort).toBeUndefined();
79
+ expect(loadConfig({ GRPC_PORT: "0" }).artifactHttpPort).toBeUndefined();
80
+ expect(loadConfig({ ARTIFACT_HTTP_PORT: "7300x" }).artifactHttpPort).toBeUndefined();
81
+ expect(loadConfig({ ARTIFACT_HTTP_PORT: "7300" }).artifactHttpPort).toBe(7300);
82
+ expect(loadConfig({ ARTIFACT_HTTP_PORT: "0" }).artifactHttpPort).toBe(0);
83
+ });
84
+
85
+ it("leaves an unset ARTIFACT_LOCAL_SERVE_URL empty, for the bound lane to answer", () => {
86
+ expect(loadConfig({}).artifactLocalServeUrl).toBe("");
87
+ expect(
88
+ loadConfig({ ARTIFACT_LOCAL_SERVE_URL: "https://artifacts.stigmer.test" })
89
+ .artifactLocalServeUrl,
90
+ ).toBe("https://artifacts.stigmer.test");
91
+ });
92
+
74
93
  // 20260913.02 (sp.console-login, Q-CL-1): the console's public PKCE client
75
94
  // id is lenient on purpose — a CLI-only self-host that set the issuer
76
95
  // before this knob existed must keep booting on upgrade, and the served
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Where the artifact file server listens, and the URL local artifact
3
+ * storage mints downloads under, on a server that was not told.
4
+ *
5
+ * The artifact download lane is a second loopback listener beside the
6
+ * unified port (domain/artifact/file-server.ts), bound only when artifact
7
+ * storage is local. `ARTIFACT_HTTP_PORT` and `ARTIFACT_LOCAL_SERVE_URL`
8
+ * place it explicitly; unset, both follow the unified port, and this module
9
+ * is the one place that says how.
10
+ *
11
+ * The port rules, in order: a configured port always wins (0 included — an
12
+ * explicit ephemeral bind); a unified port of 0 (`GRPC_PORT=0`, and the
13
+ * `portOverride: 0` test seam) is not known until listen, so the lane binds
14
+ * ephemeral too; otherwise the lane takes the unified port + 1, the default
15
+ * every install has always had. Deriving +1 from an ephemeral 0 gave port 1,
16
+ * a privileged port, and a server that reported healthy with a dead download
17
+ * lane (stigmer#1089). The caller passes the port the unified listener will
18
+ * bind (`portOverride ?? grpcPort`), the input the OAuth redirect derivation
19
+ * reads (boot/oauth-redirect-uri.ts).
20
+ *
21
+ * The serve URL: a configured value always wins; unset, it is the lane's own
22
+ * origin on localhost, read from the port the lane actually bound. The
23
+ * composition binds the lane before the unified port opens, so no request can
24
+ * mint a URL before that port exists; a read before the bind is a
25
+ * construction-order fault and throws rather than minting a URL for a port
26
+ * nothing listens on.
27
+ *
28
+ * A lane that cannot bind fails the boot, beside the storage probe it
29
+ * belongs with; artifactLaneBindError is the copy the operator reads, naming
30
+ * the address, the cause and the setting that moves the lane.
31
+ *
32
+ * Proven by __tests__/artifact-lane.test.ts and the lane cases of
33
+ * __tests__/compose.test.ts.
34
+ */
35
+
36
+ export interface ArtifactLanePortInputs {
37
+ /** `ARTIFACT_HTTP_PORT` as loaded; undefined when unset. */
38
+ readonly configured: number | undefined;
39
+ /** The port the unified listener binds; 0 means "not known until listen". */
40
+ readonly unifiedPort: number;
41
+ }
42
+
43
+ /** The port the artifact file server binds; 0 asks the OS for an ephemeral one. */
44
+ export function resolveArtifactLanePort(
45
+ inputs: ArtifactLanePortInputs,
46
+ ): number {
47
+ if (inputs.configured !== undefined) return inputs.configured;
48
+ if (inputs.unifiedPort === 0) return 0;
49
+ return inputs.unifiedPort + 1;
50
+ }
51
+
52
+ /**
53
+ * The serve URL local artifact storage mints download URLs under: the
54
+ * configured value as given, or a resolver over the lane's bound port.
55
+ */
56
+ export function resolveArtifactServeUrl(
57
+ configured: string,
58
+ boundPort: () => number | undefined,
59
+ ): string | (() => string) {
60
+ if (configured !== "") return configured;
61
+ return () => {
62
+ const port = boundPort();
63
+ if (port === undefined) {
64
+ throw new Error(
65
+ "artifact download URL requested before the artifact file server bound its port",
66
+ );
67
+ }
68
+ return `http://localhost:${port}`;
69
+ };
70
+ }
71
+
72
+ /**
73
+ * The boot failure for a lane that could not bind: the address, the socket
74
+ * error's code, and the setting that moves the lane — named as the value
75
+ * that failed when the operator set it, as the default's escape hatch when
76
+ * the port was derived.
77
+ */
78
+ export function artifactLaneBindError(
79
+ cause: unknown,
80
+ host: string,
81
+ port: number,
82
+ configured: boolean,
83
+ ): Error {
84
+ const code =
85
+ cause instanceof Error &&
86
+ typeof (cause as NodeJS.ErrnoException).code === "string"
87
+ ? ` (${(cause as NodeJS.ErrnoException).code})`
88
+ : "";
89
+ const remedy = configured
90
+ ? `ARTIFACT_HTTP_PORT=${port} cannot be bound; set it to a free port`
91
+ : "set ARTIFACT_HTTP_PORT to a free port (unset, the lane takes the unified port + 1)";
92
+ return new Error(
93
+ `the artifact file server could not bind '${host}:${port}'${code}: ${remedy}`,
94
+ { cause },
95
+ );
96
+ }