@cremini/skillpack 1.2.9 → 1.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +598 -710
  2. package/package.json +1 -2
package/dist/cli.js CHANGED
@@ -9,6 +9,26 @@ var __export = (target, all) => {
9
9
  __defProp(target, name, { get: all[name], enumerable: true });
10
10
  };
11
11
 
12
+ // src/job-schedule.ts
13
+ function normalizeJobCron(cron2) {
14
+ if (typeof cron2 !== "string") {
15
+ return void 0;
16
+ }
17
+ const normalized = cron2.trim();
18
+ return normalized ? normalized : void 0;
19
+ }
20
+ function hasJobSchedule(jobOrCron) {
21
+ if (typeof jobOrCron === "string" || jobOrCron == null) {
22
+ return !!normalizeJobCron(jobOrCron);
23
+ }
24
+ return !!normalizeJobCron(jobOrCron.cron);
25
+ }
26
+ var init_job_schedule = __esm({
27
+ "src/job-schedule.ts"() {
28
+ "use strict";
29
+ }
30
+ });
31
+
12
32
  // src/job-config.ts
13
33
  import fs2 from "fs";
14
34
  import path2 from "path";
@@ -27,9 +47,9 @@ function validateScheduledJobConfig(value, sourceLabel, index) {
27
47
  `Invalid job config from ${sourceLabel}: "jobs[${index}].name" is required`
28
48
  );
29
49
  }
30
- if (typeof job.cron !== "string" || !job.cron.trim()) {
50
+ if (job.cron !== void 0 && typeof job.cron !== "string") {
31
51
  throw new Error(
32
- `Invalid job config from ${sourceLabel}: "jobs[${index}].cron" is required`
52
+ `Invalid job config from ${sourceLabel}: "jobs[${index}].cron" must be a string`
33
53
  );
34
54
  }
35
55
  if (typeof job.prompt !== "string" || !job.prompt.trim()) {
@@ -87,18 +107,25 @@ function validateJobFileShape(value, sourceLabel) {
87
107
  function normalizeJobFile(jobFile) {
88
108
  return {
89
109
  jobs: jobFile.jobs.map((job) => ({
90
- name: job.name.trim(),
91
- cron: job.cron.trim(),
92
- prompt: job.prompt,
93
- notify: {
94
- adapter: job.notify.adapter.trim(),
95
- channelId: job.notify.channelId.trim()
96
- },
97
- ...job.enabled !== void 0 ? { enabled: job.enabled } : {},
98
- ...job.timezone !== void 0 ? { timezone: job.timezone.trim() } : {}
110
+ ...normalizeScheduledJobConfig(job)
99
111
  }))
100
112
  };
101
113
  }
114
+ function normalizeScheduledJobConfig(job) {
115
+ const normalizedCron = normalizeJobCron(job.cron);
116
+ const normalizedTimezone = typeof job.timezone === "string" && job.timezone.trim() ? job.timezone.trim() : void 0;
117
+ return {
118
+ name: job.name.trim(),
119
+ ...normalizedCron ? { cron: normalizedCron } : {},
120
+ prompt: job.prompt,
121
+ notify: {
122
+ adapter: job.notify.adapter.trim(),
123
+ channelId: job.notify.channelId.trim()
124
+ },
125
+ ...normalizedCron && job.enabled !== void 0 ? { enabled: job.enabled } : {},
126
+ ...normalizedCron && normalizedTimezone ? { timezone: normalizedTimezone } : {}
127
+ };
128
+ }
102
129
  function loadJobFile(workDir) {
103
130
  const filePath = getJobFilePath(workDir);
104
131
  if (!fs2.existsSync(filePath)) {
@@ -119,6 +146,7 @@ var JOB_FILE;
119
146
  var init_job_config = __esm({
120
147
  "src/job-config.ts"() {
121
148
  "use strict";
149
+ init_job_schedule();
122
150
  JOB_FILE = "job.json";
123
151
  }
124
152
  });
@@ -389,7 +417,7 @@ var telegram_exports = {};
389
417
  __export(telegram_exports, {
390
418
  TelegramAdapter: () => TelegramAdapter
391
419
  });
392
- import fs17 from "fs";
420
+ import fs14 from "fs";
393
421
  import TelegramBot from "node-telegram-bot-api";
394
422
  var MAX_MESSAGE_LENGTH, ACK_REACTION, TelegramAdapter;
395
423
  var init_telegram = __esm({
@@ -707,7 +735,7 @@ var init_telegram = __esm({
707
735
  async sendFileSafe(chatId, filePath, caption) {
708
736
  if (!this.bot) return;
709
737
  try {
710
- if (!fs17.existsSync(filePath)) {
738
+ if (!fs14.existsSync(filePath)) {
711
739
  console.error(`[Telegram] File not found for sending: ${filePath}`);
712
740
  return;
713
741
  }
@@ -727,8 +755,8 @@ var slack_exports = {};
727
755
  __export(slack_exports, {
728
756
  SlackAdapter: () => SlackAdapter
729
757
  });
730
- import fs18 from "fs";
731
- import path17 from "path";
758
+ import fs15 from "fs";
759
+ import path15 from "path";
732
760
  import { App, LogLevel } from "@slack/bolt";
733
761
  var INLINE_COMMANDS, SLASH_COMMANDS, MAX_MESSAGE_LENGTH2, ACK_REACTION2, PROCESSING_MESSAGE, SlackAdapter;
734
762
  var init_slack = __esm({
@@ -1363,12 +1391,12 @@ var init_slack = __esm({
1363
1391
  */
1364
1392
  async sendFileSafe(client, route, filePath, caption) {
1365
1393
  try {
1366
- if (!fs18.existsSync(filePath)) {
1394
+ if (!fs15.existsSync(filePath)) {
1367
1395
  console.error(`[Slack] File not found for sending: ${filePath}`);
1368
1396
  return;
1369
1397
  }
1370
- const filename = path17.basename(filePath);
1371
- const fileContent = fs18.readFileSync(filePath);
1398
+ const filename = path15.basename(filePath);
1399
+ const fileContent = fs15.readFileSync(filePath);
1372
1400
  await client.files.uploadV2({
1373
1401
  channel_id: route.channel,
1374
1402
  thread_ts: route.threadTs,
@@ -1402,31 +1430,43 @@ function isValidTimezone(tz) {
1402
1430
  function isValidJobName(name) {
1403
1431
  return VALID_JOB_NAME.test(name) && name.length <= 64;
1404
1432
  }
1433
+ function isRecurringJob(jobConfig) {
1434
+ return hasJobSchedule(jobConfig);
1435
+ }
1405
1436
  var VALID_JOB_NAME, SchedulerAdapter;
1406
1437
  var init_scheduler = __esm({
1407
1438
  "src/runtime/adapters/scheduler.ts"() {
1408
1439
  "use strict";
1409
1440
  init_job_config();
1441
+ init_job_schedule();
1410
1442
  VALID_JOB_NAME = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/;
1411
1443
  SchedulerAdapter = class {
1412
1444
  name = "scheduler";
1413
1445
  agent;
1414
1446
  rootDir = "";
1447
+ ipcBroadcaster = null;
1415
1448
  notifyFn = async () => {
1416
1449
  };
1417
1450
  jobs = /* @__PURE__ */ new Map();
1418
1451
  async start(ctx) {
1419
1452
  this.agent = ctx.agent;
1420
1453
  this.rootDir = ctx.rootDir;
1454
+ this.ipcBroadcaster = ctx.ipcBroadcaster ?? null;
1421
1455
  this.notifyFn = ctx.notify || (async () => {
1422
1456
  });
1457
+ console.log(
1458
+ `[Scheduler] IPC broadcaster ${this.ipcBroadcaster ? "attached" : "not available"} for agent events`
1459
+ );
1423
1460
  const jobConfigs = loadJobFile(this.rootDir).jobs;
1424
1461
  let scheduledCount = 0;
1425
1462
  let disabledCount = 0;
1463
+ let oneTimeCount = 0;
1426
1464
  for (const jc of jobConfigs) {
1427
1465
  const result = this.registerJob(jc);
1428
1466
  if (result.registered) {
1429
- if (jc.enabled === false) {
1467
+ if (!isRecurringJob(jc)) {
1468
+ oneTimeCount++;
1469
+ } else if (jc.enabled === false) {
1430
1470
  disabledCount++;
1431
1471
  } else {
1432
1472
  scheduledCount++;
@@ -1436,6 +1476,7 @@ var init_scheduler = __esm({
1436
1476
  const parts = [];
1437
1477
  if (scheduledCount > 0) parts.push(`${scheduledCount} active`);
1438
1478
  if (disabledCount > 0) parts.push(`${disabledCount} disabled`);
1479
+ if (oneTimeCount > 0) parts.push(`${oneTimeCount} one-time`);
1439
1480
  if (parts.length > 0) {
1440
1481
  console.log(`[SchedulerAdapter] Started with ${parts.join(", ")} job(s)`);
1441
1482
  } else {
@@ -1450,43 +1491,43 @@ var init_scheduler = __esm({
1450
1491
  * Does NOT persist – callers decide when to persist.
1451
1492
  */
1452
1493
  registerJob(jobConfig) {
1453
- if (!isValidJobName(jobConfig.name)) {
1454
- const msg = `[Scheduler] Invalid job name "${jobConfig.name}": must match ${VALID_JOB_NAME} and be \u226464 chars`;
1455
- console.error(msg);
1456
- return { registered: false, message: msg };
1457
- }
1458
- if (!cron.validate(jobConfig.cron)) {
1459
- const msg = `[Scheduler] Invalid cron expression for job "${jobConfig.name}": ${jobConfig.cron}`;
1494
+ const normalizedConfig = normalizeScheduledJobConfig(jobConfig);
1495
+ const normalizedCron = normalizeJobCron(normalizedConfig.cron);
1496
+ if (!isValidJobName(normalizedConfig.name)) {
1497
+ const msg = `[Scheduler] Invalid job name "${normalizedConfig.name}": must match ${VALID_JOB_NAME} and be \u226464 chars`;
1460
1498
  console.error(msg);
1461
1499
  return { registered: false, message: msg };
1462
1500
  }
1463
- if (jobConfig.timezone && !isValidTimezone(jobConfig.timezone)) {
1464
- const msg = `[Scheduler] Invalid timezone for job "${jobConfig.name}": ${jobConfig.timezone}`;
1465
- console.error(msg);
1466
- return { registered: false, message: msg };
1501
+ if (normalizedCron) {
1502
+ if (!cron.validate(normalizedCron)) {
1503
+ const msg = `[Scheduler] Invalid cron expression for job "${normalizedConfig.name}": ${normalizedCron}`;
1504
+ console.error(msg);
1505
+ return { registered: false, message: msg };
1506
+ }
1507
+ if (normalizedConfig.timezone && !isValidTimezone(normalizedConfig.timezone)) {
1508
+ const msg = `[Scheduler] Invalid timezone for job "${normalizedConfig.name}": ${normalizedConfig.timezone}`;
1509
+ console.error(msg);
1510
+ return { registered: false, message: msg };
1511
+ }
1467
1512
  }
1468
- this.removeFromMap(jobConfig.name);
1513
+ this.removeFromMap(normalizedConfig.name);
1469
1514
  let task = null;
1470
- if (jobConfig.enabled !== false) {
1471
- task = cron.schedule(
1472
- jobConfig.cron,
1473
- () => {
1474
- void this.runJob(jobConfig);
1475
- },
1476
- {
1477
- timezone: jobConfig.timezone
1478
- }
1515
+ if (normalizedCron && normalizedConfig.enabled !== false) {
1516
+ task = this.createCronTask(normalizedConfig);
1517
+ console.log(
1518
+ `[Scheduler] Job "${normalizedConfig.name}" scheduled: ${normalizedCron}${normalizedConfig.timezone ? ` (${normalizedConfig.timezone})` : ""}`
1479
1519
  );
1520
+ } else if (normalizedCron) {
1480
1521
  console.log(
1481
- `[Scheduler] Job "${jobConfig.name}" scheduled: ${jobConfig.cron}${jobConfig.timezone ? ` (${jobConfig.timezone})` : ""}`
1522
+ `[Scheduler] Job "${normalizedConfig.name}" registered (disabled)`
1482
1523
  );
1483
1524
  } else {
1484
1525
  console.log(
1485
- `[Scheduler] Job "${jobConfig.name}" registered (disabled)`
1526
+ `[Scheduler] Job "${normalizedConfig.name}" registered as one-time (manual trigger only)`
1486
1527
  );
1487
1528
  }
1488
- this.jobs.set(jobConfig.name, {
1489
- config: jobConfig,
1529
+ this.jobs.set(normalizedConfig.name, {
1530
+ config: normalizedConfig,
1490
1531
  task,
1491
1532
  running: false,
1492
1533
  notifyFailed: false
@@ -1513,8 +1554,27 @@ var init_scheduler = __esm({
1513
1554
  console.log(`[Scheduler] Running job "${jobConfig.name}"`);
1514
1555
  let fullText = "";
1515
1556
  let agentFailed = false;
1557
+ let loggedFirstTextDelta = false;
1558
+ let sawAgentStart = false;
1559
+ let sawAgentEnd = false;
1516
1560
  const pendingFiles = [];
1517
1561
  const onEvent = (event) => {
1562
+ if (event.type === "agent_start") {
1563
+ sawAgentStart = true;
1564
+ } else if (event.type === "agent_end") {
1565
+ sawAgentEnd = true;
1566
+ }
1567
+ if (event.type === "agent_start" || event.type === "agent_end") {
1568
+ console.log(
1569
+ `[Scheduler] Forwarding ${event.type} for ${channelId} (ipc=${this.ipcBroadcaster ? "yes" : "no"})`
1570
+ );
1571
+ } else if (event.type === "text_delta" && !loggedFirstTextDelta) {
1572
+ loggedFirstTextDelta = true;
1573
+ console.log(
1574
+ `[Scheduler] Forwarding first text_delta for ${channelId} (${event.delta.length} chars, ipc=${this.ipcBroadcaster ? "yes" : "no"})`
1575
+ );
1576
+ }
1577
+ this.ipcBroadcaster?.broadcastAgentEvent(channelId, event);
1518
1578
  if (event.type === "text_delta") fullText += event.delta;
1519
1579
  if (event.type === "file_output") {
1520
1580
  pendingFiles.push({
@@ -1532,6 +1592,7 @@ var init_scheduler = __esm({
1532
1592
  onEvent,
1533
1593
  void 0
1534
1594
  );
1595
+ this.ensureTerminalAgentEvent(channelId, sawAgentStart, sawAgentEnd, onEvent);
1535
1596
  if (result.errorMessage) {
1536
1597
  fullText = `\u274C \u5B9A\u65F6\u4EFB\u52A1 "${jobConfig.name}" \u6267\u884C\u5931\u8D25\uFF1A${result.errorMessage}`;
1537
1598
  agentFailed = true;
@@ -1540,6 +1601,7 @@ var init_scheduler = __esm({
1540
1601
  if (job) job.lastError = void 0;
1541
1602
  }
1542
1603
  } catch (err) {
1604
+ this.ensureTerminalAgentEvent(channelId, sawAgentStart, sawAgentEnd, onEvent);
1543
1605
  const errorMsg = err instanceof Error ? err.message : String(err);
1544
1606
  fullText = `\u274C \u5B9A\u65F6\u4EFB\u52A1 "${jobConfig.name}" \u5F02\u5E38\uFF1A${errorMsg}`;
1545
1607
  agentFailed = true;
@@ -1576,10 +1638,19 @@ var init_scheduler = __esm({
1576
1638
  return { text: fullText, notifyFailed };
1577
1639
  }
1578
1640
  async clearJobContext(channelId) {
1641
+ console.log(`[Scheduler] Clearing context for ${channelId}`);
1579
1642
  const result = await this.agent.handleCommand("clear", channelId);
1580
1643
  if (!result.success) {
1581
1644
  throw new Error(result.message || `Failed to clear context for ${channelId}`);
1582
1645
  }
1646
+ console.log(`[Scheduler] Context cleared for ${channelId}`);
1647
+ }
1648
+ ensureTerminalAgentEvent(channelId, sawAgentStart, sawAgentEnd, onEvent) {
1649
+ if (!sawAgentStart || sawAgentEnd) {
1650
+ return;
1651
+ }
1652
+ console.log(`[Scheduler] Synthesizing agent_end for ${channelId}`);
1653
+ onEvent({ type: "agent_end" });
1583
1654
  }
1584
1655
  // -------------------------------------------------------------------------
1585
1656
  // Dynamic management API
@@ -1599,10 +1670,11 @@ var init_scheduler = __esm({
1599
1670
  return { success: false, message: result.message };
1600
1671
  }
1601
1672
  this.persistJobs();
1602
- const enabled = jobConfig.enabled !== false;
1673
+ const recurring = isRecurringJob(jobConfig);
1674
+ const enabled = recurring ? jobConfig.enabled !== false : true;
1603
1675
  return {
1604
1676
  success: true,
1605
- message: enabled ? `Job "${jobConfig.name}" created and scheduled.` : `Job "${jobConfig.name}" created (disabled).`
1677
+ message: recurring ? enabled ? `Job "${jobConfig.name}" created and scheduled.` : `Job "${jobConfig.name}" created (disabled).` : `Job "${jobConfig.name}" created as a one-time task.`
1606
1678
  };
1607
1679
  }
1608
1680
  /**
@@ -1647,17 +1719,15 @@ var init_scheduler = __esm({
1647
1719
  if (!job) {
1648
1720
  return { success: false, message: `Job "${name}" not found.` };
1649
1721
  }
1722
+ if (!isRecurringJob(job.config)) {
1723
+ return {
1724
+ success: false,
1725
+ message: `Job "${name}" does not have a schedule and cannot be enabled or disabled.`
1726
+ };
1727
+ }
1650
1728
  job.config.enabled = enabled;
1651
1729
  if (enabled && !job.task) {
1652
- job.task = cron.schedule(
1653
- job.config.cron,
1654
- () => {
1655
- void this.runJob(job.config);
1656
- },
1657
- {
1658
- timezone: job.config.timezone
1659
- }
1660
- );
1730
+ job.task = this.createCronTask(job.config);
1661
1731
  } else if (enabled && job.task) {
1662
1732
  job.task.start();
1663
1733
  } else if (!enabled && job.task) {
@@ -1703,13 +1773,13 @@ var init_scheduler = __esm({
1703
1773
  for (const [, job] of this.jobs) {
1704
1774
  result.push({
1705
1775
  name: job.config.name,
1706
- cron: job.config.cron,
1776
+ ...job.config.cron ? { cron: job.config.cron } : {},
1707
1777
  prompt: job.config.prompt,
1708
1778
  notify: job.config.notify,
1709
- enabled: job.config.enabled !== false,
1710
- timezone: job.config.timezone,
1711
- lastRunAt: job.lastRunAt,
1712
- lastError: job.lastError,
1779
+ enabled: isRecurringJob(job.config) ? job.config.enabled !== false : true,
1780
+ ...job.config.timezone ? { timezone: job.config.timezone } : {},
1781
+ ...job.lastRunAt ? { lastRunAt: job.lastRunAt } : {},
1782
+ ...job.lastError ? { lastError: job.lastError } : {},
1713
1783
  running: job.running,
1714
1784
  notifyFailed: job.notifyFailed
1715
1785
  });
@@ -1749,6 +1819,21 @@ var init_scheduler = __esm({
1749
1819
  this.jobs.clear();
1750
1820
  console.log("[SchedulerAdapter] All jobs stopped.");
1751
1821
  }
1822
+ createCronTask(jobConfig) {
1823
+ const cronExpr = normalizeJobCron(jobConfig.cron);
1824
+ if (!cronExpr) {
1825
+ throw new Error(`Job "${jobConfig.name}" does not have a valid cron expression`);
1826
+ }
1827
+ return cron.schedule(
1828
+ cronExpr,
1829
+ () => {
1830
+ void this.runJob(jobConfig);
1831
+ },
1832
+ {
1833
+ timezone: jobConfig.timezone
1834
+ }
1835
+ );
1836
+ }
1752
1837
  };
1753
1838
  }
1754
1839
  });
@@ -2430,22 +2515,22 @@ async function interactiveCreate(workDir) {
2430
2515
  }
2431
2516
 
2432
2517
  // src/commands/run.ts
2433
- import path19 from "path";
2434
- import fs20 from "fs";
2518
+ import path17 from "path";
2519
+ import fs17 from "fs";
2435
2520
  import inquirer2 from "inquirer";
2436
2521
  import chalk4 from "chalk";
2437
2522
 
2438
2523
  // src/runtime/server.ts
2439
2524
  import express from "express";
2440
- import path18 from "path";
2441
- import fs19 from "fs";
2525
+ import path16 from "path";
2526
+ import fs16 from "fs";
2442
2527
  import { fileURLToPath as fileURLToPath2 } from "url";
2443
2528
  import { createServer } from "http";
2444
2529
  import { exec } from "child_process";
2445
2530
 
2446
2531
  // src/runtime/agent.ts
2447
- import path13 from "path";
2448
- import fs13 from "fs";
2532
+ import path11 from "path";
2533
+ import fs10 from "fs";
2449
2534
  import { randomUUID as randomUUID3 } from "crypto";
2450
2535
  import { fileURLToPath } from "url";
2451
2536
  import {
@@ -2676,40 +2761,238 @@ var ConfigFileAuthBackend = class {
2676
2761
  // src/runtime/agent.ts
2677
2762
  init_attachment_utils();
2678
2763
 
2679
- // src/runtime/artifacts/query-service.ts
2680
- function clampLimit(limit, fallback, max) {
2681
- if (!Number.isFinite(limit)) {
2682
- return fallback;
2764
+ // src/runtime/custom-tools/delegated-custom-tool-client.ts
2765
+ import { randomUUID } from "crypto";
2766
+ function isObject(value) {
2767
+ return !!value && typeof value === "object" && !Array.isArray(value);
2768
+ }
2769
+ function assertToolDefinitions(value) {
2770
+ if (!Array.isArray(value)) {
2771
+ throw new Error("Invalid delegated custom tool definitions response");
2772
+ }
2773
+ for (const definition of value) {
2774
+ if (!isObject(definition) || typeof definition.name !== "string" || typeof definition.label !== "string" || typeof definition.description !== "string" || !isObject(definition.parameters)) {
2775
+ throw new Error("Invalid delegated custom tool definition");
2776
+ }
2683
2777
  }
2684
- const normalized = Math.floor(limit);
2685
- return Math.max(1, Math.min(normalized, max));
2686
2778
  }
2687
- function clampOffset(offset) {
2688
- if (!Number.isFinite(offset)) {
2689
- return 0;
2779
+ function assertToolResult(value) {
2780
+ if (!isObject(value) || !Array.isArray(value.content)) {
2781
+ throw new Error("Invalid delegated custom tool result");
2782
+ }
2783
+ }
2784
+ var DelegatedCustomToolClient = class {
2785
+ constructor(transport = process, options = {}) {
2786
+ this.transport = transport;
2787
+ this.timeoutMs = options.timeoutMs ?? 30 * 60 * 1e3;
2788
+ this.transport.on("message", this.handleMessage);
2789
+ }
2790
+ pendingRequests = /* @__PURE__ */ new Map();
2791
+ timeoutMs;
2792
+ disposed = false;
2793
+ isAvailable() {
2794
+ return typeof this.transport.send === "function" && this.transport.connected !== false;
2795
+ }
2796
+ async listDefinitions() {
2797
+ if (!this.isAvailable()) {
2798
+ return [];
2799
+ }
2800
+ const data = await this.sendRequest("get_custom_tool_definitions");
2801
+ assertToolDefinitions(data);
2802
+ return data;
2803
+ }
2804
+ async executeTool(input) {
2805
+ const data = await this.sendRequest("execute_custom_tool", input);
2806
+ assertToolResult(data);
2807
+ return data;
2808
+ }
2809
+ dispose() {
2810
+ if (this.disposed) {
2811
+ return;
2812
+ }
2813
+ this.disposed = true;
2814
+ this.transport.off("message", this.handleMessage);
2815
+ this.rejectAllPending(new Error("Delegated custom tool client disposed"));
2816
+ }
2817
+ sendRequest(type, payload) {
2818
+ if (!this.isAvailable()) {
2819
+ throw new Error("Delegated custom tool IPC channel is not available");
2820
+ }
2821
+ const id = randomUUID();
2822
+ const request = {
2823
+ id,
2824
+ type,
2825
+ ...payload || {}
2826
+ };
2827
+ return new Promise((resolve, reject) => {
2828
+ const timer = setTimeout(() => {
2829
+ this.pendingRequests.delete(id);
2830
+ reject(new Error(`Delegated custom tool request timed out: ${type}`));
2831
+ }, this.timeoutMs);
2832
+ this.pendingRequests.set(id, { resolve, reject, timer });
2833
+ try {
2834
+ this.transport.send(request);
2835
+ } catch (error) {
2836
+ clearTimeout(timer);
2837
+ this.pendingRequests.delete(id);
2838
+ reject(error instanceof Error ? error : new Error(String(error)));
2839
+ }
2840
+ });
2690
2841
  }
2691
- return Math.max(0, Math.floor(offset));
2842
+ handleMessage = (message) => {
2843
+ if (!isObject(message)) {
2844
+ return;
2845
+ }
2846
+ const payload = message;
2847
+ if (typeof payload.id !== "string" || payload.type !== "result" && payload.type !== "error") {
2848
+ return;
2849
+ }
2850
+ const pending = this.pendingRequests.get(payload.id);
2851
+ if (!pending) {
2852
+ return;
2853
+ }
2854
+ clearTimeout(pending.timer);
2855
+ this.pendingRequests.delete(payload.id);
2856
+ if (payload.type === "error") {
2857
+ pending.reject(new Error(payload.message));
2858
+ return;
2859
+ }
2860
+ pending.resolve(payload.data);
2861
+ };
2862
+ rejectAllPending(error) {
2863
+ for (const [id, pending] of this.pendingRequests) {
2864
+ clearTimeout(pending.timer);
2865
+ pending.reject(error);
2866
+ this.pendingRequests.delete(id);
2867
+ }
2868
+ }
2869
+ };
2870
+
2871
+ // src/runtime/custom-tools/delegated-custom-tool-factory.ts
2872
+ function toAgentToolResult(result) {
2873
+ return {
2874
+ content: result.content,
2875
+ details: result.details
2876
+ };
2692
2877
  }
2693
- var ResultsQueryService = class {
2694
- constructor(resultStore) {
2695
- this.resultStore = resultStore;
2696
- }
2697
- async listRecentArtifacts(options = {}) {
2698
- return this.resultStore.listRecentArtifacts({
2699
- channelId: options.channelId,
2700
- limit: clampLimit(options.limit, 100, 500),
2701
- offset: clampOffset(options.offset)
2878
+ function createDelegatedCustomTools(definitions, client, runContextRef) {
2879
+ return definitions.map((definition) => ({
2880
+ name: definition.name,
2881
+ label: definition.label,
2882
+ description: definition.description,
2883
+ promptSnippet: definition.promptSnippet,
2884
+ promptGuidelines: definition.promptGuidelines,
2885
+ parameters: definition.parameters,
2886
+ async execute(toolCallId, params) {
2887
+ const runContext = runContextRef.current;
2888
+ if (!runContext) {
2889
+ throw new Error(
2890
+ `Delegated custom tool ${definition.name} is not available outside an active run.`
2891
+ );
2892
+ }
2893
+ return toAgentToolResult(
2894
+ await client.executeTool({
2895
+ toolName: definition.name,
2896
+ toolCallId,
2897
+ runContext,
2898
+ params
2899
+ })
2900
+ );
2901
+ }
2902
+ }));
2903
+ }
2904
+
2905
+ // src/runtime/host-ipc/host-ipc-client.ts
2906
+ import { randomUUID as randomUUID2 } from "crypto";
2907
+ function isObject2(value) {
2908
+ return !!value && typeof value === "object" && !Array.isArray(value);
2909
+ }
2910
+ var HostIpcClient = class {
2911
+ constructor(transport = process, options = {}) {
2912
+ this.transport = transport;
2913
+ this.timeoutMs = options.timeoutMs ?? 30 * 60 * 1e3;
2914
+ this.transport.on("message", this.handleMessage);
2915
+ }
2916
+ pendingRequests = /* @__PURE__ */ new Map();
2917
+ timeoutMs;
2918
+ disposed = false;
2919
+ isAvailable() {
2920
+ return typeof this.transport.send === "function" && this.transport.connected !== false;
2921
+ }
2922
+ async notifyChannelSessionCleared(input) {
2923
+ if (!this.isAvailable()) {
2924
+ return;
2925
+ }
2926
+ await this.sendRequest("channel_session_cleared", input);
2927
+ }
2928
+ dispose() {
2929
+ if (this.disposed) {
2930
+ return;
2931
+ }
2932
+ this.disposed = true;
2933
+ this.transport.off("message", this.handleMessage);
2934
+ this.rejectAllPending(new Error("Host IPC client disposed"));
2935
+ }
2936
+ sendRequest(type, payload) {
2937
+ if (!this.isAvailable()) {
2938
+ throw new Error("Host IPC channel is not available");
2939
+ }
2940
+ const id = randomUUID2();
2941
+ const request = {
2942
+ id,
2943
+ type,
2944
+ ...payload || {}
2945
+ };
2946
+ return new Promise((resolve, reject) => {
2947
+ const timer = setTimeout(() => {
2948
+ this.pendingRequests.delete(id);
2949
+ reject(new Error(`Host IPC request timed out: ${type}`));
2950
+ }, this.timeoutMs);
2951
+ this.pendingRequests.set(id, { resolve, reject, timer });
2952
+ try {
2953
+ this.transport.send(request);
2954
+ } catch (error) {
2955
+ clearTimeout(timer);
2956
+ this.pendingRequests.delete(id);
2957
+ reject(error instanceof Error ? error : new Error(String(error)));
2958
+ }
2702
2959
  });
2703
2960
  }
2961
+ handleMessage = (message) => {
2962
+ if (!isObject2(message)) {
2963
+ return;
2964
+ }
2965
+ const payload = message;
2966
+ if (typeof payload.id !== "string" || payload.type !== "result" && payload.type !== "error") {
2967
+ return;
2968
+ }
2969
+ const pending = this.pendingRequests.get(payload.id);
2970
+ if (!pending) {
2971
+ return;
2972
+ }
2973
+ clearTimeout(pending.timer);
2974
+ this.pendingRequests.delete(payload.id);
2975
+ if (payload.type === "error") {
2976
+ pending.reject(new Error(payload.message));
2977
+ return;
2978
+ }
2979
+ pending.resolve(payload.data);
2980
+ };
2981
+ rejectAllPending(error) {
2982
+ for (const [id, pending] of this.pendingRequests) {
2983
+ clearTimeout(pending.timer);
2984
+ pending.reject(error);
2985
+ this.pendingRequests.delete(id);
2986
+ }
2987
+ }
2704
2988
  };
2705
2989
 
2706
- // src/runtime/artifacts/snapshot-service.ts
2707
- import { randomUUID } from "crypto";
2708
- import fs9 from "fs";
2990
+ // src/runtime/tools/send-file-tool.ts
2991
+ import fs8 from "fs";
2709
2992
  import path9 from "path";
2993
+ import { Type } from "@sinclair/typebox";
2710
2994
 
2711
2995
  // src/runtime/files/metadata.ts
2712
- import fs8 from "fs";
2713
2996
  import path8 from "path";
2714
2997
  var MIME_BY_EXT = {
2715
2998
  ".png": "image/png",
@@ -2739,419 +3022,14 @@ function isWithinDirectory(parentDir, targetPath) {
2739
3022
  const relativePath = path8.relative(path8.resolve(parentDir), path8.resolve(targetPath));
2740
3023
  return relativePath !== ".." && !relativePath.startsWith(`..${path8.sep}`) && !path8.isAbsolute(relativePath);
2741
3024
  }
2742
- function toPackRelativePath(rootDir, filePath) {
2743
- const resolvedRoot = path8.resolve(rootDir);
2744
- const resolvedFile = path8.resolve(filePath);
2745
- if (!isWithinDirectory(resolvedRoot, resolvedFile)) {
2746
- throw new Error(`Path is outside the pack root: ${resolvedFile}`);
2747
- }
2748
- return path8.relative(resolvedRoot, resolvedFile).split(path8.sep).join("/");
2749
- }
2750
- function resolvePackFile(rootDir, filePath) {
2751
- if (!path8.isAbsolute(filePath)) {
2752
- throw new Error(`filePath must be absolute: ${filePath}`);
2753
- }
2754
- const resolvedPath = path8.resolve(filePath);
2755
- if (!isWithinDirectory(rootDir, resolvedPath)) {
2756
- throw new Error(`File is outside the pack root: ${resolvedPath}`);
2757
- }
2758
- if (!fs8.existsSync(resolvedPath)) {
2759
- throw new Error(`File not found: ${resolvedPath}`);
2760
- }
2761
- const stats = fs8.statSync(resolvedPath);
2762
- if (!stats.isFile()) {
2763
- throw new Error(`Path is not a file: ${resolvedPath}`);
2764
- }
2765
- fs8.accessSync(resolvedPath, fs8.constants.R_OK);
2766
- return {
2767
- resolvedPath,
2768
- fileName: path8.basename(resolvedPath),
2769
- mimeType: detectMimeType(resolvedPath),
2770
- sizeBytes: stats.size
2771
- };
2772
- }
2773
-
2774
- // src/runtime/artifacts/snapshot-service.ts
2775
- function sanitizeFileName(fileName) {
2776
- const sanitized = fileName.replace(/[^a-zA-Z0-9._-]+/g, "-");
2777
- return sanitized || "artifact";
2778
- }
2779
- function formatSnapshotStamp(isoDate) {
2780
- const normalized = isoDate.replace(/\D+/g, "").slice(0, 14);
2781
- return normalized || String(Date.now());
2782
- }
2783
- var ArtifactSnapshotService = class {
2784
- constructor(rootDir) {
2785
- this.rootDir = rootDir;
2786
- }
2787
- createSnapshots(runId, artifacts, declaredAt) {
2788
- if (artifacts.length === 0) {
2789
- return [];
2790
- }
2791
- const artifactsRoot = path9.resolve(this.rootDir, "data", "artifacts");
2792
- const runDir = path9.join(artifactsRoot, runId);
2793
- const tempDir = path9.join(
2794
- artifactsRoot,
2795
- `.tmp-${runId}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
2796
- );
2797
- const snapshots = [];
2798
- const movedPaths = [];
2799
- const snapshotNames = artifacts.map((artifact) => [
2800
- formatSnapshotStamp(declaredAt),
2801
- randomUUID(),
2802
- sanitizeFileName(artifact.fileName)
2803
- ].join("-"));
2804
- fs9.mkdirSync(artifactsRoot, { recursive: true });
2805
- fs9.mkdirSync(tempDir, { recursive: true });
2806
- try {
2807
- snapshotNames.forEach((snapshotName, index) => {
2808
- fs9.copyFileSync(
2809
- artifacts[index].filePath,
2810
- path9.join(tempDir, snapshotName)
2811
- );
2812
- });
2813
- fs9.mkdirSync(runDir, { recursive: true });
2814
- snapshotNames.forEach((snapshotName, index) => {
2815
- const artifact = artifacts[index];
2816
- const tempSnapshotPath = path9.join(tempDir, snapshotName);
2817
- const finalSnapshotPath = path9.join(runDir, snapshotName);
2818
- fs9.renameSync(tempSnapshotPath, finalSnapshotPath);
2819
- movedPaths.push(finalSnapshotPath);
2820
- snapshots.push({
2821
- declaredAt,
2822
- originalPath: toPackRelativePath(this.rootDir, artifact.filePath),
2823
- snapshotPath: path9.join("data", "artifacts", runId, snapshotName).split(path9.sep).join("/"),
2824
- fileName: artifact.fileName,
2825
- mimeType: artifact.mimeType,
2826
- sizeBytes: artifact.sizeBytes,
2827
- title: artifact.title,
2828
- isPrimary: artifact.isPrimary
2829
- });
2830
- });
2831
- fs9.rmSync(tempDir, { recursive: true, force: true });
2832
- return snapshots;
2833
- } catch (error) {
2834
- fs9.rmSync(tempDir, { recursive: true, force: true });
2835
- movedPaths.forEach((filePath) => fs9.rmSync(filePath, { force: true }));
2836
- this.removeEmptyRunDirectory(runDir);
2837
- throw error;
2838
- }
2839
- }
2840
- removeSnapshots(snapshotPaths) {
2841
- const visitedRunDirs = /* @__PURE__ */ new Set();
2842
- for (const snapshotPath of snapshotPaths) {
2843
- const resolvedPath = path9.resolve(this.rootDir, snapshotPath);
2844
- fs9.rmSync(resolvedPath, { force: true });
2845
- visitedRunDirs.add(path9.dirname(resolvedPath));
2846
- }
2847
- visitedRunDirs.forEach((runDir) => this.removeEmptyRunDirectory(runDir));
2848
- }
2849
- removeEmptyRunDirectory(runDir) {
2850
- try {
2851
- if (!fs9.existsSync(runDir)) {
2852
- return;
2853
- }
2854
- if (fs9.readdirSync(runDir).length === 0) {
2855
- fs9.rmdirSync(runDir);
2856
- }
2857
- } catch {
2858
- }
2859
- }
2860
- };
2861
-
2862
- // src/runtime/artifacts/persistence-service.ts
2863
- var ArtifactPersistenceService = class {
2864
- constructor(snapshotService, resultStore) {
2865
- this.snapshotService = snapshotService;
2866
- this.resultStore = resultStore;
2867
- }
2868
- async saveArtifacts(input) {
2869
- const declaredAt = (/* @__PURE__ */ new Date()).toISOString();
2870
- const snapshots = this.snapshotService.createSnapshots(
2871
- input.runId,
2872
- input.artifacts,
2873
- declaredAt
2874
- );
2875
- try {
2876
- await this.resultStore.insertArtifacts({
2877
- runId: input.runId,
2878
- channelId: input.channelId,
2879
- artifacts: snapshots
2880
- });
2881
- return snapshots.length;
2882
- } catch (error) {
2883
- this.snapshotService.removeSnapshots(
2884
- snapshots.map((artifact) => artifact.snapshotPath)
2885
- );
2886
- throw error;
2887
- }
2888
- }
2889
- };
2890
-
2891
- // src/runtime/artifacts/store.ts
2892
- import fs10 from "fs";
2893
- import path10 from "path";
2894
- import { randomUUID as randomUUID2 } from "crypto";
2895
- import sqlite3 from "sqlite3";
2896
- function mapArtifactRow(row) {
2897
- return {
2898
- artifactId: row.artifact_id,
2899
- runId: row.run_id,
2900
- channelId: row.channel_id,
2901
- originalPath: row.original_path,
2902
- snapshotPath: row.snapshot_path,
2903
- fileName: row.file_name,
2904
- mimeType: row.mime_type,
2905
- sizeBytes: row.size_bytes,
2906
- title: row.title,
2907
- isPrimary: row.is_primary === 1,
2908
- declaredAt: row.declared_at
2909
- };
2910
- }
2911
- var ResultStore = class {
2912
- db = null;
2913
- ready;
2914
- constructor(rootDir) {
2915
- const dataDir = path10.resolve(rootDir, "data");
2916
- fs10.mkdirSync(dataDir, { recursive: true });
2917
- this.ready = this.initialize(path10.join(dataDir, "result-v2.db"));
2918
- }
2919
- async initialize(databasePath) {
2920
- this.db = await openDatabase(databasePath);
2921
- await this.exec("PRAGMA journal_mode = WAL");
2922
- await this.exec(`
2923
- CREATE TABLE IF NOT EXISTS artifacts (
2924
- artifact_id TEXT PRIMARY KEY,
2925
- run_id TEXT NOT NULL,
2926
- channel_id TEXT NOT NULL,
2927
- original_path TEXT NOT NULL,
2928
- snapshot_path TEXT NOT NULL,
2929
- file_name TEXT NOT NULL,
2930
- mime_type TEXT,
2931
- size_bytes INTEGER NOT NULL,
2932
- title TEXT,
2933
- is_primary INTEGER NOT NULL DEFAULT 0,
2934
- declared_at TEXT NOT NULL
2935
- );
2936
-
2937
- CREATE INDEX IF NOT EXISTS idx_artifacts_channel_declared_at
2938
- ON artifacts(channel_id, declared_at DESC);
2939
- `);
2940
- }
2941
- async insertArtifacts(input) {
2942
- await this.ready;
2943
- if (input.artifacts.length === 0) {
2944
- return;
2945
- }
2946
- const insertArtifact = `
2947
- INSERT INTO artifacts (
2948
- artifact_id,
2949
- run_id,
2950
- channel_id,
2951
- original_path,
2952
- snapshot_path,
2953
- file_name,
2954
- mime_type,
2955
- size_bytes,
2956
- title,
2957
- is_primary,
2958
- declared_at
2959
- ) VALUES (
2960
- ?,
2961
- ?,
2962
- ?,
2963
- ?,
2964
- ?,
2965
- ?,
2966
- ?,
2967
- ?,
2968
- ?,
2969
- ?,
2970
- ?
2971
- )
2972
- `;
2973
- await this.exec("BEGIN");
2974
- try {
2975
- for (const artifact of input.artifacts) {
2976
- await this.run(insertArtifact, [
2977
- randomUUID2(),
2978
- input.runId,
2979
- input.channelId,
2980
- artifact.originalPath,
2981
- artifact.snapshotPath,
2982
- artifact.fileName,
2983
- artifact.mimeType ?? null,
2984
- artifact.sizeBytes,
2985
- artifact.title ?? null,
2986
- artifact.isPrimary ? 1 : 0,
2987
- artifact.declaredAt
2988
- ]);
2989
- }
2990
- await this.exec("COMMIT");
2991
- } catch (error) {
2992
- await this.rollback();
2993
- throw error;
2994
- }
2995
- }
2996
- async listRecentArtifacts(options = {}) {
2997
- await this.ready;
2998
- const limit = options.limit ?? 100;
2999
- const offset = options.offset ?? 0;
3000
- const conditions = [];
3001
- const params = [];
3002
- if (options.channelId) {
3003
- conditions.push("channel_id = ?");
3004
- params.push(options.channelId);
3005
- }
3006
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
3007
- const rows = await this.all(`
3008
- SELECT *
3009
- FROM artifacts
3010
- ${whereClause}
3011
- ORDER BY declared_at DESC, rowid DESC
3012
- LIMIT ? OFFSET ?
3013
- `, [...params, limit, offset]);
3014
- return rows.map(mapArtifactRow);
3015
- }
3016
- getDatabase() {
3017
- if (!this.db) {
3018
- throw new Error("Result store database is not ready");
3019
- }
3020
- return this.db;
3021
- }
3022
- exec(sql) {
3023
- const db = this.getDatabase();
3024
- return new Promise((resolve, reject) => {
3025
- db.exec(sql, (error) => {
3026
- if (error) {
3027
- reject(error);
3028
- return;
3029
- }
3030
- resolve();
3031
- });
3032
- });
3033
- }
3034
- run(sql, params = []) {
3035
- const db = this.getDatabase();
3036
- return new Promise((resolve, reject) => {
3037
- db.run(sql, params, (error) => {
3038
- if (error) {
3039
- reject(error);
3040
- return;
3041
- }
3042
- resolve();
3043
- });
3044
- });
3045
- }
3046
- all(sql, params = []) {
3047
- const db = this.getDatabase();
3048
- return new Promise((resolve, reject) => {
3049
- db.all(sql, params, (error, rows) => {
3050
- if (error) {
3051
- reject(error);
3052
- return;
3053
- }
3054
- resolve(rows);
3055
- });
3056
- });
3057
- }
3058
- async rollback() {
3059
- try {
3060
- await this.exec("ROLLBACK");
3061
- } catch {
3062
- }
3063
- }
3064
- };
3065
- function openDatabase(databasePath) {
3066
- return new Promise((resolve, reject) => {
3067
- const db = new sqlite3.Database(databasePath, (error) => {
3068
- if (error) {
3069
- reject(error);
3070
- return;
3071
- }
3072
- resolve(db);
3073
- });
3074
- });
3075
- }
3076
-
3077
- // src/runtime/artifacts/save-artifacts-tool.ts
3078
- import { Type } from "@sinclair/typebox";
3079
- var ArtifactItem = Type.Object({
3080
- filePath: Type.String({
3081
- description: "Absolute path to the artifact file. The file must exist, be readable, and be inside the current pack root."
3082
- }),
3083
- title: Type.Optional(
3084
- Type.String({
3085
- description: "Optional short title shown in the dashboard."
3086
- })
3087
- ),
3088
- isPrimary: Type.Optional(
3089
- Type.Boolean({
3090
- description: "Mark this artifact as a primary output."
3091
- })
3092
- )
3093
- });
3094
- var SaveArtifactsParams = Type.Object({
3095
- artifacts: Type.Array(ArtifactItem, {
3096
- minItems: 1,
3097
- description: "The artifact files to save for this run."
3098
- })
3099
- });
3100
- function textResult(text) {
3101
- return { content: [{ type: "text", text }], details: void 0 };
3102
- }
3103
- function normalizeOptionalText(value) {
3104
- const trimmed = value?.trim();
3105
- return trimmed ? trimmed : void 0;
3106
- }
3107
- function createSaveArtifactsTool(rootDir, saveCallbackRef) {
3108
- return {
3109
- name: "save_artifacts",
3110
- label: "Save Artifacts",
3111
- description: [
3112
- "Save the final output files produced by this run.",
3113
- "Always use this for user-facing deliverables that are part of the final result.",
3114
- "Do not use this for intermediate, temporary, draft, or scratch files.",
3115
- "Each filePath must be an absolute path inside the current pack root."
3116
- ].join("\n"),
3117
- promptSnippet: "save_artifacts: Save final result files for this task. Always call this for user-facing final deliverables, and never for intermediate files. Use absolute paths inside the current pack root.",
3118
- promptGuidelines: [
3119
- "Whenever you create a final result file for the user, call `save_artifacts` before finishing the response.",
3120
- "Do not call `save_artifacts` for intermediate, temporary, draft, or scratch files."
3121
- ],
3122
- parameters: SaveArtifactsParams,
3123
- async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
3124
- const saveArtifacts = saveCallbackRef.current;
3125
- if (!saveArtifacts) {
3126
- throw new Error("Artifact saving is not available for this run.");
3127
- }
3128
- const artifacts = params.artifacts.map((artifact) => {
3129
- const metadata = resolvePackFile(rootDir, artifact.filePath);
3130
- return {
3131
- filePath: metadata.resolvedPath,
3132
- fileName: metadata.fileName,
3133
- mimeType: metadata.mimeType,
3134
- sizeBytes: metadata.sizeBytes,
3135
- title: normalizeOptionalText(artifact.title),
3136
- isPrimary: artifact.isPrimary === true
3137
- };
3138
- });
3139
- const savedCount = await saveArtifacts(artifacts);
3140
- return textResult(`Saved ${savedCount} artifact(s).`);
3141
- }
3142
- };
3143
- }
3144
3025
 
3145
3026
  // src/runtime/tools/send-file-tool.ts
3146
- import fs11 from "fs";
3147
- import path11 from "path";
3148
- import { Type as Type2 } from "@sinclair/typebox";
3149
- var SendFileParams = Type2.Object({
3150
- filePath: Type2.String({
3027
+ var SendFileParams = Type.Object({
3028
+ filePath: Type.String({
3151
3029
  description: "Absolute path to the file to send to the user. The file must exist and be readable."
3152
3030
  }),
3153
- caption: Type2.Optional(
3154
- Type2.String({
3031
+ caption: Type.Optional(
3032
+ Type.String({
3155
3033
  description: "Optional caption or description to accompany the file."
3156
3034
  })
3157
3035
  )
@@ -3165,13 +3043,13 @@ function createSendFileTool(fileOutputCallbackRef) {
3165
3043
  parameters: SendFileParams,
3166
3044
  async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
3167
3045
  const { filePath, caption } = params;
3168
- if (!fs11.existsSync(filePath)) {
3046
+ if (!fs8.existsSync(filePath)) {
3169
3047
  return {
3170
3048
  content: [{ type: "text", text: `Error: File not found: ${filePath}` }],
3171
3049
  details: void 0
3172
3050
  };
3173
3051
  }
3174
- const stats = fs11.statSync(filePath);
3052
+ const stats = fs8.statSync(filePath);
3175
3053
  if (!stats.isFile()) {
3176
3054
  return {
3177
3055
  content: [
@@ -3180,7 +3058,7 @@ function createSendFileTool(fileOutputCallbackRef) {
3180
3058
  details: void 0
3181
3059
  };
3182
3060
  }
3183
- const filename = path11.basename(filePath);
3061
+ const filename = path9.basename(filePath);
3184
3062
  const mimeType = detectMimeType(filePath);
3185
3063
  const callback = fileOutputCallbackRef.current;
3186
3064
  if (callback) {
@@ -3207,51 +3085,51 @@ function createSendFileTool(fileOutputCallbackRef) {
3207
3085
  }
3208
3086
 
3209
3087
  // src/runtime/tools/manage-schedule-tool.ts
3210
- import { Type as Type3 } from "@sinclair/typebox";
3211
- var ManageScheduleParams = Type3.Object({
3212
- action: Type3.Union(
3088
+ import { Type as Type2 } from "@sinclair/typebox";
3089
+ var ManageScheduleParams = Type2.Object({
3090
+ action: Type2.Union(
3213
3091
  [
3214
- Type3.Literal("add"),
3215
- Type3.Literal("list"),
3216
- Type3.Literal("remove"),
3217
- Type3.Literal("trigger"),
3218
- Type3.Literal("enable"),
3219
- Type3.Literal("disable")
3092
+ Type2.Literal("add"),
3093
+ Type2.Literal("list"),
3094
+ Type2.Literal("remove"),
3095
+ Type2.Literal("trigger"),
3096
+ Type2.Literal("enable"),
3097
+ Type2.Literal("disable")
3220
3098
  ],
3221
3099
  { description: "The action to perform." }
3222
3100
  ),
3223
- name: Type3.Optional(
3224
- Type3.String({
3101
+ name: Type2.Optional(
3102
+ Type2.String({
3225
3103
  description: "Unique name for the scheduled task. Required for add/remove/trigger/enable/disable."
3226
3104
  })
3227
3105
  ),
3228
- cron: Type3.Optional(
3229
- Type3.String({
3106
+ cron: Type2.Optional(
3107
+ Type2.String({
3230
3108
  description: "Cron expression (5 fields: minute hour day month weekday). Required for add."
3231
3109
  })
3232
3110
  ),
3233
- prompt: Type3.Optional(
3234
- Type3.String({
3111
+ prompt: Type2.Optional(
3112
+ Type2.String({
3235
3113
  description: "The work prompt to execute when the task triggers. Required for add. Describe only what to do each run; do not repeat timing, cron, or 'every N minutes' instructions here."
3236
3114
  })
3237
3115
  ),
3238
- timezone: Type3.Optional(
3239
- Type3.String({
3116
+ timezone: Type2.Optional(
3117
+ Type2.String({
3240
3118
  description: "Optional timezone for the cron schedule, e.g. 'Asia/Shanghai', 'America/New_York'."
3241
3119
  })
3242
3120
  ),
3243
- notifyAdapter: Type3.Optional(
3244
- Type3.String({
3121
+ notifyAdapter: Type2.Optional(
3122
+ Type2.String({
3245
3123
  description: "Optional target adapter for notifications. If omitted, the current chat is used when supported (Telegram, Slack, or Web)."
3246
3124
  })
3247
3125
  ),
3248
- notifyChannelId: Type3.Optional(
3249
- Type3.String({
3126
+ notifyChannelId: Type2.Optional(
3127
+ Type2.String({
3250
3128
  description: "Optional target channelId for notifications. Must be provided together with notifyAdapter when overriding the default target."
3251
3129
  })
3252
3130
  )
3253
3131
  });
3254
- function textResult2(text) {
3132
+ function textResult(text) {
3255
3133
  return { content: [{ type: "text", text }], details: void 0 };
3256
3134
  }
3257
3135
  function getDefaultNotifyTarget(adapter, channelId) {
@@ -3291,7 +3169,7 @@ function createManageScheduleTool(schedulerRef, adapter, channelId) {
3291
3169
  async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
3292
3170
  const scheduler = schedulerRef.current;
3293
3171
  if (!scheduler) {
3294
- return textResult2(
3172
+ return textResult(
3295
3173
  "Error: Scheduler is not available. The scheduled task system may not be initialized."
3296
3174
  );
3297
3175
  }
@@ -3299,24 +3177,24 @@ function createManageScheduleTool(schedulerRef, adapter, channelId) {
3299
3177
  case "list": {
3300
3178
  const jobs = scheduler.listJobs();
3301
3179
  if (jobs.length === 0) {
3302
- return textResult2("No scheduled tasks configured.");
3180
+ return textResult("No scheduled tasks configured.");
3303
3181
  }
3304
3182
  const lines = jobs.map(
3305
3183
  (j) => `- **${j.name}**: \`${j.cron}\` \u2192 ${j.notify.adapter}:${j.notify.channelId} [${j.enabled ? "enabled" : "disabled"}]${j.running ? " (running)" : ""}${j.lastRunAt ? ` (last: ${j.lastRunAt})` : ""}`
3306
3184
  );
3307
- return textResult2(
3185
+ return textResult(
3308
3186
  `Scheduled tasks (${jobs.length}):
3309
3187
  ${lines.join("\n")}`
3310
3188
  );
3311
3189
  }
3312
3190
  case "add": {
3313
3191
  if (!params.name || !params.cron || !params.prompt) {
3314
- return textResult2(
3192
+ return textResult(
3315
3193
  "Error: 'name', 'cron', and 'prompt' are required for adding a task."
3316
3194
  );
3317
3195
  }
3318
3196
  if (params.notifyAdapter && !params.notifyChannelId || !params.notifyAdapter && params.notifyChannelId) {
3319
- return textResult2(
3197
+ return textResult(
3320
3198
  "Error: 'notifyAdapter' and 'notifyChannelId' must be provided together when overriding the notification target."
3321
3199
  );
3322
3200
  }
@@ -3325,7 +3203,7 @@ ${lines.join("\n")}`
3325
3203
  channelId: params.notifyChannelId
3326
3204
  } : getDefaultNotifyTarget(adapter, channelId);
3327
3205
  if (!notify) {
3328
- return textResult2(
3206
+ return textResult(
3329
3207
  "Error: No default notification target is available for this chat. Provide 'notifyAdapter' and 'notifyChannelId'."
3330
3208
  );
3331
3209
  }
@@ -3338,46 +3216,46 @@ ${lines.join("\n")}`
3338
3216
  timezone: params.timezone
3339
3217
  };
3340
3218
  const result = scheduler.addJob(jobConfig);
3341
- return textResult2(result.message);
3219
+ return textResult(result.message);
3342
3220
  }
3343
3221
  case "remove": {
3344
3222
  if (!params.name) {
3345
- return textResult2(
3223
+ return textResult(
3346
3224
  "Error: 'name' is required for removing a task."
3347
3225
  );
3348
3226
  }
3349
3227
  const result = scheduler.removeJob(params.name);
3350
- return textResult2(result.message);
3228
+ return textResult(result.message);
3351
3229
  }
3352
3230
  case "trigger": {
3353
3231
  if (!params.name) {
3354
- return textResult2(
3232
+ return textResult(
3355
3233
  "Error: 'name' is required for triggering a task."
3356
3234
  );
3357
3235
  }
3358
3236
  const result = await scheduler.triggerJob(params.name);
3359
- return textResult2(result.message);
3237
+ return textResult(result.message);
3360
3238
  }
3361
3239
  case "enable": {
3362
3240
  if (!params.name) {
3363
- return textResult2(
3241
+ return textResult(
3364
3242
  "Error: 'name' is required for enabling a task."
3365
3243
  );
3366
3244
  }
3367
3245
  const result = scheduler.setEnabled(params.name, true);
3368
- return textResult2(result.message);
3246
+ return textResult(result.message);
3369
3247
  }
3370
3248
  case "disable": {
3371
3249
  if (!params.name) {
3372
- return textResult2(
3250
+ return textResult(
3373
3251
  "Error: 'name' is required for disabling a task."
3374
3252
  );
3375
3253
  }
3376
3254
  const result = scheduler.setEnabled(params.name, false);
3377
- return textResult2(result.message);
3255
+ return textResult(result.message);
3378
3256
  }
3379
3257
  default:
3380
- return textResult2(
3258
+ return textResult(
3381
3259
  `Error: Unknown action '${params.action}'. Use: add, list, remove, trigger, enable, disable.`
3382
3260
  );
3383
3261
  }
@@ -3387,8 +3265,8 @@ ${lines.join("\n")}`
3387
3265
 
3388
3266
  // src/runtime/commands/help-command.ts
3389
3267
  init_commands();
3390
- import fs12 from "fs";
3391
- import path12 from "path";
3268
+ import fs9 from "fs";
3269
+ import path10 from "path";
3392
3270
  function buildHelpMessage(rootDir) {
3393
3271
  const sections = [];
3394
3272
  const commands = getVisibleCommands();
@@ -3398,7 +3276,7 @@ function buildHelpMessage(rootDir) {
3398
3276
  sections.push(`\u{1F4CB} **Available Commands**
3399
3277
 
3400
3278
  ${commandLines.join("\n")}`);
3401
- const configPath = path12.resolve(rootDir, "skillpack.json");
3279
+ const configPath = path10.resolve(rootDir, "skillpack.json");
3402
3280
  const skills = readInstalledSkills(configPath);
3403
3281
  if (skills.length > 0) {
3404
3282
  const skillLines = skills.map(
@@ -3434,11 +3312,11 @@ function handleHelpCommand(rootDir) {
3434
3312
  };
3435
3313
  }
3436
3314
  function readInstalledSkills(configPath) {
3437
- if (!fs12.existsSync(configPath)) {
3315
+ if (!fs9.existsSync(configPath)) {
3438
3316
  return [];
3439
3317
  }
3440
3318
  try {
3441
- const raw = fs12.readFileSync(configPath, "utf-8");
3319
+ const raw = fs9.readFileSync(configPath, "utf-8");
3442
3320
  const config = JSON.parse(raw);
3443
3321
  return Array.isArray(config.skills) ? config.skills : [];
3444
3322
  } catch {
@@ -3458,24 +3336,24 @@ var BUILTIN_SKILL_CREATOR_TEMPLATE_DIR = fileURLToPath(
3458
3336
  var PACK_AGENTS_FILE = "AGENTS.md";
3459
3337
  var PACK_SOUL_FILE = "SOUL.md";
3460
3338
  function materializeBuiltinSkillCreator(rootDir, skillsPath) {
3461
- if (!fs13.existsSync(BUILTIN_SKILL_CREATOR_TEMPLATE_DIR)) {
3339
+ if (!fs10.existsSync(BUILTIN_SKILL_CREATOR_TEMPLATE_DIR)) {
3462
3340
  log(
3463
3341
  `[PackAgent] Built-in skill-creator template missing: ${BUILTIN_SKILL_CREATOR_TEMPLATE_DIR}`
3464
3342
  );
3465
3343
  return null;
3466
3344
  }
3467
- const packConfigPath = path13.resolve(rootDir, "skillpack.json");
3468
- const skillDir = path13.resolve(skillsPath, BUILTIN_SKILL_CREATOR_NAME);
3469
- const skillPath = path13.join(skillDir, "SKILL.md");
3345
+ const packConfigPath = path11.resolve(rootDir, "skillpack.json");
3346
+ const skillDir = path11.resolve(skillsPath, BUILTIN_SKILL_CREATOR_NAME);
3347
+ const skillPath = path11.join(skillDir, "SKILL.md");
3470
3348
  const renderTemplate = (content) => content.replaceAll("{{SKILLS_PATH}}", skillsPath).replaceAll("{{PACK_CONFIG_PATH}}", packConfigPath);
3471
3349
  const copyDir = (srcDir, destDir) => {
3472
- fs13.mkdirSync(destDir, { recursive: true });
3473
- for (const entry of fs13.readdirSync(srcDir, { withFileTypes: true })) {
3350
+ fs10.mkdirSync(destDir, { recursive: true });
3351
+ for (const entry of fs10.readdirSync(srcDir, { withFileTypes: true })) {
3474
3352
  if (entry.name === ".DS_Store") {
3475
3353
  continue;
3476
3354
  }
3477
- const srcPath = path13.join(srcDir, entry.name);
3478
- const destPath = path13.join(destDir, entry.name);
3355
+ const srcPath = path11.join(srcDir, entry.name);
3356
+ const destPath = path11.join(destDir, entry.name);
3479
3357
  if (entry.isDirectory()) {
3480
3358
  copyDir(srcPath, destPath);
3481
3359
  continue;
@@ -3484,17 +3362,17 @@ function materializeBuiltinSkillCreator(rootDir, skillsPath) {
3484
3362
  continue;
3485
3363
  }
3486
3364
  if (entry.name.endsWith(".md") || entry.name.endsWith(".py")) {
3487
- const content = fs13.readFileSync(srcPath, "utf-8");
3488
- fs13.writeFileSync(destPath, renderTemplate(content), "utf-8");
3365
+ const content = fs10.readFileSync(srcPath, "utf-8");
3366
+ fs10.writeFileSync(destPath, renderTemplate(content), "utf-8");
3489
3367
  continue;
3490
3368
  }
3491
- fs13.copyFileSync(srcPath, destPath);
3369
+ fs10.copyFileSync(srcPath, destPath);
3492
3370
  }
3493
3371
  };
3494
- if (!fs13.existsSync(skillDir)) {
3372
+ if (!fs10.existsSync(skillDir)) {
3495
3373
  copyDir(BUILTIN_SKILL_CREATOR_TEMPLATE_DIR, skillDir);
3496
3374
  }
3497
- if (!fs13.existsSync(skillPath)) {
3375
+ if (!fs10.existsSync(skillPath)) {
3498
3376
  log(
3499
3377
  `[PackAgent] Materialized built-in skill-creator but SKILL.md is missing: ${skillPath}`
3500
3378
  );
@@ -3522,11 +3400,11 @@ function overrideBuiltinSkillCreator(base, materializedSkill) {
3522
3400
  };
3523
3401
  }
3524
3402
  function readOptionalPackPromptFile(filePath) {
3525
- if (!fs13.existsSync(filePath)) {
3403
+ if (!fs10.existsSync(filePath)) {
3526
3404
  return void 0;
3527
3405
  }
3528
3406
  try {
3529
- const content = fs13.readFileSync(filePath, "utf-8").trim();
3407
+ const content = fs10.readFileSync(filePath, "utf-8").trim();
3530
3408
  return content.length > 0 ? content : void 0;
3531
3409
  } catch (error) {
3532
3410
  console.warn(`[PackAgent] Warning: Could not read ${filePath}:`, error);
@@ -3534,8 +3412,8 @@ function readOptionalPackPromptFile(filePath) {
3534
3412
  }
3535
3413
  }
3536
3414
  function buildPackPromptBlock(rootDir) {
3537
- const agentsPath = path13.resolve(rootDir, PACK_AGENTS_FILE);
3538
- const soulPath = path13.resolve(rootDir, PACK_SOUL_FILE);
3415
+ const agentsPath = path11.resolve(rootDir, PACK_AGENTS_FILE);
3416
+ const soulPath = path11.resolve(rootDir, PACK_SOUL_FILE);
3539
3417
  const agentsContent = readOptionalPackPromptFile(agentsPath);
3540
3418
  const soulContent = readOptionalPackPromptFile(soulPath);
3541
3419
  if (!agentsContent && !soulContent) {
@@ -3594,13 +3472,15 @@ var PackAgent = class {
3594
3472
  options;
3595
3473
  channels = /* @__PURE__ */ new Map();
3596
3474
  pendingSessionCreations = /* @__PURE__ */ new Map();
3597
- schedulerRef = { current: null };
3475
+ schedulerRef = {
3476
+ current: null
3477
+ };
3598
3478
  authStorage;
3599
- artifactPersistenceService;
3479
+ delegatedCustomToolClient = new DelegatedCustomToolClient();
3480
+ hostIpcClient = new HostIpcClient();
3600
3481
  constructor(options) {
3601
3482
  this.options = options;
3602
- this.artifactPersistenceService = options.artifactPersistenceService;
3603
- const configPath = path13.resolve(options.rootDir, "data", "config.json");
3483
+ const configPath = path11.resolve(options.rootDir, "data", "config.json");
3604
3484
  const backend = new ConfigFileAuthBackend(configPath);
3605
3485
  this.authStorage = AuthStorage.fromStorage(backend);
3606
3486
  const providerMeta = SUPPORTED_PROVIDERS[options.provider];
@@ -3627,13 +3507,20 @@ var PackAgent = class {
3627
3507
  setScheduler(scheduler) {
3628
3508
  this.schedulerRef.current = scheduler;
3629
3509
  }
3630
- createCustomTools(adapter, channelId, fileOutputCallbackRef, finalArtifactsSaveCallbackRef) {
3510
+ async createCustomTools(adapter, channelId, fileOutputCallbackRef, delegatedToolRunContextRef) {
3511
+ const delegatedDefinitions = await this.delegatedCustomToolClient.listDefinitions();
3631
3512
  const tools = [
3632
3513
  createSendFileTool(fileOutputCallbackRef),
3633
- createSaveArtifactsTool(this.options.rootDir, finalArtifactsSaveCallbackRef)
3514
+ ...createDelegatedCustomTools(
3515
+ delegatedDefinitions,
3516
+ this.delegatedCustomToolClient,
3517
+ delegatedToolRunContextRef
3518
+ )
3634
3519
  ];
3635
3520
  if (adapter !== "scheduler") {
3636
- tools.push(createManageScheduleTool(this.schedulerRef, adapter, channelId));
3521
+ tools.push(
3522
+ createManageScheduleTool(this.schedulerRef, adapter, channelId)
3523
+ );
3637
3524
  }
3638
3525
  return tools;
3639
3526
  }
@@ -3651,7 +3538,9 @@ var PackAgent = class {
3651
3538
  const modelRegistry = new ModelRegistry(authStorage);
3652
3539
  if (baseUrl && modelId) {
3653
3540
  const apiProtocol = this.options.apiProtocol ?? "openai-completions";
3654
- log(`[PackAgent] Registering custom model ${provider}/${modelId} api=${apiProtocol} baseUrl=${baseUrl}`);
3541
+ log(
3542
+ `[PackAgent] Registering custom model ${provider}/${modelId} api=${apiProtocol} baseUrl=${baseUrl}`
3543
+ );
3655
3544
  modelRegistry.registerProvider(provider, {
3656
3545
  baseUrl,
3657
3546
  apiKey: this.options.apiKey,
@@ -3672,26 +3561,23 @@ var PackAgent = class {
3672
3561
  const resolvedModel = modelRegistry.find(provider, modelId);
3673
3562
  const model = resolvedModel && baseUrl && !this.options.apiProtocol ? { ...resolvedModel, baseUrl } : resolvedModel;
3674
3563
  if (resolvedModel && baseUrl) {
3675
- log(`[PackAgent] Resolved ${provider}/${modelId} api=${resolvedModel.api} baseUrl=${baseUrl}`);
3564
+ log(
3565
+ `[PackAgent] Resolved ${provider}/${modelId} api=${resolvedModel.api} baseUrl=${baseUrl}`
3566
+ );
3676
3567
  }
3677
- const sessionDir = path13.resolve(
3678
- rootDir,
3679
- "data",
3680
- "sessions",
3681
- channelId
3682
- );
3683
- fs13.mkdirSync(sessionDir, { recursive: true });
3568
+ const sessionDir = path11.resolve(rootDir, "data", "sessions", channelId);
3569
+ fs10.mkdirSync(sessionDir, { recursive: true });
3684
3570
  const sessionManager = SessionManager.continueRecent(rootDir, sessionDir);
3685
3571
  log(`[PackAgent] Session dir: ${sessionDir}`);
3686
- const workspaceDir = path13.resolve(
3572
+ const workspaceDir = path11.resolve(
3687
3573
  rootDir,
3688
3574
  "data",
3689
3575
  "workspaces",
3690
3576
  channelId
3691
3577
  );
3692
- fs13.mkdirSync(workspaceDir, { recursive: true });
3578
+ fs10.mkdirSync(workspaceDir, { recursive: true });
3693
3579
  log(`[PackAgent] Workspace dir: ${workspaceDir}`);
3694
- const skillsPath = path13.resolve(rootDir, "skills");
3580
+ const skillsPath = path11.resolve(rootDir, "skills");
3695
3581
  log(`[PackAgent] Loading skills from: ${skillsPath}`);
3696
3582
  const materializedSkillCreator = materializeBuiltinSkillCreator(
3697
3583
  rootDir,
@@ -3704,14 +3590,22 @@ var PackAgent = class {
3704
3590
  }
3705
3591
  const packPromptFiles = buildPackPromptBlock(rootDir);
3706
3592
  if (packPromptFiles.agentsContent) {
3707
- log(`[PackAgent] Loaded pack policy from: ${packPromptFiles.agentsPath}`);
3593
+ log(
3594
+ `[PackAgent] Loaded pack policy from: ${packPromptFiles.agentsPath}`
3595
+ );
3708
3596
  } else {
3709
- log(`[PackAgent] No pack policy file found at: ${packPromptFiles.agentsPath}`);
3597
+ log(
3598
+ `[PackAgent] No pack policy file found at: ${packPromptFiles.agentsPath}`
3599
+ );
3710
3600
  }
3711
3601
  if (packPromptFiles.soulContent) {
3712
- log(`[PackAgent] Loaded pack persona from: ${packPromptFiles.soulPath}`);
3602
+ log(
3603
+ `[PackAgent] Loaded pack persona from: ${packPromptFiles.soulPath}`
3604
+ );
3713
3605
  } else {
3714
- log(`[PackAgent] No pack persona file found at: ${packPromptFiles.soulPath}`);
3606
+ log(
3607
+ `[PackAgent] No pack persona file found at: ${packPromptFiles.soulPath}`
3608
+ );
3715
3609
  }
3716
3610
  log(
3717
3611
  `[PackAgent] Pack prompt injection: ${packPromptFiles.promptBlock ? "enabled" : "disabled"}`
@@ -3729,14 +3623,14 @@ var PackAgent = class {
3729
3623
  const fileOutputCallbackRef = {
3730
3624
  current: null
3731
3625
  };
3732
- const finalArtifactsSaveCallbackRef = {
3626
+ const delegatedToolRunContextRef = {
3733
3627
  current: null
3734
3628
  };
3735
- const customTools = this.createCustomTools(
3629
+ const customTools = await this.createCustomTools(
3736
3630
  adapter,
3737
3631
  channelId,
3738
3632
  fileOutputCallbackRef,
3739
- finalArtifactsSaveCallbackRef
3633
+ delegatedToolRunContextRef
3740
3634
  );
3741
3635
  const { session } = await createAgentSession({
3742
3636
  cwd: workspaceDir,
@@ -3753,7 +3647,7 @@ var PackAgent = class {
3753
3647
  running: false,
3754
3648
  pending: Promise.resolve(),
3755
3649
  fileOutputCallbackRef,
3756
- finalArtifactsSaveCallbackRef
3650
+ delegatedToolRunContextRef
3757
3651
  };
3758
3652
  this.channels.set(channelId, channelSession);
3759
3653
  return channelSession;
@@ -3776,12 +3670,10 @@ var PackAgent = class {
3776
3670
  cs.fileOutputCallbackRef.current = (event) => {
3777
3671
  onEvent(event);
3778
3672
  };
3779
- cs.finalArtifactsSaveCallbackRef.current = (artifacts) => {
3780
- return this.artifactPersistenceService.saveArtifacts({
3781
- runId,
3782
- channelId,
3783
- artifacts
3784
- });
3673
+ cs.delegatedToolRunContextRef.current = {
3674
+ runId,
3675
+ channelId,
3676
+ adapter
3785
3677
  };
3786
3678
  unsubscribe = cs.session.subscribe((event) => {
3787
3679
  switch (event.type) {
@@ -3797,7 +3689,10 @@ var PackAgent = class {
3797
3689
  if (event.message?.role === "user") {
3798
3690
  log(JSON.stringify(event.message.content, null, 2));
3799
3691
  }
3800
- onEvent({ type: "message_start", role: event.message?.role ?? "" });
3692
+ onEvent({
3693
+ type: "message_start",
3694
+ role: event.message?.role ?? ""
3695
+ });
3801
3696
  break;
3802
3697
  case "message_update":
3803
3698
  if (event.assistantMessageEvent?.type === "text_delta") {
@@ -3864,18 +3759,26 @@ var PackAgent = class {
3864
3759
  let promptText = text;
3865
3760
  const promptOptions = {};
3866
3761
  if (attachments && attachments.length > 0) {
3867
- const imageAttachments = attachments.filter((a) => isImageMime(a.mimeType));
3868
- const nonImageAttachments = attachments.filter((a) => !isImageMime(a.mimeType));
3762
+ const imageAttachments = attachments.filter(
3763
+ (a) => isImageMime(a.mimeType)
3764
+ );
3765
+ const nonImageAttachments = attachments.filter(
3766
+ (a) => !isImageMime(a.mimeType)
3767
+ );
3869
3768
  if (imageAttachments.length > 0) {
3870
3769
  promptOptions.images = attachmentsToImageContent(imageAttachments);
3871
- log(`[PackAgent] Passing ${imageAttachments.length} image(s) to LLM`);
3770
+ log(
3771
+ `[PackAgent] Passing ${imageAttachments.length} image(s) to LLM`
3772
+ );
3872
3773
  }
3873
3774
  if (nonImageAttachments.length > 0) {
3874
3775
  const attachmentPrompt = formatAttachmentsPrompt(nonImageAttachments);
3875
3776
  promptText = `${attachmentPrompt}
3876
3777
 
3877
3778
  ${text}`;
3878
- log(`[PackAgent] Injecting ${nonImageAttachments.length} non-image attachment(s) into prompt`);
3779
+ log(
3780
+ `[PackAgent] Injecting ${nonImageAttachments.length} non-image attachment(s) into prompt`
3781
+ );
3879
3782
  }
3880
3783
  }
3881
3784
  await cs.session.prompt(promptText, promptOptions);
@@ -3898,12 +3801,15 @@ ${text}`;
3898
3801
  } finally {
3899
3802
  cs.running = false;
3900
3803
  cs.fileOutputCallbackRef.current = null;
3901
- cs.finalArtifactsSaveCallbackRef.current = null;
3804
+ cs.delegatedToolRunContextRef.current = null;
3902
3805
  unsubscribe();
3903
3806
  }
3904
3807
  };
3905
3808
  const resultPromise = cs.pending.catch(() => void 0).then(run);
3906
- cs.pending = resultPromise.then(() => void 0, () => void 0);
3809
+ cs.pending = resultPromise.then(
3810
+ () => void 0,
3811
+ () => void 0
3812
+ );
3907
3813
  return resultPromise;
3908
3814
  }
3909
3815
  async handleCommand(command, channelId) {
@@ -3918,11 +3824,12 @@ ${text}`;
3918
3824
  this.channels.delete(channelId);
3919
3825
  }
3920
3826
  const { rootDir } = this.options;
3921
- const sessionDir = path13.resolve(rootDir, "data", "sessions", channelId);
3922
- if (fs13.existsSync(sessionDir)) {
3923
- fs13.rmSync(sessionDir, { recursive: true, force: true });
3827
+ const sessionDir = path11.resolve(rootDir, "data", "sessions", channelId);
3828
+ if (fs10.existsSync(sessionDir)) {
3829
+ fs10.rmSync(sessionDir, { recursive: true, force: true });
3924
3830
  log(`[PackAgent] Cleared session dir: ${sessionDir}`);
3925
3831
  }
3832
+ await this.hostIpcClient.notifyChannelSessionCleared({ channelId });
3926
3833
  return {
3927
3834
  success: true,
3928
3835
  message: command === "new" ? "New session started." : "Session cleared."
@@ -3971,14 +3878,14 @@ ${text}`;
3971
3878
  };
3972
3879
 
3973
3880
  // src/runtime/adapters/web.ts
3974
- import fs15 from "fs";
3975
- import path15 from "path";
3881
+ import fs12 from "fs";
3882
+ import path13 from "path";
3976
3883
  import { WebSocketServer } from "ws";
3977
3884
  init_commands();
3978
3885
 
3979
3886
  // src/runtime/services/conversation.ts
3980
- import fs14 from "fs";
3981
- import path14 from "path";
3887
+ import fs11 from "fs";
3888
+ import path12 from "path";
3982
3889
  import {
3983
3890
  parseSessionEntries
3984
3891
  } from "@mariozechner/pi-coding-agent";
@@ -3996,17 +3903,17 @@ var ConversationService = class {
3996
3903
  includeLegacyWeb = true,
3997
3904
  allowedPlatforms
3998
3905
  } = options;
3999
- const sessionsDir = path14.resolve(this.rootDir, "data", "sessions");
3906
+ const sessionsDir = path12.resolve(this.rootDir, "data", "sessions");
4000
3907
  const channelIds = new Set(activeChannels);
4001
3908
  const allowedPlatformSet = allowedPlatforms ? new Set(allowedPlatforms) : null;
4002
3909
  if (includeDefaultWeb) {
4003
3910
  channelIds.add(DEFAULT_WEB_CHANNEL_ID);
4004
3911
  }
4005
- if (fs14.existsSync(sessionsDir)) {
4006
- for (const entry of fs14.readdirSync(sessionsDir)) {
4007
- const channelDir = path14.join(sessionsDir, entry);
3912
+ if (fs11.existsSync(sessionsDir)) {
3913
+ for (const entry of fs11.readdirSync(sessionsDir)) {
3914
+ const channelDir = path12.join(sessionsDir, entry);
4008
3915
  try {
4009
- if (!fs14.statSync(channelDir).isDirectory()) {
3916
+ if (!fs11.statSync(channelDir).isDirectory()) {
4010
3917
  continue;
4011
3918
  }
4012
3919
  const platform = this.detectPlatform(entry);
@@ -4030,7 +3937,7 @@ var ConversationService = class {
4030
3937
  if (!includeLegacyWeb && this.isLegacyWebConversation(channelId)) {
4031
3938
  continue;
4032
3939
  }
4033
- const channelDir = path14.join(sessionsDir, channelId);
3940
+ const channelDir = path12.join(sessionsDir, channelId);
4034
3941
  const sessionFile = this.findLatestSessionFile(channelDir);
4035
3942
  let messageCount = 0;
4036
3943
  let lastMessageAt = "";
@@ -4074,7 +3981,7 @@ var ConversationService = class {
4074
3981
  * Load latest messages for a channel in a simplified format.
4075
3982
  */
4076
3983
  getMessages(channelId, limit = 100) {
4077
- const channelDir = path14.resolve(
3984
+ const channelDir = path12.resolve(
4078
3985
  this.rootDir,
4079
3986
  "data",
4080
3987
  "sessions",
@@ -4163,20 +4070,20 @@ var ConversationService = class {
4163
4070
  return messages.slice(-safeLimit);
4164
4071
  }
4165
4072
  findLatestSessionFile(channelDir) {
4166
- if (!fs14.existsSync(channelDir)) return null;
4073
+ if (!fs11.existsSync(channelDir)) return null;
4167
4074
  let stats;
4168
4075
  try {
4169
- stats = fs14.statSync(channelDir);
4076
+ stats = fs11.statSync(channelDir);
4170
4077
  } catch {
4171
4078
  return null;
4172
4079
  }
4173
4080
  if (!stats.isDirectory()) return null;
4174
- const files = fs14.readdirSync(channelDir).filter((file) => file.endsWith(".jsonl")).sort((a, b) => b.localeCompare(a));
4175
- return files[0] ? path14.join(channelDir, files[0]) : null;
4081
+ const files = fs11.readdirSync(channelDir).filter((file) => file.endsWith(".jsonl")).sort((a, b) => b.localeCompare(a));
4082
+ return files[0] ? path12.join(channelDir, files[0]) : null;
4176
4083
  }
4177
4084
  loadEntries(filePath) {
4178
4085
  try {
4179
- const content = fs14.readFileSync(filePath, "utf-8");
4086
+ const content = fs11.readFileSync(filePath, "utf-8");
4180
4087
  const fileEntries = parseSessionEntries(content);
4181
4088
  return fileEntries.filter(
4182
4089
  (entry) => entry.type !== "session"
@@ -4372,7 +4279,7 @@ var ConversationService = class {
4372
4279
 
4373
4280
  // src/runtime/adapters/web.ts
4374
4281
  function getPackConfig(rootDir) {
4375
- const raw = fs15.readFileSync(path15.join(rootDir, "skillpack.json"), "utf-8");
4282
+ const raw = fs12.readFileSync(path13.join(rootDir, "skillpack.json"), "utf-8");
4376
4283
  return JSON.parse(raw);
4377
4284
  }
4378
4285
  function parseCommand(text) {
@@ -4402,7 +4309,7 @@ function parsePositiveInt(value, fallback) {
4402
4309
  return Math.floor(parsed);
4403
4310
  }
4404
4311
  function resolveDownloadFilePath(rootDir, filePath) {
4405
- const resolvedPath = path15.isAbsolute(filePath) ? path15.resolve(filePath) : path15.resolve(rootDir, filePath);
4312
+ const resolvedPath = path13.isAbsolute(filePath) ? path13.resolve(filePath) : path13.resolve(rootDir, filePath);
4406
4313
  return isWithinDirectory(rootDir, resolvedPath) ? resolvedPath : null;
4407
4314
  }
4408
4315
  var WebAdapter = class {
@@ -4417,7 +4324,6 @@ var WebAdapter = class {
4417
4324
  this.agent = agent;
4418
4325
  this.ipcBroadcaster = ctx.ipcBroadcaster ?? null;
4419
4326
  this.conversationService = new ConversationService(rootDir);
4420
- const resultsQueryService = ctx.resultsQueryService ?? null;
4421
4327
  const currentConf = configManager.getConfig();
4422
4328
  let apiKey = currentConf.apiKey || "";
4423
4329
  let currentProvider = currentConf.provider || "openai";
@@ -4561,17 +4467,6 @@ var WebAdapter = class {
4561
4467
  )
4562
4468
  );
4563
4469
  });
4564
- app.get("/api/results/artifacts", async (req, res) => {
4565
- if (!resultsQueryService) {
4566
- res.status(503).json({ error: "Results query service is not available" });
4567
- return;
4568
- }
4569
- res.json(await resultsQueryService.listRecentArtifacts({
4570
- channelId: typeof req.query.channelId === "string" ? req.query.channelId : void 0,
4571
- limit: parsePositiveInt(req.query.limit, 100),
4572
- offset: parsePositiveInt(req.query.offset, 0)
4573
- }));
4574
- });
4575
4470
  app.get("/api/files", (req, res) => {
4576
4471
  const filePath = req.query.path;
4577
4472
  if (!filePath) {
@@ -4583,17 +4478,17 @@ var WebAdapter = class {
4583
4478
  res.status(403).json({ error: "Access denied" });
4584
4479
  return;
4585
4480
  }
4586
- if (!fs15.existsSync(resolvedPath)) {
4481
+ if (!fs12.existsSync(resolvedPath)) {
4587
4482
  res.status(404).json({ error: "File not found" });
4588
4483
  return;
4589
4484
  }
4590
- const filename = path15.basename(resolvedPath);
4485
+ const filename = path13.basename(resolvedPath);
4591
4486
  res.setHeader("Content-Type", "application/octet-stream");
4592
4487
  res.setHeader(
4593
4488
  "Content-Disposition",
4594
4489
  `attachment; filename="${filename}"`
4595
4490
  );
4596
- fs15.createReadStream(resolvedPath).pipe(res);
4491
+ fs12.createReadStream(resolvedPath).pipe(res);
4597
4492
  });
4598
4493
  const getScheduler = () => {
4599
4494
  const schedulerAdapter = ctx.adapterMap?.get("scheduler");
@@ -4615,20 +4510,20 @@ var WebAdapter = class {
4615
4510
  return;
4616
4511
  }
4617
4512
  const { name, cron: cronExpr, prompt, notify, enabled, timezone } = req.body;
4618
- if (!name || !cronExpr || !prompt || !notify?.adapter || !notify?.channelId) {
4513
+ if (!name || !prompt || !notify?.adapter || !notify?.channelId) {
4619
4514
  res.status(400).json({
4620
4515
  success: false,
4621
- message: "Required fields: name, cron, prompt, notify.adapter, notify.channelId"
4516
+ message: "Required fields: name, prompt, notify.adapter, notify.channelId"
4622
4517
  });
4623
4518
  return;
4624
4519
  }
4625
4520
  const result = scheduler.addJob({
4626
4521
  name,
4627
- cron: cronExpr,
4522
+ ...typeof cronExpr === "string" ? { cron: cronExpr } : {},
4628
4523
  prompt,
4629
4524
  notify,
4630
- enabled: enabled !== false,
4631
- timezone
4525
+ ...typeof enabled === "boolean" ? { enabled } : {},
4526
+ ...typeof timezone === "string" ? { timezone } : {}
4632
4527
  });
4633
4528
  res.json(result);
4634
4529
  });
@@ -4778,13 +4673,28 @@ var WebAdapter = class {
4778
4673
 
4779
4674
  // src/runtime/adapters/ipc.ts
4780
4675
  init_types();
4676
+ var IPC_REQUEST_TYPES = /* @__PURE__ */ new Set([
4677
+ "get_conversations",
4678
+ "create_conversation",
4679
+ "get_messages",
4680
+ "send_message",
4681
+ "command",
4682
+ "get_config",
4683
+ "update_config",
4684
+ "get_status",
4685
+ "get_scheduled_jobs",
4686
+ "add_scheduled_job",
4687
+ "update_scheduled_job",
4688
+ "set_scheduled_job_enabled",
4689
+ "trigger_scheduled_job",
4690
+ "remove_scheduled_job"
4691
+ ]);
4781
4692
  var IpcAdapter = class {
4782
4693
  name = "ipc";
4783
4694
  agent = null;
4784
4695
  rootDir = "";
4785
4696
  adapterMap = null;
4786
4697
  conversationService = null;
4787
- resultsQueryService = null;
4788
4698
  createdChannels = /* @__PURE__ */ new Set();
4789
4699
  messageListener;
4790
4700
  started = false;
@@ -4796,7 +4706,6 @@ var IpcAdapter = class {
4796
4706
  this.rootDir = ctx.rootDir;
4797
4707
  this.adapterMap = ctx.adapterMap ?? null;
4798
4708
  this.conversationService = new ConversationService(ctx.rootDir);
4799
- this.resultsQueryService = ctx.resultsQueryService ?? null;
4800
4709
  this.messageListener = (message) => {
4801
4710
  if (!this.isIpcRequest(message)) return;
4802
4711
  void this.handleRequest(message);
@@ -4841,7 +4750,7 @@ var IpcAdapter = class {
4841
4750
  isIpcRequest(message) {
4842
4751
  if (!message || typeof message !== "object") return false;
4843
4752
  const maybe = message;
4844
- return typeof maybe.id === "string" && typeof maybe.type === "string";
4753
+ return typeof maybe.id === "string" && typeof maybe.type === "string" && IPC_REQUEST_TYPES.has(maybe.type);
4845
4754
  }
4846
4755
  async handleRequest(request) {
4847
4756
  if (!this.agent || !this.conversationService) {
@@ -4880,18 +4789,6 @@ var IpcAdapter = class {
4880
4789
  this.reply(request.id, messages);
4881
4790
  return;
4882
4791
  }
4883
- case "get_recent_artifacts": {
4884
- if (!this.resultsQueryService) {
4885
- this.replyError(request.id, "Results query service is not available");
4886
- return;
4887
- }
4888
- this.reply(request.id, await this.resultsQueryService.listRecentArtifacts({
4889
- channelId: request.channelId,
4890
- limit: request.limit,
4891
- offset: request.offset
4892
- }));
4893
- return;
4894
- }
4895
4792
  case "send_message": {
4896
4793
  if (!request.channelId || typeof request.channelId !== "string") {
4897
4794
  this.replyError(request.id, "channelId is required");
@@ -5135,28 +5032,28 @@ var Lifecycle = class {
5135
5032
 
5136
5033
  // src/runtime/registry.ts
5137
5034
  import crypto from "crypto";
5138
- import fs16 from "fs";
5035
+ import fs13 from "fs";
5139
5036
  import os from "os";
5140
- import path16 from "path";
5141
- var SKILLPACK_HOME = path16.join(os.homedir(), ".skillpack");
5142
- var LEGACY_REGISTRY_FILE = path16.join(SKILLPACK_HOME, "registry.json");
5143
- var REGISTRY_DIR = path16.join(SKILLPACK_HOME, "registry.d");
5037
+ import path14 from "path";
5038
+ var SKILLPACK_HOME = path14.join(os.homedir(), ".skillpack");
5039
+ var LEGACY_REGISTRY_FILE = path14.join(SKILLPACK_HOME, "registry.json");
5040
+ var REGISTRY_DIR = path14.join(SKILLPACK_HOME, "registry.d");
5144
5041
  var migrationChecked = false;
5145
5042
  function ensureHomeDir() {
5146
- if (!fs16.existsSync(SKILLPACK_HOME)) {
5147
- fs16.mkdirSync(SKILLPACK_HOME, { recursive: true });
5043
+ if (!fs13.existsSync(SKILLPACK_HOME)) {
5044
+ fs13.mkdirSync(SKILLPACK_HOME, { recursive: true });
5148
5045
  }
5149
5046
  }
5150
5047
  function ensureRegistryDir() {
5151
5048
  ensureHomeDir();
5152
- if (!fs16.existsSync(REGISTRY_DIR)) {
5153
- fs16.mkdirSync(REGISTRY_DIR, { recursive: true });
5049
+ if (!fs13.existsSync(REGISTRY_DIR)) {
5050
+ fs13.mkdirSync(REGISTRY_DIR, { recursive: true });
5154
5051
  }
5155
5052
  }
5156
5053
  function canonicalizeDir(dir) {
5157
- const resolved = path16.resolve(dir);
5054
+ const resolved = path14.resolve(dir);
5158
5055
  try {
5159
- return fs16.realpathSync(resolved);
5056
+ return fs13.realpathSync(resolved);
5160
5057
  } catch {
5161
5058
  return resolved;
5162
5059
  }
@@ -5165,7 +5062,7 @@ function hashDir(dir) {
5165
5062
  return crypto.createHash("md5").update(canonicalizeDir(dir)).digest("hex");
5166
5063
  }
5167
5064
  function getEntryPathForCanonicalDir(dir) {
5168
- return path16.join(REGISTRY_DIR, `${hashDir(dir)}.json`);
5065
+ return path14.join(REGISTRY_DIR, `${hashDir(dir)}.json`);
5169
5066
  }
5170
5067
  function getEntryPath(dir) {
5171
5068
  ensureRegistryReady();
@@ -5173,11 +5070,11 @@ function getEntryPath(dir) {
5173
5070
  }
5174
5071
  function listEntryFiles() {
5175
5072
  ensureRegistryReady();
5176
- return fs16.readdirSync(REGISTRY_DIR).filter((file) => file.endsWith(".json")).sort().map((file) => path16.join(REGISTRY_DIR, file));
5073
+ return fs13.readdirSync(REGISTRY_DIR).filter((file) => file.endsWith(".json")).sort().map((file) => path14.join(REGISTRY_DIR, file));
5177
5074
  }
5178
5075
  function readEntryFile(filePath) {
5179
5076
  try {
5180
- const raw = fs16.readFileSync(filePath, "utf-8");
5077
+ const raw = fs13.readFileSync(filePath, "utf-8");
5181
5078
  const data = JSON.parse(raw);
5182
5079
  if (typeof data?.dir !== "string" || typeof data?.name !== "string" || typeof data?.version !== "string" || typeof data?.port !== "number" || typeof data?.pid !== "number" && data?.pid !== null || data?.status !== "running" && data?.status !== "stopped") {
5183
5080
  return null;
@@ -5210,8 +5107,8 @@ function writeEntryFile(entry) {
5210
5107
  };
5211
5108
  const entryPath = getEntryPathForCanonicalDir(normalized.dir);
5212
5109
  const tmpPath = createTmpPath(entryPath);
5213
- fs16.writeFileSync(tmpPath, JSON.stringify(normalized, null, 2), "utf-8");
5214
- fs16.renameSync(tmpPath, entryPath);
5110
+ fs13.writeFileSync(tmpPath, JSON.stringify(normalized, null, 2), "utf-8");
5111
+ fs13.renameSync(tmpPath, entryPath);
5215
5112
  }
5216
5113
  function migrateLegacyRegistryIfNeeded() {
5217
5114
  if (migrationChecked) {
@@ -5219,14 +5116,14 @@ function migrateLegacyRegistryIfNeeded() {
5219
5116
  }
5220
5117
  migrationChecked = true;
5221
5118
  ensureRegistryDir();
5222
- if (!fs16.existsSync(LEGACY_REGISTRY_FILE)) {
5119
+ if (!fs13.existsSync(LEGACY_REGISTRY_FILE)) {
5223
5120
  return;
5224
5121
  }
5225
5122
  if (listEntryFiles().length > 0) {
5226
5123
  return;
5227
5124
  }
5228
5125
  try {
5229
- const raw = fs16.readFileSync(LEGACY_REGISTRY_FILE, "utf-8");
5126
+ const raw = fs13.readFileSync(LEGACY_REGISTRY_FILE, "utf-8");
5230
5127
  const data = JSON.parse(raw);
5231
5128
  const packs = Array.isArray(data?.packs) ? data.packs : [];
5232
5129
  for (const pack of packs) {
@@ -5239,7 +5136,7 @@ function migrateLegacyRegistryIfNeeded() {
5239
5136
  } catch {
5240
5137
  }
5241
5138
  }
5242
- fs16.renameSync(LEGACY_REGISTRY_FILE, `${LEGACY_REGISTRY_FILE}.legacy`);
5139
+ fs13.renameSync(LEGACY_REGISTRY_FILE, `${LEGACY_REGISTRY_FILE}.legacy`);
5243
5140
  } catch (err) {
5244
5141
  console.warn(" [Registry] Failed to migrate legacy registry.json:", err);
5245
5142
  }
@@ -5292,7 +5189,7 @@ function deregister(dir, pid) {
5292
5189
  }
5293
5190
 
5294
5191
  // src/runtime/server.ts
5295
- var __dirname = path18.dirname(fileURLToPath2(import.meta.url));
5192
+ var __dirname = path16.dirname(fileURLToPath2(import.meta.url));
5296
5193
  async function startServer(options) {
5297
5194
  const {
5298
5195
  rootDir,
@@ -5308,8 +5205,8 @@ async function startServer(options) {
5308
5205
  const baseUrl = dataConfig.baseUrl?.trim() || void 0;
5309
5206
  const modelId = dataConfig.modelId?.trim() || (SUPPORTED_PROVIDERS[provider]?.defaultModelId ?? SUPPORTED_PROVIDERS.openai.defaultModelId);
5310
5207
  const apiProtocol = dataConfig.apiProtocol;
5311
- const packageRoot = path18.resolve(__dirname, "..");
5312
- const webDir = fs19.existsSync(path18.join(rootDir, "web")) ? path18.join(rootDir, "web") : path18.join(packageRoot, "web");
5208
+ const packageRoot = path16.resolve(__dirname, "..");
5209
+ const webDir = fs16.existsSync(path16.join(rootDir, "web")) ? path16.join(rootDir, "web") : path16.join(packageRoot, "web");
5313
5210
  const app = express();
5314
5211
  app.use(express.json());
5315
5212
  app.use(express.static(webDir));
@@ -5327,13 +5224,6 @@ async function startServer(options) {
5327
5224
  });
5328
5225
  });
5329
5226
  const lifecycle = new Lifecycle(server);
5330
- const resultStore = new ResultStore(rootDir);
5331
- const artifactSnapshotService = new ArtifactSnapshotService(rootDir);
5332
- const artifactPersistenceService = new ArtifactPersistenceService(
5333
- artifactSnapshotService,
5334
- resultStore
5335
- );
5336
- const resultsQueryService = new ResultsQueryService(resultStore);
5337
5227
  const agent = new PackAgent({
5338
5228
  apiKey,
5339
5229
  rootDir,
@@ -5341,14 +5231,16 @@ async function startServer(options) {
5341
5231
  modelId,
5342
5232
  baseUrl,
5343
5233
  apiProtocol,
5344
- lifecycleHandler: lifecycle,
5345
- artifactPersistenceService
5234
+ lifecycleHandler: lifecycle
5346
5235
  });
5347
5236
  const adapters = [];
5348
5237
  const adapterMap = /* @__PURE__ */ new Map();
5349
5238
  const hasIpcChannel = typeof process.send === "function";
5350
5239
  const ipcAdapter = new IpcAdapter();
5351
5240
  const webEnabled = runtimeMode === "standalone";
5241
+ console.log(
5242
+ `[Runtime] IPC channel ${hasIpcChannel ? "available" : "not available"} (mode=${runtimeMode})`
5243
+ );
5352
5244
  if (hasIpcChannel) {
5353
5245
  await ipcAdapter.start({
5354
5246
  agent,
@@ -5356,8 +5248,7 @@ async function startServer(options) {
5356
5248
  app,
5357
5249
  rootDir,
5358
5250
  lifecycle,
5359
- adapterMap,
5360
- resultsQueryService
5251
+ adapterMap
5361
5252
  });
5362
5253
  adapters.push(ipcAdapter);
5363
5254
  adapterMap.set(ipcAdapter.name, ipcAdapter);
@@ -5372,8 +5263,7 @@ async function startServer(options) {
5372
5263
  rootDir,
5373
5264
  lifecycle,
5374
5265
  adapterMap,
5375
- ipcBroadcaster,
5376
- resultsQueryService
5266
+ ipcBroadcaster
5377
5267
  });
5378
5268
  adapters.push(webAdapter);
5379
5269
  adapterMap.set(webAdapter.name, webAdapter);
@@ -5391,8 +5281,7 @@ async function startServer(options) {
5391
5281
  rootDir,
5392
5282
  lifecycle,
5393
5283
  adapterMap,
5394
- ipcBroadcaster,
5395
- resultsQueryService
5284
+ ipcBroadcaster
5396
5285
  });
5397
5286
  adapters.push(telegramAdapter);
5398
5287
  adapterMap.set(telegramAdapter.name, telegramAdapter);
@@ -5420,8 +5309,7 @@ async function startServer(options) {
5420
5309
  rootDir,
5421
5310
  lifecycle,
5422
5311
  adapterMap,
5423
- ipcBroadcaster,
5424
- resultsQueryService
5312
+ ipcBroadcaster
5425
5313
  });
5426
5314
  adapters.push(slackAdapter);
5427
5315
  adapterMap.set(slackAdapter.name, slackAdapter);
@@ -5453,7 +5341,7 @@ async function startServer(options) {
5453
5341
  lifecycle,
5454
5342
  notify: notifyFn,
5455
5343
  adapterMap,
5456
- resultsQueryService
5344
+ ipcBroadcaster
5457
5345
  });
5458
5346
  adapters.push(schedulerAdapter);
5459
5347
  adapterMap.set(schedulerAdapter.name, schedulerAdapter);
@@ -5541,23 +5429,23 @@ function findMissingSkills(workDir, config) {
5541
5429
  });
5542
5430
  }
5543
5431
  function copyStartTemplates2(workDir) {
5544
- const templateDir = path19.resolve(
5432
+ const templateDir = path17.resolve(
5545
5433
  new URL("../templates", import.meta.url).pathname
5546
5434
  );
5547
5435
  for (const file of ["start.sh", "start.bat"]) {
5548
- const src = path19.join(templateDir, file);
5549
- const dest = path19.join(workDir, file);
5550
- if (fs20.existsSync(src)) {
5551
- fs20.copyFileSync(src, dest);
5436
+ const src = path17.join(templateDir, file);
5437
+ const dest = path17.join(workDir, file);
5438
+ if (fs17.existsSync(src)) {
5439
+ fs17.copyFileSync(src, dest);
5552
5440
  if (file === "start.sh") {
5553
- fs20.chmodSync(dest, 493);
5441
+ fs17.chmodSync(dest, 493);
5554
5442
  }
5555
5443
  }
5556
5444
  }
5557
5445
  }
5558
5446
  async function runCommand(directory) {
5559
- const workDir = directory ? path19.resolve(directory) : process.cwd();
5560
- fs20.mkdirSync(workDir, { recursive: true });
5447
+ const workDir = directory ? path17.resolve(directory) : process.cwd();
5448
+ fs17.mkdirSync(workDir, { recursive: true });
5561
5449
  if (!configExists(workDir)) {
5562
5450
  console.log(chalk4.blue("\n No skillpack.json found. Let's set one up.\n"));
5563
5451
  const { name, description } = await inquirer2.prompt([
@@ -5601,14 +5489,14 @@ async function runCommand(directory) {
5601
5489
  }
5602
5490
 
5603
5491
  // src/cli.ts
5604
- import fs21 from "fs";
5605
- import path20 from "path";
5492
+ import fs18 from "fs";
5493
+ import path18 from "path";
5606
5494
  import { fileURLToPath as fileURLToPath3 } from "url";
5607
5495
  var packageJson = JSON.parse(
5608
- fs21.readFileSync(new URL("../package.json", import.meta.url), "utf-8")
5496
+ fs18.readFileSync(new URL("../package.json", import.meta.url), "utf-8")
5609
5497
  );
5610
5498
  var program = new Command();
5611
- var cliFilePath = path20.resolve(fileURLToPath3(import.meta.url));
5499
+ var cliFilePath = path18.resolve(fileURLToPath3(import.meta.url));
5612
5500
  program.name("skillpack").description("Assemble, package, and run Agent Skills packs").version(packageJson.version);
5613
5501
  program.command("create [directory]").description("Create a skills pack interactively").option("--config <path-or-url>", "Initialize from a local or remote skillpack.json").action(async (directory, options) => {
5614
5502
  await createCommand(directory, options);
@@ -5629,7 +5517,7 @@ program.command("zip").description("Package the current pack as a zip file (skil
5629
5517
  function normalizeUserArgs(argv) {
5630
5518
  if (argv.length === 0) return argv;
5631
5519
  const firstArg = argv[0];
5632
- if (firstArg && path20.resolve(firstArg) === cliFilePath) {
5520
+ if (firstArg && path18.resolve(firstArg) === cliFilePath) {
5633
5521
  return argv.slice(1);
5634
5522
  }
5635
5523
  return argv;