copilotoffice 2.1.0 → 2.3.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/README.md +225 -192
- package/bin/copilotoffice.js +39 -39
- package/dist/electron/main.js +599 -126
- package/dist/electron/terminal/events-watcher.js +53 -2
- package/dist/electron/terminal/ipc-relay.js +49 -1
- package/dist/electron/terminal/preload.js +57 -13
- package/dist/electron/terminal/server.js +6125 -940
- package/dist/game.bundle.js +611 -206
- package/package.json +62 -58
- package/src/index.html +44 -44
|
@@ -31,7 +31,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var events_watcher_exports = {};
|
|
32
32
|
__export(events_watcher_exports, {
|
|
33
33
|
EventsWatcher: () => EventsWatcher,
|
|
34
|
-
|
|
34
|
+
buildAskUserRelay: () => buildAskUserRelay,
|
|
35
|
+
formatToolStatus: () => formatToolStatus,
|
|
36
|
+
normalizeAskUserArgs: () => normalizeAskUserArgs
|
|
35
37
|
});
|
|
36
38
|
module.exports = __toCommonJS(events_watcher_exports);
|
|
37
39
|
var fs = __toESM(require("fs"));
|
|
@@ -223,8 +225,57 @@ function formatToolStatus(toolName, args) {
|
|
|
223
225
|
return `Using ${toolName}`;
|
|
224
226
|
}
|
|
225
227
|
}
|
|
228
|
+
function normalizeAskUserArgs(args) {
|
|
229
|
+
const a = args ?? {};
|
|
230
|
+
const questionRaw = a.question ?? a.prompt ?? a.message ?? a.text ?? "";
|
|
231
|
+
const question = typeof questionRaw === "string" ? questionRaw : String(questionRaw ?? "");
|
|
232
|
+
const rawOptions = a.options ?? a.choices ?? a.answers ?? a.selections ?? [];
|
|
233
|
+
const options = [];
|
|
234
|
+
if (Array.isArray(rawOptions)) {
|
|
235
|
+
for (const opt of rawOptions) {
|
|
236
|
+
if (typeof opt === "string") {
|
|
237
|
+
options.push({ text: opt });
|
|
238
|
+
} else if (opt && typeof opt === "object") {
|
|
239
|
+
const o = opt;
|
|
240
|
+
const text = o.text ?? o.label ?? o.value ?? o.name ?? "";
|
|
241
|
+
options.push({ text: typeof text === "string" ? text : String(text ?? "") });
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const freeformRaw = a.freeform ?? a.allowFreeform ?? a.allowFreeText ?? a.allowCustom ?? a.freeText ?? false;
|
|
246
|
+
const freeform = Boolean(freeformRaw);
|
|
247
|
+
return { question, options, freeform };
|
|
248
|
+
}
|
|
249
|
+
function buildAskUserRelay(event, backendName) {
|
|
250
|
+
const d = event.data ?? {};
|
|
251
|
+
if (event.type === "user_input.requested") {
|
|
252
|
+
const options = Array.isArray(d.choices) ? d.choices.map((c) => ({
|
|
253
|
+
text: typeof c === "string" ? c : String(c?.text ?? c?.label ?? c?.value ?? "")
|
|
254
|
+
})) : [];
|
|
255
|
+
return {
|
|
256
|
+
toolId: String(d.toolCallId ?? ""),
|
|
257
|
+
requestId: String(d.requestId ?? ""),
|
|
258
|
+
question: String(d.question ?? ""),
|
|
259
|
+
options,
|
|
260
|
+
freeform: Boolean(d.allowFreeform)
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
if (event.type === "tool.execution_start" && d.toolName === "ask_user" && backendName === "node-pty") {
|
|
264
|
+
const norm = normalizeAskUserArgs(d.arguments);
|
|
265
|
+
return {
|
|
266
|
+
toolId: String(d.toolCallId ?? ""),
|
|
267
|
+
requestId: "",
|
|
268
|
+
question: norm.question,
|
|
269
|
+
options: norm.options,
|
|
270
|
+
freeform: norm.freeform
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
226
275
|
// Annotate the CommonJS export names for ESM import in node:
|
|
227
276
|
0 && (module.exports = {
|
|
228
277
|
EventsWatcher,
|
|
229
|
-
|
|
278
|
+
buildAskUserRelay,
|
|
279
|
+
formatToolStatus,
|
|
280
|
+
normalizeAskUserArgs
|
|
230
281
|
});
|
|
@@ -156,7 +156,9 @@ var TerminalRelay = class _TerminalRelay {
|
|
|
156
156
|
* server→main copilot/terminal events without going through the renderer.
|
|
157
157
|
* Emits: 'copilot-event' (agentId, event), 'copilot-turn-start' (agentId),
|
|
158
158
|
* 'copilot-turn-end' (agentId), 'copilot-tool-start' (agentId, toolName, toolId, status),
|
|
159
|
+
* 'copilot-ask-user' (agentId, toolId, requestId, question, options, freeform),
|
|
159
160
|
* 'session-meta-updated' (agentId, meta), 'terminal-exit' (agentId, exitCode).
|
|
161
|
+
* Accepts (via mainSubmitAnswer): 'submit-answer' (officeId, agentId, requestId?, answer, wasFreeform).
|
|
160
162
|
*/
|
|
161
163
|
this.mainEvents = new import_events.EventEmitter();
|
|
162
164
|
this.server = null;
|
|
@@ -165,6 +167,8 @@ var TerminalRelay = class _TerminalRelay {
|
|
|
165
167
|
this.shuttingDown = false;
|
|
166
168
|
/** Requests that arrived while the server was not connected. Flushed on ready. */
|
|
167
169
|
this.queuedRequests = [];
|
|
170
|
+
/** Latest backend-selection outcome reported by the server on 'ready'. */
|
|
171
|
+
this.backendInfo = null;
|
|
168
172
|
this.getWindow = getWindow;
|
|
169
173
|
}
|
|
170
174
|
static {
|
|
@@ -315,6 +319,22 @@ var TerminalRelay = class _TerminalRelay {
|
|
|
315
319
|
mainSubmitPrompt(officeId, agentId, prompt, label) {
|
|
316
320
|
return this.request({ type: "submit-prompt", requestId: this.id(), officeId, agentId, prompt, label });
|
|
317
321
|
}
|
|
322
|
+
/**
|
|
323
|
+
* spec 015: answer a pending `ask_user` interaction. Distinct from
|
|
324
|
+
* mainSubmitPrompt — resolves the pending user-input interaction (SDK/ui-server)
|
|
325
|
+
* or injects keystrokes (node-pty). `requestId` is the single-resolution key.
|
|
326
|
+
*/
|
|
327
|
+
mainSubmitAnswer(officeId, agentId, a) {
|
|
328
|
+
return this.request({
|
|
329
|
+
type: "submit-answer",
|
|
330
|
+
requestId: this.id(),
|
|
331
|
+
officeId,
|
|
332
|
+
agentId,
|
|
333
|
+
answerRequestId: a.requestId,
|
|
334
|
+
answer: a.answer,
|
|
335
|
+
wasFreeform: a.wasFreeform
|
|
336
|
+
});
|
|
337
|
+
}
|
|
318
338
|
/** Fire-and-forget: control whether copilot-events are mirrored to main for an agent without a viewer. */
|
|
319
339
|
mainSetAgentForwarding(officeId, agentId, enabled) {
|
|
320
340
|
this.send({ type: "set-agent-forwarding", officeId, agentId, enabled });
|
|
@@ -324,6 +344,15 @@ var TerminalRelay = class _TerminalRelay {
|
|
|
324
344
|
clearTimeout(readyTimeout);
|
|
325
345
|
this.shuttingDown = false;
|
|
326
346
|
console.log("[Relay] Terminal server ready");
|
|
347
|
+
if (msg.backend) {
|
|
348
|
+
this.backendInfo = msg.backend;
|
|
349
|
+
if (msg.backend.fellBack) {
|
|
350
|
+
const win2 = this.getWindow();
|
|
351
|
+
if (win2 && !win2.isDestroyed()) {
|
|
352
|
+
win2.webContents.send("backend-fallback", this.backendInfo);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
327
356
|
if (this.queuedRequests.length > 0) {
|
|
328
357
|
console.log(`[Relay] Flushing ${this.queuedRequests.length} queued request(s)`);
|
|
329
358
|
for (const queued of this.queuedRequests) {
|
|
@@ -359,6 +388,12 @@ var TerminalRelay = class _TerminalRelay {
|
|
|
359
388
|
case "copilot-tool-start":
|
|
360
389
|
this.mainEvents.emit("copilot-tool-start", msg.agentId, msg.toolName, msg.toolId, msg.status);
|
|
361
390
|
break;
|
|
391
|
+
case "copilot-ask-user":
|
|
392
|
+
this.mainEvents.emit("copilot-ask-user", msg.agentId, msg.toolId, msg.requestId, msg.question, msg.options, msg.freeform);
|
|
393
|
+
break;
|
|
394
|
+
case "copilot-ask-user-complete":
|
|
395
|
+
this.mainEvents.emit("copilot-ask-user-complete", msg.agentId, msg.requestId);
|
|
396
|
+
break;
|
|
362
397
|
case "session-meta-updated":
|
|
363
398
|
this.mainEvents.emit("session-meta-updated", msg.agentId, msg.meta);
|
|
364
399
|
break;
|
|
@@ -381,6 +416,12 @@ var TerminalRelay = class _TerminalRelay {
|
|
|
381
416
|
case "copilot-tool-start":
|
|
382
417
|
win.webContents.send("copilot-tool-start", msg.agentId, msg.toolName, msg.toolId, msg.status);
|
|
383
418
|
break;
|
|
419
|
+
case "copilot-ask-user":
|
|
420
|
+
win.webContents.send("copilot-ask-user", msg.agentId, msg.toolId, msg.requestId, msg.question, msg.options, msg.freeform);
|
|
421
|
+
break;
|
|
422
|
+
case "copilot-ask-user-complete":
|
|
423
|
+
win.webContents.send("copilot-ask-user-complete", msg.agentId, msg.requestId);
|
|
424
|
+
break;
|
|
384
425
|
case "copilot-tool-complete":
|
|
385
426
|
win.webContents.send("copilot-tool-complete", msg.agentId, msg.toolId, msg.success);
|
|
386
427
|
break;
|
|
@@ -399,17 +440,24 @@ var TerminalRelay = class _TerminalRelay {
|
|
|
399
440
|
case "terminal-preload-status":
|
|
400
441
|
win.webContents.send("terminal-preload-status", msg.agentId, msg.status);
|
|
401
442
|
break;
|
|
443
|
+
case "backend-online":
|
|
444
|
+
win.webContents.send("backend-online", msg.officeId, msg.backend);
|
|
445
|
+
break;
|
|
446
|
+
case "backend-session-fallback":
|
|
447
|
+
win.webContents.send("backend-session-fallback", msg.officeId, msg.agentId, msg.reason);
|
|
448
|
+
break;
|
|
402
449
|
}
|
|
403
450
|
}
|
|
404
451
|
// ── IPC Handler Registration ──────────────────────────────────
|
|
405
452
|
registerIpc() {
|
|
453
|
+
import_electron.ipcMain.handle("terminal-backend-info", () => this.backendInfo);
|
|
406
454
|
import_electron.ipcMain.handle(
|
|
407
455
|
"terminal-start",
|
|
408
456
|
(_event, officeId, agentId, workingDir, cols, rows, preseededPrompt, launchMode) => this.request({ type: "start", requestId: this.id(), officeId, agentId, workingDir, cols, rows, preseededPrompt, launchMode })
|
|
409
457
|
);
|
|
410
458
|
import_electron.ipcMain.handle(
|
|
411
459
|
"terminal-attach",
|
|
412
|
-
(_event, officeId, agentId) => this.request({ type: "attach", requestId: this.id(), officeId, agentId })
|
|
460
|
+
(_event, officeId, agentId, foreground) => this.request({ type: "attach", requestId: this.id(), officeId, agentId, foreground })
|
|
413
461
|
);
|
|
414
462
|
import_electron.ipcMain.handle("terminal-detach", (_event, officeId, agentId) => {
|
|
415
463
|
this.send({ type: "detach", officeId, agentId });
|
|
@@ -43,8 +43,8 @@ import_electron.contextBridge.exposeInMainWorld("copilotBridge", {
|
|
|
43
43
|
terminalExists: (officeId, agentId) => {
|
|
44
44
|
return import_electron.ipcRenderer.invoke("terminal-exists", officeId, agentId);
|
|
45
45
|
},
|
|
46
|
-
terminalAttach: (officeId, agentId) => {
|
|
47
|
-
return import_electron.ipcRenderer.invoke("terminal-attach", officeId, agentId);
|
|
46
|
+
terminalAttach: (officeId, agentId, foreground) => {
|
|
47
|
+
return import_electron.ipcRenderer.invoke("terminal-attach", officeId, agentId, foreground);
|
|
48
48
|
},
|
|
49
49
|
terminalDetach: (officeId, agentId) => {
|
|
50
50
|
return import_electron.ipcRenderer.invoke("terminal-detach", officeId, agentId);
|
|
@@ -105,37 +105,67 @@ import_electron.contextBridge.exposeInMainWorld("copilotBridge", {
|
|
|
105
105
|
loadOffices: () => {
|
|
106
106
|
return import_electron.ipcRenderer.invoke("load-offices");
|
|
107
107
|
},
|
|
108
|
-
// Terminal event listeners
|
|
108
|
+
// Terminal event listeners. Each returns an unsubscribe function that removes
|
|
109
|
+
// ONLY this registration (not every listener on the channel), so a controller
|
|
110
|
+
// can dispose its own listener before re-registering. This prevents duplicate
|
|
111
|
+
// registrations from writing the same PTY byte to xterm more than once (the
|
|
112
|
+
// classic "double characters" bug). Callers may ignore the return value.
|
|
109
113
|
onTerminalData: (callback) => {
|
|
110
|
-
|
|
114
|
+
const handler = (_event, agentId, data) => callback(agentId, data);
|
|
115
|
+
import_electron.ipcRenderer.on("terminal-data", handler);
|
|
116
|
+
return () => import_electron.ipcRenderer.removeListener("terminal-data", handler);
|
|
111
117
|
},
|
|
112
118
|
onTerminalExit: (callback) => {
|
|
113
|
-
|
|
119
|
+
const handler = (_event, agentId, exitCode) => callback(agentId, exitCode);
|
|
120
|
+
import_electron.ipcRenderer.on("terminal-exit", handler);
|
|
121
|
+
return () => import_electron.ipcRenderer.removeListener("terminal-exit", handler);
|
|
114
122
|
},
|
|
115
123
|
onTerminalPreloadStatus: (callback) => {
|
|
116
|
-
|
|
124
|
+
const handler = (_event, agentId, status) => callback(agentId, status);
|
|
125
|
+
import_electron.ipcRenderer.on("terminal-preload-status", handler);
|
|
126
|
+
return () => import_electron.ipcRenderer.removeListener("terminal-preload-status", handler);
|
|
117
127
|
},
|
|
118
128
|
// Copilot activity event listeners
|
|
119
129
|
onCopilotEvent: (callback) => {
|
|
120
|
-
|
|
130
|
+
const handler = (_event, agentId, copilotEvent) => callback(agentId, copilotEvent);
|
|
131
|
+
import_electron.ipcRenderer.on("copilot-event", handler);
|
|
132
|
+
return () => import_electron.ipcRenderer.removeListener("copilot-event", handler);
|
|
121
133
|
},
|
|
122
134
|
onCopilotToolStart: (callback) => {
|
|
123
|
-
|
|
135
|
+
const handler = (_event, agentId, toolName, toolId, status) => callback(agentId, toolName, toolId, status);
|
|
136
|
+
import_electron.ipcRenderer.on("copilot-tool-start", handler);
|
|
137
|
+
return () => import_electron.ipcRenderer.removeListener("copilot-tool-start", handler);
|
|
138
|
+
},
|
|
139
|
+
// spec 015: additive ask_user relay (renderer parity — no Phaser consumer required).
|
|
140
|
+
onCopilotAskUser: (callback) => {
|
|
141
|
+
const handler = (_event, agentId, toolId, requestId, question, options, freeform) => callback(agentId, toolId, requestId, question, options, freeform);
|
|
142
|
+
import_electron.ipcRenderer.on("copilot-ask-user", handler);
|
|
143
|
+
return () => import_electron.ipcRenderer.removeListener("copilot-ask-user", handler);
|
|
124
144
|
},
|
|
125
145
|
onCopilotToolComplete: (callback) => {
|
|
126
|
-
|
|
146
|
+
const handler = (_event, agentId, toolId, success) => callback(agentId, toolId, success);
|
|
147
|
+
import_electron.ipcRenderer.on("copilot-tool-complete", handler);
|
|
148
|
+
return () => import_electron.ipcRenderer.removeListener("copilot-tool-complete", handler);
|
|
127
149
|
},
|
|
128
150
|
onCopilotTurnEnd: (callback) => {
|
|
129
|
-
|
|
151
|
+
const handler = (_event, agentId) => callback(agentId);
|
|
152
|
+
import_electron.ipcRenderer.on("copilot-turn-end", handler);
|
|
153
|
+
return () => import_electron.ipcRenderer.removeListener("copilot-turn-end", handler);
|
|
130
154
|
},
|
|
131
155
|
onCopilotTurnStart: (callback) => {
|
|
132
|
-
|
|
156
|
+
const handler = (_event, agentId) => callback(agentId);
|
|
157
|
+
import_electron.ipcRenderer.on("copilot-turn-start", handler);
|
|
158
|
+
return () => import_electron.ipcRenderer.removeListener("copilot-turn-start", handler);
|
|
133
159
|
},
|
|
134
160
|
onCopilotUserMessage: (callback) => {
|
|
135
|
-
|
|
161
|
+
const handler = (_event, agentId) => callback(agentId);
|
|
162
|
+
import_electron.ipcRenderer.on("copilot-user-message", handler);
|
|
163
|
+
return () => import_electron.ipcRenderer.removeListener("copilot-user-message", handler);
|
|
136
164
|
},
|
|
137
165
|
onSessionMetaUpdated: (callback) => {
|
|
138
|
-
|
|
166
|
+
const handler = (_event, agentId, meta) => callback(agentId, meta);
|
|
167
|
+
import_electron.ipcRenderer.on("session-meta-updated", handler);
|
|
168
|
+
return () => import_electron.ipcRenderer.removeListener("session-meta-updated", handler);
|
|
139
169
|
},
|
|
140
170
|
removeTerminalListeners: () => {
|
|
141
171
|
import_electron.ipcRenderer.removeAllListeners("terminal-data");
|
|
@@ -144,6 +174,7 @@ import_electron.contextBridge.exposeInMainWorld("copilotBridge", {
|
|
|
144
174
|
removeCopilotListeners: () => {
|
|
145
175
|
import_electron.ipcRenderer.removeAllListeners("copilot-event");
|
|
146
176
|
import_electron.ipcRenderer.removeAllListeners("copilot-tool-start");
|
|
177
|
+
import_electron.ipcRenderer.removeAllListeners("copilot-ask-user");
|
|
147
178
|
import_electron.ipcRenderer.removeAllListeners("copilot-tool-complete");
|
|
148
179
|
import_electron.ipcRenderer.removeAllListeners("copilot-turn-end");
|
|
149
180
|
import_electron.ipcRenderer.removeAllListeners("copilot-turn-start");
|
|
@@ -158,6 +189,19 @@ import_electron.contextBridge.exposeInMainWorld("copilotBridge", {
|
|
|
158
189
|
showNativeNotification: (title, body) => {
|
|
159
190
|
return import_electron.ipcRenderer.invoke("show-native-notification", title, body);
|
|
160
191
|
},
|
|
192
|
+
// Terminal backend selection (ui-server / node-pty / sdk).
|
|
193
|
+
getBackendInfo: () => {
|
|
194
|
+
return import_electron.ipcRenderer.invoke("terminal-backend-info");
|
|
195
|
+
},
|
|
196
|
+
onBackendFallback: (callback) => {
|
|
197
|
+
import_electron.ipcRenderer.on("backend-fallback", (_event, info) => callback(info));
|
|
198
|
+
},
|
|
199
|
+
onBackendOnline: (callback) => {
|
|
200
|
+
import_electron.ipcRenderer.on("backend-online", (_event, officeId, backend) => callback(officeId, backend));
|
|
201
|
+
},
|
|
202
|
+
onBackendSessionFallback: (callback) => {
|
|
203
|
+
import_electron.ipcRenderer.on("backend-session-fallback", (_event, officeId, agentId, reason) => callback(officeId, agentId, reason));
|
|
204
|
+
},
|
|
161
205
|
// Spec 003 follow-up: write to OS clipboard via Electron main process.
|
|
162
206
|
// Bypasses Permissions API + focus restrictions that make
|
|
163
207
|
// navigator.clipboard.writeText unreliable in xterm-focused contexts.
|