@cydm/happy-elves 0.1.0-beta.258 → 0.1.0-beta.260
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/apps/cli/package.json +1 -1
- package/apps/daemon/dist/runtime/external-provider.js +9 -1
- package/apps/daemon/package.json +1 -1
- package/package.json +1 -1
- package/packages/runtime-cli/dist/codex-app-server.d.ts +14 -1
- package/packages/runtime-cli/dist/codex-app-server.js +100 -0
- package/packages/runtime-cli/dist/index.d.ts +6 -1
- package/packages/runtime-cli/dist/index.js +80 -1
package/apps/cli/package.json
CHANGED
|
@@ -169,6 +169,10 @@ export function createExternalAgentRuntimeProvider(options) {
|
|
|
169
169
|
const clients = options.providers.map((config) => new ExternalProviderClient(config));
|
|
170
170
|
const clientsByAgent = new Map();
|
|
171
171
|
async function initialize() {
|
|
172
|
+
const fallback = options.fallback;
|
|
173
|
+
if (typeof fallback.initialize === "function") {
|
|
174
|
+
await fallback.initialize().catch(() => undefined);
|
|
175
|
+
}
|
|
172
176
|
await Promise.all(clients.map(async (client) => {
|
|
173
177
|
await client.initialize().catch(() => undefined);
|
|
174
178
|
for (const agent of client.listAgents())
|
|
@@ -273,7 +277,11 @@ export function createExternalAgentRuntimeProvider(options) {
|
|
|
273
277
|
return {
|
|
274
278
|
initialize,
|
|
275
279
|
diagnostics() {
|
|
276
|
-
|
|
280
|
+
const fallback = options.fallback;
|
|
281
|
+
const fallbackDiagnostics = typeof fallback.diagnostics === "function"
|
|
282
|
+
? fallback.diagnostics()
|
|
283
|
+
: [];
|
|
284
|
+
return [...fallbackDiagnostics, ...clients.map((client) => client.diagnostics())];
|
|
277
285
|
},
|
|
278
286
|
dispose() {
|
|
279
287
|
for (const client of clients)
|
package/apps/daemon/package.json
CHANGED
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RuntimePromptMode, RuntimeTurn } from "../../runtime/dist/index.js";
|
|
1
|
+
import type { RuntimeModelOption, RuntimePromptMode, RuntimeTurn } from "../../runtime/dist/index.js";
|
|
2
2
|
import type { RuntimePermissionMode } from "./permissions.js";
|
|
3
3
|
export declare class CodexAppServerSession {
|
|
4
4
|
private readonly command;
|
|
@@ -20,7 +20,12 @@ export declare class CodexAppServerSession {
|
|
|
20
20
|
threadId: string | null;
|
|
21
21
|
constructor(command: string, cwd: string, threadId?: string, onThreadIdChanged?: ((threadId: string) => void) | undefined, restartableEmptyThread?: boolean, initialModel?: string | undefined);
|
|
22
22
|
ensure(permissionMode?: RuntimePermissionMode, model?: string): Promise<string>;
|
|
23
|
+
discoverRuntimeOptions(): Promise<{
|
|
24
|
+
defaultModel?: string;
|
|
25
|
+
models?: RuntimeModelOption[];
|
|
26
|
+
} | undefined>;
|
|
23
27
|
private ensureCurrentPermission;
|
|
28
|
+
private loadAllModelListPages;
|
|
24
29
|
private ensurePermission;
|
|
25
30
|
private startNewThread;
|
|
26
31
|
private canRestartEmptyThread;
|
|
@@ -56,4 +61,12 @@ export declare class CodexAppServerSession {
|
|
|
56
61
|
private noteTurnActivity;
|
|
57
62
|
private emptyCompletionFailure;
|
|
58
63
|
}
|
|
64
|
+
export declare function discoverCodexRuntimeOptions(input: {
|
|
65
|
+
command: string;
|
|
66
|
+
cwd: string;
|
|
67
|
+
timeoutMs?: number;
|
|
68
|
+
}): Promise<{
|
|
69
|
+
defaultModel?: string;
|
|
70
|
+
models?: RuntimeModelOption[];
|
|
71
|
+
} | undefined>;
|
|
59
72
|
//# sourceMappingURL=codex-app-server.d.ts.map
|
|
@@ -33,9 +33,43 @@ export class CodexAppServerSession {
|
|
|
33
33
|
async ensure(permissionMode, model) {
|
|
34
34
|
return await this.ensurePermission(normalizeCodexPermissionMode(permissionMode), normalizeModel(model ?? this.initialModel));
|
|
35
35
|
}
|
|
36
|
+
async discoverRuntimeOptions() {
|
|
37
|
+
if (!this.rpc)
|
|
38
|
+
await this.connect();
|
|
39
|
+
return codexRuntimeOptionsFromModelList(await this.loadAllModelListPages());
|
|
40
|
+
}
|
|
36
41
|
async ensureCurrentPermission() {
|
|
37
42
|
return await this.ensurePermission(normalizeCodexPermissionMode(this.appliedPermissionMode), this.appliedModel ?? normalizeModel(this.initialModel));
|
|
38
43
|
}
|
|
44
|
+
async loadAllModelListPages() {
|
|
45
|
+
const models = [];
|
|
46
|
+
const seenModelIds = new Set();
|
|
47
|
+
const seenCursors = new Set();
|
|
48
|
+
let cursor;
|
|
49
|
+
for (let pageCount = 0; pageCount < CODEX_MODEL_LIST_PAGE_SAFETY_LIMIT; pageCount += 1) {
|
|
50
|
+
const result = await this.request("model/list", {
|
|
51
|
+
limit: CODEX_MODEL_LIST_PAGE_LIMIT,
|
|
52
|
+
...(cursor !== undefined ? { cursor } : {}),
|
|
53
|
+
});
|
|
54
|
+
for (const model of codexModelsFromModelList(result)) {
|
|
55
|
+
const id = codexModelId(model);
|
|
56
|
+
if (id && seenModelIds.has(id))
|
|
57
|
+
continue;
|
|
58
|
+
if (id)
|
|
59
|
+
seenModelIds.add(id);
|
|
60
|
+
models.push(model);
|
|
61
|
+
}
|
|
62
|
+
const nextCursor = codexModelListNextCursor(result);
|
|
63
|
+
if (nextCursor === undefined)
|
|
64
|
+
return { data: models };
|
|
65
|
+
if (seenCursors.has(nextCursor)) {
|
|
66
|
+
throw new Error(`Codex model/list returned repeated cursor ${JSON.stringify(nextCursor)}`);
|
|
67
|
+
}
|
|
68
|
+
seenCursors.add(nextCursor);
|
|
69
|
+
cursor = nextCursor;
|
|
70
|
+
}
|
|
71
|
+
throw new Error(`Codex model/list exceeded the ${CODEX_MODEL_LIST_PAGE_SAFETY_LIMIT} page safety limit`);
|
|
72
|
+
}
|
|
39
73
|
async ensurePermission(permissionMode, model) {
|
|
40
74
|
const hadProcess = Boolean(this.rpc);
|
|
41
75
|
if (!this.rpc)
|
|
@@ -349,6 +383,27 @@ export class CodexAppServerSession {
|
|
|
349
383
|
return `Codex turn completed without assistant output after runtime errors${detail ? `: ${detail}` : ""}`;
|
|
350
384
|
}
|
|
351
385
|
}
|
|
386
|
+
const CODEX_MODEL_LIST_PAGE_LIMIT = 100;
|
|
387
|
+
const CODEX_MODEL_LIST_PAGE_SAFETY_LIMIT = 100;
|
|
388
|
+
const CODEX_PROVIDER_DISCOVERY_TIMEOUT_MS = 20_000;
|
|
389
|
+
export async function discoverCodexRuntimeOptions(input) {
|
|
390
|
+
const discovery = new CodexAppServerSession(input.command, input.cwd);
|
|
391
|
+
const timeoutMs = Math.max(1, input.timeoutMs ?? CODEX_PROVIDER_DISCOVERY_TIMEOUT_MS);
|
|
392
|
+
let timeout;
|
|
393
|
+
try {
|
|
394
|
+
return await Promise.race([
|
|
395
|
+
discovery.discoverRuntimeOptions(),
|
|
396
|
+
new Promise((_, reject) => {
|
|
397
|
+
timeout = setTimeout(() => reject(new Error(`Codex provider model discovery timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
398
|
+
}),
|
|
399
|
+
]);
|
|
400
|
+
}
|
|
401
|
+
finally {
|
|
402
|
+
if (timeout)
|
|
403
|
+
clearTimeout(timeout);
|
|
404
|
+
await discovery.close();
|
|
405
|
+
}
|
|
406
|
+
}
|
|
352
407
|
function codexStderrMessage(text) {
|
|
353
408
|
try {
|
|
354
409
|
const parsed = JSON.parse(text);
|
|
@@ -415,4 +470,49 @@ function numberField(record, ...keys) {
|
|
|
415
470
|
function normalizeModel(value) {
|
|
416
471
|
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
417
472
|
}
|
|
473
|
+
function codexModelsFromModelList(value) {
|
|
474
|
+
const record = recordFrom(value);
|
|
475
|
+
return Array.isArray(record.models)
|
|
476
|
+
? record.models
|
|
477
|
+
: Array.isArray(record.items)
|
|
478
|
+
? record.items
|
|
479
|
+
: Array.isArray(record.data)
|
|
480
|
+
? record.data
|
|
481
|
+
: [];
|
|
482
|
+
}
|
|
483
|
+
function codexModelId(value) {
|
|
484
|
+
return stringField(recordFrom(value), "id", "key", "name");
|
|
485
|
+
}
|
|
486
|
+
function codexModelListNextCursor(value) {
|
|
487
|
+
const nextCursor = recordFrom(value).nextCursor;
|
|
488
|
+
if (nextCursor === undefined || nextCursor === null)
|
|
489
|
+
return undefined;
|
|
490
|
+
if (typeof nextCursor !== "string" || nextCursor.length === 0) {
|
|
491
|
+
throw new Error("Codex model/list returned an invalid nextCursor");
|
|
492
|
+
}
|
|
493
|
+
return nextCursor;
|
|
494
|
+
}
|
|
495
|
+
function codexRuntimeOptionsFromModelList(value) {
|
|
496
|
+
const rawModels = codexModelsFromModelList(value);
|
|
497
|
+
const seen = new Set();
|
|
498
|
+
const models = rawModels.flatMap((item) => {
|
|
499
|
+
const model = recordFrom(item);
|
|
500
|
+
const id = codexModelId(model)?.trim();
|
|
501
|
+
if (!id || seen.has(id))
|
|
502
|
+
return [];
|
|
503
|
+
seen.add(id);
|
|
504
|
+
const label = stringField(model, "label", "displayName", "name");
|
|
505
|
+
return [{ id, ...(label ? { label } : {}) }];
|
|
506
|
+
});
|
|
507
|
+
if (models.length === 0)
|
|
508
|
+
return undefined;
|
|
509
|
+
const defaultModel = rawModels
|
|
510
|
+
.map((item) => recordFrom(item))
|
|
511
|
+
.find((item) => item.isDefault === true);
|
|
512
|
+
const defaultModelId = defaultModel ? codexModelId(defaultModel)?.trim() : undefined;
|
|
513
|
+
return {
|
|
514
|
+
...(defaultModelId ? { defaultModel: defaultModelId } : {}),
|
|
515
|
+
models,
|
|
516
|
+
};
|
|
517
|
+
}
|
|
418
518
|
//# sourceMappingURL=codex-app-server.js.map
|
|
@@ -5,7 +5,12 @@ export type CliRuntimeProviderOptions = {
|
|
|
5
5
|
cwd: string;
|
|
6
6
|
stateDir: string;
|
|
7
7
|
codexCommand?: string;
|
|
8
|
+
/** Maximum total time for the provider-level Codex catalog probe. */
|
|
9
|
+
codexDiscoveryTimeoutMs?: number;
|
|
8
10
|
claudeCommand?: string;
|
|
9
11
|
};
|
|
10
|
-
export declare function createCliRuntimeProvider(options: CliRuntimeProviderOptions): HappyElvesRuntime
|
|
12
|
+
export declare function createCliRuntimeProvider(options: CliRuntimeProviderOptions): HappyElvesRuntime & {
|
|
13
|
+
initialize(): Promise<void>;
|
|
14
|
+
diagnostics(): Array<Record<string, unknown>>;
|
|
15
|
+
};
|
|
11
16
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -3,6 +3,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import { readClaudeHistoricalEvents, readClaudeHistoricalSessionPage } from "./claude-history.js";
|
|
5
5
|
import { renameClaudeSession } from "./claude-rename.js";
|
|
6
|
+
import { discoverCodexRuntimeOptions } from "./codex-app-server.js";
|
|
6
7
|
import { forkAndTruncateClaudeSession, forkClaudeSession } from "./claude-session-store.js";
|
|
7
8
|
import { startClaudeTurn } from "./claude-turn.js";
|
|
8
9
|
import { readCodexCanonicalHistoricalSessionPage, readCodexHistoricalEvents } from "./codex-history.js";
|
|
@@ -13,6 +14,15 @@ export { HistoryCursorStaleError, isHistoryCursorStaleError, mergeHistoricalSess
|
|
|
13
14
|
export function createCliRuntimeProvider(options) {
|
|
14
15
|
const codexCommand = options.codexCommand ?? process.env.HAPPY_ELVES_CODEX_COMMAND ?? "codex";
|
|
15
16
|
const claudeCommand = options.claudeCommand ?? process.env.HAPPY_ELVES_CLAUDE_COMMAND ?? "claude";
|
|
17
|
+
let codexOptions = codexAgentModels();
|
|
18
|
+
let codexModelDiscovery = {
|
|
19
|
+
provider: "cli",
|
|
20
|
+
component: "codex-model-discovery",
|
|
21
|
+
status: "not-run",
|
|
22
|
+
source: codexOptions.models && codexOptions.models.length > 0 ? "fallback" : undefined,
|
|
23
|
+
modelCount: codexOptions.models?.length ?? 0,
|
|
24
|
+
...(codexOptions.defaultModel ? { defaultModel: codexOptions.defaultModel } : {}),
|
|
25
|
+
};
|
|
16
26
|
const sessions = new CliSessionStore({ cwd: options.cwd, codexCommand });
|
|
17
27
|
const historySnapshotDir = path.join(options.stateDir, "history-snapshots");
|
|
18
28
|
async function listHistoricalSessionPage(input) {
|
|
@@ -66,6 +76,75 @@ export function createCliRuntimeProvider(options) {
|
|
|
66
76
|
}), loadedByBackendId);
|
|
67
77
|
}
|
|
68
78
|
return {
|
|
79
|
+
async initialize() {
|
|
80
|
+
const configured = agentModelsFromEnv("HAPPY_ELVES_CODEX");
|
|
81
|
+
if (configured.models && configured.models.length > 0) {
|
|
82
|
+
codexOptions = configured;
|
|
83
|
+
codexModelDiscovery = {
|
|
84
|
+
provider: "cli",
|
|
85
|
+
component: "codex-model-discovery",
|
|
86
|
+
status: "configured",
|
|
87
|
+
source: "env",
|
|
88
|
+
modelCount: configured.models.length,
|
|
89
|
+
...(configured.defaultModel ? { defaultModel: configured.defaultModel } : {}),
|
|
90
|
+
};
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const startedAt = Date.now();
|
|
94
|
+
const discovered = await discoverCodexRuntimeOptions({
|
|
95
|
+
command: codexCommand,
|
|
96
|
+
cwd: options.cwd,
|
|
97
|
+
timeoutMs: options.codexDiscoveryTimeoutMs,
|
|
98
|
+
}).catch((error) => {
|
|
99
|
+
codexModelDiscovery = {
|
|
100
|
+
provider: "cli",
|
|
101
|
+
component: "codex-model-discovery",
|
|
102
|
+
status: "failed",
|
|
103
|
+
source: "provider",
|
|
104
|
+
elapsedMs: Date.now() - startedAt,
|
|
105
|
+
error: error instanceof Error ? error.message : String(error),
|
|
106
|
+
};
|
|
107
|
+
return undefined;
|
|
108
|
+
});
|
|
109
|
+
if (discovered?.models && discovered.models.length > 0) {
|
|
110
|
+
codexOptions = {
|
|
111
|
+
...(configured.defaultModel ? { defaultModel: configured.defaultModel } : discovered.defaultModel ? { defaultModel: discovered.defaultModel } : {}),
|
|
112
|
+
models: discovered.models,
|
|
113
|
+
};
|
|
114
|
+
codexModelDiscovery = {
|
|
115
|
+
provider: "cli",
|
|
116
|
+
component: "codex-model-discovery",
|
|
117
|
+
status: "ok",
|
|
118
|
+
source: "provider",
|
|
119
|
+
elapsedMs: Date.now() - startedAt,
|
|
120
|
+
modelCount: discovered.models.length,
|
|
121
|
+
...(codexOptions.defaultModel ? { defaultModel: codexOptions.defaultModel } : {}),
|
|
122
|
+
};
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
codexOptions = codexAgentModels();
|
|
126
|
+
if (codexModelDiscovery.status !== "failed") {
|
|
127
|
+
codexModelDiscovery = {
|
|
128
|
+
provider: "cli",
|
|
129
|
+
component: "codex-model-discovery",
|
|
130
|
+
status: "failed",
|
|
131
|
+
source: "provider",
|
|
132
|
+
elapsedMs: Date.now() - startedAt,
|
|
133
|
+
error: "Codex provider model/list returned no models",
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
if (codexOptions.models && codexOptions.models.length > 0) {
|
|
137
|
+
codexModelDiscovery = {
|
|
138
|
+
...codexModelDiscovery,
|
|
139
|
+
source: "fallback",
|
|
140
|
+
modelCount: codexOptions.models.length,
|
|
141
|
+
...(codexOptions.defaultModel ? { defaultModel: codexOptions.defaultModel } : {}),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
diagnostics() {
|
|
146
|
+
return [codexModelDiscovery];
|
|
147
|
+
},
|
|
69
148
|
getCapabilities() {
|
|
70
149
|
return {
|
|
71
150
|
provider: "cli",
|
|
@@ -75,7 +154,7 @@ export function createCliRuntimeProvider(options) {
|
|
|
75
154
|
},
|
|
76
155
|
listAgents() {
|
|
77
156
|
return [
|
|
78
|
-
{ id: "codex", label: "Codex", ...
|
|
157
|
+
{ id: "codex", label: "Codex", ...codexOptions },
|
|
79
158
|
{ id: "claude", label: "Claude Code", ...agentModelsFromEnv("HAPPY_ELVES_CLAUDE") },
|
|
80
159
|
];
|
|
81
160
|
},
|