@stigmer/server 3.25.0 → 3.27.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.
- package/dist/authorization/evaluator.d.ts +4 -1
- package/dist/authorization/evaluator.d.ts.map +1 -1
- package/dist/authorization/evaluator.js +8 -0
- package/dist/authorization/evaluator.js.map +1 -1
- package/dist/authorization/model/agent.d.ts.map +1 -1
- package/dist/authorization/model/agent.js +4 -2
- package/dist/authorization/model/agent.js.map +1 -1
- package/dist/authorization/model/agent_channel.d.ts.map +1 -1
- package/dist/authorization/model/agent_channel.js +5 -4
- package/dist/authorization/model/agent_channel.js.map +1 -1
- package/dist/authorization/model/agent_instance.d.ts.map +1 -1
- package/dist/authorization/model/agent_instance.js +3 -2
- package/dist/authorization/model/agent_instance.js.map +1 -1
- package/dist/authorization/model/mcp_server.d.ts.map +1 -1
- package/dist/authorization/model/mcp_server.js +3 -2
- package/dist/authorization/model/mcp_server.js.map +1 -1
- package/dist/authorization/model/organization.d.ts.map +1 -1
- package/dist/authorization/model/organization.js +3 -0
- package/dist/authorization/model/organization.js.map +1 -1
- package/dist/authorization/model/plugin.d.ts.map +1 -1
- package/dist/authorization/model/plugin.js +4 -2
- package/dist/authorization/model/plugin.js.map +1 -1
- package/dist/authorization/model/rewrite.d.ts +25 -7
- package/dist/authorization/model/rewrite.d.ts.map +1 -1
- package/dist/authorization/model/rewrite.js +40 -1
- package/dist/authorization/model/rewrite.js.map +1 -1
- package/dist/authorization/model/schedule.d.ts.map +1 -1
- package/dist/authorization/model/schedule.js +8 -3
- package/dist/authorization/model/schedule.js.map +1 -1
- package/dist/authorization/model/skill.d.ts.map +1 -1
- package/dist/authorization/model/skill.js +3 -2
- package/dist/authorization/model/skill.js.map +1 -1
- package/dist/authorization/model/workflow.d.ts.map +1 -1
- package/dist/authorization/model/workflow.js +3 -2
- package/dist/authorization/model/workflow.js.map +1 -1
- package/dist/authorization/model/workflow_execution.d.ts.map +1 -1
- package/dist/authorization/model/workflow_execution.js +4 -3
- package/dist/authorization/model/workflow_execution.js.map +1 -1
- package/dist/authorization/model/workflow_instance.d.ts.map +1 -1
- package/dist/authorization/model/workflow_instance.js +5 -1
- package/dist/authorization/model/workflow_instance.js.map +1 -1
- package/dist/boot/compose.d.ts.map +1 -1
- package/dist/boot/compose.js +13 -8
- package/dist/boot/compose.js.map +1 -1
- package/dist/boot/list-indexes.d.ts +3 -0
- package/dist/boot/list-indexes.d.ts.map +1 -0
- package/dist/boot/list-indexes.js +24 -0
- package/dist/boot/list-indexes.js.map +1 -0
- package/dist/domain/agentexecution/controller.d.ts.map +1 -1
- package/dist/domain/agentexecution/controller.js +6 -8
- package/dist/domain/agentexecution/controller.js.map +1 -1
- package/dist/domain/agentexecution/list-index.d.ts +2 -0
- package/dist/domain/agentexecution/list-index.d.ts.map +1 -0
- package/dist/domain/agentexecution/list-index.js +23 -0
- package/dist/domain/agentexecution/list-index.js.map +1 -0
- package/dist/domain/agentexecution/steps.d.ts +17 -33
- package/dist/domain/agentexecution/steps.d.ts.map +1 -1
- package/dist/domain/agentexecution/steps.js +94 -95
- package/dist/domain/agentexecution/steps.js.map +1 -1
- package/dist/domain/agentexecution/usage.d.ts.map +1 -1
- package/dist/domain/agentexecution/usage.js +37 -14
- package/dist/domain/agentexecution/usage.js.map +1 -1
- package/dist/domain/artifact/controller.d.ts.map +1 -1
- package/dist/domain/artifact/controller.js +27 -15
- package/dist/domain/artifact/controller.js.map +1 -1
- package/dist/domain/artifact/list-index.d.ts +2 -0
- package/dist/domain/artifact/list-index.d.ts.map +1 -0
- package/dist/domain/artifact/list-index.js +21 -0
- package/dist/domain/artifact/list-index.js.map +1 -0
- package/dist/domain/iampolicy/access-lists.d.ts +5 -3
- package/dist/domain/iampolicy/access-lists.d.ts.map +1 -1
- package/dist/domain/iampolicy/access-lists.js +72 -19
- package/dist/domain/iampolicy/access-lists.js.map +1 -1
- package/dist/domain/iampolicy/constants.d.ts +32 -6
- package/dist/domain/iampolicy/constants.d.ts.map +1 -1
- package/dist/domain/iampolicy/constants.js +41 -6
- package/dist/domain/iampolicy/constants.js.map +1 -1
- package/dist/domain/iampolicy/controller.d.ts +6 -0
- package/dist/domain/iampolicy/controller.d.ts.map +1 -1
- package/dist/domain/iampolicy/controller.js +16 -7
- package/dist/domain/iampolicy/controller.js.map +1 -1
- package/dist/domain/iampolicy/resource-store.d.ts.map +1 -1
- package/dist/domain/iampolicy/resource-store.js +2 -6
- package/dist/domain/iampolicy/resource-store.js.map +1 -1
- package/dist/domain/iampolicy/steps.d.ts.map +1 -1
- package/dist/domain/iampolicy/steps.js +49 -7
- package/dist/domain/iampolicy/steps.js.map +1 -1
- package/dist/domain/session/list-index.d.ts +4 -0
- package/dist/domain/session/list-index.d.ts.map +1 -0
- package/dist/domain/session/list-index.js +26 -0
- package/dist/domain/session/list-index.js.map +1 -0
- package/dist/domain/session/steps.d.ts +7 -8
- package/dist/domain/session/steps.d.ts.map +1 -1
- package/dist/domain/session/steps.js +76 -78
- package/dist/domain/session/steps.js.map +1 -1
- package/dist/domain/workflow/registry/data/task-kind-registry.json +1 -0
- package/dist/domain/workflowexecution/controller.d.ts.map +1 -1
- package/dist/domain/workflowexecution/controller.js +53 -55
- package/dist/domain/workflowexecution/controller.js.map +1 -1
- package/dist/domain/workflowexecution/execution-filter.d.ts +4 -0
- package/dist/domain/workflowexecution/execution-filter.d.ts.map +1 -1
- package/dist/domain/workflowexecution/execution-filter.js +11 -5
- package/dist/domain/workflowexecution/execution-filter.js.map +1 -1
- package/dist/domain/workflowexecution/get-execution-summary.d.ts.map +1 -1
- package/dist/domain/workflowexecution/get-execution-summary.js +23 -17
- package/dist/domain/workflowexecution/get-execution-summary.js.map +1 -1
- package/dist/domain/workflowexecution/list-index.d.ts +2 -0
- package/dist/domain/workflowexecution/list-index.d.ts.map +1 -0
- package/dist/domain/workflowexecution/list-index.js +21 -0
- package/dist/domain/workflowexecution/list-index.js.map +1 -0
- package/dist/domain/workflowexecution/list-pending-approvals.d.ts.map +1 -1
- package/dist/domain/workflowexecution/list-pending-approvals.js +69 -32
- package/dist/domain/workflowexecution/list-pending-approvals.js.map +1 -1
- package/dist/domain/workflowexecution/queries.d.ts +30 -4
- package/dist/domain/workflowexecution/queries.d.ts.map +1 -1
- package/dist/domain/workflowexecution/queries.js +76 -22
- package/dist/domain/workflowexecution/queries.js.map +1 -1
- package/dist/extensions/drivers.d.ts +12 -0
- package/dist/extensions/drivers.d.ts.map +1 -1
- package/dist/extensions/gate-slots.d.ts +10 -1
- package/dist/extensions/gate-slots.d.ts.map +1 -1
- package/dist/extensions/gate-slots.js +1 -0
- package/dist/extensions/gate-slots.js.map +1 -1
- package/dist/extensions/list-read-scope.d.ts +9 -12
- package/dist/extensions/list-read-scope.d.ts.map +1 -1
- package/dist/extensions/list-read-scope.js.map +1 -1
- package/dist/extensions/principal-display.d.ts +34 -0
- package/dist/extensions/principal-display.d.ts.map +1 -0
- package/dist/extensions/principal-display.js +2 -0
- package/dist/extensions/principal-display.js.map +1 -0
- package/dist/extensions/registry.d.ts +6 -0
- package/dist/extensions/registry.d.ts.map +1 -1
- package/dist/extensions/registry.js +10 -0
- package/dist/extensions/registry.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/pipeline/apiresource-meta.d.ts +10 -0
- package/dist/pipeline/apiresource-meta.d.ts.map +1 -1
- package/dist/pipeline/apiresource-meta.js +16 -0
- package/dist/pipeline/apiresource-meta.js.map +1 -1
- package/dist/pipeline/steps/list-page.d.ts +63 -0
- package/dist/pipeline/steps/list-page.d.ts.map +1 -0
- package/dist/pipeline/steps/list-page.js +187 -0
- package/dist/pipeline/steps/list-page.js.map +1 -0
- package/dist/query/activity/handler.d.ts +6 -7
- package/dist/query/activity/handler.d.ts.map +1 -1
- package/dist/query/activity/handler.js +42 -59
- package/dist/query/activity/handler.js.map +1 -1
- package/dist/store/interface.d.ts +29 -0
- package/dist/store/interface.d.ts.map +1 -1
- package/dist/store/interface.js.map +1 -1
- package/dist/store/list-index.d.ts +123 -0
- package/dist/store/list-index.d.ts.map +1 -0
- package/dist/store/list-index.js +277 -0
- package/dist/store/list-index.js.map +1 -0
- package/dist/store/logger.d.ts +3 -0
- package/dist/store/logger.d.ts.map +1 -1
- package/dist/store/logger.js +5 -1
- package/dist/store/logger.js.map +1 -1
- package/dist/store/postgres/migrations.d.ts +10 -6
- package/dist/store/postgres/migrations.d.ts.map +1 -1
- package/dist/store/postgres/migrations.js +64 -6
- package/dist/store/postgres/migrations.js.map +1 -1
- package/dist/store/postgres/store.d.ts +26 -5
- package/dist/store/postgres/store.d.ts.map +1 -1
- package/dist/store/postgres/store.js +377 -22
- package/dist/store/postgres/store.js.map +1 -1
- package/dist/store/sqlite/migrations.d.ts +11 -5
- package/dist/store/sqlite/migrations.d.ts.map +1 -1
- package/dist/store/sqlite/migrations.js +50 -4
- package/dist/store/sqlite/migrations.js.map +1 -1
- package/dist/store/sqlite/store.d.ts +18 -3
- package/dist/store/sqlite/store.d.ts.map +1 -1
- package/dist/store/sqlite/store.js +321 -15
- package/dist/store/sqlite/store.js.map +1 -1
- package/package.json +6 -6
- package/src/authorization/__tests__/drivers.ts +6 -1
- package/src/authorization/__tests__/evaluator.test.ts +127 -2
- package/src/authorization/evaluator.ts +12 -1
- package/src/authorization/model/__tests__/registry.test.ts +64 -2
- package/src/authorization/model/agent.ts +4 -1
- package/src/authorization/model/agent_channel.ts +11 -3
- package/src/authorization/model/agent_instance.ts +3 -1
- package/src/authorization/model/mcp_server.ts +3 -1
- package/src/authorization/model/organization.ts +3 -0
- package/src/authorization/model/plugin.ts +4 -1
- package/src/authorization/model/rewrite.ts +65 -8
- package/src/authorization/model/schedule.ts +11 -2
- package/src/authorization/model/skill.ts +3 -1
- package/src/authorization/model/workflow.ts +3 -1
- package/src/authorization/model/workflow_execution.ts +4 -2
- package/src/authorization/model/workflow_instance.ts +5 -0
- package/src/boot/__tests__/list-indexes.test.ts +65 -0
- package/src/boot/compose.ts +25 -13
- package/src/boot/list-indexes.ts +25 -0
- package/src/domain/agentexecution/__tests__/agentexecution.test.ts +85 -3
- package/src/domain/agentexecution/controller.ts +8 -10
- package/src/domain/agentexecution/list-index.ts +24 -0
- package/src/domain/agentexecution/steps.ts +118 -115
- package/src/domain/agentexecution/usage.ts +38 -17
- package/src/domain/artifact/controller.ts +28 -18
- package/src/domain/artifact/list-index.ts +22 -0
- package/src/domain/iampolicy/__tests__/access-lists.test.ts +120 -6
- package/src/domain/iampolicy/__tests__/constants.test.ts +26 -4
- package/src/domain/iampolicy/__tests__/controller.test.ts +55 -1
- package/src/domain/iampolicy/__tests__/grant-path.test.ts +3 -3
- package/src/domain/iampolicy/__tests__/steps.test.ts +123 -1
- package/src/domain/iampolicy/access-lists.ts +98 -23
- package/src/domain/iampolicy/constants.ts +50 -6
- package/src/domain/iampolicy/controller.ts +30 -7
- package/src/domain/iampolicy/resource-store.ts +3 -6
- package/src/domain/iampolicy/steps.ts +89 -7
- package/src/domain/session/list-index.ts +28 -0
- package/src/domain/session/steps.ts +108 -123
- package/src/domain/workflow/registry/data/task-kind-registry.json +1 -0
- package/src/domain/workflowexecution/__tests__/list-read-scope-summaries.test.ts +155 -99
- package/src/domain/workflowexecution/__tests__/workflowexecution.test.ts +193 -31
- package/src/domain/workflowexecution/controller.ts +54 -82
- package/src/domain/workflowexecution/execution-filter.ts +20 -7
- package/src/domain/workflowexecution/get-execution-summary.ts +31 -24
- package/src/domain/workflowexecution/list-index.ts +22 -0
- package/src/domain/workflowexecution/list-pending-approvals.ts +82 -25
- package/src/domain/workflowexecution/queries.ts +134 -23
- package/src/extensions/__tests__/iam-policy-points.test.ts +30 -0
- package/src/extensions/__tests__/registry.test.ts +3 -0
- package/src/extensions/drivers.ts +12 -0
- package/src/extensions/gate-slots.ts +10 -0
- package/src/extensions/list-read-scope.ts +9 -12
- package/src/extensions/principal-display.ts +37 -0
- package/src/extensions/registry.ts +19 -0
- package/src/index.ts +19 -1
- package/src/pipeline/apiresource-meta.ts +21 -0
- package/src/pipeline/steps/__tests__/list-page.test.ts +186 -0
- package/src/pipeline/steps/list-page.ts +284 -0
- package/src/query/activity/handler.ts +57 -78
- package/src/store/README.md +51 -32
- package/src/store/__tests__/list-index.measure.test.ts +293 -0
- package/src/store/__tests__/list-index.test.ts +328 -0
- package/src/store/__tests__/store-contract.ts +346 -3
- package/src/store/interface.ts +39 -0
- package/src/store/list-index.ts +465 -0
- package/src/store/logger.ts +8 -1
- package/src/store/postgres/__tests__/list-index-repair.test.ts +133 -0
- package/src/store/postgres/__tests__/migrations.test.ts +27 -19
- package/src/store/postgres/__tests__/store-contract.test.ts +50 -3
- package/src/store/postgres/migrations.ts +69 -7
- package/src/store/postgres/store.ts +485 -29
- package/src/store/sqlite/__tests__/migrations.test.ts +51 -7
- package/src/store/sqlite/__tests__/store-contract.test.ts +70 -16
- package/src/store/sqlite/__tests__/support.ts +12 -3
- package/src/store/sqlite/migrations.ts +57 -6
- package/src/store/sqlite/store.ts +462 -21
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The relation-rewrite vocabulary the model declarations are written in —
|
|
3
|
-
* the
|
|
4
|
-
*
|
|
3
|
+
* the forms an OpenFGA `define` line takes in the cloud's `.fga` files,
|
|
4
|
+
* and nothing more:
|
|
5
5
|
*
|
|
6
6
|
* define organization: [organization] → direct(objectOf("organization"))
|
|
7
7
|
* define owner: [identity_account] or admin from organization
|
|
@@ -10,8 +10,20 @@
|
|
|
10
10
|
* or owner or platform_viewer
|
|
11
11
|
* → union(direct(objectOf(...), usersetOf(...), usersetOf(...)),
|
|
12
12
|
* computed("owner"), computed("platform_viewer"))
|
|
13
|
+
* define member: [identity_account] and viewer from organization
|
|
14
|
+
* → intersection(direct(objectOf("identity_account")), from("viewer", "organization"))
|
|
13
15
|
* define can_view: viewer → computed("viewer")
|
|
14
16
|
*
|
|
17
|
+
* `and` exists for one shape: a relation that is true only while another
|
|
18
|
+
* holds too — a team's member is a person granted the role AND still one
|
|
19
|
+
* of the organization's viewers, so leaving the organization ends every
|
|
20
|
+
* team-derived grant with no cleanup. A line is either a union or an
|
|
21
|
+
* intersection of leaves, never both: the DSL would need parentheses to
|
|
22
|
+
* mix them, the cloud's reader refuses parentheses, and `declareKind`
|
|
23
|
+
* refuses the nesting that would need them. `but not` has no node; the day
|
|
24
|
+
* a line needs it, the transcript cannot be written and the gap is a
|
|
25
|
+
* design act.
|
|
26
|
+
*
|
|
15
27
|
* A subject type is an object type or a userset; the model admits no
|
|
16
28
|
* wildcard (`type:*`) and declares no condition, so neither has a form
|
|
17
29
|
* here. The day a line needs one, the transcript cannot be written and
|
|
@@ -21,10 +33,7 @@
|
|
|
21
33
|
* file: the same relations, in the file's order, each line rewritten in
|
|
22
34
|
* these builders so a reader with the model open sees the same text, and
|
|
23
35
|
* so a structural compare against the file (the cloud's drift test at
|
|
24
|
-
* the re-pin) needs no translation table.
|
|
25
|
-
* (`and`, `but not`) do not appear in the model and have no node here;
|
|
26
|
-
* the day they do, the transcript cannot be written and the gap is a
|
|
27
|
-
* design act, not a silent approximation.
|
|
36
|
+
* the re-pin) needs no translation table.
|
|
28
37
|
*
|
|
29
38
|
* The direct form carries its type restrictions (`[identity_account,
|
|
30
39
|
* organization#viewer]`) because the evaluator ENFORCES them: a tuple
|
|
@@ -72,7 +81,12 @@ export type Rewrite =
|
|
|
72
81
|
readonly tupleset: string;
|
|
73
82
|
}
|
|
74
83
|
/** `a or b or c` — the first true member wins, in the line's order. */
|
|
75
|
-
| { readonly node: "union"; readonly members: ReadonlyArray<Rewrite> }
|
|
84
|
+
| { readonly node: "union"; readonly members: ReadonlyArray<Rewrite> }
|
|
85
|
+
/** `a and b` — true only when every member is, in the line's order; the first false one ends it. */
|
|
86
|
+
| {
|
|
87
|
+
readonly node: "intersection";
|
|
88
|
+
readonly members: ReadonlyArray<Rewrite>;
|
|
89
|
+
};
|
|
76
90
|
|
|
77
91
|
// ---------------------------------------------------------------------------
|
|
78
92
|
// Builders — named so a transcript reads like its `.fga` line.
|
|
@@ -102,6 +116,10 @@ export function union(...members: ReadonlyArray<Rewrite>): Rewrite {
|
|
|
102
116
|
return { node: "union", members };
|
|
103
117
|
}
|
|
104
118
|
|
|
119
|
+
export function intersection(...members: ReadonlyArray<Rewrite>): Rewrite {
|
|
120
|
+
return { node: "intersection", members };
|
|
121
|
+
}
|
|
122
|
+
|
|
105
123
|
// ---------------------------------------------------------------------------
|
|
106
124
|
// A kind's declaration.
|
|
107
125
|
// ---------------------------------------------------------------------------
|
|
@@ -148,7 +166,10 @@ export interface KindDeclarationInput {
|
|
|
148
166
|
* module load: a relation declared twice, a `computed` naming a relation
|
|
149
167
|
* the kind does not define, a `from` whose tupleset the kind does not
|
|
150
168
|
* define (its `relation` is the PARENT's and is resolved at evaluation),
|
|
151
|
-
* a derived rule for a relation the transcript does not declare
|
|
169
|
+
* a derived rule for a relation the transcript does not declare, and a
|
|
170
|
+
* line no `.fga` file could hold without parentheses (a union inside an
|
|
171
|
+
* intersection or the reverse, an operator nested in itself, an operator
|
|
172
|
+
* over fewer than two members).
|
|
152
173
|
*/
|
|
153
174
|
export function declareKind(input: KindDeclarationInput): KindDeclaration {
|
|
154
175
|
const type = kindEnumName(input.kind);
|
|
@@ -157,6 +178,10 @@ export function declareKind(input: KindDeclarationInput): KindDeclaration {
|
|
|
157
178
|
if (relations.has(name)) {
|
|
158
179
|
throw new Error(`${input.source}: relation '${name}' is declared twice`);
|
|
159
180
|
}
|
|
181
|
+
const shape = unwritableShape(rewrite);
|
|
182
|
+
if (shape !== undefined) {
|
|
183
|
+
throw new Error(`${input.source}: '${name}' ${shape}`);
|
|
184
|
+
}
|
|
160
185
|
relations.set(name, rewrite);
|
|
161
186
|
}
|
|
162
187
|
for (const [name, rewrite] of relations) {
|
|
@@ -199,6 +224,7 @@ function localReferences(
|
|
|
199
224
|
case "from":
|
|
200
225
|
return [{ role: "tupleset", relation: rewrite.tupleset }];
|
|
201
226
|
case "union":
|
|
227
|
+
case "intersection":
|
|
202
228
|
return rewrite.members.flatMap(localReferences);
|
|
203
229
|
default: {
|
|
204
230
|
const exhaustive: never = rewrite;
|
|
@@ -206,3 +232,34 @@ function localReferences(
|
|
|
206
232
|
}
|
|
207
233
|
}
|
|
208
234
|
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Why a line could not be written in a `.fga` file without parentheses,
|
|
238
|
+
* or undefined when it can: an operator's members are leaves (a direct
|
|
239
|
+
* list, a sibling relation, a tuple-to-userset), and there are at least
|
|
240
|
+
* two of them.
|
|
241
|
+
*/
|
|
242
|
+
function unwritableShape(rewrite: Rewrite): string | undefined {
|
|
243
|
+
switch (rewrite.node) {
|
|
244
|
+
case "this":
|
|
245
|
+
case "computed":
|
|
246
|
+
case "from":
|
|
247
|
+
return undefined;
|
|
248
|
+
case "union":
|
|
249
|
+
case "intersection": {
|
|
250
|
+
if (rewrite.members.length < 2) {
|
|
251
|
+
return `is ${rewrite.node === "union" ? "an or" : "an and"} over fewer than two members`;
|
|
252
|
+
}
|
|
253
|
+
const nested = rewrite.members.find(
|
|
254
|
+
(member) => member.node === "union" || member.node === "intersection",
|
|
255
|
+
);
|
|
256
|
+
return nested === undefined
|
|
257
|
+
? undefined
|
|
258
|
+
: `nests ${nested.node === "union" ? "or" : "and"} inside ${rewrite.node === "union" ? "or" : "and"}, which a .fga line cannot say without parentheses`;
|
|
259
|
+
}
|
|
260
|
+
default: {
|
|
261
|
+
const exhaustive: never = rewrite;
|
|
262
|
+
throw new Error(`unknown rewrite node: ${JSON.stringify(exhaustive)}`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
* definition, admin-owned like a blueprint and viewable by its owners and
|
|
4
4
|
* explicit grantees. The sessions its fires create name it back through
|
|
5
5
|
* `session#schedule`, a link the cloud writes and this edition does not
|
|
6
|
-
* (session.ts); the schedule's own relations are unaffected.
|
|
6
|
+
* (session.ts); the schedule's own relations are unaffected. An explicit
|
|
7
|
+
* grant may name an Enterprise team (`team#member`), which no open-source
|
|
8
|
+
* tuple ever does.
|
|
7
9
|
*/
|
|
8
10
|
import { ScheduleSchema } from "@stigmer/protos/ai/stigmer/agentic/schedule/v1/api_pb";
|
|
9
11
|
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
@@ -15,6 +17,7 @@ import {
|
|
|
15
17
|
from,
|
|
16
18
|
objectOf,
|
|
17
19
|
union,
|
|
20
|
+
usersetOf,
|
|
18
21
|
} from "./rewrite.js";
|
|
19
22
|
|
|
20
23
|
export const scheduleDeclaration = declareKind({
|
|
@@ -30,7 +33,13 @@ export const scheduleDeclaration = declareKind({
|
|
|
30
33
|
from("admin", "organization"),
|
|
31
34
|
),
|
|
32
35
|
],
|
|
33
|
-
[
|
|
36
|
+
[
|
|
37
|
+
"viewer",
|
|
38
|
+
union(
|
|
39
|
+
direct(objectOf("identity_account"), usersetOf("team", "member")),
|
|
40
|
+
computed("owner"),
|
|
41
|
+
),
|
|
42
|
+
],
|
|
34
43
|
["can_view", computed("viewer")],
|
|
35
44
|
["can_edit", computed("owner")],
|
|
36
45
|
["can_delete", computed("owner")],
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* `can_use` and `can_clone` rather than `can_execute`. Only `can_view`,
|
|
4
4
|
* `can_edit`, `can_delete`, `can_grant_access` and `can_view_access` are
|
|
5
5
|
* in the wire's IamPermission vocabulary; the others are the model's and
|
|
6
|
-
* are carried so the transcript is the file.
|
|
6
|
+
* are carried so the transcript is the file. `team#member` on `viewer` is
|
|
7
|
+
* the Enterprise team grant, which no open-source tuple ever names.
|
|
7
8
|
*/
|
|
8
9
|
import { SkillSchema } from "@stigmer/protos/ai/stigmer/agentic/skill/v1/api_pb";
|
|
9
10
|
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
@@ -42,6 +43,7 @@ export const skillDeclaration = declareKind({
|
|
|
42
43
|
objectOf("identity_account"),
|
|
43
44
|
usersetOf("organization", "member"),
|
|
44
45
|
usersetOf("organization", "viewer"),
|
|
46
|
+
usersetOf("team", "member"),
|
|
45
47
|
),
|
|
46
48
|
computed("owner"),
|
|
47
49
|
computed("platform_viewer"),
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* without `can_create_instance` (a workflow instance is created through
|
|
4
4
|
* the workflow-instance lane, gated by the organization). Kept as its own
|
|
5
5
|
* transcript, not a shared "blueprint" helper, so the file-by-file drift
|
|
6
|
-
* compare sees exactly this file's lines.
|
|
6
|
+
* compare sees exactly this file's lines. `team#member` on `viewer` is the
|
|
7
|
+
* Enterprise team grant, which no open-source tuple ever names.
|
|
7
8
|
*/
|
|
8
9
|
import { WorkflowSchema } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/api_pb";
|
|
9
10
|
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
@@ -42,6 +43,7 @@ export const workflowDeclaration = declareKind({
|
|
|
42
43
|
objectOf("identity_account"),
|
|
43
44
|
usersetOf("organization", "member"),
|
|
44
45
|
usersetOf("organization", "viewer"),
|
|
46
|
+
usersetOf("team", "member"),
|
|
45
47
|
),
|
|
46
48
|
computed("owner"),
|
|
47
49
|
computed("platform_viewer"),
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* audience through `execution_viewer from workflow_instance`, standing on
|
|
7
7
|
* the `workflow_instance` link the derivation writes from
|
|
8
8
|
* `spec.workflow_instance_id`. No `can_delete`: run history outlives its
|
|
9
|
-
* instance by design.
|
|
9
|
+
* instance by design. The per-run grant may name an Enterprise team
|
|
10
|
+
* (`team#member`), which no open-source tuple ever does.
|
|
10
11
|
*/
|
|
11
12
|
import { WorkflowExecutionSchema } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/api_pb";
|
|
12
13
|
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
@@ -18,6 +19,7 @@ import {
|
|
|
18
19
|
from,
|
|
19
20
|
objectOf,
|
|
20
21
|
union,
|
|
22
|
+
usersetOf,
|
|
21
23
|
} from "./rewrite.js";
|
|
22
24
|
|
|
23
25
|
export const workflowExecutionDeclaration = declareKind({
|
|
@@ -31,7 +33,7 @@ export const workflowExecutionDeclaration = declareKind({
|
|
|
31
33
|
[
|
|
32
34
|
"viewer",
|
|
33
35
|
union(
|
|
34
|
-
direct(objectOf("identity_account")),
|
|
36
|
+
direct(objectOf("identity_account"), usersetOf("team", "member")),
|
|
35
37
|
computed("owner"),
|
|
36
38
|
from("execution_viewer", "workflow_instance"),
|
|
37
39
|
),
|
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
* admits — and `private` or unset derives nothing, so each run stays its
|
|
15
15
|
* triggerer's. The cloud has no writer for this tuple today; this
|
|
16
16
|
* edition enforces the field as the contract states it.
|
|
17
|
+
*
|
|
18
|
+
* `team#member` on `viewer` is the Enterprise team grant, which no
|
|
19
|
+
* open-source tuple ever names; `execution_viewer` is not a grantable role
|
|
20
|
+
* and admits no team.
|
|
17
21
|
*/
|
|
18
22
|
import { isMessage } from "@bufbuild/protobuf";
|
|
19
23
|
|
|
@@ -85,6 +89,7 @@ export const workflowInstanceDeclaration = declareKind({
|
|
|
85
89
|
objectOf("identity_account"),
|
|
86
90
|
usersetOf("organization", "member"),
|
|
87
91
|
usersetOf("organization", "viewer"),
|
|
92
|
+
usersetOf("team", "member"),
|
|
88
93
|
),
|
|
89
94
|
computed("owner"),
|
|
90
95
|
from("viewer", "default_of"),
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pins the list-indexed surface (list-indexes.ts). The table below is
|
|
3
|
+
* every declaration's keys against its revision: a store trusts a row's
|
|
4
|
+
* facts only under the revision they were derived with, so a change to a
|
|
5
|
+
* declaration's keys without a new revision would read rows written under
|
|
6
|
+
* the old keys as if they carried the new ones. Changing a declaration
|
|
7
|
+
* therefore fails here until its revision is bumped and this table is
|
|
8
|
+
* updated in the same change.
|
|
9
|
+
*/
|
|
10
|
+
import { describe, expect, it } from "vitest";
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
ListIndexRegistry,
|
|
14
|
+
listIndexFingerprint,
|
|
15
|
+
} from "../../store/list-index.js";
|
|
16
|
+
import { LIST_INDEXES } from "../list-indexes.js";
|
|
17
|
+
|
|
18
|
+
const PINNED: Readonly<
|
|
19
|
+
Record<string, { revision: number; fingerprint: string }>
|
|
20
|
+
> = {
|
|
21
|
+
agent_execution: {
|
|
22
|
+
revision: 1,
|
|
23
|
+
fingerprint: "agent_execution{session=field:spec.session_id}",
|
|
24
|
+
},
|
|
25
|
+
artifact: {
|
|
26
|
+
revision: 1,
|
|
27
|
+
fingerprint:
|
|
28
|
+
"artifact{agent_execution=field:spec.source.agent_execution_id,workflow_execution=field:spec.source.workflow_execution_id}",
|
|
29
|
+
},
|
|
30
|
+
session: {
|
|
31
|
+
revision: 1,
|
|
32
|
+
fingerprint:
|
|
33
|
+
"session{agent_instance=field:spec.agent_instance_id,channel=label:stigmer.ai/channel-id}",
|
|
34
|
+
},
|
|
35
|
+
workflow_execution: {
|
|
36
|
+
revision: 1,
|
|
37
|
+
fingerprint:
|
|
38
|
+
"workflow_execution{workflow=field:spec.workflow_id,workflow_instance=field:spec.workflow_instance_id}",
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
describe("the list-indexed surface", () => {
|
|
43
|
+
it("opens as one registry: no kind declared twice", () => {
|
|
44
|
+
expect(() => new ListIndexRegistry(LIST_INDEXES)).not.toThrow();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("declares exactly the pinned kinds", () => {
|
|
48
|
+
const kinds = LIST_INDEXES.map(
|
|
49
|
+
(d) => listIndexFingerprint(d).split("{")[0],
|
|
50
|
+
);
|
|
51
|
+
expect(kinds.sort()).toEqual(Object.keys(PINNED).sort());
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it.each(LIST_INDEXES.map((d) => [listIndexFingerprint(d), d] as const))(
|
|
55
|
+
"%s carries the revision its keys are pinned to",
|
|
56
|
+
(fingerprint, declaration) => {
|
|
57
|
+
const kind = fingerprint.split("{")[0] ?? "";
|
|
58
|
+
const pinned = PINNED[kind];
|
|
59
|
+
expect(
|
|
60
|
+
{ revision: declaration.revision, fingerprint },
|
|
61
|
+
"a declaration's keys changed: bump its revision and update this table together",
|
|
62
|
+
).toEqual(pinned);
|
|
63
|
+
},
|
|
64
|
+
);
|
|
65
|
+
});
|
package/src/boot/compose.ts
CHANGED
|
@@ -213,6 +213,7 @@ import { registerExtensionServicesUnshadowed } from "./route-shadowing.js";
|
|
|
213
213
|
import type { ServerConfig } from "./config.js";
|
|
214
214
|
import { createInProcessClients } from "./inprocess.js";
|
|
215
215
|
import type { InProcessClients } from "./inprocess.js";
|
|
216
|
+
import { LIST_INDEXES } from "./list-indexes.js";
|
|
216
217
|
import type { Logger } from "./logger.js";
|
|
217
218
|
|
|
218
219
|
export interface ComposedServer {
|
|
@@ -336,20 +337,22 @@ export async function composeServer(
|
|
|
336
337
|
}
|
|
337
338
|
// Stage: storage — the driver selection seam (DD-010): DATABASE_URL
|
|
338
339
|
// present → Postgres (async connect + advisory-locked migrations), else
|
|
339
|
-
// sqlite on DB_PATH (
|
|
340
|
+
// sqlite on DB_PATH (its whole chain, incl. adopting a Go-created
|
|
340
341
|
// database — D2 §3 schema continuity). Postgres wins when both are set:
|
|
341
342
|
// DB_PATH always has a default value, so no other precedence could ever
|
|
342
|
-
// select Postgres (config.ts documents the contract).
|
|
343
|
-
//
|
|
344
|
-
//
|
|
345
|
-
//
|
|
346
|
-
//
|
|
343
|
+
// select Postgres (config.ts documents the contract). Either driver
|
|
344
|
+
// opens with the list indexes (list-indexes.ts) and reconciles them
|
|
345
|
+
// before it returns. A failure here is a loud boot throw, never a
|
|
346
|
+
// degraded server. The operator identity (#400) is installed by main.ts
|
|
347
|
+
// — once per PROCESS, before any writer exists — not here: composeServer
|
|
348
|
+
// is re-entrant for tests, the identity seam deliberately is not.
|
|
347
349
|
let store: Store;
|
|
350
|
+
const storeOptions = { listIndexes: LIST_INDEXES };
|
|
348
351
|
if (config.databaseUrl !== "") {
|
|
349
|
-
store = await PostgresStore.open(config.databaseUrl, logger);
|
|
352
|
+
store = await PostgresStore.open(config.databaseUrl, logger, storeOptions);
|
|
350
353
|
logger.info("storage driver selected", { driver: "postgres" });
|
|
351
354
|
} else {
|
|
352
|
-
store = SqliteStore.open(config.dbPath, logger);
|
|
355
|
+
store = SqliteStore.open(config.dbPath, logger, storeOptions);
|
|
353
356
|
logger.info("storage driver selected", {
|
|
354
357
|
driver: "sqlite",
|
|
355
358
|
dbPath: config.dbPath,
|
|
@@ -1020,7 +1023,10 @@ export async function composeServer(
|
|
|
1020
1023
|
reserve: (key, declaredSizeBytes) => {
|
|
1021
1024
|
const { ttlMs } = skillUploadSlots.reserve(key, declaredSizeBytes);
|
|
1022
1025
|
return {
|
|
1023
|
-
url: skillUploadUrl(
|
|
1026
|
+
url: skillUploadUrl(
|
|
1027
|
+
config.skillTransferBaseUrl,
|
|
1028
|
+
uploadRefOf(key),
|
|
1029
|
+
),
|
|
1024
1030
|
ttlMs,
|
|
1025
1031
|
};
|
|
1026
1032
|
},
|
|
@@ -1196,9 +1202,12 @@ export async function composeServer(
|
|
|
1196
1202
|
case "configured":
|
|
1197
1203
|
break;
|
|
1198
1204
|
case "derived":
|
|
1199
|
-
logger.info(
|
|
1200
|
-
|
|
1201
|
-
|
|
1205
|
+
logger.info(
|
|
1206
|
+
"STIGMER_OAUTH_REDIRECT_URI is not set — deriving the served console's callback for MCP server OAuth Connect",
|
|
1207
|
+
{
|
|
1208
|
+
redirectUri: oauthRedirect.uri,
|
|
1209
|
+
},
|
|
1210
|
+
);
|
|
1202
1211
|
break;
|
|
1203
1212
|
case "absent":
|
|
1204
1213
|
logger.warn(
|
|
@@ -1210,7 +1219,8 @@ export async function composeServer(
|
|
|
1210
1219
|
throw new Error(String(exhaustive));
|
|
1211
1220
|
}
|
|
1212
1221
|
}
|
|
1213
|
-
const oauthRedirectUri =
|
|
1222
|
+
const oauthRedirectUri =
|
|
1223
|
+
oauthRedirect.kind === "absent" ? "" : oauthRedirect.uri;
|
|
1214
1224
|
// The ONE fetch the McpServer domain dials user-supplied URLs with: the
|
|
1215
1225
|
// endpoint it probes at save time and the login server it reaches on
|
|
1216
1226
|
// Sign in. Every hop is judged under the edition's egress policy
|
|
@@ -1299,6 +1309,8 @@ export async function composeServer(
|
|
|
1299
1309
|
grantScope:
|
|
1300
1310
|
extensions.drivers.policyGrantScope ?? newOrganizationOnlyGrantScope(),
|
|
1301
1311
|
queries: extensions.drivers.authorizationQueries,
|
|
1312
|
+
principalDisplay: extensions.drivers.principalDisplay,
|
|
1313
|
+
gateSteps: extensions.gateSteps,
|
|
1302
1314
|
edition: extensions.edition,
|
|
1303
1315
|
logger,
|
|
1304
1316
|
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The whole list-indexed surface, one explicit list (the search
|
|
3
|
+
* registry's composition-root idiom, query/search/registry.ts; house rule:
|
|
4
|
+
* no import side effects). The store is opened with it
|
|
5
|
+
* (`StoreOpenOptions.listIndexes`), keeps a declared kind's facts on every
|
|
6
|
+
* write, and refuses a read through a declaration it was not opened with,
|
|
7
|
+
* so a lane that reads an index and this list cannot disagree silently.
|
|
8
|
+
*
|
|
9
|
+
* A kind joins when a lane needs to read one organization's or one
|
|
10
|
+
* parent's rows without decoding the whole kind; the kinds here were
|
|
11
|
+
* chosen on the hosted edition's row counts and sizes (2026-09-23: every
|
|
12
|
+
* other org-scoped kind held under a hundred rows and a hundred kilobytes).
|
|
13
|
+
*/
|
|
14
|
+
import { agentExecutionListIndex } from "../domain/agentexecution/list-index.js";
|
|
15
|
+
import { artifactListIndex } from "../domain/artifact/list-index.js";
|
|
16
|
+
import { sessionListIndex } from "../domain/session/list-index.js";
|
|
17
|
+
import { workflowExecutionListIndex } from "../domain/workflowexecution/list-index.js";
|
|
18
|
+
import type { ListIndexDeclaration } from "../store/list-index.js";
|
|
19
|
+
|
|
20
|
+
export const LIST_INDEXES: ReadonlyArray<ListIndexDeclaration> = [
|
|
21
|
+
agentExecutionListIndex,
|
|
22
|
+
artifactListIndex,
|
|
23
|
+
sessionListIndex,
|
|
24
|
+
workflowExecutionListIndex,
|
|
25
|
+
];
|
|
@@ -12,8 +12,9 @@
|
|
|
12
12
|
* byte-for-byte (sub-project DD-001: faithful-port + OSS issue);
|
|
13
13
|
* - getAgentUsageReport org scoping (oss#389) incl. the no-name-oracle
|
|
14
14
|
* rule (Go get_agent_usage_report_test.go case-for-case);
|
|
15
|
-
* - list/listBySession filter semantics over seeded rows
|
|
16
|
-
*
|
|
15
|
+
* - list/listBySession filter semantics over seeded rows; list's cursor
|
|
16
|
+
* pages (newest created first, one organization, a token refused on
|
|
17
|
+
* another request) and listBySession returning its parent whole;
|
|
17
18
|
* - update's status-clearing standard build and delete's audit-trail
|
|
18
19
|
* return, over the wire;
|
|
19
20
|
* - the populated getExecutionSummary arm (phase counts, active count,
|
|
@@ -28,6 +29,7 @@ import path from "node:path";
|
|
|
28
29
|
|
|
29
30
|
import { create, fromBinary } from "@bufbuild/protobuf";
|
|
30
31
|
import type { MessageInitShape } from "@bufbuild/protobuf";
|
|
32
|
+
import { timestampFromMs } from "@bufbuild/protobuf/wkt";
|
|
31
33
|
import { Code, ConnectError, createClient } from "@connectrpc/connect";
|
|
32
34
|
import type { Client, Transport } from "@connectrpc/connect";
|
|
33
35
|
import { createGrpcTransport } from "@connectrpc/connect-node";
|
|
@@ -146,6 +148,7 @@ function seedInput(overrides?: {
|
|
|
146
148
|
phase?: ExecutionPhase;
|
|
147
149
|
startedAt?: string;
|
|
148
150
|
completedAt?: string;
|
|
151
|
+
createdAtMs?: number;
|
|
149
152
|
}): MessageInitShape<typeof AgentExecutionSchema> & {
|
|
150
153
|
metadata: { id: string };
|
|
151
154
|
} {
|
|
@@ -170,6 +173,12 @@ function seedInput(overrides?: {
|
|
|
170
173
|
phase: overrides?.phase ?? ExecutionPhase.EXECUTION_COMPLETED,
|
|
171
174
|
startedAt: overrides?.startedAt ?? "",
|
|
172
175
|
completedAt: overrides?.completedAt ?? "",
|
|
176
|
+
audit:
|
|
177
|
+
overrides?.createdAtMs === undefined
|
|
178
|
+
? undefined
|
|
179
|
+
: {
|
|
180
|
+
specAudit: { createdAt: timestampFromMs(overrides.createdAtMs) },
|
|
181
|
+
},
|
|
173
182
|
},
|
|
174
183
|
};
|
|
175
184
|
}
|
|
@@ -322,7 +331,7 @@ describe("get / list / listBySession over the wire", () => {
|
|
|
322
331
|
expect(got.spec?.message).toBe("Say hello.");
|
|
323
332
|
});
|
|
324
333
|
|
|
325
|
-
it("list returns every row
|
|
334
|
+
it("list with no page_size returns every row whole (total_pages 1), filtered by phase on request", async () => {
|
|
326
335
|
const completed = await seed(
|
|
327
336
|
seedInput({ phase: ExecutionPhase.EXECUTION_COMPLETED }),
|
|
328
337
|
);
|
|
@@ -359,6 +368,79 @@ describe("get / list / listBySession over the wire", () => {
|
|
|
359
368
|
expect(ids).toContain(inSession);
|
|
360
369
|
expect(ids).not.toContain(outOfSession);
|
|
361
370
|
});
|
|
371
|
+
|
|
372
|
+
it("list pages one organization newest created first, with a token until the last page", async () => {
|
|
373
|
+
const org = "paging-org";
|
|
374
|
+
const base = Date.parse("2026-09-01T00:00:00Z");
|
|
375
|
+
const newestFirst: string[] = [];
|
|
376
|
+
for (let i = 0; i < 5; i++) {
|
|
377
|
+
newestFirst.unshift(
|
|
378
|
+
await seed(seedInput({ org, createdAtMs: base + i * 1000 })),
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
await seed(seedInput({ org: "other-org", createdAtMs: base + 10_000 }));
|
|
382
|
+
|
|
383
|
+
const walked: string[] = [];
|
|
384
|
+
let pageToken = "";
|
|
385
|
+
do {
|
|
386
|
+
const page = await query.list({ org, pageSize: 2, pageToken });
|
|
387
|
+
expect(page.entries.length).toBeLessThanOrEqual(2);
|
|
388
|
+
expect(page.totalPages).toBe(page.nextPageToken === "" ? 1 : 0);
|
|
389
|
+
walked.push(...page.entries.map((e) => e.metadata!.id));
|
|
390
|
+
pageToken = page.nextPageToken;
|
|
391
|
+
} while (pageToken !== "");
|
|
392
|
+
|
|
393
|
+
expect(walked).toEqual(newestFirst);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
it("list refuses a page_token issued for another request, and a negative page_size", async () => {
|
|
397
|
+
const org = "token-org";
|
|
398
|
+
await seed(
|
|
399
|
+
seedInput({ org, createdAtMs: Date.parse("2026-09-02T00:00:00Z") }),
|
|
400
|
+
);
|
|
401
|
+
await seed(
|
|
402
|
+
seedInput({ org, createdAtMs: Date.parse("2026-09-02T00:00:01Z") }),
|
|
403
|
+
);
|
|
404
|
+
const first = await query.list({ org, pageSize: 1 });
|
|
405
|
+
expect(first.nextPageToken).not.toBe("");
|
|
406
|
+
|
|
407
|
+
const replayed = await expectCode(
|
|
408
|
+
() =>
|
|
409
|
+
query.list({
|
|
410
|
+
org,
|
|
411
|
+
phase: ExecutionPhase.EXECUTION_FAILED,
|
|
412
|
+
pageSize: 1,
|
|
413
|
+
pageToken: first.nextPageToken,
|
|
414
|
+
}),
|
|
415
|
+
Code.InvalidArgument,
|
|
416
|
+
);
|
|
417
|
+
expect(replayed.rawMessage).toBe(
|
|
418
|
+
"page_token was issued for a different request",
|
|
419
|
+
);
|
|
420
|
+
await expectCode(
|
|
421
|
+
() => query.list({ org, pageToken: "not-a-token" }),
|
|
422
|
+
Code.InvalidArgument,
|
|
423
|
+
);
|
|
424
|
+
await expectCode(
|
|
425
|
+
() => query.list({ org, pageSize: -1 }),
|
|
426
|
+
Code.InvalidArgument,
|
|
427
|
+
);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it("listBySession returns the session's executions whole, whatever page_size says", async () => {
|
|
431
|
+
const ids = new Set<string>();
|
|
432
|
+
for (let i = 0; i < 3; i++) {
|
|
433
|
+
ids.add(await seed(seedInput({ sessionId: "ses_whole" })));
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const result = await query.listBySession({
|
|
437
|
+
sessionId: "ses_whole",
|
|
438
|
+
pageSize: 1,
|
|
439
|
+
});
|
|
440
|
+
expect(new Set(result.entries.map((e) => e.metadata!.id))).toEqual(ids);
|
|
441
|
+
expect(result.nextPageToken).toBe("");
|
|
442
|
+
expect(result.totalPages).toBe(1);
|
|
443
|
+
});
|
|
362
444
|
});
|
|
363
445
|
|
|
364
446
|
describe("update / delete over the wire", () => {
|
|
@@ -128,10 +128,8 @@ import { newValidateVisibilityStep } from "../../pipeline/steps/validate-visibil
|
|
|
128
128
|
import { newBuildNewStateStep } from "../../pipeline/steps/defaults.js";
|
|
129
129
|
import {
|
|
130
130
|
EXECUTION_LIST_KEY,
|
|
131
|
-
newApplyPhaseFilterStep,
|
|
132
131
|
newBuildExecutionListResponseStep,
|
|
133
|
-
|
|
134
|
-
newRestrictByReadScopeStep,
|
|
132
|
+
newQueryExecutionPageStep,
|
|
135
133
|
newQueryExecutionsBySessionStep,
|
|
136
134
|
newValidateListBySessionRequestStep,
|
|
137
135
|
newValidateListRequestStep,
|
|
@@ -527,9 +525,9 @@ async function get(
|
|
|
527
525
|
}
|
|
528
526
|
|
|
529
527
|
/**
|
|
530
|
-
* List — list.go:
|
|
531
|
-
*
|
|
532
|
-
*
|
|
528
|
+
* List — list.go: one page of the request's org (or every org), newest
|
|
529
|
+
* first, the optional phase filter and the read scope per batch
|
|
530
|
+
* (steps.ts, QueryExecutionPage).
|
|
533
531
|
*/
|
|
534
532
|
async function list(
|
|
535
533
|
deps: AgentExecutionControllerDeps,
|
|
@@ -553,16 +551,16 @@ async function list(
|
|
|
553
551
|
),
|
|
554
552
|
)
|
|
555
553
|
.addStep(newValidateListRequestStep())
|
|
556
|
-
.addStep(
|
|
557
|
-
|
|
558
|
-
|
|
554
|
+
.addStep(
|
|
555
|
+
newQueryExecutionPageStep(deps.store, deps.logger, deps.listReadScope),
|
|
556
|
+
)
|
|
559
557
|
.addStep(newBuildExecutionListResponseStep())
|
|
560
558
|
.build()
|
|
561
559
|
.execute(reqCtx);
|
|
562
560
|
return requireListResult(reqCtx.get(EXECUTION_LIST_KEY));
|
|
563
561
|
}
|
|
564
562
|
|
|
565
|
-
/** ListBySession — list_by_session.go:
|
|
563
|
+
/** ListBySession — list_by_session.go: the session's executions, whole, newest first. */
|
|
566
564
|
async function listBySession(
|
|
567
565
|
deps: AgentExecutionControllerDeps,
|
|
568
566
|
req: ListAgentExecutionsBySessionRequest,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The agent-execution list index (store/list-index.ts): the kind whose
|
|
3
|
+
* rows are the fattest in the store (every message and tool call of a run
|
|
4
|
+
* rides the row), so a lane that decodes the whole kind to keep one
|
|
5
|
+
* session's runs pays for every run on the platform. `session` is the key
|
|
6
|
+
* `listBySession` reads; the organization and the creation order are
|
|
7
|
+
* every declaration's.
|
|
8
|
+
*
|
|
9
|
+
* A change to `keys` bumps `revision` (boot/__tests__/list-indexes.test.ts
|
|
10
|
+
* pins the pair).
|
|
11
|
+
*/
|
|
12
|
+
import { AgentExecutionSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
13
|
+
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
14
|
+
|
|
15
|
+
import { declareListIndex, field } from "../../store/list-index.js";
|
|
16
|
+
|
|
17
|
+
export const agentExecutionListIndex = declareListIndex({
|
|
18
|
+
kind: ApiResourceKind.agent_execution,
|
|
19
|
+
schema: AgentExecutionSchema,
|
|
20
|
+
revision: 1,
|
|
21
|
+
keys: {
|
|
22
|
+
session: field("spec.session_id"),
|
|
23
|
+
},
|
|
24
|
+
});
|