@graphorin/server 0.5.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/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +116 -0
- package/dist/app.d.ts +174 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +684 -0
- package/dist/app.js.map +1 -0
- package/dist/commentary/audit-bridge.d.ts +57 -0
- package/dist/commentary/audit-bridge.d.ts.map +1 -0
- package/dist/commentary/audit-bridge.js +92 -0
- package/dist/commentary/audit-bridge.js.map +1 -0
- package/dist/commentary/built-in-patterns.d.ts +24 -0
- package/dist/commentary/built-in-patterns.d.ts.map +1 -0
- package/dist/commentary/built-in-patterns.js +62 -0
- package/dist/commentary/built-in-patterns.js.map +1 -0
- package/dist/commentary/index.d.ts +5 -0
- package/dist/commentary/index.js +5 -0
- package/dist/commentary/sanitizer.d.ts +37 -0
- package/dist/commentary/sanitizer.d.ts.map +1 -0
- package/dist/commentary/sanitizer.js +182 -0
- package/dist/commentary/sanitizer.js.map +1 -0
- package/dist/commentary/types.d.ts +120 -0
- package/dist/commentary/types.d.ts.map +1 -0
- package/dist/config.d.ts +760 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +217 -0
- package/dist/config.js.map +1 -0
- package/dist/consolidator/daemon.d.ts +88 -0
- package/dist/consolidator/daemon.d.ts.map +1 -0
- package/dist/consolidator/daemon.js +54 -0
- package/dist/consolidator/daemon.js.map +1 -0
- package/dist/consolidator/index.d.ts +2 -0
- package/dist/consolidator/index.js +3 -0
- package/dist/errors/index.d.ts +104 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +131 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/health/checks.d.ts +120 -0
- package/dist/health/checks.d.ts.map +1 -0
- package/dist/health/checks.js +127 -0
- package/dist/health/checks.js.map +1 -0
- package/dist/health/index.d.ts +3 -0
- package/dist/health/index.js +4 -0
- package/dist/health/routes.d.ts +66 -0
- package/dist/health/routes.d.ts.map +1 -0
- package/dist/health/routes.js +96 -0
- package/dist/health/routes.js.map +1 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +86 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/context.d.ts +66 -0
- package/dist/internal/context.d.ts.map +1 -0
- package/dist/internal/ids.js +21 -0
- package/dist/internal/ids.js.map +1 -0
- package/dist/internal/json.js +46 -0
- package/dist/internal/json.js.map +1 -0
- package/dist/lifecycle/hooks.d.ts +57 -0
- package/dist/lifecycle/hooks.d.ts.map +1 -0
- package/dist/lifecycle/index.d.ts +2 -0
- package/dist/lifecycle/index.js +3 -0
- package/dist/lifecycle/pre-bind.d.ts +38 -0
- package/dist/lifecycle/pre-bind.d.ts.map +1 -0
- package/dist/lifecycle/pre-bind.js +62 -0
- package/dist/lifecycle/pre-bind.js.map +1 -0
- package/dist/metrics/catalog.d.ts +34 -0
- package/dist/metrics/catalog.d.ts.map +1 -0
- package/dist/metrics/catalog.js +61 -0
- package/dist/metrics/catalog.js.map +1 -0
- package/dist/metrics/index.d.ts +3 -0
- package/dist/metrics/index.js +4 -0
- package/dist/metrics/registry.d.ts +63 -0
- package/dist/metrics/registry.d.ts.map +1 -0
- package/dist/metrics/registry.js +222 -0
- package/dist/metrics/registry.js.map +1 -0
- package/dist/middleware/audit.d.ts +47 -0
- package/dist/middleware/audit.d.ts.map +1 -0
- package/dist/middleware/audit.js +71 -0
- package/dist/middleware/audit.js.map +1 -0
- package/dist/middleware/auth.d.ts +50 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +164 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware/cors.d.ts +15 -0
- package/dist/middleware/cors.d.ts.map +1 -0
- package/dist/middleware/cors.js +45 -0
- package/dist/middleware/cors.js.map +1 -0
- package/dist/middleware/csrf.d.ts +15 -0
- package/dist/middleware/csrf.d.ts.map +1 -0
- package/dist/middleware/csrf.js +77 -0
- package/dist/middleware/csrf.js.map +1 -0
- package/dist/middleware/idempotency.d.ts +41 -0
- package/dist/middleware/idempotency.d.ts.map +1 -0
- package/dist/middleware/idempotency.js +198 -0
- package/dist/middleware/idempotency.js.map +1 -0
- package/dist/middleware/index.d.ts +9 -0
- package/dist/middleware/index.js +10 -0
- package/dist/middleware/rate-limit.d.ts +17 -0
- package/dist/middleware/rate-limit.d.ts.map +1 -0
- package/dist/middleware/rate-limit.js +47 -0
- package/dist/middleware/rate-limit.js.map +1 -0
- package/dist/middleware/request-state.d.ts +27 -0
- package/dist/middleware/request-state.d.ts.map +1 -0
- package/dist/middleware/request-state.js +42 -0
- package/dist/middleware/request-state.js.map +1 -0
- package/dist/middleware/scope.d.ts +24 -0
- package/dist/middleware/scope.d.ts.map +1 -0
- package/dist/middleware/scope.js +50 -0
- package/dist/middleware/scope.js.map +1 -0
- package/dist/registry/index.d.ts +135 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +96 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/replay/index.d.ts +2 -0
- package/dist/replay/index.js +3 -0
- package/dist/replay/routes.d.ts +46 -0
- package/dist/replay/routes.d.ts.map +1 -0
- package/dist/replay/routes.js +189 -0
- package/dist/replay/routes.js.map +1 -0
- package/dist/routes/agents.d.ts +41 -0
- package/dist/routes/agents.d.ts.map +1 -0
- package/dist/routes/agents.js +213 -0
- package/dist/routes/agents.js.map +1 -0
- package/dist/routes/audit.d.ts +57 -0
- package/dist/routes/audit.d.ts.map +1 -0
- package/dist/routes/audit.js +116 -0
- package/dist/routes/audit.js.map +1 -0
- package/dist/routes/auth.d.ts +26 -0
- package/dist/routes/auth.d.ts.map +1 -0
- package/dist/routes/auth.js +54 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/health.d.ts +22 -0
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +33 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/index.d.ts +10 -0
- package/dist/routes/index.js +12 -0
- package/dist/routes/mcp.d.ts +32 -0
- package/dist/routes/mcp.d.ts.map +1 -0
- package/dist/routes/mcp.js +61 -0
- package/dist/routes/mcp.js.map +1 -0
- package/dist/routes/memory.d.ts +141 -0
- package/dist/routes/memory.d.ts.map +1 -0
- package/dist/routes/memory.js +102 -0
- package/dist/routes/memory.js.map +1 -0
- package/dist/routes/sessions.d.ts +54 -0
- package/dist/routes/sessions.d.ts.map +1 -0
- package/dist/routes/sessions.js +135 -0
- package/dist/routes/sessions.js.map +1 -0
- package/dist/routes/skills.d.ts +34 -0
- package/dist/routes/skills.d.ts.map +1 -0
- package/dist/routes/skills.js +54 -0
- package/dist/routes/skills.js.map +1 -0
- package/dist/routes/tokens.d.ts +25 -0
- package/dist/routes/tokens.d.ts.map +1 -0
- package/dist/routes/tokens.js +87 -0
- package/dist/routes/tokens.js.map +1 -0
- package/dist/routes/workflows.d.ts +27 -0
- package/dist/routes/workflows.d.ts.map +1 -0
- package/dist/routes/workflows.js +220 -0
- package/dist/routes/workflows.js.map +1 -0
- package/dist/runtime/run-state.d.ts +158 -0
- package/dist/runtime/run-state.d.ts.map +1 -0
- package/dist/runtime/run-state.js +182 -0
- package/dist/runtime/run-state.js.map +1 -0
- package/dist/sse/events.d.ts +44 -0
- package/dist/sse/events.d.ts.map +1 -0
- package/dist/sse/events.js +200 -0
- package/dist/sse/events.js.map +1 -0
- package/dist/sse/index.d.ts +2 -0
- package/dist/sse/index.js +3 -0
- package/dist/triggers/daemon.d.ts +74 -0
- package/dist/triggers/daemon.d.ts.map +1 -0
- package/dist/triggers/daemon.js +114 -0
- package/dist/triggers/daemon.js.map +1 -0
- package/dist/triggers/index.d.ts +3 -0
- package/dist/triggers/index.js +4 -0
- package/dist/triggers/routes.d.ts +21 -0
- package/dist/triggers/routes.d.ts.map +1 -0
- package/dist/triggers/routes.js +117 -0
- package/dist/triggers/routes.js.map +1 -0
- package/dist/ws/dispatcher.d.ts +169 -0
- package/dist/ws/dispatcher.d.ts.map +1 -0
- package/dist/ws/dispatcher.js +303 -0
- package/dist/ws/dispatcher.js.map +1 -0
- package/dist/ws/index.d.ts +6 -0
- package/dist/ws/index.js +7 -0
- package/dist/ws/replay-buffer.d.ts +47 -0
- package/dist/ws/replay-buffer.d.ts.map +1 -0
- package/dist/ws/replay-buffer.js +88 -0
- package/dist/ws/replay-buffer.js.map +1 -0
- package/dist/ws/subjects.d.ts +71 -0
- package/dist/ws/subjects.d.ts.map +1 -0
- package/dist/ws/subjects.js +112 -0
- package/dist/ws/subjects.js.map +1 -0
- package/dist/ws/ticket.d.ts +74 -0
- package/dist/ws/ticket.d.ts.map +1 -0
- package/dist/ws/ticket.js +112 -0
- package/dist/ws/ticket.js.map +1 -0
- package/dist/ws/upgrade.d.ts +63 -0
- package/dist/ws/upgrade.d.ts.map +1 -0
- package/dist/ws/upgrade.js +324 -0
- package/dist/ws/upgrade.js.map +1 -0
- package/package.json +156 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ServerVariables } from "../internal/context.js";
|
|
2
|
+
import { MiddlewareHandler } from "hono";
|
|
3
|
+
import { ParsedScope } from "@graphorin/security/auth";
|
|
4
|
+
|
|
5
|
+
//#region src/middleware/scope.d.ts
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Required-scope spec accepted by {@link createScopeMiddleware}. Either
|
|
9
|
+
* a single string (`'agents:invoke'`), a parsed scope, or a function
|
|
10
|
+
* that derives the required scope from the request (e.g. to insert the
|
|
11
|
+
* `:id` segment lazily).
|
|
12
|
+
*
|
|
13
|
+
* @stable
|
|
14
|
+
*/
|
|
15
|
+
type ScopeRequirement = string | ParsedScope | ((path: string, params: Record<string, string>) => string | ParsedScope);
|
|
16
|
+
/**
|
|
17
|
+
* @stable
|
|
18
|
+
*/
|
|
19
|
+
declare function createScopeMiddleware(requirement: ScopeRequirement): MiddlewareHandler<{
|
|
20
|
+
Variables: ServerVariables;
|
|
21
|
+
}>;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { ScopeRequirement, createScopeMiddleware };
|
|
24
|
+
//# sourceMappingURL=scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.d.ts","names":[],"sources":["../../src/middleware/scope.ts"],"sourcesContent":[],"mappings":";;;;;;AA6BA;;;;;;;;KARY,gBAAA,YAER,sCACwB,oCAAoC;;;;iBAKhD,qBAAA,cACD,mBACZ;aAA+B"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { parseScope, scopeMatches } from "@graphorin/security/auth";
|
|
2
|
+
|
|
3
|
+
//#region src/middleware/scope.ts
|
|
4
|
+
/**
|
|
5
|
+
* Per-route scope enforcement. Reads the verified token populated by
|
|
6
|
+
* the auth middleware and short-circuits with `403` when the granted
|
|
7
|
+
* set does not match the required scope.
|
|
8
|
+
*
|
|
9
|
+
* @packageDocumentation
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* @stable
|
|
13
|
+
*/
|
|
14
|
+
function createScopeMiddleware(requirement) {
|
|
15
|
+
return async (c, next) => {
|
|
16
|
+
const auth = c.get("state").auth;
|
|
17
|
+
if (auth.kind === "anonymous") {
|
|
18
|
+
await next();
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (auth.kind === "unauthenticated") return c.json({
|
|
22
|
+
error: "auth-required",
|
|
23
|
+
message: "Authentication required for this endpoint."
|
|
24
|
+
}, 401);
|
|
25
|
+
const required = resolveRequirement(requirement, c.req.path, c.req.param());
|
|
26
|
+
const granted = auth.grantedScopes;
|
|
27
|
+
let allowed = false;
|
|
28
|
+
for (const scope of granted) if (scopeMatches(scope, required)) {
|
|
29
|
+
allowed = true;
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
if (!allowed) return c.json({
|
|
33
|
+
error: "scope-denied",
|
|
34
|
+
message: `Token lacks required scope '${required.raw}'.`,
|
|
35
|
+
hint: `Mint a token with the '${required.raw}' (or admin:*) scope.`
|
|
36
|
+
}, 403);
|
|
37
|
+
await next();
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function resolveRequirement(requirement, path, params) {
|
|
41
|
+
if (typeof requirement === "function") {
|
|
42
|
+
const next = requirement(path, params);
|
|
43
|
+
return typeof next === "string" ? parseScope(next) : next;
|
|
44
|
+
}
|
|
45
|
+
return typeof requirement === "string" ? parseScope(requirement) : requirement;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
//#endregion
|
|
49
|
+
export { createScopeMiddleware };
|
|
50
|
+
//# sourceMappingURL=scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.js","names":[],"sources":["../../src/middleware/scope.ts"],"sourcesContent":["/**\n * Per-route scope enforcement. Reads the verified token populated by\n * the auth middleware and short-circuits with `403` when the granted\n * set does not match the required scope.\n *\n * @packageDocumentation\n */\n\nimport { type ParsedScope, parseScope, scopeMatches } from '@graphorin/security/auth';\nimport type { MiddlewareHandler } from 'hono';\n\nimport type { ServerVariables } from '../internal/context.js';\n\n/**\n * Required-scope spec accepted by {@link createScopeMiddleware}. Either\n * a single string (`'agents:invoke'`), a parsed scope, or a function\n * that derives the required scope from the request (e.g. to insert the\n * `:id` segment lazily).\n *\n * @stable\n */\nexport type ScopeRequirement =\n | string\n | ParsedScope\n | ((path: string, params: Record<string, string>) => string | ParsedScope);\n\n/**\n * @stable\n */\nexport function createScopeMiddleware(\n requirement: ScopeRequirement,\n): MiddlewareHandler<{ Variables: ServerVariables }> {\n return async (c, next) => {\n const auth = c.get('state').auth;\n if (auth.kind === 'anonymous') {\n // IP-13: auth is disabled server-wide (auth.kind='none'). There is no\n // token to scope-check — the trusted-loopback operator is allowed\n // through every gate.\n await next();\n return;\n }\n if (auth.kind === 'unauthenticated') {\n return c.json(\n {\n error: 'auth-required',\n message: 'Authentication required for this endpoint.',\n },\n 401,\n );\n }\n const required = resolveRequirement(\n requirement,\n c.req.path,\n c.req.param() as Record<string, string>,\n );\n const granted = auth.grantedScopes;\n let allowed = false;\n for (const scope of granted) {\n if (scopeMatches(scope, required)) {\n allowed = true;\n break;\n }\n }\n if (!allowed) {\n return c.json(\n {\n error: 'scope-denied',\n message: `Token lacks required scope '${required.raw}'.`,\n hint: `Mint a token with the '${required.raw}' (or admin:*) scope.`,\n },\n 403,\n );\n }\n await next();\n };\n}\n\nfunction resolveRequirement(\n requirement: ScopeRequirement,\n path: string,\n params: Record<string, string>,\n): ParsedScope {\n if (typeof requirement === 'function') {\n const next = requirement(path, params);\n return typeof next === 'string' ? parseScope(next) : next;\n }\n return typeof requirement === 'string' ? parseScope(requirement) : requirement;\n}\n"],"mappings":";;;;;;;;;;;;;AA6BA,SAAgB,sBACd,aACmD;AACnD,QAAO,OAAO,GAAG,SAAS;EACxB,MAAM,OAAO,EAAE,IAAI,QAAQ,CAAC;AAC5B,MAAI,KAAK,SAAS,aAAa;AAI7B,SAAM,MAAM;AACZ;;AAEF,MAAI,KAAK,SAAS,kBAChB,QAAO,EAAE,KACP;GACE,OAAO;GACP,SAAS;GACV,EACD,IACD;EAEH,MAAM,WAAW,mBACf,aACA,EAAE,IAAI,MACN,EAAE,IAAI,OAAO,CACd;EACD,MAAM,UAAU,KAAK;EACrB,IAAI,UAAU;AACd,OAAK,MAAM,SAAS,QAClB,KAAI,aAAa,OAAO,SAAS,EAAE;AACjC,aAAU;AACV;;AAGJ,MAAI,CAAC,QACH,QAAO,EAAE,KACP;GACE,OAAO;GACP,SAAS,+BAA+B,SAAS,IAAI;GACrD,MAAM,0BAA0B,SAAS,IAAI;GAC9C,EACD,IACD;AAEH,QAAM,MAAM;;;AAIhB,SAAS,mBACP,aACA,MACA,QACa;AACb,KAAI,OAAO,gBAAgB,YAAY;EACrC,MAAM,OAAO,YAAY,MAAM,OAAO;AACtC,SAAO,OAAO,SAAS,WAAW,WAAW,KAAK,GAAG;;AAEvD,QAAO,OAAO,gBAAgB,WAAW,WAAW,YAAY,GAAG"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
//#region src/registry/index.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Registry plumbing that lets the server route handlers locate user-
|
|
4
|
+
* defined agents, workflows, sessions, memory, skills, and MCP
|
|
5
|
+
* server bindings without taking a hard peer dependency on every
|
|
6
|
+
* sibling package.
|
|
7
|
+
*
|
|
8
|
+
* Every entry is keyed by string id; lookups never throw — callers
|
|
9
|
+
* receive `undefined` and the route handler decides how to surface
|
|
10
|
+
* the miss (typically a 404 with a typed error body).
|
|
11
|
+
*
|
|
12
|
+
* @packageDocumentation
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Minimal shape the server needs from an `Agent`. Compatible with
|
|
16
|
+
* the `Agent` interface from `@graphorin/agent` but kept
|
|
17
|
+
* structurally so we avoid the peer dependency.
|
|
18
|
+
*
|
|
19
|
+
* @stable
|
|
20
|
+
*/
|
|
21
|
+
interface ServerAgentLike {
|
|
22
|
+
readonly id: string;
|
|
23
|
+
run(input: unknown, options?: {
|
|
24
|
+
readonly signal?: AbortSignal;
|
|
25
|
+
readonly sessionId?: string;
|
|
26
|
+
readonly userId?: string;
|
|
27
|
+
}): Promise<unknown>;
|
|
28
|
+
/**
|
|
29
|
+
* Streaming surface (IP-2). `@graphorin/agent` agents satisfy this
|
|
30
|
+
* structurally; `POST /agents/:id/stream` consumes it and emits
|
|
31
|
+
* every event onto the run's WS subject. Optional so plain
|
|
32
|
+
* run-only fixtures keep working (they emit a single terminal frame).
|
|
33
|
+
*/
|
|
34
|
+
stream?(input: unknown, options?: {
|
|
35
|
+
readonly signal?: AbortSignal;
|
|
36
|
+
readonly sessionId?: string;
|
|
37
|
+
readonly userId?: string;
|
|
38
|
+
}): AsyncIterable<unknown>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Minimal shape the server needs from a `Workflow`. Mirrors the
|
|
42
|
+
* `Workflow` surface from `@graphorin/workflow`.
|
|
43
|
+
*
|
|
44
|
+
* @stable
|
|
45
|
+
*/
|
|
46
|
+
interface ServerWorkflowLike {
|
|
47
|
+
readonly name: string;
|
|
48
|
+
execute(input: unknown, options?: {
|
|
49
|
+
readonly signal?: AbortSignal;
|
|
50
|
+
readonly threadId?: string;
|
|
51
|
+
}): AsyncIterable<unknown>;
|
|
52
|
+
resume?(threadId: string, directive?: {
|
|
53
|
+
readonly resume?: unknown;
|
|
54
|
+
}): AsyncIterable<unknown>;
|
|
55
|
+
getState?(threadId: string): Promise<unknown>;
|
|
56
|
+
listCheckpoints?(threadId: string): Promise<ReadonlyArray<unknown>>;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Snapshot record returned by {@link AgentRegistry.list}.
|
|
60
|
+
*
|
|
61
|
+
* @stable
|
|
62
|
+
*/
|
|
63
|
+
interface AgentSummary {
|
|
64
|
+
readonly id: string;
|
|
65
|
+
readonly description?: string;
|
|
66
|
+
readonly tags?: ReadonlyArray<string>;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Snapshot record returned by {@link WorkflowRegistry.list}.
|
|
70
|
+
*
|
|
71
|
+
* @stable
|
|
72
|
+
*/
|
|
73
|
+
interface WorkflowSummary {
|
|
74
|
+
readonly id: string;
|
|
75
|
+
readonly description?: string;
|
|
76
|
+
readonly tags?: ReadonlyArray<string>;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Registration descriptor accepted by {@link AgentRegistry.register}.
|
|
80
|
+
*
|
|
81
|
+
* @stable
|
|
82
|
+
*/
|
|
83
|
+
interface AgentRegistration {
|
|
84
|
+
readonly id: string;
|
|
85
|
+
readonly agent: ServerAgentLike;
|
|
86
|
+
readonly description?: string;
|
|
87
|
+
readonly tags?: ReadonlyArray<string>;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Registration descriptor accepted by {@link WorkflowRegistry.register}.
|
|
91
|
+
*
|
|
92
|
+
* @stable
|
|
93
|
+
*/
|
|
94
|
+
interface WorkflowRegistration {
|
|
95
|
+
readonly id: string;
|
|
96
|
+
readonly workflow: ServerWorkflowLike;
|
|
97
|
+
readonly description?: string;
|
|
98
|
+
readonly tags?: ReadonlyArray<string>;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Read/write registry for agents the server should expose. Every
|
|
102
|
+
* mutation is synchronous and the lookup is `O(1)`.
|
|
103
|
+
*
|
|
104
|
+
* The class is intentionally tiny — extension points (e.g. lazy
|
|
105
|
+
* factory loading, per-tenant scoping) live in higher-level packages
|
|
106
|
+
* and consume this surface as a primitive.
|
|
107
|
+
*
|
|
108
|
+
* @stable
|
|
109
|
+
*/
|
|
110
|
+
declare class AgentRegistry {
|
|
111
|
+
#private;
|
|
112
|
+
register(entry: AgentRegistration): void;
|
|
113
|
+
unregister(id: string): boolean;
|
|
114
|
+
get(id: string): ServerAgentLike | undefined;
|
|
115
|
+
has(id: string): boolean;
|
|
116
|
+
list(): ReadonlyArray<AgentSummary>;
|
|
117
|
+
describe(id: string): AgentSummary | undefined;
|
|
118
|
+
size(): number;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* @stable
|
|
122
|
+
*/
|
|
123
|
+
declare class WorkflowRegistry {
|
|
124
|
+
#private;
|
|
125
|
+
register(entry: WorkflowRegistration): void;
|
|
126
|
+
unregister(id: string): boolean;
|
|
127
|
+
get(id: string): ServerWorkflowLike | undefined;
|
|
128
|
+
has(id: string): boolean;
|
|
129
|
+
list(): ReadonlyArray<WorkflowSummary>;
|
|
130
|
+
describe(id: string): WorkflowSummary | undefined;
|
|
131
|
+
size(): number;
|
|
132
|
+
}
|
|
133
|
+
//#endregion
|
|
134
|
+
export { AgentRegistration, AgentRegistry, AgentSummary, ServerAgentLike, ServerWorkflowLike, WorkflowRegistration, WorkflowRegistry, WorkflowSummary };
|
|
135
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/registry/index.ts"],"sourcesContent":[],"mappings":";;AAoBA;;;;;;AAgCA;;;;;;;;AAgBA;AAWA;AAkCA;AAYA;AAiBa,UA1HI,eAAA,CA0HS;EAGR,SAAA,EAAA,EAAA,MAAA;EAaC,GAAA,CAAA,KAAA,EAAA,OAAA,EAAA,OAoBiB,CApBjB,EAAA;IAQK,SAAA,MAAA,CAAA,EA7IA,WA6IA;IAAd,SAAA,SAAA,CAAA,EAAA,MAAA;IAYc,SAAA,MAAA,CAAA,EAAA,MAAA;EAAY,CAAA,CAAA,EArJ/B,OAqJ+B,CAAA,OAAA,CAAA;EAkBvB;;;;;;EAoC0B,MAAA,EAAA,KAAA,EAAA,OAAA,EAAA,QAAA,EAAA;sBAjMf;;;MAInB;;;;;;;;UASY,kBAAA;;;sBAIiB;;MAC7B;;;MACmE;+BACzC;sCACO,QAAQ;;;;;;;UAQ7B,YAAA;;;kBAGC;;;;;;;UAQD,eAAA;;;kBAGC;;;;;;;UA+BD,iBAAA;;kBAEC;;kBAEA;;;;;;;UAQD,oBAAA;;qBAEI;;kBAEH;;;;;;;;;;;;cAaL,aAAA;;kBAGK;;mBAaC;;UAQT,cAAc;wBAYA;;;;;;cAkBX,gBAAA;;kBAGK;;mBAaC;;UAQT,cAAc;wBAYA"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
//#region src/registry/index.ts
|
|
2
|
+
/**
|
|
3
|
+
* Read/write registry for agents the server should expose. Every
|
|
4
|
+
* mutation is synchronous and the lookup is `O(1)`.
|
|
5
|
+
*
|
|
6
|
+
* The class is intentionally tiny — extension points (e.g. lazy
|
|
7
|
+
* factory loading, per-tenant scoping) live in higher-level packages
|
|
8
|
+
* and consume this surface as a primitive.
|
|
9
|
+
*
|
|
10
|
+
* @stable
|
|
11
|
+
*/
|
|
12
|
+
var AgentRegistry = class {
|
|
13
|
+
#entries = /* @__PURE__ */ new Map();
|
|
14
|
+
register(entry) {
|
|
15
|
+
this.#entries.set(entry.id, {
|
|
16
|
+
id: entry.id,
|
|
17
|
+
agent: entry.agent,
|
|
18
|
+
...entry.description !== void 0 ? { description: entry.description } : {},
|
|
19
|
+
...entry.tags !== void 0 ? { tags: Object.freeze(entry.tags.slice()) } : {}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
unregister(id) {
|
|
23
|
+
return this.#entries.delete(id);
|
|
24
|
+
}
|
|
25
|
+
get(id) {
|
|
26
|
+
return this.#entries.get(id)?.agent;
|
|
27
|
+
}
|
|
28
|
+
has(id) {
|
|
29
|
+
return this.#entries.has(id);
|
|
30
|
+
}
|
|
31
|
+
list() {
|
|
32
|
+
return Object.freeze([...this.#entries.values()].map((entry) => Object.freeze({
|
|
33
|
+
id: entry.id,
|
|
34
|
+
...entry.description !== void 0 ? { description: entry.description } : {},
|
|
35
|
+
...entry.tags !== void 0 ? { tags: entry.tags } : {}
|
|
36
|
+
})));
|
|
37
|
+
}
|
|
38
|
+
describe(id) {
|
|
39
|
+
const entry = this.#entries.get(id);
|
|
40
|
+
if (entry === void 0) return void 0;
|
|
41
|
+
return Object.freeze({
|
|
42
|
+
id: entry.id,
|
|
43
|
+
...entry.description !== void 0 ? { description: entry.description } : {},
|
|
44
|
+
...entry.tags !== void 0 ? { tags: entry.tags } : {}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
size() {
|
|
48
|
+
return this.#entries.size;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* @stable
|
|
53
|
+
*/
|
|
54
|
+
var WorkflowRegistry = class {
|
|
55
|
+
#entries = /* @__PURE__ */ new Map();
|
|
56
|
+
register(entry) {
|
|
57
|
+
this.#entries.set(entry.id, {
|
|
58
|
+
id: entry.id,
|
|
59
|
+
workflow: entry.workflow,
|
|
60
|
+
...entry.description !== void 0 ? { description: entry.description } : {},
|
|
61
|
+
...entry.tags !== void 0 ? { tags: Object.freeze(entry.tags.slice()) } : {}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
unregister(id) {
|
|
65
|
+
return this.#entries.delete(id);
|
|
66
|
+
}
|
|
67
|
+
get(id) {
|
|
68
|
+
return this.#entries.get(id)?.workflow;
|
|
69
|
+
}
|
|
70
|
+
has(id) {
|
|
71
|
+
return this.#entries.has(id);
|
|
72
|
+
}
|
|
73
|
+
list() {
|
|
74
|
+
return Object.freeze([...this.#entries.values()].map((entry) => Object.freeze({
|
|
75
|
+
id: entry.id,
|
|
76
|
+
...entry.description !== void 0 ? { description: entry.description } : {},
|
|
77
|
+
...entry.tags !== void 0 ? { tags: entry.tags } : {}
|
|
78
|
+
})));
|
|
79
|
+
}
|
|
80
|
+
describe(id) {
|
|
81
|
+
const entry = this.#entries.get(id);
|
|
82
|
+
if (entry === void 0) return void 0;
|
|
83
|
+
return Object.freeze({
|
|
84
|
+
id: entry.id,
|
|
85
|
+
...entry.description !== void 0 ? { description: entry.description } : {},
|
|
86
|
+
...entry.tags !== void 0 ? { tags: entry.tags } : {}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
size() {
|
|
90
|
+
return this.#entries.size;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
//#endregion
|
|
95
|
+
export { AgentRegistry, WorkflowRegistry };
|
|
96
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["#entries"],"sources":["../../src/registry/index.ts"],"sourcesContent":["/**\n * Registry plumbing that lets the server route handlers locate user-\n * defined agents, workflows, sessions, memory, skills, and MCP\n * server bindings without taking a hard peer dependency on every\n * sibling package.\n *\n * Every entry is keyed by string id; lookups never throw — callers\n * receive `undefined` and the route handler decides how to surface\n * the miss (typically a 404 with a typed error body).\n *\n * @packageDocumentation\n */\n\n/**\n * Minimal shape the server needs from an `Agent`. Compatible with\n * the `Agent` interface from `@graphorin/agent` but kept\n * structurally so we avoid the peer dependency.\n *\n * @stable\n */\nexport interface ServerAgentLike {\n readonly id: string;\n run(\n input: unknown,\n options?: {\n readonly signal?: AbortSignal;\n readonly sessionId?: string;\n readonly userId?: string;\n },\n ): Promise<unknown>;\n /**\n * Streaming surface (IP-2). `@graphorin/agent` agents satisfy this\n * structurally; `POST /agents/:id/stream` consumes it and emits\n * every event onto the run's WS subject. Optional so plain\n * run-only fixtures keep working (they emit a single terminal frame).\n */\n stream?(\n input: unknown,\n options?: {\n readonly signal?: AbortSignal;\n readonly sessionId?: string;\n readonly userId?: string;\n },\n ): AsyncIterable<unknown>;\n}\n\n/**\n * Minimal shape the server needs from a `Workflow`. Mirrors the\n * `Workflow` surface from `@graphorin/workflow`.\n *\n * @stable\n */\nexport interface ServerWorkflowLike {\n readonly name: string;\n execute(\n input: unknown,\n options?: { readonly signal?: AbortSignal; readonly threadId?: string },\n ): AsyncIterable<unknown>;\n resume?(threadId: string, directive?: { readonly resume?: unknown }): AsyncIterable<unknown>;\n getState?(threadId: string): Promise<unknown>;\n listCheckpoints?(threadId: string): Promise<ReadonlyArray<unknown>>;\n}\n\n/**\n * Snapshot record returned by {@link AgentRegistry.list}.\n *\n * @stable\n */\nexport interface AgentSummary {\n readonly id: string;\n readonly description?: string;\n readonly tags?: ReadonlyArray<string>;\n}\n\n/**\n * Snapshot record returned by {@link WorkflowRegistry.list}.\n *\n * @stable\n */\nexport interface WorkflowSummary {\n readonly id: string;\n readonly description?: string;\n readonly tags?: ReadonlyArray<string>;\n}\n\n/**\n * Internal registry entry — pairs the user-supplied object with the\n * metadata routes serve to clients.\n *\n * @internal\n */\ninterface AgentEntry {\n readonly id: string;\n readonly agent: ServerAgentLike;\n readonly description?: string;\n readonly tags?: ReadonlyArray<string>;\n}\n\n/**\n * @internal\n */\ninterface WorkflowEntry {\n readonly id: string;\n readonly workflow: ServerWorkflowLike;\n readonly description?: string;\n readonly tags?: ReadonlyArray<string>;\n}\n\n/**\n * Registration descriptor accepted by {@link AgentRegistry.register}.\n *\n * @stable\n */\nexport interface AgentRegistration {\n readonly id: string;\n readonly agent: ServerAgentLike;\n readonly description?: string;\n readonly tags?: ReadonlyArray<string>;\n}\n\n/**\n * Registration descriptor accepted by {@link WorkflowRegistry.register}.\n *\n * @stable\n */\nexport interface WorkflowRegistration {\n readonly id: string;\n readonly workflow: ServerWorkflowLike;\n readonly description?: string;\n readonly tags?: ReadonlyArray<string>;\n}\n\n/**\n * Read/write registry for agents the server should expose. Every\n * mutation is synchronous and the lookup is `O(1)`.\n *\n * The class is intentionally tiny — extension points (e.g. lazy\n * factory loading, per-tenant scoping) live in higher-level packages\n * and consume this surface as a primitive.\n *\n * @stable\n */\nexport class AgentRegistry {\n readonly #entries: Map<string, AgentEntry> = new Map();\n\n register(entry: AgentRegistration): void {\n this.#entries.set(entry.id, {\n id: entry.id,\n agent: entry.agent,\n ...(entry.description !== undefined ? { description: entry.description } : {}),\n ...(entry.tags !== undefined ? { tags: Object.freeze(entry.tags.slice()) } : {}),\n });\n }\n\n unregister(id: string): boolean {\n return this.#entries.delete(id);\n }\n\n get(id: string): ServerAgentLike | undefined {\n return this.#entries.get(id)?.agent;\n }\n\n has(id: string): boolean {\n return this.#entries.has(id);\n }\n\n list(): ReadonlyArray<AgentSummary> {\n return Object.freeze(\n [...this.#entries.values()].map((entry) =>\n Object.freeze({\n id: entry.id,\n ...(entry.description !== undefined ? { description: entry.description } : {}),\n ...(entry.tags !== undefined ? { tags: entry.tags } : {}),\n }),\n ),\n );\n }\n\n describe(id: string): AgentSummary | undefined {\n const entry = this.#entries.get(id);\n if (entry === undefined) return undefined;\n return Object.freeze({\n id: entry.id,\n ...(entry.description !== undefined ? { description: entry.description } : {}),\n ...(entry.tags !== undefined ? { tags: entry.tags } : {}),\n });\n }\n\n size(): number {\n return this.#entries.size;\n }\n}\n\n/**\n * @stable\n */\nexport class WorkflowRegistry {\n readonly #entries: Map<string, WorkflowEntry> = new Map();\n\n register(entry: WorkflowRegistration): void {\n this.#entries.set(entry.id, {\n id: entry.id,\n workflow: entry.workflow,\n ...(entry.description !== undefined ? { description: entry.description } : {}),\n ...(entry.tags !== undefined ? { tags: Object.freeze(entry.tags.slice()) } : {}),\n });\n }\n\n unregister(id: string): boolean {\n return this.#entries.delete(id);\n }\n\n get(id: string): ServerWorkflowLike | undefined {\n return this.#entries.get(id)?.workflow;\n }\n\n has(id: string): boolean {\n return this.#entries.has(id);\n }\n\n list(): ReadonlyArray<WorkflowSummary> {\n return Object.freeze(\n [...this.#entries.values()].map((entry) =>\n Object.freeze({\n id: entry.id,\n ...(entry.description !== undefined ? { description: entry.description } : {}),\n ...(entry.tags !== undefined ? { tags: entry.tags } : {}),\n }),\n ),\n );\n }\n\n describe(id: string): WorkflowSummary | undefined {\n const entry = this.#entries.get(id);\n if (entry === undefined) return undefined;\n return Object.freeze({\n id: entry.id,\n ...(entry.description !== undefined ? { description: entry.description } : {}),\n ...(entry.tags !== undefined ? { tags: entry.tags } : {}),\n });\n }\n\n size(): number {\n return this.#entries.size;\n }\n}\n"],"mappings":";;;;;;;;;;;AA8IA,IAAa,gBAAb,MAA2B;CACzB,CAASA,0BAAoC,IAAI,KAAK;CAEtD,SAAS,OAAgC;AACvC,QAAKA,QAAS,IAAI,MAAM,IAAI;GAC1B,IAAI,MAAM;GACV,OAAO,MAAM;GACb,GAAI,MAAM,gBAAgB,SAAY,EAAE,aAAa,MAAM,aAAa,GAAG,EAAE;GAC7E,GAAI,MAAM,SAAS,SAAY,EAAE,MAAM,OAAO,OAAO,MAAM,KAAK,OAAO,CAAC,EAAE,GAAG,EAAE;GAChF,CAAC;;CAGJ,WAAW,IAAqB;AAC9B,SAAO,MAAKA,QAAS,OAAO,GAAG;;CAGjC,IAAI,IAAyC;AAC3C,SAAO,MAAKA,QAAS,IAAI,GAAG,EAAE;;CAGhC,IAAI,IAAqB;AACvB,SAAO,MAAKA,QAAS,IAAI,GAAG;;CAG9B,OAAoC;AAClC,SAAO,OAAO,OACZ,CAAC,GAAG,MAAKA,QAAS,QAAQ,CAAC,CAAC,KAAK,UAC/B,OAAO,OAAO;GACZ,IAAI,MAAM;GACV,GAAI,MAAM,gBAAgB,SAAY,EAAE,aAAa,MAAM,aAAa,GAAG,EAAE;GAC7E,GAAI,MAAM,SAAS,SAAY,EAAE,MAAM,MAAM,MAAM,GAAG,EAAE;GACzD,CAAC,CACH,CACF;;CAGH,SAAS,IAAsC;EAC7C,MAAM,QAAQ,MAAKA,QAAS,IAAI,GAAG;AACnC,MAAI,UAAU,OAAW,QAAO;AAChC,SAAO,OAAO,OAAO;GACnB,IAAI,MAAM;GACV,GAAI,MAAM,gBAAgB,SAAY,EAAE,aAAa,MAAM,aAAa,GAAG,EAAE;GAC7E,GAAI,MAAM,SAAS,SAAY,EAAE,MAAM,MAAM,MAAM,GAAG,EAAE;GACzD,CAAC;;CAGJ,OAAe;AACb,SAAO,MAAKA,QAAS;;;;;;AAOzB,IAAa,mBAAb,MAA8B;CAC5B,CAASA,0BAAuC,IAAI,KAAK;CAEzD,SAAS,OAAmC;AAC1C,QAAKA,QAAS,IAAI,MAAM,IAAI;GAC1B,IAAI,MAAM;GACV,UAAU,MAAM;GAChB,GAAI,MAAM,gBAAgB,SAAY,EAAE,aAAa,MAAM,aAAa,GAAG,EAAE;GAC7E,GAAI,MAAM,SAAS,SAAY,EAAE,MAAM,OAAO,OAAO,MAAM,KAAK,OAAO,CAAC,EAAE,GAAG,EAAE;GAChF,CAAC;;CAGJ,WAAW,IAAqB;AAC9B,SAAO,MAAKA,QAAS,OAAO,GAAG;;CAGjC,IAAI,IAA4C;AAC9C,SAAO,MAAKA,QAAS,IAAI,GAAG,EAAE;;CAGhC,IAAI,IAAqB;AACvB,SAAO,MAAKA,QAAS,IAAI,GAAG;;CAG9B,OAAuC;AACrC,SAAO,OAAO,OACZ,CAAC,GAAG,MAAKA,QAAS,QAAQ,CAAC,CAAC,KAAK,UAC/B,OAAO,OAAO;GACZ,IAAI,MAAM;GACV,GAAI,MAAM,gBAAgB,SAAY,EAAE,aAAa,MAAM,aAAa,GAAG,EAAE;GAC7E,GAAI,MAAM,SAAS,SAAY,EAAE,MAAM,MAAM,MAAM,GAAG,EAAE;GACzD,CAAC,CACH,CACF;;CAGH,SAAS,IAAyC;EAChD,MAAM,QAAQ,MAAKA,QAAS,IAAI,GAAG;AACnC,MAAI,UAAU,OAAW,QAAO;AAChC,SAAO,OAAO,OAAO;GACnB,IAAI,MAAM;GACV,GAAI,MAAM,gBAAgB,SAAY,EAAE,aAAa,MAAM,aAAa,GAAG,EAAE;GAC7E,GAAI,MAAM,SAAS,SAAY,EAAE,MAAM,MAAM,MAAM,GAAG,EAAE;GACzD,CAAC;;CAGJ,OAAe;AACb,SAAO,MAAKA,QAAS"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ServerVariables } from "../internal/context.js";
|
|
2
|
+
import { AuditDb } from "@graphorin/security/audit";
|
|
3
|
+
import { Hono } from "hono";
|
|
4
|
+
|
|
5
|
+
//#region src/replay/routes.d.ts
|
|
6
|
+
|
|
7
|
+
/** @stable */
|
|
8
|
+
type ReplayMode = 'sanitized' | 'raw';
|
|
9
|
+
/** @stable */
|
|
10
|
+
interface ReplayApi {
|
|
11
|
+
loadRunReplay(input: {
|
|
12
|
+
readonly runId: string;
|
|
13
|
+
readonly mode: ReplayMode;
|
|
14
|
+
readonly fromMessageId?: string;
|
|
15
|
+
readonly provider?: string;
|
|
16
|
+
}): Promise<ReplayResponse>;
|
|
17
|
+
loadSessionReplay(input: {
|
|
18
|
+
readonly sessionId: string;
|
|
19
|
+
readonly mode: ReplayMode;
|
|
20
|
+
readonly fromMessageId?: string;
|
|
21
|
+
readonly provider?: string;
|
|
22
|
+
}): Promise<ReplayResponse>;
|
|
23
|
+
}
|
|
24
|
+
/** @stable */
|
|
25
|
+
interface ReplayResponse {
|
|
26
|
+
readonly events: ReadonlyArray<unknown>;
|
|
27
|
+
readonly truncated?: boolean;
|
|
28
|
+
readonly nextCursor?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* @stable
|
|
32
|
+
*/
|
|
33
|
+
interface ReplayRoutesDeps {
|
|
34
|
+
readonly replay: ReplayApi;
|
|
35
|
+
readonly auditDb?: AuditDb;
|
|
36
|
+
readonly now?: () => number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* @stable
|
|
40
|
+
*/
|
|
41
|
+
declare function createReplayRoutes(deps: ReplayRoutesDeps): Hono<{
|
|
42
|
+
Variables: ServerVariables;
|
|
43
|
+
}>;
|
|
44
|
+
//#endregion
|
|
45
|
+
export { ReplayApi, ReplayMode, ReplayResponse, ReplayRoutesDeps, createReplayRoutes };
|
|
46
|
+
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","names":[],"sources":["../../src/replay/routes.ts"],"sourcesContent":[],"mappings":";;;;;;;KA+BY,UAAA;;UAGK,SAAA;;;mBAGE;;;MAGb,QAAQ;;;mBAGK;;;MAGb,QAAQ;;;UAIG,cAAA;mBACE;;;;;;;UAoBF,gBAAA;mBACE;qBACE;;;;;;iBAOL,kBAAA,OAAyB,mBAAmB;aAAkB"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { appendAudit } from "@graphorin/security/audit";
|
|
2
|
+
import { Hono } from "hono";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { parseScope, scopeMatches } from "@graphorin/security/auth";
|
|
5
|
+
|
|
6
|
+
//#region src/replay/routes.ts
|
|
7
|
+
/**
|
|
8
|
+
* Replay endpoints with scope enforcement.
|
|
9
|
+
*
|
|
10
|
+
* POST /v1/runs/:runId/replay
|
|
11
|
+
* POST /v1/sessions/:id/replay
|
|
12
|
+
*
|
|
13
|
+
* Scope rules:
|
|
14
|
+
* - `raw: false` (default) → require `traces:read:sanitized`.
|
|
15
|
+
* - `raw: true` → require `traces:read:raw` (admin).
|
|
16
|
+
*
|
|
17
|
+
* Every successful invocation appends a `trace.replay.accessed` audit
|
|
18
|
+
* entry. The audit write is best-effort: a missing `AuditDb` does not
|
|
19
|
+
* block the replay.
|
|
20
|
+
*
|
|
21
|
+
* The actual replay stream is consumer-supplied — the framework
|
|
22
|
+
* accepts a `ReplayApi` from the operator that knows how to load
|
|
23
|
+
* trace events for a `runId` / `sessionId`. Phase 14c only owns the
|
|
24
|
+
* scope check + audit + transport.
|
|
25
|
+
*
|
|
26
|
+
* @packageDocumentation
|
|
27
|
+
*/
|
|
28
|
+
const SCOPE_SANITIZED = parseScope("traces:read:sanitized");
|
|
29
|
+
const SCOPE_RAW = parseScope("traces:read:raw");
|
|
30
|
+
const ReplayRequestSchema = z.object({
|
|
31
|
+
raw: z.boolean().optional(),
|
|
32
|
+
fromMessageId: z.string().min(1).optional(),
|
|
33
|
+
provider: z.string().min(1).optional()
|
|
34
|
+
}).strict().default({});
|
|
35
|
+
/**
|
|
36
|
+
* @stable
|
|
37
|
+
*/
|
|
38
|
+
function createReplayRoutes(deps) {
|
|
39
|
+
const app = new Hono();
|
|
40
|
+
const now = deps.now ?? Date.now;
|
|
41
|
+
app.post("/runs/:runId/replay", async (c) => {
|
|
42
|
+
const runId = c.req.param("runId");
|
|
43
|
+
const body = ReplayRequestSchema.safeParse(await safelyParseJson(c));
|
|
44
|
+
if (!body.success) return c.json(buildValidationError("Invalid replay body.", body.error.issues), 400);
|
|
45
|
+
const mode = body.data.raw === true ? "raw" : "sanitized";
|
|
46
|
+
const auth = c.get("state").auth;
|
|
47
|
+
const scopeError = enforceReplayScope(auth, mode);
|
|
48
|
+
if (scopeError !== void 0) {
|
|
49
|
+
await maybeAppendAudit(deps.auditDb, {
|
|
50
|
+
actor: auditActor(auth),
|
|
51
|
+
action: "replay:skipped",
|
|
52
|
+
target: `run:${runId}`,
|
|
53
|
+
decision: "denied",
|
|
54
|
+
ts: now(),
|
|
55
|
+
metadata: {
|
|
56
|
+
mode,
|
|
57
|
+
reason: scopeError.code
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
return c.json(scopeError.body, scopeError.status);
|
|
61
|
+
}
|
|
62
|
+
const replay = await deps.replay.loadRunReplay({
|
|
63
|
+
runId,
|
|
64
|
+
mode,
|
|
65
|
+
...body.data.fromMessageId !== void 0 ? { fromMessageId: body.data.fromMessageId } : {},
|
|
66
|
+
...body.data.provider !== void 0 ? { provider: body.data.provider } : {}
|
|
67
|
+
});
|
|
68
|
+
await maybeAppendAudit(deps.auditDb, {
|
|
69
|
+
actor: auditActor(auth),
|
|
70
|
+
action: "replay:accessed",
|
|
71
|
+
target: `run:${runId}`,
|
|
72
|
+
decision: "success",
|
|
73
|
+
ts: now(),
|
|
74
|
+
metadata: {
|
|
75
|
+
mode,
|
|
76
|
+
eventCount: replay.events.length
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
return c.json(serializeReplay(replay, mode));
|
|
80
|
+
});
|
|
81
|
+
app.post("/sessions/:id/replay", async (c) => {
|
|
82
|
+
const sessionId = c.req.param("id");
|
|
83
|
+
const body = ReplayRequestSchema.safeParse(await safelyParseJson(c));
|
|
84
|
+
if (!body.success) return c.json(buildValidationError("Invalid replay body.", body.error.issues), 400);
|
|
85
|
+
const mode = body.data.raw === true ? "raw" : "sanitized";
|
|
86
|
+
const auth = c.get("state").auth;
|
|
87
|
+
const scopeError = enforceReplayScope(auth, mode);
|
|
88
|
+
if (scopeError !== void 0) {
|
|
89
|
+
await maybeAppendAudit(deps.auditDb, {
|
|
90
|
+
actor: auditActor(auth),
|
|
91
|
+
action: "replay:skipped",
|
|
92
|
+
target: `session:${sessionId}`,
|
|
93
|
+
decision: "denied",
|
|
94
|
+
ts: now(),
|
|
95
|
+
metadata: {
|
|
96
|
+
mode,
|
|
97
|
+
reason: scopeError.code
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
return c.json(scopeError.body, scopeError.status);
|
|
101
|
+
}
|
|
102
|
+
const replay = await deps.replay.loadSessionReplay({
|
|
103
|
+
sessionId,
|
|
104
|
+
mode,
|
|
105
|
+
...body.data.fromMessageId !== void 0 ? { fromMessageId: body.data.fromMessageId } : {},
|
|
106
|
+
...body.data.provider !== void 0 ? { provider: body.data.provider } : {}
|
|
107
|
+
});
|
|
108
|
+
await maybeAppendAudit(deps.auditDb, {
|
|
109
|
+
actor: auditActor(auth),
|
|
110
|
+
action: "replay:accessed",
|
|
111
|
+
target: `session:${sessionId}`,
|
|
112
|
+
decision: "success",
|
|
113
|
+
ts: now(),
|
|
114
|
+
metadata: {
|
|
115
|
+
mode,
|
|
116
|
+
eventCount: replay.events.length
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
return c.json(serializeReplay(replay, mode));
|
|
120
|
+
});
|
|
121
|
+
return app;
|
|
122
|
+
}
|
|
123
|
+
function enforceReplayScope(auth, mode) {
|
|
124
|
+
if (auth.kind === "unauthenticated") return {
|
|
125
|
+
code: "auth-required",
|
|
126
|
+
status: 401,
|
|
127
|
+
body: {
|
|
128
|
+
error: "auth-required",
|
|
129
|
+
message: "Authentication required for replay endpoints."
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
const required = mode === "raw" ? SCOPE_RAW : SCOPE_SANITIZED;
|
|
133
|
+
for (const scope of auth.grantedScopes) if (scopeMatches(scope, required)) return void 0;
|
|
134
|
+
return {
|
|
135
|
+
code: "scope-denied",
|
|
136
|
+
status: 403,
|
|
137
|
+
body: {
|
|
138
|
+
error: "scope-denied",
|
|
139
|
+
message: `Token lacks required scope '${required.raw}'.`,
|
|
140
|
+
hint: `Mint a token with the '${required.raw}' (or admin:*) scope.`
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function auditActor(auth) {
|
|
145
|
+
if (auth.kind === "token") return {
|
|
146
|
+
kind: "token",
|
|
147
|
+
id: auth.token.tokenId,
|
|
148
|
+
...auth.token.label !== void 0 ? { label: auth.token.label } : {}
|
|
149
|
+
};
|
|
150
|
+
return {
|
|
151
|
+
kind: "system",
|
|
152
|
+
id: auth.kind === "anonymous" ? "anonymous" : "unauthenticated"
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
async function maybeAppendAudit(auditDb, entry) {
|
|
156
|
+
if (auditDb === void 0) return;
|
|
157
|
+
try {
|
|
158
|
+
await appendAudit(auditDb, entry);
|
|
159
|
+
} catch {}
|
|
160
|
+
}
|
|
161
|
+
function serializeReplay(replay, mode) {
|
|
162
|
+
return {
|
|
163
|
+
mode,
|
|
164
|
+
events: [...replay.events],
|
|
165
|
+
...replay.truncated === true ? { truncated: true } : {},
|
|
166
|
+
...replay.nextCursor !== void 0 ? { nextCursor: replay.nextCursor } : {}
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function buildValidationError(message, issues) {
|
|
170
|
+
return {
|
|
171
|
+
error: "config-invalid",
|
|
172
|
+
message,
|
|
173
|
+
issues: issues.map((i) => ({
|
|
174
|
+
path: i.path,
|
|
175
|
+
message: i.message
|
|
176
|
+
}))
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
async function safelyParseJson(c) {
|
|
180
|
+
try {
|
|
181
|
+
return await c.req.json();
|
|
182
|
+
} catch {
|
|
183
|
+
return {};
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
//#endregion
|
|
188
|
+
export { createReplayRoutes };
|
|
189
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.js","names":["SCOPE_SANITIZED: ParsedScope","SCOPE_RAW: ParsedScope","mode: ReplayMode"],"sources":["../../src/replay/routes.ts"],"sourcesContent":["/**\n * Replay endpoints with scope enforcement.\n *\n * POST /v1/runs/:runId/replay\n * POST /v1/sessions/:id/replay\n *\n * Scope rules:\n * - `raw: false` (default) → require `traces:read:sanitized`.\n * - `raw: true` → require `traces:read:raw` (admin).\n *\n * Every successful invocation appends a `trace.replay.accessed` audit\n * entry. The audit write is best-effort: a missing `AuditDb` does not\n * block the replay.\n *\n * The actual replay stream is consumer-supplied — the framework\n * accepts a `ReplayApi` from the operator that knows how to load\n * trace events for a `runId` / `sessionId`. Phase 14c only owns the\n * scope check + audit + transport.\n *\n * @packageDocumentation\n */\n\nimport { type AuditDb, appendAudit } from '@graphorin/security/audit';\nimport { type ParsedScope, parseScope, scopeMatches } from '@graphorin/security/auth';\nimport { Hono } from 'hono';\nimport type { ContentfulStatusCode } from 'hono/utils/http-status';\nimport { z } from 'zod';\n\nimport type { AuthState, ServerVariables } from '../internal/context.js';\n\n/** @stable */\nexport type ReplayMode = 'sanitized' | 'raw';\n\n/** @stable */\nexport interface ReplayApi {\n loadRunReplay(input: {\n readonly runId: string;\n readonly mode: ReplayMode;\n readonly fromMessageId?: string;\n readonly provider?: string;\n }): Promise<ReplayResponse>;\n loadSessionReplay(input: {\n readonly sessionId: string;\n readonly mode: ReplayMode;\n readonly fromMessageId?: string;\n readonly provider?: string;\n }): Promise<ReplayResponse>;\n}\n\n/** @stable */\nexport interface ReplayResponse {\n readonly events: ReadonlyArray<unknown>;\n readonly truncated?: boolean;\n readonly nextCursor?: string;\n}\n\nconst SCOPE_SANITIZED: ParsedScope = parseScope('traces:read:sanitized');\nconst SCOPE_RAW: ParsedScope = parseScope('traces:read:raw');\n\nconst ReplayRequestSchema = z\n .object({\n raw: z.boolean().optional(),\n fromMessageId: z.string().min(1).optional(),\n provider: z.string().min(1).optional(),\n })\n .strict()\n .default({});\n\n/**\n * @stable\n */\nexport interface ReplayRoutesDeps {\n readonly replay: ReplayApi;\n readonly auditDb?: AuditDb;\n readonly now?: () => number;\n}\n\n/**\n * @stable\n */\nexport function createReplayRoutes(deps: ReplayRoutesDeps): Hono<{ Variables: ServerVariables }> {\n const app = new Hono<{ Variables: ServerVariables }>();\n const now = deps.now ?? Date.now;\n\n app.post('/runs/:runId/replay', async (c) => {\n const runId = c.req.param('runId');\n const body = ReplayRequestSchema.safeParse(await safelyParseJson(c));\n if (!body.success) {\n return c.json(buildValidationError('Invalid replay body.', body.error.issues), 400);\n }\n const mode: ReplayMode = body.data.raw === true ? 'raw' : 'sanitized';\n const auth = c.get('state').auth;\n const scopeError = enforceReplayScope(auth, mode);\n if (scopeError !== undefined) {\n await maybeAppendAudit(deps.auditDb, {\n actor: auditActor(auth),\n action: 'replay:skipped',\n target: `run:${runId}`,\n decision: 'denied',\n ts: now(),\n metadata: { mode, reason: scopeError.code },\n });\n return c.json(scopeError.body, scopeError.status);\n }\n const replay = await deps.replay.loadRunReplay({\n runId,\n mode,\n ...(body.data.fromMessageId !== undefined ? { fromMessageId: body.data.fromMessageId } : {}),\n ...(body.data.provider !== undefined ? { provider: body.data.provider } : {}),\n });\n await maybeAppendAudit(deps.auditDb, {\n actor: auditActor(auth),\n action: 'replay:accessed',\n target: `run:${runId}`,\n decision: 'success',\n ts: now(),\n metadata: { mode, eventCount: replay.events.length },\n });\n return c.json(serializeReplay(replay, mode));\n });\n\n app.post('/sessions/:id/replay', async (c) => {\n const sessionId = c.req.param('id');\n const body = ReplayRequestSchema.safeParse(await safelyParseJson(c));\n if (!body.success) {\n return c.json(buildValidationError('Invalid replay body.', body.error.issues), 400);\n }\n const mode: ReplayMode = body.data.raw === true ? 'raw' : 'sanitized';\n const auth = c.get('state').auth;\n const scopeError = enforceReplayScope(auth, mode);\n if (scopeError !== undefined) {\n await maybeAppendAudit(deps.auditDb, {\n actor: auditActor(auth),\n action: 'replay:skipped',\n target: `session:${sessionId}`,\n decision: 'denied',\n ts: now(),\n metadata: { mode, reason: scopeError.code },\n });\n return c.json(scopeError.body, scopeError.status);\n }\n const replay = await deps.replay.loadSessionReplay({\n sessionId,\n mode,\n ...(body.data.fromMessageId !== undefined ? { fromMessageId: body.data.fromMessageId } : {}),\n ...(body.data.provider !== undefined ? { provider: body.data.provider } : {}),\n });\n await maybeAppendAudit(deps.auditDb, {\n actor: auditActor(auth),\n action: 'replay:accessed',\n target: `session:${sessionId}`,\n decision: 'success',\n ts: now(),\n metadata: { mode, eventCount: replay.events.length },\n });\n return c.json(serializeReplay(replay, mode));\n });\n\n return app;\n}\n\ninterface ScopeRefusal {\n readonly code: 'auth-required' | 'scope-denied';\n readonly status: ContentfulStatusCode;\n readonly body: { readonly error: string; readonly message: string; readonly hint?: string };\n}\n\nfunction enforceReplayScope(auth: AuthState, mode: ReplayMode): ScopeRefusal | undefined {\n // IP-13: both a verified token and the no-auth anonymous principal carry a\n // `grantedScopes` set; only a genuinely unauthenticated request is refused.\n if (auth.kind === 'unauthenticated') {\n return {\n code: 'auth-required',\n status: 401,\n body: {\n error: 'auth-required',\n message: 'Authentication required for replay endpoints.',\n },\n };\n }\n const required = mode === 'raw' ? SCOPE_RAW : SCOPE_SANITIZED;\n for (const scope of auth.grantedScopes) {\n if (scopeMatches(scope, required)) return undefined;\n }\n return {\n code: 'scope-denied',\n status: 403,\n body: {\n error: 'scope-denied',\n message: `Token lacks required scope '${required.raw}'.`,\n hint: `Mint a token with the '${required.raw}' (or admin:*) scope.`,\n },\n };\n}\n\nfunction auditActor(auth: AuthState): {\n readonly kind: 'token' | 'system';\n readonly id: string;\n readonly label?: string;\n} {\n if (auth.kind === 'token') {\n return {\n kind: 'token',\n id: auth.token.tokenId,\n ...(auth.token.label !== undefined ? { label: auth.token.label } : {}),\n };\n }\n // IP-13: attribute no-auth replays to the synthetic 'anonymous' principal.\n return { kind: 'system', id: auth.kind === 'anonymous' ? 'anonymous' : 'unauthenticated' };\n}\n\nasync function maybeAppendAudit(\n auditDb: AuditDb | undefined,\n entry: Parameters<typeof appendAudit>[1],\n): Promise<void> {\n if (auditDb === undefined) return;\n try {\n await appendAudit(auditDb, entry);\n } catch {\n // Best-effort — replay must not be blocked by audit failures.\n }\n}\n\nfunction serializeReplay(replay: ReplayResponse, mode: ReplayMode): Record<string, unknown> {\n return {\n mode,\n events: [...replay.events],\n ...(replay.truncated === true ? { truncated: true } : {}),\n ...(replay.nextCursor !== undefined ? { nextCursor: replay.nextCursor } : {}),\n };\n}\n\nfunction buildValidationError(\n message: string,\n issues: ReadonlyArray<{\n readonly path: ReadonlyArray<string | number>;\n readonly message: string;\n }>,\n): Record<string, unknown> {\n return {\n error: 'config-invalid',\n message,\n issues: issues.map((i) => ({ path: i.path, message: i.message })),\n };\n}\n\nasync function safelyParseJson(c: {\n readonly req: { readonly json: () => Promise<unknown> };\n}): Promise<unknown> {\n try {\n return await c.req.json();\n } catch {\n return {};\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ServerVariables } from "../internal/context.js";
|
|
2
|
+
import { AgentRegistry } from "../registry/index.js";
|
|
3
|
+
import { WsDispatcher } from "../ws/dispatcher.js";
|
|
4
|
+
import { RunStateTracker } from "../runtime/run-state.js";
|
|
5
|
+
import { Hono } from "hono";
|
|
6
|
+
|
|
7
|
+
//#region src/routes/agents.d.ts
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @stable
|
|
11
|
+
*/
|
|
12
|
+
interface AgentRoutesDeps {
|
|
13
|
+
readonly agents: AgentRegistry;
|
|
14
|
+
readonly runs: RunStateTracker;
|
|
15
|
+
readonly newRunId?: () => string;
|
|
16
|
+
/**
|
|
17
|
+
* Streaming dispatcher (IP-2). When present, `POST /:id/stream`
|
|
18
|
+
* actually runs the agent and emits every event onto the
|
|
19
|
+
* `agent:<id>/runs/<runId>/events` subject.
|
|
20
|
+
*/
|
|
21
|
+
readonly dispatcher?: WsDispatcher;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* @stable
|
|
25
|
+
*/
|
|
26
|
+
declare function createAgentRoutes(deps: AgentRoutesDeps): Hono<{
|
|
27
|
+
Variables: ServerVariables;
|
|
28
|
+
}>;
|
|
29
|
+
/**
|
|
30
|
+
* Companion router for the `/runs/...` surface. Kept separate so the
|
|
31
|
+
* `createServer` factory can mount it under the top-level base path
|
|
32
|
+
* rather than under `/agents`.
|
|
33
|
+
*
|
|
34
|
+
* @stable
|
|
35
|
+
*/
|
|
36
|
+
declare function createRunRoutes(deps: AgentRoutesDeps): Hono<{
|
|
37
|
+
Variables: ServerVariables;
|
|
38
|
+
}>;
|
|
39
|
+
//#endregion
|
|
40
|
+
export { AgentRoutesDeps, createAgentRoutes, createRunRoutes };
|
|
41
|
+
//# sourceMappingURL=agents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents.d.ts","names":[],"sources":["../../src/routes/agents.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;UA8BiB,eAAA;mBACE;iBACF;;;;;;;wBAAe;;;;;iBAsBhB,iBAAA,OAAwB,kBAAkB;aAAkB;;;;;;;;;iBA+L5D,eAAA,OAAsB,kBAAkB;aAAkB"}
|