lunel-cli 0.1.113 → 0.1.115
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 +19 -4
- package/dist/ai/codex.js +470 -95
- package/dist/ai/index.d.ts +5 -2
- package/dist/ai/index.js +3 -2
- package/dist/ai/interface.d.ts +9 -1
- package/dist/ai/opencode.d.ts +7 -3
- package/dist/ai/opencode.js +319 -35
- package/dist/index.js +126 -7
- package/package.json +1 -1
package/dist/ai/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AiEvent, ModelSelector, FileAttachment } from "./interface.js";
|
|
1
|
+
import type { AiEvent, ModelSelector, FileAttachment, CodexPromptOptions } from "./interface.js";
|
|
2
2
|
export type AiBackend = "opencode" | "codex";
|
|
3
3
|
export declare class AiManager {
|
|
4
4
|
private _providers;
|
|
@@ -23,10 +23,13 @@ export declare class AiManager {
|
|
|
23
23
|
deleteSession(backend: AiBackend, id: string): Promise<{
|
|
24
24
|
deleted: boolean;
|
|
25
25
|
}>;
|
|
26
|
+
renameSession(backend: AiBackend, id: string, title: string): Promise<{
|
|
27
|
+
session: import("./interface.js").SessionInfo;
|
|
28
|
+
}>;
|
|
26
29
|
getMessages(backend: AiBackend, sessionId: string): Promise<{
|
|
27
30
|
messages: import("./interface.js").MessageInfo[];
|
|
28
31
|
}>;
|
|
29
|
-
prompt(backend: AiBackend, sessionId: string, text: string, model?: ModelSelector, agent?: string, files?: FileAttachment[]): Promise<{
|
|
32
|
+
prompt(backend: AiBackend, sessionId: string, text: string, model?: ModelSelector, agent?: string, files?: FileAttachment[], codexOptions?: CodexPromptOptions): Promise<{
|
|
30
33
|
ack: true;
|
|
31
34
|
}>;
|
|
32
35
|
abort(backend: AiBackend, sessionId: string): Promise<Record<string, never>>;
|
package/dist/ai/index.js
CHANGED
|
@@ -72,10 +72,11 @@ export class AiManager {
|
|
|
72
72
|
createSession(backend, title) { return this.get(backend).createSession(title); }
|
|
73
73
|
getSession(backend, id) { return this.get(backend).getSession(id); }
|
|
74
74
|
deleteSession(backend, id) { return this.get(backend).deleteSession(id); }
|
|
75
|
+
renameSession(backend, id, title) { return this.get(backend).renameSession(id, title); }
|
|
75
76
|
getMessages(backend, sessionId) { return this.get(backend).getMessages(sessionId); }
|
|
76
|
-
prompt(backend, sessionId, text, model, agent, files) {
|
|
77
|
+
prompt(backend, sessionId, text, model, agent, files, codexOptions) {
|
|
77
78
|
this.get(backend).setActiveSession?.(sessionId);
|
|
78
|
-
return this.get(backend).prompt(sessionId, text, model, agent, files);
|
|
79
|
+
return this.get(backend).prompt(sessionId, text, model, agent, files, codexOptions);
|
|
79
80
|
}
|
|
80
81
|
abort(backend, sessionId) { return this.get(backend).abort(sessionId); }
|
|
81
82
|
// Metadata — backend is optional, falls back to first available
|
package/dist/ai/interface.d.ts
CHANGED
|
@@ -7,6 +7,11 @@ export interface ModelSelector {
|
|
|
7
7
|
providerID: string;
|
|
8
8
|
modelID: string;
|
|
9
9
|
}
|
|
10
|
+
export interface CodexPromptOptions {
|
|
11
|
+
reasoningEffort?: "low" | "medium" | "high";
|
|
12
|
+
speed?: "fast" | "balanced" | "quality";
|
|
13
|
+
permissionMode?: "default" | "full-access";
|
|
14
|
+
}
|
|
10
15
|
export interface FileAttachment {
|
|
11
16
|
type: "file";
|
|
12
17
|
mime: string;
|
|
@@ -56,10 +61,13 @@ export interface AIProvider {
|
|
|
56
61
|
deleteSession(id: string): Promise<{
|
|
57
62
|
deleted: boolean;
|
|
58
63
|
}>;
|
|
64
|
+
renameSession(id: string, title: string): Promise<{
|
|
65
|
+
session: SessionInfo;
|
|
66
|
+
}>;
|
|
59
67
|
getMessages(sessionId: string): Promise<{
|
|
60
68
|
messages: MessageInfo[];
|
|
61
69
|
}>;
|
|
62
|
-
prompt(sessionId: string, text: string, model?: ModelSelector, agent?: string, files?: FileAttachment[]): Promise<{
|
|
70
|
+
prompt(sessionId: string, text: string, model?: ModelSelector, agent?: string, files?: FileAttachment[], codexOptions?: CodexPromptOptions): Promise<{
|
|
63
71
|
ack: true;
|
|
64
72
|
}>;
|
|
65
73
|
abort(sessionId: string): Promise<Record<string, never>>;
|
package/dist/ai/opencode.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AIProvider, AiEventEmitter, FileAttachment, ModelSelector, MessageInfo, ProviderInfo, SessionInfo, ShareInfo } from "./interface.js";
|
|
1
|
+
import type { AIProvider, AiEventEmitter, CodexPromptOptions, FileAttachment, ModelSelector, MessageInfo, ProviderInfo, SessionInfo, ShareInfo } from "./interface.js";
|
|
2
2
|
export declare class OpenCodeProvider implements AIProvider {
|
|
3
3
|
private client;
|
|
4
4
|
private server;
|
|
@@ -24,10 +24,13 @@ export declare class OpenCodeProvider implements AIProvider {
|
|
|
24
24
|
deleteSession(id: string): Promise<{
|
|
25
25
|
deleted: boolean;
|
|
26
26
|
}>;
|
|
27
|
+
renameSession(id: string, title: string): Promise<{
|
|
28
|
+
session: SessionInfo;
|
|
29
|
+
}>;
|
|
27
30
|
getMessages(sessionId: string): Promise<{
|
|
28
31
|
messages: MessageInfo[];
|
|
29
32
|
}>;
|
|
30
|
-
prompt(sessionId: string, text: string, model?: ModelSelector, agent?: string, files?: FileAttachment[]): Promise<{
|
|
33
|
+
prompt(sessionId: string, text: string, model?: ModelSelector, agent?: string, files?: FileAttachment[], codexOptions?: CodexPromptOptions): Promise<{
|
|
31
34
|
ack: true;
|
|
32
35
|
}>;
|
|
33
36
|
abort(sessionId: string): Promise<Record<string, never>>;
|
|
@@ -50,12 +53,13 @@ export declare class OpenCodeProvider implements AIProvider {
|
|
|
50
53
|
private runSseLoop;
|
|
51
54
|
private sendPromptAsync;
|
|
52
55
|
private reconcileOpenCodeState;
|
|
56
|
+
private refreshBusySessionMessages;
|
|
53
57
|
private refreshSessionsMetadata;
|
|
54
58
|
private refreshPendingPermissions;
|
|
55
59
|
private refreshPendingQuestions;
|
|
56
60
|
private fetchOpenCodeJson;
|
|
57
61
|
private refreshSessionStatuses;
|
|
58
62
|
private trackPermissionEvent;
|
|
59
|
-
private asRecord;
|
|
60
63
|
private readString;
|
|
64
|
+
private asRecord;
|
|
61
65
|
}
|
package/dist/ai/opencode.js
CHANGED
|
@@ -23,6 +23,236 @@ function requireData(response, label) {
|
|
|
23
23
|
}
|
|
24
24
|
return response.data;
|
|
25
25
|
}
|
|
26
|
+
function asRecord(value) {
|
|
27
|
+
return value && typeof value === "object" ? value : {};
|
|
28
|
+
}
|
|
29
|
+
function readString(value) {
|
|
30
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
31
|
+
}
|
|
32
|
+
function normalizeToolOutput(output, metadata) {
|
|
33
|
+
const attachments = Array.isArray(metadata.attachments) ? metadata.attachments : [];
|
|
34
|
+
if (attachments.length === 0)
|
|
35
|
+
return output;
|
|
36
|
+
const attachmentLines = attachments
|
|
37
|
+
.map((entry) => {
|
|
38
|
+
const file = asRecord(entry);
|
|
39
|
+
const filename = readString(file.filename)
|
|
40
|
+
?? readString(file.path)
|
|
41
|
+
?? readString(file.url)
|
|
42
|
+
?? "attachment";
|
|
43
|
+
return `- ${filename}`;
|
|
44
|
+
})
|
|
45
|
+
.filter((line) => line.trim().length > 0);
|
|
46
|
+
if (attachmentLines.length === 0)
|
|
47
|
+
return output;
|
|
48
|
+
if (!output.trim()) {
|
|
49
|
+
return `Attachments:\n${attachmentLines.join("\n")}`;
|
|
50
|
+
}
|
|
51
|
+
return `${output}\n\nAttachments:\n${attachmentLines.join("\n")}`;
|
|
52
|
+
}
|
|
53
|
+
function buildPatchSummary(part) {
|
|
54
|
+
const hash = readString(part.hash);
|
|
55
|
+
const files = Array.isArray(part.files)
|
|
56
|
+
? part.files.map((value) => String(value)).filter((value) => value.trim().length > 0)
|
|
57
|
+
: [];
|
|
58
|
+
const lines = [];
|
|
59
|
+
if (hash)
|
|
60
|
+
lines.push(`Patch hash: ${hash}`);
|
|
61
|
+
if (files.length > 0) {
|
|
62
|
+
lines.push("Files:");
|
|
63
|
+
for (const file of files)
|
|
64
|
+
lines.push(`- ${file}`);
|
|
65
|
+
}
|
|
66
|
+
return lines.join("\n");
|
|
67
|
+
}
|
|
68
|
+
function normalizeOpenCodePart(part) {
|
|
69
|
+
const raw = asRecord(part);
|
|
70
|
+
const type = readString(raw.type);
|
|
71
|
+
if (!type)
|
|
72
|
+
return raw;
|
|
73
|
+
if (type === "tool") {
|
|
74
|
+
const tool = readString(raw.tool) ?? "tool";
|
|
75
|
+
const state = asRecord(raw.state);
|
|
76
|
+
const status = readString(state.status) ?? "running";
|
|
77
|
+
const metadata = asRecord(state.metadata ?? raw.metadata);
|
|
78
|
+
const normalized = {
|
|
79
|
+
...raw,
|
|
80
|
+
type: "tool",
|
|
81
|
+
toolName: tool,
|
|
82
|
+
name: tool,
|
|
83
|
+
state: status,
|
|
84
|
+
input: asRecord(state.input),
|
|
85
|
+
metadata,
|
|
86
|
+
};
|
|
87
|
+
const title = readString(state.title);
|
|
88
|
+
if (title)
|
|
89
|
+
normalized.title = title;
|
|
90
|
+
const rawText = readString(state.raw);
|
|
91
|
+
if (rawText)
|
|
92
|
+
normalized.raw = rawText;
|
|
93
|
+
const time = asRecord(state.time);
|
|
94
|
+
if (Object.keys(time).length > 0) {
|
|
95
|
+
normalized.time = time;
|
|
96
|
+
}
|
|
97
|
+
if (status === "completed") {
|
|
98
|
+
const output = typeof state.output === "string" ? state.output : "";
|
|
99
|
+
normalized.output = normalizeToolOutput(output, {
|
|
100
|
+
...metadata,
|
|
101
|
+
attachments: state.attachments,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
else if (status === "error") {
|
|
105
|
+
normalized.error = readString(state.error) ?? "Tool failed";
|
|
106
|
+
const errorMessage = readString(state.error);
|
|
107
|
+
if (errorMessage)
|
|
108
|
+
normalized.output = errorMessage;
|
|
109
|
+
}
|
|
110
|
+
const attachments = Array.isArray(state.attachments) ? state.attachments : [];
|
|
111
|
+
if (attachments.length > 0) {
|
|
112
|
+
normalized.attachments = attachments.map((entry) => normalizeOpenCodePart(entry));
|
|
113
|
+
}
|
|
114
|
+
return normalized;
|
|
115
|
+
}
|
|
116
|
+
if (type === "step-start") {
|
|
117
|
+
const snapshot = readString(raw.snapshot);
|
|
118
|
+
return {
|
|
119
|
+
...raw,
|
|
120
|
+
type: "step-start",
|
|
121
|
+
title: snapshot ? `Step started · ${snapshot}` : "Step started",
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
if (type === "step-finish") {
|
|
125
|
+
const reason = readString(raw.reason);
|
|
126
|
+
return {
|
|
127
|
+
...raw,
|
|
128
|
+
type: "step-finish",
|
|
129
|
+
title: reason ? `Step finished · ${reason}` : "Step finished",
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
if (type === "patch") {
|
|
133
|
+
return {
|
|
134
|
+
...raw,
|
|
135
|
+
type: "file-change",
|
|
136
|
+
title: "File changes",
|
|
137
|
+
output: buildPatchSummary(raw),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
if (type === "subtask") {
|
|
141
|
+
return {
|
|
142
|
+
...raw,
|
|
143
|
+
type: "tool",
|
|
144
|
+
toolName: "subtask",
|
|
145
|
+
name: "subtask",
|
|
146
|
+
state: "completed",
|
|
147
|
+
input: {
|
|
148
|
+
prompt: readString(raw.prompt) ?? "",
|
|
149
|
+
description: readString(raw.description) ?? "",
|
|
150
|
+
agent: readString(raw.agent) ?? "",
|
|
151
|
+
...(readString(raw.command) ? { command: readString(raw.command) } : {}),
|
|
152
|
+
},
|
|
153
|
+
output: readString(raw.description) ?? readString(raw.prompt) ?? "Subtask requested",
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
if (type === "agent") {
|
|
157
|
+
const name = readString(raw.name) ?? "Agent";
|
|
158
|
+
return {
|
|
159
|
+
...raw,
|
|
160
|
+
type: "step-start",
|
|
161
|
+
title: `Agent · ${name}`,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
if (type === "retry") {
|
|
165
|
+
const attempt = raw.attempt;
|
|
166
|
+
const error = asRecord(raw.error);
|
|
167
|
+
const message = readString(error.message) ?? "Retry requested";
|
|
168
|
+
return {
|
|
169
|
+
...raw,
|
|
170
|
+
type: "tool",
|
|
171
|
+
toolName: "retry",
|
|
172
|
+
name: "retry",
|
|
173
|
+
state: "error",
|
|
174
|
+
input: {
|
|
175
|
+
attempt,
|
|
176
|
+
},
|
|
177
|
+
error: message,
|
|
178
|
+
output: message,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
if (type === "compaction") {
|
|
182
|
+
const auto = raw.auto === true;
|
|
183
|
+
const overflow = raw.overflow === true;
|
|
184
|
+
return {
|
|
185
|
+
...raw,
|
|
186
|
+
type: "step-start",
|
|
187
|
+
title: `Context compacted${auto ? " · auto" : ""}${overflow ? " · overflow" : ""}`,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
if (type === "snapshot") {
|
|
191
|
+
return {
|
|
192
|
+
...raw,
|
|
193
|
+
type: "step-start",
|
|
194
|
+
title: "Workspace snapshot",
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
return raw;
|
|
198
|
+
}
|
|
199
|
+
function normalizeOpenCodeMessage(message) {
|
|
200
|
+
return {
|
|
201
|
+
id: message.info.id,
|
|
202
|
+
role: message.info.role,
|
|
203
|
+
parts: (message.parts || []).map((part) => normalizeOpenCodePart(part)),
|
|
204
|
+
time: message.info.time,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function normalizePermissionProperties(properties) {
|
|
208
|
+
const tool = asRecord(properties.tool);
|
|
209
|
+
const metadata = properties.metadata && typeof properties.metadata === "object"
|
|
210
|
+
? properties.metadata
|
|
211
|
+
: properties;
|
|
212
|
+
return {
|
|
213
|
+
id: readString(properties.id),
|
|
214
|
+
sessionID: readString(properties.sessionID) ?? readString(properties.sessionId),
|
|
215
|
+
messageID: readString(properties.messageID) ?? readString(tool.messageID),
|
|
216
|
+
callID: readString(properties.callID) ?? readString(tool.callID),
|
|
217
|
+
type: readString(properties.type) ?? readString(properties.permission) ?? "permission",
|
|
218
|
+
title: readString(properties.title)
|
|
219
|
+
?? readString(properties.permission)
|
|
220
|
+
?? "Permission requested",
|
|
221
|
+
metadata,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
function normalizeOpenCodeEvent(event) {
|
|
225
|
+
const { type, properties } = event;
|
|
226
|
+
if (type === "message.part.updated") {
|
|
227
|
+
return {
|
|
228
|
+
type,
|
|
229
|
+
properties: {
|
|
230
|
+
...properties,
|
|
231
|
+
part: normalizeOpenCodePart(properties.part),
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
if (type === "permission.updated" || type === "permission.asked") {
|
|
236
|
+
return {
|
|
237
|
+
type: "permission.updated",
|
|
238
|
+
properties: normalizePermissionProperties(properties),
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
if (type === "permission.replied") {
|
|
242
|
+
return {
|
|
243
|
+
type: "permission.replied",
|
|
244
|
+
properties: {
|
|
245
|
+
sessionID: readString(properties.sessionID) ?? readString(properties.sessionId),
|
|
246
|
+
permissionId: readString(properties.permissionID)
|
|
247
|
+
?? readString(properties.requestID)
|
|
248
|
+
?? readString(properties.permissionId)
|
|
249
|
+
?? readString(properties.id),
|
|
250
|
+
response: readString(properties.response) ?? readString(properties.reply),
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
return event;
|
|
255
|
+
}
|
|
26
256
|
export class OpenCodeProvider {
|
|
27
257
|
client = null;
|
|
28
258
|
server = null;
|
|
@@ -111,7 +341,23 @@ export class OpenCodeProvider {
|
|
|
111
341
|
}
|
|
112
342
|
async deleteSession(id) {
|
|
113
343
|
const response = await this.client.session.delete({ path: { id } });
|
|
114
|
-
|
|
344
|
+
const raw = response;
|
|
345
|
+
if (raw.error) {
|
|
346
|
+
const errMsg = typeof raw.error === "string"
|
|
347
|
+
? raw.error
|
|
348
|
+
: JSON.stringify(raw.error);
|
|
349
|
+
throw new Error(errMsg);
|
|
350
|
+
}
|
|
351
|
+
// Treat any non-error delete response as success. Some SDK/runtime combos
|
|
352
|
+
// return inconsistent boolean payloads despite successful deletion.
|
|
353
|
+
return { deleted: true };
|
|
354
|
+
}
|
|
355
|
+
async renameSession(id, title) {
|
|
356
|
+
const response = await this.client.session.update({
|
|
357
|
+
path: { id },
|
|
358
|
+
body: { title },
|
|
359
|
+
});
|
|
360
|
+
return { session: requireData(response, "session.update") };
|
|
115
361
|
}
|
|
116
362
|
// -------------------------------------------------------------------------
|
|
117
363
|
// Messages
|
|
@@ -122,12 +368,7 @@ export class OpenCodeProvider {
|
|
|
122
368
|
try {
|
|
123
369
|
const response = await this.client.session.messages({ path: { id: sessionId } });
|
|
124
370
|
const raw = requireData(response, "session.messages");
|
|
125
|
-
const messages = raw.map((m) => (
|
|
126
|
-
id: m.info.id,
|
|
127
|
-
role: m.info.role,
|
|
128
|
-
parts: m.parts || [],
|
|
129
|
-
time: m.info.time,
|
|
130
|
-
}));
|
|
371
|
+
const messages = raw.map((m) => normalizeOpenCodeMessage(m));
|
|
131
372
|
if (VERBOSE_AI_LOGS)
|
|
132
373
|
console.log("[ai] getMessages returned", messages.length, "messages");
|
|
133
374
|
return { messages };
|
|
@@ -140,7 +381,8 @@ export class OpenCodeProvider {
|
|
|
140
381
|
// -------------------------------------------------------------------------
|
|
141
382
|
// Interaction
|
|
142
383
|
// -------------------------------------------------------------------------
|
|
143
|
-
async prompt(sessionId, text, model, agent, files = []) {
|
|
384
|
+
async prompt(sessionId, text, model, agent, files = [], codexOptions) {
|
|
385
|
+
void codexOptions;
|
|
144
386
|
if (sessionId)
|
|
145
387
|
this.lastActiveSessionId = sessionId;
|
|
146
388
|
if (VERBOSE_AI_LOGS) {
|
|
@@ -313,10 +555,15 @@ export class OpenCodeProvider {
|
|
|
313
555
|
if (base.type !== "server.heartbeat") {
|
|
314
556
|
console.log("[sse]", base.type);
|
|
315
557
|
}
|
|
316
|
-
|
|
317
|
-
|
|
558
|
+
const normalizedEvent = normalizeOpenCodeEvent({
|
|
559
|
+
type: base.type,
|
|
560
|
+
properties: base.properties || {},
|
|
561
|
+
});
|
|
562
|
+
this.trackPermissionEvent(normalizedEvent.type, normalizedEvent.properties || {});
|
|
563
|
+
this.emitter?.(normalizedEvent);
|
|
318
564
|
}
|
|
319
565
|
console.log("[sse] Event stream ended, reconnecting...");
|
|
566
|
+
attempt++;
|
|
320
567
|
}
|
|
321
568
|
catch (err) {
|
|
322
569
|
if (this.shuttingDown)
|
|
@@ -378,6 +625,55 @@ export class OpenCodeProvider {
|
|
|
378
625
|
this.refreshPendingQuestions(),
|
|
379
626
|
this.refreshSessionStatuses(),
|
|
380
627
|
]);
|
|
628
|
+
await this.refreshBusySessionMessages();
|
|
629
|
+
}
|
|
630
|
+
async refreshBusySessionMessages() {
|
|
631
|
+
const server = this.server;
|
|
632
|
+
const authHeader = this.authHeader;
|
|
633
|
+
if (!server || !authHeader)
|
|
634
|
+
return;
|
|
635
|
+
const statusUrl = new URL("/session/status", server.url);
|
|
636
|
+
const statusResp = await fetch(statusUrl, {
|
|
637
|
+
headers: { Authorization: authHeader, accept: "application/json" },
|
|
638
|
+
}).catch(() => null);
|
|
639
|
+
if (!statusResp?.ok)
|
|
640
|
+
return;
|
|
641
|
+
const payload = await statusResp.json().catch(() => null);
|
|
642
|
+
if (!payload || typeof payload !== "object")
|
|
643
|
+
return;
|
|
644
|
+
for (const [sessionId, status] of Object.entries(payload)) {
|
|
645
|
+
const statusObj = status;
|
|
646
|
+
const statusType = typeof statusObj?.type === "string" ? statusObj.type.toLowerCase() : "";
|
|
647
|
+
if (statusType !== "busy")
|
|
648
|
+
continue;
|
|
649
|
+
try {
|
|
650
|
+
const response = await this.client.session.messages({ path: { id: sessionId } });
|
|
651
|
+
const raw = Array.isArray(response.data) ? response.data : [];
|
|
652
|
+
for (const m of raw) {
|
|
653
|
+
const msgObj = this.asRecord(m);
|
|
654
|
+
const info = this.asRecord(msgObj.info);
|
|
655
|
+
const parts = Array.isArray(msgObj.parts) ? msgObj.parts : [];
|
|
656
|
+
const msgId = this.readString(info.id);
|
|
657
|
+
if (!msgId)
|
|
658
|
+
continue;
|
|
659
|
+
this.emitter?.({ type: "message.updated", properties: { info } });
|
|
660
|
+
for (const part of parts) {
|
|
661
|
+
const partObj = normalizeOpenCodePart(part);
|
|
662
|
+
this.emitter?.({
|
|
663
|
+
type: "message.part.updated",
|
|
664
|
+
properties: {
|
|
665
|
+
part: { ...partObj, sessionID: sessionId, messageID: msgId },
|
|
666
|
+
message: { sessionID: sessionId, id: msgId, role: info.role },
|
|
667
|
+
},
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
console.log(`[sse] Re-synced messages for busy session ${sessionId} after reconnect`);
|
|
672
|
+
}
|
|
673
|
+
catch (err) {
|
|
674
|
+
console.warn(`[sse] Failed to refresh messages for busy session ${sessionId}:`, err.message);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
381
677
|
}
|
|
382
678
|
async refreshSessionsMetadata() {
|
|
383
679
|
const response = await this.client.session.list();
|
|
@@ -413,19 +709,7 @@ export class OpenCodeProvider {
|
|
|
413
709
|
this.knownPendingPermissionIds.add(id);
|
|
414
710
|
this.emitter?.({
|
|
415
711
|
type: "permission.updated",
|
|
416
|
-
properties:
|
|
417
|
-
id,
|
|
418
|
-
sessionID: this.readString(permission.sessionID) ?? this.readString(permission.sessionId),
|
|
419
|
-
messageID: this.readString(this.asRecord(permission.tool).messageID),
|
|
420
|
-
callID: this.readString(this.asRecord(permission.tool).callID),
|
|
421
|
-
type: this.readString(permission.permission) ?? "permission",
|
|
422
|
-
title: this.readString(permission.title)
|
|
423
|
-
?? this.readString(permission.permission)
|
|
424
|
-
?? "Permission requested",
|
|
425
|
-
metadata: permission.metadata && typeof permission.metadata === "object"
|
|
426
|
-
? permission.metadata
|
|
427
|
-
: permission,
|
|
428
|
-
},
|
|
712
|
+
properties: normalizePermissionProperties(permission),
|
|
429
713
|
});
|
|
430
714
|
}
|
|
431
715
|
for (const id of Array.from(this.knownPendingPermissionIds)) {
|
|
@@ -529,40 +813,40 @@ export class OpenCodeProvider {
|
|
|
529
813
|
}
|
|
530
814
|
trackPermissionEvent(type, properties) {
|
|
531
815
|
if (type === "permission.updated") {
|
|
532
|
-
const id =
|
|
816
|
+
const id = readString(properties.id);
|
|
533
817
|
if (id) {
|
|
534
818
|
this.knownPendingPermissionIds.add(id);
|
|
535
819
|
}
|
|
536
820
|
return;
|
|
537
821
|
}
|
|
538
822
|
if (type === "permission.replied") {
|
|
539
|
-
const id =
|
|
540
|
-
??
|
|
541
|
-
??
|
|
823
|
+
const id = readString(properties.permissionId)
|
|
824
|
+
?? readString(properties.requestID)
|
|
825
|
+
?? readString(properties.id);
|
|
542
826
|
if (id) {
|
|
543
827
|
this.knownPendingPermissionIds.delete(id);
|
|
544
828
|
}
|
|
545
829
|
}
|
|
546
830
|
if (type === "question.asked") {
|
|
547
|
-
const id =
|
|
831
|
+
const id = readString(properties.id);
|
|
548
832
|
if (id) {
|
|
549
833
|
this.knownPendingQuestionIds.add(id);
|
|
550
834
|
}
|
|
551
835
|
return;
|
|
552
836
|
}
|
|
553
837
|
if (type === "question.replied" || type === "question.rejected") {
|
|
554
|
-
const id =
|
|
555
|
-
??
|
|
556
|
-
??
|
|
838
|
+
const id = readString(properties.requestID)
|
|
839
|
+
?? readString(properties.questionId)
|
|
840
|
+
?? readString(properties.id);
|
|
557
841
|
if (id) {
|
|
558
842
|
this.knownPendingQuestionIds.delete(id);
|
|
559
843
|
}
|
|
560
844
|
}
|
|
561
845
|
}
|
|
562
|
-
asRecord(value) {
|
|
563
|
-
return value && typeof value === "object" ? value : {};
|
|
564
|
-
}
|
|
565
846
|
readString(value) {
|
|
566
|
-
return
|
|
847
|
+
return readString(value);
|
|
848
|
+
}
|
|
849
|
+
asRecord(value) {
|
|
850
|
+
return asRecord(value);
|
|
567
851
|
}
|
|
568
852
|
}
|