@selesai/code 0.3.0 → 0.3.1

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 (230) hide show
  1. package/dist/core/diagnostics.d.ts +1 -0
  2. package/dist/core/diagnostics.d.ts.map +1 -1
  3. package/dist/core/diagnostics.js.map +1 -1
  4. package/dist/core/package-manager.d.ts +10 -0
  5. package/dist/core/package-manager.d.ts.map +1 -1
  6. package/dist/core/package-manager.js +93 -8
  7. package/dist/core/package-manager.js.map +1 -1
  8. package/dist/core/package-manager.test.d.ts +2 -0
  9. package/dist/core/package-manager.test.d.ts.map +1 -0
  10. package/dist/core/package-manager.test.js +20 -0
  11. package/dist/core/package-manager.test.js.map +1 -0
  12. package/dist/core/resource-loader.d.ts +3 -0
  13. package/dist/core/resource-loader.d.ts.map +1 -1
  14. package/dist/core/resource-loader.js +10 -0
  15. package/dist/core/resource-loader.js.map +1 -1
  16. package/dist/core/settings-manager.d.ts +1 -0
  17. package/dist/core/settings-manager.d.ts.map +1 -1
  18. package/dist/core/settings-manager.js.map +1 -1
  19. package/dist/extensions/caveman/caveman-instructions.cjs +39 -0
  20. package/dist/extensions/caveman/index.js +116 -0
  21. package/dist/extensions/caveman/package.json +8 -0
  22. package/dist/extensions/caveman/test/extension.test.js +185 -0
  23. package/dist/extensions/caveman/test/helpers.test.js +67 -0
  24. package/dist/extensions/pi-intercom/broker/broker.ts +2 -3
  25. package/dist/extensions/pi-intercom/broker/paths.ts +10 -5
  26. package/dist/extensions/pi-intercom/broker/spawn.ts +2 -3
  27. package/dist/extensions/pi-intercom/config.ts +2 -2
  28. package/dist/extensions/pi-intercom/index.ts +2 -2
  29. package/dist/extensions/pi-intercom/package.json +2 -2
  30. package/dist/extensions/pi-intercom/ui/compose.ts +3 -3
  31. package/dist/extensions/pi-intercom/ui/inline-message.ts +3 -3
  32. package/dist/extensions/pi-intercom/ui/session-list.ts +3 -3
  33. package/dist/extensions/pi-powerline-footer/bash-mode/history.ts +2 -1
  34. package/dist/extensions/pi-powerline-footer/index.ts +9 -16
  35. package/dist/extensions/pi-powerline-footer/welcome.ts +23 -16
  36. package/dist/extensions/pi-powerline-footer/working-vibes.ts +2 -9
  37. package/dist/extensions/pi-rewind-hook/index.ts +1 -1
  38. package/dist/extensions/pi-rewind-hook/package.json +1 -1
  39. package/dist/extensions/pi-subagents/README.md +224 -52
  40. package/dist/extensions/pi-subagents/install.mjs +93 -0
  41. package/dist/extensions/pi-subagents/package.json +4 -5
  42. package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +1 -1
  43. package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +1 -1
  44. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +76 -13
  45. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +179 -2
  46. package/dist/extensions/pi-subagents/src/agents/agent-memory.ts +254 -0
  47. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +11 -0
  48. package/dist/extensions/pi-subagents/src/agents/agents.ts +193 -19
  49. package/dist/extensions/pi-subagents/src/agents/chain-serializer.ts +27 -2
  50. package/dist/extensions/pi-subagents/src/extension/config.ts +27 -4
  51. package/dist/extensions/pi-subagents/src/extension/doctor.ts +1 -7
  52. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +3 -2
  53. package/dist/extensions/pi-subagents/src/extension/index.ts +73 -71
  54. package/dist/extensions/pi-subagents/src/extension/rpc.ts +369 -0
  55. package/dist/extensions/pi-subagents/src/extension/schemas.ts +54 -10
  56. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +200 -0
  57. package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +21 -253
  58. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +519 -0
  59. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +1 -1
  60. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +195 -105
  61. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +88 -2
  62. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +67 -10
  63. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +34 -4
  64. package/dist/extensions/pi-subagents/src/runs/background/completion-batcher.ts +166 -0
  65. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +156 -1
  66. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +515 -0
  67. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +161 -44
  68. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +1 -2
  69. package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +3 -2
  70. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +167 -6
  71. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +514 -0
  72. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +28 -1
  73. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +873 -204
  74. package/dist/extensions/pi-subagents/src/runs/background/wait.ts +353 -0
  75. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +123 -27
  76. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +174 -70
  77. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +569 -81
  78. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +45 -22
  79. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +3 -1
  80. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +2 -2
  81. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +172 -21
  82. package/dist/extensions/pi-subagents/src/runs/shared/model-scope.ts +128 -0
  83. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +89 -0
  84. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +50 -1
  85. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +35 -4
  86. package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +63 -45
  87. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +2 -0
  88. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +125 -5
  89. package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +74 -0
  90. package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +52 -0
  91. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +28 -5
  92. package/dist/extensions/pi-subagents/src/shared/artifacts.ts +16 -1
  93. package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +15 -2
  94. package/dist/extensions/pi-subagents/src/shared/child-transcript.ts +212 -0
  95. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +133 -22
  96. package/dist/extensions/pi-subagents/src/shared/settings.ts +3 -1
  97. package/dist/extensions/pi-subagents/src/shared/types.ts +197 -4
  98. package/dist/extensions/pi-subagents/src/shared/utils.ts +100 -15
  99. package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +330 -0
  100. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +135 -4
  101. package/dist/extensions/pi-subagents/src/tui/render.ts +32 -12
  102. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +94 -0
  103. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +2772 -0
  104. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +1107 -0
  105. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +463 -0
  106. package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +274 -0
  107. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +1535 -0
  108. package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +242 -0
  109. package/dist/extensions/pi-subagents/test/integration/doctor-executor.test.ts +128 -0
  110. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +252 -0
  111. package/dist/extensions/pi-subagents/test/integration/foreground-result-size.test.ts +188 -0
  112. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +1306 -0
  113. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +1033 -0
  114. package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +397 -0
  115. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +583 -0
  116. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +691 -0
  117. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +892 -0
  118. package/dist/extensions/pi-subagents/test/integration/session-tokens.test.ts +62 -0
  119. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1824 -0
  120. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +1284 -0
  121. package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +127 -0
  122. package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +299 -0
  123. package/dist/extensions/pi-subagents/test/integration/top-level-async.test.ts +37 -0
  124. package/dist/extensions/pi-subagents/test/support/helpers.ts +189 -0
  125. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +335 -0
  126. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +129 -0
  127. package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +184 -0
  128. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +285 -0
  129. package/dist/extensions/pi-subagents/test/support/register-loader.mjs +15 -0
  130. package/dist/extensions/pi-subagents/test/support/ts-loader.mjs +108 -0
  131. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +421 -0
  132. package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +213 -0
  133. package/dist/extensions/pi-subagents/test/unit/agent-eject-disable.test.ts +343 -0
  134. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +1199 -0
  135. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +556 -0
  136. package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +420 -0
  137. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +579 -0
  138. package/dist/extensions/pi-subagents/test/unit/agent-scope.test.ts +20 -0
  139. package/dist/extensions/pi-subagents/test/unit/agent-selection.test.ts +88 -0
  140. package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +24 -0
  141. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +86 -0
  142. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +221 -0
  143. package/dist/extensions/pi-subagents/test/unit/async-permission-session.test.ts +98 -0
  144. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +285 -0
  145. package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +112 -0
  146. package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +206 -0
  147. package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +159 -0
  148. package/dist/extensions/pi-subagents/test/unit/chain-serializer.test.ts +229 -0
  149. package/dist/extensions/pi-subagents/test/unit/child-transcript.test.ts +225 -0
  150. package/dist/extensions/pi-subagents/test/unit/close-grace-timer.test.ts +101 -0
  151. package/dist/extensions/pi-subagents/test/unit/completion-batcher.test.ts +228 -0
  152. package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +36 -0
  153. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +152 -0
  154. package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +72 -0
  155. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +327 -0
  156. package/dist/extensions/pi-subagents/test/unit/control-notices.test.ts +161 -0
  157. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +159 -0
  158. package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +217 -0
  159. package/dist/extensions/pi-subagents/test/unit/extra-agent-dirs.test.ts +88 -0
  160. package/dist/extensions/pi-subagents/test/unit/file-coalescer.test.ts +68 -0
  161. package/dist/extensions/pi-subagents/test/unit/foreground-tool-call-compaction.test.ts +78 -0
  162. package/dist/extensions/pi-subagents/test/unit/fork-context.test.ts +415 -0
  163. package/dist/extensions/pi-subagents/test/unit/get-final-output.test.ts +126 -0
  164. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +271 -0
  165. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +173 -0
  166. package/dist/extensions/pi-subagents/test/unit/jsonl-writer.test.ts +119 -0
  167. package/dist/extensions/pi-subagents/test/unit/live-async-resume.test.ts +170 -0
  168. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +346 -0
  169. package/dist/extensions/pi-subagents/test/unit/model-info.test.ts +62 -0
  170. package/dist/extensions/pi-subagents/test/unit/model-scope-settings.test.ts +87 -0
  171. package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +145 -0
  172. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +162 -0
  173. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +470 -0
  174. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +329 -0
  175. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +319 -0
  176. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +70 -0
  177. package/dist/extensions/pi-subagents/test/unit/parallel-utils.test.ts +247 -0
  178. package/dist/extensions/pi-subagents/test/unit/path-handling.test.ts +113 -0
  179. package/dist/extensions/pi-subagents/test/unit/path-resolution.test.ts +94 -0
  180. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +803 -0
  181. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +209 -0
  182. package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +238 -0
  183. package/dist/extensions/pi-subagents/test/unit/proactive-skills.test.ts +118 -0
  184. package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +299 -0
  185. package/dist/extensions/pi-subagents/test/unit/prompt-template-bridge.test.ts +408 -0
  186. package/dist/extensions/pi-subagents/test/unit/prompt-workflows.test.ts +134 -0
  187. package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +257 -0
  188. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +209 -0
  189. package/dist/extensions/pi-subagents/test/unit/result-intercom.test.ts +226 -0
  190. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +294 -0
  191. package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +147 -0
  192. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +997 -0
  193. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +478 -0
  194. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +562 -0
  195. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +213 -0
  196. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +438 -0
  197. package/dist/extensions/pi-subagents/test/unit/slash-bridge.test.ts +54 -0
  198. package/dist/extensions/pi-subagents/test/unit/slash-chain-groups.test.ts +464 -0
  199. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +255 -0
  200. package/dist/extensions/pi-subagents/test/unit/status-format.test.ts +19 -0
  201. package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +245 -0
  202. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +517 -0
  203. package/dist/extensions/pi-subagents/test/unit/temp-paths.test.ts +72 -0
  204. package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +52 -0
  205. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +237 -0
  206. package/dist/extensions/pi-subagents/test/unit/total-cost.test.ts +60 -0
  207. package/dist/extensions/pi-subagents/test/unit/ts-loader.test.ts +33 -0
  208. package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +202 -0
  209. package/dist/extensions/pi-subagents/test/unit/types-fork-preamble.test.ts +23 -0
  210. package/dist/extensions/pi-subagents/test/unit/wait.test.ts +398 -0
  211. package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +88 -0
  212. package/dist/extensions/pi-subagents/test/unit/windows-hide-spawn.test.ts +26 -0
  213. package/dist/extensions/pi-subagents/test/unit/workflow-graph.test.ts +134 -0
  214. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +479 -0
  215. package/dist/extensions/pi-web-agent/src/changelog-notice.ts +5 -2
  216. package/dist/extensions/pi-web-agent/src/presentation/config-store.ts +6 -4
  217. package/dist/main.d.ts.map +1 -1
  218. package/dist/main.js +4 -0
  219. package/dist/main.js.map +1 -1
  220. package/dist/modes/interactive/components/first-time-setup.d.ts +1 -1
  221. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
  222. package/dist/modes/interactive/components/first-time-setup.js +19 -5
  223. package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
  224. package/dist/skills/caveman/SKILL.md +49 -0
  225. package/dist/skills/handoff-text/SKILL.md +13 -0
  226. package/docs/docs.json +4 -0
  227. package/docs/extensions.md +2 -0
  228. package/docs/settings.md +15 -0
  229. package/docs/shared-host-extensions.md +109 -0
  230. package/package.json +1 -1
@@ -0,0 +1,285 @@
1
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
+ import * as os from "node:os";
3
+ import * as path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import {
6
+ type AssistantMessage,
7
+ type Context,
8
+ type FauxContentBlock,
9
+ type FauxResponseStep,
10
+ fauxAssistantMessage,
11
+ fauxText,
12
+ fauxToolCall,
13
+ registerFauxProvider,
14
+ type ToolCall,
15
+ } from "@earendil-works/pi-ai";
16
+ import {
17
+ type AgentSession,
18
+ type AgentSessionEvent,
19
+ type ModelRegistry,
20
+ createAgentSession,
21
+ DefaultResourceLoader,
22
+ SessionManager,
23
+ SettingsManager,
24
+ } from "@selesai/code";
25
+
26
+ const EXTENSION_PATH = fileURLToPath(new URL("../../src/extension/index.ts", import.meta.url));
27
+ const CHILD_CLI_PATH = fileURLToPath(new URL("./real-session-child-cli.mjs", import.meta.url));
28
+
29
+ export type FauxReply = string | FauxContentBlock | FauxContentBlock[] | AssistantMessage;
30
+ export type FauxResponder = (context: Context, state: { callCount: number }) => FauxReply | Promise<FauxReply>;
31
+
32
+ export interface RealSessionRunOptions {
33
+ prompt: string;
34
+ childText: string;
35
+ respond: FauxResponder;
36
+ timeoutMs?: number;
37
+ }
38
+
39
+ export interface RealSessionRun {
40
+ responseText: string;
41
+ parentSession: AgentSession;
42
+ modelCalls: number;
43
+ dispose: () => Promise<void>;
44
+ }
45
+
46
+ export function subagentCall(args: Record<string, unknown>, id = "call-subagent-e2e"): ToolCall {
47
+ return fauxToolCall("subagent", args, { id });
48
+ }
49
+
50
+ export function routeParentThroughSubagent(input: {
51
+ childMarker: string;
52
+ subagentArgs: Record<string, unknown>;
53
+ }): FauxResponder {
54
+ return (context) => {
55
+ const isParent = (context.tools ?? []).some((tool) => tool.name === "subagent");
56
+ if (!isParent) return "Unexpected non-parent model call.";
57
+ const resultText = latestSubagentToolResultText(context.messages as Array<{ role?: string; toolName?: string; content?: unknown }>);
58
+ if (resultText !== undefined) {
59
+ return `Parent relays: ${resultText.includes(input.childMarker) ? input.childMarker : "CHILD_MISSING"}`;
60
+ }
61
+ return subagentCall(input.subagentArgs);
62
+ };
63
+ }
64
+
65
+ export function subagentToolResults(session: AgentSession): string[] {
66
+ const results: string[] = [];
67
+ for (const message of session.messages) {
68
+ if (message.role !== "toolResult") continue;
69
+ if ((message as { toolName?: string }).toolName !== "subagent") continue;
70
+ results.push(textFromContent((message as { content?: unknown }).content));
71
+ }
72
+ return results;
73
+ }
74
+
75
+ function latestSubagentToolResultText(messages: Array<{ role?: string; toolName?: string; content?: unknown }>): string | undefined {
76
+ for (let i = messages.length - 1; i >= 0; i--) {
77
+ const message = messages[i]!;
78
+ if (message.role === "toolResult" && message.toolName === "subagent") {
79
+ return textFromContent(message.content);
80
+ }
81
+ }
82
+ return undefined;
83
+ }
84
+
85
+ function textFromContent(content: unknown): string {
86
+ if (!Array.isArray(content)) return "";
87
+ return content
88
+ .map((part) => part && typeof part === "object" && (part as { type?: unknown }).type === "text"
89
+ ? String((part as { text?: unknown }).text ?? "")
90
+ : "")
91
+ .join("");
92
+ }
93
+
94
+ function toAssistantMessage(reply: FauxReply): AssistantMessage {
95
+ if (reply && typeof reply === "object" && "role" in reply) return reply as AssistantMessage;
96
+ const content: FauxContentBlock[] = typeof reply === "string" ? [fauxText(reply)] : Array.isArray(reply) ? reply : [reply];
97
+ const hasToolCall = content.some((block) => (block as { type?: string }).type === "toolCall");
98
+ return fauxAssistantMessage(content, { stopReason: hasToolCall ? "toolUse" : "stop" });
99
+ }
100
+
101
+ function createModelRegistry(model: { provider: string; id: string }) {
102
+ return {
103
+ find: (provider: string, id: string) => provider === model.provider && id === model.id ? model : undefined,
104
+ getAll: () => [model],
105
+ getAvailable: () => [model],
106
+ hasConfiguredAuth: () => true,
107
+ isUsingOAuth: () => false,
108
+ getApiKeyAndHeaders: async () => ({ ok: true, apiKey: "faux", headers: {} }),
109
+ registerProvider: () => {},
110
+ unregisterProvider: () => {},
111
+ };
112
+ }
113
+
114
+ function installChildPiShim(childText: string): () => void {
115
+ const rootDir = mkdtempSync(path.join(os.tmpdir(), "pi-real-session-cli-"));
116
+ const binDir = path.join(rootDir, "bin");
117
+ const previousPath = process.env.PATH;
118
+ const previousChildText = process.env.SELESAI_SUBAGENTS_E2E_CHILD_TEXT;
119
+ const previousArgv1 = process.argv[1];
120
+
121
+ writeFileSync(path.join(rootDir, ".keep"), "");
122
+ mkdirSync(binDir, { recursive: true });
123
+ writeFileSync(
124
+ path.join(binDir, "pi"),
125
+ `#!/bin/sh\nexec "${process.execPath}" "${CHILD_CLI_PATH}" "$@"\n`,
126
+ { mode: 0o755 },
127
+ );
128
+ writeFileSync(
129
+ path.join(binDir, "pi.cmd"),
130
+ `@echo off\r\n"${process.execPath}" "${CHILD_CLI_PATH}" %*\r\n`,
131
+ );
132
+
133
+ process.env.PATH = `${binDir}${path.delimiter}${previousPath ?? ""}`;
134
+ process.env.SELESAI_SUBAGENTS_E2E_CHILD_TEXT = childText;
135
+ if (process.platform === "win32") process.argv[1] = CHILD_CLI_PATH;
136
+
137
+ return () => {
138
+ if (previousPath === undefined) delete process.env.PATH;
139
+ else process.env.PATH = previousPath;
140
+ if (previousChildText === undefined) delete process.env.SELESAI_SUBAGENTS_E2E_CHILD_TEXT;
141
+ else process.env.SELESAI_SUBAGENTS_E2E_CHILD_TEXT = previousChildText;
142
+ if (process.platform === "win32") {
143
+ if (previousArgv1 === undefined) delete process.argv[1];
144
+ else process.argv[1] = previousArgv1;
145
+ }
146
+ rmSync(rootDir, { recursive: true, force: true });
147
+ };
148
+ }
149
+
150
+ function setEnv(name: string, value: string | undefined): void {
151
+ if (value === undefined) delete process.env[name];
152
+ else process.env[name] = value;
153
+ }
154
+
155
+ function restoreEnv(snapshot: Map<string, string | undefined>): void {
156
+ for (const [name, value] of snapshot) setEnv(name, value);
157
+ }
158
+
159
+ export async function runRealSubagentSession(options: RealSessionRunOptions): Promise<RealSessionRun> {
160
+ const cwd = mkdtempSync(path.join(os.tmpdir(), "pi-real-session-cwd-"));
161
+ const home = mkdtempSync(path.join(os.tmpdir(), "pi-real-session-home-"));
162
+ const previousCwd = process.cwd();
163
+ const envSnapshot = new Map([
164
+ ["HOME", process.env.HOME],
165
+ ["USERPROFILE", process.env.USERPROFILE],
166
+ ["SELESAI_CODING_AGENT_DIR", process.env.SELESAI_CODING_AGENT_DIR],
167
+ ["SELESAI_SUBAGENT_EXTRA_AGENT_DIRS", process.env.SELESAI_SUBAGENT_EXTRA_AGENT_DIRS],
168
+ ["SELESAI_SUBAGENT_CHILD", process.env.SELESAI_SUBAGENT_CHILD],
169
+ ["SELESAI_SUBAGENT_FANOUT_CHILD", process.env.SELESAI_SUBAGENT_FANOUT_CHILD],
170
+ ["SELESAI_SUBAGENT_DEPTH", process.env.SELESAI_SUBAGENT_DEPTH],
171
+ ["SELESAI_SUBAGENT_MAX_DEPTH", process.env.SELESAI_SUBAGENT_MAX_DEPTH],
172
+ ["SELESAI_SUBAGENT_PARENT_SESSION", process.env.SELESAI_SUBAGENT_PARENT_SESSION],
173
+ ["SELESAI_SUBAGENT_PI_BINARY", process.env.SELESAI_SUBAGENT_PI_BINARY],
174
+ ["SELESAI_SUBAGENTS_PI_CODING_AGENT_PACKAGE_ROOT", process.env.SELESAI_SUBAGENTS_PI_CODING_AGENT_PACKAGE_ROOT],
175
+ ]);
176
+ const uninstallChildPi = installChildPiShim(options.childText);
177
+ let session: AgentSession | undefined;
178
+ let faux: ReturnType<typeof registerFauxProvider> | undefined;
179
+ let disposed = false;
180
+
181
+ const dispose = async () => {
182
+ if (disposed) return;
183
+ disposed = true;
184
+ try {
185
+ await session?.extensionRunner.emit({ type: "session_shutdown", reason: "quit" });
186
+ } catch {}
187
+ try {
188
+ session?.dispose();
189
+ } catch {}
190
+ faux?.unregister();
191
+ uninstallChildPi();
192
+ restoreEnv(envSnapshot);
193
+ try {
194
+ process.chdir(previousCwd);
195
+ } catch {}
196
+ rmSync(cwd, { recursive: true, force: true });
197
+ rmSync(home, { recursive: true, force: true });
198
+ };
199
+
200
+ try {
201
+ process.chdir(cwd);
202
+ process.env.HOME = home;
203
+ process.env.USERPROFILE = home;
204
+ process.env.SELESAI_CODING_AGENT_DIR = home;
205
+ delete process.env.SELESAI_SUBAGENT_EXTRA_AGENT_DIRS;
206
+ delete process.env.SELESAI_SUBAGENT_CHILD;
207
+ delete process.env.SELESAI_SUBAGENT_FANOUT_CHILD;
208
+ delete process.env.SELESAI_SUBAGENT_DEPTH;
209
+ delete process.env.SELESAI_SUBAGENT_MAX_DEPTH;
210
+ delete process.env.SELESAI_SUBAGENT_PARENT_SESSION;
211
+ delete process.env.SELESAI_SUBAGENT_PI_BINARY;
212
+ delete process.env.SELESAI_SUBAGENTS_PI_CODING_AGENT_PACKAGE_ROOT;
213
+
214
+ faux = registerFauxProvider({
215
+ provider: "faux-e2e-parent",
216
+ models: [{ id: "parent", contextWindow: 200_000 }],
217
+ });
218
+ const model = faux.getModel();
219
+ const respond = options.respond;
220
+ const responseFactory: FauxResponseStep = async (context, _streamOptions, state) => toAssistantMessage(await respond(context, state));
221
+ faux.setResponses(Array.from({ length: 8 }, () => responseFactory));
222
+
223
+ const settingsManager = SettingsManager.inMemory({
224
+ compaction: { enabled: false },
225
+ retry: { enabled: false },
226
+ });
227
+ const loader = new DefaultResourceLoader({
228
+ cwd,
229
+ agentDir: home,
230
+ settingsManager,
231
+ additionalExtensionPaths: [EXTENSION_PATH],
232
+ noSkills: true,
233
+ noPromptTemplates: true,
234
+ noThemes: true,
235
+ noContextFiles: true,
236
+ systemPrompt: "You are an E2E parent. Delegate with the subagent tool, then report the tool result.",
237
+ });
238
+ await loader.reload();
239
+
240
+ const created = await createAgentSession({
241
+ cwd,
242
+ agentDir: home,
243
+ model,
244
+ modelRegistry: createModelRegistry(model) as unknown as ModelRegistry,
245
+ resourceLoader: loader,
246
+ sessionManager: SessionManager.create(cwd, path.join(home, "sessions")),
247
+ settingsManager,
248
+ });
249
+ session = created.session;
250
+ session.setSessionName("real-session-e2e-parent");
251
+
252
+ let responseText = "";
253
+ const unsubscribe = session.subscribe((event: AgentSessionEvent) => {
254
+ if (event.type === "message_start") responseText = "";
255
+ if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
256
+ responseText += event.assistantMessageEvent.delta;
257
+ }
258
+ });
259
+
260
+ await session.bindExtensions({});
261
+ const timeoutMs = options.timeoutMs ?? 30_000;
262
+ let timer: ReturnType<typeof setTimeout> | undefined;
263
+ try {
264
+ await Promise.race([
265
+ session.prompt(options.prompt, { expandPromptTemplates: false }),
266
+ new Promise<never>((_, reject) => {
267
+ timer = setTimeout(() => reject(new Error(`real-session E2E timed out after ${timeoutMs}ms`)), timeoutMs);
268
+ }),
269
+ ]);
270
+ } finally {
271
+ if (timer) clearTimeout(timer);
272
+ unsubscribe();
273
+ }
274
+
275
+ return {
276
+ responseText: responseText.trim() || session.getLastAssistantText()?.trim() || "",
277
+ parentSession: session,
278
+ modelCalls: faux.state.callCount,
279
+ dispose,
280
+ };
281
+ } catch (error) {
282
+ await dispose();
283
+ throw error;
284
+ }
285
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Register the .js → .ts loader hook for integration tests.
3
+ *
4
+ * Usage: node --experimental-transform-types --import ./test/support/register-loader.mjs --test test/integration/*.test.ts
5
+ *
6
+ * Handles two issues:
7
+ * 1. Source files use .js import extensions (TypeScript ESM convention) but
8
+ * files on disk are .ts — the loader rewrites .js → .ts at resolve time.
9
+ * 2. Some source files use TypeScript parameter properties (constructor(private x: T))
10
+ * which require --experimental-transform-types (not just strip-types).
11
+ */
12
+
13
+ import { register } from "node:module";
14
+
15
+ register(new URL("./ts-loader.mjs", import.meta.url));
@@ -0,0 +1,108 @@
1
+ // ESM loader hook: rewrite .js imports to .ts when the .js file doesn't exist
2
+ // but a .ts file does. This bridges the gap between source-level .js extension
3
+ // imports and the actual .ts files on disk.
4
+
5
+ import * as fs from "node:fs";
6
+ import * as path from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+
9
+ const renderPiCodingAgentShim = `
10
+ export function getMarkdownTheme() { return {}; }
11
+ export function keyText(keybinding) { return keybinding === "app.tools.expand" ? "configured-expand-key" : ""; }
12
+ `;
13
+
14
+ const renderPiTuiShim = `
15
+ function wrapText(text, width) {
16
+ if (!width || width <= 0) return [text];
17
+ const lines = [];
18
+ for (const rawLine of String(text).split("\\n")) {
19
+ if (rawLine.length === 0) {
20
+ lines.push("");
21
+ continue;
22
+ }
23
+ for (let i = 0; i < rawLine.length; i += width) {
24
+ lines.push(rawLine.slice(i, i + width));
25
+ }
26
+ }
27
+ return lines;
28
+ }
29
+
30
+ export function visibleWidth(text) {
31
+ return String(text).length;
32
+ }
33
+
34
+ export class Text {
35
+ constructor(text) {
36
+ this.text = text;
37
+ }
38
+
39
+ render(width) {
40
+ return wrapText(this.text, width);
41
+ }
42
+ }
43
+
44
+ export class Spacer {
45
+ constructor(lines = 1) {
46
+ this.lines = lines;
47
+ }
48
+
49
+ render() {
50
+ return Array.from({ length: this.lines }, () => "");
51
+ }
52
+ }
53
+
54
+ export class Markdown {
55
+ constructor(text) {
56
+ this.text = text;
57
+ }
58
+
59
+ render(width) {
60
+ return wrapText(this.text, width);
61
+ }
62
+ }
63
+
64
+ export class Container {
65
+ constructor() {
66
+ this.children = [];
67
+ }
68
+
69
+ addChild(child) {
70
+ this.children.push(child);
71
+ }
72
+
73
+ render(width) {
74
+ return this.children.flatMap((child) => child.render(width));
75
+ }
76
+ }
77
+ `;
78
+
79
+ function asDataModule(source) {
80
+ return `data:text/javascript,${encodeURIComponent(source)}`;
81
+ }
82
+
83
+ export function resolve(specifier, context, nextResolve) {
84
+ if (context.parentURL?.endsWith("/render.ts")) {
85
+ if (specifier === "@selesai/code") {
86
+ return { url: asDataModule(renderPiCodingAgentShim), shortCircuit: true };
87
+ }
88
+ if (specifier === "@earendil-works/pi-tui") {
89
+ return { url: asDataModule(renderPiTuiShim), shortCircuit: true };
90
+ }
91
+ }
92
+
93
+ if (!specifier.startsWith(".") || !specifier.endsWith(".js")) {
94
+ return nextResolve(specifier, context);
95
+ }
96
+
97
+ const parentDir = context.parentURL
98
+ ? path.dirname(fileURLToPath(context.parentURL))
99
+ : process.cwd();
100
+ const jsPath = path.resolve(parentDir, specifier);
101
+ const tsPath = jsPath.replace(/\.js$/, ".ts");
102
+
103
+ if (!fs.existsSync(jsPath) && fs.existsSync(tsPath)) {
104
+ return nextResolve(specifier.replace(/\.js$/, ".ts"), context);
105
+ }
106
+
107
+ return nextResolve(specifier, context);
108
+ }