@cremini/skillpack 1.2.10 → 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 +490 -652
  2. package/package.json +1 -2
package/dist/cli.js CHANGED
@@ -417,7 +417,7 @@ var telegram_exports = {};
417
417
  __export(telegram_exports, {
418
418
  TelegramAdapter: () => TelegramAdapter
419
419
  });
420
- import fs17 from "fs";
420
+ import fs14 from "fs";
421
421
  import TelegramBot from "node-telegram-bot-api";
422
422
  var MAX_MESSAGE_LENGTH, ACK_REACTION, TelegramAdapter;
423
423
  var init_telegram = __esm({
@@ -735,7 +735,7 @@ var init_telegram = __esm({
735
735
  async sendFileSafe(chatId, filePath, caption) {
736
736
  if (!this.bot) return;
737
737
  try {
738
- if (!fs17.existsSync(filePath)) {
738
+ if (!fs14.existsSync(filePath)) {
739
739
  console.error(`[Telegram] File not found for sending: ${filePath}`);
740
740
  return;
741
741
  }
@@ -755,8 +755,8 @@ var slack_exports = {};
755
755
  __export(slack_exports, {
756
756
  SlackAdapter: () => SlackAdapter
757
757
  });
758
- import fs18 from "fs";
759
- import path17 from "path";
758
+ import fs15 from "fs";
759
+ import path15 from "path";
760
760
  import { App, LogLevel } from "@slack/bolt";
761
761
  var INLINE_COMMANDS, SLASH_COMMANDS, MAX_MESSAGE_LENGTH2, ACK_REACTION2, PROCESSING_MESSAGE, SlackAdapter;
762
762
  var init_slack = __esm({
@@ -1391,12 +1391,12 @@ var init_slack = __esm({
1391
1391
  */
1392
1392
  async sendFileSafe(client, route, filePath, caption) {
1393
1393
  try {
1394
- if (!fs18.existsSync(filePath)) {
1394
+ if (!fs15.existsSync(filePath)) {
1395
1395
  console.error(`[Slack] File not found for sending: ${filePath}`);
1396
1396
  return;
1397
1397
  }
1398
- const filename = path17.basename(filePath);
1399
- const fileContent = fs18.readFileSync(filePath);
1398
+ const filename = path15.basename(filePath);
1399
+ const fileContent = fs15.readFileSync(filePath);
1400
1400
  await client.files.uploadV2({
1401
1401
  channel_id: route.channel,
1402
1402
  thread_ts: route.threadTs,
@@ -1444,14 +1444,19 @@ var init_scheduler = __esm({
1444
1444
  name = "scheduler";
1445
1445
  agent;
1446
1446
  rootDir = "";
1447
+ ipcBroadcaster = null;
1447
1448
  notifyFn = async () => {
1448
1449
  };
1449
1450
  jobs = /* @__PURE__ */ new Map();
1450
1451
  async start(ctx) {
1451
1452
  this.agent = ctx.agent;
1452
1453
  this.rootDir = ctx.rootDir;
1454
+ this.ipcBroadcaster = ctx.ipcBroadcaster ?? null;
1453
1455
  this.notifyFn = ctx.notify || (async () => {
1454
1456
  });
1457
+ console.log(
1458
+ `[Scheduler] IPC broadcaster ${this.ipcBroadcaster ? "attached" : "not available"} for agent events`
1459
+ );
1455
1460
  const jobConfigs = loadJobFile(this.rootDir).jobs;
1456
1461
  let scheduledCount = 0;
1457
1462
  let disabledCount = 0;
@@ -1549,8 +1554,27 @@ var init_scheduler = __esm({
1549
1554
  console.log(`[Scheduler] Running job "${jobConfig.name}"`);
1550
1555
  let fullText = "";
1551
1556
  let agentFailed = false;
1557
+ let loggedFirstTextDelta = false;
1558
+ let sawAgentStart = false;
1559
+ let sawAgentEnd = false;
1552
1560
  const pendingFiles = [];
1553
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);
1554
1578
  if (event.type === "text_delta") fullText += event.delta;
1555
1579
  if (event.type === "file_output") {
1556
1580
  pendingFiles.push({
@@ -1568,6 +1592,7 @@ var init_scheduler = __esm({
1568
1592
  onEvent,
1569
1593
  void 0
1570
1594
  );
1595
+ this.ensureTerminalAgentEvent(channelId, sawAgentStart, sawAgentEnd, onEvent);
1571
1596
  if (result.errorMessage) {
1572
1597
  fullText = `\u274C \u5B9A\u65F6\u4EFB\u52A1 "${jobConfig.name}" \u6267\u884C\u5931\u8D25\uFF1A${result.errorMessage}`;
1573
1598
  agentFailed = true;
@@ -1576,6 +1601,7 @@ var init_scheduler = __esm({
1576
1601
  if (job) job.lastError = void 0;
1577
1602
  }
1578
1603
  } catch (err) {
1604
+ this.ensureTerminalAgentEvent(channelId, sawAgentStart, sawAgentEnd, onEvent);
1579
1605
  const errorMsg = err instanceof Error ? err.message : String(err);
1580
1606
  fullText = `\u274C \u5B9A\u65F6\u4EFB\u52A1 "${jobConfig.name}" \u5F02\u5E38\uFF1A${errorMsg}`;
1581
1607
  agentFailed = true;
@@ -1612,10 +1638,19 @@ var init_scheduler = __esm({
1612
1638
  return { text: fullText, notifyFailed };
1613
1639
  }
1614
1640
  async clearJobContext(channelId) {
1641
+ console.log(`[Scheduler] Clearing context for ${channelId}`);
1615
1642
  const result = await this.agent.handleCommand("clear", channelId);
1616
1643
  if (!result.success) {
1617
1644
  throw new Error(result.message || `Failed to clear context for ${channelId}`);
1618
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" });
1619
1654
  }
1620
1655
  // -------------------------------------------------------------------------
1621
1656
  // Dynamic management API
@@ -2480,22 +2515,22 @@ async function interactiveCreate(workDir) {
2480
2515
  }
2481
2516
 
2482
2517
  // src/commands/run.ts
2483
- import path19 from "path";
2484
- import fs20 from "fs";
2518
+ import path17 from "path";
2519
+ import fs17 from "fs";
2485
2520
  import inquirer2 from "inquirer";
2486
2521
  import chalk4 from "chalk";
2487
2522
 
2488
2523
  // src/runtime/server.ts
2489
2524
  import express from "express";
2490
- import path18 from "path";
2491
- import fs19 from "fs";
2525
+ import path16 from "path";
2526
+ import fs16 from "fs";
2492
2527
  import { fileURLToPath as fileURLToPath2 } from "url";
2493
2528
  import { createServer } from "http";
2494
2529
  import { exec } from "child_process";
2495
2530
 
2496
2531
  // src/runtime/agent.ts
2497
- import path13 from "path";
2498
- import fs13 from "fs";
2532
+ import path11 from "path";
2533
+ import fs10 from "fs";
2499
2534
  import { randomUUID as randomUUID3 } from "crypto";
2500
2535
  import { fileURLToPath } from "url";
2501
2536
  import {
@@ -2726,40 +2761,238 @@ var ConfigFileAuthBackend = class {
2726
2761
  // src/runtime/agent.ts
2727
2762
  init_attachment_utils();
2728
2763
 
2729
- // src/runtime/artifacts/query-service.ts
2730
- function clampLimit(limit, fallback, max) {
2731
- if (!Number.isFinite(limit)) {
2732
- 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
+ }
2733
2777
  }
2734
- const normalized = Math.floor(limit);
2735
- return Math.max(1, Math.min(normalized, max));
2736
2778
  }
2737
- function clampOffset(offset) {
2738
- if (!Number.isFinite(offset)) {
2739
- return 0;
2779
+ function assertToolResult(value) {
2780
+ if (!isObject(value) || !Array.isArray(value.content)) {
2781
+ throw new Error("Invalid delegated custom tool result");
2740
2782
  }
2741
- return Math.max(0, Math.floor(offset));
2742
2783
  }
2743
- var ResultsQueryService = class {
2744
- constructor(resultStore) {
2745
- this.resultStore = resultStore;
2746
- }
2747
- async listRecentArtifacts(options = {}) {
2748
- return this.resultStore.listRecentArtifacts({
2749
- channelId: options.channelId,
2750
- limit: clampLimit(options.limit, 100, 500),
2751
- offset: clampOffset(options.offset)
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
+ }
2752
2840
  });
2753
2841
  }
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
+ }
2754
2869
  };
2755
2870
 
2756
- // src/runtime/artifacts/snapshot-service.ts
2757
- import { randomUUID } from "crypto";
2758
- import fs9 from "fs";
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
+ };
2877
+ }
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
+ }
2959
+ });
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
+ }
2988
+ };
2989
+
2990
+ // src/runtime/tools/send-file-tool.ts
2991
+ import fs8 from "fs";
2759
2992
  import path9 from "path";
2993
+ import { Type } from "@sinclair/typebox";
2760
2994
 
2761
2995
  // src/runtime/files/metadata.ts
2762
- import fs8 from "fs";
2763
2996
  import path8 from "path";
2764
2997
  var MIME_BY_EXT = {
2765
2998
  ".png": "image/png",
@@ -2789,419 +3022,14 @@ function isWithinDirectory(parentDir, targetPath) {
2789
3022
  const relativePath = path8.relative(path8.resolve(parentDir), path8.resolve(targetPath));
2790
3023
  return relativePath !== ".." && !relativePath.startsWith(`..${path8.sep}`) && !path8.isAbsolute(relativePath);
2791
3024
  }
2792
- function toPackRelativePath(rootDir, filePath) {
2793
- const resolvedRoot = path8.resolve(rootDir);
2794
- const resolvedFile = path8.resolve(filePath);
2795
- if (!isWithinDirectory(resolvedRoot, resolvedFile)) {
2796
- throw new Error(`Path is outside the pack root: ${resolvedFile}`);
2797
- }
2798
- return path8.relative(resolvedRoot, resolvedFile).split(path8.sep).join("/");
2799
- }
2800
- function resolvePackFile(rootDir, filePath) {
2801
- if (!path8.isAbsolute(filePath)) {
2802
- throw new Error(`filePath must be absolute: ${filePath}`);
2803
- }
2804
- const resolvedPath = path8.resolve(filePath);
2805
- if (!isWithinDirectory(rootDir, resolvedPath)) {
2806
- throw new Error(`File is outside the pack root: ${resolvedPath}`);
2807
- }
2808
- if (!fs8.existsSync(resolvedPath)) {
2809
- throw new Error(`File not found: ${resolvedPath}`);
2810
- }
2811
- const stats = fs8.statSync(resolvedPath);
2812
- if (!stats.isFile()) {
2813
- throw new Error(`Path is not a file: ${resolvedPath}`);
2814
- }
2815
- fs8.accessSync(resolvedPath, fs8.constants.R_OK);
2816
- return {
2817
- resolvedPath,
2818
- fileName: path8.basename(resolvedPath),
2819
- mimeType: detectMimeType(resolvedPath),
2820
- sizeBytes: stats.size
2821
- };
2822
- }
2823
-
2824
- // src/runtime/artifacts/snapshot-service.ts
2825
- function sanitizeFileName(fileName) {
2826
- const sanitized = fileName.replace(/[^a-zA-Z0-9._-]+/g, "-");
2827
- return sanitized || "artifact";
2828
- }
2829
- function formatSnapshotStamp(isoDate) {
2830
- const normalized = isoDate.replace(/\D+/g, "").slice(0, 14);
2831
- return normalized || String(Date.now());
2832
- }
2833
- var ArtifactSnapshotService = class {
2834
- constructor(rootDir) {
2835
- this.rootDir = rootDir;
2836
- }
2837
- createSnapshots(runId, artifacts, declaredAt) {
2838
- if (artifacts.length === 0) {
2839
- return [];
2840
- }
2841
- const artifactsRoot = path9.resolve(this.rootDir, "data", "artifacts");
2842
- const runDir = path9.join(artifactsRoot, runId);
2843
- const tempDir = path9.join(
2844
- artifactsRoot,
2845
- `.tmp-${runId}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
2846
- );
2847
- const snapshots = [];
2848
- const movedPaths = [];
2849
- const snapshotNames = artifacts.map((artifact) => [
2850
- formatSnapshotStamp(declaredAt),
2851
- randomUUID(),
2852
- sanitizeFileName(artifact.fileName)
2853
- ].join("-"));
2854
- fs9.mkdirSync(artifactsRoot, { recursive: true });
2855
- fs9.mkdirSync(tempDir, { recursive: true });
2856
- try {
2857
- snapshotNames.forEach((snapshotName, index) => {
2858
- fs9.copyFileSync(
2859
- artifacts[index].filePath,
2860
- path9.join(tempDir, snapshotName)
2861
- );
2862
- });
2863
- fs9.mkdirSync(runDir, { recursive: true });
2864
- snapshotNames.forEach((snapshotName, index) => {
2865
- const artifact = artifacts[index];
2866
- const tempSnapshotPath = path9.join(tempDir, snapshotName);
2867
- const finalSnapshotPath = path9.join(runDir, snapshotName);
2868
- fs9.renameSync(tempSnapshotPath, finalSnapshotPath);
2869
- movedPaths.push(finalSnapshotPath);
2870
- snapshots.push({
2871
- declaredAt,
2872
- originalPath: toPackRelativePath(this.rootDir, artifact.filePath),
2873
- snapshotPath: path9.join("data", "artifacts", runId, snapshotName).split(path9.sep).join("/"),
2874
- fileName: artifact.fileName,
2875
- mimeType: artifact.mimeType,
2876
- sizeBytes: artifact.sizeBytes,
2877
- title: artifact.title,
2878
- isPrimary: artifact.isPrimary
2879
- });
2880
- });
2881
- fs9.rmSync(tempDir, { recursive: true, force: true });
2882
- return snapshots;
2883
- } catch (error) {
2884
- fs9.rmSync(tempDir, { recursive: true, force: true });
2885
- movedPaths.forEach((filePath) => fs9.rmSync(filePath, { force: true }));
2886
- this.removeEmptyRunDirectory(runDir);
2887
- throw error;
2888
- }
2889
- }
2890
- removeSnapshots(snapshotPaths) {
2891
- const visitedRunDirs = /* @__PURE__ */ new Set();
2892
- for (const snapshotPath of snapshotPaths) {
2893
- const resolvedPath = path9.resolve(this.rootDir, snapshotPath);
2894
- fs9.rmSync(resolvedPath, { force: true });
2895
- visitedRunDirs.add(path9.dirname(resolvedPath));
2896
- }
2897
- visitedRunDirs.forEach((runDir) => this.removeEmptyRunDirectory(runDir));
2898
- }
2899
- removeEmptyRunDirectory(runDir) {
2900
- try {
2901
- if (!fs9.existsSync(runDir)) {
2902
- return;
2903
- }
2904
- if (fs9.readdirSync(runDir).length === 0) {
2905
- fs9.rmdirSync(runDir);
2906
- }
2907
- } catch {
2908
- }
2909
- }
2910
- };
2911
-
2912
- // src/runtime/artifacts/persistence-service.ts
2913
- var ArtifactPersistenceService = class {
2914
- constructor(snapshotService, resultStore) {
2915
- this.snapshotService = snapshotService;
2916
- this.resultStore = resultStore;
2917
- }
2918
- async saveArtifacts(input) {
2919
- const declaredAt = (/* @__PURE__ */ new Date()).toISOString();
2920
- const snapshots = this.snapshotService.createSnapshots(
2921
- input.runId,
2922
- input.artifacts,
2923
- declaredAt
2924
- );
2925
- try {
2926
- await this.resultStore.insertArtifacts({
2927
- runId: input.runId,
2928
- channelId: input.channelId,
2929
- artifacts: snapshots
2930
- });
2931
- return snapshots.length;
2932
- } catch (error) {
2933
- this.snapshotService.removeSnapshots(
2934
- snapshots.map((artifact) => artifact.snapshotPath)
2935
- );
2936
- throw error;
2937
- }
2938
- }
2939
- };
2940
-
2941
- // src/runtime/artifacts/store.ts
2942
- import fs10 from "fs";
2943
- import path10 from "path";
2944
- import { randomUUID as randomUUID2 } from "crypto";
2945
- import sqlite3 from "sqlite3";
2946
- function mapArtifactRow(row) {
2947
- return {
2948
- artifactId: row.artifact_id,
2949
- runId: row.run_id,
2950
- channelId: row.channel_id,
2951
- originalPath: row.original_path,
2952
- snapshotPath: row.snapshot_path,
2953
- fileName: row.file_name,
2954
- mimeType: row.mime_type,
2955
- sizeBytes: row.size_bytes,
2956
- title: row.title,
2957
- isPrimary: row.is_primary === 1,
2958
- declaredAt: row.declared_at
2959
- };
2960
- }
2961
- var ResultStore = class {
2962
- db = null;
2963
- ready;
2964
- constructor(rootDir) {
2965
- const dataDir = path10.resolve(rootDir, "data");
2966
- fs10.mkdirSync(dataDir, { recursive: true });
2967
- this.ready = this.initialize(path10.join(dataDir, "result-v2.db"));
2968
- }
2969
- async initialize(databasePath) {
2970
- this.db = await openDatabase(databasePath);
2971
- await this.exec("PRAGMA journal_mode = WAL");
2972
- await this.exec(`
2973
- CREATE TABLE IF NOT EXISTS artifacts (
2974
- artifact_id TEXT PRIMARY KEY,
2975
- run_id TEXT NOT NULL,
2976
- channel_id TEXT NOT NULL,
2977
- original_path TEXT NOT NULL,
2978
- snapshot_path TEXT NOT NULL,
2979
- file_name TEXT NOT NULL,
2980
- mime_type TEXT,
2981
- size_bytes INTEGER NOT NULL,
2982
- title TEXT,
2983
- is_primary INTEGER NOT NULL DEFAULT 0,
2984
- declared_at TEXT NOT NULL
2985
- );
2986
-
2987
- CREATE INDEX IF NOT EXISTS idx_artifacts_channel_declared_at
2988
- ON artifacts(channel_id, declared_at DESC);
2989
- `);
2990
- }
2991
- async insertArtifacts(input) {
2992
- await this.ready;
2993
- if (input.artifacts.length === 0) {
2994
- return;
2995
- }
2996
- const insertArtifact = `
2997
- INSERT INTO artifacts (
2998
- artifact_id,
2999
- run_id,
3000
- channel_id,
3001
- original_path,
3002
- snapshot_path,
3003
- file_name,
3004
- mime_type,
3005
- size_bytes,
3006
- title,
3007
- is_primary,
3008
- declared_at
3009
- ) VALUES (
3010
- ?,
3011
- ?,
3012
- ?,
3013
- ?,
3014
- ?,
3015
- ?,
3016
- ?,
3017
- ?,
3018
- ?,
3019
- ?,
3020
- ?
3021
- )
3022
- `;
3023
- await this.exec("BEGIN");
3024
- try {
3025
- for (const artifact of input.artifacts) {
3026
- await this.run(insertArtifact, [
3027
- randomUUID2(),
3028
- input.runId,
3029
- input.channelId,
3030
- artifact.originalPath,
3031
- artifact.snapshotPath,
3032
- artifact.fileName,
3033
- artifact.mimeType ?? null,
3034
- artifact.sizeBytes,
3035
- artifact.title ?? null,
3036
- artifact.isPrimary ? 1 : 0,
3037
- artifact.declaredAt
3038
- ]);
3039
- }
3040
- await this.exec("COMMIT");
3041
- } catch (error) {
3042
- await this.rollback();
3043
- throw error;
3044
- }
3045
- }
3046
- async listRecentArtifacts(options = {}) {
3047
- await this.ready;
3048
- const limit = options.limit ?? 100;
3049
- const offset = options.offset ?? 0;
3050
- const conditions = [];
3051
- const params = [];
3052
- if (options.channelId) {
3053
- conditions.push("channel_id = ?");
3054
- params.push(options.channelId);
3055
- }
3056
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
3057
- const rows = await this.all(`
3058
- SELECT *
3059
- FROM artifacts
3060
- ${whereClause}
3061
- ORDER BY declared_at DESC, rowid DESC
3062
- LIMIT ? OFFSET ?
3063
- `, [...params, limit, offset]);
3064
- return rows.map(mapArtifactRow);
3065
- }
3066
- getDatabase() {
3067
- if (!this.db) {
3068
- throw new Error("Result store database is not ready");
3069
- }
3070
- return this.db;
3071
- }
3072
- exec(sql) {
3073
- const db = this.getDatabase();
3074
- return new Promise((resolve, reject) => {
3075
- db.exec(sql, (error) => {
3076
- if (error) {
3077
- reject(error);
3078
- return;
3079
- }
3080
- resolve();
3081
- });
3082
- });
3083
- }
3084
- run(sql, params = []) {
3085
- const db = this.getDatabase();
3086
- return new Promise((resolve, reject) => {
3087
- db.run(sql, params, (error) => {
3088
- if (error) {
3089
- reject(error);
3090
- return;
3091
- }
3092
- resolve();
3093
- });
3094
- });
3095
- }
3096
- all(sql, params = []) {
3097
- const db = this.getDatabase();
3098
- return new Promise((resolve, reject) => {
3099
- db.all(sql, params, (error, rows) => {
3100
- if (error) {
3101
- reject(error);
3102
- return;
3103
- }
3104
- resolve(rows);
3105
- });
3106
- });
3107
- }
3108
- async rollback() {
3109
- try {
3110
- await this.exec("ROLLBACK");
3111
- } catch {
3112
- }
3113
- }
3114
- };
3115
- function openDatabase(databasePath) {
3116
- return new Promise((resolve, reject) => {
3117
- const db = new sqlite3.Database(databasePath, (error) => {
3118
- if (error) {
3119
- reject(error);
3120
- return;
3121
- }
3122
- resolve(db);
3123
- });
3124
- });
3125
- }
3126
-
3127
- // src/runtime/artifacts/save-artifacts-tool.ts
3128
- import { Type } from "@sinclair/typebox";
3129
- var ArtifactItem = Type.Object({
3130
- filePath: Type.String({
3131
- description: "Absolute path to the artifact file. The file must exist, be readable, and be inside the current pack root."
3132
- }),
3133
- title: Type.Optional(
3134
- Type.String({
3135
- description: "Optional short title shown in the dashboard."
3136
- })
3137
- ),
3138
- isPrimary: Type.Optional(
3139
- Type.Boolean({
3140
- description: "Mark this artifact as a primary output."
3141
- })
3142
- )
3143
- });
3144
- var SaveArtifactsParams = Type.Object({
3145
- artifacts: Type.Array(ArtifactItem, {
3146
- minItems: 1,
3147
- description: "The artifact files to save for this run."
3148
- })
3149
- });
3150
- function textResult(text) {
3151
- return { content: [{ type: "text", text }], details: void 0 };
3152
- }
3153
- function normalizeOptionalText(value) {
3154
- const trimmed = value?.trim();
3155
- return trimmed ? trimmed : void 0;
3156
- }
3157
- function createSaveArtifactsTool(rootDir, saveCallbackRef) {
3158
- return {
3159
- name: "save_artifacts",
3160
- label: "Save Artifacts",
3161
- description: [
3162
- "Save the final output files produced by this run.",
3163
- "Always use this for user-facing deliverables that are part of the final result.",
3164
- "Do not use this for intermediate, temporary, draft, or scratch files.",
3165
- "Each filePath must be an absolute path inside the current pack root."
3166
- ].join("\n"),
3167
- 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.",
3168
- promptGuidelines: [
3169
- "Whenever you create a final result file for the user, call `save_artifacts` before finishing the response.",
3170
- "Do not call `save_artifacts` for intermediate, temporary, draft, or scratch files."
3171
- ],
3172
- parameters: SaveArtifactsParams,
3173
- async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
3174
- const saveArtifacts = saveCallbackRef.current;
3175
- if (!saveArtifacts) {
3176
- throw new Error("Artifact saving is not available for this run.");
3177
- }
3178
- const artifacts = params.artifacts.map((artifact) => {
3179
- const metadata = resolvePackFile(rootDir, artifact.filePath);
3180
- return {
3181
- filePath: metadata.resolvedPath,
3182
- fileName: metadata.fileName,
3183
- mimeType: metadata.mimeType,
3184
- sizeBytes: metadata.sizeBytes,
3185
- title: normalizeOptionalText(artifact.title),
3186
- isPrimary: artifact.isPrimary === true
3187
- };
3188
- });
3189
- const savedCount = await saveArtifacts(artifacts);
3190
- return textResult(`Saved ${savedCount} artifact(s).`);
3191
- }
3192
- };
3193
- }
3194
3025
 
3195
3026
  // src/runtime/tools/send-file-tool.ts
3196
- import fs11 from "fs";
3197
- import path11 from "path";
3198
- import { Type as Type2 } from "@sinclair/typebox";
3199
- var SendFileParams = Type2.Object({
3200
- filePath: Type2.String({
3027
+ var SendFileParams = Type.Object({
3028
+ filePath: Type.String({
3201
3029
  description: "Absolute path to the file to send to the user. The file must exist and be readable."
3202
3030
  }),
3203
- caption: Type2.Optional(
3204
- Type2.String({
3031
+ caption: Type.Optional(
3032
+ Type.String({
3205
3033
  description: "Optional caption or description to accompany the file."
3206
3034
  })
3207
3035
  )
@@ -3215,13 +3043,13 @@ function createSendFileTool(fileOutputCallbackRef) {
3215
3043
  parameters: SendFileParams,
3216
3044
  async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
3217
3045
  const { filePath, caption } = params;
3218
- if (!fs11.existsSync(filePath)) {
3046
+ if (!fs8.existsSync(filePath)) {
3219
3047
  return {
3220
3048
  content: [{ type: "text", text: `Error: File not found: ${filePath}` }],
3221
3049
  details: void 0
3222
3050
  };
3223
3051
  }
3224
- const stats = fs11.statSync(filePath);
3052
+ const stats = fs8.statSync(filePath);
3225
3053
  if (!stats.isFile()) {
3226
3054
  return {
3227
3055
  content: [
@@ -3230,7 +3058,7 @@ function createSendFileTool(fileOutputCallbackRef) {
3230
3058
  details: void 0
3231
3059
  };
3232
3060
  }
3233
- const filename = path11.basename(filePath);
3061
+ const filename = path9.basename(filePath);
3234
3062
  const mimeType = detectMimeType(filePath);
3235
3063
  const callback = fileOutputCallbackRef.current;
3236
3064
  if (callback) {
@@ -3257,51 +3085,51 @@ function createSendFileTool(fileOutputCallbackRef) {
3257
3085
  }
3258
3086
 
3259
3087
  // src/runtime/tools/manage-schedule-tool.ts
3260
- import { Type as Type3 } from "@sinclair/typebox";
3261
- var ManageScheduleParams = Type3.Object({
3262
- action: Type3.Union(
3088
+ import { Type as Type2 } from "@sinclair/typebox";
3089
+ var ManageScheduleParams = Type2.Object({
3090
+ action: Type2.Union(
3263
3091
  [
3264
- Type3.Literal("add"),
3265
- Type3.Literal("list"),
3266
- Type3.Literal("remove"),
3267
- Type3.Literal("trigger"),
3268
- Type3.Literal("enable"),
3269
- 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")
3270
3098
  ],
3271
3099
  { description: "The action to perform." }
3272
3100
  ),
3273
- name: Type3.Optional(
3274
- Type3.String({
3101
+ name: Type2.Optional(
3102
+ Type2.String({
3275
3103
  description: "Unique name for the scheduled task. Required for add/remove/trigger/enable/disable."
3276
3104
  })
3277
3105
  ),
3278
- cron: Type3.Optional(
3279
- Type3.String({
3106
+ cron: Type2.Optional(
3107
+ Type2.String({
3280
3108
  description: "Cron expression (5 fields: minute hour day month weekday). Required for add."
3281
3109
  })
3282
3110
  ),
3283
- prompt: Type3.Optional(
3284
- Type3.String({
3111
+ prompt: Type2.Optional(
3112
+ Type2.String({
3285
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."
3286
3114
  })
3287
3115
  ),
3288
- timezone: Type3.Optional(
3289
- Type3.String({
3116
+ timezone: Type2.Optional(
3117
+ Type2.String({
3290
3118
  description: "Optional timezone for the cron schedule, e.g. 'Asia/Shanghai', 'America/New_York'."
3291
3119
  })
3292
3120
  ),
3293
- notifyAdapter: Type3.Optional(
3294
- Type3.String({
3121
+ notifyAdapter: Type2.Optional(
3122
+ Type2.String({
3295
3123
  description: "Optional target adapter for notifications. If omitted, the current chat is used when supported (Telegram, Slack, or Web)."
3296
3124
  })
3297
3125
  ),
3298
- notifyChannelId: Type3.Optional(
3299
- Type3.String({
3126
+ notifyChannelId: Type2.Optional(
3127
+ Type2.String({
3300
3128
  description: "Optional target channelId for notifications. Must be provided together with notifyAdapter when overriding the default target."
3301
3129
  })
3302
3130
  )
3303
3131
  });
3304
- function textResult2(text) {
3132
+ function textResult(text) {
3305
3133
  return { content: [{ type: "text", text }], details: void 0 };
3306
3134
  }
3307
3135
  function getDefaultNotifyTarget(adapter, channelId) {
@@ -3341,7 +3169,7 @@ function createManageScheduleTool(schedulerRef, adapter, channelId) {
3341
3169
  async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
3342
3170
  const scheduler = schedulerRef.current;
3343
3171
  if (!scheduler) {
3344
- return textResult2(
3172
+ return textResult(
3345
3173
  "Error: Scheduler is not available. The scheduled task system may not be initialized."
3346
3174
  );
3347
3175
  }
@@ -3349,24 +3177,24 @@ function createManageScheduleTool(schedulerRef, adapter, channelId) {
3349
3177
  case "list": {
3350
3178
  const jobs = scheduler.listJobs();
3351
3179
  if (jobs.length === 0) {
3352
- return textResult2("No scheduled tasks configured.");
3180
+ return textResult("No scheduled tasks configured.");
3353
3181
  }
3354
3182
  const lines = jobs.map(
3355
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})` : ""}`
3356
3184
  );
3357
- return textResult2(
3185
+ return textResult(
3358
3186
  `Scheduled tasks (${jobs.length}):
3359
3187
  ${lines.join("\n")}`
3360
3188
  );
3361
3189
  }
3362
3190
  case "add": {
3363
3191
  if (!params.name || !params.cron || !params.prompt) {
3364
- return textResult2(
3192
+ return textResult(
3365
3193
  "Error: 'name', 'cron', and 'prompt' are required for adding a task."
3366
3194
  );
3367
3195
  }
3368
3196
  if (params.notifyAdapter && !params.notifyChannelId || !params.notifyAdapter && params.notifyChannelId) {
3369
- return textResult2(
3197
+ return textResult(
3370
3198
  "Error: 'notifyAdapter' and 'notifyChannelId' must be provided together when overriding the notification target."
3371
3199
  );
3372
3200
  }
@@ -3375,7 +3203,7 @@ ${lines.join("\n")}`
3375
3203
  channelId: params.notifyChannelId
3376
3204
  } : getDefaultNotifyTarget(adapter, channelId);
3377
3205
  if (!notify) {
3378
- return textResult2(
3206
+ return textResult(
3379
3207
  "Error: No default notification target is available for this chat. Provide 'notifyAdapter' and 'notifyChannelId'."
3380
3208
  );
3381
3209
  }
@@ -3388,46 +3216,46 @@ ${lines.join("\n")}`
3388
3216
  timezone: params.timezone
3389
3217
  };
3390
3218
  const result = scheduler.addJob(jobConfig);
3391
- return textResult2(result.message);
3219
+ return textResult(result.message);
3392
3220
  }
3393
3221
  case "remove": {
3394
3222
  if (!params.name) {
3395
- return textResult2(
3223
+ return textResult(
3396
3224
  "Error: 'name' is required for removing a task."
3397
3225
  );
3398
3226
  }
3399
3227
  const result = scheduler.removeJob(params.name);
3400
- return textResult2(result.message);
3228
+ return textResult(result.message);
3401
3229
  }
3402
3230
  case "trigger": {
3403
3231
  if (!params.name) {
3404
- return textResult2(
3232
+ return textResult(
3405
3233
  "Error: 'name' is required for triggering a task."
3406
3234
  );
3407
3235
  }
3408
3236
  const result = await scheduler.triggerJob(params.name);
3409
- return textResult2(result.message);
3237
+ return textResult(result.message);
3410
3238
  }
3411
3239
  case "enable": {
3412
3240
  if (!params.name) {
3413
- return textResult2(
3241
+ return textResult(
3414
3242
  "Error: 'name' is required for enabling a task."
3415
3243
  );
3416
3244
  }
3417
3245
  const result = scheduler.setEnabled(params.name, true);
3418
- return textResult2(result.message);
3246
+ return textResult(result.message);
3419
3247
  }
3420
3248
  case "disable": {
3421
3249
  if (!params.name) {
3422
- return textResult2(
3250
+ return textResult(
3423
3251
  "Error: 'name' is required for disabling a task."
3424
3252
  );
3425
3253
  }
3426
3254
  const result = scheduler.setEnabled(params.name, false);
3427
- return textResult2(result.message);
3255
+ return textResult(result.message);
3428
3256
  }
3429
3257
  default:
3430
- return textResult2(
3258
+ return textResult(
3431
3259
  `Error: Unknown action '${params.action}'. Use: add, list, remove, trigger, enable, disable.`
3432
3260
  );
3433
3261
  }
@@ -3437,8 +3265,8 @@ ${lines.join("\n")}`
3437
3265
 
3438
3266
  // src/runtime/commands/help-command.ts
3439
3267
  init_commands();
3440
- import fs12 from "fs";
3441
- import path12 from "path";
3268
+ import fs9 from "fs";
3269
+ import path10 from "path";
3442
3270
  function buildHelpMessage(rootDir) {
3443
3271
  const sections = [];
3444
3272
  const commands = getVisibleCommands();
@@ -3448,7 +3276,7 @@ function buildHelpMessage(rootDir) {
3448
3276
  sections.push(`\u{1F4CB} **Available Commands**
3449
3277
 
3450
3278
  ${commandLines.join("\n")}`);
3451
- const configPath = path12.resolve(rootDir, "skillpack.json");
3279
+ const configPath = path10.resolve(rootDir, "skillpack.json");
3452
3280
  const skills = readInstalledSkills(configPath);
3453
3281
  if (skills.length > 0) {
3454
3282
  const skillLines = skills.map(
@@ -3484,11 +3312,11 @@ function handleHelpCommand(rootDir) {
3484
3312
  };
3485
3313
  }
3486
3314
  function readInstalledSkills(configPath) {
3487
- if (!fs12.existsSync(configPath)) {
3315
+ if (!fs9.existsSync(configPath)) {
3488
3316
  return [];
3489
3317
  }
3490
3318
  try {
3491
- const raw = fs12.readFileSync(configPath, "utf-8");
3319
+ const raw = fs9.readFileSync(configPath, "utf-8");
3492
3320
  const config = JSON.parse(raw);
3493
3321
  return Array.isArray(config.skills) ? config.skills : [];
3494
3322
  } catch {
@@ -3508,24 +3336,24 @@ var BUILTIN_SKILL_CREATOR_TEMPLATE_DIR = fileURLToPath(
3508
3336
  var PACK_AGENTS_FILE = "AGENTS.md";
3509
3337
  var PACK_SOUL_FILE = "SOUL.md";
3510
3338
  function materializeBuiltinSkillCreator(rootDir, skillsPath) {
3511
- if (!fs13.existsSync(BUILTIN_SKILL_CREATOR_TEMPLATE_DIR)) {
3339
+ if (!fs10.existsSync(BUILTIN_SKILL_CREATOR_TEMPLATE_DIR)) {
3512
3340
  log(
3513
3341
  `[PackAgent] Built-in skill-creator template missing: ${BUILTIN_SKILL_CREATOR_TEMPLATE_DIR}`
3514
3342
  );
3515
3343
  return null;
3516
3344
  }
3517
- const packConfigPath = path13.resolve(rootDir, "skillpack.json");
3518
- const skillDir = path13.resolve(skillsPath, BUILTIN_SKILL_CREATOR_NAME);
3519
- 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");
3520
3348
  const renderTemplate = (content) => content.replaceAll("{{SKILLS_PATH}}", skillsPath).replaceAll("{{PACK_CONFIG_PATH}}", packConfigPath);
3521
3349
  const copyDir = (srcDir, destDir) => {
3522
- fs13.mkdirSync(destDir, { recursive: true });
3523
- 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 })) {
3524
3352
  if (entry.name === ".DS_Store") {
3525
3353
  continue;
3526
3354
  }
3527
- const srcPath = path13.join(srcDir, entry.name);
3528
- const destPath = path13.join(destDir, entry.name);
3355
+ const srcPath = path11.join(srcDir, entry.name);
3356
+ const destPath = path11.join(destDir, entry.name);
3529
3357
  if (entry.isDirectory()) {
3530
3358
  copyDir(srcPath, destPath);
3531
3359
  continue;
@@ -3534,17 +3362,17 @@ function materializeBuiltinSkillCreator(rootDir, skillsPath) {
3534
3362
  continue;
3535
3363
  }
3536
3364
  if (entry.name.endsWith(".md") || entry.name.endsWith(".py")) {
3537
- const content = fs13.readFileSync(srcPath, "utf-8");
3538
- fs13.writeFileSync(destPath, renderTemplate(content), "utf-8");
3365
+ const content = fs10.readFileSync(srcPath, "utf-8");
3366
+ fs10.writeFileSync(destPath, renderTemplate(content), "utf-8");
3539
3367
  continue;
3540
3368
  }
3541
- fs13.copyFileSync(srcPath, destPath);
3369
+ fs10.copyFileSync(srcPath, destPath);
3542
3370
  }
3543
3371
  };
3544
- if (!fs13.existsSync(skillDir)) {
3372
+ if (!fs10.existsSync(skillDir)) {
3545
3373
  copyDir(BUILTIN_SKILL_CREATOR_TEMPLATE_DIR, skillDir);
3546
3374
  }
3547
- if (!fs13.existsSync(skillPath)) {
3375
+ if (!fs10.existsSync(skillPath)) {
3548
3376
  log(
3549
3377
  `[PackAgent] Materialized built-in skill-creator but SKILL.md is missing: ${skillPath}`
3550
3378
  );
@@ -3572,11 +3400,11 @@ function overrideBuiltinSkillCreator(base, materializedSkill) {
3572
3400
  };
3573
3401
  }
3574
3402
  function readOptionalPackPromptFile(filePath) {
3575
- if (!fs13.existsSync(filePath)) {
3403
+ if (!fs10.existsSync(filePath)) {
3576
3404
  return void 0;
3577
3405
  }
3578
3406
  try {
3579
- const content = fs13.readFileSync(filePath, "utf-8").trim();
3407
+ const content = fs10.readFileSync(filePath, "utf-8").trim();
3580
3408
  return content.length > 0 ? content : void 0;
3581
3409
  } catch (error) {
3582
3410
  console.warn(`[PackAgent] Warning: Could not read ${filePath}:`, error);
@@ -3584,8 +3412,8 @@ function readOptionalPackPromptFile(filePath) {
3584
3412
  }
3585
3413
  }
3586
3414
  function buildPackPromptBlock(rootDir) {
3587
- const agentsPath = path13.resolve(rootDir, PACK_AGENTS_FILE);
3588
- 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);
3589
3417
  const agentsContent = readOptionalPackPromptFile(agentsPath);
3590
3418
  const soulContent = readOptionalPackPromptFile(soulPath);
3591
3419
  if (!agentsContent && !soulContent) {
@@ -3644,13 +3472,15 @@ var PackAgent = class {
3644
3472
  options;
3645
3473
  channels = /* @__PURE__ */ new Map();
3646
3474
  pendingSessionCreations = /* @__PURE__ */ new Map();
3647
- schedulerRef = { current: null };
3475
+ schedulerRef = {
3476
+ current: null
3477
+ };
3648
3478
  authStorage;
3649
- artifactPersistenceService;
3479
+ delegatedCustomToolClient = new DelegatedCustomToolClient();
3480
+ hostIpcClient = new HostIpcClient();
3650
3481
  constructor(options) {
3651
3482
  this.options = options;
3652
- this.artifactPersistenceService = options.artifactPersistenceService;
3653
- const configPath = path13.resolve(options.rootDir, "data", "config.json");
3483
+ const configPath = path11.resolve(options.rootDir, "data", "config.json");
3654
3484
  const backend = new ConfigFileAuthBackend(configPath);
3655
3485
  this.authStorage = AuthStorage.fromStorage(backend);
3656
3486
  const providerMeta = SUPPORTED_PROVIDERS[options.provider];
@@ -3677,13 +3507,20 @@ var PackAgent = class {
3677
3507
  setScheduler(scheduler) {
3678
3508
  this.schedulerRef.current = scheduler;
3679
3509
  }
3680
- createCustomTools(adapter, channelId, fileOutputCallbackRef, finalArtifactsSaveCallbackRef) {
3510
+ async createCustomTools(adapter, channelId, fileOutputCallbackRef, delegatedToolRunContextRef) {
3511
+ const delegatedDefinitions = await this.delegatedCustomToolClient.listDefinitions();
3681
3512
  const tools = [
3682
3513
  createSendFileTool(fileOutputCallbackRef),
3683
- createSaveArtifactsTool(this.options.rootDir, finalArtifactsSaveCallbackRef)
3514
+ ...createDelegatedCustomTools(
3515
+ delegatedDefinitions,
3516
+ this.delegatedCustomToolClient,
3517
+ delegatedToolRunContextRef
3518
+ )
3684
3519
  ];
3685
3520
  if (adapter !== "scheduler") {
3686
- tools.push(createManageScheduleTool(this.schedulerRef, adapter, channelId));
3521
+ tools.push(
3522
+ createManageScheduleTool(this.schedulerRef, adapter, channelId)
3523
+ );
3687
3524
  }
3688
3525
  return tools;
3689
3526
  }
@@ -3701,7 +3538,9 @@ var PackAgent = class {
3701
3538
  const modelRegistry = new ModelRegistry(authStorage);
3702
3539
  if (baseUrl && modelId) {
3703
3540
  const apiProtocol = this.options.apiProtocol ?? "openai-completions";
3704
- 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
+ );
3705
3544
  modelRegistry.registerProvider(provider, {
3706
3545
  baseUrl,
3707
3546
  apiKey: this.options.apiKey,
@@ -3722,26 +3561,23 @@ var PackAgent = class {
3722
3561
  const resolvedModel = modelRegistry.find(provider, modelId);
3723
3562
  const model = resolvedModel && baseUrl && !this.options.apiProtocol ? { ...resolvedModel, baseUrl } : resolvedModel;
3724
3563
  if (resolvedModel && baseUrl) {
3725
- log(`[PackAgent] Resolved ${provider}/${modelId} api=${resolvedModel.api} baseUrl=${baseUrl}`);
3564
+ log(
3565
+ `[PackAgent] Resolved ${provider}/${modelId} api=${resolvedModel.api} baseUrl=${baseUrl}`
3566
+ );
3726
3567
  }
3727
- const sessionDir = path13.resolve(
3728
- rootDir,
3729
- "data",
3730
- "sessions",
3731
- channelId
3732
- );
3733
- fs13.mkdirSync(sessionDir, { recursive: true });
3568
+ const sessionDir = path11.resolve(rootDir, "data", "sessions", channelId);
3569
+ fs10.mkdirSync(sessionDir, { recursive: true });
3734
3570
  const sessionManager = SessionManager.continueRecent(rootDir, sessionDir);
3735
3571
  log(`[PackAgent] Session dir: ${sessionDir}`);
3736
- const workspaceDir = path13.resolve(
3572
+ const workspaceDir = path11.resolve(
3737
3573
  rootDir,
3738
3574
  "data",
3739
3575
  "workspaces",
3740
3576
  channelId
3741
3577
  );
3742
- fs13.mkdirSync(workspaceDir, { recursive: true });
3578
+ fs10.mkdirSync(workspaceDir, { recursive: true });
3743
3579
  log(`[PackAgent] Workspace dir: ${workspaceDir}`);
3744
- const skillsPath = path13.resolve(rootDir, "skills");
3580
+ const skillsPath = path11.resolve(rootDir, "skills");
3745
3581
  log(`[PackAgent] Loading skills from: ${skillsPath}`);
3746
3582
  const materializedSkillCreator = materializeBuiltinSkillCreator(
3747
3583
  rootDir,
@@ -3754,14 +3590,22 @@ var PackAgent = class {
3754
3590
  }
3755
3591
  const packPromptFiles = buildPackPromptBlock(rootDir);
3756
3592
  if (packPromptFiles.agentsContent) {
3757
- log(`[PackAgent] Loaded pack policy from: ${packPromptFiles.agentsPath}`);
3593
+ log(
3594
+ `[PackAgent] Loaded pack policy from: ${packPromptFiles.agentsPath}`
3595
+ );
3758
3596
  } else {
3759
- log(`[PackAgent] No pack policy file found at: ${packPromptFiles.agentsPath}`);
3597
+ log(
3598
+ `[PackAgent] No pack policy file found at: ${packPromptFiles.agentsPath}`
3599
+ );
3760
3600
  }
3761
3601
  if (packPromptFiles.soulContent) {
3762
- log(`[PackAgent] Loaded pack persona from: ${packPromptFiles.soulPath}`);
3602
+ log(
3603
+ `[PackAgent] Loaded pack persona from: ${packPromptFiles.soulPath}`
3604
+ );
3763
3605
  } else {
3764
- log(`[PackAgent] No pack persona file found at: ${packPromptFiles.soulPath}`);
3606
+ log(
3607
+ `[PackAgent] No pack persona file found at: ${packPromptFiles.soulPath}`
3608
+ );
3765
3609
  }
3766
3610
  log(
3767
3611
  `[PackAgent] Pack prompt injection: ${packPromptFiles.promptBlock ? "enabled" : "disabled"}`
@@ -3779,14 +3623,14 @@ var PackAgent = class {
3779
3623
  const fileOutputCallbackRef = {
3780
3624
  current: null
3781
3625
  };
3782
- const finalArtifactsSaveCallbackRef = {
3626
+ const delegatedToolRunContextRef = {
3783
3627
  current: null
3784
3628
  };
3785
- const customTools = this.createCustomTools(
3629
+ const customTools = await this.createCustomTools(
3786
3630
  adapter,
3787
3631
  channelId,
3788
3632
  fileOutputCallbackRef,
3789
- finalArtifactsSaveCallbackRef
3633
+ delegatedToolRunContextRef
3790
3634
  );
3791
3635
  const { session } = await createAgentSession({
3792
3636
  cwd: workspaceDir,
@@ -3803,7 +3647,7 @@ var PackAgent = class {
3803
3647
  running: false,
3804
3648
  pending: Promise.resolve(),
3805
3649
  fileOutputCallbackRef,
3806
- finalArtifactsSaveCallbackRef
3650
+ delegatedToolRunContextRef
3807
3651
  };
3808
3652
  this.channels.set(channelId, channelSession);
3809
3653
  return channelSession;
@@ -3826,12 +3670,10 @@ var PackAgent = class {
3826
3670
  cs.fileOutputCallbackRef.current = (event) => {
3827
3671
  onEvent(event);
3828
3672
  };
3829
- cs.finalArtifactsSaveCallbackRef.current = (artifacts) => {
3830
- return this.artifactPersistenceService.saveArtifacts({
3831
- runId,
3832
- channelId,
3833
- artifacts
3834
- });
3673
+ cs.delegatedToolRunContextRef.current = {
3674
+ runId,
3675
+ channelId,
3676
+ adapter
3835
3677
  };
3836
3678
  unsubscribe = cs.session.subscribe((event) => {
3837
3679
  switch (event.type) {
@@ -3847,7 +3689,10 @@ var PackAgent = class {
3847
3689
  if (event.message?.role === "user") {
3848
3690
  log(JSON.stringify(event.message.content, null, 2));
3849
3691
  }
3850
- onEvent({ type: "message_start", role: event.message?.role ?? "" });
3692
+ onEvent({
3693
+ type: "message_start",
3694
+ role: event.message?.role ?? ""
3695
+ });
3851
3696
  break;
3852
3697
  case "message_update":
3853
3698
  if (event.assistantMessageEvent?.type === "text_delta") {
@@ -3914,18 +3759,26 @@ var PackAgent = class {
3914
3759
  let promptText = text;
3915
3760
  const promptOptions = {};
3916
3761
  if (attachments && attachments.length > 0) {
3917
- const imageAttachments = attachments.filter((a) => isImageMime(a.mimeType));
3918
- 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
+ );
3919
3768
  if (imageAttachments.length > 0) {
3920
3769
  promptOptions.images = attachmentsToImageContent(imageAttachments);
3921
- log(`[PackAgent] Passing ${imageAttachments.length} image(s) to LLM`);
3770
+ log(
3771
+ `[PackAgent] Passing ${imageAttachments.length} image(s) to LLM`
3772
+ );
3922
3773
  }
3923
3774
  if (nonImageAttachments.length > 0) {
3924
3775
  const attachmentPrompt = formatAttachmentsPrompt(nonImageAttachments);
3925
3776
  promptText = `${attachmentPrompt}
3926
3777
 
3927
3778
  ${text}`;
3928
- 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
+ );
3929
3782
  }
3930
3783
  }
3931
3784
  await cs.session.prompt(promptText, promptOptions);
@@ -3948,12 +3801,15 @@ ${text}`;
3948
3801
  } finally {
3949
3802
  cs.running = false;
3950
3803
  cs.fileOutputCallbackRef.current = null;
3951
- cs.finalArtifactsSaveCallbackRef.current = null;
3804
+ cs.delegatedToolRunContextRef.current = null;
3952
3805
  unsubscribe();
3953
3806
  }
3954
3807
  };
3955
3808
  const resultPromise = cs.pending.catch(() => void 0).then(run);
3956
- cs.pending = resultPromise.then(() => void 0, () => void 0);
3809
+ cs.pending = resultPromise.then(
3810
+ () => void 0,
3811
+ () => void 0
3812
+ );
3957
3813
  return resultPromise;
3958
3814
  }
3959
3815
  async handleCommand(command, channelId) {
@@ -3968,11 +3824,12 @@ ${text}`;
3968
3824
  this.channels.delete(channelId);
3969
3825
  }
3970
3826
  const { rootDir } = this.options;
3971
- const sessionDir = path13.resolve(rootDir, "data", "sessions", channelId);
3972
- if (fs13.existsSync(sessionDir)) {
3973
- 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 });
3974
3830
  log(`[PackAgent] Cleared session dir: ${sessionDir}`);
3975
3831
  }
3832
+ await this.hostIpcClient.notifyChannelSessionCleared({ channelId });
3976
3833
  return {
3977
3834
  success: true,
3978
3835
  message: command === "new" ? "New session started." : "Session cleared."
@@ -4021,14 +3878,14 @@ ${text}`;
4021
3878
  };
4022
3879
 
4023
3880
  // src/runtime/adapters/web.ts
4024
- import fs15 from "fs";
4025
- import path15 from "path";
3881
+ import fs12 from "fs";
3882
+ import path13 from "path";
4026
3883
  import { WebSocketServer } from "ws";
4027
3884
  init_commands();
4028
3885
 
4029
3886
  // src/runtime/services/conversation.ts
4030
- import fs14 from "fs";
4031
- import path14 from "path";
3887
+ import fs11 from "fs";
3888
+ import path12 from "path";
4032
3889
  import {
4033
3890
  parseSessionEntries
4034
3891
  } from "@mariozechner/pi-coding-agent";
@@ -4046,17 +3903,17 @@ var ConversationService = class {
4046
3903
  includeLegacyWeb = true,
4047
3904
  allowedPlatforms
4048
3905
  } = options;
4049
- const sessionsDir = path14.resolve(this.rootDir, "data", "sessions");
3906
+ const sessionsDir = path12.resolve(this.rootDir, "data", "sessions");
4050
3907
  const channelIds = new Set(activeChannels);
4051
3908
  const allowedPlatformSet = allowedPlatforms ? new Set(allowedPlatforms) : null;
4052
3909
  if (includeDefaultWeb) {
4053
3910
  channelIds.add(DEFAULT_WEB_CHANNEL_ID);
4054
3911
  }
4055
- if (fs14.existsSync(sessionsDir)) {
4056
- for (const entry of fs14.readdirSync(sessionsDir)) {
4057
- 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);
4058
3915
  try {
4059
- if (!fs14.statSync(channelDir).isDirectory()) {
3916
+ if (!fs11.statSync(channelDir).isDirectory()) {
4060
3917
  continue;
4061
3918
  }
4062
3919
  const platform = this.detectPlatform(entry);
@@ -4080,7 +3937,7 @@ var ConversationService = class {
4080
3937
  if (!includeLegacyWeb && this.isLegacyWebConversation(channelId)) {
4081
3938
  continue;
4082
3939
  }
4083
- const channelDir = path14.join(sessionsDir, channelId);
3940
+ const channelDir = path12.join(sessionsDir, channelId);
4084
3941
  const sessionFile = this.findLatestSessionFile(channelDir);
4085
3942
  let messageCount = 0;
4086
3943
  let lastMessageAt = "";
@@ -4124,7 +3981,7 @@ var ConversationService = class {
4124
3981
  * Load latest messages for a channel in a simplified format.
4125
3982
  */
4126
3983
  getMessages(channelId, limit = 100) {
4127
- const channelDir = path14.resolve(
3984
+ const channelDir = path12.resolve(
4128
3985
  this.rootDir,
4129
3986
  "data",
4130
3987
  "sessions",
@@ -4213,20 +4070,20 @@ var ConversationService = class {
4213
4070
  return messages.slice(-safeLimit);
4214
4071
  }
4215
4072
  findLatestSessionFile(channelDir) {
4216
- if (!fs14.existsSync(channelDir)) return null;
4073
+ if (!fs11.existsSync(channelDir)) return null;
4217
4074
  let stats;
4218
4075
  try {
4219
- stats = fs14.statSync(channelDir);
4076
+ stats = fs11.statSync(channelDir);
4220
4077
  } catch {
4221
4078
  return null;
4222
4079
  }
4223
4080
  if (!stats.isDirectory()) return null;
4224
- const files = fs14.readdirSync(channelDir).filter((file) => file.endsWith(".jsonl")).sort((a, b) => b.localeCompare(a));
4225
- 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;
4226
4083
  }
4227
4084
  loadEntries(filePath) {
4228
4085
  try {
4229
- const content = fs14.readFileSync(filePath, "utf-8");
4086
+ const content = fs11.readFileSync(filePath, "utf-8");
4230
4087
  const fileEntries = parseSessionEntries(content);
4231
4088
  return fileEntries.filter(
4232
4089
  (entry) => entry.type !== "session"
@@ -4422,7 +4279,7 @@ var ConversationService = class {
4422
4279
 
4423
4280
  // src/runtime/adapters/web.ts
4424
4281
  function getPackConfig(rootDir) {
4425
- const raw = fs15.readFileSync(path15.join(rootDir, "skillpack.json"), "utf-8");
4282
+ const raw = fs12.readFileSync(path13.join(rootDir, "skillpack.json"), "utf-8");
4426
4283
  return JSON.parse(raw);
4427
4284
  }
4428
4285
  function parseCommand(text) {
@@ -4452,7 +4309,7 @@ function parsePositiveInt(value, fallback) {
4452
4309
  return Math.floor(parsed);
4453
4310
  }
4454
4311
  function resolveDownloadFilePath(rootDir, filePath) {
4455
- 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);
4456
4313
  return isWithinDirectory(rootDir, resolvedPath) ? resolvedPath : null;
4457
4314
  }
4458
4315
  var WebAdapter = class {
@@ -4467,7 +4324,6 @@ var WebAdapter = class {
4467
4324
  this.agent = agent;
4468
4325
  this.ipcBroadcaster = ctx.ipcBroadcaster ?? null;
4469
4326
  this.conversationService = new ConversationService(rootDir);
4470
- const resultsQueryService = ctx.resultsQueryService ?? null;
4471
4327
  const currentConf = configManager.getConfig();
4472
4328
  let apiKey = currentConf.apiKey || "";
4473
4329
  let currentProvider = currentConf.provider || "openai";
@@ -4611,17 +4467,6 @@ var WebAdapter = class {
4611
4467
  )
4612
4468
  );
4613
4469
  });
4614
- app.get("/api/results/artifacts", async (req, res) => {
4615
- if (!resultsQueryService) {
4616
- res.status(503).json({ error: "Results query service is not available" });
4617
- return;
4618
- }
4619
- res.json(await resultsQueryService.listRecentArtifacts({
4620
- channelId: typeof req.query.channelId === "string" ? req.query.channelId : void 0,
4621
- limit: parsePositiveInt(req.query.limit, 100),
4622
- offset: parsePositiveInt(req.query.offset, 0)
4623
- }));
4624
- });
4625
4470
  app.get("/api/files", (req, res) => {
4626
4471
  const filePath = req.query.path;
4627
4472
  if (!filePath) {
@@ -4633,17 +4478,17 @@ var WebAdapter = class {
4633
4478
  res.status(403).json({ error: "Access denied" });
4634
4479
  return;
4635
4480
  }
4636
- if (!fs15.existsSync(resolvedPath)) {
4481
+ if (!fs12.existsSync(resolvedPath)) {
4637
4482
  res.status(404).json({ error: "File not found" });
4638
4483
  return;
4639
4484
  }
4640
- const filename = path15.basename(resolvedPath);
4485
+ const filename = path13.basename(resolvedPath);
4641
4486
  res.setHeader("Content-Type", "application/octet-stream");
4642
4487
  res.setHeader(
4643
4488
  "Content-Disposition",
4644
4489
  `attachment; filename="${filename}"`
4645
4490
  );
4646
- fs15.createReadStream(resolvedPath).pipe(res);
4491
+ fs12.createReadStream(resolvedPath).pipe(res);
4647
4492
  });
4648
4493
  const getScheduler = () => {
4649
4494
  const schedulerAdapter = ctx.adapterMap?.get("scheduler");
@@ -4828,13 +4673,28 @@ var WebAdapter = class {
4828
4673
 
4829
4674
  // src/runtime/adapters/ipc.ts
4830
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
+ ]);
4831
4692
  var IpcAdapter = class {
4832
4693
  name = "ipc";
4833
4694
  agent = null;
4834
4695
  rootDir = "";
4835
4696
  adapterMap = null;
4836
4697
  conversationService = null;
4837
- resultsQueryService = null;
4838
4698
  createdChannels = /* @__PURE__ */ new Set();
4839
4699
  messageListener;
4840
4700
  started = false;
@@ -4846,7 +4706,6 @@ var IpcAdapter = class {
4846
4706
  this.rootDir = ctx.rootDir;
4847
4707
  this.adapterMap = ctx.adapterMap ?? null;
4848
4708
  this.conversationService = new ConversationService(ctx.rootDir);
4849
- this.resultsQueryService = ctx.resultsQueryService ?? null;
4850
4709
  this.messageListener = (message) => {
4851
4710
  if (!this.isIpcRequest(message)) return;
4852
4711
  void this.handleRequest(message);
@@ -4891,7 +4750,7 @@ var IpcAdapter = class {
4891
4750
  isIpcRequest(message) {
4892
4751
  if (!message || typeof message !== "object") return false;
4893
4752
  const maybe = message;
4894
- 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);
4895
4754
  }
4896
4755
  async handleRequest(request) {
4897
4756
  if (!this.agent || !this.conversationService) {
@@ -4930,18 +4789,6 @@ var IpcAdapter = class {
4930
4789
  this.reply(request.id, messages);
4931
4790
  return;
4932
4791
  }
4933
- case "get_recent_artifacts": {
4934
- if (!this.resultsQueryService) {
4935
- this.replyError(request.id, "Results query service is not available");
4936
- return;
4937
- }
4938
- this.reply(request.id, await this.resultsQueryService.listRecentArtifacts({
4939
- channelId: request.channelId,
4940
- limit: request.limit,
4941
- offset: request.offset
4942
- }));
4943
- return;
4944
- }
4945
4792
  case "send_message": {
4946
4793
  if (!request.channelId || typeof request.channelId !== "string") {
4947
4794
  this.replyError(request.id, "channelId is required");
@@ -5185,28 +5032,28 @@ var Lifecycle = class {
5185
5032
 
5186
5033
  // src/runtime/registry.ts
5187
5034
  import crypto from "crypto";
5188
- import fs16 from "fs";
5035
+ import fs13 from "fs";
5189
5036
  import os from "os";
5190
- import path16 from "path";
5191
- var SKILLPACK_HOME = path16.join(os.homedir(), ".skillpack");
5192
- var LEGACY_REGISTRY_FILE = path16.join(SKILLPACK_HOME, "registry.json");
5193
- 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");
5194
5041
  var migrationChecked = false;
5195
5042
  function ensureHomeDir() {
5196
- if (!fs16.existsSync(SKILLPACK_HOME)) {
5197
- fs16.mkdirSync(SKILLPACK_HOME, { recursive: true });
5043
+ if (!fs13.existsSync(SKILLPACK_HOME)) {
5044
+ fs13.mkdirSync(SKILLPACK_HOME, { recursive: true });
5198
5045
  }
5199
5046
  }
5200
5047
  function ensureRegistryDir() {
5201
5048
  ensureHomeDir();
5202
- if (!fs16.existsSync(REGISTRY_DIR)) {
5203
- fs16.mkdirSync(REGISTRY_DIR, { recursive: true });
5049
+ if (!fs13.existsSync(REGISTRY_DIR)) {
5050
+ fs13.mkdirSync(REGISTRY_DIR, { recursive: true });
5204
5051
  }
5205
5052
  }
5206
5053
  function canonicalizeDir(dir) {
5207
- const resolved = path16.resolve(dir);
5054
+ const resolved = path14.resolve(dir);
5208
5055
  try {
5209
- return fs16.realpathSync(resolved);
5056
+ return fs13.realpathSync(resolved);
5210
5057
  } catch {
5211
5058
  return resolved;
5212
5059
  }
@@ -5215,7 +5062,7 @@ function hashDir(dir) {
5215
5062
  return crypto.createHash("md5").update(canonicalizeDir(dir)).digest("hex");
5216
5063
  }
5217
5064
  function getEntryPathForCanonicalDir(dir) {
5218
- return path16.join(REGISTRY_DIR, `${hashDir(dir)}.json`);
5065
+ return path14.join(REGISTRY_DIR, `${hashDir(dir)}.json`);
5219
5066
  }
5220
5067
  function getEntryPath(dir) {
5221
5068
  ensureRegistryReady();
@@ -5223,11 +5070,11 @@ function getEntryPath(dir) {
5223
5070
  }
5224
5071
  function listEntryFiles() {
5225
5072
  ensureRegistryReady();
5226
- 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));
5227
5074
  }
5228
5075
  function readEntryFile(filePath) {
5229
5076
  try {
5230
- const raw = fs16.readFileSync(filePath, "utf-8");
5077
+ const raw = fs13.readFileSync(filePath, "utf-8");
5231
5078
  const data = JSON.parse(raw);
5232
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") {
5233
5080
  return null;
@@ -5260,8 +5107,8 @@ function writeEntryFile(entry) {
5260
5107
  };
5261
5108
  const entryPath = getEntryPathForCanonicalDir(normalized.dir);
5262
5109
  const tmpPath = createTmpPath(entryPath);
5263
- fs16.writeFileSync(tmpPath, JSON.stringify(normalized, null, 2), "utf-8");
5264
- fs16.renameSync(tmpPath, entryPath);
5110
+ fs13.writeFileSync(tmpPath, JSON.stringify(normalized, null, 2), "utf-8");
5111
+ fs13.renameSync(tmpPath, entryPath);
5265
5112
  }
5266
5113
  function migrateLegacyRegistryIfNeeded() {
5267
5114
  if (migrationChecked) {
@@ -5269,14 +5116,14 @@ function migrateLegacyRegistryIfNeeded() {
5269
5116
  }
5270
5117
  migrationChecked = true;
5271
5118
  ensureRegistryDir();
5272
- if (!fs16.existsSync(LEGACY_REGISTRY_FILE)) {
5119
+ if (!fs13.existsSync(LEGACY_REGISTRY_FILE)) {
5273
5120
  return;
5274
5121
  }
5275
5122
  if (listEntryFiles().length > 0) {
5276
5123
  return;
5277
5124
  }
5278
5125
  try {
5279
- const raw = fs16.readFileSync(LEGACY_REGISTRY_FILE, "utf-8");
5126
+ const raw = fs13.readFileSync(LEGACY_REGISTRY_FILE, "utf-8");
5280
5127
  const data = JSON.parse(raw);
5281
5128
  const packs = Array.isArray(data?.packs) ? data.packs : [];
5282
5129
  for (const pack of packs) {
@@ -5289,7 +5136,7 @@ function migrateLegacyRegistryIfNeeded() {
5289
5136
  } catch {
5290
5137
  }
5291
5138
  }
5292
- fs16.renameSync(LEGACY_REGISTRY_FILE, `${LEGACY_REGISTRY_FILE}.legacy`);
5139
+ fs13.renameSync(LEGACY_REGISTRY_FILE, `${LEGACY_REGISTRY_FILE}.legacy`);
5293
5140
  } catch (err) {
5294
5141
  console.warn(" [Registry] Failed to migrate legacy registry.json:", err);
5295
5142
  }
@@ -5342,7 +5189,7 @@ function deregister(dir, pid) {
5342
5189
  }
5343
5190
 
5344
5191
  // src/runtime/server.ts
5345
- var __dirname = path18.dirname(fileURLToPath2(import.meta.url));
5192
+ var __dirname = path16.dirname(fileURLToPath2(import.meta.url));
5346
5193
  async function startServer(options) {
5347
5194
  const {
5348
5195
  rootDir,
@@ -5358,8 +5205,8 @@ async function startServer(options) {
5358
5205
  const baseUrl = dataConfig.baseUrl?.trim() || void 0;
5359
5206
  const modelId = dataConfig.modelId?.trim() || (SUPPORTED_PROVIDERS[provider]?.defaultModelId ?? SUPPORTED_PROVIDERS.openai.defaultModelId);
5360
5207
  const apiProtocol = dataConfig.apiProtocol;
5361
- const packageRoot = path18.resolve(__dirname, "..");
5362
- 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");
5363
5210
  const app = express();
5364
5211
  app.use(express.json());
5365
5212
  app.use(express.static(webDir));
@@ -5377,13 +5224,6 @@ async function startServer(options) {
5377
5224
  });
5378
5225
  });
5379
5226
  const lifecycle = new Lifecycle(server);
5380
- const resultStore = new ResultStore(rootDir);
5381
- const artifactSnapshotService = new ArtifactSnapshotService(rootDir);
5382
- const artifactPersistenceService = new ArtifactPersistenceService(
5383
- artifactSnapshotService,
5384
- resultStore
5385
- );
5386
- const resultsQueryService = new ResultsQueryService(resultStore);
5387
5227
  const agent = new PackAgent({
5388
5228
  apiKey,
5389
5229
  rootDir,
@@ -5391,14 +5231,16 @@ async function startServer(options) {
5391
5231
  modelId,
5392
5232
  baseUrl,
5393
5233
  apiProtocol,
5394
- lifecycleHandler: lifecycle,
5395
- artifactPersistenceService
5234
+ lifecycleHandler: lifecycle
5396
5235
  });
5397
5236
  const adapters = [];
5398
5237
  const adapterMap = /* @__PURE__ */ new Map();
5399
5238
  const hasIpcChannel = typeof process.send === "function";
5400
5239
  const ipcAdapter = new IpcAdapter();
5401
5240
  const webEnabled = runtimeMode === "standalone";
5241
+ console.log(
5242
+ `[Runtime] IPC channel ${hasIpcChannel ? "available" : "not available"} (mode=${runtimeMode})`
5243
+ );
5402
5244
  if (hasIpcChannel) {
5403
5245
  await ipcAdapter.start({
5404
5246
  agent,
@@ -5406,8 +5248,7 @@ async function startServer(options) {
5406
5248
  app,
5407
5249
  rootDir,
5408
5250
  lifecycle,
5409
- adapterMap,
5410
- resultsQueryService
5251
+ adapterMap
5411
5252
  });
5412
5253
  adapters.push(ipcAdapter);
5413
5254
  adapterMap.set(ipcAdapter.name, ipcAdapter);
@@ -5422,8 +5263,7 @@ async function startServer(options) {
5422
5263
  rootDir,
5423
5264
  lifecycle,
5424
5265
  adapterMap,
5425
- ipcBroadcaster,
5426
- resultsQueryService
5266
+ ipcBroadcaster
5427
5267
  });
5428
5268
  adapters.push(webAdapter);
5429
5269
  adapterMap.set(webAdapter.name, webAdapter);
@@ -5441,8 +5281,7 @@ async function startServer(options) {
5441
5281
  rootDir,
5442
5282
  lifecycle,
5443
5283
  adapterMap,
5444
- ipcBroadcaster,
5445
- resultsQueryService
5284
+ ipcBroadcaster
5446
5285
  });
5447
5286
  adapters.push(telegramAdapter);
5448
5287
  adapterMap.set(telegramAdapter.name, telegramAdapter);
@@ -5470,8 +5309,7 @@ async function startServer(options) {
5470
5309
  rootDir,
5471
5310
  lifecycle,
5472
5311
  adapterMap,
5473
- ipcBroadcaster,
5474
- resultsQueryService
5312
+ ipcBroadcaster
5475
5313
  });
5476
5314
  adapters.push(slackAdapter);
5477
5315
  adapterMap.set(slackAdapter.name, slackAdapter);
@@ -5503,7 +5341,7 @@ async function startServer(options) {
5503
5341
  lifecycle,
5504
5342
  notify: notifyFn,
5505
5343
  adapterMap,
5506
- resultsQueryService
5344
+ ipcBroadcaster
5507
5345
  });
5508
5346
  adapters.push(schedulerAdapter);
5509
5347
  adapterMap.set(schedulerAdapter.name, schedulerAdapter);
@@ -5591,23 +5429,23 @@ function findMissingSkills(workDir, config) {
5591
5429
  });
5592
5430
  }
5593
5431
  function copyStartTemplates2(workDir) {
5594
- const templateDir = path19.resolve(
5432
+ const templateDir = path17.resolve(
5595
5433
  new URL("../templates", import.meta.url).pathname
5596
5434
  );
5597
5435
  for (const file of ["start.sh", "start.bat"]) {
5598
- const src = path19.join(templateDir, file);
5599
- const dest = path19.join(workDir, file);
5600
- if (fs20.existsSync(src)) {
5601
- 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);
5602
5440
  if (file === "start.sh") {
5603
- fs20.chmodSync(dest, 493);
5441
+ fs17.chmodSync(dest, 493);
5604
5442
  }
5605
5443
  }
5606
5444
  }
5607
5445
  }
5608
5446
  async function runCommand(directory) {
5609
- const workDir = directory ? path19.resolve(directory) : process.cwd();
5610
- fs20.mkdirSync(workDir, { recursive: true });
5447
+ const workDir = directory ? path17.resolve(directory) : process.cwd();
5448
+ fs17.mkdirSync(workDir, { recursive: true });
5611
5449
  if (!configExists(workDir)) {
5612
5450
  console.log(chalk4.blue("\n No skillpack.json found. Let's set one up.\n"));
5613
5451
  const { name, description } = await inquirer2.prompt([
@@ -5651,14 +5489,14 @@ async function runCommand(directory) {
5651
5489
  }
5652
5490
 
5653
5491
  // src/cli.ts
5654
- import fs21 from "fs";
5655
- import path20 from "path";
5492
+ import fs18 from "fs";
5493
+ import path18 from "path";
5656
5494
  import { fileURLToPath as fileURLToPath3 } from "url";
5657
5495
  var packageJson = JSON.parse(
5658
- fs21.readFileSync(new URL("../package.json", import.meta.url), "utf-8")
5496
+ fs18.readFileSync(new URL("../package.json", import.meta.url), "utf-8")
5659
5497
  );
5660
5498
  var program = new Command();
5661
- var cliFilePath = path20.resolve(fileURLToPath3(import.meta.url));
5499
+ var cliFilePath = path18.resolve(fileURLToPath3(import.meta.url));
5662
5500
  program.name("skillpack").description("Assemble, package, and run Agent Skills packs").version(packageJson.version);
5663
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) => {
5664
5502
  await createCommand(directory, options);
@@ -5679,7 +5517,7 @@ program.command("zip").description("Package the current pack as a zip file (skil
5679
5517
  function normalizeUserArgs(argv) {
5680
5518
  if (argv.length === 0) return argv;
5681
5519
  const firstArg = argv[0];
5682
- if (firstArg && path20.resolve(firstArg) === cliFilePath) {
5520
+ if (firstArg && path18.resolve(firstArg) === cliFilePath) {
5683
5521
  return argv.slice(1);
5684
5522
  }
5685
5523
  return argv;