@smithers-orchestrator/gateway 0.24.2 → 0.25.1
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/openapi.yaml +5010 -570
- package/package.json +1 -1
- package/src/auth/scopes.ts +18 -2
- package/src/rpc/index.ts +428 -1
package/package.json
CHANGED
package/src/auth/scopes.ts
CHANGED
|
@@ -8,6 +8,12 @@ export const GATEWAY_SCOPE_VALUES = [
|
|
|
8
8
|
"signal:submit",
|
|
9
9
|
"cron:read",
|
|
10
10
|
"cron:write",
|
|
11
|
+
"account:read",
|
|
12
|
+
"memory:read",
|
|
13
|
+
"prompt:read",
|
|
14
|
+
"score:read",
|
|
15
|
+
"ticket:read",
|
|
16
|
+
"ticket:write",
|
|
11
17
|
"observability:read",
|
|
12
18
|
] as const;
|
|
13
19
|
|
|
@@ -21,11 +27,18 @@ export const GATEWAY_SCOPE_DESCRIPTIONS: Record<GatewayScope, string> = {
|
|
|
21
27
|
"signal:submit": "Submit workflow signals.",
|
|
22
28
|
"cron:read": "List cron schedules.",
|
|
23
29
|
"cron:write": "Create, delete, and trigger cron schedules.",
|
|
30
|
+
"account:read": "List registered agent accounts (API keys redacted).",
|
|
31
|
+
"memory:read": "List cross-run memory facts.",
|
|
32
|
+
"prompt:read": "List registered prompts.",
|
|
33
|
+
"score:read": "List scorer/eval results for a run.",
|
|
34
|
+
"ticket:read": "List work docs (tickets/plans/specs/proposals).",
|
|
35
|
+
"ticket:write": "Create, update, and soft-delete work docs.",
|
|
24
36
|
"observability:read": "Read DevTools and other observability streams.",
|
|
25
37
|
};
|
|
26
38
|
|
|
27
39
|
const RUN_SCOPE_ORDER: GatewayScope[] = ["run:read", "run:write", "run:admin"];
|
|
28
40
|
const CRON_SCOPE_ORDER: GatewayScope[] = ["cron:read", "cron:write"];
|
|
41
|
+
const TICKET_SCOPE_ORDER: GatewayScope[] = ["ticket:read", "ticket:write"];
|
|
29
42
|
|
|
30
43
|
function normalizeScope(scope: string): string {
|
|
31
44
|
return scope.trim();
|
|
@@ -45,15 +58,18 @@ function gatewayScopeImplies(granted: GatewayScope, required: GatewayScope): boo
|
|
|
45
58
|
if (granted.startsWith("cron:") && required.startsWith("cron:")) {
|
|
46
59
|
return CRON_SCOPE_ORDER.indexOf(granted) >= CRON_SCOPE_ORDER.indexOf(required);
|
|
47
60
|
}
|
|
61
|
+
if (granted.startsWith("ticket:") && required.startsWith("ticket:")) {
|
|
62
|
+
return TICKET_SCOPE_ORDER.indexOf(granted) >= TICKET_SCOPE_ORDER.indexOf(required);
|
|
63
|
+
}
|
|
48
64
|
return false;
|
|
49
65
|
}
|
|
50
66
|
|
|
51
67
|
function legacyAccessImplies(scope: string, required: GatewayScope): boolean {
|
|
52
68
|
switch (scope) {
|
|
53
69
|
case "read":
|
|
54
|
-
return required === "run:read" || required === "cron:read" || required === "observability:read";
|
|
70
|
+
return required === "run:read" || required === "cron:read" || required === "account:read" || required === "memory:read" || required === "prompt:read" || required === "score:read" || required === "ticket:read" || required === "observability:read";
|
|
55
71
|
case "execute":
|
|
56
|
-
return required === "run:read" || required === "run:write" || required === "signal:submit" || required === "cron:read" || required === "cron:write";
|
|
72
|
+
return required === "run:read" || required === "run:write" || required === "signal:submit" || required === "cron:read" || required === "cron:write" || required === "ticket:read" || required === "ticket:write";
|
|
57
73
|
case "approve":
|
|
58
74
|
return required === "approval:submit" || legacyAccessImplies("execute", required);
|
|
59
75
|
case "admin":
|
package/src/rpc/index.ts
CHANGED
|
@@ -32,6 +32,7 @@ export type GatewayRpcErrorCode =
|
|
|
32
32
|
| "RunNotFound"
|
|
33
33
|
| "RUN_NOT_ACTIVE"
|
|
34
34
|
| "CronNotFound"
|
|
35
|
+
| "TicketNotFound"
|
|
35
36
|
| "NodeNotFound"
|
|
36
37
|
| "IterationNotFound"
|
|
37
38
|
| "NodeHasNoOutput"
|
|
@@ -79,8 +80,10 @@ export type GatewayRpcMethod =
|
|
|
79
80
|
| "submitSignal"
|
|
80
81
|
| "getRun"
|
|
81
82
|
| "listRuns"
|
|
83
|
+
| "getSchemaSignature"
|
|
82
84
|
| "listWorkflows"
|
|
83
85
|
| "listApprovals"
|
|
86
|
+
| "listDocs"
|
|
84
87
|
| "streamRunEvents"
|
|
85
88
|
| "streamDevTools"
|
|
86
89
|
| "getNodeOutput"
|
|
@@ -88,7 +91,15 @@ export type GatewayRpcMethod =
|
|
|
88
91
|
| "cronList"
|
|
89
92
|
| "cronCreate"
|
|
90
93
|
| "cronDelete"
|
|
91
|
-
| "cronRun"
|
|
94
|
+
| "cronRun"
|
|
95
|
+
| "listAccounts"
|
|
96
|
+
| "listMemoryFacts"
|
|
97
|
+
| "listPrompts"
|
|
98
|
+
| "listScores"
|
|
99
|
+
| "listTickets"
|
|
100
|
+
| "createTicket"
|
|
101
|
+
| "updateTicket"
|
|
102
|
+
| "deleteTicket";
|
|
92
103
|
|
|
93
104
|
export type LaunchRunRequest = {
|
|
94
105
|
workflow: string;
|
|
@@ -178,6 +189,14 @@ export type ListRunsRequest = {
|
|
|
178
189
|
};
|
|
179
190
|
};
|
|
180
191
|
|
|
192
|
+
export type GetSchemaSignatureRequest = Record<string, never>;
|
|
193
|
+
|
|
194
|
+
export type GetSchemaSignatureResponse = {
|
|
195
|
+
schemaVersion: string;
|
|
196
|
+
signature: string;
|
|
197
|
+
components?: Record<string, string>;
|
|
198
|
+
};
|
|
199
|
+
|
|
181
200
|
export type GatewayWorkflowSummary = {
|
|
182
201
|
key: string;
|
|
183
202
|
readableName?: string;
|
|
@@ -219,6 +238,26 @@ export type ListApprovalsRequest = {
|
|
|
219
238
|
|
|
220
239
|
export type ListApprovalsResponse = GatewayApprovalSummary[];
|
|
221
240
|
|
|
241
|
+
export type GatewayDocRow = {
|
|
242
|
+
path: string;
|
|
243
|
+
kind: "ticket" | "plan" | "spec" | "proposal" | "conflict" | string;
|
|
244
|
+
content: string;
|
|
245
|
+
contentHash: string;
|
|
246
|
+
updatedAtMs: number;
|
|
247
|
+
deletedAtMs: number | null;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
export type ListDocsRequest = {
|
|
251
|
+
filter?: {
|
|
252
|
+
kind?: string;
|
|
253
|
+
includeDeleted?: boolean;
|
|
254
|
+
updatedAfterMs?: number;
|
|
255
|
+
limit?: number;
|
|
256
|
+
};
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
export type ListDocsResponse = GatewayDocRow[];
|
|
260
|
+
|
|
222
261
|
export type StreamRunEventsRequest = {
|
|
223
262
|
runId: string;
|
|
224
263
|
afterSeq?: number;
|
|
@@ -266,6 +305,158 @@ export type CronRunRequest = {
|
|
|
266
305
|
input?: Record<string, unknown>;
|
|
267
306
|
};
|
|
268
307
|
|
|
308
|
+
/**
|
|
309
|
+
* One registered Smithers agent account — a row in the user-level
|
|
310
|
+
* `~/.smithers/accounts.json` registry that the `smithers agents` CLI manages,
|
|
311
|
+
* surfaced read-only by the `listAccounts` server handler (via the
|
|
312
|
+
* `@smithers-orchestrator/accounts` package). Each account is either a
|
|
313
|
+
* subscription provider (a per-account CLI `configDir`) or an API provider (an
|
|
314
|
+
* `apiKey`); the two are mutually exclusive.
|
|
315
|
+
*
|
|
316
|
+
* SECRET REDACTION: the raw `apiKey` is NEVER sent over the wire — it is a
|
|
317
|
+
* plaintext credential stored mode-600 on disk. Instead `hasApiKey` reports
|
|
318
|
+
* whether an api-key account carries a non-empty key, and `hasConfigDir`
|
|
319
|
+
* reports whether a subscription account has a config dir, so a client can
|
|
320
|
+
* render the account's auth posture without ever receiving the secret.
|
|
321
|
+
*/
|
|
322
|
+
export type GatewayAccount = {
|
|
323
|
+
/** Unique account label (the registry key, `--label`). */
|
|
324
|
+
label: string;
|
|
325
|
+
/** Provider id, one of the fixed `smithers agents` catalog. */
|
|
326
|
+
provider:
|
|
327
|
+
| "claude-code"
|
|
328
|
+
| "antigravity"
|
|
329
|
+
| "codex"
|
|
330
|
+
| "gemini"
|
|
331
|
+
| "kimi"
|
|
332
|
+
| "anthropic-api"
|
|
333
|
+
| "openai-api"
|
|
334
|
+
| "gemini-api";
|
|
335
|
+
/** Per-account CLI config dir for subscription providers (absent for api-key accounts). */
|
|
336
|
+
configDir?: string | null;
|
|
337
|
+
/** True when a subscription account has a non-empty config dir. */
|
|
338
|
+
hasConfigDir: boolean;
|
|
339
|
+
/** True when an api-key account carries a non-empty key (the key itself is NEVER returned). */
|
|
340
|
+
hasApiKey: boolean;
|
|
341
|
+
/** Optional default model baked into the account. */
|
|
342
|
+
model?: string | null;
|
|
343
|
+
/** ISO timestamp of when the account was added, when known. */
|
|
344
|
+
addedAt?: string | null;
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
export type ListAccountsRequest = Record<string, never>;
|
|
348
|
+
|
|
349
|
+
export type ListAccountsResponse = GatewayAccount[];
|
|
350
|
+
|
|
351
|
+
/** One cross-run memory fact row (the `_smithers_memory_facts` table, snake→camel cased). */
|
|
352
|
+
export type GatewayMemoryFact = {
|
|
353
|
+
namespace: string;
|
|
354
|
+
key: string;
|
|
355
|
+
valueJson: string;
|
|
356
|
+
schemaSig?: string | null;
|
|
357
|
+
createdAtMs: number;
|
|
358
|
+
updatedAtMs: number;
|
|
359
|
+
ttlMs?: number | null;
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
export type ListMemoryFactsRequest = {
|
|
363
|
+
namespace?: string;
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
export type ListMemoryFactsResponse = GatewayMemoryFact[];
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* One registered prompt row — a `.md`/`.mdx` file walked from the project's
|
|
370
|
+
* `.smithers/prompts/` directory by the `listPrompts` server handler. `id` is the
|
|
371
|
+
* prompt's relative path without extension (e.g. `refactor` or
|
|
372
|
+
* `release-content/changelog`); `entryFile` is the workspace-relative source path
|
|
373
|
+
* (e.g. `prompts/refactor.mdx`); `source` is the raw file text. The timestamps
|
|
374
|
+
* come from `fs.stat` (`birthtimeMs`/`mtimeMs`) so a freshly-edited prompt sorts
|
|
375
|
+
* recent.
|
|
376
|
+
*/
|
|
377
|
+
export type GatewayPrompt = {
|
|
378
|
+
id: string;
|
|
379
|
+
entryFile: string;
|
|
380
|
+
source: string;
|
|
381
|
+
createdAtMs?: number;
|
|
382
|
+
updatedAtMs?: number;
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
export type ListPromptsRequest = Record<string, never>;
|
|
386
|
+
|
|
387
|
+
export type ListPromptsResponse = GatewayPrompt[];
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* One scorer/eval result row (the `_smithers_scorers` table, snake→camel cased).
|
|
391
|
+
* `score` is the scorer's verdict; `latencyMs`/`durationMs` are the only timing
|
|
392
|
+
* metrics the table carries (there is NO token/cost data — those tiles are
|
|
393
|
+
* computed client-side and em-dashed when absent).
|
|
394
|
+
*/
|
|
395
|
+
export type GatewayScoreRow = {
|
|
396
|
+
runId: string;
|
|
397
|
+
nodeId: string;
|
|
398
|
+
iteration: number;
|
|
399
|
+
attempt: number;
|
|
400
|
+
scorerId: string;
|
|
401
|
+
scorerName: string;
|
|
402
|
+
source: string;
|
|
403
|
+
score: number;
|
|
404
|
+
reason?: string | null;
|
|
405
|
+
scoredAtMs: number;
|
|
406
|
+
latencyMs?: number | null;
|
|
407
|
+
durationMs?: number | null;
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
export type ListScoresRequest = {
|
|
411
|
+
runId: string;
|
|
412
|
+
nodeId?: string;
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
export type ListScoresResponse = GatewayScoreRow[];
|
|
416
|
+
|
|
417
|
+
/** A doc kind stored in `_smithers_docs`; the tickets surface uses `ticket`. */
|
|
418
|
+
export type GatewayDocKind = "ticket" | "plan" | "spec" | "proposal";
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* One LIVE doc row (the `_smithers_docs` table, snake→camel cased) returned by
|
|
422
|
+
* `listTickets`. Tombstones (`deletedAtMs != null`) are filtered server-side and
|
|
423
|
+
* NEVER appear here. `status` rides the row so a ticket's status survives reload
|
|
424
|
+
* (LOCKED Path A); `contentHash` is `sha256(content)`.
|
|
425
|
+
*/
|
|
426
|
+
export type GatewayTicketRow = {
|
|
427
|
+
path: string;
|
|
428
|
+
kind: GatewayDocKind;
|
|
429
|
+
content: string;
|
|
430
|
+
contentHash: string;
|
|
431
|
+
status?: string | null;
|
|
432
|
+
updatedAtMs: number;
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
export type ListTicketsRequest = {
|
|
436
|
+
/** Optional doc-kind filter; omit to list every kind. Defaults to all. */
|
|
437
|
+
kind?: GatewayDocKind;
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
export type ListTicketsResponse = GatewayTicketRow[];
|
|
441
|
+
|
|
442
|
+
export type CreateTicketRequest = {
|
|
443
|
+
/** Doc identity (PK); e.g. a ticket id like `feat-issues-card`. */
|
|
444
|
+
path: string;
|
|
445
|
+
content: string;
|
|
446
|
+
kind?: GatewayDocKind;
|
|
447
|
+
status?: string;
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
export type UpdateTicketRequest = {
|
|
451
|
+
path: string;
|
|
452
|
+
content?: string;
|
|
453
|
+
status?: string;
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
export type DeleteTicketRequest = {
|
|
457
|
+
path: string;
|
|
458
|
+
};
|
|
459
|
+
|
|
269
460
|
const stringSchema = (description: string): JsonSchema => ({ type: "string", description });
|
|
270
461
|
const booleanSchema = (description: string): JsonSchema => ({ type: "boolean", description });
|
|
271
462
|
const integerSchema = (description: string, minimum = 0): JsonSchema => ({
|
|
@@ -361,6 +552,7 @@ export const GATEWAY_RPC_ERRORS: Record<GatewayRpcErrorCode, GatewayRpcErrorDefi
|
|
|
361
552
|
RunNotFound: { version: SMITHERS_API_VERSION, code: "RunNotFound", httpStatus: 404, description: "The run does not exist." },
|
|
362
553
|
RUN_NOT_ACTIVE: { version: SMITHERS_API_VERSION, code: "RUN_NOT_ACTIVE", httpStatus: 409, description: "The run is not currently active and cannot be cancelled." },
|
|
363
554
|
CronNotFound: { version: SMITHERS_API_VERSION, code: "CronNotFound", httpStatus: 404, description: "The cron schedule does not exist." },
|
|
555
|
+
TicketNotFound: { version: SMITHERS_API_VERSION, code: "TicketNotFound", httpStatus: 404, description: "The ticket/work doc does not exist." },
|
|
364
556
|
NodeNotFound: { version: SMITHERS_API_VERSION, code: "NodeNotFound", httpStatus: 404, description: "The node does not exist on the run." },
|
|
365
557
|
IterationNotFound: { version: SMITHERS_API_VERSION, code: "IterationNotFound", httpStatus: 404, description: "The requested node iteration does not exist." },
|
|
366
558
|
NodeHasNoOutput: { version: SMITHERS_API_VERSION, code: "NodeHasNoOutput", httpStatus: 404, description: "The node has not produced output." },
|
|
@@ -543,6 +735,24 @@ export const GATEWAY_RPC_DEFINITIONS: readonly GatewayRpcDefinition[] = [
|
|
|
543
735
|
exampleRequest: { filter: { status: "finished", limit: 20 } },
|
|
544
736
|
exampleResponse: [{ runId: "run_01", workflowKey: "deploy", status: "finished", createdAtMs: 1710000000000 }],
|
|
545
737
|
},
|
|
738
|
+
{
|
|
739
|
+
version: SMITHERS_API_VERSION,
|
|
740
|
+
method: "getSchemaSignature",
|
|
741
|
+
title: "Get Schema Signature",
|
|
742
|
+
description: "Return the server Smithers schema migration head and table-catalog signature used by client persistence.",
|
|
743
|
+
maturity: "stable",
|
|
744
|
+
transport: "http+websocket",
|
|
745
|
+
requiredScope: "run:read",
|
|
746
|
+
requestSchema: objectSchema({}),
|
|
747
|
+
responseSchema: objectSchema({
|
|
748
|
+
schemaVersion: stringSchema("Current _smithers_schema_migrations head id."),
|
|
749
|
+
signature: stringSchema("Stable hash of the server schema catalog."),
|
|
750
|
+
components: objectSchema({}, [], "Per-table schema component hashes.", true),
|
|
751
|
+
}, ["schemaVersion", "signature"]),
|
|
752
|
+
errors: ["InvalidRequest", "Unauthorized", "Forbidden", "Internal"],
|
|
753
|
+
exampleRequest: {},
|
|
754
|
+
exampleResponse: { schemaVersion: "0016", signature: "sha256" },
|
|
755
|
+
},
|
|
546
756
|
{
|
|
547
757
|
version: SMITHERS_API_VERSION,
|
|
548
758
|
method: "listWorkflows",
|
|
@@ -587,6 +797,34 @@ export const GATEWAY_RPC_DEFINITIONS: readonly GatewayRpcDefinition[] = [
|
|
|
587
797
|
exampleRequest: { filter: { workflow: "deploy", limit: 20 } },
|
|
588
798
|
exampleResponse: [{ runId: "run_01", workflowKey: "deploy", nodeId: "approve", iteration: 0, requestTitle: "Approve deploy", requestedAtMs: 1710000000000 }],
|
|
589
799
|
},
|
|
800
|
+
{
|
|
801
|
+
version: SMITHERS_API_VERSION,
|
|
802
|
+
method: "listDocs",
|
|
803
|
+
title: "List Docs",
|
|
804
|
+
description: "List DB-backed Smithers markdown artifacts for tickets, plans, specs, proposals, and conflict markers.",
|
|
805
|
+
maturity: "stable",
|
|
806
|
+
transport: "http+websocket",
|
|
807
|
+
requiredScope: "run:read",
|
|
808
|
+
requestSchema: objectSchema({
|
|
809
|
+
filter: objectSchema({
|
|
810
|
+
kind: stringSchema("Optional doc kind filter."),
|
|
811
|
+
includeDeleted: booleanSchema("Include tombstone rows."),
|
|
812
|
+
updatedAfterMs: integerSchema("Only return docs updated after this millisecond timestamp.", 0),
|
|
813
|
+
limit: integerSchema("Maximum number of docs.", 1),
|
|
814
|
+
}),
|
|
815
|
+
}),
|
|
816
|
+
responseSchema: arraySchema(objectSchema({
|
|
817
|
+
path: stringSchema("Path relative to .smithers, e.g. tickets/smithers/0030.md."),
|
|
818
|
+
kind: stringSchema("ticket, plan, spec, proposal, or conflict."),
|
|
819
|
+
content: stringSchema("Markdown or conflict marker content."),
|
|
820
|
+
contentHash: stringSchema("SHA-256 hash of content."),
|
|
821
|
+
updatedAtMs: integerSchema("Last DB update time in milliseconds.", 0),
|
|
822
|
+
deletedAtMs: { type: ["integer", "null"], description: "Tombstone timestamp when deleted." },
|
|
823
|
+
}, ["path", "kind", "content", "contentHash", "updatedAtMs"]), "Smithers docs rows."),
|
|
824
|
+
errors: ["InvalidRequest", "Unauthorized", "Forbidden", "Internal"],
|
|
825
|
+
exampleRequest: { filter: { kind: "ticket", limit: 100 } },
|
|
826
|
+
exampleResponse: [{ path: "tickets/smithers/0030-jjhub-sse-seam.md", kind: "ticket", content: "# Ticket", contentHash: "sha256", updatedAtMs: 1710000000000, deletedAtMs: null }],
|
|
827
|
+
},
|
|
590
828
|
{
|
|
591
829
|
version: SMITHERS_API_VERSION,
|
|
592
830
|
method: "streamRunEvents",
|
|
@@ -704,6 +942,195 @@ export const GATEWAY_RPC_DEFINITIONS: readonly GatewayRpcDefinition[] = [
|
|
|
704
942
|
exampleRequest: { cronId: "cron_01", input: { dryRun: true } },
|
|
705
943
|
exampleResponse: { runId: "run_02", workflow: "deploy" },
|
|
706
944
|
},
|
|
945
|
+
{
|
|
946
|
+
version: SMITHERS_API_VERSION,
|
|
947
|
+
method: "listAccounts",
|
|
948
|
+
title: "List Accounts",
|
|
949
|
+
description: "List the registered Smithers agent accounts — the entries in the user-level `~/.smithers/accounts.json` registry that the `smithers agents` CLI manages. Each account's raw API key is redacted; `hasApiKey`/`hasConfigDir` report its auth posture instead.",
|
|
950
|
+
maturity: "stable",
|
|
951
|
+
transport: "http+websocket",
|
|
952
|
+
requiredScope: "account:read",
|
|
953
|
+
requestSchema: objectSchema({}),
|
|
954
|
+
responseSchema: arraySchema(objectSchema({
|
|
955
|
+
label: stringSchema("Unique account label (the registry key)."),
|
|
956
|
+
provider: {
|
|
957
|
+
type: "string",
|
|
958
|
+
enum: ["claude-code", "antigravity", "codex", "gemini", "kimi", "anthropic-api", "openai-api", "gemini-api"],
|
|
959
|
+
description: "Provider id, one of the fixed `smithers agents` catalog.",
|
|
960
|
+
},
|
|
961
|
+
configDir: { type: ["string", "null"], description: "Per-account CLI config dir for subscription providers (null for api-key accounts)." },
|
|
962
|
+
hasConfigDir: booleanSchema("True when a subscription account has a non-empty config dir."),
|
|
963
|
+
hasApiKey: booleanSchema("True when an api-key account carries a non-empty key (the key itself is never returned)."),
|
|
964
|
+
model: { type: ["string", "null"], description: "Optional default model baked into the account." },
|
|
965
|
+
addedAt: { type: ["string", "null"], description: "ISO timestamp of when the account was added, when known." },
|
|
966
|
+
}, ["label", "provider", "hasConfigDir", "hasApiKey"]), "Registered agent accounts."),
|
|
967
|
+
errors: ["InvalidRequest", "Unauthorized", "Forbidden", "Internal"],
|
|
968
|
+
exampleRequest: {},
|
|
969
|
+
exampleResponse: [{ label: "claude-work", provider: "claude-code", configDir: "/Users/me/.claude", hasConfigDir: true, hasApiKey: false, model: "claude-opus-4-8", addedAt: "2026-01-01T00:00:00.000Z" }],
|
|
970
|
+
},
|
|
971
|
+
{
|
|
972
|
+
version: SMITHERS_API_VERSION,
|
|
973
|
+
method: "listMemoryFacts",
|
|
974
|
+
title: "List Memory Facts",
|
|
975
|
+
description: "List cross-run memory facts, optionally scoped to a namespace.",
|
|
976
|
+
maturity: "stable",
|
|
977
|
+
transport: "http+websocket",
|
|
978
|
+
requiredScope: "memory:read",
|
|
979
|
+
requestSchema: objectSchema({ namespace: stringSchema("Optional namespace filter (e.g. 'workflow:deploy').") }),
|
|
980
|
+
responseSchema: arraySchema(objectSchema({
|
|
981
|
+
namespace: stringSchema("Fact namespace."),
|
|
982
|
+
key: stringSchema("Fact key (unique within the namespace)."),
|
|
983
|
+
valueJson: stringSchema("Stored value as a JSON string."),
|
|
984
|
+
schemaSig: { type: ["string", "null"], description: "Optional value-schema signature." },
|
|
985
|
+
createdAtMs: integerSchema("Unix epoch milliseconds when first written.", 0),
|
|
986
|
+
updatedAtMs: integerSchema("Unix epoch milliseconds when last written.", 0),
|
|
987
|
+
ttlMs: { type: ["integer", "null"], description: "Time-to-live in milliseconds, or null when the fact does not expire." },
|
|
988
|
+
}, ["namespace", "key", "valueJson", "createdAtMs", "updatedAtMs"]), "Memory facts."),
|
|
989
|
+
errors: ["InvalidRequest", "Unauthorized", "Forbidden", "Internal"],
|
|
990
|
+
exampleRequest: { namespace: "workflow:deploy" },
|
|
991
|
+
exampleResponse: [{ namespace: "workflow:deploy", key: "last-sha", valueJson: "\"abc123\"", createdAtMs: 1710000000000, updatedAtMs: 1710000000000 }],
|
|
992
|
+
},
|
|
993
|
+
{
|
|
994
|
+
version: SMITHERS_API_VERSION,
|
|
995
|
+
method: "listPrompts",
|
|
996
|
+
title: "List Prompts",
|
|
997
|
+
description: "List registered prompts — the `.md`/`.mdx` files under the project's `.smithers/prompts/` directory, each returned with its source.",
|
|
998
|
+
maturity: "stable",
|
|
999
|
+
transport: "http+websocket",
|
|
1000
|
+
requiredScope: "prompt:read",
|
|
1001
|
+
requestSchema: objectSchema({}),
|
|
1002
|
+
responseSchema: arraySchema(objectSchema({
|
|
1003
|
+
id: stringSchema("Prompt id — the relative path under `.smithers/prompts/` without its extension (e.g. 'refactor')."),
|
|
1004
|
+
entryFile: stringSchema("Workspace-relative source path (e.g. 'prompts/refactor.mdx')."),
|
|
1005
|
+
source: stringSchema("Raw prompt file text."),
|
|
1006
|
+
createdAtMs: integerSchema("Unix epoch milliseconds the file was created (fs birthtime).", 0),
|
|
1007
|
+
updatedAtMs: integerSchema("Unix epoch milliseconds the file was last modified (fs mtime).", 0),
|
|
1008
|
+
}, ["id", "entryFile", "source"]), "Registered prompts."),
|
|
1009
|
+
errors: ["InvalidRequest", "Unauthorized", "Forbidden", "Internal"],
|
|
1010
|
+
exampleRequest: {},
|
|
1011
|
+
exampleResponse: [{ id: "refactor", entryFile: "prompts/refactor.mdx", source: "# Refactor\n\nRefactor {{file}}.", createdAtMs: 1710000000000, updatedAtMs: 1710000000000 }],
|
|
1012
|
+
},
|
|
1013
|
+
{
|
|
1014
|
+
version: SMITHERS_API_VERSION,
|
|
1015
|
+
method: "listScores",
|
|
1016
|
+
title: "List Scores",
|
|
1017
|
+
description: "List scorer/eval results for one run, optionally scoped to a node.",
|
|
1018
|
+
maturity: "stable",
|
|
1019
|
+
transport: "http+websocket",
|
|
1020
|
+
requiredScope: "score:read",
|
|
1021
|
+
requestSchema: objectSchema({
|
|
1022
|
+
runId: stringSchema("Run id whose scorer results to list."),
|
|
1023
|
+
nodeId: stringSchema("Optional node id filter."),
|
|
1024
|
+
}, ["runId"]),
|
|
1025
|
+
responseSchema: arraySchema(objectSchema({
|
|
1026
|
+
runId: stringSchema("Run id the score belongs to."),
|
|
1027
|
+
nodeId: stringSchema("Node id the score was produced for."),
|
|
1028
|
+
iteration: integerSchema("Node iteration the score belongs to.", 0),
|
|
1029
|
+
attempt: integerSchema("Node attempt the score belongs to.", 0),
|
|
1030
|
+
scorerId: stringSchema("Stable scorer id."),
|
|
1031
|
+
scorerName: stringSchema("Human scorer name."),
|
|
1032
|
+
source: stringSchema("Where the score came from (e.g. 'scorer', 'eval')."),
|
|
1033
|
+
score: { type: "number", description: "The scorer's numeric verdict." },
|
|
1034
|
+
reason: { type: ["string", "null"], description: "Optional human reason for the score." },
|
|
1035
|
+
scoredAtMs: integerSchema("Unix epoch milliseconds when the score was recorded.", 0),
|
|
1036
|
+
latencyMs: { type: ["number", "null"], description: "Optional scorer latency in milliseconds." },
|
|
1037
|
+
durationMs: { type: ["number", "null"], description: "Optional node duration in milliseconds." },
|
|
1038
|
+
}, ["runId", "nodeId", "iteration", "attempt", "scorerId", "scorerName", "source", "score", "scoredAtMs"]), "Scorer results."),
|
|
1039
|
+
errors: ["InvalidRequest", "Unauthorized", "Forbidden", "RunNotFound", "Internal"],
|
|
1040
|
+
exampleRequest: { runId: "run_01", nodeId: "review" },
|
|
1041
|
+
exampleResponse: [{ runId: "run_01", nodeId: "review", iteration: 0, attempt: 0, scorerId: "correctness", scorerName: "correctness", source: "scorer", score: 0.92, scoredAtMs: 1710000000000 }],
|
|
1042
|
+
},
|
|
1043
|
+
{
|
|
1044
|
+
version: SMITHERS_API_VERSION,
|
|
1045
|
+
method: "listTickets",
|
|
1046
|
+
title: "List Tickets",
|
|
1047
|
+
description: "List live work docs (tickets/plans/specs/proposals) from `_smithers_docs`; soft-deleted tombstones are never returned.",
|
|
1048
|
+
maturity: "stable",
|
|
1049
|
+
transport: "http+websocket",
|
|
1050
|
+
requiredScope: "ticket:read",
|
|
1051
|
+
requestSchema: objectSchema({
|
|
1052
|
+
kind: { type: "string", enum: ["ticket", "plan", "spec", "proposal"], description: "Optional doc-kind filter; omit to list every kind." },
|
|
1053
|
+
}),
|
|
1054
|
+
responseSchema: arraySchema(objectSchema({
|
|
1055
|
+
path: stringSchema("Doc identity (primary key); e.g. a ticket id."),
|
|
1056
|
+
kind: { type: "string", enum: ["ticket", "plan", "spec", "proposal"], description: "Doc kind." },
|
|
1057
|
+
content: stringSchema("Full markdown body."),
|
|
1058
|
+
contentHash: stringSchema("sha256(content), lowercase hex."),
|
|
1059
|
+
status: { type: ["string", "null"], description: "Free-form status (e.g. todo/in-progress/done); rides the row so it survives reload." },
|
|
1060
|
+
updatedAtMs: integerSchema("Unix epoch milliseconds of the last write.", 0),
|
|
1061
|
+
}, ["path", "kind", "content", "contentHash", "updatedAtMs"]), "Live work docs."),
|
|
1062
|
+
errors: ["InvalidRequest", "Unauthorized", "Forbidden", "Internal"],
|
|
1063
|
+
exampleRequest: { kind: "ticket" },
|
|
1064
|
+
exampleResponse: [{ path: "feat-issues-card", kind: "ticket", content: "# Issues card", contentHash: "0000000000000000000000000000000000000000000000000000000000000000", status: "in-progress", updatedAtMs: 1710000000000 }],
|
|
1065
|
+
},
|
|
1066
|
+
{
|
|
1067
|
+
version: SMITHERS_API_VERSION,
|
|
1068
|
+
method: "createTicket",
|
|
1069
|
+
title: "Create Ticket",
|
|
1070
|
+
description: "Create or replace a work doc by `path`. Stamps `content_hash = sha256(content)` and `updated_at_ms = now`; reviving a previously soft-deleted path is intentional.",
|
|
1071
|
+
maturity: "stable",
|
|
1072
|
+
transport: "http+websocket",
|
|
1073
|
+
requiredScope: "ticket:write",
|
|
1074
|
+
requestSchema: objectSchema({
|
|
1075
|
+
path: stringSchema("Doc identity (primary key); e.g. `feat-issues-card`."),
|
|
1076
|
+
content: stringSchema("Full markdown body."),
|
|
1077
|
+
kind: { type: "string", enum: ["ticket", "plan", "spec", "proposal"], description: "Doc kind (default `ticket`)." },
|
|
1078
|
+
status: stringSchema("Optional initial status."),
|
|
1079
|
+
}, ["path", "content"]),
|
|
1080
|
+
responseSchema: objectSchema({
|
|
1081
|
+
path: stringSchema("Doc identity (primary key)."),
|
|
1082
|
+
kind: { type: "string", enum: ["ticket", "plan", "spec", "proposal"], description: "Doc kind." },
|
|
1083
|
+
content: stringSchema("Full markdown body."),
|
|
1084
|
+
contentHash: stringSchema("sha256(content), lowercase hex."),
|
|
1085
|
+
status: { type: ["string", "null"], description: "Free-form status." },
|
|
1086
|
+
updatedAtMs: integerSchema("Unix epoch milliseconds of the write.", 0),
|
|
1087
|
+
}, ["path", "kind", "content", "contentHash", "updatedAtMs"], "The created doc row."),
|
|
1088
|
+
errors: ["InvalidRequest", "Unauthorized", "Forbidden", "Internal"],
|
|
1089
|
+
exampleRequest: { path: "feat-issues-card", content: "# Issues card", kind: "ticket", status: "todo" },
|
|
1090
|
+
exampleResponse: { path: "feat-issues-card", kind: "ticket", content: "# Issues card", contentHash: "0000000000000000000000000000000000000000000000000000000000000000", status: "todo", updatedAtMs: 1710000000000 },
|
|
1091
|
+
},
|
|
1092
|
+
{
|
|
1093
|
+
version: SMITHERS_API_VERSION,
|
|
1094
|
+
method: "updateTicket",
|
|
1095
|
+
title: "Update Ticket",
|
|
1096
|
+
description: "Patch a work doc's `content` and/or `status` by `path`. Re-stamps `content_hash` + `updated_at_ms` when content changes.",
|
|
1097
|
+
maturity: "stable",
|
|
1098
|
+
transport: "http+websocket",
|
|
1099
|
+
requiredScope: "ticket:write",
|
|
1100
|
+
requestSchema: objectSchema({
|
|
1101
|
+
path: stringSchema("Doc identity (primary key)."),
|
|
1102
|
+
content: stringSchema("Optional new markdown body."),
|
|
1103
|
+
status: stringSchema("Optional new status."),
|
|
1104
|
+
}, ["path"]),
|
|
1105
|
+
responseSchema: objectSchema({
|
|
1106
|
+
path: stringSchema("Doc identity (primary key)."),
|
|
1107
|
+
kind: { type: "string", enum: ["ticket", "plan", "spec", "proposal"], description: "Doc kind." },
|
|
1108
|
+
content: stringSchema("Full markdown body."),
|
|
1109
|
+
contentHash: stringSchema("sha256(content), lowercase hex."),
|
|
1110
|
+
status: { type: ["string", "null"], description: "Free-form status." },
|
|
1111
|
+
updatedAtMs: integerSchema("Unix epoch milliseconds of the write.", 0),
|
|
1112
|
+
}, ["path", "kind", "content", "contentHash", "updatedAtMs"], "The updated doc row."),
|
|
1113
|
+
errors: ["InvalidRequest", "Unauthorized", "Forbidden", "TicketNotFound", "Internal"],
|
|
1114
|
+
exampleRequest: { path: "feat-issues-card", status: "in-progress" },
|
|
1115
|
+
exampleResponse: { path: "feat-issues-card", kind: "ticket", content: "# Issues card", contentHash: "0000000000000000000000000000000000000000000000000000000000000000", status: "in-progress", updatedAtMs: 1710000000000 },
|
|
1116
|
+
},
|
|
1117
|
+
{
|
|
1118
|
+
version: SMITHERS_API_VERSION,
|
|
1119
|
+
method: "deleteTicket",
|
|
1120
|
+
title: "Delete Ticket",
|
|
1121
|
+
description: "Soft-delete a work doc by `path` (stamps a `deleted_at_ms` tombstone). The row survives so `listTickets` hides it without losing history; the watcher never materializes a tombstone to disk.",
|
|
1122
|
+
maturity: "stable",
|
|
1123
|
+
transport: "http+websocket",
|
|
1124
|
+
requiredScope: "ticket:write",
|
|
1125
|
+
requestSchema: objectSchema({ path: stringSchema("Doc identity (primary key).") }, ["path"]),
|
|
1126
|
+
responseSchema: objectSchema({
|
|
1127
|
+
path: stringSchema("Doc identity (primary key)."),
|
|
1128
|
+
deleted: booleanSchema("True when the doc was soft-deleted."),
|
|
1129
|
+
}, ["path", "deleted"], "Soft-delete acknowledgement."),
|
|
1130
|
+
errors: ["InvalidRequest", "Unauthorized", "Forbidden", "TicketNotFound", "Internal"],
|
|
1131
|
+
exampleRequest: { path: "feat-issues-card" },
|
|
1132
|
+
exampleResponse: { path: "feat-issues-card", deleted: true },
|
|
1133
|
+
},
|
|
707
1134
|
] as const;
|
|
708
1135
|
|
|
709
1136
|
const definitionByMethod = new Map<string, GatewayRpcDefinition>(
|