@charzhu/openjaw-agent 0.3.2 → 0.3.3

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.
Files changed (3) hide show
  1. package/dist/main.js +1309 -637
  2. package/dist/main.js.map +4 -4
  3. package/package.json +33 -2
package/dist/main.js CHANGED
@@ -19,22 +19,90 @@ var __export = (target, all) => {
19
19
  __defProp(target, name, { get: all[name], enumerable: true });
20
20
  };
21
21
 
22
- // src/packageRoot.ts
22
+ // src/desktop/launcher.ts
23
+ var launcher_exports = {};
24
+ __export(launcher_exports, {
25
+ launchDesktop: () => launchDesktop
26
+ });
27
+ import { spawn } from "node:child_process";
23
28
  import { existsSync } from "node:fs";
24
- import { dirname, join } from "node:path";
29
+ import { createRequire } from "node:module";
30
+ import { dirname, resolve } from "node:path";
25
31
  import { fileURLToPath } from "node:url";
26
- function findPackageRoot() {
32
+ async function launchDesktop(forwardedArgs) {
33
+ let electronBinary;
34
+ try {
35
+ const require2 = createRequire(import.meta.url);
36
+ electronBinary = require2("electron");
37
+ } catch (err) {
38
+ process.stderr.write(ELECTRON_MISSING_HINT);
39
+ process.exit(1);
40
+ }
27
41
  const here = dirname(fileURLToPath(import.meta.url));
42
+ const candidates = [
43
+ resolve(here, "electronMain.js"),
44
+ // source layout / direct dist/desktop run
45
+ resolve(here, "desktop", "electronMain.js")
46
+ // dist/main.js → dist/desktop/electronMain.js
47
+ ];
48
+ const electronEntry = candidates.find((p) => existsSync(p)) ?? candidates[0];
49
+ const child = spawn(electronBinary, [electronEntry, ...forwardedArgs], {
50
+ stdio: "inherit",
51
+ env: process.env,
52
+ windowsHide: false
53
+ });
54
+ await new Promise((resolve7) => {
55
+ child.on("exit", (code) => {
56
+ process.exit(code ?? 0);
57
+ resolve7();
58
+ });
59
+ child.on("error", (err) => {
60
+ process.stderr.write(`openjaw-agent app: failed to spawn electron: ${err.message}
61
+ `);
62
+ process.exit(1);
63
+ resolve7();
64
+ });
65
+ });
66
+ }
67
+ var ELECTRON_MISSING_HINT;
68
+ var init_launcher = __esm({
69
+ "src/desktop/launcher.ts"() {
70
+ "use strict";
71
+ ELECTRON_MISSING_HINT = [
72
+ "",
73
+ "The desktop UI requires the optional `electron` peer dependency, which",
74
+ "is not currently installed.",
75
+ "",
76
+ "To install it globally with openjaw-agent:",
77
+ " npm install -g @charzhu/openjaw-agent --include=optional",
78
+ "",
79
+ "Or, if you are running from a local clone:",
80
+ " cd path/to/openjaw-agent && npm install electron",
81
+ "",
82
+ "CLI usage (no desktop UI) is unaffected: just run `openjaw-agent` without",
83
+ "the `app` subcommand.",
84
+ ""
85
+ ].join("\n");
86
+ __name(launchDesktop, "launchDesktop");
87
+ }
88
+ });
89
+
90
+ // src/packageRoot.ts
91
+ import { existsSync as existsSync2 } from "node:fs";
92
+ import { dirname as dirname2, join } from "node:path";
93
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
94
+ function findPackageRoot() {
95
+ const here = dirname2(fileURLToPath2(import.meta.url));
28
96
  let dir2 = here;
29
97
  for (let i = 0; i < 6; i++) {
30
- if (existsSync(join(dir2, "package.json")) && existsSync(join(dir2, "prompts"))) {
98
+ if (existsSync2(join(dir2, "package.json")) && existsSync2(join(dir2, "prompts"))) {
31
99
  return dir2;
32
100
  }
33
- const parent = dirname(dir2);
101
+ const parent = dirname2(dir2);
34
102
  if (parent === dir2) break;
35
103
  dir2 = parent;
36
104
  }
37
- return dirname(here);
105
+ return dirname2(here);
38
106
  }
39
107
  function packageRoot() {
40
108
  if (cached === null) {
@@ -73,8 +141,8 @@ __export(config_exports, {
73
141
  saveAgentConfig: () => saveAgentConfig,
74
142
  updateBridgeConfig: () => updateBridgeConfig
75
143
  });
76
- import { readFileSync, writeFileSync, existsSync as existsSync2, mkdirSync } from "node:fs";
77
- import { join as join2, dirname as dirname2 } from "node:path";
144
+ import { readFileSync, writeFileSync, existsSync as existsSync3, mkdirSync } from "node:fs";
145
+ import { join as join2, dirname as dirname3 } from "node:path";
78
146
  import { homedir } from "node:os";
79
147
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
80
148
  function getConfigDir() {
@@ -88,10 +156,10 @@ function loadAgentConfig() {
88
156
  const userConfigPath = getConfigPath();
89
157
  const bundledConfigPath = packageBundledConfigPath();
90
158
  let configPath = null;
91
- if (existsSync2(userConfigPath)) {
159
+ if (existsSync3(userConfigPath)) {
92
160
  configPath = userConfigPath;
93
- } else if (existsSync2(bundledConfigPath)) {
94
- if (!existsSync2(configDir2)) {
161
+ } else if (existsSync3(bundledConfigPath)) {
162
+ if (!existsSync3(configDir2)) {
95
163
  mkdirSync(configDir2, { recursive: true });
96
164
  }
97
165
  const bundledContent = readFileSync(bundledConfigPath, "utf8");
@@ -101,7 +169,7 @@ function loadAgentConfig() {
101
169
  console.log(` Edit it to change provider/model settings.
102
170
  `);
103
171
  } else {
104
- if (!existsSync2(configDir2)) {
172
+ if (!existsSync3(configDir2)) {
105
173
  mkdirSync(configDir2, { recursive: true });
106
174
  }
107
175
  writeFileSync(userConfigPath, stringifyYaml(DEFAULT_CONFIG), "utf8");
@@ -172,7 +240,7 @@ function saveAgentConfig(config) {
172
240
  }
173
241
  function loadRawConfigYaml() {
174
242
  const path3 = getConfigPath();
175
- if (!existsSync2(path3)) {
243
+ if (!existsSync3(path3)) {
176
244
  return { path: path3, yamlObj: void 0 };
177
245
  }
178
246
  const raw = readFileSync(path3, "utf8");
@@ -189,13 +257,13 @@ function updateBridgeConfig(name, values) {
189
257
  next2[name] = values;
190
258
  }
191
259
  const tmpPath = `${path3}.tmp`;
192
- const dir2 = dirname2(path3);
193
- if (!existsSync2(dir2)) {
260
+ const dir2 = dirname3(path3);
261
+ if (!existsSync3(dir2)) {
194
262
  mkdirSync(dir2, { recursive: true });
195
263
  }
196
264
  writeFileSync(tmpPath, stringifyYaml(next2), "utf8");
197
- const { renameSync } = await import("node:fs");
198
- renameSync(tmpPath, path3);
265
+ const { renameSync: renameSync2 } = await import("node:fs");
266
+ renameSync2(tmpPath, path3);
199
267
  }, "task");
200
268
  const next = configWriteChain.then(task, task);
201
269
  configWriteChain = next.then(
@@ -250,7 +318,7 @@ __export(image_resize_exports, {
250
318
  resizeImageForAgent: () => resizeImageForAgent
251
319
  });
252
320
  import { execSync } from "node:child_process";
253
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, unlinkSync, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "node:fs";
321
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, unlinkSync, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "node:fs";
254
322
  import { join as join3 } from "node:path";
255
323
  import { tmpdir } from "node:os";
256
324
  import { randomUUID } from "node:crypto";
@@ -301,7 +369,7 @@ Write-Output "$newW x $newH"
301
369
  `powershell -NoProfile -NonInteractive -EncodedCommand ${encoded}`,
302
370
  { timeout: 15e3, encoding: "utf-8" }
303
371
  ).trim();
304
- if (!existsSync3(outputPath)) {
372
+ if (!existsSync4(outputPath)) {
305
373
  throw new Error(`Resize produced no output (PowerShell said: ${result})`);
306
374
  }
307
375
  const resizedBuffer = readFileSync2(outputPath);
@@ -311,17 +379,17 @@ Write-Output "$newW x $newH"
311
379
  return { base64: resizedBase64, mimeType: actualMimeType };
312
380
  } finally {
313
381
  try {
314
- if (existsSync3(inputPath)) unlinkSync(inputPath);
382
+ if (existsSync4(inputPath)) unlinkSync(inputPath);
315
383
  } catch {
316
384
  }
317
385
  try {
318
- if (existsSync3(outputPath)) unlinkSync(outputPath);
386
+ if (existsSync4(outputPath)) unlinkSync(outputPath);
319
387
  } catch {
320
388
  }
321
389
  }
322
390
  }
323
391
  async function resizeImageFileForAgent(filePath) {
324
- if (!existsSync3(filePath)) {
392
+ if (!existsSync4(filePath)) {
325
393
  throw new Error(`Image file not found: ${filePath}`);
326
394
  }
327
395
  const buffer = readFileSync2(filePath);
@@ -869,7 +937,7 @@ Start-Sleep -Milliseconds 50
869
937
  }
870
938
  }
871
939
  async function wait(duration) {
872
- await new Promise((resolve6) => setTimeout(resolve6, duration * 1e3));
940
+ await new Promise((resolve7) => setTimeout(resolve7, duration * 1e3));
873
941
  return { output: `Waited ${duration} seconds` };
874
942
  }
875
943
  function getDisplayDimensions() {
@@ -1977,12 +2045,12 @@ var init_copilot_token = __esm({
1977
2045
  });
1978
2046
 
1979
2047
  // src/provider-auth.ts
1980
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "node:fs";
2048
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "node:fs";
1981
2049
  import { join as join5 } from "node:path";
1982
2050
  import { homedir as homedir2 } from "node:os";
1983
2051
  function getAuthDir() {
1984
2052
  const dir2 = join5(homedir2(), ".openjaw-agent");
1985
- if (!existsSync4(dir2)) mkdirSync3(dir2, { recursive: true });
2053
+ if (!existsSync5(dir2)) mkdirSync3(dir2, { recursive: true });
1986
2054
  return dir2;
1987
2055
  }
1988
2056
  function getProviderAuthPath() {
@@ -1993,7 +2061,7 @@ function emptyFile() {
1993
2061
  }
1994
2062
  function loadProviderCredentials() {
1995
2063
  const path3 = getProviderAuthPath();
1996
- if (!existsSync4(path3)) return emptyFile();
2064
+ if (!existsSync5(path3)) return emptyFile();
1997
2065
  const parsed = JSON.parse(readFileSync3(path3, "utf8"));
1998
2066
  return {
1999
2067
  version: 1,
@@ -2206,8 +2274,8 @@ function safeJsonParse(value) {
2206
2274
  }
2207
2275
  }
2208
2276
  async function loadWebSocket() {
2209
- const { createRequire } = await import("node:module");
2210
- return createRequire(import.meta.url)("ws");
2277
+ const { createRequire: createRequire3 } = await import("node:module");
2278
+ return createRequire3(import.meta.url)("ws");
2211
2279
  }
2212
2280
  function responsesWebSocketUrl(baseUrl) {
2213
2281
  const url = new URL(`${baseUrl.replace(/\/+$/, "")}/responses`);
@@ -2564,7 +2632,7 @@ var init_copilot = __esm({
2564
2632
  handshakeTimeout: RESPONSES_WEBSOCKET_CONNECT_TIMEOUT_MS
2565
2633
  });
2566
2634
  const request = JSON.stringify(this.buildResponsesWebSocketRequest(requestBody));
2567
- return new Promise((resolve6, reject) => {
2635
+ return new Promise((resolve7, reject) => {
2568
2636
  const accumulator = { sawTextDelta: false, text: null, toolCalls: [] };
2569
2637
  let settled = false;
2570
2638
  const timeout = setTimeout(() => {
@@ -2578,7 +2646,7 @@ var init_copilot = __esm({
2578
2646
  settled = true;
2579
2647
  clearTimeout(timeout);
2580
2648
  ws.close();
2581
- resolve6(value);
2649
+ resolve7(value);
2582
2650
  }, "finish");
2583
2651
  const fail = /* @__PURE__ */ __name((error) => {
2584
2652
  if (settled) return;
@@ -3014,13 +3082,13 @@ var init_providers = __esm({
3014
3082
  });
3015
3083
 
3016
3084
  // src/session.ts
3017
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync5, existsSync as existsSync5, mkdirSync as mkdirSync4, readdirSync } from "node:fs";
3085
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync5, existsSync as existsSync6, mkdirSync as mkdirSync4, readdirSync, renameSync, unlinkSync as unlinkSync3 } from "node:fs";
3018
3086
  import { join as join6 } from "node:path";
3019
3087
  import { homedir as homedir3 } from "node:os";
3020
3088
  import { randomUUID as randomUUID2 } from "node:crypto";
3021
3089
  function getSessionsDir() {
3022
3090
  const dir2 = join6(homedir3(), ".openjaw-agent", "sessions");
3023
- if (!existsSync5(dir2)) mkdirSync4(dir2, { recursive: true });
3091
+ if (!existsSync6(dir2)) mkdirSync4(dir2, { recursive: true });
3024
3092
  return dir2;
3025
3093
  }
3026
3094
  function sessionPath(id) {
@@ -3041,11 +3109,23 @@ function createSession(provider, model) {
3041
3109
  function saveSession(session) {
3042
3110
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3043
3111
  session.turnCount = session.messages.filter((m) => m.role === "user").length;
3044
- writeFileSync5(sessionPath(session.id), JSON.stringify(session, null, 2), "utf8");
3112
+ const finalPath = sessionPath(session.id);
3113
+ const tmpPath = `${finalPath}.tmp`;
3114
+ const payload = JSON.stringify(session, null, 2);
3115
+ try {
3116
+ writeFileSync5(tmpPath, payload, "utf8");
3117
+ renameSync(tmpPath, finalPath);
3118
+ } catch (err) {
3119
+ try {
3120
+ unlinkSync3(tmpPath);
3121
+ } catch {
3122
+ }
3123
+ throw err;
3124
+ }
3045
3125
  }
3046
3126
  function loadSession(id) {
3047
3127
  const path3 = sessionPath(id);
3048
- if (!existsSync5(path3)) return null;
3128
+ if (!existsSync6(path3)) return null;
3049
3129
  try {
3050
3130
  return JSON.parse(readFileSync4(path3, "utf8"));
3051
3131
  } catch {
@@ -3430,7 +3510,7 @@ var init_cache_monitor = __esm({
3430
3510
  });
3431
3511
 
3432
3512
  // src/telemetry.ts
3433
- import { appendFileSync, existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync5 } from "node:fs";
3513
+ import { appendFileSync, existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync5 } from "node:fs";
3434
3514
  import { join as join7 } from "node:path";
3435
3515
  import { homedir as homedir4 } from "node:os";
3436
3516
  function now() {
@@ -3453,7 +3533,7 @@ var init_telemetry = __esm({
3453
3533
  constructor(sessionId) {
3454
3534
  this.sessionId = sessionId;
3455
3535
  try {
3456
- if (!existsSync6(TELEMETRY_DIR)) {
3536
+ if (!existsSync7(TELEMETRY_DIR)) {
3457
3537
  mkdirSync5(TELEMETRY_DIR, { recursive: true });
3458
3538
  }
3459
3539
  } catch {
@@ -3484,7 +3564,7 @@ var init_telemetry = __esm({
3484
3564
  getRecentEvents(limit = 20) {
3485
3565
  try {
3486
3566
  const todayFile = join7(TELEMETRY_DIR, `${today()}.jsonl`);
3487
- if (!existsSync6(todayFile)) return [];
3567
+ if (!existsSync7(todayFile)) return [];
3488
3568
  const lines = readFileSync5(todayFile, "utf-8").trim().split("\n");
3489
3569
  return lines.slice(-limit).map((l) => JSON.parse(l));
3490
3570
  } catch {
@@ -4409,8 +4489,8 @@ function isOAuthAbortError(err) {
4409
4489
  function oauthDomain(enterpriseUrl) {
4410
4490
  return enterpriseUrl ? normalizeCopilotEnterpriseDomain(enterpriseUrl) : "github.com";
4411
4491
  }
4412
- async function startCopilotDeviceFlow(clientId, enterpriseUrl) {
4413
- if (!clientId.trim()) {
4492
+ async function startCopilotDeviceFlow(clientId2, enterpriseUrl) {
4493
+ if (!clientId2.trim()) {
4414
4494
  throw new Error("GitHub OAuth client ID is required for Copilot login.");
4415
4495
  }
4416
4496
  const normalizedEnterpriseUrl = enterpriseUrl ? normalizeCopilotEnterpriseDomain(enterpriseUrl) : void 0;
@@ -4423,7 +4503,7 @@ async function startCopilotDeviceFlow(clientId, enterpriseUrl) {
4423
4503
  "User-Agent": "openjaw-agent/0.1.0"
4424
4504
  },
4425
4505
  body: JSON.stringify({
4426
- client_id: clientId,
4506
+ client_id: clientId2,
4427
4507
  scope: "read:user"
4428
4508
  })
4429
4509
  });
@@ -4439,7 +4519,7 @@ async function startCopilotDeviceFlow(clientId, enterpriseUrl) {
4439
4519
  verificationUri: body.verification_uri,
4440
4520
  userCode: body.user_code,
4441
4521
  deviceCode: body.device_code,
4442
- clientId,
4522
+ clientId: clientId2,
4443
4523
  intervalSeconds: body.interval ?? 5,
4444
4524
  enterpriseUrl: normalizedEnterpriseUrl
4445
4525
  };
@@ -4743,8 +4823,8 @@ For local proxy mode without API keys, use /connect maestro.`
4743
4823
  }
4744
4824
  };
4745
4825
  }
4746
- const clientId = resolveCopilotClientId();
4747
- if (!clientId) {
4826
+ const clientId2 = resolveCopilotClientId();
4827
+ if (!clientId2) {
4748
4828
  emit({
4749
4829
  type: "error",
4750
4830
  content: [
@@ -4760,7 +4840,7 @@ For local proxy mode without API keys, use /connect maestro.`
4760
4840
  }
4761
4841
  const label = enterpriseUrl ? `GitHub Enterprise (${enterpriseUrl})` : "GitHub.com";
4762
4842
  try {
4763
- const flow = await startCopilotDeviceFlow(clientId, enterpriseUrl);
4843
+ const flow = await startCopilotDeviceFlow(clientId2, enterpriseUrl);
4764
4844
  emit({
4765
4845
  type: "system",
4766
4846
  content: [
@@ -5285,6 +5365,66 @@ var init_agent_loop = __esm({
5285
5365
  this.conversationHistory = next.messages;
5286
5366
  return { session_id: next.id, title: next.summary };
5287
5367
  }
5368
+ /**
5369
+ * Create a brand-new empty session and switch this AgentLoop over to it.
5370
+ * Unlike branchSession, the new session starts with an empty history —
5371
+ * this is the "+" button / `/clear`-equivalent for users who want a
5372
+ * fresh conversation without affecting their current one.
5373
+ *
5374
+ * Persists the current session first (so any unsaved counters / summary
5375
+ * land on disk) before swapping. Returns null if a turn is currently
5376
+ * in flight (matching swapToSession's defense-in-depth posture); the
5377
+ * caller is responsible for interrupting first.
5378
+ */
5379
+ createAndSwitchTo() {
5380
+ if (this.isRunning) return null;
5381
+ saveSession(this.session);
5382
+ const next = createSession(this.config.llm.provider, this.config.llm.model);
5383
+ saveSession(next);
5384
+ this.session = next;
5385
+ this.conversationHistory = next.messages;
5386
+ this._toolExposureState = createToolExposureState();
5387
+ this._compactedOnResume = false;
5388
+ this._toolRoundsInRun = 0;
5389
+ return next;
5390
+ }
5391
+ /**
5392
+ * Resume an existing on-disk session as the active session for this loop.
5393
+ * Persists the current session first, then loads the target's messages
5394
+ * into `conversationHistory` and rebinds `this.session` so subsequent
5395
+ * `prompt.submit` turns run against the resumed conversation.
5396
+ *
5397
+ * Returns the loaded `SessionData` on success, or `null` if:
5398
+ * - a turn is currently in flight (callers must abort or wait first);
5399
+ * - the target session id doesn't exist on disk.
5400
+ *
5401
+ * Provider/model are NOT switched. The session's recorded provider/model
5402
+ * fields are updated to whatever the loop is currently configured for —
5403
+ * matching the constructor's resume behaviour, which loads the messages
5404
+ * but keeps the AgentLoop's `config.llm.provider/model` as-is. A user
5405
+ * who wants the resumed session's original model must call `switchModel`
5406
+ * separately (or run via the `/model` slash command).
5407
+ *
5408
+ * Required by both A3 defer-on-switch and any future A2 concurrent-
5409
+ * streaming refactor (the desktop UI's `session.resume` RPC depends on
5410
+ * this method actually rebinding history — see the plan, section
5411
+ * "Multi-session story").
5412
+ */
5413
+ swapToSession(targetId) {
5414
+ if (this.isRunning) return null;
5415
+ const target = loadSession(targetId);
5416
+ if (!target) return null;
5417
+ saveSession(this.session);
5418
+ target.provider = this.config.llm.provider;
5419
+ target.model = this.config.llm.model;
5420
+ this.session = target;
5421
+ this.conversationHistory = target.messages;
5422
+ this._toolExposureState = createToolExposureState();
5423
+ this._compactedOnResume = false;
5424
+ this._toolRoundsInRun = 0;
5425
+ saveSession(this.session);
5426
+ return target;
5427
+ }
5288
5428
  /** Read-only access to the underlying session metadata. */
5289
5429
  getSessionMeta() {
5290
5430
  return {
@@ -5848,18 +5988,18 @@ ${summary}
5848
5988
  content: parsed.question,
5849
5989
  choices: parsed.choices ?? void 0
5850
5990
  };
5851
- const userResponse = await new Promise((resolve6) => {
5991
+ const userResponse = await new Promise((resolve7) => {
5852
5992
  if (this._pendingAskUserResponse !== null) {
5853
5993
  const buffered = this._pendingAskUserResponse;
5854
5994
  this._pendingAskUserResponse = null;
5855
- resolve6(buffered);
5995
+ resolve7(buffered);
5856
5996
  return;
5857
5997
  }
5858
- this._askUserResolver = resolve6;
5998
+ this._askUserResolver = resolve7;
5859
5999
  setTimeout(() => {
5860
- if (this._askUserResolver === resolve6) {
6000
+ if (this._askUserResolver === resolve7) {
5861
6001
  this._askUserResolver = null;
5862
- resolve6("[No response from user \u2014 timed out after 5 minutes]");
6002
+ resolve7("[No response from user \u2014 timed out after 5 minutes]");
5863
6003
  }
5864
6004
  }, 5 * 60 * 1e3);
5865
6005
  });
@@ -5963,14 +6103,14 @@ __export(settings_exports, {
5963
6103
  });
5964
6104
  import { z } from "zod";
5965
6105
  import { readFile, writeFile, mkdir } from "node:fs/promises";
5966
- import { existsSync as existsSync7 } from "node:fs";
6106
+ import { existsSync as existsSync8 } from "node:fs";
5967
6107
  import { join as join9 } from "node:path";
5968
6108
  import { homedir as homedir6 } from "node:os";
5969
6109
  import { parse as parseYaml2, stringify as stringifyYaml2 } from "yaml";
5970
6110
  async function ensureDirectories() {
5971
6111
  const dirs = [OPENJAW_DIR, SESSIONS_DIR, MEMORY_DIR];
5972
6112
  for (const dir2 of dirs) {
5973
- if (!existsSync7(dir2)) {
6113
+ if (!existsSync8(dir2)) {
5974
6114
  await mkdir(dir2, { recursive: true });
5975
6115
  }
5976
6116
  }
@@ -5981,7 +6121,7 @@ async function loadConfig() {
5981
6121
  }
5982
6122
  await ensureDirectories();
5983
6123
  let rawConfig = {};
5984
- if (existsSync7(CONFIG_PATH)) {
6124
+ if (existsSync8(CONFIG_PATH)) {
5985
6125
  const content = await readFile(CONFIG_PATH, "utf-8");
5986
6126
  rawConfig = parseYaml2(content);
5987
6127
  }
@@ -6031,7 +6171,13 @@ var init_settings = __esm({
6031
6171
  MicrosoftConfigSchema = z.object({
6032
6172
  tenant_id: z.string().optional(),
6033
6173
  client_id: z.string().optional(),
6034
- client_secret: z.string().optional()
6174
+ client_secret: z.string().optional(),
6175
+ // Graph token acquisition strategy:
6176
+ // 'auto' (default) — try the WAM broker first (reliable, browser-free on
6177
+ // Windows), fall back to CDP browser-token extraction.
6178
+ // 'wam' — broker only (no CDP fallback).
6179
+ // 'cdp' — legacy browser-token extraction only (disable WAM).
6180
+ auth_mode: z.enum(["auto", "wam", "cdp"]).optional()
6035
6181
  });
6036
6182
  BrowserConfigSchema = z.object({
6037
6183
  executable: z.string().optional(),
@@ -6531,7 +6677,7 @@ var init_browser = __esm({
6531
6677
  await Page.domContentEventFired();
6532
6678
  } else if (options.waitFor === "networkidle") {
6533
6679
  await Page.loadEventFired();
6534
- await new Promise((resolve6) => setTimeout(resolve6, 1e3));
6680
+ await new Promise((resolve7) => setTimeout(resolve7, 1e3));
6535
6681
  }
6536
6682
  const result = await Runtime.evaluate({
6537
6683
  expression: "document.title"
@@ -7302,7 +7448,7 @@ var init_browser = __esm({
7302
7448
  if (exists) {
7303
7449
  return true;
7304
7450
  }
7305
- await new Promise((resolve6) => setTimeout(resolve6, 200));
7451
+ await new Promise((resolve7) => setTimeout(resolve7, 200));
7306
7452
  }
7307
7453
  return false;
7308
7454
  }
@@ -7530,10 +7676,10 @@ function createBrowseTools(config, sharedBrowser) {
7530
7676
  }
7531
7677
  },
7532
7678
  execute: /* @__PURE__ */ __name(async (input) => {
7533
- const { join: join48 } = await import("node:path");
7679
+ const { join: join49 } = await import("node:path");
7534
7680
  const { tmpdir: tmpdir13 } = await import("node:os");
7535
7681
  const { randomUUID: randomUUID15 } = await import("node:crypto");
7536
- const screenshotPath = join48(tmpdir13(), `openjaw-browser-${randomUUID15().slice(0, 8)}.png`);
7682
+ const screenshotPath = join49(tmpdir13(), `openjaw-browser-${randomUUID15().slice(0, 8)}.png`);
7537
7683
  const screenshot = await browser.screenshot({ fullPage: false, path: screenshotPath });
7538
7684
  const snapshot = await browser.snapshot({ full: false });
7539
7685
  return {
@@ -8228,7 +8374,7 @@ var init_outlook_desktop = __esm({
8228
8374
  }
8229
8375
  }
8230
8376
  sleep(ms) {
8231
- return new Promise((resolve6) => setTimeout(resolve6, ms));
8377
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
8232
8378
  }
8233
8379
  };
8234
8380
  }
@@ -8826,14 +8972,14 @@ var init_outlook_web = __esm({
8826
8972
  await this.browser.typeChars(text);
8827
8973
  }
8828
8974
  sleep(ms) {
8829
- return new Promise((resolve6) => setTimeout(resolve6, ms));
8975
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
8830
8976
  }
8831
8977
  };
8832
8978
  }
8833
8979
  });
8834
8980
 
8835
8981
  // ../openjaw-mcp/dist/auth/token-pool.js
8836
- import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "node:fs";
8982
+ import { existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "node:fs";
8837
8983
  import { join as join10 } from "node:path";
8838
8984
  import { homedir as homedir7 } from "node:os";
8839
8985
  function decodeJwtPayload(jwt) {
@@ -9030,7 +9176,7 @@ var init_token_pool = __esm({
9030
9176
  * Load pool from disk, including legacy token files.
9031
9177
  */
9032
9178
  load() {
9033
- if (existsSync8(POOL_FILE)) {
9179
+ if (existsSync9(POOL_FILE)) {
9034
9180
  try {
9035
9181
  const raw = readFileSync6(POOL_FILE, "utf-8");
9036
9182
  const data = JSON.parse(raw);
@@ -9057,7 +9203,7 @@ var init_token_pool = __esm({
9057
9203
  */
9058
9204
  loadLegacyFiles() {
9059
9205
  const legacyDir = join10(POOL_DIR, "tokens");
9060
- if (!existsSync8(legacyDir))
9206
+ if (!existsSync9(legacyDir))
9061
9207
  return;
9062
9208
  try {
9063
9209
  const { readdirSync: readdirSync8 } = __require("node:fs");
@@ -9241,7 +9387,7 @@ var init_cdp_token_extractor = __esm({
9241
9387
  }
9242
9388
  logger_default.info("CDP: reloading tab for token refresh", { url: targetTab.url, audience });
9243
9389
  await this.reloadTab(targetTab);
9244
- await new Promise((resolve6) => setTimeout(resolve6, PAGE_RELOAD_WAIT_MS));
9390
+ await new Promise((resolve7) => setTimeout(resolve7, PAGE_RELOAD_WAIT_MS));
9245
9391
  if (!targetTab.webSocketDebuggerUrl)
9246
9392
  return [];
9247
9393
  const freshPages = await this.listPages();
@@ -9358,19 +9504,19 @@ var init_cdp_token_extractor = __esm({
9358
9504
  * Evaluate a JS expression in a tab and return the string result.
9359
9505
  */
9360
9506
  async evaluateInTab(wsUrl, expression) {
9361
- return new Promise((resolve6) => {
9507
+ return new Promise((resolve7) => {
9362
9508
  let ws;
9363
9509
  try {
9364
9510
  ws = new WebSocket(wsUrl);
9365
9511
  } catch (err) {
9366
9512
  logger_default.warn("CDP: WebSocket constructor failed", { wsUrl: wsUrl.substring(0, 60), error: String(err) });
9367
- resolve6(null);
9513
+ resolve7(null);
9368
9514
  return;
9369
9515
  }
9370
9516
  const timer = setTimeout(() => {
9371
9517
  logger_default.warn("CDP: evaluateInTab timeout", { wsUrl: wsUrl.substring(0, 60) });
9372
9518
  ws.close();
9373
- resolve6(null);
9519
+ resolve7(null);
9374
9520
  }, CDP_TIMEOUT_MS);
9375
9521
  ws.on("open", () => {
9376
9522
  ws.send(JSON.stringify({
@@ -9384,18 +9530,18 @@ var init_cdp_token_extractor = __esm({
9384
9530
  if (resp.id === 1) {
9385
9531
  clearTimeout(timer);
9386
9532
  ws.close();
9387
- resolve6(resp.result?.result?.value ?? null);
9533
+ resolve7(resp.result?.result?.value ?? null);
9388
9534
  }
9389
9535
  });
9390
9536
  ws.on("error", (err) => {
9391
9537
  logger_default.warn("CDP: evaluateInTab WS error", { error: String(err), wsUrl: wsUrl.substring(0, 60) });
9392
9538
  clearTimeout(timer);
9393
- resolve6(null);
9539
+ resolve7(null);
9394
9540
  });
9395
9541
  });
9396
9542
  }
9397
9543
  async extractFromTab(wsUrl) {
9398
- return new Promise((resolve6, reject) => {
9544
+ return new Promise((resolve7, reject) => {
9399
9545
  const ws = new WebSocket(wsUrl);
9400
9546
  const timer = setTimeout(() => {
9401
9547
  ws.close();
@@ -9415,14 +9561,14 @@ var init_cdp_token_extractor = __esm({
9415
9561
  ws.close();
9416
9562
  const value = resp.result?.result?.value;
9417
9563
  if (!value) {
9418
- resolve6([]);
9564
+ resolve7([]);
9419
9565
  return;
9420
9566
  }
9421
9567
  try {
9422
9568
  const tokens = JSON.parse(value);
9423
- resolve6(tokens);
9569
+ resolve7(tokens);
9424
9570
  } catch {
9425
- resolve6([]);
9571
+ resolve7([]);
9426
9572
  }
9427
9573
  }
9428
9574
  });
@@ -9438,11 +9584,11 @@ var init_cdp_token_extractor = __esm({
9438
9584
  async reloadTab(page) {
9439
9585
  if (!page.webSocketDebuggerUrl)
9440
9586
  return;
9441
- return new Promise((resolve6, reject) => {
9587
+ return new Promise((resolve7, reject) => {
9442
9588
  const ws = new WebSocket(page.webSocketDebuggerUrl);
9443
9589
  const timer = setTimeout(() => {
9444
9590
  ws.close();
9445
- resolve6();
9591
+ resolve7();
9446
9592
  }, 1e4);
9447
9593
  ws.on("open", () => {
9448
9594
  ws.send(JSON.stringify({
@@ -9456,7 +9602,7 @@ var init_cdp_token_extractor = __esm({
9456
9602
  if (resp.id === 1) {
9457
9603
  clearTimeout(timer);
9458
9604
  ws.close();
9459
- resolve6();
9605
+ resolve7();
9460
9606
  }
9461
9607
  });
9462
9608
  ws.on("error", (err) => {
@@ -9496,13 +9642,172 @@ var init_cdp_token_extractor = __esm({
9496
9642
  }
9497
9643
  });
9498
9644
 
9645
+ // ../openjaw-mcp/dist/auth/wam-token-provider.js
9646
+ var wam_token_provider_exports = {};
9647
+ __export(wam_token_provider_exports, {
9648
+ WAM_GRAPH_SCOPES: () => WAM_GRAPH_SCOPES,
9649
+ acquireGraphToken: () => acquireGraphToken,
9650
+ acquireTokenForAudience: () => acquireTokenForAudience,
9651
+ configureWam: () => configureWam,
9652
+ isWamAvailable: () => isWamAvailable,
9653
+ setWamWindowHandleProvider: () => setWamWindowHandleProvider
9654
+ });
9655
+ import { InteractionRequiredAuthError } from "@azure/msal-node";
9656
+ function configureWam(opts) {
9657
+ if (opts.clientId)
9658
+ clientId = opts.clientId;
9659
+ if (opts.tenant)
9660
+ tenant = opts.tenant;
9661
+ if (typeof opts.enabled === "boolean")
9662
+ wamEnabled = opts.enabled;
9663
+ }
9664
+ function setWamWindowHandleProvider(fn) {
9665
+ windowHandleProvider = fn;
9666
+ }
9667
+ async function getBrokerPlugin() {
9668
+ if (brokerLoadAttempted)
9669
+ return nativeBrokerPlugin;
9670
+ brokerLoadAttempted = true;
9671
+ if (process.platform !== "win32") {
9672
+ logger_default.debug("WAM: not win32, broker unavailable");
9673
+ return null;
9674
+ }
9675
+ try {
9676
+ const { NativeBrokerPlugin } = await import("@azure/msal-node-extensions");
9677
+ const plugin = new NativeBrokerPlugin();
9678
+ nativeBrokerPlugin = plugin;
9679
+ } catch (err) {
9680
+ logger_default.warn("WAM: failed to load NativeBrokerPlugin", { error: err instanceof Error ? err.message : String(err) });
9681
+ nativeBrokerPlugin = null;
9682
+ }
9683
+ return nativeBrokerPlugin;
9684
+ }
9685
+ async function getApp() {
9686
+ const plugin = await getBrokerPlugin();
9687
+ if (!plugin || !plugin.isBrokerAvailable)
9688
+ return null;
9689
+ if (!msalApp) {
9690
+ const { PublicClientApplication } = await import("@azure/msal-node");
9691
+ msalApp = new PublicClientApplication({
9692
+ auth: { clientId, authority: `https://login.microsoftonline.com/${tenant}` },
9693
+ broker: { nativeBrokerPlugin: plugin }
9694
+ });
9695
+ }
9696
+ return msalApp;
9697
+ }
9698
+ function withBrokerLock(fn) {
9699
+ const result = _brokerQueue.then(fn, fn);
9700
+ _brokerQueue = result.then(() => void 0, () => void 0);
9701
+ return result;
9702
+ }
9703
+ function isInteractionRequired(err) {
9704
+ if (err instanceof InteractionRequiredAuthError)
9705
+ return true;
9706
+ const msg = err instanceof Error ? err.message : String(err);
9707
+ return /interaction[_\-\s]?required|no[_\-\s]?account/i.test(msg);
9708
+ }
9709
+ async function acquireUnlocked(app, scopes) {
9710
+ const accounts = await app.getAllAccounts();
9711
+ const account = selectedAccount ?? accounts.find((a) => a.tenantId === tenant) ?? (accounts.length === 1 ? accounts[0] : void 0);
9712
+ if (account) {
9713
+ try {
9714
+ return await app.acquireTokenSilent({ scopes, account });
9715
+ } catch (err) {
9716
+ if (!isInteractionRequired(err)) {
9717
+ logger_default.warn("WAM: silent acquire failed (non-interactive)", {
9718
+ error: err instanceof Error ? err.message.split("\n")[0] : String(err)
9719
+ });
9720
+ throw err;
9721
+ }
9722
+ logger_default.info("WAM: silent failed, interaction required");
9723
+ }
9724
+ }
9725
+ const windowHandle = windowHandleProvider?.() ?? void 0;
9726
+ const result = await app.acquireTokenInteractive({
9727
+ scopes,
9728
+ windowHandle,
9729
+ openBrowser: noop,
9730
+ prompt: "select_account",
9731
+ loginHint: account?.username
9732
+ });
9733
+ if (result.account)
9734
+ selectedAccount = result.account;
9735
+ return result;
9736
+ }
9737
+ async function acquireGraphToken(scopes = WAM_GRAPH_SCOPES) {
9738
+ return acquireTokenForScopes(scopes);
9739
+ }
9740
+ async function acquireTokenForAudience(audience, _perms = []) {
9741
+ const base = audience.endsWith("/") ? audience.slice(0, -1) : audience;
9742
+ return acquireTokenForScopes([`${base}/.default`]);
9743
+ }
9744
+ async function acquireTokenForScopes(scopes) {
9745
+ if (!wamEnabled || brokerPoisoned)
9746
+ return null;
9747
+ try {
9748
+ const app = await getApp();
9749
+ if (!app)
9750
+ return null;
9751
+ const result = await withBrokerLock(() => acquireUnlocked(app, scopes));
9752
+ return result?.accessToken ?? null;
9753
+ } catch (err) {
9754
+ const msg = err instanceof Error ? err.message : String(err);
9755
+ if (/AccountUnusable|broker.*unavailable/i.test(msg)) {
9756
+ brokerPoisoned = true;
9757
+ logger_default.warn("WAM: broker poisoned, disabling for this session", { error: msg.split("\n")[0] });
9758
+ } else {
9759
+ logger_default.warn("WAM: acquire failed, will fall back", { error: msg.split("\n")[0] });
9760
+ }
9761
+ return null;
9762
+ }
9763
+ }
9764
+ async function isWamAvailable() {
9765
+ if (!wamEnabled || brokerPoisoned)
9766
+ return false;
9767
+ const plugin = await getBrokerPlugin();
9768
+ return !!plugin && plugin.isBrokerAvailable;
9769
+ }
9770
+ var CORP_TENANT, DEFAULT_CLIENT_ID, WAM_GRAPH_SCOPES, clientId, tenant, windowHandleProvider, wamEnabled, brokerLoadAttempted, nativeBrokerPlugin, msalApp, brokerPoisoned, selectedAccount, _brokerQueue, noop;
9771
+ var init_wam_token_provider = __esm({
9772
+ "../openjaw-mcp/dist/auth/wam-token-provider.js"() {
9773
+ "use strict";
9774
+ init_logger();
9775
+ CORP_TENANT = "72f988bf-86f1-41af-91ab-2d7cd011db47";
9776
+ DEFAULT_CLIENT_ID = "99fa64eb-feda-4f94-aecd-30637ca7bf2d";
9777
+ WAM_GRAPH_SCOPES = ["https://graph.microsoft.com/.default"];
9778
+ clientId = DEFAULT_CLIENT_ID;
9779
+ tenant = CORP_TENANT;
9780
+ windowHandleProvider = null;
9781
+ wamEnabled = true;
9782
+ __name(configureWam, "configureWam");
9783
+ __name(setWamWindowHandleProvider, "setWamWindowHandleProvider");
9784
+ brokerLoadAttempted = false;
9785
+ nativeBrokerPlugin = null;
9786
+ msalApp = null;
9787
+ brokerPoisoned = false;
9788
+ selectedAccount = null;
9789
+ __name(getBrokerPlugin, "getBrokerPlugin");
9790
+ __name(getApp, "getApp");
9791
+ _brokerQueue = Promise.resolve();
9792
+ __name(withBrokerLock, "withBrokerLock");
9793
+ noop = /* @__PURE__ */ __name(async () => {
9794
+ }, "noop");
9795
+ __name(isInteractionRequired, "isInteractionRequired");
9796
+ __name(acquireUnlocked, "acquireUnlocked");
9797
+ __name(acquireGraphToken, "acquireGraphToken");
9798
+ __name(acquireTokenForAudience, "acquireTokenForAudience");
9799
+ __name(acquireTokenForScopes, "acquireTokenForScopes");
9800
+ __name(isWamAvailable, "isWamAvailable");
9801
+ }
9802
+ });
9803
+
9499
9804
  // ../openjaw-mcp/dist/auth/graph-token-provider.js
9500
- import { existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "node:fs";
9501
- import { join as join11, dirname as dirname3 } from "node:path";
9805
+ import { existsSync as existsSync10, readFileSync as readFileSync7, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "node:fs";
9806
+ import { join as join11, dirname as dirname4 } from "node:path";
9502
9807
  import { homedir as homedir8 } from "node:os";
9503
9808
  import { exec as exec2 } from "node:child_process";
9504
9809
  import { promisify as promisify2 } from "node:util";
9505
- import { fileURLToPath as fileURLToPath2 } from "node:url";
9810
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
9506
9811
  function getSharedPool() {
9507
9812
  if (!sharedPool) {
9508
9813
  sharedPool = new TokenPool();
@@ -9522,9 +9827,10 @@ var init_graph_token_provider = __esm({
9522
9827
  init_logger();
9523
9828
  init_token_pool();
9524
9829
  init_cdp_token_extractor();
9830
+ init_wam_token_provider();
9525
9831
  execAsync2 = promisify2(exec2);
9526
- __filename = fileURLToPath2(import.meta.url);
9527
- __dirname = dirname3(__filename);
9832
+ __filename = fileURLToPath3(import.meta.url);
9833
+ __dirname = dirname4(__filename);
9528
9834
  TOKEN_PROFILES = {
9529
9835
  "graph-chat": {
9530
9836
  scopes: ["Chat.Read"],
@@ -9598,6 +9904,23 @@ var init_graph_token_provider = __esm({
9598
9904
  this.scheduleProactiveRefresh(pooled.expiresAt);
9599
9905
  return pooled.secret;
9600
9906
  }
9907
+ {
9908
+ const wamJwt = await acquireTokenForAudience(this.preferredAudience, this.requiredScopes);
9909
+ if (wamJwt) {
9910
+ const added = this.pool.addTokenFromJwt(wamJwt, "wam");
9911
+ const token = this.pool.getToken(this.requiredScopes, this.preferredAudience);
9912
+ if (token) {
9913
+ this.saveLegacyToken(token.secret);
9914
+ this.scheduleProactiveRefresh(token.expiresAt);
9915
+ logger_default.info("Token acquired via WAM broker", { tokenName: this.tokenName });
9916
+ return token.secret;
9917
+ }
9918
+ if (added) {
9919
+ this.scheduleProactiveRefresh(added.expiresAt);
9920
+ return added.secret;
9921
+ }
9922
+ }
9923
+ }
9601
9924
  logger_default.info("Tier 1 (pool) empty \u2014 trying Tier 2 (CDP extraction)...", {
9602
9925
  tokenName: this.tokenName,
9603
9926
  audience: this.preferredAudience
@@ -9855,7 +10178,7 @@ var init_graph_token_provider = __esm({
9855
10178
  * Tier 4: Legacy Python script fallback.
9856
10179
  */
9857
10180
  async extractViaLegacyScript() {
9858
- if (!existsSync9(this.extractScript)) {
10181
+ if (!existsSync10(this.extractScript)) {
9859
10182
  return false;
9860
10183
  }
9861
10184
  try {
@@ -9871,7 +10194,7 @@ var init_graph_token_provider = __esm({
9871
10194
  */
9872
10195
  migrateLegacyToken(tokenName) {
9873
10196
  const legacyPath = join11(homedir8(), ".graph-token", "tokens", `${tokenName}.json`);
9874
- if (!existsSync9(legacyPath))
10197
+ if (!existsSync10(legacyPath))
9875
10198
  return;
9876
10199
  try {
9877
10200
  const data = JSON.parse(readFileSync7(legacyPath, "utf-8"));
@@ -10130,9 +10453,9 @@ var init_outlook_graph = __esm({
10130
10453
  const wellKnown = WELL_KNOWN_FOLDERS[lower];
10131
10454
  if (wellKnown)
10132
10455
  return wellKnown;
10133
- const cached7 = this.folderIdCache.get(lower);
10134
- if (cached7)
10135
- return cached7;
10456
+ const cached8 = this.folderIdCache.get(lower);
10457
+ if (cached8)
10458
+ return cached8;
10136
10459
  try {
10137
10460
  const result = await this.graphGet(`/me/mailFolders?$filter=displayName eq '${folderName.replace(/'/g, "''")}'&$top=1`);
10138
10461
  if (result.value.length > 0) {
@@ -10390,7 +10713,7 @@ var init_outlook_graph = __esm({
10390
10713
  import { DatabaseSync } from "node:sqlite";
10391
10714
  import { join as join12 } from "node:path";
10392
10715
  import { homedir as homedir9 } from "node:os";
10393
- import { mkdirSync as mkdirSync8, existsSync as existsSync10 } from "node:fs";
10716
+ import { mkdirSync as mkdirSync8, existsSync as existsSync11 } from "node:fs";
10394
10717
  function hasFts5() {
10395
10718
  return _hasFts5;
10396
10719
  }
@@ -10406,7 +10729,7 @@ function detectFts5(database) {
10406
10729
  }
10407
10730
  function getMemoryDb() {
10408
10731
  if (!db) {
10409
- if (!existsSync10(DB_DIR)) {
10732
+ if (!existsSync11(DB_DIR)) {
10410
10733
  mkdirSync8(DB_DIR, { recursive: true });
10411
10734
  }
10412
10735
  db = new DatabaseSync(DB_PATH);
@@ -10490,9 +10813,9 @@ var init_db = __esm({
10490
10813
  import { createHash as createHash2 } from "node:crypto";
10491
10814
  function encodeAtom(word, dim2 = HRR_DIM) {
10492
10815
  const cacheKey = `${word}:${dim2}`;
10493
- const cached7 = atomCache.get(cacheKey);
10494
- if (cached7)
10495
- return cached7;
10816
+ const cached8 = atomCache.get(cacheKey);
10817
+ if (cached8)
10818
+ return cached8;
10496
10819
  const phases = new Float64Array(dim2);
10497
10820
  const bytesNeeded = dim2 * 4;
10498
10821
  const chunks = [];
@@ -10767,7 +11090,7 @@ __export(store_exports, {
10767
11090
  MemoryStore: () => MemoryStore
10768
11091
  });
10769
11092
  import { readFile as readFile2, readdir } from "node:fs/promises";
10770
- import { existsSync as existsSync11 } from "node:fs";
11093
+ import { existsSync as existsSync12 } from "node:fs";
10771
11094
  import { join as join13 } from "node:path";
10772
11095
  var MemoryStore;
10773
11096
  var init_store = __esm({
@@ -10825,7 +11148,7 @@ var init_store = __esm({
10825
11148
  const db2 = this.ensureDb();
10826
11149
  let migrated = 0;
10827
11150
  const insert = db2.prepare("INSERT INTO memories (content, source, created_at) VALUES (?, ?, ?)");
10828
- if (existsSync11(this.memoryFile)) {
11151
+ if (existsSync12(this.memoryFile)) {
10829
11152
  const content = await readFile2(this.memoryFile, "utf-8");
10830
11153
  const entries = this.parseMarkdownToEntries(content, "MEMORY.md");
10831
11154
  for (const entry of entries) {
@@ -10833,7 +11156,7 @@ var init_store = __esm({
10833
11156
  migrated++;
10834
11157
  }
10835
11158
  }
10836
- if (existsSync11(this.memoryDir)) {
11159
+ if (existsSync12(this.memoryDir)) {
10837
11160
  const files = await readdir(this.memoryDir);
10838
11161
  const mdFiles = files.filter((f) => f.endsWith(".md")).sort().reverse();
10839
11162
  for (const file2 of mdFiles) {
@@ -11117,13 +11440,13 @@ function createMemoryTools(config) {
11117
11440
  const todos = input.todos;
11118
11441
  try {
11119
11442
  const { appendFile: appendFile2, mkdir: mkdir5 } = await import("node:fs/promises");
11120
- const { existsSync: existsSync34 } = await import("node:fs");
11121
- const { join: join48 } = await import("node:path");
11122
- const { homedir: homedir32 } = await import("node:os");
11123
- const memoryDir = join48(homedir32(), ".openjaw", "memory");
11124
- if (!existsSync34(memoryDir))
11443
+ const { existsSync: existsSync36 } = await import("node:fs");
11444
+ const { join: join49 } = await import("node:path");
11445
+ const { homedir: homedir33 } = await import("node:os");
11446
+ const memoryDir = join49(homedir33(), ".openjaw", "memory");
11447
+ if (!existsSync36(memoryDir))
11125
11448
  await mkdir5(memoryDir, { recursive: true });
11126
- const todoPath = join48(memoryDir, "TODOS.md");
11449
+ const todoPath = join49(memoryDir, "TODOS.md");
11127
11450
  const { writeFile: writeFile5 } = await import("node:fs/promises");
11128
11451
  await writeFile5(todoPath, `# Session Todos
11129
11452
 
@@ -12444,7 +12767,7 @@ var init_teams_desktop = __esm({
12444
12767
  }
12445
12768
  }
12446
12769
  sleep(ms) {
12447
- return new Promise((resolve6) => setTimeout(resolve6, ms));
12770
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
12448
12771
  }
12449
12772
  /**
12450
12773
  * Get the current Teams window state
@@ -13311,7 +13634,7 @@ var init_teams_web = __esm({
13311
13634
  }
13312
13635
  }
13313
13636
  sleep(ms) {
13314
- return new Promise((resolve6) => setTimeout(resolve6, ms));
13637
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
13315
13638
  }
13316
13639
  };
13317
13640
  }
@@ -13708,9 +14031,9 @@ ${loopContent}` : loopContent;
13708
14031
  */
13709
14032
  async findChannelByName(searchTerm) {
13710
14033
  const searchLower = searchTerm.toLowerCase();
13711
- const cached7 = this.channelIdCache.get(searchLower);
13712
- if (cached7) {
13713
- return { ...cached7, displayName: searchTerm };
14034
+ const cached8 = this.channelIdCache.get(searchLower);
14035
+ if (cached8) {
14036
+ return { ...cached8, displayName: searchTerm };
13714
14037
  }
13715
14038
  try {
13716
14039
  const teamsData = await this.graphGet("/v1.0/me/joinedTeams");
@@ -13890,9 +14213,9 @@ ${loopContent}` : loopContent;
13890
14213
  * Results are cached per chat ID.
13891
14214
  */
13892
14215
  async getChatMembers(chatOrChannelId) {
13893
- const cached7 = this.chatMembersCache.get(chatOrChannelId);
13894
- if (cached7)
13895
- return cached7;
14216
+ const cached8 = this.chatMembersCache.get(chatOrChannelId);
14217
+ if (cached8)
14218
+ return cached8;
13896
14219
  try {
13897
14220
  let endpoint;
13898
14221
  if (this.contextType === "channel" && this.currentChannelTeamId && chatOrChannelId === this.currentChannelId) {
@@ -14130,9 +14453,9 @@ ${loopContent}` : loopContent;
14130
14453
  */
14131
14454
  async resolveChannelIds(teamName, channelName) {
14132
14455
  const cacheKey = `${teamName}/${channelName}`;
14133
- const cached7 = this.channelIdCache.get(cacheKey);
14134
- if (cached7)
14135
- return cached7;
14456
+ const cached8 = this.channelIdCache.get(cacheKey);
14457
+ if (cached8)
14458
+ return cached8;
14136
14459
  const teamsData = await this.graphGet("/v1.0/me/joinedTeams");
14137
14460
  const team = teamsData.value?.find((t) => t.displayName.toLowerCase() === teamName.toLowerCase());
14138
14461
  if (!team) {
@@ -14709,7 +15032,7 @@ var init_teams_chat_monitor = __esm({
14709
15032
  return this.sentMessages.has(normalized);
14710
15033
  }
14711
15034
  sleep(ms) {
14712
- return new Promise((resolve6) => setTimeout(resolve6, ms));
15035
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
14713
15036
  }
14714
15037
  /**
14715
15038
  * Attempt to reconnect the browser after detecting a disconnection.
@@ -15496,7 +15819,7 @@ ${lines.join("\n")}`;
15496
15819
  globalThis.__teamsSeenMessages.set(chatName, seenIds);
15497
15820
  }
15498
15821
  if (syncMode) {
15499
- return new Promise((resolve6) => {
15822
+ return new Promise((resolve7) => {
15500
15823
  const timer2 = setInterval(async () => {
15501
15824
  try {
15502
15825
  const current = await channel.readCurrentChatMessages();
@@ -15505,7 +15828,7 @@ ${lines.join("\n")}`;
15505
15828
  if (!seenIds.has(msgId) && msg.sender !== currentUserName) {
15506
15829
  clearInterval(timer2);
15507
15830
  seenIds.add(msgId);
15508
- resolve6({
15831
+ resolve7({
15509
15832
  success: true,
15510
15833
  channel: channelType,
15511
15834
  sync: true,
@@ -15582,7 +15905,7 @@ ${lines.join("\n")}`;
15582
15905
  globalThis.__teamsSeenMessages.set(chatName, seenIds);
15583
15906
  }
15584
15907
  if (syncMode) {
15585
- return new Promise((resolve6) => {
15908
+ return new Promise((resolve7) => {
15586
15909
  const timer2 = setInterval(async () => {
15587
15910
  try {
15588
15911
  const current = await channel.readCurrentChatMessages();
@@ -15591,7 +15914,7 @@ ${lines.join("\n")}`;
15591
15914
  if (!seenIds.has(msgId) && msg.sender !== currentUserName) {
15592
15915
  clearInterval(timer2);
15593
15916
  seenIds.add(msgId);
15594
- resolve6({
15917
+ resolve7({
15595
15918
  success: true,
15596
15919
  channel: channelType,
15597
15920
  sync: true,
@@ -15949,7 +16272,7 @@ ${lines.join("\n")}`;
15949
16272
  return allTools;
15950
16273
  }
15951
16274
  function sleep2(ms) {
15952
- return new Promise((resolve6) => setTimeout(resolve6, ms));
16275
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
15953
16276
  }
15954
16277
  var MAX_STORED_ENTRIES, ENTRY_TTL_MS;
15955
16278
  var init_chat = __esm({
@@ -15974,8 +16297,8 @@ var init_chat = __esm({
15974
16297
 
15975
16298
  // ../openjaw-mcp/dist/tools/files.js
15976
16299
  import { readFile as readFile3, writeFile as writeFile2, readdir as readdir2, stat, mkdir as mkdir2, unlink } from "node:fs/promises";
15977
- import { existsSync as existsSync12, readFileSync as readFileSync8 } from "node:fs";
15978
- import { join as join15, dirname as dirname4, basename, extname } from "node:path";
16300
+ import { existsSync as existsSync13, readFileSync as readFileSync8 } from "node:fs";
16301
+ import { join as join15, dirname as dirname5, basename, extname } from "node:path";
15979
16302
  import { execSync as execSync3 } from "node:child_process";
15980
16303
  function createFileTools(_config) {
15981
16304
  return [
@@ -16012,7 +16335,7 @@ function createFileTools(_config) {
16012
16335
  execute: /* @__PURE__ */ __name(async (input) => {
16013
16336
  const path3 = input.path;
16014
16337
  const encoding = input.encoding ?? "utf-8";
16015
- if (!existsSync12(path3)) {
16338
+ if (!existsSync13(path3)) {
16016
16339
  return { error: `File not found: ${path3}` };
16017
16340
  }
16018
16341
  const BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
@@ -16123,12 +16446,12 @@ function createFileTools(_config) {
16123
16446
  const append2 = input.append ?? false;
16124
16447
  const createDirs = input.create_dirs ?? true;
16125
16448
  if (createDirs) {
16126
- const dir2 = dirname4(path3);
16127
- if (!existsSync12(dir2)) {
16449
+ const dir2 = dirname5(path3);
16450
+ if (!existsSync13(dir2)) {
16128
16451
  await mkdir2(dir2, { recursive: true });
16129
16452
  }
16130
16453
  }
16131
- if (append2 && existsSync12(path3)) {
16454
+ if (append2 && existsSync13(path3)) {
16132
16455
  const existing = await readFile3(path3, "utf-8");
16133
16456
  await writeFile2(path3, existing + content, "utf-8");
16134
16457
  } else {
@@ -16175,7 +16498,7 @@ function createFileTools(_config) {
16175
16498
  const recursive = input.recursive ?? false;
16176
16499
  const includeHidden = input.include_hidden ?? false;
16177
16500
  const pattern = input.pattern;
16178
- if (!existsSync12(dirPath)) {
16501
+ if (!existsSync13(dirPath)) {
16179
16502
  return { error: `Directory not found: ${dirPath}` };
16180
16503
  }
16181
16504
  const results = [];
@@ -16223,7 +16546,7 @@ function createFileTools(_config) {
16223
16546
  requiresConfirmation: true,
16224
16547
  execute: /* @__PURE__ */ __name(async (input) => {
16225
16548
  const path3 = input.path;
16226
- if (!existsSync12(path3)) {
16549
+ if (!existsSync13(path3)) {
16227
16550
  return { error: `File not found: ${path3}` };
16228
16551
  }
16229
16552
  await unlink(path3);
@@ -16245,7 +16568,7 @@ function createFileTools(_config) {
16245
16568
  },
16246
16569
  execute: /* @__PURE__ */ __name(async (input) => {
16247
16570
  const path3 = input.path;
16248
- if (!existsSync12(path3)) {
16571
+ if (!existsSync13(path3)) {
16249
16572
  return { error: `Path not found: ${path3}` };
16250
16573
  }
16251
16574
  const stats = await stat(path3);
@@ -16275,7 +16598,7 @@ function createFileTools(_config) {
16275
16598
  },
16276
16599
  execute: /* @__PURE__ */ __name(async (input) => {
16277
16600
  const filePath = input.path;
16278
- if (!existsSync12(filePath)) {
16601
+ if (!existsSync13(filePath)) {
16279
16602
  return { error: `Image not found: ${filePath}` };
16280
16603
  }
16281
16604
  const ext = extname(filePath).toLowerCase();
@@ -16321,7 +16644,7 @@ function createFileTools(_config) {
16321
16644
  const path3 = input.path;
16322
16645
  const oldStr = input.old_str;
16323
16646
  const newStr = input.new_str;
16324
- if (!existsSync12(path3)) {
16647
+ if (!existsSync13(path3)) {
16325
16648
  return { error: `File not found: ${path3}` };
16326
16649
  }
16327
16650
  const fileStats = await stat(path3);
@@ -16507,7 +16830,7 @@ var init_web_search_types = __esm({
16507
16830
  });
16508
16831
 
16509
16832
  // ../openjaw-mcp/dist/tools/shell.js
16510
- import { spawn } from "node:child_process";
16833
+ import { spawn as spawn2 } from "node:child_process";
16511
16834
  import clipboardy from "clipboardy";
16512
16835
  import notifier from "node-notifier";
16513
16836
  function truncateOutput(text, maxLines = 200) {
@@ -16773,12 +17096,12 @@ function createShellTools(_config, hooks) {
16773
17096
  const shell = input.shell ?? true;
16774
17097
  if (input.background) {
16775
17098
  const { tmpdir: tmpdir13 } = await import("node:os");
16776
- const { join: join48 } = await import("node:path");
17099
+ const { join: join49 } = await import("node:path");
16777
17100
  const { randomUUID: randomUUID15 } = await import("node:crypto");
16778
17101
  const { createWriteStream: createWriteStream2 } = await import("node:fs");
16779
17102
  const taskId = randomUUID15().slice(0, 8);
16780
- const outputPath = join48(tmpdir13(), `oj-bg-${taskId}.log`);
16781
- const detached = spawn(command, [], {
17103
+ const outputPath = join49(tmpdir13(), `oj-bg-${taskId}.log`);
17104
+ const detached = spawn2(command, [], {
16782
17105
  shell,
16783
17106
  cwd,
16784
17107
  detached: true,
@@ -16797,8 +17120,8 @@ function createShellTools(_config, hooks) {
16797
17120
  message: `Command started in background (PID: ${detached.pid}). Output: ${outputPath}`
16798
17121
  };
16799
17122
  }
16800
- return new Promise((resolve6) => {
16801
- const proc = spawn(command, [], {
17123
+ return new Promise((resolve7) => {
17124
+ const proc = spawn2(command, [], {
16802
17125
  shell,
16803
17126
  cwd,
16804
17127
  timeout,
@@ -16815,7 +17138,7 @@ function createShellTools(_config, hooks) {
16815
17138
  proc.on("close", (code) => {
16816
17139
  const stdoutResult = truncateOutput(stdout.trim());
16817
17140
  const stderrResult = truncateOutput(stderr.trim());
16818
- resolve6({
17141
+ resolve7({
16819
17142
  command,
16820
17143
  exitCode: code,
16821
17144
  stdout: stdoutResult.text,
@@ -16826,7 +17149,7 @@ function createShellTools(_config, hooks) {
16826
17149
  });
16827
17150
  });
16828
17151
  proc.on("error", (error) => {
16829
- resolve6({
17152
+ resolve7({
16830
17153
  command,
16831
17154
  exitCode: -1,
16832
17155
  stdout: "",
@@ -16860,8 +17183,8 @@ function createShellTools(_config, hooks) {
16860
17183
  },
16861
17184
  requiresConfirmation: false,
16862
17185
  execute: /* @__PURE__ */ __name(async (input) => {
16863
- const { writeFileSync: writeFileSync23, unlinkSync: unlinkSync9 } = await import("node:fs");
16864
- const { join: join48 } = await import("node:path");
17186
+ const { writeFileSync: writeFileSync23, unlinkSync: unlinkSync10 } = await import("node:fs");
17187
+ const { join: join49 } = await import("node:path");
16865
17188
  const { tmpdir: tmpdir13 } = await import("node:os");
16866
17189
  const { randomUUID: randomUUID15 } = await import("node:crypto");
16867
17190
  const { execFile: execFile3 } = await import("node:child_process");
@@ -16880,14 +17203,14 @@ function createShellTools(_config, hooks) {
16880
17203
  const interpreter = interpreterMap[language];
16881
17204
  if (!interpreter)
16882
17205
  return { error: `Unsupported language: ${language}` };
16883
- const tmpFile = join48(tmpdir13(), `oj-code-${randomUUID15().slice(0, 8)}${ext}`);
17206
+ const tmpFile = join49(tmpdir13(), `oj-code-${randomUUID15().slice(0, 8)}${ext}`);
16884
17207
  try {
16885
17208
  writeFileSync23(tmpFile, code, "utf-8");
16886
17209
  const startTime = Date.now();
16887
- const result = await new Promise((resolve6) => {
17210
+ const result = await new Promise((resolve7) => {
16888
17211
  execFile3(interpreter.cmd, [...interpreter.args, tmpFile], { timeout, maxBuffer: 10 * 1024 * 1024 }, (error, stdout2, stderr2) => {
16889
17212
  const exitCode = error ? typeof error.code === "number" ? error.code : 1 : 0;
16890
- resolve6({ exitCode, stdout: stdout2 ?? "", stderr: stderr2 ?? "" });
17213
+ resolve7({ exitCode, stdout: stdout2 ?? "", stderr: stderr2 ?? "" });
16891
17214
  });
16892
17215
  });
16893
17216
  const executionTimeMs = Date.now() - startTime;
@@ -16913,7 +17236,7 @@ function createShellTools(_config, hooks) {
16913
17236
  };
16914
17237
  } finally {
16915
17238
  try {
16916
- unlinkSync9(tmpFile);
17239
+ unlinkSync10(tmpFile);
16917
17240
  } catch {
16918
17241
  }
16919
17242
  }
@@ -16980,7 +17303,7 @@ function createShellTools(_config, hooks) {
16980
17303
  required: ["title", "message"]
16981
17304
  },
16982
17305
  execute: /* @__PURE__ */ __name(async (input) => {
16983
- return new Promise((resolve6) => {
17306
+ return new Promise((resolve7) => {
16984
17307
  notifier.notify({
16985
17308
  title: input.title,
16986
17309
  message: input.message,
@@ -16988,9 +17311,9 @@ function createShellTools(_config, hooks) {
16988
17311
  sound: true
16989
17312
  }, (err) => {
16990
17313
  if (err) {
16991
- resolve6({ error: err.message });
17314
+ resolve7({ error: err.message });
16992
17315
  } else {
16993
- resolve6({ success: true });
17316
+ resolve7({ success: true });
16994
17317
  }
16995
17318
  });
16996
17319
  });
@@ -17094,7 +17417,7 @@ function createShellTools(_config, hooks) {
17094
17417
  },
17095
17418
  execute: /* @__PURE__ */ __name(async (input) => {
17096
17419
  const seconds = Math.min(Math.max(0.1, input.seconds), 60);
17097
- await new Promise((resolve6) => setTimeout(resolve6, seconds * 1e3));
17420
+ await new Promise((resolve7) => setTimeout(resolve7, seconds * 1e3));
17098
17421
  return { waited: seconds, message: `Waited ${seconds} seconds` };
17099
17422
  }, "execute")
17100
17423
  },
@@ -18206,7 +18529,7 @@ var init_office_desktop = __esm({
18206
18529
  return Array.isArray(parsed) ? parsed : [parsed];
18207
18530
  }
18208
18531
  sleep(ms) {
18209
- return new Promise((resolve6) => setTimeout(resolve6, ms));
18532
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
18210
18533
  }
18211
18534
  };
18212
18535
  }
@@ -19887,7 +20210,7 @@ public class Win32Send {
19887
20210
  }
19888
20211
  }
19889
20212
  sleep(ms) {
19890
- return new Promise((resolve6) => setTimeout(resolve6, ms));
20213
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
19891
20214
  }
19892
20215
  };
19893
20216
  }
@@ -19952,7 +20275,7 @@ function getActiveMonitors() {
19952
20275
  return result;
19953
20276
  }
19954
20277
  function sleep3(ms) {
19955
- return new Promise((resolve6) => setTimeout(resolve6, ms));
20278
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
19956
20279
  }
19957
20280
  async function fileHash(filePath) {
19958
20281
  const data = fs.readFileSync(filePath);
@@ -20634,6 +20957,224 @@ var init_wechat = __esm({
20634
20957
  }
20635
20958
  });
20636
20959
 
20960
+ // ../openjaw-mcp/dist/tools/workiq.js
20961
+ import { spawn as spawn3 } from "node:child_process";
20962
+ import { accessSync, chmodSync, constants, existsSync as existsSync14, readFileSync as readFileSync9 } from "node:fs";
20963
+ import { createRequire as createRequire2 } from "node:module";
20964
+ import { homedir as homedir10 } from "node:os";
20965
+ import { dirname as dirname6, join as join16 } from "node:path";
20966
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
20967
+ function workiqStateFilePath() {
20968
+ return join16(homedir10(), ".work-iq-cli", ".workiq.json");
20969
+ }
20970
+ function workiqPlatformDir(pkgDir) {
20971
+ if (process.platform === "win32") {
20972
+ const nativeDir = join16(pkgDir, "bin", `win-${process.arch}`);
20973
+ if (!existsSync14(nativeDir) && process.arch !== "x64")
20974
+ return "win-x64";
20975
+ return `win-${process.arch}`;
20976
+ }
20977
+ if (process.platform === "darwin")
20978
+ return `osx-${process.arch}`;
20979
+ return `linux-${process.arch}`;
20980
+ }
20981
+ function ensureExecutable(binaryPath) {
20982
+ if (process.platform === "win32")
20983
+ return;
20984
+ try {
20985
+ accessSync(binaryPath, constants.X_OK);
20986
+ } catch {
20987
+ try {
20988
+ chmodSync(binaryPath, 493);
20989
+ } catch {
20990
+ }
20991
+ }
20992
+ }
20993
+ function resolveWorkIQBinaryInPackage(pkgDir) {
20994
+ const binaryName = process.platform === "win32" ? "workiq.exe" : "workiq";
20995
+ const binaryPath = join16(pkgDir, "bin", workiqPlatformDir(pkgDir), binaryName);
20996
+ if (!existsSync14(binaryPath))
20997
+ return null;
20998
+ ensureExecutable(binaryPath);
20999
+ return binaryPath;
21000
+ }
21001
+ function resolveWorkIQBinary(nodeModulesPath) {
21002
+ return resolveWorkIQBinaryInPackage(join16(nodeModulesPath, "@microsoft", "workiq"));
21003
+ }
21004
+ function isWorkIQEulaAccepted() {
21005
+ const path3 = workiqStateFilePath();
21006
+ if (!existsSync14(path3))
21007
+ return false;
21008
+ try {
21009
+ const parsed = JSON.parse(readFileSync9(path3, "utf-8"));
21010
+ if (typeof parsed !== "object" || parsed === null)
21011
+ return false;
21012
+ const obj = parsed;
21013
+ const v = obj["I-accept-EULA"] ?? obj["i-accept-eula"] ?? obj.eulaAccepted ?? obj.eula_accepted ?? obj.acceptedEula ?? obj.eula;
21014
+ if (typeof v === "string")
21015
+ return v.trim().toLowerCase() === "true";
21016
+ return v === true || v === 1;
21017
+ } catch {
21018
+ return false;
21019
+ }
21020
+ }
21021
+ function findWorkIQPackageDir() {
21022
+ const anchors = [
21023
+ () => fileURLToPath4(import.meta.url),
21024
+ () => createRequire2(import.meta.url).resolve("@charzhu/openjaw/package.json"),
21025
+ () => join16(process.cwd(), "index.js")
21026
+ ];
21027
+ for (const getAnchor of anchors) {
21028
+ try {
21029
+ const req = createRequire2(getAnchor());
21030
+ const pkgJson = req.resolve("@microsoft/workiq/package.json");
21031
+ return dirname6(pkgJson);
21032
+ } catch {
21033
+ }
21034
+ }
21035
+ return null;
21036
+ }
21037
+ function findWorkIQBinary() {
21038
+ const pkgDir = findWorkIQPackageDir();
21039
+ if (pkgDir) {
21040
+ const found = resolveWorkIQBinaryInPackage(pkgDir);
21041
+ if (found)
21042
+ return found;
21043
+ }
21044
+ for (const nm of [join16(process.cwd(), "node_modules")]) {
21045
+ const found = resolveWorkIQBinary(nm);
21046
+ if (found)
21047
+ return found;
21048
+ }
21049
+ return null;
21050
+ }
21051
+ function runWorkIQ(binaryPath, args, stdinText, timeoutMs) {
21052
+ return new Promise((resolve7) => {
21053
+ const proc = spawn3(binaryPath, args, {
21054
+ stdio: ["pipe", "pipe", "pipe"],
21055
+ windowsHide: true,
21056
+ timeout: timeoutMs
21057
+ });
21058
+ let stdout = "";
21059
+ let stderr = "";
21060
+ proc.stdout?.on("data", (d) => {
21061
+ stdout += d.toString();
21062
+ });
21063
+ proc.stderr?.on("data", (d) => {
21064
+ stderr += d.toString();
21065
+ });
21066
+ proc.on("error", (err) => {
21067
+ resolve7({ exitCode: -1, stdout, stderr, error: err.message });
21068
+ });
21069
+ proc.on("close", (code) => {
21070
+ resolve7({ exitCode: code, stdout: stdout.trim(), stderr: stderr.trim() });
21071
+ });
21072
+ if (stdinText !== void 0) {
21073
+ proc.stdin?.write(stdinText.endsWith("\n") ? stdinText : stdinText + "\n");
21074
+ }
21075
+ proc.stdin?.end();
21076
+ });
21077
+ }
21078
+ function createWorkIQTools(_config) {
21079
+ return [
21080
+ {
21081
+ name: "workiq_ask",
21082
+ description: 'Query Microsoft 365 work data (your emails, meetings, calendar, documents, people, projects) by asking a natural-language question. Backed by the WorkIQ CLI. Prefer this for "what/who/when" questions about your work context. First use requires accepting the WorkIQ license via workiq_accept_eula.',
21083
+ parameters: {
21084
+ type: "object",
21085
+ properties: {
21086
+ question: {
21087
+ type: "string",
21088
+ description: 'The natural-language question about your M365 work data, e.g. "what meetings do I have tomorrow?"'
21089
+ },
21090
+ timeout: {
21091
+ type: "number",
21092
+ description: "Timeout in milliseconds (default 120000, max 300000)."
21093
+ }
21094
+ },
21095
+ required: ["question"]
21096
+ },
21097
+ requiresConfirmation: false,
21098
+ execute: /* @__PURE__ */ __name(async (input) => {
21099
+ if (!isWorkIQEulaAccepted())
21100
+ return EULA_REFUSAL;
21101
+ const binary = findWorkIQBinary();
21102
+ if (!binary) {
21103
+ return {
21104
+ error: "WorkIQ CLI is unavailable: no @microsoft/workiq binary was found for this platform/arch. It ships as a bundled dependency \u2014 try reinstalling openjaw-mcp dependencies (`npm install`)."
21105
+ };
21106
+ }
21107
+ const question = String(input.question ?? "").trim();
21108
+ if (!question)
21109
+ return { error: "workiq_ask requires a non-empty question." };
21110
+ const timeout = Math.min(Number(input.timeout) || 12e4, 3e5);
21111
+ const result = await runWorkIQ(binary, ["ask"], question, timeout);
21112
+ if (result.error) {
21113
+ return { error: `WorkIQ failed to run: ${result.error}`, stderr: result.stderr };
21114
+ }
21115
+ return {
21116
+ answer: result.stdout,
21117
+ ...result.stderr ? { stderr: result.stderr } : {},
21118
+ success: result.exitCode === 0,
21119
+ ...result.exitCode !== 0 ? { exitCode: result.exitCode } : {}
21120
+ };
21121
+ }, "execute")
21122
+ },
21123
+ {
21124
+ name: "workiq_accept_eula",
21125
+ description: "Accept the WorkIQ CLI license agreement (one-time, required before workiq_ask works). Runs `workiq accept-eula`; the CLI records acceptance in ~/.work-iq-cli/.workiq.json and it persists across all apps on this machine.",
21126
+ parameters: {
21127
+ type: "object",
21128
+ properties: {}
21129
+ },
21130
+ requiresConfirmation: true,
21131
+ execute: /* @__PURE__ */ __name(async () => {
21132
+ const binary = findWorkIQBinary();
21133
+ if (!binary) {
21134
+ return {
21135
+ error: "WorkIQ CLI is unavailable: no @microsoft/workiq binary was found for this platform/arch. It ships as a bundled dependency \u2014 try reinstalling openjaw-mcp dependencies (`npm install`)."
21136
+ };
21137
+ }
21138
+ if (isWorkIQEulaAccepted()) {
21139
+ return { accepted: true, message: "WorkIQ license was already accepted." };
21140
+ }
21141
+ const result = await runWorkIQ(binary, ["accept-eula"], void 0, 6e4);
21142
+ if (result.error) {
21143
+ return { error: `Failed to run accept-eula: ${result.error}`, stderr: result.stderr };
21144
+ }
21145
+ const accepted = isWorkIQEulaAccepted();
21146
+ return {
21147
+ accepted,
21148
+ success: result.exitCode === 0 && accepted,
21149
+ ...result.stdout ? { output: result.stdout } : {},
21150
+ ...result.stderr ? { stderr: result.stderr } : {},
21151
+ ...!accepted ? { message: "accept-eula ran but acceptance was not recorded; the user may have declined in the CLI." } : {}
21152
+ };
21153
+ }, "execute")
21154
+ }
21155
+ ];
21156
+ }
21157
+ var EULA_REFUSAL;
21158
+ var init_workiq = __esm({
21159
+ "../openjaw-mcp/dist/tools/workiq.js"() {
21160
+ "use strict";
21161
+ __name(workiqStateFilePath, "workiqStateFilePath");
21162
+ __name(workiqPlatformDir, "workiqPlatformDir");
21163
+ __name(ensureExecutable, "ensureExecutable");
21164
+ __name(resolveWorkIQBinaryInPackage, "resolveWorkIQBinaryInPackage");
21165
+ __name(resolveWorkIQBinary, "resolveWorkIQBinary");
21166
+ __name(isWorkIQEulaAccepted, "isWorkIQEulaAccepted");
21167
+ __name(findWorkIQPackageDir, "findWorkIQPackageDir");
21168
+ __name(findWorkIQBinary, "findWorkIQBinary");
21169
+ __name(runWorkIQ, "runWorkIQ");
21170
+ EULA_REFUSAL = {
21171
+ eula_required: true,
21172
+ message: "WorkIQ requires a one-time license agreement before first use. Call the `workiq_accept_eula` tool to accept it (the user will be asked to confirm), then retry `workiq_ask`."
21173
+ };
21174
+ __name(createWorkIQTools, "createWorkIQTools");
21175
+ }
21176
+ });
21177
+
20637
21178
  // ../openjaw-mcp/dist/tools/categories.js
20638
21179
  function categoryForTool2(toolName) {
20639
21180
  for (const [prefix, category] of TOOL_PREFIX_MAP) {
@@ -20655,12 +21196,16 @@ var init_categories = __esm({
20655
21196
  init_memory();
20656
21197
  init_office();
20657
21198
  init_wechat();
21199
+ init_workiq();
20658
21200
  TOOL_CATEGORIES = {
20659
21201
  browser: /* @__PURE__ */ __name((config, browser) => createBrowseTools(config, browser), "browser"),
20660
21202
  email: /* @__PURE__ */ __name((config, browser) => createEmailTools(config, browser), "email"),
20661
21203
  teams: /* @__PURE__ */ __name((config, browser) => createChatTools(config, browser), "teams"),
20662
21204
  files: /* @__PURE__ */ __name((config) => createFileTools(config), "files"),
20663
- system: /* @__PURE__ */ __name((config, _browser, hooks) => createShellTools(config, hooks), "system"),
21205
+ // WorkIQ CLI tools (M365 natural-language query) ride the always-loaded
21206
+ // `system` category alongside shell tools — no separate profile wiring,
21207
+ // and they auth themselves so there's nothing to gate at load time.
21208
+ system: /* @__PURE__ */ __name((config, _browser, hooks) => [...createShellTools(config, hooks), ...createWorkIQTools(config)], "system"),
20664
21209
  memory: /* @__PURE__ */ __name((config) => createMemoryTools(config), "memory"),
20665
21210
  office: /* @__PURE__ */ __name((config) => createOfficeTools(config), "office"),
20666
21211
  wechat: /* @__PURE__ */ __name((config) => createWeChatTools(config), "wechat")
@@ -20698,6 +21243,7 @@ var init_categories = __esm({
20698
21243
  ["sleep", "system"],
20699
21244
  ["ask_user", "system"],
20700
21245
  ["config", "system"],
21246
+ ["workiq_", "system"],
20701
21247
  ["todo_write", "memory"]
20702
21248
  ];
20703
21249
  __name(categoryForTool2, "categoryForTool");
@@ -20966,11 +21512,11 @@ var init_registry = __esm({
20966
21512
  });
20967
21513
 
20968
21514
  // src/prompts/identity.ts
20969
- import { readFileSync as readFileSync9 } from "node:fs";
20970
- import { join as join16 } from "node:path";
21515
+ import { readFileSync as readFileSync10 } from "node:fs";
21516
+ import { join as join17 } from "node:path";
20971
21517
  function getIdentitySection() {
20972
21518
  if (cached2 === void 0) {
20973
- cached2 = readFileSync9(join16(packagePromptsDir(), "IDENTITY.md"), "utf8");
21519
+ cached2 = readFileSync10(join17(packagePromptsDir(), "IDENTITY.md"), "utf8");
20974
21520
  }
20975
21521
  return cached2;
20976
21522
  }
@@ -20984,11 +21530,11 @@ var init_identity = __esm({
20984
21530
  });
20985
21531
 
20986
21532
  // src/prompts/reasoning.ts
20987
- import { readFileSync as readFileSync10 } from "node:fs";
20988
- import { join as join17 } from "node:path";
21533
+ import { readFileSync as readFileSync11 } from "node:fs";
21534
+ import { join as join18 } from "node:path";
20989
21535
  function getReasoningSection() {
20990
21536
  if (cached3 === void 0) {
20991
- cached3 = readFileSync10(join17(packagePromptsDir(), "REASONING.md"), "utf8");
21537
+ cached3 = readFileSync11(join18(packagePromptsDir(), "REASONING.md"), "utf8");
20992
21538
  }
20993
21539
  return cached3;
20994
21540
  }
@@ -21002,11 +21548,11 @@ var init_reasoning = __esm({
21002
21548
  });
21003
21549
 
21004
21550
  // src/prompts/safety.ts
21005
- import { readFileSync as readFileSync11 } from "node:fs";
21006
- import { join as join18 } from "node:path";
21551
+ import { readFileSync as readFileSync12 } from "node:fs";
21552
+ import { join as join19 } from "node:path";
21007
21553
  function getSafetySection() {
21008
21554
  if (cached4 === void 0) {
21009
- cached4 = readFileSync11(join18(packagePromptsDir(), "SAFETY.md"), "utf8");
21555
+ cached4 = readFileSync12(join19(packagePromptsDir(), "SAFETY.md"), "utf8");
21010
21556
  }
21011
21557
  return cached4;
21012
21558
  }
@@ -21020,11 +21566,11 @@ var init_safety = __esm({
21020
21566
  });
21021
21567
 
21022
21568
  // src/prompts/computerUse.ts
21023
- import { readFileSync as readFileSync12 } from "node:fs";
21024
- import { join as join19 } from "node:path";
21569
+ import { readFileSync as readFileSync13 } from "node:fs";
21570
+ import { join as join20 } from "node:path";
21025
21571
  function getComputerUseSection() {
21026
21572
  if (cached5 === void 0) {
21027
- cached5 = readFileSync12(join19(packagePromptsDir(), "COMPUTER_USE.md"), "utf8");
21573
+ cached5 = readFileSync13(join20(packagePromptsDir(), "COMPUTER_USE.md"), "utf8");
21028
21574
  }
21029
21575
  return cached5;
21030
21576
  }
@@ -21038,19 +21584,19 @@ var init_computerUse = __esm({
21038
21584
  });
21039
21585
 
21040
21586
  // src/prompts/user.ts
21041
- import { readFileSync as readFileSync13, existsSync as existsSync13, writeFileSync as writeFileSync9, mkdirSync as mkdirSync10 } from "node:fs";
21042
- import { join as join20 } from "node:path";
21043
- import { homedir as homedir10 } from "node:os";
21587
+ import { readFileSync as readFileSync14, existsSync as existsSync15, writeFileSync as writeFileSync9, mkdirSync as mkdirSync10 } from "node:fs";
21588
+ import { join as join21 } from "node:path";
21589
+ import { homedir as homedir11 } from "node:os";
21044
21590
  function getUserSection() {
21045
21591
  if (cached6 === void 0) {
21046
- if (!existsSync13(userOverridePath)) {
21047
- const bundledPath = join20(packagePromptsDir(), "USER.md");
21048
- if (existsSync13(bundledPath)) {
21049
- if (!existsSync13(configDir)) mkdirSync10(configDir, { recursive: true });
21050
- writeFileSync9(userOverridePath, readFileSync13(bundledPath, "utf8"), "utf8");
21592
+ if (!existsSync15(userOverridePath)) {
21593
+ const bundledPath = join21(packagePromptsDir(), "USER.md");
21594
+ if (existsSync15(bundledPath)) {
21595
+ if (!existsSync15(configDir)) mkdirSync10(configDir, { recursive: true });
21596
+ writeFileSync9(userOverridePath, readFileSync14(bundledPath, "utf8"), "utf8");
21051
21597
  }
21052
21598
  }
21053
- cached6 = existsSync13(userOverridePath) ? readFileSync13(userOverridePath, "utf8") : null;
21599
+ cached6 = existsSync15(userOverridePath) ? readFileSync14(userOverridePath, "utf8") : null;
21054
21600
  }
21055
21601
  return cached6;
21056
21602
  }
@@ -21059,8 +21605,8 @@ var init_user = __esm({
21059
21605
  "src/prompts/user.ts"() {
21060
21606
  "use strict";
21061
21607
  init_packageRoot();
21062
- configDir = join20(homedir10(), ".openjaw-agent");
21063
- userOverridePath = join20(configDir, "USER.md");
21608
+ configDir = join21(homedir11(), ".openjaw-agent");
21609
+ userOverridePath = join21(configDir, "USER.md");
21064
21610
  __name(getUserSection, "getUserSection");
21065
21611
  }
21066
21612
  });
@@ -21072,8 +21618,8 @@ __export(memory_exports, {
21072
21618
  setMemoryPrefetchQuery: () => setMemoryPrefetchQuery,
21073
21619
  shouldSkipPrefetch: () => shouldSkipPrefetch
21074
21620
  });
21075
- import { join as join21 } from "node:path";
21076
- import { homedir as homedir11 } from "node:os";
21621
+ import { join as join22 } from "node:path";
21622
+ import { homedir as homedir12 } from "node:os";
21077
21623
  function setMemoryPrefetchQuery(query) {
21078
21624
  currentQuery = query;
21079
21625
  }
@@ -21124,7 +21670,7 @@ function getMemorySection() {
21124
21670
  if (shouldSkipPrefetch(currentQuery)) return null;
21125
21671
  try {
21126
21672
  const { DatabaseSync: DatabaseSync2 } = __require("node:sqlite");
21127
- const dbPath = join21(homedir11(), ".openjaw", "memory.db");
21673
+ const dbPath = join22(homedir12(), ".openjaw", "memory.db");
21128
21674
  let db2;
21129
21675
  try {
21130
21676
  db2 = new DatabaseSync2(dbPath, { readOnly: true });
@@ -21339,9 +21885,9 @@ var init_frontmatter = __esm({
21339
21885
  });
21340
21886
 
21341
21887
  // src/skills/registry.ts
21342
- import { existsSync as existsSync14, readFileSync as readFileSync14, readdirSync as readdirSync2, statSync } from "node:fs";
21343
- import { join as join22 } from "node:path";
21344
- import { homedir as homedir12 } from "node:os";
21888
+ import { existsSync as existsSync16, readFileSync as readFileSync15, readdirSync as readdirSync2, statSync } from "node:fs";
21889
+ import { join as join23 } from "node:path";
21890
+ import { homedir as homedir13 } from "node:os";
21345
21891
  function skillRoots() {
21346
21892
  return rootsOverride ?? { bundledDir: packageSkillsDir(), userDir: DEFAULT_USER_DIR };
21347
21893
  }
@@ -21361,7 +21907,7 @@ function loadSkillBody(skillName) {
21361
21907
  const skill = findSkill(skillName);
21362
21908
  if (!skill) return null;
21363
21909
  try {
21364
- const content = readFileSync14(skill.filePath, "utf-8").trim();
21910
+ const content = readFileSync15(skill.filePath, "utf-8").trim();
21365
21911
  const parsed = parseSkillFile(content, `${skill.name}.md`);
21366
21912
  return parsed.body;
21367
21913
  } catch {
@@ -21417,7 +21963,7 @@ function normalizeSkillName(name) {
21417
21963
  function loadFlatSkillsFromDir(dir2, source, priority, out) {
21418
21964
  for (const entry of safeReadDir(dir2)) {
21419
21965
  if (!entry.isFile() || !isMarkdown(entry.name)) continue;
21420
- const filePath = join22(dir2, entry.name);
21966
+ const filePath = join23(dir2, entry.name);
21421
21967
  const skill = parseSkillAtPath(filePath, dir2, entry.name, source, entry.name);
21422
21968
  if (skill) putSkill(out, skill, priority);
21423
21969
  }
@@ -21425,10 +21971,10 @@ function loadFlatSkillsFromDir(dir2, source, priority, out) {
21425
21971
  function loadPackagedSkillsFromDir(dir2, out) {
21426
21972
  for (const entry of safeReadDir(dir2)) {
21427
21973
  if (!entry.isDirectory()) continue;
21428
- const rootDir2 = join22(dir2, entry.name);
21974
+ const rootDir2 = join23(dir2, entry.name);
21429
21975
  const entrypoint = findPackageEntrypoint(rootDir2);
21430
21976
  if (!entrypoint) continue;
21431
- const filePath = join22(rootDir2, entrypoint);
21977
+ const filePath = join23(rootDir2, entrypoint);
21432
21978
  const skill = parseSkillAtPath(filePath, rootDir2, entrypoint, "user", `${entry.name}.md`);
21433
21979
  if (skill) putSkill(out, skill, 2);
21434
21980
  }
@@ -21444,7 +21990,7 @@ function findPackageEntrypoint(dir2) {
21444
21990
  }
21445
21991
  function parseSkillAtPath(filePath, rootDir2, entrypoint, source, fallbackFilename) {
21446
21992
  try {
21447
- const content = readFileSync14(filePath, "utf-8").trim();
21993
+ const content = readFileSync15(filePath, "utf-8").trim();
21448
21994
  if (!content) return null;
21449
21995
  const parsed = parseSkillFile(content, fallbackFilename);
21450
21996
  const name = normalizeSkillName(parsed.meta.name);
@@ -21469,7 +22015,7 @@ function putSkill(out, skill, priority) {
21469
22015
  }
21470
22016
  }
21471
22017
  function safeReadDir(dir2) {
21472
- if (!existsSync14(dir2)) return [];
22018
+ if (!existsSync16(dir2)) return [];
21473
22019
  try {
21474
22020
  return readdirSync2(dir2, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
21475
22021
  } catch {
@@ -21481,11 +22027,11 @@ function buildRegistrySignature() {
21481
22027
  return [signatureForDir(roots.bundledDir), signatureForDir(roots.userDir)].join("|");
21482
22028
  }
21483
22029
  function signatureForDir(dir2) {
21484
- if (!existsSync14(dir2)) return `${dir2}:missing`;
22030
+ if (!existsSync16(dir2)) return `${dir2}:missing`;
21485
22031
  const parts = [];
21486
22032
  try {
21487
22033
  for (const entry of safeReadDir(dir2)) {
21488
- const fullPath = join22(dir2, entry.name);
22034
+ const fullPath = join23(dir2, entry.name);
21489
22035
  if (entry.isFile()) {
21490
22036
  if (!isMarkdown(entry.name)) continue;
21491
22037
  parts.push(fileSignature(fullPath, `f:${entry.name}`));
@@ -21493,7 +22039,7 @@ function signatureForDir(dir2) {
21493
22039
  parts.push(fileSignature(fullPath, `d:${entry.name}`));
21494
22040
  for (const child of safeReadDir(fullPath)) {
21495
22041
  if (child.isFile() && isMarkdown(child.name)) {
21496
- parts.push(fileSignature(join22(fullPath, child.name), `d:${entry.name}/${child.name}`));
22042
+ parts.push(fileSignature(join23(fullPath, child.name), `d:${entry.name}/${child.name}`));
21497
22043
  }
21498
22044
  }
21499
22045
  }
@@ -21520,7 +22066,7 @@ var init_registry2 = __esm({
21520
22066
  "use strict";
21521
22067
  init_frontmatter();
21522
22068
  init_packageRoot();
21523
- DEFAULT_USER_DIR = join22(homedir12(), ".openjaw-agent", "skills");
22069
+ DEFAULT_USER_DIR = join23(homedir13(), ".openjaw-agent", "skills");
21524
22070
  ENTRYPOINT_NAMES = ["SKILL.md", "skill.md"];
21525
22071
  cachedSkills = null;
21526
22072
  rootsOverride = null;
@@ -21586,9 +22132,9 @@ __export(context_exports, {
21586
22132
  getContextSection: () => getContextSection,
21587
22133
  setActiveBridges: () => setActiveBridges
21588
22134
  });
21589
- import { existsSync as existsSync15, readFileSync as readFileSync15, readdirSync as readdirSync3 } from "node:fs";
22135
+ import { existsSync as existsSync17, readFileSync as readFileSync16, readdirSync as readdirSync3 } from "node:fs";
21590
22136
  import { execSync as execSync4 } from "node:child_process";
21591
- import { join as join23 } from "node:path";
22137
+ import { join as join24 } from "node:path";
21592
22138
  function detectProjectInfo() {
21593
22139
  const cwd = process.cwd();
21594
22140
  const lines = [];
@@ -21602,9 +22148,9 @@ function detectProjectInfo() {
21602
22148
  } catch {
21603
22149
  }
21604
22150
  try {
21605
- const pkgPath = join23(cwd, "package.json");
21606
- if (existsSync15(pkgPath)) {
21607
- const pkg = JSON.parse(readFileSync15(pkgPath, "utf8"));
22151
+ const pkgPath = join24(cwd, "package.json");
22152
+ if (existsSync17(pkgPath)) {
22153
+ const pkg = JSON.parse(readFileSync16(pkgPath, "utf8"));
21608
22154
  const name = pkg.name || "unknown";
21609
22155
  const desc = pkg.description ? ` \u2014 ${pkg.description}`.slice(0, 120) : "";
21610
22156
  lines.push(`- Project: ${name}${desc}`);
@@ -21628,7 +22174,7 @@ function detectProjectInfo() {
21628
22174
  ["package.json", "JavaScript"]
21629
22175
  ];
21630
22176
  for (const [file2, lang] of indicators) {
21631
- if (existsSync15(join23(cwd, file2))) {
22177
+ if (existsSync17(join24(cwd, file2))) {
21632
22178
  lines.push(`- Language: ${lang} (${file2})`);
21633
22179
  break;
21634
22180
  }
@@ -21642,9 +22188,9 @@ function detectProjectInfo() {
21642
22188
  } catch {
21643
22189
  }
21644
22190
  try {
21645
- const readmePath = join23(cwd, "README.md");
21646
- if (existsSync15(readmePath)) {
21647
- let content = readFileSync15(readmePath, "utf8").slice(0, 500).replace(/\r?\n/g, " ").trim();
22191
+ const readmePath = join24(cwd, "README.md");
22192
+ if (existsSync17(readmePath)) {
22193
+ let content = readFileSync16(readmePath, "utf8").slice(0, 500).replace(/\r?\n/g, " ").trim();
21648
22194
  if (content.length > 300) content = content.slice(0, 297) + "...";
21649
22195
  if (content) lines.push(`- README: ${content}`);
21650
22196
  }
@@ -21753,6 +22299,30 @@ var init_mcp = __esm({
21753
22299
  }
21754
22300
  });
21755
22301
 
22302
+ // src/prompts/workiq.ts
22303
+ function getWorkIQSection() {
22304
+ if (cached7 === void 0) {
22305
+ cached7 = [
22306
+ "## Microsoft 365 work data (WorkIQ)",
22307
+ "",
22308
+ 'You can answer questions about the user\'s work context \u2014 their emails, meetings, calendar, documents, people, and projects \u2014 using the `workiq_ask` tool. Pass a natural-language question (e.g. "what meetings do I have tomorrow?", "summarize my unread email from Alice") and it returns an answer from the WorkIQ CLI.',
22309
+ "",
22310
+ "Guidance:",
22311
+ "- Prefer `workiq_ask` over web search or guessing when the user asks about *their own* work data (meetings, emails, documents, people, projects).",
22312
+ "- First use requires a one-time license acceptance. If `workiq_ask` returns `eula_required`, call `workiq_accept_eula` (the user will be asked to confirm) and then retry the original question.",
22313
+ "- `workiq_ask` signs the user in on first use via the system account; you do not need to handle authentication."
22314
+ ].join("\n");
22315
+ }
22316
+ return cached7;
22317
+ }
22318
+ var cached7;
22319
+ var init_workiq2 = __esm({
22320
+ "src/prompts/workiq.ts"() {
22321
+ "use strict";
22322
+ __name(getWorkIQSection, "getWorkIQSection");
22323
+ }
22324
+ });
22325
+
21756
22326
  // src/prompts/index.ts
21757
22327
  var prompts_exports = {};
21758
22328
  __export(prompts_exports, {
@@ -21781,6 +22351,7 @@ var init_prompts = __esm({
21781
22351
  init_skills();
21782
22352
  init_context();
21783
22353
  init_mcp();
22354
+ init_workiq2();
21784
22355
  init_sections();
21785
22356
  buildSections = /* @__PURE__ */ __name((opts = {}) => [
21786
22357
  // Static sections (cached across calls)
@@ -21788,6 +22359,7 @@ var init_prompts = __esm({
21788
22359
  systemPromptSection("reasoning", () => getReasoningSection()),
21789
22360
  systemPromptSection("safety", () => getSafetySection()),
21790
22361
  systemPromptSection("computerUse", () => getComputerUseSection()),
22362
+ systemPromptSection("workiq", () => getWorkIQSection()),
21791
22363
  // Dynamic boundary marker
21792
22364
  systemPromptSection("boundary", () => SYSTEM_PROMPT_DYNAMIC_BOUNDARY),
21793
22365
  // Dynamic sections (memoized within a resolve cycle via section cache)
@@ -21907,9 +22479,9 @@ __export(telegram_exports, {
21907
22479
  TelegramBridge: () => TelegramBridge
21908
22480
  });
21909
22481
  import TelegramBot from "node-telegram-bot-api";
21910
- import { writeFileSync as writeFileSync10, unlinkSync as unlinkSync3, existsSync as existsSync16 } from "node:fs";
21911
- import { join as join24 } from "node:path";
21912
- import { tmpdir as tmpdir4, homedir as homedir13 } from "node:os";
22482
+ import { writeFileSync as writeFileSync10, unlinkSync as unlinkSync4, existsSync as existsSync18 } from "node:fs";
22483
+ import { join as join25 } from "node:path";
22484
+ import { tmpdir as tmpdir4, homedir as homedir14 } from "node:os";
21913
22485
  import { randomUUID as randomUUID4 } from "node:crypto";
21914
22486
  var MAX_TELEGRAM_MSG, MIME_TYPES, TelegramBridge;
21915
22487
  var init_telegram = __esm({
@@ -22110,12 +22682,12 @@ var init_telegram = __esm({
22110
22682
  Options: ${chunk.choices.join(" | ")}` : "";
22111
22683
  await this.bot.sendMessage(chatId, `\u2753 ${question}${choicesText}`);
22112
22684
  updateStatus("\u2753 Waiting for your response...");
22113
- const userReply = await new Promise((resolve6) => {
22114
- this._pendingReplyResolver = resolve6;
22685
+ const userReply = await new Promise((resolve7) => {
22686
+ this._pendingReplyResolver = resolve7;
22115
22687
  setTimeout(() => {
22116
- if (this._pendingReplyResolver === resolve6) {
22688
+ if (this._pendingReplyResolver === resolve7) {
22117
22689
  this._pendingReplyResolver = null;
22118
- resolve6("[No response \u2014 timed out]");
22690
+ resolve7("[No response \u2014 timed out]");
22119
22691
  }
22120
22692
  }, 5 * 60 * 1e3);
22121
22693
  });
@@ -22152,7 +22724,7 @@ Options: ${chunk.choices.join(" | ")}` : "";
22152
22724
  try {
22153
22725
  const fileId = voice.file_id;
22154
22726
  const filePath = await this.bot.getFileLink(fileId);
22155
- const tempFile = join24(tmpdir4(), `oj-tg-voice-${randomUUID4().slice(0, 8)}.ogg`);
22727
+ const tempFile = join25(tmpdir4(), `oj-tg-voice-${randomUUID4().slice(0, 8)}.ogg`);
22156
22728
  const response = await fetch(filePath);
22157
22729
  const buffer = Buffer.from(await response.arrayBuffer());
22158
22730
  writeFileSync10(tempFile, buffer);
@@ -22168,7 +22740,7 @@ Options: ${chunk.choices.join(" | ")}` : "";
22168
22740
  transcription = "[Could not transcribe voice message. Please type your message instead.]";
22169
22741
  }
22170
22742
  try {
22171
- if (existsSync16(tempFile)) unlinkSync3(tempFile);
22743
+ if (existsSync18(tempFile)) unlinkSync4(tempFile);
22172
22744
  } catch {
22173
22745
  }
22174
22746
  if (transcription.startsWith("[")) {
@@ -22226,7 +22798,7 @@ Options: ${chunk.choices.join(" | ")}` : "";
22226
22798
  const response = await fetch(filePath);
22227
22799
  const buffer = Buffer.from(await response.arrayBuffer());
22228
22800
  const ext = fileName.includes(".") ? "." + fileName.split(".").pop() : "";
22229
- const tempPath = join24(tmpdir4(), `oj-tg-doc-${randomUUID4().slice(0, 8)}${ext}`);
22801
+ const tempPath = join25(tmpdir4(), `oj-tg-doc-${randomUUID4().slice(0, 8)}${ext}`);
22230
22802
  writeFileSync10(tempPath, buffer);
22231
22803
  const text = caption || `I've sent you a file: ${fileName}. It's saved at ${tempPath}. Please analyze it.`;
22232
22804
  const fullText = `${text}
@@ -22234,7 +22806,7 @@ Options: ${chunk.choices.join(" | ")}` : "";
22234
22806
  [File received: ${fileName} (${buffer.length} bytes), saved to: ${tempPath}]`;
22235
22807
  await this.handleText(chatId, fullText);
22236
22808
  try {
22237
- if (existsSync16(tempPath)) unlinkSync3(tempPath);
22809
+ if (existsSync18(tempPath)) unlinkSync4(tempPath);
22238
22810
  } catch {
22239
22811
  }
22240
22812
  } catch {
@@ -22260,12 +22832,12 @@ Options: ${chunk.choices.join(" | ")}` : "";
22260
22832
  let match;
22261
22833
  while ((match = pattern.exec(normalized)) !== null) {
22262
22834
  const filePath = match[1];
22263
- const resolvedPath = filePath.startsWith("~") ? join24(homedir13(), filePath.slice(1)) : filePath;
22835
+ const resolvedPath = filePath.startsWith("~") ? join25(homedir14(), filePath.slice(1)) : filePath;
22264
22836
  if (sentFiles.has(resolvedPath)) continue;
22265
22837
  if (filePath.includes("http") || filePath.includes("node_modules")) continue;
22266
22838
  const fileName = filePath.split(/[\\/]/).pop() || "";
22267
22839
  if (fileName.startsWith("oj-") || fileName.endsWith(".log") || fileName.endsWith(".tmp")) continue;
22268
- if (existsSync16(resolvedPath)) {
22840
+ if (existsSync18(resolvedPath)) {
22269
22841
  try {
22270
22842
  const { statSync: statSync5 } = await import("node:fs");
22271
22843
  const stat2 = statSync5(resolvedPath);
@@ -22429,26 +23001,27 @@ var init_meta = __esm({
22429
23001
  // src/mcp-client.ts
22430
23002
  var mcp_client_exports = {};
22431
23003
  __export(mcp_client_exports, {
22432
- MCPClientManager: () => MCPClientManager
23004
+ MCPClientManager: () => MCPClientManager,
23005
+ restoreStdinAfterReadlinePrompt: () => restoreStdinAfterReadlinePrompt
22433
23006
  });
22434
23007
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
22435
23008
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
22436
23009
  import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
22437
23010
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
22438
- import { existsSync as existsSync17, readFileSync as readFileSync17, readdirSync as readdirSync4, writeFileSync as writeFileSync11, mkdirSync as mkdirSync11 } from "node:fs";
22439
- import { join as join25, dirname as dirname5, resolve } from "node:path";
22440
- import { homedir as homedir14 } from "node:os";
23011
+ import { existsSync as existsSync19, readFileSync as readFileSync18, readdirSync as readdirSync4, writeFileSync as writeFileSync11, mkdirSync as mkdirSync11 } from "node:fs";
23012
+ import { join as join26, dirname as dirname7, resolve as resolve2 } from "node:path";
23013
+ import { homedir as homedir15 } from "node:os";
22441
23014
  import { execSync as execSync5 } from "node:child_process";
22442
23015
  import { EventEmitter } from "node:events";
22443
23016
  import * as readline from "node:readline";
22444
23017
  function findCopilotOAuthToken(serverUrl) {
22445
- if (!existsSync17(COPILOT_OAUTH_DIR)) return null;
23018
+ if (!existsSync19(COPILOT_OAUTH_DIR)) return null;
22446
23019
  try {
22447
23020
  const files = readdirSync4(COPILOT_OAUTH_DIR).filter((f) => f.endsWith(".tokens.json"));
22448
23021
  for (const file2 of files) {
22449
- const filePath = join25(COPILOT_OAUTH_DIR, file2);
23022
+ const filePath = join26(COPILOT_OAUTH_DIR, file2);
22450
23023
  try {
22451
- const data = JSON.parse(readFileSync17(filePath, "utf-8"));
23024
+ const data = JSON.parse(readFileSync18(filePath, "utf-8"));
22452
23025
  if (data.scope && data.accessToken) {
22453
23026
  const urlDomain = new URL(serverUrl).hostname;
22454
23027
  if (data.scope.includes(urlDomain)) {
@@ -22464,18 +23037,18 @@ function findCopilotOAuthToken(serverUrl) {
22464
23037
  }
22465
23038
  function refreshCopilotToken(refreshToken, tokenFilePath) {
22466
23039
  try {
22467
- const existing = JSON.parse(readFileSync17(tokenFilePath, "utf-8"));
23040
+ const existing = JSON.parse(readFileSync18(tokenFilePath, "utf-8"));
22468
23041
  const jwtParts = existing.accessToken.split(".");
22469
23042
  if (jwtParts.length < 2) return null;
22470
23043
  const padded = jwtParts[1] + "=".repeat(4 - jwtParts[1].length % 4);
22471
23044
  const claims = JSON.parse(Buffer.from(padded, "base64").toString("utf-8"));
22472
- const clientId = claims.azp;
23045
+ const clientId2 = claims.azp;
22473
23046
  const tid = claims.tid;
22474
23047
  const aud = claims.aud;
22475
- if (!clientId || !tid || !aud) return null;
23048
+ if (!clientId2 || !tid || !aud) return null;
22476
23049
  const scope = `${aud}/.default offline_access`;
22477
23050
  const bodyParams = [
22478
- `client_id=${clientId}`,
23051
+ `client_id=${clientId2}`,
22479
23052
  `grant_type=refresh_token`,
22480
23053
  `refresh_token=${encodeURIComponent(refreshToken)}`,
22481
23054
  `scope=${encodeURIComponent(scope)}`
@@ -22535,7 +23108,7 @@ function discoverAllConfigs(cwd) {
22535
23108
  for (const { name: pluginName, servers } of pluginConfigs) {
22536
23109
  addServers(servers, `~/.copilot/.../plugins/${pluginName}/.mcp.json`, "plugin", false);
22537
23110
  }
22538
- const vscodeConfig = readVSCodeMcpConfig(join25(cwd, ".vscode", "mcp.json"), cwd);
23111
+ const vscodeConfig = readVSCodeMcpConfig(join26(cwd, ".vscode", "mcp.json"), cwd);
22539
23112
  if (vscodeConfig) {
22540
23113
  addServers(vscodeConfig, ".vscode/mcp.json", "vscode", false);
22541
23114
  }
@@ -22546,18 +23119,18 @@ function discoverAllConfigs(cwd) {
22546
23119
  return result;
22547
23120
  }
22548
23121
  function readMcpConfig(filePath) {
22549
- if (!existsSync17(filePath)) return null;
23122
+ if (!existsSync19(filePath)) return null;
22550
23123
  try {
22551
- const raw = JSON.parse(readFileSync17(filePath, "utf-8"));
23124
+ const raw = JSON.parse(readFileSync18(filePath, "utf-8"));
22552
23125
  return raw.mcpServers || null;
22553
23126
  } catch {
22554
23127
  return null;
22555
23128
  }
22556
23129
  }
22557
23130
  function readVSCodeMcpConfig(filePath, cwd) {
22558
- if (!existsSync17(filePath)) return null;
23131
+ if (!existsSync19(filePath)) return null;
22559
23132
  try {
22560
- const raw = JSON.parse(readFileSync17(filePath, "utf-8"));
23133
+ const raw = JSON.parse(readFileSync18(filePath, "utf-8"));
22561
23134
  const servers = raw.servers;
22562
23135
  if (!servers || typeof servers !== "object") return null;
22563
23136
  const result = {};
@@ -22578,10 +23151,10 @@ function readVSCodeMcpConfig(filePath, cwd) {
22578
23151
  }
22579
23152
  function walkParentsForMcpJson(startDir) {
22580
23153
  const results = [];
22581
- let dir2 = resolve(startDir);
22582
- const root = dirname5(dir2) === dir2 ? dir2 : "";
23154
+ let dir2 = resolve2(startDir);
23155
+ const root = dirname7(dir2) === dir2 ? dir2 : "";
22583
23156
  while (dir2 && dir2 !== root) {
22584
- const filePath = join25(dir2, ".mcp.json");
23157
+ const filePath = join26(dir2, ".mcp.json");
22585
23158
  const config = readMcpConfig(filePath);
22586
23159
  if (config) {
22587
23160
  const filtered = {};
@@ -22592,20 +23165,20 @@ function walkParentsForMcpJson(startDir) {
22592
23165
  results.push({ path: filePath, servers: filtered });
22593
23166
  }
22594
23167
  }
22595
- const parent = dirname5(dir2);
23168
+ const parent = dirname7(dir2);
22596
23169
  if (parent === dir2) break;
22597
23170
  dir2 = parent;
22598
23171
  }
22599
23172
  return results;
22600
23173
  }
22601
23174
  function discoverCopilotPlugins() {
22602
- if (!existsSync17(COPILOT_PLUGINS_DIR)) return [];
23175
+ if (!existsSync19(COPILOT_PLUGINS_DIR)) return [];
22603
23176
  const results = [];
22604
23177
  try {
22605
23178
  const entries = readdirSync4(COPILOT_PLUGINS_DIR, { withFileTypes: true });
22606
23179
  for (const entry of entries) {
22607
23180
  if (!entry.isDirectory()) continue;
22608
- const mcpPath = join25(COPILOT_PLUGINS_DIR, entry.name, ".mcp.json");
23181
+ const mcpPath = join26(COPILOT_PLUGINS_DIR, entry.name, ".mcp.json");
22609
23182
  const config = readMcpConfig(mcpPath);
22610
23183
  if (config) {
22611
23184
  const filtered = {};
@@ -22622,9 +23195,9 @@ function discoverCopilotPlugins() {
22622
23195
  return results;
22623
23196
  }
22624
23197
  function discoverAgencyBuiltins() {
22625
- if (!existsSync17(AGENCY_CONFIG) || !existsSync17(AGENCY_EXE)) return {};
23198
+ if (!existsSync19(AGENCY_CONFIG) || !existsSync19(AGENCY_EXE)) return {};
22626
23199
  try {
22627
- const content = readFileSync17(AGENCY_CONFIG, "utf-8");
23200
+ const content = readFileSync18(AGENCY_CONFIG, "utf-8");
22628
23201
  const result = {};
22629
23202
  const builtinsMatch = content.match(/\[mcps\.builtins\]([\s\S]*?)(?=\n\[|$)/);
22630
23203
  if (!builtinsMatch) return {};
@@ -22692,17 +23265,17 @@ function truncateDescription(desc, maxLen) {
22692
23265
  return desc.slice(0, maxLen - 3) + "...";
22693
23266
  }
22694
23267
  function loadConsent() {
22695
- if (!existsSync17(OJ_CONSENT_FILE)) {
23268
+ if (!existsSync19(OJ_CONSENT_FILE)) {
22696
23269
  return { approved: [], denied: [] };
22697
23270
  }
22698
23271
  try {
22699
- return JSON.parse(readFileSync17(OJ_CONSENT_FILE, "utf-8"));
23272
+ return JSON.parse(readFileSync18(OJ_CONSENT_FILE, "utf-8"));
22700
23273
  } catch {
22701
23274
  return { approved: [], denied: [] };
22702
23275
  }
22703
23276
  }
22704
23277
  function saveConsent(data) {
22705
- if (!existsSync17(OJ_AGENT_DIR)) {
23278
+ if (!existsSync19(OJ_AGENT_DIR)) {
22706
23279
  mkdirSync11(OJ_AGENT_DIR, { recursive: true });
22707
23280
  }
22708
23281
  writeFileSync11(OJ_CONSENT_FILE, JSON.stringify(data, null, 2) + "\n", "utf-8");
@@ -22713,66 +23286,82 @@ async function promptConsent(servers) {
22713
23286
  input: process.stdin,
22714
23287
  output: process.stderr
22715
23288
  });
22716
- const ask = /* @__PURE__ */ __name((question) => new Promise((resolve6) => rl.question(question, resolve6)), "ask");
22717
- const w = process.stderr.columns || 80;
22718
- const inner = w - 4;
22719
- const hLine = "\u2500".repeat(inner);
22720
- const pad = /* @__PURE__ */ __name((s, len) => s + " ".repeat(Math.max(0, len - stripAnsi(s).length)), "pad");
22721
- console.error("");
22722
- console.error(` \x1B[36m\u256D${hLine}\u256E\x1B[0m`);
22723
- console.error(` \x1B[36m\u2502\x1B[0m ${pad("\x1B[36m\x1B[1m\u{1F50C} New MCP Servers Discovered\x1B[0m", inner - 1)}\x1B[36m\u2502\x1B[0m`);
22724
- console.error(` \x1B[36m\u251C${hLine}\u2524\x1B[0m`);
22725
- const bySource = /* @__PURE__ */ new Map();
22726
- for (const s of servers) {
22727
- const list = bySource.get(s.source) || [];
22728
- list.push(s);
22729
- bySource.set(s.source, list);
22730
- }
22731
- for (const [source, sourceServers] of bySource) {
22732
- const srcLine = `\x1B[2m From ${source}\x1B[0m`;
22733
- console.error(` \x1B[36m\u2502\x1B[0m ${pad(srcLine, inner - 1)}\x1B[36m\u2502\x1B[0m`);
22734
- for (const s of sourceServers) {
22735
- const cmdParts = [s.def.command, ...(s.def.args || []).slice(0, 2)];
22736
- const cmdStr = cmdParts.join(" ");
22737
- const truncCmd = cmdStr.length > inner - s.name.length - 12 ? cmdStr.slice(0, inner - s.name.length - 15) + "..." : cmdStr;
22738
- const serverLine = ` \x1B[33m\u25CB\x1B[0m \x1B[1m${s.name}\x1B[0m \x1B[2m${truncCmd}\x1B[0m`;
22739
- console.error(` \x1B[36m\u2502\x1B[0m ${pad(serverLine, inner - 1)}\x1B[36m\u2502\x1B[0m`);
22740
- }
22741
- console.error(` \x1B[36m\u2502\x1B[0m ${" ".repeat(inner - 1)}\x1B[36m\u2502\x1B[0m`);
22742
- }
22743
- console.error(` \x1B[36m\u251C${hLine}\u2524\x1B[0m`);
22744
- console.error(` \x1B[36m\u2502\x1B[0m ${pad("\x1B[2m Y = enable all \xB7 N = skip all \xB7 S = select individually\x1B[0m", inner - 1)}\x1B[36m\u2502\x1B[0m`);
22745
- console.error(` \x1B[36m\u2570${hLine}\u256F\x1B[0m`);
22746
- console.error("");
22747
- const answer = await ask(" \x1B[36m\u276F\x1B[0m Enable these servers? \x1B[2m(Y/n/s)\x1B[0m: ");
22748
- const trimmed = answer.trim().toLowerCase();
22749
- let approved;
22750
- if (trimmed === "" || trimmed === "y" || trimmed === "yes") {
22751
- approved = servers.map((s) => s.name);
22752
- console.error(` \x1B[32m\u25CF All ${approved.length} servers enabled\x1B[0m`);
22753
- } else if (trimmed === "n" || trimmed === "no") {
22754
- approved = [];
22755
- console.error(` \x1B[33m\u25CB All servers skipped\x1B[0m`);
22756
- } else if (trimmed === "s" || trimmed === "select") {
22757
- approved = [];
23289
+ try {
23290
+ const ask = /* @__PURE__ */ __name((question) => new Promise((resolve7) => rl.question(question, resolve7)), "ask");
23291
+ const w = process.stderr.columns || 80;
23292
+ const inner = w - 4;
23293
+ const hLine = "\u2500".repeat(inner);
23294
+ const pad = /* @__PURE__ */ __name((s, len) => s + " ".repeat(Math.max(0, len - stripAnsi(s).length)), "pad");
22758
23295
  console.error("");
23296
+ console.error(` \x1B[36m\u256D${hLine}\u256E\x1B[0m`);
23297
+ console.error(` \x1B[36m\u2502\x1B[0m ${pad("\x1B[36m\x1B[1m\u{1F50C} New MCP Servers Discovered\x1B[0m", inner - 1)}\x1B[36m\u2502\x1B[0m`);
23298
+ console.error(` \x1B[36m\u251C${hLine}\u2524\x1B[0m`);
23299
+ const bySource = /* @__PURE__ */ new Map();
22759
23300
  for (const s of servers) {
22760
- const a = await ask(` \x1B[36m\u276F\x1B[0m Enable \x1B[33m\x1B[1m${s.name}\x1B[0m? \x1B[2m(y/N)\x1B[0m: `);
22761
- if (a.trim().toLowerCase() === "y" || a.trim().toLowerCase() === "yes") {
22762
- approved.push(s.name);
22763
- console.error(` \x1B[32m\u25CF ${s.name} enabled\x1B[0m`);
22764
- } else {
22765
- console.error(` \x1B[2m\u25CB ${s.name} skipped\x1B[0m`);
23301
+ const list = bySource.get(s.source) || [];
23302
+ list.push(s);
23303
+ bySource.set(s.source, list);
23304
+ }
23305
+ for (const [source, sourceServers] of bySource) {
23306
+ const srcLine = `\x1B[2m From ${source}\x1B[0m`;
23307
+ console.error(` \x1B[36m\u2502\x1B[0m ${pad(srcLine, inner - 1)}\x1B[36m\u2502\x1B[0m`);
23308
+ for (const s of sourceServers) {
23309
+ const cmdParts = [s.def.command, ...(s.def.args || []).slice(0, 2)];
23310
+ const cmdStr = cmdParts.join(" ");
23311
+ const truncCmd = cmdStr.length > inner - s.name.length - 12 ? cmdStr.slice(0, inner - s.name.length - 15) + "..." : cmdStr;
23312
+ const serverLine = ` \x1B[33m\u25CB\x1B[0m \x1B[1m${s.name}\x1B[0m \x1B[2m${truncCmd}\x1B[0m`;
23313
+ console.error(` \x1B[36m\u2502\x1B[0m ${pad(serverLine, inner - 1)}\x1B[36m\u2502\x1B[0m`);
23314
+ }
23315
+ console.error(` \x1B[36m\u2502\x1B[0m ${" ".repeat(inner - 1)}\x1B[36m\u2502\x1B[0m`);
23316
+ }
23317
+ console.error(` \x1B[36m\u251C${hLine}\u2524\x1B[0m`);
23318
+ console.error(` \x1B[36m\u2502\x1B[0m ${pad("\x1B[2m Y = enable all \xB7 N = skip all \xB7 S = select individually\x1B[0m", inner - 1)}\x1B[36m\u2502\x1B[0m`);
23319
+ console.error(` \x1B[36m\u2570${hLine}\u256F\x1B[0m`);
23320
+ console.error("");
23321
+ const answer = await ask(" \x1B[36m\u276F\x1B[0m Enable these servers? \x1B[2m(Y/n/s)\x1B[0m: ");
23322
+ const trimmed = answer.trim().toLowerCase();
23323
+ let approved;
23324
+ if (trimmed === "" || trimmed === "y" || trimmed === "yes") {
23325
+ approved = servers.map((s) => s.name);
23326
+ console.error(` \x1B[32m\u25CF All ${approved.length} servers enabled\x1B[0m`);
23327
+ } else if (trimmed === "n" || trimmed === "no") {
23328
+ approved = [];
23329
+ console.error(` \x1B[33m\u25CB All servers skipped\x1B[0m`);
23330
+ } else if (trimmed === "s" || trimmed === "select") {
23331
+ approved = [];
23332
+ console.error("");
23333
+ for (const s of servers) {
23334
+ const a = await ask(` \x1B[36m\u276F\x1B[0m Enable \x1B[33m\x1B[1m${s.name}\x1B[0m? \x1B[2m(y/N)\x1B[0m: `);
23335
+ if (a.trim().toLowerCase() === "y" || a.trim().toLowerCase() === "yes") {
23336
+ approved.push(s.name);
23337
+ console.error(` \x1B[32m\u25CF ${s.name} enabled\x1B[0m`);
23338
+ } else {
23339
+ console.error(` \x1B[2m\u25CB ${s.name} skipped\x1B[0m`);
23340
+ }
22766
23341
  }
22767
- }
22768
- console.error(`
23342
+ console.error(`
22769
23343
  \x1B[32m${approved.length}/${servers.length} servers enabled\x1B[0m`);
22770
- } else {
22771
- approved = servers.map((s) => s.name);
23344
+ } else {
23345
+ approved = servers.map((s) => s.name);
23346
+ }
23347
+ console.error("");
23348
+ return approved;
23349
+ } finally {
23350
+ rl.close();
23351
+ restoreStdinAfterReadlinePrompt();
23352
+ }
23353
+ }
23354
+ function restoreStdinAfterReadlinePrompt(input = process.stdin) {
23355
+ try {
23356
+ if (input.isTTY && input.setRawMode) {
23357
+ input.setRawMode(false);
23358
+ }
23359
+ } catch {
23360
+ }
23361
+ try {
23362
+ input.pause();
23363
+ } catch {
22772
23364
  }
22773
- console.error("");
22774
- rl.close();
22775
- return approved;
22776
23365
  }
22777
23366
  function stripAnsi(str) {
22778
23367
  return str.replace(/\x1b\[[0-9;]*m/g, "");
@@ -22781,14 +23370,14 @@ var OJ_AGENT_DIR, OJ_MCP_CONFIG, OJ_CONSENT_FILE, COPILOT_MCP_CONFIG, COPILOT_PL
22781
23370
  var init_mcp_client = __esm({
22782
23371
  "src/mcp-client.ts"() {
22783
23372
  "use strict";
22784
- OJ_AGENT_DIR = join25(homedir14(), ".openjaw-agent");
22785
- OJ_MCP_CONFIG = join25(OJ_AGENT_DIR, "mcp.json");
22786
- OJ_CONSENT_FILE = join25(OJ_AGENT_DIR, "mcp-consent.json");
22787
- COPILOT_MCP_CONFIG = join25(homedir14(), ".copilot", "mcp-config.json");
22788
- COPILOT_PLUGINS_DIR = join25(homedir14(), ".copilot", "installed-plugins", "copilot-plugins");
22789
- COPILOT_OAUTH_DIR = join25(homedir14(), ".copilot", "mcp-oauth-config");
22790
- AGENCY_CONFIG = join25(homedir14(), ".agency", "agency.toml");
22791
- AGENCY_EXE = join25(process.env.APPDATA || join25(homedir14(), "AppData", "Roaming"), "agency", "CurrentVersion", "agency.exe");
23373
+ OJ_AGENT_DIR = join26(homedir15(), ".openjaw-agent");
23374
+ OJ_MCP_CONFIG = join26(OJ_AGENT_DIR, "mcp.json");
23375
+ OJ_CONSENT_FILE = join26(OJ_AGENT_DIR, "mcp-consent.json");
23376
+ COPILOT_MCP_CONFIG = join26(homedir15(), ".copilot", "mcp-config.json");
23377
+ COPILOT_PLUGINS_DIR = join26(homedir15(), ".copilot", "installed-plugins", "copilot-plugins");
23378
+ COPILOT_OAUTH_DIR = join26(homedir15(), ".copilot", "mcp-oauth-config");
23379
+ AGENCY_CONFIG = join26(homedir15(), ".agency", "agency.toml");
23380
+ AGENCY_EXE = join26(process.env.APPDATA || join26(homedir15(), "AppData", "Roaming"), "agency", "CurrentVersion", "agency.exe");
22792
23381
  SELF_PATTERNS = ["openjaw-mcp/dist/index.js", "openjaw-mcp\\dist\\index.js", "openjaw-agent"];
22793
23382
  __name(findCopilotOAuthToken, "findCopilotOAuthToken");
22794
23383
  __name(refreshCopilotToken, "refreshCopilotToken");
@@ -22988,11 +23577,11 @@ var init_mcp_client = __esm({
22988
23577
  * Add a new MCP server at runtime. Saves to ~/.openjaw-agent/mcp.json and connects.
22989
23578
  */
22990
23579
  async addServer(name, def) {
22991
- if (!existsSync17(OJ_AGENT_DIR)) mkdirSync11(OJ_AGENT_DIR, { recursive: true });
23580
+ if (!existsSync19(OJ_AGENT_DIR)) mkdirSync11(OJ_AGENT_DIR, { recursive: true });
22992
23581
  let config = {};
22993
- if (existsSync17(OJ_MCP_CONFIG)) {
23582
+ if (existsSync19(OJ_MCP_CONFIG)) {
22994
23583
  try {
22995
- const raw = JSON.parse(readFileSync17(OJ_MCP_CONFIG, "utf-8"));
23584
+ const raw = JSON.parse(readFileSync18(OJ_MCP_CONFIG, "utf-8"));
22996
23585
  config = raw.mcpServers || {};
22997
23586
  } catch {
22998
23587
  }
@@ -23100,6 +23689,7 @@ var init_mcp_client = __esm({
23100
23689
  const headers = {};
23101
23690
  if (def.env?.AUTHORIZATION) headers["Authorization"] = def.env.AUTHORIZATION;
23102
23691
  if (def.env?.AUTH_TOKEN) headers["Authorization"] = `Bearer ${def.env.AUTH_TOKEN}`;
23692
+ if (def.headers) Object.assign(headers, def.headers);
23103
23693
  transport = new SSEClientTransport(new URL(def.url), {
23104
23694
  requestInit: Object.keys(headers).length > 0 ? { headers } : void 0
23105
23695
  });
@@ -23120,10 +23710,10 @@ var init_mcp_client = __esm({
23120
23710
  let tokenExpiry = 0;
23121
23711
  if (copilotTokenFile) {
23122
23712
  try {
23123
- const cached7 = JSON.parse(readFileSync17(copilotTokenFile, "utf-8"));
23124
- cachedToken = cached7.accessToken;
23125
- cachedRefreshToken = cached7.refreshToken;
23126
- tokenExpiry = cached7.expiresAt || 0;
23713
+ const cached8 = JSON.parse(readFileSync18(copilotTokenFile, "utf-8"));
23714
+ cachedToken = cached8.accessToken;
23715
+ cachedRefreshToken = cached8.refreshToken;
23716
+ tokenExpiry = cached8.expiresAt || 0;
23127
23717
  } catch {
23128
23718
  }
23129
23719
  }
@@ -23163,11 +23753,15 @@ var init_mcp_client = __esm({
23163
23753
  return globalThis.fetch(url, { ...init, headers });
23164
23754
  }, "customFetch");
23165
23755
  }
23756
+ const requestHeaders = {};
23757
+ if (staticAuth) requestHeaders["Authorization"] = staticAuth;
23758
+ if (def.headers) Object.assign(requestHeaders, def.headers);
23166
23759
  const transportOpts = {};
23167
23760
  if (customFetch) {
23168
23761
  transportOpts.fetch = customFetch;
23169
- } else if (staticAuth) {
23170
- transportOpts.requestInit = { headers: { Authorization: staticAuth } };
23762
+ }
23763
+ if (Object.keys(requestHeaders).length > 0) {
23764
+ transportOpts.requestInit = { headers: requestHeaders };
23171
23765
  }
23172
23766
  transport = new StreamableHTTPClientTransport(new URL(def.url), transportOpts);
23173
23767
  } else {
@@ -23228,6 +23822,7 @@ var init_mcp_client = __esm({
23228
23822
  __name(loadConsent, "loadConsent");
23229
23823
  __name(saveConsent, "saveConsent");
23230
23824
  __name(promptConsent, "promptConsent");
23825
+ __name(restoreStdinAfterReadlinePrompt, "restoreStdinAfterReadlinePrompt");
23231
23826
  __name(stripAnsi, "stripAnsi");
23232
23827
  }
23233
23828
  });
@@ -23558,13 +24153,13 @@ __export(scheduler_exports, {
23558
24153
  stopAllTasks: () => stopAllTasks,
23559
24154
  stopTask: () => stopTask
23560
24155
  });
23561
- import { readFileSync as readFileSync18, writeFileSync as writeFileSync12, existsSync as existsSync18, mkdirSync as mkdirSync12 } from "node:fs";
23562
- import { join as join26 } from "node:path";
23563
- import { homedir as homedir15 } from "node:os";
24156
+ import { readFileSync as readFileSync19, writeFileSync as writeFileSync12, existsSync as existsSync20, mkdirSync as mkdirSync12 } from "node:fs";
24157
+ import { join as join27 } from "node:path";
24158
+ import { homedir as homedir16 } from "node:os";
23564
24159
  function getJobsPath() {
23565
- const dir2 = join26(homedir15(), ".openjaw-agent");
23566
- if (!existsSync18(dir2)) mkdirSync12(dir2, { recursive: true });
23567
- return join26(dir2, "cron-jobs.json");
24160
+ const dir2 = join27(homedir16(), ".openjaw-agent");
24161
+ if (!existsSync20(dir2)) mkdirSync12(dir2, { recursive: true });
24162
+ return join27(dir2, "cron-jobs.json");
23568
24163
  }
23569
24164
  function parseInterval(input) {
23570
24165
  const match = input.match(/^(\d+)\s*(s|sec|seconds?|m|min|minutes?|h|hr|hours?|d|day|days?)$/i);
@@ -23701,9 +24296,9 @@ function saveJobs() {
23701
24296
  }
23702
24297
  function loadJobs() {
23703
24298
  const path3 = getJobsPath();
23704
- if (!existsSync18(path3)) return [];
24299
+ if (!existsSync20(path3)) return [];
23705
24300
  try {
23706
- const raw = readFileSync18(path3, "utf8");
24301
+ const raw = readFileSync19(path3, "utf8");
23707
24302
  return JSON.parse(raw);
23708
24303
  } catch {
23709
24304
  return [];
@@ -23935,10 +24530,10 @@ __export(repl_exports, {
23935
24530
  });
23936
24531
  import * as readline2 from "readline";
23937
24532
  import { readFile as readFile4 } from "node:fs/promises";
23938
- import { existsSync as existsSync19 } from "node:fs";
23939
- import { join as join27, dirname as dirname6 } from "node:path";
23940
- import { homedir as homedir16 } from "node:os";
23941
- import { fileURLToPath as fileURLToPath3 } from "node:url";
24533
+ import { existsSync as existsSync21 } from "node:fs";
24534
+ import { join as join28, dirname as dirname8 } from "node:path";
24535
+ import { homedir as homedir17 } from "node:os";
24536
+ import { fileURLToPath as fileURLToPath5 } from "node:url";
23942
24537
  async function selectPrompt(message, choices, defaultIdx) {
23943
24538
  const { default: select } = await import("@inquirer/select");
23944
24539
  return select({ message, choices, default: defaultIdx });
@@ -23954,8 +24549,8 @@ async function loadPrompts(promptsDir) {
23954
24549
  const files = ["IDENTITY.md", "REASONING.md", "COMPUTER_USE.md", "SAFETY.md", "USER.md"];
23955
24550
  const parts = [];
23956
24551
  for (const file2 of files) {
23957
- const path3 = join27(promptsDir, file2);
23958
- if (existsSync19(path3)) {
24552
+ const path3 = join28(promptsDir, file2);
24553
+ if (existsSync21(path3)) {
23959
24554
  const content = await readFile4(path3, "utf8");
23960
24555
  parts.push(content);
23961
24556
  }
@@ -23971,11 +24566,11 @@ async function loadPrompts(promptsDir) {
23971
24566
  }
23972
24567
  async function loadMemorySummary() {
23973
24568
  const memoryPaths = [
23974
- join27(homedir16(), ".openjaw", "MEMORY.md"),
23975
- join27(homedir16(), ".openjaw", "memory", "MEMORY.md")
24569
+ join28(homedir17(), ".openjaw", "MEMORY.md"),
24570
+ join28(homedir17(), ".openjaw", "memory", "MEMORY.md")
23976
24571
  ];
23977
24572
  for (const memoryPath of memoryPaths) {
23978
- if (existsSync19(memoryPath)) {
24573
+ if (existsSync21(memoryPath)) {
23979
24574
  const content = await readFile4(memoryPath, "utf8");
23980
24575
  return content.slice(0, 2e3);
23981
24576
  }
@@ -23983,8 +24578,8 @@ async function loadMemorySummary() {
23983
24578
  return "";
23984
24579
  }
23985
24580
  async function startREPL(resumeSessionId) {
23986
- const promptsDir = join27(__dirname2, "..", "prompts");
23987
- const packagePromptsDir2 = existsSync19(promptsDir) ? promptsDir : join27(homedir16(), ".openjaw-agent", "prompts");
24581
+ const promptsDir = join28(__dirname2, "..", "prompts");
24582
+ const packagePromptsDir2 = existsSync21(promptsDir) ? promptsDir : join28(homedir17(), ".openjaw-agent", "prompts");
23988
24583
  await ensureDirectories();
23989
24584
  const config = await loadConfig();
23990
24585
  const systemPrompt = await loadPrompts(packagePromptsDir2);
@@ -24548,8 +25143,8 @@ var init_repl = __esm({
24548
25143
  init_connect();
24549
25144
  init_scheduler();
24550
25145
  __name(selectPrompt, "selectPrompt");
24551
- __filename2 = fileURLToPath3(import.meta.url);
24552
- __dirname2 = dirname6(__filename2);
25146
+ __filename2 = fileURLToPath5(import.meta.url);
25147
+ __dirname2 = dirname8(__filename2);
24553
25148
  C = {
24554
25149
  reset: "\x1B[0m",
24555
25150
  dim: "\x1B[2m",
@@ -24574,9 +25169,9 @@ var init_repl = __esm({
24574
25169
  });
24575
25170
 
24576
25171
  // src/voice/tts.ts
24577
- import { execSync as execSync6, spawn as spawn2 } from "node:child_process";
24578
- import { existsSync as existsSync20, unlinkSync as unlinkSync4, mkdirSync as mkdirSync14 } from "node:fs";
24579
- import { join as join28 } from "node:path";
25172
+ import { execSync as execSync6, spawn as spawn4 } from "node:child_process";
25173
+ import { existsSync as existsSync22, unlinkSync as unlinkSync5, mkdirSync as mkdirSync14 } from "node:fs";
25174
+ import { join as join29 } from "node:path";
24580
25175
  import { tmpdir as tmpdir5 } from "node:os";
24581
25176
  import { randomUUID as randomUUID5 } from "node:crypto";
24582
25177
  function hasEdgeTts() {
@@ -24630,8 +25225,8 @@ async function listVoices() {
24630
25225
  }
24631
25226
  }
24632
25227
  async function speakEdgeTts(text, voice, options) {
24633
- if (!existsSync20(TTS_TEMP_DIR)) mkdirSync14(TTS_TEMP_DIR, { recursive: true });
24634
- const audioFile = join28(TTS_TEMP_DIR, `tts-${randomUUID5().slice(0, 8)}.mp3`);
25228
+ if (!existsSync22(TTS_TEMP_DIR)) mkdirSync14(TTS_TEMP_DIR, { recursive: true });
25229
+ const audioFile = join29(TTS_TEMP_DIR, `tts-${randomUUID5().slice(0, 8)}.mp3`);
24635
25230
  let playerProc = null;
24636
25231
  let stopped = false;
24637
25232
  try {
@@ -24643,9 +25238,9 @@ async function speakEdgeTts(text, voice, options) {
24643
25238
  timeout: 15e3,
24644
25239
  stdio: ["pipe", "pipe", "pipe"]
24645
25240
  });
24646
- if (stopped || !existsSync20(audioFile)) return { stop: /* @__PURE__ */ __name(() => {
25241
+ if (stopped || !existsSync22(audioFile)) return { stop: /* @__PURE__ */ __name(() => {
24647
25242
  }, "stop") };
24648
- playerProc = spawn2("powershell.exe", [
25243
+ playerProc = spawn4("powershell.exe", [
24649
25244
  "-NoProfile",
24650
25245
  "-Command",
24651
25246
  `Add-Type -AssemblyName PresentationCore; $p = New-Object System.Windows.Media.MediaPlayer; $p.Open([Uri]"${audioFile}"); $p.Play(); Start-Sleep -Milliseconds 500; while($p.Position -lt $p.NaturalDuration.TimeSpan -and $p.HasAudio){ Start-Sleep -Milliseconds 200 }; $p.Close()`
@@ -24655,14 +25250,14 @@ async function speakEdgeTts(text, voice, options) {
24655
25250
  });
24656
25251
  playerProc.on("exit", () => {
24657
25252
  try {
24658
- if (existsSync20(audioFile)) unlinkSync4(audioFile);
25253
+ if (existsSync22(audioFile)) unlinkSync5(audioFile);
24659
25254
  } catch {
24660
25255
  }
24661
25256
  playerProc = null;
24662
25257
  });
24663
25258
  } catch {
24664
25259
  try {
24665
- if (existsSync20(audioFile)) unlinkSync4(audioFile);
25260
+ if (existsSync22(audioFile)) unlinkSync5(audioFile);
24666
25261
  } catch {
24667
25262
  }
24668
25263
  }
@@ -24674,7 +25269,7 @@ async function speakEdgeTts(text, voice, options) {
24674
25269
  playerProc = null;
24675
25270
  }
24676
25271
  try {
24677
- if (existsSync20(audioFile)) unlinkSync4(audioFile);
25272
+ if (existsSync22(audioFile)) unlinkSync5(audioFile);
24678
25273
  } catch {
24679
25274
  }
24680
25275
  }, "stop")
@@ -24691,7 +25286,7 @@ $synth.Speak('${safeText}')
24691
25286
  $synth.Dispose()
24692
25287
  `;
24693
25288
  const encoded = Buffer.from(script, "utf16le").toString("base64");
24694
- const proc = spawn2("powershell.exe", ["-NoProfile", "-EncodedCommand", encoded], {
25289
+ const proc = spawn4("powershell.exe", ["-NoProfile", "-EncodedCommand", encoded], {
24695
25290
  stdio: ["pipe", "pipe", "pipe"],
24696
25291
  windowsHide: true
24697
25292
  });
@@ -24713,7 +25308,7 @@ var init_tts = __esm({
24713
25308
  "use strict";
24714
25309
  DEFAULT_VOICE_EN = "en-US-AriaNeural";
24715
25310
  DEFAULT_VOICE_ZH = "zh-CN-XiaoxiaoNeural";
24716
- TTS_TEMP_DIR = join28(tmpdir5(), "oj-tts");
25311
+ TTS_TEMP_DIR = join29(tmpdir5(), "oj-tts");
24717
25312
  edgeTtsAvailable = null;
24718
25313
  __name(hasEdgeTts, "hasEdgeTts");
24719
25314
  __name(ensureEdgeTts, "ensureEdgeTts");
@@ -24727,13 +25322,13 @@ var init_tts = __esm({
24727
25322
  });
24728
25323
 
24729
25324
  // src/voice/stt.ts
24730
- import { spawn as spawn3, execSync as execSync7 } from "node:child_process";
24731
- import { join as join29 } from "node:path";
25325
+ import { spawn as spawn5, execSync as execSync7 } from "node:child_process";
25326
+ import { join as join30 } from "node:path";
24732
25327
  import { tmpdir as tmpdir6 } from "node:os";
24733
- import { existsSync as existsSync21, unlinkSync as unlinkSync5 } from "node:fs";
25328
+ import { existsSync as existsSync23, unlinkSync as unlinkSync6 } from "node:fs";
24734
25329
  import { randomUUID as randomUUID6 } from "node:crypto";
24735
25330
  async function listenOnce(timeoutSeconds = 10, language = "en-US") {
24736
- const resultFile = join29(tmpdir6(), `oj-stt-${randomUUID6().slice(0, 8)}.txt`);
25331
+ const resultFile = join30(tmpdir6(), `oj-stt-${randomUUID6().slice(0, 8)}.txt`);
24737
25332
  const script = `
24738
25333
  $ProgressPreference = 'SilentlyContinue'
24739
25334
  $ErrorActionPreference = 'Stop'
@@ -24768,8 +25363,8 @@ try {
24768
25363
  }
24769
25364
  `;
24770
25365
  const encoded = Buffer.from(script, "utf16le").toString("base64");
24771
- return new Promise((resolve6) => {
24772
- const proc = spawn3("powershell.exe", ["-NoProfile", "-STA", "-EncodedCommand", encoded], {
25366
+ return new Promise((resolve7) => {
25367
+ const proc = spawn5("powershell.exe", ["-NoProfile", "-STA", "-EncodedCommand", encoded], {
24773
25368
  stdio: ["pipe", "pipe", "pipe"],
24774
25369
  windowsHide: true
24775
25370
  });
@@ -24783,25 +25378,25 @@ try {
24783
25378
  });
24784
25379
  const timer = setTimeout(() => {
24785
25380
  if (!proc.killed) proc.kill();
24786
- resolve6(null);
25381
+ resolve7(null);
24787
25382
  }, (timeoutSeconds + 5) * 1e3);
24788
25383
  proc.on("exit", () => {
24789
25384
  clearTimeout(timer);
24790
25385
  const output = stdout.replace(/#< CLIXML[\s\S]*/m, "").trim();
24791
25386
  if (output === "NO_SPEECH" || output.startsWith("ERROR") || !output) {
24792
- resolve6(null);
25387
+ resolve7(null);
24793
25388
  return;
24794
25389
  }
24795
25390
  const parts = output.split("|");
24796
25391
  const text = parts[0]?.trim();
24797
25392
  const confidence = parseFloat(parts[1] || "0");
24798
25393
  if (text) {
24799
- resolve6({ text, confidence: isNaN(confidence) ? 0.5 : confidence });
25394
+ resolve7({ text, confidence: isNaN(confidence) ? 0.5 : confidence });
24800
25395
  } else {
24801
- resolve6(null);
25396
+ resolve7(null);
24802
25397
  }
24803
25398
  try {
24804
- if (existsSync21(resultFile)) unlinkSync5(resultFile);
25399
+ if (existsSync23(resultFile)) unlinkSync6(resultFile);
24805
25400
  } catch {
24806
25401
  }
24807
25402
  });
@@ -25035,9 +25630,9 @@ __export(clipboard_image_exports, {
25035
25630
  readClipboardImage: () => readClipboardImage
25036
25631
  });
25037
25632
  import { execSync as execSync8 } from "node:child_process";
25038
- import { join as join30 } from "node:path";
25633
+ import { join as join31 } from "node:path";
25039
25634
  import { tmpdir as tmpdir7 } from "node:os";
25040
- import { readFileSync as readFileSync20, unlinkSync as unlinkSync6, existsSync as existsSync22 } from "node:fs";
25635
+ import { readFileSync as readFileSync21, unlinkSync as unlinkSync7, existsSync as existsSync24 } from "node:fs";
25041
25636
  import { randomUUID as randomUUID7 } from "node:crypto";
25042
25637
  function encodePS(script) {
25043
25638
  return Buffer.from(script, "utf16le").toString("base64");
@@ -25062,7 +25657,7 @@ Add-Type -AssemblyName System.Windows.Forms
25062
25657
  }
25063
25658
  function readClipboardImage() {
25064
25659
  try {
25065
- const tempPath = join30(tmpdir7(), `oj-clip-${randomUUID7().slice(0, 8)}.png`);
25660
+ const tempPath = join31(tmpdir7(), `oj-clip-${randomUUID7().slice(0, 8)}.png`);
25066
25661
  const script = `
25067
25662
  $ProgressPreference = 'SilentlyContinue'
25068
25663
  $ErrorActionPreference = 'Stop'
@@ -25079,16 +25674,16 @@ $img.Dispose()
25079
25674
  { encoding: "utf-8", timeout: 8e3, windowsHide: true, stdio: ["pipe", "pipe", "pipe"] }
25080
25675
  ).trim();
25081
25676
  const clean = output.replace(/#< CLIXML[\s\S]*/m, "").trim();
25082
- if (clean === "NO_IMAGE" || !existsSync22(tempPath)) {
25677
+ if (clean === "NO_IMAGE" || !existsSync24(tempPath)) {
25083
25678
  return null;
25084
25679
  }
25085
25680
  const match = clean.match(/(\d+)x(\d+)/);
25086
25681
  const width = match ? parseInt(match[1], 10) : 0;
25087
25682
  const height = match ? parseInt(match[2], 10) : 0;
25088
- const buffer = readFileSync20(tempPath);
25683
+ const buffer = readFileSync21(tempPath);
25089
25684
  const base64 = buffer.toString("base64");
25090
25685
  try {
25091
- unlinkSync6(tempPath);
25686
+ unlinkSync7(tempPath);
25092
25687
  } catch {
25093
25688
  }
25094
25689
  return { base64, mimeType: "image/png", width, height };
@@ -25914,9 +26509,9 @@ __export(pet_exports, {
25914
26509
  getRandomReaction: () => getRandomReaction,
25915
26510
  renamePet: () => renamePet
25916
26511
  });
25917
- import { existsSync as existsSync23, readFileSync as readFileSync21, writeFileSync as writeFileSync14, mkdirSync as mkdirSync15 } from "node:fs";
25918
- import { join as join31 } from "node:path";
25919
- import { homedir as homedir17 } from "node:os";
26512
+ import { existsSync as existsSync25, readFileSync as readFileSync22, writeFileSync as writeFileSync14, mkdirSync as mkdirSync15 } from "node:fs";
26513
+ import { join as join32 } from "node:path";
26514
+ import { homedir as homedir18 } from "node:os";
25920
26515
  function hashCode(str) {
25921
26516
  let hash3 = 0;
25922
26517
  for (let i = 0; i < str.length; i++) {
@@ -25934,20 +26529,20 @@ function seededRandom(seed) {
25934
26529
  };
25935
26530
  }
25936
26531
  function getPetPath() {
25937
- return join31(homedir17(), ".openjaw-agent", "pet.json");
26532
+ return join32(homedir18(), ".openjaw-agent", "pet.json");
25938
26533
  }
25939
26534
  function loadStoredPet() {
25940
26535
  const path3 = getPetPath();
25941
- if (!existsSync23(path3)) return null;
26536
+ if (!existsSync25(path3)) return null;
25942
26537
  try {
25943
- return JSON.parse(readFileSync21(path3, "utf-8"));
26538
+ return JSON.parse(readFileSync22(path3, "utf-8"));
25944
26539
  } catch {
25945
26540
  return null;
25946
26541
  }
25947
26542
  }
25948
26543
  function saveStoredPet(data) {
25949
- const dir2 = join31(homedir17(), ".openjaw-agent");
25950
- if (!existsSync23(dir2)) mkdirSync15(dir2, { recursive: true });
26544
+ const dir2 = join32(homedir18(), ".openjaw-agent");
26545
+ if (!existsSync25(dir2)) mkdirSync15(dir2, { recursive: true });
25951
26546
  writeFileSync14(getPetPath(), JSON.stringify(data, null, 2), "utf-8");
25952
26547
  }
25953
26548
  function generatePet(userId) {
@@ -26496,34 +27091,34 @@ ${output}
26496
27091
  }
26497
27092
  }
26498
27093
  function expandUrl(ref, warnings) {
26499
- return new Promise((resolve6) => {
27094
+ return new Promise((resolve7) => {
26500
27095
  const url = ref.target;
26501
27096
  const mod = url.startsWith("https") ? https : http;
26502
27097
  const req = mod.get(url, { timeout: 15e3 }, (res) => {
26503
27098
  if (res.statusCode && (res.statusCode >= 300 && res.statusCode < 400) && res.headers.location) {
26504
27099
  const redirectMod = res.headers.location.startsWith("https") ? https : http;
26505
27100
  const req2 = redirectMod.get(res.headers.location, { timeout: 15e3 }, (res2) => {
26506
- collectResponse(res2, ref, warnings, resolve6);
27101
+ collectResponse(res2, ref, warnings, resolve7);
26507
27102
  });
26508
27103
  req2.on("error", (e) => {
26509
27104
  warnings.push(`\u26A0\uFE0F @url fetch error: ${e.message}`);
26510
- resolve6(null);
27105
+ resolve7(null);
26511
27106
  });
26512
27107
  return;
26513
27108
  }
26514
- collectResponse(res, ref, warnings, resolve6);
27109
+ collectResponse(res, ref, warnings, resolve7);
26515
27110
  });
26516
27111
  req.on("error", (e) => {
26517
27112
  warnings.push(`\u26A0\uFE0F @url fetch error: ${e.message}`);
26518
- resolve6(null);
27113
+ resolve7(null);
26519
27114
  });
26520
27115
  });
26521
27116
  }
26522
- function collectResponse(res, ref, warnings, resolve6) {
27117
+ function collectResponse(res, ref, warnings, resolve7) {
26523
27118
  if (res.statusCode && res.statusCode >= 400) {
26524
27119
  warnings.push(`\u26A0\uFE0F @url returned HTTP ${res.statusCode}: ${ref.target}`);
26525
27120
  res.resume();
26526
- resolve6(null);
27121
+ resolve7(null);
26527
27122
  return;
26528
27123
  }
26529
27124
  const chunks = [];
@@ -26535,14 +27130,14 @@ function collectResponse(res, ref, warnings, resolve6) {
26535
27130
  text = text.slice(0, 5e4) + "\n\u2026 (truncated)";
26536
27131
  }
26537
27132
  const tokens = estimateTokens2(text);
26538
- resolve6(`\u{1F310} @url:${ref.target} (${tokens} tokens)
27133
+ resolve7(`\u{1F310} @url:${ref.target} (${tokens} tokens)
26539
27134
  \`\`\`
26540
27135
  ${text}
26541
27136
  \`\`\``);
26542
27137
  });
26543
27138
  res.on("error", (e) => {
26544
27139
  warnings.push(`\u26A0\uFE0F @url fetch error: ${e.message}`);
26545
- resolve6(null);
27140
+ resolve7(null);
26546
27141
  });
26547
27142
  }
26548
27143
  function stripHtml(html) {
@@ -27043,13 +27638,13 @@ Type /resume <id> to resume.` });
27043
27638
  if (input === "/export") {
27044
27639
  try {
27045
27640
  const { writeFile: writeFile5, mkdir: mkdir5 } = await import("node:fs/promises");
27046
- const { join: join48 } = await import("node:path");
27047
- const { homedir: homedir32 } = await import("node:os");
27048
- const { existsSync: existsSync34 } = await import("node:fs");
27049
- const exportDir = join48(homedir32(), ".openjaw-agent", "exports");
27050
- if (!existsSync34(exportDir)) await mkdir5(exportDir, { recursive: true });
27641
+ const { join: join49 } = await import("node:path");
27642
+ const { homedir: homedir33 } = await import("node:os");
27643
+ const { existsSync: existsSync36 } = await import("node:fs");
27644
+ const exportDir = join49(homedir33(), ".openjaw-agent", "exports");
27645
+ if (!existsSync36(exportDir)) await mkdir5(exportDir, { recursive: true });
27051
27646
  const filename = `session-${agentLoop.sessionId}.md`;
27052
- const filepath = join48(exportDir, filename);
27647
+ const filepath = join49(exportDir, filename);
27053
27648
  const lines = [
27054
27649
  `# OpenJaw Agent Session ${agentLoop.sessionId}`,
27055
27650
  `Date: ${(/* @__PURE__ */ new Date()).toISOString()}`,
@@ -27268,9 +27863,9 @@ ${list}` });
27268
27863
  const cmd = lang === "python" ? "python" : lang === "node" ? "node" : "pwsh";
27269
27864
  const args = lang === "python" ? ["-i", "-u"] : lang === "node" ? ["-i"] : ["-NoProfile", "-NoLogo"];
27270
27865
  try {
27271
- const { spawn: spawn8 } = await import("node:child_process");
27866
+ const { spawn: spawn10 } = await import("node:child_process");
27272
27867
  if (replRef.current) replRef.current.kill();
27273
- const proc = spawn8(cmd, args, {
27868
+ const proc = spawn10(cmd, args, {
27274
27869
  stdio: ["pipe", "pipe", "pipe"],
27275
27870
  windowsHide: true
27276
27871
  });
@@ -27310,7 +27905,7 @@ ${list}` });
27310
27905
  if (replRef.current && replLangRef.current && !input.startsWith("/")) {
27311
27906
  const proc = replRef.current;
27312
27907
  proc.stdin?.write(input + "\n");
27313
- await new Promise((resolve6) => setTimeout(resolve6, 800));
27908
+ await new Promise((resolve7) => setTimeout(resolve7, 800));
27314
27909
  const flush = proc._ojFlush;
27315
27910
  const output = flush ? flush() : "";
27316
27911
  if (output.trim()) {
@@ -27624,14 +28219,14 @@ Options: ${chunk.choices.join(" | ")}` : chunk.content;
27624
28219
  waitingForAskUserRef.current = true;
27625
28220
  setIsRunning(false);
27626
28221
  setWaitingForAskUser(true);
27627
- await new Promise((resolve6) => {
28222
+ await new Promise((resolve7) => {
27628
28223
  const checkInterval = setInterval(() => {
27629
28224
  if (!waitingForAskUserRef.current || !agentLoop.isWaitingForAskUser) {
27630
28225
  clearInterval(checkInterval);
27631
28226
  waitingForAskUserRef.current = false;
27632
28227
  setWaitingForAskUser(false);
27633
28228
  setIsRunning(true);
27634
- resolve6();
28229
+ resolve7();
27635
28230
  }
27636
28231
  }, 200);
27637
28232
  });
@@ -27906,7 +28501,7 @@ var skill_tool_exports = {};
27906
28501
  __export(skill_tool_exports, {
27907
28502
  createSkillTool: () => createSkillTool
27908
28503
  });
27909
- import { readFileSync as readFileSync23, writeFileSync as writeFileSync15 } from "node:fs";
28504
+ import { readFileSync as readFileSync24, writeFileSync as writeFileSync15 } from "node:fs";
27910
28505
  function resolveSkillTimeoutMs() {
27911
28506
  const raw = process.env["OPENJAW_SKILL_TIMEOUT_MS"];
27912
28507
  const parsed = raw ? Number(raw) : NaN;
@@ -27927,8 +28522,8 @@ async function withTimeout(promise, ms) {
27927
28522
  try {
27928
28523
  return await Promise.race([
27929
28524
  promise,
27930
- new Promise((resolve6) => {
27931
- timeoutId = setTimeout(() => resolve6(SKILL_TIMEOUT), ms);
28525
+ new Promise((resolve7) => {
28526
+ timeoutId = setTimeout(() => resolve7(SKILL_TIMEOUT), ms);
27932
28527
  })
27933
28528
  ]);
27934
28529
  } finally {
@@ -28074,7 +28669,7 @@ function extractLessons(chunks) {
28074
28669
  }
28075
28670
  function appendLessonsLearned(skillPath, lessons) {
28076
28671
  try {
28077
- const content = readFileSync23(skillPath, "utf8");
28672
+ const content = readFileSync24(skillPath, "utf8");
28078
28673
  const datestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
28079
28674
  if (content.includes("## Lessons Learned")) {
28080
28675
  const updated = content.replace(
@@ -28119,9 +28714,9 @@ var teams_exports = {};
28119
28714
  __export(teams_exports, {
28120
28715
  TeamsBridge: () => TeamsBridge
28121
28716
  });
28122
- import { readFileSync as readFileSync24, writeFileSync as writeFileSync16, existsSync as existsSync25 } from "node:fs";
28123
- import { join as join33 } from "node:path";
28124
- import { homedir as homedir19, tmpdir as tmpdir8 } from "node:os";
28717
+ import { readFileSync as readFileSync25, writeFileSync as writeFileSync16, existsSync as existsSync27 } from "node:fs";
28718
+ import { join as join34 } from "node:path";
28719
+ import { homedir as homedir20, tmpdir as tmpdir8 } from "node:os";
28125
28720
  import { randomUUID as randomUUID8 } from "node:crypto";
28126
28721
  var AGENT_PREFIX, POLL_INTERVAL, TeamsBridge;
28127
28722
  var init_teams = __esm({
@@ -28209,7 +28804,7 @@ var init_teams = __esm({
28209
28804
  const { resizeImageFileForAgent: resizeImageFileForAgent2 } = await Promise.resolve().then(() => (init_image_resize(), image_resize_exports));
28210
28805
  imageData = await resizeImageFileForAgent2(imageFile);
28211
28806
  } catch {
28212
- const imgBuffer = readFileSync24(imageFile);
28807
+ const imgBuffer = readFileSync25(imageFile);
28213
28808
  const ext = imageFile.split(".").pop()?.toLowerCase() || "png";
28214
28809
  const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "gif" ? "image/gif" : ext === "webp" ? "image/webp" : "image/png";
28215
28810
  imageData = { base64: imgBuffer.toString("base64"), mimeType };
@@ -28285,7 +28880,7 @@ var init_teams = __esm({
28285
28880
  const resp = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
28286
28881
  if (!resp.ok) return null;
28287
28882
  const buffer = Buffer.from(await resp.arrayBuffer());
28288
- const tempPath = join33(tmpdir8(), `oj-teams-${randomUUID8().slice(0, 6)}-${filename}`);
28883
+ const tempPath = join34(tmpdir8(), `oj-teams-${randomUUID8().slice(0, 6)}-${filename}`);
28289
28884
  writeFileSync16(tempPath, buffer);
28290
28885
  return tempPath;
28291
28886
  } catch {
@@ -28300,7 +28895,7 @@ var init_teams = __esm({
28300
28895
  const contentType = resp.headers.get("content-type") ?? "image/png";
28301
28896
  const ext = contentType.includes("jpeg") ? ".jpg" : contentType.includes("gif") ? ".gif" : ".png";
28302
28897
  const buffer = Buffer.from(await resp.arrayBuffer());
28303
- const tempPath = join33(tmpdir8(), `oj-teams-img-${randomUUID8().slice(0, 6)}${ext}`);
28898
+ const tempPath = join34(tmpdir8(), `oj-teams-img-${randomUUID8().slice(0, 6)}${ext}`);
28304
28899
  writeFileSync16(tempPath, buffer);
28305
28900
  return tempPath;
28306
28901
  } catch {
@@ -28341,7 +28936,7 @@ var init_teams = __esm({
28341
28936
  async sendFileToSelfChat(filePath) {
28342
28937
  const token = this.getGraphToken();
28343
28938
  const fileName = filePath.split(/[\\/]/).pop() || "file";
28344
- const fileBuffer = readFileSync24(filePath);
28939
+ const fileBuffer = readFileSync25(filePath);
28345
28940
  const uploadResp = await fetch(
28346
28941
  `https://graph.microsoft.com/v1.0/me/drive/root:/OpenJaw-Shared/${fileName}:/content`,
28347
28942
  {
@@ -28394,7 +28989,7 @@ var init_teams = __esm({
28394
28989
  if (filePath.includes("http") || filePath.includes("node_modules")) continue;
28395
28990
  const fileName = filePath.split(/[\\/]/).pop() || "";
28396
28991
  if (fileName.startsWith("oj-") || fileName.endsWith(".log") || fileName.endsWith(".tmp")) continue;
28397
- if (existsSync25(filePath)) {
28992
+ if (existsSync27(filePath)) {
28398
28993
  try {
28399
28994
  const { statSync: statSync5 } = await import("node:fs");
28400
28995
  const stat2 = statSync5(filePath);
@@ -28412,9 +29007,9 @@ var init_teams = __esm({
28412
29007
  }
28413
29008
  /** Get Graph access token from disk. */
28414
29009
  getGraphToken() {
28415
- const tokenPath = join33(homedir19(), ".graph-token", "tokens", "graph-chat.json");
28416
- if (!existsSync25(tokenPath)) throw new Error("Graph token not found");
28417
- const data = JSON.parse(readFileSync24(tokenPath, "utf-8"));
29010
+ const tokenPath = join34(homedir20(), ".graph-token", "tokens", "graph-chat.json");
29011
+ if (!existsSync27(tokenPath)) throw new Error("Graph token not found");
29012
+ const data = JSON.parse(readFileSync25(tokenPath, "utf-8"));
28418
29013
  if (!data.access_token) throw new Error("No access_token");
28419
29014
  return data.access_token;
28420
29015
  }
@@ -28538,12 +29133,12 @@ var init_teams = __esm({
28538
29133
  Options: ${chunk.choices.join(" | ")}` : "";
28539
29134
  await this.sendToSelfChat(`\u2753 ${question}${choicesText}`);
28540
29135
  if (!answerSent) await updateStatus("\u{1F916} OpenJaw Agent \u2014 \u2753 Waiting for your response...");
28541
- const userReply = await new Promise((resolve6) => {
28542
- this._pendingReplyResolver = resolve6;
29136
+ const userReply = await new Promise((resolve7) => {
29137
+ this._pendingReplyResolver = resolve7;
28543
29138
  setTimeout(() => {
28544
- if (this._pendingReplyResolver === resolve6) {
29139
+ if (this._pendingReplyResolver === resolve7) {
28545
29140
  this._pendingReplyResolver = null;
28546
- resolve6("[No response \u2014 timed out]");
29141
+ resolve7("[No response \u2014 timed out]");
28547
29142
  }
28548
29143
  }, 5 * 60 * 1e3);
28549
29144
  });
@@ -28615,8 +29210,8 @@ __export(feishu_exports, {
28615
29210
  FeishuBridge: () => FeishuBridge
28616
29211
  });
28617
29212
  import * as lark from "@larksuiteoapi/node-sdk";
28618
- import { readFileSync as readFileSync25, existsSync as existsSync26 } from "node:fs";
28619
- import { join as join34 } from "node:path";
29213
+ import { readFileSync as readFileSync26, existsSync as existsSync28 } from "node:fs";
29214
+ import { join as join35 } from "node:path";
28620
29215
  import { tmpdir as tmpdir9 } from "node:os";
28621
29216
  import { randomUUID as randomUUID9 } from "node:crypto";
28622
29217
  var FeishuBridge;
@@ -28852,9 +29447,9 @@ var init_feishu = __esm({
28852
29447
  params: { type: "image" }
28853
29448
  });
28854
29449
  if (resp) {
28855
- const tempPath = join34(tmpdir9(), `oj-feishu-img-${randomUUID9().slice(0, 6)}.png`);
29450
+ const tempPath = join35(tmpdir9(), `oj-feishu-img-${randomUUID9().slice(0, 6)}.png`);
28856
29451
  await resp.writeFile(tempPath);
28857
- const buffer = readFileSync25(tempPath);
29452
+ const buffer = readFileSync26(tempPath);
28858
29453
  let imageData;
28859
29454
  try {
28860
29455
  const { resizeImageFileForAgent: resizeImageFileForAgent2 } = await Promise.resolve().then(() => (init_image_resize(), image_resize_exports));
@@ -28875,7 +29470,7 @@ var init_feishu = __esm({
28875
29470
  params: { type: "file" }
28876
29471
  });
28877
29472
  if (resp) {
28878
- const tempPath = join34(tmpdir9(), `oj-feishu-file-${randomUUID9().slice(0, 6)}-${fileName}`);
29473
+ const tempPath = join35(tmpdir9(), `oj-feishu-file-${randomUUID9().slice(0, 6)}-${fileName}`);
28879
29474
  await resp.writeFile(tempPath);
28880
29475
  await this.handleMessage(chatId, `I've sent you a file: ${fileName}. It's saved at ${tempPath}. Please analyze it.`);
28881
29476
  }
@@ -28952,7 +29547,7 @@ var init_feishu = __esm({
28952
29547
  const fileName = filePath.split(/[\\/]/).pop() || "";
28953
29548
  if (fileName.startsWith("oj-") || fileName.endsWith(".log") || fileName.endsWith(".tmp")) continue;
28954
29549
  if (filePath.includes("http") || filePath.includes("node_modules")) continue;
28955
- const exists = existsSync26(filePath);
29550
+ const exists = existsSync28(filePath);
28956
29551
  this.emit({ type: "system", content: `\u{1F50D} Found path: ${filePath} (exists: ${exists})` });
28957
29552
  if (exists) {
28958
29553
  try {
@@ -29027,12 +29622,12 @@ ${err.stack?.split("\n").slice(0, 3).join("\n")}` : String(err);
29027
29622
  }
29028
29623
  /** Wait for the next user message (used by ask_user flow) */
29029
29624
  waitForUserReply(_chatId) {
29030
- return new Promise((resolve6) => {
29031
- this._pendingReplyResolver = resolve6;
29625
+ return new Promise((resolve7) => {
29626
+ this._pendingReplyResolver = resolve7;
29032
29627
  setTimeout(() => {
29033
- if (this._pendingReplyResolver === resolve6) {
29628
+ if (this._pendingReplyResolver === resolve7) {
29034
29629
  this._pendingReplyResolver = null;
29035
- resolve6("[No response \u2014 timed out]");
29630
+ resolve7("[No response \u2014 timed out]");
29036
29631
  }
29037
29632
  }, 5 * 60 * 1e3);
29038
29633
  });
@@ -29053,10 +29648,10 @@ __export(wechat_exports, {
29053
29648
  sniffMediaKind: () => sniffMediaKind,
29054
29649
  validateMediaForUpload: () => validateMediaForUpload
29055
29650
  });
29056
- import { existsSync as existsSync27, readFileSync as readFileSync26, writeFileSync as writeFileSync18, unlinkSync as unlinkSync7, statSync as statSync3 } from "node:fs";
29651
+ import { existsSync as existsSync29, readFileSync as readFileSync27, writeFileSync as writeFileSync18, unlinkSync as unlinkSync8, statSync as statSync3 } from "node:fs";
29057
29652
  import { mkdirSync as mkdirSync16 } from "node:fs";
29058
- import { extname as extname3, join as join35 } from "node:path";
29059
- import { homedir as homedir21 } from "node:os";
29653
+ import { extname as extname3, join as join36 } from "node:path";
29654
+ import { homedir as homedir22 } from "node:os";
29060
29655
  import { randomBytes, randomUUID as randomUUID10, createDecipheriv, createCipheriv, createHash as createHash3 } from "node:crypto";
29061
29656
  function mimeFromFilename(fileName) {
29062
29657
  return MIME_MAP[extname3(fileName).toLowerCase()] || "application/octet-stream";
@@ -29124,7 +29719,7 @@ function expandEnvVars(p) {
29124
29719
  return v !== void 0 ? v : m;
29125
29720
  });
29126
29721
  if (out.startsWith("~")) {
29127
- out = join35(homedir21(), out.slice(1).replace(/^[\\/]/, ""));
29722
+ out = join36(homedir22(), out.slice(1).replace(/^[\\/]/, ""));
29128
29723
  }
29129
29724
  return out;
29130
29725
  }
@@ -29274,15 +29869,15 @@ var init_wechat2 = __esm({
29274
29869
  }
29275
29870
  // ── Auth ──
29276
29871
  getAccountPath() {
29277
- const dir2 = join35(homedir21(), ".openjaw-agent");
29278
- if (!existsSync27(dir2)) mkdirSync16(dir2, { recursive: true });
29279
- return join35(dir2, "wechat-account.json");
29872
+ const dir2 = join36(homedir22(), ".openjaw-agent");
29873
+ if (!existsSync29(dir2)) mkdirSync16(dir2, { recursive: true });
29874
+ return join36(dir2, "wechat-account.json");
29280
29875
  }
29281
29876
  loadAccount() {
29282
29877
  const path3 = this.getAccountPath();
29283
- if (!existsSync27(path3)) return false;
29878
+ if (!existsSync29(path3)) return false;
29284
29879
  try {
29285
- this.account = JSON.parse(readFileSync26(path3, "utf-8"));
29880
+ this.account = JSON.parse(readFileSync27(path3, "utf-8"));
29286
29881
  return !!this.account?.token;
29287
29882
  } catch {
29288
29883
  return false;
@@ -29298,7 +29893,7 @@ var init_wechat2 = __esm({
29298
29893
  /** Delete saved session and clear current account (for /wechat logout) */
29299
29894
  logout() {
29300
29895
  const path3 = this.getAccountPath();
29301
- if (existsSync27(path3)) unlinkSync7(path3);
29896
+ if (existsSync29(path3)) unlinkSync8(path3);
29302
29897
  this.account = null;
29303
29898
  this.emit({ type: "system", content: "\u{1F7E2} WeChat: \u5DF2\u767B\u51FA\uFF0C\u4E0B\u6B21\u6D88\u606F\u5C06\u91CD\u65B0\u626B\u7801" });
29304
29899
  }
@@ -29323,9 +29918,9 @@ var init_wechat2 = __esm({
29323
29918
  try {
29324
29919
  const qrTerminal = await import("qrcode-terminal");
29325
29920
  const mod = qrTerminal.default || qrTerminal;
29326
- const qrAscii = await new Promise((resolve6, reject) => {
29921
+ const qrAscii = await new Promise((resolve7, reject) => {
29327
29922
  try {
29328
- mod.generate(qrUrl, { small: true }, (out) => resolve6(out));
29923
+ mod.generate(qrUrl, { small: true }, (out) => resolve7(out));
29329
29924
  } catch (e) {
29330
29925
  reject(e);
29331
29926
  }
@@ -29591,12 +30186,12 @@ Scan URL manually: ${qrUrl}` });
29591
30186
  const choicesText = chunk.choices?.length ? `
29592
30187
  \u9009\u9879: ${chunk.choices.join(" | ")}` : "";
29593
30188
  await this.sendText(userId, `\u2753 ${question}${choicesText}`, contextToken);
29594
- const userReply = await new Promise((resolve6) => {
29595
- this._pendingReplyResolver = resolve6;
30189
+ const userReply = await new Promise((resolve7) => {
30190
+ this._pendingReplyResolver = resolve7;
29596
30191
  setTimeout(() => {
29597
- if (this._pendingReplyResolver === resolve6) {
30192
+ if (this._pendingReplyResolver === resolve7) {
29598
30193
  this._pendingReplyResolver = null;
29599
- resolve6("[No response \u2014 timed out]");
30194
+ resolve7("[No response \u2014 timed out]");
29600
30195
  }
29601
30196
  }, 5 * 60 * 1e3);
29602
30197
  });
@@ -29760,7 +30355,7 @@ Scan URL manually: ${qrUrl}` });
29760
30355
  */
29761
30356
  async uploadFile(filePath, fileName, toUserId) {
29762
30357
  if (!this.account) return null;
29763
- const plaintext = readFileSync26(filePath);
30358
+ const plaintext = readFileSync27(filePath);
29764
30359
  const rawsize = plaintext.length;
29765
30360
  if (rawsize === 0) {
29766
30361
  this.emit({ type: "system", content: `\u26A0 WeChat upload skipped (file is empty): ${fileName}` });
@@ -29920,7 +30515,7 @@ Scan URL manually: ${qrUrl}` });
29920
30515
  if (!fileName) continue;
29921
30516
  if (fileName.startsWith("oj-") || fileName.endsWith(".log") || fileName.endsWith(".tmp")) continue;
29922
30517
  if (filePath.includes("http://") || filePath.includes("https://") || filePath.includes("node_modules")) continue;
29923
- if (!existsSync27(filePath)) continue;
30518
+ if (!existsSync29(filePath)) continue;
29924
30519
  attemptedFiles.add(dedupKey);
29925
30520
  attempted++;
29926
30521
  try {
@@ -30042,9 +30637,9 @@ __export(legacy_ink_ui_exports, {
30042
30637
  });
30043
30638
  import React6 from "react";
30044
30639
  import { render } from "ink";
30045
- import { existsSync as existsSync28 } from "node:fs";
30046
- import { join as join36 } from "node:path";
30047
- import { homedir as homedir22 } from "node:os";
30640
+ import { existsSync as existsSync30 } from "node:fs";
30641
+ import { join as join37 } from "node:path";
30642
+ import { homedir as homedir23 } from "node:os";
30048
30643
  import { EventEmitter as EventEmitter2 } from "node:events";
30049
30644
  async function startInkUI(resumeSessionId, bridges = []) {
30050
30645
  await ensureDirectories();
@@ -30072,12 +30667,12 @@ async function startInkUI(resumeSessionId, bridges = []) {
30072
30667
  });
30073
30668
  const agentLoop = new AgentLoop(agentConfig, toolRegistry, resumeSessionId);
30074
30669
  toolRegistry.setWebSearchExecutor(createWebSearchExecutor(agentLoop));
30075
- const memoryDbPath = join36(homedir22(), ".openjaw", "memory.db");
30670
+ const memoryDbPath = join37(homedir23(), ".openjaw", "memory.db");
30076
30671
  const memoryLegacyPaths = [
30077
- join36(homedir22(), ".openjaw", "MEMORY.md"),
30078
- join36(homedir22(), ".openjaw", "memory", "MEMORY.md")
30672
+ join37(homedir23(), ".openjaw", "MEMORY.md"),
30673
+ join37(homedir23(), ".openjaw", "memory", "MEMORY.md")
30079
30674
  ];
30080
- const memoryStatus = existsSync28(memoryDbPath) ? "loaded" : memoryLegacyPaths.some((p) => existsSync28(p)) ? "legacy" : "empty";
30675
+ const memoryStatus = existsSync30(memoryDbPath) ? "loaded" : memoryLegacyPaths.some((p) => existsSync30(p)) ? "legacy" : "empty";
30081
30676
  const systemPromptFn = /* @__PURE__ */ __name(() => getSystemPrompt({ mcpManager }), "systemPromptFn");
30082
30677
  const { createSkillTool: createSkillTool2 } = await Promise.resolve().then(() => (init_skill_tool(), skill_tool_exports));
30083
30678
  toolRegistry.registerTool(createSkillTool2(agentConfig, toolRegistry, systemPromptFn));
@@ -30344,6 +30939,15 @@ var init_agentBus = __esm({
30344
30939
  hasRpc(method) {
30345
30940
  return this.handlers.has(method);
30346
30941
  }
30942
+ /**
30943
+ * Read the currently-registered handler for `method`, or undefined if
30944
+ * none. The desktop layer uses this to wrap an existing handler with
30945
+ * a lock-aware override (electronMain.ts registers `session.resume`
30946
+ * after registerRpcHandlers and needs to delegate to the original).
30947
+ */
30948
+ getRpc(method) {
30949
+ return this.handlers.get(method);
30950
+ }
30347
30951
  /**
30348
30952
  * RPC dispatch entry point. Mirrors hermes's WebSocket `request(method, params)`
30349
30953
  * but is a direct in-process function call. When no handler is registered,
@@ -30445,9 +31049,9 @@ var init_agentBus = __esm({
30445
31049
  });
30446
31050
 
30447
31051
  // src/bridges/registry.ts
30448
- import { existsSync as existsSync29, unlinkSync as unlinkSync8 } from "node:fs";
30449
- import { homedir as homedir23 } from "node:os";
30450
- import { join as join37 } from "node:path";
31052
+ import { existsSync as existsSync31, unlinkSync as unlinkSync9 } from "node:fs";
31053
+ import { homedir as homedir24 } from "node:os";
31054
+ import { join as join38 } from "node:path";
30451
31055
  var BRIDGE_REGISTRY, BRIDGE_NAMES, isBridgeName, normalizeCredentialsForConfig, BridgeManager, parseListInput;
30452
31056
  var init_registry3 = __esm({
30453
31057
  "src/bridges/registry.ts"() {
@@ -30546,15 +31150,15 @@ var init_registry3 = __esm({
30546
31150
  return { instance, stop: /* @__PURE__ */ __name(() => instance.stop(), "stop") };
30547
31151
  },
30548
31152
  async onForget(instance) {
30549
- const wechatPath = join37(homedir23(), ".openjaw-agent", "wechat-account.json");
31153
+ const wechatPath = join38(homedir24(), ".openjaw-agent", "wechat-account.json");
30550
31154
  if (instance && typeof instance.logout === "function") {
30551
31155
  try {
30552
31156
  instance.logout();
30553
31157
  } catch {
30554
31158
  }
30555
- } else if (existsSync29(wechatPath)) {
31159
+ } else if (existsSync31(wechatPath)) {
30556
31160
  try {
30557
- unlinkSync8(wechatPath);
31161
+ unlinkSync9(wechatPath);
30558
31162
  } catch {
30559
31163
  }
30560
31164
  }
@@ -30827,12 +31431,19 @@ var init_registry3 = __esm({
30827
31431
 
30828
31432
  // src/bootstrap.ts
30829
31433
  import { EventEmitter as EventEmitter4 } from "node:events";
30830
- import { existsSync as existsSync30 } from "node:fs";
30831
- import { homedir as homedir24 } from "node:os";
30832
- import { join as join38 } from "node:path";
31434
+ import { existsSync as existsSync32 } from "node:fs";
31435
+ import { homedir as homedir25 } from "node:os";
31436
+ import { join as join39 } from "node:path";
30833
31437
  async function bootstrapAgent(opts = {}) {
30834
31438
  await ensureDirectories();
30835
31439
  const config = await loadConfig();
31440
+ const { configureWam: configureWam2 } = await Promise.resolve().then(() => (init_wam_token_provider(), wam_token_provider_exports));
31441
+ const authMode = config.microsoft?.auth_mode ?? "auto";
31442
+ configureWam2({
31443
+ enabled: authMode !== "cdp",
31444
+ ...config.microsoft?.client_id ? { clientId: config.microsoft.client_id } : {},
31445
+ ...config.microsoft?.tenant_id ? { tenant: config.microsoft.tenant_id } : {}
31446
+ });
30836
31447
  const toolRegistry = new ToolRegistry(config);
30837
31448
  const memoryStore = new MemoryStore(config);
30838
31449
  await toolRegistry.initializeProfile("core");
@@ -30840,9 +31451,10 @@ async function bootstrapAgent(opts = {}) {
30840
31451
  const { createMetaTool: createMetaTool2 } = await Promise.resolve().then(() => (init_meta(), meta_exports));
30841
31452
  toolRegistry.registerTool(createMetaTool2(toolRegistry));
30842
31453
  const agentConfig = loadAgentConfig();
31454
+ const interactiveMcp = opts.interactiveMcp !== false;
30843
31455
  const mcpManager = new MCPClientManager();
30844
31456
  try {
30845
- await mcpManager.prepare(process.cwd(), true);
31457
+ await mcpManager.prepare(process.cwd(), interactiveMcp);
30846
31458
  } catch (err) {
30847
31459
  process.stderr.write(
30848
31460
  `\x1B[33m\u26A0 MCP discovery failed: ${err instanceof Error ? err.message : String(err)}\x1B[0m
@@ -30859,12 +31471,12 @@ async function bootstrapAgent(opts = {}) {
30859
31471
  });
30860
31472
  const agentLoop = new AgentLoop(agentConfig, toolRegistry, opts.resumeSessionId);
30861
31473
  toolRegistry.setWebSearchExecutor(createWebSearchExecutor(agentLoop));
30862
- const memoryDbPath = join38(homedir24(), ".openjaw", "memory.db");
31474
+ const memoryDbPath = join39(homedir25(), ".openjaw", "memory.db");
30863
31475
  const memoryLegacyPaths = [
30864
- join38(homedir24(), ".openjaw", "MEMORY.md"),
30865
- join38(homedir24(), ".openjaw", "memory", "MEMORY.md")
31476
+ join39(homedir25(), ".openjaw", "MEMORY.md"),
31477
+ join39(homedir25(), ".openjaw", "memory", "MEMORY.md")
30866
31478
  ];
30867
- const memoryStatus = existsSync30(memoryDbPath) ? "loaded" : memoryLegacyPaths.some((p) => existsSync30(p)) ? "legacy" : "empty";
31479
+ const memoryStatus = existsSync32(memoryDbPath) ? "loaded" : memoryLegacyPaths.some((p) => existsSync32(p)) ? "legacy" : "empty";
30868
31480
  const systemPromptFn = /* @__PURE__ */ __name(() => getSystemPrompt({ mcpManager }), "systemPromptFn");
30869
31481
  const { createSkillTool: createSkillTool2 } = await Promise.resolve().then(() => (init_skill_tool(), skill_tool_exports));
30870
31482
  toolRegistry.registerTool(createSkillTool2(agentConfig, toolRegistry, systemPromptFn));
@@ -31097,8 +31709,8 @@ function createPromptCollector(bus, getSessionId) {
31097
31709
  }, "emit");
31098
31710
  const register = /* @__PURE__ */ __name((kind) => {
31099
31711
  const requestId = randomUUID12();
31100
- const promise = new Promise((resolve6) => {
31101
- pending.set(requestId, { kind, resolve: resolve6 });
31712
+ const promise = new Promise((resolve7) => {
31713
+ pending.set(requestId, { kind, resolve: resolve7 });
31102
31714
  });
31103
31715
  return { promise, requestId };
31104
31716
  }, "register");
@@ -31656,7 +32268,7 @@ import { c as _c } from "react/compiler-runtime";
31656
32268
  import { jsx as jsx10 } from "react/jsx-runtime";
31657
32269
  import { jsx as jsx22 } from "react/jsx-runtime";
31658
32270
  import { Buffer as Buffer2 } from "buffer";
31659
- import { spawn as spawn4 } from "child_process";
32271
+ import { spawn as spawn6 } from "child_process";
31660
32272
  import { jsx as jsx32 } from "react/jsx-runtime";
31661
32273
  import { ansiCodesToString, reduceAnsiCodes, tokenize as tokenize2, undoAnsiCodes } from "@alcalzone/ansi-tokenize";
31662
32274
  import emojiRegex from "emoji-regex";
@@ -31726,7 +32338,7 @@ import { Buffer as Buffer3 } from "buffer";
31726
32338
  import { createContext as createContext7, useEffect as useEffect4, useState as useState22 } from "react";
31727
32339
  import { c as _c10 } from "react/compiler-runtime";
31728
32340
  import { jsx as jsx12 } from "react/jsx-runtime";
31729
- import { readFileSync as readFileSync27 } from "fs";
32341
+ import { readFileSync as readFileSync28 } from "fs";
31730
32342
  import codeExcerpt from "code-excerpt";
31731
32343
  import StackUtils from "stack-utils";
31732
32344
  import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
@@ -31734,7 +32346,7 @@ import { jsx as jsx14 } from "react/jsx-runtime";
31734
32346
  import { ansiCodesToString as ansiCodesToString3, diffAnsiCodes as diffAnsiCodes2 } from "@alcalzone/ansi-tokenize";
31735
32347
  import { styledCharsFromTokens, tokenize as tokenize3 } from "@alcalzone/ansi-tokenize";
31736
32348
  import bidiFactory from "bidi-js";
31737
- import noop from "lodash-es/noop.js";
32349
+ import noop3 from "lodash-es/noop.js";
31738
32350
  import { LegacyRoot } from "react-reconciler/constants.js";
31739
32351
  import { jsx as jsx15 } from "react/jsx-runtime";
31740
32352
  import { default as default2, UncontrolledTextInput } from "ink-text-input";
@@ -32082,8 +32694,8 @@ function supportsOsc52Clipboard(terminal2 = env.terminal) {
32082
32694
  return OSC52_CAPABLE_TERMINALS.includes(terminal2 ?? "");
32083
32695
  }
32084
32696
  function execFileNoThrow(file2, args, options = {}) {
32085
- return new Promise((resolve6) => {
32086
- const child = spawn4(file2, args, {
32697
+ return new Promise((resolve7) => {
32698
+ const child = spawn6(file2, args, {
32087
32699
  cwd: options.useCwd ? process.cwd() : void 0,
32088
32700
  env: options.env,
32089
32701
  stdio: "pipe"
@@ -32105,13 +32717,13 @@ function execFileNoThrow(file2, args, options = {}) {
32105
32717
  if (timer) {
32106
32718
  clearTimeout(timer);
32107
32719
  }
32108
- resolve6({ stdout, stderr, code: 1, error: String(error) });
32720
+ resolve7({ stdout, stderr, code: 1, error: String(error) });
32109
32721
  });
32110
32722
  child.on("close", (code) => {
32111
32723
  if (timer) {
32112
32724
  clearTimeout(timer);
32113
32725
  }
32114
- resolve6({ stdout, stderr, code: timedOut ? 124 : code ?? 0 });
32726
+ resolve7({ stdout, stderr, code: timedOut ? 124 : code ?? 0 });
32115
32727
  });
32116
32728
  if (options.input) {
32117
32729
  child.stdin?.write(options.input);
@@ -33323,11 +33935,11 @@ function sliceAnsi(str, start, end) {
33323
33935
  }
33324
33936
  if (end !== void 0) {
33325
33937
  const key = `${start}|${end}|${str}`;
33326
- const cached7 = sliceCache.get(key);
33327
- if (cached7 !== void 0) {
33938
+ const cached8 = sliceCache.get(key);
33939
+ if (cached8 !== void 0) {
33328
33940
  sliceCache.delete(key);
33329
- sliceCache.set(key, cached7);
33330
- return cached7;
33941
+ sliceCache.set(key, cached8);
33942
+ return cached8;
33331
33943
  }
33332
33944
  const result = computeSlice(str, start, end);
33333
33945
  if (sliceCache.size >= SLICE_CACHE_LIMIT) {
@@ -33382,11 +33994,11 @@ function computeSlice(str, start, end) {
33382
33994
  return result;
33383
33995
  }
33384
33996
  function lineWidth(line) {
33385
- const cached7 = cache.get(line);
33386
- if (cached7 !== void 0) {
33997
+ const cached8 = cache.get(line);
33998
+ if (cached8 !== void 0) {
33387
33999
  cache.delete(line);
33388
- cache.set(line, cached7);
33389
- return cached7;
34000
+ cache.set(line, cached8);
34001
+ return cached8;
33390
34002
  }
33391
34003
  const width = stringWidth(line);
33392
34004
  if (cache.size >= MAX_CACHE_SIZE) {
@@ -33403,11 +34015,11 @@ function evictLineWidthCache(keepRatio = 0) {
33403
34015
  }
33404
34016
  function memoizedWrap(text, maxWidth, wrapType) {
33405
34017
  const key = `${maxWidth}|${wrapType}|${text}`;
33406
- const cached7 = wrapCache.get(key);
33407
- if (cached7 !== void 0) {
34018
+ const cached8 = wrapCache.get(key);
34019
+ if (cached8 !== void 0) {
33408
34020
  wrapCache.delete(key);
33409
- wrapCache.set(key, cached7);
33410
- return cached7;
34021
+ wrapCache.set(key, cached8);
34022
+ return cached8;
33411
34023
  }
33412
34024
  const result = computeWrap(text, maxWidth, wrapType);
33413
34025
  if (wrapCache.size >= WRAP_CACHE_LIMIT) {
@@ -35367,9 +35979,9 @@ function collectRemovedRects(parent, removed, underAbsolute = false) {
35367
35979
  }
35368
35980
  const elem = removed;
35369
35981
  const isAbsolute2 = underAbsolute || elem.style.position === "absolute";
35370
- const cached7 = nodeCache.get(elem);
35371
- if (cached7) {
35372
- addPendingClear(parent, cached7, isAbsolute2);
35982
+ const cached8 = nodeCache.get(elem);
35983
+ if (cached8) {
35984
+ addPendingClear(parent, cached8, isAbsolute2);
35373
35985
  nodeCache.delete(elem);
35374
35986
  }
35375
35987
  for (const child of elem.childNodes) {
@@ -37084,8 +37696,8 @@ function setTerminalFocused(v) {
37084
37696
  cb();
37085
37697
  }
37086
37698
  if (!v) {
37087
- for (const resolve6 of resolvers) {
37088
- resolve6();
37699
+ for (const resolve7 of resolvers) {
37700
+ resolve7();
37089
37701
  }
37090
37702
  resolvers.clear();
37091
37703
  }
@@ -37489,31 +38101,31 @@ function renderNodeToOutput(node, output, {
37489
38101
  if (y < 0 && node.style.position === "absolute") {
37490
38102
  y = 0;
37491
38103
  }
37492
- const cached7 = nodeCache.get(node);
37493
- if (!node.dirty && !skipSelfBlit && node.pendingScrollDelta === void 0 && cached7 && cached7.x === x && cached7.y === y && cached7.width === width && cached7.height === height && prevScreen) {
38104
+ const cached8 = nodeCache.get(node);
38105
+ if (!node.dirty && !skipSelfBlit && node.pendingScrollDelta === void 0 && cached8 && cached8.x === x && cached8.y === y && cached8.width === width && cached8.height === height && prevScreen) {
37494
38106
  const fx = Math.floor(x);
37495
38107
  const fy = Math.floor(y);
37496
38108
  const fw = Math.floor(width);
37497
38109
  const fh = Math.floor(height);
37498
38110
  output.blit(prevScreen, fx, fy, fw, fh);
37499
38111
  if (node.style.position === "absolute") {
37500
- absoluteRectsCur.push(cached7);
38112
+ absoluteRectsCur.push(cached8);
37501
38113
  }
37502
38114
  blitEscapingAbsoluteDescendants(node, output, prevScreen, fx, fy, fw, fh);
37503
38115
  return;
37504
38116
  }
37505
- const positionChanged = cached7 !== void 0 && (cached7.x !== x || cached7.y !== y || cached7.width !== width || cached7.height !== height);
38117
+ const positionChanged = cached8 !== void 0 && (cached8.x !== x || cached8.y !== y || cached8.width !== width || cached8.height !== height);
37506
38118
  if (positionChanged) {
37507
38119
  layoutShifted = true;
37508
38120
  absoluteOverlayMoved ||= node.style.position === "absolute";
37509
38121
  }
37510
- if (cached7 && (node.dirty || positionChanged)) {
38122
+ if (cached8 && (node.dirty || positionChanged)) {
37511
38123
  output.clear(
37512
38124
  {
37513
- x: Math.floor(cached7.x),
37514
- y: Math.floor(cached7.y),
37515
- width: Math.floor(cached7.width),
37516
- height: Math.floor(cached7.height)
38125
+ x: Math.floor(cached8.x),
38126
+ y: Math.floor(cached8.y),
38127
+ width: Math.floor(cached8.width),
38128
+ height: Math.floor(cached8.height)
37517
38129
  },
37518
38130
  node.style.position === "absolute"
37519
38131
  );
@@ -37688,7 +38300,7 @@ function renderNodeToOutput(node, output, {
37688
38300
  const delta = contentCached.y - contentY;
37689
38301
  const regionTop = Math.floor(y + contentYoga.getComputedTop());
37690
38302
  const regionBottom = regionTop + innerHeight - 1;
37691
- if (cached7?.y === y && cached7.height === height && innerHeight > 0 && Math.abs(delta) < innerHeight) {
38303
+ if (cached8?.y === y && cached8.height === height && innerHeight > 0 && Math.abs(delta) < innerHeight) {
37692
38304
  hint = { top: regionTop, bottom: regionBottom, delta };
37693
38305
  scrollHint = hint;
37694
38306
  } else {
@@ -37988,13 +38600,13 @@ function blitEscapingAbsoluteDescendants(node, output, prevScreen, px, py, pw, p
37988
38600
  }
37989
38601
  const elem = child;
37990
38602
  if (elem.style.position === "absolute") {
37991
- const cached7 = nodeCache.get(elem);
37992
- if (cached7) {
37993
- absoluteRectsCur.push(cached7);
37994
- const cx = Math.floor(cached7.x);
37995
- const cy = Math.floor(cached7.y);
37996
- const cw = Math.floor(cached7.width);
37997
- const ch = Math.floor(cached7.height);
38603
+ const cached8 = nodeCache.get(elem);
38604
+ if (cached8) {
38605
+ absoluteRectsCur.push(cached8);
38606
+ const cx = Math.floor(cached8.x);
38607
+ const cy = Math.floor(cached8.y);
38608
+ const cw = Math.floor(cached8.width);
38609
+ const ch = Math.floor(cached8.height);
37998
38610
  if (cx < px || cy < py || cx + cw > pr || cy + ch > pb) {
37999
38611
  output.blit(prevScreen, cx, cy, cw, ch);
38000
38612
  }
@@ -38010,20 +38622,20 @@ function renderScrolledChildren(node, output, offsetX, offsetY, hasRemovedChild,
38010
38622
  const childElem = childNode;
38011
38623
  const cy = childElem.yogaNode;
38012
38624
  if (cy) {
38013
- const cached7 = nodeCache.get(childElem);
38625
+ const cached8 = nodeCache.get(childElem);
38014
38626
  let top;
38015
38627
  let height;
38016
- if (cached7?.top !== void 0 && !childElem.dirty && cumHeightShift === 0) {
38017
- top = cached7.top;
38018
- height = cached7.height;
38628
+ if (cached8?.top !== void 0 && !childElem.dirty && cumHeightShift === 0) {
38629
+ top = cached8.top;
38630
+ height = cached8.height;
38019
38631
  } else {
38020
38632
  top = cy.getComputedTop();
38021
38633
  height = cy.getComputedHeight();
38022
38634
  if (childElem.dirty) {
38023
- cumHeightShift += height - (cached7 ? cached7.height : 0);
38635
+ cumHeightShift += height - (cached8 ? cached8.height : 0);
38024
38636
  }
38025
- if (cached7) {
38026
- cached7.top = top;
38637
+ if (cached8) {
38638
+ cached8.top = top;
38027
38639
  }
38028
38640
  }
38029
38641
  const bottom = top + height;
@@ -38709,7 +39321,7 @@ function ErrorOverview({ error }) {
38709
39321
  let lineWidth2 = 0;
38710
39322
  if (filePath && origin?.line) {
38711
39323
  try {
38712
- const sourceCode = readFileSync27(filePath, "utf8");
39324
+ const sourceCode = readFileSync28(filePath, "utf8");
38713
39325
  excerpt = codeExcerpt(sourceCode, origin.line);
38714
39326
  if (excerpt) {
38715
39327
  for (const { line } of excerpt) {
@@ -40433,11 +41045,11 @@ var init_entry_exports = __esm({
40433
41045
  return rawStringWidth(str);
40434
41046
  }
40435
41047
  }
40436
- const cached7 = widthCache.get(str);
40437
- if (cached7 !== void 0) {
41048
+ const cached8 = widthCache.get(str);
41049
+ if (cached8 !== void 0) {
40438
41050
  widthCache.delete(str);
40439
- widthCache.set(str, cached7);
40440
- return cached7;
41051
+ widthCache.set(str, cached8);
41052
+ return cached8;
40441
41053
  }
40442
41054
  const w = rawStringWidth(str);
40443
41055
  if (widthCache.size >= WIDTH_CACHE_LIMIT) {
@@ -42542,9 +43154,9 @@ $ npm install --save-dev react-devtools-core
42542
43154
  if (char.length === 1) {
42543
43155
  const code = char.charCodeAt(0);
42544
43156
  if (code < 128) {
42545
- const cached7 = this.ascii[code];
42546
- if (cached7 !== -1) {
42547
- return cached7;
43157
+ const cached8 = this.ascii[code];
43158
+ if (cached8 !== -1) {
43159
+ return cached8;
42548
43160
  }
42549
43161
  const index2 = this.strings.length;
42550
43162
  this.strings.push(char);
@@ -43323,11 +43935,11 @@ $ npm install --save-dev react-devtools-core
43323
43935
  * and the terminal doesn't respond, the promise remains pending.
43324
43936
  */
43325
43937
  send(query) {
43326
- return new Promise((resolve6) => {
43938
+ return new Promise((resolve7) => {
43327
43939
  this.queue.push({
43328
43940
  kind: "query",
43329
43941
  match: query.match,
43330
- resolve: /* @__PURE__ */ __name((r) => resolve6(r), "resolve")
43942
+ resolve: /* @__PURE__ */ __name((r) => resolve7(r), "resolve")
43331
43943
  });
43332
43944
  this.stdout.write(query.request);
43333
43945
  });
@@ -43342,8 +43954,8 @@ $ npm install --save-dev react-devtools-core
43342
43954
  * Safe to call with no pending queries — still waits for a round-trip.
43343
43955
  */
43344
43956
  flush() {
43345
- return new Promise((resolve6) => {
43346
- this.queue.push({ kind: "sentinel", resolve: resolve6 });
43957
+ return new Promise((resolve7) => {
43958
+ this.queue.push({ kind: "sentinel", resolve: resolve7 });
43347
43959
  this.stdout.write(SENTINEL);
43348
43960
  });
43349
43961
  }
@@ -45823,8 +46435,8 @@ $ npm install --save-dev react-devtools-core
45823
46435
  }
45824
46436
  }
45825
46437
  async waitUntilExit() {
45826
- this.exitPromise ||= new Promise((resolve6, reject) => {
45827
- this.resolveExitPromise = resolve6;
46438
+ this.exitPromise ||= new Promise((resolve7, reject) => {
46439
+ this.resolveExitPromise = resolve7;
45828
46440
  this.rejectExitPromise = reject;
45829
46441
  });
45830
46442
  return this.exitPromise;
@@ -46238,7 +46850,7 @@ var init_details = __esm({
46238
46850
  });
46239
46851
 
46240
46852
  // src/lib/clipboard.ts
46241
- import { execFile, spawn as spawn5 } from "node:child_process";
46853
+ import { execFile, spawn as spawn7 } from "node:child_process";
46242
46854
  import { promisify as promisify6 } from "node:util";
46243
46855
  function isUsableClipboardText(text) {
46244
46856
  if (!text || !/[^\s]/.test(text)) {
@@ -46311,14 +46923,14 @@ function writeClipboardCommands(platform2, env2) {
46311
46923
  attempts.push({ cmd: "xsel", args: ["--clipboard", "--input"] });
46312
46924
  return attempts;
46313
46925
  }
46314
- async function writeClipboardText(text, platform2 = process.platform, start = spawn5, env2 = process.env) {
46926
+ async function writeClipboardText(text, platform2 = process.platform, start = spawn7, env2 = process.env) {
46315
46927
  const candidates = writeClipboardCommands(platform2, env2);
46316
46928
  for (const { cmd, args } of candidates) {
46317
46929
  try {
46318
- const ok = await new Promise((resolve6) => {
46930
+ const ok = await new Promise((resolve7) => {
46319
46931
  const child = start(cmd, [...args], { stdio: ["pipe", "ignore", "ignore"], windowsHide: true });
46320
- child.once("error", () => resolve6(false));
46321
- child.once("close", (code) => resolve6(code === 0));
46932
+ child.once("error", () => resolve7(false));
46933
+ child.once("close", (code) => resolve7(code === 0));
46322
46934
  child.stdin?.end(text);
46323
46935
  });
46324
46936
  if (ok) {
@@ -46377,8 +46989,8 @@ async function readOsc52Clipboard(querier, timeoutMs = 500) {
46377
46989
  if (!querier) {
46378
46990
  return null;
46379
46991
  }
46380
- const timeout = new Promise((resolve6) => {
46381
- setTimeout(() => resolve6(void 0), timeoutMs);
46992
+ const timeout = new Promise((resolve7) => {
46993
+ setTimeout(() => resolve7(void 0), timeoutMs);
46382
46994
  });
46383
46995
  const query = querier.send({
46384
46996
  request: buildOsc52ClipboardQuery(),
@@ -46408,8 +47020,8 @@ var init_osc52 = __esm({
46408
47020
 
46409
47021
  // src/lib/terminalSetup.ts
46410
47022
  import { copyFile, mkdir as mkdir3, readFile as readFile5, writeFile as writeFile3 } from "node:fs/promises";
46411
- import { homedir as homedir25 } from "node:os";
46412
- import { join as join39 } from "node:path";
47023
+ import { homedir as homedir26 } from "node:os";
47024
+ import { join as join40 } from "node:path";
46413
47025
  function detectVSCodeLikeTerminal(env2 = process.env) {
46414
47026
  const askpass = env2["VSCODE_GIT_ASKPASS_MAIN"]?.toLowerCase() ?? "";
46415
47027
  if (env2["CURSOR_TRACE_ID"] || askpass.includes("cursor")) {
@@ -46463,14 +47075,14 @@ function stripJsonComments(content) {
46463
47075
  function isRemoteShellSession(env2) {
46464
47076
  return Boolean(env2["SSH_CONNECTION"] || env2["SSH_TTY"] || env2["SSH_CLIENT"]);
46465
47077
  }
46466
- function getVSCodeStyleConfigDir(appName, platform2 = process.platform, env2 = process.env, homeDir = homedir25()) {
47078
+ function getVSCodeStyleConfigDir(appName, platform2 = process.platform, env2 = process.env, homeDir = homedir26()) {
46467
47079
  if (platform2 === "darwin") {
46468
- return join39(homeDir, "Library", "Application Support", appName, "User");
47080
+ return join40(homeDir, "Library", "Application Support", appName, "User");
46469
47081
  }
46470
47082
  if (platform2 === "win32") {
46471
- return env2["APPDATA"] ? join39(env2["APPDATA"], appName, "User") : null;
47083
+ return env2["APPDATA"] ? join40(env2["APPDATA"], appName, "User") : null;
46472
47084
  }
46473
- return join39(homeDir, ".config", appName, "User");
47085
+ return join40(homeDir, ".config", appName, "User");
46474
47086
  }
46475
47087
  function isKeybinding(value) {
46476
47088
  return typeof value === "object" && value !== null;
@@ -46538,7 +47150,7 @@ async function backupFile(filePath, ops) {
46538
47150
  async function configureTerminalKeybindings(terminal2, options) {
46539
47151
  const env2 = options?.env ?? process.env;
46540
47152
  const platform2 = options?.platform ?? process.platform;
46541
- const homeDir = options?.homeDir ?? homedir25();
47153
+ const homeDir = options?.homeDir ?? homedir26();
46542
47154
  const ops = { ...DEFAULT_FILE_OPS, ...options?.fileOps ?? {} };
46543
47155
  const meta = TERMINAL_META[terminal2];
46544
47156
  if (isRemoteShellSession(env2)) {
@@ -46554,7 +47166,7 @@ async function configureTerminalKeybindings(terminal2, options) {
46554
47166
  message: `Could not determine ${meta.label} settings path on this platform.`
46555
47167
  };
46556
47168
  }
46557
- const keybindingsFile = join39(configDir2, "keybindings.json");
47169
+ const keybindingsFile = join40(configDir2, "keybindings.json");
46558
47170
  try {
46559
47171
  await ops.mkdir(configDir2, { recursive: true });
46560
47172
  let keybindings = [];
@@ -46637,7 +47249,7 @@ async function shouldPromptForTerminalSetup(options) {
46637
47249
  return false;
46638
47250
  }
46639
47251
  const platform2 = options?.platform ?? process.platform;
46640
- const homeDir = options?.homeDir ?? homedir25();
47252
+ const homeDir = options?.homeDir ?? homedir26();
46641
47253
  const ops = { ...DEFAULT_FILE_OPS, ...options?.fileOps ?? {} };
46642
47254
  const meta = TERMINAL_META[detected];
46643
47255
  const configDir2 = getVSCodeStyleConfigDir(meta.appName, platform2, env2, homeDir);
@@ -46645,7 +47257,7 @@ async function shouldPromptForTerminalSetup(options) {
46645
47257
  return false;
46646
47258
  }
46647
47259
  try {
46648
- const content = await ops.readFile(join39(configDir2, "keybindings.json"), "utf8");
47260
+ const content = await ops.readFile(join40(configDir2, "keybindings.json"), "utf8");
46649
47261
  const parsed = JSON.parse(stripJsonComments(content));
46650
47262
  if (!Array.isArray(parsed)) {
46651
47263
  return true;
@@ -47396,8 +48008,8 @@ var init_bridges = __esm({
47396
48008
  // src/lib/memory.ts
47397
48009
  import { createWriteStream } from "node:fs";
47398
48010
  import { mkdir as mkdir4, readdir as readdir3, readFile as readFile6, writeFile as writeFile4 } from "node:fs/promises";
47399
- import { homedir as homedir26, tmpdir as tmpdir11 } from "node:os";
47400
- import { join as join40 } from "node:path";
48011
+ import { homedir as homedir27, tmpdir as tmpdir11 } from "node:os";
48012
+ import { join as join41 } from "node:path";
47401
48013
  import { pipeline } from "node:stream/promises";
47402
48014
  import { getHeapSnapshot, getHeapSpaceStatistics, getHeapStatistics } from "node:v8";
47403
48015
  async function captureMemoryDiagnostics(trigger) {
@@ -47471,11 +48083,11 @@ async function captureMemoryDiagnostics(trigger) {
47471
48083
  async function performHeapDump(trigger = "manual") {
47472
48084
  try {
47473
48085
  const diagnostics = await captureMemoryDiagnostics(trigger);
47474
- const dir2 = process.env.OPENJAW_HEAPDUMP_DIR?.trim() || join40(homedir26() || tmpdir11(), ".openjaw-agent", "heapdumps");
48086
+ const dir2 = process.env.OPENJAW_HEAPDUMP_DIR?.trim() || join41(homedir27() || tmpdir11(), ".openjaw-agent", "heapdumps");
47475
48087
  await mkdir4(dir2, { recursive: true });
47476
48088
  const base = `hermes-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-${process.pid}-${trigger}`;
47477
- const heapPath = join40(dir2, `${base}.heapsnapshot`);
47478
- const diagPath = join40(dir2, `${base}.diagnostics.json`);
48089
+ const heapPath = join41(dir2, `${base}.heapsnapshot`);
48090
+ const diagPath = join41(dir2, `${base}.diagnostics.json`);
47479
48091
  await writeFile4(diagPath, JSON.stringify(diagnostics, null, 2), { mode: 384 });
47480
48092
  await pipeline(getHeapSnapshot(), createWriteStream(heapPath, { mode: 384 }));
47481
48093
  return { diagPath, heapPath, success: true };
@@ -48332,15 +48944,15 @@ var init_ops = __esm({
48332
48944
  }
48333
48945
  const [a, b] = parts;
48334
48946
  const history = getSpawnHistory();
48335
- const resolve6 = /* @__PURE__ */ __name((token) => {
48947
+ const resolve7 = /* @__PURE__ */ __name((token) => {
48336
48948
  const n = parseInt(token, 10);
48337
48949
  if (Number.isFinite(n) && n >= 1 && n <= history.length) {
48338
48950
  return history[n - 1] ?? null;
48339
48951
  }
48340
48952
  return null;
48341
48953
  }, "resolve");
48342
- const baseline = resolve6(a);
48343
- const candidate = resolve6(b);
48954
+ const baseline = resolve7(a);
48955
+ const candidate = resolve7(b);
48344
48956
  if (!baseline || !candidate) {
48345
48957
  return ctx.transcript.sys(`replay-diff: could not resolve indices \xB7 history has ${history.length} entries`);
48346
48958
  }
@@ -49234,8 +49846,8 @@ var init_setup = __esm({
49234
49846
 
49235
49847
  // src/app/slash/catalog.ts
49236
49848
  import { readdirSync as readdirSync6 } from "node:fs";
49237
- import { homedir as homedir27 } from "node:os";
49238
- import { isAbsolute, join as join41, resolve as resolve3, sep } from "node:path";
49849
+ import { homedir as homedir28 } from "node:os";
49850
+ import { isAbsolute, join as join42, resolve as resolve4, sep } from "node:path";
49239
49851
  function buildCommandsCatalog(extras = {}) {
49240
49852
  const canon = {};
49241
49853
  const pairs = [];
@@ -49337,7 +49949,7 @@ function buildPathCompletions(word) {
49337
49949
  prefix = expanded.slice(lastSep + 1);
49338
49950
  }
49339
49951
  }
49340
- const resolved = isAbsolute(dir2) ? dir2 : resolve3(process.cwd(), dir2);
49952
+ const resolved = isAbsolute(dir2) ? dir2 : resolve4(process.cwd(), dir2);
49341
49953
  const entries = readdirSync6(resolved, { withFileTypes: true });
49342
49954
  const items = entries.filter((e) => e.name.toLowerCase().startsWith(prefix.toLowerCase())).filter((e) => prefix || !e.name.startsWith(".")).slice(0, 50).map((e) => {
49343
49955
  const full = dir2 + e.name + (e.isDirectory() ? sep : "");
@@ -49377,7 +49989,7 @@ var init_catalog = __esm({
49377
49989
  slashifyPair = /* @__PURE__ */ __name((cmd) => [`/${cmd.name}`, cmd.help ?? ""], "slashifyPair");
49378
49990
  __name(buildCommandsCatalog, "buildCommandsCatalog");
49379
49991
  __name(buildSlashCompletions, "buildSlashCompletions");
49380
- tildeExpand = /* @__PURE__ */ __name((p) => p.startsWith("~") ? join41(homedir27(), p.slice(1).replace(/^[\\/]/, "")) : p, "tildeExpand");
49992
+ tildeExpand = /* @__PURE__ */ __name((p) => p.startsWith("~") ? join42(homedir28(), p.slice(1).replace(/^[\\/]/, "")) : p, "tildeExpand");
49381
49993
  __name(buildPathCompletions, "buildPathCompletions");
49382
49994
  }
49383
49995
  });
@@ -49542,39 +50154,39 @@ var init_planner = __esm({
49542
50154
  });
49543
50155
 
49544
50156
  // src/workflows/persistence.ts
49545
- import { existsSync as existsSync31, mkdirSync as mkdirSync17, readdirSync as readdirSync7, readFileSync as readFileSync28, writeFileSync as writeFileSync19 } from "node:fs";
49546
- import { homedir as homedir28 } from "node:os";
49547
- import { basename as basename3, join as join42, resolve as resolve4 } from "node:path";
50157
+ import { existsSync as existsSync33, mkdirSync as mkdirSync17, readdirSync as readdirSync7, readFileSync as readFileSync29, writeFileSync as writeFileSync19 } from "node:fs";
50158
+ import { homedir as homedir29 } from "node:os";
50159
+ import { basename as basename3, join as join43, resolve as resolve5 } from "node:path";
49548
50160
  function ensureDir(path3) {
49549
- if (!existsSync31(path3)) mkdirSync17(path3, { recursive: true });
50161
+ if (!existsSync33(path3)) mkdirSync17(path3, { recursive: true });
49550
50162
  }
49551
50163
  function safeSegment(value) {
49552
50164
  return value.replace(/[^a-zA-Z0-9._-]+/g, "-").slice(0, 80) || "default";
49553
50165
  }
49554
50166
  function readJson(path3) {
49555
50167
  try {
49556
- return JSON.parse(readFileSync28(path3, "utf8"));
50168
+ return JSON.parse(readFileSync29(path3, "utf8"));
49557
50169
  } catch {
49558
50170
  return null;
49559
50171
  }
49560
50172
  }
49561
50173
  function saveWorkflowSnapshot(snapshot) {
49562
50174
  ensureDir(workflowDir());
49563
- const path3 = join42(workflowDir(), `${safeSegment(snapshot.id)}.json`);
50175
+ const path3 = join43(workflowDir(), `${safeSegment(snapshot.id)}.json`);
49564
50176
  writeFileSync19(path3, `${JSON.stringify(snapshot, null, 2)}
49565
50177
  `, "utf8");
49566
50178
  return path3;
49567
50179
  }
49568
50180
  function loadWorkflowSnapshot(id) {
49569
- const path3 = join42(workflowDir(), `${safeSegment(id)}.json`);
50181
+ const path3 = join43(workflowDir(), `${safeSegment(id)}.json`);
49570
50182
  return readJson(path3);
49571
50183
  }
49572
50184
  function listWorkflowSnapshots(limit = 30) {
49573
- if (!existsSync31(workflowDir())) return [];
50185
+ if (!existsSync33(workflowDir())) return [];
49574
50186
  const entries = [];
49575
50187
  for (const entry of readdirSync7(workflowDir(), { withFileTypes: true })) {
49576
50188
  if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
49577
- const path3 = join42(workflowDir(), entry.name);
50189
+ const path3 = join43(workflowDir(), entry.name);
49578
50190
  const snapshot = readJson(path3);
49579
50191
  if (!snapshot) continue;
49580
50192
  entries.push({
@@ -49592,10 +50204,10 @@ function listWorkflowSnapshots(limit = 30) {
49592
50204
  }
49593
50205
  function saveSpawnTreeSnapshot(input) {
49594
50206
  const sessionId = safeSegment(input.session_id ?? "default");
49595
- const dir2 = join42(spawnTreeDir(), sessionId);
50207
+ const dir2 = join43(spawnTreeDir(), sessionId);
49596
50208
  ensureDir(dir2);
49597
50209
  const stamp = new Date((input.finished_at ?? Date.now() / 1e3) * 1e3).toISOString().replace(/[:.]/g, "-");
49598
- const path3 = join42(dir2, `${stamp}-${Math.random().toString(36).slice(2, 8)}.json`);
50210
+ const path3 = join43(dir2, `${stamp}-${Math.random().toString(36).slice(2, 8)}.json`);
49599
50211
  const snapshot = {
49600
50212
  count: input.subagents?.length ?? 0,
49601
50213
  finished_at: input.finished_at,
@@ -49609,12 +50221,12 @@ function saveSpawnTreeSnapshot(input) {
49609
50221
  return path3;
49610
50222
  }
49611
50223
  function listSpawnTreeSnapshots(sessionId = "default", limit = 30) {
49612
- const dir2 = join42(spawnTreeDir(), safeSegment(sessionId));
49613
- if (!existsSync31(dir2)) return [];
50224
+ const dir2 = join43(spawnTreeDir(), safeSegment(sessionId));
50225
+ if (!existsSync33(dir2)) return [];
49614
50226
  const entries = [];
49615
50227
  for (const entry of readdirSync7(dir2, { withFileTypes: true })) {
49616
50228
  if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
49617
- const path3 = join42(dir2, entry.name);
50229
+ const path3 = join43(dir2, entry.name);
49618
50230
  const snapshot = readJson(path3);
49619
50231
  if (!snapshot) continue;
49620
50232
  entries.push({
@@ -49629,8 +50241,8 @@ function listSpawnTreeSnapshots(sessionId = "default", limit = 30) {
49629
50241
  return entries.sort((a, b) => (b.finished_at ?? 0) - (a.finished_at ?? 0)).slice(0, Math.max(1, limit));
49630
50242
  }
49631
50243
  function loadSpawnTreeSnapshot(path3) {
49632
- const root = resolve4(spawnTreeDir());
49633
- const resolved = resolve4(path3);
50244
+ const root = resolve5(spawnTreeDir());
50245
+ const resolved = resolve5(path3);
49634
50246
  if (!resolved.startsWith(root)) return null;
49635
50247
  const snapshot = readJson(resolved);
49636
50248
  return snapshot ? { ...snapshot, path: resolved } : null;
@@ -49639,9 +50251,9 @@ var rootDir, workflowDir, spawnTreeDir;
49639
50251
  var init_persistence = __esm({
49640
50252
  "src/workflows/persistence.ts"() {
49641
50253
  "use strict";
49642
- rootDir = /* @__PURE__ */ __name(() => join42(homedir28(), ".openjaw-agent"), "rootDir");
49643
- workflowDir = /* @__PURE__ */ __name(() => join42(rootDir(), "workflows"), "workflowDir");
49644
- spawnTreeDir = /* @__PURE__ */ __name(() => join42(rootDir(), "spawn-trees"), "spawnTreeDir");
50254
+ rootDir = /* @__PURE__ */ __name(() => join43(homedir29(), ".openjaw-agent"), "rootDir");
50255
+ workflowDir = /* @__PURE__ */ __name(() => join43(rootDir(), "workflows"), "workflowDir");
50256
+ spawnTreeDir = /* @__PURE__ */ __name(() => join43(rootDir(), "spawn-trees"), "spawnTreeDir");
49645
50257
  __name(ensureDir, "ensureDir");
49646
50258
  __name(safeSegment, "safeSegment");
49647
50259
  __name(readJson, "readJson");
@@ -50221,11 +50833,11 @@ ${highlights}` : ""
50221
50833
  });
50222
50834
 
50223
50835
  // src/rpcHandlers.ts
50224
- import { spawn as spawn6 } from "node:child_process";
50836
+ import { spawn as spawn8 } from "node:child_process";
50225
50837
  import { randomUUID as randomUUID14 } from "node:crypto";
50226
- import { existsSync as existsSync32, mkdirSync as mkdirSync18, readFileSync as readFileSync29, rmSync, statSync as statSync4, writeFileSync as writeFileSync20 } from "node:fs";
50227
- import { homedir as homedir29 } from "node:os";
50228
- import { basename as basename4, extname as extname4, join as join43 } from "node:path";
50838
+ import { existsSync as existsSync34, mkdirSync as mkdirSync18, readFileSync as readFileSync30, rmSync, statSync as statSync4, writeFileSync as writeFileSync20 } from "node:fs";
50839
+ import { homedir as homedir30 } from "node:os";
50840
+ import { basename as basename4, extname as extname4, join as join44 } from "node:path";
50229
50841
  function registerRpcHandlers(options) {
50230
50842
  const { agentConfig, agentLoop, bridgeEmitter, bridgeManager, bus, mcpManager, systemPromptFn, toolRegistry, voiceManager } = options;
50231
50843
  let currentRun = null;
@@ -50450,6 +51062,7 @@ ${helpMessage}` : field.label;
50450
51062
  const modelInfo = agentLoop.getActiveModelMetadata();
50451
51063
  const nextEffort = parsed.clear ? void 0 : modelInfo ? resolveReasoningEffortForModel(modelInfo, parsed.effort) : parsed.effort;
50452
51064
  agentLoop.updateReasoningEffort(nextEffort);
51065
+ agentConfig.llm.model_reasoning_effort = nextEffort;
50453
51066
  bus.emitEvent({
50454
51067
  payload: sessionInfoSnapshot(agentLoop, toolRegistry),
50455
51068
  session_id: agentLoop.sessionId,
@@ -50539,17 +51152,39 @@ ${helpMessage}` : field.label;
50539
51152
  return { ok: false };
50540
51153
  }
50541
51154
  });
50542
- bus.registerRpc("session.create", () => ({
50543
- info: sessionInfoSnapshot(agentLoop, toolRegistry),
50544
- session_id: agentLoop.sessionId
50545
- }));
51155
+ bus.registerRpc("session.create", () => {
51156
+ if (agentLoop.turnCount === 0 && agentLoop.history.length === 0) {
51157
+ return {
51158
+ info: sessionInfoSnapshot(agentLoop, toolRegistry),
51159
+ session_id: agentLoop.sessionId
51160
+ };
51161
+ }
51162
+ const created = agentLoop.createAndSwitchTo();
51163
+ if (!created) {
51164
+ return {
51165
+ error: "cannot create a new session while a turn is in flight",
51166
+ session_id: agentLoop.sessionId
51167
+ };
51168
+ }
51169
+ bus.emitEvent({
51170
+ payload: sessionInfoSnapshot(agentLoop, toolRegistry),
51171
+ session_id: agentLoop.sessionId,
51172
+ type: "session.info"
51173
+ });
51174
+ return {
51175
+ info: sessionInfoSnapshot(agentLoop, toolRegistry),
51176
+ session_id: agentLoop.sessionId
51177
+ };
51178
+ });
50546
51179
  bus.registerRpc("session.resume", (params) => {
50547
51180
  const id = String(params.session_id ?? agentLoop.sessionId);
50548
- const data = loadSession(id);
51181
+ const swapped = agentLoop.swapToSession(id);
51182
+ const data = swapped ?? loadSession(id);
51183
+ const visibleMessages = (data?.messages ?? []).filter((m) => !messageIsInternalSelfPrompt(m));
50549
51184
  return {
50550
51185
  info: sessionInfoSnapshot(agentLoop, toolRegistry),
50551
- message_count: data?.messages.length ?? 0,
50552
- messages: (data?.messages ?? []).map((m) => {
51186
+ message_count: visibleMessages.length,
51187
+ messages: visibleMessages.map((m) => {
50553
51188
  if (m.role === "tool_result") {
50554
51189
  return {
50555
51190
  role: "tool",
@@ -50557,13 +51192,14 @@ ${helpMessage}` : field.label;
50557
51192
  };
50558
51193
  }
50559
51194
  const content = m.content;
51195
+ const raw = typeof content === "string" ? content : Array.isArray(content) ? content.map((c) => c.type === "text" ? c.text : "[image]").join("") : "";
50560
51196
  return {
50561
51197
  role: m.role,
50562
- text: typeof content === "string" ? content : Array.isArray(content) ? content.map((c) => c.type === "text" ? c.text : "[image]").join("") : ""
51198
+ text: stripDisplayPrefix(raw, m.role)
50563
51199
  };
50564
51200
  }),
50565
- resumed: data ? id : void 0,
50566
- session_id: id
51201
+ resumed: swapped ? id : void 0,
51202
+ session_id: agentLoop.sessionId
50567
51203
  };
50568
51204
  });
50569
51205
  bus.registerRpc("session.list", () => ({
@@ -50573,6 +51209,10 @@ ${helpMessage}` : field.label;
50573
51209
  preview: s.summary,
50574
51210
  source: `${s.provider}/${s.model}`,
50575
51211
  started_at: Date.parse(s.createdAt) || Date.now(),
51212
+ // updated_at drives the sidebar's "last activity" date grouping.
51213
+ // listSessions already sorts by updatedAt desc, so emitting it
51214
+ // here saves the renderer a second parse.
51215
+ updated_at: Date.parse(s.updatedAt) || Date.now(),
50576
51216
  title: s.summary || s.id
50577
51217
  }))
50578
51218
  }));
@@ -50607,12 +51247,12 @@ ${helpMessage}` : field.label;
50607
51247
  const id = String(params.session_id ?? agentLoop.sessionId) || agentLoop.sessionId;
50608
51248
  const data = loadSession(id);
50609
51249
  const messages = data?.messages ?? agentLoop.history;
50610
- const exportDir = join43(homedir29(), ".openjaw-agent", "exports");
50611
- if (!existsSync32(exportDir)) {
51250
+ const exportDir = join44(homedir30(), ".openjaw-agent", "exports");
51251
+ if (!existsSync34(exportDir)) {
50612
51252
  mkdirSync18(exportDir, { recursive: true });
50613
51253
  }
50614
51254
  const safeId = id.replace(/[^a-zA-Z0-9_.-]/g, "_") || agentLoop.sessionId;
50615
- const file2 = join43(exportDir, `session-${safeId}.md`);
51255
+ const file2 = join44(exportDir, `session-${safeId}.md`);
50616
51256
  const lines = [
50617
51257
  `# OpenJaw Agent Session ${id}`,
50618
51258
  `Date: ${(/* @__PURE__ */ new Date()).toISOString()}`,
@@ -50642,8 +51282,8 @@ ${helpMessage}` : field.label;
50642
51282
  return { deleted: "" };
50643
51283
  }
50644
51284
  try {
50645
- const file2 = join43(homedir29(), ".openjaw-agent", "sessions", `${id}.json`);
50646
- if (existsSync32(file2)) {
51285
+ const file2 = join44(homedir30(), ".openjaw-agent", "sessions", `${id}.json`);
51286
+ if (existsSync34(file2)) {
50647
51287
  rmSync(file2);
50648
51288
  }
50649
51289
  return { deleted: id };
@@ -50716,11 +51356,11 @@ ${helpMessage}` : field.label;
50716
51356
  bus.registerRpc("shell.exec", async (params) => {
50717
51357
  const command = String(params.command ?? "");
50718
51358
  if (!command) return { code: -1, stderr: "empty command" };
50719
- return await new Promise((resolve6) => {
51359
+ return await new Promise((resolve7) => {
50720
51360
  const isWin = process.platform === "win32";
50721
51361
  const shell = isWin ? "powershell.exe" : "sh";
50722
51362
  const args = isWin ? ["-NoProfile", "-Command", command] : ["-c", command];
50723
- const child = spawn6(shell, args, { timeout: 3e4 });
51363
+ const child = spawn8(shell, args, { timeout: 3e4 });
50724
51364
  let stdout = "";
50725
51365
  let stderr = "";
50726
51366
  let truncated = false;
@@ -50743,10 +51383,10 @@ ${helpMessage}` : field.label;
50743
51383
  if (truncated) {
50744
51384
  stderr += "\n[output truncated to 1MB]";
50745
51385
  }
50746
- resolve6({ code: code ?? -1, stderr, stdout });
51386
+ resolve7({ code: code ?? -1, stderr, stdout });
50747
51387
  });
50748
51388
  child.on("error", (err) => {
50749
- resolve6({ code: -1, stderr: err.message });
51389
+ resolve7({ code: -1, stderr: err.message });
50750
51390
  });
50751
51391
  });
50752
51392
  });
@@ -50777,6 +51417,11 @@ ${helpMessage}` : field.label;
50777
51417
  const modelInfo = agentLoop.getModelMetadata(provider, model);
50778
51418
  const reasoningEffort = parsedEffort?.clear ? void 0 : parsedEffort?.effort ? modelInfo ? resolveReasoningEffortForModel(modelInfo, parsedEffort.effort) : parsedEffort.effort : modelInfo ? resolveReasoningEffortForModel(modelInfo, agentLoop.reasoningEffort) : agentLoop.reasoningEffort;
50779
51419
  agentLoop.switchModel(provider, model, reasoningEffort);
51420
+ bus.emitEvent({
51421
+ payload: sessionInfoSnapshot(agentLoop, toolRegistry),
51422
+ session_id: agentLoop.sessionId,
51423
+ type: "session.info"
51424
+ });
50780
51425
  return {
50781
51426
  info: sessionInfoSnapshot(agentLoop, toolRegistry),
50782
51427
  ok: true
@@ -50854,8 +51499,8 @@ ${helpMessage}` : field.label;
50854
51499
  if (auth.auth_type !== "oauth") {
50855
51500
  throw new Error(`${PROVIDER_LABELS[slug]} does not use OAuth (auth_type=${auth.auth_type})`);
50856
51501
  }
50857
- const clientId = resolveCopilotClientId();
50858
- if (!clientId) {
51502
+ const clientId2 = resolveCopilotClientId();
51503
+ if (!clientId2) {
50859
51504
  throw new Error(
50860
51505
  "GitHub Copilot login needs a GitHub OAuth App client ID. Set GITHUB_COPILOT_CLIENT_ID or llm.copilot_oauth_client_id in ~/.openjaw-agent/config.yaml."
50861
51506
  );
@@ -50867,7 +51512,7 @@ ${helpMessage}` : field.label;
50867
51512
  }
50868
51513
  return agentConfig.llm.copilot_enterprise_url;
50869
51514
  })();
50870
- const flow = await startCopilotDeviceFlow(clientId, enterpriseUrl);
51515
+ const flow = await startCopilotDeviceFlow(clientId2, enterpriseUrl);
50871
51516
  const flowId = randomUUID14();
50872
51517
  const controller = new AbortController();
50873
51518
  const timer = setTimeout(() => {
@@ -51388,13 +52033,13 @@ ${helpMessage}` : field.label;
51388
52033
  const firstSpace = raw.search(/\s/);
51389
52034
  const path3 = firstSpace > 0 ? raw.slice(0, firstSpace) : raw;
51390
52035
  const remainder = firstSpace > 0 ? raw.slice(firstSpace).trim() : "";
51391
- if (!existsSync32(path3)) {
52036
+ if (!existsSync34(path3)) {
51392
52037
  throw new Error(`image.attach: file not found: ${path3}`);
51393
52038
  }
51394
52039
  let buffer;
51395
52040
  let fileSize = 0;
51396
52041
  try {
51397
- buffer = readFileSync29(path3);
52042
+ buffer = readFileSync30(path3);
51398
52043
  fileSize = statSync4(path3).size;
51399
52044
  } catch (err) {
51400
52045
  throw new Error(`image.attach: ${err instanceof Error ? err.message : String(err)}`);
@@ -51440,13 +52085,13 @@ ${helpMessage}` : field.label;
51440
52085
  }
51441
52086
  });
51442
52087
  bus.registerRpc("reload.env", () => {
51443
- const envPath = join43(homedir29(), ".openjaw-agent", ".env");
51444
- if (!existsSync32(envPath)) {
52088
+ const envPath = join44(homedir30(), ".openjaw-agent", ".env");
52089
+ if (!existsSync34(envPath)) {
51445
52090
  return { updated: 0 };
51446
52091
  }
51447
52092
  let updated = 0;
51448
52093
  try {
51449
- const raw = readFileSync29(envPath, "utf-8");
52094
+ const raw = readFileSync30(envPath, "utf-8");
51450
52095
  for (const line of raw.split(/\r?\n/)) {
51451
52096
  const trimmed = line.trim();
51452
52097
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -51625,7 +52270,7 @@ ${helpMessage}` : field.label;
51625
52270
  }
51626
52271
  };
51627
52272
  }
51628
- var PROVIDERS2, PROVIDER_LABELS, PROVIDER_AUTH, isProviderAuthenticated, buildProviderOption, fetchLiveModels, parseReasoningEffortInput, skillDescription, listRegistrySkills, registrySkillsByCategory, formatSkillExecutionResult, reasoningEffortsForModel, resolveReasoningEffortForModel, BRIDGE_SOURCES, isBridgeSource, inferBridgeSource, bridgeLabels, bridgeEventLabels, stripBridgeGlyph, firstLogLine, bridgeUser, formatBridgeText, runProcess, contentToText, sessionMessageToMarkdown, sessionInfoSnapshot, MCP_TOOL_PREFIX, MAX_TOTAL_TOOLS, syncMcpTools;
52273
+ var PROVIDERS2, PROVIDER_LABELS, PROVIDER_AUTH, isProviderAuthenticated, buildProviderOption, fetchLiveModels, parseReasoningEffortInput, skillDescription, listRegistrySkills, registrySkillsByCategory, formatSkillExecutionResult, reasoningEffortsForModel, resolveReasoningEffortForModel, BRIDGE_SOURCES, isBridgeSource, inferBridgeSource, bridgeLabels, bridgeEventLabels, stripBridgeGlyph, firstLogLine, bridgeUser, formatBridgeText, runProcess, contentToText, sessionMessageToMarkdown, DISPLAY_PREFIX_PATTERNS, stripDisplayPrefix, INTERNAL_SELF_PROMPT_PATTERNS, isInternalSelfPrompt, messageIsInternalSelfPrompt, sessionInfoSnapshot, MCP_TOOL_PREFIX, MAX_TOTAL_TOOLS, syncMcpTools;
51629
52274
  var init_rpcHandlers = __esm({
51630
52275
  "src/rpcHandlers.ts"() {
51631
52276
  "use strict";
@@ -51835,8 +52480,8 @@ var init_rpcHandlers = __esm({
51835
52480
  ${raw}`;
51836
52481
  return `${header}: ${raw}`;
51837
52482
  }, "formatBridgeText");
51838
- runProcess = /* @__PURE__ */ __name((command, args, timeout = 2e4) => new Promise((resolve6) => {
51839
- const child = spawn6(command, args, { timeout, windowsHide: true });
52483
+ runProcess = /* @__PURE__ */ __name((command, args, timeout = 2e4) => new Promise((resolve7) => {
52484
+ const child = spawn8(command, args, { timeout, windowsHide: true });
51840
52485
  let stdout = "";
51841
52486
  let stderr = "";
51842
52487
  let truncated = false;
@@ -51859,9 +52504,9 @@ ${raw}`;
51859
52504
  if (truncated) {
51860
52505
  stderr += "\n[output truncated to 1MB]";
51861
52506
  }
51862
- resolve6({ code: code ?? -1, stderr, stdout });
52507
+ resolve7({ code: code ?? -1, stderr, stdout });
51863
52508
  });
51864
- child.on("error", (err) => resolve6({ code: -1, stderr: err.message, stdout }));
52509
+ child.on("error", (err) => resolve7({ code: -1, stderr: err.message, stdout }));
51865
52510
  }), "runProcess");
51866
52511
  contentToText = /* @__PURE__ */ __name((content) => {
51867
52512
  if (typeof content === "string") return content;
@@ -51885,6 +52530,27 @@ ${raw}`;
51885
52530
  }
51886
52531
  return [];
51887
52532
  }, "sessionMessageToMarkdown");
52533
+ DISPLAY_PREFIX_PATTERNS = [
52534
+ /^\[USER TASK\]:\s*/
52535
+ ];
52536
+ stripDisplayPrefix = /* @__PURE__ */ __name((text, role) => {
52537
+ if (role !== "user") return text;
52538
+ let out = text;
52539
+ for (const pattern of DISPLAY_PREFIX_PATTERNS) {
52540
+ out = out.replace(pattern, "");
52541
+ }
52542
+ return out;
52543
+ }, "stripDisplayPrefix");
52544
+ INTERNAL_SELF_PROMPT_PATTERNS = [
52545
+ /^\[System:\s/
52546
+ ];
52547
+ isInternalSelfPrompt = /* @__PURE__ */ __name((rawText) => INTERNAL_SELF_PROMPT_PATTERNS.some((p) => p.test(rawText)), "isInternalSelfPrompt");
52548
+ messageIsInternalSelfPrompt = /* @__PURE__ */ __name((m) => {
52549
+ if (m.role !== "user") return false;
52550
+ const content = m.content;
52551
+ const raw = typeof content === "string" ? content : Array.isArray(content) ? content.map((c) => c.type === "text" ? c.text : "").join("") : "";
52552
+ return isInternalSelfPrompt(raw);
52553
+ }, "messageIsInternalSelfPrompt");
51888
52554
  sessionInfoSnapshot = /* @__PURE__ */ __name((agentLoop, toolRegistry) => ({
51889
52555
  cwd: process.cwd(),
51890
52556
  model: agentLoop.model,
@@ -52019,14 +52685,14 @@ var init_memoryMonitor = __esm({
52019
52685
  });
52020
52686
 
52021
52687
  // src/lib/openExternalUrl.ts
52022
- import { spawn as spawn7 } from "node:child_process";
52688
+ import { spawn as spawn9 } from "node:child_process";
52023
52689
  import { platform } from "node:os";
52024
52690
  function openExternalUrl(rawUrl, dependencies = {}) {
52025
52691
  const url = parseSafeUrl(rawUrl);
52026
52692
  if (!url) {
52027
52693
  return false;
52028
52694
  }
52029
- const spawnFn = dependencies.spawn ?? spawn7;
52695
+ const spawnFn = dependencies.spawn ?? spawn9;
52030
52696
  const platformId = dependencies.platform?.() ?? platform();
52031
52697
  const command = openCommand(platformId);
52032
52698
  if (!command) {
@@ -52519,9 +53185,9 @@ var init_useVirtualHistory = __esm({
52519
53185
  viewportHeight
52520
53186
  }) => itemCount > 0 && viewportHeight > 0 && !sticky && !liveTailActive, "shouldSetVirtualClamp");
52521
53187
  ensureVirtualItemHeight = /* @__PURE__ */ __name((heights, key, index, estimate, estimateHeight) => {
52522
- const cached7 = heights.get(key);
52523
- if (cached7 !== void 0) {
52524
- return Math.max(1, Math.floor(cached7));
53188
+ const cached8 = heights.get(key);
53189
+ if (cached8 !== void 0) {
53190
+ return Math.max(1, Math.floor(cached8));
52525
53191
  }
52526
53192
  const seeded = Math.max(1, Math.floor(estimateHeight?.(index, key) ?? estimate));
52527
53193
  heights.set(key, seeded);
@@ -53959,7 +54625,7 @@ function createGatewayEventHandler(ctx) {
53959
54625
  setTimeout(async () => {
53960
54626
  let sid = getUiState().sid;
53961
54627
  for (let i = 0; !sid && i < 40; i += 1) {
53962
- await new Promise((resolve6) => setTimeout(resolve6, 100));
54628
+ await new Promise((resolve7) => setTimeout(resolve7, 100));
53963
54629
  sid = getUiState().sid;
53964
54630
  }
53965
54631
  if (!sid) {
@@ -54706,21 +55372,21 @@ var init_useCompletion = __esm({
54706
55372
  });
54707
55373
 
54708
55374
  // src/lib/history.ts
54709
- import { appendFileSync as appendFileSync4, existsSync as existsSync33, mkdirSync as mkdirSync19, readFileSync as readFileSync30 } from "node:fs";
54710
- import { homedir as homedir30 } from "node:os";
54711
- import { join as join44 } from "node:path";
55375
+ import { appendFileSync as appendFileSync4, existsSync as existsSync35, mkdirSync as mkdirSync19, readFileSync as readFileSync31 } from "node:fs";
55376
+ import { homedir as homedir31 } from "node:os";
55377
+ import { join as join45 } from "node:path";
54712
55378
  function load() {
54713
55379
  if (cache3) {
54714
55380
  return cache3;
54715
55381
  }
54716
55382
  try {
54717
- if (!existsSync33(file)) {
55383
+ if (!existsSync35(file)) {
54718
55384
  cache3 = [];
54719
55385
  return cache3;
54720
55386
  }
54721
55387
  const entries = [];
54722
55388
  let current = [];
54723
- for (const line of readFileSync30(file, "utf8").split("\n")) {
55389
+ for (const line of readFileSync31(file, "utf8").split("\n")) {
54724
55390
  if (line.startsWith("+")) {
54725
55391
  current.push(line.slice(1));
54726
55392
  } else if (current.length) {
@@ -54751,7 +55417,7 @@ function append(line) {
54751
55417
  items.splice(0, items.length - MAX);
54752
55418
  }
54753
55419
  try {
54754
- if (!existsSync33(dir)) {
55420
+ if (!existsSync35(dir)) {
54755
55421
  mkdirSync19(dir, { recursive: true });
54756
55422
  }
54757
55423
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace("Z", "");
@@ -54768,8 +55434,8 @@ var init_history = __esm({
54768
55434
  "src/lib/history.ts"() {
54769
55435
  "use strict";
54770
55436
  MAX = 1e3;
54771
- dir = process.env.OPENJAW_HOME ?? join44(homedir30(), ".openjaw-agent");
54772
- file = join44(dir, ".openjaw-agent_history");
55437
+ dir = process.env.OPENJAW_HOME ?? join45(homedir31(), ".openjaw-agent");
55438
+ file = join45(dir, ".openjaw-agent_history");
54773
55439
  cache3 = null;
54774
55440
  __name(load, "load");
54775
55441
  __name(append, "append");
@@ -54862,8 +55528,8 @@ var init_useQueue = __esm({
54862
55528
  });
54863
55529
 
54864
55530
  // src/lib/editor.ts
54865
- import { accessSync, constants } from "node:fs";
54866
- import { delimiter, join as join45 } from "node:path";
55531
+ import { accessSync as accessSync2, constants as constants2 } from "node:fs";
55532
+ import { delimiter, join as join46 } from "node:path";
54867
55533
  var FALLBACKS, isExecutable, resolveEditor;
54868
55534
  var init_editor = __esm({
54869
55535
  "src/lib/editor.ts"() {
@@ -54871,7 +55537,7 @@ var init_editor = __esm({
54871
55537
  FALLBACKS = ["editor", "nano", "pico", "vi", "emacs"];
54872
55538
  isExecutable = /* @__PURE__ */ __name((path3) => {
54873
55539
  try {
54874
- accessSync(path3, constants.X_OK);
55540
+ accessSync2(path3, constants2.X_OK);
54875
55541
  return true;
54876
55542
  } catch {
54877
55543
  return false;
@@ -54886,7 +55552,7 @@ var init_editor = __esm({
54886
55552
  return ["notepad.exe"];
54887
55553
  }
54888
55554
  const dirs = (env2.PATH ?? "").split(delimiter).filter(Boolean);
54889
- const found = FALLBACKS.flatMap((name) => dirs.map((d) => join45(d, name))).find(isExecutable);
55555
+ const found = FALLBACKS.flatMap((name) => dirs.map((d) => join46(d, name))).find(isExecutable);
54890
55556
  return [found ?? "vi"];
54891
55557
  }, "resolveEditor");
54892
55558
  }
@@ -54894,9 +55560,9 @@ var init_editor = __esm({
54894
55560
 
54895
55561
  // src/app/useComposerState.ts
54896
55562
  import { spawnSync } from "node:child_process";
54897
- import { mkdtempSync, readFileSync as readFileSync31, rmSync as rmSync2, writeFileSync as writeFileSync21 } from "node:fs";
55563
+ import { mkdtempSync, readFileSync as readFileSync32, rmSync as rmSync2, writeFileSync as writeFileSync21 } from "node:fs";
54898
55564
  import { tmpdir as tmpdir12 } from "node:os";
54899
- import { join as join46 } from "node:path";
55565
+ import { join as join47 } from "node:path";
54900
55566
  import { useStore } from "@nanostores/react";
54901
55567
  import { useCallback as useCallback7, useMemo as useMemo6, useState as useState12 } from "react";
54902
55568
  function insertAtCursor(value, cursor, text) {
@@ -55053,8 +55719,8 @@ function useComposerState({
55053
55719
  [handleResolvedPaste, onClipboardPaste, querier]
55054
55720
  );
55055
55721
  const openEditor = useCallback7(async () => {
55056
- const dir2 = mkdtempSync(join46(tmpdir12(), "hermes-"));
55057
- const file2 = join46(dir2, "prompt.md");
55722
+ const dir2 = mkdtempSync(join47(tmpdir12(), "hermes-"));
55723
+ const file2 = join47(dir2, "prompt.md");
55058
55724
  const [cmd, ...args] = resolveEditor();
55059
55725
  writeFileSync21(file2, [...inputBuf, input].join("\n"));
55060
55726
  let exitCode = null;
@@ -55065,7 +55731,7 @@ function useComposerState({
55065
55731
  if (exitCode !== 0) {
55066
55732
  return;
55067
55733
  }
55068
- const text = readFileSync31(file2, "utf8").trimEnd();
55734
+ const text = readFileSync32(file2, "utf8").trimEnd();
55069
55735
  if (!text) {
55070
55736
  return;
55071
55737
  }
@@ -57147,8 +57813,8 @@ __export(perfPane_exports, {
57147
57813
  logFrameEvent: () => logFrameEvent
57148
57814
  });
57149
57815
  import { appendFileSync as appendFileSync5, mkdirSync as mkdirSync20 } from "node:fs";
57150
- import { homedir as homedir31 } from "node:os";
57151
- import { dirname as dirname7, join as join47 } from "node:path";
57816
+ import { homedir as homedir32 } from "node:os";
57817
+ import { dirname as dirname9, join as join48 } from "node:path";
57152
57818
  import { Profiler } from "react";
57153
57819
  import { jsx as jsx17 } from "react/jsx-runtime";
57154
57820
  function PerfPane({ children, id }) {
@@ -57164,13 +57830,13 @@ var init_perfPane = __esm({
57164
57830
  init_entry_exports();
57165
57831
  ENABLED = /^(?:1|true|yes|on)$/i.test((process.env.OPENJAW_DEV_PERF ?? "").trim());
57166
57832
  THRESHOLD_MS = Number(process.env.OPENJAW_DEV_PERF_MS ?? "2") || 0;
57167
- LOG_PATH2 = process.env.OPENJAW_DEV_PERF_LOG?.trim() || join47(homedir31(), ".openjaw-agent", "perf.log");
57833
+ LOG_PATH2 = process.env.OPENJAW_DEV_PERF_LOG?.trim() || join48(homedir32(), ".openjaw-agent", "perf.log");
57168
57834
  logReady = false;
57169
57835
  writeRow = /* @__PURE__ */ __name((row) => {
57170
57836
  if (!logReady) {
57171
57837
  logReady = true;
57172
57838
  try {
57173
- mkdirSync20(dirname7(LOG_PATH2), { recursive: true });
57839
+ mkdirSync20(dirname9(LOG_PATH2), { recursive: true });
57174
57840
  } catch {
57175
57841
  }
57176
57842
  }
@@ -62428,7 +63094,7 @@ var init_mathUnicode = __esm({
62428
63094
 
62429
63095
  // src/lib/syntax.ts
62430
63096
  function highlightLine(line, lang, t) {
62431
- const spec = resolve5(lang);
63097
+ const spec = resolve6(lang);
62432
63098
  if (!spec) {
62433
63099
  return [["", line]];
62434
63100
  }
@@ -62460,7 +63126,7 @@ function highlightLine(line, lang, t) {
62460
63126
  }
62461
63127
  return tokens;
62462
63128
  }
62463
- var KW, TS, PY, SH, GO, RUST, SQL, LANGS, ALIAS, resolve5, isHighlightable, TOKEN_RE;
63129
+ var KW, TS, PY, SH, GO, RUST, SQL, LANGS, ALIAS, resolve6, isHighlightable, TOKEN_RE;
62464
63130
  var init_syntax = __esm({
62465
63131
  "src/lib/syntax.ts"() {
62466
63132
  "use strict";
@@ -62514,8 +63180,8 @@ var init_syntax = __esm({
62514
63180
  yml: "yaml",
62515
63181
  zsh: "sh"
62516
63182
  };
62517
- resolve5 = /* @__PURE__ */ __name((lang) => LANGS[ALIAS[lang] ?? lang] ?? null, "resolve");
62518
- isHighlightable = /* @__PURE__ */ __name((lang) => resolve5(lang) !== null, "isHighlightable");
63183
+ resolve6 = /* @__PURE__ */ __name((lang) => LANGS[ALIAS[lang] ?? lang] ?? null, "resolve");
63184
+ isHighlightable = /* @__PURE__ */ __name((lang) => resolve6(lang) !== null, "isHighlightable");
62519
63185
  TOKEN_RE = /'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*`|\b\d+(?:\.\d+)?\b|[A-Za-z_$][\w$]*/g;
62520
63186
  __name(highlightLine, "highlightLine");
62521
63187
  }
@@ -62615,9 +63281,9 @@ function MdImpl({ compact, t, text }) {
62615
63281
  const nodes = useMemo14(() => {
62616
63282
  const bucket = cacheBucket(t);
62617
63283
  const cacheKey = `${compact ? "1" : "0"}|${text}`;
62618
- const cached7 = cacheGet(bucket, cacheKey);
62619
- if (cached7) {
62620
- return cached7;
63284
+ const cached8 = cacheGet(bucket, cacheKey);
63285
+ if (cached8) {
63286
+ return cached8;
62621
63287
  }
62622
63288
  const lines = ensureEmojiPresentation(text).split("\n");
62623
63289
  const nodes2 = [];
@@ -64850,12 +65516,18 @@ var init_entry = __esm({
64850
65516
  // src/main.ts
64851
65517
  async function main() {
64852
65518
  const args = process.argv.slice(2);
65519
+ if (args[0] === "app") {
65520
+ const { launchDesktop: launchDesktop2 } = await Promise.resolve().then(() => (init_launcher(), launcher_exports));
65521
+ await launchDesktop2(args.slice(1));
65522
+ return;
65523
+ }
64853
65524
  if (args.includes("--help") || args.includes("-h")) {
64854
65525
  console.log(`
64855
65526
  OpenJaw Agent \u2014 Autonomous desktop AI assistant
64856
65527
 
64857
65528
  Usage:
64858
65529
  openjaw-agent Start new session (Ink UI)
65530
+ openjaw-agent app Start the Electron desktop UI (optional)
64859
65531
  openjaw-agent --legacy-ui Start with the previous Ink UI (pre-rewrite)
64860
65532
  openjaw-agent --telegram Ink UI + Telegram bridge (hybrid)
64861
65533
  openjaw-agent --telegram --headless Telegram only (no desktop UI)