@skilder-ai/runtime 0.9.10 → 0.9.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -87799,6 +87799,9 @@ function loadEnv(projectRoot2, packagePath2, quiet = false) {
87799
87799
  var fs = __toESM(require("fs"), 1);
87800
87800
  var path = __toESM(require("path"), 1);
87801
87801
  var crypto2 = __toESM(require("crypto"), 1);
87802
+ function getAssetCacheDir(config2) {
87803
+ return config2.assetCacheDir ?? path.join(config2.workingDir, "assets");
87804
+ }
87802
87805
  var SKILDER_FILE_PREFIX = "@skilder-file:/";
87803
87806
  var SKILDER_ASSET_PREFIX = "@skilder-asset:/";
87804
87807
  function isSkilderAssetPath(filePath) {
@@ -87835,7 +87838,7 @@ function parseAssetPath(assetPath) {
87835
87838
  }
87836
87839
  function resolveAssetCachePath(assetPath, config2) {
87837
87840
  const { resourceId, extension } = parseAssetPath(assetPath);
87838
- return path.join(config2.workingDir, "assets", `${resourceId}${extension}`);
87841
+ return path.join(getAssetCacheDir(config2), `${resourceId}${extension}`);
87839
87842
  }
87840
87843
  async function writeAssetCache(assetPath, buffer, config2) {
87841
87844
  const cachePath = resolveAssetCachePath(assetPath, config2);
@@ -87867,7 +87870,14 @@ function resolveFilePath(filePath, config2) {
87867
87870
  return resolveAssetCachePath(filePath, config2);
87868
87871
  }
87869
87872
  if (filePath.startsWith(SKILDER_FILE_PREFIX)) {
87870
- const filename = filePath.slice(SKILDER_FILE_PREFIX.length);
87873
+ const rest = filePath.slice(SKILDER_FILE_PREFIX.length);
87874
+ if (rest.split("/").some((seg) => seg === "..")) {
87875
+ throw new Error(`Path traversal detected: ${filePath}`);
87876
+ }
87877
+ const filename = path.basename(rest);
87878
+ if (!filename || filename === ".") {
87879
+ throw new Error(`Invalid skilder file path: ${filePath}`);
87880
+ }
87871
87881
  const resolved = path.normalize(path.join(config2.workingDir, filename));
87872
87882
  if (!resolved.startsWith(workingDirPrefix) && resolved !== config2.workingDir) {
87873
87883
  throw new Error(`Path traversal detected: ${filePath}`);
@@ -87881,6 +87891,11 @@ function resolveFilePath(filePath, config2) {
87881
87891
  if (normalized === config2.workingDir || normalized.startsWith(workingDirPrefix)) {
87882
87892
  return normalized;
87883
87893
  }
87894
+ const assetCacheDir = getAssetCacheDir(config2);
87895
+ const assetCachePrefix = assetCacheDir.endsWith(path.sep) ? assetCacheDir : assetCacheDir + path.sep;
87896
+ if (normalized === assetCacheDir || normalized.startsWith(assetCachePrefix)) {
87897
+ return normalized;
87898
+ }
87884
87899
  for (const allowed of config2.allowedPaths) {
87885
87900
  const allowedPrefix = allowed.endsWith(path.sep) ? allowed : allowed + path.sep;
87886
87901
  if (normalized === allowed || normalized.startsWith(allowedPrefix)) {
@@ -87888,7 +87903,7 @@ function resolveFilePath(filePath, config2) {
87888
87903
  }
87889
87904
  }
87890
87905
  throw new Error(
87891
- `File path not in allowed locations: ${filePath}. Allowed: ${[config2.workingDir, ...config2.allowedPaths].join(", ")}`
87906
+ `File path not in allowed locations: ${filePath}. Allowed: ${[config2.workingDir, assetCacheDir, ...config2.allowedPaths].join(", ")}`
87892
87907
  );
87893
87908
  }
87894
87909
  function generateSkilderPath(extension) {
@@ -110508,12 +110523,13 @@ var DefaultMCPClient = class {
110508
110523
 
110509
110524
  // ../common/src/node-helpers/mcp-client.ts
110510
110525
  async function createSkilderMCPClient(options) {
110511
- const { url: url2, userKey, delegateDepth } = options;
110526
+ const { url: url2, userKey, delegateDepth, name: name21 = "skilder-studio" } = options;
110512
110527
  const headers = { user_key: userKey };
110513
110528
  if (delegateDepth != null) {
110514
110529
  headers["x-delegate-depth"] = String(delegateDepth);
110515
110530
  }
110516
110531
  return createMCPClient({
110532
+ name: name21,
110517
110533
  transport: {
110518
110534
  type: "http",
110519
110535
  url: url2,
@@ -110745,6 +110761,10 @@ function buildStreamTransport(transport) {
110745
110761
  };
110746
110762
  }
110747
110763
 
110764
+ // ../common/src/connection-options.ts
110765
+ var CONNECTED_WINDOW_HOURS = 24;
110766
+ var CONNECTED_WINDOW_MS = CONNECTED_WINDOW_HOURS * 60 * 60 * 1e3;
110767
+
110748
110768
  // ../common/src/services/logger.service.ts
110749
110769
  var import_pino = __toESM(require_pino(), 1);
110750
110770
  var import_pino_pretty = __toESM(require_pino_pretty(), 1);
@@ -137449,13 +137469,17 @@ var AIProviderService = class AIProviderService2 {
137449
137469
  /**
137450
137470
  * Chat with a specific model (sync).
137451
137471
  * Accepts config directly - caller is responsible for providing decrypted config.
137472
+ *
137473
+ * @param abortSignal Optional AbortSignal; when aborted, the underlying
137474
+ * provider request is cancelled (no cost / connection leak on timeout).
137452
137475
  */
137453
- async chat(config2, provider, modelName, message, slug) {
137476
+ async chat(config2, provider, modelName, message, slug, abortSignal) {
137454
137477
  const model = this.getProviderModel(provider, modelName, config2, slug);
137455
137478
  try {
137456
137479
  const result = await generateText({
137457
137480
  model,
137458
- messages: [{ role: "user", content: message }]
137481
+ messages: [{ role: "user", content: message }],
137482
+ abortSignal
137459
137483
  });
137460
137484
  return result.text;
137461
137485
  } catch (error48) {
@@ -137776,7 +137800,8 @@ var CACHE_BUCKETS = {
137776
137800
  RATE_LIMIT_KEY: "rate-limit-key",
137777
137801
  RATE_LIMIT_IP: "rate-limit-ip",
137778
137802
  FASTIFY_RATE_LIMIT: "fastify-rate-limit",
137779
- GITHUB_RATE_LIMIT: "github-rate-limit"
137803
+ GITHUB_RATE_LIMIT: "github-rate-limit",
137804
+ COMMUNITY_SKILLS_RATE_LIMIT: "community-skills-rate-limit"
137780
137805
  };
137781
137806
  var CACHE_BUCKET_TTLS = {
137782
137807
  HEARTBEAT: 30 * 1e3,
@@ -137797,8 +137822,10 @@ var CACHE_BUCKET_TTLS = {
137797
137822
  // 1 hour
137798
137823
  FASTIFY_RATE_LIMIT: 60 * 1e3,
137799
137824
  // 1 minute
137800
- GITHUB_RATE_LIMIT: 2 * 60 * 1e3
137825
+ GITHUB_RATE_LIMIT: 2 * 60 * 1e3,
137801
137826
  // 2 minutes (window is 1 min, extra margin ensures NATS TTL doesn't evict before resetAt check)
137827
+ COMMUNITY_SKILLS_RATE_LIMIT: 2 * 60 * 1e3
137828
+ // 2 minutes (window is 1 min, see GITHUB_RATE_LIMIT for rationale)
137802
137829
  };
137803
137830
  var CACHE_SERVICE = "cache.service";
137804
137831
  var CACHE_SERVICE_CONFIG = "cache.service.config";
@@ -137838,6 +137865,10 @@ function createCacheServiceConfig() {
137838
137865
  {
137839
137866
  name: CACHE_BUCKETS.GITHUB_RATE_LIMIT,
137840
137867
  ttlMs: parseTTL("GITHUB_RATE_LIMIT_CACHE_TTL", CACHE_BUCKET_TTLS.GITHUB_RATE_LIMIT)
137868
+ },
137869
+ {
137870
+ name: CACHE_BUCKETS.COMMUNITY_SKILLS_RATE_LIMIT,
137871
+ ttlMs: parseTTL("COMMUNITY_SKILLS_RATE_LIMIT_CACHE_TTL", CACHE_BUCKET_TTLS.COMMUNITY_SKILLS_RATE_LIMIT)
137841
137872
  }
137842
137873
  ]
137843
137874
  };
@@ -138782,6 +138813,7 @@ var dgraph_resolvers_types_exports = {};
138782
138813
  __export(dgraph_resolvers_types_exports, {
138783
138814
  ActiveStatus: () => ActiveStatus,
138784
138815
  AiProviderType: () => AiProviderType,
138816
+ ConversationMode: () => ConversationMode,
138785
138817
  HatColor: () => HatColor,
138786
138818
  InvitationStatus: () => InvitationStatus,
138787
138819
  McpServerStatus: () => McpServerStatus,
@@ -138813,6 +138845,11 @@ var ActiveStatus = /* @__PURE__ */ ((ActiveStatus2) => {
138813
138845
  ActiveStatus2["Inactive"] = "INACTIVE";
138814
138846
  return ActiveStatus2;
138815
138847
  })(ActiveStatus || {});
138848
+ var ConversationMode = /* @__PURE__ */ ((ConversationMode2) => {
138849
+ ConversationMode2["Manage"] = "MANAGE";
138850
+ ConversationMode2["Run"] = "RUN";
138851
+ return ConversationMode2;
138852
+ })(ConversationMode || {});
138816
138853
  var HatColor = /* @__PURE__ */ ((HatColor2) => {
138817
138854
  HatColor2["Blue"] = "BLUE";
138818
138855
  HatColor2["Gray"] = "GRAY";
@@ -144319,6 +144356,7 @@ var FileService = class FileService2 extends LifecycleService {
144319
144356
  getFileConfig() {
144320
144357
  return {
144321
144358
  workingDir: this.workingDir,
144359
+ assetCacheDir: this.getAssetCacheDir(),
144322
144360
  allowedPaths: this.allowedPaths,
144323
144361
  maxFileSizeMB: this.maxFileSizeMB
144324
144362
  };
@@ -144541,7 +144579,8 @@ var DelegateService = class DelegateService2 {
144541
144579
  createSkilderMCPClient({
144542
144580
  url: runtimeUrl,
144543
144581
  userKey,
144544
- delegateDepth: currentDepth + 1
144582
+ delegateDepth: currentDepth + 1,
144583
+ name: "skilder-runtime-delegate"
144545
144584
  }),
144546
144585
  new Promise((_3, reject) => {
144547
144586
  connectionTimer = setTimeout(() => reject(new Error("MCP client connection timeout")), DELEGATE_MCP_CLIENT_TIMEOUT_MS);
@@ -145382,6 +145421,17 @@ function handleGetHat(hatName, ctx) {
145382
145421
  }
145383
145422
  const hat = ctx.catalog.hats.find((h2) => h2.name === hatName);
145384
145423
  if (!hat) {
145424
+ const hidden = ctx.catalog.hiddenHats?.find((h2) => h2.name === hatName);
145425
+ if (hidden?.reason === "no-skills") {
145426
+ return errorResult(
145427
+ `The hat "${hatName}" exists but has no skills yet, so it can't be worn. Add at least one skill to it (add-skill-to-hat), then learn it. Do not create it again.`
145428
+ );
145429
+ }
145430
+ if (hidden?.reason === "disabled") {
145431
+ return errorResult(
145432
+ `The hat "${hatName}" exists but is disabled, so it can't be worn. Enable it first, then learn it. Do not create it again.`
145433
+ );
145434
+ }
145385
145435
  const availableHats = ctx.catalog.hats.map((h2) => h2.name).join(", ") || "none";
145386
145436
  return errorResult(`Error: Hat "${hatName}" not found. Available hats: ${availableHats}`);
145387
145437
  }
@@ -154147,24 +154197,43 @@ function rewriteWorkspacePathsInResult(result, workspaceId, workspaceDir, runtim
154147
154197
  return result;
154148
154198
  const esc2 = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
154149
154199
  const workingRootName = import_path3.default.basename(import_path3.default.dirname(workspaceDir));
154150
- const patterns = [new RegExp(esc2(workspaceDir), "g")];
154200
+ const absPatterns = [new RegExp(esc2(workspaceDir), "g")];
154151
154201
  if (workingRootName) {
154152
- patterns.push(new RegExp(`(?<![\\w/.-])${esc2(workingRootName)}/${esc2(workspaceId)}`, "g"));
154202
+ absPatterns.push(new RegExp(`(?<![\\w/.-])${esc2(workingRootName)}/${esc2(workspaceId)}`, "g"));
154153
154203
  }
154154
154204
  const replacement = `@skilder-file:/${runtimeId}`;
154205
+ const bareSkilderPattern = /@skilder-file:\/([^/"'\s]+)(?=["'\s]|$)/g;
154206
+ const rewriteText = (input) => {
154207
+ let text2 = input;
154208
+ for (const p2 of absPatterns)
154209
+ text2 = text2.replace(p2, replacement);
154210
+ text2 = text2.replace(bareSkilderPattern, `@skilder-file:/${runtimeId}/$1`);
154211
+ return text2;
154212
+ };
154213
+ const rewriteValue = (val) => {
154214
+ if (typeof val === "string")
154215
+ return rewriteText(val);
154216
+ if (Array.isArray(val))
154217
+ return val.map(rewriteValue);
154218
+ if (val && typeof val === "object") {
154219
+ return Object.fromEntries(Object.entries(val).map(([k3, v2]) => [k3, rewriteValue(v2)]));
154220
+ }
154221
+ return val;
154222
+ };
154155
154223
  const rewritten = result.content.map((block) => {
154156
154224
  if (block && block.type === "text" && typeof block.text === "string") {
154157
- let text2 = block.text;
154158
- const before = text2;
154159
- for (const p2 of patterns)
154160
- text2 = text2.replace(p2, replacement);
154161
- if (text2 === before)
154225
+ const text2 = rewriteText(block.text);
154226
+ if (text2 === block.text)
154162
154227
  return block;
154163
154228
  return { ...block, text: text2 };
154164
154229
  }
154165
154230
  return block;
154166
154231
  });
154167
- return { ...result, content: rewritten };
154232
+ const out = { ...result, content: rewritten };
154233
+ if (result.structuredContent && typeof result.structuredContent === "object") {
154234
+ out.structuredContent = rewriteValue(result.structuredContent);
154235
+ }
154236
+ return out;
154168
154237
  }
154169
154238
  function formatScriptError(stderr, exitCode) {
154170
154239
  const lines = stderr.split("\n");