@agentshouse/kit 0.1.0-alpha.3 → 0.1.0-alpha.30
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/README.md +697 -260
- package/bin/connect-linux.sh +200 -25
- package/bin/kit.js +179 -157
- package/bootstrap.json +17 -0
- package/package.json +7 -2
- package/src/adapter-mcp.js +76 -12
- package/src/agents.js +63 -8
- package/src/binding.js +52 -0
- package/src/blocks.js +2 -26
- package/src/bridge.js +28 -14
- package/src/bytes.js +19 -8
- package/src/configuration.js +13 -209
- package/src/connector-login.js +3 -84
- package/src/contract.js +36 -11
- package/src/delivery.js +4 -7
- package/src/directory.js +6 -26
- package/src/draft.js +4 -82
- package/src/git-main.js +138 -0
- package/src/git.js +62 -0
- package/src/google/calendar.js +39 -45
- package/src/google/drive.js +61 -186
- package/src/google/entries.js +346 -0
- package/src/google/gateway.js +3 -6
- package/src/google/gmail.js +33 -23
- package/src/google/oauth.js +106 -66
- package/src/google/source.js +21 -23
- package/src/host.js +6 -2
- package/src/house-main.js +65 -8
- package/src/house.js +446 -127
- package/src/how-we-work.md +25 -0
- package/src/inbound.js +2 -18
- package/src/kit-work-stream.js +118 -0
- package/src/launcher.js +46 -10
- package/src/local-files.js +3 -11
- package/src/login.js +23 -11
- package/src/mcp.js +18 -0
- package/src/mount.js +13 -21
- package/src/original.js +4 -4
- package/src/pacing.js +41 -0
- package/src/plan.js +37 -45
- package/src/repository.js +3 -61
- package/src/session-bin/git +2 -0
- package/src/setup.js +14 -15
- package/src/skill-set/ask-matt/PHASE-BOUNDARIES.md +55 -0
- package/src/skill-set/ask-matt/SKILL.md +91 -0
- package/src/skill-set/ask-matt/agents/openai.yaml +4 -0
- package/src/skill-set/code-review/LICENSE +21 -0
- package/src/skill-set/code-review/SKILL.md +89 -0
- package/src/skill-set/code-review/agents/openai.yaml +3 -0
- package/src/skill-set/codebase-design/DEEPENING.md +37 -0
- package/src/skill-set/codebase-design/DESIGN-IT-TWICE.md +44 -0
- package/src/skill-set/codebase-design/LICENSE +21 -0
- package/src/skill-set/codebase-design/SKILL.md +116 -0
- package/src/skill-set/codebase-design/agents/openai.yaml +3 -0
- package/src/skill-set/diagnosing-bugs/LICENSE +21 -0
- package/src/skill-set/diagnosing-bugs/SKILL.md +140 -0
- package/src/skill-set/diagnosing-bugs/agents/openai.yaml +3 -0
- package/src/skill-set/diagnosing-bugs/scripts/hitl-loop.template.sh +44 -0
- package/src/skill-set/domain-modeling/ADR-FORMAT.md +47 -0
- package/src/skill-set/domain-modeling/CONTEXT-FORMAT.md +60 -0
- package/src/skill-set/domain-modeling/LICENSE +21 -0
- package/src/skill-set/domain-modeling/SKILL.md +76 -0
- package/src/skill-set/domain-modeling/agents/openai.yaml +3 -0
- package/src/skill-set/grill-me/LICENSE +21 -0
- package/src/skill-set/grill-me/SKILL.md +8 -0
- package/src/skill-set/grill-me/agents/openai.yaml +4 -0
- package/src/skill-set/grill-with-docs/LICENSE +21 -0
- package/src/skill-set/grill-with-docs/SKILL.md +8 -0
- package/src/skill-set/grill-with-docs/agents/openai.yaml +4 -0
- package/src/skill-set/grilling/LICENSE +21 -0
- package/src/{starter-set → skill-set}/grilling/SKILL.md +1 -1
- package/src/skill-set/handoff/LICENSE +21 -0
- package/src/skill-set/handoff/SKILL.md +17 -0
- package/src/skill-set/handoff/agents/openai.yaml +4 -0
- package/src/skill-set/implement/LICENSE +21 -0
- package/src/skill-set/implement/SKILL.md +16 -0
- package/src/skill-set/implement/agents/openai.yaml +4 -0
- package/src/skill-set/improve-codebase-architecture/HTML-REPORT.md +123 -0
- package/src/skill-set/improve-codebase-architecture/LICENSE +21 -0
- package/src/skill-set/improve-codebase-architecture/SKILL.md +72 -0
- package/src/skill-set/improve-codebase-architecture/agents/openai.yaml +4 -0
- package/src/skill-set/prototype/LICENSE +21 -0
- package/src/skill-set/prototype/LOGIC.md +67 -0
- package/src/skill-set/prototype/SKILL.md +28 -0
- package/src/skill-set/prototype/UI.md +112 -0
- package/src/skill-set/prototype/agents/openai.yaml +3 -0
- package/src/skill-set/research/LICENSE +21 -0
- package/src/skill-set/research/SKILL.md +14 -0
- package/src/skill-set/research/agents/openai.yaml +3 -0
- package/src/skill-set/resolving-merge-conflicts/LICENSE +21 -0
- package/src/skill-set/resolving-merge-conflicts/SKILL.md +16 -0
- package/src/skill-set/resolving-merge-conflicts/agents/openai.yaml +3 -0
- package/src/skill-set/tdd/LICENSE +21 -0
- package/src/skill-set/tdd/SKILL.md +40 -0
- package/src/skill-set/tdd/agents/openai.yaml +3 -0
- package/src/skill-set/tdd/mocking.md +59 -0
- package/src/skill-set/tdd/tests.md +77 -0
- package/src/skill-set/teach/GLOSSARY-FORMAT.md +35 -0
- package/src/skill-set/teach/LEARNING-RECORD-FORMAT.md +46 -0
- package/src/skill-set/teach/LICENSE +21 -0
- package/src/skill-set/teach/MISSION-FORMAT.md +31 -0
- package/src/skill-set/teach/RESOURCES-FORMAT.md +32 -0
- package/src/skill-set/teach/SKILL.md +141 -0
- package/src/skill-set/teach/agents/openai.yaml +4 -0
- package/src/skill-set/to-questionnaire/LICENSE +21 -0
- package/src/skill-set/to-questionnaire/SKILL.md +55 -0
- package/src/skill-set/to-questionnaire/agents/openai.yaml +4 -0
- package/src/skill-set/to-spec/LICENSE +21 -0
- package/src/skill-set/to-spec/SKILL.md +76 -0
- package/src/skill-set/to-spec/agents/openai.yaml +4 -0
- package/src/skill-set/to-tickets/LICENSE +21 -0
- package/src/skill-set/to-tickets/SKILL.md +106 -0
- package/src/skill-set/to-tickets/agents/openai.yaml +4 -0
- package/src/skill-set/wait-what/LICENSE +21 -0
- package/src/skill-set/wait-what/SKILL.md +8 -0
- package/src/skill-set/wait-what/agents/openai.yaml +4 -0
- package/src/skill-set/wayfinder/LICENSE +21 -0
- package/src/skill-set/wayfinder/SKILL.md +129 -0
- package/src/skill-set/wayfinder/agents/openai.yaml +4 -0
- package/src/skill-set/writing-for-agents/LICENSE +21 -0
- package/src/skill-set/writing-for-agents/SKILL-MECHANICS.md +22 -0
- package/src/skill-set/writing-for-agents/SKILL.md +83 -0
- package/src/skill-set/writing-for-agents/agents/openai.yaml +3 -0
- package/src/skill-set.js +85 -0
- package/src/source.js +93 -54
- package/src/state.js +19 -1
- package/src/sync-library.js +7 -11
- package/src/sync-state.js +16 -3
- package/src/sync.js +36 -21
- package/src/tasks.js +75 -0
- package/src/telegram-view.js +214 -0
- package/src/telegram.js +161 -66
- package/src/turn-frames.js +69 -0
- package/src/worker.js +674 -184
- package/src/working-copy.js +1221 -0
- package/src/workspace.js +40 -32
- package/src/google/declared.js +0 -146
- package/src/google/id.js +0 -1
- package/src/instance/google.js +0 -1
- package/src/starter-set.js +0 -17
- /package/src/{starter-set/grilling → skill-set/ask-matt}/LICENSE +0 -0
- /package/src/{starter-set → skill-set}/grilling/agents/openai.yaml +0 -0
package/src/house.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { chmod, mkdir, readFile, rm } from "node:fs/promises";
|
|
3
|
+
import { connect, createServer } from "node:net";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
3
5
|
import { assertAcquiredCommandAdapter } from "./adapter.js";
|
|
6
|
+
import { readFrames, writeFrame } from "./channel.js";
|
|
4
7
|
import {
|
|
5
|
-
assistantDraftsPath,
|
|
6
8
|
assistantTextPath,
|
|
7
9
|
ADAPTER_KINDS,
|
|
8
10
|
INTERACTION_CORRELATION_MAX,
|
|
@@ -21,11 +23,14 @@ import {
|
|
|
21
23
|
KIT_SESSIONS_REPORT_PATH,
|
|
22
24
|
KIT_SETUP_ACQUIRE_PATH,
|
|
23
25
|
KIT_SETUP_REPORT_PATH,
|
|
26
|
+
KIT_SOURCES_DESIRED_PATH,
|
|
27
|
+
KIT_SOURCES_REPORT_PATH,
|
|
24
28
|
KIT_WORKSPACES_ACQUIRE_PATH,
|
|
25
29
|
LEASE_RENEWAL_MARGIN_MS,
|
|
26
30
|
MCP_PROTOCOL_VERSION,
|
|
27
31
|
WORKER_RUN_TOKEN_MARGIN_MS,
|
|
28
32
|
runOutcomePath,
|
|
33
|
+
runRequestPath,
|
|
29
34
|
secretInputPath,
|
|
30
35
|
SECRET_VALUE_MAX,
|
|
31
36
|
sessionInteractionsPath,
|
|
@@ -35,7 +40,10 @@ import { declarationReader } from "./declaration.js";
|
|
|
35
40
|
import { houseRefusal, KitRefusal, throwHouseAnswer } from "./refusal.js";
|
|
36
41
|
|
|
37
42
|
const LEASE = /^ahl_[A-Za-z0-9_-]+$/;
|
|
38
|
-
const
|
|
43
|
+
const RESIDENT = "Resident";
|
|
44
|
+
const LEASE_ACTIVE = "environment_control_lease_active";
|
|
45
|
+
const UPGRADE_REQUIRED = "kit_upgrade_required";
|
|
46
|
+
const UPDATE_DRAINING = "kit_update_draining";
|
|
39
47
|
const ITEM_OUTCOMES = ["accepted", "duplicate", "rejected"];
|
|
40
48
|
|
|
41
49
|
async function readCredential(directory) {
|
|
@@ -49,6 +57,10 @@ async function readCredential(directory) {
|
|
|
49
57
|
return stored;
|
|
50
58
|
}
|
|
51
59
|
|
|
60
|
+
function latchRefusal(answer, dependencies) {
|
|
61
|
+
if (answer?.error?.code === UPGRADE_REQUIRED) dependencies.contractRefused?.();
|
|
62
|
+
}
|
|
63
|
+
|
|
52
64
|
async function houseAnswer(house, path, init, dependencies, signal) {
|
|
53
65
|
const fetcher = dependencies.fetch ?? fetch;
|
|
54
66
|
let response;
|
|
@@ -66,6 +78,7 @@ async function houseAnswer(house, path, init, dependencies, signal) {
|
|
|
66
78
|
} catch {
|
|
67
79
|
answer = null;
|
|
68
80
|
}
|
|
81
|
+
latchRefusal(answer, dependencies);
|
|
69
82
|
throwHouseAnswer(response.status, answer, path);
|
|
70
83
|
if (answer === null) throw new KitRefusal("kit_house_unavailable", path);
|
|
71
84
|
return answer;
|
|
@@ -105,7 +118,291 @@ function heldLease(answer) {
|
|
|
105
118
|
return { lease: answer.lease, expiresAt: Date.parse(answer.expires_at) };
|
|
106
119
|
}
|
|
107
120
|
|
|
121
|
+
function residentSocket(directory) {
|
|
122
|
+
return join(directory, "run", "resident.sock");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function updateDrain(directory) {
|
|
126
|
+
return join(directory, "state", "update-drain");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function refuseDrained(directory) {
|
|
130
|
+
if (existsSync(updateDrain(directory))) throw new KitRefusal(UPDATE_DRAINING);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function residentAnswer(status, code) {
|
|
134
|
+
const body = Buffer.from(JSON.stringify({ error: { code } })).toString("base64");
|
|
135
|
+
return { status, type: "application/json", body };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function newWork(path) {
|
|
139
|
+
return path === "/kit/telegram/updates" || (path.endsWith("/acquire") && path !== "/kit/commands/acquire");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function residentRelay(session, request, signal, draining) {
|
|
143
|
+
const path = typeof request.path === "string" ? request.path : "";
|
|
144
|
+
if (!path.startsWith("/kit/") || path.startsWith("/kit/lease")) {
|
|
145
|
+
return residentAnswer(403, "kit_resident_route_refused");
|
|
146
|
+
}
|
|
147
|
+
if (draining && newWork(path)) {
|
|
148
|
+
return residentAnswer(409, UPDATE_DRAINING);
|
|
149
|
+
}
|
|
150
|
+
let authorization;
|
|
151
|
+
try {
|
|
152
|
+
authorization = await heldControl(session, signal);
|
|
153
|
+
} catch (error) {
|
|
154
|
+
return residentAnswer(error.status ?? 401, error.code ?? "kit_lease_rejected");
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
const response = await (session.dependencies.fetch ?? fetch)(`${session.house}${path}`, {
|
|
158
|
+
method: request.method,
|
|
159
|
+
headers: { ...request.headers, authorization },
|
|
160
|
+
body: request.text ?? (request.bytes === undefined ? undefined : Buffer.from(request.bytes, "base64")),
|
|
161
|
+
signal,
|
|
162
|
+
});
|
|
163
|
+
const body = Buffer.from(await response.arrayBuffer());
|
|
164
|
+
if (response.status === 409) latchRefusal(JSON.parse(body.toString("utf8")), session.dependencies);
|
|
165
|
+
return {
|
|
166
|
+
status: response.status,
|
|
167
|
+
type: response.headers.get("content-type"),
|
|
168
|
+
body: body.length === 0 ? null : body.toString("base64"),
|
|
169
|
+
};
|
|
170
|
+
} catch {
|
|
171
|
+
return residentAnswer(503, "kit_house_unavailable");
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export async function serveResident(session, directory, signal, drain, push, capture, stream = null) {
|
|
176
|
+
const socket = residentSocket(directory);
|
|
177
|
+
await mkdir(dirname(socket), { recursive: true, mode: 0o700 });
|
|
178
|
+
await rm(socket, { force: true });
|
|
179
|
+
const connections = new Set();
|
|
180
|
+
let holder = null;
|
|
181
|
+
let listening = null;
|
|
182
|
+
let draining = false;
|
|
183
|
+
function answerDrain(connection) {
|
|
184
|
+
draining = true;
|
|
185
|
+
drain(holder !== null).then(
|
|
186
|
+
(answer) => {
|
|
187
|
+
if (!connection.destroyed) writeFrame(connection, answer);
|
|
188
|
+
connection.end();
|
|
189
|
+
},
|
|
190
|
+
() => connection.destroy(),
|
|
191
|
+
);
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
function hold(connection) {
|
|
195
|
+
if (holder !== null || draining || existsSync(updateDrain(directory))) {
|
|
196
|
+
writeFrame(connection, { refused: draining || holder === null ? UPDATE_DRAINING : LEASE_ACTIVE });
|
|
197
|
+
connection.end();
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
holder = connection;
|
|
201
|
+
renewControl(session, signal).then(
|
|
202
|
+
() => {
|
|
203
|
+
writeFrame(connection, { held: true });
|
|
204
|
+
if (holder === connection) listening = connection;
|
|
205
|
+
},
|
|
206
|
+
(error) => {
|
|
207
|
+
writeFrame(connection, { refused: error.code ?? "kit_lease_rejected" });
|
|
208
|
+
connection.end();
|
|
209
|
+
},
|
|
210
|
+
);
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
const server = createServer((connection) => {
|
|
214
|
+
connections.add(connection);
|
|
215
|
+
connection.on("error", () => connection.destroy());
|
|
216
|
+
const relayed = new Map();
|
|
217
|
+
let held = null;
|
|
218
|
+
connection.on("close", () => {
|
|
219
|
+
connections.delete(connection);
|
|
220
|
+
if (holder === connection) holder = null;
|
|
221
|
+
if (listening === connection) listening = null;
|
|
222
|
+
for (const cancelled of relayed.values()) cancelled.abort();
|
|
223
|
+
});
|
|
224
|
+
readFrames(connection, {
|
|
225
|
+
onFrame: (request) => {
|
|
226
|
+
if (held === null) {
|
|
227
|
+
held = request.drain === true ? answerDrain(connection) : hold(connection);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (!held) return;
|
|
231
|
+
if (request.stream !== undefined) {
|
|
232
|
+
if (request.stream?.type === "gap") stream?.write(request.stream);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
if (request.cancel === true) {
|
|
236
|
+
relayed.get(request.id)?.abort();
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const cancelled = new AbortController();
|
|
240
|
+
relayed.set(request.id, cancelled);
|
|
241
|
+
const relaySignal = AbortSignal.any([signal, cancelled.signal]);
|
|
242
|
+
const answered =
|
|
243
|
+
request.push !== undefined
|
|
244
|
+
? push(request.push, relaySignal).then((pushed) => ({ pushed }))
|
|
245
|
+
: request.capture !== undefined
|
|
246
|
+
? capture(request.capture, relaySignal).then(
|
|
247
|
+
(captured) => ({ captured }),
|
|
248
|
+
(error) => ({ refused: error?.code ?? "kit_resident_unavailable" }),
|
|
249
|
+
)
|
|
250
|
+
: residentRelay(session, request, relaySignal, draining);
|
|
251
|
+
void answered.then((answer) => {
|
|
252
|
+
relayed.delete(request.id);
|
|
253
|
+
if (!connection.destroyed) writeFrame(connection, { id: request.id, ...answer });
|
|
254
|
+
});
|
|
255
|
+
},
|
|
256
|
+
onRefusal: () => connection.destroy(),
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
await new Promise((resolve, reject) => {
|
|
260
|
+
server.once("error", reject);
|
|
261
|
+
server.listen(socket, resolve);
|
|
262
|
+
});
|
|
263
|
+
await chmod(socket, 0o600);
|
|
264
|
+
return {
|
|
265
|
+
serving: () => holder !== null,
|
|
266
|
+
forward(frame) {
|
|
267
|
+
if (listening !== null && !listening.destroyed) writeFrame(listening, { stream: frame });
|
|
268
|
+
},
|
|
269
|
+
async close() {
|
|
270
|
+
for (const connection of connections) connection.destroy();
|
|
271
|
+
await new Promise((resolve) => server.close(resolve));
|
|
272
|
+
await rm(socket, { force: true });
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export function requestDrain(directory) {
|
|
278
|
+
const connection = connect(residentSocket(directory));
|
|
279
|
+
return new Promise((resolve, reject) => {
|
|
280
|
+
const unavailable = () => reject(new KitRefusal("kit_resident_unavailable"));
|
|
281
|
+
connection.on("error", unavailable);
|
|
282
|
+
connection.on("close", unavailable);
|
|
283
|
+
readFrames(connection, {
|
|
284
|
+
onFrame: (answer) => {
|
|
285
|
+
resolve(answer);
|
|
286
|
+
connection.end();
|
|
287
|
+
},
|
|
288
|
+
onRefusal: () => connection.destroy(),
|
|
289
|
+
});
|
|
290
|
+
writeFrame(connection, { drain: true });
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
async function residentDelegation(directory, fetcher) {
|
|
295
|
+
const connection = connect(residentSocket(directory));
|
|
296
|
+
const pending = new Map();
|
|
297
|
+
const watchers = new Set();
|
|
298
|
+
let greet;
|
|
299
|
+
const greeting = new Promise((resolve) => {
|
|
300
|
+
greet = resolve;
|
|
301
|
+
});
|
|
302
|
+
const unavailable = () => residentAnswer(503, "kit_resident_unavailable");
|
|
303
|
+
function settle(id, answer) {
|
|
304
|
+
pending.get(id)?.(answer);
|
|
305
|
+
pending.delete(id);
|
|
306
|
+
if (pending.size === 0) connection.unref();
|
|
307
|
+
}
|
|
308
|
+
connection.on("error", () => connection.destroy());
|
|
309
|
+
connection.on("close", () => {
|
|
310
|
+
greet(null);
|
|
311
|
+
for (const id of [...pending.keys()]) settle(id, unavailable());
|
|
312
|
+
});
|
|
313
|
+
readFrames(connection, {
|
|
314
|
+
onFrame: (frame) => {
|
|
315
|
+
if (frame.id !== undefined) settle(frame.id, frame);
|
|
316
|
+
else if (frame.stream !== undefined) for (const heard of watchers) heard.viewed?.(frame.stream);
|
|
317
|
+
else greet(frame);
|
|
318
|
+
},
|
|
319
|
+
onRefusal: () => connection.destroy(),
|
|
320
|
+
});
|
|
321
|
+
writeFrame(connection, { hold: true });
|
|
322
|
+
const held = await greeting;
|
|
323
|
+
if (held === null) return null;
|
|
324
|
+
if (held.held !== true) {
|
|
325
|
+
connection.destroy();
|
|
326
|
+
throw new KitRefusal(held.refused);
|
|
327
|
+
}
|
|
328
|
+
connection.unref();
|
|
329
|
+
let next = 0;
|
|
330
|
+
function exchange(frame, signal) {
|
|
331
|
+
signal?.throwIfAborted();
|
|
332
|
+
if (connection.destroyed) return Promise.resolve(unavailable());
|
|
333
|
+
return new Promise((resolve, reject) => {
|
|
334
|
+
const id = (next += 1);
|
|
335
|
+
const cancel = () => {
|
|
336
|
+
pending.delete(id);
|
|
337
|
+
settle(id);
|
|
338
|
+
if (!connection.destroyed) writeFrame(connection, { id, cancel: true });
|
|
339
|
+
reject(signal.reason);
|
|
340
|
+
};
|
|
341
|
+
signal?.addEventListener("abort", cancel, { once: true });
|
|
342
|
+
pending.set(id, (answer) => {
|
|
343
|
+
signal?.removeEventListener("abort", cancel);
|
|
344
|
+
resolve(answer);
|
|
345
|
+
});
|
|
346
|
+
connection.ref();
|
|
347
|
+
writeFrame(connection, { id, ...frame });
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
async function push(request, signal) {
|
|
351
|
+
const answer = await exchange({ push: request }, signal);
|
|
352
|
+
if (answer.pushed === undefined) throw new KitRefusal("kit_resident_unavailable");
|
|
353
|
+
return answer.pushed;
|
|
354
|
+
}
|
|
355
|
+
async function capture(source, signal) {
|
|
356
|
+
const answer = await exchange({ capture: source }, signal);
|
|
357
|
+
if (answer.refused !== undefined) throw new KitRefusal(answer.refused);
|
|
358
|
+
if (answer.captured === undefined) throw new KitRefusal("kit_resident_unavailable");
|
|
359
|
+
return answer.captured;
|
|
360
|
+
}
|
|
361
|
+
async function delegated(url, init = {}) {
|
|
362
|
+
const { authorization, ...headers } = init.headers ?? {};
|
|
363
|
+
if (authorization !== RESIDENT) return fetcher(url, init);
|
|
364
|
+
const address = new URL(url);
|
|
365
|
+
const answer = await exchange(
|
|
366
|
+
{
|
|
367
|
+
method: init.method ?? "GET",
|
|
368
|
+
path: `${address.pathname}${address.search}`,
|
|
369
|
+
headers,
|
|
370
|
+
...(typeof init.body === "string"
|
|
371
|
+
? { text: init.body }
|
|
372
|
+
: init.body === undefined
|
|
373
|
+
? {}
|
|
374
|
+
: { bytes: Buffer.from(init.body).toString("base64") }),
|
|
375
|
+
},
|
|
376
|
+
init.signal,
|
|
377
|
+
);
|
|
378
|
+
return new Response(answer.body === null ? null : Buffer.from(answer.body, "base64"), {
|
|
379
|
+
status: answer.status,
|
|
380
|
+
headers: answer.type === null ? {} : { "content-type": answer.type },
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
function relay(signal, heard) {
|
|
384
|
+
return new Promise((resolve) => {
|
|
385
|
+
const end = () => {
|
|
386
|
+
signal.removeEventListener("abort", end);
|
|
387
|
+
connection.removeListener("close", end);
|
|
388
|
+
if (!watchers.delete(heard)) return;
|
|
389
|
+
heard.closed?.();
|
|
390
|
+
resolve();
|
|
391
|
+
};
|
|
392
|
+
watchers.add(heard);
|
|
393
|
+
heard.opened?.((frame) => {
|
|
394
|
+
if (!connection.destroyed) writeFrame(connection, { stream: frame });
|
|
395
|
+
});
|
|
396
|
+
signal.addEventListener("abort", end, { once: true });
|
|
397
|
+
connection.once("close", end);
|
|
398
|
+
if (signal.aborted || connection.destroyed) end();
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
return { connection, fetch: delegated, push, capture, relay };
|
|
402
|
+
}
|
|
403
|
+
|
|
108
404
|
export async function openSession(options, dependencies = {}) {
|
|
405
|
+
if (options.resident !== true) refuseDrained(options.directory);
|
|
109
406
|
const session = {
|
|
110
407
|
house: options.house,
|
|
111
408
|
dependencies,
|
|
@@ -114,27 +411,42 @@ export async function openSession(options, dependencies = {}) {
|
|
|
114
411
|
expiresAt: 0,
|
|
115
412
|
signal: options.signal,
|
|
116
413
|
};
|
|
414
|
+
const resident =
|
|
415
|
+
options.acquire === true || options.resident === true
|
|
416
|
+
? null
|
|
417
|
+
: await residentDelegation(options.directory, dependencies.fetch ?? fetch);
|
|
418
|
+
if (resident !== null) {
|
|
419
|
+
session.resident = resident.connection;
|
|
420
|
+
session.push = resident.push;
|
|
421
|
+
session.capture = resident.capture;
|
|
422
|
+
session.relay = resident.relay;
|
|
423
|
+
session.dependencies = { ...dependencies, fetch: resident.fetch };
|
|
424
|
+
return session;
|
|
425
|
+
}
|
|
426
|
+
session.dependencies = {
|
|
427
|
+
...dependencies,
|
|
428
|
+
contractRefused: () => {
|
|
429
|
+
session.refused = true;
|
|
430
|
+
},
|
|
431
|
+
};
|
|
117
432
|
const credential = await readCredential(options.directory);
|
|
118
|
-
|
|
119
|
-
session,
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
);
|
|
433
|
+
const answer = await houseRequest(
|
|
434
|
+
session.house,
|
|
435
|
+
`Bearer ${credential}`,
|
|
436
|
+
"/kit/lease",
|
|
437
|
+
{},
|
|
438
|
+
session.dependencies,
|
|
439
|
+
session.signal,
|
|
440
|
+
).catch((error) => {
|
|
441
|
+
if (options.resident === true && session.refused) return null;
|
|
442
|
+
throw error;
|
|
443
|
+
});
|
|
444
|
+
if (answer !== null) Object.assign(session, heldLease(answer));
|
|
131
445
|
return session;
|
|
132
446
|
}
|
|
133
447
|
|
|
134
|
-
|
|
135
|
-
if (session.
|
|
136
|
-
return `Bearer ${session.lease}`;
|
|
137
|
-
}
|
|
448
|
+
async function renewControl(session, signal) {
|
|
449
|
+
if (session.refused) throw new KitRefusal(UPGRADE_REQUIRED);
|
|
138
450
|
Object.assign(
|
|
139
451
|
session,
|
|
140
452
|
heldLease(
|
|
@@ -148,10 +460,18 @@ export async function heldControl(session, signal = session.signal) {
|
|
|
148
460
|
),
|
|
149
461
|
),
|
|
150
462
|
);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
export async function heldControl(session, signal = session.signal) {
|
|
466
|
+
if (session.resident !== undefined) return RESIDENT;
|
|
467
|
+
if (session.refused) throw new KitRefusal(UPGRADE_REQUIRED);
|
|
468
|
+
if (session.expiresAt - session.now().getTime() <= LEASE_RENEWAL_MARGIN_MS) {
|
|
469
|
+
await renewControl(session, signal);
|
|
470
|
+
}
|
|
151
471
|
return `Bearer ${session.lease}`;
|
|
152
472
|
}
|
|
153
473
|
|
|
154
|
-
function controlSleep(milliseconds, signal) {
|
|
474
|
+
export function controlSleep(milliseconds, signal) {
|
|
155
475
|
return new Promise((resolve, reject) => {
|
|
156
476
|
if (signal.aborted) {
|
|
157
477
|
reject(new KitRefusal("kit_command_cancelled"));
|
|
@@ -195,9 +515,11 @@ export function mutatingCall(classification) {
|
|
|
195
515
|
}
|
|
196
516
|
|
|
197
517
|
export async function releaseControl(session, options = {}) {
|
|
518
|
+
session.resident?.end();
|
|
198
519
|
if (session.lease === null) return;
|
|
199
520
|
const held = session.lease;
|
|
200
521
|
try {
|
|
522
|
+
if (session.refused) throw new KitRefusal(UPGRADE_REQUIRED);
|
|
201
523
|
await houseRequest(
|
|
202
524
|
session.house,
|
|
203
525
|
`Bearer ${held}`,
|
|
@@ -229,6 +551,7 @@ export async function maintainControl(session, operation, signal, { release = tr
|
|
|
229
551
|
signal?.addEventListener("abort", cancelOperation, { once: true });
|
|
230
552
|
});
|
|
231
553
|
const renewal = (async () => {
|
|
554
|
+
if (session.resident !== undefined) return;
|
|
232
555
|
const sleep = session.dependencies.controlSleep ?? controlSleep;
|
|
233
556
|
while (!stopped) {
|
|
234
557
|
const delay = Math.max(
|
|
@@ -245,7 +568,7 @@ export async function maintainControl(session, operation, signal, { release = tr
|
|
|
245
568
|
throw new KitRefusal("kit_lease_rejected");
|
|
246
569
|
}
|
|
247
570
|
} catch (error) {
|
|
248
|
-
if (stopped) return;
|
|
571
|
+
if (stopped || session.refused) return;
|
|
249
572
|
rejectFailure(error);
|
|
250
573
|
maintenance.abort();
|
|
251
574
|
return;
|
|
@@ -253,20 +576,14 @@ export async function maintainControl(session, operation, signal, { release = tr
|
|
|
253
576
|
}
|
|
254
577
|
})();
|
|
255
578
|
const running = Promise.resolve().then(() => operation(operationSignal));
|
|
256
|
-
let ended = false;
|
|
257
579
|
try {
|
|
258
|
-
|
|
259
|
-
ended = true;
|
|
260
|
-
return answer;
|
|
261
|
-
} catch (error) {
|
|
262
|
-
ended = error instanceof KitRefusal && error.code === "kit_command_cancelled";
|
|
263
|
-
throw error;
|
|
580
|
+
return await Promise.race([running, failure, cancellation]);
|
|
264
581
|
} finally {
|
|
265
582
|
stopped = true;
|
|
266
583
|
maintenance.abort();
|
|
267
584
|
signal?.removeEventListener("abort", cancelOperation);
|
|
268
585
|
await Promise.all([renewal, running.catch(() => undefined)]);
|
|
269
|
-
if (
|
|
586
|
+
if (release) await releaseControl(session);
|
|
270
587
|
}
|
|
271
588
|
}
|
|
272
589
|
|
|
@@ -426,6 +743,30 @@ export async function desiredAgents(session, signal = session.signal) {
|
|
|
426
743
|
return answer.agents;
|
|
427
744
|
}
|
|
428
745
|
|
|
746
|
+
export async function desiredSources(session, signal = session.signal) {
|
|
747
|
+
const answer = await houseRequest(
|
|
748
|
+
session.house,
|
|
749
|
+
await heldControl(session, signal),
|
|
750
|
+
KIT_SOURCES_DESIRED_PATH,
|
|
751
|
+
{},
|
|
752
|
+
session.dependencies,
|
|
753
|
+
signal,
|
|
754
|
+
);
|
|
755
|
+
if (!Array.isArray(answer?.sources)) throw new KitRefusal("kit_sources_rejected");
|
|
756
|
+
return answer.sources;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
export async function reportSources(session, sources, signal = session.signal) {
|
|
760
|
+
return houseRequest(
|
|
761
|
+
session.house,
|
|
762
|
+
await heldControl(session, signal),
|
|
763
|
+
KIT_SOURCES_REPORT_PATH,
|
|
764
|
+
{ sources },
|
|
765
|
+
session.dependencies,
|
|
766
|
+
signal,
|
|
767
|
+
);
|
|
768
|
+
}
|
|
769
|
+
|
|
429
770
|
const HOUSE_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
430
771
|
|
|
431
772
|
export async function acquireSetup(session, signal = session.signal) {
|
|
@@ -479,6 +820,7 @@ export async function reportLogin(session, login, outcome, signal = session.sign
|
|
|
479
820
|
}
|
|
480
821
|
|
|
481
822
|
const envelope = declarationReader("kit_acquire_rejected");
|
|
823
|
+
const input = declarationReader("kit_run_input_unreadable");
|
|
482
824
|
|
|
483
825
|
function acquiredRoute(value, declaration) {
|
|
484
826
|
const route = envelope.plain(value, declaration);
|
|
@@ -496,79 +838,82 @@ function acquiredRoute(value, declaration) {
|
|
|
496
838
|
|
|
497
839
|
function conversationText(value, declaration) {
|
|
498
840
|
if (value === null) return null;
|
|
499
|
-
if (typeof value !== "string" || value.length > 4096)
|
|
841
|
+
if (typeof value !== "string" || value.length > 4096) input.refuse(declaration);
|
|
500
842
|
return value;
|
|
501
843
|
}
|
|
502
844
|
|
|
503
845
|
function conversationMessage(value, declaration) {
|
|
504
|
-
const message =
|
|
846
|
+
const message = input.plain(value, declaration);
|
|
505
847
|
const text = conversationText(message.text, `${declaration}.text`);
|
|
506
848
|
const caption = conversationText(message.caption, `${declaration}.caption`);
|
|
507
|
-
if (!Array.isArray(message.files))
|
|
849
|
+
if (!Array.isArray(message.files)) input.refuse(`${declaration}.files`);
|
|
508
850
|
const files = message.files.map((value, index) => {
|
|
509
851
|
const name = `${declaration}.files[${index}]`;
|
|
510
|
-
const file =
|
|
852
|
+
const file = input.plain(value, name);
|
|
511
853
|
if (file.bytes !== null && (!Number.isSafeInteger(file.bytes) || file.bytes < 0)) {
|
|
512
|
-
|
|
854
|
+
input.refuse(`${name}.bytes`);
|
|
513
855
|
}
|
|
514
856
|
return {
|
|
515
|
-
path:
|
|
516
|
-
media_kind:
|
|
517
|
-
media_type: file.media_type === null ? null :
|
|
857
|
+
path: input.text(file.path, `${name}.path`, 2048),
|
|
858
|
+
media_kind: input.text(file.media_kind, `${name}.media_kind`, 128),
|
|
859
|
+
media_type: file.media_type === null ? null : input.text(file.media_type, `${name}.media_type`, 255),
|
|
518
860
|
bytes: file.bytes,
|
|
519
861
|
};
|
|
520
862
|
});
|
|
521
|
-
if (!text && !caption && files.length === 0)
|
|
863
|
+
if (!text && !caption && files.length === 0) input.refuse(declaration);
|
|
522
864
|
return {
|
|
523
|
-
message_id:
|
|
865
|
+
message_id: input.text(message.message_id, `${declaration}.message_id`, 128),
|
|
524
866
|
text, caption, files,
|
|
525
867
|
};
|
|
526
868
|
}
|
|
527
869
|
|
|
528
|
-
function
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
const turn =
|
|
870
|
+
function runRequest(answer) {
|
|
871
|
+
const request = input.plain(answer, "request");
|
|
872
|
+
if (request.kind === "conversation") {
|
|
873
|
+
if (request.origin !== "user") input.refuse("request.origin");
|
|
874
|
+
const turn = input.plain(request.turn, "request.turn");
|
|
533
875
|
return {
|
|
876
|
+
kind: "conversation",
|
|
534
877
|
origin: "user",
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
.map((message, index) => conversationMessage(message, `${declaration}.conversation.turn.messages[${index}]`)),
|
|
541
|
-
},
|
|
878
|
+
turn: {
|
|
879
|
+
id: input.text(turn.id, "request.turn.id", 128),
|
|
880
|
+
sequence: input.positiveCount(turn.sequence, "request.turn.sequence"),
|
|
881
|
+
messages: input.filled(turn.messages, "request.turn.messages")
|
|
882
|
+
.map((message, index) => conversationMessage(message, `request.turn.messages[${index}]`)),
|
|
542
883
|
},
|
|
543
884
|
};
|
|
544
885
|
}
|
|
545
|
-
if (
|
|
546
|
-
const
|
|
547
|
-
|
|
886
|
+
if (request.kind !== "routine") input.refuse("request.kind");
|
|
887
|
+
const routine = input.plain(request.routine, "request.routine");
|
|
888
|
+
if (typeof request.payload !== "string" || Array.from(request.payload).length > 20_000) input.refuse("request.payload");
|
|
889
|
+
if (!Array.isArray(request.references)) input.refuse("request.references");
|
|
548
890
|
return {
|
|
549
|
-
|
|
891
|
+
kind: "routine",
|
|
892
|
+
origin: input.text(request.origin, "request.origin", 32),
|
|
550
893
|
routine: {
|
|
551
|
-
name:
|
|
552
|
-
version:
|
|
894
|
+
name: input.text(routine.name, "request.routine.name", 256),
|
|
895
|
+
version: input.positiveCount(routine.version, "request.routine.version"),
|
|
553
896
|
},
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
),
|
|
560
|
-
requested_at: envelope.text(
|
|
561
|
-
request.requested_at,
|
|
562
|
-
`${declaration}.request.requested_at`,
|
|
563
|
-
64,
|
|
564
|
-
),
|
|
565
|
-
deliver_at:
|
|
566
|
-
request.deliver_at === null
|
|
567
|
-
? null
|
|
568
|
-
: envelope.text(request.deliver_at, `${declaration}.request.deliver_at`, 64),
|
|
897
|
+
instruction: input.text(request.instruction, "request.instruction", 256 * 1024),
|
|
898
|
+
payload: request.payload,
|
|
899
|
+
references: request.references.map((reference, index) => input.plain(reference, `request.references[${index}]`)),
|
|
900
|
+
requested_by: input.text(request.requested_by, "request.requested_by", 128),
|
|
901
|
+
requested_at: input.text(request.requested_at, "request.requested_at", 64),
|
|
902
|
+
deliver_at: request.deliver_at === null ? null : input.text(request.deliver_at, "request.deliver_at", 64),
|
|
569
903
|
};
|
|
570
904
|
}
|
|
571
905
|
|
|
906
|
+
export async function readRunRequest(session, run, token, signal = session.signal) {
|
|
907
|
+
return runRequest(await houseRequest(
|
|
908
|
+
session.house,
|
|
909
|
+
`Bearer ${token}`,
|
|
910
|
+
runRequestPath(run),
|
|
911
|
+
{},
|
|
912
|
+
session.dependencies,
|
|
913
|
+
signal,
|
|
914
|
+
));
|
|
915
|
+
}
|
|
916
|
+
|
|
572
917
|
function acquiredAssignment(value, declaration) {
|
|
573
918
|
const acquired = envelope.plain(value, declaration);
|
|
574
919
|
const bridge = envelope.plain(acquired.session, `${declaration}.session`);
|
|
@@ -582,9 +927,13 @@ function acquiredAssignment(value, declaration) {
|
|
|
582
927
|
if (Number.isNaN(Date.parse(expiresAt))) {
|
|
583
928
|
envelope.refuse(`${declaration}.session.bridge_token_expires_at`);
|
|
584
929
|
}
|
|
930
|
+
if (typeof plan.base_instructions !== "string") {
|
|
931
|
+
envelope.refuse(`${declaration}.plan.base_instructions`);
|
|
932
|
+
}
|
|
585
933
|
return {
|
|
586
934
|
run: envelope.text(acquired.run, `${declaration}.run`, 128),
|
|
587
935
|
agentId: envelope.text(plan.agent_id, `${declaration}.plan.agent_id`, 128),
|
|
936
|
+
baseInstructions: plan.base_instructions,
|
|
588
937
|
workingDirectory: envelope.text(
|
|
589
938
|
plan.working_directory,
|
|
590
939
|
`${declaration}.plan.working_directory`,
|
|
@@ -597,7 +946,6 @@ function acquiredAssignment(value, declaration) {
|
|
|
597
946
|
token: envelope.text(bridge.bridge_token, `${declaration}.session.bridge_token`, 512),
|
|
598
947
|
expiresAt: Date.parse(expiresAt),
|
|
599
948
|
},
|
|
600
|
-
request: acquiredRequest(acquired, declaration),
|
|
601
949
|
};
|
|
602
950
|
}
|
|
603
951
|
|
|
@@ -628,14 +976,17 @@ export async function commitRun(session, run) {
|
|
|
628
976
|
return answer;
|
|
629
977
|
}
|
|
630
978
|
|
|
631
|
-
export async function reportRunOutcome(session, run, reported) {
|
|
979
|
+
export async function reportRunOutcome(session, run, reported, plan = null) {
|
|
632
980
|
const answer = await houseRequest(
|
|
633
981
|
session.house,
|
|
634
982
|
await heldControl(session),
|
|
635
983
|
runOutcomePath(run),
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
984
|
+
withPlan(
|
|
985
|
+
reported.cause === null
|
|
986
|
+
? { operation_id: reported.operation, outcome: reported.outcome }
|
|
987
|
+
: { operation_id: reported.operation, outcome: reported.outcome, cause: reported.cause },
|
|
988
|
+
plan,
|
|
989
|
+
),
|
|
639
990
|
session.dependencies,
|
|
640
991
|
session.signal,
|
|
641
992
|
);
|
|
@@ -668,27 +1019,16 @@ export async function reportSessions(session, held) {
|
|
|
668
1019
|
return sessions;
|
|
669
1020
|
}
|
|
670
1021
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
session.house,
|
|
674
|
-
await heldControl(session),
|
|
675
|
-
assistantDraftsPath(workerSession),
|
|
676
|
-
{ blocks },
|
|
677
|
-
session.dependencies,
|
|
678
|
-
session.signal,
|
|
679
|
-
);
|
|
680
|
-
if (!ASSISTANT_DRAFT_STATES.includes(answer?.state)) {
|
|
681
|
-
throw new KitRefusal("kit_assistant_draft_rejected", workerSession);
|
|
682
|
-
}
|
|
683
|
-
return { state: answer.state };
|
|
1022
|
+
function withPlan(body, plan) {
|
|
1023
|
+
return plan === null || plan === undefined ? body : { ...body, plan };
|
|
684
1024
|
}
|
|
685
1025
|
|
|
686
|
-
export async function acceptAssistantText(session, workerSession, sealed) {
|
|
1026
|
+
export async function acceptAssistantText(session, workerSession, sealed, plan = null) {
|
|
687
1027
|
const answer = await houseRequest(
|
|
688
1028
|
session.house,
|
|
689
1029
|
await heldControl(session),
|
|
690
1030
|
assistantTextPath(workerSession),
|
|
691
|
-
{ operation_id: sealed.operation, blocks: sealed.blocks },
|
|
1031
|
+
withPlan({ operation_id: sealed.operation, blocks: sealed.blocks }, plan),
|
|
692
1032
|
session.dependencies,
|
|
693
1033
|
session.signal,
|
|
694
1034
|
);
|
|
@@ -703,27 +1043,12 @@ export async function acceptAssistantText(session, workerSession, sealed) {
|
|
|
703
1043
|
return { turn: answer.turn, delivery: answer.delivery };
|
|
704
1044
|
}
|
|
705
1045
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
session.house,
|
|
709
|
-
await heldControl(session),
|
|
710
|
-
`/kit/sessions/${encodeURIComponent(workerSession)}/plans`,
|
|
711
|
-
snapshot,
|
|
712
|
-
session.dependencies,
|
|
713
|
-
session.signal,
|
|
714
|
-
);
|
|
715
|
-
if (
|
|
716
|
-
answer?.session !== workerSession ||
|
|
717
|
-
typeof answer.plan !== "string" ||
|
|
718
|
-
!["live", "frozen"].includes(answer.state)
|
|
719
|
-
) {
|
|
720
|
-
throw new KitRefusal("kit_plan_rejected", workerSession);
|
|
721
|
-
}
|
|
722
|
-
return { plan: answer.plan, state: answer.state };
|
|
1046
|
+
function named(value) {
|
|
1047
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
723
1048
|
}
|
|
724
1049
|
|
|
725
1050
|
export async function observeSessionStart(session, workerSession) {
|
|
726
|
-
|
|
1051
|
+
const answer = await houseRequest(
|
|
727
1052
|
session.house,
|
|
728
1053
|
await heldControl(session),
|
|
729
1054
|
`/kit/sessions/${encodeURIComponent(workerSession)}/started`,
|
|
@@ -731,14 +1056,19 @@ export async function observeSessionStart(session, workerSession) {
|
|
|
731
1056
|
session.dependencies,
|
|
732
1057
|
session.signal,
|
|
733
1058
|
);
|
|
1059
|
+
const conversation = named(answer?.conversation_id);
|
|
1060
|
+
const turn = named(answer?.turn_id);
|
|
1061
|
+
return conversation === null || turn === null
|
|
1062
|
+
? null
|
|
1063
|
+
: { conversation_id: conversation, turn_id: turn, attempt_id: workerSession };
|
|
734
1064
|
}
|
|
735
1065
|
|
|
736
|
-
export async function observeSessionStop(session, workerSession, turnCompleted = false) {
|
|
1066
|
+
export async function observeSessionStop(session, workerSession, turnCompleted = false, plan = null) {
|
|
737
1067
|
return houseRequest(
|
|
738
1068
|
session.house,
|
|
739
1069
|
await heldControl(session),
|
|
740
1070
|
`/kit/sessions/${encodeURIComponent(workerSession)}/stopped`,
|
|
741
|
-
turnCompleted ? { turn_completed: true } : {},
|
|
1071
|
+
withPlan(turnCompleted ? { turn_completed: true } : {}, plan),
|
|
742
1072
|
session.dependencies,
|
|
743
1073
|
session.signal,
|
|
744
1074
|
);
|
|
@@ -803,17 +1133,6 @@ function acquiredInterrupt(value, declaration) {
|
|
|
803
1133
|
};
|
|
804
1134
|
}
|
|
805
1135
|
|
|
806
|
-
export async function reportSessionHeartbeat(session, workerSession) {
|
|
807
|
-
await houseRequest(
|
|
808
|
-
session.house,
|
|
809
|
-
await heldControl(session),
|
|
810
|
-
`/kit/sessions/${encodeURIComponent(workerSession)}/heartbeat`,
|
|
811
|
-
{},
|
|
812
|
-
session.dependencies,
|
|
813
|
-
session.signal,
|
|
814
|
-
);
|
|
815
|
-
}
|
|
816
|
-
|
|
817
1136
|
function answeredValue(value, declaration) {
|
|
818
1137
|
if (typeof value === "boolean") return value;
|
|
819
1138
|
if (typeof value === "number") {
|
|
@@ -983,12 +1302,12 @@ export async function reportInteractionOutcome(session, interaction, outcome) {
|
|
|
983
1302
|
return { disposition: answer.disposition ?? null };
|
|
984
1303
|
}
|
|
985
1304
|
|
|
986
|
-
export async function reportInterruptOutcome(session, interrupt, outcome) {
|
|
1305
|
+
export async function reportInterruptOutcome(session, interrupt, outcome, plan = null) {
|
|
987
1306
|
const answer = await houseRequest(
|
|
988
1307
|
session.house,
|
|
989
1308
|
await heldControl(session),
|
|
990
1309
|
`/kit/interrupts/${encodeURIComponent(interrupt)}/outcome`,
|
|
991
|
-
{ outcome },
|
|
1310
|
+
withPlan({ outcome }, outcome === "interrupted" ? plan : null),
|
|
992
1311
|
session.dependencies,
|
|
993
1312
|
session.signal,
|
|
994
1313
|
);
|