@kindgi/api 0.1.4-rc.1 → 0.1.4-rc.3
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 +12 -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 +23 -4
- package/dist/app.js.map +1 -1
- package/dist/deploy-versions.d.ts +8 -5
- package/dist/deploy-versions.d.ts.map +1 -1
- package/dist/deploy-versions.js +3 -9
- package/dist/deploy-versions.js.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +25 -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 +14 -3
- package/dist/eval-run-binding.d.ts.map +1 -1
- package/dist/eval-run-binding.js.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/handler-binding.d.ts +14 -2
- package/dist/handler-binding.d.ts.map +1 -1
- package/dist/hitl-binding.d.ts +5 -0
- package/dist/hitl-binding.d.ts.map +1 -1
- package/dist/index.d.ts +10 -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 +16 -1
- package/dist/judged-dispatcher.d.ts.map +1 -1
- package/dist/judged-dispatcher.js +45 -6
- 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 +512 -22
- package/dist/openapi/operations.js.map +1 -1
- package/dist/openapi/schemas.d.ts +37 -0
- package/dist/openapi/schemas.d.ts.map +1 -1
- package/dist/openapi/schemas.js +709 -5
- 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/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 +31 -3
- package/dist/routes/agents.js.map +1 -1
- package/dist/routes/approvals.d.ts.map +1 -1
- package/dist/routes/approvals.js +40 -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/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 +33 -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 +2 -1
- package/dist/routes/eval-comparison.d.ts.map +1 -1
- package/dist/routes/eval-comparison.js +22 -9
- package/dist/routes/eval-comparison.js.map +1 -1
- package/dist/routes/flows.d.ts +14 -7
- package/dist/routes/flows.d.ts.map +1 -1
- package/dist/routes/flows.js +10 -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/hierarchy-errors.d.ts +11 -0
- package/dist/routes/hierarchy-errors.d.ts.map +1 -1
- package/dist/routes/hierarchy-errors.js +13 -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 +47 -3
- 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 +22 -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 +42 -3
- package/dist/routes/teams.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/openapi.json +7008 -3624
- package/package.json +21 -21
- package/src/agent-binding.ts +12 -2
- package/src/agent-pins.ts +3 -1
- package/src/app.ts +37 -3
- package/src/deploy-versions.ts +11 -14
- package/src/errors.ts +25 -0
- package/src/eval-case-binding.ts +7 -0
- package/src/eval-run-binding.ts +24 -3
- package/src/flow-pins.ts +49 -9
- package/src/gate-policy-binding.ts +171 -0
- package/src/gate.ts +467 -0
- package/src/handler-binding.ts +14 -2
- package/src/hitl-binding.ts +5 -0
- package/src/index.ts +46 -1
- package/src/judged-dispatcher.ts +74 -6
- 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 +619 -22
- package/src/openapi/schemas.ts +788 -5
- package/src/publish-refused.ts +31 -0
- package/src/retention-binding.ts +30 -0
- package/src/routes/agent-releases.ts +581 -0
- package/src/routes/agents.ts +43 -2
- package/src/routes/approvals.ts +58 -8
- package/src/routes/audit.ts +10 -0
- package/src/routes/auth.ts +1 -1
- package/src/routes/conversations.ts +31 -3
- package/src/routes/deployments.ts +50 -5
- package/src/routes/env.ts +1 -0
- package/src/routes/eval-comparison.ts +24 -10
- package/src/routes/flows.ts +27 -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/hierarchy-errors.ts +14 -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 +54 -2
- package/src/routes/retention.ts +21 -2
- package/src/routes/reviewers.ts +23 -4
- package/src/routes/runs.ts +33 -20
- package/src/routes/secrets.ts +2 -0
- package/src/routes/segments.ts +75 -0
- package/src/routes/teams.ts +53 -3
- package/src/routes/uuid-param.ts +28 -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.3",
|
|
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.3",
|
|
36
|
+
"@kindgi/audit-events": "0.1.4-rc.3",
|
|
37
|
+
"@kindgi/compliance": "0.1.4-rc.3",
|
|
38
|
+
"@kindgi/authz": "0.1.4-rc.3",
|
|
39
|
+
"@kindgi/blob-binding": "0.1.4-rc.3",
|
|
40
|
+
"@kindgi/capabilities": "0.1.4-rc.3",
|
|
41
|
+
"@kindgi/crypto": "0.1.4-rc.3",
|
|
42
|
+
"@kindgi/flow": "0.1.4-rc.3",
|
|
43
|
+
"@kindgi/guardrails": "0.1.4-rc.3",
|
|
44
|
+
"@kindgi/memory": "0.1.4-rc.3",
|
|
45
|
+
"@kindgi/platform": "0.1.4-rc.3",
|
|
46
|
+
"@kindgi/provenance": "0.1.4-rc.3",
|
|
47
|
+
"@kindgi/runtime": "0.1.4-rc.3",
|
|
48
|
+
"@kindgi/policy-contract": "0.1.4-rc.3",
|
|
49
|
+
"@kindgi/schema": "0.1.4-rc.3",
|
|
50
|
+
"@kindgi/tools": "0.1.4-rc.3",
|
|
51
|
+
"@kindgi/types": "0.1.4-rc.3",
|
|
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.3",
|
|
57
|
+
"@kindgi/specs": "0.1.4-rc.3",
|
|
58
|
+
"@kindgi/testing": "0.1.4-rc.3",
|
|
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,7 @@
|
|
|
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
8
|
|
|
9
9
|
import type { RegistryReadOnly } from './registry-read-only.js';
|
|
10
10
|
|
|
@@ -80,7 +80,8 @@ export interface AgentRegistryBinding {
|
|
|
80
80
|
* Soft-tombstone a specific `(agentId, version)`. Returns
|
|
81
81
|
* `{ unregistered: true }` on success; `{ unregistered: false }`
|
|
82
82
|
* when the version was unknown OR already tombstoned — the route
|
|
83
|
-
* 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
|
|
84
85
|
* unregister leaves NO active versions, the agent has no latest
|
|
85
86
|
* version — it enters the derived "retired" state
|
|
86
87
|
* (GET returns 410 gone) but the identity persists. Publish of a
|
|
@@ -238,6 +239,15 @@ export type AgentPublishOutcome =
|
|
|
238
239
|
|
|
239
240
|
export type AgentUnregisterOutcome = {
|
|
240
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[];
|
|
241
251
|
};
|
|
242
252
|
|
|
243
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
|
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);
|
package/src/errors.ts
CHANGED
|
@@ -161,6 +161,8 @@ export const ERROR_CODE_TO_STATUS: Readonly<Record<string, number>> = {
|
|
|
161
161
|
// Admin plane — policies.
|
|
162
162
|
'policy-not-found': 404,
|
|
163
163
|
'policy-already-registered': 409,
|
|
164
|
+
'policy-scope-taken': 409,
|
|
165
|
+
'policy-scope-changed': 409,
|
|
164
166
|
// Admin plane — eval suites.
|
|
165
167
|
'block-not-found': 404,
|
|
166
168
|
'block-already-registered': 409,
|
|
@@ -174,10 +176,33 @@ export const ERROR_CODE_TO_STATUS: Readonly<Record<string, number>> = {
|
|
|
174
176
|
'judge-class-not-found': 404,
|
|
175
177
|
'judge-class-name-taken': 409,
|
|
176
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,
|
|
177
200
|
'run-not-finished': 409,
|
|
178
201
|
'item-not-found': 400,
|
|
179
202
|
// The judgment binding can't list judged runs, so no test sets from judgments.
|
|
180
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,
|
|
181
206
|
'eval-run-already-terminal': 409,
|
|
182
207
|
'dispatcher-not-registered': 422,
|
|
183
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
|
@@ -4,7 +4,16 @@
|
|
|
4
4
|
import type { AgentId } from '@kindgi/agents';
|
|
5
5
|
import type { FlowVersionOverrides } from '@kindgi/flow';
|
|
6
6
|
import type { Scope } from '@kindgi/platform';
|
|
7
|
-
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';
|
|
8
17
|
|
|
9
18
|
import type { EvalKind } from './eval-suite-binding.js';
|
|
10
19
|
|
|
@@ -123,7 +132,8 @@ export interface EvalRunFilter {
|
|
|
123
132
|
* What a comparison eval run compares the candidate against:
|
|
124
133
|
* - `'recorded'`: the output each case recorded (what was judged);
|
|
125
134
|
* - `{ agentId, version }`: that version, replayed under the same rules;
|
|
126
|
-
* - `{ 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).
|
|
127
137
|
*/
|
|
128
138
|
export type EvalBaseline =
|
|
129
139
|
| 'recorded'
|
|
@@ -131,13 +141,22 @@ export type EvalBaseline =
|
|
|
131
141
|
| {
|
|
132
142
|
readonly live: {
|
|
133
143
|
readonly projectId?: ProjectId;
|
|
134
|
-
|
|
144
|
+
/** A segment path in `projectId`, coarse to fine. */
|
|
145
|
+
readonly segments?: readonly ScopeSegment[];
|
|
135
146
|
};
|
|
136
147
|
};
|
|
137
148
|
|
|
138
149
|
/** Whether replayed reads use the past run's results when it has them, or run live. */
|
|
139
150
|
export type EvalReads = 'recorded' | 'live';
|
|
140
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
|
+
|
|
141
160
|
/** How a comparison eval run runs its cases (absent: the run isn't a comparison). */
|
|
142
161
|
export interface EvalComparison {
|
|
143
162
|
readonly baseline: EvalBaseline;
|
|
@@ -152,6 +171,8 @@ export interface EvalComparison {
|
|
|
152
171
|
* at 0.4.0"), without publishing a new flow version.
|
|
153
172
|
*/
|
|
154
173
|
readonly versions?: FlowVersionOverrides;
|
|
174
|
+
/** Which judgments count (absent: `as-recorded`). */
|
|
175
|
+
readonly classWeights?: EvalClassWeights;
|
|
155
176
|
}
|
|
156
177
|
|
|
157
178
|
export interface EvalRunStartInput {
|
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
|
}
|