poe-code 3.0.72 → 3.0.73

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.
@@ -0,0 +1,3930 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __esm = (fn, res) => function __init() {
8
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
9
+ };
10
+ var __commonJS = (cb, mod) => function __require() {
11
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
12
+ };
13
+ var __export = (target, all) => {
14
+ for (var name in all)
15
+ __defProp(target, name, { get: all[name], enumerable: true });
16
+ };
17
+ var __copyProps = (to, from, except, desc) => {
18
+ if (from && typeof from === "object" || typeof from === "function") {
19
+ for (let key of __getOwnPropNames(from))
20
+ if (!__hasOwnProp.call(to, key) && key !== except)
21
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
22
+ }
23
+ return to;
24
+ };
25
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
26
+ // If the importer is in node compatibility mode or this is not an ESM
27
+ // file that has been converted to a CommonJS file using a Babel-
28
+ // compatible transform (i.e. "__esModule" has not been set), then set
29
+ // "default" to the CommonJS "module.exports" for node compatibility.
30
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
31
+ mod
32
+ ));
33
+
34
+ // src/templates/python/env.hbs
35
+ var require_env = __commonJS({
36
+ "src/templates/python/env.hbs"(exports, module) {
37
+ module.exports = "POE_API_KEY={{apiKey}}\nPOE_BASE_URL=https://api.poe.com/v1\nMODEL={{model}}\n";
38
+ }
39
+ });
40
+
41
+ // src/templates/python/main.py.hbs
42
+ var require_main_py = __commonJS({
43
+ "src/templates/python/main.py.hbs"(exports, module) {
44
+ module.exports = 'import os\nfrom openai import OpenAI\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nclient = OpenAI(\n api_key=os.getenv("POE_API_KEY"),\n base_url=os.getenv("POE_BASE_URL")\n)\n\nresponse = client.chat.completions.create(\n model=os.getenv("MODEL", "{{model}}"),\n messages=[{"role": "user", "content": "Tell me a joke"}]\n)\n\nprint(response.choices[0].message.content)\n';
45
+ }
46
+ });
47
+
48
+ // src/templates/python/requirements.txt.hbs
49
+ var require_requirements_txt = __commonJS({
50
+ "src/templates/python/requirements.txt.hbs"(exports, module) {
51
+ module.exports = "openai>=1.0.0\npython-dotenv>=1.0.0\n";
52
+ }
53
+ });
54
+
55
+ // src/templates/codex/config.toml.hbs
56
+ var require_config_toml = __commonJS({
57
+ "src/templates/codex/config.toml.hbs"(exports, module) {
58
+ module.exports = 'model_provider = "poe"\nmodel = "{{{model}}}"\nmodel_reasoning_effort = "{{reasoningEffort}}"\nmodel_verbosity = "medium"\n\n[model_providers.poe]\nname = "poe"\nbase_url = "{{{baseUrl}}}"\nwire_api = "responses"\nexperimental_bearer_token = "{{apiKey}}"\n';
59
+ }
60
+ });
61
+
62
+ // packages/auth/src/encrypted-file-auth-store.ts
63
+ import { createCipheriv, createDecipheriv, randomBytes, scrypt } from "node:crypto";
64
+ import { promises as fs } from "node:fs";
65
+ import { homedir as homedir2, hostname, userInfo } from "node:os";
66
+ import path2 from "node:path";
67
+ function defaultMachineIdentity() {
68
+ return {
69
+ hostname: hostname(),
70
+ username: userInfo().username
71
+ };
72
+ }
73
+ async function deriveEncryptionKey(getMachineIdentity) {
74
+ const machineIdentity = await getMachineIdentity();
75
+ const secret = `${machineIdentity.hostname}:${machineIdentity.username}`;
76
+ return await new Promise((resolve, reject) => {
77
+ scrypt(secret, ENCRYPTION_SALT, ENCRYPTION_KEY_BYTES, (error, derivedKey) => {
78
+ if (error) {
79
+ reject(error);
80
+ return;
81
+ }
82
+ resolve(Buffer.from(derivedKey));
83
+ });
84
+ });
85
+ }
86
+ function parseEncryptedDocument(raw) {
87
+ try {
88
+ const parsed = JSON.parse(raw);
89
+ if (!isRecord(parsed)) {
90
+ return null;
91
+ }
92
+ if (parsed.version !== ENCRYPTION_VERSION) {
93
+ return null;
94
+ }
95
+ if (typeof parsed.iv !== "string" || typeof parsed.authTag !== "string" || typeof parsed.ciphertext !== "string") {
96
+ return null;
97
+ }
98
+ return {
99
+ version: parsed.version,
100
+ iv: parsed.iv,
101
+ authTag: parsed.authTag,
102
+ ciphertext: parsed.ciphertext
103
+ };
104
+ } catch {
105
+ return null;
106
+ }
107
+ }
108
+ function isRecord(value) {
109
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
110
+ }
111
+ function isNotFoundError(error) {
112
+ return Boolean(
113
+ error && typeof error === "object" && "code" in error && error.code === "ENOENT"
114
+ );
115
+ }
116
+ var ENCRYPTION_ALGORITHM, ENCRYPTION_VERSION, ENCRYPTION_KEY_BYTES, ENCRYPTION_IV_BYTES, ENCRYPTION_AUTH_TAG_BYTES, ENCRYPTION_SALT, ENCRYPTION_FILE_MODE, EncryptedFileAuthStore;
117
+ var init_encrypted_file_auth_store = __esm({
118
+ "packages/auth/src/encrypted-file-auth-store.ts"() {
119
+ "use strict";
120
+ ENCRYPTION_ALGORITHM = "aes-256-gcm";
121
+ ENCRYPTION_VERSION = 1;
122
+ ENCRYPTION_KEY_BYTES = 32;
123
+ ENCRYPTION_IV_BYTES = 12;
124
+ ENCRYPTION_AUTH_TAG_BYTES = 16;
125
+ ENCRYPTION_SALT = "poe-code:encrypted-file-auth-store:v1";
126
+ ENCRYPTION_FILE_MODE = 384;
127
+ EncryptedFileAuthStore = class {
128
+ fs;
129
+ filePath;
130
+ getMachineIdentity;
131
+ getRandomBytes;
132
+ keyPromise = null;
133
+ constructor(input = {}) {
134
+ this.fs = input.fs ?? fs;
135
+ this.filePath = input.filePath ?? path2.join(
136
+ (input.getHomeDirectory ?? homedir2)(),
137
+ ".poe-code",
138
+ "credentials.enc"
139
+ );
140
+ this.getMachineIdentity = input.getMachineIdentity ?? defaultMachineIdentity;
141
+ this.getRandomBytes = input.getRandomBytes ?? randomBytes;
142
+ }
143
+ async getApiKey() {
144
+ let rawDocument;
145
+ try {
146
+ rawDocument = await this.fs.readFile(this.filePath, "utf8");
147
+ } catch (error) {
148
+ if (isNotFoundError(error)) {
149
+ return null;
150
+ }
151
+ throw error;
152
+ }
153
+ const document = parseEncryptedDocument(rawDocument);
154
+ if (!document) {
155
+ return null;
156
+ }
157
+ const key = await this.getEncryptionKey();
158
+ try {
159
+ const iv = Buffer.from(document.iv, "base64");
160
+ const authTag = Buffer.from(document.authTag, "base64");
161
+ const ciphertext = Buffer.from(document.ciphertext, "base64");
162
+ if (iv.byteLength !== ENCRYPTION_IV_BYTES || authTag.byteLength !== ENCRYPTION_AUTH_TAG_BYTES) {
163
+ return null;
164
+ }
165
+ const decipher = createDecipheriv(ENCRYPTION_ALGORITHM, key, iv);
166
+ decipher.setAuthTag(authTag);
167
+ const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
168
+ return plaintext.toString("utf8");
169
+ } catch {
170
+ return null;
171
+ }
172
+ }
173
+ async setApiKey(apiKey) {
174
+ const key = await this.getEncryptionKey();
175
+ const iv = this.getRandomBytes(ENCRYPTION_IV_BYTES);
176
+ const cipher = createCipheriv(ENCRYPTION_ALGORITHM, key, iv);
177
+ const ciphertext = Buffer.concat([
178
+ cipher.update(apiKey, "utf8"),
179
+ cipher.final()
180
+ ]);
181
+ const authTag = cipher.getAuthTag();
182
+ const document = {
183
+ version: ENCRYPTION_VERSION,
184
+ iv: iv.toString("base64"),
185
+ authTag: authTag.toString("base64"),
186
+ ciphertext: ciphertext.toString("base64")
187
+ };
188
+ await this.fs.mkdir(path2.dirname(this.filePath), { recursive: true });
189
+ await this.fs.writeFile(this.filePath, JSON.stringify(document), {
190
+ encoding: "utf8"
191
+ });
192
+ await this.fs.chmod(this.filePath, ENCRYPTION_FILE_MODE);
193
+ }
194
+ async deleteApiKey() {
195
+ try {
196
+ await this.fs.unlink(this.filePath);
197
+ } catch (error) {
198
+ if (!isNotFoundError(error)) {
199
+ throw error;
200
+ }
201
+ }
202
+ }
203
+ getEncryptionKey() {
204
+ if (!this.keyPromise) {
205
+ this.keyPromise = deriveEncryptionKey(this.getMachineIdentity);
206
+ }
207
+ return this.keyPromise;
208
+ }
209
+ };
210
+ }
211
+ });
212
+
213
+ // packages/auth/src/keychain-auth-store.ts
214
+ import { spawn } from "node:child_process";
215
+ function runSecurityCommand(command, args) {
216
+ return new Promise((resolve) => {
217
+ const child = spawn(command, args, {
218
+ stdio: ["ignore", "pipe", "pipe"]
219
+ });
220
+ let stdout = "";
221
+ let stderr = "";
222
+ child.stdout?.setEncoding("utf8");
223
+ child.stdout?.on("data", (chunk) => {
224
+ stdout += chunk.toString();
225
+ });
226
+ child.stderr?.setEncoding("utf8");
227
+ child.stderr?.on("data", (chunk) => {
228
+ stderr += chunk.toString();
229
+ });
230
+ child.on("error", (error) => {
231
+ const message = error instanceof Error ? error.message : String(error ?? "Unknown error");
232
+ resolve({
233
+ stdout,
234
+ stderr: stderr ? `${stderr}${message}` : message,
235
+ exitCode: 127
236
+ });
237
+ });
238
+ child.on("close", (code) => {
239
+ resolve({
240
+ stdout,
241
+ stderr,
242
+ exitCode: code ?? 0
243
+ });
244
+ });
245
+ });
246
+ }
247
+ function stripTrailingLineBreak(value) {
248
+ if (value.endsWith("\r\n")) {
249
+ return value.slice(0, -2);
250
+ }
251
+ if (value.endsWith("\n") || value.endsWith("\r")) {
252
+ return value.slice(0, -1);
253
+ }
254
+ return value;
255
+ }
256
+ function isKeychainEntryNotFound(result) {
257
+ if (result.exitCode === KEYCHAIN_ITEM_NOT_FOUND_EXIT_CODE) {
258
+ return true;
259
+ }
260
+ const output = `${result.stderr}
261
+ ${result.stdout}`.toLowerCase();
262
+ return output.includes("could not be found") || output.includes("item not found") || output.includes("errsecitemnotfound");
263
+ }
264
+ function createSecurityCliFailure(operation, result) {
265
+ const details = result.stderr.trim() || result.stdout.trim();
266
+ if (details) {
267
+ return new Error(
268
+ `Failed to ${operation}: security exited with code ${result.exitCode}: ${details}`
269
+ );
270
+ }
271
+ return new Error(`Failed to ${operation}: security exited with code ${result.exitCode}`);
272
+ }
273
+ var SECURITY_CLI, KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT, KEYCHAIN_ITEM_NOT_FOUND_EXIT_CODE, KeychainAuthStore;
274
+ var init_keychain_auth_store = __esm({
275
+ "packages/auth/src/keychain-auth-store.ts"() {
276
+ "use strict";
277
+ SECURITY_CLI = "security";
278
+ KEYCHAIN_SERVICE = "poe-code";
279
+ KEYCHAIN_ACCOUNT = "api-key";
280
+ KEYCHAIN_ITEM_NOT_FOUND_EXIT_CODE = 44;
281
+ KeychainAuthStore = class {
282
+ runCommand;
283
+ service;
284
+ account;
285
+ constructor(input = {}) {
286
+ this.runCommand = input.runCommand ?? runSecurityCommand;
287
+ this.service = input.service ?? KEYCHAIN_SERVICE;
288
+ this.account = input.account ?? KEYCHAIN_ACCOUNT;
289
+ }
290
+ async getApiKey() {
291
+ const result = await this.executeSecurityCommand(
292
+ ["find-generic-password", "-s", this.service, "-a", this.account, "-w"],
293
+ "read API key from macOS Keychain"
294
+ );
295
+ if (result.exitCode === 0) {
296
+ return stripTrailingLineBreak(result.stdout);
297
+ }
298
+ if (isKeychainEntryNotFound(result)) {
299
+ return null;
300
+ }
301
+ throw createSecurityCliFailure("read API key from macOS Keychain", result);
302
+ }
303
+ async setApiKey(apiKey) {
304
+ const result = await this.executeSecurityCommand(
305
+ [
306
+ "add-generic-password",
307
+ "-s",
308
+ this.service,
309
+ "-a",
310
+ this.account,
311
+ "-w",
312
+ apiKey,
313
+ "-U"
314
+ ],
315
+ "store API key in macOS Keychain"
316
+ );
317
+ if (result.exitCode !== 0) {
318
+ throw createSecurityCliFailure("store API key in macOS Keychain", result);
319
+ }
320
+ }
321
+ async deleteApiKey() {
322
+ const result = await this.executeSecurityCommand(
323
+ ["delete-generic-password", "-s", this.service, "-a", this.account],
324
+ "delete API key from macOS Keychain"
325
+ );
326
+ if (result.exitCode === 0 || isKeychainEntryNotFound(result)) {
327
+ return;
328
+ }
329
+ throw createSecurityCliFailure("delete API key from macOS Keychain", result);
330
+ }
331
+ async executeSecurityCommand(args, operation) {
332
+ try {
333
+ return await this.runCommand(SECURITY_CLI, args);
334
+ } catch (error) {
335
+ const message = error instanceof Error ? error.message : String(error);
336
+ throw new Error(`Failed to ${operation}: ${message}`);
337
+ }
338
+ }
339
+ };
340
+ }
341
+ });
342
+
343
+ // packages/auth/src/create-auth-store.ts
344
+ import { promises as nodeFs } from "node:fs";
345
+ import { homedir as homedir3 } from "node:os";
346
+ import path3 from "node:path";
347
+ function createAuthStore(input = {}) {
348
+ const backend = resolveBackend(input);
349
+ const platform = input.platform ?? process.platform;
350
+ if (backend === "keychain" && platform !== MACOS_PLATFORM) {
351
+ throw new Error(
352
+ `POE_AUTH_BACKEND=keychain is only supported on macOS. Current platform: ${platform}`
353
+ );
354
+ }
355
+ const store = authStoreFactories[backend](input);
356
+ return {
357
+ backend,
358
+ store: enableLegacyCredentialsMigration(store, input)
359
+ };
360
+ }
361
+ function resolveBackend(input) {
362
+ const configuredBackend = input.backend ?? input.env?.[AUTH_BACKEND_ENV_VAR] ?? process.env[AUTH_BACKEND_ENV_VAR];
363
+ if (configuredBackend === "keychain") {
364
+ return "keychain";
365
+ }
366
+ return "file";
367
+ }
368
+ function enableLegacyCredentialsMigration(store, input) {
369
+ const migrationContext = createLegacyMigrationContext(input);
370
+ const readApiKeyFromStore = store.getApiKey.bind(store);
371
+ let hasCheckedLegacyCredentials = false;
372
+ let legacyMigrationPromise = null;
373
+ store.getApiKey = async () => {
374
+ const storedApiKey = await readApiKeyFromStore();
375
+ if (isNonEmptyString(storedApiKey)) {
376
+ return storedApiKey;
377
+ }
378
+ if (hasCheckedLegacyCredentials) {
379
+ return null;
380
+ }
381
+ if (!legacyMigrationPromise) {
382
+ legacyMigrationPromise = migrateLegacyApiKey(store, migrationContext).finally(() => {
383
+ hasCheckedLegacyCredentials = true;
384
+ legacyMigrationPromise = null;
385
+ });
386
+ }
387
+ return legacyMigrationPromise;
388
+ };
389
+ return store;
390
+ }
391
+ async function migrateLegacyApiKey(store, migrationContext) {
392
+ const legacyCredentials = await loadLegacyCredentials(
393
+ migrationContext.fs,
394
+ migrationContext.filePath
395
+ );
396
+ if (!legacyCredentials || !isNonEmptyString(legacyCredentials.apiKey)) {
397
+ return null;
398
+ }
399
+ const plaintextApiKey = legacyCredentials.apiKey;
400
+ try {
401
+ await store.setApiKey(plaintextApiKey);
402
+ delete legacyCredentials.apiKey;
403
+ await saveLegacyCredentials(
404
+ migrationContext.fs,
405
+ migrationContext.filePath,
406
+ legacyCredentials
407
+ );
408
+ } catch (error) {
409
+ migrationContext.logWarning(
410
+ `Failed to migrate plaintext API key from ${migrationContext.filePath}.`,
411
+ error
412
+ );
413
+ }
414
+ return plaintextApiKey;
415
+ }
416
+ function createLegacyMigrationContext(input) {
417
+ const legacyCredentialsInput = input.legacyCredentials;
418
+ const getHomeDirectory = legacyCredentialsInput?.getHomeDirectory ?? homedir3;
419
+ return {
420
+ fs: legacyCredentialsInput?.fs ?? input.fileStore?.fs ?? nodeFs,
421
+ filePath: legacyCredentialsInput?.filePath ?? path3.join(
422
+ getHomeDirectory(),
423
+ LEGACY_CREDENTIALS_RELATIVE_PATH
424
+ ),
425
+ logWarning: legacyCredentialsInput?.logWarning ?? defaultMigrationWarning
426
+ };
427
+ }
428
+ async function loadLegacyCredentials(fs2, filePath) {
429
+ let raw;
430
+ try {
431
+ raw = await fs2.readFile(filePath, "utf8");
432
+ } catch (error) {
433
+ if (isNotFoundError2(error)) {
434
+ return null;
435
+ }
436
+ return null;
437
+ }
438
+ try {
439
+ const parsed = JSON.parse(raw);
440
+ if (!isRecord2(parsed)) {
441
+ return null;
442
+ }
443
+ return parsed;
444
+ } catch {
445
+ return null;
446
+ }
447
+ }
448
+ async function saveLegacyCredentials(fs2, filePath, document) {
449
+ await fs2.mkdir(path3.dirname(filePath), { recursive: true });
450
+ await fs2.writeFile(filePath, `${JSON.stringify(document, null, 2)}
451
+ `, {
452
+ encoding: "utf8"
453
+ });
454
+ }
455
+ function defaultMigrationWarning(message, error) {
456
+ const details = toErrorDetails(error);
457
+ if (details.length > 0) {
458
+ console.warn(`${message} ${details}`);
459
+ return;
460
+ }
461
+ console.warn(message);
462
+ }
463
+ function toErrorDetails(error) {
464
+ if (error instanceof Error) {
465
+ return error.message;
466
+ }
467
+ return typeof error === "string" ? error : "";
468
+ }
469
+ function isNonEmptyString(value) {
470
+ return typeof value === "string" && value.length > 0;
471
+ }
472
+ function isRecord2(value) {
473
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
474
+ }
475
+ function isNotFoundError2(error) {
476
+ return Boolean(
477
+ error && typeof error === "object" && "code" in error && error.code === "ENOENT"
478
+ );
479
+ }
480
+ var AUTH_BACKEND_ENV_VAR, MACOS_PLATFORM, LEGACY_CREDENTIALS_RELATIVE_PATH, authStoreFactories;
481
+ var init_create_auth_store = __esm({
482
+ "packages/auth/src/create-auth-store.ts"() {
483
+ "use strict";
484
+ init_encrypted_file_auth_store();
485
+ init_keychain_auth_store();
486
+ AUTH_BACKEND_ENV_VAR = "POE_AUTH_BACKEND";
487
+ MACOS_PLATFORM = "darwin";
488
+ LEGACY_CREDENTIALS_RELATIVE_PATH = ".poe-code/credentials.json";
489
+ authStoreFactories = {
490
+ file: (input) => new EncryptedFileAuthStore(input.fileStore),
491
+ keychain: (input) => new KeychainAuthStore(input.keychainStore)
492
+ };
493
+ }
494
+ });
495
+
496
+ // packages/auth/src/index.ts
497
+ var init_src = __esm({
498
+ "packages/auth/src/index.ts"() {
499
+ "use strict";
500
+ init_create_auth_store();
501
+ init_encrypted_file_auth_store();
502
+ init_keychain_auth_store();
503
+ }
504
+ });
505
+
506
+ // packages/poe-agent/src/chat.ts
507
+ function toChatCompletionsUrl(baseUrl) {
508
+ const trimmedBaseUrl = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
509
+ if (trimmedBaseUrl.endsWith("/v1")) {
510
+ return `${trimmedBaseUrl}/chat/completions`;
511
+ }
512
+ return `${trimmedBaseUrl}/v1/chat/completions`;
513
+ }
514
+ function parseToolArguments(rawArguments) {
515
+ const parsed = JSON.parse(rawArguments);
516
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
517
+ throw new Error("Tool call arguments must be a JSON object");
518
+ }
519
+ return parsed;
520
+ }
521
+ async function readResponseText(response) {
522
+ try {
523
+ const text = await response.text();
524
+ return text.trim() || void 0;
525
+ } catch {
526
+ return void 0;
527
+ }
528
+ }
529
+ function getErrorMessage(error) {
530
+ if (error instanceof Error) {
531
+ return error.message;
532
+ }
533
+ return String(error);
534
+ }
535
+ var PoeChatService;
536
+ var init_chat = __esm({
537
+ "packages/poe-agent/src/chat.ts"() {
538
+ "use strict";
539
+ PoeChatService = class {
540
+ apiKey;
541
+ model;
542
+ fetchFn;
543
+ chatCompletionsUrl;
544
+ toolExecutor;
545
+ maxToolCallIterations;
546
+ conversationHistory = [];
547
+ toolCallCallback;
548
+ constructor(options) {
549
+ this.apiKey = options.apiKey;
550
+ this.model = options.model;
551
+ this.fetchFn = options.fetch ?? globalThis.fetch;
552
+ this.chatCompletionsUrl = toChatCompletionsUrl(options.baseUrl ?? "https://api.poe.com");
553
+ this.toolExecutor = options.toolExecutor;
554
+ this.toolCallCallback = options.onToolCall;
555
+ this.maxToolCallIterations = options.maxToolCallIterations ?? 100;
556
+ if (options.systemPrompt) {
557
+ this.conversationHistory.push({
558
+ role: "system",
559
+ content: options.systemPrompt
560
+ });
561
+ }
562
+ }
563
+ clearConversationHistory() {
564
+ this.conversationHistory = [];
565
+ }
566
+ async sendMessage(userMessage, options) {
567
+ this.conversationHistory.push({
568
+ role: "user",
569
+ content: userMessage
570
+ });
571
+ let iterationCount = 0;
572
+ while (iterationCount < this.maxToolCallIterations) {
573
+ const response = await this.requestCompletion(options?.tools, options?.signal);
574
+ const assistantMessage = response.choices[0]?.message;
575
+ if (!assistantMessage) {
576
+ throw new Error("Poe API response did not include an assistant message");
577
+ }
578
+ this.conversationHistory.push(assistantMessage);
579
+ const hasToolCalls = assistantMessage.tool_calls && assistantMessage.tool_calls.length > 0 && this.toolExecutor;
580
+ if (!hasToolCalls) {
581
+ return assistantMessage;
582
+ }
583
+ await this.executeToolCalls(assistantMessage.tool_calls);
584
+ iterationCount += 1;
585
+ }
586
+ throw new Error("Maximum tool call iterations reached");
587
+ }
588
+ async requestCompletion(tools, signal) {
589
+ const requestBody = {
590
+ model: this.model,
591
+ messages: this.conversationHistory
592
+ };
593
+ if (tools && tools.length > 0) {
594
+ requestBody.tools = tools;
595
+ }
596
+ const response = await this.fetchFn(this.chatCompletionsUrl, {
597
+ method: "POST",
598
+ headers: {
599
+ "Content-Type": "application/json",
600
+ Authorization: `Bearer ${this.apiKey}`
601
+ },
602
+ body: JSON.stringify(requestBody),
603
+ signal
604
+ });
605
+ if (!response.ok) {
606
+ const errorBody = await readResponseText(response);
607
+ const details = errorBody || response.statusText || "Unknown error";
608
+ throw new Error(`Poe API request failed (${response.status}): ${details}`);
609
+ }
610
+ const payload = await response.json();
611
+ if (!Array.isArray(payload.choices)) {
612
+ throw new Error("Poe API response had invalid choices payload");
613
+ }
614
+ return payload;
615
+ }
616
+ async executeToolCalls(toolCalls) {
617
+ if (!this.toolExecutor) {
618
+ return;
619
+ }
620
+ for (const toolCall of toolCalls) {
621
+ let args = {};
622
+ try {
623
+ args = parseToolArguments(toolCall.function.arguments);
624
+ this.emitToolCallEvent({
625
+ phase: "started",
626
+ toolCallId: toolCall.id,
627
+ toolName: toolCall.function.name,
628
+ args
629
+ });
630
+ const result = await this.toolExecutor.executeTool(toolCall.function.name, args);
631
+ this.emitToolCallEvent({
632
+ phase: "completed",
633
+ toolCallId: toolCall.id,
634
+ toolName: toolCall.function.name,
635
+ args,
636
+ result
637
+ });
638
+ this.conversationHistory.push({
639
+ role: "tool",
640
+ tool_call_id: toolCall.id,
641
+ name: toolCall.function.name,
642
+ content: result
643
+ });
644
+ } catch (error) {
645
+ const message = getErrorMessage(error);
646
+ this.emitToolCallEvent({
647
+ phase: "failed",
648
+ toolCallId: toolCall.id,
649
+ toolName: toolCall.function.name,
650
+ args,
651
+ error: message
652
+ });
653
+ this.conversationHistory.push({
654
+ role: "tool",
655
+ tool_call_id: toolCall.id,
656
+ name: toolCall.function.name,
657
+ content: `Error: ${message}`
658
+ });
659
+ }
660
+ }
661
+ }
662
+ emitToolCallEvent(event) {
663
+ if (this.toolCallCallback) {
664
+ this.toolCallCallback(event);
665
+ }
666
+ }
667
+ };
668
+ }
669
+ });
670
+
671
+ // packages/poe-agent/src/system-prompt.ts
672
+ import { readFileSync } from "node:fs";
673
+ import { readFile } from "node:fs/promises";
674
+ async function loadSystemPrompt() {
675
+ return readFile(systemPromptUrl, "utf8");
676
+ }
677
+ var systemPromptUrl;
678
+ var init_system_prompt = __esm({
679
+ "packages/poe-agent/src/system-prompt.ts"() {
680
+ "use strict";
681
+ systemPromptUrl = new URL("./SYSTEM_PROMPT.md", import.meta.url);
682
+ }
683
+ });
684
+
685
+ // packages/poe-agent/src/tool-executor.ts
686
+ import { exec as execCallback } from "node:child_process";
687
+ import fsPromises2 from "node:fs/promises";
688
+ import path4 from "node:path";
689
+ import { promisify } from "node:util";
690
+ function getRequiredString(args, key, allowEmptyString = false) {
691
+ const value = args[key];
692
+ if (typeof value !== "string") {
693
+ throw new Error(`Tool argument "${key}" must be a string`);
694
+ }
695
+ if (!allowEmptyString && value.trim().length === 0) {
696
+ throw new Error(`Tool argument "${key}" must not be empty`);
697
+ }
698
+ return value;
699
+ }
700
+ function getOptionalString(args, key) {
701
+ const value = args[key];
702
+ if (value === void 0) {
703
+ return void 0;
704
+ }
705
+ if (typeof value !== "string") {
706
+ throw new Error(`Tool argument "${key}" must be a string`);
707
+ }
708
+ return value;
709
+ }
710
+ function countOccurrences(text, search) {
711
+ let count = 0;
712
+ let index = 0;
713
+ while ((index = text.indexOf(search, index)) !== -1) {
714
+ count++;
715
+ index += search.length;
716
+ }
717
+ return count;
718
+ }
719
+ async function defaultRunCommand(command, cwd) {
720
+ try {
721
+ const result = await exec(command, {
722
+ cwd,
723
+ timeout: 3e4,
724
+ maxBuffer: 1024 * 1024
725
+ });
726
+ const combinedOutput = [result.stdout, result.stderr].map((output) => output.trim()).filter((output) => output.length > 0).join("\n");
727
+ return combinedOutput || "Command completed with no output";
728
+ } catch (error) {
729
+ if (error instanceof Error) {
730
+ const stderr = Reflect.get(error, "stderr");
731
+ if (typeof stderr === "string" && stderr.trim().length > 0) {
732
+ throw new Error(`Command failed: ${stderr.trim()}`);
733
+ }
734
+ const stdout = Reflect.get(error, "stdout");
735
+ if (typeof stdout === "string" && stdout.trim().length > 0) {
736
+ throw new Error(`Command failed: ${stdout.trim()}`);
737
+ }
738
+ throw new Error(`Command failed: ${error.message}`);
739
+ }
740
+ throw new Error(`Command failed: ${String(error)}`);
741
+ }
742
+ }
743
+ async function defaultSearchWeb(query, fetchFn) {
744
+ const url = new URL("https://api.duckduckgo.com/");
745
+ url.searchParams.set("q", query);
746
+ url.searchParams.set("format", "json");
747
+ url.searchParams.set("no_redirect", "1");
748
+ url.searchParams.set("no_html", "1");
749
+ url.searchParams.set("skip_disambig", "1");
750
+ const response = await fetchFn(url.toString());
751
+ if (!response.ok) {
752
+ throw new Error(`Web search failed (${response.status})`);
753
+ }
754
+ const body = await response.json();
755
+ const lines = [];
756
+ if (typeof body.AbstractText === "string" && body.AbstractText.trim().length > 0) {
757
+ lines.push(body.AbstractText.trim());
758
+ }
759
+ if (body.RelatedTopics) {
760
+ const queue = [...body.RelatedTopics];
761
+ while (queue.length > 0 && lines.length < 5) {
762
+ const current = queue.shift();
763
+ if (!current) continue;
764
+ if (typeof current.Text === "string" && current.Text.trim().length > 0) {
765
+ lines.push(current.Text.trim());
766
+ }
767
+ if (current.Topics) {
768
+ queue.push(...current.Topics);
769
+ }
770
+ }
771
+ }
772
+ if (lines.length === 0) {
773
+ return "No search results found.";
774
+ }
775
+ return lines.join("\n");
776
+ }
777
+ var exec, DefaultToolExecutor;
778
+ var init_tool_executor = __esm({
779
+ "packages/poe-agent/src/tool-executor.ts"() {
780
+ "use strict";
781
+ exec = promisify(execCallback);
782
+ DefaultToolExecutor = class {
783
+ cwd;
784
+ allowedPaths;
785
+ fs;
786
+ runCommandFn;
787
+ searchWebFn;
788
+ constructor(options = {}) {
789
+ this.cwd = path4.resolve(options.cwd ?? process.cwd());
790
+ this.allowedPaths = (options.allowedPaths ?? [this.cwd]).map(
791
+ (allowedPath) => path4.resolve(this.cwd, allowedPath)
792
+ );
793
+ this.fs = options.fs ?? fsPromises2;
794
+ this.runCommandFn = options.runCommand ?? defaultRunCommand;
795
+ const fetchFn = options.fetch ?? globalThis.fetch;
796
+ this.searchWebFn = options.searchWeb ?? ((query) => defaultSearchWeb(query, fetchFn));
797
+ }
798
+ async executeTool(name, args) {
799
+ switch (name) {
800
+ case "read_file":
801
+ return this.executeReadFile(args);
802
+ case "edit_file":
803
+ return this.executeEditFile(args);
804
+ case "list_files":
805
+ return this.executeListFiles(args);
806
+ case "run_command":
807
+ return this.executeRunCommand(args);
808
+ case "search_web":
809
+ return this.executeSearchWeb(args);
810
+ default:
811
+ throw new Error(`Unsupported tool: ${name}`);
812
+ }
813
+ }
814
+ getAvailableTools() {
815
+ return [
816
+ {
817
+ type: "function",
818
+ function: {
819
+ name: "read_file",
820
+ description: "Read UTF-8 content from a file.",
821
+ parameters: {
822
+ type: "object",
823
+ properties: {
824
+ path: {
825
+ type: "string",
826
+ description: "Path to the file to read."
827
+ }
828
+ },
829
+ required: ["path"]
830
+ }
831
+ }
832
+ },
833
+ {
834
+ type: "function",
835
+ function: {
836
+ name: "edit_file",
837
+ description: "Edit or create files. Use 'str_replace' to replace exact text in an existing file (old_str must appear exactly once). Use 'create' to create a new file (fails if file already exists).",
838
+ parameters: {
839
+ type: "object",
840
+ properties: {
841
+ command: {
842
+ type: "string",
843
+ enum: ["str_replace", "create"],
844
+ description: "Operation to perform."
845
+ },
846
+ path: {
847
+ type: "string",
848
+ description: "File path."
849
+ },
850
+ old_str: {
851
+ type: "string",
852
+ description: "Exact string to find and replace (str_replace only). Must match exactly once."
853
+ },
854
+ new_str: {
855
+ type: "string",
856
+ description: "Replacement string (str_replace only)."
857
+ },
858
+ file_text: {
859
+ type: "string",
860
+ description: "Full file content (create only)."
861
+ }
862
+ },
863
+ required: ["command", "path"]
864
+ }
865
+ }
866
+ },
867
+ {
868
+ type: "function",
869
+ function: {
870
+ name: "list_files",
871
+ description: "List files in a directory.",
872
+ parameters: {
873
+ type: "object",
874
+ properties: {
875
+ path: {
876
+ type: "string",
877
+ description: "Directory path to list. Defaults to current working directory."
878
+ }
879
+ }
880
+ }
881
+ }
882
+ },
883
+ {
884
+ type: "function",
885
+ function: {
886
+ name: "run_command",
887
+ description: "Run a shell command.",
888
+ parameters: {
889
+ type: "object",
890
+ properties: {
891
+ command: {
892
+ type: "string",
893
+ description: "Command to execute."
894
+ },
895
+ cwd: {
896
+ type: "string",
897
+ description: "Working directory for command execution."
898
+ }
899
+ },
900
+ required: ["command"]
901
+ }
902
+ }
903
+ },
904
+ {
905
+ type: "function",
906
+ function: {
907
+ name: "search_web",
908
+ description: "Search the web for a query.",
909
+ parameters: {
910
+ type: "object",
911
+ properties: {
912
+ query: {
913
+ type: "string",
914
+ description: "Search query."
915
+ }
916
+ },
917
+ required: ["query"]
918
+ }
919
+ }
920
+ }
921
+ ];
922
+ }
923
+ async executeReadFile(args) {
924
+ const filePath = this.resolveAllowedPath(getRequiredString(args, "path"));
925
+ return this.fs.readFile(filePath, "utf8");
926
+ }
927
+ async executeEditFile(args) {
928
+ const command = getRequiredString(args, "command");
929
+ const filePath = this.resolveAllowedPath(getRequiredString(args, "path"));
930
+ const displayedPath = path4.relative(this.cwd, filePath) || path4.basename(filePath);
931
+ if (command === "str_replace") {
932
+ const oldStr = getRequiredString(args, "old_str", true);
933
+ const newStr = getRequiredString(args, "new_str", true);
934
+ const content = await this.fs.readFile(filePath, "utf8");
935
+ const count = countOccurrences(content, oldStr);
936
+ if (count === 0) {
937
+ throw new Error("old_str not found in file");
938
+ }
939
+ if (count > 1) {
940
+ throw new Error(`old_str appears ${count} times \u2014 must be unique`);
941
+ }
942
+ await this.fs.writeFile(filePath, content.replace(oldStr, newStr), "utf8");
943
+ return `Edited file: ${displayedPath}`;
944
+ }
945
+ if (command === "create") {
946
+ const fileText = getRequiredString(args, "file_text", true);
947
+ if (await this.fileExists(filePath)) {
948
+ throw new Error("File already exists \u2014 use str_replace to edit");
949
+ }
950
+ await this.fs.mkdir(path4.dirname(filePath), { recursive: true });
951
+ await this.fs.writeFile(filePath, fileText, "utf8");
952
+ return `Created file: ${displayedPath}`;
953
+ }
954
+ throw new Error(`Unknown edit_file command: ${command}`);
955
+ }
956
+ async executeListFiles(args) {
957
+ const rawPath = getOptionalString(args, "path") ?? ".";
958
+ const directoryPath = this.resolveAllowedPath(rawPath);
959
+ const entries = await this.fs.readdir(directoryPath);
960
+ const names = entries.sort((left, right) => left.localeCompare(right));
961
+ if (names.length === 0) {
962
+ return "(empty directory)";
963
+ }
964
+ return names.join("\n");
965
+ }
966
+ async executeRunCommand(args) {
967
+ const command = getRequiredString(args, "command");
968
+ const commandCwdArg = getOptionalString(args, "cwd");
969
+ const commandCwd = commandCwdArg ? this.resolveAllowedPath(commandCwdArg) : this.cwd;
970
+ return this.runCommandFn(command, commandCwd);
971
+ }
972
+ async executeSearchWeb(args) {
973
+ const query = getRequiredString(args, "query");
974
+ return this.searchWebFn(query);
975
+ }
976
+ resolveAllowedPath(inputPath) {
977
+ const resolvedPath = path4.resolve(this.cwd, inputPath);
978
+ const isAllowed = this.allowedPaths.some((allowedPath) => {
979
+ if (allowedPath === resolvedPath) return true;
980
+ const rel = path4.relative(allowedPath, resolvedPath);
981
+ return rel.length > 0 && !rel.startsWith("..") && !path4.isAbsolute(rel);
982
+ });
983
+ if (!isAllowed) {
984
+ throw new Error(`Path is outside allowed paths: ${inputPath}`);
985
+ }
986
+ return resolvedPath;
987
+ }
988
+ async fileExists(filePath) {
989
+ try {
990
+ await this.fs.readFile(filePath, "utf8");
991
+ return true;
992
+ } catch {
993
+ return false;
994
+ }
995
+ }
996
+ };
997
+ }
998
+ });
999
+
1000
+ // packages/poe-agent/src/agent-session.ts
1001
+ async function createAgentSession(options = {}) {
1002
+ const model = resolveRequiredModel(options.model);
1003
+ const apiKey = await resolveApiKey(options.apiKey);
1004
+ const systemPrompt = await loadSystemPrompt();
1005
+ const toolExecutor = new DefaultToolExecutor({
1006
+ cwd: options.cwd,
1007
+ allowedPaths: options.allowedPaths
1008
+ });
1009
+ const tools = toolExecutor.getAvailableTools();
1010
+ let currentOnSessionUpdate;
1011
+ const chatService = new PoeChatService({
1012
+ apiKey,
1013
+ model,
1014
+ baseUrl: options.baseUrl,
1015
+ fetch: options.fetch,
1016
+ systemPrompt,
1017
+ toolExecutor,
1018
+ maxToolCallIterations: options.maxToolCallIterations,
1019
+ onToolCall: (event) => {
1020
+ if (!currentOnSessionUpdate) return;
1021
+ for (const update of mapToolLifecycleEventToSessionUpdates(event)) {
1022
+ currentOnSessionUpdate(update);
1023
+ }
1024
+ }
1025
+ });
1026
+ let disposed = false;
1027
+ return {
1028
+ async sendMessage(prompt, sendOptions) {
1029
+ if (disposed) {
1030
+ throw new Error("Agent session is already disposed.");
1031
+ }
1032
+ currentOnSessionUpdate = sendOptions?.onSessionUpdate;
1033
+ const response = await chatService.sendMessage(prompt, {
1034
+ tools,
1035
+ signal: sendOptions?.signal
1036
+ });
1037
+ if (currentOnSessionUpdate && response.role === "assistant" && response.content.length > 0) {
1038
+ currentOnSessionUpdate({
1039
+ sessionUpdate: "agent_message_chunk",
1040
+ content: {
1041
+ type: "text",
1042
+ text: response.content
1043
+ }
1044
+ });
1045
+ }
1046
+ return response;
1047
+ },
1048
+ async dispose() {
1049
+ if (disposed) {
1050
+ return;
1051
+ }
1052
+ disposed = true;
1053
+ chatService.clearConversationHistory();
1054
+ const disposableToolExecutor = toolExecutor;
1055
+ if (typeof disposableToolExecutor.dispose === "function") {
1056
+ await disposableToolExecutor.dispose();
1057
+ }
1058
+ }
1059
+ };
1060
+ }
1061
+ async function resolveApiKey(explicitApiKey) {
1062
+ const normalizedExplicitApiKey = normalizeNonEmptyString(explicitApiKey);
1063
+ if (normalizedExplicitApiKey) {
1064
+ return normalizedExplicitApiKey;
1065
+ }
1066
+ const { store } = createAuthStore();
1067
+ const storedApiKey = normalizeNonEmptyString(await store.getApiKey());
1068
+ if (storedApiKey) {
1069
+ return storedApiKey;
1070
+ }
1071
+ throw new Error("Missing Poe API key. Provide apiKey or run 'poe-code login'.");
1072
+ }
1073
+ function resolveRequiredModel(model) {
1074
+ const normalizedModel = normalizeNonEmptyString(model);
1075
+ if (normalizedModel) {
1076
+ return normalizedModel;
1077
+ }
1078
+ throw new Error("Missing model. Provide a non-empty model to createAgentSession.");
1079
+ }
1080
+ function normalizeNonEmptyString(value) {
1081
+ if (typeof value !== "string") {
1082
+ return void 0;
1083
+ }
1084
+ const trimmed = value.trim();
1085
+ return trimmed.length > 0 ? trimmed : void 0;
1086
+ }
1087
+ function mapToolLifecycleEventToSessionUpdates(event) {
1088
+ if (event.phase === "started") {
1089
+ const toolCall = {
1090
+ sessionUpdate: "tool_call",
1091
+ toolCallId: event.toolCallId,
1092
+ title: event.toolName,
1093
+ kind: "execute",
1094
+ status: "pending",
1095
+ rawInput: event.args
1096
+ };
1097
+ const inProgressUpdate = {
1098
+ sessionUpdate: "tool_call_update",
1099
+ toolCallId: event.toolCallId,
1100
+ kind: "execute",
1101
+ status: "in_progress"
1102
+ };
1103
+ return [toolCall, inProgressUpdate];
1104
+ }
1105
+ const terminalUpdate = {
1106
+ sessionUpdate: "tool_call_update",
1107
+ toolCallId: event.toolCallId,
1108
+ kind: "execute",
1109
+ status: event.phase === "completed" ? "completed" : "failed"
1110
+ };
1111
+ if (event.phase === "completed" && event.result !== void 0) {
1112
+ terminalUpdate.rawOutput = event.result;
1113
+ }
1114
+ if (event.phase === "failed" && event.error !== void 0) {
1115
+ terminalUpdate.rawOutput = event.error;
1116
+ }
1117
+ return [terminalUpdate];
1118
+ }
1119
+ var init_agent_session = __esm({
1120
+ "packages/poe-agent/src/agent-session.ts"() {
1121
+ "use strict";
1122
+ init_src();
1123
+ init_chat();
1124
+ init_system_prompt();
1125
+ init_tool_executor();
1126
+ }
1127
+ });
1128
+
1129
+ // packages/poe-agent/src/index.ts
1130
+ var src_exports = {};
1131
+ __export(src_exports, {
1132
+ createAgentSession: () => createAgentSession
1133
+ });
1134
+ var init_src2 = __esm({
1135
+ "packages/poe-agent/src/index.ts"() {
1136
+ "use strict";
1137
+ init_agent_session();
1138
+ }
1139
+ });
1140
+
1141
+ // src/cli/constants.ts
1142
+ var DEFAULT_FRONTIER_MODEL = "anthropic/claude-sonnet-4.6";
1143
+ var CLAUDE_CODE_VARIANTS = {
1144
+ haiku: "anthropic/claude-haiku-4.5",
1145
+ sonnet: "anthropic/claude-sonnet-4.6",
1146
+ opus: "anthropic/claude-opus-4.6"
1147
+ };
1148
+ var DEFAULT_CLAUDE_CODE_MODEL = CLAUDE_CODE_VARIANTS.sonnet;
1149
+ var CODEX_MODELS = [
1150
+ "openai/gpt-5.3-codex",
1151
+ "openai/gpt-5.2-codex",
1152
+ "openai/gpt-5.2",
1153
+ "openai/gpt-5.2-chat",
1154
+ "openai/gpt-5.2-pro",
1155
+ "openai/gpt-5.1",
1156
+ "openai/gpt-5.1-codex-mini"
1157
+ ];
1158
+ var DEFAULT_CODEX_MODEL = CODEX_MODELS[0];
1159
+ var KIMI_MODELS = [
1160
+ "novitaai/kimi-k2.5",
1161
+ "novitaai/kimi-k2-thinking"
1162
+ ];
1163
+ var DEFAULT_KIMI_MODEL = KIMI_MODELS[0];
1164
+
1165
+ // packages/poe-acp-client/src/acp-client.ts
1166
+ import { isAbsolute } from "node:path";
1167
+
1168
+ // packages/poe-acp-client/src/acp-transport.ts
1169
+ import {
1170
+ spawn as spawnChildProcess
1171
+ } from "node:child_process";
1172
+
1173
+ // packages/poe-acp-client/src/types.ts
1174
+ var ACP_ERROR_CODE_PARSE = -32700;
1175
+ var ACP_ERROR_CODE_INVALID_REQUEST = -32600;
1176
+ var ACP_ERROR_CODE_METHOD_NOT_FOUND = -32601;
1177
+ var ACP_ERROR_CODE_INVALID_PARAMS = -32602;
1178
+ var ACP_ERROR_CODE_INTERNAL = -32603;
1179
+ var ACP_ERROR_CODE_RESOURCE_NOT_FOUND = -32002;
1180
+ function isObjectRecord(value) {
1181
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1182
+ }
1183
+ function isAcpErrorCode(value) {
1184
+ return typeof value === "number" && Number.isInteger(value) && value >= -2147483648 && value <= 2147483647;
1185
+ }
1186
+ var AcpError = class extends Error {
1187
+ code;
1188
+ data;
1189
+ constructor(code, message, data) {
1190
+ super(message);
1191
+ this.name = "AcpError";
1192
+ this.code = code;
1193
+ if (data !== void 0) {
1194
+ this.data = data;
1195
+ }
1196
+ }
1197
+ };
1198
+ function isAcpError(value) {
1199
+ if (value instanceof AcpError) {
1200
+ return true;
1201
+ }
1202
+ if (!isObjectRecord(value)) {
1203
+ return false;
1204
+ }
1205
+ if (!isAcpErrorCode(value.code) || typeof value.message !== "string") {
1206
+ return false;
1207
+ }
1208
+ return value.data === void 0 || Object.prototype.hasOwnProperty.call(value, "data");
1209
+ }
1210
+
1211
+ // packages/poe-acp-client/src/jsonrpc-message-layer.ts
1212
+ function isObjectRecord2(value) {
1213
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1214
+ }
1215
+ function hasOwn(value, property) {
1216
+ return Object.prototype.hasOwnProperty.call(value, property);
1217
+ }
1218
+ function isRequestId(value) {
1219
+ return value === null || typeof value === "string" || typeof value === "number";
1220
+ }
1221
+ function toRequestId(value) {
1222
+ return isRequestId(value) ? value : null;
1223
+ }
1224
+ function parseError() {
1225
+ return new AcpError(ACP_ERROR_CODE_PARSE, "Parse error");
1226
+ }
1227
+ function invalidRequest() {
1228
+ return new AcpError(ACP_ERROR_CODE_INVALID_REQUEST, "Invalid Request");
1229
+ }
1230
+ function methodNotFound(method) {
1231
+ return new AcpError(
1232
+ ACP_ERROR_CODE_METHOD_NOT_FOUND,
1233
+ `Method not found: "${method}"`
1234
+ );
1235
+ }
1236
+ function internalError(message) {
1237
+ return new AcpError(ACP_ERROR_CODE_INTERNAL, message);
1238
+ }
1239
+ function isJsonRpcErrorObject(value) {
1240
+ if (!isObjectRecord2(value)) {
1241
+ return false;
1242
+ }
1243
+ if (!isAcpErrorCode(value.code) || typeof value.message !== "string") {
1244
+ return false;
1245
+ }
1246
+ return value.data === void 0 || hasOwn(value, "data");
1247
+ }
1248
+ function toResponseError(error) {
1249
+ return new AcpError(error.code, error.message, error.data);
1250
+ }
1251
+ function toDispatchError(error) {
1252
+ if (error instanceof AcpError) {
1253
+ return error;
1254
+ }
1255
+ if (isAcpError(error)) {
1256
+ return new AcpError(error.code, error.message, error.data);
1257
+ }
1258
+ if (error instanceof Error && error.message.length > 0) {
1259
+ return internalError(error.message);
1260
+ }
1261
+ return internalError("Internal error");
1262
+ }
1263
+ function chunkToString(chunk) {
1264
+ if (typeof chunk === "string") {
1265
+ return chunk;
1266
+ }
1267
+ if (chunk instanceof Uint8Array) {
1268
+ return Buffer.from(chunk).toString("utf8");
1269
+ }
1270
+ return String(chunk);
1271
+ }
1272
+ function normalizeLine(line) {
1273
+ return line.endsWith("\r") ? line.slice(0, -1) : line;
1274
+ }
1275
+ async function* readLines(stream) {
1276
+ let buffer = "";
1277
+ for await (const chunk of stream) {
1278
+ buffer += chunkToString(chunk);
1279
+ while (true) {
1280
+ const newlineIndex = buffer.indexOf("\n");
1281
+ if (newlineIndex === -1) {
1282
+ break;
1283
+ }
1284
+ const line = buffer.slice(0, newlineIndex);
1285
+ buffer = buffer.slice(newlineIndex + 1);
1286
+ yield normalizeLine(line);
1287
+ }
1288
+ }
1289
+ if (buffer.length > 0) {
1290
+ yield normalizeLine(buffer);
1291
+ }
1292
+ }
1293
+ function createResponseMessage(id, result) {
1294
+ return {
1295
+ jsonrpc: "2.0",
1296
+ id,
1297
+ result: result === void 0 ? null : result
1298
+ };
1299
+ }
1300
+ function createJsonRpcErrorResponse(id, error) {
1301
+ const response = {
1302
+ jsonrpc: "2.0",
1303
+ id,
1304
+ error: {
1305
+ code: error.code,
1306
+ message: error.message
1307
+ }
1308
+ };
1309
+ if (error.data !== void 0) {
1310
+ response.error.data = error.data;
1311
+ }
1312
+ return response;
1313
+ }
1314
+ function serializeJsonRpcMessage(message) {
1315
+ return `${JSON.stringify(message)}
1316
+ `;
1317
+ }
1318
+ function parseJsonRpcMessage(line) {
1319
+ let parsed;
1320
+ try {
1321
+ parsed = JSON.parse(line);
1322
+ } catch {
1323
+ return {
1324
+ type: "invalid",
1325
+ id: null,
1326
+ error: parseError()
1327
+ };
1328
+ }
1329
+ if (!isObjectRecord2(parsed)) {
1330
+ return {
1331
+ type: "invalid",
1332
+ id: null,
1333
+ error: invalidRequest()
1334
+ };
1335
+ }
1336
+ const id = toRequestId(parsed.id);
1337
+ if (parsed.jsonrpc !== "2.0") {
1338
+ return {
1339
+ type: "invalid",
1340
+ id,
1341
+ error: invalidRequest()
1342
+ };
1343
+ }
1344
+ const hasMethod = hasOwn(parsed, "method");
1345
+ const hasId = hasOwn(parsed, "id");
1346
+ if (hasMethod) {
1347
+ if (typeof parsed.method !== "string") {
1348
+ return {
1349
+ type: "invalid",
1350
+ id,
1351
+ error: invalidRequest()
1352
+ };
1353
+ }
1354
+ if (hasId) {
1355
+ if (!isRequestId(parsed.id)) {
1356
+ return {
1357
+ type: "invalid",
1358
+ id: null,
1359
+ error: invalidRequest()
1360
+ };
1361
+ }
1362
+ const request = {
1363
+ jsonrpc: "2.0",
1364
+ id: parsed.id,
1365
+ method: parsed.method
1366
+ };
1367
+ if (hasOwn(parsed, "params")) {
1368
+ request.params = parsed.params;
1369
+ }
1370
+ return { type: "request", message: request };
1371
+ }
1372
+ const notification = {
1373
+ jsonrpc: "2.0",
1374
+ method: parsed.method
1375
+ };
1376
+ if (hasOwn(parsed, "params")) {
1377
+ notification.params = parsed.params;
1378
+ }
1379
+ return {
1380
+ type: "notification",
1381
+ message: notification
1382
+ };
1383
+ }
1384
+ if (!hasId || !isRequestId(parsed.id)) {
1385
+ return {
1386
+ type: "invalid",
1387
+ id,
1388
+ error: invalidRequest()
1389
+ };
1390
+ }
1391
+ const hasResult = hasOwn(parsed, "result");
1392
+ const hasError = hasOwn(parsed, "error");
1393
+ if (hasResult === hasError) {
1394
+ return {
1395
+ type: "invalid",
1396
+ id: parsed.id,
1397
+ error: invalidRequest()
1398
+ };
1399
+ }
1400
+ if (hasResult) {
1401
+ return {
1402
+ type: "response",
1403
+ message: {
1404
+ jsonrpc: "2.0",
1405
+ id: parsed.id,
1406
+ result: parsed.result
1407
+ }
1408
+ };
1409
+ }
1410
+ if (!isJsonRpcErrorObject(parsed.error)) {
1411
+ return {
1412
+ type: "invalid",
1413
+ id: parsed.id,
1414
+ error: invalidRequest()
1415
+ };
1416
+ }
1417
+ return {
1418
+ type: "response",
1419
+ message: {
1420
+ jsonrpc: "2.0",
1421
+ id: parsed.id,
1422
+ error: parsed.error
1423
+ }
1424
+ };
1425
+ }
1426
+ var JsonRpcMessageLayer = class {
1427
+ input;
1428
+ output;
1429
+ requestHandlers = /* @__PURE__ */ new Map();
1430
+ notificationHandlers = /* @__PURE__ */ new Map();
1431
+ pending = /* @__PURE__ */ new Map();
1432
+ defaultTimeoutMs;
1433
+ nextRequestId;
1434
+ disposed = false;
1435
+ constructor(options) {
1436
+ this.input = options.input;
1437
+ this.output = options.output;
1438
+ this.defaultTimeoutMs = options.requestTimeoutMs ?? 3e4;
1439
+ this.nextRequestId = options.firstRequestId ?? 1;
1440
+ if (!Number.isFinite(this.defaultTimeoutMs) || this.defaultTimeoutMs < 0) {
1441
+ throw new Error("requestTimeoutMs must be a non-negative finite number");
1442
+ }
1443
+ if (!Number.isFinite(this.nextRequestId)) {
1444
+ throw new Error("firstRequestId must be a finite number");
1445
+ }
1446
+ void this.consumeInput();
1447
+ }
1448
+ onRequest(method, handler) {
1449
+ this.requestHandlers.set(method, handler);
1450
+ }
1451
+ onNotification(method, handler) {
1452
+ this.notificationHandlers.set(method, handler);
1453
+ }
1454
+ pendingRequestCount() {
1455
+ return this.pending.size;
1456
+ }
1457
+ sendNotification(method, params) {
1458
+ const notification = {
1459
+ jsonrpc: "2.0",
1460
+ method
1461
+ };
1462
+ if (params !== void 0) {
1463
+ notification.params = params;
1464
+ }
1465
+ this.sendMessage(notification);
1466
+ }
1467
+ sendRequest(method, params, options = {}) {
1468
+ if (this.disposed) {
1469
+ throw new Error("JSON-RPC message layer is disposed");
1470
+ }
1471
+ const id = options.id === void 0 ? this.nextRequestId++ : options.id;
1472
+ if (this.pending.has(id)) {
1473
+ throw new Error(`A request with id ${JSON.stringify(id)} is already pending`);
1474
+ }
1475
+ const timeoutMs = options.timeoutMs ?? this.defaultTimeoutMs;
1476
+ if (!Number.isFinite(timeoutMs) || timeoutMs < 0) {
1477
+ throw new Error("timeoutMs must be a non-negative finite number");
1478
+ }
1479
+ const request = {
1480
+ jsonrpc: "2.0",
1481
+ id,
1482
+ method
1483
+ };
1484
+ if (params !== void 0) {
1485
+ request.params = params;
1486
+ }
1487
+ return new Promise((resolve, reject) => {
1488
+ const timeout = setTimeout(() => {
1489
+ this.pending.delete(id);
1490
+ reject(
1491
+ new Error(`JSON-RPC request "${method}" timed out after ${timeoutMs}ms`)
1492
+ );
1493
+ }, timeoutMs);
1494
+ this.pending.set(id, {
1495
+ method,
1496
+ resolve,
1497
+ reject,
1498
+ timeout
1499
+ });
1500
+ try {
1501
+ this.sendMessage(request);
1502
+ } catch (error) {
1503
+ clearTimeout(timeout);
1504
+ this.pending.delete(id);
1505
+ reject(error);
1506
+ }
1507
+ });
1508
+ }
1509
+ dispose(reason = new Error("JSON-RPC message layer disposed")) {
1510
+ if (this.disposed) {
1511
+ return;
1512
+ }
1513
+ this.disposed = true;
1514
+ this.rejectAllPending(reason);
1515
+ }
1516
+ async consumeInput() {
1517
+ try {
1518
+ for await (const line of readLines(this.input)) {
1519
+ if (this.disposed || line.length === 0) {
1520
+ continue;
1521
+ }
1522
+ await this.handleIncomingLine(line);
1523
+ }
1524
+ if (!this.disposed) {
1525
+ this.dispose(new Error("JSON-RPC input stream closed"));
1526
+ }
1527
+ } catch (error) {
1528
+ if (!this.disposed) {
1529
+ this.dispose(
1530
+ error instanceof Error ? error : new Error(`JSON-RPC input stream failed: ${String(error)}`)
1531
+ );
1532
+ }
1533
+ }
1534
+ }
1535
+ async handleIncomingLine(line) {
1536
+ const parsed = parseJsonRpcMessage(line);
1537
+ if (parsed.type === "invalid") {
1538
+ this.sendMessage(createJsonRpcErrorResponse(parsed.id, parsed.error));
1539
+ return;
1540
+ }
1541
+ if (parsed.type === "response") {
1542
+ this.handleResponse(parsed.message);
1543
+ return;
1544
+ }
1545
+ if (parsed.type === "notification") {
1546
+ await this.handleNotification(parsed.message);
1547
+ return;
1548
+ }
1549
+ await this.handleRequest(parsed.message);
1550
+ }
1551
+ handleResponse(message) {
1552
+ const pending = this.pending.get(message.id);
1553
+ if (!pending) {
1554
+ return;
1555
+ }
1556
+ this.pending.delete(message.id);
1557
+ if (pending.timeout !== null) {
1558
+ clearTimeout(pending.timeout);
1559
+ }
1560
+ if ("error" in message) {
1561
+ pending.reject(toResponseError(message.error));
1562
+ return;
1563
+ }
1564
+ pending.resolve(message.result);
1565
+ }
1566
+ async handleRequest(message) {
1567
+ const handler = this.requestHandlers.get(message.method);
1568
+ if (!handler) {
1569
+ this.sendMessage(createJsonRpcErrorResponse(message.id, methodNotFound(message.method)));
1570
+ return;
1571
+ }
1572
+ try {
1573
+ const result = await handler(message.params, {
1574
+ id: message.id,
1575
+ method: message.method
1576
+ });
1577
+ this.sendMessage(createResponseMessage(message.id, result));
1578
+ } catch (error) {
1579
+ this.sendMessage(createJsonRpcErrorResponse(message.id, toDispatchError(error)));
1580
+ }
1581
+ }
1582
+ async handleNotification(message) {
1583
+ const handler = this.notificationHandlers.get(message.method);
1584
+ if (!handler) {
1585
+ return;
1586
+ }
1587
+ try {
1588
+ await handler(message.params, { method: message.method });
1589
+ } catch {
1590
+ }
1591
+ }
1592
+ sendMessage(message) {
1593
+ if (this.disposed) {
1594
+ throw new Error("JSON-RPC message layer is disposed");
1595
+ }
1596
+ this.output.write(serializeJsonRpcMessage(message));
1597
+ }
1598
+ rejectAllPending(error) {
1599
+ for (const pending of this.pending.values()) {
1600
+ if (pending.timeout !== null) {
1601
+ clearTimeout(pending.timeout);
1602
+ }
1603
+ pending.reject(error);
1604
+ }
1605
+ this.pending.clear();
1606
+ }
1607
+ };
1608
+
1609
+ // packages/poe-acp-client/src/acp-transport.ts
1610
+ function assertExtensionMethod(method) {
1611
+ if (!method.startsWith("_")) {
1612
+ throw new Error('Extension method must start with "_"');
1613
+ }
1614
+ }
1615
+ var AcpTransport = class {
1616
+ closed;
1617
+ command;
1618
+ child;
1619
+ layer;
1620
+ stderrChunks = [];
1621
+ resolveClosed = null;
1622
+ closeEvent = null;
1623
+ closeReason = null;
1624
+ constructor(options) {
1625
+ const {
1626
+ command,
1627
+ args = [],
1628
+ cwd,
1629
+ env,
1630
+ requestTimeoutMs,
1631
+ firstRequestId,
1632
+ spawn: spawn2 = spawnChildProcess
1633
+ } = options;
1634
+ this.command = command;
1635
+ this.closed = new Promise((resolve) => {
1636
+ this.resolveClosed = resolve;
1637
+ });
1638
+ this.child = spawn2(command, [...args], {
1639
+ cwd,
1640
+ env,
1641
+ stdio: ["pipe", "pipe", "pipe"]
1642
+ });
1643
+ this.child.stderr.setEncoding("utf8");
1644
+ this.child.stderr.on("data", (chunk) => {
1645
+ this.stderrChunks.push(String(chunk));
1646
+ });
1647
+ this.layer = new JsonRpcMessageLayer({
1648
+ input: this.child.stdout,
1649
+ output: this.child.stdin,
1650
+ requestTimeoutMs,
1651
+ firstRequestId
1652
+ });
1653
+ this.child.once("error", (error) => {
1654
+ const reason = error instanceof Error ? error : new Error(String(error));
1655
+ this.close(reason, this.child.exitCode ?? null, this.child.signalCode ?? null);
1656
+ });
1657
+ this.child.once("close", (code, signal) => {
1658
+ const reason = this.closeReason ?? new Error(
1659
+ `ACP transport closed (command "${this.command}", code: ${code ?? "null"}${signal ? `, signal: ${signal}` : ""})`
1660
+ );
1661
+ this.close(reason, code ?? null, signal ?? null);
1662
+ });
1663
+ }
1664
+ sendRequest(method, params, options = {}) {
1665
+ return this.layer.sendRequest(method, params, options);
1666
+ }
1667
+ sendExtRequest(method, params, options = {}) {
1668
+ assertExtensionMethod(method);
1669
+ return this.layer.sendRequest(method, params, options);
1670
+ }
1671
+ sendNotification(method, params) {
1672
+ this.layer.sendNotification(method, params);
1673
+ }
1674
+ sendExtNotification(method, params) {
1675
+ assertExtensionMethod(method);
1676
+ this.layer.sendNotification(method, params);
1677
+ }
1678
+ onRequest(method, handler) {
1679
+ this.layer.onRequest(method, handler);
1680
+ }
1681
+ onExtRequest(method, handler) {
1682
+ assertExtensionMethod(method);
1683
+ this.layer.onRequest(method, handler);
1684
+ }
1685
+ onNotification(method, handler) {
1686
+ this.layer.onNotification(method, handler);
1687
+ }
1688
+ onExtNotification(method, handler) {
1689
+ assertExtensionMethod(method);
1690
+ this.layer.onNotification(method, handler);
1691
+ }
1692
+ getStderrOutput() {
1693
+ return this.stderrChunks.join("");
1694
+ }
1695
+ pendingRequestCount() {
1696
+ return this.layer.pendingRequestCount();
1697
+ }
1698
+ dispose(reason = new Error("ACP transport disposed")) {
1699
+ if (this.closeEvent !== null) {
1700
+ return;
1701
+ }
1702
+ this.closeReason = reason;
1703
+ this.layer.dispose(reason);
1704
+ if (!this.child.stdin.destroyed && !this.child.stdin.writableEnded) {
1705
+ this.child.stdin.end();
1706
+ }
1707
+ if (this.child.exitCode !== null || this.child.signalCode !== null) {
1708
+ this.close(reason, this.child.exitCode, this.child.signalCode);
1709
+ return;
1710
+ }
1711
+ const killed = this.child.kill();
1712
+ if (!killed) {
1713
+ this.close(reason, this.child.exitCode, this.child.signalCode);
1714
+ }
1715
+ }
1716
+ close(reason, code, signal) {
1717
+ if (this.closeEvent !== null) {
1718
+ return;
1719
+ }
1720
+ this.layer.dispose(reason);
1721
+ this.closeEvent = {
1722
+ code,
1723
+ signal,
1724
+ reason,
1725
+ stderr: this.getStderrOutput()
1726
+ };
1727
+ this.resolveClosed?.(this.closeEvent);
1728
+ this.resolveClosed = null;
1729
+ }
1730
+ };
1731
+
1732
+ // packages/poe-acp-client/src/acp-client.ts
1733
+ function toError(reason) {
1734
+ return reason instanceof Error ? reason : new Error(String(reason));
1735
+ }
1736
+ function invalidParams(message) {
1737
+ return new AcpError(
1738
+ ACP_ERROR_CODE_INVALID_PARAMS,
1739
+ `Invalid params: ${message}`
1740
+ );
1741
+ }
1742
+ function resourceNotFound(resource) {
1743
+ return new AcpError(
1744
+ ACP_ERROR_CODE_RESOURCE_NOT_FOUND,
1745
+ `Resource not found: ${resource}`
1746
+ );
1747
+ }
1748
+ function assertAbsolutePath(path5) {
1749
+ if (!isAbsolute(path5)) {
1750
+ throw invalidParams('"path" must be an absolute path');
1751
+ }
1752
+ }
1753
+ function assertOneBasedLineNumber(line) {
1754
+ if (line === null || line === void 0) {
1755
+ return;
1756
+ }
1757
+ if (!Number.isInteger(line) || line < 1) {
1758
+ throw invalidParams('"line" must be a 1-based integer');
1759
+ }
1760
+ }
1761
+ function assertExtensionMethod2(method) {
1762
+ if (!method.startsWith("_")) {
1763
+ throw new Error('Extension method must start with "_"');
1764
+ }
1765
+ }
1766
+ function isInjectedTransportOptions(options) {
1767
+ return "transport" in options;
1768
+ }
1769
+ function createAsyncQueue() {
1770
+ const values = [];
1771
+ const waiters = [];
1772
+ let closed = false;
1773
+ let failure = null;
1774
+ const resolveOne = (value) => {
1775
+ const waiter = waiters.shift();
1776
+ if (!waiter) {
1777
+ return false;
1778
+ }
1779
+ waiter.resolve({ done: false, value });
1780
+ return true;
1781
+ };
1782
+ const iterator = {
1783
+ push(value) {
1784
+ if (closed || failure) {
1785
+ return;
1786
+ }
1787
+ if (!resolveOne(value)) {
1788
+ values.push(value);
1789
+ }
1790
+ },
1791
+ complete() {
1792
+ if (closed || failure) {
1793
+ return;
1794
+ }
1795
+ closed = true;
1796
+ while (waiters.length > 0) {
1797
+ waiters.shift()?.resolve({ done: true, value: void 0 });
1798
+ }
1799
+ },
1800
+ fail(error) {
1801
+ if (closed || failure) {
1802
+ return;
1803
+ }
1804
+ failure = error;
1805
+ while (waiters.length > 0) {
1806
+ waiters.shift()?.reject(error);
1807
+ }
1808
+ },
1809
+ async next() {
1810
+ if (values.length > 0) {
1811
+ const value = values.shift();
1812
+ return { done: false, value };
1813
+ }
1814
+ if (failure) {
1815
+ throw failure;
1816
+ }
1817
+ if (closed) {
1818
+ return { done: true, value: void 0 };
1819
+ }
1820
+ return new Promise((resolve, reject) => {
1821
+ waiters.push({ resolve, reject });
1822
+ });
1823
+ },
1824
+ async return() {
1825
+ iterator.complete();
1826
+ return { done: true, value: void 0 };
1827
+ },
1828
+ async throw(error) {
1829
+ const normalizedError = toError(error);
1830
+ iterator.fail(normalizedError);
1831
+ throw normalizedError;
1832
+ },
1833
+ [Symbol.asyncIterator]() {
1834
+ return iterator;
1835
+ }
1836
+ };
1837
+ return iterator;
1838
+ }
1839
+ var AcpClient = class {
1840
+ transport;
1841
+ clientProtocolVersion;
1842
+ clientCapabilities;
1843
+ clientInfo;
1844
+ permissionHandler;
1845
+ fsHandler;
1846
+ terminalHandler;
1847
+ activePromptUpdates = /* @__PURE__ */ new Map();
1848
+ trackedTerminalIds = /* @__PURE__ */ new Map();
1849
+ hasRegisteredFsReadHandler = false;
1850
+ hasRegisteredFsWriteHandler = false;
1851
+ hasRegisteredTerminalHandlers = false;
1852
+ disposed = false;
1853
+ lifecycleState = "uninitialized";
1854
+ negotiatedVersion = null;
1855
+ availableAuthMethods = [];
1856
+ negotiatedAgentCapabilities;
1857
+ negotiatedAgentInfo;
1858
+ constructor(options) {
1859
+ this.transport = isInjectedTransportOptions(options) ? options.transport : new AcpTransport({
1860
+ command: options.command,
1861
+ args: options.args,
1862
+ cwd: options.cwd,
1863
+ env: options.env,
1864
+ requestTimeoutMs: options.requestTimeoutMs,
1865
+ firstRequestId: options.firstRequestId,
1866
+ spawn: options.spawn
1867
+ });
1868
+ this.clientProtocolVersion = options.protocolVersion ?? 1;
1869
+ this.clientCapabilities = options.clientCapabilities;
1870
+ this.clientInfo = options.clientInfo;
1871
+ this.permissionHandler = options.handlers?.permission ?? options.permissionHandler;
1872
+ this.fsHandler = options.handlers?.fs ?? options.fsHandler;
1873
+ this.terminalHandler = options.handlers?.terminal ?? options.terminalHandler;
1874
+ this.transport.onRequest(
1875
+ "session/request_permission",
1876
+ async (params) => {
1877
+ if (!this.permissionHandler) {
1878
+ return {
1879
+ outcome: { outcome: "cancelled" }
1880
+ };
1881
+ }
1882
+ const outcome = await this.permissionHandler({
1883
+ toolCall: params.toolCall,
1884
+ options: params.options
1885
+ });
1886
+ return { outcome };
1887
+ }
1888
+ );
1889
+ this.registerCapabilityHandlers(this.clientCapabilities);
1890
+ this.transport.onNotification("session/update", (params) => {
1891
+ this.handleSessionUpdateNotification(params);
1892
+ });
1893
+ }
1894
+ get state() {
1895
+ return this.lifecycleState;
1896
+ }
1897
+ get negotiatedProtocolVersion() {
1898
+ return this.negotiatedVersion;
1899
+ }
1900
+ get authMethods() {
1901
+ return [...this.availableAuthMethods];
1902
+ }
1903
+ get agentCapabilities() {
1904
+ return this.negotiatedAgentCapabilities;
1905
+ }
1906
+ get agentInfo() {
1907
+ return this.negotiatedAgentInfo;
1908
+ }
1909
+ get closed() {
1910
+ return this.transport.closed;
1911
+ }
1912
+ async initialize(clientCapabilities) {
1913
+ if (this.lifecycleState !== "uninitialized") {
1914
+ throw new Error("initialize() can only be called once.");
1915
+ }
1916
+ if (clientCapabilities !== void 0) {
1917
+ this.clientCapabilities = clientCapabilities;
1918
+ this.registerCapabilityHandlers(clientCapabilities);
1919
+ }
1920
+ const response = await this.transport.sendRequest("initialize", {
1921
+ protocolVersion: this.clientProtocolVersion,
1922
+ clientInfo: this.clientInfo,
1923
+ clientCapabilities: this.clientCapabilities
1924
+ });
1925
+ const negotiatedProtocolVersion = Math.min(
1926
+ this.clientProtocolVersion,
1927
+ response.protocolVersion
1928
+ );
1929
+ this.negotiatedVersion = negotiatedProtocolVersion;
1930
+ this.negotiatedAgentCapabilities = response.agentCapabilities;
1931
+ this.negotiatedAgentInfo = response.agentInfo;
1932
+ this.availableAuthMethods = response.authMethods ? [...response.authMethods] : [];
1933
+ this.lifecycleState = this.availableAuthMethods.length > 0 ? "initialized" : "ready";
1934
+ return {
1935
+ protocolVersion: negotiatedProtocolVersion,
1936
+ ...this.negotiatedAgentCapabilities !== void 0 ? { agentCapabilities: this.negotiatedAgentCapabilities } : {},
1937
+ ...this.negotiatedAgentInfo !== void 0 ? { agentInfo: this.negotiatedAgentInfo } : {},
1938
+ ...this.availableAuthMethods.length > 0 ? { authMethods: this.authMethods } : {}
1939
+ };
1940
+ }
1941
+ async authenticate(methodId) {
1942
+ if (this.lifecycleState === "uninitialized") {
1943
+ throw new Error("Cannot authenticate before initialize().");
1944
+ }
1945
+ if (this.lifecycleState === "ready") {
1946
+ throw new Error("Authentication is not required for this agent.");
1947
+ }
1948
+ if (!this.availableAuthMethods.some((authMethod) => authMethod.id === methodId)) {
1949
+ throw new Error(`Unknown auth method "${methodId}".`);
1950
+ }
1951
+ const response = await this.transport.sendRequest("authenticate", {
1952
+ methodId
1953
+ });
1954
+ this.lifecycleState = "ready";
1955
+ return response;
1956
+ }
1957
+ async newSession(cwd, mcpServers) {
1958
+ this.assertReady("session/new");
1959
+ this.assertMcpServerCapabilitySupport(mcpServers);
1960
+ return this.transport.sendRequest("session/new", {
1961
+ cwd,
1962
+ mcpServers
1963
+ });
1964
+ }
1965
+ async loadSession(sessionId, cwd, mcpServers) {
1966
+ this.assertReady("session/load");
1967
+ if (this.negotiatedAgentCapabilities?.loadSession !== true) {
1968
+ throw new Error(
1969
+ 'Cannot call "session/load" because the agent does not support session loading.'
1970
+ );
1971
+ }
1972
+ this.assertMcpServerCapabilitySupport(mcpServers);
1973
+ return this.transport.sendRequest("session/load", {
1974
+ sessionId,
1975
+ cwd,
1976
+ mcpServers
1977
+ });
1978
+ }
1979
+ async cancelSession(sessionId) {
1980
+ this.assertReady("session/cancel");
1981
+ const payload = { sessionId };
1982
+ this.transport.sendNotification("session/cancel", payload);
1983
+ }
1984
+ async setMode(sessionId, modeId) {
1985
+ this.assertReady("session/set_mode");
1986
+ return this.transport.sendRequest("session/set_mode", {
1987
+ sessionId,
1988
+ modeId
1989
+ });
1990
+ }
1991
+ async setConfigOption(sessionId, configId, value) {
1992
+ this.assertReady("session/set_config_option");
1993
+ const response = await this.transport.sendRequest("session/set_config_option", {
1994
+ sessionId,
1995
+ configId,
1996
+ value
1997
+ });
1998
+ return response.configOptions;
1999
+ }
2000
+ prompt(sessionId, content) {
2001
+ this.assertReady("session/prompt");
2002
+ this.assertPromptContentCapabilitySupport(content);
2003
+ if (this.activePromptUpdates.has(sessionId)) {
2004
+ throw new Error(
2005
+ `Cannot call "session/prompt" while another prompt is in progress for session "${sessionId}".`
2006
+ );
2007
+ }
2008
+ const updates = createAsyncQueue();
2009
+ this.activePromptUpdates.set(sessionId, updates);
2010
+ let requestPromise;
2011
+ try {
2012
+ requestPromise = this.transport.sendRequest("session/prompt", {
2013
+ sessionId,
2014
+ prompt: content
2015
+ });
2016
+ } catch (error) {
2017
+ const normalizedError = toError(error);
2018
+ this.activePromptUpdates.delete(sessionId);
2019
+ updates.fail(normalizedError);
2020
+ throw normalizedError;
2021
+ }
2022
+ const response = requestPromise.then((promptResponse) => {
2023
+ this.activePromptUpdates.delete(sessionId);
2024
+ updates.complete();
2025
+ return promptResponse;
2026
+ }).catch((error) => {
2027
+ const normalizedError = toError(error);
2028
+ this.activePromptUpdates.delete(sessionId);
2029
+ updates.fail(normalizedError);
2030
+ throw normalizedError;
2031
+ });
2032
+ return {
2033
+ response,
2034
+ [Symbol.asyncIterator]() {
2035
+ return updates;
2036
+ }
2037
+ };
2038
+ }
2039
+ async sendExtRequest(method, params, options = {}) {
2040
+ assertExtensionMethod2(method);
2041
+ return this.transport.sendRequest(method, params, options);
2042
+ }
2043
+ async sendExtNotification(method, params) {
2044
+ assertExtensionMethod2(method);
2045
+ this.transport.sendNotification(method, params);
2046
+ }
2047
+ onExtRequest(method, handler) {
2048
+ assertExtensionMethod2(method);
2049
+ this.transport.onRequest(method, handler);
2050
+ }
2051
+ onExtNotification(method, handler) {
2052
+ assertExtensionMethod2(method);
2053
+ this.transport.onNotification(method, handler);
2054
+ }
2055
+ async dispose() {
2056
+ if (this.disposed) {
2057
+ if (this.transport.closed) {
2058
+ await this.transport.closed;
2059
+ }
2060
+ return;
2061
+ }
2062
+ this.disposed = true;
2063
+ const disposeReason = new Error("ACP client disposed");
2064
+ for (const updates of this.activePromptUpdates.values()) {
2065
+ updates.fail(disposeReason);
2066
+ }
2067
+ this.activePromptUpdates.clear();
2068
+ if (typeof this.transport.dispose === "function") {
2069
+ this.transport.dispose(disposeReason);
2070
+ }
2071
+ if (this.transport.closed) {
2072
+ await this.transport.closed;
2073
+ }
2074
+ }
2075
+ assertReady(operation) {
2076
+ if (this.lifecycleState === "ready") {
2077
+ return;
2078
+ }
2079
+ if (this.lifecycleState === "uninitialized") {
2080
+ throw new Error(`Cannot call "${operation}" before initialize().`);
2081
+ }
2082
+ throw new Error(`Cannot call "${operation}" before authentication completes.`);
2083
+ }
2084
+ registerCapabilityHandlers(capabilities) {
2085
+ if (!this.hasRegisteredFsReadHandler && capabilities?.fs?.readTextFile === true && this.fsHandler?.readTextFile) {
2086
+ const readTextFile = this.fsHandler.readTextFile;
2087
+ this.transport.onRequest(
2088
+ "fs/read_text_file",
2089
+ async (params) => {
2090
+ assertAbsolutePath(params.path);
2091
+ assertOneBasedLineNumber(params.line);
2092
+ const content = await readTextFile({
2093
+ sessionId: params.sessionId,
2094
+ path: params.path,
2095
+ line: params.line,
2096
+ limit: params.limit
2097
+ });
2098
+ return { content };
2099
+ }
2100
+ );
2101
+ this.hasRegisteredFsReadHandler = true;
2102
+ }
2103
+ if (!this.hasRegisteredFsWriteHandler && capabilities?.fs?.writeTextFile === true && this.fsHandler?.writeTextFile) {
2104
+ const writeTextFile = this.fsHandler.writeTextFile;
2105
+ this.transport.onRequest(
2106
+ "fs/write_text_file",
2107
+ async (params) => {
2108
+ assertAbsolutePath(params.path);
2109
+ await writeTextFile({
2110
+ sessionId: params.sessionId,
2111
+ path: params.path,
2112
+ content: params.content
2113
+ });
2114
+ return {};
2115
+ }
2116
+ );
2117
+ this.hasRegisteredFsWriteHandler = true;
2118
+ }
2119
+ if (!this.hasRegisteredTerminalHandlers && capabilities?.terminal === true && this.terminalHandler) {
2120
+ const terminalHandler = this.terminalHandler;
2121
+ this.transport.onRequest(
2122
+ "terminal/create",
2123
+ async (params) => {
2124
+ const terminalId = await terminalHandler.create({
2125
+ sessionId: params.sessionId,
2126
+ command: params.command,
2127
+ args: params.args,
2128
+ cwd: params.cwd,
2129
+ env: params.env,
2130
+ outputByteLimit: params.outputByteLimit
2131
+ });
2132
+ this.trackTerminal(params.sessionId, terminalId);
2133
+ return { terminalId };
2134
+ }
2135
+ );
2136
+ this.transport.onRequest(
2137
+ "terminal/output",
2138
+ async (params) => {
2139
+ this.assertKnownTerminal(params.sessionId, params.terminalId);
2140
+ return terminalHandler.output({
2141
+ sessionId: params.sessionId,
2142
+ terminalId: params.terminalId
2143
+ });
2144
+ }
2145
+ );
2146
+ this.transport.onRequest(
2147
+ "terminal/wait_for_exit",
2148
+ async (params) => {
2149
+ this.assertKnownTerminal(params.sessionId, params.terminalId);
2150
+ return terminalHandler.waitForExit({
2151
+ sessionId: params.sessionId,
2152
+ terminalId: params.terminalId
2153
+ });
2154
+ }
2155
+ );
2156
+ this.transport.onRequest(
2157
+ "terminal/kill",
2158
+ async (params) => {
2159
+ this.assertKnownTerminal(params.sessionId, params.terminalId);
2160
+ await terminalHandler.kill({
2161
+ sessionId: params.sessionId,
2162
+ terminalId: params.terminalId
2163
+ });
2164
+ return {};
2165
+ }
2166
+ );
2167
+ this.transport.onRequest(
2168
+ "terminal/release",
2169
+ async (params) => {
2170
+ this.assertKnownTerminal(params.sessionId, params.terminalId);
2171
+ await terminalHandler.release({
2172
+ sessionId: params.sessionId,
2173
+ terminalId: params.terminalId
2174
+ });
2175
+ this.untrackTerminal(params.sessionId, params.terminalId);
2176
+ return {};
2177
+ }
2178
+ );
2179
+ this.hasRegisteredTerminalHandlers = true;
2180
+ }
2181
+ }
2182
+ assertMcpServerCapabilitySupport(mcpServers) {
2183
+ const mcpCapabilities = this.negotiatedAgentCapabilities?.mcpCapabilities;
2184
+ for (const mcpServer of mcpServers) {
2185
+ if (!("type" in mcpServer)) {
2186
+ continue;
2187
+ }
2188
+ if (mcpServer.type === "http" && mcpCapabilities?.http !== true) {
2189
+ throw new Error('Agent does not support MCP server type "http".');
2190
+ }
2191
+ if (mcpServer.type === "sse" && mcpCapabilities?.sse !== true) {
2192
+ throw new Error('Agent does not support MCP server type "sse".');
2193
+ }
2194
+ }
2195
+ }
2196
+ handleSessionUpdateNotification(notification) {
2197
+ const activePrompt = this.activePromptUpdates.get(notification.sessionId);
2198
+ if (!activePrompt) {
2199
+ return;
2200
+ }
2201
+ activePrompt.push({
2202
+ jsonrpc: "2.0",
2203
+ method: "session/update",
2204
+ params: notification
2205
+ });
2206
+ }
2207
+ trackTerminal(sessionId, terminalId) {
2208
+ const sessionTerminals = this.trackedTerminalIds.get(sessionId);
2209
+ if (sessionTerminals) {
2210
+ sessionTerminals.add(terminalId);
2211
+ return;
2212
+ }
2213
+ this.trackedTerminalIds.set(sessionId, /* @__PURE__ */ new Set([terminalId]));
2214
+ }
2215
+ assertKnownTerminal(sessionId, terminalId) {
2216
+ const sessionTerminals = this.trackedTerminalIds.get(sessionId);
2217
+ if (sessionTerminals?.has(terminalId) === true) {
2218
+ return;
2219
+ }
2220
+ throw resourceNotFound(`terminal "${terminalId}"`);
2221
+ }
2222
+ untrackTerminal(sessionId, terminalId) {
2223
+ const sessionTerminals = this.trackedTerminalIds.get(sessionId);
2224
+ if (!sessionTerminals) {
2225
+ return;
2226
+ }
2227
+ sessionTerminals.delete(terminalId);
2228
+ if (sessionTerminals.size === 0) {
2229
+ this.trackedTerminalIds.delete(sessionId);
2230
+ }
2231
+ }
2232
+ assertPromptContentCapabilitySupport(content) {
2233
+ const promptCapabilities = this.negotiatedAgentCapabilities?.promptCapabilities;
2234
+ for (const block of content) {
2235
+ if (block.type === "image" && promptCapabilities?.image !== true) {
2236
+ throw new Error('Agent does not support prompt content type "image".');
2237
+ }
2238
+ if (block.type === "audio" && promptCapabilities?.audio !== true) {
2239
+ throw new Error('Agent does not support prompt content type "audio".');
2240
+ }
2241
+ if (block.type === "resource" && promptCapabilities?.embeddedContext !== true) {
2242
+ throw new Error('Agent does not support prompt content type "resource".');
2243
+ }
2244
+ }
2245
+ }
2246
+ };
2247
+
2248
+ // packages/poe-acp-client/src/run-report.ts
2249
+ import * as fsPromises from "node:fs/promises";
2250
+ import { homedir } from "node:os";
2251
+ import { join } from "node:path";
2252
+
2253
+ // packages/poe-acp-client/src/stream-helpers.ts
2254
+ async function extractUsageFromSessionUpdateStream(stream) {
2255
+ const updates = [];
2256
+ for await (const entry of stream) {
2257
+ const update = toSessionUpdate(entry);
2258
+ if (update.sessionUpdate === "usage_update") {
2259
+ updates.push(update);
2260
+ }
2261
+ }
2262
+ return updates;
2263
+ }
2264
+ async function extractToolCallSummariesFromSessionUpdateStream(stream) {
2265
+ const summaries = /* @__PURE__ */ new Map();
2266
+ for await (const entry of stream) {
2267
+ const update = toSessionUpdate(entry);
2268
+ if (update.sessionUpdate === "tool_call") {
2269
+ const summary = {
2270
+ toolCallId: update.toolCallId,
2271
+ title: update.title
2272
+ };
2273
+ if (update.kind !== void 0) {
2274
+ summary.kind = update.kind;
2275
+ }
2276
+ if (update.status !== void 0) {
2277
+ summary.status = update.status;
2278
+ }
2279
+ if (update.rawInput !== void 0) {
2280
+ summary.rawInput = update.rawInput;
2281
+ }
2282
+ if (update.rawOutput !== void 0) {
2283
+ summary.rawOutput = update.rawOutput;
2284
+ }
2285
+ summaries.set(update.toolCallId, summary);
2286
+ continue;
2287
+ }
2288
+ if (update.sessionUpdate === "tool_call_update") {
2289
+ const existing = summaries.get(update.toolCallId);
2290
+ const summary = existing ?? {
2291
+ toolCallId: update.toolCallId,
2292
+ title: toTitle(update.title, update.toolCallId)
2293
+ };
2294
+ if (update.title !== null && update.title !== void 0 && update.title.length > 0) {
2295
+ summary.title = update.title;
2296
+ }
2297
+ if (update.kind !== null && update.kind !== void 0) {
2298
+ summary.kind = update.kind;
2299
+ }
2300
+ if (update.status !== null && update.status !== void 0) {
2301
+ summary.status = update.status;
2302
+ }
2303
+ if (update.rawInput !== void 0) {
2304
+ summary.rawInput = update.rawInput;
2305
+ }
2306
+ if (update.rawOutput !== void 0) {
2307
+ summary.rawOutput = update.rawOutput;
2308
+ }
2309
+ summaries.set(update.toolCallId, summary);
2310
+ }
2311
+ }
2312
+ return Array.from(summaries.values());
2313
+ }
2314
+ function toSessionUpdate(entry) {
2315
+ if (isSessionUpdateNotification(entry)) {
2316
+ return entry.params.update;
2317
+ }
2318
+ return entry;
2319
+ }
2320
+ function isSessionUpdateNotification(entry) {
2321
+ return typeof entry.jsonrpc === "string" && entry.method === "session/update";
2322
+ }
2323
+ function toTitle(value, fallback) {
2324
+ if (typeof value === "string" && value.length > 0) {
2325
+ return value;
2326
+ }
2327
+ return fallback;
2328
+ }
2329
+
2330
+ // packages/poe-acp-client/src/run-report.ts
2331
+ async function generateRunReportFromSessionUpdateStream(stream, options = {}) {
2332
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
2333
+ const bufferedEntries = [];
2334
+ let runIdFromStream;
2335
+ for await (const entry of stream) {
2336
+ bufferedEntries.push(entry);
2337
+ if (runIdFromStream) {
2338
+ continue;
2339
+ }
2340
+ if (isSessionUpdateNotification2(entry)) {
2341
+ const sessionId = toNonEmptyString(entry.params.sessionId);
2342
+ if (sessionId) {
2343
+ runIdFromStream = sessionId;
2344
+ }
2345
+ }
2346
+ }
2347
+ const runId = toNonEmptyString(options.runId) ?? runIdFromStream;
2348
+ if (!runId) {
2349
+ throw new Error("Run id is required via options.runId or session/update stream items");
2350
+ }
2351
+ const startTime = normalizeTime(options.startTime, now);
2352
+ const endTime = normalizeTime(options.endTime, now);
2353
+ const toolCalls = await extractToolCallSummariesFromSessionUpdateStream(bufferedEntries);
2354
+ const usageUpdates = await extractUsageFromSessionUpdateStream(bufferedEntries);
2355
+ const usage = summarizeUsage(usageUpdates);
2356
+ const errors = collectErrors(toolCalls, options.errors);
2357
+ const exitStatus = options.exitStatus ?? (errors.length > 0 ? "failed" : "success");
2358
+ return {
2359
+ runId,
2360
+ startTime,
2361
+ endTime,
2362
+ exitStatus,
2363
+ toolCalls,
2364
+ usage,
2365
+ errors
2366
+ };
2367
+ }
2368
+ function isSessionUpdateNotification2(entry) {
2369
+ return typeof entry.jsonrpc === "string" && entry.method === "session/update";
2370
+ }
2371
+ function normalizeTime(value, now) {
2372
+ if (value instanceof Date) {
2373
+ return value.toISOString();
2374
+ }
2375
+ if (typeof value === "string" && value.length > 0) {
2376
+ const parsed = new Date(value);
2377
+ if (!Number.isNaN(parsed.getTime())) {
2378
+ return parsed.toISOString();
2379
+ }
2380
+ }
2381
+ return now().toISOString();
2382
+ }
2383
+ function summarizeUsage(updates) {
2384
+ let used = 0;
2385
+ let size = 0;
2386
+ let cost;
2387
+ for (const update of updates) {
2388
+ used += update.used;
2389
+ size += update.size;
2390
+ if (update.cost !== void 0) {
2391
+ cost = update.cost;
2392
+ }
2393
+ }
2394
+ const usage = {
2395
+ used,
2396
+ size,
2397
+ updates: updates.length
2398
+ };
2399
+ if (cost !== void 0) {
2400
+ usage.cost = cost;
2401
+ }
2402
+ return usage;
2403
+ }
2404
+ function collectErrors(toolCalls, additionalErrors) {
2405
+ const errors = [];
2406
+ for (const toolCall of toolCalls) {
2407
+ if (toolCall.status !== "failed") {
2408
+ continue;
2409
+ }
2410
+ errors.push({
2411
+ toolCallId: toolCall.toolCallId,
2412
+ message: toErrorMessage(toolCall)
2413
+ });
2414
+ }
2415
+ if (additionalErrors) {
2416
+ for (const message of additionalErrors) {
2417
+ const text = toNonEmptyString(message);
2418
+ if (text) {
2419
+ errors.push({ message: text });
2420
+ }
2421
+ }
2422
+ }
2423
+ return errors;
2424
+ }
2425
+ function toErrorMessage(toolCall) {
2426
+ if (typeof toolCall.rawOutput === "string" && toolCall.rawOutput.length > 0) {
2427
+ return toolCall.rawOutput;
2428
+ }
2429
+ if (toolCall.rawOutput instanceof Error && toolCall.rawOutput.message.length > 0) {
2430
+ return toolCall.rawOutput.message;
2431
+ }
2432
+ if (toolCall.rawOutput !== void 0 && toolCall.rawOutput !== null) {
2433
+ const encoded = trySerialize(toolCall.rawOutput);
2434
+ if (encoded) {
2435
+ return encoded;
2436
+ }
2437
+ }
2438
+ return `${toolCall.title} failed`;
2439
+ }
2440
+ function trySerialize(value) {
2441
+ try {
2442
+ const serialized = JSON.stringify(value);
2443
+ if (typeof serialized === "string" && serialized.length > 0) {
2444
+ return serialized;
2445
+ }
2446
+ } catch {
2447
+ return void 0;
2448
+ }
2449
+ return void 0;
2450
+ }
2451
+ function toNonEmptyString(value) {
2452
+ if (typeof value !== "string" || value.length === 0) {
2453
+ return void 0;
2454
+ }
2455
+ return value;
2456
+ }
2457
+
2458
+ // packages/config-mutations/src/execution/apply-mutation.ts
2459
+ import Mustache from "mustache";
2460
+
2461
+ // packages/config-mutations/src/formats/json.ts
2462
+ import * as jsonc from "jsonc-parser";
2463
+ function isConfigObject(value) {
2464
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2465
+ }
2466
+ function parse2(content) {
2467
+ if (!content || content.trim() === "") {
2468
+ return {};
2469
+ }
2470
+ const errors = [];
2471
+ const parsed = jsonc.parse(content, errors, {
2472
+ allowTrailingComma: true,
2473
+ disallowComments: false
2474
+ });
2475
+ if (errors.length > 0) {
2476
+ throw new Error(`JSON parse error: ${jsonc.printParseErrorCode(errors[0].error)}`);
2477
+ }
2478
+ if (parsed === null || parsed === void 0) {
2479
+ return {};
2480
+ }
2481
+ if (!isConfigObject(parsed)) {
2482
+ throw new Error("Expected JSON object.");
2483
+ }
2484
+ return parsed;
2485
+ }
2486
+ function serialize(obj) {
2487
+ return `${JSON.stringify(obj, null, 2)}
2488
+ `;
2489
+ }
2490
+ function merge(base, patch) {
2491
+ const result = { ...base };
2492
+ for (const [key, value] of Object.entries(patch)) {
2493
+ if (value === void 0) {
2494
+ continue;
2495
+ }
2496
+ const existing = result[key];
2497
+ if (isConfigObject(existing) && isConfigObject(value)) {
2498
+ result[key] = merge(existing, value);
2499
+ continue;
2500
+ }
2501
+ result[key] = value;
2502
+ }
2503
+ return result;
2504
+ }
2505
+ function prune(obj, shape) {
2506
+ let changed = false;
2507
+ const result = { ...obj };
2508
+ for (const [key, pattern] of Object.entries(shape)) {
2509
+ if (!(key in result)) {
2510
+ continue;
2511
+ }
2512
+ const current = result[key];
2513
+ if (isConfigObject(pattern) && Object.keys(pattern).length === 0) {
2514
+ delete result[key];
2515
+ changed = true;
2516
+ continue;
2517
+ }
2518
+ if (isConfigObject(pattern) && isConfigObject(current)) {
2519
+ const { changed: childChanged, result: childResult } = prune(
2520
+ current,
2521
+ pattern
2522
+ );
2523
+ if (childChanged) {
2524
+ changed = true;
2525
+ }
2526
+ if (Object.keys(childResult).length === 0) {
2527
+ delete result[key];
2528
+ } else {
2529
+ result[key] = childResult;
2530
+ }
2531
+ continue;
2532
+ }
2533
+ delete result[key];
2534
+ changed = true;
2535
+ }
2536
+ return { changed, result };
2537
+ }
2538
+ var jsonFormat = {
2539
+ parse: parse2,
2540
+ serialize,
2541
+ merge,
2542
+ prune
2543
+ };
2544
+
2545
+ // packages/config-mutations/src/formats/toml.ts
2546
+ import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
2547
+ function isConfigObject2(value) {
2548
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2549
+ }
2550
+ function parse3(content) {
2551
+ if (!content || content.trim() === "") {
2552
+ return {};
2553
+ }
2554
+ const parsed = parseToml(content);
2555
+ if (!isConfigObject2(parsed)) {
2556
+ throw new Error("Expected TOML document to be a table.");
2557
+ }
2558
+ return parsed;
2559
+ }
2560
+ function serialize2(obj) {
2561
+ const serialized = stringifyToml(obj);
2562
+ return serialized.endsWith("\n") ? serialized : `${serialized}
2563
+ `;
2564
+ }
2565
+ function merge2(base, patch) {
2566
+ const result = { ...base };
2567
+ for (const [key, value] of Object.entries(patch)) {
2568
+ if (value === void 0) {
2569
+ continue;
2570
+ }
2571
+ const existing = result[key];
2572
+ if (isConfigObject2(existing) && isConfigObject2(value)) {
2573
+ result[key] = merge2(existing, value);
2574
+ continue;
2575
+ }
2576
+ result[key] = value;
2577
+ }
2578
+ return result;
2579
+ }
2580
+ function prune2(obj, shape) {
2581
+ let changed = false;
2582
+ const result = { ...obj };
2583
+ for (const [key, pattern] of Object.entries(shape)) {
2584
+ if (!(key in result)) {
2585
+ continue;
2586
+ }
2587
+ const current = result[key];
2588
+ if (isConfigObject2(pattern) && Object.keys(pattern).length === 0) {
2589
+ delete result[key];
2590
+ changed = true;
2591
+ continue;
2592
+ }
2593
+ if (isConfigObject2(pattern) && isConfigObject2(current)) {
2594
+ const { changed: childChanged, result: childResult } = prune2(
2595
+ current,
2596
+ pattern
2597
+ );
2598
+ if (childChanged) {
2599
+ changed = true;
2600
+ }
2601
+ if (Object.keys(childResult).length === 0) {
2602
+ delete result[key];
2603
+ } else {
2604
+ result[key] = childResult;
2605
+ }
2606
+ continue;
2607
+ }
2608
+ delete result[key];
2609
+ changed = true;
2610
+ }
2611
+ return { changed, result };
2612
+ }
2613
+ var tomlFormat = {
2614
+ parse: parse3,
2615
+ serialize: serialize2,
2616
+ merge: merge2,
2617
+ prune: prune2
2618
+ };
2619
+
2620
+ // packages/config-mutations/src/formats/index.ts
2621
+ var formatRegistry = {
2622
+ json: jsonFormat,
2623
+ toml: tomlFormat
2624
+ };
2625
+ var extensionMap = {
2626
+ ".json": "json",
2627
+ ".toml": "toml"
2628
+ };
2629
+ function getConfigFormat(pathOrFormat) {
2630
+ if (pathOrFormat in formatRegistry) {
2631
+ return formatRegistry[pathOrFormat];
2632
+ }
2633
+ const ext = getExtension(pathOrFormat);
2634
+ const formatName = extensionMap[ext];
2635
+ if (!formatName) {
2636
+ throw new Error(
2637
+ `Unsupported config format. Cannot detect format from "${pathOrFormat}". Supported extensions: ${Object.keys(extensionMap).join(", ")}. Supported format names: ${Object.keys(formatRegistry).join(", ")}.`
2638
+ );
2639
+ }
2640
+ return formatRegistry[formatName];
2641
+ }
2642
+ function detectFormat(path5) {
2643
+ const ext = getExtension(path5);
2644
+ return extensionMap[ext];
2645
+ }
2646
+ function getExtension(path5) {
2647
+ const lastDot = path5.lastIndexOf(".");
2648
+ if (lastDot === -1) {
2649
+ return "";
2650
+ }
2651
+ return path5.slice(lastDot).toLowerCase();
2652
+ }
2653
+
2654
+ // packages/config-mutations/src/execution/path-utils.ts
2655
+ import path from "node:path";
2656
+ function expandHome(targetPath, homeDir) {
2657
+ if (!targetPath?.startsWith("~")) {
2658
+ return targetPath;
2659
+ }
2660
+ if (targetPath.startsWith("~./")) {
2661
+ targetPath = `~/.${targetPath.slice(3)}`;
2662
+ }
2663
+ let remainder = targetPath.slice(1);
2664
+ if (remainder.startsWith("/") || remainder.startsWith("\\")) {
2665
+ remainder = remainder.slice(1);
2666
+ } else if (remainder.startsWith(".")) {
2667
+ remainder = remainder.slice(1);
2668
+ if (remainder.startsWith("/") || remainder.startsWith("\\")) {
2669
+ remainder = remainder.slice(1);
2670
+ }
2671
+ }
2672
+ return remainder.length === 0 ? homeDir : path.join(homeDir, remainder);
2673
+ }
2674
+ function validateHomePath(targetPath) {
2675
+ if (typeof targetPath !== "string" || targetPath.length === 0) {
2676
+ throw new Error("Target path must be a non-empty string.");
2677
+ }
2678
+ if (!targetPath.startsWith("~")) {
2679
+ throw new Error(
2680
+ `All target paths must be home-relative (start with ~). Received: "${targetPath}"`
2681
+ );
2682
+ }
2683
+ }
2684
+ function resolvePath(rawPath, homeDir, pathMapper) {
2685
+ validateHomePath(rawPath);
2686
+ const expanded = expandHome(rawPath, homeDir);
2687
+ if (!pathMapper) {
2688
+ return expanded;
2689
+ }
2690
+ const rawDirectory = path.dirname(expanded);
2691
+ const mappedDirectory = pathMapper.mapTargetDirectory({
2692
+ targetDirectory: rawDirectory
2693
+ });
2694
+ const filename = path.basename(expanded);
2695
+ return filename.length === 0 ? mappedDirectory : path.join(mappedDirectory, filename);
2696
+ }
2697
+
2698
+ // packages/config-mutations/src/fs-utils.ts
2699
+ function isNotFound(error) {
2700
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
2701
+ }
2702
+ async function readFileIfExists(fs2, target) {
2703
+ try {
2704
+ return await fs2.readFile(target, "utf8");
2705
+ } catch (error) {
2706
+ if (isNotFound(error)) {
2707
+ return null;
2708
+ }
2709
+ throw error;
2710
+ }
2711
+ }
2712
+ async function pathExists(fs2, target) {
2713
+ try {
2714
+ await fs2.stat(target);
2715
+ return true;
2716
+ } catch (error) {
2717
+ if (isNotFound(error)) {
2718
+ return false;
2719
+ }
2720
+ throw error;
2721
+ }
2722
+ }
2723
+ function createTimestamp() {
2724
+ return (/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-").replaceAll(".", "-");
2725
+ }
2726
+
2727
+ // packages/config-mutations/src/execution/apply-mutation.ts
2728
+ function resolveValue(resolver, options) {
2729
+ if (typeof resolver === "function") {
2730
+ return resolver(options);
2731
+ }
2732
+ return resolver;
2733
+ }
2734
+ function createInvalidDocumentBackupPath(targetPath) {
2735
+ const ext = targetPath.includes(".") ? targetPath.split(".").pop() : "bak";
2736
+ return `${targetPath}.invalid-${createTimestamp()}.${ext}`;
2737
+ }
2738
+ async function backupInvalidDocument(fs2, targetPath, content) {
2739
+ const backupPath = createInvalidDocumentBackupPath(targetPath);
2740
+ await fs2.writeFile(backupPath, content, { encoding: "utf8" });
2741
+ }
2742
+ function describeMutation(kind, targetPath) {
2743
+ const displayPath = targetPath ?? "target";
2744
+ switch (kind) {
2745
+ case "ensureDirectory":
2746
+ return `Create ${displayPath}`;
2747
+ case "removeDirectory":
2748
+ return `Remove directory ${displayPath}`;
2749
+ case "backup":
2750
+ return `Backup ${displayPath}`;
2751
+ case "templateWrite":
2752
+ return `Write ${displayPath}`;
2753
+ case "chmod":
2754
+ return `Set permissions on ${displayPath}`;
2755
+ case "removeFile":
2756
+ return `Remove ${displayPath}`;
2757
+ case "configMerge":
2758
+ case "configPrune":
2759
+ case "configTransform":
2760
+ case "templateMergeToml":
2761
+ case "templateMergeJson":
2762
+ return `Update ${displayPath}`;
2763
+ default:
2764
+ return "Operation";
2765
+ }
2766
+ }
2767
+ function pruneKeysByPrefix(table, prefix) {
2768
+ const result = {};
2769
+ for (const [key, value] of Object.entries(table)) {
2770
+ if (!key.startsWith(prefix)) {
2771
+ result[key] = value;
2772
+ }
2773
+ }
2774
+ return result;
2775
+ }
2776
+ function isConfigObject3(value) {
2777
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2778
+ }
2779
+ function mergeWithPruneByPrefix(base, patch, pruneByPrefix) {
2780
+ const result = { ...base };
2781
+ const prefixMap = pruneByPrefix ?? {};
2782
+ for (const [key, value] of Object.entries(patch)) {
2783
+ const current = result[key];
2784
+ const prefix = prefixMap[key];
2785
+ if (isConfigObject3(current) && isConfigObject3(value)) {
2786
+ if (prefix) {
2787
+ const pruned = pruneKeysByPrefix(current, prefix);
2788
+ result[key] = { ...pruned, ...value };
2789
+ } else {
2790
+ result[key] = mergeWithPruneByPrefix(
2791
+ current,
2792
+ value,
2793
+ prefixMap
2794
+ );
2795
+ }
2796
+ continue;
2797
+ }
2798
+ result[key] = value;
2799
+ }
2800
+ return result;
2801
+ }
2802
+ async function applyMutation(mutation, context, options) {
2803
+ switch (mutation.kind) {
2804
+ case "ensureDirectory":
2805
+ return applyEnsureDirectory(mutation, context, options);
2806
+ case "removeDirectory":
2807
+ return applyRemoveDirectory(mutation, context, options);
2808
+ case "removeFile":
2809
+ return applyRemoveFile(mutation, context, options);
2810
+ case "chmod":
2811
+ return applyChmod(mutation, context, options);
2812
+ case "backup":
2813
+ return applyBackup(mutation, context, options);
2814
+ case "configMerge":
2815
+ return applyConfigMerge(mutation, context, options);
2816
+ case "configPrune":
2817
+ return applyConfigPrune(mutation, context, options);
2818
+ case "configTransform":
2819
+ return applyConfigTransform(mutation, context, options);
2820
+ case "templateWrite":
2821
+ return applyTemplateWrite(mutation, context, options);
2822
+ case "templateMergeToml":
2823
+ return applyTemplateMerge(mutation, context, options, "toml");
2824
+ case "templateMergeJson":
2825
+ return applyTemplateMerge(mutation, context, options, "json");
2826
+ default: {
2827
+ const never = mutation;
2828
+ throw new Error(`Unknown mutation kind: ${never.kind}`);
2829
+ }
2830
+ }
2831
+ }
2832
+ async function applyEnsureDirectory(mutation, context, options) {
2833
+ const rawPath = resolveValue(mutation.path, options);
2834
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
2835
+ const details = {
2836
+ kind: mutation.kind,
2837
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
2838
+ targetPath
2839
+ };
2840
+ const existed = await pathExists(context.fs, targetPath);
2841
+ if (!context.dryRun) {
2842
+ await context.fs.mkdir(targetPath, { recursive: true });
2843
+ }
2844
+ return {
2845
+ outcome: {
2846
+ changed: !existed,
2847
+ effect: "mkdir",
2848
+ detail: existed ? "noop" : "create"
2849
+ },
2850
+ details
2851
+ };
2852
+ }
2853
+ async function applyRemoveDirectory(mutation, context, options) {
2854
+ const rawPath = resolveValue(mutation.path, options);
2855
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
2856
+ const details = {
2857
+ kind: mutation.kind,
2858
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
2859
+ targetPath
2860
+ };
2861
+ const existed = await pathExists(context.fs, targetPath);
2862
+ if (!existed) {
2863
+ return {
2864
+ outcome: { changed: false, effect: "none", detail: "noop" },
2865
+ details
2866
+ };
2867
+ }
2868
+ if (typeof context.fs.rm !== "function") {
2869
+ return {
2870
+ outcome: { changed: false, effect: "none", detail: "noop" },
2871
+ details
2872
+ };
2873
+ }
2874
+ if (mutation.force) {
2875
+ if (!context.dryRun) {
2876
+ await context.fs.rm(targetPath, { recursive: true, force: true });
2877
+ }
2878
+ return {
2879
+ outcome: { changed: true, effect: "delete", detail: "delete" },
2880
+ details
2881
+ };
2882
+ }
2883
+ const entries = await context.fs.readdir(targetPath);
2884
+ if (entries.length > 0) {
2885
+ return {
2886
+ outcome: { changed: false, effect: "none", detail: "noop" },
2887
+ details
2888
+ };
2889
+ }
2890
+ if (!context.dryRun) {
2891
+ await context.fs.rm(targetPath, { recursive: true, force: true });
2892
+ }
2893
+ return {
2894
+ outcome: { changed: true, effect: "delete", detail: "delete" },
2895
+ details
2896
+ };
2897
+ }
2898
+ async function applyRemoveFile(mutation, context, options) {
2899
+ const rawPath = resolveValue(mutation.target, options);
2900
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
2901
+ const details = {
2902
+ kind: mutation.kind,
2903
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
2904
+ targetPath
2905
+ };
2906
+ try {
2907
+ const content = await context.fs.readFile(targetPath, "utf8");
2908
+ const trimmed = content.trim();
2909
+ if (mutation.whenContentMatches && !mutation.whenContentMatches.test(trimmed)) {
2910
+ return {
2911
+ outcome: { changed: false, effect: "none", detail: "noop" },
2912
+ details
2913
+ };
2914
+ }
2915
+ if (mutation.whenEmpty && trimmed.length > 0) {
2916
+ return {
2917
+ outcome: { changed: false, effect: "none", detail: "noop" },
2918
+ details
2919
+ };
2920
+ }
2921
+ if (!context.dryRun) {
2922
+ await context.fs.unlink(targetPath);
2923
+ }
2924
+ return {
2925
+ outcome: { changed: true, effect: "delete", detail: "delete" },
2926
+ details
2927
+ };
2928
+ } catch (error) {
2929
+ if (isNotFound(error)) {
2930
+ return {
2931
+ outcome: { changed: false, effect: "none", detail: "noop" },
2932
+ details
2933
+ };
2934
+ }
2935
+ throw error;
2936
+ }
2937
+ }
2938
+ async function applyChmod(mutation, context, options) {
2939
+ const rawPath = resolveValue(mutation.target, options);
2940
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
2941
+ const details = {
2942
+ kind: mutation.kind,
2943
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
2944
+ targetPath
2945
+ };
2946
+ if (typeof context.fs.chmod !== "function") {
2947
+ return {
2948
+ outcome: { changed: false, effect: "none", detail: "noop" },
2949
+ details
2950
+ };
2951
+ }
2952
+ try {
2953
+ const stat = await context.fs.stat(targetPath);
2954
+ const currentMode = typeof stat.mode === "number" ? stat.mode & 511 : null;
2955
+ if (currentMode === mutation.mode) {
2956
+ return {
2957
+ outcome: { changed: false, effect: "none", detail: "noop" },
2958
+ details
2959
+ };
2960
+ }
2961
+ if (!context.dryRun) {
2962
+ await context.fs.chmod(targetPath, mutation.mode);
2963
+ }
2964
+ return {
2965
+ outcome: { changed: true, effect: "chmod", detail: "update" },
2966
+ details
2967
+ };
2968
+ } catch (error) {
2969
+ if (isNotFound(error)) {
2970
+ return {
2971
+ outcome: { changed: false, effect: "none", detail: "noop" },
2972
+ details
2973
+ };
2974
+ }
2975
+ throw error;
2976
+ }
2977
+ }
2978
+ async function applyBackup(mutation, context, options) {
2979
+ const rawPath = resolveValue(mutation.target, options);
2980
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
2981
+ const details = {
2982
+ kind: mutation.kind,
2983
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
2984
+ targetPath
2985
+ };
2986
+ const content = await readFileIfExists(context.fs, targetPath);
2987
+ if (content === null) {
2988
+ return {
2989
+ outcome: { changed: false, effect: "none", detail: "noop" },
2990
+ details
2991
+ };
2992
+ }
2993
+ if (!context.dryRun) {
2994
+ const backupPath = `${targetPath}.backup-${createTimestamp()}`;
2995
+ await context.fs.writeFile(backupPath, content, { encoding: "utf8" });
2996
+ }
2997
+ return {
2998
+ outcome: { changed: true, effect: "copy", detail: "backup" },
2999
+ details
3000
+ };
3001
+ }
3002
+ async function applyConfigMerge(mutation, context, options) {
3003
+ const rawPath = resolveValue(mutation.target, options);
3004
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
3005
+ const details = {
3006
+ kind: mutation.kind,
3007
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
3008
+ targetPath
3009
+ };
3010
+ const formatName = mutation.format ?? detectFormat(rawPath);
3011
+ if (!formatName) {
3012
+ throw new Error(
3013
+ `Cannot detect config format for "${rawPath}". Provide explicit format option.`
3014
+ );
3015
+ }
3016
+ const format = getConfigFormat(formatName);
3017
+ const rawContent = await readFileIfExists(context.fs, targetPath);
3018
+ let current;
3019
+ try {
3020
+ current = rawContent === null ? {} : format.parse(rawContent);
3021
+ } catch {
3022
+ if (rawContent !== null) {
3023
+ await backupInvalidDocument(context.fs, targetPath, rawContent);
3024
+ }
3025
+ current = {};
3026
+ }
3027
+ const value = resolveValue(mutation.value, options);
3028
+ let merged;
3029
+ if (mutation.pruneByPrefix) {
3030
+ merged = mergeWithPruneByPrefix(current, value, mutation.pruneByPrefix);
3031
+ } else {
3032
+ merged = format.merge(current, value);
3033
+ }
3034
+ const serialized = format.serialize(merged);
3035
+ const changed = serialized !== rawContent;
3036
+ if (changed && !context.dryRun) {
3037
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
3038
+ }
3039
+ return {
3040
+ outcome: {
3041
+ changed,
3042
+ effect: changed ? "write" : "none",
3043
+ detail: changed ? rawContent === null ? "create" : "update" : "noop"
3044
+ },
3045
+ details
3046
+ };
3047
+ }
3048
+ async function applyConfigPrune(mutation, context, options) {
3049
+ const rawPath = resolveValue(mutation.target, options);
3050
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
3051
+ const details = {
3052
+ kind: mutation.kind,
3053
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
3054
+ targetPath
3055
+ };
3056
+ const rawContent = await readFileIfExists(context.fs, targetPath);
3057
+ if (rawContent === null) {
3058
+ return {
3059
+ outcome: { changed: false, effect: "none", detail: "noop" },
3060
+ details
3061
+ };
3062
+ }
3063
+ const formatName = mutation.format ?? detectFormat(rawPath);
3064
+ if (!formatName) {
3065
+ throw new Error(
3066
+ `Cannot detect config format for "${rawPath}". Provide explicit format option.`
3067
+ );
3068
+ }
3069
+ const format = getConfigFormat(formatName);
3070
+ let current;
3071
+ try {
3072
+ current = format.parse(rawContent);
3073
+ } catch {
3074
+ return {
3075
+ outcome: { changed: false, effect: "none", detail: "noop" },
3076
+ details
3077
+ };
3078
+ }
3079
+ if (mutation.onlyIf && !mutation.onlyIf(current, options)) {
3080
+ return {
3081
+ outcome: { changed: false, effect: "none", detail: "noop" },
3082
+ details
3083
+ };
3084
+ }
3085
+ const shape = resolveValue(mutation.shape, options);
3086
+ const { changed, result } = format.prune(current, shape);
3087
+ if (!changed) {
3088
+ return {
3089
+ outcome: { changed: false, effect: "none", detail: "noop" },
3090
+ details
3091
+ };
3092
+ }
3093
+ if (Object.keys(result).length === 0) {
3094
+ if (!context.dryRun) {
3095
+ await context.fs.unlink(targetPath);
3096
+ }
3097
+ return {
3098
+ outcome: { changed: true, effect: "delete", detail: "delete" },
3099
+ details
3100
+ };
3101
+ }
3102
+ const serialized = format.serialize(result);
3103
+ if (!context.dryRun) {
3104
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
3105
+ }
3106
+ return {
3107
+ outcome: { changed: true, effect: "write", detail: "update" },
3108
+ details
3109
+ };
3110
+ }
3111
+ async function applyConfigTransform(mutation, context, options) {
3112
+ const rawPath = resolveValue(mutation.target, options);
3113
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
3114
+ const details = {
3115
+ kind: mutation.kind,
3116
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
3117
+ targetPath
3118
+ };
3119
+ const formatName = mutation.format ?? detectFormat(rawPath);
3120
+ if (!formatName) {
3121
+ throw new Error(
3122
+ `Cannot detect config format for "${rawPath}". Provide explicit format option.`
3123
+ );
3124
+ }
3125
+ const format = getConfigFormat(formatName);
3126
+ const rawContent = await readFileIfExists(context.fs, targetPath);
3127
+ let current;
3128
+ try {
3129
+ current = rawContent === null ? {} : format.parse(rawContent);
3130
+ } catch {
3131
+ if (rawContent !== null) {
3132
+ await backupInvalidDocument(context.fs, targetPath, rawContent);
3133
+ }
3134
+ current = {};
3135
+ }
3136
+ const { content: transformed, changed } = mutation.transform(current, options);
3137
+ if (!changed) {
3138
+ return {
3139
+ outcome: { changed: false, effect: "none", detail: "noop" },
3140
+ details
3141
+ };
3142
+ }
3143
+ if (transformed === null) {
3144
+ if (rawContent === null) {
3145
+ return {
3146
+ outcome: { changed: false, effect: "none", detail: "noop" },
3147
+ details
3148
+ };
3149
+ }
3150
+ if (!context.dryRun) {
3151
+ await context.fs.unlink(targetPath);
3152
+ }
3153
+ return {
3154
+ outcome: { changed: true, effect: "delete", detail: "delete" },
3155
+ details
3156
+ };
3157
+ }
3158
+ const serialized = format.serialize(transformed);
3159
+ if (!context.dryRun) {
3160
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
3161
+ }
3162
+ return {
3163
+ outcome: {
3164
+ changed: true,
3165
+ effect: "write",
3166
+ detail: rawContent === null ? "create" : "update"
3167
+ },
3168
+ details
3169
+ };
3170
+ }
3171
+ async function applyTemplateWrite(mutation, context, options) {
3172
+ if (!context.templates) {
3173
+ throw new Error(
3174
+ "Template mutations require a templates loader. Provide templates function to runMutations context."
3175
+ );
3176
+ }
3177
+ const rawPath = resolveValue(mutation.target, options);
3178
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
3179
+ const details = {
3180
+ kind: mutation.kind,
3181
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
3182
+ targetPath
3183
+ };
3184
+ const template = await context.templates(mutation.templateId);
3185
+ const templateContext = mutation.context ? resolveValue(mutation.context, options) : {};
3186
+ const rendered = Mustache.render(template, templateContext);
3187
+ const existed = await pathExists(context.fs, targetPath);
3188
+ if (!context.dryRun) {
3189
+ await context.fs.writeFile(targetPath, rendered, { encoding: "utf8" });
3190
+ }
3191
+ return {
3192
+ outcome: {
3193
+ changed: true,
3194
+ effect: "write",
3195
+ detail: existed ? "update" : "create"
3196
+ },
3197
+ details
3198
+ };
3199
+ }
3200
+ async function applyTemplateMerge(mutation, context, options, formatName) {
3201
+ if (!context.templates) {
3202
+ throw new Error(
3203
+ "Template mutations require a templates loader. Provide templates function to runMutations context."
3204
+ );
3205
+ }
3206
+ const rawPath = resolveValue(mutation.target, options);
3207
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
3208
+ const details = {
3209
+ kind: mutation.kind,
3210
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
3211
+ targetPath
3212
+ };
3213
+ const format = getConfigFormat(formatName);
3214
+ const template = await context.templates(mutation.templateId);
3215
+ const templateContext = mutation.context ? resolveValue(mutation.context, options) : {};
3216
+ const rendered = Mustache.render(template, templateContext);
3217
+ let templateDoc;
3218
+ try {
3219
+ templateDoc = format.parse(rendered);
3220
+ } catch (error) {
3221
+ throw new Error(
3222
+ `Failed to parse rendered template "${mutation.templateId}" as ${formatName.toUpperCase()}: ${error}`,
3223
+ { cause: error }
3224
+ );
3225
+ }
3226
+ const rawContent = await readFileIfExists(context.fs, targetPath);
3227
+ let current;
3228
+ try {
3229
+ current = rawContent === null ? {} : format.parse(rawContent);
3230
+ } catch {
3231
+ if (rawContent !== null) {
3232
+ await backupInvalidDocument(context.fs, targetPath, rawContent);
3233
+ }
3234
+ current = {};
3235
+ }
3236
+ const merged = format.merge(current, templateDoc);
3237
+ const serialized = format.serialize(merged);
3238
+ const changed = serialized !== rawContent;
3239
+ if (changed && !context.dryRun) {
3240
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
3241
+ }
3242
+ return {
3243
+ outcome: {
3244
+ changed,
3245
+ effect: changed ? "write" : "none",
3246
+ detail: changed ? rawContent === null ? "create" : "update" : "noop"
3247
+ },
3248
+ details
3249
+ };
3250
+ }
3251
+
3252
+ // packages/config-mutations/src/execution/run-mutations.ts
3253
+ async function runMutations(mutations, context, options) {
3254
+ const effects = [];
3255
+ let anyChanged = false;
3256
+ const resolverOptions = options ?? {};
3257
+ for (const mutation of mutations) {
3258
+ const { outcome } = await executeMutation(
3259
+ mutation,
3260
+ context,
3261
+ resolverOptions
3262
+ );
3263
+ effects.push(outcome);
3264
+ if (outcome.changed) {
3265
+ anyChanged = true;
3266
+ }
3267
+ }
3268
+ return {
3269
+ changed: anyChanged,
3270
+ effects
3271
+ };
3272
+ }
3273
+ async function executeMutation(mutation, context, options) {
3274
+ context.observers?.onStart?.({
3275
+ kind: mutation.kind,
3276
+ label: mutation.label ?? mutation.kind,
3277
+ targetPath: void 0
3278
+ // Will be resolved during apply
3279
+ });
3280
+ try {
3281
+ const { outcome, details } = await applyMutation(mutation, context, options);
3282
+ context.observers?.onComplete?.(details, outcome);
3283
+ return { outcome, details };
3284
+ } catch (error) {
3285
+ context.observers?.onError?.(
3286
+ {
3287
+ kind: mutation.kind,
3288
+ label: mutation.label ?? mutation.kind,
3289
+ targetPath: void 0
3290
+ },
3291
+ error
3292
+ );
3293
+ throw error;
3294
+ }
3295
+ }
3296
+
3297
+ // packages/config-mutations/src/template/render.ts
3298
+ import Mustache2 from "mustache";
3299
+ var originalEscape = Mustache2.escape;
3300
+
3301
+ // src/services/service-install.ts
3302
+ async function runServiceInstall(definition, context) {
3303
+ const checkContext = {
3304
+ isDryRun: context.isDryRun,
3305
+ runCommand: context.runCommand
3306
+ };
3307
+ let needsInstall = false;
3308
+ try {
3309
+ await definition.check.run(checkContext);
3310
+ context.logger(`${definition.summary} already installed.`);
3311
+ } catch (error) {
3312
+ const detail = error instanceof Error ? error.message : String(error);
3313
+ context.logger(`${definition.summary} not detected: ${detail}`);
3314
+ needsInstall = true;
3315
+ }
3316
+ if (!needsInstall) {
3317
+ return false;
3318
+ }
3319
+ if (context.isDryRun) {
3320
+ logInstallDryRun(definition, context);
3321
+ return true;
3322
+ }
3323
+ const platformSteps = filterStepsByPlatform(definition.steps, context.platform);
3324
+ for (const step of platformSteps) {
3325
+ await runInstallStep(step, context);
3326
+ }
3327
+ await definition.check.run(checkContext);
3328
+ if (definition.postChecks) {
3329
+ for (const postCheck of definition.postChecks) {
3330
+ await postCheck.run(checkContext);
3331
+ }
3332
+ }
3333
+ context.logger(
3334
+ definition.successMessage ?? `${definition.summary} installed.`
3335
+ );
3336
+ return true;
3337
+ }
3338
+ function describeInstallCommand(step) {
3339
+ return `[${step.id}] ${formatCommand(step.command, step.args)}`;
3340
+ }
3341
+ function formatCommand(command, args) {
3342
+ return [command, ...args.map(quoteIfNeeded)].join(" ");
3343
+ }
3344
+ function quoteIfNeeded(value) {
3345
+ if (value.length === 0) {
3346
+ return '""';
3347
+ }
3348
+ if (value.includes(" ") || value.includes(" ") || value.includes("\n")) {
3349
+ return `"${value.replaceAll('"', '\\"')}"`;
3350
+ }
3351
+ return value;
3352
+ }
3353
+ function filterStepsByPlatform(steps, platform) {
3354
+ return steps.filter(
3355
+ (step) => !step.platforms || step.platforms.includes(platform)
3356
+ );
3357
+ }
3358
+ function logInstallDryRun(definition, context) {
3359
+ context.logger(`Dry run: would install ${definition.summary}.`);
3360
+ const platformSteps = filterStepsByPlatform(definition.steps, context.platform);
3361
+ for (const step of platformSteps) {
3362
+ context.logger(`Dry run: ${describeInstallCommand(step)}`);
3363
+ }
3364
+ }
3365
+ async function runInstallStep(step, context) {
3366
+ context.logger(`Running ${describeInstallCommand(step)}`);
3367
+ const result = await context.runCommand(step.command, step.args);
3368
+ if (result.exitCode !== 0) {
3369
+ const stderr = result.stderr.trim();
3370
+ const suffix = stderr.length > 0 ? `: ${stderr}` : "";
3371
+ throw new Error(
3372
+ `${describeInstallCommand(step)} failed with exit code ${result.exitCode}${suffix}`
3373
+ );
3374
+ }
3375
+ }
3376
+
3377
+ // src/providers/create-provider.ts
3378
+ var templateImports = {
3379
+ "python/env.hbs": () => Promise.resolve().then(() => __toESM(require_env(), 1)),
3380
+ "python/main.py.hbs": () => Promise.resolve().then(() => __toESM(require_main_py(), 1)),
3381
+ "python/requirements.txt.hbs": () => Promise.resolve().then(() => __toESM(require_requirements_txt(), 1)),
3382
+ "codex/config.toml.hbs": () => Promise.resolve().then(() => __toESM(require_config_toml(), 1))
3383
+ };
3384
+ async function loadTemplate(templateId) {
3385
+ const loader = templateImports[templateId];
3386
+ if (!loader) {
3387
+ throw new Error(`Template not found: ${templateId}`);
3388
+ }
3389
+ const module = await loader();
3390
+ return module.default;
3391
+ }
3392
+ function createProvider(opts) {
3393
+ const provider2 = {
3394
+ id: opts.id,
3395
+ summary: opts.summary,
3396
+ name: opts.name,
3397
+ aliases: opts.aliases,
3398
+ label: opts.label,
3399
+ branding: opts.branding,
3400
+ disabled: opts.disabled,
3401
+ supportsStdinPrompt: opts.supportsStdinPrompt,
3402
+ configurePrompts: opts.configurePrompts,
3403
+ postConfigureMessages: opts.postConfigureMessages,
3404
+ isolatedEnv: opts.isolatedEnv,
3405
+ async configure(context, runOptions) {
3406
+ await runMutations(opts.manifest.configure, {
3407
+ fs: context.fs,
3408
+ homeDir: context.env.homeDir,
3409
+ observers: runOptions?.observers,
3410
+ templates: loadTemplate,
3411
+ pathMapper: context.pathMapper
3412
+ }, context.options);
3413
+ context.command.flushDryRun({ emitIfEmpty: false });
3414
+ },
3415
+ async unconfigure(context, runOptions) {
3416
+ if (!opts.manifest.unconfigure) {
3417
+ return false;
3418
+ }
3419
+ const result = await runMutations(opts.manifest.unconfigure, {
3420
+ fs: context.fs,
3421
+ homeDir: context.env.homeDir,
3422
+ observers: runOptions?.observers,
3423
+ templates: loadTemplate,
3424
+ pathMapper: context.pathMapper
3425
+ }, context.options);
3426
+ context.command.flushDryRun({ emitIfEmpty: false });
3427
+ return result.changed;
3428
+ }
3429
+ };
3430
+ if (opts.install) {
3431
+ provider2.install = createInstallRunner(opts.install);
3432
+ }
3433
+ if (opts.test) {
3434
+ provider2.test = opts.test;
3435
+ }
3436
+ if (opts.spawn) {
3437
+ provider2.spawn = opts.spawn;
3438
+ }
3439
+ return provider2;
3440
+ }
3441
+ function createInstallRunner(definition) {
3442
+ return async (context) => {
3443
+ await runServiceInstall(definition, {
3444
+ isDryRun: context.logger.context.dryRun,
3445
+ runCommand: context.command.runCommand,
3446
+ logger: (message) => context.logger.verbose(message),
3447
+ platform: context.env.platform
3448
+ });
3449
+ };
3450
+ }
3451
+
3452
+ // src/providers/poe-agent.ts
3453
+ function createEventQueue() {
3454
+ const values = [];
3455
+ const waiters = [];
3456
+ let completed = false;
3457
+ return {
3458
+ push(value) {
3459
+ if (completed) {
3460
+ return;
3461
+ }
3462
+ const waiter = waiters.shift();
3463
+ if (waiter) {
3464
+ waiter({ done: false, value });
3465
+ return;
3466
+ }
3467
+ values.push(value);
3468
+ },
3469
+ complete() {
3470
+ if (completed) {
3471
+ return;
3472
+ }
3473
+ completed = true;
3474
+ while (waiters.length > 0) {
3475
+ const waiter = waiters.shift();
3476
+ waiter?.({ done: true, value: void 0 });
3477
+ }
3478
+ },
3479
+ [Symbol.asyncIterator]() {
3480
+ return {
3481
+ next() {
3482
+ if (values.length > 0) {
3483
+ const value = values.shift();
3484
+ return Promise.resolve({ done: false, value });
3485
+ }
3486
+ if (completed) {
3487
+ return Promise.resolve({ done: true, value: void 0 });
3488
+ }
3489
+ return new Promise((resolve) => {
3490
+ waiters.push(resolve);
3491
+ });
3492
+ }
3493
+ };
3494
+ }
3495
+ };
3496
+ }
3497
+ function createToolRenderState() {
3498
+ return {
3499
+ startedToolCalls: /* @__PURE__ */ new Set(),
3500
+ toolCallKinds: /* @__PURE__ */ new Map(),
3501
+ toolCallTitles: /* @__PURE__ */ new Map()
3502
+ };
3503
+ }
3504
+ function toErrorMessage2(value) {
3505
+ if (value instanceof Error) {
3506
+ return value.message;
3507
+ }
3508
+ if (typeof value === "string") {
3509
+ return value;
3510
+ }
3511
+ return String(value);
3512
+ }
3513
+ function toErrorStack(value) {
3514
+ if (value instanceof Error && typeof value.stack === "string" && value.stack.length > 0) {
3515
+ return value.stack;
3516
+ }
3517
+ return void 0;
3518
+ }
3519
+ function mapLegacyToolKind(kind) {
3520
+ if (kind === "read") {
3521
+ return "read";
3522
+ }
3523
+ if (kind === "execute") {
3524
+ return "execute";
3525
+ }
3526
+ if (kind === "edit" || kind === "delete" || kind === "move") {
3527
+ return "write";
3528
+ }
3529
+ if (kind === "other" || kind === "search" || kind === "think" || kind === "fetch" || kind === "switch_mode") {
3530
+ return "other";
3531
+ }
3532
+ return void 0;
3533
+ }
3534
+ function mapLegacyToolStatus(status) {
3535
+ if (status === "pending" || status === "in_progress" || status === "completed" || status === "failed" || status === "cancelled") {
3536
+ return status;
3537
+ }
3538
+ return void 0;
3539
+ }
3540
+ function normalizeSessionUpdate(update) {
3541
+ if (update.sessionUpdate === "agent_message_chunk" || update.sessionUpdate === "agent_thought_chunk") {
3542
+ return {
3543
+ sessionUpdate: update.sessionUpdate,
3544
+ content: update.content
3545
+ };
3546
+ }
3547
+ if (update.sessionUpdate === "tool_call") {
3548
+ const normalized2 = {
3549
+ sessionUpdate: "tool_call",
3550
+ toolCallId: update.toolCallId,
3551
+ title: update.title
3552
+ };
3553
+ const kind2 = mapLegacyToolKind(update.kind);
3554
+ if (kind2 !== void 0) {
3555
+ normalized2.kind = kind2;
3556
+ }
3557
+ const status2 = mapLegacyToolStatus(update.status);
3558
+ if (status2 !== void 0) {
3559
+ normalized2.status = status2;
3560
+ }
3561
+ if (update.rawInput !== void 0) {
3562
+ normalized2.rawInput = update.rawInput;
3563
+ }
3564
+ if (update._meta !== void 0) {
3565
+ normalized2._meta = update._meta;
3566
+ }
3567
+ return normalized2;
3568
+ }
3569
+ const normalized = {
3570
+ sessionUpdate: "tool_call_update",
3571
+ toolCallId: update.toolCallId
3572
+ };
3573
+ const kind = mapLegacyToolKind(update.kind);
3574
+ if (kind !== void 0) {
3575
+ normalized.kind = kind;
3576
+ }
3577
+ const status = mapLegacyToolStatus(update.status);
3578
+ if (status !== void 0) {
3579
+ normalized.status = status;
3580
+ }
3581
+ if (update.rawOutput !== void 0) {
3582
+ normalized.rawOutput = update.rawOutput;
3583
+ }
3584
+ if (update._meta !== void 0) {
3585
+ normalized._meta = update._meta;
3586
+ }
3587
+ return normalized;
3588
+ }
3589
+ function toRenderKind(kind) {
3590
+ if (kind === "execute") {
3591
+ return "exec";
3592
+ }
3593
+ if (kind === "write") {
3594
+ return "edit";
3595
+ }
3596
+ if (kind === "read") {
3597
+ return "read";
3598
+ }
3599
+ return "other";
3600
+ }
3601
+ function toToolOutput(value) {
3602
+ if (typeof value === "string") {
3603
+ return value;
3604
+ }
3605
+ if (value === void 0) {
3606
+ return "";
3607
+ }
3608
+ try {
3609
+ const serialized = JSON.stringify(value);
3610
+ if (typeof serialized === "string") {
3611
+ return serialized;
3612
+ }
3613
+ } catch {
3614
+ }
3615
+ return String(value);
3616
+ }
3617
+ function toPromptText(prompt) {
3618
+ const lines = [];
3619
+ for (const block of prompt) {
3620
+ if (block.type === "text") {
3621
+ lines.push(block.text);
3622
+ continue;
3623
+ }
3624
+ if (block.type === "resource_link") {
3625
+ lines.push(`${block.name}: ${block.uri}`);
3626
+ continue;
3627
+ }
3628
+ if (block.type === "resource") {
3629
+ if ("text" in block.resource) {
3630
+ lines.push(block.resource.text);
3631
+ }
3632
+ continue;
3633
+ }
3634
+ }
3635
+ return lines.join("\n");
3636
+ }
3637
+ function toEventsFromSessionUpdate(notification, state) {
3638
+ const update = notification.params.update;
3639
+ if (update.sessionUpdate === "agent_message_chunk" && update.content.type === "text") {
3640
+ return [{ event: "agent_message", text: update.content.text }];
3641
+ }
3642
+ if (update.sessionUpdate === "agent_thought_chunk" && update.content.type === "text") {
3643
+ return [{ event: "reasoning", text: update.content.text }];
3644
+ }
3645
+ if (update.sessionUpdate === "usage_update") {
3646
+ const cachedTokens = Math.max(0, update.size - update.used);
3647
+ const usage = {
3648
+ event: "usage",
3649
+ inputTokens: update.used,
3650
+ outputTokens: 0
3651
+ };
3652
+ if (cachedTokens > 0) {
3653
+ usage.cachedTokens = cachedTokens;
3654
+ }
3655
+ if (update.cost && update.cost.currency === "USD") {
3656
+ usage.costUsd = update.cost.amount;
3657
+ }
3658
+ return [usage];
3659
+ }
3660
+ if (update.sessionUpdate === "tool_call") {
3661
+ const renderKind = toRenderKind(update.kind);
3662
+ state.toolCallKinds.set(update.toolCallId, renderKind);
3663
+ state.toolCallTitles.set(update.toolCallId, update.title);
3664
+ if (state.startedToolCalls.has(update.toolCallId)) {
3665
+ return [];
3666
+ }
3667
+ state.startedToolCalls.add(update.toolCallId);
3668
+ return [{
3669
+ event: "tool_start",
3670
+ kind: renderKind,
3671
+ title: update.title,
3672
+ id: update.toolCallId
3673
+ }];
3674
+ }
3675
+ if (update.sessionUpdate === "tool_call_update") {
3676
+ const renderKind = toRenderKind(update.kind ?? void 0) || state.toolCallKinds.get(update.toolCallId) || "other";
3677
+ state.toolCallKinds.set(update.toolCallId, renderKind);
3678
+ const events = [];
3679
+ const toolTitle = state.toolCallTitles.get(update.toolCallId) ?? update.toolCallId;
3680
+ const status = update.status;
3681
+ const shouldStart = !state.startedToolCalls.has(update.toolCallId) && (status === "pending" || status === "in_progress");
3682
+ if (shouldStart) {
3683
+ state.startedToolCalls.add(update.toolCallId);
3684
+ events.push({
3685
+ event: "tool_start",
3686
+ kind: renderKind,
3687
+ title: toolTitle,
3688
+ id: update.toolCallId
3689
+ });
3690
+ }
3691
+ if (status === "completed" || status === "failed" || status === "cancelled") {
3692
+ if (!state.startedToolCalls.has(update.toolCallId)) {
3693
+ state.startedToolCalls.add(update.toolCallId);
3694
+ events.push({
3695
+ event: "tool_start",
3696
+ kind: renderKind,
3697
+ title: toolTitle,
3698
+ id: update.toolCallId
3699
+ });
3700
+ }
3701
+ events.push({
3702
+ event: "tool_complete",
3703
+ kind: renderKind,
3704
+ path: toToolOutput(update.rawOutput),
3705
+ id: update.toolCallId
3706
+ });
3707
+ }
3708
+ return events;
3709
+ }
3710
+ return [];
3711
+ }
3712
+ function emitEvent(callback, event) {
3713
+ if (!callback) {
3714
+ return;
3715
+ }
3716
+ callback(event);
3717
+ }
3718
+ function createInMemoryAcpTransport(options) {
3719
+ const sessions = /* @__PURE__ */ new Map();
3720
+ const notificationHandlers = /* @__PURE__ */ new Map();
3721
+ const requestHandlers = /* @__PURE__ */ new Map();
3722
+ let sessionCounter = 0;
3723
+ let closed = false;
3724
+ let resolveClosed;
3725
+ const closedPromise = new Promise((resolve) => {
3726
+ resolveClosed = resolve;
3727
+ });
3728
+ const closeTransport = (reason) => {
3729
+ if (closed) {
3730
+ return;
3731
+ }
3732
+ closed = true;
3733
+ const entries = Array.from(sessions.values());
3734
+ sessions.clear();
3735
+ void Promise.all(entries.map(async (session) => {
3736
+ await session.dispose();
3737
+ })).finally(() => {
3738
+ resolveClosed?.({
3739
+ code: 0,
3740
+ signal: null,
3741
+ reason,
3742
+ stderr: ""
3743
+ });
3744
+ });
3745
+ };
3746
+ return {
3747
+ closed: closedPromise,
3748
+ async sendRequest(method, params) {
3749
+ if (method === "initialize") {
3750
+ const request = params;
3751
+ const response = {
3752
+ protocolVersion: request.protocolVersion ?? 1,
3753
+ agentInfo: { name: "poe-agent", version: "0.0.1" },
3754
+ agentCapabilities: {
3755
+ sessionCapabilities: {},
3756
+ promptCapabilities: {}
3757
+ }
3758
+ };
3759
+ return response;
3760
+ }
3761
+ if (method === "session/new") {
3762
+ const request = params;
3763
+ const { createAgentSession: createAgentSession2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
3764
+ const session = await createAgentSession2({
3765
+ model: options.model,
3766
+ cwd: request.cwd || options.cwd
3767
+ });
3768
+ const sessionId = `poe-agent-session-${sessionCounter + 1}`;
3769
+ sessionCounter += 1;
3770
+ sessions.set(sessionId, session);
3771
+ const response = { sessionId };
3772
+ return response;
3773
+ }
3774
+ if (method === "session/prompt") {
3775
+ const request = params;
3776
+ const session = sessions.get(request.sessionId);
3777
+ if (!session) {
3778
+ throw new Error(`Unknown session "${request.sessionId}".`);
3779
+ }
3780
+ const promptText = toPromptText(request.prompt);
3781
+ await session.sendMessage(promptText, {
3782
+ onSessionUpdate: (legacyUpdate) => {
3783
+ const normalizedUpdate = normalizeSessionUpdate(legacyUpdate);
3784
+ const handlers2 = notificationHandlers.get("session/update");
3785
+ if (!handlers2 || handlers2.length === 0) {
3786
+ return;
3787
+ }
3788
+ const notification = {
3789
+ sessionId: request.sessionId,
3790
+ update: normalizedUpdate
3791
+ };
3792
+ for (const handler of handlers2) {
3793
+ void handler(notification, { method: "session/update" });
3794
+ }
3795
+ }
3796
+ });
3797
+ const response = { stopReason: "completed" };
3798
+ return response;
3799
+ }
3800
+ const handlers = requestHandlers.get(method);
3801
+ if (handlers && handlers.length > 0) {
3802
+ const result = handlers[0](params, { id: null, method });
3803
+ return await Promise.resolve(result);
3804
+ }
3805
+ throw new Error(`Unsupported ACP request method "${method}".`);
3806
+ },
3807
+ sendNotification(method, params) {
3808
+ if (method === "session/cancel") {
3809
+ const sessionId = params?.sessionId;
3810
+ if (sessionId && sessions.has(sessionId)) {
3811
+ const session = sessions.get(sessionId);
3812
+ sessions.delete(sessionId);
3813
+ void session?.dispose();
3814
+ }
3815
+ }
3816
+ },
3817
+ onRequest(method, handler) {
3818
+ const current = requestHandlers.get(method) ?? [];
3819
+ requestHandlers.set(method, [...current, handler]);
3820
+ },
3821
+ onNotification(method, handler) {
3822
+ const current = notificationHandlers.get(method) ?? [];
3823
+ notificationHandlers.set(method, [...current, handler]);
3824
+ },
3825
+ dispose(reason) {
3826
+ closeTransport(reason ?? new Error("ACP in-memory transport disposed"));
3827
+ }
3828
+ };
3829
+ }
3830
+ async function runPoeAgentAcpLifecycle(options) {
3831
+ const sessionUpdates = [];
3832
+ const toolState = createToolRenderState();
3833
+ let sessionId = "";
3834
+ let assistantText = "";
3835
+ const transport = createInMemoryAcpTransport({
3836
+ model: options.model,
3837
+ cwd: options.cwd
3838
+ });
3839
+ const client = new AcpClient({ transport });
3840
+ try {
3841
+ await client.initialize();
3842
+ const session = await client.newSession(options.cwd, []);
3843
+ sessionId = session.sessionId;
3844
+ emitEvent(options.onEvent, {
3845
+ event: "session_start",
3846
+ threadId: sessionId
3847
+ });
3848
+ const turn = client.prompt(sessionId, [{ type: "text", text: options.prompt }]);
3849
+ for await (const notification of turn) {
3850
+ sessionUpdates.push(notification);
3851
+ const update = notification.params.update;
3852
+ if (update.sessionUpdate === "agent_message_chunk" && update.content.type === "text") {
3853
+ assistantText += update.content.text;
3854
+ }
3855
+ for (const event of toEventsFromSessionUpdate(notification, toolState)) {
3856
+ emitEvent(options.onEvent, event);
3857
+ }
3858
+ }
3859
+ const promptResponse = await turn.response;
3860
+ await generateRunReportFromSessionUpdateStream(sessionUpdates, {
3861
+ runId: sessionId,
3862
+ exitStatus: promptResponse.stopReason === "completed" ? "success" : "failed"
3863
+ });
3864
+ return {
3865
+ stdout: assistantText.length > 0 ? `${assistantText}
3866
+ ` : "",
3867
+ stderr: "",
3868
+ exitCode: promptResponse.stopReason === "completed" ? 0 : 1,
3869
+ threadId: sessionId,
3870
+ sessionId
3871
+ };
3872
+ } catch (error) {
3873
+ emitEvent(options.onEvent, {
3874
+ event: "error",
3875
+ message: toErrorMessage2(error),
3876
+ ...toErrorStack(error) ? { stack: toErrorStack(error) } : {}
3877
+ });
3878
+ throw error;
3879
+ } finally {
3880
+ await client.dispose();
3881
+ }
3882
+ }
3883
+ function spawnPoeAgentWithAcp(options) {
3884
+ const queue = createEventQueue();
3885
+ const model = options.model ?? DEFAULT_FRONTIER_MODEL;
3886
+ const cwd = options.cwd ?? process.cwd();
3887
+ const done = runPoeAgentAcpLifecycle({
3888
+ prompt: options.prompt,
3889
+ model,
3890
+ cwd,
3891
+ onEvent: (event) => {
3892
+ queue.push(event);
3893
+ }
3894
+ }).finally(() => {
3895
+ queue.complete();
3896
+ });
3897
+ return {
3898
+ events: queue,
3899
+ done
3900
+ };
3901
+ }
3902
+ var poeAgentService = createProvider({
3903
+ id: "poe-agent",
3904
+ name: "poe-agent",
3905
+ label: "Poe Agent",
3906
+ summary: "Run one-shot prompts with the built-in Poe agent runtime.",
3907
+ supportsStdinPrompt: true,
3908
+ manifest: {
3909
+ configure: []
3910
+ },
3911
+ async spawn(context, options) {
3912
+ const result = await runPoeAgentAcpLifecycle({
3913
+ prompt: options.prompt,
3914
+ model: options.model ?? DEFAULT_FRONTIER_MODEL,
3915
+ cwd: options.cwd ?? context.env.cwd
3916
+ });
3917
+ return {
3918
+ stdout: result.stdout,
3919
+ stderr: result.stderr,
3920
+ exitCode: result.exitCode
3921
+ };
3922
+ }
3923
+ });
3924
+ var provider = poeAgentService;
3925
+ export {
3926
+ poeAgentService,
3927
+ provider,
3928
+ spawnPoeAgentWithAcp
3929
+ };
3930
+ //# sourceMappingURL=poe-agent.js.map