@centralu/host-linux-arm64 0.1.0-beta.19 → 0.1.0-beta.20
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/host/bundle-info.json +2 -2
- package/host/main.mjs +472 -85
- package/package.json +1 -1
package/host/bundle-info.json
CHANGED
package/host/main.mjs
CHANGED
|
@@ -42965,7 +42965,7 @@ import { mkdirSync as mkdirSync11, writeSync as writeSync4 } from "node:fs";
|
|
|
42965
42965
|
|
|
42966
42966
|
// packages/protocol/src/brand.ts
|
|
42967
42967
|
var APP_NAME = "Centralu";
|
|
42968
|
-
var APP_VERSION = "0.1.0-beta.
|
|
42968
|
+
var APP_VERSION = "0.1.0-beta.20";
|
|
42969
42969
|
var APP_SLUG = "centralu";
|
|
42970
42970
|
var CLIENT_INFO = { name: APP_SLUG, title: APP_NAME, version: APP_VERSION };
|
|
42971
42971
|
var DATA_DIR = ".centralu";
|
|
@@ -58249,6 +58249,8 @@ var AdapterCapabilities = external_exports.object({
|
|
|
58249
58249
|
* The idle check (#297) needs that difference — a session whose background work is invisible is never called idle.
|
|
58250
58250
|
*/
|
|
58251
58251
|
backgroundTasks: external_exports.boolean().default(false),
|
|
58252
|
+
/** Whether send accepts input during a running turn without interrupting it (#592). Absent is unsupported. */
|
|
58253
|
+
midTurnInput: external_exports.boolean().optional(),
|
|
58252
58254
|
/**
|
|
58253
58255
|
* Whether this tool can defer individual Centralu tool definitions until the model asks for
|
|
58254
58256
|
* them. This is a serving property, not a list of tool names: the host uses it when it counts
|
|
@@ -59837,6 +59839,14 @@ var MessageImage = external_exports.discriminatedUnion("ok", [
|
|
|
59837
59839
|
external_exports.object({ ok: external_exports.literal(true), mime: external_exports.string(), data: external_exports.string(), file: external_exports.string().optional() }),
|
|
59838
59840
|
external_exports.object({ ok: external_exports.literal(false), reason: MessageImageRefusal, message: external_exports.string(), file: external_exports.string().optional() })
|
|
59839
59841
|
]);
|
|
59842
|
+
var AppHomeViewResult = external_exports.object({
|
|
59843
|
+
instanceId: external_exports.string(),
|
|
59844
|
+
tool: external_exports.string(),
|
|
59845
|
+
resourceUri: external_exports.string(),
|
|
59846
|
+
toolInput: external_exports.record(external_exports.string(), external_exports.unknown()),
|
|
59847
|
+
toolResult: external_exports.looseObject({ content: external_exports.array(external_exports.record(external_exports.string(), external_exports.unknown())) }),
|
|
59848
|
+
runId: external_exports.string()
|
|
59849
|
+
});
|
|
59840
59850
|
var RpcMethods = {
|
|
59841
59851
|
"agents.createSession": { params: CreateSessionParams, result: SessionInfo },
|
|
59842
59852
|
"agents.send": {
|
|
@@ -60433,7 +60443,8 @@ var RpcMethods = {
|
|
|
60433
60443
|
* tool-result).
|
|
60434
60444
|
*
|
|
60435
60445
|
* If there is no `home`, or that tool did not declare a screen, or the app cannot be reached
|
|
60436
|
-
* (not trusted, or failed to start), this fails with a reason and leaves no
|
|
60446
|
+
* (not trusted, or failed to start), this fails with a reason and leaves no live screen behind. A
|
|
60447
|
+
* keyed open keeps only a failure reservation during the lease grace period, without holding the app. If
|
|
60437
60448
|
* the app itself answered with a failure, this still opens — drawing a failure is also the
|
|
60438
60449
|
* screen's job.
|
|
60439
60450
|
*
|
|
@@ -60442,21 +60453,16 @@ var RpcMethods = {
|
|
|
60442
60453
|
* machine. Absent: this host's own app, as before.
|
|
60443
60454
|
*/
|
|
60444
60455
|
"apps.openView": {
|
|
60456
|
+
// openKey: one client-chosen UUID per logical open. Repeats reuse the instance and home outcome,
|
|
60457
|
+
// including after a planned host handover, so a lost answer never runs the home tool twice.
|
|
60445
60458
|
/*
|
|
60446
60459
|
* `lease` (#392, additive): the view belongs to this window's connection. Once the connection ends, the host waits
|
|
60447
60460
|
* a few minutes for a window to claim it (`apps.holdViews`) and then closes it, so a reloaded or closed window does
|
|
60448
60461
|
* not hold its apps open for good. Without it (an older window, or a linked machine's view) the view lives until
|
|
60449
60462
|
* `apps.closeView`.
|
|
60450
60463
|
*/
|
|
60451
|
-
params: external_exports.object({ appId: AppId, projectId: ProjectId.nullable(), ...machineParam, lease: external_exports.boolean().optional() }),
|
|
60452
|
-
result:
|
|
60453
|
-
instanceId: external_exports.string(),
|
|
60454
|
-
tool: external_exports.string(),
|
|
60455
|
-
resourceUri: external_exports.string(),
|
|
60456
|
-
toolInput: external_exports.record(external_exports.string(), external_exports.unknown()),
|
|
60457
|
-
toolResult: external_exports.looseObject({ content: external_exports.array(external_exports.record(external_exports.string(), external_exports.unknown())) }),
|
|
60458
|
-
runId: external_exports.string()
|
|
60459
|
-
})
|
|
60464
|
+
params: external_exports.object({ appId: AppId, projectId: ProjectId.nullable(), ...machineParam, lease: external_exports.boolean().optional(), openKey: external_exports.string().uuid().optional() }),
|
|
60465
|
+
result: AppHomeViewResult
|
|
60460
60466
|
},
|
|
60461
60467
|
/**
|
|
60462
60468
|
* Closes the pinned screen (M4 B-2). Releases the app the instance was holding on to — an app
|
|
@@ -60467,6 +60473,11 @@ var RpcMethods = {
|
|
|
60467
60473
|
params: external_exports.object({ instanceId: external_exports.string() }),
|
|
60468
60474
|
result: external_exports.object({ ok: external_exports.literal(true) })
|
|
60469
60475
|
},
|
|
60476
|
+
/** Reclaims a keyed home open after losing its answer; never calls the home tool. */
|
|
60477
|
+
"apps.recoverView": {
|
|
60478
|
+
params: external_exports.object({ appId: AppId, projectId: ProjectId.nullable(), ...machineParam, lease: external_exports.boolean().optional(), openKey: external_exports.string().uuid() }),
|
|
60479
|
+
result: AppHomeViewResult
|
|
60480
|
+
},
|
|
60470
60481
|
/**
|
|
60471
60482
|
* A window claims the pinned screens it shows (#392), after every reconnect: each leased instance
|
|
60472
60483
|
* named becomes this connection's again and stops waiting to be closed. `missing` lists the ids
|
|
@@ -63175,6 +63186,7 @@ var ViewHost = class _ViewHost {
|
|
|
63175
63186
|
}
|
|
63176
63187
|
opts;
|
|
63177
63188
|
instances = /* @__PURE__ */ new Map();
|
|
63189
|
+
openingHomes = /* @__PURE__ */ new Map();
|
|
63178
63190
|
origins = /* @__PURE__ */ new Map();
|
|
63179
63191
|
/** Origin servers being closed, by key: one started again for the same key waits for its port to be free */
|
|
63180
63192
|
closingOrigins = /* @__PURE__ */ new Map();
|
|
@@ -63212,6 +63224,42 @@ var ViewHost = class _ViewHost {
|
|
|
63212
63224
|
this.instances.set(id2, { id: id2, app: ref, uri, doc: null, release, lease });
|
|
63213
63225
|
return { instanceId: id2 };
|
|
63214
63226
|
}
|
|
63227
|
+
/** A logical home open may run its tool once, even when its answer is lost in a swap. */
|
|
63228
|
+
homeOpen(app2, key, holder) {
|
|
63229
|
+
const inst = [...this.instances.values()].find((i) => i.home?.key === key);
|
|
63230
|
+
if (!inst) return null;
|
|
63231
|
+
if (!sameApp(inst.app, app2)) fail("This open key belongs to another app");
|
|
63232
|
+
if (inst.home.outcome) {
|
|
63233
|
+
if (holder !== void 0) this.hold([inst.id], holder);
|
|
63234
|
+
return Promise.resolve(inst.home.outcome);
|
|
63235
|
+
}
|
|
63236
|
+
if (inst.home.error) return Promise.reject(new Error(inst.home.error));
|
|
63237
|
+
const opening = this.openingHomes.get(inst.id);
|
|
63238
|
+
if (opening) {
|
|
63239
|
+
if (holder !== void 0) this.hold([inst.id], holder);
|
|
63240
|
+
return opening;
|
|
63241
|
+
}
|
|
63242
|
+
return Promise.reject(new Error(
|
|
63243
|
+
"Host restarted while the home tool was running \u2014 the app may or may not have done this; check before opening again"
|
|
63244
|
+
));
|
|
63245
|
+
}
|
|
63246
|
+
rememberHome(instanceId, key, work) {
|
|
63247
|
+
const inst = this.instances.get(instanceId);
|
|
63248
|
+
inst.home = { key };
|
|
63249
|
+
const opening = work().then((outcome) => {
|
|
63250
|
+
inst.home.outcome = outcome;
|
|
63251
|
+
return outcome;
|
|
63252
|
+
}, (e) => {
|
|
63253
|
+
inst.home.error = e instanceof Error ? e.message : String(e);
|
|
63254
|
+
inst.release?.();
|
|
63255
|
+
inst.release = null;
|
|
63256
|
+
inst.lease = { holder: null, orphanSince: Date.now() };
|
|
63257
|
+
this.scheduleSweep();
|
|
63258
|
+
throw e;
|
|
63259
|
+
}).finally(() => this.openingHomes.delete(instanceId));
|
|
63260
|
+
this.openingHomes.set(instanceId, opening);
|
|
63261
|
+
return opening;
|
|
63262
|
+
}
|
|
63215
63263
|
close(instanceId) {
|
|
63216
63264
|
this.drop(instanceId);
|
|
63217
63265
|
}
|
|
@@ -63265,7 +63313,7 @@ var ViewHost = class _ViewHost {
|
|
|
63265
63313
|
}
|
|
63266
63314
|
/** Every open instance, oldest first — what a planned hand-over records (view-handover.ts) */
|
|
63267
63315
|
list() {
|
|
63268
|
-
return [...this.instances.values()].map((i) => ({ id: i.id, app: { ...i.app }, uri: i.uri, ...i.lease ? { leased: true } : {} }));
|
|
63316
|
+
return [...this.instances.values()].map((i) => ({ id: i.id, app: { ...i.app }, uri: i.uri, ...i.lease ? { leased: true } : {}, ...i.home ? { home: i.home } : {} }));
|
|
63269
63317
|
}
|
|
63270
63318
|
/**
|
|
63271
63319
|
* Opens again, **under their old ids**, the instances a previous host handed over (#280: a build
|
|
@@ -63292,8 +63340,22 @@ var ViewHost = class _ViewHost {
|
|
|
63292
63340
|
this.log(`[agent-host] view ${v.uri} (${_ViewHost.originKey(ref)}) not restored: ${err.message}`);
|
|
63293
63341
|
continue;
|
|
63294
63342
|
}
|
|
63295
|
-
|
|
63296
|
-
|
|
63343
|
+
let lease = v.leased === true ? { holder: null, orphanSince: now } : null;
|
|
63344
|
+
let home;
|
|
63345
|
+
if (v.home && typeof v.home.key === "string" && RpcMethods["apps.openView"].params.shape.openKey.safeParse(v.home.key).success) {
|
|
63346
|
+
const outcome = RpcMethods["apps.openView"].result.safeParse(v.home.outcome);
|
|
63347
|
+
home = {
|
|
63348
|
+
key: v.home.key,
|
|
63349
|
+
...outcome.success && outcome.data.instanceId === v.id ? { outcome: outcome.data } : {},
|
|
63350
|
+
...typeof v.home.error === "string" ? { error: v.home.error } : {}
|
|
63351
|
+
};
|
|
63352
|
+
}
|
|
63353
|
+
if (home && !home.outcome) {
|
|
63354
|
+
release?.();
|
|
63355
|
+
release = null;
|
|
63356
|
+
lease = { holder: null, orphanSince: now };
|
|
63357
|
+
}
|
|
63358
|
+
this.instances.set(v.id, { id: v.id, app: ref, uri: v.uri, doc: null, release, lease, ...home ? { home } : {} });
|
|
63297
63359
|
opened.push(v.id);
|
|
63298
63360
|
}
|
|
63299
63361
|
if (opened.some((id2) => this.instances.get(id2)?.lease)) this.scheduleSweep();
|
|
@@ -98104,7 +98166,7 @@ var OriginPorts = class {
|
|
|
98104
98166
|
};
|
|
98105
98167
|
|
|
98106
98168
|
// packages/agent-host/src/sessions/manager.ts
|
|
98107
|
-
import { randomUUID as
|
|
98169
|
+
import { randomUUID as randomUUID9 } from "node:crypto";
|
|
98108
98170
|
|
|
98109
98171
|
// packages/agent-host/src/sessions/tool-cost.ts
|
|
98110
98172
|
import { createHash as createHash8 } from "node:crypto";
|
|
@@ -100183,10 +100245,11 @@ function finalAnswer(messages2) {
|
|
|
100183
100245
|
}
|
|
100184
100246
|
var AgentRunWait = class {
|
|
100185
100247
|
constructor(notify, sessionName, onUsage = () => {
|
|
100186
|
-
}) {
|
|
100248
|
+
}, readAnswer2) {
|
|
100187
100249
|
this.notify = notify;
|
|
100188
100250
|
this.sessionName = sessionName;
|
|
100189
100251
|
this.onUsage = onUsage;
|
|
100252
|
+
this.readAnswer = readAnswer2;
|
|
100190
100253
|
this.done = new Promise((resolve11, reject) => this.settle = { resolve: resolve11, reject });
|
|
100191
100254
|
this.done.catch(() => {
|
|
100192
100255
|
});
|
|
@@ -100194,6 +100257,7 @@ var AgentRunWait = class {
|
|
|
100194
100257
|
notify;
|
|
100195
100258
|
sessionName;
|
|
100196
100259
|
onUsage;
|
|
100260
|
+
readAnswer;
|
|
100197
100261
|
/** The person stopped that session's turn (`SessionManager.interrupt`) */
|
|
100198
100262
|
stoppedByPerson = false;
|
|
100199
100263
|
/** The person deleted that session — there is no session left to conclude */
|
|
@@ -100201,17 +100265,46 @@ var AgentRunWait = class {
|
|
|
100201
100265
|
done;
|
|
100202
100266
|
settle;
|
|
100203
100267
|
settled = false;
|
|
100268
|
+
inputs = 0;
|
|
100269
|
+
trackedInputs = false;
|
|
100270
|
+
ending;
|
|
100271
|
+
get waitingForInputs() {
|
|
100272
|
+
return this.inputs > 0;
|
|
100273
|
+
}
|
|
100274
|
+
get finished() {
|
|
100275
|
+
return this.settled;
|
|
100276
|
+
}
|
|
100277
|
+
get tracksInputs() {
|
|
100278
|
+
return this.trackedInputs;
|
|
100279
|
+
}
|
|
100280
|
+
get completedAnswer() {
|
|
100281
|
+
return this.ending?.answer;
|
|
100282
|
+
}
|
|
100283
|
+
/** A parent's note may join this turn or run after it. Hold before send, since adapters can emit synchronously. */
|
|
100284
|
+
holdInput() {
|
|
100285
|
+
this.trackedInputs = true;
|
|
100286
|
+
this.inputs++;
|
|
100287
|
+
let released = false;
|
|
100288
|
+
return () => {
|
|
100289
|
+
if (released) return;
|
|
100290
|
+
released = true;
|
|
100291
|
+
this.inputs--;
|
|
100292
|
+
if (!this.inputs && this.ending) this.finish(this.ending);
|
|
100293
|
+
};
|
|
100294
|
+
}
|
|
100204
100295
|
onEvent(e) {
|
|
100205
100296
|
if (e.type === "usage_update") {
|
|
100206
100297
|
const t = e.tokens;
|
|
100207
100298
|
return this.onUsage({ input: t.inputTokens + t.cacheReadTokens + t.cacheCreationTokens, output: t.outputTokens });
|
|
100208
100299
|
}
|
|
100209
100300
|
if (this.settled) return;
|
|
100301
|
+
if (e.type === "message_delta" || e.type === "state_change" && e.state === "working") this.ending = void 0;
|
|
100210
100302
|
switch (e.type) {
|
|
100211
100303
|
case "turn_complete":
|
|
100212
100304
|
if (this.stoppedByPerson) return this.fail(new Error("the person stopped the agent before it finished"));
|
|
100213
|
-
this.
|
|
100214
|
-
|
|
100305
|
+
this.ending = { ...e.output === void 0 ? {} : { output: e.output }, ...this.readAnswer ? { answer: this.readAnswer() } : {} };
|
|
100306
|
+
if (this.inputs) return;
|
|
100307
|
+
return this.finish(this.ending);
|
|
100215
100308
|
case "error":
|
|
100216
100309
|
return this.fail(new Error(this.stoppedByPerson ? "the person stopped the agent before it finished" : `the agent's turn failed: ${e.error.message}`));
|
|
100217
100310
|
case "limit_reached":
|
|
@@ -100225,7 +100318,12 @@ var AgentRunWait = class {
|
|
|
100225
100318
|
fail(err) {
|
|
100226
100319
|
if (this.settled) return;
|
|
100227
100320
|
this.settled = true;
|
|
100228
|
-
this.settle.reject(err);
|
|
100321
|
+
this.settle.reject(this.stoppedByPerson ? new Error("the person stopped the agent before it finished") : err);
|
|
100322
|
+
}
|
|
100323
|
+
finish(ending) {
|
|
100324
|
+
if (this.settled) return;
|
|
100325
|
+
this.settled = true;
|
|
100326
|
+
this.settle.resolve(ending);
|
|
100229
100327
|
}
|
|
100230
100328
|
};
|
|
100231
100329
|
|
|
@@ -100457,6 +100555,11 @@ function agentFrame(parentName, task) {
|
|
|
100457
100555
|
task
|
|
100458
100556
|
].join("\n");
|
|
100459
100557
|
}
|
|
100558
|
+
function agentNoteFrame(parentName, text2) {
|
|
100559
|
+
return `[A note from the parent session "${parentName}" through Centralu's agent_session. These are the parent's words about your ongoing work.]
|
|
100560
|
+
|
|
100561
|
+
${text2}`;
|
|
100562
|
+
}
|
|
100460
100563
|
function agentBranch(task, id2, max = 40) {
|
|
100461
100564
|
const slug = task.toLowerCase().normalize("NFKD").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, max).replace(/-+$/, "");
|
|
100462
100565
|
return `${APP_SLUG}/${slug || "agent"}-${id2.replace(/[^a-z0-9]/gi, "").slice(0, 4).toLowerCase()}`;
|
|
@@ -100739,6 +100842,72 @@ import { exec as exec6 } from "node:child_process";
|
|
|
100739
100842
|
// packages/agent-host/src/adapters/claude/index.ts
|
|
100740
100843
|
init_sdk();
|
|
100741
100844
|
import { homedir as homedir5 } from "node:os";
|
|
100845
|
+
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
100846
|
+
|
|
100847
|
+
// packages/agent-host/src/adapters/input-receipts.ts
|
|
100848
|
+
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
100849
|
+
var InputReceipts = class {
|
|
100850
|
+
pending = /* @__PURE__ */ new Map();
|
|
100851
|
+
ended = /* @__PURE__ */ new Set();
|
|
100852
|
+
create(observer) {
|
|
100853
|
+
const id2 = randomUUID7();
|
|
100854
|
+
let resolve11;
|
|
100855
|
+
let reject;
|
|
100856
|
+
const done = new Promise((yes, no2) => {
|
|
100857
|
+
resolve11 = yes;
|
|
100858
|
+
reject = no2;
|
|
100859
|
+
});
|
|
100860
|
+
done.catch(() => {
|
|
100861
|
+
});
|
|
100862
|
+
const receipt = { id: id2, done, cancelled: false, completed: false };
|
|
100863
|
+
this.pending.set(id2, { receipt, resolve: resolve11, reject, observer });
|
|
100864
|
+
return receipt;
|
|
100865
|
+
}
|
|
100866
|
+
get size() {
|
|
100867
|
+
return this.pending.size;
|
|
100868
|
+
}
|
|
100869
|
+
get ids() {
|
|
100870
|
+
return [...this.pending.keys()];
|
|
100871
|
+
}
|
|
100872
|
+
/** Codex's turn/start response can arrive after that turn's completion notification. */
|
|
100873
|
+
bind(receipt, turn) {
|
|
100874
|
+
const p = this.pending.get(receipt.id);
|
|
100875
|
+
if (!p) return;
|
|
100876
|
+
p.turn = turn;
|
|
100877
|
+
if (this.ended.has(turn)) this.finish(receipt.id);
|
|
100878
|
+
}
|
|
100879
|
+
endTurn(turn) {
|
|
100880
|
+
if (this.pending.size) this.ended.add(turn);
|
|
100881
|
+
for (const [id2, p] of this.pending) if (p.turn === turn) this.finish(id2);
|
|
100882
|
+
}
|
|
100883
|
+
/** Retries can reorder inputs; only UUIDs actually reported as consumed have completed. */
|
|
100884
|
+
complete(ids) {
|
|
100885
|
+
for (const id2 of ids) this.finish(id2);
|
|
100886
|
+
}
|
|
100887
|
+
failAll(error51) {
|
|
100888
|
+
const turns = [...new Set([...this.pending.values()].flatMap((p) => p.turn ? [p.turn] : []))];
|
|
100889
|
+
const pending = [...this.pending.values()];
|
|
100890
|
+
this.pending.clear();
|
|
100891
|
+
this.ended.clear();
|
|
100892
|
+
for (const p of pending) {
|
|
100893
|
+
p.receipt.cancelled = true;
|
|
100894
|
+
p.observer?.fail(error51);
|
|
100895
|
+
p.reject(error51);
|
|
100896
|
+
}
|
|
100897
|
+
return turns;
|
|
100898
|
+
}
|
|
100899
|
+
finish(id2) {
|
|
100900
|
+
const p = this.pending.get(id2);
|
|
100901
|
+
if (!p) return;
|
|
100902
|
+
this.pending.delete(id2);
|
|
100903
|
+
if (!this.pending.size) this.ended.clear();
|
|
100904
|
+
p.receipt.completed = true;
|
|
100905
|
+
p.observer?.complete();
|
|
100906
|
+
p.resolve();
|
|
100907
|
+
}
|
|
100908
|
+
};
|
|
100909
|
+
|
|
100910
|
+
// packages/agent-host/src/adapters/claude/index.ts
|
|
100742
100911
|
import { execFile as execFile4 } from "node:child_process";
|
|
100743
100912
|
import { promisify as promisify4 } from "node:util";
|
|
100744
100913
|
|
|
@@ -102543,6 +102712,12 @@ var ClaudeSession = class {
|
|
|
102543
102712
|
launch;
|
|
102544
102713
|
externalId = null;
|
|
102545
102714
|
queue = [];
|
|
102715
|
+
inputReceipts = new InputReceipts();
|
|
102716
|
+
/** Person messages already handed to the SDK, so an interrupt receipt can report cancellations. */
|
|
102717
|
+
personInputs = /* @__PURE__ */ new Set();
|
|
102718
|
+
trackedInputKnown = false;
|
|
102719
|
+
/** Installed only after the initial CLI metadata confirms the protocol we have verified. */
|
|
102720
|
+
sendTracked;
|
|
102546
102721
|
/** The CLI version this process last reported (#297) */
|
|
102547
102722
|
reportedVersion = null;
|
|
102548
102723
|
notify = null;
|
|
@@ -102635,11 +102810,13 @@ var ClaudeSession = class {
|
|
|
102635
102810
|
const next = self2.queue.shift();
|
|
102636
102811
|
if (next !== void 0) {
|
|
102637
102812
|
self2.inTurn.push(next);
|
|
102813
|
+
if (!next.parentNote) self2.personInputs.add(next.uuid);
|
|
102638
102814
|
yield {
|
|
102639
102815
|
type: "user",
|
|
102640
102816
|
parent_tool_use_id: null,
|
|
102641
102817
|
session_id: self2.externalId ?? "",
|
|
102642
|
-
|
|
102818
|
+
uuid: next.uuid,
|
|
102819
|
+
message: { role: "user", content: [{ type: "text", text: next.text }] }
|
|
102643
102820
|
};
|
|
102644
102821
|
continue;
|
|
102645
102822
|
}
|
|
@@ -102800,7 +102977,23 @@ var ClaudeSession = class {
|
|
|
102800
102977
|
for await (const msg of q2) {
|
|
102801
102978
|
if (this.closed) break;
|
|
102802
102979
|
const m = msg;
|
|
102980
|
+
if (!this.turnOpen && m.user_message_uuid && !m.parent_tool_use_id && (m.type === "assistant" || m.type === "stream_event")) {
|
|
102981
|
+
this.turnOpen = true;
|
|
102982
|
+
if (!this.pending.size && !this.questions.size) this.emit({ type: "state_change", sessionId: this.sessionId, state: "working" });
|
|
102983
|
+
}
|
|
102803
102984
|
if (m.type === "system" && m.subtype === "init" && m.session_id) this.externalId = m.session_id;
|
|
102985
|
+
if (m.type === "system" && m.subtype === "init" && !this.trackedInputKnown) {
|
|
102986
|
+
this.trackedInputKnown = true;
|
|
102987
|
+
const version2 = typeof m.claude_code_version === "string" ? /^(\d+)\.(\d+)\.(\d+)$/.exec(m.claude_code_version) : null;
|
|
102988
|
+
const supported = version2 && (Number(version2[1]) > 2 || Number(version2[1]) === 2 && (Number(version2[2]) > 1 || Number(version2[2]) === 1 && Number(version2[3]) >= 289));
|
|
102989
|
+
if (supported && Array.isArray(m.capabilities) && m.capabilities.includes("interrupt_receipt_v1") && m.capabilities.includes("interrupt_cancel_queued_v1")) {
|
|
102990
|
+
this.sendTracked = (text2, observer) => {
|
|
102991
|
+
const receipt = this.inputReceipts.create(observer);
|
|
102992
|
+
this.sendInput(text2, receipt.id, receipt);
|
|
102993
|
+
return receipt.done;
|
|
102994
|
+
};
|
|
102995
|
+
}
|
|
102996
|
+
}
|
|
102804
102997
|
if (m.type === "system" && m.subtype === "init" && typeof m.claude_code_version === "string" && m.claude_code_version !== this.reportedVersion) {
|
|
102805
102998
|
this.reportedVersion = m.claude_code_version;
|
|
102806
102999
|
this.emit({ type: "agent_version", sessionId: this.sessionId, version: m.claude_code_version });
|
|
@@ -102814,8 +103007,23 @@ var ClaudeSession = class {
|
|
|
102814
103007
|
if (m.type === "result") this.turnOpen = false;
|
|
102815
103008
|
let events2 = this.stream.push(msg);
|
|
102816
103009
|
const retrying = m.type === "result" && this.retryAfterRefreshRace(msg);
|
|
103010
|
+
if (m.type === "result" && !retrying) {
|
|
103011
|
+
const result = msg;
|
|
103012
|
+
for (const id2 of result.user_message_uuids ?? (result.user_message_uuid ? [result.user_message_uuid] : [])) this.personInputs.delete(id2);
|
|
103013
|
+
}
|
|
103014
|
+
let completedInputs;
|
|
103015
|
+
if (m.type === "result" && !retrying && this.inputReceipts.size && events2.some((e) => e.type === "turn_complete" || e.type === "error")) {
|
|
103016
|
+
const result = msg;
|
|
103017
|
+
const ids = result.user_message_uuids ?? [];
|
|
103018
|
+
const failure3 = events2.find((e) => e.type === "error");
|
|
103019
|
+
if (ids.length && failure3?.type === "error") this.cancelParentNotes(new Error(failure3.error.message));
|
|
103020
|
+
else if (ids.length) completedInputs = ids;
|
|
103021
|
+
}
|
|
102817
103022
|
if (retrying) events2 = events2.filter((e) => e.type !== "error");
|
|
102818
|
-
for (const e of events2)
|
|
103023
|
+
for (const e of events2) {
|
|
103024
|
+
this.emit(e);
|
|
103025
|
+
if (e.type === "turn_complete" && completedInputs) this.inputReceipts.complete(completedInputs);
|
|
103026
|
+
}
|
|
102819
103027
|
this.armScheduleLook();
|
|
102820
103028
|
if (m.type === "result" && !retrying) void this.reportContext(q2);
|
|
102821
103029
|
}
|
|
@@ -102984,14 +103192,18 @@ var ClaudeSession = class {
|
|
|
102984
103192
|
}
|
|
102985
103193
|
}
|
|
102986
103194
|
send(text2) {
|
|
103195
|
+
this.sendInput(text2);
|
|
103196
|
+
}
|
|
103197
|
+
sendInput(text2, uuid3 = randomUUID8(), note) {
|
|
102987
103198
|
const goal = isGoalCommand(text2);
|
|
102988
103199
|
if (!this.sendGate && !(goal && this.goalCommand === void 0)) {
|
|
102989
|
-
this.deliver(text2);
|
|
103200
|
+
this.deliver(text2, uuid3, !!note);
|
|
102990
103201
|
return;
|
|
102991
103202
|
}
|
|
102992
103203
|
const gate = this.sendGate = (this.sendGate ?? this.goalKnown).then(() => {
|
|
103204
|
+
if (note?.cancelled) return;
|
|
102993
103205
|
if (!this.closed) {
|
|
102994
|
-
this.deliver(text2);
|
|
103206
|
+
this.deliver(text2, uuid3, !!note);
|
|
102995
103207
|
return;
|
|
102996
103208
|
}
|
|
102997
103209
|
this.emit({
|
|
@@ -103008,7 +103220,7 @@ var ClaudeSession = class {
|
|
|
103008
103220
|
if (this.sendGate === gate) this.sendGate = null;
|
|
103009
103221
|
});
|
|
103010
103222
|
}
|
|
103011
|
-
deliver(text2) {
|
|
103223
|
+
deliver(text2, uuid3, parentNote) {
|
|
103012
103224
|
if (isGoalCommand(text2)) {
|
|
103013
103225
|
if (this.goalCommand !== true) {
|
|
103014
103226
|
this.emit({
|
|
@@ -103023,11 +103235,12 @@ var ClaudeSession = class {
|
|
|
103023
103235
|
}
|
|
103024
103236
|
this.stream.goal.commandSent();
|
|
103025
103237
|
}
|
|
103026
|
-
this.
|
|
103238
|
+
const starting = !this.turnOpen;
|
|
103239
|
+
this.queue.push({ text: text2, uuid: uuid3, parentNote });
|
|
103027
103240
|
this.turnOpen = true;
|
|
103028
103241
|
this.notify?.();
|
|
103029
103242
|
this.notify = null;
|
|
103030
|
-
this.emit({ type: "state_change", sessionId: this.sessionId, state: "working" });
|
|
103243
|
+
if (starting) this.emit({ type: "state_change", sessionId: this.sessionId, state: "working" });
|
|
103031
103244
|
}
|
|
103032
103245
|
respondApproval(requestId, decision, scope, matcher) {
|
|
103033
103246
|
const p = this.pending.get(requestId);
|
|
@@ -103166,7 +103379,11 @@ var ClaudeSession = class {
|
|
|
103166
103379
|
* 2) SDK interrupt: this actually cuts the turn. We can call this method because we pass the
|
|
103167
103380
|
* prompt as an async generator, in streaming input mode.
|
|
103168
103381
|
*/
|
|
103169
|
-
interrupt() {
|
|
103382
|
+
interrupt(cancelQueued = this.inputReceipts.size > 0, reason = new Error("The parent note was stopped before it finished")) {
|
|
103383
|
+
this.inputReceipts.failAll(reason);
|
|
103384
|
+
const held3 = (cancelQueued ? this.queue.filter((input) => !input.parentNote).length : 0) + (this.retry?.texts.filter((input) => !input.parentNote).length ?? 0);
|
|
103385
|
+
if (cancelQueued) this.queue.length = 0;
|
|
103386
|
+
this.reportUndelivered(held3);
|
|
103170
103387
|
this.opts.apps?.cancelAll();
|
|
103171
103388
|
this.cancelRetry();
|
|
103172
103389
|
for (const [id2, p] of this.pending) {
|
|
@@ -103176,7 +103393,13 @@ var ClaudeSession = class {
|
|
|
103176
103393
|
this.pending.clear();
|
|
103177
103394
|
this.releaseQuestions("Stopped by user");
|
|
103178
103395
|
if (this.turnOpen) this.stream.stopped();
|
|
103179
|
-
void this.query?.interrupt().
|
|
103396
|
+
void this.query?.interrupt(cancelQueued ? { cancelQueued: true } : void 0).then((response) => {
|
|
103397
|
+
const cancelled = response?.cancelled;
|
|
103398
|
+
if (!Array.isArray(cancelled)) return;
|
|
103399
|
+
let count = 0;
|
|
103400
|
+
for (const id2 of cancelled) if (typeof id2 === "string" && this.personInputs.delete(id2)) count++;
|
|
103401
|
+
this.reportUndelivered(count);
|
|
103402
|
+
}).catch((err) => {
|
|
103180
103403
|
this.emit({
|
|
103181
103404
|
type: "error",
|
|
103182
103405
|
sessionId: this.sessionId,
|
|
@@ -103185,6 +103408,34 @@ var ClaudeSession = class {
|
|
|
103185
103408
|
});
|
|
103186
103409
|
this.emit({ type: "state_change", sessionId: this.sessionId, state: "waiting_input", reason: "interrupted" });
|
|
103187
103410
|
}
|
|
103411
|
+
/** A failed turn is already over. Cancel its parent notes without interrupting the person's next turn. */
|
|
103412
|
+
cancelParentNotes(reason) {
|
|
103413
|
+
const ids = this.inputReceipts.ids;
|
|
103414
|
+
this.inputReceipts.failAll(reason);
|
|
103415
|
+
this.queue = this.queue.filter((input) => !input.parentNote);
|
|
103416
|
+
if (this.retry) {
|
|
103417
|
+
const texts = this.retry.texts;
|
|
103418
|
+
texts.splice(0, texts.length, ...texts.filter((input) => !input.parentNote));
|
|
103419
|
+
if (!texts.length) this.cancelRetry();
|
|
103420
|
+
}
|
|
103421
|
+
const query = this.query;
|
|
103422
|
+
if (!query?.cancelAsyncMessage || this.personInputs.size || this.queue.length || this.retry?.texts.length || this.sendGate) return;
|
|
103423
|
+
for (const id2 of ids) void query.cancelAsyncMessage(id2).catch((error51) => {
|
|
103424
|
+
this.emit({ type: "error", sessionId: this.sessionId, error: {
|
|
103425
|
+
code: "internal",
|
|
103426
|
+
message: `Could not cancel the parent note: ${error51.message}`,
|
|
103427
|
+
retryable: false
|
|
103428
|
+
} });
|
|
103429
|
+
});
|
|
103430
|
+
}
|
|
103431
|
+
reportUndelivered(count) {
|
|
103432
|
+
if (!count) return;
|
|
103433
|
+
this.emit({
|
|
103434
|
+
type: "error",
|
|
103435
|
+
sessionId: this.sessionId,
|
|
103436
|
+
error: { code: "internal", message: `${count} message(s) were still queued when the turn stopped and were not delivered \u2014 please resend`, retryable: false }
|
|
103437
|
+
});
|
|
103438
|
+
}
|
|
103188
103439
|
/**
|
|
103189
103440
|
* Stops one background task (#290). Measured on a backgrounded shell: `background_tasks_changed` without it,
|
|
103190
103441
|
* `task_updated` killed and `task_notification` stopped follow at once, and the stream turns that into the
|
|
@@ -103203,6 +103454,7 @@ var ClaudeSession = class {
|
|
|
103203
103454
|
* same way; only swapping the process was missing it.
|
|
103204
103455
|
*/
|
|
103205
103456
|
async dispose() {
|
|
103457
|
+
this.inputReceipts.failAll(new Error("The session closed before the parent note finished"));
|
|
103206
103458
|
this.closed = true;
|
|
103207
103459
|
this.notify?.();
|
|
103208
103460
|
this.settleGoalKnown();
|
|
@@ -103244,6 +103496,7 @@ var ClaudeSession = class {
|
|
|
103244
103496
|
* the process (#98) — and ends every background task the session listed (#290), for the same reason.
|
|
103245
103497
|
*/
|
|
103246
103498
|
releaseAgents(why) {
|
|
103499
|
+
this.inputReceipts.failAll(new Error(why));
|
|
103247
103500
|
if (this.scheduleLook) clearTimeout(this.scheduleLook);
|
|
103248
103501
|
this.scheduleLook = null;
|
|
103249
103502
|
for (const e of this.stream.release(why)) this.emit(e);
|
|
@@ -103391,6 +103644,8 @@ var ClaudeAdapter = class _ClaudeAdapter {
|
|
|
103391
103644
|
processModel: "per-session",
|
|
103392
103645
|
builtInSubagents: true,
|
|
103393
103646
|
peerMessaging: true,
|
|
103647
|
+
// The query's prompt iterator reads queued user messages while the current turn continues.
|
|
103648
|
+
midTurnInput: true,
|
|
103394
103649
|
/*
|
|
103395
103650
|
* An idle session may be put to sleep (#568): a resumed process read the prompt cache as the live one did (~100% at
|
|
103396
103651
|
* 2, 6 and 10 min on the 1 h lifetime, CLI 2.1.29x with haiku, #561) for +0.7 s (p50) / +1.8 s (p95) before the first
|
|
@@ -105157,6 +105412,8 @@ var CodexSession = class {
|
|
|
105157
105412
|
* **that we ourselves started**, and flush the queue as a single turn once that turn ends.
|
|
105158
105413
|
*/
|
|
105159
105414
|
pendingInputs = [];
|
|
105415
|
+
inputReceipts = new InputReceipts();
|
|
105416
|
+
inputGroups = /* @__PURE__ */ new Map();
|
|
105160
105417
|
/** An unsteerable turn (compact/review) is running — we know because we are the ones who started it */
|
|
105161
105418
|
blockingTurn = false;
|
|
105162
105419
|
/**
|
|
@@ -105524,6 +105781,16 @@ var CodexSession = class {
|
|
|
105524
105781
|
this.activityBefore = null;
|
|
105525
105782
|
}
|
|
105526
105783
|
this.noteAppCall(n);
|
|
105784
|
+
if (n.method === "item/started" || n.method === "item/completed") {
|
|
105785
|
+
const p = n.params ?? {};
|
|
105786
|
+
if (p.item?.type === "userMessage" && p.item.clientId && p.turnId) {
|
|
105787
|
+
const group = this.inputGroups.get(p.item.clientId);
|
|
105788
|
+
if (group) {
|
|
105789
|
+
this.inputGroups.delete(p.item.clientId);
|
|
105790
|
+
this.takeInputTurn({ turn: { id: p.turnId } }, group);
|
|
105791
|
+
}
|
|
105792
|
+
}
|
|
105793
|
+
}
|
|
105527
105794
|
if (n.method === "turn/completed" && this.blockingTurn) {
|
|
105528
105795
|
this.blockingTurn = false;
|
|
105529
105796
|
this.flushPending();
|
|
@@ -105541,6 +105808,7 @@ var CodexSession = class {
|
|
|
105541
105808
|
}
|
|
105542
105809
|
if (n.method === "mcpServer/startupStatus/updated" && !this.firstMcpFailure(n.params)) return;
|
|
105543
105810
|
for (const e of normalizeNotification(this.sessionId, n)) {
|
|
105811
|
+
if (e.type === "error" && this.inputReceipts.size) this.interrupt(new Error(e.error.message));
|
|
105544
105812
|
if (e.type === "message_image" && !e.data && e.path) {
|
|
105545
105813
|
void imageEventFromDisk(this.sessionId, e.path).then((filled) => this.emit(filled));
|
|
105546
105814
|
continue;
|
|
@@ -105559,6 +105827,10 @@ var CodexSession = class {
|
|
|
105559
105827
|
else this.activityBefore = e.activity;
|
|
105560
105828
|
}
|
|
105561
105829
|
this.emit(e);
|
|
105830
|
+
if (e.type === "turn_complete") {
|
|
105831
|
+
const ended = turnIdOf(n.params);
|
|
105832
|
+
if (ended) this.inputReceipts.endTurn(ended);
|
|
105833
|
+
}
|
|
105562
105834
|
}
|
|
105563
105835
|
this.noteSpawn(n);
|
|
105564
105836
|
if (n.method === "turn/completed" && this.unlinked.size > 0) {
|
|
@@ -105730,13 +106002,21 @@ var CodexSession = class {
|
|
|
105730
106002
|
applyRules(matchers) {
|
|
105731
106003
|
this.alwaysAllow.addAll(matchers);
|
|
105732
106004
|
}
|
|
106005
|
+
sendTracked(text2, observer) {
|
|
106006
|
+
const receipt = this.inputReceipts.create(observer);
|
|
106007
|
+
this.sendInput(text2, receipt);
|
|
106008
|
+
return receipt.done;
|
|
106009
|
+
}
|
|
105733
106010
|
send(text2) {
|
|
106011
|
+
this.sendInput(text2);
|
|
106012
|
+
}
|
|
106013
|
+
sendInput(text2, receipt) {
|
|
105734
106014
|
const nth = ++this.sendsIssued;
|
|
105735
106015
|
void this.ready.then(() => {
|
|
105736
|
-
if (nth <= this.sendsStopped) return;
|
|
106016
|
+
if (nth <= this.sendsStopped || receipt?.cancelled) return;
|
|
105737
106017
|
if (!this.threadId) throw new Error("Thread is not ready");
|
|
105738
106018
|
if (this.blockingTurn) {
|
|
105739
|
-
this.pendingInputs.push(text2);
|
|
106019
|
+
this.pendingInputs.push({ text: text2, receipt });
|
|
105740
106020
|
return;
|
|
105741
106021
|
}
|
|
105742
106022
|
if (text2.trim() === "/compact") {
|
|
@@ -105789,8 +106069,10 @@ var CodexSession = class {
|
|
|
105789
106069
|
say2(status === "active" ? `Goal set: ${arg}` : `Goal set (${status}): ${arg}`);
|
|
105790
106070
|
});
|
|
105791
106071
|
}
|
|
106072
|
+
if (receipt) this.inputGroups.set(receipt.id, [receipt]);
|
|
105792
106073
|
return this.client.request("turn/start", {
|
|
105793
106074
|
threadId: this.threadId,
|
|
106075
|
+
...receipt ? { clientUserMessageId: receipt.id } : {},
|
|
105794
106076
|
input: [{ type: "text", text: text2 }],
|
|
105795
106077
|
/*
|
|
105796
106078
|
* Reasoning effort is passed per turn — this is the slot codex documents as applying
|
|
@@ -105799,8 +106081,11 @@ var CodexSession = class {
|
|
|
105799
106081
|
*/
|
|
105800
106082
|
...this.opts.effort ? { effort: this.opts.effort } : {},
|
|
105801
106083
|
...this.outputSchemaParam()
|
|
105802
|
-
}).then((res) => this.
|
|
106084
|
+
}).then((res) => this.takeInputTurn(res, receipt ? [receipt] : []));
|
|
105803
106085
|
}).catch((e) => {
|
|
106086
|
+
if (receipt) this.inputGroups.delete(receipt.id);
|
|
106087
|
+
if (receipt?.cancelled || receipt?.completed) return;
|
|
106088
|
+
if (receipt) this.interrupt(e);
|
|
105804
106089
|
if (this.startFailed) return;
|
|
105805
106090
|
this.emit({
|
|
105806
106091
|
type: "error",
|
|
@@ -105812,14 +106097,22 @@ var CodexSession = class {
|
|
|
105812
106097
|
/** Sends the messages that were queued out as one turn — each message as its own input item (does not blur the boundary) */
|
|
105813
106098
|
flushPending() {
|
|
105814
106099
|
if (this.pendingInputs.length === 0 || !this.threadId) return;
|
|
105815
|
-
const
|
|
106100
|
+
const pending = this.pendingInputs;
|
|
106101
|
+
const receipts = pending.flatMap((p) => p.receipt ? [p.receipt] : []);
|
|
106102
|
+
const clientId = receipts[0]?.id;
|
|
106103
|
+
if (clientId) this.inputGroups.set(clientId, receipts);
|
|
106104
|
+
const input = pending.map(({ text: text2 }) => ({ type: "text", text: text2 }));
|
|
105816
106105
|
this.pendingInputs = [];
|
|
105817
106106
|
void this.client.request("turn/start", {
|
|
105818
106107
|
threadId: this.threadId,
|
|
106108
|
+
...clientId ? { clientUserMessageId: clientId } : {},
|
|
105819
106109
|
input,
|
|
105820
106110
|
...this.opts.effort ? { effort: this.opts.effort } : {},
|
|
105821
106111
|
...this.outputSchemaParam()
|
|
105822
|
-
}).then((res) => this.
|
|
106112
|
+
}).then((res) => this.takeInputTurn(res, receipts)).catch((e) => {
|
|
106113
|
+
if (clientId) this.inputGroups.delete(clientId);
|
|
106114
|
+
if (receipts.length && receipts.every((r) => r.cancelled || r.completed)) return;
|
|
106115
|
+
if (this.inputReceipts.size) this.interrupt(e);
|
|
105823
106116
|
this.emit({
|
|
105824
106117
|
type: "error",
|
|
105825
106118
|
sessionId: this.sessionId,
|
|
@@ -105827,6 +106120,25 @@ var CodexSession = class {
|
|
|
105827
106120
|
});
|
|
105828
106121
|
});
|
|
105829
106122
|
}
|
|
106123
|
+
takeInputTurn(res, receipts) {
|
|
106124
|
+
if (receipts.length && receipts.every((r) => r.completed)) return;
|
|
106125
|
+
const id2 = turnIdOf(res);
|
|
106126
|
+
if (!receipts.length) {
|
|
106127
|
+
this.takeStartedTurn(id2);
|
|
106128
|
+
return;
|
|
106129
|
+
}
|
|
106130
|
+
if (!id2) {
|
|
106131
|
+
this.interrupt(new Error("Codex did not identify the turn accepting the parent note"));
|
|
106132
|
+
return;
|
|
106133
|
+
}
|
|
106134
|
+
if (receipts.some((r) => r.cancelled) && !this.endedTurns.includes(id2)) {
|
|
106135
|
+
void this.client.request("turn/interrupt", { threadId: this.threadId, turnId: id2 }).catch(() => {
|
|
106136
|
+
});
|
|
106137
|
+
}
|
|
106138
|
+
if (receipts.some((r) => r.cancelled)) return;
|
|
106139
|
+
this.takeStartedTurn(id2);
|
|
106140
|
+
for (const receipt of receipts) this.inputReceipts.bind(receipt, id2);
|
|
106141
|
+
}
|
|
105830
106142
|
/**
|
|
105831
106143
|
* Takes a running turn's id from a response, unless the notifications already named one or already ended it. The
|
|
105832
106144
|
* response and the notifications race: `turn/started` can come after the response (Stop pressed very quickly), and
|
|
@@ -105894,7 +106206,14 @@ var CodexSession = class {
|
|
|
105894
106206
|
}
|
|
105895
106207
|
return out;
|
|
105896
106208
|
}
|
|
105897
|
-
interrupt() {
|
|
106209
|
+
interrupt(reason = new Error("The parent note was stopped before it finished")) {
|
|
106210
|
+
const turns = this.inputReceipts.failAll(reason);
|
|
106211
|
+
this.pendingInputs = this.pendingInputs.filter((p) => !p.receipt);
|
|
106212
|
+
for (const turnId of turns) {
|
|
106213
|
+
if (!this.threadId || turnId === this.turnId || this.endedTurns.includes(turnId)) continue;
|
|
106214
|
+
void this.client.request("turn/interrupt", { threadId: this.threadId, turnId }).catch(() => {
|
|
106215
|
+
});
|
|
106216
|
+
}
|
|
105898
106217
|
this.opts.apps?.cancelAll();
|
|
105899
106218
|
if (!this.threadId && this.sendsIssued > this.sendsStopped) {
|
|
105900
106219
|
this.sendsStopped = this.sendsIssued;
|
|
@@ -105927,6 +106246,8 @@ var CodexSession = class {
|
|
|
105927
106246
|
}
|
|
105928
106247
|
/** The app-server is gone, and the child threads with it — each still listed ends as stopped. */
|
|
105929
106248
|
releaseChildren() {
|
|
106249
|
+
this.inputGroups.clear();
|
|
106250
|
+
this.inputReceipts.failAll(new Error("The session process ended before the parent note finished"));
|
|
105930
106251
|
for (const e of this.children.release("Ended with the session process")) this.emit(e);
|
|
105931
106252
|
}
|
|
105932
106253
|
/**
|
|
@@ -106102,6 +106423,8 @@ var CodexAdapter = class {
|
|
|
106102
106423
|
processModel: "per-session",
|
|
106103
106424
|
builtInSubagents: true,
|
|
106104
106425
|
peerMessaging: false,
|
|
106426
|
+
// 0.160 TurnStartParams: turn/start steers an already-active regular turn; compact/review input is queued.
|
|
106427
|
+
midTurnInput: true,
|
|
106105
106428
|
/*
|
|
106106
106429
|
* Not put to sleep (#568). Waking a large thread measured 7-13 s (`thread/resume` scales with the rollout, ~13.5 s for
|
|
106107
106430
|
* 550 MB), and its cache on resume is unmeasured. The memory is better won by one shared app-server per account
|
|
@@ -109950,6 +110273,8 @@ var SessionManager = class {
|
|
|
109950
110273
|
* app-server), even though the screen said "starting next turn".
|
|
109951
110274
|
*/
|
|
109952
110275
|
restartAfterTurn = /* @__PURE__ */ new Set();
|
|
110276
|
+
/** Input arriving after a note completes waits for its deferred process replacement, including disposal. */
|
|
110277
|
+
deferredRestarts = /* @__PURE__ */ new Map();
|
|
109953
110278
|
/** A new host served a different Centralu tool prefix; replace the old agent at its safe boundary. */
|
|
109954
110279
|
definitionsRestartAfterTurn = /* @__PURE__ */ new Set();
|
|
109955
110280
|
/** A kept process was started with adapter settings from an older host build (#589). */
|
|
@@ -110061,7 +110386,7 @@ var SessionManager = class {
|
|
|
110061
110386
|
throw Object.assign(new Error(`Directory not found: ${path2}`), { code: "internal" });
|
|
110062
110387
|
}
|
|
110063
110388
|
const existing = this.store.findProjectByPath(path2);
|
|
110064
|
-
const id2 = existing?.id ??
|
|
110389
|
+
const id2 = existing?.id ?? randomUUID9();
|
|
110065
110390
|
this.store.addProject({ id: id2, path: path2, name: basename5(path2) });
|
|
110066
110391
|
return this.projectInfo(id2, path2);
|
|
110067
110392
|
}
|
|
@@ -110589,7 +110914,7 @@ var SessionManager = class {
|
|
|
110589
110914
|
);
|
|
110590
110915
|
}
|
|
110591
110916
|
}
|
|
110592
|
-
const id2 =
|
|
110917
|
+
const id2 = randomUUID9();
|
|
110593
110918
|
let worktree = null;
|
|
110594
110919
|
if (params.worktree && params.projectId) {
|
|
110595
110920
|
const summary = await gitSummary(params.cwd, this.gitTrust(params.projectId));
|
|
@@ -111610,20 +111935,30 @@ var SessionManager = class {
|
|
|
111610
111935
|
}
|
|
111611
111936
|
}
|
|
111612
111937
|
}
|
|
111938
|
+
const run = e.sessionId ? this.agentRuns.get(e.sessionId) : void 0;
|
|
111939
|
+
run?.onEvent(e);
|
|
111613
111940
|
this.emit(seq != null ? { ...e, seq } : e);
|
|
111614
|
-
if (endedTurn && e.sessionId) void this.reportBackIfAwaited(e.sessionId, e);
|
|
111615
|
-
if (e.sessionId) this.agentRuns.get(e.sessionId)?.onEvent(e);
|
|
111941
|
+
if (endedTurn && e.sessionId && !run?.tracksInputs) void this.reportBackIfAwaited(e.sessionId, e, run?.completedAnswer);
|
|
111616
111942
|
if (e.type === "approval_resolved" && e.sessionId) this.raiseHostAsks(e.sessionId);
|
|
111617
111943
|
if (endedTurn && e.sessionId) this.forgetCommits(e.sessionId);
|
|
111618
|
-
if (endedTurn && e.sessionId
|
|
111619
|
-
|
|
111620
|
-
|
|
111621
|
-
|
|
111622
|
-
|
|
111623
|
-
|
|
111624
|
-
|
|
111625
|
-
|
|
111626
|
-
|
|
111944
|
+
if (endedTurn && e.sessionId) this.applyDeferredRestart(e.sessionId);
|
|
111945
|
+
}
|
|
111946
|
+
/** A note receipt follows turn_complete: re-check the same deferred change when the last hold is released. */
|
|
111947
|
+
applyDeferredRestart(sessionId) {
|
|
111948
|
+
const session2 = this.meta.get(sessionId);
|
|
111949
|
+
if (!session2 || inTurn(session2.state) || this.deferredRestarts.has(sessionId) || this.agentRuns.get(sessionId)?.waitingForInputs || !this.restartAfterTurn.delete(sessionId)) return;
|
|
111950
|
+
const apps = this.appsRestartAfterTurn.delete(sessionId);
|
|
111951
|
+
const definitions = this.definitionsRestartAfterTurn.delete(sessionId);
|
|
111952
|
+
const launchSettings = this.launchSettingsRestartAfterTurn.delete(sessionId);
|
|
111953
|
+
if (!(apps || definitions || launchSettings || this.settingsDrifted(sessionId)) || !this.handles.has(sessionId)) return;
|
|
111954
|
+
const cause2 = launchSettings ? "launch_settings" : apps ? "thread_restart" : definitions ? "instructions" : this.restartCause(sessionId);
|
|
111955
|
+
const restarting = Promise.resolve().then(() => this.restartSession(sessionId, cause2)).then(() => {
|
|
111956
|
+
}).finally(() => {
|
|
111957
|
+
if (this.deferredRestarts.get(sessionId) === restarting) this.deferredRestarts.delete(sessionId);
|
|
111958
|
+
});
|
|
111959
|
+
this.deferredRestarts.set(sessionId, restarting);
|
|
111960
|
+
void restarting.catch(() => {
|
|
111961
|
+
});
|
|
111627
111962
|
}
|
|
111628
111963
|
/**
|
|
111629
111964
|
* Notifies the orchestrator when the work it assigned finishes — **the other half of "one window".**
|
|
@@ -111637,14 +111972,14 @@ var SessionManager = class {
|
|
|
111637
111972
|
* - Once notified, the marker is cleared. Otherwise that session would wake the orchestrator on
|
|
111638
111973
|
* every turn it runs on its own afterward — which would become a loop of waking each other
|
|
111639
111974
|
*/
|
|
111640
|
-
async reportBackIfAwaited(sessionId, ending) {
|
|
111975
|
+
async reportBackIfAwaited(sessionId, ending, answer) {
|
|
111641
111976
|
const orchestratorId = this.awaitingReport.get(sessionId);
|
|
111642
111977
|
if (!orchestratorId) return;
|
|
111643
111978
|
this.awaitingReport.delete(sessionId);
|
|
111644
111979
|
const target = this.meta.get(sessionId);
|
|
111645
111980
|
if (!target || !this.meta.has(orchestratorId)) return;
|
|
111646
111981
|
const project2 = target.projectId ? this.store.listProjects().find((p) => p.id === target.projectId)?.name ?? "(project no longer exists)" : "(none)";
|
|
111647
|
-
await this.deliverReport(orchestratorId, sessionId, target.name, project2, previewOf(this.store, sessionId, 600), ending);
|
|
111982
|
+
await this.deliverReport(orchestratorId, sessionId, target.name, project2, answer?.slice(0, 600) ?? previewOf(this.store, sessionId, 600), ending);
|
|
111648
111983
|
}
|
|
111649
111984
|
/** Reports the end of a turn the orchestrator asked to hear about, as one message in its session (#166) */
|
|
111650
111985
|
async deliverReport(orchestratorId, sessionId, name, project2, preview, ending) {
|
|
@@ -111980,7 +112315,9 @@ If you need more, you can read that session's recent conversation with read_sess
|
|
|
111980
112315
|
* exposed on send(), which anything else can call — a boundary that can be flipped on by accident
|
|
111981
112316
|
* is not a boundary.
|
|
111982
112317
|
*/
|
|
111983
|
-
async deliver(sessionId, text2, attachments, from, relayed, fromApp, fromAppVia = "inline") {
|
|
112318
|
+
async deliver(sessionId, text2, attachments, from, relayed, fromApp, fromAppVia = "inline", sendInput) {
|
|
112319
|
+
const restarting = this.deferredRestarts.get(sessionId);
|
|
112320
|
+
if (restarting) await restarting;
|
|
111984
112321
|
const m = this.meta.get(sessionId);
|
|
111985
112322
|
if (!m) throw Object.assign(new Error(`Session not found: ${sessionId}`), { code: "session_not_found" });
|
|
111986
112323
|
if (!this.handles.has(sessionId)) {
|
|
@@ -112027,7 +112364,8 @@ If you need more, you can read that session's recent conversation with read_sess
|
|
|
112027
112364
|
const adapterText = fromApp ? appMessageFrame(fromApp, text2, fromAppVia) : from && (relayed || this.directs(sessionId) && !this.directs(from.sessionId) && from.sessionId !== HUB_SENDER) ? untrustedSourceSessionNotification(from.sessionId) : attachments?.length ? `${text2}
|
|
112028
112365
|
|
|
112029
112366
|
${attachments.map((a) => `@${a.path}`).join("\n")}` : text2;
|
|
112030
|
-
h
|
|
112367
|
+
if (sendInput) sendInput(h, adapterText);
|
|
112368
|
+
else h.send(adapterText);
|
|
112031
112369
|
}
|
|
112032
112370
|
/**
|
|
112033
112371
|
* Switches a session's agent (claude <-> codex).
|
|
@@ -112710,6 +113048,7 @@ ${String(stderr).slice(0, 2e3)}`
|
|
|
112710
113048
|
sessionIdle(sessionId) {
|
|
112711
113049
|
const m = this.meta.get(sessionId);
|
|
112712
113050
|
if (!m) throw Object.assign(new Error(`Session not found: ${sessionId}`), { code: "session_not_found" });
|
|
113051
|
+
if (this.agentRuns.get(sessionId)?.waitingForInputs) return { idle: false, reason: "turn" };
|
|
112713
113052
|
return sessionIdle(m, {
|
|
112714
113053
|
running: this.handles.has(sessionId),
|
|
112715
113054
|
reportsBackground: !!this.adapters.get(m.tool)?.capabilities.backgroundTasks
|
|
@@ -112737,7 +113076,7 @@ ${String(stderr).slice(0, 2e3)}`
|
|
|
112737
113076
|
*/
|
|
112738
113077
|
async sleepSession(sessionId) {
|
|
112739
113078
|
const h = this.handles.get(sessionId);
|
|
112740
|
-
if (!h || this.resuming.has(sessionId) || this.sleeping.has(sessionId) || this.leaving.has(sessionId)) return false;
|
|
113079
|
+
if (!h || this.resuming.has(sessionId) || this.deferredRestarts.has(sessionId) || this.sleeping.has(sessionId) || this.leaving.has(sessionId)) return false;
|
|
112741
113080
|
if (!this.sessionIdle(sessionId).idle || this.waitsOn(sessionId).length > 0) return false;
|
|
112742
113081
|
this.handles.delete(sessionId);
|
|
112743
113082
|
this.running.delete(sessionId);
|
|
@@ -112757,12 +113096,13 @@ ${String(stderr).slice(0, 2e3)}`
|
|
|
112757
113096
|
return true;
|
|
112758
113097
|
}
|
|
112759
113098
|
interrupt(sessionId) {
|
|
112760
|
-
this.requireHandle(sessionId)
|
|
113099
|
+
const handle = this.requireHandle(sessionId);
|
|
113100
|
+
const run = this.agentRuns.get(sessionId);
|
|
113101
|
+
if (run) run.stoppedByPerson = true;
|
|
113102
|
+
handle.interrupt();
|
|
112761
113103
|
for (const d2 of this.delegations.values()) {
|
|
112762
113104
|
if (d2.callerId === sessionId && (!d2.agent || (d2.waiters ?? 0) > 0)) d2.stop("the person stopped the session that asked");
|
|
112763
113105
|
}
|
|
112764
|
-
const run = this.agentRuns.get(sessionId);
|
|
112765
|
-
if (run) run.stoppedByPerson = true;
|
|
112766
113106
|
}
|
|
112767
113107
|
/**
|
|
112768
113108
|
* Restarts only the agent (an extension of FR-10).
|
|
@@ -113402,7 +113742,7 @@ ${String(stderr).slice(0, 2e3)}`
|
|
|
113402
113742
|
askHost(sessionId, detail, signal) {
|
|
113403
113743
|
return new Promise((resolve11) => {
|
|
113404
113744
|
if (signal?.aborted) return resolve11(null);
|
|
113405
|
-
const requestId = `${detail.kind === "project_access" ? "xp" : detail.kind === "agent_start" ? "ag" : detail.kind === "centralu_update" ? "up" : "mk"}-${
|
|
113745
|
+
const requestId = `${detail.kind === "project_access" ? "xp" : detail.kind === "agent_start" ? "ag" : detail.kind === "centralu_update" ? "up" : "mk"}-${randomUUID9()}`;
|
|
113406
113746
|
this.hostAsks.set(requestId, { requestId, sessionId, detail, shown: false, resolve: resolve11 });
|
|
113407
113747
|
signal?.addEventListener("abort", () => {
|
|
113408
113748
|
if (!this.hostAsks.delete(requestId)) return;
|
|
@@ -113528,31 +113868,38 @@ ${String(stderr).slice(0, 2e3)}`
|
|
|
113528
113868
|
/** Registers the wait on the delegated session's turn (the same watcher an app's agent uses, `AgentRunWait`) */
|
|
113529
113869
|
startDelegation(key, callerId, sessionId, target, agent = false) {
|
|
113530
113870
|
let notice;
|
|
113531
|
-
|
|
113871
|
+
let answerAfter = this.meta.get(sessionId)?.lastSeq ?? 0;
|
|
113872
|
+
const wait = new AgentRunWait((message) => notice = message, () => this.meta.get(sessionId)?.name ?? "the session", void 0, () => {
|
|
113873
|
+
const answer = finalAnswer(this.store.loadMessages(sessionId, 50).filter((m) => m.seq > answerAfter));
|
|
113874
|
+
answerAfter = this.meta.get(sessionId)?.lastSeq ?? answerAfter;
|
|
113875
|
+
return answer;
|
|
113876
|
+
});
|
|
113532
113877
|
const d2 = {
|
|
113533
113878
|
callerId,
|
|
113534
113879
|
sessionId,
|
|
113535
113880
|
target,
|
|
113536
113881
|
wait,
|
|
113537
|
-
settled
|
|
113882
|
+
get settled() {
|
|
113883
|
+
return wait.finished;
|
|
113884
|
+
},
|
|
113538
113885
|
notice: () => notice,
|
|
113539
113886
|
outcome: wait.done.then(
|
|
113540
|
-
() => ({ ok: true }),
|
|
113887
|
+
(r) => ({ ok: true, answer: r.answer }),
|
|
113541
113888
|
(e) => ({ ok: false, error: e.message })
|
|
113542
113889
|
),
|
|
113543
113890
|
stop: (why) => {
|
|
113544
113891
|
if (d2.settled) return;
|
|
113892
|
+
wait.fail(new Error(`${why}, so the delegated turn was stopped`));
|
|
113545
113893
|
try {
|
|
113546
113894
|
this.handles.get(sessionId)?.interrupt();
|
|
113547
113895
|
} catch {
|
|
113548
113896
|
}
|
|
113549
|
-
wait.fail(new Error(`${why}, so the delegated turn was stopped`));
|
|
113550
113897
|
},
|
|
113551
113898
|
...agent ? { agent: true } : {}
|
|
113552
113899
|
};
|
|
113553
|
-
void d2.outcome.then(() => {
|
|
113554
|
-
d2.settled = true;
|
|
113900
|
+
void d2.outcome.then((r) => {
|
|
113555
113901
|
if (this.agentRuns.get(sessionId) === wait) this.agentRuns.delete(sessionId);
|
|
113902
|
+
if (agent) void this.reportBackIfAwaited(sessionId, r.ok ? { type: "turn_complete", sessionId } : { type: "error", sessionId, error: { code: "internal", message: r.error, retryable: false } }, r.ok ? r.answer : void 0);
|
|
113556
113903
|
});
|
|
113557
113904
|
this.agentRuns.set(sessionId, wait);
|
|
113558
113905
|
this.delegations.set(key, d2);
|
|
@@ -113589,7 +113936,7 @@ ${String(stderr).slice(0, 2e3)}`
|
|
|
113589
113936
|
error: `${d2.target.name}'s session "${sessionName()}" [${d2.sessionId}] did not finish: ${r.error}. Tell the person, or ask again with a clearer task.`
|
|
113590
113937
|
};
|
|
113591
113938
|
}
|
|
113592
|
-
const answer = finalAnswer(this.store.loadMessages(d2.sessionId, 50));
|
|
113939
|
+
const answer = r.answer ?? finalAnswer(this.store.loadMessages(d2.sessionId, 50));
|
|
113593
113940
|
const { granted, outside } = readableGrants(pathsIn(answer), d2.target.path);
|
|
113594
113941
|
if (granted.length) {
|
|
113595
113942
|
const set3 = this.readGrants.get(d2.callerId) ?? /* @__PURE__ */ new Set();
|
|
@@ -113708,8 +114055,8 @@ ${String(stderr).slice(0, 2e3)}`
|
|
|
113708
114055
|
*
|
|
113709
114056
|
* status where it stands, without waiting (the answer, once the turn the caller gave it has ended)
|
|
113710
114057
|
* wait waits on that turn up to the bound, as start_agent does
|
|
113711
|
-
* send gives it more
|
|
113712
|
-
*
|
|
114058
|
+
* send gives it more, including during the caller's turn on adapters that accept mid-turn input; refused
|
|
114059
|
+
* while it works on the person's own message or has a higher preset than the caller
|
|
113713
114060
|
* stop stops the turn the caller gave it; a turn the person started there is theirs
|
|
113714
114061
|
* delete cleans up a child's merged, clean worktree (`cleanUpWorktreeChild`, #76's hard gate, part 3)
|
|
113715
114062
|
*/
|
|
@@ -113743,26 +114090,48 @@ ${String(stderr).slice(0, 2e3)}`
|
|
|
113743
114090
|
return { ok: true, state: "deleted", sessionId: child.id, sessionName: child.name, tool: child.tool, branch: r.branch, proof: r.proof };
|
|
113744
114091
|
}
|
|
113745
114092
|
case "send": {
|
|
113746
|
-
if (d2 && !d2.settled) {
|
|
113747
|
-
return { ok: false, error: `"${child.name}" is still working on what you gave it. Wait for it (action "wait") or stop it first.` };
|
|
113748
|
-
}
|
|
113749
|
-
if (d2) this.delegations.delete(key);
|
|
113750
|
-
if (child.state === "working" || child.state === "waiting_approval" || this.agentRuns.has(child.id)) {
|
|
113751
|
-
return { ok: false, error: `"${child.name}" is busy with something else (the person may be using it). Try again later.` };
|
|
113752
|
-
}
|
|
113753
114093
|
if (!presetWithin(child.permissionPreset, caller.permissionPreset)) {
|
|
113754
114094
|
return {
|
|
113755
114095
|
ok: false,
|
|
113756
114096
|
error: `"${child.name}" now has more permissions (${child.permissionPreset}) than this session (${caller.permissionPreset}), so this session cannot give it work. Tell the person.`
|
|
113757
114097
|
};
|
|
113758
114098
|
}
|
|
114099
|
+
const from = { sessionId: callerId, name: labelOf(this.store, caller) };
|
|
114100
|
+
if (d2 && !d2.settled) {
|
|
114101
|
+
const text2 = agentNoteFrame(from.name, opts.text ?? "");
|
|
114102
|
+
if (!this.adapters.get(child.tool)?.capabilities.midTurnInput || !this.handles.get(child.id)?.sendTracked) {
|
|
114103
|
+
return { ok: false, error: `"${child.name}" cannot take messages while working. Wait for it (action "wait") or stop it first.` };
|
|
114104
|
+
}
|
|
114105
|
+
const releaseHold = d2.wait.holdInput();
|
|
114106
|
+
const release = () => {
|
|
114107
|
+
releaseHold();
|
|
114108
|
+
this.applyDeferredRestart(child.id);
|
|
114109
|
+
};
|
|
114110
|
+
try {
|
|
114111
|
+
await this.deliver(child.id, text2, void 0, from, false, void 0, "inline", (h, input) => {
|
|
114112
|
+
const fail4 = (e) => {
|
|
114113
|
+
d2.wait.fail(e);
|
|
114114
|
+
release();
|
|
114115
|
+
};
|
|
114116
|
+
void h.sendTracked(input, { complete: release, fail: fail4 }).then(release, fail4);
|
|
114117
|
+
});
|
|
114118
|
+
} catch (e) {
|
|
114119
|
+
release();
|
|
114120
|
+
return { ok: false, error: `Could not send it: ${e.message}` };
|
|
114121
|
+
}
|
|
114122
|
+
return this.agentWorking(d2, false);
|
|
114123
|
+
}
|
|
114124
|
+
if (child.state === "working" || child.state === "waiting_approval" || this.agentRuns.has(child.id) && !this.agentRuns.get(child.id).finished) {
|
|
114125
|
+
return { ok: false, error: `"${child.name}" is busy with something else (the person may be using it). Try again later.` };
|
|
114126
|
+
}
|
|
114127
|
+
if (d2) this.delegations.delete(key);
|
|
113759
114128
|
const full = this.agentCeilingError(child.projectId ?? "");
|
|
113760
114129
|
if (full) return { ok: false, error: full };
|
|
113761
114130
|
const project2 = this.store.listProjects().find((p) => p.id === child.projectId);
|
|
113762
114131
|
const cwd = this.store.sessionCwd(child.id) ?? child.worktree?.path ?? project2?.path ?? "";
|
|
113763
114132
|
const next = this.startDelegation(key, callerId, child.id, { id: child.projectId ?? "", name: project2?.name ?? "", path: cwd }, true);
|
|
113764
114133
|
try {
|
|
113765
|
-
await this.deliver(child.id, opts.text ?? "", void 0,
|
|
114134
|
+
await this.deliver(child.id, agentFrame(from.name, opts.text ?? ""), void 0, from, false);
|
|
113766
114135
|
} catch (e) {
|
|
113767
114136
|
this.delegations.delete(key);
|
|
113768
114137
|
if (this.agentRuns.get(child.id) === next.wait) this.agentRuns.delete(child.id);
|
|
@@ -113782,7 +114151,7 @@ ${String(stderr).slice(0, 2e3)}`
|
|
|
113782
114151
|
if (!summary.isRepo) return { error: "This project is not a git repository, so it has no worktrees. Start the agent without one." };
|
|
113783
114152
|
const asked = opts.branch?.trim();
|
|
113784
114153
|
if (asked) return await gitValidBranchName(asked) ? { branch: asked } : { error: `Not a valid branch name: ${asked}` };
|
|
113785
|
-
return { branch: agentBranch(opts.task,
|
|
114154
|
+
return { branch: agentBranch(opts.task, randomUUID9()) };
|
|
113786
114155
|
}
|
|
113787
114156
|
/**
|
|
113788
114157
|
* Cleans up a child's merged worktree (#76's hard gate, moved from the retired worktree manager's tool to
|
|
@@ -113951,7 +114320,7 @@ ${String(stderr).slice(0, 2e3)}`
|
|
|
113951
114320
|
if (r === "bound") return this.agentWorking(d2);
|
|
113952
114321
|
if (this.delegations.get(key) === d2) this.delegations.delete(key);
|
|
113953
114322
|
if (!r.ok) return { ok: false, error: `"${name()}" [${d2.sessionId}] did not finish: ${r.error}. Tell the person, or give it a clearer task.` };
|
|
113954
|
-
const answer = finalAnswer(this.store.loadMessages(d2.sessionId, 50));
|
|
114323
|
+
const answer = r.answer ?? finalAnswer(this.store.loadMessages(d2.sessionId, 50));
|
|
113955
114324
|
const { granted, outside } = readableGrants(pathsIn(answer), d2.target.path);
|
|
113956
114325
|
if (granted.length) {
|
|
113957
114326
|
const set3 = this.readGrants.get(d2.callerId) ?? /* @__PURE__ */ new Set();
|
|
@@ -113978,7 +114347,7 @@ ${String(stderr).slice(0, 2e3)}`
|
|
|
113978
114347
|
if (signal.aborted) return resolve11(null);
|
|
113979
114348
|
const app2 = { appId: q2.app.appId, projectId: q2.app.projectId, name: q2.appName };
|
|
113980
114349
|
if (q2.origin.kind === "session" && this.meta.has(q2.origin.sessionId)) {
|
|
113981
|
-
const requestId = `cap-${
|
|
114350
|
+
const requestId = `cap-${randomUUID9()}`;
|
|
113982
114351
|
const ask = {
|
|
113983
114352
|
requestId,
|
|
113984
114353
|
sessionId: q2.origin.sessionId,
|
|
@@ -113997,7 +114366,7 @@ ${String(stderr).slice(0, 2e3)}`
|
|
|
113997
114366
|
return;
|
|
113998
114367
|
}
|
|
113999
114368
|
const origin = q2.origin.kind === "view" ? q2.origin.app : q2.app;
|
|
114000
|
-
const id2 = `q-${
|
|
114369
|
+
const id2 = `q-${randomUUID9()}`;
|
|
114001
114370
|
this.appQuestions.set(id2, {
|
|
114002
114371
|
question: { id: id2, app: app2, capability: q2.capability, text: q2.text, origin: { appId: origin.appId, projectId: origin.projectId }, askedAt: q2.askedAt, expiresAt: q2.expiresAt },
|
|
114003
114372
|
resolve: resolve11
|
|
@@ -115032,13 +115401,24 @@ async function stopStrays(pids, roots, selfPid = process.pid, held3 = []) {
|
|
|
115032
115401
|
}
|
|
115033
115402
|
|
|
115034
115403
|
// packages/agent-host/src/app-home-view.ts
|
|
115035
|
-
async function openHomeView(apps, views2, ref, holder) {
|
|
115404
|
+
async function openHomeView(apps, views2, ref, holder, openKey) {
|
|
115405
|
+
const prior = openKey ? views2.homeOpen(ref, openKey, holder) : null;
|
|
115406
|
+
if (prior) return prior;
|
|
115036
115407
|
const home = await apps.homeView(ref);
|
|
115408
|
+
const concurrent = openKey ? views2.homeOpen(ref, openKey, holder) : null;
|
|
115409
|
+
if (concurrent) return concurrent;
|
|
115037
115410
|
const toolInput = {};
|
|
115038
|
-
const
|
|
115039
|
-
|
|
115040
|
-
|
|
115041
|
-
|
|
115411
|
+
const invoke = async () => {
|
|
115412
|
+
const out = await apps.call(ref, home.tool, toolInput, { kind: "view" });
|
|
115413
|
+
if (!out.result) throw Object.assign(new Error(out.error ?? `The home tool "${home.tool}" did not answer`), { code: "internal" });
|
|
115414
|
+
return { tool: home.tool, resourceUri: home.resourceUri, toolInput, toolResult: out.result, runId: out.runId };
|
|
115415
|
+
};
|
|
115416
|
+
if (openKey) {
|
|
115417
|
+
const { instanceId } = views2.open(ref, home.resourceUri, holder);
|
|
115418
|
+
return views2.rememberHome(instanceId, openKey, async () => ({ instanceId, ...await invoke() }));
|
|
115419
|
+
}
|
|
115420
|
+
const outcome = await invoke();
|
|
115421
|
+
return { ...views2.open(ref, home.resourceUri, holder), ...outcome };
|
|
115042
115422
|
}
|
|
115043
115423
|
|
|
115044
115424
|
// packages/agent-host/src/builder-requests.ts
|
|
@@ -115351,8 +115731,14 @@ function createRpcHandler(mgr2, adapters2, { terminals: terminals2, updates: upd
|
|
|
115351
115731
|
return { appId: d2.app.appId, projectId: d2.app.projectId, uri: d2.uri, origin: d2.origin, resource: d2.resource };
|
|
115352
115732
|
},
|
|
115353
115733
|
"apps.openView": async (p, ctx) => {
|
|
115354
|
-
const { appId, projectId, lease } = RpcMethods["apps.openView"].params.parse(p);
|
|
115355
|
-
return openHomeView(requireExternalApps(), requireViews(), { appId, projectId }, lease && ctx ? ctx.client : void 0);
|
|
115734
|
+
const { appId, projectId, lease, openKey } = RpcMethods["apps.openView"].params.parse(p);
|
|
115735
|
+
return openHomeView(requireExternalApps(), requireViews(), { appId, projectId }, lease && ctx ? ctx.client : void 0, openKey);
|
|
115736
|
+
},
|
|
115737
|
+
"apps.recoverView": async (p, ctx) => {
|
|
115738
|
+
const { appId, projectId, openKey, lease } = RpcMethods["apps.recoverView"].params.parse(p);
|
|
115739
|
+
const prior = requireViews().homeOpen({ appId, projectId }, openKey, lease && ctx ? ctx.client : void 0);
|
|
115740
|
+
if (prior) return prior;
|
|
115741
|
+
throw new Error("The original app open could not be recovered \u2014 the home tool may or may not have run; check before opening again");
|
|
115356
115742
|
},
|
|
115357
115743
|
"apps.holdViews": async (p, ctx) => {
|
|
115358
115744
|
const { instanceIds } = RpcMethods["apps.holdViews"].params.parse(p);
|
|
@@ -120129,6 +120515,7 @@ var ROUTES = {
|
|
|
120129
120515
|
* link, not the window, so its views are opened without one and live until closed, as before
|
|
120130
120516
|
*/
|
|
120131
120517
|
"apps.openView": { kind: "project", orMachine: true, result: (r, q2) => q2.openedView(r), params: ({ lease: _lease, ...p }) => p },
|
|
120518
|
+
"apps.recoverView": { kind: "project", orMachine: true, result: (r, q2) => q2.openedView(r), params: ({ lease: _lease, ...p }) => p },
|
|
120132
120519
|
"apps.readResource": view(["projectId", "instanceId"]),
|
|
120133
120520
|
"apps.invoke": view(["projectId", "instanceId"]),
|
|
120134
120521
|
"apps.inlineReopen": session((r, q2) => q2.inlineView(r)),
|
|
@@ -120488,7 +120875,7 @@ function storeMirror(store2) {
|
|
|
120488
120875
|
var start = new StartSequence();
|
|
120489
120876
|
start.at("launch variables");
|
|
120490
120877
|
var launch = takeLaunchEnv();
|
|
120491
|
-
var BUILD = true ? "
|
|
120878
|
+
var BUILD = true ? "c6afa2e" : "dev";
|
|
120492
120879
|
var { values } = parseArgs({
|
|
120493
120880
|
options: {
|
|
120494
120881
|
port: { type: "string", default: "5175" },
|
package/package.json
CHANGED