@lamplitisles/codex-for-love 0.4.2 → 0.5.0
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 +3 -3
- package/vendor/IMPORTS.md +9 -15
- package/vendor/build/_app/immutable/assets/0.CkWBasWr.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/DFTMMhXH.js +1 -0
- package/vendor/build/_app/immutable/entry/app.Ci6fUWwj.js +2 -0
- package/vendor/build/_app/immutable/entry/start.DWuEW6S3.js +1 -0
- package/vendor/build/_app/immutable/nodes/0.C8SV4li-.js +1 -0
- package/vendor/build/_app/immutable/nodes/1.BQJiYb4b.js +1 -0
- package/vendor/build/_app/immutable/nodes/2.CReDueqi.js +330 -0
- package/vendor/build/_app/version.json +1 -1
- package/vendor/build/index.html +10 -9
- package/vendor/config.example.toml +3 -2
- package/vendor/runtime/cli.mjs +219 -131
- package/vendor/runtime/companion-mcp.mjs +92 -6
- package/vendor/build/_app/immutable/assets/0.DfSR8oPO.css +0 -2
- package/vendor/build/_app/immutable/chunks/BxP8U2lE.js +0 -1
- package/vendor/build/_app/immutable/chunks/CTZw3jzS.js +0 -3
- package/vendor/build/_app/immutable/entry/app.Di6q1DhK.js +0 -2
- package/vendor/build/_app/immutable/entry/start.CwKZcHfn.js +0 -1
- package/vendor/build/_app/immutable/nodes/0.PSG_XCTv.js +0 -1
- package/vendor/build/_app/immutable/nodes/1.CJlXDQ0m.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,13 +13,12 @@ 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 * as fs from "node:fs";
|
|
17
|
-
import { constants, createReadStream } from "node:fs";
|
|
16
|
+
import { createConnection, isIP as isIP$1 } from "net";
|
|
18
17
|
import { DatabaseSync } from "node:sqlite";
|
|
19
18
|
import { execFile } from "node:child_process";
|
|
20
19
|
import { promisify } from "node:util";
|
|
21
20
|
import { createServer } from "node:http";
|
|
21
|
+
import * as fs from "node:fs";
|
|
22
22
|
import * as qs from "node:querystring";
|
|
23
23
|
//#region \0rolldown/runtime.js
|
|
24
24
|
var __create = Object.create;
|
|
@@ -6197,12 +6197,12 @@ const schema = object({
|
|
|
6197
6197
|
user: string().min(1)
|
|
6198
6198
|
}).strict().optional(),
|
|
6199
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"),
|
|
6200
6201
|
codex: object({
|
|
6201
6202
|
command: string().min(1).default("codex-app-server"),
|
|
6202
6203
|
model: string().min(1).default(DEFAULT_CODEX_MODEL),
|
|
6203
6204
|
version: literal(SUPPORTED_CODEX_VERSION).default(SUPPORTED_CODEX_VERSION),
|
|
6204
6205
|
home: string().min(1).optional(),
|
|
6205
|
-
provenance: string().min(1).optional(),
|
|
6206
6206
|
local_compaction: boolean().default(false),
|
|
6207
6207
|
context_round_limit: number$1().int().min(0).default(10)
|
|
6208
6208
|
}).strict().default({
|
|
@@ -6237,8 +6237,7 @@ async function loadConfig(path) {
|
|
|
6237
6237
|
} : void 0,
|
|
6238
6238
|
codex: {
|
|
6239
6239
|
...config.codex,
|
|
6240
|
-
home: config.codex.home ? resolve(base, config.codex.home) : void 0
|
|
6241
|
-
provenance: config.codex.provenance ? resolve(base, config.codex.provenance) : void 0
|
|
6240
|
+
home: config.codex.home ? resolve(base, config.codex.home) : void 0
|
|
6242
6241
|
},
|
|
6243
6242
|
keet: config.keet ? {
|
|
6244
6243
|
...config.keet.endpoint ? { endpoint: keetEndpoint(config.keet.endpoint) } : {},
|
|
@@ -61940,7 +61939,7 @@ function validateBearerToken(value) {
|
|
|
61940
61939
|
function isLoopbackHostname(hostname) {
|
|
61941
61940
|
const normalized = hostname.replace(/^\[|\]$/g, "").replace(/\.$/, "").toLowerCase();
|
|
61942
61941
|
if (normalized === "localhost") return true;
|
|
61943
|
-
const family = isIP(normalized);
|
|
61942
|
+
const family = isIP$1(normalized);
|
|
61944
61943
|
if (family === 4) return normalized.startsWith("127.");
|
|
61945
61944
|
if (family === 6) return normalized === "::1" || normalized.startsWith("::ffff:127.");
|
|
61946
61945
|
return false;
|
|
@@ -62600,7 +62599,8 @@ function partnerPaths(workspace) {
|
|
|
62600
62599
|
database: join(managedRoot, "session.sqlite"),
|
|
62601
62600
|
relationshipJournal: join(managedRoot, "relationship.jsonl"),
|
|
62602
62601
|
attachments: join(managedRoot, "attachments"),
|
|
62603
|
-
audio: join(managedRoot, "audio")
|
|
62602
|
+
audio: join(managedRoot, "audio"),
|
|
62603
|
+
conversationImages: join(managedRoot, "conversation-images.json")
|
|
62604
62604
|
};
|
|
62605
62605
|
}
|
|
62606
62606
|
//#endregion
|
|
@@ -62890,72 +62890,6 @@ const compactionPrompt = [
|
|
|
62890
62890
|
"- Output checkpoint text only. Do not call Tools, take actions, or mention compaction."
|
|
62891
62891
|
].join("\n");
|
|
62892
62892
|
//#endregion
|
|
62893
|
-
//#region apps/partner/runtime/provenance.ts
|
|
62894
|
-
const CFL_FORK_REPOSITORY = "https://github.com/lamplitisles/codex";
|
|
62895
|
-
const CFL_FORK_SOURCE_REVISION = "445477b6a83514611ac206d2ab04b79374555a4c";
|
|
62896
|
-
const provenanceSchema = object({
|
|
62897
|
-
schemaVersion: literal(1),
|
|
62898
|
-
forkRepository: literal(CFL_FORK_REPOSITORY),
|
|
62899
|
-
sourceRevision: string(),
|
|
62900
|
-
releaseTag: string(),
|
|
62901
|
-
codexVersion: literal(SUPPORTED_CODEX_VERSION),
|
|
62902
|
-
target: _enum(["x86_64-unknown-linux-musl", "aarch64-apple-darwin"]),
|
|
62903
|
-
executables: object({
|
|
62904
|
-
"bin/codex-app-server": string().regex(/^[a-f0-9]{64}$/u),
|
|
62905
|
-
"bin/codex-code-mode-host": string().regex(/^[a-f0-9]{64}$/u)
|
|
62906
|
-
}).strict()
|
|
62907
|
-
}).strict().superRefine((value, context) => {
|
|
62908
|
-
const mac = value.target === "aarch64-apple-darwin";
|
|
62909
|
-
const revision = mac ? "c1139f7b2793e94c14243689d756b09c0186708d" : CFL_FORK_SOURCE_REVISION;
|
|
62910
|
-
const tag = mac ? /^cfl\/v0\.154\.0-app-server-darwin\.[0-9]+$/u : /^cfl\/v0\.154\.0-app-server-musl\.[0-9]+$/u;
|
|
62911
|
-
if (value.sourceRevision !== revision || !tag.test(value.releaseTag)) context.addIssue({
|
|
62912
|
-
code: "custom",
|
|
62913
|
-
message: "Source revision/release identity does not match the selected platform"
|
|
62914
|
-
});
|
|
62915
|
-
});
|
|
62916
|
-
async function sha256(path) {
|
|
62917
|
-
const hash = createHash("sha256");
|
|
62918
|
-
for await (const chunk of createReadStream(path)) hash.update(chunk);
|
|
62919
|
-
return hash.digest("hex");
|
|
62920
|
-
}
|
|
62921
|
-
function provenanceError(path, message) {
|
|
62922
|
-
return /* @__PURE__ */ new Error(`Invalid Codex provenance at ${path}: ${message}`);
|
|
62923
|
-
}
|
|
62924
|
-
/**
|
|
62925
|
-
* Verifies the exact executable selected for local compaction before the SDK
|
|
62926
|
-
* starts it. The app-server handshake still verifies the runtime version;
|
|
62927
|
-
* this sidecar binds the selected path to the pinned source and patch set.
|
|
62928
|
-
*/
|
|
62929
|
-
async function verifyCodexArtifact(command, provenancePath) {
|
|
62930
|
-
if (!isAbsolute(command)) throw new Error("Local compaction requires an absolute codex.command so the selected build can be verified");
|
|
62931
|
-
const selectedPath = resolve(command);
|
|
62932
|
-
let raw;
|
|
62933
|
-
try {
|
|
62934
|
-
raw = await readFile(provenancePath, "utf8");
|
|
62935
|
-
} catch (error) {
|
|
62936
|
-
throw new Error(`Codex provenance is required and could not be read at ${provenancePath}: ${String(error)}`);
|
|
62937
|
-
}
|
|
62938
|
-
let provenance;
|
|
62939
|
-
try {
|
|
62940
|
-
provenance = provenanceSchema.parse(JSON.parse(raw));
|
|
62941
|
-
} catch (error) {
|
|
62942
|
-
throw provenanceError(provenancePath, error instanceof Error ? error.message : String(error));
|
|
62943
|
-
}
|
|
62944
|
-
await verifyExecutableHashes(selectedPath, provenancePath, provenance.executables["bin/codex-app-server"], provenance.executables["bin/codex-code-mode-host"]);
|
|
62945
|
-
}
|
|
62946
|
-
async function verifyExecutableHashes(selectedPath, provenancePath, expectedBinaryHash, expectedHelperHash) {
|
|
62947
|
-
try {
|
|
62948
|
-
if (!(await stat(selectedPath)).isFile()) throw new Error("selected path is not a regular file");
|
|
62949
|
-
await access(selectedPath, constants.X_OK);
|
|
62950
|
-
} catch (error) {
|
|
62951
|
-
throw new Error(`Configured Codex executable cannot be verified at ${selectedPath}: ${String(error)}`);
|
|
62952
|
-
}
|
|
62953
|
-
if (await sha256(selectedPath) !== expectedBinaryHash) throw provenanceError(provenancePath, `binary hash mismatch for ${selectedPath}`);
|
|
62954
|
-
const helper = join(dirname(selectedPath), "codex-code-mode-host");
|
|
62955
|
-
await access(helper, constants.X_OK);
|
|
62956
|
-
if (await sha256(helper) !== expectedHelperHash) throw provenanceError(provenancePath, "code-mode host hash does not match");
|
|
62957
|
-
}
|
|
62958
|
-
//#endregion
|
|
62959
62893
|
//#region apps/partner/runtime/store.ts
|
|
62960
62894
|
function identityConflict() {
|
|
62961
62895
|
return Object.assign(/* @__PURE__ */ new Error("Message ID already belongs to different content"), { code: "MESSAGE_IDENTITY_CONFLICT" });
|
|
@@ -64723,8 +64657,6 @@ async function convertDshSession(config, inputPath, companionStatePath, attachme
|
|
|
64723
64657
|
});
|
|
64724
64658
|
const injected = dependencies.appServer ?? {};
|
|
64725
64659
|
const selectedCodexPath = injected.codexPath ?? config.codex.command;
|
|
64726
|
-
if (config.codex.local_compaction && !config.codex.provenance) throw new Error("Local compaction requires codex.provenance for the selected custom Codex build");
|
|
64727
|
-
if (config.codex.provenance) await verifyCodexArtifact(selectedCodexPath, config.codex.provenance);
|
|
64728
64660
|
const environment = {
|
|
64729
64661
|
...config.codex.home ? { CODEX_HOME: config.codex.home } : {},
|
|
64730
64662
|
...injected.env ?? {}
|
|
@@ -65549,6 +65481,80 @@ async function readClip(root, name, frameCount) {
|
|
|
65549
65481
|
return;
|
|
65550
65482
|
}
|
|
65551
65483
|
}
|
|
65484
|
+
//#endregion
|
|
65485
|
+
//#region apps/partner/runtime/conversation-images.ts
|
|
65486
|
+
const CURSOR_BYTES = 40;
|
|
65487
|
+
const CURSOR_TOKEN = /^[A-Za-z0-9_-]{54}$/u;
|
|
65488
|
+
const IMAGE_ID = /^[a-f0-9]{64}$/u;
|
|
65489
|
+
function cursorFor(image) {
|
|
65490
|
+
if (!Number.isSafeInteger(image.created) || image.created < 0 || !IMAGE_ID.test(image.id)) throw new Error("Invalid conversation image cursor data");
|
|
65491
|
+
const value = Buffer.alloc(CURSOR_BYTES);
|
|
65492
|
+
value.writeBigUInt64BE(BigInt(image.created));
|
|
65493
|
+
Buffer.from(image.id, "hex").copy(value, 8);
|
|
65494
|
+
return value.toString("base64url");
|
|
65495
|
+
}
|
|
65496
|
+
function readCursor(cursor) {
|
|
65497
|
+
if (!cursor) return void 0;
|
|
65498
|
+
if (!CURSOR_TOKEN.test(cursor)) return void 0;
|
|
65499
|
+
try {
|
|
65500
|
+
const value = Buffer.from(cursor, "base64url");
|
|
65501
|
+
if (value.length !== CURSOR_BYTES) return void 0;
|
|
65502
|
+
const created = Number(value.readBigUInt64BE());
|
|
65503
|
+
if (!Number.isSafeInteger(created)) return void 0;
|
|
65504
|
+
return [created, value.subarray(8).toString("hex")];
|
|
65505
|
+
} catch {
|
|
65506
|
+
return;
|
|
65507
|
+
}
|
|
65508
|
+
}
|
|
65509
|
+
function conversationImageId(origin, source) {
|
|
65510
|
+
return createHash("sha256").update(`${origin}:${source}`).digest("hex");
|
|
65511
|
+
}
|
|
65512
|
+
function pageConversationImages(images, limit = 5, cursor) {
|
|
65513
|
+
const after = readCursor(cursor);
|
|
65514
|
+
if (cursor && !after) throw new Error("Invalid conversation image cursor");
|
|
65515
|
+
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));
|
|
65516
|
+
const page = ordered.slice(0, limit);
|
|
65517
|
+
return {
|
|
65518
|
+
images: page,
|
|
65519
|
+
...ordered.length > page.length ? { nextCursor: cursorFor(page.at(-1)) } : {}
|
|
65520
|
+
};
|
|
65521
|
+
}
|
|
65522
|
+
async function readConversationImages(path) {
|
|
65523
|
+
try {
|
|
65524
|
+
const value = JSON.parse(await readFile(path, "utf8"));
|
|
65525
|
+
return Array.isArray(value) ? value : [];
|
|
65526
|
+
} catch (error) {
|
|
65527
|
+
if (error.code === "ENOENT") return [];
|
|
65528
|
+
throw error;
|
|
65529
|
+
}
|
|
65530
|
+
}
|
|
65531
|
+
async function withAvailability(images) {
|
|
65532
|
+
return Promise.all(images.map(async (image) => {
|
|
65533
|
+
try {
|
|
65534
|
+
await access(image.path);
|
|
65535
|
+
return {
|
|
65536
|
+
...image,
|
|
65537
|
+
available: true
|
|
65538
|
+
};
|
|
65539
|
+
} catch {
|
|
65540
|
+
return {
|
|
65541
|
+
...image,
|
|
65542
|
+
available: false
|
|
65543
|
+
};
|
|
65544
|
+
}
|
|
65545
|
+
}));
|
|
65546
|
+
}
|
|
65547
|
+
/** Atomic metadata projection shared by the HTTP host and the standalone MCP. */
|
|
65548
|
+
async function writeConversationImages(path, images) {
|
|
65549
|
+
await mkdir(dirname(path), {
|
|
65550
|
+
recursive: true,
|
|
65551
|
+
mode: 448
|
|
65552
|
+
});
|
|
65553
|
+
const target = resolve(path);
|
|
65554
|
+
const temporary = `${target}.${process.pid}.${randomUUID()}.tmp`;
|
|
65555
|
+
await writeFile(temporary, `${JSON.stringify(images)}\n`, { mode: 384 });
|
|
65556
|
+
await rename(temporary, target);
|
|
65557
|
+
}
|
|
65552
65558
|
const HISTORY_PAGE_MESSAGES = 50;
|
|
65553
65559
|
/** Match DSH's 50-message history window against CFL's rendered projection. */
|
|
65554
65560
|
function visibleHistoryPage(messages, results, before) {
|
|
@@ -65725,6 +65731,9 @@ function historyImage(path, operationId, index) {
|
|
|
65725
65731
|
function noHistoryYet(error) {
|
|
65726
65732
|
return error instanceof AppServerInvalidRequestError && error.rpcMessage.includes("thread/turns/list is unavailable before first user message");
|
|
65727
65733
|
}
|
|
65734
|
+
function noRolloutForThread(error) {
|
|
65735
|
+
return error instanceof AppServerInvalidRequestError && error.rpcMessage.startsWith("no rollout found for thread id ");
|
|
65736
|
+
}
|
|
65728
65737
|
function rpcMessage(error) {
|
|
65729
65738
|
return error instanceof AppServerInvalidRequestError ? error.rpcMessage : void 0;
|
|
65730
65739
|
}
|
|
@@ -65841,6 +65850,24 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
65841
65850
|
recursive: true,
|
|
65842
65851
|
mode: 448
|
|
65843
65852
|
});
|
|
65853
|
+
const catalogue = new Map((await readConversationImages(paths.conversationImages)).map((image) => [image.id, image]));
|
|
65854
|
+
let reconcilingCatalogue = false;
|
|
65855
|
+
let reconciledCatalogueIds = /* @__PURE__ */ new Set();
|
|
65856
|
+
async function catalogueImage(origin, image, created) {
|
|
65857
|
+
const id = conversationImageId(origin, `${image.operation_id}:${image.id}`);
|
|
65858
|
+
const previous = catalogue.get(id);
|
|
65859
|
+
catalogue.set(id, {
|
|
65860
|
+
id,
|
|
65861
|
+
filename: image.name,
|
|
65862
|
+
path: resolve(image.path),
|
|
65863
|
+
mediaType: image.media_type,
|
|
65864
|
+
created: reconcilingCatalogue ? created : previous?.created ?? created,
|
|
65865
|
+
origin,
|
|
65866
|
+
available: true
|
|
65867
|
+
});
|
|
65868
|
+
reconciledCatalogueIds.add(id);
|
|
65869
|
+
if (!reconcilingCatalogue) await writeConversationImages(paths.conversationImages, await withAvailability([...catalogue.values()]));
|
|
65870
|
+
}
|
|
65844
65871
|
const store = new Store(paths.database);
|
|
65845
65872
|
const now = dependencies.now ?? Date.now;
|
|
65846
65873
|
const listeners = /* @__PURE__ */ new Set();
|
|
@@ -65872,7 +65899,6 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
65872
65899
|
let unsubscribeAppServer = [];
|
|
65873
65900
|
let startupPending = false;
|
|
65874
65901
|
let compacting = false;
|
|
65875
|
-
let compactWaiter;
|
|
65876
65902
|
let eventChain = Promise.resolve();
|
|
65877
65903
|
let admission = Promise.resolve();
|
|
65878
65904
|
let closePromise;
|
|
@@ -66048,7 +66074,7 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66048
66074
|
if (previous?.status === "replaced") return;
|
|
66049
66075
|
if (previous?.status !== "attachment-error" || previous.error !== error) await store.markOutcome(owner, "attachment-error", error);
|
|
66050
66076
|
}
|
|
66051
|
-
async function projectGeneratedImage(operationId, sourceIds, item) {
|
|
66077
|
+
async function projectGeneratedImage(operationId, sourceIds, item, created) {
|
|
66052
66078
|
if (item.type !== "imageGeneration") return void 0;
|
|
66053
66079
|
if (item.status === "failed") {
|
|
66054
66080
|
processError("image.generation_failed", new Error(imageFailure("failed")), {
|
|
@@ -66063,6 +66089,7 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66063
66089
|
const existing = await store.generatedImageForItem(operationId, itemId);
|
|
66064
66090
|
if (existing) try {
|
|
66065
66091
|
await access(existing.path);
|
|
66092
|
+
await catalogueImage("partner", existing, created);
|
|
66066
66093
|
return existing.id;
|
|
66067
66094
|
} catch {
|
|
66068
66095
|
storageError = true;
|
|
@@ -66085,6 +66112,7 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66085
66112
|
return;
|
|
66086
66113
|
}
|
|
66087
66114
|
await store.saveGeneratedImage(generated, itemId);
|
|
66115
|
+
await catalogueImage("partner", generated, created);
|
|
66088
66116
|
return generated.id;
|
|
66089
66117
|
} catch (error) {
|
|
66090
66118
|
storageError = true;
|
|
@@ -66175,6 +66203,7 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66175
66203
|
await store.ensureMessage(sourceId, turnTime(turn.startedAt, now()), source.input, source.images);
|
|
66176
66204
|
messageIds.add(sourceId);
|
|
66177
66205
|
}
|
|
66206
|
+
if (!sourceId.startsWith("keet:")) for (const image of source.images) await catalogueImage(isUnder(join(paths.managedRoot, "historical-media"), image.path) ? "historical" : "owner", image, turnTime(turn.startedAt, now()));
|
|
66178
66207
|
await store.markObserved(sourceId, segment);
|
|
66179
66208
|
}
|
|
66180
66209
|
acknowledgeInputIds(officialIds);
|
|
@@ -66187,7 +66216,7 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66187
66216
|
const completedAt = status === "completed" ? completedTurnTime(turn.completedAt) : void 0;
|
|
66188
66217
|
for (const item of turn.items ?? []) {
|
|
66189
66218
|
if (item.type !== "imageGeneration") continue;
|
|
66190
|
-
const generatedId = await projectGeneratedImage(resultOperationId(turn.id), sourceIds, item);
|
|
66219
|
+
const generatedId = await projectGeneratedImage(resultOperationId(turn.id), sourceIds, item, completedTurnTime(turn.completedAt) ?? turnTime(turn.startedAt, now()));
|
|
66191
66220
|
if (generatedId) generatedIds.push(generatedId);
|
|
66192
66221
|
else if (item.status === "failed") {
|
|
66193
66222
|
resultStatus = "failed";
|
|
@@ -66217,15 +66246,23 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66217
66246
|
}
|
|
66218
66247
|
/** Full history is hydrated once at startup/resume; events update one turn thereafter. */
|
|
66219
66248
|
async function hydrateHistory() {
|
|
66249
|
+
reconcilingCatalogue = true;
|
|
66250
|
+
reconciledCatalogueIds = /* @__PURE__ */ new Set();
|
|
66220
66251
|
const nextTurns = await listTurns();
|
|
66221
66252
|
turns.clear();
|
|
66222
66253
|
turnInputs.clear();
|
|
66223
66254
|
turnResults.clear();
|
|
66224
66255
|
presentationFingerprints.clear();
|
|
66225
|
-
|
|
66226
|
-
|
|
66227
|
-
|
|
66228
|
-
|
|
66256
|
+
try {
|
|
66257
|
+
for (const turn of nextTurns) {
|
|
66258
|
+
registerTurn(turn);
|
|
66259
|
+
const complete = await reconcileTurn(turn.id, turn);
|
|
66260
|
+
if (complete) await projectTurn(complete, false);
|
|
66261
|
+
}
|
|
66262
|
+
} finally {
|
|
66263
|
+
reconcilingCatalogue = false;
|
|
66264
|
+
for (const id of catalogue.keys()) if (!reconciledCatalogueIds.has(id)) catalogue.delete(id);
|
|
66265
|
+
await writeConversationImages(paths.conversationImages, await withAvailability([...catalogue.values()]));
|
|
66229
66266
|
}
|
|
66230
66267
|
const unresolved = await unresolvedMessages();
|
|
66231
66268
|
for (const message of unresolved) unresolvedInputIds.add(message.id);
|
|
@@ -66378,7 +66415,7 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66378
66415
|
}
|
|
66379
66416
|
}
|
|
66380
66417
|
function drainRejectedSteers() {
|
|
66381
|
-
if (closing || activeTurnId || recoveryPromise || !rejectedSteers.length) return recoveryPromise;
|
|
66418
|
+
if (closing || compacting || activeTurnId || recoveryPromise || !rejectedSteers.length) return recoveryPromise;
|
|
66382
66419
|
const intents = rejectedSteers.splice(0);
|
|
66383
66420
|
const ids = intents.flatMap((intent) => intent.ids);
|
|
66384
66421
|
const merged = {
|
|
@@ -66402,7 +66439,7 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66402
66439
|
}
|
|
66403
66440
|
function drainKeet() {
|
|
66404
66441
|
admission = admission.then(async () => {
|
|
66405
|
-
if (closing || activeTurnId || recoveryPromise || !keetEnabled) return;
|
|
66442
|
+
if (closing || compacting || activeTurnId || recoveryPromise || !keetEnabled) return;
|
|
66406
66443
|
const pending = (await store.pendingMessages()).filter((item) => item.id.startsWith("keet:") && !observedInputIds.has(item.id));
|
|
66407
66444
|
if (!pending.length || activeTurnId || closing) return;
|
|
66408
66445
|
const first = pending[0];
|
|
@@ -66504,11 +66541,11 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66504
66541
|
scheduleKeetReconnect();
|
|
66505
66542
|
});
|
|
66506
66543
|
}
|
|
66507
|
-
async function restoreUnconsumed(turnId) {
|
|
66544
|
+
async function restoreUnconsumed(turnId, preserveQueued = false) {
|
|
66508
66545
|
const ids = turnId ? [...sourceIdsForTurn(turnId)] : [];
|
|
66509
66546
|
const selectedSteers = pendingSteers.filter((intent) => turnId === void 0 || intent.turnId === turnId);
|
|
66510
66547
|
const selectedStarts = [...pendingStarts.values()].filter((intent) => turnId === void 0 || intent.turnId === turnId);
|
|
66511
|
-
const selectedRejected = rejectedSteers.filter((intent) => turnId === void 0 || intent.turnId === turnId);
|
|
66548
|
+
const selectedRejected = preserveQueued ? [] : rejectedSteers.filter((intent) => turnId === void 0 || intent.turnId === turnId);
|
|
66512
66549
|
for (const intent of selectedSteers) ids.push(...intent.ids);
|
|
66513
66550
|
for (const intent of selectedStarts) ids.push(...intent.ids);
|
|
66514
66551
|
for (const intent of selectedRejected) ids.push(...intent.ids);
|
|
@@ -66576,18 +66613,14 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66576
66613
|
endedAt: boundary.time
|
|
66577
66614
|
};
|
|
66578
66615
|
await refreshBootstrap();
|
|
66579
|
-
compactWaiter?.resolve();
|
|
66580
|
-
compactWaiter = void 0;
|
|
66581
66616
|
}
|
|
66582
|
-
function failCompaction(
|
|
66617
|
+
function failCompaction() {
|
|
66583
66618
|
if (lifecycle?.status === "running") lifecycle = {
|
|
66584
66619
|
...lifecycle,
|
|
66585
66620
|
status: "failed",
|
|
66586
66621
|
endSeq: eventSequence,
|
|
66587
66622
|
endedAt: now()
|
|
66588
66623
|
};
|
|
66589
|
-
compactWaiter?.reject(error instanceof Error ? error : new Error(String(error)));
|
|
66590
|
-
compactWaiter = void 0;
|
|
66591
66624
|
}
|
|
66592
66625
|
async function handleServerEvent(notification) {
|
|
66593
66626
|
if (closing) return;
|
|
@@ -66669,11 +66702,14 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66669
66702
|
turnControllers.delete(turn.id);
|
|
66670
66703
|
}
|
|
66671
66704
|
if (reconciled && (turnStatus(reconciled) === "interrupted" || turnStatus(reconciled) === "cancelled")) await restoreUnconsumed(completedTurnId);
|
|
66672
|
-
else if (reconciled && !userItems(reconciled).length && completedTurnId) await restoreUnconsumed(completedTurnId);
|
|
66705
|
+
else if (reconciled && !userItems(reconciled).length && completedTurnId) await restoreUnconsumed(completedTurnId, turnStatus(reconciled) === "completed");
|
|
66673
66706
|
syncActiveTurn();
|
|
66674
66707
|
if (compacting) {
|
|
66675
66708
|
compacting = false;
|
|
66676
|
-
if (lifecycle?.status === "running")
|
|
66709
|
+
if (lifecycle?.status === "running") {
|
|
66710
|
+
if (turnStatus(reconciled) === "completed") await finishCompaction(reconciled?.id);
|
|
66711
|
+
else failCompaction();
|
|
66712
|
+
}
|
|
66677
66713
|
}
|
|
66678
66714
|
await refreshBootstrap();
|
|
66679
66715
|
if (!activeTurnId) {
|
|
@@ -66697,6 +66733,9 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66697
66733
|
if (notification.method === "turn/started") {
|
|
66698
66734
|
const turn = registerTurn(record(notification.params).turn);
|
|
66699
66735
|
if (turn) {
|
|
66736
|
+
if (compacting) {
|
|
66737
|
+
for (const intent of rejectedSteers) if (!intent.turnId) intent.turnId = turn.id;
|
|
66738
|
+
}
|
|
66700
66739
|
activeTurnId = turn.id;
|
|
66701
66740
|
activeOperationId = sourceIdsForTurn(turn.id)[0] ?? activeOperationId;
|
|
66702
66741
|
if (!turnControllers.has(turn.id)) turnControllers.set(turn.id, new AbortController());
|
|
@@ -66727,8 +66766,6 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66727
66766
|
await refreshBootstrap();
|
|
66728
66767
|
const injected = dependencies.appServer ?? {};
|
|
66729
66768
|
const selectedCodexPath = injected.codexPath ?? config.codex.command;
|
|
66730
|
-
if (config.codex.local_compaction && !config.codex.provenance) throw new Error("Local compaction requires codex.provenance for the selected custom Codex build");
|
|
66731
|
-
if (config.codex.provenance) await verifyCodexArtifact(selectedCodexPath, config.codex.provenance);
|
|
66732
66769
|
const environment = {
|
|
66733
66770
|
...config.codex.home ? { CODEX_HOME: config.codex.home } : {},
|
|
66734
66771
|
...keetEnabled ? { CFL_KEET_TOKEN: credentials.keet } : {},
|
|
@@ -66796,29 +66833,44 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66796
66833
|
} : {}
|
|
66797
66834
|
}
|
|
66798
66835
|
};
|
|
66836
|
+
const connectedAppServer = appServer;
|
|
66837
|
+
if (!connectedAppServer) throw new Error("Codex app-server connection was not initialized");
|
|
66799
66838
|
let response;
|
|
66800
|
-
|
|
66801
|
-
|
|
66802
|
-
...shared,
|
|
66803
|
-
threadId: marker.threadId,
|
|
66804
|
-
excludeTurns: true
|
|
66805
|
-
};
|
|
66806
|
-
response = await appServer.call("thread/resume", resume);
|
|
66807
|
-
threadId = response.thread.id;
|
|
66808
|
-
} else {
|
|
66839
|
+
let resumed = false;
|
|
66840
|
+
const startThread = async () => {
|
|
66809
66841
|
const start = {
|
|
66810
66842
|
...shared,
|
|
66811
66843
|
historyMode: "paginated",
|
|
66812
66844
|
sessionStartSource: "startup"
|
|
66813
66845
|
};
|
|
66814
|
-
|
|
66815
|
-
threadId =
|
|
66846
|
+
const started = await connectedAppServer.call("thread/start", start);
|
|
66847
|
+
threadId = started.thread.id;
|
|
66816
66848
|
if (!threadId) throw new Error("Codex app-server did not return a thread id");
|
|
66817
66849
|
await writeFile(markerPath, JSON.stringify({
|
|
66818
66850
|
threadId,
|
|
66819
66851
|
model: config.codex.model
|
|
66820
66852
|
}), { mode: 384 });
|
|
66821
|
-
|
|
66853
|
+
return started;
|
|
66854
|
+
};
|
|
66855
|
+
if (marker?.threadId) {
|
|
66856
|
+
const resume = {
|
|
66857
|
+
...shared,
|
|
66858
|
+
threadId: marker.threadId,
|
|
66859
|
+
excludeTurns: true
|
|
66860
|
+
};
|
|
66861
|
+
try {
|
|
66862
|
+
response = await connectedAppServer.call("thread/resume", resume);
|
|
66863
|
+
threadId = response.thread.id;
|
|
66864
|
+
resumed = true;
|
|
66865
|
+
} catch (error) {
|
|
66866
|
+
if (!noRolloutForThread(error) || messageIds.size) throw error;
|
|
66867
|
+
await unlink(markerPath);
|
|
66868
|
+
marker = void 0;
|
|
66869
|
+
startupPending = true;
|
|
66870
|
+
await refreshBootstrap();
|
|
66871
|
+
response = await startThread();
|
|
66872
|
+
}
|
|
66873
|
+
} else response = await startThread();
|
|
66822
66874
|
const readinessDeadline = Date.now() + 1e4;
|
|
66823
66875
|
for (;;) {
|
|
66824
66876
|
const mcpStatuses = [];
|
|
@@ -66838,7 +66890,7 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66838
66890
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
66839
66891
|
}
|
|
66840
66892
|
if (response.model !== void 0 && response.model !== config.codex.model) throw new Error(`Codex selected ${String(response.model)} instead of configured ${config.codex.model}`);
|
|
66841
|
-
if (marker?.threadId && marker.model !== config.codex.model) await writeFile(markerPath, JSON.stringify({
|
|
66893
|
+
if (resumed && marker?.threadId && marker.model !== config.codex.model) await writeFile(markerPath, JSON.stringify({
|
|
66842
66894
|
threadId,
|
|
66843
66895
|
model: config.codex.model
|
|
66844
66896
|
}), { mode: 384 });
|
|
@@ -66851,7 +66903,6 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66851
66903
|
closing = true;
|
|
66852
66904
|
for (const unsubscribe of unsubscribeAppServer.splice(0)) unsubscribe();
|
|
66853
66905
|
for (const controller of turnControllers.values()) controller.abort();
|
|
66854
|
-
compactWaiter?.reject(error instanceof Error ? error : new Error(String(error)));
|
|
66855
66906
|
await appServer?.close().catch(() => void 0);
|
|
66856
66907
|
await store.close().catch(() => void 0);
|
|
66857
66908
|
throw error;
|
|
@@ -66914,6 +66965,34 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
66914
66965
|
return;
|
|
66915
66966
|
}
|
|
66916
66967
|
},
|
|
66968
|
+
async conversationImages(options = {}) {
|
|
66969
|
+
await eventChain;
|
|
66970
|
+
const page = pageConversationImages(await withAvailability([...catalogue.values()]), options.limit ?? 5, options.cursor);
|
|
66971
|
+
return {
|
|
66972
|
+
images: page.images.map(({ id, filename, mediaType, created, origin, available }) => ({
|
|
66973
|
+
id,
|
|
66974
|
+
filename,
|
|
66975
|
+
mediaType,
|
|
66976
|
+
created,
|
|
66977
|
+
origin,
|
|
66978
|
+
available,
|
|
66979
|
+
url: `/api/conversation-images/${id}`
|
|
66980
|
+
})),
|
|
66981
|
+
...page.nextCursor ? { nextCursor: page.nextCursor } : {}
|
|
66982
|
+
};
|
|
66983
|
+
},
|
|
66984
|
+
async conversationImage(id) {
|
|
66985
|
+
const image = catalogue.get(id);
|
|
66986
|
+
if (!image) return void 0;
|
|
66987
|
+
try {
|
|
66988
|
+
return {
|
|
66989
|
+
media_type: image.mediaType,
|
|
66990
|
+
data: await readFile(image.path)
|
|
66991
|
+
};
|
|
66992
|
+
} catch {
|
|
66993
|
+
return;
|
|
66994
|
+
}
|
|
66995
|
+
},
|
|
66917
66996
|
avatar: (kind) => avatars[kind],
|
|
66918
66997
|
async petAsset(activity) {
|
|
66919
66998
|
return pet ? localPetClip(config.state, activity) : void 0;
|
|
@@ -67041,7 +67120,6 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
67041
67120
|
if (replaces.length) await setRestoredDraft([...unresolvedInputIds]);
|
|
67042
67121
|
await eventChain;
|
|
67043
67122
|
syncActiveTurn();
|
|
67044
|
-
if (rejectedSteers.length) await (drainRejectedSteers() ?? Promise.resolve());
|
|
67045
67123
|
const intent = {
|
|
67046
67124
|
ids: [id],
|
|
67047
67125
|
input,
|
|
@@ -67049,6 +67127,12 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
67049
67127
|
transportId: id,
|
|
67050
67128
|
source: "owner"
|
|
67051
67129
|
};
|
|
67130
|
+
if (compacting && !activeTurnId) {
|
|
67131
|
+
rejectedSteers.push(intent);
|
|
67132
|
+
notify();
|
|
67133
|
+
return;
|
|
67134
|
+
}
|
|
67135
|
+
if (rejectedSteers.length) await (drainRejectedSteers() ?? Promise.resolve());
|
|
67052
67136
|
if (activeTurnId) await steerIntent(intent, activeTurnId);
|
|
67053
67137
|
else await startIntent(intent);
|
|
67054
67138
|
notify();
|
|
@@ -67082,27 +67166,15 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
67082
67166
|
startedAt: now()
|
|
67083
67167
|
};
|
|
67084
67168
|
compacting = true;
|
|
67085
|
-
|
|
67086
|
-
compactWaiter = {
|
|
67087
|
-
resolve: resolveWait,
|
|
67088
|
-
reject: rejectWait
|
|
67089
|
-
};
|
|
67090
|
-
});
|
|
67091
|
-
let timeout;
|
|
67169
|
+
notify();
|
|
67092
67170
|
try {
|
|
67093
67171
|
await appServer.call("thread/compact/start", { threadId });
|
|
67094
|
-
await Promise.race([wait, new Promise((_, reject) => {
|
|
67095
|
-
timeout = setTimeout(() => reject(/* @__PURE__ */ new Error("Codex compaction timed out")), 6e4);
|
|
67096
|
-
})]);
|
|
67097
67172
|
} catch (error) {
|
|
67098
67173
|
compacting = false;
|
|
67099
|
-
failCompaction(
|
|
67174
|
+
failCompaction();
|
|
67100
67175
|
processError("compaction.failed", error);
|
|
67101
67176
|
notify();
|
|
67102
67177
|
throw error;
|
|
67103
|
-
} finally {
|
|
67104
|
-
if (timeout) clearTimeout(timeout);
|
|
67105
|
-
compactWaiter = void 0;
|
|
67106
67178
|
}
|
|
67107
67179
|
notify();
|
|
67108
67180
|
});
|
|
@@ -67125,8 +67197,6 @@ async function createPartner(config, credentials, dependencies = {}) {
|
|
|
67125
67197
|
keetFeed?.close();
|
|
67126
67198
|
for (const unsubscribe of unsubscribeAppServer.splice(0)) unsubscribe();
|
|
67127
67199
|
for (const controller of turnControllers.values()) controller.abort();
|
|
67128
|
-
compactWaiter?.reject(/* @__PURE__ */ new Error("Partner is closing"));
|
|
67129
|
-
compactWaiter = void 0;
|
|
67130
67200
|
compacting = false;
|
|
67131
67201
|
const serverClose = appServer?.close() ?? Promise.resolve();
|
|
67132
67202
|
closePromise = (async () => {
|
|
@@ -67896,6 +67966,25 @@ function createWebServer(partner, assets, options = {}) {
|
|
|
67896
67966
|
response.end(image.data);
|
|
67897
67967
|
return;
|
|
67898
67968
|
}
|
|
67969
|
+
if (path === "/api/conversation-images" && request.method === "GET") {
|
|
67970
|
+
const params = Object.fromEntries(new URL(request.url, "http://localhost").searchParams);
|
|
67971
|
+
const options = object({
|
|
67972
|
+
limit: number().int().min(1).max(50).optional(),
|
|
67973
|
+
cursor: string().min(1).max(500).optional()
|
|
67974
|
+
}).strict().parse(params);
|
|
67975
|
+
return json(response, await partner.conversationImages(options));
|
|
67976
|
+
}
|
|
67977
|
+
if (path.startsWith("/api/conversation-images/") && request.method === "GET") {
|
|
67978
|
+
const id = string().regex(/^[a-f0-9]{64}$/u).parse(path.slice(25));
|
|
67979
|
+
const image = await partner.conversationImage(id);
|
|
67980
|
+
if (!image) return json(response, { error: "Image not found" }, 404);
|
|
67981
|
+
response.writeHead(200, {
|
|
67982
|
+
"content-type": image.media_type,
|
|
67983
|
+
"cache-control": "private, max-age=31536000, immutable"
|
|
67984
|
+
});
|
|
67985
|
+
response.end(image.data);
|
|
67986
|
+
return;
|
|
67987
|
+
}
|
|
67899
67988
|
if (path.startsWith("/api/audio/") && request.method === "GET") {
|
|
67900
67989
|
const id = string().regex(/^[a-f0-9]{64}\.mp3$/u).parse(path.slice(11));
|
|
67901
67990
|
const audio = await partner.audio(id.slice(0, -4));
|
|
@@ -67951,7 +68040,7 @@ function createWebServer(partner, assets, options = {}) {
|
|
|
67951
68040
|
}
|
|
67952
68041
|
if (path === "/api/compact" && request.method === "POST") {
|
|
67953
68042
|
await partner.compact();
|
|
67954
|
-
return json(response, { ok: true });
|
|
68043
|
+
return json(response, { ok: true }, 202);
|
|
67955
68044
|
}
|
|
67956
68045
|
if (path === "/api/cancel" && request.method === "POST") {
|
|
67957
68046
|
const { id } = object({ id: string().min(1).max(300) }).strict().parse(await body(request));
|
|
@@ -68052,8 +68141,7 @@ if (nativePackageRoot) {
|
|
|
68052
68141
|
...config,
|
|
68053
68142
|
codex: {
|
|
68054
68143
|
...config.codex,
|
|
68055
|
-
command: join(root, "bin", "codex-app-server")
|
|
68056
|
-
provenance: join(root, "provenance.json")
|
|
68144
|
+
command: join(root, "bin", "codex-app-server")
|
|
68057
68145
|
}
|
|
68058
68146
|
};
|
|
68059
68147
|
}
|
|
@@ -68111,7 +68199,7 @@ if (command === "credential") {
|
|
|
68111
68199
|
await partner.close();
|
|
68112
68200
|
process.exitCode = 1;
|
|
68113
68201
|
});
|
|
68114
|
-
app.server.listen(config.port,
|
|
68202
|
+
app.server.listen(config.port, config.listen_host, () => console.log(`Companion: http://${config.listen_host}:${config.port}`));
|
|
68115
68203
|
let stopping = false;
|
|
68116
68204
|
const stop = async () => {
|
|
68117
68205
|
if (stopping) return;
|