lunel-cli 0.1.115 → 0.1.117
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/dist/ai/codex.d.ts +3 -0
- package/dist/ai/codex.js +83 -2
- package/dist/ai/opencode.d.ts +3 -0
- package/dist/ai/opencode.js +25 -10
- package/package.json +1 -1
package/dist/ai/codex.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export declare class CodexProvider implements AIProvider {
|
|
|
13
13
|
private pendingQuestionRequestIds;
|
|
14
14
|
private assistantMessageIdByTurnId;
|
|
15
15
|
private partTextById;
|
|
16
|
+
private debugLog;
|
|
17
|
+
private debugHistory;
|
|
16
18
|
init(): Promise<void>;
|
|
17
19
|
destroy(): Promise<void>;
|
|
18
20
|
subscribe(emitter: AiEventEmitter): () => void;
|
|
@@ -87,6 +89,7 @@ export declare class CodexProvider implements AIProvider {
|
|
|
87
89
|
private resolveSessionFromPayload;
|
|
88
90
|
private resolveInFlightTurnId;
|
|
89
91
|
private decodeMessagesFromThreadRead;
|
|
92
|
+
private logThreadReadSummary;
|
|
90
93
|
private decodeStoredToolLikePart;
|
|
91
94
|
private describeStoredToolName;
|
|
92
95
|
private extractStoredToolInput;
|
package/dist/ai/codex.js
CHANGED
|
@@ -51,6 +51,17 @@ export class CodexProvider {
|
|
|
51
51
|
pendingQuestionRequestIds = new Map();
|
|
52
52
|
assistantMessageIdByTurnId = new Map();
|
|
53
53
|
partTextById = new Map();
|
|
54
|
+
debugLog(message, fields) {
|
|
55
|
+
if (!DEBUG_MODE)
|
|
56
|
+
return;
|
|
57
|
+
const suffix = fields ? ` ${JSON.stringify(fields)}` : "";
|
|
58
|
+
console.log(`[codex] ${message}${suffix}`);
|
|
59
|
+
}
|
|
60
|
+
debugHistory(message, fields) {
|
|
61
|
+
if (!DEBUG_MODE)
|
|
62
|
+
return;
|
|
63
|
+
console.log(`[codex-history] ${message} ${JSON.stringify(fields)}`);
|
|
64
|
+
}
|
|
54
65
|
async init() {
|
|
55
66
|
if (DEBUG_MODE)
|
|
56
67
|
console.log("Starting Codex app-server...");
|
|
@@ -100,7 +111,10 @@ export class CodexProvider {
|
|
|
100
111
|
};
|
|
101
112
|
}
|
|
102
113
|
async createSession(title) {
|
|
103
|
-
const result = await this.call("thread/start", {
|
|
114
|
+
const result = await this.call("thread/start", {
|
|
115
|
+
cwd: process.cwd(),
|
|
116
|
+
persistExtendedHistory: true,
|
|
117
|
+
});
|
|
104
118
|
const threadObject = this.extractThreadObject(result);
|
|
105
119
|
const threadId = this.extractThreadId(threadObject);
|
|
106
120
|
if (!threadId) {
|
|
@@ -207,6 +221,11 @@ export class CodexProvider {
|
|
|
207
221
|
}
|
|
208
222
|
async getMessages(sessionId) {
|
|
209
223
|
const session = this.ensureLocalSession(sessionId);
|
|
224
|
+
this.debugLog("getMessages start", {
|
|
225
|
+
sessionId,
|
|
226
|
+
existingMessageCount: session.messages.length,
|
|
227
|
+
existingPartCount: session.messages.reduce((sum, message) => sum + (message.parts?.length || 0), 0),
|
|
228
|
+
});
|
|
210
229
|
const result = await this.call("thread/read", {
|
|
211
230
|
threadId: session.id,
|
|
212
231
|
includeTurns: true,
|
|
@@ -215,7 +234,16 @@ export class CodexProvider {
|
|
|
215
234
|
if (!threadObject) {
|
|
216
235
|
return { messages: session.messages };
|
|
217
236
|
}
|
|
237
|
+
this.logThreadReadSummary(sessionId, threadObject);
|
|
218
238
|
const historyMessages = this.decodeMessagesFromThreadRead(sessionId, threadObject);
|
|
239
|
+
this.debugLog("getMessages decoded thread", {
|
|
240
|
+
sessionId,
|
|
241
|
+
turnCount: this.readArray(threadObject.turns).length,
|
|
242
|
+
decodedMessageCount: historyMessages.length,
|
|
243
|
+
decodedPartCount: historyMessages.reduce((sum, message) => sum + (message.parts?.length || 0), 0),
|
|
244
|
+
decodedRoles: historyMessages.map((message) => message.role),
|
|
245
|
+
decodedPartTypes: historyMessages.flatMap((message) => (message.parts || []).map((part) => String(this.asRecord(part).type ?? "unknown"))),
|
|
246
|
+
});
|
|
219
247
|
if (historyMessages.length > 0) {
|
|
220
248
|
session.messages = historyMessages;
|
|
221
249
|
}
|
|
@@ -1107,7 +1135,10 @@ export class CodexProvider {
|
|
|
1107
1135
|
return;
|
|
1108
1136
|
}
|
|
1109
1137
|
const session = this.sessions.get(threadId);
|
|
1110
|
-
const params = {
|
|
1138
|
+
const params = {
|
|
1139
|
+
threadId,
|
|
1140
|
+
persistExtendedHistory: true,
|
|
1141
|
+
};
|
|
1111
1142
|
if (session?.cwd) {
|
|
1112
1143
|
params.cwd = session.cwd;
|
|
1113
1144
|
}
|
|
@@ -1174,9 +1205,11 @@ export class CodexProvider {
|
|
|
1174
1205
|
const assistantParts = [];
|
|
1175
1206
|
let assistantMessageId;
|
|
1176
1207
|
let assistantTimestamp = turnTime;
|
|
1208
|
+
const turnItemTypes = [];
|
|
1177
1209
|
for (const item of items) {
|
|
1178
1210
|
const itemObject = this.asRecord(item);
|
|
1179
1211
|
const type = this.normalizedItemType(this.readString(itemObject.type) ?? "");
|
|
1212
|
+
turnItemTypes.push(type || "unknown");
|
|
1180
1213
|
const itemId = this.readString(itemObject.id) ?? crypto.randomUUID();
|
|
1181
1214
|
const timestamp = this.extractUpdatedAt(itemObject) ?? this.extractCreatedAt(itemObject) ?? (turnTime + orderOffset++);
|
|
1182
1215
|
if (type === "usermessage") {
|
|
@@ -1239,6 +1272,7 @@ export class CodexProvider {
|
|
|
1239
1272
|
|| type === "mcptoolcall"
|
|
1240
1273
|
|| type === "dynamictoolcall"
|
|
1241
1274
|
|| type === "collabtoolcall"
|
|
1275
|
+
|| type === "collabagenttoolcall"
|
|
1242
1276
|
|| type === "websearch"
|
|
1243
1277
|
|| type === "imageview") {
|
|
1244
1278
|
assistantMessageId = assistantMessageId ?? (turnId ? `assistant:${turnId}` : itemId);
|
|
@@ -1268,9 +1302,53 @@ export class CodexProvider {
|
|
|
1268
1302
|
this.assistantMessageIdByTurnId.set(turnId, resolvedAssistantMessageId);
|
|
1269
1303
|
}
|
|
1270
1304
|
}
|
|
1305
|
+
this.debugLog("decoded stored turn", {
|
|
1306
|
+
threadId,
|
|
1307
|
+
turnId: turnId ?? null,
|
|
1308
|
+
itemCount: items.length,
|
|
1309
|
+
itemTypes: turnItemTypes,
|
|
1310
|
+
emittedUserMessages: messages.filter((message) => message.role === "user").length,
|
|
1311
|
+
emittedAssistantParts: assistantParts.length,
|
|
1312
|
+
});
|
|
1271
1313
|
}
|
|
1272
1314
|
return messages;
|
|
1273
1315
|
}
|
|
1316
|
+
logThreadReadSummary(sessionId, threadObject) {
|
|
1317
|
+
const turns = this.readArray(threadObject.turns);
|
|
1318
|
+
const turnSummaries = turns.map((turn, index) => {
|
|
1319
|
+
const turnObject = this.asRecord(turn);
|
|
1320
|
+
const items = this.readArray(turnObject.items);
|
|
1321
|
+
return {
|
|
1322
|
+
index,
|
|
1323
|
+
turnId: this.readString(turnObject.id) ?? null,
|
|
1324
|
+
status: this.readString(turnObject.status) ?? this.readString(this.asRecord(turnObject.status).type) ?? null,
|
|
1325
|
+
itemCount: items.length,
|
|
1326
|
+
itemTypes: items.map((item) => this.normalizedItemType(this.readString(this.asRecord(item).type) ?? "") || "unknown"),
|
|
1327
|
+
itemSummaries: items.map((item) => {
|
|
1328
|
+
const itemObject = this.asRecord(item);
|
|
1329
|
+
const type = this.normalizedItemType(this.readString(itemObject.type) ?? "") || "unknown";
|
|
1330
|
+
return {
|
|
1331
|
+
id: this.readString(itemObject.id) ?? null,
|
|
1332
|
+
type,
|
|
1333
|
+
keys: Object.keys(itemObject).sort(),
|
|
1334
|
+
textPreview: this.firstString(itemObject, ["text", "summary", "message", "query", "path", "tool", "command"])?.slice(0, 120) ?? null,
|
|
1335
|
+
hasAggregatedOutput: typeof itemObject.aggregatedOutput === "string" && itemObject.aggregatedOutput.length > 0,
|
|
1336
|
+
aggregatedOutputLength: typeof itemObject.aggregatedOutput === "string" ? itemObject.aggregatedOutput.length : 0,
|
|
1337
|
+
changesCount: Array.isArray(itemObject.changes) ? itemObject.changes.length : 0,
|
|
1338
|
+
hasResult: itemObject.result != null,
|
|
1339
|
+
hasContentItems: Array.isArray(itemObject.contentItems) && itemObject.contentItems.length > 0,
|
|
1340
|
+
status: this.readString(itemObject.status) ?? null,
|
|
1341
|
+
};
|
|
1342
|
+
}),
|
|
1343
|
+
};
|
|
1344
|
+
});
|
|
1345
|
+
this.debugHistory("thread/read summary", {
|
|
1346
|
+
sessionId,
|
|
1347
|
+
threadId: this.readString(threadObject.id) ?? null,
|
|
1348
|
+
turnCount: turns.length,
|
|
1349
|
+
turnSummaries,
|
|
1350
|
+
});
|
|
1351
|
+
}
|
|
1274
1352
|
decodeStoredToolLikePart(type, itemObject, threadId, messageId, itemId) {
|
|
1275
1353
|
if (type === "filechange" || type === "diff" || this.isFileChangeStructuredItem(type, itemObject)) {
|
|
1276
1354
|
const output = this.decodeFileLikeItemText(itemObject) ?? this.describeCompletedItemOutput(itemObject, itemObject, type) ?? "File changes";
|
|
@@ -1312,6 +1390,9 @@ export class CodexProvider {
|
|
|
1312
1390
|
if (type === "imageview") {
|
|
1313
1391
|
return "image-view";
|
|
1314
1392
|
}
|
|
1393
|
+
if (type === "collabtoolcall" || type === "collabagenttoolcall") {
|
|
1394
|
+
return "agent";
|
|
1395
|
+
}
|
|
1315
1396
|
if (type === "enteredreviewmode") {
|
|
1316
1397
|
return "review";
|
|
1317
1398
|
}
|
package/dist/ai/opencode.d.ts
CHANGED
|
@@ -8,6 +8,9 @@ export declare class OpenCodeProvider implements AIProvider {
|
|
|
8
8
|
private emitter;
|
|
9
9
|
private knownPendingPermissionIds;
|
|
10
10
|
private knownPendingQuestionIds;
|
|
11
|
+
private debugLog;
|
|
12
|
+
private debugWarn;
|
|
13
|
+
private debugError;
|
|
11
14
|
init(): Promise<void>;
|
|
12
15
|
destroy(): Promise<void>;
|
|
13
16
|
subscribe(emitter: AiEventEmitter): () => void;
|
package/dist/ai/opencode.js
CHANGED
|
@@ -262,6 +262,21 @@ export class OpenCodeProvider {
|
|
|
262
262
|
emitter = null;
|
|
263
263
|
knownPendingPermissionIds = new Set();
|
|
264
264
|
knownPendingQuestionIds = new Set();
|
|
265
|
+
debugLog(message, ...args) {
|
|
266
|
+
if (!VERBOSE_AI_LOGS)
|
|
267
|
+
return;
|
|
268
|
+
console.log(message, ...args);
|
|
269
|
+
}
|
|
270
|
+
debugWarn(message, ...args) {
|
|
271
|
+
if (!VERBOSE_AI_LOGS)
|
|
272
|
+
return;
|
|
273
|
+
console.warn(message, ...args);
|
|
274
|
+
}
|
|
275
|
+
debugError(message, ...args) {
|
|
276
|
+
if (!VERBOSE_AI_LOGS)
|
|
277
|
+
return;
|
|
278
|
+
console.error(message, ...args);
|
|
279
|
+
}
|
|
265
280
|
async init() {
|
|
266
281
|
const opencodeUsername = "lunel";
|
|
267
282
|
const opencodePassword = crypto.randomBytes(32).toString("base64url");
|
|
@@ -521,13 +536,13 @@ export class OpenCodeProvider {
|
|
|
521
536
|
path: { id: this.lastActiveSessionId },
|
|
522
537
|
});
|
|
523
538
|
if (checkResp.error) {
|
|
524
|
-
|
|
539
|
+
this.debugWarn(`[sse] OpenCode session ${this.lastActiveSessionId} was garbage-collected. Notifying app.`);
|
|
525
540
|
const gcSessionId = this.lastActiveSessionId;
|
|
526
541
|
this.lastActiveSessionId = null;
|
|
527
542
|
this.emitter?.({ type: "session_gc", properties: { sessionId: gcSessionId } });
|
|
528
543
|
}
|
|
529
544
|
else {
|
|
530
|
-
|
|
545
|
+
this.debugLog(`[sse] Active session ${this.lastActiveSessionId} still valid.`);
|
|
531
546
|
}
|
|
532
547
|
}
|
|
533
548
|
if (attempt > 0) {
|
|
@@ -535,7 +550,7 @@ export class OpenCodeProvider {
|
|
|
535
550
|
}
|
|
536
551
|
const events = await this.client.event.subscribe();
|
|
537
552
|
if (attempt > 0) {
|
|
538
|
-
|
|
553
|
+
this.debugLog(`[sse] reconnected after ${attempt} attempt(s)`);
|
|
539
554
|
}
|
|
540
555
|
attempt = 0;
|
|
541
556
|
for await (const raw of events.stream) {
|
|
@@ -549,11 +564,11 @@ export class OpenCodeProvider {
|
|
|
549
564
|
? parsed.payload
|
|
550
565
|
: parsed;
|
|
551
566
|
if (!base || typeof base.type !== "string") {
|
|
552
|
-
|
|
567
|
+
this.debugWarn("[sse] Dropped malformed event:", redactSensitive(JSON.stringify(parsed).substring(0, 200)));
|
|
553
568
|
continue;
|
|
554
569
|
}
|
|
555
570
|
if (base.type !== "server.heartbeat") {
|
|
556
|
-
|
|
571
|
+
this.debugLog("[sse]", base.type);
|
|
557
572
|
}
|
|
558
573
|
const normalizedEvent = normalizeOpenCodeEvent({
|
|
559
574
|
type: base.type,
|
|
@@ -562,7 +577,7 @@ export class OpenCodeProvider {
|
|
|
562
577
|
this.trackPermissionEvent(normalizedEvent.type, normalizedEvent.properties || {});
|
|
563
578
|
this.emitter?.(normalizedEvent);
|
|
564
579
|
}
|
|
565
|
-
|
|
580
|
+
this.debugLog("[sse] Event stream ended, reconnecting...");
|
|
566
581
|
attempt++;
|
|
567
582
|
}
|
|
568
583
|
catch (err) {
|
|
@@ -570,9 +585,9 @@ export class OpenCodeProvider {
|
|
|
570
585
|
return;
|
|
571
586
|
attempt++;
|
|
572
587
|
const delay = backoffMs(attempt - 1);
|
|
573
|
-
|
|
588
|
+
this.debugError(`[sse] Stream error (attempt ${attempt}/${SSE_MAX_RETRIES}): ${err.message}. Retrying in ${delay}ms`);
|
|
574
589
|
if (attempt >= SSE_MAX_RETRIES) {
|
|
575
|
-
|
|
590
|
+
this.debugError("[sse] Max retries reached. Sending error event to app and giving up.");
|
|
576
591
|
this.emitter?.({
|
|
577
592
|
type: "sse_dead",
|
|
578
593
|
properties: { error: err.message, attempts: attempt },
|
|
@@ -668,10 +683,10 @@ export class OpenCodeProvider {
|
|
|
668
683
|
});
|
|
669
684
|
}
|
|
670
685
|
}
|
|
671
|
-
|
|
686
|
+
this.debugLog(`[sse] Re-synced messages for busy session ${sessionId} after reconnect`);
|
|
672
687
|
}
|
|
673
688
|
catch (err) {
|
|
674
|
-
|
|
689
|
+
this.debugWarn(`[sse] Failed to refresh messages for busy session ${sessionId}:`, err.message);
|
|
675
690
|
}
|
|
676
691
|
}
|
|
677
692
|
}
|