@astralyn/pi 1.0.2 → 1.0.4

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 (86) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/bundle/chunks/{anthropic-messages-YET4T7BO.js → anthropic-messages-YIOJIMVL.js} +1 -1
  3. package/dist/bundle/chunks/azure-openai-responses-LDW4FGAQ.js +2 -0
  4. package/dist/bundle/chunks/{chunk-5RBGZ6YW.js → chunk-23OTKHCH.js} +53 -53
  5. package/dist/bundle/chunks/{chunk-MPDALHNJ.js → chunk-57XI7YJH.js} +1 -1
  6. package/dist/bundle/chunks/{chunk-MWM24AOB.js → chunk-73WG7F7N.js} +1 -1
  7. package/dist/bundle/chunks/{chunk-SVJ7OGEB.js → chunk-A6ALEEY5.js} +1 -1
  8. package/dist/bundle/chunks/chunk-D3ZCGC4T.js +2 -0
  9. package/dist/bundle/chunks/{chunk-IZ3S3GDX.js → chunk-IFELM7MW.js} +1 -1
  10. package/dist/bundle/chunks/chunk-RZ2V53SP.js +2 -0
  11. package/dist/bundle/chunks/{chunk-V66VGGOM.js → chunk-TCQ4HHQD.js} +1 -1
  12. package/dist/bundle/chunks/chunk-TXJO4AJ7.js +11 -0
  13. package/dist/bundle/chunks/{chunk-NY3CWK44.js → chunk-UCXU7KLO.js} +1 -1
  14. package/dist/bundle/chunks/{cloudflare-workers-ai-system-one-QI46LUZ6.js → cloudflare-workers-ai-system-one-OWU5SM6J.js} +1 -1
  15. package/dist/bundle/chunks/codemode-worker.js +0 -1
  16. package/dist/bundle/chunks/{google-generative-ai-YTZCSHQN.js → google-generative-ai-S5A5IBU4.js} +1 -1
  17. package/dist/bundle/chunks/{google-vertex-OTFZJJXQ.js → google-vertex-UBRQFDB4.js} +1 -1
  18. package/dist/bundle/chunks/{mistral-conversations-ELH4V4TY.js → mistral-conversations-A2N5FSOV.js} +1 -1
  19. package/dist/bundle/chunks/{openai-codex-responses-OLVTU4UA.js → openai-codex-responses-Q6JHCWLE.js} +1 -1
  20. package/dist/bundle/chunks/{openai-completions-53M2KJ67.js → openai-completions-H4VMO7AU.js} +1 -1
  21. package/dist/bundle/chunks/{openai-responses-4ABDEL4Z.js → openai-responses-G4PGNM2H.js} +1 -1
  22. package/dist/bundle/chunks/{typesafe-system-one-RREJAKWB.js → typesafe-system-one-KYKKNU3E.js} +1 -1
  23. package/dist/bundle/chunks/virtual-modules-IJTAY26N.js +2 -0
  24. package/dist/bundle/cli.js +1 -1
  25. package/dist/bundle/index.js +1 -1
  26. package/dist/bundle/rpc-entry.js +1 -1
  27. package/dist/config.d.ts +18 -1
  28. package/dist/config.d.ts.map +1 -1
  29. package/dist/config.js +44 -6
  30. package/dist/config.js.map +1 -1
  31. package/dist/core/bash-executor.d.ts.map +1 -1
  32. package/dist/core/bash-executor.js +2 -7
  33. package/dist/core/bash-executor.js.map +1 -1
  34. package/dist/core/keybindings.d.ts +4 -4
  35. package/dist/core/model-resolver.js +1 -1
  36. package/dist/core/model-resolver.js.map +1 -1
  37. package/dist/core/tasks/runtime.d.ts +1 -0
  38. package/dist/core/tasks/runtime.d.ts.map +1 -1
  39. package/dist/core/tasks/runtime.js +4 -0
  40. package/dist/core/tasks/runtime.js.map +1 -1
  41. package/dist/core/tasks/types.d.ts +2 -0
  42. package/dist/core/tasks/types.d.ts.map +1 -1
  43. package/dist/core/tasks/types.js.map +1 -1
  44. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  45. package/dist/core/tools/output-accumulator.js +6 -11
  46. package/dist/core/tools/output-accumulator.js.map +1 -1
  47. package/dist/extensions/codemode/execute.d.ts.map +1 -1
  48. package/dist/extensions/codemode/execute.js +49 -8
  49. package/dist/extensions/codemode/execute.js.map +1 -1
  50. package/dist/extensions/codemode/tool.js +1 -1
  51. package/dist/extensions/codemode/tool.js.map +1 -1
  52. package/dist/extensions/mcp/tools.d.ts.map +1 -1
  53. package/dist/extensions/mcp/tools.js +4 -9
  54. package/dist/extensions/mcp/tools.js.map +1 -1
  55. package/dist/modes/interactive/interactive-mode.d.ts +7 -0
  56. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  57. package/dist/modes/interactive/interactive-mode.js +42 -6
  58. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  59. package/dist/modes/interactive/tasks/index.d.ts.map +1 -1
  60. package/dist/modes/interactive/tasks/index.js +10 -18
  61. package/dist/modes/interactive/tasks/index.js.map +1 -1
  62. package/dist/utils/output-files.d.ts +17 -0
  63. package/dist/utils/output-files.d.ts.map +1 -0
  64. package/dist/utils/output-files.js +30 -0
  65. package/dist/utils/output-files.js.map +1 -0
  66. package/docs/bundled/tasks.md +2 -2
  67. package/docs/bundled/tool-presentation.md +1 -1
  68. package/docs/codemode.md +3 -3
  69. package/docs/extensions.md +1 -0
  70. package/docs/keybindings.md +7 -7
  71. package/docs/providers.md +5 -1
  72. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  73. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  74. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  75. package/examples/extensions/gondolin/package-lock.json +2 -2
  76. package/examples/extensions/gondolin/package.json +1 -1
  77. package/examples/extensions/sandbox/package-lock.json +2 -2
  78. package/examples/extensions/sandbox/package.json +1 -1
  79. package/examples/extensions/with-deps/package-lock.json +2 -2
  80. package/examples/extensions/with-deps/package.json +1 -1
  81. package/npm-shrinkwrap.json +54 -54
  82. package/package.json +11 -11
  83. package/dist/bundle/chunks/azure-openai-responses-SBLL5YN3.js +0 -2
  84. package/dist/bundle/chunks/chunk-G4RY3GN7.js +0 -11
  85. package/dist/bundle/chunks/chunk-TRHTWM3C.js +0 -2
  86. package/dist/bundle/chunks/virtual-modules-EDIGEIG6.js +0 -2
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/tasks/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAC5E,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAMjF,MAAM,WAAW,OAAO;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,OAAO,IAAI,IAAI,CAAC;CAChB;AAED,wEAAwE;AACxE,wBAAgB,WAAW,CAAC,GAAG,EAAE;IAAE,KAAK,EAAE,YAAY,CAAC;IAAC,EAAE,EAAE,kBAAkB,CAAA;CAAE,GAAG,OAAO,CAsGzF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/tasks/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAC5E,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAOjF,MAAM,WAAW,OAAO;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,OAAO,IAAI,IAAI,CAAC;CAChB;AAED,wEAAwE;AACxE,wBAAgB,WAAW,CAAC,GAAG,EAAE;IAAE,KAAK,EAAE,YAAY,CAAC;IAAC,EAAE,EAAE,kBAAkB,CAAA;CAAE,GAAG,OAAO,CA8FzF"}
@@ -2,6 +2,7 @@ import { getKeybindings } from "@earendil-works/pi-tui";
2
2
  import { isTaskTerminal } from "../../../core/tasks/types.js";
3
3
  import { TASKS_DETACH_HINT_DELAY_MS } from "../../../core/tools/tasks/constants.js";
4
4
  import { keyLabel } from "../components/keybinding-hints.js";
5
+ import { theme } from "../theme/theme.js";
5
6
  import { TasksMenu } from "./manager.js";
6
7
  /** Host-owned task UI; it is available without loading an extension. */
7
8
  export function bindTasksUI(ctx) {
@@ -17,38 +18,29 @@ export function bindTasksUI(ctx) {
17
18
  clearTimeout(hintTimer);
18
19
  hintTimer = undefined;
19
20
  const all = ctx.tasks.list();
20
- const active = all.filter((task) => !isTaskTerminal(task.status));
21
- const recent = all.filter((task) => isTaskTerminal(task.status) && task.mode === "background").length;
22
- // Name the foreground count: it is the work the conversation is waiting on, and the only
23
- // work the detach key can still move.
24
- const foreground = active.filter((task) => task.mode === "foreground").length;
25
- const activeLabel = foreground
26
- ? foreground === active.length
27
- ? `Tasks ${foreground} foreground`
28
- : `Tasks ${active.length} active · ${foreground} foreground`
29
- : `Tasks ${active.length} active`;
21
+ const background = all.filter((task) => task.mode === "background" && !isTaskTerminal(task.status)).length;
30
22
  const parts = [];
31
- if (active.length)
32
- parts.push(`${activeLabel} · /tasks`);
33
- else if (recent)
34
- parts.push(`Tasks ${recent} recent background results · /tasks`);
23
+ if (background) {
24
+ parts.push(theme.fg("text", `${background} background task${background === 1 ? "" : "s"}`));
25
+ parts.push(theme.fg("accent", "/tasks") + theme.fg("muted", " to view"));
26
+ }
35
27
  // Teach the detach key once something has run long enough to be worth moving, and
36
- // only while it can move: a stopping execution is aborted, so the key does nothing.
28
+ // only while it can move, independently of the background task summary.
37
29
  const detachKey = keyLabel("app.tasks.detach");
38
30
  const now = Date.now();
39
- const movable = (ctx.tasks.enabled ? all : []).filter((task) => task.mode === "foreground" && (task.status === "running" || task.status === "queued"));
31
+ const movable = all.filter((task) => ctx.tasks.canDetach(task.id));
40
32
  const waits = movable
41
33
  .map((task) => task.startedAt + TASKS_DETACH_HINT_DELAY_MS - now)
42
34
  .filter((wait) => wait > 0);
43
35
  if (detachKey && movable.length > waits.length)
44
- parts.push(`${detachKey} to background`);
36
+ parts.push(theme.fg("accent", detachKey) + theme.fg("muted", " to run in background"));
45
37
  if (detachKey && waits.length > 0) {
46
38
  // The service notifies on state changes, not on time, so wake up when the next
47
39
  // execution crosses the threshold.
48
40
  hintTimer = setTimeout(update, Math.min(...waits));
49
41
  hintTimer.unref?.();
50
42
  }
51
- ctx.ui.setStatus("background", parts.length ? parts.join(" · ") : undefined);
43
+ ctx.ui.setStatus("background", parts.length ? parts.join(theme.fg("dim", " · ")) : undefined);
52
44
  };
53
45
  unsubscribe = ctx.tasks.subscribe(update);
54
46
  unsubscribeDetachKey = ctx.ui.onTerminalInput((data) => {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/modes/interactive/tasks/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { getKeybindings } from \"@earendil-works/pi-tui\";\nimport type { ExtensionUIContext } from \"../../../core/extensions/types.ts\";\nimport { isTaskTerminal, type TasksContext } from \"../../../core/tasks/types.ts\";\nimport { TASKS_DETACH_HINT_DELAY_MS } from \"../../../core/tools/tasks/constants.ts\";\nimport { keyLabel } from \"../components/keybinding-hints.ts\";\nimport { TasksMenu } from \"./manager.ts\";\nimport type { TasksPanelState } from \"./model.ts\";\n\nexport interface TasksUI {\n\topen(): Promise<void>;\n\tdispose(): void;\n}\n\n/** Host-owned task UI; it is available without loading an extension. */\nexport function bindTasksUI(ctx: { tasks: TasksContext; ui: ExtensionUIContext }): TasksUI {\n\tconst state: TasksPanelState = { tab: \"output\" };\n\tlet unsubscribe: (() => void) | undefined;\n\tlet unsubscribeDetachKey: (() => void) | undefined;\n\tlet closeMenu: (() => void) | undefined;\n\tlet hintTimer: ReturnType<typeof setTimeout> | undefined;\n\tconst start = () => {\n\t\tunsubscribe?.();\n\t\tunsubscribeDetachKey?.();\n\t\tconst update = () => {\n\t\t\tclearTimeout(hintTimer);\n\t\t\thintTimer = undefined;\n\t\t\tconst all = ctx.tasks.list();\n\t\t\tconst active = all.filter((task) => !isTaskTerminal(task.status));\n\t\t\tconst recent = all.filter((task) => isTaskTerminal(task.status) && task.mode === \"background\").length;\n\t\t\t// Name the foreground count: it is the work the conversation is waiting on, and the only\n\t\t\t// work the detach key can still move.\n\t\t\tconst foreground = active.filter((task) => task.mode === \"foreground\").length;\n\t\t\tconst activeLabel = foreground\n\t\t\t\t? foreground === active.length\n\t\t\t\t\t? `Tasks ${foreground} foreground`\n\t\t\t\t\t: `Tasks ${active.length} active · ${foreground} foreground`\n\t\t\t\t: `Tasks ${active.length} active`;\n\t\t\tconst parts: string[] = [];\n\t\t\tif (active.length) parts.push(`${activeLabel} · /tasks`);\n\t\t\telse if (recent) parts.push(`Tasks ${recent} recent background results · /tasks`);\n\t\t\t// Teach the detach key once something has run long enough to be worth moving, and\n\t\t\t// only while it can move: a stopping execution is aborted, so the key does nothing.\n\t\t\tconst detachKey = keyLabel(\"app.tasks.detach\");\n\t\t\tconst now = Date.now();\n\t\t\tconst movable = (ctx.tasks.enabled ? all : []).filter(\n\t\t\t\t(task) => task.mode === \"foreground\" && (task.status === \"running\" || task.status === \"queued\"),\n\t\t\t);\n\t\t\tconst waits = movable\n\t\t\t\t.map((task) => task.startedAt + TASKS_DETACH_HINT_DELAY_MS - now)\n\t\t\t\t.filter((wait) => wait > 0);\n\t\t\tif (detachKey && movable.length > waits.length) parts.push(`${detachKey} to background`);\n\t\t\tif (detachKey && waits.length > 0) {\n\t\t\t\t// The service notifies on state changes, not on time, so wake up when the next\n\t\t\t\t// execution crosses the threshold.\n\t\t\t\thintTimer = setTimeout(update, Math.min(...waits));\n\t\t\t\thintTimer.unref?.();\n\t\t\t}\n\t\t\tctx.ui.setStatus(\"background\", parts.length ? parts.join(\" · \") : undefined);\n\t\t};\n\t\tunsubscribe = ctx.tasks.subscribe(update);\n\t\tunsubscribeDetachKey = ctx.ui.onTerminalInput((data) => {\n\t\t\tif (!getKeybindings().matches(data, \"app.tasks.detach\")) return undefined;\n\t\t\tlet count: number;\n\t\t\ttry {\n\t\t\t\tcount = ctx.tasks.detachForeground();\n\t\t\t} catch (error) {\n\t\t\t\t// Work could move, but every background slot is taken.\n\t\t\t\tctx.ui.notify(error instanceof Error ? error.message : String(error), \"warning\");\n\t\t\t\treturn { consume: true };\n\t\t\t}\n\t\t\t// Nothing can move: let the key fall through to editor bindings instead of\n\t\t\t// spending it on a \"nothing happened\" status line.\n\t\t\tif (count === 0) return undefined;\n\t\t\tctx.ui.notify(\n\t\t\t\t`Moved ${count} execution${count === 1 ? \"\" : \"s\"} to the background. Use /tasks to manage tasks.`,\n\t\t\t);\n\t\t\treturn { consume: true };\n\t\t});\n\t\tupdate();\n\t};\n\tconst dispose = () => {\n\t\tunsubscribe?.();\n\t\tunsubscribe = undefined;\n\t\tunsubscribeDetachKey?.();\n\t\tunsubscribeDetachKey = undefined;\n\t\tclearTimeout(hintTimer);\n\t\thintTimer = undefined;\n\t\tcloseMenu?.();\n\t\tcloseMenu = undefined;\n\t\tctx.ui.setStatus(\"background\", undefined);\n\t};\n\tstart();\n\treturn {\n\t\tdispose,\n\t\topen: async () => {\n\t\t\tcloseMenu?.();\n\t\t\tawait ctx.ui.custom<void>(\n\t\t\t\t(tui, theme, keybindings, done) => {\n\t\t\t\t\tconst close = () => {\n\t\t\t\t\t\tif (closeMenu === close) closeMenu = undefined;\n\t\t\t\t\t\tdone();\n\t\t\t\t\t};\n\t\t\t\t\tcloseMenu = close;\n\t\t\t\t\treturn new TasksMenu({\n\t\t\t\t\t\tstate,\n\t\t\t\t\t\ttui,\n\t\t\t\t\t\ttheme,\n\t\t\t\t\t\tkeybindings,\n\t\t\t\t\t\thost: ctx.tasks,\n\t\t\t\t\t\tonClose: close,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\t{ overlay: true, overlayOptions: { anchor: \"center\", width: \"100%\", maxHeight: \"100%\" } },\n\t\t\t);\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/modes/interactive/tasks/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { getKeybindings } from \"@earendil-works/pi-tui\";\nimport type { ExtensionUIContext } from \"../../../core/extensions/types.ts\";\nimport { isTaskTerminal, type TasksContext } from \"../../../core/tasks/types.ts\";\nimport { TASKS_DETACH_HINT_DELAY_MS } from \"../../../core/tools/tasks/constants.ts\";\nimport { keyLabel } from \"../components/keybinding-hints.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { TasksMenu } from \"./manager.ts\";\nimport type { TasksPanelState } from \"./model.ts\";\n\nexport interface TasksUI {\n\topen(): Promise<void>;\n\tdispose(): void;\n}\n\n/** Host-owned task UI; it is available without loading an extension. */\nexport function bindTasksUI(ctx: { tasks: TasksContext; ui: ExtensionUIContext }): TasksUI {\n\tconst state: TasksPanelState = { tab: \"output\" };\n\tlet unsubscribe: (() => void) | undefined;\n\tlet unsubscribeDetachKey: (() => void) | undefined;\n\tlet closeMenu: (() => void) | undefined;\n\tlet hintTimer: ReturnType<typeof setTimeout> | undefined;\n\tconst start = () => {\n\t\tunsubscribe?.();\n\t\tunsubscribeDetachKey?.();\n\t\tconst update = () => {\n\t\t\tclearTimeout(hintTimer);\n\t\t\thintTimer = undefined;\n\t\t\tconst all = ctx.tasks.list();\n\t\t\tconst background = all.filter((task) => task.mode === \"background\" && !isTaskTerminal(task.status)).length;\n\t\t\tconst parts: string[] = [];\n\t\t\tif (background) {\n\t\t\t\tparts.push(theme.fg(\"text\", `${background} background task${background === 1 ? \"\" : \"s\"}`));\n\t\t\t\tparts.push(theme.fg(\"accent\", \"/tasks\") + theme.fg(\"muted\", \" to view\"));\n\t\t\t}\n\t\t\t// Teach the detach key once something has run long enough to be worth moving, and\n\t\t\t// only while it can move, independently of the background task summary.\n\t\t\tconst detachKey = keyLabel(\"app.tasks.detach\");\n\t\t\tconst now = Date.now();\n\t\t\tconst movable = all.filter((task) => ctx.tasks.canDetach(task.id));\n\t\t\tconst waits = movable\n\t\t\t\t.map((task) => task.startedAt + TASKS_DETACH_HINT_DELAY_MS - now)\n\t\t\t\t.filter((wait) => wait > 0);\n\t\t\tif (detachKey && movable.length > waits.length)\n\t\t\t\tparts.push(theme.fg(\"accent\", detachKey) + theme.fg(\"muted\", \" to run in background\"));\n\t\t\tif (detachKey && waits.length > 0) {\n\t\t\t\t// The service notifies on state changes, not on time, so wake up when the next\n\t\t\t\t// execution crosses the threshold.\n\t\t\t\thintTimer = setTimeout(update, Math.min(...waits));\n\t\t\t\thintTimer.unref?.();\n\t\t\t}\n\t\t\tctx.ui.setStatus(\"background\", parts.length ? parts.join(theme.fg(\"dim\", \" · \")) : undefined);\n\t\t};\n\t\tunsubscribe = ctx.tasks.subscribe(update);\n\t\tunsubscribeDetachKey = ctx.ui.onTerminalInput((data) => {\n\t\t\tif (!getKeybindings().matches(data, \"app.tasks.detach\")) return undefined;\n\t\t\tlet count: number;\n\t\t\ttry {\n\t\t\t\tcount = ctx.tasks.detachForeground();\n\t\t\t} catch (error) {\n\t\t\t\t// Work could move, but every background slot is taken.\n\t\t\t\tctx.ui.notify(error instanceof Error ? error.message : String(error), \"warning\");\n\t\t\t\treturn { consume: true };\n\t\t\t}\n\t\t\t// Nothing can move: let the key fall through to editor bindings instead of\n\t\t\t// spending it on a \"nothing happened\" status line.\n\t\t\tif (count === 0) return undefined;\n\t\t\tctx.ui.notify(\n\t\t\t\t`Moved ${count} execution${count === 1 ? \"\" : \"s\"} to the background. Use /tasks to manage tasks.`,\n\t\t\t);\n\t\t\treturn { consume: true };\n\t\t});\n\t\tupdate();\n\t};\n\tconst dispose = () => {\n\t\tunsubscribe?.();\n\t\tunsubscribe = undefined;\n\t\tunsubscribeDetachKey?.();\n\t\tunsubscribeDetachKey = undefined;\n\t\tclearTimeout(hintTimer);\n\t\thintTimer = undefined;\n\t\tcloseMenu?.();\n\t\tcloseMenu = undefined;\n\t\tctx.ui.setStatus(\"background\", undefined);\n\t};\n\tstart();\n\treturn {\n\t\tdispose,\n\t\topen: async () => {\n\t\t\tcloseMenu?.();\n\t\t\tawait ctx.ui.custom<void>(\n\t\t\t\t(tui, theme, keybindings, done) => {\n\t\t\t\t\tconst close = () => {\n\t\t\t\t\t\tif (closeMenu === close) closeMenu = undefined;\n\t\t\t\t\t\tdone();\n\t\t\t\t\t};\n\t\t\t\t\tcloseMenu = close;\n\t\t\t\t\treturn new TasksMenu({\n\t\t\t\t\t\tstate,\n\t\t\t\t\t\ttui,\n\t\t\t\t\t\ttheme,\n\t\t\t\t\t\tkeybindings,\n\t\t\t\t\t\thost: ctx.tasks,\n\t\t\t\t\t\tonClose: close,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\t{ overlay: true, overlayOptions: { anchor: \"center\", width: \"100%\", maxHeight: \"100%\" } },\n\t\t\t);\n\t\t},\n\t};\n}\n"]}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Output files: files pi writes so the model can use output it was not shown in full, such as the
3
+ * full text of truncated tool output, binary MCP resources, and images shown by codemode scripts.
4
+ * Every output file is created here, so where they are stored can change in one place. Today they
5
+ * go to the OS temp directory.
6
+ */
7
+ import { type WriteStream } from "node:fs";
8
+ /** A new, unused path: `<dir>/<prefix>-<random hex><extension>`. `extension` includes the dot. */
9
+ export declare function createOutputFilePath(prefix: string, extension: string): string;
10
+ /** Write `data` to a new output file and return its path. */
11
+ export declare function writeOutputFile(prefix: string, extension: string, data: string | Uint8Array): Promise<string>;
12
+ /** Open a new output file for streamed output. */
13
+ export declare function createOutputFileStream(prefix: string, extension: string): {
14
+ path: string;
15
+ stream: WriteStream;
16
+ };
17
+ //# sourceMappingURL=output-files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output-files.d.ts","sourceRoot":"","sources":["../../src/utils/output-files.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAQ9D,kGAAkG;AAClG,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED,6DAA6D;AAC7D,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAKnH;AAED,kDAAkD;AAClD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,CAG/G"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Output files: files pi writes so the model can use output it was not shown in full, such as the
3
+ * full text of truncated tool output, binary MCP resources, and images shown by codemode scripts.
4
+ * Every output file is created here, so where they are stored can change in one place. Today they
5
+ * go to the OS temp directory.
6
+ */
7
+ import { randomBytes } from "node:crypto";
8
+ import { createWriteStream } from "node:fs";
9
+ import { writeFile } from "node:fs/promises";
10
+ import { tmpdir } from "node:os";
11
+ import { join } from "node:path";
12
+ /** Output can carry private data, so only the user may read the files. */
13
+ const OUTPUT_FILE_MODE = 0o600;
14
+ /** A new, unused path: `<dir>/<prefix>-<random hex><extension>`. `extension` includes the dot. */
15
+ export function createOutputFilePath(prefix, extension) {
16
+ return join(tmpdir(), `${prefix}-${randomBytes(8).toString("hex")}${extension}`);
17
+ }
18
+ /** Write `data` to a new output file and return its path. */
19
+ export async function writeOutputFile(prefix, extension, data) {
20
+ const path = createOutputFilePath(prefix, extension);
21
+ // `wx` never follows a link someone else placed at the path.
22
+ await writeFile(path, data, { mode: OUTPUT_FILE_MODE, flag: "wx" });
23
+ return path;
24
+ }
25
+ /** Open a new output file for streamed output. */
26
+ export function createOutputFileStream(prefix, extension) {
27
+ const path = createOutputFilePath(prefix, extension);
28
+ return { path, stream: createWriteStream(path, { mode: OUTPUT_FILE_MODE, flags: "wx" }) };
29
+ }
30
+ //# sourceMappingURL=output-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output-files.js","sourceRoot":"","sources":["../../src/utils/output-files.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAoB,MAAM,SAAS,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,0EAA0E;AAC1E,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAE/B,kGAAkG;AAClG,MAAM,UAAU,oBAAoB,CAAC,MAAc,EAAE,SAAiB;IACrE,OAAO,IAAI,CAAC,MAAM,EAAE,EAAE,GAAG,MAAM,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,SAAS,EAAE,CAAC,CAAC;AAClF,CAAC;AAED,6DAA6D;AAC7D,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAc,EAAE,SAAiB,EAAE,IAAyB;IACjG,MAAM,IAAI,GAAG,oBAAoB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACrD,6DAA6D;IAC7D,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACpE,OAAO,IAAI,CAAC;AACb,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,sBAAsB,CAAC,MAAc,EAAE,SAAiB;IACvE,MAAM,IAAI,GAAG,oBAAoB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACrD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AAC3F,CAAC","sourcesContent":["/**\n * Output files: files pi writes so the model can use output it was not shown in full, such as the\n * full text of truncated tool output, binary MCP resources, and images shown by codemode scripts.\n * Every output file is created here, so where they are stored can change in one place. Today they\n * go to the OS temp directory.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { createWriteStream, type WriteStream } from \"node:fs\";\nimport { writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/** Output can carry private data, so only the user may read the files. */\nconst OUTPUT_FILE_MODE = 0o600;\n\n/** A new, unused path: `<dir>/<prefix>-<random hex><extension>`. `extension` includes the dot. */\nexport function createOutputFilePath(prefix: string, extension: string): string {\n\treturn join(tmpdir(), `${prefix}-${randomBytes(8).toString(\"hex\")}${extension}`);\n}\n\n/** Write `data` to a new output file and return its path. */\nexport async function writeOutputFile(prefix: string, extension: string, data: string | Uint8Array): Promise<string> {\n\tconst path = createOutputFilePath(prefix, extension);\n\t// `wx` never follows a link someone else placed at the path.\n\tawait writeFile(path, data, { mode: OUTPUT_FILE_MODE, flag: \"wx\" });\n\treturn path;\n}\n\n/** Open a new output file for streamed output. */\nexport function createOutputFileStream(prefix: string, extension: string): { path: string; stream: WriteStream } {\n\tconst path = createOutputFilePath(prefix, extension);\n\treturn { path, stream: createWriteStream(path, { mode: OUTPUT_FILE_MODE, flags: \"wx\" }) };\n}\n"]}
@@ -12,7 +12,7 @@ Start Bash through the native `bash` tool (the optional native Windows `powershe
12
12
 
13
13
  Omitting `background` keeps the normal foreground wait. A background submission returns an execution reference, not a successful final outcome. Up to eight background executions run at once; a ninth background submission is rejected without starting. Foreground commands are never limited by background work. `bg create` has been removed; its old stored results and background notifications show their saved text in transcripts.
14
14
 
15
- In interactive mode, **Ctrl+B** moves eligible foreground managed tasks to the background, oldest first, while background slots remain; when every slot is taken it says so instead. Once a foreground execution has run for ten seconds and can still move, the statusline shows `Ctrl+B to background`. It works even when `/tasks` owns focus. The same execution continues: no cancellation, restart or timeout reset. The handoff result names the path the work took, because the model cannot see it: a command submitted with `background: true` reports a handoff from an empty log, while one moved mid-run reports that its execution continued unchanged, that the log already holds output, and that a supplied timeout is still measured from command startup. It does not detach ordinary file tools, or user `!` shell commands.
15
+ In interactive mode, **Ctrl+B** moves eligible foreground managed tasks to the background, oldest first, while background slots remain; when every slot is taken it says so instead. Once a foreground execution has run for ten seconds and can still move, the statusline shows `Ctrl+B to run in background`, even when no background tasks exist. The hint is hidden while every background slot is taken, background execution is disabled, or no eligible foreground execution remains. It uses the configured shortcut and disappears if that shortcut is disabled. The shortcut works even when `/tasks` owns focus. The same execution continues: no cancellation, restart or timeout reset. The handoff result names the path the work took, because the model cannot see it: a command submitted with `background: true` reports a handoff from an empty log, while one moved mid-run reports that its execution continued unchanged, that the log already holds output, and that a supplied timeout is still measured from command startup. It does not detach ordinary file tools, or user `!` shell commands.
16
16
 
17
17
  When nothing is eligible, the key is not consumed: it falls through to other bindings instead of reporting a no-op.
18
18
 
@@ -90,7 +90,7 @@ Controls use the `app.tasks.*` bindings: `search`, `previousTab`, `nextTab`, `ne
90
90
 
91
91
  The `/hotkeys` table lists the task bindings next to the editor ones, and the panel's `?` help lists every control, so both detach scopes are discoverable without reading these docs.
92
92
 
93
- The statusline names the foreground share of active managed work — all foreground, or an `N foreground` breakdown once background work is active too — and points to `/tasks`. When only retained background results remain, it labels that scope explicitly; foreground results arrive inline in the transcript instead of staying in the statusline. The model-facing `tasks list` keeps its background-only scope and foreground omission count.
93
+ The statusline counts only queued, running and stopping background tasks: `1 background task · /tasks to view` or `2 background tasks · /tasks to view`. Foreground tasks and retained results do not contribute to the count; the summary disappears when no active background tasks remain. Finished results remain available in `/tasks`. The independent detach hint can appear alone or after the summary: `1 background task · /tasks to view · Ctrl+B to run in background`. The count uses normal text, `/tasks` and the shortcut use the accent color, and descriptions and separators are muted. The model-facing `tasks list` keeps its background-only scope and foreground omission count.
94
94
 
95
95
  ## Completion notifications
96
96
 
@@ -33,7 +33,7 @@ The tests failed.
33
33
 
34
34
  The bash renderer shows its own `Elapsed` line while a command runs and a fixed `Took` line after it settles. The shell adds no progress row, and other renderers that refresh over time schedule their own repaints: they arm a timer in renderer state, call the render context's `invalidate()`, and clear the timer on the first settled render. The shell calls `state.dispose()` when a row is disposed, so a timer never outlives its row.
35
35
 
36
- The `Ctrl+B` background hint is not part of a tool row. Once a foreground managed execution has run for ten seconds and can still move, the built-in Tasks UI shows `Ctrl+B to background` in the statusline, next to the `tasks N active` count.
36
+ The `Ctrl+B` background hint is not part of a tool row. Once a foreground managed execution has run for ten seconds and can still move, the built-in Tasks UI shows `Ctrl+B to run in background` in the statusline. It appears on its own when only foreground work exists, or after `1 background task · /tasks to view` (pluralized for multiple tasks) while background work is active. Finished tasks do not keep the summary visible.
37
37
 
38
38
  ## Implementation boundary
39
39
 
package/docs/codemode.md CHANGED
@@ -23,7 +23,7 @@ The result starts with `Script completed` or `Script failed`, the wall time, and
23
23
  |---|---|
24
24
  | `tools.<name>(args)` | Call a tool. See [Call tools](#call-tools). |
25
25
  | `text(value)` | Add a text item to the output. Strings are added as is, other values as JSON. |
26
- | `image(value)` | Add an image to the output: a base64 `data:` URL, an `{ image_url }` object, or an image block `{ type: "image", data, mimeType }` such as those returned by MCP tools and `models.generateImages()`. Remote URLs are not supported. PNG, JPEG, GIF, and WebP are accepted. |
26
+ | `image(value)` | Add an image to the output: a base64 `data:` URL, an `{ image_url }` object, or an image block `{ type: "image", data, mimeType }` such as those returned by MCP tools and `models.generateImages()`. Remote URLs are not supported. PNG, JPEG, GIF, and WebP are accepted. Each image is also saved to a temp file, and the result names the path before the image. |
27
27
  | `console.log(...)` | Like `text()`; `info`, `warn`, `error`, and `debug` do the same. |
28
28
  | `return value` | A top-level `return` adds the value like `text()`. |
29
29
  | `exit()` | End the script successfully. |
@@ -54,7 +54,7 @@ While `codemode` is active, `codemode.mode` in [settings](settings.md#tools) dec
54
54
 
55
55
  `store(key, value)` keeps a JSON value under a string key for later `codemode` calls; storing `undefined` deletes the key. `load(key)` returns the value, or `undefined`. Writes are kept only when the script succeeds: each successful script that stores values appends a `codemode-store` custom entry to the session, so resumed sessions keep the values and each branch sees only the values written on its path.
56
56
 
57
- The store is for small state such as IDs, cursors, or summaries. One value may have at most 262144 characters of JSON and all values together at most 1048576. Do not store image data; show images with `image()` or write them to a file with a tool.
57
+ The store is for small state such as IDs, cursors, or summaries. One value may have at most 262144 characters of JSON and all values together at most 1048576. Do not store image data; show images with `image()`, which also saves them to a temp file.
58
58
 
59
59
  ## Models
60
60
 
@@ -185,7 +185,7 @@ type TextBlock = { type: "text"; text: string };
185
185
  type ImageBlock = { type: "image"; data: string; mimeType: string };
186
186
  ```
187
187
 
188
- Show generated images with `image(block)`. Do not print `data` with `text()`, `console`, or `return`: it is large and the model cannot read it as text. Generated images are not saved to disk; to keep one, write it to a file with a tool.
188
+ Show generated images with `image(block)`. Do not print `data` with `text()`, `console`, or `return`: it is large and the model cannot read it as text. `image()` also saves each image to a temp file and puts its path in the result, so a later turn can copy or move the file.
189
189
 
190
190
  ```js
191
191
  // @options: {"timeout_ms": 300000}
@@ -1164,6 +1164,7 @@ Session-bound `TasksContext` supervises native and extension-owned execution. Fo
1164
1164
  | `wait(id, timeoutMs?, signal?)` | Observation only; timeout or cancellation ends the wait, not execution |
1165
1165
  | `kill(id)` | Requests cancellation; `stopping` is not a terminal status |
1166
1166
  | `detach(id)` / `detachForeground()` | Hands off one task, or eligible foreground tasks oldest first while background slots remain, without restarting them; throws when every background slot is taken |
1167
+ | `canDetach(id)` | Reports whether an execution can move now, including host policy and available background slots; does not change the execution |
1167
1168
  | `subscribe(listener)` | Observes task state and progress |
1168
1169
  | `retain(id)` | Returns an idempotent release function protecting retained data, without delaying notifications |
1169
1170
  | `holdDelivery(id)` | Explicitly retains data and delays automatic delivery until released |
@@ -36,8 +36,8 @@ Modifier combinations: `ctrl+shift+x`, `alt+ctrl+x`, `ctrl+shift+alt+x`, `super+
36
36
  | `tui.editor.cursorRight` | `right`, `ctrl+f` | Move cursor right |
37
37
  | `tui.editor.cursorWordLeft` | `alt+left`, `ctrl+left`, `alt+b` | Move cursor word left |
38
38
  | `tui.editor.cursorWordRight` | `alt+right`, `ctrl+right`, `alt+f` | Move cursor word right |
39
- | `tui.editor.cursorLineStart` | `home`, `ctrl+home`, `ctrl+a` | Move to line start |
40
- | `tui.editor.cursorLineEnd` | `end`, `ctrl+end`, `ctrl+e` | Move to line end |
39
+ | `tui.editor.cursorLineStart` | `home`, `ctrl+a` | Move to line start |
40
+ | `tui.editor.cursorLineEnd` | `end`, `ctrl+e` | Move to line end |
41
41
  | `tui.editor.jumpForward` | `ctrl+]` | Jump forward to character |
42
42
  | `tui.editor.jumpBackward` | `ctrl+alt+]` | Jump backward to character |
43
43
  | `tui.editor.pageUp` | `pageUp`, `ctrl+pageUp` | Scroll up by page |
@@ -90,12 +90,12 @@ These actions apply when interactive mode uses `--tui-mode fullscreen` and targe
90
90
 
91
91
  Hold Alt while scrolling the mouse wheel for five-times-faster scrolling. Hovering over autocomplete or settings lists keeps their selection and scroll position unchanged; click an item to select it.
92
92
 
93
- Fullscreen transcript bindings take precedence over editor bindings. The default unmodified navigation keys therefore control the transcript in fullscreen mode, while their `ctrl` variants continue to control the editor. Outside fullscreen mode, both variants control the editor.
93
+ Fullscreen transcript bindings take precedence over editor bindings using the same key. `pageUp` and `pageDown` therefore control the transcript in fullscreen mode while their `ctrl` variants keep controlling the editor; `home` and `end` always control the editor, while `ctrl+home` and `ctrl+end` control the transcript in fullscreen mode. Outside fullscreen mode, all of these keys control the editor.
94
94
 
95
95
  | Key | Default mode | Fullscreen mode |
96
96
  |-----|--------------|-----------------|
97
- | `home`, `end` | Editor | Transcript |
98
- | `ctrl+home`, `ctrl+end` | Editor | Editor |
97
+ | `home`, `end` | Editor | Editor |
98
+ | `ctrl+home`, `ctrl+end` | Editor | Transcript |
99
99
  | `pageUp`, `pageDown` | Editor | Transcript |
100
100
  | `ctrl+pageUp`, `ctrl+pageDown` | Editor | Editor |
101
101
 
@@ -115,8 +115,8 @@ This routing remains configurable through the ordinary action bindings. For exam
115
115
  | `tui.altScreen.searchNext` | `enter`, `ctrl+g` | Select the next search match while searching |
116
116
  | `tui.altScreen.searchPrevious` | `shift+enter`, `ctrl+shift+g` | Select the previous search match while searching |
117
117
  | `tui.altScreen.searchClose` | `escape` | Close transcript search |
118
- | `tui.altScreen.top` | `home` | Scroll to the beginning of the transcript |
119
- | `tui.altScreen.bottom` | `end` | Scroll to the transcript end and follow new output |
118
+ | `tui.altScreen.top` | `ctrl+home` | Scroll to the beginning of the transcript |
119
+ | `tui.altScreen.bottom` | `ctrl+end` | Scroll to the transcript end and follow new output |
120
120
 
121
121
  ### Application
122
122
 
package/docs/providers.md CHANGED
@@ -83,7 +83,7 @@ pi
83
83
  |----------|----------------------|------------------|
84
84
  | Anthropic | `ANTHROPIC_API_KEY` | `anthropic` |
85
85
  | Ant Ling | `ANT_LING_API_KEY` | `ant-ling` |
86
- | Azure OpenAI Responses | `AZURE_OPENAI_API_KEY` | `azure-openai-responses` |
86
+ | Azure OpenAI | `AZURE_OPENAI_API_KEY` | `azure` |
87
87
  | OpenAI | `OPENAI_API_KEY` | `openai` |
88
88
  | DeepSeek | `DEEPSEEK_API_KEY` | `deepseek` |
89
89
  | NVIDIA NIM | `NVIDIA_API_KEY` | `nvidia` |
@@ -218,6 +218,10 @@ export AZURE_OPENAI_API_VERSION=2024-02-01
218
218
  export AZURE_OPENAI_DEPLOYMENT_NAME_MAP=gpt-4=my-gpt4,gpt-4o=my-gpt4o
219
219
  ```
220
220
 
221
+ The `auth.json` key for this provider is `azure`, renamed from `azure-openai-responses`; rename it in `models.json` and `settings.json` as well. The `AZURE_OPENAI_*` environment variables are unchanged.
222
+
223
+ The `azure` provider serves both Azure OpenAI Responses deployments and Azure AI Foundry Chat Completions deployments (for example `azure/deepseek-v4-pro`).
224
+
221
225
  ### Amazon Bedrock
222
226
 
223
227
  Use `/login amazon-bedrock` to store a Bedrock API key, or configure one of the ambient AWS credential sources below:
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-custom-provider",
9
- "version": "1.0.2",
9
+ "version": "1.0.3",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sdk": "^0.52.0"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-anthropic",
3
3
  "private": true,
4
- "version": "1.0.2",
4
+ "version": "1.0.3",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-gitlab-duo",
3
3
  "private": true,
4
- "version": "1.0.2",
4
+ "version": "1.0.3",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-gondolin",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-gondolin",
9
- "version": "1.0.2",
9
+ "version": "1.0.3",
10
10
  "dependencies": {
11
11
  "@earendil-works/gondolin": "0.12.0"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-gondolin",
3
3
  "private": true,
4
- "version": "1.0.2",
4
+ "version": "1.0.3",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-sandbox",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-sandbox",
9
- "version": "1.0.2",
9
+ "version": "1.0.3",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sandbox-runtime": "^0.0.26"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-sandbox",
3
3
  "private": true,
4
- "version": "1.0.2",
4
+ "version": "1.0.3",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-with-deps",
9
- "version": "1.0.2",
9
+ "version": "1.0.3",
10
10
  "dependencies": {
11
11
  "ms": "^2.1.3"
12
12
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
3
  "private": true,
4
- "version": "1.0.2",
4
+ "version": "1.0.3",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",