@simular-ai/sai-sdk 0.0.0-stage → 0.1.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +371 -2
- package/dist/client.d.ts +78 -0
- package/dist/client.js +110 -0
- package/dist/client.js.map +1 -0
- package/dist/control.d.ts +74 -0
- package/dist/control.js +137 -0
- package/dist/control.js.map +1 -0
- package/dist/errors.d.ts +71 -0
- package/dist/errors.js +114 -0
- package/dist/errors.js.map +1 -0
- package/dist/http.d.ts +22 -0
- package/dist/http.js +83 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/learn.d.ts +121 -0
- package/dist/learn.js +174 -0
- package/dist/learn.js.map +1 -0
- package/dist/machines.d.ts +15 -0
- package/dist/machines.js +47 -0
- package/dist/machines.js.map +1 -0
- package/dist/sessions.d.ts +152 -0
- package/dist/sessions.js +378 -0
- package/dist/sessions.js.map +1 -0
- package/dist/tenants.d.ts +102 -0
- package/dist/tenants.js +157 -0
- package/dist/tenants.js.map +1 -0
- package/dist/turns.d.ts +32 -0
- package/dist/turns.js +50 -0
- package/dist/turns.js.map +1 -0
- package/dist/types.d.ts +456 -0
- package/dist/types.js +364 -0
- package/dist/types.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/package.json +39 -4
package/dist/learn.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Learn and judge (research preview): turn recorded turns, or a task the agent does first, into
|
|
3
|
+
* Simulang code you own; repair that code; run, judge and repair it on a machine until it passes
|
|
4
|
+
* (policy iteration); and judge turns against criteria you choose now.
|
|
5
|
+
*
|
|
6
|
+
* Maps to `/v1/agents/learn` and `/v1/agents/judge`.
|
|
7
|
+
*/
|
|
8
|
+
import { RateLimitError } from './errors.js';
|
|
9
|
+
import { idOf } from './machines.js';
|
|
10
|
+
import { parseJudgement, parseLearnResult, parseLearnRun, } from './types.js';
|
|
11
|
+
/** A conversion is two model calls of up to a minute or two each, plus one retry. */
|
|
12
|
+
const LEARN_TIMEOUT_MS = 600_000;
|
|
13
|
+
const JUDGE_TIMEOUT_MS = 180_000;
|
|
14
|
+
/** The last log lines of a run sent with an evaluation; the API takes 20 KB per result. */
|
|
15
|
+
const EVAL_LOG_LINES = 30;
|
|
16
|
+
/** How often `wait` reads a learn job; a job's steps take tens of seconds to minutes. */
|
|
17
|
+
const LEARN_POLL_INTERVAL_MS = 10_000;
|
|
18
|
+
const turnId = (t) => {
|
|
19
|
+
const id = typeof t === 'string' ? t : t.id;
|
|
20
|
+
if (!id)
|
|
21
|
+
throw new Error('This turn has no id: the message was answered without starting one.');
|
|
22
|
+
return id;
|
|
23
|
+
};
|
|
24
|
+
function evaluationJson(e) {
|
|
25
|
+
const r = e.result;
|
|
26
|
+
return {
|
|
27
|
+
inputs: e.inputs,
|
|
28
|
+
...(r && {
|
|
29
|
+
result: {
|
|
30
|
+
result: r.result,
|
|
31
|
+
logs: r.logs.slice(-EVAL_LOG_LINES),
|
|
32
|
+
...(r.error && { error: { kind: r.error.kind, message: r.error.message } }),
|
|
33
|
+
},
|
|
34
|
+
}),
|
|
35
|
+
...(e.judgement !== undefined && {
|
|
36
|
+
judgement: typeof e.judgement === 'string' ? e.judgement : e.judgement.id,
|
|
37
|
+
}),
|
|
38
|
+
...(e.passed !== undefined && { passed: e.passed }),
|
|
39
|
+
...(e.note !== undefined && { note: e.note }),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export class Learn {
|
|
43
|
+
http;
|
|
44
|
+
constructor(http) {
|
|
45
|
+
this.http = http;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Make Simulang code, and optionally run, judge and repair it on a machine.
|
|
49
|
+
*
|
|
50
|
+
* - `create(turns)`: code from recorded turns (ordered, one session, each sent with
|
|
51
|
+
* `recordTrajectory`). Answers with the code; a longer task takes longer.
|
|
52
|
+
* - `create([], { initialCode, guidance | evaluations })`: a repair. Answers with the code.
|
|
53
|
+
* - `create(turns, { maxIterations: 3 })` or `create([], { taskTemplate, inputs, maxIterations: 3 })`:
|
|
54
|
+
* a job on the machine (from `turns`, or after the agent does the task). Answers at once with
|
|
55
|
+
* `status: 'running'`; read it with `wait()`.
|
|
56
|
+
*
|
|
57
|
+
* A result with `validation.errors` comes back `failed`.
|
|
58
|
+
*/
|
|
59
|
+
async create(turns, opts = {}) {
|
|
60
|
+
const body = { turns: turns.map(turnId) };
|
|
61
|
+
// The old names go out as sent, so code that uses them keeps working with an older API.
|
|
62
|
+
if (opts.taskTemplate !== undefined)
|
|
63
|
+
body.taskTemplate = opts.taskTemplate;
|
|
64
|
+
else if (opts.template !== undefined)
|
|
65
|
+
body.template = opts.template;
|
|
66
|
+
if (opts.inputs !== undefined)
|
|
67
|
+
body.inputs = opts.inputs;
|
|
68
|
+
if (opts.initialCode !== undefined)
|
|
69
|
+
body.initialCode = opts.initialCode;
|
|
70
|
+
if (opts.evaluations)
|
|
71
|
+
body.evaluations = opts.evaluations.map(evaluationJson);
|
|
72
|
+
if (opts.guidance !== undefined)
|
|
73
|
+
body.guidance = opts.guidance;
|
|
74
|
+
else if (opts.feedback !== undefined)
|
|
75
|
+
body.feedback = opts.feedback;
|
|
76
|
+
if (opts.machine !== undefined)
|
|
77
|
+
body.machineId = idOf(opts.machine);
|
|
78
|
+
for (const k of [
|
|
79
|
+
'history',
|
|
80
|
+
'runsWithoutReset',
|
|
81
|
+
'maxIterations',
|
|
82
|
+
'rollouts',
|
|
83
|
+
'criteria',
|
|
84
|
+
'reset',
|
|
85
|
+
'judgeInitialAgentRun',
|
|
86
|
+
'continueWithAgent',
|
|
87
|
+
'rolloutMinutes',
|
|
88
|
+
'agentRunMinutes',
|
|
89
|
+
'continuationMinutes',
|
|
90
|
+
'maxJobMinutes',
|
|
91
|
+
]) {
|
|
92
|
+
if (opts[k] !== undefined)
|
|
93
|
+
body[k] = opts[k];
|
|
94
|
+
}
|
|
95
|
+
if (opts.model !== undefined)
|
|
96
|
+
body.model = opts.model;
|
|
97
|
+
if (opts.idempotencyKey !== undefined)
|
|
98
|
+
body.idempotency_key = opts.idempotencyKey;
|
|
99
|
+
return parseLearnResult(await this.http.json('POST', '/v1/agents/learn', {
|
|
100
|
+
json: body,
|
|
101
|
+
timeoutMs: opts.timeoutMs ?? LEARN_TIMEOUT_MS,
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
/** A learn call's result, or a job's progress so far. */
|
|
105
|
+
async get(learnId) {
|
|
106
|
+
return parseLearnResult(await this.http.json('GET', `/v1/agents/learn/${encodeURIComponent(learnId)}`));
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Read a learn job until it ends (`succeeded`, `failed` or `stopped`). `timeoutMs` throws a
|
|
110
|
+
* `TimeoutError` (DOMException) with the job still running; it keeps running.
|
|
111
|
+
*/
|
|
112
|
+
async wait(job, opts = {}) {
|
|
113
|
+
const learnId = typeof job === 'string' ? job : job.id;
|
|
114
|
+
const poll = opts.pollMs ?? LEARN_POLL_INTERVAL_MS;
|
|
115
|
+
const deadline = opts.timeoutMs === undefined ? null : Date.now() + opts.timeoutMs;
|
|
116
|
+
for (;;) {
|
|
117
|
+
let r;
|
|
118
|
+
try {
|
|
119
|
+
r = await this.get(learnId);
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
if (!(err instanceof RateLimitError))
|
|
123
|
+
throw err;
|
|
124
|
+
await sleep((err.retryAfter ?? 30) * 1000);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (r.status !== 'running')
|
|
128
|
+
return r;
|
|
129
|
+
if (deadline !== null && Date.now() + poll > deadline) {
|
|
130
|
+
throw new DOMException(`Learn job ${learnId} is still running.`, 'TimeoutError');
|
|
131
|
+
}
|
|
132
|
+
await sleep(poll);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Stop a learn job at once, whatever step it is on. It keeps the last complete results and
|
|
137
|
+
* reads `stopped` / `cancelled` once it has ended, normally within seconds.
|
|
138
|
+
*/
|
|
139
|
+
async cancel(job) {
|
|
140
|
+
const learnId = typeof job === 'string' ? job : job.id;
|
|
141
|
+
await this.http.json('POST', `/v1/agents/learn/${encodeURIComponent(learnId)}/cancel`);
|
|
142
|
+
}
|
|
143
|
+
/** One run of a learn job's code: what it returned and logged, and its judgement. */
|
|
144
|
+
async rollout(job, iteration, rollout) {
|
|
145
|
+
const learnId = typeof job === 'string' ? job : job.id;
|
|
146
|
+
return parseLearnRun(await this.http.json('GET', `/v1/agents/learn/${encodeURIComponent(learnId)}/iterations/${iteration}/rollouts/${rollout}`));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
150
|
+
export class Judge {
|
|
151
|
+
http;
|
|
152
|
+
constructor(http) {
|
|
153
|
+
this.http = http;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Judge recorded `turns` (one session, as one task) against `criteria`: a sentence for a model
|
|
157
|
+
* to judge, or a list of `Check`s. Nothing runs again, and the turns do not change.
|
|
158
|
+
*/
|
|
159
|
+
async create(turns, criteria, opts = {}) {
|
|
160
|
+
const body = { turns: turns.map(turnId), criteria };
|
|
161
|
+
if (opts.task !== undefined)
|
|
162
|
+
body.task = opts.task;
|
|
163
|
+
if (opts.idempotencyKey !== undefined)
|
|
164
|
+
body.idempotency_key = opts.idempotencyKey;
|
|
165
|
+
return parseJudgement(await this.http.json('POST', '/v1/agents/judge', {
|
|
166
|
+
json: body,
|
|
167
|
+
timeoutMs: opts.timeoutMs ?? JUDGE_TIMEOUT_MS,
|
|
168
|
+
}));
|
|
169
|
+
}
|
|
170
|
+
async get(judgementId) {
|
|
171
|
+
return parseJudgement(await this.http.json('GET', `/v1/agents/judge/${encodeURIComponent(judgementId)}`));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=learn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learn.js","sourceRoot":"","sources":["../src/learn.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Http } from './http.js';
|
|
2
|
+
import { type LiveView, type Machine } from './types.js';
|
|
3
|
+
export type MachineRef = Machine | string;
|
|
4
|
+
/** Machines: the computers Sai works on. Cloud computers, or the customer's own PC with the Sai app. */
|
|
5
|
+
export declare class Machines {
|
|
6
|
+
private readonly http;
|
|
7
|
+
constructor(http: Http);
|
|
8
|
+
list(): Promise<Machine[]>;
|
|
9
|
+
get(machineId: string): Promise<Machine>;
|
|
10
|
+
/** A view-only stream of the screen (Apache Guacamole). The machine must be running. */
|
|
11
|
+
live(machine: MachineRef): Promise<LiveView>;
|
|
12
|
+
/** @internal */
|
|
13
|
+
resolveId(machine: MachineRef | undefined): Promise<string>;
|
|
14
|
+
}
|
|
15
|
+
export declare function idOf(machine: MachineRef): string;
|
package/dist/machines.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { BadRequestError, NotFoundError } from './errors.js';
|
|
2
|
+
import { parseMachine } from './types.js';
|
|
3
|
+
/** Machines: the computers Sai works on. Cloud computers, or the customer's own PC with the Sai app. */
|
|
4
|
+
export class Machines {
|
|
5
|
+
http;
|
|
6
|
+
constructor(http) {
|
|
7
|
+
this.http = http;
|
|
8
|
+
}
|
|
9
|
+
async list() {
|
|
10
|
+
const d = await this.http.json('GET', '/v1/agents/machines');
|
|
11
|
+
return (d.machines ?? []).map(parseMachine);
|
|
12
|
+
}
|
|
13
|
+
async get(machineId) {
|
|
14
|
+
const m = (await this.list()).find((x) => x.id === machineId);
|
|
15
|
+
if (!m)
|
|
16
|
+
throw new NotFoundError(`Machine ${machineId} not found on this account.`, { status: 404 });
|
|
17
|
+
return m;
|
|
18
|
+
}
|
|
19
|
+
/** A view-only stream of the screen (Apache Guacamole). The machine must be running. */
|
|
20
|
+
async live(machine) {
|
|
21
|
+
const id = idOf(machine);
|
|
22
|
+
const d = await this.http.json('POST', `/v1/agents/machines/${encodeURIComponent(id)}/live`);
|
|
23
|
+
return {
|
|
24
|
+
machineId: typeof d.machineId === 'string' ? d.machineId : id,
|
|
25
|
+
websocketUrl: String(d.websocketUrl),
|
|
26
|
+
width: Number(d.width ?? 0),
|
|
27
|
+
height: Number(d.height ?? 0),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** @internal */
|
|
31
|
+
async resolveId(machine) {
|
|
32
|
+
if (machine)
|
|
33
|
+
return idOf(machine);
|
|
34
|
+
const machines = await this.list();
|
|
35
|
+
if (machines.length === 1)
|
|
36
|
+
return machines[0].id;
|
|
37
|
+
if (machines.length === 0) {
|
|
38
|
+
throw new BadRequestError('No computer on this account. Create one in the Sai app or the portal.', { status: 400 });
|
|
39
|
+
}
|
|
40
|
+
const names = machines.map((m) => (m.name ? `${m.id} (${m.name})` : m.id)).join(', ');
|
|
41
|
+
throw new BadRequestError(`Several computers on this account; pass machine. Candidates: ${names}`, { status: 400 });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function idOf(machine) {
|
|
45
|
+
return typeof machine === 'string' ? machine : machine.id;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=machines.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"machines.js","sourceRoot":"","sources":["../src/machines.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE5D,OAAO,EAAE,YAAY,EAA+B,MAAM,YAAY,CAAA;AAItE,wGAAwG;AACxG,MAAM,OAAO,QAAQ;IACU;IAA7B,YAA6B,IAAU;QAAV,SAAI,GAAJ,IAAI,CAAM;IAAG,CAAC;IAE3C,KAAK,CAAC,IAAI;QACR,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAC5B,KAAK,EACL,qBAAqB,CACtB,CAAA;QACD,OAAO,CAAC,CAAC,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;IAC7C,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,SAAiB;QACzB,MAAM,CAAC,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAA;QAC7D,IAAI,CAAC,CAAC;YACJ,MAAM,IAAI,aAAa,CAAC,WAAW,SAAS,6BAA6B,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;QAC7F,OAAO,CAAC,CAAA;IACV,CAAC;IAED,wFAAwF;IACxF,KAAK,CAAC,IAAI,CAAC,OAAmB;QAC5B,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,CAAA;QACxB,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAC5B,MAAM,EACN,uBAAuB,kBAAkB,CAAC,EAAE,CAAC,OAAO,CACrD,CAAA;QACD,OAAO;YACL,SAAS,EAAE,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;YAC7D,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC;YACpC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;YAC3B,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC;SAC9B,CAAA;IACH,CAAC;IAED,gBAAgB;IAChB,KAAK,CAAC,SAAS,CAAC,OAA+B;QAC7C,IAAI,OAAO;YAAE,OAAO,IAAI,CAAC,OAAO,CAAC,CAAA;QACjC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAA;QAClC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,QAAQ,CAAC,CAAC,CAAE,CAAC,EAAE,CAAA;QACjD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,eAAe,CACvB,uEAAuE,EACvE,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAA;QACH,CAAC;QACD,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACrF,MAAM,IAAI,eAAe,CACvB,gEAAgE,KAAK,EAAE,EACvE,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAA;IACH,CAAC;CACF;AAED,MAAM,UAAU,IAAI,CAAC,OAAmB;IACtC,OAAO,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAA;AAC3D,CAAC"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sessions: a conversation with Sai on one computer. Sai decides every click.
|
|
3
|
+
*
|
|
4
|
+
* Maps to `/v1/agents/*`: `new-session`, `message` (wait=false), `events`,
|
|
5
|
+
* `approve`, `abort`. Every message gets a turn id (`turn.id`, read with
|
|
6
|
+
* `sai.turns`); a message with `inputs`, `outputSchema` or `successCriteria`
|
|
7
|
+
* is structured and its turn also returns a typed result.
|
|
8
|
+
*/
|
|
9
|
+
import type { Http } from './http.js';
|
|
10
|
+
import { type MachineRef, type Machines } from './machines.js';
|
|
11
|
+
import { type Approval, type Attachment, type Check, type Event, type TurnRecord, type TurnRecordStatus, type TurnResult, type TurnStatus } from './types.js';
|
|
12
|
+
/**
|
|
13
|
+
* What an `onApproval` handler answers: a response (`yes`, `no`, `task`), a
|
|
14
|
+
* `[response, selections]` pair for a choice, or null to leave the approval
|
|
15
|
+
* pending (e.g. a human answers it in a browser).
|
|
16
|
+
*/
|
|
17
|
+
export type ApprovalAnswer = string | [string, string[][]] | null;
|
|
18
|
+
export type ApprovalHandler = (approval: Approval) => ApprovalAnswer | Promise<ApprovalAnswer>;
|
|
19
|
+
export interface SendOptions {
|
|
20
|
+
/** An id from `sai.models.list()` (or `auto`); sticks to the session until changed. */
|
|
21
|
+
model?: string;
|
|
22
|
+
attachments?: Attachment[];
|
|
23
|
+
/** Values for the `{{name}}` placeholders in the message. Makes the message structured. */
|
|
24
|
+
inputs?: Record<string, string | number | boolean>;
|
|
25
|
+
/** JSON Schema (at most 32 KB) the turn's output must match. Makes the message structured. */
|
|
26
|
+
outputSchema?: Record<string, unknown>;
|
|
27
|
+
/**
|
|
28
|
+
* A guideline for a judge, or yes/no checks, the turn is checked against.
|
|
29
|
+
* Makes the message structured.
|
|
30
|
+
*/
|
|
31
|
+
successCriteria?: string | Check[];
|
|
32
|
+
/** The same key again returns the earlier turn and sends nothing. */
|
|
33
|
+
idempotencyKey?: string;
|
|
34
|
+
/** Keep a screenshot and the action for each step (`sai.turns.trajectory()`). */
|
|
35
|
+
recordTrajectory?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Run the turn this message starts without approval cards, as if "Trust for
|
|
38
|
+
* this task" had been answered; the grant ends with the turn. A message that
|
|
39
|
+
* steers a running turn changes nothing.
|
|
40
|
+
*/
|
|
41
|
+
autonomous?: boolean;
|
|
42
|
+
}
|
|
43
|
+
export interface WaitOptions {
|
|
44
|
+
onApproval?: ApprovalHandler;
|
|
45
|
+
/** Throws a `TimeoutError` (DOMException) with the turn still running. */
|
|
46
|
+
timeoutMs?: number;
|
|
47
|
+
/** Seconds the server may hold one poll; it caps at 60. */
|
|
48
|
+
pollWait?: number;
|
|
49
|
+
}
|
|
50
|
+
export declare class Sessions {
|
|
51
|
+
private readonly http;
|
|
52
|
+
private readonly machines;
|
|
53
|
+
constructor(http: Http, machines: Machines);
|
|
54
|
+
/**
|
|
55
|
+
* Start a fresh conversation on a computer: the next message has no memory
|
|
56
|
+
* of earlier ones. `machine` may be omitted with one computer.
|
|
57
|
+
*/
|
|
58
|
+
create(opts?: {
|
|
59
|
+
machine?: MachineRef;
|
|
60
|
+
}): Promise<Session>;
|
|
61
|
+
/** Reattach to a session started elsewhere, e.g. to poll or abort it. */
|
|
62
|
+
resume(sessionId: string, opts?: {
|
|
63
|
+
machine?: MachineRef;
|
|
64
|
+
}): Session;
|
|
65
|
+
}
|
|
66
|
+
/** A conversation with Sai. Each `send()` is a turn; turns share context. */
|
|
67
|
+
export declare class Session {
|
|
68
|
+
private readonly http;
|
|
69
|
+
machineId: string | null;
|
|
70
|
+
id: string | null;
|
|
71
|
+
constructor(http: Http, machineId: string | null, id: string | null);
|
|
72
|
+
/**
|
|
73
|
+
* Give Sai a task, or steer the running one. Returns at once. Sent while a
|
|
74
|
+
* turn is running, the message steers that turn (`turn.queued` is true).
|
|
75
|
+
*
|
|
76
|
+
* Any of `inputs`, `outputSchema` or `successCriteria` makes the message
|
|
77
|
+
* structured: the message is a template whose `{{name}}` placeholders are
|
|
78
|
+
* filled from `inputs`, the output must match `outputSchema`, and the turn
|
|
79
|
+
* is checked against `successCriteria`. `wait()` then also returns the
|
|
80
|
+
* turn's record in `result.record`. A structured message is refused with
|
|
81
|
+
* `ConflictError` (`session_busy`) while a turn is running. These go to the
|
|
82
|
+
* API as `options`.
|
|
83
|
+
*/
|
|
84
|
+
send(message: string, opts?: SendOptions): Promise<Turn>;
|
|
85
|
+
/** `send()` then `wait()`: the answer, or where it stopped. */
|
|
86
|
+
run(message: string, opts?: SendOptions & WaitOptions): Promise<TurnResult>;
|
|
87
|
+
/** Stop the running turn. Pending approvals are denied. False if nothing was running. */
|
|
88
|
+
abort(): Promise<boolean>;
|
|
89
|
+
/**
|
|
90
|
+
* Answer an approval: `yes`, `no`, or `task` (allow for the rest of the task).
|
|
91
|
+
* A `choice` approval needs `selections`, one list of option values per
|
|
92
|
+
* question. Throws `LinkOnlyApprovalError` (with `approvalUrl`) when only a
|
|
93
|
+
* browser can answer it. Resolves to `approved`, `approved_task` or `denied`.
|
|
94
|
+
*/
|
|
95
|
+
respond(approval: Approval | string, response?: string, opts?: {
|
|
96
|
+
selections?: string[][];
|
|
97
|
+
}): Promise<string>;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* One message's work. Iterate `events()` to watch it, or `wait()` for the
|
|
101
|
+
* result. `id` is the turn the message started, or the running turn it joined
|
|
102
|
+
* (`sai.turns.get(turn)`); null when the message was answered without one.
|
|
103
|
+
*/
|
|
104
|
+
export declare class Turn {
|
|
105
|
+
#private;
|
|
106
|
+
readonly session: Session;
|
|
107
|
+
private readonly http;
|
|
108
|
+
readonly queued: boolean;
|
|
109
|
+
readonly id: string | null;
|
|
110
|
+
private cursor;
|
|
111
|
+
private last;
|
|
112
|
+
private readonly structured;
|
|
113
|
+
private readonly replay;
|
|
114
|
+
private readonly local;
|
|
115
|
+
constructor(session: Session, http: Http, queued: boolean, done?: TurnResult | null, turn?: {
|
|
116
|
+
id: string | null;
|
|
117
|
+
structured: boolean;
|
|
118
|
+
replay?: TurnRecord;
|
|
119
|
+
});
|
|
120
|
+
get status(): TurnStatus;
|
|
121
|
+
/** The last known state, or null before the first poll. */
|
|
122
|
+
get result(): TurnResult | null;
|
|
123
|
+
/**
|
|
124
|
+
* Yield events until the turn is idle or errors. It keeps going through
|
|
125
|
+
* `needs_approval`: answer with `turn.respond(ev.approval, ...)` inside the
|
|
126
|
+
* loop, or let a human answer a link-only approval in a browser. Without
|
|
127
|
+
* `raw`, framing, reasoning and tool-call frames are skipped.
|
|
128
|
+
*/
|
|
129
|
+
events(opts?: {
|
|
130
|
+
raw?: boolean;
|
|
131
|
+
pollWait?: number;
|
|
132
|
+
}): AsyncGenerator<Event>;
|
|
133
|
+
/**
|
|
134
|
+
* Wait until the turn is done, or stops on an approval nobody answered.
|
|
135
|
+
* Without `onApproval`, resolves with `status === 'needs_approval'` and the
|
|
136
|
+
* `approval` so the caller can decide. With it, the handler's answer is sent
|
|
137
|
+
* and waiting continues; a handler returning null leaves the approval to
|
|
138
|
+
* someone else (a link-only one to a human) and keeps waiting. For a
|
|
139
|
+
* structured message, it then waits for the turn's record (`result.record`:
|
|
140
|
+
* output, evidence), which is worked out after the turn.
|
|
141
|
+
*/
|
|
142
|
+
wait(opts?: WaitOptions): Promise<TurnResult>;
|
|
143
|
+
respond(approval: Approval | string, response?: string, opts?: {
|
|
144
|
+
selections?: string[][];
|
|
145
|
+
}): Promise<string>;
|
|
146
|
+
abort(): Promise<boolean>;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Read the turn's record until its status is in `stop`. Throws a `TimeoutError`
|
|
150
|
+
* (DOMException) past `deadline` (epoch ms).
|
|
151
|
+
*/
|
|
152
|
+
export declare function waitTurnRecord(http: Http, turnId: string, deadline: number | null, stop?: readonly TurnRecordStatus[]): Promise<TurnRecord>;
|