@crouter/sdk 0.3.377

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.
Files changed (84) hide show
  1. package/README.md +170 -0
  2. package/dist/client.d.ts +153 -0
  3. package/dist/client.js +491 -0
  4. package/dist/error-codes.d.ts +7 -0
  5. package/dist/error-codes.js +35 -0
  6. package/dist/errors.d.ts +43 -0
  7. package/dist/errors.js +76 -0
  8. package/dist/index.d.ts +33 -0
  9. package/dist/index.js +13 -0
  10. package/dist/keygen-cli.d.ts +2 -0
  11. package/dist/keygen-cli.js +6 -0
  12. package/dist/keygen-command.d.ts +6 -0
  13. package/dist/keygen-command.js +157 -0
  14. package/dist/oauth/index.d.ts +147 -0
  15. package/dist/oauth/index.js +377 -0
  16. package/dist/oauth/keygen.d.ts +18 -0
  17. package/dist/oauth/keygen.js +46 -0
  18. package/dist/resources/activity.d.ts +22 -0
  19. package/dist/resources/activity.js +43 -0
  20. package/dist/resources/attachments.d.ts +15 -0
  21. package/dist/resources/attachments.js +13 -0
  22. package/dist/resources/bash.d.ts +9 -0
  23. package/dist/resources/bash.js +15 -0
  24. package/dist/resources/canvas/history.d.ts +11 -0
  25. package/dist/resources/canvas/history.js +20 -0
  26. package/dist/resources/canvas.d.ts +19 -0
  27. package/dist/resources/canvas.js +42 -0
  28. package/dist/resources/crons.d.ts +15 -0
  29. package/dist/resources/crons.js +33 -0
  30. package/dist/resources/custom-objects.d.ts +20 -0
  31. package/dist/resources/custom-objects.js +84 -0
  32. package/dist/resources/files.d.ts +34 -0
  33. package/dist/resources/files.js +34 -0
  34. package/dist/resources/forward.d.ts +7 -0
  35. package/dist/resources/forward.js +64 -0
  36. package/dist/resources/human/inbox.d.ts +14 -0
  37. package/dist/resources/human/inbox.js +30 -0
  38. package/dist/resources/human/requests.d.ts +13 -0
  39. package/dist/resources/human/requests.js +26 -0
  40. package/dist/resources/human.d.ts +8 -0
  41. package/dist/resources/human.js +10 -0
  42. package/dist/resources/identifiers.d.ts +8 -0
  43. package/dist/resources/identifiers.js +33 -0
  44. package/dist/resources/memory.d.ts +69 -0
  45. package/dist/resources/memory.js +30 -0
  46. package/dist/resources/models/config.d.ts +8 -0
  47. package/dist/resources/models/config.js +9 -0
  48. package/dist/resources/models/credentials.d.ts +10 -0
  49. package/dist/resources/models/credentials.js +17 -0
  50. package/dist/resources/models.d.ts +8 -0
  51. package/dist/resources/models.js +10 -0
  52. package/dist/resources/node-stream.d.ts +34 -0
  53. package/dist/resources/node-stream.js +176 -0
  54. package/dist/resources/nodes/jobs.d.ts +9 -0
  55. package/dist/resources/nodes/jobs.js +14 -0
  56. package/dist/resources/nodes/result.d.ts +8 -0
  57. package/dist/resources/nodes/result.js +11 -0
  58. package/dist/resources/nodes/worktree.d.ts +9 -0
  59. package/dist/resources/nodes/worktree.js +14 -0
  60. package/dist/resources/nodes.d.ts +23 -0
  61. package/dist/resources/nodes.js +47 -0
  62. package/dist/resources/providers.d.ts +51 -0
  63. package/dist/resources/providers.js +15 -0
  64. package/dist/resources/questions.d.ts +49 -0
  65. package/dist/resources/questions.js +21 -0
  66. package/dist/resources/request.d.ts +3 -0
  67. package/dist/resources/request.js +11 -0
  68. package/dist/resources/run-reply.d.ts +79 -0
  69. package/dist/resources/run-reply.js +84 -0
  70. package/dist/resources/run-stream.d.ts +40 -0
  71. package/dist/resources/run-stream.js +206 -0
  72. package/dist/resources/runs.d.ts +184 -0
  73. package/dist/resources/runs.js +197 -0
  74. package/dist/resources/shares.d.ts +40 -0
  75. package/dist/resources/shares.js +19 -0
  76. package/dist/resources/uploads.d.ts +27 -0
  77. package/dist/resources/uploads.js +11 -0
  78. package/dist/schema.d.ts +7 -0
  79. package/dist/schema.js +10 -0
  80. package/dist/stores/postgres.d.ts +29 -0
  81. package/dist/stores/postgres.js +86 -0
  82. package/dist/types.d.ts +80 -0
  83. package/dist/types.js +1 -0
  84. package/package.json +55 -0
@@ -0,0 +1,8 @@
1
+ import { ModelConfig } from './models/config.js';
2
+ import { ModelCredentials } from './models/credentials.js';
3
+ import type { Request } from './request.js';
4
+ export declare class Models {
5
+ readonly credentials: ModelCredentials;
6
+ readonly config: ModelConfig;
7
+ constructor(request: Request);
8
+ }
@@ -0,0 +1,10 @@
1
+ import { ModelConfig } from './models/config.js';
2
+ import { ModelCredentials } from './models/credentials.js';
3
+ export class Models {
4
+ credentials;
5
+ config;
6
+ constructor(request) {
7
+ this.credentials = new ModelCredentials(request);
8
+ this.config = new ModelConfig(request);
9
+ }
10
+ }
@@ -0,0 +1,34 @@
1
+ import { type NodeDetailDTO, type NodeEventDTO, type NodeOutcomeDTO } from '@crouter/api';
2
+ /** One event from a node's stream. */
3
+ export type NodeStreamEvent = NodeEventDTO;
4
+ /** The `type` discriminant of a stream event, and the name `on` and `off` take. */
5
+ export type NodeStreamEventType = NodeStreamEvent['type'];
6
+ /** A listener for one event type, receiving only that type's event. */
7
+ export type NodeStreamEventListener<T extends NodeStreamEventType> = (event: Extract<NodeStreamEvent, {
8
+ type: T;
9
+ }>) => void;
10
+ /** A parsed node event stream. Aborting it disconnects this observer, not the node. */
11
+ export declare class NodeStream implements AsyncIterable<NodeStreamEvent> {
12
+ /** The node this stream follows; resolves once the daemon has created it. */
13
+ readonly node: Promise<NodeDetailDTO>;
14
+ private readonly controller;
15
+ private readonly listeners;
16
+ private readonly settled;
17
+ private resolveOutcome;
18
+ private rejectOutcome;
19
+ private finished;
20
+ constructor(node: Promise<NodeDetailDTO>, open: (signal: AbortSignal) => Promise<Response>, signal?: AbortSignal);
21
+ /** Adds a listener for one event type. */
22
+ on<T extends NodeStreamEventType>(type: T, listener: NodeStreamEventListener<T>): this;
23
+ /** Removes a listener added with `on`. */
24
+ off<T extends NodeStreamEventType>(type: T, listener: NodeStreamEventListener<T>): this;
25
+ /** Disconnects this observer. The node keeps running. */
26
+ abort(): void;
27
+ /** The node's settled outcome. Rejects with `APIError` on a terminal stream error. */
28
+ finalOutcome(): Promise<NodeOutcomeDTO>;
29
+ [Symbol.asyncIterator](): AsyncIterator<NodeStreamEvent>;
30
+ private consume;
31
+ private handleRecord;
32
+ private emit;
33
+ private fail;
34
+ }
@@ -0,0 +1,176 @@
1
+ import { isErrorBody } from '@crouter/api';
2
+ import { ResourceIdentifierError } from './identifiers.js';
3
+ import { APIConnectionError, APIError, APIUserAbortError, mapError } from '../errors.js';
4
+ /** A parsed node event stream. Aborting it disconnects this observer, not the node. */
5
+ export class NodeStream {
6
+ /** The node this stream follows; resolves once the daemon has created it. */
7
+ node;
8
+ controller = new AbortController();
9
+ listeners = new Map();
10
+ settled;
11
+ resolveOutcome;
12
+ rejectOutcome;
13
+ finished = false;
14
+ constructor(node, open, signal) {
15
+ this.node = node;
16
+ this.settled = new Promise((resolve, reject) => {
17
+ this.resolveOutcome = resolve;
18
+ this.rejectOutcome = reject;
19
+ });
20
+ void this.settled.catch(() => undefined);
21
+ if (signal !== undefined) {
22
+ if (signal.aborted)
23
+ this.controller.abort(signal.reason);
24
+ else
25
+ signal.addEventListener('abort', () => this.controller.abort(signal.reason), { once: true });
26
+ }
27
+ void this.consume(open);
28
+ }
29
+ /** Adds a listener for one event type. */
30
+ on(type, listener) {
31
+ const listeners = this.listeners.get(type) ?? new Set();
32
+ listeners.add(listener);
33
+ this.listeners.set(type, listeners);
34
+ return this;
35
+ }
36
+ /** Removes a listener added with `on`. */
37
+ off(type, listener) {
38
+ this.listeners.get(type)?.delete(listener);
39
+ return this;
40
+ }
41
+ /** Disconnects this observer. The node keeps running. */
42
+ abort() {
43
+ this.controller.abort();
44
+ }
45
+ /** The node's settled outcome. Rejects with `APIError` on a terminal stream error. */
46
+ finalOutcome() {
47
+ return this.settled;
48
+ }
49
+ async *[Symbol.asyncIterator]() {
50
+ const events = [];
51
+ let wake;
52
+ const listener = (event) => {
53
+ events.push(event);
54
+ wake?.();
55
+ wake = undefined;
56
+ };
57
+ void this.settled.then(() => { wake?.(); wake = undefined; }, () => { wake?.(); wake = undefined; });
58
+ for (const type of eventTypes)
59
+ this.on(type, listener);
60
+ try {
61
+ for (;;) {
62
+ const event = events.shift();
63
+ if (event !== undefined) {
64
+ yield event;
65
+ continue;
66
+ }
67
+ if (this.finished) {
68
+ await this.settled;
69
+ return;
70
+ }
71
+ await new Promise((resolve) => { wake = resolve; });
72
+ }
73
+ }
74
+ finally {
75
+ for (const type of eventTypes)
76
+ this.off(type, listener);
77
+ }
78
+ }
79
+ async consume(open) {
80
+ try {
81
+ const response = await open(this.controller.signal);
82
+ if (!response.ok)
83
+ throw await responseError(response);
84
+ if (response.body === null)
85
+ throw new APIConnectionError(0, 'stream_ended', 'node event stream had no response body');
86
+ const reader = response.body.getReader();
87
+ const decoder = new TextDecoder();
88
+ let buffer = '';
89
+ for (;;) {
90
+ const { done, value } = await reader.read();
91
+ buffer += decoder.decode(value, { stream: !done });
92
+ const records = buffer.split(/\r\n\r\n|\n\n|\r\r/);
93
+ buffer = records.pop() ?? '';
94
+ for (const record of records)
95
+ this.handleRecord(record);
96
+ if (done) {
97
+ if (buffer !== '')
98
+ this.handleRecord(buffer);
99
+ break;
100
+ }
101
+ }
102
+ if (!this.finished)
103
+ this.fail(new APIConnectionError(0, 'stream_ended', 'node event stream ended before node.settled'));
104
+ }
105
+ catch (error) {
106
+ this.fail(this.controller.signal.aborted
107
+ ? new APIUserAbortError(0, 'request_aborted', 'request aborted by caller signal')
108
+ : error instanceof ResourceIdentifierError
109
+ ? error
110
+ : error instanceof APIError ? mapError(error) : error);
111
+ }
112
+ }
113
+ handleRecord(record) {
114
+ let type;
115
+ const data = [];
116
+ for (const line of record.split(/\r\n|\r|\n/)) {
117
+ if (line.startsWith(':'))
118
+ continue;
119
+ const separator = line.indexOf(':');
120
+ const field = separator === -1 ? line : line.slice(0, separator);
121
+ const value = separator === -1 ? '' : line.slice(separator + 1).replace(/^ /, '');
122
+ if (field === 'event')
123
+ type = value;
124
+ if (field === 'data')
125
+ data.push(value);
126
+ }
127
+ if (type === undefined || data.length === 0)
128
+ return;
129
+ const parsed = JSON.parse(data.join('\n'));
130
+ const event = { ...parsed, type };
131
+ this.emit(event);
132
+ if (event.type === 'node.settled') {
133
+ this.finished = true;
134
+ this.resolveOutcome(event.outcome);
135
+ }
136
+ else if (event.type === 'error' && event.error.code !== 'stream_gap') {
137
+ this.fail(new APIError(0, event.error.code, event.error.message, event.error.details));
138
+ }
139
+ }
140
+ emit(event) {
141
+ for (const listener of this.listeners.get(event.type) ?? [])
142
+ listener(event);
143
+ }
144
+ fail(error) {
145
+ if (this.finished)
146
+ return;
147
+ this.finished = true;
148
+ this.rejectOutcome(error);
149
+ }
150
+ }
151
+ async function responseError(response) {
152
+ const text = (await response.text()).trim();
153
+ let body;
154
+ try {
155
+ body = text === '' ? undefined : JSON.parse(text);
156
+ }
157
+ catch {
158
+ return new APIError(response.status, 'invalid_response', text.slice(0, 500), undefined, response.headers);
159
+ }
160
+ if (isErrorBody(body)) {
161
+ return new APIError(response.status, body.error.code, body.error.message, body.error.details, response.headers);
162
+ }
163
+ return new APIError(response.status, 'internal', `request failed with status ${response.status}`, undefined, response.headers);
164
+ }
165
+ const eventTypes = [
166
+ 'node.output_text.delta',
167
+ 'node.output_text.done',
168
+ 'node.tool_call.started',
169
+ 'node.tool_call.completed',
170
+ 'node.turn.started',
171
+ 'node.turn.completed',
172
+ 'node.report.pushed',
173
+ 'node.status.changed',
174
+ 'node.settled',
175
+ 'error',
176
+ ];
@@ -0,0 +1,9 @@
1
+ import { type BashJobStatusDTO, type BashJobStopResultDTO } from '@crouter/api';
2
+ import type { RequestOptions } from '../../types.js';
3
+ import type { Request } from '../request.js';
4
+ export declare class NodeJobs {
5
+ private readonly request;
6
+ constructor(request: Request);
7
+ list(id: string, options?: RequestOptions): Promise<BashJobStatusDTO[]>;
8
+ cancel(id: string, jobId: string, options?: RequestOptions): Promise<BashJobStopResultDTO>;
9
+ }
@@ -0,0 +1,14 @@
1
+ import { routes } from '@crouter/api';
2
+ import { jobId as validatedJobId, nodeId } from '../identifiers.js';
3
+ export class NodeJobs {
4
+ request;
5
+ constructor(request) {
6
+ this.request = request;
7
+ }
8
+ async list(id, options) {
9
+ return this.request('GET', routes.nodeJobs(nodeId(id)), undefined, options);
10
+ }
11
+ async cancel(id, jobId, options) {
12
+ return this.request('DELETE', routes.nodeJob(nodeId(id), validatedJobId(jobId)), undefined, options);
13
+ }
14
+ }
@@ -0,0 +1,8 @@
1
+ import { type SubmitResultDTO, type SubmitResultRequest } from '@crouter/api';
2
+ import type { RequestOptions } from '../../types.js';
3
+ import type { Request } from '../request.js';
4
+ export declare class NodeResult {
5
+ private readonly request;
6
+ constructor(request: Request);
7
+ submit(id: string, body: SubmitResultRequest, options?: RequestOptions): Promise<SubmitResultDTO>;
8
+ }
@@ -0,0 +1,11 @@
1
+ import { routes } from '@crouter/api';
2
+ import { nodeId } from '../identifiers.js';
3
+ export class NodeResult {
4
+ request;
5
+ constructor(request) {
6
+ this.request = request;
7
+ }
8
+ async submit(id, body, options) {
9
+ return this.request('POST', routes.nodeResult(nodeId(id)), body, options);
10
+ }
11
+ }
@@ -0,0 +1,9 @@
1
+ import { type AbandonWorktreeRequest, type AbandonWorktreeResultDTO, type CloseWorktreeResultDTO } from '@crouter/api';
2
+ import type { RequestOptions } from '../../types.js';
3
+ import type { Request } from '../request.js';
4
+ export declare class NodeWorktree {
5
+ private readonly request;
6
+ constructor(request: Request);
7
+ close(id: string, options?: RequestOptions): Promise<CloseWorktreeResultDTO>;
8
+ abandon(id: string, body: AbandonWorktreeRequest, options?: RequestOptions): Promise<AbandonWorktreeResultDTO>;
9
+ }
@@ -0,0 +1,14 @@
1
+ import { routes, } from '@crouter/api';
2
+ import { nodeId } from '../identifiers.js';
3
+ export class NodeWorktree {
4
+ request;
5
+ constructor(request) {
6
+ this.request = request;
7
+ }
8
+ async close(id, options) {
9
+ return this.request('POST', routes.nodeWorktreeClose(nodeId(id)), {}, options);
10
+ }
11
+ async abandon(id, body, options) {
12
+ return this.request('POST', routes.nodeWorktreeAbandon(nodeId(id)), body, options);
13
+ }
14
+ }
@@ -0,0 +1,23 @@
1
+ import { type NodeConfigPatch, type NodeDetailDTO, type RelaunchRootResultDTO, type ReviveAllResultDTO, type ReviveRequest, type ReviveResultDTO, type PromoteRequest, type WaitRequest, type YieldRequest } from '@crouter/api';
2
+ import type { RequestOptions } from '../types.js';
3
+ import { NodeJobs } from './nodes/jobs.js';
4
+ import { NodeResult } from './nodes/result.js';
5
+ import { NodeWorktree } from './nodes/worktree.js';
6
+ import type { Request } from './request.js';
7
+ export declare class NodesResource {
8
+ private readonly request;
9
+ readonly jobs: NodeJobs;
10
+ readonly worktree: NodeWorktree;
11
+ readonly result: NodeResult;
12
+ constructor(request: Request);
13
+ update(id: string, patch: NodeConfigPatch, options?: RequestOptions): Promise<NodeDetailDTO>;
14
+ fork(id: string, options?: RequestOptions): Promise<NodeDetailDTO>;
15
+ revive(id: string, body?: ReviveRequest, options?: RequestOptions): Promise<ReviveResultDTO>;
16
+ reviveAll(options?: RequestOptions): Promise<ReviveAllResultDTO>;
17
+ promote(id: string, body?: PromoteRequest, options?: RequestOptions): Promise<NodeDetailDTO>;
18
+ demote(id: string, options?: RequestOptions): Promise<NodeDetailDTO>;
19
+ recycle(id: string, options?: RequestOptions): Promise<NodeDetailDTO>;
20
+ yield(id: string, body?: YieldRequest, options?: RequestOptions): Promise<NodeDetailDTO>;
21
+ wait(id: string, body: WaitRequest, options?: RequestOptions): Promise<NodeDetailDTO>;
22
+ relaunchRoot(id: string, options?: RequestOptions): Promise<RelaunchRootResultDTO>;
23
+ }
@@ -0,0 +1,47 @@
1
+ import { routes, } from '@crouter/api';
2
+ import { nodeId } from './identifiers.js';
3
+ import { NodeJobs } from './nodes/jobs.js';
4
+ import { NodeResult } from './nodes/result.js';
5
+ import { NodeWorktree } from './nodes/worktree.js';
6
+ export class NodesResource {
7
+ request;
8
+ jobs;
9
+ worktree;
10
+ result;
11
+ constructor(request) {
12
+ this.request = request;
13
+ this.jobs = new NodeJobs(request);
14
+ this.worktree = new NodeWorktree(request);
15
+ this.result = new NodeResult(request);
16
+ }
17
+ async update(id, patch, options) {
18
+ return this.request('PATCH', routes.nodeConfig(nodeId(id)), patch, options);
19
+ }
20
+ async fork(id, options) {
21
+ return this.request('POST', routes.nodeFork(nodeId(id)), {}, options);
22
+ }
23
+ async revive(id, body, options) {
24
+ return this.request('POST', routes.nodeRevive(nodeId(id)), body ?? {}, options);
25
+ }
26
+ reviveAll(options) {
27
+ return this.request('POST', routes.reviveAll(), {}, options);
28
+ }
29
+ async promote(id, body, options) {
30
+ return this.request('POST', routes.nodePromote(nodeId(id)), body ?? {}, options);
31
+ }
32
+ async demote(id, options) {
33
+ return this.request('POST', routes.nodeDemote(nodeId(id)), {}, options);
34
+ }
35
+ async recycle(id, options) {
36
+ return this.request('POST', routes.nodeRecycle(nodeId(id)), {}, options);
37
+ }
38
+ async yield(id, body, options) {
39
+ return this.request('POST', routes.nodeYield(nodeId(id)), body ?? {}, options);
40
+ }
41
+ async wait(id, body, options) {
42
+ return this.request('POST', routes.nodeWait(nodeId(id)), body, options);
43
+ }
44
+ async relaunchRoot(id, options) {
45
+ return this.request('POST', routes.nodeRelaunchRoot(nodeId(id)), {}, options);
46
+ }
47
+ }
@@ -0,0 +1,51 @@
1
+ import type { RequestOptions } from '../types.js';
2
+ import type { Request } from './request.js';
3
+ /** Help-only provider manifest: granted leaves, never transport or credentials. */
4
+ export type ProviderToolEntry = {
5
+ provider: string;
6
+ display_name: string;
7
+ status: 'available';
8
+ major: number;
9
+ groups: Array<{
10
+ group: string;
11
+ leaves: Array<{
12
+ tool: string;
13
+ description: string;
14
+ summary: string;
15
+ params: unknown[];
16
+ output: unknown[];
17
+ }>;
18
+ }>;
19
+ commands: Array<{
20
+ kind: 'branch';
21
+ name: string;
22
+ description: string;
23
+ whenToUse: string;
24
+ summary: string;
25
+ children: unknown[];
26
+ }>;
27
+ } | {
28
+ provider: string;
29
+ display_name: string;
30
+ status: 'installing' | 'install_failed';
31
+ error?: string;
32
+ groups: [];
33
+ commands: [];
34
+ };
35
+ export declare class Providers {
36
+ private readonly request;
37
+ constructor(request: Request);
38
+ /** Omit provider to list every reachable provider; name one to inspect it. */
39
+ tools(provider: string, options?: RequestOptions): Promise<ProviderToolEntry>;
40
+ tools(provider?: undefined, options?: RequestOptions): Promise<{
41
+ providers: ProviderToolEntry[];
42
+ }>;
43
+ /** The daemon checks scope, mints an outbound token and calls the provider. */
44
+ call(input: {
45
+ provider: string;
46
+ tool: string;
47
+ arguments: Record<string, unknown>;
48
+ version?: number;
49
+ approval?: string;
50
+ }, options?: RequestOptions): Promise<unknown>;
51
+ }
@@ -0,0 +1,15 @@
1
+ import { pathSegment } from './identifiers.js';
2
+ export class Providers {
3
+ request;
4
+ constructor(request) {
5
+ this.request = request;
6
+ }
7
+ tools(provider, options) {
8
+ const path = provider === undefined ? '/v1/providers/tools' : `/v1/providers/${pathSegment(provider, 'provider')}/tools`;
9
+ return this.request('GET', path, undefined, options);
10
+ }
11
+ /** The daemon checks scope, mints an outbound token and calls the provider. */
12
+ call(input, options) {
13
+ return this.request('POST', '/v1/provider/call', input, options);
14
+ }
15
+ }
@@ -0,0 +1,49 @@
1
+ import type { RequestOptions } from '../types.js';
2
+ import type { Request } from './request.js';
3
+ /** One input or display element of a question. `id` is absent on a slot that takes no response. */
4
+ export interface QuestionSlot {
5
+ id?: string;
6
+ kind: string;
7
+ /** Zero-based step the slot appears on. */
8
+ step: number;
9
+ config: Record<string, unknown>;
10
+ }
11
+ /** The fields a `waiting_on_user` run event and `questions.get` share. */
12
+ export interface QuestionFields {
13
+ question_id: string;
14
+ /** The app whose run asked. */
15
+ initiating_app: string;
16
+ title: string;
17
+ subtitle?: string;
18
+ /** Number of steps. */
19
+ steps: number;
20
+ slots: QuestionSlot[];
21
+ }
22
+ export type QuestionStatus = 'open' | 'answered' | 'canceled';
23
+ /** Responses keyed by slot id. */
24
+ export type QuestionResponses = Record<string, unknown>;
25
+ /** A question an agent in a run asked the user. */
26
+ export interface Question extends QuestionFields {
27
+ run_id: string;
28
+ /** The asking node. */
29
+ node_id: string;
30
+ status: QuestionStatus;
31
+ /** Present once answered. */
32
+ answer?: QuestionResponses;
33
+ }
34
+ export interface QuestionAnswered {
35
+ question_id: string;
36
+ status: 'answered';
37
+ answer: QuestionResponses;
38
+ }
39
+ /** Questions from agents in runs (app listener only). */
40
+ export declare class Questions {
41
+ private readonly request;
42
+ constructor(request: Request);
43
+ /** Read a question of a run the caller can read; anything else is `not_found`. */
44
+ get(questionId: string, options?: RequestOptions): Promise<Question>;
45
+ /** Answer an open question of the caller's own run. An answer that does not match a slot is
46
+ * `invalid_request` with `param` naming the slot, and records nothing. A question already answered or
47
+ * canceled — including the loser of a race — is 409 `run_settled` with `details.state` and `details.result`. */
48
+ answer(questionId: string, responses: QuestionResponses, options?: RequestOptions): Promise<QuestionAnswered>;
49
+ }
@@ -0,0 +1,21 @@
1
+ import { pathSegment } from './identifiers.js';
2
+ /** Questions from agents in runs (app listener only). */
3
+ export class Questions {
4
+ request;
5
+ constructor(request) {
6
+ this.request = request;
7
+ }
8
+ /** Read a question of a run the caller can read; anything else is `not_found`. */
9
+ get(questionId, options) {
10
+ return this.request('GET', questionPath(questionId), undefined, options);
11
+ }
12
+ /** Answer an open question of the caller's own run. An answer that does not match a slot is
13
+ * `invalid_request` with `param` naming the slot, and records nothing. A question already answered or
14
+ * canceled — including the loser of a race — is 409 `run_settled` with `details.state` and `details.result`. */
15
+ answer(questionId, responses, options = {}) {
16
+ return this.request('POST', `${questionPath(questionId)}/answer`, { responses }, { ...options, maxRetries: 0 });
17
+ }
18
+ }
19
+ function questionPath(id) {
20
+ return `/v1/questions/${encodeURIComponent(pathSegment(id, 'question id'))}`;
21
+ }
@@ -0,0 +1,3 @@
1
+ import type { RequestOptions } from '../types.js';
2
+ export type Request = <T>(method: string, path: string, body?: unknown, options?: RequestOptions) => Promise<T>;
3
+ export declare function withQuery(path: string, query: object | undefined): string;
@@ -0,0 +1,11 @@
1
+ export function withQuery(path, query) {
2
+ if (query === undefined)
3
+ return path;
4
+ const search = new URLSearchParams();
5
+ for (const [key, value] of Object.entries(query)) {
6
+ if (value !== undefined)
7
+ search.set(key, String(value));
8
+ }
9
+ const suffix = search.toString();
10
+ return suffix === '' ? path : `${path}?${suffix}`;
11
+ }
@@ -0,0 +1,79 @@
1
+ import type { AssistantStopReasonDTO, RunOutcomeDTO, RunStatusDTO, TurnErrorDTO } from '@crouter/api';
2
+ import type { RunStream } from './run-stream.js';
3
+ /** What `runs.message` returns: the message a reply answers. */
4
+ export interface SentRunMessage {
5
+ run_id: string;
6
+ message_id: string;
7
+ sequence_number: number;
8
+ }
9
+ /** One event of the reply to a message (`runs.reply`). */
10
+ export type RunReplyEvent =
11
+ /** Streamed text of the turn's current assistant message. */
12
+ {
13
+ type: 'text.delta';
14
+ delta: string;
15
+ sequence_number: number;
16
+ }
17
+ /** One assistant message ended; `text` is its whole text, possibly empty (a refused or failed call writes none). */
18
+ | {
19
+ type: 'message.done';
20
+ text: string;
21
+ stop_reason: AssistantStopReasonDTO;
22
+ sequence_number: number;
23
+ }
24
+ /** The turn ended; always the last event of a reply that reached its turn. `error` is set when the turn's last assistant message stopped with `error`. */
25
+ | {
26
+ type: 'turn.completed';
27
+ run_status: Exclude<RunStatusDTO, 'settled'>;
28
+ stop_reason: string;
29
+ error?: TurnErrorDTO;
30
+ message_ids: string[];
31
+ sequence_number: number;
32
+ }
33
+ /** The run settled before the turn completed; the reply ends here. */
34
+ | {
35
+ type: 'run.settled';
36
+ outcome: RunOutcomeDTO;
37
+ sequence_number: number;
38
+ };
39
+ /** How a reply ended (`RunReply.collect`):
40
+ * - `completed`: the turn finished cleanly; `text` may still be empty when the model wrote nothing.
41
+ * - `error`: the turn's last assistant message failed; `error` says why.
42
+ * - `waiting_on_user`: the turn ended on a question to the person; the run waits for an answer.
43
+ * - `settled`: the run settled before the turn completed; `outcome` says how. */
44
+ export type RunReplyEnding = 'completed' | 'error' | 'waiting_on_user' | 'settled';
45
+ /** The whole reply, from `RunReply.collect`. */
46
+ export interface RunReplyResult {
47
+ /** Every assistant message's trimmed text in the turn, joined with a blank line; empty when the turn wrote none. */
48
+ text: string;
49
+ ended: RunReplyEnding;
50
+ /** Set when `ended` is `error`. */
51
+ error?: TurnErrorDTO;
52
+ /** Set when `ended` is `settled`. */
53
+ outcome?: RunOutcomeDTO;
54
+ }
55
+ /** `RunReply.collect` options. */
56
+ export interface RunReplyCollectOptions {
57
+ /** Called with each streamed piece of text. When a turn speaks again after a tool call, the first piece
58
+ * of the new message starts with a blank line, so the pieces concatenate to `text` (up to trimming). */
59
+ onDelta?: (delta: string) => void;
60
+ }
61
+ /**
62
+ * The reply to one message: the root's turn that delivers it. Consume it once:
63
+ * iterate it, or call `collect()` (text plus how it ended, with an optional
64
+ * `onDelta` while it streams) or `text()`. It ends after `turn.completed` (or `run.settled`) and
65
+ * closes its event stream; aborting the `signal` rejects with `APIUserAbortError`.
66
+ */
67
+ export declare class RunReply implements AsyncIterable<RunReplyEvent> {
68
+ private readonly open;
69
+ private readonly sent;
70
+ private consumed;
71
+ constructor(open: () => RunStream, sent: SentRunMessage);
72
+ [Symbol.asyncIterator](): AsyncIterator<RunReplyEvent>;
73
+ /** Consume the reply: stream its text to `onDelta` and resolve to the whole text and how the turn ended.
74
+ * A stream that closes before the turn ends throws, as iterating does. */
75
+ collect(options?: RunReplyCollectOptions): Promise<RunReplyResult>;
76
+ /** The reply's text: every assistant message's trimmed text in the turn, joined with a blank line. Empty when the turn wrote none.
77
+ * `onDelta` receives the text as it streams, as in `collect`. */
78
+ text(options?: RunReplyCollectOptions): Promise<string>;
79
+ }