@oh-my-pi/pi-coding-agent 15.0.1 → 15.1.0

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 (168) hide show
  1. package/CHANGELOG.md +94 -1
  2. package/examples/custom-tools/README.md +11 -7
  3. package/examples/custom-tools/hello/index.ts +2 -2
  4. package/examples/extensions/README.md +19 -8
  5. package/examples/extensions/api-demo.ts +15 -19
  6. package/examples/extensions/hello.ts +5 -6
  7. package/examples/extensions/plan-mode.ts +1 -1
  8. package/examples/extensions/reload-runtime.ts +4 -3
  9. package/examples/extensions/with-deps/index.ts +4 -3
  10. package/examples/sdk/06-extensions.ts +4 -2
  11. package/package.json +8 -18
  12. package/src/autoresearch/tools/init-experiment.ts +38 -41
  13. package/src/autoresearch/tools/log-experiment.ts +32 -41
  14. package/src/autoresearch/tools/run-experiment.ts +3 -3
  15. package/src/autoresearch/tools/update-notes.ts +11 -11
  16. package/src/commands/commit.ts +10 -0
  17. package/src/commit/agentic/tools/analyze-file.ts +4 -4
  18. package/src/commit/agentic/tools/git-file-diff.ts +4 -4
  19. package/src/commit/agentic/tools/git-hunk.ts +5 -5
  20. package/src/commit/agentic/tools/git-overview.ts +4 -4
  21. package/src/commit/agentic/tools/propose-changelog.ts +13 -13
  22. package/src/commit/agentic/tools/propose-commit.ts +6 -6
  23. package/src/commit/agentic/tools/recent-commits.ts +3 -3
  24. package/src/commit/agentic/tools/schemas.ts +28 -28
  25. package/src/commit/agentic/tools/split-commit.ts +22 -21
  26. package/src/commit/analysis/summary.ts +4 -4
  27. package/src/commit/changelog/generate.ts +7 -11
  28. package/src/commit/shared-llm.ts +22 -34
  29. package/src/config/config-file.ts +35 -13
  30. package/src/config/model-registry.ts +40 -191
  31. package/src/config/models-config-schema.ts +166 -0
  32. package/src/config/settings-schema.ts +29 -0
  33. package/src/discovery/claude-plugins.ts +19 -7
  34. package/src/edit/index.ts +2 -2
  35. package/src/edit/modes/apply-patch.ts +7 -6
  36. package/src/edit/modes/patch.ts +18 -25
  37. package/src/edit/modes/replace.ts +18 -20
  38. package/src/eval/js/shared/rewrite-imports.ts +131 -10
  39. package/src/eval/py/executor.ts +233 -623
  40. package/src/eval/py/kernel.ts +27 -2
  41. package/src/eval/py/runner.py +42 -11
  42. package/src/eval/py/runtime.ts +1 -0
  43. package/src/exa/factory.ts +5 -4
  44. package/src/exa/mcp-client.ts +1 -1
  45. package/src/exa/researcher.ts +9 -20
  46. package/src/exa/search.ts +26 -52
  47. package/src/exa/types.ts +1 -1
  48. package/src/exa/websets.ts +54 -53
  49. package/src/exec/bash-executor.ts +2 -1
  50. package/src/extensibility/custom-commands/loader.ts +5 -3
  51. package/src/extensibility/custom-commands/types.ts +4 -2
  52. package/src/extensibility/custom-tools/loader.ts +5 -3
  53. package/src/extensibility/custom-tools/types.ts +7 -6
  54. package/src/extensibility/custom-tools/wrapper.ts +1 -1
  55. package/src/extensibility/extensions/get-commands-handler.ts +77 -0
  56. package/src/extensibility/extensions/loader.ts +7 -3
  57. package/src/extensibility/extensions/types.ts +9 -5
  58. package/src/extensibility/extensions/wrapper.ts +1 -2
  59. package/src/extensibility/hooks/loader.ts +3 -1
  60. package/src/extensibility/hooks/tool-wrapper.ts +1 -1
  61. package/src/extensibility/hooks/types.ts +4 -2
  62. package/src/extensibility/plugins/legacy-pi-compat.ts +78 -31
  63. package/src/extensibility/shared-events.ts +1 -1
  64. package/src/extensibility/typebox.ts +391 -0
  65. package/src/goals/tools/goal-tool.ts +6 -12
  66. package/src/hashline/input.ts +2 -1
  67. package/src/hashline/parser.ts +27 -3
  68. package/src/hashline/types.ts +4 -4
  69. package/src/hindsight/state.ts +2 -2
  70. package/src/index.ts +0 -2
  71. package/src/internal-urls/docs-index.generated.ts +15 -15
  72. package/src/internal-urls/router.ts +8 -0
  73. package/src/internal-urls/types.ts +21 -0
  74. package/src/lsp/config.ts +15 -6
  75. package/src/lsp/defaults.json +6 -2
  76. package/src/lsp/types.ts +30 -38
  77. package/src/mcp/manager.ts +1 -1
  78. package/src/mcp/tool-bridge.ts +1 -1
  79. package/src/modes/acp/acp-agent.ts +248 -50
  80. package/src/modes/components/session-observer-overlay.ts +12 -1
  81. package/src/modes/components/status-line/segments.ts +39 -4
  82. package/src/modes/controllers/command-controller.ts +27 -2
  83. package/src/modes/controllers/event-controller.ts +3 -4
  84. package/src/modes/controllers/extension-ui-controller.ts +3 -2
  85. package/src/modes/interactive-mode.ts +1 -1
  86. package/src/modes/rpc/host-tools.ts +1 -1
  87. package/src/modes/rpc/host-uris.ts +235 -0
  88. package/src/modes/rpc/rpc-client.ts +1 -1
  89. package/src/modes/rpc/rpc-mode.ts +27 -1
  90. package/src/modes/rpc/rpc-types.ts +58 -1
  91. package/src/modes/runtime-init.ts +2 -1
  92. package/src/modes/theme/defaults/dark-poimandres.json +1 -0
  93. package/src/modes/theme/defaults/light-poimandres.json +1 -0
  94. package/src/modes/theme/theme.ts +117 -117
  95. package/src/modes/types.ts +1 -1
  96. package/src/modes/utils/context-usage.ts +2 -2
  97. package/src/prompts/tools/github.md +4 -4
  98. package/src/prompts/tools/hashline.md +22 -26
  99. package/src/prompts/tools/read.md +55 -37
  100. package/src/sdk.ts +31 -8
  101. package/src/session/agent-session.ts +74 -104
  102. package/src/session/messages.ts +16 -51
  103. package/src/session/session-manager.ts +22 -2
  104. package/src/session/streaming-output.ts +16 -6
  105. package/src/task/discovery.ts +5 -2
  106. package/src/task/executor.ts +210 -87
  107. package/src/task/index.ts +15 -11
  108. package/src/task/render.ts +32 -5
  109. package/src/task/types.ts +54 -39
  110. package/src/tools/ask.ts +12 -12
  111. package/src/tools/ast-edit.ts +11 -15
  112. package/src/tools/ast-grep.ts +9 -10
  113. package/src/tools/bash-command-fixup.ts +47 -0
  114. package/src/tools/bash.ts +48 -38
  115. package/src/tools/browser/render.ts +2 -2
  116. package/src/tools/browser.ts +39 -53
  117. package/src/tools/calculator.ts +12 -11
  118. package/src/tools/checkpoint.ts +7 -7
  119. package/src/tools/debug.ts +40 -43
  120. package/src/tools/eval.ts +16 -10
  121. package/src/tools/find.ts +10 -13
  122. package/src/tools/gh.ts +108 -132
  123. package/src/tools/hindsight-recall.ts +4 -6
  124. package/src/tools/hindsight-reflect.ts +5 -5
  125. package/src/tools/hindsight-retain.ts +15 -17
  126. package/src/tools/image-gen.ts +31 -81
  127. package/src/tools/index.ts +4 -1
  128. package/src/tools/inspect-image.ts +8 -9
  129. package/src/tools/irc.ts +15 -27
  130. package/src/tools/job.ts +30 -28
  131. package/src/tools/output-meta.ts +26 -0
  132. package/src/tools/read.ts +39 -12
  133. package/src/tools/recipe/index.ts +7 -9
  134. package/src/tools/render-mermaid.ts +12 -12
  135. package/src/tools/report-tool-issue.ts +4 -4
  136. package/src/tools/resolve.ts +11 -11
  137. package/src/tools/review.ts +14 -26
  138. package/src/tools/search-tool-bm25.ts +7 -9
  139. package/src/tools/search.ts +19 -22
  140. package/src/tools/ssh.ts +10 -9
  141. package/src/tools/todo-write.ts +26 -34
  142. package/src/tools/vim.ts +10 -26
  143. package/src/tools/write.ts +25 -5
  144. package/src/tools/yield.ts +100 -54
  145. package/src/web/search/index.ts +9 -24
  146. package/src/web/search/providers/anthropic.ts +5 -0
  147. package/src/web/search/providers/exa.ts +3 -0
  148. package/src/web/search/providers/gemini.ts +5 -0
  149. package/src/web/search/providers/jina.ts +5 -2
  150. package/src/web/search/providers/zai.ts +5 -2
  151. package/src/prompts/compaction/branch-summary-context.md +0 -5
  152. package/src/prompts/compaction/branch-summary-preamble.md +0 -2
  153. package/src/prompts/compaction/branch-summary.md +0 -30
  154. package/src/prompts/compaction/compaction-short-summary.md +0 -9
  155. package/src/prompts/compaction/compaction-summary-context.md +0 -5
  156. package/src/prompts/compaction/compaction-summary.md +0 -38
  157. package/src/prompts/compaction/compaction-turn-prefix.md +0 -17
  158. package/src/prompts/compaction/compaction-update-summary.md +0 -45
  159. package/src/prompts/system/auto-handoff-threshold-focus.md +0 -1
  160. package/src/prompts/system/file-operations.md +0 -10
  161. package/src/prompts/system/handoff-document.md +0 -49
  162. package/src/prompts/system/summarization-system.md +0 -3
  163. package/src/session/compaction/branch-summarization.ts +0 -324
  164. package/src/session/compaction/compaction.ts +0 -1420
  165. package/src/session/compaction/errors.ts +0 -31
  166. package/src/session/compaction/index.ts +0 -8
  167. package/src/session/compaction/pruning.ts +0 -91
  168. package/src/session/compaction/utils.ts +0 -184
@@ -8,19 +8,20 @@
8
8
  * the `patch` mode.
9
9
  */
10
10
 
11
- import { type Static, Type } from "@sinclair/typebox";
11
+ import * as z from "zod/v4";
12
12
  import { parseApplyPatch, parseApplyPatchStreaming } from "../apply-patch/parser";
13
13
  import { ApplyPatchError } from "../diff";
14
14
  import type { PatchEditEntry } from "./patch";
15
15
 
16
- export const applyPatchSchema = Type.Object({
17
- input: Type.String({
18
- description:
16
+ export const applyPatchSchema = z.object({
17
+ input: z
18
+ .string()
19
+ .describe(
19
20
  "Full Codex apply_patch envelope, including '*** Begin Patch' and '*** End Patch'. Contains any mix of Add/Delete/Update (with optional Move to) file operations.",
20
- }),
21
+ ),
21
22
  });
22
23
 
23
- export type ApplyPatchParams = Static<typeof applyPatchSchema>;
24
+ export type ApplyPatchParams = z.infer<typeof applyPatchSchema>;
24
25
 
25
26
  export type ApplyPatchEntry = PatchEditEntry & { path: string };
26
27
 
@@ -8,9 +8,8 @@
8
8
  import * as fs from "node:fs";
9
9
  import * as path from "node:path";
10
10
  import type { AgentToolResult } from "@oh-my-pi/pi-agent-core";
11
- import { StringEnum } from "@oh-my-pi/pi-ai";
12
11
  import { isEnoent } from "@oh-my-pi/pi-utils";
13
- import { type Static, Type } from "@sinclair/typebox";
12
+ import * as z from "zod/v4";
14
13
  import {
15
14
  type FileDiagnosticsResult,
16
15
  flushLspWritethroughBatch,
@@ -1577,29 +1576,23 @@ export async function computePatchDiff(
1577
1576
  }
1578
1577
  }
1579
1578
 
1580
- export const patchEditEntrySchema = Type.Object(
1581
- {
1582
- op: Type.Optional(
1583
- StringEnum(["create", "delete", "update"], {
1584
- description: "Operation (default: update)",
1585
- }),
1586
- ),
1587
- rename: Type.Optional(Type.String({ description: "New path for move" })),
1588
- diff: Type.Optional(Type.String({ description: "Diff hunks (update) or full content (create)" })),
1589
- },
1590
- { additionalProperties: false },
1591
- );
1592
-
1593
- export const patchEditSchema = Type.Object(
1594
- {
1595
- path: Type.String({ description: "file path for edits" }),
1596
- edits: Type.Array(patchEditEntrySchema, { description: "Patch operations", minItems: 1 }),
1597
- },
1598
- { additionalProperties: false },
1599
- );
1600
-
1601
- export type PatchEditEntry = Static<typeof patchEditEntrySchema>;
1602
- export type PatchParams = Static<typeof patchEditSchema>;
1579
+ export const patchEditEntrySchema = z
1580
+ .object({
1581
+ op: z.enum(["create", "delete", "update"]).optional().describe("Operation (default: update)"),
1582
+ rename: z.string().describe("New path for move").optional(),
1583
+ diff: z.string().describe("Diff hunks (update) or full content (create)").optional(),
1584
+ })
1585
+ .strict();
1586
+
1587
+ export const patchEditSchema = z
1588
+ .object({
1589
+ path: z.string().describe("file path for edits"),
1590
+ edits: z.array(patchEditEntrySchema).min(1).describe("Patch operations"),
1591
+ })
1592
+ .strict();
1593
+
1594
+ export type PatchEditEntry = z.infer<typeof patchEditEntrySchema>;
1595
+ export type PatchParams = z.infer<typeof patchEditSchema>;
1603
1596
 
1604
1597
  export interface ExecutePatchSingleOptions {
1605
1598
  session: ToolSession;
@@ -5,7 +5,7 @@
5
5
  * fallback strategies for finding text in files.
6
6
  */
7
7
  import type { AgentToolResult } from "@oh-my-pi/pi-agent-core";
8
- import { type Static, Type } from "@sinclair/typebox";
8
+ import * as z from "zod/v4";
9
9
  import type { WritethroughCallback, WritethroughDeferredHandle } from "../../lsp";
10
10
  import type { ToolSession } from "../../tools";
11
11
  import { invalidateFsScanAfterWrite } from "../../tools/fs-cache-invalidation";
@@ -976,25 +976,23 @@ export function findContextLine(
976
976
  return { index: undefined, confidence: bestScore };
977
977
  }
978
978
 
979
- export const replaceEditEntrySchema = Type.Object(
980
- {
981
- old_text: Type.String({ description: "Text to find (fuzzy whitespace matching enabled)" }),
982
- new_text: Type.String({ description: "Replacement text" }),
983
- all: Type.Optional(Type.Boolean({ description: "Replace all occurrences (default: unique match required)" })),
984
- },
985
- { additionalProperties: false },
986
- );
987
-
988
- export const replaceEditSchema = Type.Object(
989
- {
990
- path: Type.String({ description: "file path for edits" }),
991
- edits: Type.Array(replaceEditEntrySchema, { description: "Replacements", minItems: 1 }),
992
- },
993
- { additionalProperties: false },
994
- );
995
-
996
- export type ReplaceEditEntry = Static<typeof replaceEditEntrySchema>;
997
- export type ReplaceParams = Static<typeof replaceEditSchema>;
979
+ export const replaceEditEntrySchema = z
980
+ .object({
981
+ old_text: z.string().describe("Text to find (fuzzy whitespace matching enabled)"),
982
+ new_text: z.string().describe("Replacement text"),
983
+ all: z.boolean().describe("Replace all occurrences (default: unique match required)").optional(),
984
+ })
985
+ .strict();
986
+
987
+ export const replaceEditSchema = z
988
+ .object({
989
+ path: z.string().describe("file path for edits"),
990
+ edits: z.array(replaceEditEntrySchema).min(1).describe("Replacements"),
991
+ })
992
+ .strict();
993
+
994
+ export type ReplaceEditEntry = z.infer<typeof replaceEditEntrySchema>;
995
+ export type ReplaceParams = z.infer<typeof replaceEditSchema>;
998
996
 
999
997
  export interface ExecuteReplaceSingleOptions {
1000
998
  session: ToolSession;
@@ -22,9 +22,32 @@ type BabelImportDeclaration = {
22
22
  }>;
23
23
  };
24
24
 
25
- type BabelLexicalDecl =
26
- | { type: "VariableDeclaration"; kind: "const" | "let" | "var"; start: number; end: number }
27
- | { type: "ClassDeclaration"; start: number; end: number; id: { start: number; end: number; name: string } | null };
25
+ type BabelBindingPattern = {
26
+ type: string;
27
+ name?: string;
28
+ properties?: ReadonlyArray<unknown>;
29
+ elements?: ReadonlyArray<unknown | null>;
30
+ argument?: unknown;
31
+ left?: unknown;
32
+ value?: unknown;
33
+ };
34
+
35
+ type BabelVariableDeclaration = {
36
+ type: "VariableDeclaration";
37
+ kind: "const" | "let" | "var";
38
+ start: number;
39
+ end: number;
40
+ declarations?: ReadonlyArray<{ id: BabelBindingPattern }>;
41
+ };
42
+
43
+ type BabelClassDeclaration = {
44
+ type: "ClassDeclaration";
45
+ start: number;
46
+ end: number;
47
+ id: { start: number; end: number; name: string } | null;
48
+ };
49
+
50
+ type BabelLexicalDecl = BabelVariableDeclaration | BabelClassDeclaration;
28
51
 
29
52
  type BabelExpressionStatement = {
30
53
  type: "ExpressionStatement";
@@ -167,6 +190,53 @@ export function rewriteImports(code: string): string {
167
190
  return result;
168
191
  }
169
192
 
193
+ function collectBindingNames(pattern: unknown, names: string[]): void {
194
+ if (!pattern || typeof pattern !== "object") return;
195
+ const node = pattern as BabelBindingPattern & { parameter?: unknown };
196
+ switch (node.type) {
197
+ case "Identifier":
198
+ if (typeof node.name === "string") names.push(node.name);
199
+ return;
200
+ case "ObjectPattern":
201
+ for (const property of node.properties ?? []) collectBindingNames(property, names);
202
+ return;
203
+ case "ObjectProperty":
204
+ case "Property":
205
+ collectBindingNames(node.value, names);
206
+ return;
207
+ case "ArrayPattern":
208
+ for (const element of node.elements ?? []) collectBindingNames(element, names);
209
+ return;
210
+ case "AssignmentPattern":
211
+ collectBindingNames(node.left, names);
212
+ return;
213
+ case "RestElement":
214
+ collectBindingNames(node.argument, names);
215
+ return;
216
+ case "TSParameterProperty":
217
+ collectBindingNames(node.parameter, names);
218
+ return;
219
+ default:
220
+ return;
221
+ }
222
+ }
223
+
224
+ function getLexicalBindingNames(node: BabelLexicalDecl): string[] {
225
+ const names: string[] = [];
226
+ if (node.type === "VariableDeclaration") {
227
+ for (const declaration of node.declarations ?? []) collectBindingNames(declaration.id, names);
228
+ } else if (node.id) {
229
+ names.push(node.id.name);
230
+ }
231
+ return names;
232
+ }
233
+
234
+ function appendGlobalBindingPublish(source: string, names: readonly string[]): string {
235
+ if (names.length === 0) return source;
236
+ const assignments = names.map(name => `this[${JSON.stringify(name)}] = ${name};`).join("\n");
237
+ return `${source};\n${assignments}`;
238
+ }
239
+
170
240
  /**
171
241
  * Demote top-level `const`/`let`/`class` declarations to `var` so they persist on the
172
242
  * worker's globalThis across indirect `eval` calls. Indirect eval gives each call its own
@@ -177,10 +247,14 @@ export function rewriteImports(code: string): string {
177
247
  * let { a, b } = obj; -> var { a, b } = obj;
178
248
  * class Foo extends Bar {} -> var Foo = class extends Bar {};
179
249
  *
250
+ * When the source must run inside the async wrapper, demoted `var`s would normally become
251
+ * function-scoped. In that mode we publish each top-level binding back to the wrapper's
252
+ * lexical `this`, which is the worker global object.
253
+ *
180
254
  * Nested declarations (inside functions, blocks, classes) are left alone \u2014 they're
181
255
  * scoped to their enclosing function/block regardless of `var` vs `let`/`const`.
182
256
  */
183
- function demoteTopLevelLexicals(code: string): string {
257
+ function demoteTopLevelLexicals(code: string, options: { publishGlobals?: boolean } = {}): string {
184
258
  if (!/\b(?:const|let|class)\b/.test(code)) return code;
185
259
 
186
260
  const ast = parseProgram(code);
@@ -191,10 +265,10 @@ function demoteTopLevelLexicals(code: string): string {
191
265
  const targets: BabelLexicalDecl[] = [];
192
266
  for (const node of ast.program.body) {
193
267
  if (node.type === "VariableDeclaration") {
194
- const decl = node as unknown as BabelLexicalDecl & { kind: string };
195
- if (decl.kind === "const" || decl.kind === "let") targets.push(decl as BabelLexicalDecl);
268
+ const decl = node as unknown as BabelVariableDeclaration;
269
+ if (decl.kind === "const" || decl.kind === "let") targets.push(decl);
196
270
  } else if (node.type === "ClassDeclaration") {
197
- const decl = node as unknown as Extract<BabelLexicalDecl, { type: "ClassDeclaration" }>;
271
+ const decl = node as unknown as BabelClassDeclaration;
198
272
  if (decl.id) targets.push(decl);
199
273
  }
200
274
  }
@@ -204,6 +278,7 @@ function demoteTopLevelLexicals(code: string): string {
204
278
  let result = code;
205
279
  for (const node of targets) {
206
280
  const segment = result.slice(node.start, node.end);
281
+ const bindingNames = options.publishGlobals ? getLexicalBindingNames(node) : [];
207
282
  let replacement: string;
208
283
  if (node.type === "VariableDeclaration") {
209
284
  replacement = `var${segment.slice(node.kind.length)}`;
@@ -215,7 +290,8 @@ function demoteTopLevelLexicals(code: string): string {
215
290
  const hasTrailingSemi = segment.endsWith(";");
216
291
  replacement = `var ${id.name} = class${tail}${hasTrailingSemi ? "" : ";"}`;
217
292
  }
218
- result = result.slice(0, node.start) + replacement + result.slice(node.end);
293
+ result =
294
+ result.slice(0, node.start) + appendGlobalBindingPublish(replacement, bindingNames) + result.slice(node.end);
219
295
  }
220
296
  return result;
221
297
  }
@@ -238,6 +314,50 @@ function returnFinalExpression(code: string): { source: string; returned: boolea
238
314
  return { source: `${prefix}__omp_set_final_expr__((${trimmedStatement}));${suffix}`, returned: true };
239
315
  }
240
316
 
317
+ function isExecutionBoundary(type: string): boolean {
318
+ return (
319
+ type === "FunctionDeclaration" ||
320
+ type === "FunctionExpression" ||
321
+ type === "ArrowFunctionExpression" ||
322
+ type === "ObjectMethod" ||
323
+ type === "ClassMethod" ||
324
+ type === "ClassPrivateMethod" ||
325
+ type === "PrivateMethod"
326
+ );
327
+ }
328
+
329
+ function containsAsyncWrapperSyntax(value: unknown): boolean {
330
+ if (!value || typeof value !== "object") return false;
331
+ if (Array.isArray(value)) {
332
+ for (const item of value) {
333
+ if (containsAsyncWrapperSyntax(item)) return true;
334
+ }
335
+ return false;
336
+ }
337
+
338
+ const node = value as Record<string, unknown>;
339
+ const type = node.type;
340
+ if (type === "ReturnStatement" || type === "AwaitExpression") return true;
341
+ if (type === "ForOfStatement" && node.await === true) return true;
342
+ if (typeof type === "string" && isExecutionBoundary(type)) return false;
343
+
344
+ for (const key in node) {
345
+ if (key === "loc" || key === "extra" || key === "range") continue;
346
+ if (key === "leadingComments" || key === "trailingComments" || key === "innerComments") continue;
347
+ if (containsAsyncWrapperSyntax(node[key])) return true;
348
+ }
349
+ return false;
350
+ }
351
+
352
+ function requiresAsyncWrapper(code: string): boolean {
353
+ const ast = parseProgram(code);
354
+ if (!ast) return false;
355
+ for (const node of ast.program.body) {
356
+ if (containsAsyncWrapperSyntax(node)) return true;
357
+ }
358
+ return false;
359
+ }
360
+
241
361
  /**
242
362
  * Strip TypeScript syntax (type annotations, `interface`, `as`, `satisfies`, generics in
243
363
  * call expressions, etc.) before the import/lexical rewriters parse the code. We use Bun's
@@ -267,11 +387,12 @@ const LOOKS_LIKE_TS =
267
387
  export function wrapCode(code: string): { source: string; asyncWrapped: boolean; finalExpressionReturned: boolean } {
268
388
  const stripped = stripTypeScript(code);
269
389
  const finalExpression = returnFinalExpression(stripped);
390
+ const importsRewritten = rewriteImports(finalExpression.source);
391
+ const needsAsyncWrapper = requiresAsyncWrapper(importsRewritten);
270
392
  const rewritten = {
271
- source: demoteTopLevelLexicals(rewriteImports(finalExpression.source)),
393
+ source: demoteTopLevelLexicals(importsRewritten, { publishGlobals: needsAsyncWrapper }),
272
394
  returned: finalExpression.returned,
273
395
  };
274
- const needsAsyncWrapper = /\bawait\b|\breturn\b/.test(rewritten.source);
275
396
  if (!needsAsyncWrapper) {
276
397
  return { source: rewritten.source, asyncWrapped: false, finalExpressionReturned: rewritten.returned };
277
398
  }