@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,48 +1,159 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* listByWorkflow, getExecutionSummary, listPendingApprovals)
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* The workflowexecution read surfaces' shared reads (list,
|
|
3
|
+
* listByWorkflow, getExecutionSummary, listPendingApprovals), all through
|
|
4
|
+
* the list index (list-index.ts beside this file; the store's contract in
|
|
5
|
+
* store/interface.ts) — the request's org or workflow key narrows in the
|
|
6
|
+
* store, newest created first. Malformed rows are skipped rather than
|
|
7
|
+
* failing the read, Go's proto.Unmarshal-continue loop.
|
|
8
|
+
*
|
|
9
|
+
* The two lists share one reader: the default order pages
|
|
10
|
+
* (pipeline/steps/list-page.ts), with the lane's filters and then the read
|
|
11
|
+
* scope on each batch; another sort field sorts the whole matching set and
|
|
12
|
+
* returns its first page_size entries with no token, because only the
|
|
13
|
+
* creation order is an index order.
|
|
6
14
|
*/
|
|
7
|
-
import { fromBinary } from "@bufbuild/protobuf";
|
|
15
|
+
import { create, fromBinary } from "@bufbuild/protobuf";
|
|
8
16
|
|
|
9
17
|
import type { WorkflowExecution } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/api_pb";
|
|
10
18
|
import { WorkflowExecutionSchema } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/api_pb";
|
|
19
|
+
import {
|
|
20
|
+
ExecutionSortField,
|
|
21
|
+
WorkflowExecutionListSchema,
|
|
22
|
+
} from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/io_pb";
|
|
23
|
+
import type { WorkflowExecutionList } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/io_pb";
|
|
11
24
|
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
12
25
|
|
|
13
26
|
import type { Logger } from "../../boot/logger.js";
|
|
14
|
-
import {
|
|
27
|
+
import type { CallerIdentity } from "../../extensions/identity.js";
|
|
28
|
+
import type { ListReadScope } from "../../extensions/list-read-scope.js";
|
|
29
|
+
import { restrictListByReadScope } from "../../extensions/list-read-scope.js";
|
|
30
|
+
import { internalError, invalidArgumentError } from "../../pipeline/errors.js";
|
|
31
|
+
import {
|
|
32
|
+
LIST_PAGE_MAX_SIZE,
|
|
33
|
+
listPageFingerprint,
|
|
34
|
+
readListPage,
|
|
35
|
+
} from "../../pipeline/steps/list-page.js";
|
|
36
|
+
import type { ListPageRequest } from "../../pipeline/steps/list-page.js";
|
|
15
37
|
import type { Store } from "../../store/interface.js";
|
|
38
|
+
import type { ListIndexQuery, ListIndexRow } from "../../store/list-index.js";
|
|
39
|
+
import { applySortField } from "./execution-filter.js";
|
|
40
|
+
import { workflowExecutionListIndex } from "./list-index.js";
|
|
41
|
+
|
|
42
|
+
/** The keys the workflow-execution index declares. */
|
|
43
|
+
export type WorkflowExecutionListKey = "workflow" | "workflow_instance";
|
|
44
|
+
|
|
45
|
+
/** A stored row, or undefined (logged) when it does not decode. */
|
|
46
|
+
export function decodeWorkflowExecution(
|
|
47
|
+
data: Uint8Array,
|
|
48
|
+
logger: Logger,
|
|
49
|
+
): WorkflowExecution | undefined {
|
|
50
|
+
try {
|
|
51
|
+
return fromBinary(WorkflowExecutionSchema, data);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
logger.warn("Failed to unmarshal workflow execution, skipping", {
|
|
54
|
+
error: error instanceof Error ? error.message : String(error),
|
|
55
|
+
});
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
16
59
|
|
|
17
60
|
/**
|
|
18
|
-
*
|
|
19
|
-
* Go (each handler wraps the same
|
|
20
|
-
* caller supplies it.
|
|
61
|
+
* Every workflow execution the query matches, newest created first. The
|
|
62
|
+
* Internal message varies by caller in Go (each handler wraps the same
|
|
63
|
+
* store error with its own text), so the caller supplies it.
|
|
21
64
|
*/
|
|
22
|
-
export async function
|
|
65
|
+
export async function loadWorkflowExecutions(
|
|
23
66
|
store: Store,
|
|
24
67
|
logger: Logger,
|
|
68
|
+
query: ListIndexQuery<WorkflowExecutionListKey>,
|
|
25
69
|
internalMessage: string,
|
|
26
70
|
): Promise<WorkflowExecution[]> {
|
|
27
|
-
let rows:
|
|
71
|
+
let rows: ListIndexRow[];
|
|
28
72
|
try {
|
|
29
|
-
rows = await store.
|
|
73
|
+
rows = await store.queryResources(workflowExecutionListIndex, query);
|
|
30
74
|
} catch (error) {
|
|
31
75
|
throw internalError(error, internalMessage);
|
|
32
76
|
}
|
|
33
|
-
|
|
34
77
|
const executions: WorkflowExecution[] = [];
|
|
35
|
-
for (const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
execution
|
|
39
|
-
} catch (error) {
|
|
40
|
-
logger.warn("Failed to unmarshal workflow execution, skipping", {
|
|
41
|
-
error: error instanceof Error ? error.message : String(error),
|
|
42
|
-
});
|
|
43
|
-
continue;
|
|
78
|
+
for (const row of rows) {
|
|
79
|
+
const execution = decodeWorkflowExecution(row.data, logger);
|
|
80
|
+
if (execution !== undefined) {
|
|
81
|
+
executions.push(execution);
|
|
44
82
|
}
|
|
45
|
-
executions.push(execution);
|
|
46
83
|
}
|
|
47
84
|
return executions;
|
|
48
85
|
}
|
|
86
|
+
|
|
87
|
+
export interface WorkflowExecutionListRead {
|
|
88
|
+
readonly store: Store;
|
|
89
|
+
readonly logger: Logger;
|
|
90
|
+
readonly listReadScope: ListReadScope | undefined;
|
|
91
|
+
readonly identity: CallerIdentity;
|
|
92
|
+
readonly query: ListIndexQuery<WorkflowExecutionListKey>;
|
|
93
|
+
readonly request: ListPageRequest;
|
|
94
|
+
readonly sortField: ExecutionSortField;
|
|
95
|
+
readonly sortAscending: boolean;
|
|
96
|
+
/** The lane's filters beyond the indexed predicates. */
|
|
97
|
+
readonly keep: (execution: WorkflowExecution) => boolean;
|
|
98
|
+
/** The request's other fields, which its token is bound to. */
|
|
99
|
+
readonly fingerprint: Readonly<Record<string, unknown>>;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** One list response: a page in the default order, or the whole set sorted by another field. */
|
|
103
|
+
export async function readWorkflowExecutionList(
|
|
104
|
+
read: WorkflowExecutionListRead,
|
|
105
|
+
): Promise<WorkflowExecutionList> {
|
|
106
|
+
const scope = (
|
|
107
|
+
executions: WorkflowExecution[],
|
|
108
|
+
): Promise<WorkflowExecution[]> =>
|
|
109
|
+
restrictListByReadScope(
|
|
110
|
+
read.listReadScope,
|
|
111
|
+
read.identity,
|
|
112
|
+
ApiResourceKind.workflow_execution,
|
|
113
|
+
executions,
|
|
114
|
+
"",
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
if (read.sortField === ExecutionSortField.UNSPECIFIED) {
|
|
118
|
+
const page = await readListPage({
|
|
119
|
+
store: read.store,
|
|
120
|
+
declaration: workflowExecutionListIndex,
|
|
121
|
+
query: read.query,
|
|
122
|
+
request: read.request,
|
|
123
|
+
fingerprint: listPageFingerprint(read.fingerprint),
|
|
124
|
+
decode: (data) => decodeWorkflowExecution(data, read.logger),
|
|
125
|
+
keep: read.keep,
|
|
126
|
+
scope,
|
|
127
|
+
failure: "failed to list workflow executions",
|
|
128
|
+
});
|
|
129
|
+
return create(WorkflowExecutionListSchema, {
|
|
130
|
+
entries: page.entries,
|
|
131
|
+
nextPageToken: page.nextPageToken,
|
|
132
|
+
totalPages: page.nextPageToken === "" ? 1 : 0,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Another sort field never issues a token, so none can be continued.
|
|
137
|
+
if (read.request.pageToken !== "") {
|
|
138
|
+
throw invalidArgumentError("invalid page_token");
|
|
139
|
+
}
|
|
140
|
+
if (read.request.pageSize < 0) {
|
|
141
|
+
throw invalidArgumentError("page_size must not be negative");
|
|
142
|
+
}
|
|
143
|
+
const executions = await scope(
|
|
144
|
+
(
|
|
145
|
+
await loadWorkflowExecutions(
|
|
146
|
+
read.store,
|
|
147
|
+
read.logger,
|
|
148
|
+
read.query,
|
|
149
|
+
"failed to list workflow executions",
|
|
150
|
+
)
|
|
151
|
+
).filter(read.keep),
|
|
152
|
+
);
|
|
153
|
+
applySortField(executions, read.sortField, read.sortAscending);
|
|
154
|
+
const size = Math.min(read.request.pageSize, LIST_PAGE_MAX_SIZE);
|
|
155
|
+
return create(WorkflowExecutionListSchema, {
|
|
156
|
+
entries: size === 0 ? executions : executions.slice(0, size),
|
|
157
|
+
totalPages: 1,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
* contextual checkMyPermission refuse UNIMPLEMENTED with the edition
|
|
16
16
|
* reason (the domain suite pins the refusal; this file pins the
|
|
17
17
|
* registry);
|
|
18
|
+
* - `drivers.principalDisplay` — how an access list names a grantee
|
|
19
|
+
* that is not a person (a team); single instance; absent = the id
|
|
20
|
+
* fallback shape;
|
|
18
21
|
* - `ResourceAuthorizationLifecycle` gains the OPTIONAL `onPolicyGranted`
|
|
19
22
|
* and `onPolicyRevoked` (Q7 i): a unit's lifecycle that carries them
|
|
20
23
|
* resolves through the existing single-instance point with both hooks
|
|
@@ -26,6 +29,7 @@ import { describe, expect, it } from "vitest";
|
|
|
26
29
|
import type { IamPolicyStore } from "../../domain/iampolicy/store.js";
|
|
27
30
|
import type { AuthorizationQueryEngine } from "../authorization-queries.js";
|
|
28
31
|
import type { PolicyGrantScope } from "../policy-grant-scope.js";
|
|
32
|
+
import type { PrincipalDisplay } from "../principal-display.js";
|
|
29
33
|
import { resolveExtensions } from "../registry.js";
|
|
30
34
|
import type { ResourceAuthorizationLifecycle } from "../resource-authorization.js";
|
|
31
35
|
|
|
@@ -125,6 +129,32 @@ describe("the authorizationQueries capability point", () => {
|
|
|
125
129
|
});
|
|
126
130
|
});
|
|
127
131
|
|
|
132
|
+
describe("the principalDisplay driver point", () => {
|
|
133
|
+
const display: PrincipalDisplay = { resolve: unimplemented };
|
|
134
|
+
|
|
135
|
+
it("is undefined with no extensions — a non-person grantee renders by its id", () => {
|
|
136
|
+
expect(resolveExtensions([]).drivers.principalDisplay).toBeUndefined();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("carries the one registered instance through", () => {
|
|
140
|
+
const resolved = resolveExtensions([
|
|
141
|
+
{ name: "cloud-iam", drivers: { principalDisplay: display } },
|
|
142
|
+
]);
|
|
143
|
+
expect(resolved.drivers.principalDisplay).toBe(display);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("throws on a second registration, naming both units", () => {
|
|
147
|
+
expect(() =>
|
|
148
|
+
resolveExtensions([
|
|
149
|
+
{ name: "display-a", drivers: { principalDisplay: display } },
|
|
150
|
+
{ name: "display-b", drivers: { principalDisplay: display } },
|
|
151
|
+
]),
|
|
152
|
+
).toThrowError(
|
|
153
|
+
/extension 'display-b' registers a PrincipalDisplay, but 'display-a' already did/,
|
|
154
|
+
);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
128
158
|
describe("the widened ResourceAuthorizationLifecycle contract", () => {
|
|
129
159
|
const resourceHooks = {
|
|
130
160
|
onResourceCreated: unimplemented,
|
|
@@ -480,6 +480,9 @@ describe("resolveExtensions — loud-fail throws (DD-006 §2b)", () => {
|
|
|
480
480
|
"agent-execution-create:pre-side-effect-gate",
|
|
481
481
|
"agent-execution-recover:pre-side-effect-gate",
|
|
482
482
|
"agent-execution-submit-approval:gate",
|
|
483
|
+
// The eighth: the IamPolicy create chain before the write, where an
|
|
484
|
+
// edition that serves teams refuses a team it cannot admit.
|
|
485
|
+
"iam-policy-create:pre-side-effect-gate",
|
|
483
486
|
// 20260911.11 Q-IA-9: the seventh ratified slot — after the account
|
|
484
487
|
// persists inside provisionMyAccount, before the reply (the cloud's
|
|
485
488
|
// personal-organization ensure and backfill ride it).
|
|
@@ -31,6 +31,9 @@
|
|
|
31
31
|
* port, which kinds an edition grants on, and the tuple-half query
|
|
32
32
|
* engine only an authorization backend can answer) — landed with
|
|
33
33
|
* 20260913.01, gate ruling Q-OR-10
|
|
34
|
+
* - principal display (how an access list names a grantee that is not
|
|
35
|
+
* a person: a team, in the editions that serve teams) — landed with
|
|
36
|
+
* the Team kind
|
|
34
37
|
* - outbound egress (which addresses the control plane may dial when
|
|
35
38
|
* it reaches a URL a user supplied: the MCP endpoint it probes at save
|
|
36
39
|
* time and the login server it reaches on Sign in) — landed with the
|
|
@@ -70,6 +73,7 @@ import type { OutboundEgressPolicy } from "./outbound-egress.js";
|
|
|
70
73
|
import type { ListReadScope } from "./list-read-scope.js";
|
|
71
74
|
import type { OrganizationDirectory } from "./organization-directory.js";
|
|
72
75
|
import type { PolicyGrantScope } from "./policy-grant-scope.js";
|
|
76
|
+
import type { PrincipalDisplay } from "./principal-display.js";
|
|
73
77
|
import type { ResourceAuthorizationLifecycle } from "./resource-authorization.js";
|
|
74
78
|
import type { ScheduleFireCallerMint } from "./schedule-fire-caller.js";
|
|
75
79
|
|
|
@@ -233,6 +237,14 @@ export interface ExtensionDrivers {
|
|
|
233
237
|
* with the edition reason — the identityFederation absent shape.
|
|
234
238
|
*/
|
|
235
239
|
readonly authorizationQueries?: AuthorizationQueryEngine;
|
|
240
|
+
/**
|
|
241
|
+
* The principal display (single-instance point): how an access list
|
|
242
|
+
* names a grantee that is not a person — a team, in the editions that
|
|
243
|
+
* serve teams (extensions/principal-display.ts carries the contract).
|
|
244
|
+
* When absent, such a grantee renders by its id, the fallback shape
|
|
245
|
+
* every access list has always had.
|
|
246
|
+
*/
|
|
247
|
+
readonly principalDisplay?: PrincipalDisplay;
|
|
236
248
|
/**
|
|
237
249
|
* The license-status provider (single-instance point): what license
|
|
238
250
|
* this server holds, answered by PlatformQueryController.getLicenseStatus
|
|
@@ -34,6 +34,15 @@
|
|
|
34
34
|
* Non-transactional in the `org-create:post-persist` sense: a gate
|
|
35
35
|
* failure fails the request, the row survives, the next call heals.
|
|
36
36
|
*
|
|
37
|
+
* The eighth, `iam-policy-create:pre-side-effect-gate`: the IamPolicy
|
|
38
|
+
* `create` chain (the user grant lane), after ValidateGrantableRole and
|
|
39
|
+
* before Grant, so nothing is written when a gate refuses. It exists for
|
|
40
|
+
* the checks a grant needs that take a read, which the synchronous grant
|
|
41
|
+
* scope may not make: the Enterprise and Cloud editions refuse a team
|
|
42
|
+
* that does not exist, a team of another organization than the granted
|
|
43
|
+
* resource, and a team member who is not one of the organization's
|
|
44
|
+
* viewers. Never on `bootstrapPolicy`, the platform's structural lane.
|
|
45
|
+
*
|
|
37
46
|
* Two enforcement layers, deliberately redundant, both derived from the
|
|
38
47
|
* ONE literal tuple below (lockstep by construction):
|
|
39
48
|
* - GateSlotName (compile time): the union of declared slot literals.
|
|
@@ -71,6 +80,7 @@ export const GATE_SLOT_NAMES = [
|
|
|
71
80
|
"org-create:post-persist",
|
|
72
81
|
"sandbox-acquisition:gate",
|
|
73
82
|
"identity-account-provision:post-persist",
|
|
83
|
+
"iam-policy-create:pre-side-effect-gate",
|
|
74
84
|
] as const;
|
|
75
85
|
|
|
76
86
|
/** The declared slot-name union — a registration outside it fails tsc. */
|
|
@@ -81,18 +81,15 @@
|
|
|
81
81
|
* driver's cost is proportional to the candidates offered (the cloud
|
|
82
82
|
* checks each one against its authorization engine), and the whole
|
|
83
83
|
* kind across every tenant is what rolled back on 2026-09-14; the
|
|
84
|
-
* request's org is tens of rows.
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* untouched with the org unconsulted (the first line above). Two lanes
|
|
94
|
-
* have no org on the wire (`session.list`, `apiKey.findAll`) and offer
|
|
95
|
-
* the kind until P1 entry 12 pages them.
|
|
84
|
+
* request's org is tens of rows. The lanes over the session, execution
|
|
85
|
+
* and artifact kinds narrow by the request's org or parent in the
|
|
86
|
+
* store's indexed read (store/list-index.ts; every posture honours the
|
|
87
|
+
* request) and page there (pipeline/steps/list-page.ts, one batch per
|
|
88
|
+
* scope call), passing `""`; the small org-scoped lists (environment,
|
|
89
|
+
* memory, schedule, agent-instance, agent-share and agent-channel; the
|
|
90
|
+
* getBy* parent filters) apply their own filter above the helper call
|
|
91
|
+
* and pass `""` too. `apiKey.findAll` has no org on the wire and offers
|
|
92
|
+
* the kind: a few rows per person.
|
|
96
93
|
* - A KIND WHOSE AUTHORIZATION IS ITS PARENT'S CARRIES THAT PARENT ON
|
|
97
94
|
* EVERY CANDIDATE (stigmer-cloud 20260913.04 T04, Q-LB-33..36). When
|
|
98
95
|
* `kind_meta` declares PARENT scope with INHERITED owner
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The principal-display driver point: how an access list names a grantee
|
|
3
|
+
* that is not a person. Single instance, registered as
|
|
4
|
+
* `drivers.principalDisplay`.
|
|
5
|
+
*
|
|
6
|
+
* An access list ("who has access to this agent, with which roles") names
|
|
7
|
+
* every grantee. People are resolved by the core from the identity-account
|
|
8
|
+
* port (domain/iampolicy/display-resolver.ts), the same in every edition.
|
|
9
|
+
* A grantee of another kind — a team, in the Enterprise and Cloud editions
|
|
10
|
+
* — lives in a store only that edition holds, so its name comes from this
|
|
11
|
+
* point. Absent (open source, which grants to no team), such a grantee
|
|
12
|
+
* renders in the fallback shape the access list has always used: its kind,
|
|
13
|
+
* with its id standing in as its name.
|
|
14
|
+
*
|
|
15
|
+
* The contract:
|
|
16
|
+
* - It answers for the ids it knows and omits the rest; an omitted id
|
|
17
|
+
* takes the fallback shape, never an error (a team deleted between
|
|
18
|
+
* the grant read and this call is an omission, not a fault).
|
|
19
|
+
* - It is never asked about the identity account; the core owns people.
|
|
20
|
+
* - A fault it cannot answer through THROWS (the ListReadScope rule:
|
|
21
|
+
* an empty answer is a real answer, never an outage in disguise), and
|
|
22
|
+
* the list RPC fails with it.
|
|
23
|
+
*/
|
|
24
|
+
import type { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
25
|
+
import type { ApiResourceRefView } from "@stigmer/protos/ai/stigmer/iam/iampolicy/v1/io_pb";
|
|
26
|
+
|
|
27
|
+
/** The principal-display contract (single-instance point, ExtensionDrivers.principalDisplay). */
|
|
28
|
+
export interface PrincipalDisplay {
|
|
29
|
+
/**
|
|
30
|
+
* Display views for the principals of `kind` with these ids, keyed by
|
|
31
|
+
* id; ids it does not know are absent from the map.
|
|
32
|
+
*/
|
|
33
|
+
resolve(
|
|
34
|
+
kind: ApiResourceKind,
|
|
35
|
+
ids: ReadonlyArray<string>,
|
|
36
|
+
): Promise<ReadonlyMap<string, ApiResourceRefView>>;
|
|
37
|
+
}
|
|
@@ -60,6 +60,7 @@ import type { LicenseStatusProvider } from "./license-status.js";
|
|
|
60
60
|
import type { OutboundEgressPolicy } from "./outbound-egress.js";
|
|
61
61
|
import type { ListReadScope } from "./list-read-scope.js";
|
|
62
62
|
import type { PolicyGrantScope } from "./policy-grant-scope.js";
|
|
63
|
+
import type { PrincipalDisplay } from "./principal-display.js";
|
|
63
64
|
import type { ModelCatalogProvider } from "../domain/workflow/registry/model-catalog-provider.js";
|
|
64
65
|
import type { VisitorErrorPolicy } from "../pipeline/interceptors/error-boundary.js";
|
|
65
66
|
import type { PipelineStep } from "../pipeline/pipeline.js";
|
|
@@ -288,6 +289,11 @@ export interface ResolvedExtensionDrivers {
|
|
|
288
289
|
* tuple-half RPC arms refuse UNIMPLEMENTED with the edition reason.
|
|
289
290
|
*/
|
|
290
291
|
readonly authorizationQueries: AuthorizationQueryEngine | undefined;
|
|
292
|
+
/**
|
|
293
|
+
* The principal display — undefined = an access list names a grantee
|
|
294
|
+
* that is not a person by its id (the fallback shape).
|
|
295
|
+
*/
|
|
296
|
+
readonly principalDisplay: PrincipalDisplay | undefined;
|
|
291
297
|
/**
|
|
292
298
|
* The license-status provider — undefined = compose.ts installs the
|
|
293
299
|
* built-in `absent` answer, so every edition serves getLicenseStatus.
|
|
@@ -367,6 +373,8 @@ export function resolveExtensions(
|
|
|
367
373
|
let policyGrantScopeDeclaredBy: string | undefined;
|
|
368
374
|
let authorizationQueries: AuthorizationQueryEngine | undefined;
|
|
369
375
|
let authorizationQueriesDeclaredBy: string | undefined;
|
|
376
|
+
let principalDisplay: PrincipalDisplay | undefined;
|
|
377
|
+
let principalDisplayDeclaredBy: string | undefined;
|
|
370
378
|
let licenseStatus: LicenseStatusProvider | undefined;
|
|
371
379
|
let licenseStatusDeclaredBy: string | undefined;
|
|
372
380
|
let outboundEgress: OutboundEgressPolicy | undefined;
|
|
@@ -568,6 +576,16 @@ export function resolveExtensions(
|
|
|
568
576
|
authorizationQueriesDeclaredBy = unit.name;
|
|
569
577
|
}
|
|
570
578
|
|
|
579
|
+
if (unit.drivers?.principalDisplay !== undefined) {
|
|
580
|
+
if (principalDisplayDeclaredBy !== undefined) {
|
|
581
|
+
throw new Error(
|
|
582
|
+
`extension '${unit.name}' registers a PrincipalDisplay, but '${principalDisplayDeclaredBy}' already did — exactly one may be composed`,
|
|
583
|
+
);
|
|
584
|
+
}
|
|
585
|
+
principalDisplay = unit.drivers.principalDisplay;
|
|
586
|
+
principalDisplayDeclaredBy = unit.name;
|
|
587
|
+
}
|
|
588
|
+
|
|
571
589
|
if (unit.drivers?.licenseStatus !== undefined) {
|
|
572
590
|
if (licenseStatusDeclaredBy !== undefined) {
|
|
573
591
|
throw new Error(
|
|
@@ -748,6 +766,7 @@ export function resolveExtensions(
|
|
|
748
766
|
iamPolicyStore,
|
|
749
767
|
policyGrantScope,
|
|
750
768
|
authorizationQueries,
|
|
769
|
+
principalDisplay,
|
|
751
770
|
licenseStatus,
|
|
752
771
|
outboundEgress,
|
|
753
772
|
platformClientStore,
|
package/src/index.ts
CHANGED
|
@@ -164,6 +164,9 @@ export type {
|
|
|
164
164
|
export { iamPolicyStoreContract } from "./domain/iampolicy/store-contract.js";
|
|
165
165
|
export type { PolicyGrantScope } from "./extensions/policy-grant-scope.js";
|
|
166
166
|
export type { AuthorizationQueryEngine } from "./extensions/authorization-queries.js";
|
|
167
|
+
// How an access list names a grantee that is not a person
|
|
168
|
+
// (drivers.principalDisplay): a team, in the editions that serve teams.
|
|
169
|
+
export type { PrincipalDisplay } from "./extensions/principal-display.js";
|
|
167
170
|
export type { IdentityFederation } from "./extensions/identity-federation.js";
|
|
168
171
|
export type { AccountsBySubject } from "./domain/identityaccount/resolve.js";
|
|
169
172
|
export { identityIdForSubject } from "./domain/identityaccount/resolve.js";
|
|
@@ -288,8 +291,14 @@ export { ScheduleFireCallerRefusedError } from "./extensions/schedule-fire-calle
|
|
|
288
291
|
// through the REAL boundary mechanism, never a re-derivation.
|
|
289
292
|
export type { VisitorErrorPolicy } from "./pipeline/interceptors/error-boundary.js";
|
|
290
293
|
export { createErrorBoundaryInterceptor } from "./pipeline/interceptors/error-boundary.js";
|
|
294
|
+
// The tuple lifecycle's resolution, for a kind a composition serves outside
|
|
295
|
+
// the generic chains (a cloud-served create): `resolveResourceCreatedEvent`
|
|
296
|
+
// derives the creation event from the kind's `kind_meta` exactly as the
|
|
297
|
+
// shared CreateAuthorizationTuples step does, so the driver sees one shape
|
|
298
|
+
// whichever chain created the row.
|
|
291
299
|
export {
|
|
292
300
|
diffVisibilityShapes,
|
|
301
|
+
resolveResourceCreatedEvent,
|
|
293
302
|
visibilityShapesFor,
|
|
294
303
|
} from "./pipeline/steps/authorization-tuples.js";
|
|
295
304
|
|
|
@@ -380,11 +389,20 @@ export { newResolveSlugStep } from "./pipeline/steps/slug.js";
|
|
|
380
389
|
// The driver interfaces and the store-fault classes the ratified mapping
|
|
381
390
|
// keys on (typed not-found → NotFound; anything else rethrows as an
|
|
382
391
|
// infrastructure fault — the guidelines' instanceof idiom).
|
|
383
|
-
export type { Store } from "./store/interface.js";
|
|
392
|
+
export type { Store, StoreOpenOptions } from "./store/interface.js";
|
|
384
393
|
export {
|
|
385
394
|
AuditNotFoundError,
|
|
386
395
|
ResourceNotFoundError,
|
|
387
396
|
} from "./store/interface.js";
|
|
397
|
+
// The list index's read shapes, which `Store.queryResources` speaks
|
|
398
|
+
// (store/list-index.ts). Declaring an index stays internal: the list is
|
|
399
|
+
// the composition root's (boot/list-indexes.ts), one per server.
|
|
400
|
+
export type {
|
|
401
|
+
ListIndexCursor,
|
|
402
|
+
ListIndexDeclaration,
|
|
403
|
+
ListIndexQuery,
|
|
404
|
+
ListIndexRow,
|
|
405
|
+
} from "./store/list-index.js";
|
|
388
406
|
// The maintenance-surface row shape (20260830.04 Stage 1, ruling Q3):
|
|
389
407
|
// what findResourcesRawOrderedAfter pages and what
|
|
390
408
|
// replaceResourceDataIfUnchanged guards on — the secret-convergence
|
|
@@ -200,6 +200,27 @@ export function grantableRolesFor(
|
|
|
200
200
|
return getOption(valueDesc, kind_meta).authorization?.grantableRoles ?? [];
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
+
/**
|
|
204
|
+
* The roles a TEAM may be granted on a resource of `kind` according to the
|
|
205
|
+
* contract — `kind_meta.authorization.team_grantable_roles`, a subset of
|
|
206
|
+
* `grantableRolesFor(kind)` (the subset is pinned by a test, never assumed
|
|
207
|
+
* here). Empty for every kind a team cannot be granted access to, and for
|
|
208
|
+
* `organization`, so an edition that grants only on organizations grants
|
|
209
|
+
* nothing to a team. Total, never a throw, for the same reason as
|
|
210
|
+
* `grantableRolesFor`.
|
|
211
|
+
*/
|
|
212
|
+
export function teamGrantableRolesFor(
|
|
213
|
+
kind: ApiResourceKind,
|
|
214
|
+
): ReadonlyArray<IamRole> {
|
|
215
|
+
const valueDesc = kindValueDescriptor(kind);
|
|
216
|
+
if (valueDesc === undefined || !hasOption(valueDesc, kind_meta)) {
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
return (
|
|
220
|
+
getOption(valueDesc, kind_meta).authorization?.teamGrantableRoles ?? []
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
203
224
|
/**
|
|
204
225
|
* Whether an edition serves a tier: the tier names the MINIMUM edition,
|
|
205
226
|
* the editions are ordered oss < enterprise < cloud (each composes the
|