copilotoffice 2.1.0 → 2.2.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 +108 -75
- package/dist/electron/main.js +19 -1
- package/dist/electron/terminal/ipc-relay.js +19 -1
- package/dist/electron/terminal/preload.js +50 -13
- package/dist/electron/terminal/server.js +5740 -887
- package/dist/game.bundle.js +75 -30
- package/package.json +8 -4
package/dist/game.bundle.js
CHANGED
|
@@ -151867,7 +151867,7 @@ ${h2.join(`
|
|
|
151867
151867
|
document.body.appendChild(el2);
|
|
151868
151868
|
return el2;
|
|
151869
151869
|
}
|
|
151870
|
-
function showClipboardToast(message, kind = "info") {
|
|
151870
|
+
function showClipboardToast(message, kind = "info", durationMs = TOAST_DURATION_MS) {
|
|
151871
151871
|
const el2 = ensureToastElement();
|
|
151872
151872
|
if (!el2) return;
|
|
151873
151873
|
el2.textContent = message;
|
|
@@ -151876,7 +151876,7 @@ ${h2.join(`
|
|
|
151876
151876
|
if (dismissTimer) clearTimeout(dismissTimer);
|
|
151877
151877
|
dismissTimer = setTimeout(() => {
|
|
151878
151878
|
el2.style.opacity = "0";
|
|
151879
|
-
},
|
|
151879
|
+
}, durationMs);
|
|
151880
151880
|
}
|
|
151881
151881
|
var TOAST_ID, TOAST_DURATION_MS, dismissTimer, KIND_BG;
|
|
151882
151882
|
var init_clipboardToast = __esm({
|
|
@@ -152554,6 +152554,14 @@ ${h2.join(`
|
|
|
152554
152554
|
// Disposable returned by xterm.terminal.onData(...). Re-registered per show()
|
|
152555
152555
|
// so the handler's closure captures the new agent id (feature 002, C3/V6).
|
|
152556
152556
|
this.onDataDisposable = null;
|
|
152557
|
+
// Unsubscribe functions for the bridge IPC listeners this overlay owns
|
|
152558
|
+
// (terminal-data, terminal-exit, session-meta-updated). Disposed and
|
|
152559
|
+
// re-created on every setupTerminalListeners() call so an overlay can never
|
|
152560
|
+
// accumulate duplicate listeners — duplicates would write each PTY byte to
|
|
152561
|
+
// xterm more than once (the "double characters" bug).
|
|
152562
|
+
this.bridgeListenerDisposers = [];
|
|
152563
|
+
// Teams status listener has no disposer on the bridge; bind it at most once.
|
|
152564
|
+
this.teamsListenerBound = false;
|
|
152557
152565
|
// Toggled while show() is awaiting detach/attach so onData cannot fire input
|
|
152558
152566
|
// against a half-attached agent (feature 002, V5).
|
|
152559
152567
|
this.isSwitchingAgent = false;
|
|
@@ -152582,35 +152590,50 @@ ${h2.join(`
|
|
|
152582
152590
|
}
|
|
152583
152591
|
setupTerminalListeners() {
|
|
152584
152592
|
if (typeof window !== "undefined" && window.copilotBridge) {
|
|
152585
|
-
|
|
152586
|
-
|
|
152587
|
-
|
|
152588
|
-
|
|
152593
|
+
for (const dispose of this.bridgeListenerDisposers) {
|
|
152594
|
+
try {
|
|
152595
|
+
dispose();
|
|
152596
|
+
} catch {
|
|
152589
152597
|
}
|
|
152590
|
-
}
|
|
152591
|
-
|
|
152592
|
-
|
|
152593
|
-
|
|
152598
|
+
}
|
|
152599
|
+
this.bridgeListenerDisposers = [];
|
|
152600
|
+
this.bridgeListenerDisposers.push(
|
|
152601
|
+
window.copilotBridge.onTerminalData((agentId, data) => {
|
|
152602
|
+
if (this.isReplaying) return;
|
|
152603
|
+
if (agentId === this.currentAgentId && this.terminal) {
|
|
152604
|
+
this.terminal.write(data);
|
|
152605
|
+
}
|
|
152606
|
+
})
|
|
152607
|
+
);
|
|
152608
|
+
this.bridgeListenerDisposers.push(
|
|
152609
|
+
window.copilotBridge.onTerminalExit((agentId, exitCode) => {
|
|
152610
|
+
if (agentId === this.currentAgentId && this.terminal) {
|
|
152611
|
+
this.terminal.writeln(`\r
|
|
152594
152612
|
[Process exited with code ${exitCode}]`);
|
|
152595
|
-
|
|
152596
|
-
|
|
152597
|
-
|
|
152598
|
-
|
|
152599
|
-
|
|
152600
|
-
|
|
152601
|
-
|
|
152602
|
-
|
|
152603
|
-
|
|
152604
|
-
|
|
152605
|
-
|
|
152606
|
-
|
|
152613
|
+
}
|
|
152614
|
+
})
|
|
152615
|
+
);
|
|
152616
|
+
this.bridgeListenerDisposers.push(
|
|
152617
|
+
window.copilotBridge.onSessionMetaUpdated((agentId, meta) => {
|
|
152618
|
+
if (agentId === this.currentAgentId) {
|
|
152619
|
+
this.updateSessionTitleDisplay(meta?.title || null);
|
|
152620
|
+
}
|
|
152621
|
+
})
|
|
152622
|
+
);
|
|
152623
|
+
if (!this.teamsListenerBound) {
|
|
152624
|
+
window.copilotBridge.onTeamsStatusChanged?.((status) => {
|
|
152625
|
+
if (status?.agentId === this.currentAgentId) {
|
|
152626
|
+
this.setTeamsButtonState(!!status.online);
|
|
152627
|
+
}
|
|
152628
|
+
});
|
|
152629
|
+
this.teamsListenerBound = true;
|
|
152630
|
+
}
|
|
152607
152631
|
}
|
|
152608
152632
|
}
|
|
152609
152633
|
/**
|
|
152610
|
-
* Re-register IPC listeners
|
|
152611
|
-
*
|
|
152612
|
-
*
|
|
152613
|
-
* all guard on `this.currentAgentId`.
|
|
152634
|
+
* Re-register this overlay's IPC listeners. setupTerminalListeners() is
|
|
152635
|
+
* idempotent (it disposes prior registrations first), so calling this after
|
|
152636
|
+
* returning from another scene cannot create duplicate listeners.
|
|
152614
152637
|
*/
|
|
152615
152638
|
reattachListeners() {
|
|
152616
152639
|
this.setupTerminalListeners();
|
|
@@ -152948,7 +152971,7 @@ ${h2.join(`
|
|
|
152948
152971
|
} else {
|
|
152949
152972
|
this.fitAndResizeTerminal({ officeId, agentId: agent.id });
|
|
152950
152973
|
const attachResult = await withTimeout(
|
|
152951
|
-
window.copilotBridge.terminalAttach(officeId, agent.id),
|
|
152974
|
+
window.copilotBridge.terminalAttach(officeId, agent.id, true),
|
|
152952
152975
|
IPC_TIMEOUT,
|
|
152953
152976
|
"terminalAttach"
|
|
152954
152977
|
);
|
|
@@ -154107,9 +154130,13 @@ ${h2.join(`
|
|
|
154107
154130
|
} catch {
|
|
154108
154131
|
}
|
|
154109
154132
|
this.spriteCardElement = null;
|
|
154110
|
-
|
|
154111
|
-
|
|
154133
|
+
for (const dispose of this.bridgeListenerDisposers) {
|
|
154134
|
+
try {
|
|
154135
|
+
dispose();
|
|
154136
|
+
} catch {
|
|
154137
|
+
}
|
|
154112
154138
|
}
|
|
154139
|
+
this.bridgeListenerDisposers = [];
|
|
154113
154140
|
}
|
|
154114
154141
|
};
|
|
154115
154142
|
}
|
|
@@ -160844,7 +160871,7 @@ Failed to start terminal: ${startResult.error || "unknown error"}`);
|
|
|
160844
160871
|
this.updateSessionIdDisplay();
|
|
160845
160872
|
}
|
|
160846
160873
|
}
|
|
160847
|
-
const attachResult = await window.copilotBridge.terminalAttach(options.officeId, options.agentId);
|
|
160874
|
+
const attachResult = await window.copilotBridge.terminalAttach(options.officeId, options.agentId, true);
|
|
160848
160875
|
if (!attachResult.success) {
|
|
160849
160876
|
this.terminal.writeln("\r\nFailed to attach terminal session.");
|
|
160850
160877
|
this.setStatus("Attach failed");
|
|
@@ -163315,6 +163342,24 @@ Terminal error: ${error?.message || String(error)}`);
|
|
|
163315
163342
|
const kind = toast.level === "error" ? "error" : toast.level === "warn" ? "error" : "info";
|
|
163316
163343
|
showClipboardToast(toast.message, kind);
|
|
163317
163344
|
});
|
|
163345
|
+
let backendFallbackToastShown = false;
|
|
163346
|
+
const showBackendFallbackToast = (info) => {
|
|
163347
|
+
if (!info || !info.fellBack || backendFallbackToastShown) return;
|
|
163348
|
+
backendFallbackToastShown = true;
|
|
163349
|
+
const detail = info.reason ? ` (${info.reason})` : "";
|
|
163350
|
+
showClipboardToast(`Terminal: ${info.requested} unavailable \u2014 using ${info.name}${detail}`, "error", 1e4);
|
|
163351
|
+
};
|
|
163352
|
+
window.copilotBridge.onBackendFallback?.((info) => showBackendFallbackToast(info));
|
|
163353
|
+
void window.copilotBridge.getBackendInfo?.().then((info) => showBackendFallbackToast(info));
|
|
163354
|
+
window.copilotBridge.onBackendOnline?.((officeId, _backend) => {
|
|
163355
|
+
const officeName = officeManager.getOffice(officeId)?.config.name ?? officeId;
|
|
163356
|
+
showClipboardToast(`GitHub Copilot SDK server online for ${officeName}`, "success", 1e4);
|
|
163357
|
+
});
|
|
163358
|
+
window.copilotBridge.onBackendSessionFallback?.((_officeId, agentId, reason) => {
|
|
163359
|
+
const agentName = getAgentConfig(agentId)?.name ?? agentId;
|
|
163360
|
+
const detail = reason ? ` (${reason})` : "";
|
|
163361
|
+
showClipboardToast(`${agentName}: UI-server unavailable \u2014 using node-pty${detail}`, "error", 1e4);
|
|
163362
|
+
});
|
|
163318
163363
|
window.copilotBridge.onTeamsStatusChanged?.((status) => {
|
|
163319
163364
|
if (!status?.agentId) return;
|
|
163320
163365
|
if (status.online) teamsOnlineAgentIds.add(status.agentId);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "copilotoffice",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "A 2D pixel-art game where you interact with AI agents
|
|
3
|
+
"version": "2.2.0",
|
|
4
|
+
"description": "A 2D pixel-art desktop game where you walk around a virtual office and interact with AI agents that run real GitHub Copilot CLI sessions — plan, debug, orchestrate fleets, and drive agents from Microsoft Teams.",
|
|
5
5
|
"main": "dist/electron/main.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"copilotoffice": "bin/copilotoffice.js"
|
|
@@ -26,15 +26,19 @@
|
|
|
26
26
|
},
|
|
27
27
|
"keywords": [
|
|
28
28
|
"copilot",
|
|
29
|
+
"copilot-cli",
|
|
30
|
+
"ai-agents",
|
|
29
31
|
"game",
|
|
30
32
|
"pixel-art",
|
|
31
|
-
"
|
|
33
|
+
"phaser",
|
|
34
|
+
"electron",
|
|
35
|
+
"typescript"
|
|
32
36
|
],
|
|
33
37
|
"author": "",
|
|
34
38
|
"license": "ISC",
|
|
35
39
|
"type": "commonjs",
|
|
36
40
|
"dependencies": {
|
|
37
|
-
"@github/copilot-sdk": "
|
|
41
|
+
"@github/copilot-sdk": "1.0.5",
|
|
38
42
|
"@xterm/addon-fit": "^0.11.0",
|
|
39
43
|
"@xterm/xterm": "^6.0.0",
|
|
40
44
|
"ansi-to-html": "^0.7.2",
|