alchemylab-opencode 1.0.18 → 1.0.19

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 (68) hide show
  1. package/dist/{arktype-aI7TBD0R-HX4BR3RX.js → arktype-aI7TBD0R-LH6JTJZZ.js} +2 -2
  2. package/dist/{arktype-aI7TBD0R-HX4BR3RX.js.map → arktype-aI7TBD0R-LH6JTJZZ.js.map} +1 -1
  3. package/dist/chunk-2FM4SJ4F.js +506 -0
  4. package/dist/chunk-2FM4SJ4F.js.map +7 -0
  5. package/dist/{chunk-O5VWOPCL.js → chunk-4UCKACGO.js} +2 -2
  6. package/dist/{chunk-O5VWOPCL.js.map → chunk-4UCKACGO.js.map} +1 -1
  7. package/dist/{chunk-VL3RSEZ2.js → chunk-AHJSL352.js} +9 -9
  8. package/dist/{chunk-VL3RSEZ2.js.map → chunk-AHJSL352.js.map} +1 -1
  9. package/dist/{chunk-S3PMXQSO.js → chunk-ASJTXZ3G.js} +9 -7
  10. package/dist/{chunk-S3PMXQSO.js.map → chunk-ASJTXZ3G.js.map} +1 -1
  11. package/dist/{chunk-PWFRMDDF.js → chunk-BGADAZN4.js} +1 -1
  12. package/dist/{chunk-YX3AWX2A.js → chunk-E2D2QSW7.js} +67 -12
  13. package/dist/chunk-E2D2QSW7.js.map +7 -0
  14. package/dist/{chunk-3AF5MXLF.js → chunk-FXA7YNMN.js} +5 -5
  15. package/dist/{chunk-3AF5MXLF.js.map → chunk-FXA7YNMN.js.map} +1 -1
  16. package/dist/{chunk-FMP3YLWX.js → chunk-H5AN6PD4.js} +4 -4
  17. package/dist/{chunk-FMP3YLWX.js.map → chunk-H5AN6PD4.js.map} +1 -1
  18. package/dist/{chunk-6UDEKTJV.js → chunk-PV6JYN6T.js} +287 -24
  19. package/dist/chunk-PV6JYN6T.js.map +7 -0
  20. package/dist/{chunk-SNATISLF.js → chunk-RYVKLRJZ.js} +7 -5
  21. package/dist/{chunk-SNATISLF.js.map → chunk-RYVKLRJZ.js.map} +1 -1
  22. package/dist/{chunk-J4KUHHYM.js → chunk-SZM3G6UF.js} +151 -583
  23. package/dist/chunk-SZM3G6UF.js.map +7 -0
  24. package/dist/{chunk-Z6HKGDR4.js → chunk-WHJ5FGUS.js} +43 -15
  25. package/dist/chunk-WHJ5FGUS.js.map +7 -0
  26. package/dist/{default-u_dwuiYb-WOFHQQ3K.js → default-u_dwuiYb-644FBWJS.js} +5 -5
  27. package/dist/{effect-QlVUlMFu-UERLUUQW.js → effect-QlVUlMFu-GLKKNZAC.js} +3 -3
  28. package/dist/{effect-QlVUlMFu-UERLUUQW.js.map → effect-QlVUlMFu-GLKKNZAC.js.map} +1 -1
  29. package/dist/log-TOR3B2F5.js +19 -0
  30. package/dist/{mcp-4SNUY4OD.js → mcp-W2Z3DXFD.js} +4 -3
  31. package/dist/{registry-EQ7TBD6Z.js → registry-FWM7Q3YW.js} +5 -4
  32. package/dist/{run-XIMVXVMO.js → run-PMUMTYTZ.js} +13 -11
  33. package/dist/{run-XIMVXVMO.js.map → run-PMUMTYTZ.js.map} +1 -1
  34. package/dist/{run-service-RE7EK2UK.js → run-service-B4BDQWO3.js} +3 -2
  35. package/dist/run.js +1 -1
  36. package/dist/serve.js +5 -1
  37. package/dist/serve.js.map +2 -2
  38. package/dist/{server-P3JM3FFL.js → server-HETOVWO6.js} +11 -10
  39. package/dist/{session-FV32QIWG.js → session-37HDF4U2.js} +5 -4
  40. package/dist/{share-next-MK3GU4BJ.js → share-next-MP7REE74.js} +6 -5
  41. package/dist/share-next-MP7REE74.js.map +7 -0
  42. package/dist/{sury-CWZTCd75-XC3NCMTP.js → sury-CWZTCd75-SVV5EKIT.js} +3 -3
  43. package/dist/{sury-CWZTCd75-XC3NCMTP.js.map → sury-CWZTCd75-SVV5EKIT.js.map} +1 -1
  44. package/dist/{typebox-Dei93FPO-QOBKW4L7.js → typebox-Dei93FPO-LMTONO64.js} +2 -2
  45. package/dist/{typebox-Dei93FPO-QOBKW4L7.js.map → typebox-Dei93FPO-LMTONO64.js.map} +1 -1
  46. package/dist/{valibot--1zFm7rT-LHHLSLYY.js → valibot--1zFm7rT-HRT4VHTJ.js} +3 -3
  47. package/dist/{valibot--1zFm7rT-LHHLSLYY.js.map → valibot--1zFm7rT-HRT4VHTJ.js.map} +1 -1
  48. package/dist/{valibot-D_HTw1Gn-UR62BIDY.js → valibot-D_HTw1Gn-SZJR3J76.js} +5 -5
  49. package/dist/{valibot-D_HTw1Gn-UR62BIDY.js.map → valibot-D_HTw1Gn-SZJR3J76.js.map} +1 -1
  50. package/dist/{worktree-N3PSXGW4.js → worktree-ADWVQ6IR.js} +7 -6
  51. package/dist/{worktree-N3PSXGW4.js.map → worktree-ADWVQ6IR.js.map} +1 -1
  52. package/dist/{zod-Bwrt9trS-J2GOMA2R.js → zod-Bwrt9trS-5S7UQQDY.js} +3 -3
  53. package/dist/{zod-Bwrt9trS-J2GOMA2R.js.map → zod-Bwrt9trS-5S7UQQDY.js.map} +1 -1
  54. package/dist/{zod-DSgpEGAE-VTXQW6SB.js → zod-DSgpEGAE-X2KPFU7C.js} +7 -7
  55. package/dist/{zod-DSgpEGAE-VTXQW6SB.js.map → zod-DSgpEGAE-X2KPFU7C.js.map} +1 -1
  56. package/package.json +9 -4
  57. package/dist/chunk-6UDEKTJV.js.map +0 -7
  58. package/dist/chunk-J4KUHHYM.js.map +0 -7
  59. package/dist/chunk-YX3AWX2A.js.map +0 -7
  60. package/dist/chunk-Z6HKGDR4.js.map +0 -7
  61. /package/dist/{chunk-PWFRMDDF.js.map → chunk-BGADAZN4.js.map} +0 -0
  62. /package/dist/{default-u_dwuiYb-WOFHQQ3K.js.map → default-u_dwuiYb-644FBWJS.js.map} +0 -0
  63. /package/dist/{mcp-4SNUY4OD.js.map → log-TOR3B2F5.js.map} +0 -0
  64. /package/dist/{registry-EQ7TBD6Z.js.map → mcp-W2Z3DXFD.js.map} +0 -0
  65. /package/dist/{run-service-RE7EK2UK.js.map → registry-FWM7Q3YW.js.map} +0 -0
  66. /package/dist/{server-P3JM3FFL.js.map → run-service-B4BDQWO3.js.map} +0 -0
  67. /package/dist/{session-FV32QIWG.js.map → server-HETOVWO6.js.map} +0 -0
  68. /package/dist/{share-next-MK3GU4BJ.js.map → session-37HDF4U2.js.map} +0 -0
@@ -10,7 +10,7 @@ if (typeof globalThis.__name === 'undefined') { globalThis.__name = (t, n) => {
10
10
  if (typeof globalThis.__export === 'undefined') { globalThis.__export = (t, a) => { for (const k in a) Object.defineProperty(t, k, { get: a[k], enumerable: true }); }; }
11
11
 
12
12
 
13
- // node_modules/@standard-community/standard-openapi/dist/index-DZEfthgZ.js
13
+ // ../../../../../../../../Users/robin/Documents/alchemy_dev_new/opencode-server/node_modules/@standard-community/standard-openapi/dist/index-DZEfthgZ.js
14
14
  var errorMessageWrapper = (message) => `standard-openapi: ${message}`;
15
15
  var openapiVendorMap = /* @__PURE__ */ new Map();
16
16
  var getToOpenAPISchemaFn = async (vendor) => {
@@ -21,13 +21,13 @@ var getToOpenAPISchemaFn = async (vendor) => {
21
21
  let vendorFn;
22
22
  switch (vendor) {
23
23
  case "valibot":
24
- vendorFn = (await import("./valibot-D_HTw1Gn-UR62BIDY.js")).default();
24
+ vendorFn = (await import("./valibot-D_HTw1Gn-SZJR3J76.js")).default();
25
25
  break;
26
26
  case "zod":
27
- vendorFn = (await import("./zod-DSgpEGAE-VTXQW6SB.js")).default();
27
+ vendorFn = (await import("./zod-DSgpEGAE-X2KPFU7C.js")).default();
28
28
  break;
29
29
  default:
30
- vendorFn = (await import("./default-u_dwuiYb-WOFHQQ3K.js")).default();
30
+ vendorFn = (await import("./default-u_dwuiYb-644FBWJS.js")).default();
31
31
  }
32
32
  openapiVendorMap.set(vendor, vendorFn);
33
33
  return vendorFn;
@@ -46,4 +46,4 @@ export {
46
46
  errorMessageWrapper,
47
47
  toOpenAPISchema
48
48
  };
49
- //# sourceMappingURL=chunk-3AF5MXLF.js.map
49
+ //# sourceMappingURL=chunk-FXA7YNMN.js.map
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../node_modules/@standard-community/standard-openapi/dist/index-DZEfthgZ.js"],
3
+ "sources": ["../../../../../../../../../Users/robin/Documents/alchemy_dev_new/opencode-server/node_modules/@standard-community/standard-openapi/dist/index-DZEfthgZ.js"],
4
4
  "sourcesContent": ["const errorMessageWrapper = (message) => `standard-openapi: ${message}`;\nconst openapiVendorMap = /* @__PURE__ */ new Map();\n\nconst getToOpenAPISchemaFn = async (vendor) => {\n const cached = openapiVendorMap.get(vendor);\n if (cached) {\n return cached;\n }\n let vendorFn;\n switch (vendor) {\n case \"valibot\":\n vendorFn = (await import('./valibot-D_HTw1Gn.js')).default();\n break;\n case \"zod\":\n vendorFn = (await import('./zod-DSgpEGAE.js')).default();\n break;\n default:\n vendorFn = (await import('./default-u_dwuiYb.js')).default();\n }\n openapiVendorMap.set(vendor, vendorFn);\n return vendorFn;\n};\n\nconst toOpenAPISchema = async (schema, context = {}) => {\n const fn = await getToOpenAPISchemaFn(schema[\"~standard\"].vendor);\n const { components = {}, options } = context;\n const _schema = await fn(schema, { components, options });\n return {\n schema: _schema,\n components: Object.keys(components).length > 0 ? components : void 0\n };\n};\nfunction loadVendor(vendor, fn) {\n openapiVendorMap.set(vendor, fn);\n}\n\nexport { errorMessageWrapper as e, loadVendor as l, toOpenAPISchema as t };\n"],
5
5
  "mappings": ";;;;;;;;;;;;;AAAA,IAAM,sBAAsB,CAAC,YAAY,qBAAqB,OAAO;AACrE,IAAM,mBAAmC,oBAAI,IAAI;AAEjD,IAAM,uBAAuB,OAAO,WAAW;AAC7C,QAAM,SAAS,iBAAiB,IAAI,MAAM;AAC1C,MAAI,QAAQ;AACV,WAAO;AAAA,EACT;AACA,MAAI;AACJ,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,kBAAY,MAAM,OAAO,gCAAuB,GAAG,QAAQ;AAC3D;AAAA,IACF,KAAK;AACH,kBAAY,MAAM,OAAO,4BAAmB,GAAG,QAAQ;AACvD;AAAA,IACF;AACE,kBAAY,MAAM,OAAO,gCAAuB,GAAG,QAAQ;AAAA,EAC/D;AACA,mBAAiB,IAAI,QAAQ,QAAQ;AACrC,SAAO;AACT;AAEA,IAAM,kBAAkB,OAAO,QAAQ,UAAU,CAAC,MAAM;AACtD,QAAM,KAAK,MAAM,qBAAqB,OAAO,WAAW,EAAE,MAAM;AAChE,QAAM,EAAE,aAAa,CAAC,GAAG,QAAQ,IAAI;AACrC,QAAM,UAAU,MAAM,GAAG,QAAQ,EAAE,YAAY,QAAQ,CAAC;AACxD,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,YAAY,OAAO,KAAK,UAAU,EAAE,SAAS,IAAI,aAAa;AAAA,EAChE;AACF;",
6
6
  "names": []
@@ -11,12 +11,12 @@ if (typeof globalThis.__export === 'undefined') { globalThis.__export = (t, a) =
11
11
 
12
12
  import {
13
13
  convertToOpenAPISchema
14
- } from "./chunk-O5VWOPCL.js";
14
+ } from "./chunk-4UCKACGO.js";
15
15
  import {
16
16
  toJsonSchema
17
- } from "./chunk-VL3RSEZ2.js";
17
+ } from "./chunk-AHJSL352.js";
18
18
 
19
- // node_modules/@standard-community/standard-openapi/dist/default-u_dwuiYb.js
19
+ // ../../../../../../../../Users/robin/Documents/alchemy_dev_new/opencode-server/node_modules/@standard-community/standard-openapi/dist/default-u_dwuiYb.js
20
20
  function getToOpenAPISchemaFn() {
21
21
  return async (schema, context) => convertToOpenAPISchema(
22
22
  await toJsonSchema(schema, context.options),
@@ -27,4 +27,4 @@ function getToOpenAPISchemaFn() {
27
27
  export {
28
28
  getToOpenAPISchemaFn
29
29
  };
30
- //# sourceMappingURL=chunk-FMP3YLWX.js.map
30
+ //# sourceMappingURL=chunk-H5AN6PD4.js.map
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../node_modules/@standard-community/standard-openapi/dist/default-u_dwuiYb.js"],
3
+ "sources": ["../../../../../../../../../Users/robin/Documents/alchemy_dev_new/opencode-server/node_modules/@standard-community/standard-openapi/dist/default-u_dwuiYb.js"],
4
4
  "sourcesContent": ["import { toJsonSchema } from '@standard-community/standard-json';\nimport { convertToOpenAPISchema } from './vendors/convert.js';\n\nfunction getToOpenAPISchemaFn() {\n return async (schema, context) => convertToOpenAPISchema(\n await toJsonSchema(schema, context.options),\n context\n );\n}\n\nexport { getToOpenAPISchemaFn as default };\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;;;;;;AAGA,SAAS,uBAAuB;AAC9B,SAAO,OAAO,QAAQ,YAAY;AAAA,IAChC,MAAM,aAAa,QAAQ,QAAQ,OAAO;AAAA,IAC1C;AAAA,EACF;AACF;",
6
6
  "names": []
@@ -36,18 +36,14 @@ import {
36
36
  spawn,
37
37
  withTimeout,
38
38
  withTransientReadRetry
39
- } from "./chunk-YX3AWX2A.js";
39
+ } from "./chunk-E2D2QSW7.js";
40
40
  import {
41
41
  AppFileSystem,
42
42
  BusEvent,
43
43
  Database,
44
- Filesystem,
45
44
  Flag,
46
- Glob,
47
- Global,
48
45
  Instance,
49
46
  InstanceState,
50
- Log,
51
47
  MessageTable,
52
48
  NamedError,
53
49
  NotFoundError,
@@ -64,7 +60,7 @@ import {
64
60
  makeRuntime,
65
61
  runPromiseExit,
66
62
  which
67
- } from "./chunk-J4KUHHYM.js";
63
+ } from "./chunk-SZM3G6UF.js";
68
64
  import {
69
65
  Identifier,
70
66
  MessageID,
@@ -75,6 +71,13 @@ import {
75
71
  Newtype,
76
72
  withStatics
77
73
  } from "./chunk-BI5OUHTQ.js";
74
+ import {
75
+ Filesystem,
76
+ Glob,
77
+ Global,
78
+ Log,
79
+ ProtectedRoot
80
+ } from "./chunk-2FM4SJ4F.js";
78
81
  import {
79
82
  __callDispose,
80
83
  __export,
@@ -2327,7 +2330,18 @@ var Snapshot;
2327
2330
  })
2328
2331
  );
2329
2332
  });
2333
+ const assertRestorable = () => {
2334
+ if (!ProtectedRoot.allows(state2.worktree)) {
2335
+ log5.error("refused to restore a snapshot in the user's project folder", { worktree: state2.worktree });
2336
+ return new Error(
2337
+ `snapshot restore refused: ${state2.worktree} is the user's project folder. Nothing was written.`
2338
+ );
2339
+ }
2340
+ return void 0;
2341
+ };
2330
2342
  const restore2 = Effect7.fnUntraced(function* (snapshot) {
2343
+ const refused = assertRestorable();
2344
+ if (refused) return yield* Effect7.die(refused);
2331
2345
  return yield* locked(
2332
2346
  Effect7.gen(function* () {
2333
2347
  log5.info("restore", { commit: snapshot });
@@ -2353,6 +2367,8 @@ var Snapshot;
2353
2367
  );
2354
2368
  });
2355
2369
  const revert2 = Effect7.fnUntraced(function* (patches) {
2370
+ const refused = assertRestorable();
2371
+ if (refused) return yield* Effect7.die(refused);
2356
2372
  return yield* locked(
2357
2373
  Effect7.gen(function* () {
2358
2374
  const ops = [];
@@ -3310,6 +3326,9 @@ var MessageV2;
3310
3326
  root: z12.string()
3311
3327
  }),
3312
3328
  summary: z12.boolean().optional(),
3329
+ /** Alchemy fork: answered the user BESIDE a running tool call (an aside).
3330
+ * The main loop neither exits on it nor answers that user message again. */
3331
+ aside: z12.boolean().optional(),
3313
3332
  cost: z12.number(),
3314
3333
  tokens: z12.object({
3315
3334
  total: z12.number().optional(),
@@ -3576,7 +3595,9 @@ var MessageV2;
3576
3595
  state: "output-error",
3577
3596
  toolCallId: part2.callID,
3578
3597
  input: part2.state.input,
3579
- errorText: "[Tool execution was interrupted]",
3598
+ // An aside (alchemy fork) answers the user while the tool
3599
+ // still runs: say so, or the model believes the run died.
3600
+ errorText: options?.runningToolNote ?? "[Tool execution was interrupted]",
3580
3601
  ...differentModel ? {} : { callProviderMetadata: part2.metadata }
3581
3602
  });
3582
3603
  }
@@ -6902,7 +6923,7 @@ var Plugin;
6902
6923
  const state = yield* InstanceState.make(
6903
6924
  Effect11.fn("Plugin.state")(function* (ctx) {
6904
6925
  const hooks = [];
6905
- const { Server } = yield* Effect11.promise(() => import("./server-P3JM3FFL.js"));
6926
+ const { Server } = yield* Effect11.promise(() => import("./server-HETOVWO6.js"));
6906
6927
  const client2 = createOpencodeClient({
6907
6928
  baseUrl: "http://localhost:4096",
6908
6929
  directory: ctx.directory,
@@ -10291,6 +10312,7 @@ var ProviderTransform;
10291
10312
  }
10292
10313
  function unsupportedParts(msgs, model) {
10293
10314
  return msgs.map((msg) => {
10315
+ if (msg.role === "tool" && Array.isArray(msg.content)) return unsupportedToolMedia(msg, model);
10294
10316
  if (msg.role !== "user" || !Array.isArray(msg.content)) return msg;
10295
10317
  const filtered = msg.content.map((part) => {
10296
10318
  if (part.type !== "file" && part.type !== "image") return part;
@@ -10320,6 +10342,25 @@ var ProviderTransform;
10320
10342
  return { ...msg, content: filtered };
10321
10343
  });
10322
10344
  }
10345
+ function unsupportedToolMedia(msg, model) {
10346
+ let changed = false;
10347
+ const content = msg.content.map((part) => {
10348
+ if (part?.type !== "tool-result" || part.output?.type !== "content" || !Array.isArray(part.output.value)) return part;
10349
+ const value = part.output.value.map((item) => {
10350
+ if (!item || item.type !== "media" && item.type !== "file-data" && item.type !== "image-data") return item;
10351
+ const modality = typeof item.mediaType === "string" ? mimeToModality(item.mediaType) : void 0;
10352
+ if (!modality || model.capabilities.input[modality]) return item;
10353
+ changed = true;
10354
+ const hint = modality === "pdf" ? " Use read_files on the same path for the PDF's text." : "";
10355
+ return {
10356
+ type: "text",
10357
+ text: `NOTE: ${part.toolName ?? "the tool"} returned a ${modality === "pdf" ? "PDF" : modality} that this model cannot take as input, so it was left out.${hint}`
10358
+ };
10359
+ });
10360
+ return { ...part, output: { ...part.output, value } };
10361
+ });
10362
+ return changed ? { ...msg, content } : msg;
10363
+ }
10323
10364
  function message(msgs, model, options2) {
10324
10365
  msgs = unsupportedParts(msgs, model);
10325
10366
  msgs = normalizeMessages(msgs, model, options2);
@@ -10391,13 +10432,39 @@ var ProviderTransform;
10391
10432
  ProviderTransform2.topK = topK;
10392
10433
  const WIDELY_SUPPORTED_EFFORTS = ["low", "medium", "high"];
10393
10434
  const OPENAI_EFFORTS = ["none", "minimal", ...WIDELY_SUPPORTED_EFFORTS, "xhigh"];
10435
+ function claudeVersion(id) {
10436
+ const m = /claude-(opus|sonnet|haiku|fable)-(\d{1,2})(?:[-.](\d{1,2}))?(?!\d)/.exec(id.toLowerCase());
10437
+ if (!m) return void 0;
10438
+ return { family: m[1], major: Number(m[2]), minor: m[3] === void 0 ? 0 : Number(m[3]) };
10439
+ }
10440
+ ProviderTransform2.claudeVersion = claudeVersion;
10441
+ const EFFORT_LADDER = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
10442
+ function nearestVariant(wanted, available) {
10443
+ if (!wanted || available.length === 0) return void 0;
10444
+ if (available.includes(wanted)) return wanted;
10445
+ const at = EFFORT_LADDER.indexOf(wanted);
10446
+ if (at < 0) return void 0;
10447
+ let best;
10448
+ let bestDistance = Infinity;
10449
+ for (const v of available) {
10450
+ const i = EFFORT_LADDER.indexOf(v);
10451
+ if (i < 0) continue;
10452
+ const d = Math.abs(i - at);
10453
+ if (d < bestDistance || d === bestDistance && best !== void 0 && i < EFFORT_LADDER.indexOf(best)) {
10454
+ best = v;
10455
+ bestDistance = d;
10456
+ }
10457
+ }
10458
+ return best;
10459
+ }
10460
+ ProviderTransform2.nearestVariant = nearestVariant;
10394
10461
  function variants(model) {
10395
10462
  if (!model.capabilities.reasoning) return {};
10396
10463
  const id = model.id.toLowerCase();
10397
- const isAnthropicAdaptive = ["opus-4-6", "opus-4.6", "sonnet-4-6", "sonnet-4.6"].some(
10398
- (v) => model.api.id.includes(v)
10399
- );
10400
- const adaptiveEfforts = ["low", "medium", "high", "max"];
10464
+ const claude = claudeVersion(model.api.id);
10465
+ const isAnthropicAdaptive = !!claude && (claude.major > 4 || claude.major === 4 && claude.minor >= 6);
10466
+ const takesXhigh = model.api.npm === "@ai-sdk/anthropic" && !!claude && claude.major >= 5 && (claude.family === "opus" || claude.family === "sonnet");
10467
+ const adaptiveEfforts = takesXhigh ? ["low", "medium", "high", "xhigh", "max"] : ["low", "medium", "high", "max"];
10401
10468
  if (id.includes("deepseek") || id.includes("minimax") || id.includes("glm") || id.includes("mistral") || id.includes("kimi") || id.includes("k2p5") || id.includes("qwen"))
10402
10469
  return {};
10403
10470
  if (id.includes("grok") && id.includes("grok-3-mini")) {
@@ -12906,7 +12973,7 @@ var Skill;
12906
12973
  Effect15.catch(
12907
12974
  Effect15.fnUntraced(function* (err) {
12908
12975
  const message = ConfigMarkdown.FrontmatterError.isInstance(err) ? err.data.message : `Failed to parse skill ${match}`;
12909
- const { Session: Session3 } = yield* Effect15.promise(() => import("./session-FV32QIWG.js"));
12976
+ const { Session: Session3 } = yield* Effect15.promise(() => import("./session-37HDF4U2.js"));
12910
12977
  yield* bus.publish(Session3.Event.Error, { error: new NamedError.Unknown({ message }).toObject() });
12911
12978
  log5.error("failed to load skill", { skill: match, err });
12912
12979
  return void 0;
@@ -14474,6 +14541,24 @@ var LLM;
14474
14541
  LLM2.hasToolCalls = hasToolCalls;
14475
14542
  })(LLM || (LLM = {}));
14476
14543
 
14544
+ // packages/opencode/src/session/repeated-preamble.ts
14545
+ function repeatedPreamble(parts2, finished) {
14546
+ if (phaseOf(finished) !== "final_answer") return void 0;
14547
+ const text5 = finished.text.trim();
14548
+ if (!text5) return void 0;
14549
+ return parts2.find(
14550
+ (p) => p.type === "text" && p.id !== finished.id && phaseOf(p) === "commentary" && p.text.trim() === text5
14551
+ );
14552
+ }
14553
+ function phaseOf(part) {
14554
+ for (const value of Object.values(part.metadata ?? {})) {
14555
+ if (value && typeof value === "object" && typeof value.phase === "string") {
14556
+ return value.phase;
14557
+ }
14558
+ }
14559
+ return void 0;
14560
+ }
14561
+
14477
14562
  // packages/opencode/src/session/overflow.ts
14478
14563
  var COMPACTION_BUFFER = 2e4;
14479
14564
  function isOverflow(input) {
@@ -14918,6 +15003,14 @@ var SessionProcessor;
14918
15003
  ctx.currentText.time = { start: Date.now(), end: Date.now() };
14919
15004
  if (value.providerMetadata) ctx.currentText.metadata = value.providerMetadata;
14920
15005
  yield* session.updatePart(ctx.currentText);
15006
+ const repeated = repeatedPreamble(MessageV2.parts(ctx.assistantMessage.id), ctx.currentText);
15007
+ if (repeated) {
15008
+ yield* session.removePart({
15009
+ sessionID: repeated.sessionID,
15010
+ messageID: repeated.messageID,
15011
+ partID: repeated.id
15012
+ });
15013
+ }
14921
15014
  ctx.currentText = void 0;
14922
15015
  return;
14923
15016
  case "finish":
@@ -15411,6 +15504,21 @@ When constructing the summary, try to stick to this template:
15411
15504
  );
15412
15505
  })(SessionCompaction || (SessionCompaction = {}));
15413
15506
 
15507
+ // packages/opencode/src/session/aside-audience.ts
15508
+ var PERSON_KINDS = /* @__PURE__ */ new Set(["inbox-chat", "via-herald"]);
15509
+ function writtenByAPerson(parts2) {
15510
+ const kinds = parts2.map((p) => p?.metadata?.alchemy?.kind).filter((k) => typeof k === "string" && k.length > 0);
15511
+ if (kinds.length === 0) return true;
15512
+ return kinds.some((k) => PERSON_KINDS.has(k));
15513
+ }
15514
+ var TYPED_CALL = /\bto=functions\.|^\s*functions\.[A-Za-z_]+\s*[({]/m;
15515
+ function asideTextToDrop(parts2) {
15516
+ const texts = parts2.filter((p) => p.type === "text");
15517
+ const clean = texts.filter((p) => (p.text ?? "").trim().length > 0 && !TYPED_CALL.test(p.text ?? ""));
15518
+ const keep = clean.length > 0 ? clean[clean.length - 1] : void 0;
15519
+ return { drop: texts.filter((p) => p !== keep).map((p) => p.id), answered: keep !== void 0 };
15520
+ }
15521
+
15414
15522
  // packages/opencode/src/session/instruction.ts
15415
15523
  import os5 from "os";
15416
15524
  import path11 from "path";
@@ -16525,7 +16633,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
16525
16633
  item.execute = (args, opts) => Effect26.runPromise(
16526
16634
  Effect26.gen(function* () {
16527
16635
  const ctx = context(args, opts);
16528
- const mcpModule = yield* Effect26.promise(() => import("./mcp-4SNUY4OD.js"));
16636
+ const mcpModule = yield* Effect26.promise(() => import("./mcp-W2Z3DXFD.js"));
16529
16637
  mcpModule.MCP.currentToolContext = { sessionId: ctx.sessionID, messageId: ctx.messageID };
16530
16638
  yield* plugin.trigger(
16531
16639
  "tool.execute.before",
@@ -16974,9 +17082,8 @@ NOTE: At any point in time through this workflow you should feel free to ask the
16974
17082
  throw error;
16975
17083
  }
16976
17084
  const model = input.model ?? ag.model ?? (yield* lastModel(input.sessionID));
16977
- const same = ag.model && model.providerID === ag.model.providerID && model.modelID === ag.model.modelID;
16978
- const full = !input.variant && ag.variant && same ? yield* provider2.getModel(model.providerID, model.modelID).pipe(Effect26.catch(() => Effect26.succeed(void 0))) : void 0;
16979
- const variant = input.variant ?? (ag.variant && full?.variants?.[ag.variant] ? ag.variant : void 0);
17085
+ const full = !input.variant && ag.variant ? yield* provider2.getModel(model.providerID, model.modelID).pipe(Effect26.catch(() => Effect26.succeed(void 0))) : void 0;
17086
+ const variant = input.variant ?? ProviderTransform.nearestVariant(ag.variant, Object.keys(full?.variants ?? {}));
16980
17087
  const info = {
16981
17088
  id: input.messageID ?? MessageID.ascending(),
16982
17089
  role: "user",
@@ -17294,6 +17401,80 @@ NOTE: At any point in time through this workflow you should feel free to ask the
17294
17401
  for (const part of parts2) yield* sessions.updatePart(part);
17295
17402
  return { info, parts: parts2 };
17296
17403
  }, Effect26.scoped);
17404
+ const inFlightTool = Effect26.fn("SessionPrompt.inFlightTool")(function* (sessionID) {
17405
+ const msgs = yield* MessageV2.filterCompactedEffect(sessionID);
17406
+ for (let i = msgs.length - 1; i >= 0; i--) {
17407
+ const m = msgs[i];
17408
+ if (m.info.role !== "assistant") continue;
17409
+ if (m.info.aside) continue;
17410
+ const part = m.parts.find((p) => p.type === "tool" && (p.state.status === "running" || p.state.status === "pending"));
17411
+ return part && part.type === "tool" ? part.tool : null;
17412
+ }
17413
+ return null;
17414
+ });
17415
+ const ASIDE_PROMPT = [
17416
+ "A tool you called is still running and its result has not arrived. The user just wrote to you.",
17417
+ "Answer them now, in a sentence or two, from what you already know. You cannot use tools until the run returns, so do not promise to look anything up.",
17418
+ "Do not restate at length what you are doing; if their message changes the work, say what you will do once the run returns."
17419
+ ].join(" ");
17420
+ const aside = Effect26.fn("SessionPrompt.aside")(function* (input) {
17421
+ const ctx = yield* InstanceState.context;
17422
+ const msgs = yield* MessageV2.filterCompactedEffect(input.sessionID);
17423
+ const model = yield* getModel(input.user.model.providerID, input.user.model.modelID, input.sessionID);
17424
+ const agent = yield* agents.get(input.user.agent);
17425
+ if (!agent) return null;
17426
+ const msg = {
17427
+ id: MessageID.ascending(),
17428
+ parentID: input.user.id,
17429
+ role: "assistant",
17430
+ mode: agent.name,
17431
+ agent: agent.name,
17432
+ variant: input.user.variant,
17433
+ path: { cwd: ctx.directory, root: ctx.worktree },
17434
+ cost: 0,
17435
+ tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
17436
+ modelID: model.id,
17437
+ providerID: model.providerID,
17438
+ time: { created: Date.now() },
17439
+ sessionID: input.sessionID,
17440
+ aside: true
17441
+ };
17442
+ yield* sessions.updateMessage(msg);
17443
+ const handle = yield* processor.create({ assistantMessage: msg, sessionID: input.sessionID, model });
17444
+ const [env, instructions, modelMsgs] = yield* Effect26.all([
17445
+ Effect26.promise(() => SystemPrompt.environment(model)),
17446
+ instruction.system().pipe(Effect26.orDie),
17447
+ Effect26.promise(
17448
+ () => MessageV2.toModelMessages(msgs, model, {
17449
+ runningToolNote: `[${input.running} is still running. You are answering the user beside it; its result arrives when it finishes.]`
17450
+ })
17451
+ )
17452
+ ]);
17453
+ log5.info("aside", { sessionID: input.sessionID, running: input.running, messageID: msg.id });
17454
+ yield* handle.process({
17455
+ user: input.user,
17456
+ agent,
17457
+ permission: input.session.permission,
17458
+ sessionID: input.sessionID,
17459
+ parentSessionID: input.session.parentID,
17460
+ system: [...env, ...instructions, ASIDE_PROMPT],
17461
+ messages: modelMsgs,
17462
+ tools: {},
17463
+ model
17464
+ });
17465
+ const done = yield* lastAssistant(input.sessionID);
17466
+ if (done.info.id !== msg.id) return done;
17467
+ const { drop, answered } = asideTextToDrop(done.parts);
17468
+ if (!answered) {
17469
+ log5.warn("aside wrote no usable answer; the message waits for the run", { sessionID: input.sessionID, messageID: msg.id });
17470
+ yield* sessions.removeMessage({ sessionID: input.sessionID, messageID: msg.id });
17471
+ return null;
17472
+ }
17473
+ for (const partID of drop) {
17474
+ yield* sessions.removePart({ sessionID: input.sessionID, messageID: msg.id, partID });
17475
+ }
17476
+ return drop.length > 0 ? yield* lastAssistant(input.sessionID) : done;
17477
+ });
17297
17478
  const prompt2 = Effect26.fn("SessionPrompt.prompt")(
17298
17479
  function* (input) {
17299
17480
  const session = yield* sessions.get(input.sessionID);
@@ -17309,6 +17490,23 @@ NOTE: At any point in time through this workflow you should feel free to ask the
17309
17490
  yield* sessions.setPermission({ sessionID: session.id, permission: permissions });
17310
17491
  }
17311
17492
  if (input.noReply === true) return message;
17493
+ {
17494
+ const st0 = yield* InstanceState.get(state);
17495
+ if (st0.runners.get(input.sessionID)?.busy === true && writtenByAPerson(message.parts)) {
17496
+ const running = yield* inFlightTool(input.sessionID);
17497
+ if (running) {
17498
+ const answered = yield* aside({ sessionID: input.sessionID, session, user: message.info, running }).pipe(
17499
+ // Effect 4: catchCause (catchAllCause is Effect 3, and the
17500
+ // miss took every mid-run message down with it, found live).
17501
+ Effect26.catchCause((cause) => {
17502
+ log5.warn("aside failed; the message waits for the run", { sessionID: input.sessionID, cause: String(cause) });
17503
+ return Effect26.succeed(null);
17504
+ })
17505
+ );
17506
+ if (answered) return answered;
17507
+ }
17508
+ }
17509
+ }
17312
17510
  const st8 = yield* InstanceState.get(state);
17313
17511
  const wasBusy = st8.runners.get(input.sessionID)?.busy === true;
17314
17512
  let result = yield* loop2({ sessionID: input.sessionID });
@@ -17343,6 +17541,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
17343
17541
  let tasks = [];
17344
17542
  for (let i = msgs.length - 1; i >= 0; i--) {
17345
17543
  const msg2 = msgs[i];
17544
+ if (msg2.info.role === "assistant" && msg2.info.aside) continue;
17346
17545
  if (!lastUser && msg2.info.role === "user") lastUser = msg2.info;
17347
17546
  if (!lastAssistant2 && msg2.info.role === "assistant") lastAssistant2 = msg2.info;
17348
17547
  if (!lastFinished && msg2.info.role === "assistant" && msg2.info.finish) lastFinished = msg2.info;
@@ -17443,8 +17642,12 @@ NOTE: At any point in time through this workflow you should feel free to ask the
17443
17642
  }
17444
17643
  if (step === 1) SessionSummary.summarize({ sessionID, messageID: lastUser.id });
17445
17644
  if (step > 1 && lastFinished) {
17645
+ const answeredByAside = new Set(
17646
+ msgs.filter((m) => m.info.role === "assistant" && m.info.aside).map((m) => m.info.parentID)
17647
+ );
17446
17648
  for (const m of msgs) {
17447
17649
  if (m.info.role !== "user" || Identifier.compareAscending(m.info.id, lastFinished.id) <= 0) continue;
17650
+ if (answeredByAside.has(m.info.id)) continue;
17448
17651
  for (const p of m.parts) {
17449
17652
  if (p.type !== "text" || p.ignored || p.synthetic) continue;
17450
17653
  if (!p.text.trim()) continue;
@@ -18101,7 +18304,7 @@ var Session;
18101
18304
  const cfg = yield* config.get();
18102
18305
  if (cfg.share === "disabled") throw new Error("Sharing is disabled in configuration");
18103
18306
  const result = yield* Effect27.promise(async () => {
18104
- const { ShareNext } = await import("./share-next-MK3GU4BJ.js");
18307
+ const { ShareNext } = await import("./share-next-MP7REE74.js");
18105
18308
  return ShareNext.create(id);
18106
18309
  });
18107
18310
  yield* Effect27.sync(() => SyncEvent.run(Session3.Event.Updated, { sessionID: id, info: { share: { url: result.url } } }));
@@ -18109,7 +18312,7 @@ var Session;
18109
18312
  });
18110
18313
  const unshare2 = Effect27.fn("Session.unshare")(function* (id) {
18111
18314
  yield* Effect27.promise(async () => {
18112
- const { ShareNext } = await import("./share-next-MK3GU4BJ.js");
18315
+ const { ShareNext } = await import("./share-next-MP7REE74.js");
18113
18316
  await ShareNext.remove(id);
18114
18317
  });
18115
18318
  yield* Effect27.sync(() => SyncEvent.run(Session3.Event.Updated, { sessionID: id, info: { share: { url: null } } }));
@@ -21405,11 +21608,11 @@ Expected payload format:
21405
21608
  [{"tool": "tool_name", "parameters": {...}}, {...}]`;
21406
21609
  },
21407
21610
  async execute(params, ctx) {
21408
- const { Session: Session3 } = await import("./session-FV32QIWG.js");
21611
+ const { Session: Session3 } = await import("./session-37HDF4U2.js");
21409
21612
  const { PartID: PartID2 } = await import("./schema-EWYI6LGA.js");
21410
21613
  const toolCalls = params.tool_calls.slice(0, 25);
21411
21614
  const discardedCalls = params.tool_calls.slice(25);
21412
- const { ToolRegistry: ToolRegistry2 } = await import("./registry-EQ7TBD6Z.js");
21615
+ const { ToolRegistry: ToolRegistry2 } = await import("./registry-FWM7Q3YW.js");
21413
21616
  const availableTools = await ToolRegistry2.tools({ modelID: ModelID.make(""), providerID: ProviderID.make("") });
21414
21617
  const toolMap = new Map(availableTools.map((t) => [t.id, t]));
21415
21618
  const executeCall = async (call) => {
@@ -22362,6 +22565,62 @@ function abortAfterAny(ms, ...signals) {
22362
22565
  };
22363
22566
  }
22364
22567
 
22568
+ // packages/opencode/src/tool/webfetch-guard.ts
22569
+ import { lookup } from "node:dns/promises";
22570
+ import { isIP } from "node:net";
22571
+ function v4Blocked(ip) {
22572
+ const [a, b] = ip.split(".").map((n) => Number(n));
22573
+ if (a === 0 || a === 10 || a === 127) return true;
22574
+ if (a === 100 && b >= 64 && b <= 127) return true;
22575
+ if (a === 169 && b === 254) return true;
22576
+ if (a === 172 && b >= 16 && b <= 31) return true;
22577
+ if (a === 192 && b === 168) return true;
22578
+ if (a === 198 && (b === 18 || b === 19)) return true;
22579
+ if (a >= 224) return true;
22580
+ return false;
22581
+ }
22582
+ function v6Blocked(ip) {
22583
+ const low = ip.toLowerCase();
22584
+ if (low === "::" || low === "::1") return true;
22585
+ const mapped = low.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/);
22586
+ if (mapped) return v4Blocked(mapped[1]);
22587
+ if (/^f[cd]/.test(low)) return true;
22588
+ if (/^fe[89ab]/.test(low)) return true;
22589
+ if (/^ff/.test(low)) return true;
22590
+ return false;
22591
+ }
22592
+ function isNonPublicAddress(ip) {
22593
+ const kind = isIP(ip);
22594
+ if (kind === 4) return v4Blocked(ip);
22595
+ if (kind === 6) return v6Blocked(ip);
22596
+ return true;
22597
+ }
22598
+ async function assertPublicUrl(raw) {
22599
+ const url = new URL(raw);
22600
+ const host = url.hostname.replace(/^\[|\]$/g, "");
22601
+ if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local") || host.endsWith(".internal")) {
22602
+ throw new Error(`webfetch reaches the public web only; ${url.host} is on this machine or its network`);
22603
+ }
22604
+ const addresses = isIP(host) ? [host] : (await lookup(host, { all: true })).map((a) => a.address);
22605
+ if (addresses.length === 0 || addresses.some(isNonPublicAddress)) {
22606
+ throw new Error(`webfetch reaches the public web only; ${url.host} resolves to an address on this machine or its network`);
22607
+ }
22608
+ }
22609
+ async function fetchPublic(raw, init, maxRedirects = 5) {
22610
+ let current = raw;
22611
+ for (let hop = 0; hop <= maxRedirects; hop++) {
22612
+ await assertPublicUrl(current);
22613
+ const response = await fetch(current, { ...init, redirect: "manual" });
22614
+ const location = response.headers.get("location");
22615
+ if (response.status >= 300 && response.status < 400 && location) {
22616
+ current = new URL(location, current).toString();
22617
+ continue;
22618
+ }
22619
+ return response;
22620
+ }
22621
+ throw new Error(`webfetch stopped after ${maxRedirects} redirects`);
22622
+ }
22623
+
22365
22624
  // packages/opencode/src/tool/webfetch.ts
22366
22625
  var MAX_RESPONSE_SIZE = 5 * 1024 * 1024;
22367
22626
  var DEFAULT_TIMEOUT2 = 30 * 1e3;
@@ -22408,8 +22667,8 @@ var WebFetchTool = Tool.define("webfetch", {
22408
22667
  Accept: acceptHeader,
22409
22668
  "Accept-Language": "en-US,en;q=0.9"
22410
22669
  };
22411
- const initial = await fetch(params.url, { signal, headers });
22412
- const response = initial.status === 403 && initial.headers.get("cf-mitigated") === "challenge" ? await fetch(params.url, { signal, headers: { ...headers, "User-Agent": "opencode" } }) : initial;
22670
+ const initial = await fetchPublic(params.url, { signal, headers });
22671
+ const response = initial.status === 403 && initial.headers.get("cf-mitigated") === "challenge" ? await fetchPublic(params.url, { signal, headers: { ...headers, "User-Agent": "opencode" } }) : initial;
22413
22672
  clearTimeout2();
22414
22673
  if (!response.ok) {
22415
22674
  throw new Error(`Request failed with status code: ${response.status}`);
@@ -23633,6 +23892,10 @@ var ApplyPatchTool = Tool.define("apply_patch", {
23633
23892
  files
23634
23893
  }
23635
23894
  });
23895
+ for (const change of fileChanges) {
23896
+ ProtectedRoot.assert(change.filePath, "apply_patch");
23897
+ if (change.movePath) ProtectedRoot.assert(change.movePath, "apply_patch");
23898
+ }
23636
23899
  const updates = [];
23637
23900
  for (const change of fileChanges) {
23638
23901
  const edited = change.type === "delete" ? void 0 : change.movePath ?? change.filePath;
@@ -23939,4 +24202,4 @@ export {
23939
24202
  Session,
23940
24203
  Plugin
23941
24204
  };
23942
- //# sourceMappingURL=chunk-6UDEKTJV.js.map
24205
+ //# sourceMappingURL=chunk-PV6JYN6T.js.map