@minipi/mini-pi 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +44 -10
  2. package/bin/mini-pi-cli.cjs +1199 -629
  3. package/package.json +3 -3
@@ -85,7 +85,7 @@ var init_tslib = __esm({
85
85
  });
86
86
 
87
87
  // node_modules/.pnpm/@anthropic-ai+sdk@0.125.0/node_modules/@anthropic-ai/sdk/internal/errors.mjs
88
- function isAbortError2(err) {
88
+ function isAbortError3(err) {
89
89
  return typeof err === "object" && err !== null && // Spec-compliant fetch implementations
90
90
  ("name" in err && err.name === "AbortError" || // Expo fetch
91
91
  "message" in err && String(err.message).includes("FetchRequestCanceledException"));
@@ -279,10 +279,10 @@ var init_values = __esm({
279
279
  });
280
280
 
281
281
  // node_modules/.pnpm/@anthropic-ai+sdk@0.125.0/node_modules/@anthropic-ai/sdk/internal/utils/sleep.mjs
282
- var sleep2;
282
+ var sleep3;
283
283
  var init_sleep = __esm({
284
284
  "node_modules/.pnpm/@anthropic-ai+sdk@0.125.0/node_modules/@anthropic-ai/sdk/internal/utils/sleep.mjs"() {
285
- sleep2 = (ms, signal) => new Promise((resolve6) => {
285
+ sleep3 = (ms, signal) => new Promise((resolve6) => {
286
286
  if (signal?.aborted)
287
287
  return resolve6();
288
288
  const onAbort = () => {
@@ -964,18 +964,18 @@ __export(node_exports, {
964
964
  child_process: () => child_process,
965
965
  crypto: () => crypto2,
966
966
  fs: () => fs,
967
- os: () => os,
967
+ os: () => os2,
968
968
  path: () => path2,
969
969
  stream: () => stream,
970
970
  util: () => util
971
971
  });
972
- var child_process, crypto2, fs, os, path2, stream, util;
972
+ var child_process, crypto2, fs, os2, path2, stream, util;
973
973
  var init_node = __esm({
974
974
  "node_modules/.pnpm/@anthropic-ai+sdk@0.125.0/node_modules/@anthropic-ai/sdk/internal/node.mjs"() {
975
975
  child_process = __toESM(require("node:child_process"), 1);
976
976
  crypto2 = __toESM(require("node:crypto"), 1);
977
977
  fs = __toESM(require("node:fs"), 1);
978
- os = __toESM(require("node:os"), 1);
978
+ os2 = __toESM(require("node:os"), 1);
979
979
  path2 = __toESM(require("node:path"), 1);
980
980
  stream = __toESM(require("node:stream"), 1);
981
981
  util = __toESM(require("node:util"), 1);
@@ -1361,11 +1361,11 @@ function defaultLogger() {
1361
1361
  const envLevel = readEnv2("ANTHROPIC_LOG");
1362
1362
  if (!cachedDefaultLogger || envLevel !== lastEnvLevel) {
1363
1363
  lastEnvLevel = envLevel;
1364
- cachedDefaultLogger = filterLogger(console, parseLogLevel2(envLevel, "process.env['ANTHROPIC_LOG']", filterLogger(console, defaultLogLevel)) ?? defaultLogLevel);
1364
+ cachedDefaultLogger = filterLogger(console, parseLogLevel3(envLevel, "process.env['ANTHROPIC_LOG']", filterLogger(console, defaultLogLevel)) ?? defaultLogLevel);
1365
1365
  }
1366
1366
  return cachedDefaultLogger;
1367
1367
  }
1368
- var defaultLogLevel, levelNumbers2, parseLogLevel2, noopLogger2, cachedLoggers2, lastEnvLevel, cachedDefaultLogger, formatRequestDetails2;
1368
+ var defaultLogLevel, levelNumbers2, parseLogLevel3, noopLogger2, cachedLoggers2, lastEnvLevel, cachedDefaultLogger, formatRequestDetails2;
1369
1369
  var init_log = __esm({
1370
1370
  "node_modules/.pnpm/@anthropic-ai+sdk@0.125.0/node_modules/@anthropic-ai/sdk/internal/utils/log.mjs"() {
1371
1371
  init_values();
@@ -1378,7 +1378,7 @@ var init_log = __esm({
1378
1378
  info: 400,
1379
1379
  debug: 500
1380
1380
  };
1381
- parseLogLevel2 = (maybeLevel, sourceName, logger2) => {
1381
+ parseLogLevel3 = (maybeLevel, sourceName, logger2) => {
1382
1382
  if (!maybeLevel) {
1383
1383
  return void 0;
1384
1384
  }
@@ -2155,7 +2155,7 @@ var init_streaming = __esm({
2155
2155
  }
2156
2156
  done = true;
2157
2157
  } catch (e) {
2158
- if (isAbortError2(e))
2158
+ if (isAbortError3(e))
2159
2159
  return;
2160
2160
  throw e;
2161
2161
  } finally {
@@ -2199,7 +2199,7 @@ var init_streaming = __esm({
2199
2199
  }
2200
2200
  done = true;
2201
2201
  } catch (e) {
2202
- if (isAbortError2(e))
2202
+ if (isAbortError3(e))
2203
2203
  return;
2204
2204
  throw e;
2205
2205
  } finally {
@@ -2371,11 +2371,11 @@ var init_parse = __esm({
2371
2371
  function isFetchOriginError(err) {
2372
2372
  return typeof err === "object" && err !== null && fetchOriginErrors.has(err);
2373
2373
  }
2374
- function isRetryableError(err) {
2374
+ function isRetryableError2(err) {
2375
2375
  const seen = /* @__PURE__ */ new Set();
2376
2376
  while (typeof err === "object" && err !== null && !seen.has(err)) {
2377
2377
  seen.add(err);
2378
- if (isFetchOriginError(err) || isAbortError2(err) || err instanceof APIConnectionError2 || err instanceof RetryableError) {
2378
+ if (isFetchOriginError(err) || isAbortError3(err) || err instanceof APIConnectionError2 || err instanceof RetryableError) {
2379
2379
  return true;
2380
2380
  }
2381
2381
  err = err.cause;
@@ -5004,7 +5004,7 @@ var init_poller = __esm({
5004
5004
  const wait = applyJitter(backoff2(attempt));
5005
5005
  log.warn("poll failed, backing off", { error: String(e), backoff_ms: wait });
5006
5006
  attempt++;
5007
- await sleep2(wait, __classPrivateFieldGet2(this, _WorkPoller_controller, "f").signal);
5007
+ await sleep3(wait, __classPrivateFieldGet2(this, _WorkPoller_controller, "f").signal);
5008
5008
  continue;
5009
5009
  }
5010
5010
  attempt = 0;
@@ -5012,7 +5012,7 @@ var init_poller = __esm({
5012
5012
  if (__classPrivateFieldGet2(this, _WorkPoller_drain, "f"))
5013
5013
  return;
5014
5014
  idle.onEmptyPoll();
5015
- await sleep2(jitter(1e3, 3e3), __classPrivateFieldGet2(this, _WorkPoller_controller, "f").signal);
5015
+ await sleep3(jitter(1e3, 3e3), __classPrivateFieldGet2(this, _WorkPoller_controller, "f").signal);
5016
5016
  continue;
5017
5017
  }
5018
5018
  idle.onClaim();
@@ -5444,7 +5444,7 @@ var init_SessionToolRunner = __esm({
5444
5444
  });
5445
5445
  }
5446
5446
  ctrl.signal.throwIfAborted();
5447
- await sleep2(backoff3, ctrl.signal);
5447
+ await sleep3(backoff3, ctrl.signal);
5448
5448
  backoff3 = Math.min(backoff3 * 2, STREAM_BACKOFF_CAP_MS);
5449
5449
  }
5450
5450
  }, _SessionToolRunner_reconcile = /**
@@ -5696,7 +5696,7 @@ var init_SessionToolRunner = __esm({
5696
5696
  backoff_ms: waitMs,
5697
5697
  error: String(e)
5698
5698
  });
5699
- await sleep2(waitMs, ctrl.signal);
5699
+ await sleep3(waitMs, ctrl.signal);
5700
5700
  }
5701
5701
  }
5702
5702
  __classPrivateFieldGet2(this, _SessionToolRunner_logger, "f").error("failed to send tool result", {
@@ -5709,7 +5709,7 @@ var init_SessionToolRunner = __esm({
5709
5709
  async function _SessionToolRunner_drain2() {
5710
5710
  if (__classPrivateFieldGet2(this, _SessionToolRunner_inFlightCount, "f") === 0)
5711
5711
  return;
5712
- await Promise.race([new Promise((r) => __classPrivateFieldSet2(this, _SessionToolRunner_onIdle, r, "f")), sleep2(DRAIN_TIMEOUT_MS)]);
5712
+ await Promise.race([new Promise((r) => __classPrivateFieldSet2(this, _SessionToolRunner_onIdle, r, "f")), sleep3(DRAIN_TIMEOUT_MS)]);
5713
5713
  __classPrivateFieldSet2(this, _SessionToolRunner_onIdle, null, "f");
5714
5714
  if (__classPrivateFieldGet2(this, _SessionToolRunner_inFlightCount, "f") > 0) {
5715
5715
  __classPrivateFieldGet2(this, _SessionToolRunner_logger, "f").warn("drain timeout exceeded");
@@ -8087,7 +8087,7 @@ async function heartbeatLoop(client, work, lease, logger2, requestOptions, onLea
8087
8087
  };
8088
8088
  await beat();
8089
8089
  while (!lease.signal.aborted) {
8090
- await sleep2(intervalMs, lease.signal);
8090
+ await sleep3(intervalMs, lease.signal);
8091
8091
  lease.signal.throwIfAborted();
8092
8092
  await beat();
8093
8093
  }
@@ -9887,7 +9887,7 @@ var init_BetaMessageStream = __esm({
9887
9887
  _BetaMessageStream_logger.set(this, void 0);
9888
9888
  _BetaMessageStream_handleError.set(this, (error) => {
9889
9889
  __classPrivateFieldSet2(this, _BetaMessageStream_errored, true, "f");
9890
- if (isAbortError2(error)) {
9890
+ if (isAbortError3(error)) {
9891
9891
  error = new APIUserAbortError2();
9892
9892
  }
9893
9893
  if (error instanceof APIUserAbortError2) {
@@ -14725,7 +14725,7 @@ var init_MessageStream = __esm({
14725
14725
  _MessageStream_logger.set(this, void 0);
14726
14726
  _MessageStream_handleError.set(this, (error) => {
14727
14727
  __classPrivateFieldSet2(this, _MessageStream_errored, true, "f");
14728
- if (isAbortError2(error)) {
14728
+ if (isAbortError3(error)) {
14729
14729
  error = new APIUserAbortError2();
14730
14730
  }
14731
14731
  if (error instanceof APIUserAbortError2) {
@@ -15973,7 +15973,7 @@ var init_client = __esm({
15973
15973
  this.timeout = options.timeout ?? _a5.DEFAULT_TIMEOUT;
15974
15974
  this.logger = options.logger ?? console;
15975
15975
  this.logLevel = defaultLogLevel;
15976
- this.logLevel = parseLogLevel2(options.logLevel, "ClientOptions.logLevel", loggerFor2(this)) ?? parseLogLevel2(readEnv2("ANTHROPIC_LOG"), "process.env['ANTHROPIC_LOG']", loggerFor2(this)) ?? defaultLogLevel;
15976
+ this.logLevel = parseLogLevel3(options.logLevel, "ClientOptions.logLevel", loggerFor2(this)) ?? parseLogLevel3(readEnv2("ANTHROPIC_LOG"), "process.env['ANTHROPIC_LOG']", loggerFor2(this)) ?? defaultLogLevel;
15977
15977
  this.fetchOptions = options.fetchOptions;
15978
15978
  this.maxRetries = validatePositiveInteger2("maxRetries", options.maxRetries ?? 2);
15979
15979
  this.fetch = options.fetch ?? getDefaultFetch2();
@@ -16352,9 +16352,9 @@ var init_client = __esm({
16352
16352
  if (options.signal?.aborted) {
16353
16353
  throw new APIUserAbortError2();
16354
16354
  }
16355
- const isTimeout = isAbortError2(response) || /timed? ?out/i.test(String(response) + ("cause" in response ? String(response.cause) : ""));
16355
+ const isTimeout = isAbortError3(response) || /timed? ?out/i.test(String(response) + ("cause" in response ? String(response.cause) : ""));
16356
16356
  const hasMiddleware = this.middleware.length > 0 || !!options.middleware?.length || this.backendMiddleware().length > 0;
16357
- if (hasMiddleware && !isTimeout && !isRetryableError(response)) {
16357
+ if (hasMiddleware && !isTimeout && !isRetryableError2(response)) {
16358
16358
  loggerFor2(this).info(`[${requestLogID}] middleware error (not retryable)`);
16359
16359
  loggerFor2(this).debug(`[${requestLogID}] middleware error (not retryable)`, formatRequestDetails2({
16360
16360
  retryOfRequestLogID,
@@ -16532,7 +16532,7 @@ var init_client = __esm({
16532
16532
  const maxRetries = options.maxRetries ?? this.maxRetries;
16533
16533
  timeoutMillis = this.calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries);
16534
16534
  }
16535
- await sleep2(timeoutMillis);
16535
+ await sleep3(timeoutMillis);
16536
16536
  return this.makeRequest(options, retriesRemaining - 1, requestLogID);
16537
16537
  }
16538
16538
  calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries) {
@@ -16696,6 +16696,432 @@ var init_sdk = __esm({
16696
16696
  }
16697
16697
  });
16698
16698
 
16699
+ // src/agent/model/config.ts
16700
+ var REQUEST_TIMEOUT_MS = 12e4;
16701
+ var STREAM_IDLE_TIMEOUT_MS = 12e4;
16702
+ var DEFAULT_MAX_TOKENS = 8192;
16703
+ function resolveMaxTokens(value) {
16704
+ return typeof value === "number" && Number.isFinite(value) && value >= 1 ? Math.floor(value) : DEFAULT_MAX_TOKENS;
16705
+ }
16706
+ function resolveStreamIdleTimeout(value) {
16707
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : STREAM_IDLE_TIMEOUT_MS;
16708
+ }
16709
+
16710
+ // src/agent/model/errors.ts
16711
+ var STREAM_IDLE_TIMEOUT_ERROR_NAME = "StreamIdleTimeoutError";
16712
+ var PARTIAL_STREAM_ERROR_NAME = "PartialStreamInterruptedError";
16713
+ var ERROR_CAUSE_MAX_DEPTH = 5;
16714
+ function describeError(error) {
16715
+ return error instanceof Error ? error.message : String(error);
16716
+ }
16717
+ function findInErrorChain(error, read) {
16718
+ let current = error;
16719
+ for (let depth = 0; current && typeof current === "object" && depth < ERROR_CAUSE_MAX_DEPTH; depth += 1) {
16720
+ const candidate = current;
16721
+ const value = read(candidate);
16722
+ if (value !== void 0) {
16723
+ return value;
16724
+ }
16725
+ current = candidate.cause;
16726
+ }
16727
+ return void 0;
16728
+ }
16729
+ function nameInErrorChain(error, name) {
16730
+ let current = error;
16731
+ for (let depth = 0; current && typeof current === "object" && depth < ERROR_CAUSE_MAX_DEPTH; depth += 1) {
16732
+ const candidate = current;
16733
+ if (candidate.name === name) {
16734
+ return true;
16735
+ }
16736
+ current = candidate.cause;
16737
+ }
16738
+ return false;
16739
+ }
16740
+ var ABORT_ERROR_PATTERNS = [/^AbortError$/i, /APIUserAbortError/i];
16741
+ function isAbortError(error) {
16742
+ let current = error;
16743
+ for (let depth = 0; current && typeof current === "object" && depth < ERROR_CAUSE_MAX_DEPTH; depth += 1) {
16744
+ const candidate = current;
16745
+ for (const value of [candidate.name, candidate.constructor?.name]) {
16746
+ if (typeof value === "string" && ABORT_ERROR_PATTERNS.some((pattern) => pattern.test(value))) {
16747
+ return true;
16748
+ }
16749
+ }
16750
+ current = candidate.cause;
16751
+ }
16752
+ return false;
16753
+ }
16754
+ var StreamIdleTimeoutError = class extends Error {
16755
+ constructor(idleMs) {
16756
+ super(`\u6D41\u5F0F\u54CD\u5E94 ${idleMs}ms \u5185\u6CA1\u6709\u6536\u5230\u4EFB\u4F55\u6570\u636E\uFF0C\u5DF2\u4E2D\u6B62\u672C\u6B21\u8BF7\u6C42`);
16757
+ this.name = STREAM_IDLE_TIMEOUT_ERROR_NAME;
16758
+ }
16759
+ };
16760
+ function isStreamIdleTimeoutError(error) {
16761
+ return nameInErrorChain(error, STREAM_IDLE_TIMEOUT_ERROR_NAME);
16762
+ }
16763
+ var PartialStreamInterruptedError = class extends Error {
16764
+ constructor(cause) {
16765
+ super(
16766
+ `\u6D41\u5F0F\u54CD\u5E94\u5728\u5DF2\u8F93\u51FA\u90E8\u5206\u5185\u5BB9\u540E\u4E2D\u65AD\uFF0C\u672A\u81EA\u52A8\u91CD\u8BD5\uFF08\u907F\u514D\u91CD\u590D\u8F93\u51FA\u4E0E\u91CD\u590D\u8BA1\u8D39\uFF09\uFF1A${describeError(cause)}`
16767
+ );
16768
+ this.name = PARTIAL_STREAM_ERROR_NAME;
16769
+ this.cause = cause;
16770
+ }
16771
+ };
16772
+ function isPartialStreamInterrupted(error) {
16773
+ return nameInErrorChain(error, PARTIAL_STREAM_ERROR_NAME);
16774
+ }
16775
+ function toNonRetryableIfPartialStream(error, emittedContent) {
16776
+ if (emittedContent && isRetryableError(error)) {
16777
+ return new PartialStreamInterruptedError(error);
16778
+ }
16779
+ return error;
16780
+ }
16781
+ var RETRYABLE_ERROR_CODES = /* @__PURE__ */ new Set([
16782
+ "ETIMEDOUT",
16783
+ "ECONNRESET",
16784
+ "ECONNREFUSED",
16785
+ "EPIPE",
16786
+ "ENOTFOUND",
16787
+ "EAI_AGAIN",
16788
+ "UND_ERR_CONNECT_TIMEOUT",
16789
+ "UND_ERR_SOCKET"
16790
+ ]);
16791
+ var RETRYABLE_ERROR_NAME_PATTERNS = [
16792
+ /^APIConnectionError$/,
16793
+ /^APIConnectionTimeoutError$/,
16794
+ /^APIConnectionTimeoutError\d*$/
16795
+ ];
16796
+ function isRetryableError(error) {
16797
+ if (!error || typeof error !== "object") {
16798
+ return false;
16799
+ }
16800
+ if (isPartialStreamInterrupted(error)) {
16801
+ return false;
16802
+ }
16803
+ if (isStreamIdleTimeoutError(error)) {
16804
+ return true;
16805
+ }
16806
+ const name = findInErrorChain(
16807
+ error,
16808
+ (candidate) => typeof candidate.name === "string" ? candidate.name : void 0
16809
+ );
16810
+ if (name && RETRYABLE_ERROR_NAME_PATTERNS.some((pattern) => pattern.test(name))) {
16811
+ return true;
16812
+ }
16813
+ const status = findInErrorChain(
16814
+ error,
16815
+ (candidate) => typeof candidate.status === "number" ? candidate.status : void 0
16816
+ );
16817
+ if (status !== void 0) {
16818
+ return status === 429 || status === 408 || status >= 500;
16819
+ }
16820
+ const code = findInErrorChain(
16821
+ error,
16822
+ (candidate) => typeof candidate.code === "string" ? candidate.code : void 0
16823
+ );
16824
+ if (code !== void 0 && RETRYABLE_ERROR_CODES.has(code)) {
16825
+ return true;
16826
+ }
16827
+ return false;
16828
+ }
16829
+
16830
+ // src/agent/model/retry.ts
16831
+ var MAX_REQUEST_ATTEMPTS = 3;
16832
+ var RETRY_BASE_DELAY_MS = 1e3;
16833
+ var RETRY_JITTER_RATIO = 0.2;
16834
+ function applyRetryJitter(delayMs, random = Math.random) {
16835
+ const factor = 1 - RETRY_JITTER_RATIO + random() * RETRY_JITTER_RATIO * 2;
16836
+ return Math.max(0, Math.round(delayMs * factor));
16837
+ }
16838
+ function readHeader(headers, name) {
16839
+ if (!headers || typeof headers !== "object") {
16840
+ return null;
16841
+ }
16842
+ const getter = headers.get;
16843
+ if (typeof getter === "function") {
16844
+ const value = getter.call(headers, name);
16845
+ return typeof value === "string" ? value : null;
16846
+ }
16847
+ const record = headers;
16848
+ for (const key of [name, name.toLowerCase(), name.toUpperCase()]) {
16849
+ const value = record[key];
16850
+ if (typeof value === "string") {
16851
+ return value;
16852
+ }
16853
+ }
16854
+ return null;
16855
+ }
16856
+ function findHeaders(error) {
16857
+ let current = error;
16858
+ for (let depth = 0; current && typeof current === "object" && depth < 5; depth += 1) {
16859
+ const candidate = current;
16860
+ if (candidate.headers && typeof candidate.headers === "object") {
16861
+ return candidate.headers;
16862
+ }
16863
+ current = candidate.cause;
16864
+ }
16865
+ return void 0;
16866
+ }
16867
+ function retryAfterMsFromError(error, now = Date.now()) {
16868
+ const headers = findHeaders(error);
16869
+ if (!headers) {
16870
+ return void 0;
16871
+ }
16872
+ const ms = readHeader(headers, "retry-after-ms");
16873
+ if (ms !== null && ms.trim() !== "") {
16874
+ const parsed = Number(ms);
16875
+ if (Number.isFinite(parsed) && parsed >= 0) {
16876
+ return parsed;
16877
+ }
16878
+ }
16879
+ const raw = readHeader(headers, "retry-after");
16880
+ if (raw !== null && raw.trim() !== "") {
16881
+ const seconds = Number(raw);
16882
+ if (Number.isFinite(seconds) && seconds >= 0) {
16883
+ return seconds * 1e3;
16884
+ }
16885
+ const date = Date.parse(raw);
16886
+ if (Number.isFinite(date)) {
16887
+ return Math.max(0, date - now);
16888
+ }
16889
+ }
16890
+ return void 0;
16891
+ }
16892
+ function resolveRetryDelayMs(error, backoffMs, now = Date.now(), random = Math.random) {
16893
+ const serverMs = retryAfterMsFromError(error, now);
16894
+ const base = serverMs === void 0 ? backoffMs : Math.max(serverMs, backoffMs);
16895
+ return applyRetryJitter(base, random);
16896
+ }
16897
+ function sleep(ms, signal) {
16898
+ return new Promise((resolve6, reject) => {
16899
+ if (signal?.aborted) {
16900
+ reject(new Error("aborted"));
16901
+ return;
16902
+ }
16903
+ const timer = setTimeout(() => {
16904
+ signal?.removeEventListener("abort", onAbort);
16905
+ resolve6();
16906
+ }, ms);
16907
+ const onAbort = () => {
16908
+ clearTimeout(timer);
16909
+ reject(new Error("aborted"));
16910
+ };
16911
+ signal?.addEventListener("abort", onAbort, { once: true });
16912
+ });
16913
+ }
16914
+ async function withRetry(operation, options = {}) {
16915
+ const attempts = Math.max(1, options.attempts ?? MAX_REQUEST_ATTEMPTS);
16916
+ const baseDelayMs = options.baseDelayMs ?? RETRY_BASE_DELAY_MS;
16917
+ const wait = options.wait ?? sleep;
16918
+ const random = options.random ?? Math.random;
16919
+ const now = options.now ?? Date.now;
16920
+ for (let attempt = 1; ; attempt += 1) {
16921
+ try {
16922
+ return await operation();
16923
+ } catch (error) {
16924
+ const cancelled = options.signal?.aborted === true || isAbortError(error);
16925
+ if (cancelled || attempt >= attempts || !isRetryableError(error)) {
16926
+ throw error;
16927
+ }
16928
+ const backoffMs = baseDelayMs * 2 ** (attempt - 1);
16929
+ const delayMs = resolveRetryDelayMs(error, backoffMs, now(), random);
16930
+ options.onRetry?.(attempt, error, delayMs);
16931
+ await wait(delayMs, options.signal);
16932
+ }
16933
+ }
16934
+ }
16935
+
16936
+ // src/agent/message.ts
16937
+ function createTextContent(value) {
16938
+ return {
16939
+ type: "text",
16940
+ text: value
16941
+ };
16942
+ }
16943
+ function createUserMessage(input) {
16944
+ return {
16945
+ role: "user",
16946
+ content: [createTextContent(input)],
16947
+ timestamp: Date.now()
16948
+ };
16949
+ }
16950
+ function messageText(message) {
16951
+ return message.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
16952
+ }
16953
+ function isTextContent(block) {
16954
+ return block.type === "text";
16955
+ }
16956
+
16957
+ // src/agent/model/stream.ts
16958
+ function createStreamIdleWatchdog(parent, idleMs = STREAM_IDLE_TIMEOUT_MS) {
16959
+ const controller = new AbortController();
16960
+ let timedOut = false;
16961
+ let timer = null;
16962
+ let disposed = false;
16963
+ const clear = () => {
16964
+ if (timer) {
16965
+ clearTimeout(timer);
16966
+ timer = null;
16967
+ }
16968
+ };
16969
+ const arm = () => {
16970
+ clear();
16971
+ if (disposed || controller.signal.aborted) {
16972
+ return;
16973
+ }
16974
+ timer = setTimeout(() => {
16975
+ timedOut = true;
16976
+ controller.abort();
16977
+ }, idleMs);
16978
+ };
16979
+ const onParentAbort = () => {
16980
+ clear();
16981
+ controller.abort();
16982
+ };
16983
+ if (parent?.aborted) {
16984
+ controller.abort();
16985
+ } else {
16986
+ parent?.addEventListener("abort", onParentAbort, { once: true });
16987
+ arm();
16988
+ }
16989
+ return {
16990
+ signal: controller.signal,
16991
+ touch: arm,
16992
+ timedOut: () => timedOut,
16993
+ dispose: () => {
16994
+ disposed = true;
16995
+ clear();
16996
+ parent?.removeEventListener("abort", onParentAbort);
16997
+ }
16998
+ };
16999
+ }
17000
+ function classifyStreamFailure(error, watchdog, signal, idleMs = STREAM_IDLE_TIMEOUT_MS) {
17001
+ if (watchdog.timedOut() && signal?.aborted !== true) {
17002
+ return new StreamIdleTimeoutError(idleMs);
17003
+ }
17004
+ return error;
17005
+ }
17006
+ function assertStreamNotIdleTimedOut(watchdog, idleMs, signal) {
17007
+ if (watchdog.timedOut() && signal?.aborted !== true) {
17008
+ throw new StreamIdleTimeoutError(idleMs);
17009
+ }
17010
+ }
17011
+ function createDeltaEmitter(onDelta) {
17012
+ let any = false;
17013
+ return {
17014
+ onDelta: onDelta ? (delta) => {
17015
+ any = true;
17016
+ onDelta(delta);
17017
+ } : void 0,
17018
+ emittedAny: () => any
17019
+ };
17020
+ }
17021
+ function mapFinishReason(reason) {
17022
+ if (reason === "tool_calls" || reason === "function_call") {
17023
+ return "toolUse";
17024
+ }
17025
+ if (reason === "length") {
17026
+ return "length";
17027
+ }
17028
+ if (reason === "content_filter") {
17029
+ return "error";
17030
+ }
17031
+ return "stop";
17032
+ }
17033
+ async function collectOpenAIStream(chunks, onDelta, onChunk) {
17034
+ let text = "";
17035
+ let finishReason = null;
17036
+ let usage = null;
17037
+ const toolCalls = /* @__PURE__ */ new Map();
17038
+ for await (const chunk of chunks) {
17039
+ onChunk?.();
17040
+ if (chunk.usage) {
17041
+ usage = chunk.usage;
17042
+ }
17043
+ const choice = chunk.choices?.[0];
17044
+ if (!choice) {
17045
+ continue;
17046
+ }
17047
+ const delta = choice.delta;
17048
+ if (delta?.content) {
17049
+ text += delta.content;
17050
+ onDelta?.(delta.content);
17051
+ }
17052
+ for (const call of delta?.tool_calls ?? []) {
17053
+ const index = call.index ?? 0;
17054
+ const entry = toolCalls.get(index) ?? { id: "", name: "", args: "" };
17055
+ if (call.id) {
17056
+ entry.id = call.id;
17057
+ }
17058
+ if (call.function?.name) {
17059
+ entry.name += call.function.name;
17060
+ }
17061
+ if (call.function?.arguments) {
17062
+ entry.args += call.function.arguments;
17063
+ }
17064
+ toolCalls.set(index, entry);
17065
+ }
17066
+ if (choice.finish_reason) {
17067
+ finishReason = choice.finish_reason;
17068
+ }
17069
+ }
17070
+ const content = [];
17071
+ if (text) {
17072
+ content.push(createTextContent(text));
17073
+ }
17074
+ for (const [index, call] of [...toolCalls.entries()].sort((a, b) => a[0] - b[0])) {
17075
+ let args = {};
17076
+ if (call.args) {
17077
+ try {
17078
+ args = JSON.parse(call.args);
17079
+ } catch {
17080
+ args = {};
17081
+ }
17082
+ }
17083
+ content.push({
17084
+ type: "toolCall",
17085
+ id: call.id || `call_${index}`,
17086
+ name: call.name,
17087
+ arguments: args
17088
+ });
17089
+ }
17090
+ return {
17091
+ role: "assistant",
17092
+ content,
17093
+ stopReason: mapFinishReason(finishReason),
17094
+ usage: {
17095
+ input: usage?.prompt_tokens ?? 0,
17096
+ output: usage?.completion_tokens ?? 0,
17097
+ totalTokens: usage?.total_tokens ?? 0
17098
+ },
17099
+ timestamp: Date.now()
17100
+ };
17101
+ }
17102
+
17103
+ // src/agent/model/response.ts
17104
+ function createErrorResponse(error, signal, label = "\u6A21\u578B\u8C03\u7528") {
17105
+ if (signal?.aborted || isAbortError(error)) {
17106
+ return {
17107
+ role: "assistant",
17108
+ content: [createTextContent(`${label}\u5DF2\u53D6\u6D88`)],
17109
+ stopReason: "aborted",
17110
+ usage: { input: 0, output: 0, totalTokens: 0 },
17111
+ errorMessage: "aborted",
17112
+ timestamp: Date.now()
17113
+ };
17114
+ }
17115
+ return {
17116
+ role: "assistant",
17117
+ content: [createTextContent(`${label}\u5931\u8D25\uFF1A${describeError(error)}`)],
17118
+ stopReason: "error",
17119
+ usage: { input: 0, output: 0, totalTokens: 0 },
17120
+ errorMessage: describeError(error),
17121
+ timestamp: Date.now()
17122
+ };
17123
+ }
17124
+
16699
17125
  // node_modules/.pnpm/openai@7.15.0/node_modules/openai/internal/tslib.mjs
16700
17126
  function __classPrivateFieldSet(receiver, state2, value, kind, f) {
16701
17127
  if (kind === "m")
@@ -16727,7 +17153,7 @@ var uuid4 = function() {
16727
17153
  };
16728
17154
 
16729
17155
  // node_modules/.pnpm/openai@7.15.0/node_modules/openai/internal/errors.mjs
16730
- function isAbortError(err) {
17156
+ function isAbortError2(err) {
16731
17157
  return typeof err === "object" && err !== null && // Spec-compliant fetch implementations
16732
17158
  ("name" in err && err.name === "AbortError" || // Expo fetch
16733
17159
  "message" in err && String(err.message).includes("FetchRequestCanceledException"));
@@ -16936,7 +17362,7 @@ var safeJSON = (text) => {
16936
17362
  };
16937
17363
 
16938
17364
  // node_modules/.pnpm/openai@7.15.0/node_modules/openai/internal/utils/sleep.mjs
16939
- var sleep = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
17365
+ var sleep2 = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
16940
17366
 
16941
17367
  // node_modules/.pnpm/openai@7.15.0/node_modules/openai/internal/shims.mjs
16942
17368
  function getDefaultFetch() {
@@ -17321,7 +17747,7 @@ var _Stream_client;
17321
17747
  var _Stream_isTeeBranch;
17322
17748
  var _Stream_cancelIterator;
17323
17749
  function isTransportAbortError(error) {
17324
- return !(error instanceof APIError) && isAbortError(error);
17750
+ return !(error instanceof APIError) && isAbortError2(error);
17325
17751
  }
17326
17752
  function createStreamTeeQueue() {
17327
17753
  let entries = [];
@@ -17522,7 +17948,7 @@ var Stream = class _Stream {
17522
17948
  }
17523
17949
  done = true;
17524
17950
  } catch (e) {
17525
- if (controller.signal.aborted || isAbortError(e)) {
17951
+ if (controller.signal.aborted || isAbortError2(e)) {
17526
17952
  return;
17527
17953
  }
17528
17954
  throw e;
@@ -17674,7 +18100,7 @@ function createAbortableSSESource(body, signal) {
17674
18100
  }
17675
18101
  return { value: result.value, done: false };
17676
18102
  } catch (error) {
17677
- if (signal.aborted && (isAbortError(error) || error === signal.reason)) {
18103
+ if (signal.aborted && (isAbortError2(error) || error === signal.reason)) {
17678
18104
  return ended;
17679
18105
  }
17680
18106
  throw error;
@@ -17781,7 +18207,7 @@ async function* _iterSSEMessages(response, controller) {
17781
18207
  }
17782
18208
  } catch (error) {
17783
18209
  failed = true;
17784
- if (!signal.aborted || !isAbortError(error) && error !== signal.reason) {
18210
+ if (!signal.aborted || !isAbortError2(error) && error !== signal.reason) {
17785
18211
  throw error;
17786
18212
  }
17787
18213
  } finally {
@@ -17930,7 +18356,7 @@ async function defaultParseResponse(client, props) {
17930
18356
  return body;
17931
18357
  }
17932
18358
  function asAbortError(error, signal) {
17933
- if (!signal.aborted || error !== signal.reason || isAbortError(error)) {
18359
+ if (!signal.aborted || error !== signal.reason || isAbortError2(error)) {
17934
18360
  return error;
17935
18361
  }
17936
18362
  const message = "This operation was aborted";
@@ -29873,7 +30299,7 @@ async function pollWithResponse(retrieve, intermediateStatuses, terminalStatuses
29873
30299
  }
29874
30300
  }
29875
30301
  const signal = options && Object.prototype.propertyIsEnumerable.call(options, "signal") ? options.signal : void 0;
29876
- await (signal ? sleepUntilAborted(sleepInterval, signal) : sleep(sleepInterval));
30302
+ await (signal ? sleepUntilAborted(sleepInterval, signal) : sleep2(sleepInterval));
29877
30303
  } else if (terminalStatuses.includes(status)) {
29878
30304
  return data;
29879
30305
  }
@@ -30534,7 +30960,7 @@ async function waitForFileProcessing(resource, id, pollInterval, maxWait) {
30534
30960
  const start = performance.now();
30535
30961
  let file = await resource.retrieve(id);
30536
30962
  while (!file.status || !terminalStates.has(file.status)) {
30537
- await sleep(pollInterval);
30963
+ await sleep2(pollInterval);
30538
30964
  file = await resource.retrieve(id);
30539
30965
  if (performance.now() - start > maxWait) {
30540
30966
  throw new APIConnectionTimeoutError({
@@ -34299,7 +34725,7 @@ var OpenAI = class {
34299
34725
  if (callerSignal?.aborted || req.signal?.aborted) {
34300
34726
  throw this._makeUserAbortError(callerSignal?.aborted ? callerSignal : req.signal);
34301
34727
  }
34302
- const isTimeout = isAbortError(response) || /timed? ?out/i.test(String(response) + ("cause" in response ? String(response.cause) : ""));
34728
+ const isTimeout = isAbortError2(response) || /timed? ?out/i.test(String(response) + ("cause" in response ? String(response.cause) : ""));
34303
34729
  if (retriesRemaining && !hasStreamingBody && (!x509Authentication || isTransientX509ConnectionError2(response))) {
34304
34730
  loggerFor(this).info(`[${requestLogID}] connection ${isTimeout ? "timed out" : "failed"} - ${retryMessage}`);
34305
34731
  loggerFor(this).debug(`[${requestLogID}] connection ${isTimeout ? "timed out" : "failed"} (${retryMessage})`, formatRequestDetails({
@@ -34526,7 +34952,7 @@ var OpenAI = class {
34526
34952
  if (x509Authentication) {
34527
34953
  await x509Authentication.waitForRetry(timeoutMillis, x509Authentication.effectiveSignal());
34528
34954
  } else {
34529
- await sleep(timeoutMillis);
34955
+ await sleep2(timeoutMillis);
34530
34956
  }
34531
34957
  return this.makeRequest(options, retriesRemaining - 1, requestLogID);
34532
34958
  }
@@ -34773,30 +35199,6 @@ var brand_privateBedrockClient = Symbol.for("openai.privateBedrockClient");
34773
35199
  var _a4;
34774
35200
  _a4 = brand_privateBedrockClient;
34775
35201
 
34776
- // src/agent/model.ts
34777
- init_sdk();
34778
-
34779
- // src/agent/message.ts
34780
- function createTextContent(value) {
34781
- return {
34782
- type: "text",
34783
- text: value
34784
- };
34785
- }
34786
- function createUserMessage(input) {
34787
- return {
34788
- role: "user",
34789
- content: [createTextContent(input)],
34790
- timestamp: Date.now()
34791
- };
34792
- }
34793
- function messageText(message) {
34794
- return message.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
34795
- }
34796
- function isTextContent(block) {
34797
- return block.type === "text";
34798
- }
34799
-
34800
35202
  // node_modules/.pnpm/chalk@6.0.0/node_modules/chalk/source/utilities.js
34801
35203
  function stringReplaceAll(string, substring, postfix) {
34802
35204
  let index = string.indexOf(substring);
@@ -35369,7 +35771,7 @@ var LEVEL_WEIGHT = {
35369
35771
  };
35370
35772
  var DEFAULT_LOG_LEVEL = "info";
35371
35773
  var LOG_LEVEL_ENV = "MINI_PI_LOG_LEVEL";
35372
- function parseLogLevel3(value) {
35774
+ function parseLogLevel2(value) {
35373
35775
  if (typeof value !== "string") {
35374
35776
  return void 0;
35375
35777
  }
@@ -35407,7 +35809,7 @@ var defaultLogSink = (entry) => {
35407
35809
  process.stderr.write(`${paint(entry.level, LEVEL_LABEL[entry.level])}${scope} ${body}
35408
35810
  `);
35409
35811
  };
35410
- var currentLevel = parseLogLevel3(process.env[LOG_LEVEL_ENV]) ?? DEFAULT_LOG_LEVEL;
35812
+ var currentLevel = parseLogLevel2(process.env[LOG_LEVEL_ENV]) ?? DEFAULT_LOG_LEVEL;
35411
35813
  var currentSink = defaultLogSink;
35412
35814
  function createLogger(scope) {
35413
35815
  const emit = (level) => (message, ...args) => {
@@ -35426,10 +35828,7 @@ function createLogger(scope) {
35426
35828
  }
35427
35829
  var logger = createLogger();
35428
35830
 
35429
- // src/agent/model.ts
35430
- var REQUEST_TIMEOUT_MS = 12e4;
35431
- var STREAM_IDLE_TIMEOUT_MS = 12e4;
35432
- var DEFAULT_MAX_TOKENS = 8192;
35831
+ // src/agent/model/openai.ts
35433
35832
  var REASONING_MODEL_PATTERN = /^o\d/i;
35434
35833
  function buildOpenAIRequest(params) {
35435
35834
  const payload = {
@@ -35463,378 +35862,10 @@ function isUnsupportedMaxTokensError(error) {
35463
35862
  message
35464
35863
  );
35465
35864
  }
35466
- function buildAnthropicRequest(params) {
35467
- return {
35468
- model: params.model,
35469
- max_tokens: Math.max(1, Math.floor(params.maxTokens ?? DEFAULT_MAX_TOKENS)),
35470
- system: params.system,
35471
- messages: params.messages,
35472
- tools: params.tools.length > 0 ? params.tools : void 0
35473
- };
35474
- }
35475
- var MAX_REQUEST_ATTEMPTS = 3;
35476
- var RETRY_BASE_DELAY_MS = 1e3;
35477
- var STREAM_IDLE_TIMEOUT_ERROR_NAME = "StreamIdleTimeoutError";
35478
- var StreamIdleTimeoutError = class extends Error {
35479
- constructor(idleMs) {
35480
- super(`\u6D41\u5F0F\u54CD\u5E94 ${idleMs}ms \u5185\u6CA1\u6709\u6536\u5230\u4EFB\u4F55\u6570\u636E\uFF0C\u5DF2\u4E2D\u6B62\u672C\u6B21\u8BF7\u6C42`);
35481
- this.name = STREAM_IDLE_TIMEOUT_ERROR_NAME;
35482
- }
35483
- };
35484
- function isStreamIdleTimeoutError(error) {
35485
- let current = error;
35486
- for (let depth = 0; current && typeof current === "object" && depth < 5; depth += 1) {
35487
- const candidate = current;
35488
- if (candidate.name === STREAM_IDLE_TIMEOUT_ERROR_NAME) {
35489
- return true;
35490
- }
35491
- current = candidate.cause;
35492
- }
35493
- return false;
35494
- }
35495
- function createStreamIdleWatchdog(parent, idleMs = STREAM_IDLE_TIMEOUT_MS) {
35496
- const controller = new AbortController();
35497
- let timedOut = false;
35498
- let timer = null;
35499
- let disposed = false;
35500
- const clear = () => {
35501
- if (timer) {
35502
- clearTimeout(timer);
35503
- timer = null;
35504
- }
35505
- };
35506
- const arm = () => {
35507
- clear();
35508
- if (disposed || controller.signal.aborted) {
35509
- return;
35510
- }
35511
- timer = setTimeout(() => {
35512
- timedOut = true;
35513
- controller.abort();
35514
- }, idleMs);
35515
- };
35516
- const onParentAbort = () => {
35517
- clear();
35518
- controller.abort();
35519
- };
35520
- if (parent?.aborted) {
35521
- controller.abort();
35522
- } else {
35523
- parent?.addEventListener("abort", onParentAbort, { once: true });
35524
- arm();
35525
- }
35526
- return {
35527
- signal: controller.signal,
35528
- touch: arm,
35529
- timedOut: () => timedOut,
35530
- dispose: () => {
35531
- disposed = true;
35532
- clear();
35533
- parent?.removeEventListener("abort", onParentAbort);
35534
- }
35535
- };
35536
- }
35537
- function resolveMaxTokens(value) {
35538
- return typeof value === "number" && Number.isFinite(value) && value >= 1 ? Math.floor(value) : DEFAULT_MAX_TOKENS;
35539
- }
35540
- function resolveStreamIdleTimeout(value) {
35541
- return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : STREAM_IDLE_TIMEOUT_MS;
35542
- }
35543
- function classifyStreamFailure(error, watchdog, signal, idleMs = STREAM_IDLE_TIMEOUT_MS) {
35544
- if (watchdog.timedOut() && signal?.aborted !== true) {
35545
- return new StreamIdleTimeoutError(idleMs);
35546
- }
35547
- return error;
35548
- }
35549
- function assertStreamNotIdleTimedOut(watchdog, idleMs, signal) {
35550
- if (watchdog.timedOut() && signal?.aborted !== true) {
35551
- throw new StreamIdleTimeoutError(idleMs);
35552
- }
35553
- }
35554
- var PARTIAL_STREAM_ERROR_NAME = "PartialStreamInterruptedError";
35555
- var PartialStreamInterruptedError = class extends Error {
35556
- constructor(cause) {
35557
- super(
35558
- `\u6D41\u5F0F\u54CD\u5E94\u5728\u5DF2\u8F93\u51FA\u90E8\u5206\u5185\u5BB9\u540E\u4E2D\u65AD\uFF0C\u672A\u81EA\u52A8\u91CD\u8BD5\uFF08\u907F\u514D\u91CD\u590D\u8F93\u51FA\u4E0E\u91CD\u590D\u8BA1\u8D39\uFF09\uFF1A${describeError(cause)}`
35559
- );
35560
- this.name = PARTIAL_STREAM_ERROR_NAME;
35561
- this.cause = cause;
35562
- }
35563
- };
35564
- function isPartialStreamInterrupted(error) {
35565
- let current = error;
35566
- for (let depth = 0; current && typeof current === "object" && depth < 5; depth += 1) {
35567
- const candidate = current;
35568
- if (candidate.name === PARTIAL_STREAM_ERROR_NAME) {
35569
- return true;
35570
- }
35571
- current = candidate.cause;
35572
- }
35573
- return false;
35574
- }
35575
- function toNonRetryableIfPartialStream(error, emittedContent) {
35576
- if (emittedContent && isRetryableError2(error)) {
35577
- return new PartialStreamInterruptedError(error);
35578
- }
35579
- return error;
35580
- }
35581
- var RETRYABLE_ERROR_CODES = /* @__PURE__ */ new Set([
35582
- "ETIMEDOUT",
35583
- "ECONNRESET",
35584
- "ECONNREFUSED",
35585
- "EPIPE",
35586
- "ENOTFOUND",
35587
- "EAI_AGAIN",
35588
- "UND_ERR_CONNECT_TIMEOUT",
35589
- "UND_ERR_SOCKET"
35590
- ]);
35591
- var RETRYABLE_ERROR_NAME_PATTERNS = [
35592
- /^APIConnectionError$/,
35593
- /^APIConnectionTimeoutError$/,
35594
- /^APIConnectionTimeoutError\d*$/
35595
- ];
35596
- var ERROR_CAUSE_MAX_DEPTH = 5;
35597
- function findInErrorChain(error, read) {
35598
- let current = error;
35599
- for (let depth = 0; current && typeof current === "object" && depth < ERROR_CAUSE_MAX_DEPTH; depth += 1) {
35600
- const candidate = current;
35601
- const value = read(candidate);
35602
- if (value !== void 0) {
35603
- return value;
35604
- }
35605
- current = candidate.cause;
35606
- }
35607
- return void 0;
35608
- }
35609
- function isRetryableError2(error) {
35610
- if (!error || typeof error !== "object") {
35611
- return false;
35612
- }
35613
- if (isPartialStreamInterrupted(error)) {
35614
- return false;
35615
- }
35616
- if (isStreamIdleTimeoutError(error)) {
35617
- return true;
35618
- }
35619
- const name = findInErrorChain(
35620
- error,
35621
- (candidate) => typeof candidate.name === "string" ? candidate.name : void 0
35622
- );
35623
- if (name && RETRYABLE_ERROR_NAME_PATTERNS.some((pattern) => pattern.test(name))) {
35624
- return true;
35625
- }
35626
- const status = findInErrorChain(
35627
- error,
35628
- (candidate) => typeof candidate.status === "number" ? candidate.status : void 0
35629
- );
35630
- if (status !== void 0) {
35631
- return status === 429 || status === 408 || status >= 500;
35632
- }
35633
- const code = findInErrorChain(
35634
- error,
35635
- (candidate) => typeof candidate.code === "string" ? candidate.code : void 0
35636
- );
35637
- if (code !== void 0 && RETRYABLE_ERROR_CODES.has(code)) {
35638
- return true;
35639
- }
35640
- return false;
35641
- }
35642
- var RETRY_JITTER_RATIO = 0.2;
35643
- function applyRetryJitter(delayMs, random = Math.random) {
35644
- const factor = 1 - RETRY_JITTER_RATIO + random() * RETRY_JITTER_RATIO * 2;
35645
- return Math.max(0, Math.round(delayMs * factor));
35646
- }
35647
- function readHeader(headers, name) {
35648
- if (!headers || typeof headers !== "object") {
35649
- return null;
35650
- }
35651
- const getter = headers.get;
35652
- if (typeof getter === "function") {
35653
- const value = getter.call(headers, name);
35654
- return typeof value === "string" ? value : null;
35655
- }
35656
- const record = headers;
35657
- for (const key of [name, name.toLowerCase(), name.toUpperCase()]) {
35658
- const value = record[key];
35659
- if (typeof value === "string") {
35660
- return value;
35661
- }
35662
- }
35663
- return null;
35664
- }
35665
- function retryAfterMsFromError(error, now = Date.now()) {
35666
- const headers = findInErrorChain(
35667
- error,
35668
- (candidate) => candidate.headers && typeof candidate.headers === "object" ? candidate.headers : void 0
35669
- );
35670
- if (!headers) {
35671
- return void 0;
35672
- }
35673
- const ms = readHeader(headers, "retry-after-ms");
35674
- if (ms !== null && ms.trim() !== "") {
35675
- const parsed = Number(ms);
35676
- if (Number.isFinite(parsed) && parsed >= 0) {
35677
- return parsed;
35678
- }
35679
- }
35680
- const raw = readHeader(headers, "retry-after");
35681
- if (raw !== null && raw.trim() !== "") {
35682
- const seconds = Number(raw);
35683
- if (Number.isFinite(seconds) && seconds >= 0) {
35684
- return seconds * 1e3;
35685
- }
35686
- const date = Date.parse(raw);
35687
- if (Number.isFinite(date)) {
35688
- return Math.max(0, date - now);
35689
- }
35690
- }
35691
- return void 0;
35692
- }
35693
- function resolveRetryDelayMs(error, backoffMs, now = Date.now(), random = Math.random) {
35694
- const serverMs = retryAfterMsFromError(error, now);
35695
- const base = serverMs === void 0 ? backoffMs : Math.max(serverMs, backoffMs);
35696
- return applyRetryJitter(base, random);
35697
- }
35698
- function sleep3(ms, signal) {
35699
- return new Promise((resolve6, reject) => {
35700
- if (signal?.aborted) {
35701
- reject(new Error("aborted"));
35702
- return;
35703
- }
35704
- const timer = setTimeout(() => {
35705
- signal?.removeEventListener("abort", onAbort);
35706
- resolve6();
35707
- }, ms);
35708
- const onAbort = () => {
35709
- clearTimeout(timer);
35710
- reject(new Error("aborted"));
35711
- };
35712
- signal?.addEventListener("abort", onAbort, { once: true });
35713
- });
35714
- }
35715
- async function withRetry(operation, options = {}) {
35716
- const attempts = Math.max(1, options.attempts ?? MAX_REQUEST_ATTEMPTS);
35717
- const baseDelayMs = options.baseDelayMs ?? RETRY_BASE_DELAY_MS;
35718
- const wait = options.wait ?? sleep3;
35719
- const random = options.random ?? Math.random;
35720
- const now = options.now ?? Date.now;
35721
- for (let attempt = 1; ; attempt += 1) {
35722
- try {
35723
- return await operation();
35724
- } catch (error) {
35725
- const cancelled = options.signal?.aborted === true || isAbortError3(error);
35726
- if (cancelled || attempt >= attempts || !isRetryableError2(error)) {
35727
- throw error;
35728
- }
35729
- const backoffMs = baseDelayMs * 2 ** (attempt - 1);
35730
- const delayMs = resolveRetryDelayMs(error, backoffMs, now(), random);
35731
- options.onRetry?.(attempt, error, delayMs);
35732
- await wait(delayMs, options.signal);
35733
- }
35734
- }
35735
- }
35736
- var ABORT_ERROR_PATTERNS = [/^AbortError$/i, /APIUserAbortError/i];
35737
- function isAbortError3(error) {
35738
- let current = error;
35739
- for (let depth = 0; current && typeof current === "object" && depth < 5; depth += 1) {
35740
- const candidate = current;
35741
- for (const value of [candidate.name, candidate.constructor?.name]) {
35742
- if (typeof value === "string" && ABORT_ERROR_PATTERNS.some((pattern) => pattern.test(value))) {
35743
- return true;
35744
- }
35745
- }
35746
- current = candidate.cause;
35747
- }
35748
- return false;
35749
- }
35750
- function describeError(error) {
35751
- return error instanceof Error ? error.message : String(error);
35752
- }
35753
35865
  function isUnsupportedStreamOptionsError(error) {
35754
35866
  const message = error instanceof Error ? error.message : String(error);
35755
35867
  return /stream_options/i.test(message);
35756
35868
  }
35757
- function mapFinishReason(reason) {
35758
- if (reason === "tool_calls" || reason === "function_call") {
35759
- return "toolUse";
35760
- }
35761
- if (reason === "length") {
35762
- return "length";
35763
- }
35764
- if (reason === "content_filter") {
35765
- return "error";
35766
- }
35767
- return "stop";
35768
- }
35769
- async function collectOpenAIStream(chunks, onDelta, onChunk) {
35770
- let text = "";
35771
- let finishReason = null;
35772
- let usage = null;
35773
- const toolCalls = /* @__PURE__ */ new Map();
35774
- for await (const chunk of chunks) {
35775
- onChunk?.();
35776
- if (chunk.usage) {
35777
- usage = chunk.usage;
35778
- }
35779
- const choice = chunk.choices?.[0];
35780
- if (!choice) {
35781
- continue;
35782
- }
35783
- const delta = choice.delta;
35784
- if (delta?.content) {
35785
- text += delta.content;
35786
- onDelta?.(delta.content);
35787
- }
35788
- for (const call of delta?.tool_calls ?? []) {
35789
- const index = call.index ?? 0;
35790
- const entry = toolCalls.get(index) ?? { id: "", name: "", args: "" };
35791
- if (call.id) {
35792
- entry.id = call.id;
35793
- }
35794
- if (call.function?.name) {
35795
- entry.name += call.function.name;
35796
- }
35797
- if (call.function?.arguments) {
35798
- entry.args += call.function.arguments;
35799
- }
35800
- toolCalls.set(index, entry);
35801
- }
35802
- if (choice.finish_reason) {
35803
- finishReason = choice.finish_reason;
35804
- }
35805
- }
35806
- const content = [];
35807
- if (text) {
35808
- content.push(createTextContent(text));
35809
- }
35810
- for (const [index, call] of [...toolCalls.entries()].sort((a, b) => a[0] - b[0])) {
35811
- let args = {};
35812
- if (call.args) {
35813
- try {
35814
- args = JSON.parse(call.args);
35815
- } catch {
35816
- args = {};
35817
- }
35818
- }
35819
- content.push({
35820
- type: "toolCall",
35821
- id: call.id || `call_${index}`,
35822
- name: call.name,
35823
- arguments: args
35824
- });
35825
- }
35826
- return {
35827
- role: "assistant",
35828
- content,
35829
- stopReason: mapFinishReason(finishReason),
35830
- usage: {
35831
- input: usage?.prompt_tokens ?? 0,
35832
- output: usage?.completion_tokens ?? 0,
35833
- totalTokens: usage?.total_tokens ?? 0
35834
- },
35835
- timestamp: Date.now()
35836
- };
35837
- }
35838
35869
  var OpenAIModel = class {
35839
35870
  client;
35840
35871
  model;
@@ -35867,10 +35898,10 @@ var OpenAIModel = class {
35867
35898
  try {
35868
35899
  return await this.requestWithFallbacks(input, messages, tools);
35869
35900
  } catch (error) {
35870
- if (!isAbortError3(error) && !input.signal?.aborted) {
35901
+ if (!isAbortError(error) && !input.signal?.aborted) {
35871
35902
  logger.error("OpenAI API error:", error);
35872
35903
  }
35873
- return this.createErrorResponse(error, input.signal);
35904
+ return createErrorResponse(error, input.signal);
35874
35905
  }
35875
35906
  }
35876
35907
  /**
@@ -35913,12 +35944,11 @@ var OpenAIModel = class {
35913
35944
  }
35914
35945
  /** 流式请求:逐段回调 onDelta,最终拼装成完整消息 */
35915
35946
  async streamCompletion(input, messages, tools) {
35916
- const watchdog = createStreamIdleWatchdog(input.signal, this.streamIdleTimeoutMs);
35917
- const emitted = { any: false };
35918
- const onDelta = input.onDelta ? (delta) => {
35919
- emitted.any = true;
35920
- input.onDelta?.(delta);
35921
- } : void 0;
35947
+ const watchdog = createStreamIdleWatchdog(
35948
+ input.signal,
35949
+ this.streamIdleTimeoutMs
35950
+ );
35951
+ const emitter = createDeltaEmitter(input.onDelta);
35922
35952
  try {
35923
35953
  const stream2 = await this.client.chat.completions.create(
35924
35954
  buildOpenAIRequest({
@@ -35931,7 +35961,7 @@ var OpenAIModel = class {
35931
35961
  }),
35932
35962
  { signal: watchdog.signal, timeout: REQUEST_TIMEOUT_MS }
35933
35963
  );
35934
- const message = await collectOpenAIStream(stream2, onDelta, watchdog.touch);
35964
+ const message = await collectOpenAIStream(stream2, emitter.onDelta, watchdog.touch);
35935
35965
  assertStreamNotIdleTimedOut(watchdog, this.streamIdleTimeoutMs, input.signal);
35936
35966
  return message;
35937
35967
  } catch (error) {
@@ -35941,31 +35971,11 @@ var OpenAIModel = class {
35941
35971
  input.signal,
35942
35972
  this.streamIdleTimeoutMs
35943
35973
  );
35944
- throw toNonRetryableIfPartialStream(failure, emitted.any);
35974
+ throw toNonRetryableIfPartialStream(failure, emitter.emittedAny());
35945
35975
  } finally {
35946
35976
  watchdog.dispose();
35947
35977
  }
35948
35978
  }
35949
- createErrorResponse(error, signal) {
35950
- if (signal?.aborted || isAbortError3(error)) {
35951
- return {
35952
- role: "assistant",
35953
- content: [createTextContent("\u6A21\u578B\u8C03\u7528\u5DF2\u53D6\u6D88")],
35954
- stopReason: "aborted",
35955
- usage: { input: 0, output: 0, totalTokens: 0 },
35956
- errorMessage: "aborted",
35957
- timestamp: Date.now()
35958
- };
35959
- }
35960
- return {
35961
- role: "assistant",
35962
- content: [createTextContent(`\u6A21\u578B\u8C03\u7528\u5931\u8D25\uFF1A${describeError(error)}`)],
35963
- stopReason: "error",
35964
- usage: { input: 0, output: 0, totalTokens: 0 },
35965
- errorMessage: describeError(error),
35966
- timestamp: Date.now()
35967
- };
35968
- }
35969
35979
  convertMessages(systemPrompt, messages) {
35970
35980
  const result = [];
35971
35981
  result.push({
@@ -36021,6 +36031,18 @@ var OpenAIModel = class {
36021
36031
  function createOpenAIModel(config) {
36022
36032
  return new OpenAIModel(config);
36023
36033
  }
36034
+
36035
+ // src/agent/model/anthropic.ts
36036
+ init_sdk();
36037
+ function buildAnthropicRequest(params) {
36038
+ return {
36039
+ model: params.model,
36040
+ max_tokens: Math.max(1, Math.floor(params.maxTokens ?? DEFAULT_MAX_TOKENS)),
36041
+ system: params.system,
36042
+ messages: params.messages,
36043
+ tools: params.tools.length > 0 ? params.tools : void 0
36044
+ };
36045
+ }
36024
36046
  var AnthropicModel = class {
36025
36047
  client;
36026
36048
  model;
@@ -36056,16 +36078,19 @@ var AnthropicModel = class {
36056
36078
  );
36057
36079
  return this.convertResponse(response);
36058
36080
  } catch (error) {
36059
- if (!isAbortError3(error) && !input.signal?.aborted) {
36081
+ if (!isAbortError(error) && !input.signal?.aborted) {
36060
36082
  logger.error("Anthropic API error:", error);
36061
36083
  }
36062
- return this.createErrorResponse(error, input.signal);
36084
+ return createErrorResponse(error, input.signal, "Anthropic \u6A21\u578B\u8C03\u7528");
36063
36085
  }
36064
36086
  }
36065
36087
  /** 流式请求:经 stream 事件逐段回调 onDelta,最终取回完整消息 */
36066
36088
  async streamCompletion(input, system, messages, tools) {
36067
- const watchdog = createStreamIdleWatchdog(input.signal, this.streamIdleTimeoutMs);
36068
- const emitted = { any: false };
36089
+ const watchdog = createStreamIdleWatchdog(
36090
+ input.signal,
36091
+ this.streamIdleTimeoutMs
36092
+ );
36093
+ const emitter = createDeltaEmitter(input.onDelta);
36069
36094
  try {
36070
36095
  const stream2 = this.client.messages.stream(
36071
36096
  buildAnthropicRequest({
@@ -36077,10 +36102,9 @@ var AnthropicModel = class {
36077
36102
  }),
36078
36103
  { signal: watchdog.signal, timeout: REQUEST_TIMEOUT_MS }
36079
36104
  );
36080
- if (input.onDelta) {
36105
+ if (emitter.onDelta) {
36081
36106
  stream2.on("text", (delta) => {
36082
- emitted.any = true;
36083
- input.onDelta?.(delta);
36107
+ emitter.onDelta?.(delta);
36084
36108
  });
36085
36109
  }
36086
36110
  stream2.on("streamEvent", () => watchdog.touch());
@@ -36094,7 +36118,7 @@ var AnthropicModel = class {
36094
36118
  input.signal,
36095
36119
  this.streamIdleTimeoutMs
36096
36120
  );
36097
- throw toNonRetryableIfPartialStream(failure, emitted.any);
36121
+ throw toNonRetryableIfPartialStream(failure, emitter.emittedAny());
36098
36122
  } finally {
36099
36123
  watchdog.dispose();
36100
36124
  }
@@ -36175,54 +36199,37 @@ var AnthropicModel = class {
36175
36199
  });
36176
36200
  }
36177
36201
  }
36178
- let stopReason = "stop";
36179
- if (response.stop_reason === "tool_use") {
36180
- stopReason = "toolUse";
36181
- } else if (response.stop_reason === "max_tokens") {
36182
- stopReason = "length";
36183
- } else if (response.stop_reason === "end_turn") {
36184
- stopReason = "stop";
36185
- } else {
36186
- stopReason = "error";
36187
- }
36188
- const usage = {
36189
- input: response.usage.input_tokens,
36190
- output: response.usage.output_tokens,
36191
- totalTokens: response.usage.input_tokens + response.usage.output_tokens
36192
- };
36193
36202
  return {
36194
36203
  role: "assistant",
36195
36204
  content,
36196
- stopReason,
36197
- usage,
36205
+ stopReason: mapStopReason(response.stop_reason),
36206
+ usage: {
36207
+ input: response.usage.input_tokens,
36208
+ output: response.usage.output_tokens,
36209
+ totalTokens: response.usage.input_tokens + response.usage.output_tokens
36210
+ },
36198
36211
  timestamp: Date.now()
36199
36212
  };
36200
36213
  }
36201
- createErrorResponse(error, signal) {
36202
- if (signal?.aborted || isAbortError3(error)) {
36203
- return {
36204
- role: "assistant",
36205
- content: [createTextContent("\u6A21\u578B\u8C03\u7528\u5DF2\u53D6\u6D88")],
36206
- stopReason: "aborted",
36207
- usage: { input: 0, output: 0, totalTokens: 0 },
36208
- errorMessage: "aborted",
36209
- timestamp: Date.now()
36210
- };
36211
- }
36212
- return {
36213
- role: "assistant",
36214
- content: [createTextContent(`Anthropic \u6A21\u578B\u8C03\u7528\u5931\u8D25\uFF1A${describeError(error)}`)],
36215
- stopReason: "error",
36216
- usage: { input: 0, output: 0, totalTokens: 0 },
36217
- timestamp: Date.now(),
36218
- errorMessage: describeError(error)
36219
- };
36220
- }
36221
36214
  };
36215
+ function mapStopReason(reason) {
36216
+ if (reason === "tool_use") {
36217
+ return "toolUse";
36218
+ }
36219
+ if (reason === "max_tokens") {
36220
+ return "length";
36221
+ }
36222
+ if (reason === "end_turn") {
36223
+ return "stop";
36224
+ }
36225
+ return "error";
36226
+ }
36222
36227
  function createAnthropicModel(config) {
36223
36228
  return new AnthropicModel(config);
36224
36229
  }
36225
- async function createModelFromProvider(config) {
36230
+
36231
+ // src/agent/model/factory.ts
36232
+ function createModelFromProvider(config) {
36226
36233
  switch (config.sdkType) {
36227
36234
  case "OpenAI":
36228
36235
  return createOpenAIModel({
@@ -37316,12 +37323,62 @@ async function readSearchableFile(absolutePath2) {
37316
37323
  }
37317
37324
  }
37318
37325
 
37326
+ // src/agent/tools/skill.ts
37327
+ var emptySkillSource = {
37328
+ loadSkill: () => null,
37329
+ loadAllMetadata: () => []
37330
+ };
37331
+ function loadSkillTool(source) {
37332
+ return {
37333
+ name: "load_skill",
37334
+ description: `Load the full content of a skill by its exact name. Skills are listed in the system prompt under "\u53EF\u7528 Skills" (name + description). Call this when the user's request matches a skill, then follow its instructions. Only names from that list are accepted; paths are not.`,
37335
+ parameters: {
37336
+ type: "object",
37337
+ properties: {
37338
+ name: {
37339
+ type: "string",
37340
+ description: 'Exact skill name from the "\u53EF\u7528 Skills" list, e.g. "cli-output-presentation".'
37341
+ }
37342
+ },
37343
+ required: ["name"]
37344
+ },
37345
+ async execute(args) {
37346
+ const name = stringArg(args.name, "").trim();
37347
+ if (!name) {
37348
+ return {
37349
+ content: [createTextContent("\u7F3A\u5C11 skill \u540D\u79F0\uFF1A\u8BF7\u63D0\u4F9B name \u53C2\u6570")],
37350
+ isError: true
37351
+ };
37352
+ }
37353
+ const skill = source.loadSkill(name);
37354
+ if (!skill) {
37355
+ const available = source.loadAllMetadata().map((s) => s.name);
37356
+ return {
37357
+ content: [
37358
+ createTextContent(
37359
+ `\u672A\u627E\u5230 skill: ${name}
37360
+ \u53EF\u7528\u7684 skill\uFF1A${available.length > 0 ? available.join("\u3001") : "\uFF08\u5F53\u524D\u6CA1\u6709\u53EF\u7528 skill\uFF09"}`
37361
+ )
37362
+ ],
37363
+ isError: true,
37364
+ details: { skill: name, available }
37365
+ };
37366
+ }
37367
+ return {
37368
+ content: [createTextContent(skill.content)],
37369
+ details: { skill: skill.name, chars: skill.content.length }
37370
+ };
37371
+ }
37372
+ };
37373
+ }
37374
+
37319
37375
  // src/agent/tools/registry.ts
37320
37376
  var READ_ONLY_TOOL_NAMES = [
37321
37377
  "list_files",
37322
37378
  "glob",
37323
37379
  "grep",
37324
- "read_file"
37380
+ "read_file",
37381
+ "load_skill"
37325
37382
  ];
37326
37383
  var READ_ONLY_TOOLS = new Set(READ_ONLY_TOOL_NAMES);
37327
37384
  var ToolRegistry = class _ToolRegistry {
@@ -37409,6 +37466,7 @@ function createToolRegistry(workspaceRoot, options) {
37409
37466
  globTool(workspaceRoot),
37410
37467
  grepTool(workspaceRoot),
37411
37468
  readFileTool(workspaceRoot),
37469
+ loadSkillTool(options?.skillLoader ?? emptySkillSource),
37412
37470
  writeFileTool(workspaceRoot),
37413
37471
  editFileTool(workspaceRoot),
37414
37472
  bashTool(workspaceRoot)
@@ -38263,10 +38321,11 @@ var GENERAL_INSTRUCTIONS = [
38263
38321
  "\u6240\u4EE5\u5FC5\u8981\u7684\u4E0A\u4E0B\u6587\uFF08\u8DEF\u5F84\u3001\u7B26\u53F7\u540D\u3001\u5173\u952E\u6570\u5B57\uFF09\u5FC5\u987B\u5199\u8FDB\u6765\u3002",
38264
38322
  "\u505A\u5B8C\u7684\u90E8\u5206\u548C\u6CA1\u505A\u5B8C\u7684\u90E8\u5206\u8981\u5206\u5F00\u8BF4\u6E05\u695A\uFF0C\u4E0D\u8981\u628A\u534A\u622A\u7ED3\u8BBA\u8BF4\u6210\u5DF2\u5B8C\u6210\u3002"
38265
38323
  ].join("\n");
38324
+ var DELEGATABLE_READ_ONLY_TOOLS = READ_ONLY_TOOL_NAMES.filter((name) => name !== "load_skill");
38266
38325
  var SUBAGENT_ROLE_PRESETS = {
38267
38326
  explore: {
38268
38327
  label: "explore",
38269
- tools: READ_ONLY_TOOL_NAMES,
38328
+ tools: DELEGATABLE_READ_ONLY_TOOLS,
38270
38329
  maxTurns: 20,
38271
38330
  // 纯读取,最容易被大范围检索拖长:给得比 implement 短
38272
38331
  timeoutMs: 12e4,
@@ -38275,7 +38334,7 @@ var SUBAGENT_ROLE_PRESETS = {
38275
38334
  },
38276
38335
  implement: {
38277
38336
  label: "implement",
38278
- tools: [...READ_ONLY_TOOL_NAMES, "write_file", "edit_file"],
38337
+ tools: [...DELEGATABLE_READ_ONLY_TOOLS, "write_file", "edit_file"],
38279
38338
  maxTurns: 40,
38280
38339
  // 要改文件还要自述验证(通常含一次测试/构建),最慢
38281
38340
  timeoutMs: 3e5,
@@ -38285,7 +38344,7 @@ var SUBAGENT_ROLE_PRESETS = {
38285
38344
  },
38286
38345
  review: {
38287
38346
  label: "review",
38288
- tools: READ_ONLY_TOOL_NAMES,
38347
+ tools: DELEGATABLE_READ_ONLY_TOOLS,
38289
38348
  maxTurns: 10,
38290
38349
  timeoutMs: 6e4,
38291
38350
  canEscalate: false,
@@ -38293,7 +38352,7 @@ var SUBAGENT_ROLE_PRESETS = {
38293
38352
  },
38294
38353
  general: {
38295
38354
  label: "general",
38296
- tools: READ_ONLY_TOOL_NAMES,
38355
+ tools: DELEGATABLE_READ_ONLY_TOOLS,
38297
38356
  maxTurns: 30,
38298
38357
  timeoutMs: 18e4,
38299
38358
  canEscalate: true,
@@ -39688,6 +39747,26 @@ var SettingsStore = class {
39688
39747
  this.settings.subagentTrace = enabled;
39689
39748
  await this.persist();
39690
39749
  }
39750
+ /**
39751
+ * 启动时是否检测新版本(`/update` 开关)。
39752
+ *
39753
+ * **默认开启**:与委派不同,更新检测的代价是"每天最多一次带超时的
39754
+ * 请求",而收益(不永远用旧版本)对每个用户都成立。想安静的人
39755
+ * `/update off` 一次即可,也不必先知道 settings.json 有这个字段。
39756
+ */
39757
+ async getUpdateCheckEnabled() {
39758
+ await this.initialize();
39759
+ return this.settings.updateCheck !== false;
39760
+ }
39761
+ /**
39762
+ * 开关启动时的版本更新检测。
39763
+ * @throws settings.json 处于损坏状态时拒绝写入
39764
+ */
39765
+ async setUpdateCheckEnabled(enabled) {
39766
+ await this.initialize();
39767
+ this.settings.updateCheck = enabled;
39768
+ await this.persist();
39769
+ }
39691
39770
  /**
39692
39771
  * 获取所有设置
39693
39772
  */
@@ -40156,6 +40235,10 @@ var COMMANDS = [
40156
40235
  usage: "/subagent [on|off|trace on|trace off]",
40157
40236
  description: "\u5F00\u5173\u5B50 Agent \u59D4\u6D3E\uFF08\u9ED8\u8BA4\u5173\u95ED\uFF09\uFF1Btrace \u5B50\u9879\u5F00\u5173\u59D4\u6D3E\u6392\u969C\u843D\u76D8"
40158
40237
  },
40238
+ {
40239
+ usage: "/update [on|off]",
40240
+ description: "\u68C0\u67E5\u662F\u5426\u6709\u65B0\u7248\u672C\uFF08\u4E0D\u5E26\u53C2\u6570\u7ACB\u5373\u68C0\u67E5\uFF09\uFF1Bon/off \u5F00\u5173\u542F\u52A8\u68C0\u6D4B"
40241
+ },
40159
40242
  { usage: "/status", description: "\u67E5\u770B\u6A21\u578B\u3001\u4F1A\u8BDD\u6587\u4EF6\u3001\u4E0A\u4E0B\u6587\u4E0E token \u7528\u91CF\u3001\u786E\u8BA4\u6A21\u5F0F" },
40160
40243
  { usage: "/sessions", description: "\u5217\u51FA\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u7684\u4F1A\u8BDD" },
40161
40244
  { usage: "/switch <n>", description: "\u5207\u6362\u5230\u6307\u5B9A\u4F1A\u8BDD\uFF08\u6062\u590D\u5176\u5386\u53F2\u4E0A\u4E0B\u6587\uFF09" },
@@ -40195,7 +40278,7 @@ function printHelp() {
40195
40278
  console.log(source_default.dim(" - \u76F4\u63A5\u8F93\u5165\u95EE\u9898\u5373\u53EF\u5F00\u59CB\u5BF9\u8BDD"));
40196
40279
  console.log(source_default.dim(" - \u652F\u6301\u591A\u8F6E\u5BF9\u8BDD\uFF0C\u4E0A\u4E0B\u6587\u4F1A\u81EA\u52A8\u4FDD\u6301"));
40197
40280
  console.log(source_default.dim(" - \u8F93\u5165\u7F16\u7A0B\u95EE\u9898\u6216\u6587\u4EF6\u64CD\u4F5C\u8BF7\u6C42"));
40198
- console.log(source_default.dim(" - \u5F53\u5339\u914D\u5230 skill \u65F6\u4F1A\u81EA\u52A8\u63D0\u793A\uFF0C\u4F7F\u7528 /load \u52A0\u8F7D\u5B8C\u6574\u5185\u5BB9"));
40281
+ console.log(source_default.dim(" - \u9700\u8981\u67D0\u4E2A skill \u65F6\u6A21\u578B\u4F1A\u81EA\u884C\u52A0\u8F7D\uFF0C\u4E5F\u53EF\u7528 /load \u624B\u52A8\u52A0\u8F7D"));
40199
40282
  console.log("");
40200
40283
  }
40201
40284
 
@@ -41280,6 +41363,7 @@ var ASCII_ICONS = {
41280
41363
  glob: "[glob]",
41281
41364
  grep: "[grep]",
41282
41365
  read_file: "[read]",
41366
+ load_skill: "[skill]",
41283
41367
  write_file: "[write]",
41284
41368
  edit_file: "[edit]",
41285
41369
  bash: "[bash]"
@@ -41289,6 +41373,7 @@ var EMOJI_ICONS = {
41289
41373
  glob: "\u{1F50E}",
41290
41374
  grep: "\u{1F50D}",
41291
41375
  read_file: "\u{1F4D6}",
41376
+ load_skill: "\u{1F4DA}",
41292
41377
  write_file: "\u270F\uFE0F",
41293
41378
  edit_file: "\u{1F527}",
41294
41379
  bash: "\u{1F4BB}"
@@ -41298,6 +41383,7 @@ var TOOL_COLORS = {
41298
41383
  glob: "cyan",
41299
41384
  grep: "cyan",
41300
41385
  read_file: "cyan",
41386
+ load_skill: "cyan",
41301
41387
  write_file: "magenta",
41302
41388
  edit_file: "yellow",
41303
41389
  bash: "green"
@@ -42121,6 +42207,7 @@ function handleSkills(sessionManager) {
42121
42207
  console.log(source_default.red("\n\u274C SessionManager \u672A\u521D\u59CB\u5316\n"));
42122
42208
  return;
42123
42209
  }
42210
+ sessionManager.getSkillLoader().clearCache();
42124
42211
  const metadata = sessionManager.loadSkillMetadata();
42125
42212
  if (metadata.length === 0) {
42126
42213
  console.log(source_default.dim("\n\u{1F4ED} \u6CA1\u6709\u627E\u5230\u4EFB\u4F55 skill\n"));
@@ -42159,11 +42246,32 @@ function handleSkills(sessionManager) {
42159
42246
  console.log(source_default.dim("\u4F7F\u7528 /load <name> \u52A0\u8F7D skill \u5B8C\u6574\u5185\u5BB9"));
42160
42247
  console.log("");
42161
42248
  }
42249
+ function composeLoadedSkillSections(sessionManager, names) {
42250
+ const sections = [];
42251
+ for (const name of names) {
42252
+ const content = sessionManager.loadSkillContent(name);
42253
+ if (content) {
42254
+ sections.push(`## \u5DF2\u52A0\u8F7D Skill: ${name}
42255
+
42256
+ ${content}`);
42257
+ }
42258
+ }
42259
+ return sections.length > 0 ? `
42260
+
42261
+ ${sections.join("\n\n")}` : "";
42262
+ }
42162
42263
  async function handleLoadSkill(sessionManager, skillName, options) {
42163
42264
  if (!sessionManager) {
42164
42265
  console.log(source_default.red("\n\u274C SessionManager \u672A\u521D\u59CB\u5316\n"));
42165
42266
  return;
42166
42267
  }
42268
+ const names = options.loadedSkillNames ?? (options.loadedSkillNames = []);
42269
+ if (names.includes(skillName)) {
42270
+ console.log(source_default.dim(`
42271
+ \u2139\uFE0F skill ${skillName} \u5DF2\u52A0\u8F7D\uFF0C\u4E0D\u91CD\u590D\u6CE8\u5165
42272
+ `));
42273
+ return;
42274
+ }
42167
42275
  const skillContent = sessionManager.loadSkillContent(skillName);
42168
42276
  if (!skillContent) {
42169
42277
  console.log(source_default.red(`
@@ -42172,32 +42280,15 @@ async function handleLoadSkill(sessionManager, skillName, options) {
42172
42280
  console.log(source_default.dim("\u4F7F\u7528 /skills \u67E5\u770B\u6240\u6709\u53EF\u7528\u7684 skills\n"));
42173
42281
  return;
42174
42282
  }
42175
- const skillSection = `
42176
-
42177
- ## \u5DF2\u52A0\u8F7D Skill: ${skillName}
42178
-
42179
- ${skillContent}`;
42180
- options.systemPrompt = options.systemPrompt + skillSection;
42283
+ names.push(skillName);
42284
+ const base = options.baseSystemPrompt ?? options.systemPrompt;
42285
+ options.baseSystemPrompt = base;
42286
+ options.systemPrompt = base + composeLoadedSkillSections(sessionManager, names);
42181
42287
  console.log(source_default.green(`
42182
42288
  \u2705 \u5DF2\u52A0\u8F7D skill: ${skillName}
42183
42289
  `));
42184
42290
  console.log(source_default.dim("\u8BE5 skill \u7684\u5185\u5BB9\u5DF2\u6CE8\u5165\u5230\u4E0A\u4E0B\u6587\u4E2D\uFF0C\u540E\u7EED\u5BF9\u8BDD\u5C06\u53C2\u8003\u6B64 skill\u3002\n"));
42185
42291
  }
42186
- function checkSkillMatch(sessionManager, userInput) {
42187
- if (!sessionManager) return;
42188
- const matches = sessionManager.findMatchingSkills(userInput);
42189
- if (matches.length > 0) {
42190
- const topMatches = matches.slice(0, 3);
42191
- console.log("");
42192
- console.log(source_default.cyan("\u{1F4A1} \u53D1\u73B0\u5339\u914D\u7684 Skills:"));
42193
- for (const skill of topMatches) {
42194
- console.log(source_default.white(` - ${skill.name}`) + source_default.dim(`: ${skill.description}`));
42195
- }
42196
- console.log(source_default.dim(`
42197
- \u4F7F\u7528 /load <name> \u52A0\u8F7D skill \u83B7\u53D6\u66F4\u4E13\u4E1A\u7684\u5E2E\u52A9`));
42198
- console.log("");
42199
- }
42200
- }
42201
42292
 
42202
42293
  // src/cli/subagentCommand.ts
42203
42294
  var SUBAGENT_ON_WORDS = ["on", "\u5F00", "\u5F00\u542F", "\u6253\u5F00", "true", "enable"];
@@ -42300,6 +42391,446 @@ function printSubAgentCommandResult(outcome, previous) {
42300
42391
  }
42301
42392
  }
42302
42393
 
42394
+ // src/cli/updateCheck.ts
42395
+ var import_promises7 = require("node:fs/promises");
42396
+ var import_node_os4 = require("node:os");
42397
+ var import_node_path10 = require("node:path");
42398
+
42399
+ // package.json
42400
+ var package_default = {
42401
+ name: "@minipi/mini-pi",
42402
+ version: "1.0.2",
42403
+ description: "Mini Pi Code Agent - AI\u7F16\u7A0B\u52A9\u624B",
42404
+ bin: {
42405
+ "mini-pi": "./bin/mini-pi-cli.cjs"
42406
+ },
42407
+ files: [
42408
+ "bin/mini-pi-cli.cjs",
42409
+ "README.md"
42410
+ ],
42411
+ engines: {
42412
+ node: ">=22"
42413
+ },
42414
+ publishConfig: {
42415
+ access: "public"
42416
+ },
42417
+ repository: {
42418
+ type: "git",
42419
+ url: "git+https://github.com/arixse/mini-pi.git"
42420
+ },
42421
+ bugs: {
42422
+ url: "https://github.com/arixse/mini-pi/issues"
42423
+ },
42424
+ homepage: "https://github.com/arixse/mini-pi#readme",
42425
+ scripts: {
42426
+ dev: "tsx watch src/cli/entry.ts",
42427
+ "dev:cli": "tsx src/cli/entry.ts",
42428
+ "build:cli": "esbuild src/cli/entry.ts --bundle --platform=node --outfile=bin/mini-pi-cli.cjs --format=cjs --external:react --external:react-dom --external:lucide-react --external:vite",
42429
+ prepare: "npm run build:cli",
42430
+ "link:cli": "npm link",
42431
+ typecheck: "tsc --noEmit",
42432
+ test: 'tsx --test --test-concurrency=4 "src/**/*.test.ts"',
42433
+ "test:coverage": 'tsx --test --test-concurrency=4 --experimental-test-coverage --test-coverage-exclude="**/*.test.ts" --test-coverage-exclude="**/shim/*.cjs" --test-coverage-lines=85 --test-coverage-branches=85 --test-coverage-functions=90 "src/**/*.test.ts"',
42434
+ check: "npm run typecheck && npm run test:coverage"
42435
+ },
42436
+ keywords: [],
42437
+ author: "",
42438
+ license: "ISC",
42439
+ packageManager: "pnpm@10.17.0",
42440
+ type: "module",
42441
+ dependencies: {
42442
+ "@anthropic-ai/sdk": "^0.125.0",
42443
+ "@vitejs/plugin-react": "^6.1.0",
42444
+ chalk: "^6.0.0",
42445
+ concurrently: "^10.0.5",
42446
+ dotenv: "^17.4.2",
42447
+ express: "^5.2.1",
42448
+ "lucide-react": "^1.34.0",
42449
+ openai: "^7.15.0",
42450
+ react: "^19.2.8",
42451
+ "react-dom": "^19.2.8",
42452
+ tsx: "^4.23.12",
42453
+ vite: "^8.2.2"
42454
+ },
42455
+ devDependencies: {
42456
+ "@types/express": "^5.0.6",
42457
+ "@types/node": "^26.3.0",
42458
+ "@types/react": "^19.2.18",
42459
+ "@types/react-dom": "^19.2.5",
42460
+ esbuild: "^0.25.2",
42461
+ typescript: "^7.0.2"
42462
+ }
42463
+ };
42464
+
42465
+ // src/cli/version.ts
42466
+ var FALLBACK_VERSION = "0.0.0";
42467
+ var FALLBACK_PACKAGE_NAME = "mini-pi";
42468
+ var declared = package_default.version;
42469
+ var APP_VERSION = typeof declared === "string" && declared.length > 0 ? declared : FALLBACK_VERSION;
42470
+ var declaredName = package_default.name;
42471
+ var PACKAGE_NAME = typeof declaredName === "string" && declaredName.length > 0 ? declaredName : FALLBACK_PACKAGE_NAME;
42472
+ function formatVersion(version = APP_VERSION) {
42473
+ return `v${version}`;
42474
+ }
42475
+ function wantsVersion(argv) {
42476
+ return argv.includes("--version") || argv.includes("-v");
42477
+ }
42478
+
42479
+ // src/cli/versionCompare.ts
42480
+ var SEMVER_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
42481
+ function isSemver(version) {
42482
+ return SEMVER_PATTERN.test(version.trim());
42483
+ }
42484
+ function parseVersion(raw) {
42485
+ const withoutPrefix = raw.trim().replace(/^[vV=]+/, "");
42486
+ const [coreAndPre] = withoutPrefix.split("+");
42487
+ const [corePart, ...preParts] = coreAndPre.split("-");
42488
+ const core = corePart.split(".").map((part) => {
42489
+ const value = Number.parseInt(part, 10);
42490
+ return Number.isFinite(value) && value >= 0 ? value : 0;
42491
+ });
42492
+ while (core.length < 3) {
42493
+ core.push(0);
42494
+ }
42495
+ return {
42496
+ core: [core[0] ?? 0, core[1] ?? 0, core[2] ?? 0],
42497
+ prerelease: preParts.length > 0 ? preParts.join("-").split(".").filter(Boolean) : []
42498
+ };
42499
+ }
42500
+ function comparePrerelease(left, right) {
42501
+ if (left.length === 0 && right.length === 0) {
42502
+ return 0;
42503
+ }
42504
+ if (left.length === 0) {
42505
+ return 1;
42506
+ }
42507
+ if (right.length === 0) {
42508
+ return -1;
42509
+ }
42510
+ const length = Math.max(left.length, right.length);
42511
+ for (let index = 0; index < length; index += 1) {
42512
+ const a = left[index];
42513
+ const b = right[index];
42514
+ if (a === void 0) {
42515
+ return -1;
42516
+ }
42517
+ if (b === void 0) {
42518
+ return 1;
42519
+ }
42520
+ const aNumeric = /^\d+$/.test(a);
42521
+ const bNumeric = /^\d+$/.test(b);
42522
+ if (aNumeric && bNumeric) {
42523
+ const diff = Number(a) - Number(b);
42524
+ if (diff !== 0) {
42525
+ return diff < 0 ? -1 : 1;
42526
+ }
42527
+ continue;
42528
+ }
42529
+ if (aNumeric !== bNumeric) {
42530
+ return aNumeric ? -1 : 1;
42531
+ }
42532
+ if (a !== b) {
42533
+ return a < b ? -1 : 1;
42534
+ }
42535
+ }
42536
+ return 0;
42537
+ }
42538
+ function compareVersions(a, b) {
42539
+ const left = parseVersion(a);
42540
+ const right = parseVersion(b);
42541
+ for (let index = 0; index < 3; index += 1) {
42542
+ if (left.core[index] !== right.core[index]) {
42543
+ return left.core[index] < right.core[index] ? -1 : 1;
42544
+ }
42545
+ }
42546
+ return comparePrerelease(left.prerelease, right.prerelease);
42547
+ }
42548
+ function isNewerVersion(latest, current) {
42549
+ if (!isSemver(latest) || !isSemver(current)) {
42550
+ return false;
42551
+ }
42552
+ return compareVersions(latest, current) > 0;
42553
+ }
42554
+ function parseLatestVersion(payload) {
42555
+ if (payload === null || typeof payload !== "object") {
42556
+ return null;
42557
+ }
42558
+ const record = payload;
42559
+ for (const candidate of [record.version, record["dist-tags"]?.latest]) {
42560
+ if (typeof candidate === "string" && isSemver(candidate)) {
42561
+ return candidate.trim();
42562
+ }
42563
+ }
42564
+ return null;
42565
+ }
42566
+
42567
+ // src/cli/updateCheck.ts
42568
+ var DEFAULT_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
42569
+ var DEFAULT_TIMEOUT_MS = 5e3;
42570
+ var DEFAULT_REGISTRY_URL = "https://registry.npmjs.org";
42571
+ var UPDATE_CHECK_CACHE_FILE = "update-check.json";
42572
+ var SKIP_UPDATE_CHECK_ENV = "MINI_PI_SKIP_UPDATE_CHECK";
42573
+ function isCacheFresh(checkedAt, now, maxAgeMs) {
42574
+ if (!Number.isFinite(checkedAt) || !Number.isFinite(now)) {
42575
+ return false;
42576
+ }
42577
+ if (checkedAt > now) {
42578
+ return false;
42579
+ }
42580
+ return now - checkedAt < maxAgeMs;
42581
+ }
42582
+ async function checkForUpdate(input) {
42583
+ const { current } = input;
42584
+ if (!input.enabled) {
42585
+ return { status: "disabled" };
42586
+ }
42587
+ const now = (input.now ?? Date.now)();
42588
+ const maxAgeMs = input.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
42589
+ let latest = null;
42590
+ if (!input.force) {
42591
+ const cache = await input.readCache();
42592
+ if (cache && isCacheFresh(cache.checkedAt, now, maxAgeMs)) {
42593
+ latest = cache.latestVersion;
42594
+ }
42595
+ }
42596
+ if (latest === null) {
42597
+ latest = await input.fetchLatest();
42598
+ if (latest !== null) {
42599
+ try {
42600
+ await input.writeCache({ checkedAt: now, latestVersion: latest });
42601
+ } catch {
42602
+ }
42603
+ }
42604
+ }
42605
+ if (latest === null) {
42606
+ return {
42607
+ status: "check-failed",
42608
+ current,
42609
+ reason: "\u65E0\u6CD5\u83B7\u53D6\u6700\u65B0\u7248\u672C\u53F7\uFF08\u7F51\u7EDC\u4E0D\u53EF\u8FBE\u6216\u8BF7\u6C42\u8D85\u65F6\uFF09"
42610
+ };
42611
+ }
42612
+ return isNewerVersion(latest, current) ? { status: "update-available", current, latest } : { status: "up-to-date", current, latest };
42613
+ }
42614
+ async function readCacheFile(cachePath) {
42615
+ try {
42616
+ const content = await (0, import_promises7.readFile)(cachePath, "utf-8");
42617
+ const data = JSON.parse(content);
42618
+ if (data === null || typeof data !== "object") {
42619
+ return null;
42620
+ }
42621
+ const record = data;
42622
+ if (typeof record.checkedAt !== "number" || !Number.isFinite(record.checkedAt) || typeof record.latestVersion !== "string" || record.latestVersion.length === 0) {
42623
+ return null;
42624
+ }
42625
+ return { checkedAt: record.checkedAt, latestVersion: record.latestVersion };
42626
+ } catch {
42627
+ return null;
42628
+ }
42629
+ }
42630
+ async function writeCacheFile(cachePath, cache) {
42631
+ try {
42632
+ await (0, import_promises7.mkdir)((0, import_node_path10.join)(cachePath, ".."), { recursive: true });
42633
+ await (0, import_promises7.writeFile)(cachePath, JSON.stringify(cache), "utf-8");
42634
+ } catch {
42635
+ }
42636
+ }
42637
+ function createRegistryFetcher(input) {
42638
+ const url = `${input.registryUrl.replace(/\/+$/, "")}/${input.packageName}/latest`;
42639
+ return async () => {
42640
+ try {
42641
+ const response = await input.fetchImpl(url, {
42642
+ signal: AbortSignal.timeout(input.timeoutMs)
42643
+ });
42644
+ if (!response.ok) {
42645
+ return null;
42646
+ }
42647
+ return parseLatestVersion(await response.json());
42648
+ } catch {
42649
+ return null;
42650
+ }
42651
+ };
42652
+ }
42653
+ function createUpdateChecker(overrides = {}) {
42654
+ const current = overrides.current ?? APP_VERSION;
42655
+ const packageName = overrides.packageName ?? PACKAGE_NAME;
42656
+ const cachePath = overrides.cachePath ?? (0, import_node_path10.join)((0, import_node_os4.homedir)(), ".mini-pi", UPDATE_CHECK_CACHE_FILE);
42657
+ const registryUrl = overrides.registryUrl ?? overrides.env?.npm_config_registry ?? DEFAULT_REGISTRY_URL;
42658
+ const timeoutMs = overrides.timeoutMs ?? DEFAULT_TIMEOUT_MS;
42659
+ const maxAgeMs = overrides.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
42660
+ const fetchImpl = overrides.fetchImpl ?? ((url, init) => fetch(url, init));
42661
+ const now = overrides.now ?? Date.now;
42662
+ const isInteractive = overrides.isInteractive ?? Boolean(process.stdout.isTTY);
42663
+ const env2 = overrides.env ?? process.env;
42664
+ const settingsStore = overrides.settingsStore;
42665
+ const fetchLatest = createRegistryFetcher({
42666
+ registryUrl,
42667
+ packageName,
42668
+ timeoutMs,
42669
+ fetchImpl
42670
+ });
42671
+ const readCache = () => readCacheFile(cachePath);
42672
+ const writeCache = (cache) => writeCacheFile(cachePath, cache);
42673
+ const runCheck = (force) => checkForUpdate({
42674
+ current,
42675
+ // 开关判定在 checkOnStartup 里做;走到这里必然是"该查"
42676
+ enabled: true,
42677
+ force,
42678
+ fetchLatest,
42679
+ readCache,
42680
+ writeCache,
42681
+ now,
42682
+ maxAgeMs
42683
+ });
42684
+ return {
42685
+ async checkOnStartup() {
42686
+ if (!isInteractive) {
42687
+ return { status: "skipped" };
42688
+ }
42689
+ if (env2[SKIP_UPDATE_CHECK_ENV]) {
42690
+ return { status: "disabled" };
42691
+ }
42692
+ if (settingsStore && !await settingsStore.getUpdateCheckEnabled()) {
42693
+ return { status: "disabled" };
42694
+ }
42695
+ return runCheck(false);
42696
+ },
42697
+ checkNow: () => runCheck(true),
42698
+ async isEnabled() {
42699
+ if (env2[SKIP_UPDATE_CHECK_ENV]) {
42700
+ return false;
42701
+ }
42702
+ if (!settingsStore) {
42703
+ return true;
42704
+ }
42705
+ return settingsStore.getUpdateCheckEnabled();
42706
+ },
42707
+ async setEnabled(enabled) {
42708
+ if (!settingsStore) {
42709
+ throw new Error("settings \u672A\u914D\u7F6E\uFF0C\u65E0\u6CD5\u6301\u4E45\u5316\u66F4\u65B0\u68C0\u6D4B\u5F00\u5173");
42710
+ }
42711
+ await settingsStore.setUpdateCheckEnabled(enabled);
42712
+ }
42713
+ };
42714
+ }
42715
+ function updateNoticeLines(current, latest, packageName = PACKAGE_NAME) {
42716
+ return [
42717
+ `\u2B06\uFE0F \u53D1\u73B0\u65B0\u7248\u672C ${formatVersion(latest)}\uFF08\u5F53\u524D ${formatVersion(current)}\uFF09`,
42718
+ ` \u66F4\u65B0\u547D\u4EE4\uFF1Anpm install -g ${packageName}`
42719
+ ];
42720
+ }
42721
+ function printCheckResult(result) {
42722
+ switch (result.status) {
42723
+ case "update-available": {
42724
+ const [headline, command] = updateNoticeLines(
42725
+ result.current,
42726
+ result.latest,
42727
+ PACKAGE_NAME
42728
+ );
42729
+ console.log();
42730
+ console.log(source_default.yellow(headline));
42731
+ console.log(source_default.dim(command));
42732
+ console.log();
42733
+ return;
42734
+ }
42735
+ case "up-to-date":
42736
+ console.log(
42737
+ source_default.green(`
42738
+ \u2705 \u5DF2\u662F\u6700\u65B0\u7248\u672C\uFF08${formatVersion(result.current)}\uFF09
42739
+ `)
42740
+ );
42741
+ return;
42742
+ case "check-failed":
42743
+ console.log(source_default.yellow(`
42744
+ \u26A0\uFE0F \u68C0\u67E5\u5931\u8D25\uFF1A${result.reason}
42745
+ `));
42746
+ return;
42747
+ case "disabled":
42748
+ console.log(source_default.dim("\n\u{1F6AB} \u542F\u52A8\u68C0\u6D4B\u5DF2\u5173\u95ED\uFF08/update on \u91CD\u65B0\u5F00\u542F\uFF09\n"));
42749
+ return;
42750
+ case "skipped":
42751
+ console.log(source_default.dim("\n\u2139\uFE0F \u975E\u4EA4\u4E92\u7EC8\u7AEF\uFF0C\u8DF3\u8FC7\u66F4\u65B0\u68C0\u6D4B\n"));
42752
+ return;
42753
+ }
42754
+ }
42755
+ function printUpdateNotice(result) {
42756
+ if (result.status !== "update-available") {
42757
+ return;
42758
+ }
42759
+ printCheckResult(result);
42760
+ }
42761
+ var UPDATE_ON_WORDS = ["on", "\u5F00", "\u5F00\u542F", "\u6253\u5F00", "true", "enable"];
42762
+ var UPDATE_OFF_WORDS = ["off", "\u5173", "\u5173\u95ED", "false", "disable"];
42763
+ function parseUpdateCommand(argument) {
42764
+ const raw = argument.trim();
42765
+ if (!raw) {
42766
+ return { kind: "check" };
42767
+ }
42768
+ const word = raw.toLowerCase();
42769
+ if (UPDATE_ON_WORDS.includes(word)) {
42770
+ return { kind: "set", enabled: true };
42771
+ }
42772
+ if (UPDATE_OFF_WORDS.includes(word)) {
42773
+ return { kind: "set", enabled: false };
42774
+ }
42775
+ return { kind: "unknown", raw };
42776
+ }
42777
+ async function applyUpdateCommand(input) {
42778
+ const command = parseUpdateCommand(input.argument);
42779
+ if (command.kind === "unknown") {
42780
+ return {
42781
+ kind: "error",
42782
+ message: `\u65E0\u6CD5\u8BC6\u522B\u7684\u53C2\u6570\uFF1A${command.raw}\u3002\u7528\u6CD5\uFF1A/update [on|off]\uFF08\u4E0D\u5E26\u53C2\u6570\u5219\u7ACB\u5373\u68C0\u67E5\uFF09`
42783
+ };
42784
+ }
42785
+ if (command.kind === "set") {
42786
+ if (!input.persist) {
42787
+ return { kind: "error", message: "\u66F4\u65B0\u68C0\u6D4B\u672A\u914D\u7F6E\uFF0C\u65E0\u6CD5\u6301\u4E45\u5316\u5F00\u5173" };
42788
+ }
42789
+ try {
42790
+ await input.persist(command.enabled);
42791
+ } catch {
42792
+ return { kind: "toggle", enabled: command.enabled, persistFailed: true };
42793
+ }
42794
+ return { kind: "toggle", enabled: command.enabled };
42795
+ }
42796
+ if (!input.check) {
42797
+ return { kind: "error", message: "\u66F4\u65B0\u68C0\u6D4B\u672A\u914D\u7F6E" };
42798
+ }
42799
+ return { kind: "check", result: await input.check() };
42800
+ }
42801
+ function printUpdateOutcome(outcome, previous) {
42802
+ if (outcome.kind === "error") {
42803
+ console.log(source_default.red(`
42804
+ \u274C ${outcome.message}
42805
+ `));
42806
+ return;
42807
+ }
42808
+ if (outcome.kind === "toggle") {
42809
+ if (outcome.enabled === previous) {
42810
+ console.log(
42811
+ source_default.dim(
42812
+ outcome.enabled ? "\n\u{1F91D} \u542F\u52A8\u68C0\u6D4B\u5DF2\u7ECF\u662F\u5F00\u542F\u72B6\u6001\n" : "\n\u{1F91D} \u542F\u52A8\u68C0\u6D4B\u5DF2\u7ECF\u662F\u5173\u95ED\u72B6\u6001\n"
42813
+ )
42814
+ );
42815
+ } else {
42816
+ console.log(
42817
+ outcome.enabled ? source_default.green("\n\u2705 \u5DF2\u5F00\u542F\u542F\u52A8\u65F6\u7684\u7248\u672C\u66F4\u65B0\u68C0\u6D4B\n") : source_default.yellow(
42818
+ "\n\u{1F6AB} \u5DF2\u5173\u95ED\u542F\u52A8\u65F6\u7684\u7248\u672C\u66F4\u65B0\u68C0\u6D4B\uFF08/update on \u91CD\u65B0\u5F00\u542F\uFF09\n"
42819
+ )
42820
+ );
42821
+ }
42822
+ if (outcome.persistFailed) {
42823
+ console.log(
42824
+ source_default.red(
42825
+ "\u26A0\uFE0F \u5F00\u5173\u672A\u80FD\u5199\u5165 settings.json\uFF08\u6587\u4EF6\u4E0D\u53EF\u5199\uFF09\uFF0C\u91CD\u542F\u540E\u6062\u590D\u9ED8\u8BA4\n"
42826
+ )
42827
+ );
42828
+ }
42829
+ return;
42830
+ }
42831
+ printCheckResult(outcome.result);
42832
+ }
42833
+
42303
42834
  // src/cli/dispatch.ts
42304
42835
  async function handleSlashCommand(input, ctx) {
42305
42836
  const { options, rl } = ctx;
@@ -42393,9 +42924,12 @@ async function handleSlashCommand(input, ctx) {
42393
42924
  if (input === "/reload") {
42394
42925
  if (options.onReload) {
42395
42926
  try {
42396
- const { model, systemPrompt, contextWindow, contextWindowSource } = await options.onReload();
42927
+ const { model, systemPrompt, baseSystemPrompt, contextWindow, contextWindowSource } = await options.onReload();
42397
42928
  options.model = model;
42398
42929
  options.systemPrompt = systemPrompt;
42930
+ if (baseSystemPrompt !== void 0) {
42931
+ options.baseSystemPrompt = baseSystemPrompt;
42932
+ }
42399
42933
  if (contextWindow !== void 0) {
42400
42934
  options.contextWindow = contextWindow;
42401
42935
  options.contextWindowSource = contextWindowSource ?? "configured";
@@ -42441,6 +42975,19 @@ async function handleSlashCommand(input, ctx) {
42441
42975
  }
42442
42976
  return true;
42443
42977
  }
42978
+ if (input === "/update" || input.startsWith("/update ")) {
42979
+ const checker = ctx.options.updateChecker;
42980
+ const enabled = checker ? await checker.isEnabled() : false;
42981
+ const outcome = await applyUpdateCommand({
42982
+ argument: input.slice("/update".length).trim(),
42983
+ enabled,
42984
+ persist: checker ? (value) => checker.setEnabled(value) : void 0,
42985
+ // 手动检查忽略开关与缓存:用户显式敲了命令,就没有"已关闭"可言
42986
+ check: checker ? () => checker.checkNow() : void 0
42987
+ });
42988
+ printUpdateOutcome(outcome, enabled);
42989
+ return true;
42990
+ }
42444
42991
  return false;
42445
42992
  }
42446
42993
 
@@ -42520,15 +43067,21 @@ var LOGO_LINES = [
42520
43067
  " \u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551",
42521
43068
  " \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D"
42522
43069
  ];
42523
- function printLogo(width = process.stdout.columns ?? DEFAULT_TERMINAL_WIDTH) {
43070
+ function printLogo(width = process.stdout.columns ?? DEFAULT_TERMINAL_WIDTH, version = APP_VERSION) {
43071
+ const stamp = source_default.dim(` ${formatVersion(version)}`);
42524
43072
  if (width < MIN_LOGO_WIDTH) {
42525
43073
  console.log(source_default.cyan(" mini-pi"));
43074
+ console.log(stamp);
42526
43075
  return;
42527
43076
  }
42528
43077
  console.log("");
42529
43078
  for (const line of LOGO_LINES) {
42530
43079
  console.log(source_default.cyan(line));
42531
43080
  }
43081
+ console.log(stamp);
43082
+ }
43083
+ function printVersion(version = APP_VERSION) {
43084
+ console.log(`mini-pi ${formatVersion(version)}`);
42532
43085
  }
42533
43086
  function printWelcome(providerName, modelName, contextWindow) {
42534
43087
  const rule = () => printSeparator();
@@ -42855,7 +43408,6 @@ async function startRepl(options) {
42855
43408
  rl.prompt();
42856
43409
  return;
42857
43410
  }
42858
- checkSkillMatch(options.sessionManager, input);
42859
43411
  if (!options.model) {
42860
43412
  console.log(source_default.yellow("\u26A0\uFE0F \u5C1A\u672A\u914D\u7F6E\u6A21\u578B\uFF0C\u8BF7\u4F7F\u7528 /login \u548C /model \u547D\u4EE4\u8FDB\u884C\u914D\u7F6E"));
42861
43413
  rl.prompt();
@@ -42993,7 +43545,7 @@ ${projectStack}` : "",
42993
43545
 
42994
43546
  // src/cli/projectStack.ts
42995
43547
  var import_node_fs7 = require("node:fs");
42996
- var import_node_path10 = require("node:path");
43548
+ var import_node_path11 = require("node:path");
42997
43549
  function emptyStack() {
42998
43550
  return { frameworks: [], commands: [] };
42999
43551
  }
@@ -43052,26 +43604,26 @@ function detectPackageManager(root, pkg) {
43052
43604
  ["package-lock.json", "npm"]
43053
43605
  ];
43054
43606
  for (const [file, manager] of locks) {
43055
- if ((0, import_node_fs7.existsSync)((0, import_node_path10.join)(root, file))) {
43607
+ if ((0, import_node_fs7.existsSync)((0, import_node_path11.join)(root, file))) {
43056
43608
  return manager;
43057
43609
  }
43058
43610
  }
43059
- const declared = str(pkg, "packageManager");
43060
- if (!declared) {
43611
+ const declared2 = str(pkg, "packageManager");
43612
+ if (!declared2) {
43061
43613
  return void 0;
43062
43614
  }
43063
- const at = declared.indexOf("@");
43064
- const name = at > 0 ? declared.slice(0, at) : declared;
43615
+ const at = declared2.indexOf("@");
43616
+ const name = at > 0 ? declared2.slice(0, at) : declared2;
43065
43617
  return name === "" ? void 0 : name;
43066
43618
  }
43067
43619
  function detectProjectStack(workspaceRoot) {
43068
- const pkg = readJson((0, import_node_path10.join)(workspaceRoot, "package.json"));
43620
+ const pkg = readJson((0, import_node_path11.join)(workspaceRoot, "package.json"));
43069
43621
  if (pkg) {
43070
43622
  const dependencies = [...keys(pkg, "dependencies"), ...keys(pkg, "devDependencies")];
43071
43623
  const packageManager = detectPackageManager(workspaceRoot, pkg);
43072
43624
  const runPrefix = packageManager && packageManager !== "npm" ? packageManager : "npm run";
43073
43625
  const scripts = keys(pkg, "scripts");
43074
- const typescript = (0, import_node_fs7.existsSync)((0, import_node_path10.join)(workspaceRoot, "tsconfig.json")) || dependencies.includes("typescript");
43626
+ const typescript = (0, import_node_fs7.existsSync)((0, import_node_path11.join)(workspaceRoot, "tsconfig.json")) || dependencies.includes("typescript");
43075
43627
  const nodeRange = str(pkg["engines"], "node");
43076
43628
  return {
43077
43629
  ...str(pkg, "name") ? { name: str(pkg, "name") } : {},
@@ -43080,7 +43632,7 @@ function detectProjectStack(workspaceRoot) {
43080
43632
  ...packageManager ? { packageManager } : {},
43081
43633
  ...nodeRange ? { runtime: `node ${nodeRange}` } : {},
43082
43634
  typescript,
43083
- git: (0, import_node_fs7.existsSync)((0, import_node_path10.join)(workspaceRoot, ".git")),
43635
+ git: (0, import_node_fs7.existsSync)((0, import_node_path11.join)(workspaceRoot, ".git")),
43084
43636
  frameworks: FRAMEWORK_HINTS.filter(([dep]) => dependencies.includes(dep)).map(
43085
43637
  ([, label]) => label
43086
43638
  ),
@@ -43096,8 +43648,8 @@ function detectProjectStack(workspaceRoot) {
43096
43648
  ["Cargo.toml", "Rust", ["cargo build", "cargo test"]]
43097
43649
  ];
43098
43650
  for (const [file, language, commands] of otherLanguages) {
43099
- if ((0, import_node_fs7.existsSync)((0, import_node_path10.join)(workspaceRoot, file))) {
43100
- return { ...emptyStack(), language, commands, git: (0, import_node_fs7.existsSync)((0, import_node_path10.join)(workspaceRoot, ".git")) };
43651
+ if ((0, import_node_fs7.existsSync)((0, import_node_path11.join)(workspaceRoot, file))) {
43652
+ return { ...emptyStack(), language, commands, git: (0, import_node_fs7.existsSync)((0, import_node_path11.join)(workspaceRoot, ".git")) };
43101
43653
  }
43102
43654
  }
43103
43655
  return emptyStack();
@@ -43143,29 +43695,62 @@ function formatProjectStack(stack) {
43143
43695
  // src/agent/sessionManager.ts
43144
43696
  var import_node_crypto2 = require("node:crypto");
43145
43697
  var import_node_fs9 = require("node:fs");
43146
- var import_node_path12 = require("node:path");
43147
- var import_node_os5 = require("node:os");
43698
+ var import_node_path13 = require("node:path");
43699
+ var import_node_os6 = require("node:os");
43148
43700
 
43149
43701
  // src/agent/skillLoader.ts
43150
43702
  var import_node_fs8 = require("node:fs");
43151
- var import_node_path11 = require("node:path");
43152
- var import_node_os4 = require("node:os");
43703
+ var import_node_path12 = require("node:path");
43704
+ var import_node_os5 = require("node:os");
43705
+
43706
+ // src/agent/skillFrontmatter.ts
43707
+ var FRONTMATTER_RE = /^\uFEFF?---\s*\r?\n([\s\S]*?)\r?\n---/;
43153
43708
  function parseFrontmatter(content) {
43154
- const frontmatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
43155
- if (!frontmatterMatch) {
43709
+ const match = FRONTMATTER_RE.exec(content);
43710
+ if (!match) {
43156
43711
  return null;
43157
43712
  }
43158
- const frontmatter = frontmatterMatch[1];
43159
- const nameMatch = frontmatter.match(/^name:\s*(.+)$/m);
43160
- const descMatch = frontmatter.match(/^description:\s*(.+)$/m);
43161
- if (!nameMatch) {
43713
+ const name = readField(match[1], "name");
43714
+ if (!name) {
43162
43715
  return null;
43163
43716
  }
43164
43717
  return {
43165
- name: nameMatch[1].trim(),
43166
- description: descMatch ? descMatch[1].trim() : ""
43718
+ name,
43719
+ description: readField(match[1], "description") ?? ""
43167
43720
  };
43168
43721
  }
43722
+ function readField(block, key) {
43723
+ const lines = block.split(/\r?\n/);
43724
+ const keyPattern = new RegExp(`^${key}\\s*:`);
43725
+ const start = lines.findIndex((line) => keyPattern.test(line));
43726
+ if (start < 0) {
43727
+ return null;
43728
+ }
43729
+ const rawValue = lines[start].slice(lines[start].indexOf(":") + 1).trim();
43730
+ const isBlockScalar = /^[>|]/.test(rawValue);
43731
+ const parts = isBlockScalar ? [] : [rawValue];
43732
+ for (let i = start + 1; i < lines.length; i++) {
43733
+ const line = lines[i];
43734
+ if (!/^\s+\S/.test(line)) break;
43735
+ if (/^\s*[\w.-]+\s*:/.test(line)) break;
43736
+ parts.push(line.trim());
43737
+ }
43738
+ const value = parts.join(" ").trim();
43739
+ return value === "" ? null : stripQuotes(value);
43740
+ }
43741
+ function stripQuotes(value) {
43742
+ if (value.length < 2) {
43743
+ return value;
43744
+ }
43745
+ const first = value[0];
43746
+ const last = value[value.length - 1];
43747
+ if (first === '"' && last === '"' || first === "'" && last === "'") {
43748
+ return value.slice(1, -1);
43749
+ }
43750
+ return value;
43751
+ }
43752
+
43753
+ // src/agent/skillLoader.ts
43169
43754
  var SkillLoader = class {
43170
43755
  skillDirs;
43171
43756
  metadataCache = null;
@@ -43180,11 +43765,11 @@ var SkillLoader = class {
43180
43765
  this.skillDirs = customDirs;
43181
43766
  } else {
43182
43767
  this.skillDirs = [
43183
- { path: (0, import_node_path11.join)((0, import_node_os4.homedir)(), ".agents", "skills"), source: "global-agents" },
43184
- { path: (0, import_node_path11.join)((0, import_node_os4.homedir)(), ".mini-pi", "skills"), source: "global-mini-pi" },
43185
- { path: (0, import_node_path11.join)(workspaceRoot, ".mini-pi", "skills"), source: "project" },
43186
- { path: (0, import_node_path11.join)(workspaceRoot, ".pi", "skills"), source: "project" },
43187
- { path: (0, import_node_path11.join)(workspaceRoot, ".agents", "skills"), source: "project" }
43768
+ { path: (0, import_node_path12.join)((0, import_node_os5.homedir)(), ".agents", "skills"), source: "global-agents" },
43769
+ { path: (0, import_node_path12.join)((0, import_node_os5.homedir)(), ".mini-pi", "skills"), source: "global-mini-pi" },
43770
+ { path: (0, import_node_path12.join)(workspaceRoot, ".mini-pi", "skills"), source: "project" },
43771
+ { path: (0, import_node_path12.join)(workspaceRoot, ".pi", "skills"), source: "project" },
43772
+ { path: (0, import_node_path12.join)(workspaceRoot, ".agents", "skills"), source: "project" }
43188
43773
  ];
43189
43774
  }
43190
43775
  }
@@ -43206,8 +43791,8 @@ var SkillLoader = class {
43206
43791
  const entries = (0, import_node_fs8.readdirSync)(dirPath, { withFileTypes: true });
43207
43792
  for (const entry of entries) {
43208
43793
  if (!entry.isDirectory()) continue;
43209
- const skillDir = (0, import_node_path11.join)(dirPath, entry.name);
43210
- const skillFile = (0, import_node_path11.join)(skillDir, "SKILL.md");
43794
+ const skillDir = (0, import_node_path12.join)(dirPath, entry.name);
43795
+ const skillFile = (0, import_node_path12.join)(skillDir, "SKILL.md");
43211
43796
  if (!(0, import_node_fs8.existsSync)(skillFile)) continue;
43212
43797
  try {
43213
43798
  const content = (0, import_node_fs8.readFileSync)(skillFile, "utf8");
@@ -43220,6 +43805,10 @@ var SkillLoader = class {
43220
43805
  skillFile,
43221
43806
  source
43222
43807
  });
43808
+ } else {
43809
+ logger.warn(
43810
+ `\u8DF3\u8FC7\u65E0\u6CD5\u89E3\u6790\u7684 skill\uFF1A${skillFile}\uFF08\u7F3A\u5C11 name \u5B57\u6BB5\u6216 frontmatter \u683C\u5F0F\u4E0D\u53D7\u652F\u6301\uFF09`
43811
+ );
43223
43812
  }
43224
43813
  } catch (error) {
43225
43814
  logger.warn(`Failed to read skill ${skillFile}:`, error);
@@ -43274,37 +43863,6 @@ var SkillLoader = class {
43274
43863
  return null;
43275
43864
  }
43276
43865
  }
43277
- /**
43278
- * 检查用户输入是否匹配某个 skill
43279
- * 基于 skill 名称和描述中的关键词进行匹配
43280
- */
43281
- findMatchingSkills(userInput) {
43282
- const metadata = this.loadAllMetadata();
43283
- const inputLower = userInput.toLowerCase();
43284
- const matches = [];
43285
- for (const skill of metadata) {
43286
- let score = 0;
43287
- if (inputLower.includes(skill.name.toLowerCase())) {
43288
- score += 100;
43289
- }
43290
- const nameWords = skill.name.toLowerCase().split(/[-_\s]+/);
43291
- for (const word of nameWords) {
43292
- if (word.length > 2 && inputLower.includes(word)) {
43293
- score += 30;
43294
- }
43295
- }
43296
- const descWords = skill.description.toLowerCase().split(/\s+/);
43297
- for (const word of descWords) {
43298
- if (word.length > 3 && inputLower.includes(word)) {
43299
- score += 5;
43300
- }
43301
- }
43302
- if (score > 0) {
43303
- matches.push({ skill, score });
43304
- }
43305
- }
43306
- return matches.sort((a, b) => b.score - a.score).map((m) => m.skill);
43307
- }
43308
43866
  /**
43309
43867
  * 清除缓存(用于重新加载)
43310
43868
  */
@@ -43314,7 +43872,9 @@ var SkillLoader = class {
43314
43872
  }
43315
43873
  /**
43316
43874
  * 生成 skill 摘要列表(用于注入 system prompt)
43317
- * 这是渐进式披露的核心:只暴露轻量级元数据
43875
+ *
43876
+ * 渐进式披露:系统提示词里只放轻量元数据,全文由模型通过 `load_skill`
43877
+ * 工具按需取用——每次请求的固定开销就只有这一份清单。
43318
43878
  */
43319
43879
  generateSkillSummary() {
43320
43880
  const metadata = this.loadAllMetadata();
@@ -43324,7 +43884,7 @@ var SkillLoader = class {
43324
43884
  const lines = [
43325
43885
  "## \u53EF\u7528 Skills",
43326
43886
  "",
43327
- "\u4EE5\u4E0B\u662F\u4F60\u53EF\u7528\u7684 skills\u3002\u5F53\u7528\u6237\u7684\u8BF7\u6C42\u5339\u914D\u67D0\u4E2A skill \u65F6\uFF0C\u4F60\u5E94\u8BE5\u52A0\u8F7D\u5E76\u53C2\u8003\u8BE5 skill \u7684\u5B8C\u6574\u5185\u5BB9\u6765\u6307\u5BFC\u4F60\u7684\u56DE\u7B54\u3002",
43887
+ "\u4EE5\u4E0B\u662F\u4F60\u53EF\u7528\u7684 skills\u3002\u5F53\u7528\u6237\u7684\u8BF7\u6C42\u4E0E\u67D0\u4E2A skill \u76F8\u5173\u65F6\uFF0C\u4F60\u5E94\u8BE5\u52A0\u8F7D\u5E76\u53C2\u8003\u8BE5 skill \u7684\u5B8C\u6574\u5185\u5BB9\u6765\u6307\u5BFC\u4F60\u7684\u56DE\u7B54\u3002",
43328
43888
  ""
43329
43889
  ];
43330
43890
  const bySource = /* @__PURE__ */ new Map();
@@ -43350,7 +43910,9 @@ var SkillLoader = class {
43350
43910
  lines.push("");
43351
43911
  }
43352
43912
  lines.push("---");
43353
- lines.push("\u5F53\u7528\u6237\u8BF7\u6C42\u5339\u914D\u67D0\u4E2A skill \u65F6\uFF0C\u8BF7\u544A\u77E5\u7528\u6237\u4F60\u53EF\u4EE5\u52A0\u8F7D\u8BE5 skill \u6765\u63D0\u4F9B\u66F4\u4E13\u4E1A\u7684\u5E2E\u52A9\uFF0C\u5E76\u8BE2\u95EE\u662F\u5426\u9700\u8981\u52A0\u8F7D\u3002");
43913
+ lines.push(
43914
+ "\u5F53\u7528\u6237\u7684\u8BF7\u6C42\u4E0E\u67D0\u4E2A skill \u76F8\u5173\u65F6\uFF0C\u8C03\u7528 load_skill \u5DE5\u5177\u52A0\u8F7D\u5B83\u7684\u5B8C\u6574\u5185\u5BB9\uFF0C\u7136\u540E\u6309\u5176\u4E2D\u7684\u89C4\u5219\u6267\u884C\uFF1B\u4E0D\u8981\u8BE2\u95EE\u7528\u6237\uFF0C\u76F4\u63A5\u52A0\u8F7D\u3002"
43915
+ );
43354
43916
  return lines.join("\n");
43355
43917
  }
43356
43918
  };
@@ -43360,14 +43922,14 @@ var WORKSPACE_MARKER = ".workspace-key";
43360
43922
  var MAX_DIR_NAME_LENGTH = 120;
43361
43923
  function absolutePath(workspaceRoot) {
43362
43924
  try {
43363
- return (0, import_node_fs9.realpathSync)((0, import_node_path12.resolve)(workspaceRoot));
43925
+ return (0, import_node_fs9.realpathSync)((0, import_node_path13.resolve)(workspaceRoot));
43364
43926
  } catch {
43365
- return (0, import_node_path12.resolve)(workspaceRoot);
43927
+ return (0, import_node_path13.resolve)(workspaceRoot);
43366
43928
  }
43367
43929
  }
43368
43930
  function normalizeWorkspaceKey(workspaceRoot) {
43369
43931
  const absolute = absolutePath(workspaceRoot);
43370
- return (0, import_node_os5.platform)() === "win32" ? absolute.toLowerCase() : absolute;
43932
+ return (0, import_node_os6.platform)() === "win32" ? absolute.toLowerCase() : absolute;
43371
43933
  }
43372
43934
  function workspaceKeyHash(workspaceRoot) {
43373
43935
  return (0, import_node_crypto2.createHash)("sha256").update(normalizeWorkspaceKey(workspaceRoot)).digest("hex").slice(0, 8);
@@ -43380,12 +43942,12 @@ function workspaceSessionDirName(workspaceRoot) {
43380
43942
  var SessionManager = class {
43381
43943
  constructor(workspaceRoot, options) {
43382
43944
  this.workspaceRoot = workspaceRoot;
43383
- this.sessionsDir = options?.sessionsDir ?? (0, import_node_path12.join)((0, import_node_os5.homedir)(), ".mini-pi", "sessions");
43945
+ this.sessionsDir = options?.sessionsDir ?? (0, import_node_path13.join)((0, import_node_os6.homedir)(), ".mini-pi", "sessions");
43384
43946
  this.workspaceKey = normalizeWorkspaceKey(workspaceRoot);
43385
43947
  this.projectSessionsDir = this.resolveProjectSessionsDir();
43386
- this.globalAgentsPath = options?.globalAgentsPath ?? (0, import_node_path12.join)((0, import_node_os5.homedir)(), ".mini-pi", "AGENTS.md");
43387
- this.projectAgentsPath = (0, import_node_path12.join)(workspaceRoot, "AGENTS.md");
43388
- this.skillLoader = new SkillLoader(workspaceRoot, options?.customSkillDirs);
43948
+ this.globalAgentsPath = options?.globalAgentsPath ?? (0, import_node_path13.join)((0, import_node_os6.homedir)(), ".mini-pi", "AGENTS.md");
43949
+ this.projectAgentsPath = (0, import_node_path13.join)(workspaceRoot, "AGENTS.md");
43950
+ this.skillLoader = options?.skillLoader ?? new SkillLoader(workspaceRoot, options?.customSkillDirs);
43389
43951
  this.ensureSessionsDir();
43390
43952
  }
43391
43953
  sessionsDir;
@@ -43408,10 +43970,10 @@ var SessionManager = class {
43408
43970
  */
43409
43971
  resolveProjectSessionsDir() {
43410
43972
  const name = workspaceSessionDirName(this.workspaceRoot);
43411
- const candidate = (0, import_node_path12.join)(this.sessionsDir, name);
43973
+ const candidate = (0, import_node_path13.join)(this.sessionsDir, name);
43412
43974
  const owner = this.readWorkspaceMarker(candidate);
43413
43975
  if (owner !== null && owner !== this.workspaceKey) {
43414
- return (0, import_node_path12.join)(
43976
+ return (0, import_node_path13.join)(
43415
43977
  this.sessionsDir,
43416
43978
  `${name}-${workspaceKeyHash(this.workspaceRoot)}`
43417
43979
  );
@@ -43421,7 +43983,7 @@ var SessionManager = class {
43421
43983
  /** 读目录下的归属标记;不存在或读不出时返回 null(视为"无人认领") */
43422
43984
  readWorkspaceMarker(dir) {
43423
43985
  try {
43424
- const raw = (0, import_node_fs9.readFileSync)((0, import_node_path12.join)(dir, WORKSPACE_MARKER), "utf8").trim();
43986
+ const raw = (0, import_node_fs9.readFileSync)((0, import_node_path13.join)(dir, WORKSPACE_MARKER), "utf8").trim();
43425
43987
  return raw.length > 0 ? raw : null;
43426
43988
  } catch {
43427
43989
  return null;
@@ -43454,7 +44016,7 @@ var SessionManager = class {
43454
44016
  if (!(0, import_node_fs9.existsSync)(this.projectSessionsDir)) {
43455
44017
  (0, import_node_fs9.mkdirSync)(this.projectSessionsDir, { recursive: true });
43456
44018
  }
43457
- const marker = (0, import_node_path12.join)(this.projectSessionsDir, WORKSPACE_MARKER);
44019
+ const marker = (0, import_node_path13.join)(this.projectSessionsDir, WORKSPACE_MARKER);
43458
44020
  if (!(0, import_node_fs9.existsSync)(marker)) {
43459
44021
  try {
43460
44022
  (0, import_node_fs9.writeFileSync)(marker, `${this.workspaceKey}
@@ -43525,14 +44087,6 @@ ${parts.join("\n\n---\n\n")}`;
43525
44087
  const skill = this.skillLoader.loadSkill(skillName);
43526
44088
  return skill ? skill.content : null;
43527
44089
  }
43528
- /**
43529
- * 根据用户输入查找匹配的 skills
43530
- * @param userInput 用户输入
43531
- * @returns 匹配的 skill 列表
43532
- */
43533
- findMatchingSkills(userInput) {
43534
- return this.skillLoader.findMatchingSkills(userInput);
43535
- }
43536
44090
  /**
43537
44091
  * 生成 skill 摘要(用于注入 system prompt)
43538
44092
  */
@@ -43559,11 +44113,11 @@ ${parts.join("\n\n---\n\n")}`;
43559
44113
  */
43560
44114
  allocateSessionPath() {
43561
44115
  const timestamp = this.generateTimestamp();
43562
- let path5 = (0, import_node_path12.join)(this.projectSessionsDir, `${timestamp}.jsonl`);
44116
+ let path5 = (0, import_node_path13.join)(this.projectSessionsDir, `${timestamp}.jsonl`);
43563
44117
  let suffix = 1;
43564
44118
  while ((0, import_node_fs9.existsSync)(path5)) {
43565
44119
  suffix += 1;
43566
- path5 = (0, import_node_path12.join)(this.projectSessionsDir, `${timestamp}-${suffix}.jsonl`);
44120
+ path5 = (0, import_node_path13.join)(this.projectSessionsDir, `${timestamp}-${suffix}.jsonl`);
43567
44121
  }
43568
44122
  return path5;
43569
44123
  }
@@ -43615,7 +44169,7 @@ ${parts.join("\n\n---\n\n")}`;
43615
44169
  return [];
43616
44170
  }
43617
44171
  return files.map((fileName) => {
43618
- const filePath = (0, import_node_path12.join)(this.projectSessionsDir, fileName);
44172
+ const filePath = (0, import_node_path13.join)(this.projectSessionsDir, fileName);
43619
44173
  let sizeBytes = 0;
43620
44174
  try {
43621
44175
  sizeBytes = (0, import_node_fs9.statSync)(filePath).size;
@@ -43742,13 +44296,20 @@ async function tryCreateDefaultSettings(providerService, settingsStore) {
43742
44296
  }
43743
44297
  return void 0;
43744
44298
  }
43745
- async function main() {
44299
+ async function main(argv = process.argv.slice(2)) {
44300
+ if (wantsVersion(argv)) {
44301
+ printVersion();
44302
+ return;
44303
+ }
43746
44304
  const workspaceRoot = process.cwd();
43747
44305
  const providerService = new ModelProviderService();
43748
44306
  const settingsStore = new SettingsStore();
44307
+ const updateChecker = createUpdateChecker({ settingsStore });
43749
44308
  const { model, providerName, modelName } = await createModelFromSettings(providerService, settingsStore);
44309
+ const skillLoader = new SkillLoader(workspaceRoot);
43750
44310
  const subAgentRuntime = new SubAgentRuntimeProvider();
43751
44311
  const toolRegistry = createToolRegistry(workspaceRoot, {
44312
+ skillLoader,
43752
44313
  extraTools: [
43753
44314
  createSubAgentTool({
43754
44315
  workspaceRoot,
@@ -43767,7 +44328,7 @@ async function main() {
43767
44328
  const { window: contextWindow, source: contextWindowSource } = await resolveContextWindowFromSettings(settingsStore, modelName);
43768
44329
  const subAgentEnabled = await settingsStore.getSubagentEnabled();
43769
44330
  const subAgentTrace = await settingsStore.getSubagentTrace();
43770
- const sessionManager = new SessionManager(workspaceRoot);
44331
+ const sessionManager = new SessionManager(workspaceRoot, { skillLoader });
43771
44332
  if (model) {
43772
44333
  sessionManager.setModel(model);
43773
44334
  }
@@ -43777,6 +44338,7 @@ async function main() {
43777
44338
  console.log(source_default.dim(`[Skills]
43778
44339
  ${skills.length > 0 ? skills.map((skill) => skill.name).join(",") : ""}`));
43779
44340
  }
44341
+ const loadedSkillNames = [];
43780
44342
  const buildPrompt = () => buildSystemPrompt({
43781
44343
  workspaceRoot,
43782
44344
  fixedContext: sessionManager.getFixedContext(),
@@ -43799,6 +44361,8 @@ async function main() {
43799
44361
  );
43800
44362
  console.log();
43801
44363
  }
44364
+ const updateResult = await updateChecker.checkOnStartup();
44365
+ printUpdateNotice(updateResult);
43802
44366
  const onNewSession = () => {
43803
44367
  return sessionManager.createNewSession();
43804
44368
  };
@@ -43810,7 +44374,9 @@ async function main() {
43810
44374
  if (newModel) {
43811
44375
  sessionManager.setModel(newModel);
43812
44376
  }
43813
- const newSystemPrompt = buildPrompt();
44377
+ sessionManager.getSkillLoader().clearCache();
44378
+ const newBasePrompt = buildPrompt();
44379
+ const newSystemPrompt = newBasePrompt + composeLoadedSkillSections(sessionManager, loadedSkillNames);
43814
44380
  const resolved = await resolveContextWindowFromSettings(
43815
44381
  settingsStore,
43816
44382
  newModelName
@@ -43820,6 +44386,7 @@ async function main() {
43820
44386
  return {
43821
44387
  model: newModel,
43822
44388
  systemPrompt: newSystemPrompt,
44389
+ baseSystemPrompt: newBasePrompt,
43823
44390
  contextWindow: resolved.window,
43824
44391
  contextWindowSource: resolved.source
43825
44392
  };
@@ -43844,6 +44411,8 @@ async function main() {
43844
44411
  await startRepl({
43845
44412
  prompt: "You: ",
43846
44413
  systemPrompt,
44414
+ baseSystemPrompt: systemPrompt,
44415
+ loadedSkillNames,
43847
44416
  messages,
43848
44417
  model,
43849
44418
  toolRegistry,
@@ -43853,6 +44422,7 @@ async function main() {
43853
44422
  workspaceRoot,
43854
44423
  providerService,
43855
44424
  settingsStore,
44425
+ updateChecker,
43856
44426
  sessionStore,
43857
44427
  sessionManager,
43858
44428
  contextWindow,