copilotoffice 1.1.1 → 2.1.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/dist/electron/main.js +2384 -175
- package/dist/electron/terminal/ipc-relay.js +191 -6
- package/dist/electron/terminal/preload.js +45 -0
- package/dist/electron/terminal/server.js +310 -68
- package/dist/game.bundle.js +14319 -8771
- package/package.json +8 -6
|
@@ -3044,7 +3044,7 @@ var require_main = __commonJS({
|
|
|
3044
3044
|
exports2.createMessageConnection = exports2.createServerSocketTransport = exports2.createClientSocketTransport = exports2.createServerPipeTransport = exports2.createClientPipeTransport = exports2.generateRandomPipeName = exports2.StreamMessageWriter = exports2.StreamMessageReader = exports2.SocketMessageWriter = exports2.SocketMessageReader = exports2.PortMessageWriter = exports2.PortMessageReader = exports2.IPCMessageWriter = exports2.IPCMessageReader = void 0;
|
|
3045
3045
|
var ril_1 = require_ril();
|
|
3046
3046
|
ril_1.default.install();
|
|
3047
|
-
var
|
|
3047
|
+
var path5 = require("path");
|
|
3048
3048
|
var os4 = require("os");
|
|
3049
3049
|
var crypto_1 = require("crypto");
|
|
3050
3050
|
var net_1 = require("net");
|
|
@@ -3180,9 +3180,9 @@ var require_main = __commonJS({
|
|
|
3180
3180
|
}
|
|
3181
3181
|
let result;
|
|
3182
3182
|
if (XDG_RUNTIME_DIR) {
|
|
3183
|
-
result =
|
|
3183
|
+
result = path5.join(XDG_RUNTIME_DIR, `vscode-ipc-${randomSuffix}.sock`);
|
|
3184
3184
|
} else {
|
|
3185
|
-
result =
|
|
3185
|
+
result = path5.join(os4.tmpdir(), `vscode-${randomSuffix}.sock`);
|
|
3186
3186
|
}
|
|
3187
3187
|
const limit = safeIpcPathLengths.get(process.platform);
|
|
3188
3188
|
if (limit !== void 0 && result.length > limit) {
|
|
@@ -5067,10 +5067,10 @@ var init_dist = __esm({
|
|
|
5067
5067
|
});
|
|
5068
5068
|
|
|
5069
5069
|
// electron/terminal/server.ts
|
|
5070
|
-
var
|
|
5070
|
+
var path4 = __toESM(require("path"));
|
|
5071
5071
|
var os3 = __toESM(require("os"));
|
|
5072
|
-
var
|
|
5073
|
-
var
|
|
5072
|
+
var fs3 = __toESM(require("fs"));
|
|
5073
|
+
var crypto3 = __toESM(require("crypto"));
|
|
5074
5074
|
var import_child_process2 = require("child_process");
|
|
5075
5075
|
|
|
5076
5076
|
// electron/terminal/events-watcher.ts
|
|
@@ -5407,14 +5407,7 @@ var CopilotSdkProcess = class {
|
|
|
5407
5407
|
this.emitPrompt();
|
|
5408
5408
|
continue;
|
|
5409
5409
|
}
|
|
5410
|
-
this.
|
|
5411
|
-
this.queuedSend = this.queuedSend.then(async () => {
|
|
5412
|
-
await this.session.send({ prompt, mode: "enqueue" });
|
|
5413
|
-
}).catch((error) => {
|
|
5414
|
-
this.emitData(`\x1B[31m[SDK send failed: ${String(error)}]\x1B[0m\r
|
|
5415
|
-
`);
|
|
5416
|
-
this.emitPrompt();
|
|
5417
|
-
});
|
|
5410
|
+
this.enqueuePrompt(prompt);
|
|
5418
5411
|
continue;
|
|
5419
5412
|
}
|
|
5420
5413
|
if (ch === "\b" || ch === "\x7F") {
|
|
@@ -5428,6 +5421,34 @@ var CopilotSdkProcess = class {
|
|
|
5428
5421
|
this.emitData(ch);
|
|
5429
5422
|
}
|
|
5430
5423
|
}
|
|
5424
|
+
/**
|
|
5425
|
+
* Submit a complete prompt directly to the SDK session, bypassing the
|
|
5426
|
+
* line-editor. Handles multi-line prompts atomically (no premature submit on
|
|
5427
|
+
* embedded newlines) and echoes the prompt so it appears in the terminal as
|
|
5428
|
+
* if typed. This is the robust path used by programmatic drivers (e.g. Teams
|
|
5429
|
+
* remote dispatch) instead of racing keystrokes through `write()`.
|
|
5430
|
+
*/
|
|
5431
|
+
submitPrompt(text, label) {
|
|
5432
|
+
if (this.closed) return;
|
|
5433
|
+
const prompt = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").trim();
|
|
5434
|
+
if (!prompt) return;
|
|
5435
|
+
this.lineBuffer = "";
|
|
5436
|
+
const tag = label ? `\x1B[2;36m[${label}]\x1B[0m ` : "";
|
|
5437
|
+
this.emitData(`${tag}${prompt.replace(/\n/g, "\r\n")}\r
|
|
5438
|
+
`);
|
|
5439
|
+
this.enqueuePrompt(prompt);
|
|
5440
|
+
}
|
|
5441
|
+
/** Queue a prompt for the SDK session, serialized after any in-flight send. */
|
|
5442
|
+
enqueuePrompt(prompt) {
|
|
5443
|
+
this.promptPending = true;
|
|
5444
|
+
this.queuedSend = this.queuedSend.then(async () => {
|
|
5445
|
+
await this.session.send({ prompt, mode: "enqueue" });
|
|
5446
|
+
}).catch((error) => {
|
|
5447
|
+
this.emitData(`\x1B[31m[SDK send failed: ${String(error)}]\x1B[0m\r
|
|
5448
|
+
`);
|
|
5449
|
+
this.emitPrompt();
|
|
5450
|
+
});
|
|
5451
|
+
}
|
|
5431
5452
|
resize(_cols, _rows) {
|
|
5432
5453
|
}
|
|
5433
5454
|
onData(callback) {
|
|
@@ -5595,25 +5616,127 @@ function createSdkCliLaunchConfig(cliPath) {
|
|
|
5595
5616
|
return { cliPath, cliArgs: [] };
|
|
5596
5617
|
}
|
|
5597
5618
|
|
|
5619
|
+
// electron/terminal/agent-viewers.ts
|
|
5620
|
+
function addAgentViewer(ck, maps) {
|
|
5621
|
+
maps.activeAgentViewers.add(ck);
|
|
5622
|
+
const termKey = maps.agentToTerminal.get(ck);
|
|
5623
|
+
if (termKey && termKey !== ck) {
|
|
5624
|
+
maps.activeAgentViewers.add(termKey);
|
|
5625
|
+
return { aliasKey: termKey };
|
|
5626
|
+
}
|
|
5627
|
+
return { aliasKey: null };
|
|
5628
|
+
}
|
|
5629
|
+
function removeAgentViewer(ck, maps) {
|
|
5630
|
+
maps.activeAgentViewers.delete(ck);
|
|
5631
|
+
const termKey = maps.agentToTerminal.get(ck);
|
|
5632
|
+
if (termKey && termKey !== ck) {
|
|
5633
|
+
maps.activeAgentViewers.delete(termKey);
|
|
5634
|
+
return { aliasKey: termKey };
|
|
5635
|
+
}
|
|
5636
|
+
return { aliasKey: null };
|
|
5637
|
+
}
|
|
5638
|
+
function hasActiveViewer(ck, maps) {
|
|
5639
|
+
if (maps.activeAgentViewers.has(ck)) return true;
|
|
5640
|
+
for (const [alias, termKey] of maps.agentToTerminal) {
|
|
5641
|
+
if (termKey === ck && maps.activeAgentViewers.has(alias)) return true;
|
|
5642
|
+
}
|
|
5643
|
+
return false;
|
|
5644
|
+
}
|
|
5645
|
+
|
|
5646
|
+
// electron/terminal/session-repair.ts
|
|
5647
|
+
var crypto = __toESM(require("crypto"));
|
|
5648
|
+
function repairDuplicateSessionIds(officeId, data, options = {}) {
|
|
5649
|
+
const logger = options.logger ?? { warn: (msg) => console.warn(msg) };
|
|
5650
|
+
const mintId = options.mintId ?? (() => crypto.randomUUID());
|
|
5651
|
+
const seen = /* @__PURE__ */ new Map();
|
|
5652
|
+
let repaired = false;
|
|
5653
|
+
for (const [agentId, sessionId] of data.sessionIds.entries()) {
|
|
5654
|
+
const firstAgent = seen.get(sessionId);
|
|
5655
|
+
if (firstAgent === void 0) {
|
|
5656
|
+
seen.set(sessionId, agentId);
|
|
5657
|
+
continue;
|
|
5658
|
+
}
|
|
5659
|
+
let fresh = mintId();
|
|
5660
|
+
while (seen.has(fresh) || data.sessionIds.get(agentId) === fresh) {
|
|
5661
|
+
fresh = mintId();
|
|
5662
|
+
}
|
|
5663
|
+
data.sessionIds.set(agentId, fresh);
|
|
5664
|
+
seen.set(fresh, agentId);
|
|
5665
|
+
logger.warn(
|
|
5666
|
+
`[TermServer] Repaired duplicate sessionId for officeId=${officeId} agentId=${agentId} from=${sessionId} to=${fresh}`
|
|
5667
|
+
);
|
|
5668
|
+
repaired = true;
|
|
5669
|
+
}
|
|
5670
|
+
return repaired;
|
|
5671
|
+
}
|
|
5672
|
+
|
|
5673
|
+
// electron/terminal/pty-registry.ts
|
|
5674
|
+
var crypto2 = __toESM(require("crypto"));
|
|
5675
|
+
var fs2 = __toESM(require("fs"));
|
|
5676
|
+
var path3 = __toESM(require("path"));
|
|
5677
|
+
var DEFAULT_REGISTRY_FILE = path3.join(process.cwd(), ".data", "pty-pids.json");
|
|
5678
|
+
function readPtyRegistry(file = DEFAULT_REGISTRY_FILE) {
|
|
5679
|
+
try {
|
|
5680
|
+
const raw = fs2.readFileSync(file, "utf8");
|
|
5681
|
+
const parsed = JSON.parse(raw);
|
|
5682
|
+
if (!Array.isArray(parsed)) return [];
|
|
5683
|
+
return parsed.filter(
|
|
5684
|
+
(r) => !!r && typeof r.pid === "number" && Number.isFinite(r.pid)
|
|
5685
|
+
);
|
|
5686
|
+
} catch {
|
|
5687
|
+
return [];
|
|
5688
|
+
}
|
|
5689
|
+
}
|
|
5690
|
+
function writePtyRegistry(records, file = DEFAULT_REGISTRY_FILE) {
|
|
5691
|
+
try {
|
|
5692
|
+
fs2.mkdirSync(path3.dirname(file), { recursive: true });
|
|
5693
|
+
const tmp = `${file}.${process.pid}.${crypto2.randomBytes(4).toString("hex")}.tmp`;
|
|
5694
|
+
fs2.writeFileSync(tmp, JSON.stringify(records, null, 2));
|
|
5695
|
+
fs2.renameSync(tmp, file);
|
|
5696
|
+
} catch {
|
|
5697
|
+
}
|
|
5698
|
+
}
|
|
5699
|
+
function registerPty(record, file = DEFAULT_REGISTRY_FILE) {
|
|
5700
|
+
const records = readPtyRegistry(file).filter((r) => r.pid !== record.pid);
|
|
5701
|
+
records.push(record);
|
|
5702
|
+
writePtyRegistry(records, file);
|
|
5703
|
+
}
|
|
5704
|
+
function unregisterPty(pid, file = DEFAULT_REGISTRY_FILE) {
|
|
5705
|
+
const records = readPtyRegistry(file);
|
|
5706
|
+
const next = records.filter((r) => r.pid !== pid);
|
|
5707
|
+
if (next.length !== records.length) {
|
|
5708
|
+
writePtyRegistry(next, file);
|
|
5709
|
+
}
|
|
5710
|
+
}
|
|
5711
|
+
|
|
5598
5712
|
// electron/terminal/server.ts
|
|
5599
5713
|
var ptyProcesses = /* @__PURE__ */ new Map();
|
|
5714
|
+
var DEBUG_COLD_START = process.env.COPILOT_OFFICE_DEBUG_COLD_START === "1";
|
|
5600
5715
|
var agentToTerminal = /* @__PURE__ */ new Map();
|
|
5601
5716
|
var activeAgentViewers = /* @__PURE__ */ new Set();
|
|
5717
|
+
var agentForwardKeys = /* @__PURE__ */ new Set();
|
|
5718
|
+
var lastPtyDataAt = /* @__PURE__ */ new Map();
|
|
5719
|
+
var viewerMaps = { activeAgentViewers, agentToTerminal };
|
|
5602
5720
|
var agentWatchers = /* @__PURE__ */ new Map();
|
|
5603
5721
|
var terminalBackend = null;
|
|
5604
5722
|
var eventSourceFactory = new FileWatcherEventSourceFactory();
|
|
5605
5723
|
var agentReadyState = /* @__PURE__ */ new Map();
|
|
5606
5724
|
var agentInTurn = /* @__PURE__ */ new Map();
|
|
5725
|
+
var userMessageSeq = /* @__PURE__ */ new Map();
|
|
5726
|
+
var lastUserMessageText = /* @__PURE__ */ new Map();
|
|
5727
|
+
function normalizePromptText(s) {
|
|
5728
|
+
return s.replace(/\s+/g, " ").trim();
|
|
5729
|
+
}
|
|
5607
5730
|
var MAX_BUFFER_BYTES = 512 * 1024;
|
|
5608
5731
|
var agentScrollbackBuffers = /* @__PURE__ */ new Map();
|
|
5609
5732
|
var agentScrollbackBytes = /* @__PURE__ */ new Map();
|
|
5610
|
-
var DATA_DIR =
|
|
5611
|
-
var OLD_SESSION_FILE =
|
|
5612
|
-
var OFFICES_FILE =
|
|
5733
|
+
var DATA_DIR = path4.join(process.cwd(), ".data");
|
|
5734
|
+
var OLD_SESSION_FILE = path4.join(DATA_DIR, "copilot-office-sessions.json");
|
|
5735
|
+
var OFFICES_FILE = path4.join(DATA_DIR, "copilot-offices.json");
|
|
5613
5736
|
var officeSessions = /* @__PURE__ */ new Map();
|
|
5614
5737
|
var hasAutoTitled = /* @__PURE__ */ new Set();
|
|
5615
5738
|
function getSessionFile(officeId) {
|
|
5616
|
-
return
|
|
5739
|
+
return path4.join(DATA_DIR, `${officeId}.sessions.json`);
|
|
5617
5740
|
}
|
|
5618
5741
|
function getOfficeSession(officeId) {
|
|
5619
5742
|
let data = officeSessions.get(officeId);
|
|
@@ -5626,12 +5749,8 @@ function getOfficeSession(officeId) {
|
|
|
5626
5749
|
function compositeKey(officeId, agentId) {
|
|
5627
5750
|
return `${officeId}:${agentId}`;
|
|
5628
5751
|
}
|
|
5629
|
-
function
|
|
5630
|
-
|
|
5631
|
-
for (const [alias, termKey] of agentToTerminal) {
|
|
5632
|
-
if (termKey === ck && activeAgentViewers.has(alias)) return true;
|
|
5633
|
-
}
|
|
5634
|
-
return false;
|
|
5752
|
+
function hasActiveViewer2(ck) {
|
|
5753
|
+
return hasActiveViewer(ck, viewerMaps);
|
|
5635
5754
|
}
|
|
5636
5755
|
function send(msg) {
|
|
5637
5756
|
if (process.send) {
|
|
@@ -5655,9 +5774,9 @@ function appendToScrollback(agentId, data) {
|
|
|
5655
5774
|
}
|
|
5656
5775
|
async function loadOfficeSessionFile(officeId) {
|
|
5657
5776
|
try {
|
|
5658
|
-
await
|
|
5777
|
+
await fs3.promises.mkdir(DATA_DIR, { recursive: true });
|
|
5659
5778
|
const filePath = getSessionFile(officeId);
|
|
5660
|
-
const raw = await
|
|
5779
|
+
const raw = await fs3.promises.readFile(filePath, "utf8").catch(() => null);
|
|
5661
5780
|
const data = getOfficeSession(officeId);
|
|
5662
5781
|
if (raw) {
|
|
5663
5782
|
const parsed = JSON.parse(raw);
|
|
@@ -5675,6 +5794,10 @@ async function loadOfficeSessionFile(officeId) {
|
|
|
5675
5794
|
data.sessionMeta = /* @__PURE__ */ new Map();
|
|
5676
5795
|
await saveOfficeSessionFile(officeId);
|
|
5677
5796
|
}
|
|
5797
|
+
const repaired = repairDuplicateSessionIds(officeId, data);
|
|
5798
|
+
if (repaired) {
|
|
5799
|
+
await saveOfficeSessionFile(officeId);
|
|
5800
|
+
}
|
|
5678
5801
|
console.log(`[TermServer] Loaded sessions for ${officeId}: ${data.sessionIds.size} current, ${data.sessionHistory.size} history`);
|
|
5679
5802
|
}
|
|
5680
5803
|
} catch (e) {
|
|
@@ -5683,27 +5806,27 @@ async function loadOfficeSessionFile(officeId) {
|
|
|
5683
5806
|
}
|
|
5684
5807
|
async function saveOfficeSessionFile(officeId) {
|
|
5685
5808
|
try {
|
|
5686
|
-
await
|
|
5809
|
+
await fs3.promises.mkdir(DATA_DIR, { recursive: true });
|
|
5687
5810
|
const data = getOfficeSession(officeId);
|
|
5688
5811
|
const json = {
|
|
5689
5812
|
current: Object.fromEntries(data.sessionIds),
|
|
5690
5813
|
history: Object.fromEntries(data.sessionHistory),
|
|
5691
5814
|
metadata: Object.fromEntries(data.sessionMeta)
|
|
5692
5815
|
};
|
|
5693
|
-
await
|
|
5816
|
+
await fs3.promises.writeFile(getSessionFile(officeId), JSON.stringify(json, null, 2));
|
|
5694
5817
|
} catch (e) {
|
|
5695
5818
|
console.error(`[TermServer] Failed to save sessions for ${officeId}:`, e);
|
|
5696
5819
|
}
|
|
5697
5820
|
}
|
|
5698
5821
|
async function createEmptySessionFile(officeId) {
|
|
5699
|
-
await
|
|
5822
|
+
await fs3.promises.mkdir(DATA_DIR, { recursive: true });
|
|
5700
5823
|
const filePath = getSessionFile(officeId);
|
|
5701
5824
|
try {
|
|
5702
|
-
await
|
|
5825
|
+
await fs3.promises.access(filePath, fs3.constants.F_OK);
|
|
5703
5826
|
await loadOfficeSessionFile(officeId);
|
|
5704
5827
|
} catch {
|
|
5705
5828
|
const empty = { current: {}, history: {}, metadata: {} };
|
|
5706
|
-
await
|
|
5829
|
+
await fs3.promises.writeFile(filePath, JSON.stringify(empty, null, 2));
|
|
5707
5830
|
getOfficeSession(officeId);
|
|
5708
5831
|
console.log(`[TermServer] Created empty session file for ${officeId}: ${filePath}`);
|
|
5709
5832
|
}
|
|
@@ -5723,19 +5846,19 @@ async function migrateGlobalSessionFile() {
|
|
|
5723
5846
|
try {
|
|
5724
5847
|
const migratedMarker = OLD_SESSION_FILE + ".migrated";
|
|
5725
5848
|
try {
|
|
5726
|
-
await
|
|
5849
|
+
await fs3.promises.access(migratedMarker, fs3.constants.F_OK);
|
|
5727
5850
|
return;
|
|
5728
5851
|
} catch {
|
|
5729
5852
|
}
|
|
5730
5853
|
let oldRaw = null;
|
|
5731
5854
|
try {
|
|
5732
|
-
oldRaw = await
|
|
5855
|
+
oldRaw = await fs3.promises.readFile(OLD_SESSION_FILE, "utf8");
|
|
5733
5856
|
} catch {
|
|
5734
5857
|
return;
|
|
5735
5858
|
}
|
|
5736
5859
|
let firstOfficeId = "office-0";
|
|
5737
5860
|
try {
|
|
5738
|
-
const officesRaw = await
|
|
5861
|
+
const officesRaw = await fs3.promises.readFile(OFFICES_FILE, "utf8");
|
|
5739
5862
|
const officesData = JSON.parse(officesRaw);
|
|
5740
5863
|
if (Array.isArray(officesData.offices) && officesData.offices.length > 0) {
|
|
5741
5864
|
firstOfficeId = officesData.offices[0].id || "office-0";
|
|
@@ -5744,22 +5867,22 @@ async function migrateGlobalSessionFile() {
|
|
|
5744
5867
|
}
|
|
5745
5868
|
const firstOfficeFile = getSessionFile(firstOfficeId);
|
|
5746
5869
|
try {
|
|
5747
|
-
await
|
|
5870
|
+
await fs3.promises.access(firstOfficeFile, fs3.constants.F_OK);
|
|
5748
5871
|
} catch {
|
|
5749
|
-
await
|
|
5872
|
+
await fs3.promises.writeFile(firstOfficeFile, oldRaw);
|
|
5750
5873
|
console.log(`[TermServer] Migrated global sessions to ${firstOfficeFile}`);
|
|
5751
5874
|
}
|
|
5752
|
-
await
|
|
5875
|
+
await fs3.promises.rename(OLD_SESSION_FILE, migratedMarker);
|
|
5753
5876
|
console.log(`[TermServer] Renamed old session file to ${migratedMarker}`);
|
|
5754
5877
|
} catch (e) {
|
|
5755
5878
|
console.error("[TermServer] Migration error:", e);
|
|
5756
5879
|
}
|
|
5757
5880
|
}
|
|
5758
5881
|
async function loadAllOfficeSessions() {
|
|
5759
|
-
await
|
|
5882
|
+
await fs3.promises.mkdir(DATA_DIR, { recursive: true });
|
|
5760
5883
|
await migrateGlobalSessionFile();
|
|
5761
5884
|
try {
|
|
5762
|
-
const files = await
|
|
5885
|
+
const files = await fs3.promises.readdir(DATA_DIR);
|
|
5763
5886
|
for (const file of files) {
|
|
5764
5887
|
const match = file.match(/^(.+)\.sessions\.json$/);
|
|
5765
5888
|
if (match) {
|
|
@@ -5777,6 +5900,46 @@ function getTerminalKey(officeId, agentId) {
|
|
|
5777
5900
|
if (ptyProcesses.has(ck)) return ck;
|
|
5778
5901
|
return null;
|
|
5779
5902
|
}
|
|
5903
|
+
function submitViaKeystrokes(proc, prompt, ck) {
|
|
5904
|
+
const text = prompt.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
5905
|
+
const safeWrite = (data) => {
|
|
5906
|
+
try {
|
|
5907
|
+
proc.write(data);
|
|
5908
|
+
} catch {
|
|
5909
|
+
}
|
|
5910
|
+
lastPtyDataAt.set(ck, Date.now());
|
|
5911
|
+
};
|
|
5912
|
+
const waitForIdle = (quietMs, capMs) => new Promise((resolve) => {
|
|
5913
|
+
const start = Date.now();
|
|
5914
|
+
const tick = () => {
|
|
5915
|
+
const last = lastPtyDataAt.get(ck) ?? 0;
|
|
5916
|
+
if (Date.now() - last >= quietMs || Date.now() - start >= capMs) resolve();
|
|
5917
|
+
else setTimeout(tick, 50);
|
|
5918
|
+
};
|
|
5919
|
+
tick();
|
|
5920
|
+
});
|
|
5921
|
+
void (async () => {
|
|
5922
|
+
await waitForIdle(300, 3e3);
|
|
5923
|
+
safeWrite("");
|
|
5924
|
+
safeWrite(`\x1B[200~${text}\x1B[201~`);
|
|
5925
|
+
await waitForIdle(250, 2e3);
|
|
5926
|
+
const delay = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
5927
|
+
const want = normalizePromptText(text);
|
|
5928
|
+
const baseline = userMessageSeq.get(ck) ?? 0;
|
|
5929
|
+
const accepted = () => (userMessageSeq.get(ck) ?? 0) > baseline && lastUserMessageText.get(ck) === want;
|
|
5930
|
+
const MAX_ATTEMPTS = 12;
|
|
5931
|
+
const POLL_MS = 500;
|
|
5932
|
+
for (let attempt = 0; attempt < MAX_ATTEMPTS && !accepted(); attempt++) {
|
|
5933
|
+
safeWrite("\r");
|
|
5934
|
+
for (let waited = 0; waited < POLL_MS && !accepted(); waited += 50) {
|
|
5935
|
+
await delay(50);
|
|
5936
|
+
}
|
|
5937
|
+
}
|
|
5938
|
+
if (!accepted()) {
|
|
5939
|
+
console.warn(`[TermServer] submitViaKeystrokes: prompt not confirmed accepted after ${MAX_ATTEMPTS} Enter attempts for ${ck} \u2014 it may not have submitted.`);
|
|
5940
|
+
}
|
|
5941
|
+
})();
|
|
5942
|
+
}
|
|
5780
5943
|
function killAllPtyProcesses() {
|
|
5781
5944
|
console.log(`[TermServer] Killing ${ptyProcesses.size} PTY processes`);
|
|
5782
5945
|
ptyProcesses.forEach((proc) => killPtyProcess(proc));
|
|
@@ -5787,9 +5950,15 @@ function killAllPtyProcesses() {
|
|
|
5787
5950
|
}
|
|
5788
5951
|
function killPtyProcess(proc) {
|
|
5789
5952
|
proc.process.kill();
|
|
5953
|
+
unregisterPty(proc.pid);
|
|
5790
5954
|
}
|
|
5955
|
+
var yoloEnabled = false;
|
|
5956
|
+
var additionalParams = "";
|
|
5791
5957
|
var pendingPreseededPrompts = /* @__PURE__ */ new Map();
|
|
5792
5958
|
async function startTerminalForAgent(officeId, agentId, workingDir, cols, rows, preseededPrompt, launchMode = "copilot") {
|
|
5959
|
+
if (process.env.COPILOT_E2E === "1") {
|
|
5960
|
+
launchMode = "shell";
|
|
5961
|
+
}
|
|
5793
5962
|
if (!terminalBackend || !terminalBackend.isAvailable()) {
|
|
5794
5963
|
return { success: false, error: "terminal backend not available" };
|
|
5795
5964
|
}
|
|
@@ -5806,7 +5975,7 @@ async function startTerminalForAgent(officeId, agentId, workingDir, cols, rows,
|
|
|
5806
5975
|
const officeData = getOfficeSession(officeId);
|
|
5807
5976
|
let sessionId = officeData.sessionIds.get(agentId);
|
|
5808
5977
|
if (!sessionId) {
|
|
5809
|
-
sessionId =
|
|
5978
|
+
sessionId = crypto3.randomUUID();
|
|
5810
5979
|
officeData.sessionIds.set(agentId, sessionId);
|
|
5811
5980
|
await saveOfficeSessionFile(officeId);
|
|
5812
5981
|
console.log(`[TermServer] New session GUID for ${ck}: ${sessionId}`);
|
|
@@ -5817,9 +5986,9 @@ async function startTerminalForAgent(officeId, agentId, workingDir, cols, rows,
|
|
|
5817
5986
|
const shell = os3.platform() === "win32" ? "powershell.exe" : "bash";
|
|
5818
5987
|
let cwd = process.cwd();
|
|
5819
5988
|
if (workingDir) {
|
|
5820
|
-
const customPath =
|
|
5989
|
+
const customPath = path4.isAbsolute(workingDir) ? workingDir : path4.join(process.cwd(), workingDir);
|
|
5821
5990
|
try {
|
|
5822
|
-
await
|
|
5991
|
+
await fs3.promises.access(customPath, fs3.constants.F_OK);
|
|
5823
5992
|
cwd = customPath;
|
|
5824
5993
|
} catch {
|
|
5825
5994
|
}
|
|
@@ -5847,6 +6016,9 @@ async function startTerminalForAgent(officeId, agentId, workingDir, cols, rows,
|
|
|
5847
6016
|
workingDir
|
|
5848
6017
|
});
|
|
5849
6018
|
agentToTerminal.set(ck, terminalKey);
|
|
6019
|
+
if (terminalBackend.name === "node-pty") {
|
|
6020
|
+
registerPty({ pid: proc.pid, agentId, sessionId, startedAt: Date.now() });
|
|
6021
|
+
}
|
|
5850
6022
|
if (!shellOnlyMode) {
|
|
5851
6023
|
send({ type: "terminal-preload-status", agentId, status: "preloading" });
|
|
5852
6024
|
}
|
|
@@ -5905,8 +6077,16 @@ async function startTerminalForAgent(officeId, agentId, workingDir, cols, rows,
|
|
|
5905
6077
|
console.log(`[TermServer] Forwarding turn_start for ${ck}`);
|
|
5906
6078
|
send({ type: "copilot-turn-start", agentId });
|
|
5907
6079
|
} else if (event.type === "user.message") {
|
|
6080
|
+
userMessageSeq.set(ck, (userMessageSeq.get(ck) ?? 0) + 1);
|
|
6081
|
+
let rawUserText = "";
|
|
6082
|
+
{
|
|
6083
|
+
const d = event.data ?? {};
|
|
6084
|
+
const raw = d.content || d.message || d.text || d.input || d.prompt || d.body || "";
|
|
6085
|
+
rawUserText = String(raw);
|
|
6086
|
+
lastUserMessageText.set(ck, normalizePromptText(rawUserText));
|
|
6087
|
+
}
|
|
5908
6088
|
console.log(`[TermServer] Forwarding user_message for ${ck}, data keys: ${JSON.stringify(Object.keys(event.data || {}))}`);
|
|
5909
|
-
send({ type: "copilot-user-message", agentId });
|
|
6089
|
+
send({ type: "copilot-user-message", agentId, text: rawUserText });
|
|
5910
6090
|
const existing = officeData.sessionMeta.get(agentId);
|
|
5911
6091
|
const existingTitle = typeof existing?.title === "string" ? existing.title.trim() : "";
|
|
5912
6092
|
if (existingTitle) {
|
|
@@ -5939,8 +6119,10 @@ async function startTerminalForAgent(officeId, agentId, workingDir, cols, rows,
|
|
|
5939
6119
|
console.log(`[TermServer] Subagent FAILED for ${ck}: ${d.agentName ?? "unknown"} (toolCallId: ${d.toolCallId ?? "?"}, error: ${d.error ?? "unknown"})`);
|
|
5940
6120
|
}
|
|
5941
6121
|
const isFleetCriticalEvent = event.type.startsWith("subagent.") || event.type === "system.notification" || event.type === "tool.execution_start" && event.data?.toolName === "task";
|
|
5942
|
-
if (isFleetCriticalEvent ||
|
|
6122
|
+
if (isFleetCriticalEvent || hasActiveViewer2(ck)) {
|
|
5943
6123
|
send({ type: "copilot-event", agentId, event });
|
|
6124
|
+
} else if (agentForwardKeys.has(ck)) {
|
|
6125
|
+
send({ type: "copilot-event", agentId, event, mainOnly: true });
|
|
5944
6126
|
} else {
|
|
5945
6127
|
console.warn(`[TermServer] Dropped copilot-event ${event.type} for ${ck} \u2014 no active viewer (viewers: [${[...activeAgentViewers].join(", ")}])`);
|
|
5946
6128
|
}
|
|
@@ -5952,18 +6134,23 @@ async function startTerminalForAgent(officeId, agentId, workingDir, cols, rows,
|
|
|
5952
6134
|
let flushTimer = null;
|
|
5953
6135
|
const flushData = () => {
|
|
5954
6136
|
flushTimer = null;
|
|
5955
|
-
if (pendingData &&
|
|
6137
|
+
if (pendingData && hasActiveViewer2(ck)) {
|
|
5956
6138
|
send({ type: "terminal-data", agentId, data: pendingData });
|
|
5957
6139
|
}
|
|
5958
6140
|
pendingData = "";
|
|
5959
6141
|
};
|
|
5960
6142
|
proc.onData((data) => {
|
|
6143
|
+
lastPtyDataAt.set(ck, Date.now());
|
|
5961
6144
|
appendToScrollback(ck, data);
|
|
5962
|
-
if (!shellOnlyMode && terminalBackend?.name === "node-pty" && !hasSignalledReady
|
|
5963
|
-
|
|
5964
|
-
|
|
6145
|
+
if (!shellOnlyMode && terminalBackend?.name === "node-pty" && !hasSignalledReady) {
|
|
6146
|
+
const hasLegacyReadyMarker = data.includes("Environment loaded");
|
|
6147
|
+
const hasInteractiveFooter = data.includes("/ commands") || data.includes("? help");
|
|
6148
|
+
if (hasLegacyReadyMarker || hasInteractiveFooter) {
|
|
6149
|
+
console.log(`[TermServer] Ready signal for ${ck}: PTY marker detected`);
|
|
6150
|
+
signalReady();
|
|
6151
|
+
}
|
|
5965
6152
|
}
|
|
5966
|
-
if (!
|
|
6153
|
+
if (!hasActiveViewer2(ck)) return;
|
|
5967
6154
|
pendingData += data;
|
|
5968
6155
|
if (pendingData.length >= MAX_PENDING_BYTES) {
|
|
5969
6156
|
if (flushTimer) clearTimeout(flushTimer);
|
|
@@ -5974,12 +6161,16 @@ async function startTerminalForAgent(officeId, agentId, workingDir, cols, rows,
|
|
|
5974
6161
|
});
|
|
5975
6162
|
proc.onExit(({ exitCode }) => {
|
|
5976
6163
|
send({ type: "terminal-exit", agentId, exitCode });
|
|
6164
|
+
unregisterPty(proc.pid);
|
|
5977
6165
|
ptyProcesses.delete(terminalKey);
|
|
5978
6166
|
activeAgentViewers.delete(ck);
|
|
5979
6167
|
agentScrollbackBuffers.delete(ck);
|
|
5980
6168
|
agentScrollbackBytes.delete(ck);
|
|
5981
6169
|
agentReadyState.delete(ck);
|
|
5982
6170
|
agentInTurn.delete(ck);
|
|
6171
|
+
lastPtyDataAt.delete(ck);
|
|
6172
|
+
userMessageSeq.delete(ck);
|
|
6173
|
+
lastUserMessageText.delete(ck);
|
|
5983
6174
|
const w = agentWatchers.get(ck);
|
|
5984
6175
|
if (w) {
|
|
5985
6176
|
w.stop();
|
|
@@ -5988,8 +6179,10 @@ async function startTerminalForAgent(officeId, agentId, workingDir, cols, rows,
|
|
|
5988
6179
|
});
|
|
5989
6180
|
if (!shellOnlyMode && terminalBackend.name === "node-pty") {
|
|
5990
6181
|
setTimeout(() => {
|
|
5991
|
-
|
|
5992
|
-
|
|
6182
|
+
const yoloFlag = yoloEnabled ? " --yolo" : "";
|
|
6183
|
+
const extraParams = additionalParams ? ` ${additionalParams}` : "";
|
|
6184
|
+
console.log(`[TermServer] Starting copilot --session-id for ${ck}: ${sessionId}${yoloEnabled ? " (yolo)" : ""}${additionalParams ? ` (params: ${additionalParams})` : ""}`);
|
|
6185
|
+
proc.write(`copilot --session-id=${sessionId}${yoloFlag}${extraParams}\r`);
|
|
5993
6186
|
}, 500);
|
|
5994
6187
|
}
|
|
5995
6188
|
return { success: true, pid: proc.pid, sessionId };
|
|
@@ -6020,12 +6213,47 @@ async function handleMessage(msg) {
|
|
|
6020
6213
|
}
|
|
6021
6214
|
break;
|
|
6022
6215
|
}
|
|
6216
|
+
case "submit-prompt": {
|
|
6217
|
+
const key = getTerminalKey(msg.officeId, msg.agentId);
|
|
6218
|
+
const proc = key ? ptyProcesses.get(key) : null;
|
|
6219
|
+
if (proc) {
|
|
6220
|
+
agentForwardKeys.add(compositeKey(msg.officeId, msg.agentId));
|
|
6221
|
+
const backendProc = proc.process;
|
|
6222
|
+
if (typeof backendProc.submitPrompt === "function") {
|
|
6223
|
+
backendProc.submitPrompt(msg.prompt, msg.label);
|
|
6224
|
+
} else {
|
|
6225
|
+
submitViaKeystrokes(backendProc, msg.prompt, key);
|
|
6226
|
+
}
|
|
6227
|
+
send({ type: "response", requestId: msg.requestId, result: { success: true } });
|
|
6228
|
+
} else {
|
|
6229
|
+
const ck = compositeKey(msg.officeId, msg.agentId);
|
|
6230
|
+
console.log(`[TermServer] SUBMIT-PROMPT FAILED \u2014 no PTY for ${ck}`);
|
|
6231
|
+
send({ type: "response", requestId: msg.requestId, result: { success: false, error: `No PTY for ${ck}` } });
|
|
6232
|
+
}
|
|
6233
|
+
break;
|
|
6234
|
+
}
|
|
6235
|
+
case "set-agent-forwarding": {
|
|
6236
|
+
const ck = compositeKey(msg.officeId, msg.agentId);
|
|
6237
|
+
if (msg.enabled) agentForwardKeys.add(ck);
|
|
6238
|
+
else agentForwardKeys.delete(ck);
|
|
6239
|
+
break;
|
|
6240
|
+
}
|
|
6023
6241
|
case "resize": {
|
|
6024
6242
|
const key = getTerminalKey(msg.officeId, msg.agentId);
|
|
6025
6243
|
const proc = key ? ptyProcesses.get(key) : null;
|
|
6026
6244
|
if (proc) proc.process.resize(msg.cols, msg.rows);
|
|
6027
6245
|
break;
|
|
6028
6246
|
}
|
|
6247
|
+
case "set-yolo": {
|
|
6248
|
+
yoloEnabled = msg.enabled;
|
|
6249
|
+
console.log(`[TermServer] YOLO mode ${yoloEnabled ? "ENABLED" : "disabled"}`);
|
|
6250
|
+
break;
|
|
6251
|
+
}
|
|
6252
|
+
case "set-additional-params": {
|
|
6253
|
+
additionalParams = (msg.params ?? "").trim();
|
|
6254
|
+
console.log(`[TermServer] Additional params ${additionalParams ? `set: ${additionalParams}` : "cleared"}`);
|
|
6255
|
+
break;
|
|
6256
|
+
}
|
|
6029
6257
|
case "kill": {
|
|
6030
6258
|
const ck = compositeKey(msg.officeId, msg.agentId);
|
|
6031
6259
|
const key = getTerminalKey(msg.officeId, msg.agentId);
|
|
@@ -6058,11 +6286,9 @@ async function handleMessage(msg) {
|
|
|
6058
6286
|
case "attach": {
|
|
6059
6287
|
const ck = compositeKey(msg.officeId, msg.agentId);
|
|
6060
6288
|
console.log(`[TermServer] Attaching viewer for ${ck}`);
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
activeAgentViewers.add(termKey);
|
|
6065
|
-
console.log(`[TermServer] Also marking original key ${termKey} as active viewer (transferred session)`);
|
|
6289
|
+
const { aliasKey } = addAgentViewer(ck, viewerMaps);
|
|
6290
|
+
if (aliasKey) {
|
|
6291
|
+
console.log(`[TermServer] Also marking original key ${aliasKey} as active viewer (transferred session)`);
|
|
6066
6292
|
}
|
|
6067
6293
|
const chunks = agentScrollbackBuffers.get(ck) || [];
|
|
6068
6294
|
const rawScrollback = chunks.join("");
|
|
@@ -6072,11 +6298,7 @@ async function handleMessage(msg) {
|
|
|
6072
6298
|
case "detach": {
|
|
6073
6299
|
const ck = compositeKey(msg.officeId, msg.agentId);
|
|
6074
6300
|
console.log(`[TermServer] Detaching viewer for ${ck}`);
|
|
6075
|
-
|
|
6076
|
-
const termKey = agentToTerminal.get(ck);
|
|
6077
|
-
if (termKey && termKey !== ck) {
|
|
6078
|
-
activeAgentViewers.delete(termKey);
|
|
6079
|
-
}
|
|
6301
|
+
removeAgentViewer(ck, viewerMaps);
|
|
6080
6302
|
break;
|
|
6081
6303
|
}
|
|
6082
6304
|
case "exists": {
|
|
@@ -6093,6 +6315,15 @@ async function handleMessage(msg) {
|
|
|
6093
6315
|
const officeData = getOfficeSession(msg.officeId);
|
|
6094
6316
|
const current = officeData.sessionIds.get(msg.agentId);
|
|
6095
6317
|
const changed = !!normalized && current !== normalized;
|
|
6318
|
+
if (changed && normalized) {
|
|
6319
|
+
for (const [otherAgent, otherSid] of officeData.sessionIds) {
|
|
6320
|
+
if (otherAgent !== msg.agentId && otherSid === normalized) {
|
|
6321
|
+
console.warn(`[TermServer] Rejected set-session-id ${normalized} for ${compositeKey(msg.officeId, msg.agentId)} \u2014 already in use by ${otherAgent}`);
|
|
6322
|
+
send({ type: "response", requestId: msg.requestId, result: { success: false, error: "sessionId already in use by another agent in this office" } });
|
|
6323
|
+
return;
|
|
6324
|
+
}
|
|
6325
|
+
}
|
|
6326
|
+
}
|
|
6096
6327
|
if (changed) {
|
|
6097
6328
|
archiveSessionId(msg.officeId, msg.agentId);
|
|
6098
6329
|
officeData.sessionIds.set(msg.agentId, normalized);
|
|
@@ -6119,15 +6350,18 @@ async function handleMessage(msg) {
|
|
|
6119
6350
|
const ptyProc = termKey ? ptyProcesses.get(termKey) : null;
|
|
6120
6351
|
let cwd = process.cwd();
|
|
6121
6352
|
if (ptyProc?.workingDir) {
|
|
6122
|
-
const customPath =
|
|
6353
|
+
const customPath = path4.join(process.cwd(), ptyProc.workingDir);
|
|
6123
6354
|
try {
|
|
6124
|
-
await
|
|
6355
|
+
await fs3.promises.access(customPath, fs3.constants.F_OK);
|
|
6125
6356
|
cwd = customPath;
|
|
6126
6357
|
} catch {
|
|
6127
6358
|
}
|
|
6128
6359
|
}
|
|
6129
6360
|
try {
|
|
6130
|
-
|
|
6361
|
+
const wtArgs = ["-d", cwd, "copilot", "--session-id", sid];
|
|
6362
|
+
if (yoloEnabled) wtArgs.push("--yolo");
|
|
6363
|
+
if (additionalParams) wtArgs.push(...additionalParams.split(/\s+/).filter(Boolean));
|
|
6364
|
+
(0, import_child_process2.spawn)("wt", wtArgs, { detached: true, stdio: "ignore" }).unref();
|
|
6131
6365
|
send({ type: "response", requestId: msg.requestId, result: { success: true } });
|
|
6132
6366
|
} catch (error) {
|
|
6133
6367
|
send({ type: "response", requestId: msg.requestId, result: { success: false, error: String(error) } });
|
|
@@ -6159,7 +6393,7 @@ async function handleMessage(msg) {
|
|
|
6159
6393
|
hasAutoTitled.delete(ck);
|
|
6160
6394
|
send({ type: "session-meta-updated", agentId: msg.agentId, meta: { title: "" } });
|
|
6161
6395
|
archiveSessionId(msg.officeId, msg.agentId);
|
|
6162
|
-
const newSessionId =
|
|
6396
|
+
const newSessionId = crypto3.randomUUID();
|
|
6163
6397
|
officeDataReset.sessionIds.set(msg.agentId, newSessionId);
|
|
6164
6398
|
await saveOfficeSessionFile(msg.officeId);
|
|
6165
6399
|
console.log(`[TermServer] Reset session for ${ck}: new GUID ${newSessionId}`);
|
|
@@ -6210,7 +6444,7 @@ async function handleMessage(msg) {
|
|
|
6210
6444
|
}
|
|
6211
6445
|
officeData.sessionMeta.clear();
|
|
6212
6446
|
for (const agentId of officeData.sessionIds.keys()) {
|
|
6213
|
-
officeData.sessionIds.set(agentId,
|
|
6447
|
+
officeData.sessionIds.set(agentId, crypto3.randomUUID());
|
|
6214
6448
|
}
|
|
6215
6449
|
await saveOfficeSessionFile(officeId);
|
|
6216
6450
|
console.log(`[TermServer] All sessions reset for ${officeId}`);
|
|
@@ -6266,7 +6500,15 @@ async function handleMessage(msg) {
|
|
|
6266
6500
|
}
|
|
6267
6501
|
case "get-all-session-meta": {
|
|
6268
6502
|
const officeData = getOfficeSession(msg.officeId);
|
|
6269
|
-
|
|
6503
|
+
const merged = {};
|
|
6504
|
+
for (const [agentId, meta] of officeData.sessionMeta.entries()) {
|
|
6505
|
+
const sessionId = officeData.sessionIds.get(agentId);
|
|
6506
|
+
merged[agentId] = sessionId ? { ...meta, sessionId } : { ...meta };
|
|
6507
|
+
}
|
|
6508
|
+
for (const [agentId, sessionId] of officeData.sessionIds.entries()) {
|
|
6509
|
+
if (!merged[agentId]) merged[agentId] = { title: "", sessionId };
|
|
6510
|
+
}
|
|
6511
|
+
send({ type: "response", requestId: msg.requestId, result: merged });
|
|
6270
6512
|
break;
|
|
6271
6513
|
}
|
|
6272
6514
|
case "create-office-session": {
|
|
@@ -6277,7 +6519,7 @@ async function handleMessage(msg) {
|
|
|
6277
6519
|
case "delete-office-session": {
|
|
6278
6520
|
const filePath = getSessionFile(msg.officeId);
|
|
6279
6521
|
try {
|
|
6280
|
-
await
|
|
6522
|
+
await fs3.promises.unlink(filePath);
|
|
6281
6523
|
officeSessions.delete(msg.officeId);
|
|
6282
6524
|
console.log(`[TermServer] Deleted session file for ${msg.officeId}`);
|
|
6283
6525
|
} catch {
|