@kindgi/api 0.1.4-rc.0 → 0.1.4-rc.2
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/agent-binding.d.ts +20 -2
- package/dist/agent-binding.d.ts.map +1 -1
- package/dist/agent-pins.d.ts.map +1 -1
- package/dist/agent-pins.js +4 -1
- package/dist/agent-pins.js.map +1 -1
- package/dist/app.d.ts +8 -0
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +30 -5
- package/dist/app.js.map +1 -1
- package/dist/deploy-versions.d.ts +10 -5
- package/dist/deploy-versions.d.ts.map +1 -1
- package/dist/deploy-versions.js +4 -10
- package/dist/deploy-versions.js.map +1 -1
- package/dist/derive-agent-version.d.ts +9 -1
- package/dist/derive-agent-version.d.ts.map +1 -1
- package/dist/derive-agent-version.js +11 -1
- package/dist/derive-agent-version.js.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +26 -0
- package/dist/errors.js.map +1 -1
- package/dist/eval-case-binding.d.ts +10 -0
- package/dist/eval-case-binding.d.ts.map +1 -1
- package/dist/eval-run-binding.d.ts +21 -3
- package/dist/eval-run-binding.d.ts.map +1 -1
- package/dist/eval-run-binding.js.map +1 -1
- package/dist/eval-run-dispatcher.d.ts +3 -0
- package/dist/eval-run-dispatcher.d.ts.map +1 -1
- package/dist/eval-run-dispatcher.js.map +1 -1
- package/dist/flow-binding.d.ts +8 -0
- package/dist/flow-binding.d.ts.map +1 -1
- package/dist/flow-pins.d.ts +20 -7
- package/dist/flow-pins.d.ts.map +1 -1
- package/dist/flow-pins.js +36 -11
- package/dist/flow-pins.js.map +1 -1
- package/dist/gate-policy-binding.d.ts +164 -0
- package/dist/gate-policy-binding.d.ts.map +1 -0
- package/dist/gate-policy-binding.js +12 -0
- package/dist/gate-policy-binding.js.map +1 -0
- package/dist/gate.d.ts +56 -0
- package/dist/gate.d.ts.map +1 -0
- package/dist/gate.js +359 -0
- package/dist/gate.js.map +1 -0
- package/dist/guardrail-binding.d.ts +8 -0
- package/dist/guardrail-binding.d.ts.map +1 -1
- package/dist/handler-binding.d.ts +17 -2
- package/dist/handler-binding.d.ts.map +1 -1
- package/dist/index.d.ts +11 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/judged-dispatcher.d.ts +20 -1
- package/dist/judged-dispatcher.d.ts.map +1 -1
- package/dist/judged-dispatcher.js +57 -7
- package/dist/judged-dispatcher.js.map +1 -1
- package/dist/judgment-binding.d.ts +38 -0
- package/dist/judgment-binding.d.ts.map +1 -1
- package/dist/judgment-binding.js +19 -0
- package/dist/judgment-binding.js.map +1 -1
- package/dist/live-version-binding.d.ts +206 -0
- package/dist/live-version-binding.d.ts.map +1 -0
- package/dist/live-version-binding.js +4 -0
- package/dist/live-version-binding.js.map +1 -0
- package/dist/middleware/authorize.d.ts.map +1 -1
- package/dist/middleware/authorize.js +13 -2
- package/dist/middleware/authorize.js.map +1 -1
- package/dist/middleware/project-ref.d.ts +25 -0
- package/dist/middleware/project-ref.d.ts.map +1 -0
- package/dist/middleware/project-ref.js +72 -0
- package/dist/middleware/project-ref.js.map +1 -0
- package/dist/openapi/generate.d.ts.map +1 -1
- package/dist/openapi/generate.js +4 -0
- package/dist/openapi/generate.js.map +1 -1
- package/dist/openapi/operations.d.ts +6 -0
- package/dist/openapi/operations.d.ts.map +1 -1
- package/dist/openapi/operations.js +524 -27
- package/dist/openapi/operations.js.map +1 -1
- package/dist/openapi/schemas.d.ts +43 -0
- package/dist/openapi/schemas.d.ts.map +1 -1
- package/dist/openapi/schemas.js +1050 -7
- package/dist/openapi/schemas.js.map +1 -1
- package/dist/publish-refused.d.ts +18 -0
- package/dist/publish-refused.d.ts.map +1 -0
- package/dist/publish-refused.js +22 -0
- package/dist/publish-refused.js.map +1 -0
- package/dist/registry-read-only.d.ts +32 -0
- package/dist/registry-read-only.d.ts.map +1 -0
- package/dist/registry-read-only.js +22 -0
- package/dist/registry-read-only.js.map +1 -0
- package/dist/retention-binding.d.ts +29 -0
- package/dist/retention-binding.d.ts.map +1 -1
- package/dist/routes/agent-releases.d.ts +40 -0
- package/dist/routes/agent-releases.d.ts.map +1 -0
- package/dist/routes/agent-releases.js +462 -0
- package/dist/routes/agent-releases.js.map +1 -0
- package/dist/routes/agents.d.ts +7 -1
- package/dist/routes/agents.d.ts.map +1 -1
- package/dist/routes/agents.js +38 -4
- package/dist/routes/agents.js.map +1 -1
- package/dist/routes/approvals.d.ts.map +1 -1
- package/dist/routes/approvals.js +27 -9
- package/dist/routes/approvals.js.map +1 -1
- package/dist/routes/audit.d.ts.map +1 -1
- package/dist/routes/audit.js +7 -0
- package/dist/routes/audit.js.map +1 -1
- package/dist/routes/auth.js +1 -1
- package/dist/routes/auth.js.map +1 -1
- package/dist/routes/blocks.d.ts.map +1 -1
- package/dist/routes/blocks.js +37 -12
- package/dist/routes/blocks.js.map +1 -1
- package/dist/routes/conversations.d.ts +0 -7
- package/dist/routes/conversations.d.ts.map +1 -1
- package/dist/routes/conversations.js +19 -3
- package/dist/routes/conversations.js.map +1 -1
- package/dist/routes/deployments.d.ts +6 -0
- package/dist/routes/deployments.d.ts.map +1 -1
- package/dist/routes/deployments.js +49 -5
- package/dist/routes/deployments.js.map +1 -1
- package/dist/routes/env.d.ts.map +1 -1
- package/dist/routes/env.js +1 -0
- package/dist/routes/env.js.map +1 -1
- package/dist/routes/eval-comparison.d.ts +4 -2
- package/dist/routes/eval-comparison.d.ts.map +1 -1
- package/dist/routes/eval-comparison.js +59 -10
- package/dist/routes/eval-comparison.js.map +1 -1
- package/dist/routes/eval-runs.d.ts +7 -1
- package/dist/routes/eval-runs.d.ts.map +1 -1
- package/dist/routes/eval-runs.js +34 -3
- package/dist/routes/eval-runs.js.map +1 -1
- package/dist/routes/eval-versions.d.ts +25 -0
- package/dist/routes/eval-versions.d.ts.map +1 -0
- package/dist/routes/eval-versions.js +66 -0
- package/dist/routes/eval-versions.js.map +1 -0
- package/dist/routes/flows.d.ts +14 -7
- package/dist/routes/flows.d.ts.map +1 -1
- package/dist/routes/flows.js +14 -8
- package/dist/routes/flows.js.map +1 -1
- package/dist/routes/gate-policies.d.ts +19 -0
- package/dist/routes/gate-policies.d.ts.map +1 -0
- package/dist/routes/gate-policies.js +191 -0
- package/dist/routes/gate-policies.js.map +1 -0
- package/dist/routes/gate-policy-spec.d.ts +17 -0
- package/dist/routes/gate-policy-spec.d.ts.map +1 -0
- package/dist/routes/gate-policy-spec.js +192 -0
- package/dist/routes/gate-policy-spec.js.map +1 -0
- package/dist/routes/gate-policy-wire.d.ts +3 -0
- package/dist/routes/gate-policy-wire.d.ts.map +1 -0
- package/dist/routes/gate-policy-wire.js +18 -0
- package/dist/routes/gate-policy-wire.js.map +1 -0
- package/dist/routes/guardrails.d.ts.map +1 -1
- package/dist/routes/guardrails.js +4 -0
- package/dist/routes/guardrails.js.map +1 -1
- package/dist/routes/hierarchy-errors.d.ts +21 -0
- package/dist/routes/hierarchy-errors.d.ts.map +1 -1
- package/dist/routes/hierarchy-errors.js +21 -0
- package/dist/routes/hierarchy-errors.js.map +1 -1
- package/dist/routes/judged-suites.js +7 -0
- package/dist/routes/judged-suites.js.map +1 -1
- package/dist/routes/judgments.d.ts +4 -1
- package/dist/routes/judgments.d.ts.map +1 -1
- package/dist/routes/judgments.js +92 -19
- package/dist/routes/judgments.js.map +1 -1
- package/dist/routes/live-scope-wire.d.ts +32 -0
- package/dist/routes/live-scope-wire.d.ts.map +1 -0
- package/dist/routes/live-scope-wire.js +71 -0
- package/dist/routes/live-scope-wire.js.map +1 -0
- package/dist/routes/policies.d.ts +6 -3
- package/dist/routes/policies.d.ts.map +1 -1
- package/dist/routes/policies.js +55 -3
- package/dist/routes/policies.js.map +1 -1
- package/dist/routes/projects.d.ts.map +1 -1
- package/dist/routes/projects.js +59 -9
- package/dist/routes/projects.js.map +1 -1
- package/dist/routes/retention.d.ts.map +1 -1
- package/dist/routes/retention.js +18 -2
- package/dist/routes/retention.js.map +1 -1
- package/dist/routes/reviewers.d.ts +8 -1
- package/dist/routes/reviewers.d.ts.map +1 -1
- package/dist/routes/reviewers.js +20 -3
- package/dist/routes/reviewers.js.map +1 -1
- package/dist/routes/runs.d.ts.map +1 -1
- package/dist/routes/runs.js +23 -13
- package/dist/routes/runs.js.map +1 -1
- package/dist/routes/secrets.d.ts.map +1 -1
- package/dist/routes/secrets.js +2 -0
- package/dist/routes/secrets.js.map +1 -1
- package/dist/routes/segments.d.ts +17 -0
- package/dist/routes/segments.d.ts.map +1 -0
- package/dist/routes/segments.js +68 -0
- package/dist/routes/segments.js.map +1 -0
- package/dist/routes/teams.d.ts.map +1 -1
- package/dist/routes/teams.js +53 -8
- package/dist/routes/teams.js.map +1 -1
- package/dist/routes/tools.d.ts.map +1 -1
- package/dist/routes/tools.js +4 -0
- package/dist/routes/tools.js.map +1 -1
- package/dist/routes/uuid-param.d.ts +11 -0
- package/dist/routes/uuid-param.d.ts.map +1 -0
- package/dist/routes/uuid-param.js +19 -0
- package/dist/routes/uuid-param.js.map +1 -0
- package/dist/tool-binding.d.ts +8 -0
- package/dist/tool-binding.d.ts.map +1 -1
- package/openapi.json +6484 -2402
- package/package.json +21 -21
- package/src/agent-binding.ts +21 -2
- package/src/agent-pins.ts +3 -1
- package/src/app.ts +47 -4
- package/src/deploy-versions.ts +12 -15
- package/src/derive-agent-version.ts +12 -1
- package/src/errors.ts +26 -0
- package/src/eval-case-binding.ts +7 -0
- package/src/eval-run-binding.ts +31 -3
- package/src/eval-run-dispatcher.ts +3 -0
- package/src/flow-binding.ts +9 -0
- package/src/flow-pins.ts +49 -9
- package/src/gate-policy-binding.ts +171 -0
- package/src/gate.ts +467 -0
- package/src/guardrail-binding.ts +9 -0
- package/src/handler-binding.ts +17 -2
- package/src/index.ts +47 -1
- package/src/judged-dispatcher.ts +100 -9
- package/src/judgment-binding.ts +62 -0
- package/src/live-version-binding.ts +237 -0
- package/src/middleware/authorize.ts +18 -2
- package/src/middleware/project-ref.ts +88 -0
- package/src/openapi/generate.ts +5 -0
- package/src/openapi/operations.ts +664 -27
- package/src/openapi/schemas.ts +1175 -33
- package/src/publish-refused.ts +31 -0
- package/src/registry-read-only.ts +43 -0
- package/src/retention-binding.ts +30 -0
- package/src/routes/agent-releases.ts +581 -0
- package/src/routes/agents.ts +53 -3
- package/src/routes/approvals.ts +36 -8
- package/src/routes/audit.ts +10 -0
- package/src/routes/auth.ts +1 -1
- package/src/routes/blocks.ts +39 -14
- package/src/routes/conversations.ts +31 -3
- package/src/routes/deployments.ts +75 -5
- package/src/routes/env.ts +1 -0
- package/src/routes/eval-comparison.ts +59 -11
- package/src/routes/eval-runs.ts +46 -3
- package/src/routes/eval-versions.ts +110 -0
- package/src/routes/flows.ts +34 -11
- package/src/routes/gate-policies.ts +226 -0
- package/src/routes/gate-policy-spec.ts +229 -0
- package/src/routes/gate-policy-wire.ts +20 -0
- package/src/routes/guardrails.ts +7 -0
- package/src/routes/hierarchy-errors.ts +23 -0
- package/src/routes/judged-suites.ts +5 -0
- package/src/routes/judgments.ts +107 -23
- package/src/routes/live-scope-wire.ts +90 -0
- package/src/routes/policies.ts +61 -3
- package/src/routes/projects.ts +72 -9
- package/src/routes/retention.ts +21 -2
- package/src/routes/reviewers.ts +23 -4
- package/src/routes/runs.ts +34 -20
- package/src/routes/secrets.ts +2 -0
- package/src/routes/segments.ts +75 -0
- package/src/routes/teams.ts +66 -8
- package/src/routes/tools.ts +7 -0
- package/src/routes/uuid-param.ts +28 -0
- package/src/tool-binding.ts +9 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kindgi/api",
|
|
3
|
-
"version": "0.1.4-rc.
|
|
3
|
+
"version": "0.1.4-rc.2",
|
|
4
4
|
"description": "REST + SSE HTTP surface for Kindgi™. createApp assembles a Hono app serving the /v1/* REST API (bearer or session-token auth; OpenAPI 3.1 document at /v1/openapi.json) and an optional S3-compatible /s3/* surface (SigV4), over caller-plugged bindings for the runtime (runs, agents, flows, HITL, supervisor) and for storage. Route conventions: docs/API-ROUTE-CONVENTIONS.md.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -32,30 +32,30 @@
|
|
|
32
32
|
"openapi.json"
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@kindgi/agents": "0.1.4-rc.
|
|
36
|
-
"@kindgi/audit-events": "0.1.4-rc.
|
|
37
|
-
"@kindgi/compliance": "0.1.4-rc.
|
|
38
|
-
"@kindgi/authz": "0.1.4-rc.
|
|
39
|
-
"@kindgi/blob-binding": "0.1.4-rc.
|
|
40
|
-
"@kindgi/capabilities": "0.1.4-rc.
|
|
41
|
-
"@kindgi/crypto": "0.1.4-rc.
|
|
42
|
-
"@kindgi/flow": "0.1.4-rc.
|
|
43
|
-
"@kindgi/guardrails": "0.1.4-rc.
|
|
44
|
-
"@kindgi/memory": "0.1.4-rc.
|
|
45
|
-
"@kindgi/platform": "0.1.4-rc.
|
|
46
|
-
"@kindgi/provenance": "0.1.4-rc.
|
|
47
|
-
"@kindgi/runtime": "0.1.4-rc.
|
|
48
|
-
"@kindgi/policy-contract": "0.1.4-rc.
|
|
49
|
-
"@kindgi/schema": "0.1.4-rc.
|
|
50
|
-
"@kindgi/tools": "0.1.4-rc.
|
|
51
|
-
"@kindgi/types": "0.1.4-rc.
|
|
35
|
+
"@kindgi/agents": "0.1.4-rc.2",
|
|
36
|
+
"@kindgi/audit-events": "0.1.4-rc.2",
|
|
37
|
+
"@kindgi/compliance": "0.1.4-rc.2",
|
|
38
|
+
"@kindgi/authz": "0.1.4-rc.2",
|
|
39
|
+
"@kindgi/blob-binding": "0.1.4-rc.2",
|
|
40
|
+
"@kindgi/capabilities": "0.1.4-rc.2",
|
|
41
|
+
"@kindgi/crypto": "0.1.4-rc.2",
|
|
42
|
+
"@kindgi/flow": "0.1.4-rc.2",
|
|
43
|
+
"@kindgi/guardrails": "0.1.4-rc.2",
|
|
44
|
+
"@kindgi/memory": "0.1.4-rc.2",
|
|
45
|
+
"@kindgi/platform": "0.1.4-rc.2",
|
|
46
|
+
"@kindgi/provenance": "0.1.4-rc.2",
|
|
47
|
+
"@kindgi/runtime": "0.1.4-rc.2",
|
|
48
|
+
"@kindgi/policy-contract": "0.1.4-rc.2",
|
|
49
|
+
"@kindgi/schema": "0.1.4-rc.2",
|
|
50
|
+
"@kindgi/tools": "0.1.4-rc.2",
|
|
51
|
+
"@kindgi/types": "0.1.4-rc.2",
|
|
52
52
|
"@scalar/hono-api-reference": "^0.12.2",
|
|
53
53
|
"hono": "^4.6.14"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@kindgi/audit-events-inmemory": "0.1.4-rc.
|
|
57
|
-
"@kindgi/specs": "0.1.4-rc.
|
|
58
|
-
"@kindgi/testing": "0.1.4-rc.
|
|
56
|
+
"@kindgi/audit-events-inmemory": "0.1.4-rc.2",
|
|
57
|
+
"@kindgi/specs": "0.1.4-rc.2",
|
|
58
|
+
"@kindgi/testing": "0.1.4-rc.2",
|
|
59
59
|
"@types/aws4": "^1.11.6",
|
|
60
60
|
"@types/node": "^22.10.5",
|
|
61
61
|
"aws4": "^1.13.2",
|
package/src/agent-binding.ts
CHANGED
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
import type { Agent, AgentId } from '@kindgi/agents';
|
|
5
5
|
import type { TupleEnqueueHook } from '@kindgi/authz';
|
|
6
6
|
import type { Scope } from '@kindgi/platform';
|
|
7
|
-
import type { Cursor, ProjectId, Semver, TenantId } from '@kindgi/types';
|
|
7
|
+
import type { Cursor, LiveScope, ProjectId, Semver, TenantId } from '@kindgi/types';
|
|
8
|
+
|
|
9
|
+
import type { RegistryReadOnly } from './registry-read-only.js';
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Caller-plugged surface for the agent catalog. Same shape as
|
|
@@ -22,6 +24,13 @@ import type { Cursor, ProjectId, Semver, TenantId } from '@kindgi/types';
|
|
|
22
24
|
* cursor round-trips as a string; it never inspects the payload.
|
|
23
25
|
*/
|
|
24
26
|
export interface AgentRegistryBinding {
|
|
27
|
+
/**
|
|
28
|
+
* Set when this registry takes no writes (under `kindgi dev`, the
|
|
29
|
+
* pack's files are the source of its agents): every write is refused
|
|
30
|
+
* with `409 registry-read-only` and this reason, before the binding is
|
|
31
|
+
* called. See `RegistryReadOnly`.
|
|
32
|
+
*/
|
|
33
|
+
readonly readOnly?: RegistryReadOnly;
|
|
25
34
|
/**
|
|
26
35
|
* Cursor-paginated list of agents (latest version per id, sorted by
|
|
27
36
|
* agent id ascending). Optional `nameFilter` is a prefix match on the
|
|
@@ -71,7 +80,8 @@ export interface AgentRegistryBinding {
|
|
|
71
80
|
* Soft-tombstone a specific `(agentId, version)`. Returns
|
|
72
81
|
* `{ unregistered: true }` on success; `{ unregistered: false }`
|
|
73
82
|
* when the version was unknown OR already tombstoned — the route
|
|
74
|
-
* flips the latter to `404
|
|
83
|
+
* flips the latter to `404` — or, with `live`, when it's a scope's live
|
|
84
|
+
* version (`409 agent-version-live`). Head row is preserved; when this
|
|
75
85
|
* unregister leaves NO active versions, the agent has no latest
|
|
76
86
|
* version — it enters the derived "retired" state
|
|
77
87
|
* (GET returns 410 gone) but the identity persists. Publish of a
|
|
@@ -229,6 +239,15 @@ export type AgentPublishOutcome =
|
|
|
229
239
|
|
|
230
240
|
export type AgentUnregisterOutcome = {
|
|
231
241
|
readonly unregistered: boolean;
|
|
242
|
+
/**
|
|
243
|
+
* Set when the version wasn't unregistered because it's live: the
|
|
244
|
+
* scopes whose live pin it is (evals step 4). Unregister stops a version
|
|
245
|
+
* being chosen, and a live pin is a standing choice, so the operator
|
|
246
|
+
* moves those pins first (rollback, unpin, or promote another version).
|
|
247
|
+
* The route answers `409 agent-version-live`. Absent: a registry that
|
|
248
|
+
* doesn't know live versions, as before.
|
|
249
|
+
*/
|
|
250
|
+
readonly live?: readonly LiveScope[];
|
|
232
251
|
};
|
|
233
252
|
|
|
234
253
|
export type AgentReinstateVersionOutcome =
|
package/src/agent-pins.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type { AgentRegistryBinding } from './agent-binding.js';
|
|
|
10
10
|
import type { BlockRegistryBinding } from './block-binding.js';
|
|
11
11
|
import { resolveBlockPins } from './block-pins.js';
|
|
12
12
|
import { type DeployedVersionOutcome, deployVersion } from './deploy-versions.js';
|
|
13
|
+
import { PublishRefused } from './publish-refused.js';
|
|
13
14
|
import type { ToolRegistryBinding } from './tool-binding.js';
|
|
14
15
|
|
|
15
16
|
/** One block reference that matched no published version. */
|
|
@@ -120,7 +121,8 @@ export async function publishDeployedAgent(
|
|
|
120
121
|
publish: async (version) => {
|
|
121
122
|
const outcome = await agents.publish({ tenantId, projectId, agent: version, enqueueTuples });
|
|
122
123
|
if (outcome.kind === 'ok') return 'ok';
|
|
123
|
-
|
|
124
|
+
if (outcome.kind === 'already-registered') return 'taken';
|
|
125
|
+
throw new PublishRefused('agent', `${agent.id}@${version.version}`, outcome.kind);
|
|
124
126
|
},
|
|
125
127
|
});
|
|
126
128
|
}
|
package/src/app.ts
CHANGED
|
@@ -48,6 +48,7 @@ import type {
|
|
|
48
48
|
} from './identity-provider-binding.js';
|
|
49
49
|
import type { ImageRegistryBinding } from './image-registry-binding.js';
|
|
50
50
|
import type { JudgmentRegistryBinding } from './judgment-binding.js';
|
|
51
|
+
import type { AgentReleaseBindings } from './live-version-binding.js';
|
|
51
52
|
import type { MCPClientProbeBinding, MCPEndpointRegistryBinding } from './mcp-endpoint-binding.js';
|
|
52
53
|
import type { MemoryBinding } from './memory-binding.js';
|
|
53
54
|
import { type TokenResolver, bearerAuthMiddleware } from './middleware/auth.js';
|
|
@@ -59,6 +60,7 @@ import {
|
|
|
59
60
|
idempotencyMiddleware,
|
|
60
61
|
} from './middleware/idempotency.js';
|
|
61
62
|
import { principalMiddleware } from './middleware/principal.js';
|
|
63
|
+
import { PROJECT_REF_ROUTES, refuseBadProjectId } from './middleware/project-ref.js';
|
|
62
64
|
import { publicRunCorsMiddleware, publicRunRouteMatcher } from './middleware/public-run-routes.js';
|
|
63
65
|
import { requestIdMiddleware } from './middleware/request-id.js';
|
|
64
66
|
import { sigv4Middleware } from './middleware/sigv4.js';
|
|
@@ -93,6 +95,7 @@ import { evalRunsRouters } from './routes/eval-runs.js';
|
|
|
93
95
|
import { evalSuitesRouter } from './routes/eval-suites.js';
|
|
94
96
|
import { eventTriggersRouter } from './routes/event-triggers.js';
|
|
95
97
|
import { flowsRouter } from './routes/flows.js';
|
|
98
|
+
import { gatePoliciesRouter } from './routes/gate-policies.js';
|
|
96
99
|
import { guardrailsRouter } from './routes/guardrails.js';
|
|
97
100
|
import { identityRouter } from './routes/identity.js';
|
|
98
101
|
import { judgedSuitesRouter } from './routes/judged-suites.js';
|
|
@@ -609,6 +612,13 @@ export interface CreateAppInput {
|
|
|
609
612
|
* and `GET /v1/eval-suites/:suiteId/versions/:version/cases`.
|
|
610
613
|
*/
|
|
611
614
|
readonly evalCaseStore?: EvalCaseStoreBinding;
|
|
615
|
+
/**
|
|
616
|
+
* Optional. Live versions of agents per scope, and their promotions
|
|
617
|
+
* (`/v1/agents/{id}/live`, `/live-versions`, `/promotions`,
|
|
618
|
+
* `/live/rollback`, `/live/unpin`). Absent → a run takes the latest
|
|
619
|
+
* version, as before.
|
|
620
|
+
*/
|
|
621
|
+
readonly agentReleases?: AgentReleaseBindings;
|
|
612
622
|
/**
|
|
613
623
|
* Optional. Push-based pub/sub binding used by SSE endpoints to
|
|
614
624
|
* deliver run events without polling. When present, `GET
|
|
@@ -963,6 +973,11 @@ export function createApp(input: CreateAppInput): Hono<AppEnv> {
|
|
|
963
973
|
|
|
964
974
|
const tenantHierarchyBinding: TenantHierarchyBinding = input.tenantHierarchyBinding;
|
|
965
975
|
v1.use('*', idempotencyMiddleware(input.idempotencyStore ?? createInMemoryIdempotencyStore()));
|
|
976
|
+
// A `projectId` in a write's body that the route can't use is refused
|
|
977
|
+
// before any binding sees it: a 400 if it isn't a UUID, a 404 if it
|
|
978
|
+
// names no project (T247).
|
|
979
|
+
const projectRef = refuseBadProjectId(input.projectBinding);
|
|
980
|
+
for (const path of PROJECT_REF_ROUTES) v1.use(path, projectRef);
|
|
966
981
|
v1.route(
|
|
967
982
|
'/runs',
|
|
968
983
|
runsRouter(
|
|
@@ -1001,7 +1016,7 @@ export function createApp(input: CreateAppInput): Hono<AppEnv> {
|
|
|
1001
1016
|
// so `/v1/approvals/reviewers/*` resolves here rather than being
|
|
1002
1017
|
// captured by the `:approvalId` param on the approvals router.
|
|
1003
1018
|
if (input.reviewerRegistry !== undefined) {
|
|
1004
|
-
v1.route('/approvals/reviewers', reviewersRouter(input.reviewerRegistry));
|
|
1019
|
+
v1.route('/approvals/reviewers', reviewersRouter(input.reviewerRegistry, authorizer));
|
|
1005
1020
|
}
|
|
1006
1021
|
if (input.reviewerBinding !== undefined && input.hitlBinding !== undefined) {
|
|
1007
1022
|
v1.route(
|
|
@@ -1029,9 +1044,22 @@ export function createApp(input: CreateAppInput): Hono<AppEnv> {
|
|
|
1029
1044
|
if (input.agentRegistry !== undefined) {
|
|
1030
1045
|
v1.route(
|
|
1031
1046
|
'/agents',
|
|
1032
|
-
agentsRouter(
|
|
1047
|
+
agentsRouter(
|
|
1048
|
+
input.agentRegistry,
|
|
1049
|
+
authorizer,
|
|
1050
|
+
input.toolRegistry,
|
|
1051
|
+
input.blockRegistry,
|
|
1052
|
+
input.agentReleases,
|
|
1053
|
+
{
|
|
1054
|
+
...(input.evalRunBinding !== undefined && { evalRuns: input.evalRunBinding }),
|
|
1055
|
+
...(input.projectBinding !== undefined && { projects: input.projectBinding }),
|
|
1056
|
+
},
|
|
1057
|
+
),
|
|
1033
1058
|
);
|
|
1034
1059
|
}
|
|
1060
|
+
if (input.agentReleases?.gatePolicies !== undefined) {
|
|
1061
|
+
v1.route('/gate-policies', gatePoliciesRouter(input.agentReleases.gatePolicies, authorizer));
|
|
1062
|
+
}
|
|
1035
1063
|
if (input.flowRegistry !== undefined) {
|
|
1036
1064
|
v1.route(
|
|
1037
1065
|
'/flows',
|
|
@@ -1039,7 +1067,11 @@ export function createApp(input: CreateAppInput): Hono<AppEnv> {
|
|
|
1039
1067
|
input.flowRegistry,
|
|
1040
1068
|
authorizer,
|
|
1041
1069
|
input.toolRegistry !== undefined && input.agentRegistry !== undefined
|
|
1042
|
-
? {
|
|
1070
|
+
? {
|
|
1071
|
+
tools: input.toolRegistry,
|
|
1072
|
+
agents: input.agentRegistry,
|
|
1073
|
+
...(input.agentReleases !== undefined && { live: input.agentReleases.live }),
|
|
1074
|
+
}
|
|
1043
1075
|
: undefined,
|
|
1044
1076
|
),
|
|
1045
1077
|
);
|
|
@@ -1149,6 +1181,7 @@ export function createApp(input: CreateAppInput): Hono<AppEnv> {
|
|
|
1149
1181
|
authorizer,
|
|
1150
1182
|
input.conversationBinding,
|
|
1151
1183
|
input.flowRegistry,
|
|
1184
|
+
input.reviewerBinding,
|
|
1152
1185
|
),
|
|
1153
1186
|
);
|
|
1154
1187
|
v1.route('/judge-classes', judgeClassesRouter(input.judgmentRegistry, authorizer));
|
|
@@ -1265,6 +1298,7 @@ export function createApp(input: CreateAppInput): Hono<AppEnv> {
|
|
|
1265
1298
|
}),
|
|
1266
1299
|
...(input.agentRegistry !== undefined && { agentRegistry: input.agentRegistry }),
|
|
1267
1300
|
...(input.flowRegistry !== undefined && { flowRegistry: input.flowRegistry }),
|
|
1301
|
+
...(input.agentReleases !== undefined && { liveVersions: input.agentReleases.live }),
|
|
1268
1302
|
// The deployments router's agents publish loop resolves the
|
|
1269
1303
|
// Default project through this binding. Optional at wiring so
|
|
1270
1304
|
// app compositions without a project binding still boot; the loop
|
|
@@ -1289,7 +1323,16 @@ export function createApp(input: CreateAppInput): Hono<AppEnv> {
|
|
|
1289
1323
|
// `evalSuiteRegistry` binding: the dispatcher itself surfaces
|
|
1290
1324
|
// `suite-not-found` when the caller-plugged binding can't resolve
|
|
1291
1325
|
// the suite id, so the API layer stays consumer-neutral.
|
|
1292
|
-
const evalRuns = evalRunsRouters(
|
|
1326
|
+
const evalRuns = evalRunsRouters(
|
|
1327
|
+
input.evalRunBinding,
|
|
1328
|
+
input.flowRegistry !== undefined
|
|
1329
|
+
? {
|
|
1330
|
+
flows: input.flowRegistry,
|
|
1331
|
+
...(input.agentRegistry !== undefined && { agents: input.agentRegistry }),
|
|
1332
|
+
...(input.toolRegistry !== undefined && { tools: input.toolRegistry }),
|
|
1333
|
+
}
|
|
1334
|
+
: undefined,
|
|
1335
|
+
);
|
|
1293
1336
|
v1.route('/eval-suites', evalRuns.start);
|
|
1294
1337
|
v1.route('/eval-runs', evalRuns.readback);
|
|
1295
1338
|
}
|
package/src/deploy-versions.ts
CHANGED
|
@@ -21,8 +21,7 @@ export interface PinnedDefinition {
|
|
|
21
21
|
*
|
|
22
22
|
* - `registered`: the definition's version was free, and is now
|
|
23
23
|
* registered with these pins;
|
|
24
|
-
* - `unchanged`: it's registered with the same definition and pins
|
|
25
|
-
* its project is gone, which a deploy has always left as it is);
|
|
24
|
+
* - `unchanged`: it's registered with the same definition and pins;
|
|
26
25
|
* - `reused`: an earlier deploy already registered this definition and
|
|
27
26
|
* these pins under another number (`version`), so a redeploy changes
|
|
28
27
|
* nothing;
|
|
@@ -39,8 +38,8 @@ export type DeployedVersionOutcome =
|
|
|
39
38
|
readonly pinChanges?: readonly PinChange[];
|
|
40
39
|
};
|
|
41
40
|
|
|
42
|
-
/** What registering one version did: stored it, found the number taken
|
|
43
|
-
export type PublishOutcome = 'ok' | 'taken'
|
|
41
|
+
/** What registering one version did: stored it, or found the number taken. */
|
|
42
|
+
export type PublishOutcome = 'ok' | 'taken';
|
|
44
43
|
|
|
45
44
|
export interface DeployVersionInput<T extends PinnedDefinition> {
|
|
46
45
|
/** Names the definition in an error, e.g. `agent "acme.matcher"`. */
|
|
@@ -51,7 +50,11 @@ export interface DeployVersionInput<T extends PinnedDefinition> {
|
|
|
51
50
|
readonly pinsDigest: string;
|
|
52
51
|
/** Every active version registered under the definition's id. */
|
|
53
52
|
readonly existing: readonly T[];
|
|
54
|
-
/**
|
|
53
|
+
/**
|
|
54
|
+
* Register one version: `taken` when the number is (an unregistered
|
|
55
|
+
* version holds it). A registry's refusal (e.g. `project-not-found`)
|
|
56
|
+
* throws `PublishRefused`, and the deploy rolls back.
|
|
57
|
+
*/
|
|
55
58
|
readonly publish: (version: T) => Promise<PublishOutcome>;
|
|
56
59
|
}
|
|
57
60
|
|
|
@@ -78,9 +81,7 @@ export async function deployVersion<T extends PinnedDefinition>(
|
|
|
78
81
|
if (atAuthored !== undefined && same(atAuthored)) return { kind: 'unchanged', version: authored };
|
|
79
82
|
if (atAuthored === undefined) {
|
|
80
83
|
const outcome = await input.publish({ ...definition, pins, pinsDigest });
|
|
81
|
-
if (outcome
|
|
82
|
-
return { kind: outcome === 'ok' ? 'registered' : 'unchanged', version: authored };
|
|
83
|
-
}
|
|
84
|
+
if (outcome === 'ok') return { kind: 'registered', version: authored };
|
|
84
85
|
// Taken with no active version: an unregistered version holds the
|
|
85
86
|
// number. Register the next one.
|
|
86
87
|
}
|
|
@@ -103,9 +104,7 @@ export async function deployVersion<T extends PinnedDefinition>(
|
|
|
103
104
|
}
|
|
104
105
|
const taken = new Set(existing.map((v) => v.version));
|
|
105
106
|
const version = await registerNextFree(input, reason, taken);
|
|
106
|
-
return version
|
|
107
|
-
? { kind: 'unchanged', version: authored }
|
|
108
|
-
: { kind: 'renumbered', version, reason, ...changes };
|
|
107
|
+
return { kind: 'renumbered', version, reason, ...changes };
|
|
109
108
|
}
|
|
110
109
|
|
|
111
110
|
/** Why the definition's version can't be registered as it is. */
|
|
@@ -120,13 +119,12 @@ function derivationReason(
|
|
|
120
119
|
/**
|
|
121
120
|
* Register the definition with its pins under the first free version
|
|
122
121
|
* after its own (`nextVersion`), recording where it came from.
|
|
123
|
-
* `undefined` when it was left as it is (its project is gone).
|
|
124
122
|
*/
|
|
125
123
|
async function registerNextFree<T extends PinnedDefinition>(
|
|
126
124
|
input: DeployVersionInput<T>,
|
|
127
125
|
reason: VersionDerivationReason,
|
|
128
126
|
taken: ReadonlySet<string>,
|
|
129
|
-
): Promise<string
|
|
127
|
+
): Promise<string> {
|
|
130
128
|
const { definition, pins, pinsDigest } = input;
|
|
131
129
|
const authored = definition.version;
|
|
132
130
|
let candidate = nextVersion(authored);
|
|
@@ -140,7 +138,6 @@ async function registerNextFree<T extends PinnedDefinition>(
|
|
|
140
138
|
derivedFrom: { version: authored, reason },
|
|
141
139
|
});
|
|
142
140
|
if (outcome === 'ok') return candidate;
|
|
143
|
-
if (outcome === 'skipped') return undefined;
|
|
144
141
|
// Taken: an unregistered version holds this one too.
|
|
145
142
|
}
|
|
146
143
|
candidate = nextVersion(candidate);
|
|
@@ -151,7 +148,7 @@ async function registerNextFree<T extends PinnedDefinition>(
|
|
|
151
148
|
}
|
|
152
149
|
|
|
153
150
|
/** A definition: everything but its version and what the runtime sets. */
|
|
154
|
-
function definitionKey(definition: PinnedDefinition): string {
|
|
151
|
+
export function definitionKey(definition: PinnedDefinition): string {
|
|
155
152
|
const { version: _v, pins: _p, pinsDigest: _d, derivedFrom: _f, ...rest } = definition;
|
|
156
153
|
return canonicalize(rest);
|
|
157
154
|
}
|
|
@@ -16,6 +16,7 @@ import type { ProjectId, Semver, TenantId } from '@kindgi/types';
|
|
|
16
16
|
import type { AgentRegistryBinding, AgentVersionRecord } from './agent-binding.js';
|
|
17
17
|
import { activeAgentVersions } from './agent-pins.js';
|
|
18
18
|
import type { BlockRegistryBinding } from './block-binding.js';
|
|
19
|
+
import { definitionKey } from './deploy-versions.js';
|
|
19
20
|
|
|
20
21
|
/** Which data-block pins to swap, by kind then block id → exact version. */
|
|
21
22
|
export interface PinSwaps {
|
|
@@ -48,6 +49,8 @@ export interface SwapIssue {
|
|
|
48
49
|
|
|
49
50
|
export type DeriveAgentVersionOutcome =
|
|
50
51
|
| { readonly kind: 'ok'; readonly agent: Agent }
|
|
52
|
+
/** An active version already has this definition and these pins: that one, unchanged. */
|
|
53
|
+
| { readonly kind: 'reused'; readonly agent: Agent }
|
|
51
54
|
| { readonly kind: 'not-found' }
|
|
52
55
|
| { readonly kind: 'unpinned' }
|
|
53
56
|
| { readonly kind: 'invalid'; readonly issues: readonly SwapIssue[] }
|
|
@@ -63,7 +66,10 @@ const MAX_TRIES = 100;
|
|
|
63
66
|
* reaching an agent without a code change. The new version is the old
|
|
64
67
|
* one's bag with those pins swapped (`derivedFrom: { version, reason:
|
|
65
68
|
* 'edited', label, by }`), numbered the next free patch after the
|
|
66
|
-
* agent's highest version (versions never change).
|
|
69
|
+
* agent's highest version (versions never change). If an active version
|
|
70
|
+
* already holds that definition and those pins (the same swap derived
|
|
71
|
+
* before, or a deploy that registered it), that version is returned
|
|
72
|
+
* unchanged (`reused`) rather than a duplicate.
|
|
67
73
|
*
|
|
68
74
|
* Only data-block pins swap: a swap must name a block the version
|
|
69
75
|
* already references (adding one is a code change), at a published,
|
|
@@ -114,6 +120,11 @@ async function publishNextFree(
|
|
|
114
120
|
): Promise<DeriveAgentVersionOutcome> {
|
|
115
121
|
const { agents, tenantId, agentId } = input;
|
|
116
122
|
const active = await activeAgentVersions(agents, tenantId, agentId);
|
|
123
|
+
// The same swap again (or a deploy that registered it) finds the
|
|
124
|
+
// version that already holds it: versions are never duplicated.
|
|
125
|
+
const key = definitionKey(derived);
|
|
126
|
+
const same = active.find((a) => a.pinsDigest === derived.pinsDigest && definitionKey(a) === key);
|
|
127
|
+
if (same !== undefined) return { kind: 'reused', agent: same };
|
|
117
128
|
const highest =
|
|
118
129
|
latestVersion(active.map((a) => a.version as unknown as string)) ??
|
|
119
130
|
(derived.version as unknown as string);
|
package/src/errors.ts
CHANGED
|
@@ -67,6 +67,7 @@ export const ERROR_CODE_TO_STATUS: Readonly<Record<string, number>> = {
|
|
|
67
67
|
'duplicate-edge-id': 409,
|
|
68
68
|
'agent-version-mismatch': 409,
|
|
69
69
|
'agent-already-registered': 409,
|
|
70
|
+
'registry-read-only': 409,
|
|
70
71
|
'agent-gone': 410,
|
|
71
72
|
'flow-gone': 410,
|
|
72
73
|
'policy-gone': 410,
|
|
@@ -160,6 +161,8 @@ export const ERROR_CODE_TO_STATUS: Readonly<Record<string, number>> = {
|
|
|
160
161
|
// Admin plane — policies.
|
|
161
162
|
'policy-not-found': 404,
|
|
162
163
|
'policy-already-registered': 409,
|
|
164
|
+
'policy-scope-taken': 409,
|
|
165
|
+
'policy-scope-changed': 409,
|
|
163
166
|
// Admin plane — eval suites.
|
|
164
167
|
'block-not-found': 404,
|
|
165
168
|
'block-already-registered': 409,
|
|
@@ -173,10 +176,33 @@ export const ERROR_CODE_TO_STATUS: Readonly<Record<string, number>> = {
|
|
|
173
176
|
'judge-class-not-found': 404,
|
|
174
177
|
'judge-class-name-taken': 409,
|
|
175
178
|
'judge-class-not-applicable': 400,
|
|
179
|
+
// The judge class is restricted (`assertableBy`), and the caller isn't one who may assert it.
|
|
180
|
+
'judge-class-not-allowed': 403,
|
|
181
|
+
// Live versions of agents and their promotions.
|
|
182
|
+
'agent-version-not-found': 404,
|
|
183
|
+
'promotion-not-found': 404,
|
|
184
|
+
'nothing-to-roll-back': 409,
|
|
185
|
+
'not-pinned': 409,
|
|
186
|
+
// The gate (evals step 4b).
|
|
187
|
+
'gate-failed': 422,
|
|
188
|
+
'promotion-superseded': 409,
|
|
189
|
+
// The binding can't record a gated promotion, so one a policy applies to is refused.
|
|
190
|
+
'promotion-gate-unsupported': 501,
|
|
191
|
+
'separate-approver-required': 403,
|
|
192
|
+
'gate-policy-not-found': 404,
|
|
193
|
+
'gate-policy-already-registered': 409,
|
|
194
|
+
'gate-policy-scope-taken': 409,
|
|
195
|
+
'gate-policy-scope-changed': 409,
|
|
196
|
+
'gate-policy-scope-unpinned': 409,
|
|
197
|
+
'gate-policy-needs-pin': 409,
|
|
198
|
+
/** Unregister: the version is live in a scope; move that pin first. */
|
|
199
|
+
'agent-version-live': 409,
|
|
176
200
|
'run-not-finished': 409,
|
|
177
201
|
'item-not-found': 400,
|
|
178
202
|
// The judgment binding can't list judged runs, so no test sets from judgments.
|
|
179
203
|
'test-sets-not-supported': 501,
|
|
204
|
+
// Authorization is enforced, but a membership change can't be kept in step with it.
|
|
205
|
+
'authz-membership-unsupported': 501,
|
|
180
206
|
'eval-run-already-terminal': 409,
|
|
181
207
|
'dispatcher-not-registered': 422,
|
|
182
208
|
'dispatcher-input-invalid': 400,
|
package/src/eval-case-binding.ts
CHANGED
|
@@ -31,6 +31,13 @@ export interface JudgedItemSummary extends JudgedItem {
|
|
|
31
31
|
readonly yesWeight: number;
|
|
32
32
|
/** The weight behind all judgments of the item. */
|
|
33
33
|
readonly totalWeight: number;
|
|
34
|
+
/**
|
|
35
|
+
* The same two, counting only judgments recorded while their class was
|
|
36
|
+
* restricted (`Judgment.restricted`), for a comparison weighted
|
|
37
|
+
* `restricted-only`. Absent from a test set built before restrictions:
|
|
38
|
+
* no restricted evidence.
|
|
39
|
+
*/
|
|
40
|
+
readonly restricted?: { readonly yesWeight: number; readonly totalWeight: number };
|
|
34
41
|
/** The reasons given, newest first. */
|
|
35
42
|
readonly reasons: readonly { readonly verdict: 'yes' | 'no'; readonly reason: string }[];
|
|
36
43
|
}
|
package/src/eval-run-binding.ts
CHANGED
|
@@ -2,8 +2,18 @@
|
|
|
2
2
|
// Copyright (C) 2026 Kindgi Inc.
|
|
3
3
|
|
|
4
4
|
import type { AgentId } from '@kindgi/agents';
|
|
5
|
+
import type { FlowVersionOverrides } from '@kindgi/flow';
|
|
5
6
|
import type { Scope } from '@kindgi/platform';
|
|
6
|
-
import type {
|
|
7
|
+
import type {
|
|
8
|
+
Cursor,
|
|
9
|
+
FlowId,
|
|
10
|
+
ProjectId,
|
|
11
|
+
RunId,
|
|
12
|
+
ScopeSegment,
|
|
13
|
+
Semver,
|
|
14
|
+
TenantId,
|
|
15
|
+
Timestamp,
|
|
16
|
+
} from '@kindgi/types';
|
|
7
17
|
|
|
8
18
|
import type { EvalKind } from './eval-suite-binding.js';
|
|
9
19
|
|
|
@@ -122,7 +132,8 @@ export interface EvalRunFilter {
|
|
|
122
132
|
* What a comparison eval run compares the candidate against:
|
|
123
133
|
* - `'recorded'`: the output each case recorded (what was judged);
|
|
124
134
|
* - `{ agentId, version }`: that version, replayed under the same rules;
|
|
125
|
-
* - `{ live }`: the version live in a scope
|
|
135
|
+
* - `{ live }`: the version live in a scope: a project, and a segment
|
|
136
|
+
* path in it (coarse to fine, as live versions resolve it).
|
|
126
137
|
*/
|
|
127
138
|
export type EvalBaseline =
|
|
128
139
|
| 'recorded'
|
|
@@ -130,13 +141,22 @@ export type EvalBaseline =
|
|
|
130
141
|
| {
|
|
131
142
|
readonly live: {
|
|
132
143
|
readonly projectId?: ProjectId;
|
|
133
|
-
|
|
144
|
+
/** A segment path in `projectId`, coarse to fine. */
|
|
145
|
+
readonly segments?: readonly ScopeSegment[];
|
|
134
146
|
};
|
|
135
147
|
};
|
|
136
148
|
|
|
137
149
|
/** Whether replayed reads use the past run's results when it has them, or run live. */
|
|
138
150
|
export type EvalReads = 'recorded' | 'live';
|
|
139
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Which judgments a comparison counts: every judgment at its class's
|
|
154
|
+
* weight (`as-recorded`, the default), or only those recorded while their
|
|
155
|
+
* class was restricted (`assertableBy`), the others weighing 0
|
|
156
|
+
* (`restricted-only`; T200).
|
|
157
|
+
*/
|
|
158
|
+
export type EvalClassWeights = 'as-recorded' | 'restricted-only';
|
|
159
|
+
|
|
140
160
|
/** How a comparison eval run runs its cases (absent: the run isn't a comparison). */
|
|
141
161
|
export interface EvalComparison {
|
|
142
162
|
readonly baseline: EvalBaseline;
|
|
@@ -145,6 +165,14 @@ export interface EvalComparison {
|
|
|
145
165
|
readonly repetitions: number;
|
|
146
166
|
/** How many ranked items `weightedPrecisionAtK` looks at (1–100). */
|
|
147
167
|
readonly k: number;
|
|
168
|
+
/**
|
|
169
|
+
* For a flow candidate: agents and tools its replays run at other exact
|
|
170
|
+
* versions than the flow version's pins ("this flow, with `acme.scorer`
|
|
171
|
+
* at 0.4.0"), without publishing a new flow version.
|
|
172
|
+
*/
|
|
173
|
+
readonly versions?: FlowVersionOverrides;
|
|
174
|
+
/** Which judgments count (absent: `as-recorded`). */
|
|
175
|
+
readonly classWeights?: EvalClassWeights;
|
|
148
176
|
}
|
|
149
177
|
|
|
150
178
|
export interface EvalRunStartInput {
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { randomUUID } from 'node:crypto';
|
|
5
5
|
|
|
6
6
|
import type { ReplayTurnReport } from '@kindgi/agents';
|
|
7
|
+
import type { FlowVersionOverrides } from '@kindgi/flow';
|
|
7
8
|
import type { RunReplayRef } from '@kindgi/runtime';
|
|
8
9
|
import type { ProjectId, RunId, TenantId, Timestamp } from '@kindgi/types';
|
|
9
10
|
|
|
@@ -63,6 +64,8 @@ export interface EvalRunSubjectInvokeInput {
|
|
|
63
64
|
readonly replay?: RunReplayRef;
|
|
64
65
|
/** The conversation before the past turn, oldest first: a replayed turn starts from it. */
|
|
65
66
|
readonly history?: readonly unknown[];
|
|
67
|
+
/** A flow target's agents and tools at other exact versions (`EvalComparison.versions`). */
|
|
68
|
+
readonly versions?: FlowVersionOverrides;
|
|
66
69
|
}
|
|
67
70
|
|
|
68
71
|
export interface EvalRunSubjectInvokeOutcome {
|
package/src/flow-binding.ts
CHANGED
|
@@ -6,6 +6,8 @@ import type { Flow } from '@kindgi/flow';
|
|
|
6
6
|
import type { Scope } from '@kindgi/platform';
|
|
7
7
|
import type { Cursor, FlowId, ProjectId, TenantId } from '@kindgi/types';
|
|
8
8
|
|
|
9
|
+
import type { RegistryReadOnly } from './registry-read-only.js';
|
|
10
|
+
|
|
9
11
|
/**
|
|
10
12
|
* Caller-plugged surface for the flow catalog. Mirrors
|
|
11
13
|
* `AgentRegistryBinding` 1:1 — the API package does NOT own registry
|
|
@@ -26,6 +28,13 @@ import type { Cursor, FlowId, ProjectId, TenantId } from '@kindgi/types';
|
|
|
26
28
|
* cursor round-trips as a string; it never inspects the payload.
|
|
27
29
|
*/
|
|
28
30
|
export interface FlowRegistryBinding {
|
|
31
|
+
/**
|
|
32
|
+
* Set when this registry takes no writes (under `kindgi dev`, the
|
|
33
|
+
* pack's files are the source of its flows): every write is refused
|
|
34
|
+
* with `409 registry-read-only` and this reason, before the binding is
|
|
35
|
+
* called. See `RegistryReadOnly`.
|
|
36
|
+
*/
|
|
37
|
+
readonly readOnly?: RegistryReadOnly;
|
|
29
38
|
/**
|
|
30
39
|
* Cursor-paginated list of flows (latest version per id, sorted by
|
|
31
40
|
* flow id ascending). Optional `nameFilter` is a prefix match on the
|
package/src/flow-pins.ts
CHANGED
|
@@ -11,6 +11,8 @@ import type { AgentRegistryBinding } from './agent-binding.js';
|
|
|
11
11
|
import { type UnpinnableRef, activeToolVersions } from './agent-pins.js';
|
|
12
12
|
import { type DeployedVersionOutcome, deployVersion } from './deploy-versions.js';
|
|
13
13
|
import type { FlowRegistryBinding } from './flow-binding.js';
|
|
14
|
+
import type { LiveVersionBinding } from './live-version-binding.js';
|
|
15
|
+
import { PublishRefused } from './publish-refused.js';
|
|
14
16
|
import type { ToolRegistryBinding } from './tool-binding.js';
|
|
15
17
|
|
|
16
18
|
export type FlowPinsOutcome =
|
|
@@ -18,18 +20,32 @@ export type FlowPinsOutcome =
|
|
|
18
20
|
| { readonly kind: 'unpinnable'; readonly issues: readonly UnpinnableRef[] };
|
|
19
21
|
|
|
20
22
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
* Where a flow version is published, so its agent pins are what a run
|
|
24
|
+
* there would get: the agents' live versions for the project.
|
|
25
|
+
*/
|
|
26
|
+
export interface FlowPinsLive {
|
|
27
|
+
readonly binding: LiveVersionBinding;
|
|
28
|
+
readonly projectId: ProjectId;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Pin a flow version when it's published, to what a run would have bound
|
|
33
|
+
* then, so every run of the version uses those: each tool it runs to its
|
|
34
|
+
* latest active version, and each agent it runs at no named version to
|
|
35
|
+
* the version a run that names none gets in the flow's project. That's
|
|
36
|
+
* the agent's live version there (project, then its org, then the
|
|
37
|
+
* tenant), with `live`, else its latest. A gated agent's flow runs what
|
|
38
|
+
* its promotions let go live, not an unpromoted newer version. A tool or
|
|
39
|
+
* agent with no published version makes the flow unpinnable: the caller
|
|
40
|
+
* refuses the publish, naming each, rather than store a partly pinned
|
|
41
|
+
* version.
|
|
27
42
|
*/
|
|
28
43
|
export async function resolveFlowPins(
|
|
29
44
|
tools: ToolRegistryBinding,
|
|
30
45
|
agents: AgentRegistryBinding,
|
|
31
46
|
tenantId: TenantId,
|
|
32
47
|
flow: Flow,
|
|
48
|
+
live?: FlowPinsLive,
|
|
33
49
|
): Promise<FlowPinsOutcome> {
|
|
34
50
|
const refs = flowRefs(flow);
|
|
35
51
|
const pinned: { tools: Record<string, string>; agents: Record<string, string> } = {
|
|
@@ -47,8 +63,8 @@ export async function resolveFlowPins(
|
|
|
47
63
|
});
|
|
48
64
|
}
|
|
49
65
|
for (const id of refs.agents) {
|
|
50
|
-
const
|
|
51
|
-
if (
|
|
66
|
+
const version = await implicitAgentVersion(agents, live, tenantId, id);
|
|
67
|
+
if (version !== undefined) pinned.agents[id] = version;
|
|
52
68
|
else
|
|
53
69
|
issues.push({
|
|
54
70
|
path: '/nodes',
|
|
@@ -59,6 +75,29 @@ export async function resolveFlowPins(
|
|
|
59
75
|
return { kind: 'ok', pins: pinned };
|
|
60
76
|
}
|
|
61
77
|
|
|
78
|
+
/**
|
|
79
|
+
* The version a run of agent `id` that names none gets now: its live
|
|
80
|
+
* version for `live`'s project when one is pinned on the way up, else its
|
|
81
|
+
* latest; `undefined` when it has no published version.
|
|
82
|
+
*/
|
|
83
|
+
async function implicitAgentVersion(
|
|
84
|
+
agents: AgentRegistryBinding,
|
|
85
|
+
live: FlowPinsLive | undefined,
|
|
86
|
+
tenantId: TenantId,
|
|
87
|
+
id: string,
|
|
88
|
+
): Promise<string | undefined> {
|
|
89
|
+
if (live !== undefined) {
|
|
90
|
+
const resolved = await live.binding.resolve({
|
|
91
|
+
tenantId,
|
|
92
|
+
agentId: id,
|
|
93
|
+
projectId: live.projectId,
|
|
94
|
+
});
|
|
95
|
+
if (resolved !== null) return resolved.version as unknown as string;
|
|
96
|
+
}
|
|
97
|
+
const latest = await agents.get({ tenantId, agentId: id as AgentId });
|
|
98
|
+
return latest === null ? undefined : (latest.version as unknown as string);
|
|
99
|
+
}
|
|
100
|
+
|
|
62
101
|
export interface PublishDeployedFlowInput {
|
|
63
102
|
readonly flows: FlowRegistryBinding;
|
|
64
103
|
readonly tenantId: TenantId;
|
|
@@ -83,7 +122,8 @@ export async function publishDeployedFlow(
|
|
|
83
122
|
publish: async (version) => {
|
|
84
123
|
const outcome = await flows.publish({ tenantId, projectId, flow: version, enqueueTuples });
|
|
85
124
|
if (outcome.kind === 'ok') return 'ok';
|
|
86
|
-
|
|
125
|
+
if (outcome.kind === 'already-registered') return 'taken';
|
|
126
|
+
throw new PublishRefused('flow', `${flow.id}@${version.version}`, outcome.kind);
|
|
87
127
|
},
|
|
88
128
|
});
|
|
89
129
|
}
|