@office-agents/sdk 0.0.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/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/lockdown.d.ts +3 -0
- package/dist/lockdown.d.ts.map +1 -0
- package/dist/lockdown.js +103 -0
- package/dist/lockdown.js.map +1 -0
- package/dist/message-utils.d.ts +44 -0
- package/dist/message-utils.d.ts.map +1 -0
- package/dist/message-utils.js +134 -0
- package/dist/message-utils.js.map +1 -0
- package/dist/oauth/index.d.ts +44 -0
- package/dist/oauth/index.d.ts.map +1 -0
- package/dist/oauth/index.js +259 -0
- package/dist/oauth/index.js.map +1 -0
- package/dist/provider-config.d.ts +29 -0
- package/dist/provider-config.d.ts.map +1 -0
- package/dist/provider-config.js +101 -0
- package/dist/provider-config.js.map +1 -0
- package/dist/runtime.d.ts +93 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +688 -0
- package/dist/runtime.js.map +1 -0
- package/dist/sandbox.d.ts +2 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +22 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/skills/index.d.ts +16 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +96 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/storage/db.d.ts +55 -0
- package/dist/storage/db.d.ts.map +1 -0
- package/dist/storage/db.js +229 -0
- package/dist/storage/db.js.map +1 -0
- package/dist/storage/index.d.ts +4 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +3 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/namespace.d.ts +9 -0
- package/dist/storage/namespace.d.ts.map +1 -0
- package/dist/storage/namespace.js +14 -0
- package/dist/storage/namespace.js.map +1 -0
- package/dist/tools/bash.d.ts +2 -0
- package/dist/tools/bash.d.ts.map +1 -0
- package/dist/tools/bash.js +69 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/read-file.d.ts +2 -0
- package/dist/tools/read-file.d.ts.map +1 -0
- package/dist/tools/read-file.js +116 -0
- package/dist/tools/read-file.js.map +1 -0
- package/dist/tools/types.d.ts +16 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +28 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/truncate.d.ts +19 -0
- package/dist/truncate.d.ts.map +1 -0
- package/dist/truncate.js +101 -0
- package/dist/truncate.js.map +1 -0
- package/dist/vfs/index.d.ts +35 -0
- package/dist/vfs/index.d.ts.map +1 -0
- package/dist/vfs/index.js +168 -0
- package/dist/vfs/index.js.map +1 -0
- package/dist/web/config.d.ts +12 -0
- package/dist/web/config.d.ts.map +1 -0
- package/dist/web/config.js +41 -0
- package/dist/web/config.js.map +1 -0
- package/dist/web/fetch.d.ts +5 -0
- package/dist/web/fetch.d.ts.map +1 -0
- package/dist/web/fetch.js +174 -0
- package/dist/web/fetch.js.map +1 -0
- package/dist/web/search.d.ts +9 -0
- package/dist/web/search.d.ts.map +1 -0
- package/dist/web/search.js +238 -0
- package/dist/web/search.js.map +1 -0
- package/dist/web/types.d.ts +56 -0
- package/dist/web/types.d.ts.map +1 -0
- package/dist/web/types.js +2 -0
- package/dist/web/types.js.map +1 -0
- package/package.json +47 -0
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,688 @@
|
|
|
1
|
+
import { Agent, } from "@mariozechner/pi-agent-core";
|
|
2
|
+
import { getModel, getModels, getProviders, streamSimple, } from "@mariozechner/pi-ai";
|
|
3
|
+
import { agentMessagesToChatMessages, deriveStats, extractPartsFromAssistantMessage, generateId, } from "./message-utils";
|
|
4
|
+
import { loadOAuthCredentials, refreshOAuthToken, saveOAuthCredentials, } from "./oauth";
|
|
5
|
+
import { applyProxyToModel, buildCustomModel, loadSavedConfig, saveConfig, } from "./provider-config";
|
|
6
|
+
import { addSkill, getInstalledSkills, removeSkill, syncSkillsToVfs, } from "./skills";
|
|
7
|
+
import { createSession, deleteSession, getOrCreateCurrentSession, getSession, listSessions, loadVfsFiles, saveSession, saveVfsFiles, } from "./storage";
|
|
8
|
+
import { deleteFile, listUploads, resetVfs, restoreVfs, setCustomCommands, setStaticFiles, snapshotVfs, writeFile, } from "./vfs";
|
|
9
|
+
const INITIAL_STATS = { ...deriveStats([]), contextWindow: 0 };
|
|
10
|
+
function thinkingLevelToAgent(level) {
|
|
11
|
+
return level === "none" ? "off" : level;
|
|
12
|
+
}
|
|
13
|
+
export class AgentRuntime {
|
|
14
|
+
constructor(adapter) {
|
|
15
|
+
this.agent = null;
|
|
16
|
+
this.config = null;
|
|
17
|
+
this.pendingConfig = null;
|
|
18
|
+
this.streamingMessageId = null;
|
|
19
|
+
this.isStreaming = false;
|
|
20
|
+
this.documentId = null;
|
|
21
|
+
this.currentSessionId = null;
|
|
22
|
+
this.sessionLoaded = false;
|
|
23
|
+
this.followMode = true;
|
|
24
|
+
this.skills = [];
|
|
25
|
+
this.listeners = new Set();
|
|
26
|
+
this.handleAgentEvent = (event) => {
|
|
27
|
+
console.log("[Runtime] Agent event:", event.type, event);
|
|
28
|
+
switch (event.type) {
|
|
29
|
+
case "message_start": {
|
|
30
|
+
if (event.message.role === "assistant") {
|
|
31
|
+
const id = generateId();
|
|
32
|
+
this.streamingMessageId = id;
|
|
33
|
+
const parts = extractPartsFromAssistantMessage(event.message);
|
|
34
|
+
const chatMessage = {
|
|
35
|
+
id,
|
|
36
|
+
role: "assistant",
|
|
37
|
+
parts,
|
|
38
|
+
timestamp: event.message.timestamp,
|
|
39
|
+
};
|
|
40
|
+
this.updateMessages((msgs) => [...msgs, chatMessage]);
|
|
41
|
+
}
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
case "message_update": {
|
|
45
|
+
if (event.message.role === "assistant" && this.streamingMessageId) {
|
|
46
|
+
const streamId = this.streamingMessageId;
|
|
47
|
+
this.updateMessages((msgs) => {
|
|
48
|
+
const messages = [...msgs];
|
|
49
|
+
const idx = messages.findIndex((m) => m.id === streamId);
|
|
50
|
+
if (idx !== -1) {
|
|
51
|
+
const parts = extractPartsFromAssistantMessage(event.message, messages[idx].parts);
|
|
52
|
+
messages[idx] = { ...messages[idx], parts };
|
|
53
|
+
}
|
|
54
|
+
return messages;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
case "message_end": {
|
|
60
|
+
if (event.message.role === "assistant") {
|
|
61
|
+
const assistantMsg = event.message;
|
|
62
|
+
const isError = assistantMsg.stopReason === "error" ||
|
|
63
|
+
assistantMsg.stopReason === "aborted";
|
|
64
|
+
const streamId = this.streamingMessageId;
|
|
65
|
+
this.updateMessages((msgs) => {
|
|
66
|
+
const messages = [...msgs];
|
|
67
|
+
const idx = messages.findIndex((m) => m.id === streamId);
|
|
68
|
+
if (isError) {
|
|
69
|
+
if (idx !== -1) {
|
|
70
|
+
messages.splice(idx, 1);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else if (idx !== -1) {
|
|
74
|
+
const parts = extractPartsFromAssistantMessage(event.message, messages[idx].parts);
|
|
75
|
+
messages[idx] = { ...messages[idx], parts };
|
|
76
|
+
}
|
|
77
|
+
return messages;
|
|
78
|
+
}, {
|
|
79
|
+
error: isError
|
|
80
|
+
? assistantMsg.errorMessage || "Request failed"
|
|
81
|
+
: this.state.error,
|
|
82
|
+
sessionStats: isError
|
|
83
|
+
? this.state.sessionStats
|
|
84
|
+
: {
|
|
85
|
+
...deriveStats(this.agent?.state.messages ?? []),
|
|
86
|
+
contextWindow: this.state.sessionStats.contextWindow,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
this.streamingMessageId = null;
|
|
90
|
+
}
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
case "tool_execution_start": {
|
|
94
|
+
this.updateMessages((msgs) => {
|
|
95
|
+
const messages = [...msgs];
|
|
96
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
97
|
+
const msg = messages[i];
|
|
98
|
+
const partIdx = msg.parts.findIndex((p) => p.type === "toolCall" && p.id === event.toolCallId);
|
|
99
|
+
if (partIdx !== -1) {
|
|
100
|
+
const parts = [...msg.parts];
|
|
101
|
+
const part = parts[partIdx];
|
|
102
|
+
if (part.type === "toolCall") {
|
|
103
|
+
parts[partIdx] = { ...part, status: "running" };
|
|
104
|
+
messages[i] = { ...msg, parts };
|
|
105
|
+
}
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return messages;
|
|
110
|
+
});
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
case "tool_execution_update": {
|
|
114
|
+
this.updateMessages((msgs) => {
|
|
115
|
+
const messages = [...msgs];
|
|
116
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
117
|
+
const msg = messages[i];
|
|
118
|
+
const partIdx = msg.parts.findIndex((p) => p.type === "toolCall" && p.id === event.toolCallId);
|
|
119
|
+
if (partIdx !== -1) {
|
|
120
|
+
const parts = [...msg.parts];
|
|
121
|
+
const part = parts[partIdx];
|
|
122
|
+
if (part.type === "toolCall") {
|
|
123
|
+
let partialText;
|
|
124
|
+
if (typeof event.partialResult === "string") {
|
|
125
|
+
partialText = event.partialResult;
|
|
126
|
+
}
|
|
127
|
+
else if (event.partialResult?.content &&
|
|
128
|
+
Array.isArray(event.partialResult.content)) {
|
|
129
|
+
partialText = event.partialResult.content
|
|
130
|
+
.filter((c) => c.type === "text")
|
|
131
|
+
.map((c) => c.text)
|
|
132
|
+
.join("\n");
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
partialText = JSON.stringify(event.partialResult, null, 2);
|
|
136
|
+
}
|
|
137
|
+
parts[partIdx] = { ...part, result: partialText };
|
|
138
|
+
messages[i] = { ...msg, parts };
|
|
139
|
+
}
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return messages;
|
|
144
|
+
});
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
case "tool_execution_end": {
|
|
148
|
+
let resultText;
|
|
149
|
+
let resultImages;
|
|
150
|
+
if (typeof event.result === "string") {
|
|
151
|
+
resultText = event.result;
|
|
152
|
+
}
|
|
153
|
+
else if (event.result?.content &&
|
|
154
|
+
Array.isArray(event.result.content)) {
|
|
155
|
+
resultText = event.result.content
|
|
156
|
+
.filter((c) => c.type === "text")
|
|
157
|
+
.map((c) => c.text)
|
|
158
|
+
.join("\n");
|
|
159
|
+
const images = event.result.content
|
|
160
|
+
.filter((c) => c.type === "image")
|
|
161
|
+
.map((c) => ({
|
|
162
|
+
data: c.data,
|
|
163
|
+
mimeType: c.mimeType,
|
|
164
|
+
}));
|
|
165
|
+
if (images.length > 0)
|
|
166
|
+
resultImages = images;
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
resultText = JSON.stringify(event.result, null, 2);
|
|
170
|
+
}
|
|
171
|
+
if (!event.isError && this.followMode) {
|
|
172
|
+
this.adapter.onToolResult?.(event.toolCallId, resultText, false);
|
|
173
|
+
}
|
|
174
|
+
this.updateMessages((msgs) => {
|
|
175
|
+
const messages = [...msgs];
|
|
176
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
177
|
+
const msg = messages[i];
|
|
178
|
+
const partIdx = msg.parts.findIndex((p) => p.type === "toolCall" && p.id === event.toolCallId);
|
|
179
|
+
if (partIdx !== -1) {
|
|
180
|
+
const parts = [...msg.parts];
|
|
181
|
+
const part = parts[partIdx];
|
|
182
|
+
if (part.type === "toolCall") {
|
|
183
|
+
parts[partIdx] = {
|
|
184
|
+
...part,
|
|
185
|
+
status: event.isError ? "error" : "complete",
|
|
186
|
+
result: resultText,
|
|
187
|
+
images: resultImages,
|
|
188
|
+
};
|
|
189
|
+
messages[i] = { ...msg, parts };
|
|
190
|
+
}
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return messages;
|
|
195
|
+
});
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
case "agent_end": {
|
|
199
|
+
this.isStreaming = false;
|
|
200
|
+
this.streamingMessageId = null;
|
|
201
|
+
this.update({ isStreaming: false });
|
|
202
|
+
this.onStreamingEnd();
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
this.adapter = adapter;
|
|
208
|
+
const saved = loadSavedConfig();
|
|
209
|
+
const validConfig = saved?.provider && saved?.apiKey && saved?.model ? saved : null;
|
|
210
|
+
this.followMode = validConfig?.followMode ?? true;
|
|
211
|
+
this.state = {
|
|
212
|
+
messages: [],
|
|
213
|
+
isStreaming: false,
|
|
214
|
+
error: null,
|
|
215
|
+
providerConfig: validConfig,
|
|
216
|
+
sessionStats: INITIAL_STATS,
|
|
217
|
+
currentSession: null,
|
|
218
|
+
sessions: [],
|
|
219
|
+
nameMap: {},
|
|
220
|
+
uploads: [],
|
|
221
|
+
isUploading: false,
|
|
222
|
+
skills: [],
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
getState() {
|
|
226
|
+
return this.state;
|
|
227
|
+
}
|
|
228
|
+
subscribe(listener) {
|
|
229
|
+
this.listeners.add(listener);
|
|
230
|
+
return () => this.listeners.delete(listener);
|
|
231
|
+
}
|
|
232
|
+
emit() {
|
|
233
|
+
for (const listener of this.listeners) {
|
|
234
|
+
listener(this.state);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
update(partial) {
|
|
238
|
+
this.state = { ...this.state, ...partial };
|
|
239
|
+
this.emit();
|
|
240
|
+
}
|
|
241
|
+
updateMessages(updater, extra) {
|
|
242
|
+
this.state = {
|
|
243
|
+
...this.state,
|
|
244
|
+
messages: updater(this.state.messages),
|
|
245
|
+
...extra,
|
|
246
|
+
};
|
|
247
|
+
this.emit();
|
|
248
|
+
}
|
|
249
|
+
setAdapter(adapter) {
|
|
250
|
+
this.adapter = adapter;
|
|
251
|
+
}
|
|
252
|
+
getAvailableProviders() {
|
|
253
|
+
return getProviders();
|
|
254
|
+
}
|
|
255
|
+
getModelsForProvider(provider) {
|
|
256
|
+
try {
|
|
257
|
+
return getModels(provider);
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
return [];
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
async getActiveApiKey(config) {
|
|
264
|
+
if (config.authMethod !== "oauth") {
|
|
265
|
+
return config.apiKey;
|
|
266
|
+
}
|
|
267
|
+
const creds = loadOAuthCredentials(config.provider);
|
|
268
|
+
if (!creds)
|
|
269
|
+
return config.apiKey;
|
|
270
|
+
if (Date.now() < creds.expires) {
|
|
271
|
+
return creds.access;
|
|
272
|
+
}
|
|
273
|
+
const refreshed = await refreshOAuthToken(config.provider, creds.refresh, config.proxyUrl, config.useProxy);
|
|
274
|
+
saveOAuthCredentials(config.provider, refreshed);
|
|
275
|
+
return refreshed.access;
|
|
276
|
+
}
|
|
277
|
+
applyConfig(config) {
|
|
278
|
+
let contextWindow = 0;
|
|
279
|
+
let baseModel;
|
|
280
|
+
if (config.provider === "custom") {
|
|
281
|
+
const custom = buildCustomModel(config);
|
|
282
|
+
if (!custom)
|
|
283
|
+
return;
|
|
284
|
+
baseModel = custom;
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
try {
|
|
288
|
+
baseModel = getModel(config.provider, config.model);
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
contextWindow = baseModel.contextWindow;
|
|
295
|
+
this.config = config;
|
|
296
|
+
const proxiedModel = applyProxyToModel(baseModel, config);
|
|
297
|
+
const existingMessages = this.agent?.state.messages ?? [];
|
|
298
|
+
if (this.agent) {
|
|
299
|
+
this.agent.abort();
|
|
300
|
+
}
|
|
301
|
+
const systemPrompt = this.adapter.buildSystemPrompt(this.skills);
|
|
302
|
+
const agent = new Agent({
|
|
303
|
+
initialState: {
|
|
304
|
+
model: proxiedModel,
|
|
305
|
+
systemPrompt,
|
|
306
|
+
thinkingLevel: thinkingLevelToAgent(config.thinking),
|
|
307
|
+
tools: this.adapter.tools,
|
|
308
|
+
messages: existingMessages,
|
|
309
|
+
},
|
|
310
|
+
streamFn: async (model, context, options) => {
|
|
311
|
+
const cfg = this.config ?? config;
|
|
312
|
+
const apiKey = await this.getActiveApiKey(cfg);
|
|
313
|
+
return streamSimple(model, context, {
|
|
314
|
+
...options,
|
|
315
|
+
apiKey,
|
|
316
|
+
});
|
|
317
|
+
},
|
|
318
|
+
});
|
|
319
|
+
this.agent = agent;
|
|
320
|
+
agent.subscribe(this.handleAgentEvent);
|
|
321
|
+
this.pendingConfig = null;
|
|
322
|
+
this.followMode = config.followMode ?? true;
|
|
323
|
+
this.update({
|
|
324
|
+
providerConfig: config,
|
|
325
|
+
error: null,
|
|
326
|
+
sessionStats: {
|
|
327
|
+
...this.state.sessionStats,
|
|
328
|
+
contextWindow,
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
setProviderConfig(config) {
|
|
333
|
+
if (this.isStreaming) {
|
|
334
|
+
this.pendingConfig = config;
|
|
335
|
+
this.update({ providerConfig: config });
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
this.applyConfig(config);
|
|
339
|
+
}
|
|
340
|
+
abort() {
|
|
341
|
+
this.agent?.abort();
|
|
342
|
+
this.isStreaming = false;
|
|
343
|
+
this.update({ isStreaming: false });
|
|
344
|
+
}
|
|
345
|
+
async sendMessage(content, attachments) {
|
|
346
|
+
if (this.pendingConfig) {
|
|
347
|
+
this.applyConfig(this.pendingConfig);
|
|
348
|
+
}
|
|
349
|
+
const agent = this.agent;
|
|
350
|
+
if (!agent || !this.state.providerConfig) {
|
|
351
|
+
this.update({ error: "Please configure your API key first" });
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const userMessage = {
|
|
355
|
+
id: generateId(),
|
|
356
|
+
role: "user",
|
|
357
|
+
parts: [{ type: "text", text: content }],
|
|
358
|
+
timestamp: Date.now(),
|
|
359
|
+
};
|
|
360
|
+
this.isStreaming = true;
|
|
361
|
+
this.update({
|
|
362
|
+
messages: [...this.state.messages, userMessage],
|
|
363
|
+
isStreaming: true,
|
|
364
|
+
error: null,
|
|
365
|
+
});
|
|
366
|
+
try {
|
|
367
|
+
let promptContent = content;
|
|
368
|
+
if (this.adapter.getDocumentMetadata) {
|
|
369
|
+
try {
|
|
370
|
+
const meta = await this.adapter.getDocumentMetadata();
|
|
371
|
+
if (meta) {
|
|
372
|
+
const tag = this.adapter.metadataTag || "doc_context";
|
|
373
|
+
promptContent = `<${tag}>\n${JSON.stringify(meta.metadata, null, 2)}\n</${tag}>\n\n${content}`;
|
|
374
|
+
if (meta.nameMap) {
|
|
375
|
+
this.update({ nameMap: meta.nameMap });
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
catch (err) {
|
|
380
|
+
console.error("[Runtime] Failed to get document metadata:", err);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (attachments && attachments.length > 0) {
|
|
384
|
+
const paths = attachments
|
|
385
|
+
.map((name) => `/home/user/uploads/${name}`)
|
|
386
|
+
.join("\n");
|
|
387
|
+
promptContent = `<attachments>\n${paths}\n</attachments>\n\n${promptContent}`;
|
|
388
|
+
}
|
|
389
|
+
await agent.prompt(promptContent);
|
|
390
|
+
}
|
|
391
|
+
catch (err) {
|
|
392
|
+
console.error("[Runtime] sendMessage error:", err);
|
|
393
|
+
this.isStreaming = false;
|
|
394
|
+
this.update({
|
|
395
|
+
isStreaming: false,
|
|
396
|
+
error: err instanceof Error ? err.message : "An error occurred",
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
clearMessages() {
|
|
401
|
+
this.abort();
|
|
402
|
+
this.agent?.reset();
|
|
403
|
+
resetVfs();
|
|
404
|
+
if (this.currentSessionId) {
|
|
405
|
+
Promise.all([
|
|
406
|
+
saveSession(this.currentSessionId, []),
|
|
407
|
+
saveVfsFiles(this.currentSessionId, []),
|
|
408
|
+
]).catch(console.error);
|
|
409
|
+
}
|
|
410
|
+
this.update({
|
|
411
|
+
messages: [],
|
|
412
|
+
error: null,
|
|
413
|
+
sessionStats: INITIAL_STATS,
|
|
414
|
+
uploads: [],
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
async refreshSessions() {
|
|
418
|
+
if (!this.documentId)
|
|
419
|
+
return;
|
|
420
|
+
const sessions = await listSessions(this.documentId);
|
|
421
|
+
this.update({ sessions });
|
|
422
|
+
}
|
|
423
|
+
async newSession() {
|
|
424
|
+
if (!this.documentId)
|
|
425
|
+
return;
|
|
426
|
+
if (this.isStreaming)
|
|
427
|
+
return;
|
|
428
|
+
try {
|
|
429
|
+
this.agent?.reset();
|
|
430
|
+
resetVfs();
|
|
431
|
+
const session = await createSession(this.documentId);
|
|
432
|
+
this.currentSessionId = session.id;
|
|
433
|
+
await this.refreshSessions();
|
|
434
|
+
this.update({
|
|
435
|
+
messages: [],
|
|
436
|
+
currentSession: session,
|
|
437
|
+
error: null,
|
|
438
|
+
sessionStats: INITIAL_STATS,
|
|
439
|
+
uploads: [],
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
catch (err) {
|
|
443
|
+
console.error("[Runtime] Failed to create session:", err);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
async switchSession(sessionId) {
|
|
447
|
+
if (this.currentSessionId === sessionId)
|
|
448
|
+
return;
|
|
449
|
+
if (this.isStreaming)
|
|
450
|
+
return;
|
|
451
|
+
this.agent?.reset();
|
|
452
|
+
try {
|
|
453
|
+
const [session, vfsFiles] = await Promise.all([
|
|
454
|
+
getSession(sessionId),
|
|
455
|
+
loadVfsFiles(sessionId),
|
|
456
|
+
]);
|
|
457
|
+
if (!session)
|
|
458
|
+
return;
|
|
459
|
+
await restoreVfs(vfsFiles);
|
|
460
|
+
this.currentSessionId = session.id;
|
|
461
|
+
if (session.agentMessages.length > 0 && this.agent) {
|
|
462
|
+
this.agent.replaceMessages(session.agentMessages);
|
|
463
|
+
}
|
|
464
|
+
const uploadNames = await listUploads();
|
|
465
|
+
const stats = deriveStats(session.agentMessages);
|
|
466
|
+
this.update({
|
|
467
|
+
messages: agentMessagesToChatMessages(session.agentMessages, this.adapter.metadataTag),
|
|
468
|
+
currentSession: session,
|
|
469
|
+
error: null,
|
|
470
|
+
sessionStats: {
|
|
471
|
+
...stats,
|
|
472
|
+
contextWindow: this.state.sessionStats.contextWindow,
|
|
473
|
+
},
|
|
474
|
+
uploads: uploadNames.map((name) => ({ name, size: 0 })),
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
catch (err) {
|
|
478
|
+
console.error("[Runtime] Failed to switch session:", err);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
async deleteCurrentSession() {
|
|
482
|
+
if (!this.currentSessionId || !this.documentId)
|
|
483
|
+
return;
|
|
484
|
+
if (this.isStreaming)
|
|
485
|
+
return;
|
|
486
|
+
this.agent?.reset();
|
|
487
|
+
const deletedId = this.currentSessionId;
|
|
488
|
+
await Promise.all([deleteSession(deletedId), saveVfsFiles(deletedId, [])]);
|
|
489
|
+
const session = await getOrCreateCurrentSession(this.documentId);
|
|
490
|
+
this.currentSessionId = session.id;
|
|
491
|
+
const vfsFiles = await loadVfsFiles(session.id);
|
|
492
|
+
await restoreVfs(vfsFiles);
|
|
493
|
+
if (session.agentMessages.length > 0 && this.agent) {
|
|
494
|
+
this.agent.replaceMessages(session.agentMessages);
|
|
495
|
+
}
|
|
496
|
+
await this.refreshSessions();
|
|
497
|
+
const uploadNames = await listUploads();
|
|
498
|
+
const stats = deriveStats(session.agentMessages);
|
|
499
|
+
this.update({
|
|
500
|
+
messages: agentMessagesToChatMessages(session.agentMessages, this.adapter.metadataTag),
|
|
501
|
+
currentSession: session,
|
|
502
|
+
error: null,
|
|
503
|
+
sessionStats: {
|
|
504
|
+
...stats,
|
|
505
|
+
contextWindow: this.state.sessionStats.contextWindow,
|
|
506
|
+
},
|
|
507
|
+
uploads: uploadNames.map((name) => ({ name, size: 0 })),
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
async onStreamingEnd() {
|
|
511
|
+
if (!this.currentSessionId)
|
|
512
|
+
return;
|
|
513
|
+
const sessionId = this.currentSessionId;
|
|
514
|
+
const agentMessages = this.agent?.state.messages ?? [];
|
|
515
|
+
try {
|
|
516
|
+
const vfsFiles = await snapshotVfs();
|
|
517
|
+
await Promise.all([
|
|
518
|
+
saveSession(sessionId, agentMessages),
|
|
519
|
+
saveVfsFiles(sessionId, vfsFiles),
|
|
520
|
+
]);
|
|
521
|
+
await this.refreshSessions();
|
|
522
|
+
const updated = await getSession(sessionId);
|
|
523
|
+
if (updated) {
|
|
524
|
+
this.update({ currentSession: updated });
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
catch (e) {
|
|
528
|
+
console.error(e);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
async init() {
|
|
532
|
+
if (this.sessionLoaded)
|
|
533
|
+
return;
|
|
534
|
+
this.sessionLoaded = true;
|
|
535
|
+
if (this.adapter.staticFiles) {
|
|
536
|
+
setStaticFiles(this.adapter.staticFiles);
|
|
537
|
+
}
|
|
538
|
+
if (this.adapter.customCommands) {
|
|
539
|
+
setCustomCommands(this.adapter.customCommands);
|
|
540
|
+
}
|
|
541
|
+
try {
|
|
542
|
+
const id = await this.adapter.getDocumentId();
|
|
543
|
+
this.documentId = id;
|
|
544
|
+
const skills = await getInstalledSkills();
|
|
545
|
+
this.skills = skills;
|
|
546
|
+
await syncSkillsToVfs();
|
|
547
|
+
const saved = loadSavedConfig();
|
|
548
|
+
if (saved?.provider && saved?.apiKey && saved?.model) {
|
|
549
|
+
this.applyConfig(saved);
|
|
550
|
+
}
|
|
551
|
+
const session = await getOrCreateCurrentSession(id);
|
|
552
|
+
this.currentSessionId = session.id;
|
|
553
|
+
const [sessions, vfsFiles] = await Promise.all([
|
|
554
|
+
listSessions(id),
|
|
555
|
+
loadVfsFiles(session.id),
|
|
556
|
+
]);
|
|
557
|
+
if (vfsFiles.length > 0) {
|
|
558
|
+
await restoreVfs(vfsFiles);
|
|
559
|
+
}
|
|
560
|
+
if (session.agentMessages.length > 0 && this.agent) {
|
|
561
|
+
this.agent.replaceMessages(session.agentMessages);
|
|
562
|
+
}
|
|
563
|
+
const uploadNames = await listUploads();
|
|
564
|
+
const stats = deriveStats(session.agentMessages);
|
|
565
|
+
this.update({
|
|
566
|
+
messages: agentMessagesToChatMessages(session.agentMessages, this.adapter.metadataTag),
|
|
567
|
+
currentSession: session,
|
|
568
|
+
sessions,
|
|
569
|
+
skills,
|
|
570
|
+
sessionStats: {
|
|
571
|
+
...stats,
|
|
572
|
+
contextWindow: this.state.sessionStats.contextWindow,
|
|
573
|
+
},
|
|
574
|
+
uploads: uploadNames.map((name) => ({ name, size: 0 })),
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
catch (err) {
|
|
578
|
+
console.error("[Runtime] Failed to load session:", err);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
async uploadFiles(files) {
|
|
582
|
+
if (files.length === 0)
|
|
583
|
+
return;
|
|
584
|
+
this.update({ isUploading: true });
|
|
585
|
+
try {
|
|
586
|
+
for (const file of files) {
|
|
587
|
+
await writeFile(file.name, file.data);
|
|
588
|
+
const uploads = [...this.state.uploads];
|
|
589
|
+
const exists = uploads.findIndex((u) => u.name === file.name);
|
|
590
|
+
if (exists !== -1) {
|
|
591
|
+
uploads[exists] = { name: file.name, size: file.size };
|
|
592
|
+
}
|
|
593
|
+
else {
|
|
594
|
+
uploads.push({ name: file.name, size: file.size });
|
|
595
|
+
}
|
|
596
|
+
this.update({ uploads });
|
|
597
|
+
}
|
|
598
|
+
if (this.currentSessionId) {
|
|
599
|
+
const snapshot = await snapshotVfs();
|
|
600
|
+
await saveVfsFiles(this.currentSessionId, snapshot);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
catch (err) {
|
|
604
|
+
console.error("Failed to upload file:", err);
|
|
605
|
+
}
|
|
606
|
+
finally {
|
|
607
|
+
this.update({ isUploading: false });
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
async removeUpload(name) {
|
|
611
|
+
try {
|
|
612
|
+
await deleteFile(name);
|
|
613
|
+
this.update({
|
|
614
|
+
uploads: this.state.uploads.filter((u) => u.name !== name),
|
|
615
|
+
});
|
|
616
|
+
if (this.currentSessionId) {
|
|
617
|
+
const snapshot = await snapshotVfs();
|
|
618
|
+
await saveVfsFiles(this.currentSessionId, snapshot);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
catch (err) {
|
|
622
|
+
console.error("Failed to delete file:", err);
|
|
623
|
+
this.update({
|
|
624
|
+
uploads: this.state.uploads.filter((u) => u.name !== name),
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
async refreshSkillsAndRebuildAgent() {
|
|
629
|
+
this.skills = await getInstalledSkills();
|
|
630
|
+
this.update({ skills: this.skills });
|
|
631
|
+
if (this.state.providerConfig) {
|
|
632
|
+
this.applyConfig(this.state.providerConfig);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
async installSkill(inputs) {
|
|
636
|
+
if (inputs.length === 0)
|
|
637
|
+
return;
|
|
638
|
+
try {
|
|
639
|
+
await addSkill(inputs);
|
|
640
|
+
await this.refreshSkillsAndRebuildAgent();
|
|
641
|
+
}
|
|
642
|
+
catch (err) {
|
|
643
|
+
console.error("[Runtime] Failed to install skill:", err);
|
|
644
|
+
this.update({
|
|
645
|
+
error: err instanceof Error ? err.message : "Failed to install skill",
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
async uninstallSkill(name) {
|
|
650
|
+
try {
|
|
651
|
+
await removeSkill(name);
|
|
652
|
+
await this.refreshSkillsAndRebuildAgent();
|
|
653
|
+
}
|
|
654
|
+
catch (err) {
|
|
655
|
+
console.error("[Runtime] Failed to uninstall skill:", err);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
toggleFollowMode() {
|
|
659
|
+
if (!this.state.providerConfig)
|
|
660
|
+
return;
|
|
661
|
+
const newFollowMode = !this.state.providerConfig.followMode;
|
|
662
|
+
this.followMode = newFollowMode;
|
|
663
|
+
const newConfig = {
|
|
664
|
+
...this.state.providerConfig,
|
|
665
|
+
followMode: newFollowMode,
|
|
666
|
+
};
|
|
667
|
+
saveConfig(newConfig);
|
|
668
|
+
this.update({ providerConfig: newConfig });
|
|
669
|
+
}
|
|
670
|
+
toggleExpandToolCalls() {
|
|
671
|
+
if (!this.state.providerConfig)
|
|
672
|
+
return;
|
|
673
|
+
const newConfig = {
|
|
674
|
+
...this.state.providerConfig,
|
|
675
|
+
expandToolCalls: !this.state.providerConfig.expandToolCalls,
|
|
676
|
+
};
|
|
677
|
+
saveConfig(newConfig);
|
|
678
|
+
this.update({ providerConfig: newConfig });
|
|
679
|
+
}
|
|
680
|
+
getName(id) {
|
|
681
|
+
return this.state.nameMap[id];
|
|
682
|
+
}
|
|
683
|
+
dispose() {
|
|
684
|
+
this.agent?.abort();
|
|
685
|
+
this.listeners.clear();
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
//# sourceMappingURL=runtime.js.map
|