@lamplitisles/codex-for-love 0.4.3 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/vendor/build/_app/immutable/assets/0.DbZI1-K0.css +2 -0
- package/vendor/build/_app/immutable/chunks/{CtNIUOtb.js → BZwtE-rc.js} +3 -3
- package/vendor/build/_app/immutable/chunks/C2DTc7yw.js +3 -0
- package/vendor/build/_app/immutable/chunks/CyqZh0gB.js +1 -0
- package/vendor/build/_app/immutable/chunks/MojBje2g.js +1 -0
- package/vendor/build/_app/immutable/entry/app.zliTtOtE.js +2 -0
- package/vendor/build/_app/immutable/entry/start.D7gYhQNp.js +1 -0
- package/vendor/build/_app/immutable/nodes/0.DreEev9t.js +1 -0
- package/vendor/build/_app/immutable/nodes/1.Bd5XGanj.js +1 -0
- package/vendor/build/_app/immutable/nodes/2.GSHICjNc.js +330 -0
- package/vendor/build/_app/version.json +1 -1
- package/vendor/build/index.html +10 -9
- package/vendor/runtime/cli.mjs +277 -70
- package/vendor/runtime/companion-mcp.mjs +91 -3
- package/vendor/build/_app/immutable/assets/0.DfSR8oPO.css +0 -2
- package/vendor/build/_app/immutable/chunks/CTZw3jzS.js +0 -3
- package/vendor/build/_app/immutable/chunks/tYXWoiZl.js +0 -1
- package/vendor/build/_app/immutable/entry/app.C3QvzSBj.js +0 -2
- package/vendor/build/_app/immutable/entry/start.DN_PDYWk.js +0 -1
- package/vendor/build/_app/immutable/nodes/0.PSG_XCTv.js +0 -1
- package/vendor/build/_app/immutable/nodes/1.YrgYwNeI.js +0 -1
- package/vendor/build/_app/immutable/nodes/2.C7zq-Uwo.js +0 -327
package/vendor/runtime/cli.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
|
3
3
|
import { access, copyFile, lstat, mkdir, readFile, readdir, realpath, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
4
4
|
import { basename, dirname, extname, isAbsolute, join, normalize, relative, resolve, sep } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { isIP } from "node:net";
|
|
6
7
|
import { zstdDecompressSync } from "node:zlib";
|
|
7
8
|
import { spawn } from "child_process";
|
|
8
9
|
import { once } from "events";
|
|
@@ -12,7 +13,7 @@ import { delimiter, dirname as dirname$1, isAbsolute as isAbsolute$1, join as jo
|
|
|
12
13
|
import { randomUUID as randomUUID$1 } from "crypto";
|
|
13
14
|
import { createInterface } from "readline";
|
|
14
15
|
import { homedir } from "os";
|
|
15
|
-
import { createConnection, isIP } from "net";
|
|
16
|
+
import { createConnection, isIP as isIP$1 } from "net";
|
|
16
17
|
import { DatabaseSync } from "node:sqlite";
|
|
17
18
|
import { execFile } from "node:child_process";
|
|
18
19
|
import { promisify } from "node:util";
|
|
@@ -6196,6 +6197,7 @@ const schema = object({
|
|
|
6196
6197
|
user: string().min(1)
|
|
6197
6198
|
}).strict().optional(),
|
|
6198
6199
|
port: number$1().int().min(1024).max(65535).default(3082),
|
|
6200
|
+
listen_host: string().refine((value) => isIP(value) === 4, "Listen host must be an IPv4 address").default("127.0.0.1"),
|
|
6199
6201
|
codex: object({
|
|
6200
6202
|
command: string().min(1).default("codex-app-server"),
|
|
6201
6203
|
model: string().min(1).default(DEFAULT_CODEX_MODEL),
|
|
@@ -61937,7 +61939,7 @@ function validateBearerToken(value) {
|
|
|
61937
61939
|
function isLoopbackHostname(hostname) {
|
|
61938
61940
|
const normalized = hostname.replace(/^\[|\]$/g, "").replace(/\.$/, "").toLowerCase();
|
|
61939
61941
|
if (normalized === "localhost") return true;
|
|
61940
|
-
const family = isIP(normalized);
|
|
61942
|
+
const family = isIP$1(normalized);
|
|
61941
61943
|
if (family === 4) return normalized.startsWith("127.");
|
|
61942
61944
|
if (family === 6) return normalized === "::1" || normalized.startsWith("::ffff:127.");
|
|
61943
61945
|
return false;
|
|
@@ -62597,7 +62599,8 @@ function partnerPaths(workspace) {
|
|
|
62597
62599
|
database: join(managedRoot, "session.sqlite"),
|
|
62598
62600
|
relationshipJournal: join(managedRoot, "relationship.jsonl"),
|
|
62599
62601
|
attachments: join(managedRoot, "attachments"),
|
|
62600
|
-
audio: join(managedRoot, "audio")
|
|
62602
|
+
audio: join(managedRoot, "audio"),
|
|
62603
|
+
conversationImages: join(managedRoot, "conversation-images.json")
|
|
62601
62604
|
};
|
|
62602
62605
|
}
|
|
62603
62606
|
//#endregion
|
|
@@ -63237,6 +63240,21 @@ var Store = class {
|
|
|
63237
63240
|
};
|
|
63238
63241
|
}));
|
|
63239
63242
|
}
|
|
63243
|
+
async messageMeta(id) {
|
|
63244
|
+
return this.transaction(() => {
|
|
63245
|
+
const row = this.db.prepare(`
|
|
63246
|
+
SELECT m.sequence,m.id,m.created,r.revision
|
|
63247
|
+
FROM message_meta m JOIN message_revisions r ON r.message_id=m.id
|
|
63248
|
+
WHERE m.id=?
|
|
63249
|
+
`).get(id);
|
|
63250
|
+
return row ? {
|
|
63251
|
+
id: row.id,
|
|
63252
|
+
created: Number(row.created),
|
|
63253
|
+
sequence: Number(row.sequence),
|
|
63254
|
+
revision: Number(row.revision)
|
|
63255
|
+
} : void 0;
|
|
63256
|
+
});
|
|
63257
|
+
}
|
|
63240
63258
|
async pendingMessages() {
|
|
63241
63259
|
return this.transaction(() => this.db.prepare(`
|
|
63242
63260
|
SELECT m.sequence,m.id,m.created,r.revision,p.input
|
|
@@ -64439,6 +64457,7 @@ function buildRollout(extraction, media, threadId, destination, persona, relatio
|
|
|
64439
64457
|
lastByTurn.set(record.turn, index);
|
|
64440
64458
|
}
|
|
64441
64459
|
});
|
|
64460
|
+
const startedAtByTurn = new Map([...firstByTurn].map(([turn, index]) => [turn, Math.floor(extraction.records[index].time / 1e3)]));
|
|
64442
64461
|
for (const [index, record] of extraction.records.entries()) {
|
|
64443
64462
|
const timestamp = new Date(record.time).toISOString();
|
|
64444
64463
|
if (record.type === "compact") {
|
|
@@ -64477,6 +64496,7 @@ function buildRollout(extraction, media, threadId, destination, persona, relatio
|
|
|
64477
64496
|
type: "task_complete",
|
|
64478
64497
|
turn_id: turnId,
|
|
64479
64498
|
last_agent_message: null,
|
|
64499
|
+
started_at: Math.floor(record.time / 1e3),
|
|
64480
64500
|
completed_at: Math.floor(record.time / 1e3)
|
|
64481
64501
|
}, timestamp);
|
|
64482
64502
|
boundaries.push({
|
|
@@ -64489,10 +64509,11 @@ function buildRollout(extraction, media, threadId, destination, persona, relatio
|
|
|
64489
64509
|
continue;
|
|
64490
64510
|
}
|
|
64491
64511
|
const turnId = `turn-${record.turn}`;
|
|
64512
|
+
const startedAt = startedAtByTurn.get(record.turn);
|
|
64492
64513
|
if (firstByTurn.get(record.turn) === index) add("event_msg", {
|
|
64493
64514
|
type: "task_started",
|
|
64494
64515
|
turn_id: turnId,
|
|
64495
|
-
started_at:
|
|
64516
|
+
started_at: startedAt,
|
|
64496
64517
|
model_context_window: null,
|
|
64497
64518
|
collaboration_mode_kind: "default"
|
|
64498
64519
|
}, timestamp);
|
|
@@ -64548,7 +64569,7 @@ function buildRollout(extraction, media, threadId, destination, persona, relatio
|
|
|
64548
64569
|
type: "task_complete",
|
|
64549
64570
|
turn_id: turnId,
|
|
64550
64571
|
last_agent_message: record.type === "assistant" ? record.text : null,
|
|
64551
|
-
started_at:
|
|
64572
|
+
started_at: startedAt,
|
|
64552
64573
|
completed_at: Math.floor(record.time / 1e3),
|
|
64553
64574
|
duration_ms: null
|
|
64554
64575
|
}, timestamp);
|
|
@@ -65478,17 +65499,92 @@ async function readClip(root, name, frameCount) {
|
|
|
65478
65499
|
return;
|
|
65479
65500
|
}
|
|
65480
65501
|
}
|
|
65502
|
+
//#endregion
|
|
65503
|
+
//#region apps/partner/runtime/conversation-images.ts
|
|
65504
|
+
const CURSOR_BYTES = 40;
|
|
65505
|
+
const CURSOR_TOKEN = /^[A-Za-z0-9_-]{54}$/u;
|
|
65506
|
+
const IMAGE_ID = /^[a-f0-9]{64}$/u;
|
|
65507
|
+
function cursorFor(image) {
|
|
65508
|
+
if (!Number.isSafeInteger(image.created) || image.created < 0 || !IMAGE_ID.test(image.id)) throw new Error("Invalid conversation image cursor data");
|
|
65509
|
+
const value = Buffer.alloc(CURSOR_BYTES);
|
|
65510
|
+
value.writeBigUInt64BE(BigInt(image.created));
|
|
65511
|
+
Buffer.from(image.id, "hex").copy(value, 8);
|
|
65512
|
+
return value.toString("base64url");
|
|
65513
|
+
}
|
|
65514
|
+
function readCursor(cursor) {
|
|
65515
|
+
if (!cursor) return void 0;
|
|
65516
|
+
if (!CURSOR_TOKEN.test(cursor)) return void 0;
|
|
65517
|
+
try {
|
|
65518
|
+
const value = Buffer.from(cursor, "base64url");
|
|
65519
|
+
if (value.length !== CURSOR_BYTES) return void 0;
|
|
65520
|
+
const created = Number(value.readBigUInt64BE());
|
|
65521
|
+
if (!Number.isSafeInteger(created)) return void 0;
|
|
65522
|
+
return [created, value.subarray(8).toString("hex")];
|
|
65523
|
+
} catch {
|
|
65524
|
+
return;
|
|
65525
|
+
}
|
|
65526
|
+
}
|
|
65527
|
+
function conversationImageId(origin, source) {
|
|
65528
|
+
return createHash("sha256").update(`${origin}:${source}`).digest("hex");
|
|
65529
|
+
}
|
|
65530
|
+
function pageConversationImages(images, limit = 5, cursor) {
|
|
65531
|
+
const after = readCursor(cursor);
|
|
65532
|
+
if (cursor && !after) throw new Error("Invalid conversation image cursor");
|
|
65533
|
+
const ordered = images.filter((image) => !after || image.created < after[0] || image.created === after[0] && image.id < after[1]).sort((a, b) => b.created - a.created || b.id.localeCompare(a.id));
|
|
65534
|
+
const page = ordered.slice(0, limit);
|
|
65535
|
+
return {
|
|
65536
|
+
images: page,
|
|
65537
|
+
...ordered.length > page.length ? { nextCursor: cursorFor(page.at(-1)) } : {}
|
|
65538
|
+
};
|
|
65539
|
+
}
|
|
65540
|
+
async function readConversationImages(path) {
|
|
65541
|
+
try {
|
|
65542
|
+
const value = JSON.parse(await readFile(path, "utf8"));
|
|
65543
|
+
return Array.isArray(value) ? value : [];
|
|
65544
|
+
} catch (error) {
|
|
65545
|
+
if (error.code === "ENOENT") return [];
|
|
65546
|
+
throw error;
|
|
65547
|
+
}
|
|
65548
|
+
}
|
|
65549
|
+
async function withAvailability(images) {
|
|
65550
|
+
return Promise.all(images.map(async (image) => {
|
|
65551
|
+
try {
|
|
65552
|
+
await access(image.path);
|
|
65553
|
+
return {
|
|
65554
|
+
...image,
|
|
65555
|
+
available: true
|
|
65556
|
+
};
|
|
65557
|
+
} catch {
|
|
65558
|
+
return {
|
|
65559
|
+
...image,
|
|
65560
|
+
available: false
|
|
65561
|
+
};
|
|
65562
|
+
}
|
|
65563
|
+
}));
|
|
65564
|
+
}
|
|
65565
|
+
/** Atomic metadata projection shared by the HTTP host and the standalone MCP. */
|
|
65566
|
+
async function writeConversationImages(path, images) {
|
|
65567
|
+
await mkdir(dirname(path), {
|
|
65568
|
+
recursive: true,
|
|
65569
|
+
mode: 448
|
|
65570
|
+
});
|
|
65571
|
+
const target = resolve(path);
|
|
65572
|
+
const temporary = `${target}.${process.pid}.${randomUUID()}.tmp`;
|
|
65573
|
+
await writeFile(temporary, `${JSON.stringify(images)}\n`, { mode: 384 });
|
|
65574
|
+
await rename(temporary, target);
|
|
65575
|
+
}
|
|
65481
65576
|
const HISTORY_PAGE_MESSAGES = 50;
|
|
65577
|
+
const HISTORICAL_FALLBACK_START = new Date(2026, 8, 1).getTime();
|
|
65482
65578
|
/** Match DSH's 50-message history window against CFL's rendered projection. */
|
|
65483
65579
|
function visibleHistoryPage(messages, results, before) {
|
|
65484
65580
|
const candidates = messages.filter((message) => before === void 0 || message.sequence < before);
|
|
65485
|
-
const
|
|
65581
|
+
const resultBySource = new Map(results.map((result) => [result.sourceIds.at(-1), result]));
|
|
65486
65582
|
const selected = [];
|
|
65487
65583
|
let visible = 0;
|
|
65488
65584
|
let index = candidates.length - 1;
|
|
65489
65585
|
for (; index >= 0; index -= 1) {
|
|
65490
65586
|
const message = candidates[index];
|
|
65491
|
-
const result =
|
|
65587
|
+
const result = resultBySource.get(message.id);
|
|
65492
65588
|
const units = 1 + (result ? result.answers.length + result.generatedIds.length + result.voiceIds.length + (result.error || [
|
|
65493
65589
|
"failed",
|
|
65494
65590
|
"interrupted",
|
|
@@ -65531,11 +65627,17 @@ function turnStatus(turn) {
|
|
|
65531
65627
|
function isActiveTurn(turn) {
|
|
65532
65628
|
return turnStatus(turn) === "inProgress" || turnStatus(turn) === "in_progress" || turnStatus(turn) === "active";
|
|
65533
65629
|
}
|
|
65534
|
-
function
|
|
65630
|
+
function recordedTurnTime(value) {
|
|
65535
65631
|
const number = numberOrNull(value);
|
|
65536
|
-
if (number === null) return
|
|
65632
|
+
if (number === null) return void 0;
|
|
65537
65633
|
return number > 1e10 ? Math.round(number) : Math.round(number * 1e3);
|
|
65538
65634
|
}
|
|
65635
|
+
function turnTime(value, fallback) {
|
|
65636
|
+
return recordedTurnTime(value) ?? fallback;
|
|
65637
|
+
}
|
|
65638
|
+
function historicalFallbackTime(sequence) {
|
|
65639
|
+
return HISTORICAL_FALLBACK_START + Math.max(0, sequence - 1) * 1e3;
|
|
65640
|
+
}
|
|
65539
65641
|
function completedTurnTime(value) {
|
|
65540
65642
|
const raw = numberOrNull(value);
|
|
65541
65643
|
if (raw === null) return void 0;
|
|
@@ -65546,7 +65648,7 @@ function inputTimeContext(source, now) {
|
|
|
65546
65648
|
const time = `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;
|
|
65547
65649
|
return {
|
|
65548
65650
|
kind: "application",
|
|
65549
|
-
value: `${source === "
|
|
65651
|
+
value: `${source === "human" ? "Current human input" : "Qualifying Keet input"} received around local ${time}. Trusted delivery metadata; not user-authored text or an instruction.`
|
|
65550
65652
|
};
|
|
65551
65653
|
}
|
|
65552
65654
|
function textFromUserItem(item) {
|
|
@@ -65654,6 +65756,9 @@ function historyImage(path, operationId, index) {
|
|
|
65654
65756
|
function noHistoryYet(error) {
|
|
65655
65757
|
return error instanceof AppServerInvalidRequestError && error.rpcMessage.includes("thread/turns/list is unavailable before first user message");
|
|
65656
65758
|
}
|
|
65759
|
+
function noRolloutForThread(error) {
|
|
65760
|
+
return error instanceof AppServerInvalidRequestError && error.rpcMessage.startsWith("no rollout found for thread id ");
|
|
65761
|
+
}
|
|
65657
65762
|
function rpcMessage(error) {
|
|
65658
65763
|
return error instanceof AppServerInvalidRequestError ? error.rpcMessage : void 0;
|
|
65659
65764
|
}
|
|
@@ -65770,10 +65875,33 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
65770
65875
|
recursive: true,
|
|
65771
65876
|
mode: 448
|
|
65772
65877
|
});
|
|
65878
|
+
const catalogue = new Map((await readConversationImages(paths.conversationImages)).map((image) => [image.id, image]));
|
|
65879
|
+
let reconcilingCatalogue = false;
|
|
65880
|
+
let reconciledCatalogueIds = /* @__PURE__ */ new Set();
|
|
65881
|
+
async function catalogueImage(origin, image, created) {
|
|
65882
|
+
const id = conversationImageId(origin, `${image.operation_id}:${image.id}`);
|
|
65883
|
+
const previous = catalogue.get(id);
|
|
65884
|
+
catalogue.set(id, {
|
|
65885
|
+
id,
|
|
65886
|
+
filename: image.name,
|
|
65887
|
+
path: resolve(image.path),
|
|
65888
|
+
mediaType: image.media_type,
|
|
65889
|
+
created: reconcilingCatalogue ? created : previous?.created ?? created,
|
|
65890
|
+
origin,
|
|
65891
|
+
available: true
|
|
65892
|
+
});
|
|
65893
|
+
reconciledCatalogueIds.add(id);
|
|
65894
|
+
if (!reconcilingCatalogue) await writeConversationImages(paths.conversationImages, await withAvailability([...catalogue.values()]));
|
|
65895
|
+
}
|
|
65773
65896
|
const store = new Store(paths.database);
|
|
65774
65897
|
const now = dependencies.now ?? Date.now;
|
|
65775
65898
|
const listeners = /* @__PURE__ */ new Set();
|
|
65776
|
-
const
|
|
65899
|
+
const storedMessages = await store.allMessages();
|
|
65900
|
+
const messageIds = new Set(storedMessages.map((message) => message.id));
|
|
65901
|
+
const messagesById = new Map(storedMessages.map((message) => [message.id, {
|
|
65902
|
+
created: message.created,
|
|
65903
|
+
sequence: message.sequence
|
|
65904
|
+
}]));
|
|
65777
65905
|
const turns = /* @__PURE__ */ new Map();
|
|
65778
65906
|
/** One turn owns an ordered list of application input identities. */
|
|
65779
65907
|
const turnInputs = /* @__PURE__ */ new Map();
|
|
@@ -65801,7 +65929,6 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
65801
65929
|
let unsubscribeAppServer = [];
|
|
65802
65930
|
let startupPending = false;
|
|
65803
65931
|
let compacting = false;
|
|
65804
|
-
let compactWaiter;
|
|
65805
65932
|
let eventChain = Promise.resolve();
|
|
65806
65933
|
let admission = Promise.resolve();
|
|
65807
65934
|
let closePromise;
|
|
@@ -65971,13 +66098,13 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
65971
66098
|
return status === "failed" ? "Native image generation failed" : "Native image artifact unavailable";
|
|
65972
66099
|
}
|
|
65973
66100
|
async function markAttachmentError(sourceIds, error) {
|
|
65974
|
-
const
|
|
65975
|
-
if (!
|
|
65976
|
-
const previous = await store.outcome(
|
|
66101
|
+
const sourceId = sourceIds.at(-1);
|
|
66102
|
+
if (!sourceId) return;
|
|
66103
|
+
const previous = await store.outcome(sourceId);
|
|
65977
66104
|
if (previous?.status === "replaced") return;
|
|
65978
|
-
if (previous?.status !== "attachment-error" || previous.error !== error) await store.markOutcome(
|
|
66105
|
+
if (previous?.status !== "attachment-error" || previous.error !== error) await store.markOutcome(sourceId, "attachment-error", error);
|
|
65979
66106
|
}
|
|
65980
|
-
async function projectGeneratedImage(operationId, sourceIds, item) {
|
|
66107
|
+
async function projectGeneratedImage(operationId, sourceIds, item, created) {
|
|
65981
66108
|
if (item.type !== "imageGeneration") return void 0;
|
|
65982
66109
|
if (item.status === "failed") {
|
|
65983
66110
|
processError("image.generation_failed", new Error(imageFailure("failed")), {
|
|
@@ -65992,6 +66119,7 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
65992
66119
|
const existing = await store.generatedImageForItem(operationId, itemId);
|
|
65993
66120
|
if (existing) try {
|
|
65994
66121
|
await access(existing.path);
|
|
66122
|
+
await catalogueImage("agent", existing, created);
|
|
65995
66123
|
return existing.id;
|
|
65996
66124
|
} catch {
|
|
65997
66125
|
storageError = true;
|
|
@@ -66014,6 +66142,7 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66014
66142
|
return;
|
|
66015
66143
|
}
|
|
66016
66144
|
await store.saveGeneratedImage(generated, itemId);
|
|
66145
|
+
await catalogueImage("agent", generated, created);
|
|
66017
66146
|
return generated.id;
|
|
66018
66147
|
} catch (error) {
|
|
66019
66148
|
storageError = true;
|
|
@@ -66100,9 +66229,20 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66100
66229
|
text_offset: 0,
|
|
66101
66230
|
text_length: source.input.length
|
|
66102
66231
|
};
|
|
66103
|
-
|
|
66104
|
-
|
|
66232
|
+
let message = messagesById.get(sourceId);
|
|
66233
|
+
if (!message) {
|
|
66234
|
+
const created = turnTime(turn.startedAt, now());
|
|
66235
|
+
const stored = await store.ensureMessage(sourceId, created, source.input, source.images);
|
|
66105
66236
|
messageIds.add(sourceId);
|
|
66237
|
+
message = {
|
|
66238
|
+
created: stored.created,
|
|
66239
|
+
sequence: stored.sequence
|
|
66240
|
+
};
|
|
66241
|
+
messagesById.set(sourceId, message);
|
|
66242
|
+
}
|
|
66243
|
+
if (!sourceId.startsWith("keet:")) for (const image of source.images) {
|
|
66244
|
+
const origin = isUnder(join(paths.managedRoot, "historical-media"), image.path) ? "historical" : "human";
|
|
66245
|
+
await catalogueImage(origin, image, origin === "historical" ? recordedTurnTime(turn.startedAt) ?? historicalFallbackTime(message.sequence) : message.created);
|
|
66106
66246
|
}
|
|
66107
66247
|
await store.markObserved(sourceId, segment);
|
|
66108
66248
|
}
|
|
@@ -66116,7 +66256,7 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66116
66256
|
const completedAt = status === "completed" ? completedTurnTime(turn.completedAt) : void 0;
|
|
66117
66257
|
for (const item of turn.items ?? []) {
|
|
66118
66258
|
if (item.type !== "imageGeneration") continue;
|
|
66119
|
-
const generatedId = await projectGeneratedImage(resultOperationId(turn.id), sourceIds, item);
|
|
66259
|
+
const generatedId = await projectGeneratedImage(resultOperationId(turn.id), sourceIds, item, completedTurnTime(turn.completedAt) ?? turnTime(turn.startedAt, now()));
|
|
66120
66260
|
if (generatedId) generatedIds.push(generatedId);
|
|
66121
66261
|
else if (item.status === "failed") {
|
|
66122
66262
|
resultStatus = "failed";
|
|
@@ -66146,15 +66286,23 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66146
66286
|
}
|
|
66147
66287
|
/** Full history is hydrated once at startup/resume; events update one turn thereafter. */
|
|
66148
66288
|
async function hydrateHistory() {
|
|
66289
|
+
reconcilingCatalogue = true;
|
|
66290
|
+
reconciledCatalogueIds = /* @__PURE__ */ new Set();
|
|
66149
66291
|
const nextTurns = await listTurns();
|
|
66150
66292
|
turns.clear();
|
|
66151
66293
|
turnInputs.clear();
|
|
66152
66294
|
turnResults.clear();
|
|
66153
66295
|
presentationFingerprints.clear();
|
|
66154
|
-
|
|
66155
|
-
|
|
66156
|
-
|
|
66157
|
-
|
|
66296
|
+
try {
|
|
66297
|
+
for (const turn of nextTurns) {
|
|
66298
|
+
registerTurn(turn);
|
|
66299
|
+
const complete = await reconcileTurn(turn.id, turn);
|
|
66300
|
+
if (complete) await projectTurn(complete, false);
|
|
66301
|
+
}
|
|
66302
|
+
} finally {
|
|
66303
|
+
reconcilingCatalogue = false;
|
|
66304
|
+
for (const id of catalogue.keys()) if (!reconciledCatalogueIds.has(id)) catalogue.delete(id);
|
|
66305
|
+
await writeConversationImages(paths.conversationImages, await withAvailability([...catalogue.values()]));
|
|
66158
66306
|
}
|
|
66159
66307
|
const unresolved = await unresolvedMessages();
|
|
66160
66308
|
for (const message of unresolved) unresolvedInputIds.add(message.id);
|
|
@@ -66307,7 +66455,7 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66307
66455
|
}
|
|
66308
66456
|
}
|
|
66309
66457
|
function drainRejectedSteers() {
|
|
66310
|
-
if (closing || activeTurnId || recoveryPromise || !rejectedSteers.length) return recoveryPromise;
|
|
66458
|
+
if (closing || compacting || activeTurnId || recoveryPromise || !rejectedSteers.length) return recoveryPromise;
|
|
66311
66459
|
const intents = rejectedSteers.splice(0);
|
|
66312
66460
|
const ids = intents.flatMap((intent) => intent.ids);
|
|
66313
66461
|
const merged = {
|
|
@@ -66315,7 +66463,7 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66315
66463
|
input: intents.map((intent) => intent.input).join("\n"),
|
|
66316
66464
|
images: intents.flatMap((intent) => intent.images),
|
|
66317
66465
|
transportId: clientIdForInputs(ids),
|
|
66318
|
-
source: intents.every((intent) => intent.source === "
|
|
66466
|
+
source: intents.every((intent) => intent.source === "human") ? "human" : "none"
|
|
66319
66467
|
};
|
|
66320
66468
|
recoveryPromise = (async () => {
|
|
66321
66469
|
try {
|
|
@@ -66331,7 +66479,7 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66331
66479
|
}
|
|
66332
66480
|
function drainKeet() {
|
|
66333
66481
|
admission = admission.then(async () => {
|
|
66334
|
-
if (closing || activeTurnId || recoveryPromise || !keetEnabled) return;
|
|
66482
|
+
if (closing || compacting || activeTurnId || recoveryPromise || !keetEnabled) return;
|
|
66335
66483
|
const pending = (await store.pendingMessages()).filter((item) => item.id.startsWith("keet:") && !observedInputIds.has(item.id));
|
|
66336
66484
|
if (!pending.length || activeTurnId || closing) return;
|
|
66337
66485
|
const first = pending[0];
|
|
@@ -66433,11 +66581,11 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66433
66581
|
scheduleKeetReconnect();
|
|
66434
66582
|
});
|
|
66435
66583
|
}
|
|
66436
|
-
async function restoreUnconsumed(turnId) {
|
|
66584
|
+
async function restoreUnconsumed(turnId, preserveQueued = false) {
|
|
66437
66585
|
const ids = turnId ? [...sourceIdsForTurn(turnId)] : [];
|
|
66438
66586
|
const selectedSteers = pendingSteers.filter((intent) => turnId === void 0 || intent.turnId === turnId);
|
|
66439
66587
|
const selectedStarts = [...pendingStarts.values()].filter((intent) => turnId === void 0 || intent.turnId === turnId);
|
|
66440
|
-
const selectedRejected = rejectedSteers.filter((intent) => turnId === void 0 || intent.turnId === turnId);
|
|
66588
|
+
const selectedRejected = preserveQueued ? [] : rejectedSteers.filter((intent) => turnId === void 0 || intent.turnId === turnId);
|
|
66441
66589
|
for (const intent of selectedSteers) ids.push(...intent.ids);
|
|
66442
66590
|
for (const intent of selectedStarts) ids.push(...intent.ids);
|
|
66443
66591
|
for (const intent of selectedRejected) ids.push(...intent.ids);
|
|
@@ -66505,18 +66653,14 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66505
66653
|
endedAt: boundary.time
|
|
66506
66654
|
};
|
|
66507
66655
|
await refreshBootstrap();
|
|
66508
|
-
compactWaiter?.resolve();
|
|
66509
|
-
compactWaiter = void 0;
|
|
66510
66656
|
}
|
|
66511
|
-
function failCompaction(
|
|
66657
|
+
function failCompaction() {
|
|
66512
66658
|
if (lifecycle?.status === "running") lifecycle = {
|
|
66513
66659
|
...lifecycle,
|
|
66514
66660
|
status: "failed",
|
|
66515
66661
|
endSeq: eventSequence,
|
|
66516
66662
|
endedAt: now()
|
|
66517
66663
|
};
|
|
66518
|
-
compactWaiter?.reject(error instanceof Error ? error : new Error(String(error)));
|
|
66519
|
-
compactWaiter = void 0;
|
|
66520
66664
|
}
|
|
66521
66665
|
async function handleServerEvent(notification) {
|
|
66522
66666
|
if (closing) return;
|
|
@@ -66598,11 +66742,14 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66598
66742
|
turnControllers.delete(turn.id);
|
|
66599
66743
|
}
|
|
66600
66744
|
if (reconciled && (turnStatus(reconciled) === "interrupted" || turnStatus(reconciled) === "cancelled")) await restoreUnconsumed(completedTurnId);
|
|
66601
|
-
else if (reconciled && !userItems(reconciled).length && completedTurnId) await restoreUnconsumed(completedTurnId);
|
|
66745
|
+
else if (reconciled && !userItems(reconciled).length && completedTurnId) await restoreUnconsumed(completedTurnId, turnStatus(reconciled) === "completed");
|
|
66602
66746
|
syncActiveTurn();
|
|
66603
66747
|
if (compacting) {
|
|
66604
66748
|
compacting = false;
|
|
66605
|
-
if (lifecycle?.status === "running")
|
|
66749
|
+
if (lifecycle?.status === "running") {
|
|
66750
|
+
if (turnStatus(reconciled) === "completed") await finishCompaction(reconciled?.id);
|
|
66751
|
+
else failCompaction();
|
|
66752
|
+
}
|
|
66606
66753
|
}
|
|
66607
66754
|
await refreshBootstrap();
|
|
66608
66755
|
if (!activeTurnId) {
|
|
@@ -66626,6 +66773,9 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66626
66773
|
if (notification.method === "turn/started") {
|
|
66627
66774
|
const turn = registerTurn(record(notification.params).turn);
|
|
66628
66775
|
if (turn) {
|
|
66776
|
+
if (compacting) {
|
|
66777
|
+
for (const intent of rejectedSteers) if (!intent.turnId) intent.turnId = turn.id;
|
|
66778
|
+
}
|
|
66629
66779
|
activeTurnId = turn.id;
|
|
66630
66780
|
activeOperationId = sourceIdsForTurn(turn.id)[0] ?? activeOperationId;
|
|
66631
66781
|
if (!turnControllers.has(turn.id)) turnControllers.set(turn.id, new AbortController());
|
|
@@ -66723,29 +66873,44 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66723
66873
|
} : {}
|
|
66724
66874
|
}
|
|
66725
66875
|
};
|
|
66876
|
+
const connectedAppServer = appServer;
|
|
66877
|
+
if (!connectedAppServer) throw new Error("Codex app-server connection was not initialized");
|
|
66726
66878
|
let response;
|
|
66727
|
-
|
|
66728
|
-
|
|
66729
|
-
...shared,
|
|
66730
|
-
threadId: marker.threadId,
|
|
66731
|
-
excludeTurns: true
|
|
66732
|
-
};
|
|
66733
|
-
response = await appServer.call("thread/resume", resume);
|
|
66734
|
-
threadId = response.thread.id;
|
|
66735
|
-
} else {
|
|
66879
|
+
let resumed = false;
|
|
66880
|
+
const startThread = async () => {
|
|
66736
66881
|
const start = {
|
|
66737
66882
|
...shared,
|
|
66738
66883
|
historyMode: "paginated",
|
|
66739
66884
|
sessionStartSource: "startup"
|
|
66740
66885
|
};
|
|
66741
|
-
|
|
66742
|
-
threadId =
|
|
66886
|
+
const started = await connectedAppServer.call("thread/start", start);
|
|
66887
|
+
threadId = started.thread.id;
|
|
66743
66888
|
if (!threadId) throw new Error("Codex app-server did not return a thread id");
|
|
66744
66889
|
await writeFile(markerPath, JSON.stringify({
|
|
66745
66890
|
threadId,
|
|
66746
66891
|
model: config.codex.model
|
|
66747
66892
|
}), { mode: 384 });
|
|
66748
|
-
|
|
66893
|
+
return started;
|
|
66894
|
+
};
|
|
66895
|
+
if (marker?.threadId) {
|
|
66896
|
+
const resume = {
|
|
66897
|
+
...shared,
|
|
66898
|
+
threadId: marker.threadId,
|
|
66899
|
+
excludeTurns: true
|
|
66900
|
+
};
|
|
66901
|
+
try {
|
|
66902
|
+
response = await connectedAppServer.call("thread/resume", resume);
|
|
66903
|
+
threadId = response.thread.id;
|
|
66904
|
+
resumed = true;
|
|
66905
|
+
} catch (error) {
|
|
66906
|
+
if (!noRolloutForThread(error) || messageIds.size) throw error;
|
|
66907
|
+
await unlink(markerPath);
|
|
66908
|
+
marker = void 0;
|
|
66909
|
+
startupPending = true;
|
|
66910
|
+
await refreshBootstrap();
|
|
66911
|
+
response = await startThread();
|
|
66912
|
+
}
|
|
66913
|
+
} else response = await startThread();
|
|
66749
66914
|
const readinessDeadline = Date.now() + 1e4;
|
|
66750
66915
|
for (;;) {
|
|
66751
66916
|
const mcpStatuses = [];
|
|
@@ -66765,7 +66930,7 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66765
66930
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
66766
66931
|
}
|
|
66767
66932
|
if (response.model !== void 0 && response.model !== config.codex.model) throw new Error(`Codex selected ${String(response.model)} instead of configured ${config.codex.model}`);
|
|
66768
|
-
if (marker?.threadId && marker.model !== config.codex.model) await writeFile(markerPath, JSON.stringify({
|
|
66933
|
+
if (resumed && marker?.threadId && marker.model !== config.codex.model) await writeFile(markerPath, JSON.stringify({
|
|
66769
66934
|
threadId,
|
|
66770
66935
|
model: config.codex.model
|
|
66771
66936
|
}), { mode: 384 });
|
|
@@ -66778,7 +66943,6 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66778
66943
|
closing = true;
|
|
66779
66944
|
for (const unsubscribe of unsubscribeAppServer.splice(0)) unsubscribe();
|
|
66780
66945
|
for (const controller of turnControllers.values()) controller.abort();
|
|
66781
|
-
compactWaiter?.reject(error instanceof Error ? error : new Error(String(error)));
|
|
66782
66946
|
await appServer?.close().catch(() => void 0);
|
|
66783
66947
|
await store.close().catch(() => void 0);
|
|
66784
66948
|
throw error;
|
|
@@ -66841,6 +67005,34 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66841
67005
|
return;
|
|
66842
67006
|
}
|
|
66843
67007
|
},
|
|
67008
|
+
async conversationImages(options = {}) {
|
|
67009
|
+
await eventChain;
|
|
67010
|
+
const page = pageConversationImages(await withAvailability([...catalogue.values()]), options.limit ?? 5, options.cursor);
|
|
67011
|
+
return {
|
|
67012
|
+
images: page.images.map(({ id, filename, mediaType, created, origin, available }) => ({
|
|
67013
|
+
id,
|
|
67014
|
+
filename,
|
|
67015
|
+
mediaType,
|
|
67016
|
+
created,
|
|
67017
|
+
origin,
|
|
67018
|
+
available,
|
|
67019
|
+
url: `/api/conversation-images/${id}`
|
|
67020
|
+
})),
|
|
67021
|
+
...page.nextCursor ? { nextCursor: page.nextCursor } : {}
|
|
67022
|
+
};
|
|
67023
|
+
},
|
|
67024
|
+
async conversationImage(id) {
|
|
67025
|
+
const image = catalogue.get(id);
|
|
67026
|
+
if (!image) return void 0;
|
|
67027
|
+
try {
|
|
67028
|
+
return {
|
|
67029
|
+
media_type: image.mediaType,
|
|
67030
|
+
data: await readFile(image.path)
|
|
67031
|
+
};
|
|
67032
|
+
} catch {
|
|
67033
|
+
return;
|
|
67034
|
+
}
|
|
67035
|
+
},
|
|
66844
67036
|
avatar: (kind) => avatars[kind],
|
|
66845
67037
|
async petAsset(activity) {
|
|
66846
67038
|
return pet ? localPetClip(config.state, activity) : void 0;
|
|
@@ -66956,6 +67148,11 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66956
67148
|
const materialized = await materializeImages(paths.workspaceRoot, images);
|
|
66957
67149
|
await store.admit(id, input, materialized);
|
|
66958
67150
|
messageIds.add(id);
|
|
67151
|
+
const message = await store.messageMeta(id);
|
|
67152
|
+
if (message) messagesById.set(id, {
|
|
67153
|
+
created: message.created,
|
|
67154
|
+
sequence: message.sequence
|
|
67155
|
+
});
|
|
66959
67156
|
if (operationTurn(id) || pendingStarts.has(id) || pendingSteers.some((intent) => intent.ids.includes(id)) || await store.outcome(id)) return;
|
|
66960
67157
|
if (unresolvedInputIds.has(id) && !replaces.includes(id)) throw new Error("Message delivery is unresolved; inspect the conversation before retrying");
|
|
66961
67158
|
for (const replacedId of replaces) {
|
|
@@ -66968,14 +67165,19 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66968
67165
|
if (replaces.length) await setRestoredDraft([...unresolvedInputIds]);
|
|
66969
67166
|
await eventChain;
|
|
66970
67167
|
syncActiveTurn();
|
|
66971
|
-
if (rejectedSteers.length) await (drainRejectedSteers() ?? Promise.resolve());
|
|
66972
67168
|
const intent = {
|
|
66973
67169
|
ids: [id],
|
|
66974
67170
|
input,
|
|
66975
67171
|
images: materialized,
|
|
66976
67172
|
transportId: id,
|
|
66977
|
-
source: "
|
|
67173
|
+
source: "human"
|
|
66978
67174
|
};
|
|
67175
|
+
if (compacting && !activeTurnId) {
|
|
67176
|
+
rejectedSteers.push(intent);
|
|
67177
|
+
notify();
|
|
67178
|
+
return;
|
|
67179
|
+
}
|
|
67180
|
+
if (rejectedSteers.length) await (drainRejectedSteers() ?? Promise.resolve());
|
|
66979
67181
|
if (activeTurnId) await steerIntent(intent, activeTurnId);
|
|
66980
67182
|
else await startIntent(intent);
|
|
66981
67183
|
notify();
|
|
@@ -67009,27 +67211,15 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
67009
67211
|
startedAt: now()
|
|
67010
67212
|
};
|
|
67011
67213
|
compacting = true;
|
|
67012
|
-
|
|
67013
|
-
compactWaiter = {
|
|
67014
|
-
resolve: resolveWait,
|
|
67015
|
-
reject: rejectWait
|
|
67016
|
-
};
|
|
67017
|
-
});
|
|
67018
|
-
let timeout;
|
|
67214
|
+
notify();
|
|
67019
67215
|
try {
|
|
67020
67216
|
await appServer.call("thread/compact/start", { threadId });
|
|
67021
|
-
await Promise.race([wait, new Promise((_, reject) => {
|
|
67022
|
-
timeout = setTimeout(() => reject(/* @__PURE__ */ new Error("Codex compaction timed out")), 6e4);
|
|
67023
|
-
})]);
|
|
67024
67217
|
} catch (error) {
|
|
67025
67218
|
compacting = false;
|
|
67026
|
-
failCompaction(
|
|
67219
|
+
failCompaction();
|
|
67027
67220
|
processError("compaction.failed", error);
|
|
67028
67221
|
notify();
|
|
67029
67222
|
throw error;
|
|
67030
|
-
} finally {
|
|
67031
|
-
if (timeout) clearTimeout(timeout);
|
|
67032
|
-
compactWaiter = void 0;
|
|
67033
67223
|
}
|
|
67034
67224
|
notify();
|
|
67035
67225
|
});
|
|
@@ -67052,8 +67242,6 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
67052
67242
|
keetFeed?.close();
|
|
67053
67243
|
for (const unsubscribe of unsubscribeAppServer.splice(0)) unsubscribe();
|
|
67054
67244
|
for (const controller of turnControllers.values()) controller.abort();
|
|
67055
|
-
compactWaiter?.reject(/* @__PURE__ */ new Error("Partner is closing"));
|
|
67056
|
-
compactWaiter = void 0;
|
|
67057
67245
|
compacting = false;
|
|
67058
67246
|
const serverClose = appServer?.close() ?? Promise.resolve();
|
|
67059
67247
|
closePromise = (async () => {
|
|
@@ -67823,6 +68011,25 @@ function createWebServer(partner, assets, options = {}) {
|
|
|
67823
68011
|
response.end(image.data);
|
|
67824
68012
|
return;
|
|
67825
68013
|
}
|
|
68014
|
+
if (path === "/api/conversation-images" && request.method === "GET") {
|
|
68015
|
+
const params = Object.fromEntries(new URL(request.url, "http://localhost").searchParams);
|
|
68016
|
+
const options = object({
|
|
68017
|
+
limit: number().int().min(1).max(50).optional(),
|
|
68018
|
+
cursor: string().min(1).max(500).optional()
|
|
68019
|
+
}).strict().parse(params);
|
|
68020
|
+
return json(response, await partner.conversationImages(options));
|
|
68021
|
+
}
|
|
68022
|
+
if (path.startsWith("/api/conversation-images/") && request.method === "GET") {
|
|
68023
|
+
const id = string().regex(/^[a-f0-9]{64}$/u).parse(path.slice(25));
|
|
68024
|
+
const image = await partner.conversationImage(id);
|
|
68025
|
+
if (!image) return json(response, { error: "Image not found" }, 404);
|
|
68026
|
+
response.writeHead(200, {
|
|
68027
|
+
"content-type": image.media_type,
|
|
68028
|
+
"cache-control": "private, max-age=31536000, immutable"
|
|
68029
|
+
});
|
|
68030
|
+
response.end(image.data);
|
|
68031
|
+
return;
|
|
68032
|
+
}
|
|
67826
68033
|
if (path.startsWith("/api/audio/") && request.method === "GET") {
|
|
67827
68034
|
const id = string().regex(/^[a-f0-9]{64}\.mp3$/u).parse(path.slice(11));
|
|
67828
68035
|
const audio = await partner.audio(id.slice(0, -4));
|
|
@@ -67878,7 +68085,7 @@ function createWebServer(partner, assets, options = {}) {
|
|
|
67878
68085
|
}
|
|
67879
68086
|
if (path === "/api/compact" && request.method === "POST") {
|
|
67880
68087
|
await partner.compact();
|
|
67881
|
-
return json(response, { ok: true });
|
|
68088
|
+
return json(response, { ok: true }, 202);
|
|
67882
68089
|
}
|
|
67883
68090
|
if (path === "/api/cancel" && request.method === "POST") {
|
|
67884
68091
|
const { id } = object({ id: string().min(1).max(300) }).strict().parse(await body(request));
|
|
@@ -68037,7 +68244,7 @@ if (command === "credential") {
|
|
|
68037
68244
|
await partner.close();
|
|
68038
68245
|
process.exitCode = 1;
|
|
68039
68246
|
});
|
|
68040
|
-
app.server.listen(config.port,
|
|
68247
|
+
app.server.listen(config.port, config.listen_host, () => console.log(`Companion: http://${config.listen_host}:${config.port}`));
|
|
68041
68248
|
let stopping = false;
|
|
68042
68249
|
const stop = async () => {
|
|
68043
68250
|
if (stopping) return;
|