@agents-ensemble/core 0.21.0 → 0.22.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 (266) hide show
  1. package/assets/pi/pi-cli-env-wrapper.cmd +28 -0
  2. package/assets/pi/pi-cli-env-wrapper.sh +19 -0
  3. package/assets/pi/settings.json +4 -0
  4. package/dist/acp/acp-preset-bins.d.ts +3 -3
  5. package/dist/acp/acp-preset-bins.d.ts.map +1 -1
  6. package/dist/acp/acp-preset-bins.js +3 -3
  7. package/dist/acp/acp-preset-bins.js.map +1 -1
  8. package/dist/acp/index.d.ts +3 -1
  9. package/dist/acp/index.d.ts.map +1 -1
  10. package/dist/acp/index.js +2 -1
  11. package/dist/acp/index.js.map +1 -1
  12. package/dist/acp/pi-acp-wrapper.d.ts +3 -0
  13. package/dist/acp/pi-acp-wrapper.d.ts.map +1 -0
  14. package/dist/acp/pi-acp-wrapper.js +41 -0
  15. package/dist/acp/pi-acp-wrapper.js.map +1 -0
  16. package/dist/acp/pi-builtin-config.d.ts +10 -0
  17. package/dist/acp/pi-builtin-config.d.ts.map +1 -0
  18. package/dist/acp/pi-builtin-config.js +36 -0
  19. package/dist/acp/pi-builtin-config.js.map +1 -0
  20. package/dist/acp/pi-runtime.d.ts +23 -0
  21. package/dist/acp/pi-runtime.d.ts.map +1 -0
  22. package/dist/acp/pi-runtime.js +160 -0
  23. package/dist/acp/pi-runtime.js.map +1 -0
  24. package/dist/acp/pi-session-map.d.ts +36 -0
  25. package/dist/acp/pi-session-map.d.ts.map +1 -0
  26. package/dist/acp/pi-session-map.js +266 -0
  27. package/dist/acp/pi-session-map.js.map +1 -0
  28. package/dist/acp/resolve-acp-spawn.d.ts +9 -3
  29. package/dist/acp/resolve-acp-spawn.d.ts.map +1 -1
  30. package/dist/acp/resolve-acp-spawn.js +41 -9
  31. package/dist/acp/resolve-acp-spawn.js.map +1 -1
  32. package/dist/acp/resolve-bundled-acp-bin.d.ts +2 -2
  33. package/dist/acp/resolve-bundled-acp-bin.d.ts.map +1 -1
  34. package/dist/acp/resolve-bundled-acp-bin.js +4 -4
  35. package/dist/acp/resolve-bundled-acp-bin.js.map +1 -1
  36. package/dist/acp/validate-acp-preset-prerequisites.d.ts +1 -1
  37. package/dist/acp/validate-acp-preset-prerequisites.d.ts.map +1 -1
  38. package/dist/acp/validate-acp-preset-prerequisites.js +30 -10
  39. package/dist/acp/validate-acp-preset-prerequisites.js.map +1 -1
  40. package/dist/conductor/conductor-agent.d.ts +15 -3
  41. package/dist/conductor/conductor-agent.d.ts.map +1 -1
  42. package/dist/conductor/conductor-auth.d.ts +2 -3
  43. package/dist/conductor/conductor-auth.d.ts.map +1 -1
  44. package/dist/conductor/conductor-auth.js +5 -2
  45. package/dist/conductor/conductor-auth.js.map +1 -1
  46. package/dist/conductor/conductor-dispatch-lifecycle.d.ts +85 -0
  47. package/dist/conductor/conductor-dispatch-lifecycle.d.ts.map +1 -0
  48. package/dist/conductor/conductor-dispatch-lifecycle.js +190 -0
  49. package/dist/conductor/conductor-dispatch-lifecycle.js.map +1 -0
  50. package/dist/conductor/conductor-history.d.ts +6 -0
  51. package/dist/conductor/conductor-history.d.ts.map +1 -1
  52. package/dist/conductor/conductor-history.js +11 -6
  53. package/dist/conductor/conductor-history.js.map +1 -1
  54. package/dist/conductor/conductor-path-safety.d.ts +23 -0
  55. package/dist/conductor/conductor-path-safety.d.ts.map +1 -0
  56. package/dist/conductor/conductor-path-safety.js +319 -0
  57. package/dist/conductor/conductor-path-safety.js.map +1 -0
  58. package/dist/conductor/conductor-pi-auth.d.ts +57 -2
  59. package/dist/conductor/conductor-pi-auth.d.ts.map +1 -1
  60. package/dist/conductor/conductor-pi-auth.js +718 -40
  61. package/dist/conductor/conductor-pi-auth.js.map +1 -1
  62. package/dist/conductor/conductor-recovery-inbox.d.ts +73 -0
  63. package/dist/conductor/conductor-recovery-inbox.d.ts.map +1 -0
  64. package/dist/conductor/conductor-recovery-inbox.js +181 -0
  65. package/dist/conductor/conductor-recovery-inbox.js.map +1 -0
  66. package/dist/conductor/conductor-send-reconnect.d.ts +27 -5
  67. package/dist/conductor/conductor-send-reconnect.d.ts.map +1 -1
  68. package/dist/conductor/conductor-send-reconnect.js +88 -39
  69. package/dist/conductor/conductor-send-reconnect.js.map +1 -1
  70. package/dist/conductor/conductor-session-driver.d.ts +32 -7
  71. package/dist/conductor/conductor-session-driver.d.ts.map +1 -1
  72. package/dist/conductor/conductor-session-driver.js +474 -233
  73. package/dist/conductor/conductor-session-driver.js.map +1 -1
  74. package/dist/conductor/conductor-session.d.ts +2 -0
  75. package/dist/conductor/conductor-session.d.ts.map +1 -1
  76. package/dist/conductor/conductor-session.js +404 -156
  77. package/dist/conductor/conductor-session.js.map +1 -1
  78. package/dist/conductor/conductor-setup.d.ts +30 -0
  79. package/dist/conductor/conductor-setup.d.ts.map +1 -0
  80. package/dist/conductor/conductor-setup.js +243 -0
  81. package/dist/conductor/conductor-setup.js.map +1 -0
  82. package/dist/conductor/cursor-bootstrap-state.d.ts +9 -0
  83. package/dist/conductor/cursor-bootstrap-state.d.ts.map +1 -0
  84. package/dist/conductor/cursor-bootstrap-state.js +11 -0
  85. package/dist/conductor/cursor-bootstrap-state.js.map +1 -0
  86. package/dist/conductor/cursor-run-snapshots.d.ts +30 -0
  87. package/dist/conductor/cursor-run-snapshots.d.ts.map +1 -0
  88. package/dist/conductor/cursor-run-snapshots.js +120 -0
  89. package/dist/conductor/cursor-run-snapshots.js.map +1 -0
  90. package/dist/conductor/index.d.ts +15 -6
  91. package/dist/conductor/index.d.ts.map +1 -1
  92. package/dist/conductor/index.js +8 -4
  93. package/dist/conductor/index.js.map +1 -1
  94. package/dist/conductor/operator-command.d.ts +1 -1
  95. package/dist/conductor/operator-command.js +1 -1
  96. package/dist/conductor/operator-command.js.map +1 -1
  97. package/dist/conductor/operator-input-binding.d.ts +12 -0
  98. package/dist/conductor/operator-input-binding.d.ts.map +1 -1
  99. package/dist/conductor/pi-auth-recovery-snapshot-store.d.ts +4 -1
  100. package/dist/conductor/pi-auth-recovery-snapshot-store.d.ts.map +1 -1
  101. package/dist/conductor/pi-auth-recovery-snapshot-store.js +4 -1
  102. package/dist/conductor/pi-auth-recovery-snapshot-store.js.map +1 -1
  103. package/dist/conductor/pi-conductor-agent.d.ts +6 -3
  104. package/dist/conductor/pi-conductor-agent.d.ts.map +1 -1
  105. package/dist/conductor/pi-conductor-agent.js +272 -53
  106. package/dist/conductor/pi-conductor-agent.js.map +1 -1
  107. package/dist/conductor/pi-conductor-session-file.d.ts +13 -0
  108. package/dist/conductor/pi-conductor-session-file.d.ts.map +1 -0
  109. package/dist/conductor/pi-conductor-session-file.js +21 -0
  110. package/dist/conductor/pi-conductor-session-file.js.map +1 -0
  111. package/dist/conductor/pi-resource-loader.d.ts +6 -0
  112. package/dist/conductor/pi-resource-loader.d.ts.map +1 -1
  113. package/dist/conductor/pi-resource-loader.js +26 -12
  114. package/dist/conductor/pi-resource-loader.js.map +1 -1
  115. package/dist/conductor/pi-user-root-lock.d.ts +8 -0
  116. package/dist/conductor/pi-user-root-lock.d.ts.map +1 -0
  117. package/dist/conductor/pi-user-root-lock.js +53 -0
  118. package/dist/conductor/pi-user-root-lock.js.map +1 -0
  119. package/dist/conductor/recovery-inbox-command.d.ts +29 -0
  120. package/dist/conductor/recovery-inbox-command.d.ts.map +1 -0
  121. package/dist/conductor/recovery-inbox-command.js +248 -0
  122. package/dist/conductor/recovery-inbox-command.js.map +1 -0
  123. package/dist/conductor/session/events/session-event.d.ts +15 -2
  124. package/dist/conductor/session/events/session-event.d.ts.map +1 -1
  125. package/dist/conductor/session/events/session-log-event-groups.d.ts +1 -1
  126. package/dist/conductor/session/events/session-log-event-groups.d.ts.map +1 -1
  127. package/dist/conductor/session/events/session-log-event-groups.js +1 -0
  128. package/dist/conductor/session/events/session-log-event-groups.js.map +1 -1
  129. package/dist/conductor/session/events/session-log-event.d.ts +4 -0
  130. package/dist/conductor/session/events/session-log-event.d.ts.map +1 -1
  131. package/dist/conductor/session/format-session-event.js +25 -4
  132. package/dist/conductor/session/format-session-event.js.map +1 -1
  133. package/dist/conductor/session/select-dispatch-batch.d.ts +2 -0
  134. package/dist/conductor/session/select-dispatch-batch.d.ts.map +1 -1
  135. package/dist/conductor/session/select-dispatch-batch.js +31 -2
  136. package/dist/conductor/session/select-dispatch-batch.js.map +1 -1
  137. package/dist/conductor/session/session-event-queue.d.ts +5 -0
  138. package/dist/conductor/session/session-event-queue.d.ts.map +1 -1
  139. package/dist/conductor/session/session-event-queue.js +41 -4
  140. package/dist/conductor/session/session-event-queue.js.map +1 -1
  141. package/dist/conductor/session/session-event.d.ts +1 -1
  142. package/dist/conductor/session/session-event.d.ts.map +1 -1
  143. package/dist/conductor/session-policy.d.ts.map +1 -1
  144. package/dist/conductor/session-policy.js +4 -2
  145. package/dist/conductor/session-policy.js.map +1 -1
  146. package/dist/conductor/submit-operator-input.d.ts +3 -0
  147. package/dist/conductor/submit-operator-input.d.ts.map +1 -1
  148. package/dist/conductor/submit-operator-input.js +21 -0
  149. package/dist/conductor/submit-operator-input.js.map +1 -1
  150. package/dist/config/deep-merge.d.ts.map +1 -1
  151. package/dist/config/deep-merge.js +13 -3
  152. package/dist/config/deep-merge.js.map +1 -1
  153. package/dist/config/defaults.d.ts.map +1 -1
  154. package/dist/config/defaults.js +1 -1
  155. package/dist/config/defaults.js.map +1 -1
  156. package/dist/config/doctor-settings.d.ts +59 -0
  157. package/dist/config/doctor-settings.d.ts.map +1 -0
  158. package/dist/config/doctor-settings.js +343 -0
  159. package/dist/config/doctor-settings.js.map +1 -0
  160. package/dist/config/ensemble-config-digest.d.ts +2 -4
  161. package/dist/config/ensemble-config-digest.d.ts.map +1 -1
  162. package/dist/config/ensemble-config-digest.js +2 -4
  163. package/dist/config/ensemble-config-digest.js.map +1 -1
  164. package/dist/config/index.d.ts +3 -1
  165. package/dist/config/index.d.ts.map +1 -1
  166. package/dist/config/index.js +1 -0
  167. package/dist/config/index.js.map +1 -1
  168. package/dist/config/load-ensemble-config.d.ts.map +1 -1
  169. package/dist/config/load-ensemble-config.js +8 -1
  170. package/dist/config/load-ensemble-config.js.map +1 -1
  171. package/dist/config/parse-config.d.ts.map +1 -1
  172. package/dist/config/parse-config.js +34 -14
  173. package/dist/config/parse-config.js.map +1 -1
  174. package/dist/config/resolve-settings.d.ts +3 -1
  175. package/dist/config/resolve-settings.d.ts.map +1 -1
  176. package/dist/config/resolve-settings.js +7 -4
  177. package/dist/config/resolve-settings.js.map +1 -1
  178. package/dist/config/types.d.ts +14 -5
  179. package/dist/config/types.d.ts.map +1 -1
  180. package/dist/dispatch/attach-worker.js +1 -1
  181. package/dist/dispatch/attach-worker.js.map +1 -1
  182. package/dist/dispatch/worker-acp-session.d.ts +21 -3
  183. package/dist/dispatch/worker-acp-session.d.ts.map +1 -1
  184. package/dist/dispatch/worker-acp-session.js +50 -13
  185. package/dist/dispatch/worker-acp-session.js.map +1 -1
  186. package/dist/ensemble-paths.d.ts +19 -19
  187. package/dist/ensemble-paths.d.ts.map +1 -1
  188. package/dist/ensemble-paths.js +40 -22
  189. package/dist/ensemble-paths.js.map +1 -1
  190. package/dist/issue/resolve-issue-url.d.ts +4 -0
  191. package/dist/issue/resolve-issue-url.d.ts.map +1 -1
  192. package/dist/issue/resolve-issue-url.js +2 -2
  193. package/dist/issue/resolve-issue-url.js.map +1 -1
  194. package/dist/operator-runtime.d.ts +17 -4
  195. package/dist/operator-runtime.d.ts.map +1 -1
  196. package/dist/operator-runtime.js +12 -3
  197. package/dist/operator-runtime.js.map +1 -1
  198. package/dist/profile/types.d.ts +4 -0
  199. package/dist/profile/types.d.ts.map +1 -1
  200. package/dist/profile/types.js +29 -12
  201. package/dist/profile/types.js.map +1 -1
  202. package/dist/representation/escape-terminal-control-characters.d.ts +9 -0
  203. package/dist/representation/escape-terminal-control-characters.d.ts.map +1 -0
  204. package/dist/representation/escape-terminal-control-characters.js +34 -0
  205. package/dist/representation/escape-terminal-control-characters.js.map +1 -0
  206. package/dist/representation/index.d.ts +1 -0
  207. package/dist/representation/index.d.ts.map +1 -1
  208. package/dist/representation/index.js +1 -0
  209. package/dist/representation/index.js.map +1 -1
  210. package/dist/runtime/types.d.ts +9 -0
  211. package/dist/runtime/types.d.ts.map +1 -1
  212. package/dist/runtime/worker-runtime.d.ts +14 -1
  213. package/dist/runtime/worker-runtime.d.ts.map +1 -1
  214. package/dist/runtime/worker-runtime.js +197 -50
  215. package/dist/runtime/worker-runtime.js.map +1 -1
  216. package/dist/runtime/worker-session.d.ts +2 -1
  217. package/dist/runtime/worker-session.d.ts.map +1 -1
  218. package/dist/runtime/worker-session.js +1 -0
  219. package/dist/runtime/worker-session.js.map +1 -1
  220. package/dist/session/export-session-bundle.d.ts +77 -0
  221. package/dist/session/export-session-bundle.d.ts.map +1 -0
  222. package/dist/session/export-session-bundle.js +640 -0
  223. package/dist/session/export-session-bundle.js.map +1 -0
  224. package/dist/session/index.d.ts +4 -3
  225. package/dist/session/index.d.ts.map +1 -1
  226. package/dist/session/index.js +2 -1
  227. package/dist/session/index.js.map +1 -1
  228. package/dist/session/open-question-control.d.ts +9 -0
  229. package/dist/session/open-question-control.d.ts.map +1 -1
  230. package/dist/session/open-question-control.js +26 -1
  231. package/dist/session/open-question-control.js.map +1 -1
  232. package/dist/session/open-questions-store.d.ts +1 -1
  233. package/dist/session/open-questions-store.d.ts.map +1 -1
  234. package/dist/session/open-questions-store.js +3 -6
  235. package/dist/session/open-questions-store.js.map +1 -1
  236. package/dist/session/persisted-file.d.ts +5 -0
  237. package/dist/session/persisted-file.d.ts.map +1 -0
  238. package/dist/session/persisted-file.js +56 -0
  239. package/dist/session/persisted-file.js.map +1 -0
  240. package/dist/session/session-resume-store.d.ts +77 -10
  241. package/dist/session/session-resume-store.d.ts.map +1 -1
  242. package/dist/session/session-resume-store.js +346 -83
  243. package/dist/session/session-resume-store.js.map +1 -1
  244. package/dist/session/session-snapshot-paths.d.ts +7 -1
  245. package/dist/session/session-snapshot-paths.d.ts.map +1 -1
  246. package/dist/session/session-snapshot-paths.js +29 -26
  247. package/dist/session/session-snapshot-paths.js.map +1 -1
  248. package/dist/session/session-state-store.d.ts +2 -2
  249. package/dist/session/session-state-store.d.ts.map +1 -1
  250. package/dist/session/session-state-store.js +5 -16
  251. package/dist/session/session-state-store.js.map +1 -1
  252. package/dist/session/tool-call-snapshot-history.d.ts +12 -1
  253. package/dist/session/tool-call-snapshot-history.d.ts.map +1 -1
  254. package/dist/session/tool-call-snapshot-history.js +75 -70
  255. package/dist/session/tool-call-snapshot-history.js.map +1 -1
  256. package/dist/session/worker-session-history.d.ts +13 -1
  257. package/dist/session/worker-session-history.d.ts.map +1 -1
  258. package/dist/session/worker-session-history.js +171 -50
  259. package/dist/session/worker-session-history.js.map +1 -1
  260. package/dist/workspace/index.d.ts +1 -1
  261. package/dist/workspace/index.d.ts.map +1 -1
  262. package/dist/worktree/worktree.d.ts +6 -1
  263. package/dist/worktree/worktree.d.ts.map +1 -1
  264. package/dist/worktree/worktree.js +123 -2
  265. package/dist/worktree/worktree.js.map +1 -1
  266. package/package.json +9 -4
@@ -1,21 +1,19 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import { constants } from 'node:fs';
3
- import { chmod, copyFile, mkdir } from 'node:fs/promises';
4
- import { basename, join } from 'node:path';
2
+ import { chmod, mkdir } from 'node:fs/promises';
3
+ import { basename } from 'node:path';
5
4
  import { getModel, getModels, getProviders, } from '@earendil-works/pi-ai/compat';
6
- import { getSupportedThinkingLevels, } from '@earendil-works/pi-ai';
5
+ import { createAssistantMessageEventStream, getSupportedThinkingLevels, } from '@earendil-works/pi-ai';
7
6
  import { ModelsError } from '@earendil-works/pi-ai/models';
8
7
  import { createAgentSession, createCodemodeExtension, createMcpExtension, createToolSearchExtension, DefaultResourceLoader, SessionManager, SettingsManager, } from '@earendil-works/pi-coding-agent';
9
8
  import { resolveEnsemblePaths } from '../ensemble-paths.js';
10
- import { createPiConductorAuthContext, resolvePiConductorApiKey, } from './conductor-pi-auth.js';
9
+ import { createPiConductorAuthContext, hasPiConductorCommandCredential, preparePiConductorCommandAuthForRequest, resolvePiConductorApiKey, } from './conductor-pi-auth.js';
11
10
  import { markConductorAgentFactoryCleanupIncomplete } from './conductor-agent-factory-cleanup.js';
12
11
  import { toPiCodingAgentTools } from './conductor-tool-pi-adapter.js';
13
12
  import { getPiHeadlessSettingsManagerOverrides, getPiHeadlessSetting, getPiSkillsDiscoverySettingsOverrides, } from './pi-headless-settings.js';
14
13
  import { expandPiResourcePrompt, loadPiResources, } from './pi-resource-loader.js';
15
- /** Project-local Pi transcript storage used by the conductor backend. */
16
- export const PI_SESSION_ROOT = '.ensemble/pi/sessions';
17
14
  export const PI_CONDUCTOR_MESSAGE_HISTORY_ENTRY_TYPE = 'agents-ensemble/conductor-message-history/v1';
18
15
  const PI_MCP_STARTUP_OBSERVATION_MS = 2_000;
16
+ const PI_COMMAND_RESOLUTION_ERROR_MESSAGE = 'API key auth failed for provider: failed to resolve API key for Pi provider from the configured command.';
19
17
  const PI_THINKING_LEVELS = [
20
18
  'off',
21
19
  'minimal',
@@ -31,7 +29,7 @@ export async function resolvePiModelConfig(options) {
31
29
  const authContext = await createPiConductorAuthContext(options, resources);
32
30
  return resolvePiModelConfigFromResources(options, resources, authContext);
33
31
  }
34
- async function resolvePiModelConfigFromResources(options, resources, authContext) {
32
+ async function resolvePiModelConfigFromResources(options, resources, authContext, commandAuthRequestGate) {
35
33
  const env = options.env ?? process.env;
36
34
  const settings = resources.settings;
37
35
  const selection = resolvePiModelSelection(options.modelId, settings, resources.models);
@@ -41,19 +39,36 @@ async function resolvePiModelConfigFromResources(options, resources, authContext
41
39
  const registryModel = projectDefinesModel
42
40
  ? undefined
43
41
  : authContext.modelRegistry.find(selection.provider, selection.modelId);
44
- const apiKey = await resolvePiConductorApiKey({
45
- modelRuntime: authContext.modelRuntime,
46
- resources,
47
- provider: selection.provider,
48
- explicitApiKey: options.apiKey,
49
- env,
50
- });
42
+ const commandAuthConfigured = options.apiKey === undefined &&
43
+ hasPiConductorCommandCredential(resources, selection.provider, env);
44
+ const apiKey = commandAuthConfigured
45
+ ? undefined
46
+ : await resolvePiConductorApiKey({
47
+ modelRuntime: authContext.modelRuntime,
48
+ resources,
49
+ provider: selection.provider,
50
+ explicitApiKey: options.apiKey,
51
+ env,
52
+ });
51
53
  const builtInModel = getModel(selection.provider, selection.modelId);
52
54
  const model = resolvePiModel(selection.provider, selection.modelId, providerConfig, registryModel ?? builtInModel, registryModel);
53
55
  if (!model) {
54
56
  throw new Error(`Pi conductor model "${selection.provider}/${selection.modelId}" was not found in pi-ai or models.json. ` +
55
57
  'Set defaultProvider/defaultModel in Pi settings.json or pass a built-in provider/model id.');
56
58
  }
59
+ if (commandAuthConfigured) {
60
+ // Pi calls hasConfiguredAuth() during prompt setup and getAuth() just
61
+ // before sending a provider request. Keep the command out of setup and
62
+ // defer registration/resolution until the latter, while preserving Pi's
63
+ // own auth resolver and precedence.
64
+ installPiCommandAuthRequestGate({
65
+ modelRuntime: authContext.modelRuntime,
66
+ resources,
67
+ provider: selection.provider,
68
+ env,
69
+ gate: commandAuthRequestGate ?? { active: false },
70
+ });
71
+ }
57
72
  return {
58
73
  provider: selection.provider,
59
74
  modelId: selection.modelId,
@@ -73,14 +88,16 @@ export class PiConductorAgent {
73
88
  modelId;
74
89
  mcpStartupState;
75
90
  mcpLifecycleState;
91
+ commandAuthRequestGate;
76
92
  piSkills;
93
+ piPromptResources;
77
94
  onStreamText;
78
95
  onContextCompaction;
79
96
  unsubscribe;
80
97
  activeSend;
81
98
  closed = false;
82
99
  closePromise;
83
- constructor(session, resources, settingsManager, headlessSettingsOverrides, systemPromptRef, agentId, modelId, mcpStartupState, mcpLifecycleState, piSkills, onStreamText, onContextCompaction) {
100
+ constructor(session, resources, settingsManager, headlessSettingsOverrides, systemPromptRef, agentId, modelId, mcpStartupState, mcpLifecycleState, commandAuthRequestGate, piSkills, piPromptResources, onStreamText, onContextCompaction) {
84
101
  this.session = session;
85
102
  this.resources = resources;
86
103
  this.settingsManager = settingsManager;
@@ -90,7 +107,9 @@ export class PiConductorAgent {
90
107
  this.modelId = modelId;
91
108
  this.mcpStartupState = mcpStartupState;
92
109
  this.mcpLifecycleState = mcpLifecycleState;
110
+ this.commandAuthRequestGate = commandAuthRequestGate;
93
111
  this.piSkills = piSkills;
112
+ this.piPromptResources = piPromptResources;
94
113
  this.onStreamText = onStreamText;
95
114
  this.onContextCompaction = onContextCompaction;
96
115
  this.unsubscribe = this.session.subscribe((event) => this.handleEvent(event));
@@ -135,9 +154,11 @@ export class PiConductorAgent {
135
154
  };
136
155
  this.activeSend = state;
137
156
  try {
138
- await this.session.prompt(expandPiResourcePrompt(prompt, this.resources, {
139
- expandSkills: !this.piSkills,
140
- }), {
157
+ await this.session.prompt(this.piPromptResources
158
+ ? expandPiResourcePrompt(prompt, this.resources, {
159
+ expandSkills: !this.piSkills,
160
+ })
161
+ : prompt, {
141
162
  expandPromptTemplates: false,
142
163
  source: 'rpc',
143
164
  });
@@ -147,7 +168,7 @@ export class PiConductorAgent {
147
168
  runId,
148
169
  status: 'error',
149
170
  error: {
150
- message: errorMessage(error),
171
+ message: piConductorErrorMessage(error),
151
172
  ...(error instanceof ModelsError ? { code: error.code } : {}),
152
173
  },
153
174
  modelId: this.modelId,
@@ -164,6 +185,7 @@ export class PiConductorAgent {
164
185
  modelId: this.modelId,
165
186
  };
166
187
  }
188
+ sanitizePiAssistantMessageInPlace(finalMessage);
167
189
  const status = statusFromStopReason(finalMessage.stopReason);
168
190
  const result = assistantText(finalMessage) || undefined;
169
191
  if (status === 'finished' &&
@@ -323,6 +345,15 @@ export class PiConductorAgent {
323
345
  }
324
346
  handleEvent(event) {
325
347
  const activeSend = this.activeSend;
348
+ if ((event.type === 'message_start' ||
349
+ event.type === 'message_update' ||
350
+ event.type === 'message_end') &&
351
+ event.message.role === 'assistant') {
352
+ // AgentSession emits public listeners before appending message_end to the
353
+ // Pi transcript. Mutating this message keeps the event and persisted entry
354
+ // safe if a failure came from a nonstandard Pi stream path.
355
+ sanitizePiAssistantMessageInPlace(event.message);
356
+ }
326
357
  if (event.type === 'compaction_end' &&
327
358
  event.result != null &&
328
359
  !event.aborted &&
@@ -344,6 +375,12 @@ export class PiConductorAgent {
344
375
  }
345
376
  return;
346
377
  }
378
+ if (event.type === 'agent_end') {
379
+ for (const message of event.messages) {
380
+ if (message.role === 'assistant')
381
+ sanitizePiAssistantMessageInPlace(message);
382
+ }
383
+ }
347
384
  if (event.type === 'agent_end' && activeSend) {
348
385
  activeSend.finalMessage = lastAssistantMessage(event.messages);
349
386
  for (const message of event.messages) {
@@ -354,6 +391,135 @@ export class PiConductorAgent {
354
391
  }
355
392
  }
356
393
  }
394
+ function installPiCommandAuthRequestGate(input) {
395
+ const originalHasConfiguredAuth = input.modelRuntime.hasConfiguredAuth.bind(input.modelRuntime);
396
+ input.modelRuntime.hasConfiguredAuth = (provider) => provider === input.provider || originalHasConfiguredAuth(provider);
397
+ const originalGetAuth = input.modelRuntime.getAuth.bind(input.modelRuntime);
398
+ input.modelRuntime.getAuth = (async (providerOrModel, overrides) => {
399
+ const provider = typeof providerOrModel === 'string'
400
+ ? providerOrModel
401
+ : providerOrModel.provider;
402
+ if (provider !== input.provider ||
403
+ !input.gate.active ||
404
+ overrides?.apiKey !== undefined) {
405
+ return originalGetAuth(providerOrModel, overrides);
406
+ }
407
+ await preparePiConductorCommandAuthForRequest({
408
+ modelRuntime: input.modelRuntime,
409
+ resources: input.resources,
410
+ provider: input.provider,
411
+ env: input.env,
412
+ });
413
+ try {
414
+ return await originalGetAuth(providerOrModel, overrides);
415
+ }
416
+ catch (error) {
417
+ if (isPiCommandResolutionError(error)) {
418
+ throw new Error(PI_COMMAND_RESOLUTION_ERROR_MESSAGE);
419
+ }
420
+ throw error;
421
+ }
422
+ });
423
+ const originalStreamSimple = input.modelRuntime.streamSimple.bind(input.modelRuntime);
424
+ input.modelRuntime.streamSimple = ((model, context, options) => {
425
+ if (model.provider !== input.provider) {
426
+ return originalStreamSimple(model, context, options);
427
+ }
428
+ // Pi 1.2.0's lazy stream starts its setup synchronously. That setup calls
429
+ // prepareRequest(), which starts getAuth() before streamSimple() returns.
430
+ // Keep the command gate active only for that setup call: prompt hooks and
431
+ // the provider response stream itself must not trigger command resolution
432
+ // through an unrelated getAuth() lookup.
433
+ const output = createAssistantMessageEventStream();
434
+ void (async () => {
435
+ try {
436
+ const wasActive = input.gate.active;
437
+ let response;
438
+ input.gate.active = true;
439
+ try {
440
+ response = originalStreamSimple(model, context, options);
441
+ }
442
+ finally {
443
+ input.gate.active = wasActive;
444
+ }
445
+ let terminalEventSeen = false;
446
+ for await (const event of response) {
447
+ if (event.type === 'done' || event.type === 'error') {
448
+ terminalEventSeen = true;
449
+ }
450
+ output.push(sanitizePiAssistantEvent(event));
451
+ }
452
+ if (!terminalEventSeen) {
453
+ output.end(sanitizePiAssistantMessage(await response.result()));
454
+ }
455
+ }
456
+ catch (error) {
457
+ output.push({
458
+ type: 'error',
459
+ reason: 'error',
460
+ error: createPiRequestFailureMessage(model, piConductorErrorMessage(error)),
461
+ });
462
+ }
463
+ })();
464
+ return output;
465
+ });
466
+ }
467
+ function sanitizePiAssistantEvent(event) {
468
+ if (event.type === 'done') {
469
+ return { ...event, message: sanitizePiAssistantMessage(event.message) };
470
+ }
471
+ if (event.type === 'error') {
472
+ return { ...event, error: sanitizePiAssistantMessage(event.error) };
473
+ }
474
+ if ('partial' in event) {
475
+ return { ...event, partial: sanitizePiAssistantMessage(event.partial) };
476
+ }
477
+ return event;
478
+ }
479
+ function sanitizePiAssistantMessage(message) {
480
+ if (!message.errorMessage)
481
+ return message;
482
+ const safeErrorMessage = sanitizePiCommandResolutionMessage(message.errorMessage);
483
+ return safeErrorMessage === message.errorMessage
484
+ ? message
485
+ : { ...message, errorMessage: safeErrorMessage };
486
+ }
487
+ function sanitizePiAssistantMessageInPlace(message) {
488
+ if (!message.errorMessage)
489
+ return;
490
+ message.errorMessage = sanitizePiCommandResolutionMessage(message.errorMessage);
491
+ }
492
+ function sanitizePiCommandResolutionMessage(message) {
493
+ return isPiCommandResolutionError(message)
494
+ ? PI_COMMAND_RESOLUTION_ERROR_MESSAGE
495
+ : message;
496
+ }
497
+ function createPiRequestFailureMessage(model, errorMessage) {
498
+ return {
499
+ role: 'assistant',
500
+ content: [],
501
+ api: model.api,
502
+ provider: model.provider,
503
+ model: model.id,
504
+ usage: {
505
+ input: 0,
506
+ output: 0,
507
+ cacheRead: 0,
508
+ cacheWrite: 0,
509
+ totalTokens: 0,
510
+ cost: {
511
+ input: 0,
512
+ output: 0,
513
+ cacheRead: 0,
514
+ cacheWrite: 0,
515
+ total: 0,
516
+ },
517
+ },
518
+ stopReason: 'error',
519
+ errorMessage: sanitizePiCommandResolutionMessage(errorMessage),
520
+ timestamp: Date.now(),
521
+ };
522
+ }
357
523
  function appendPiConductorMessageHistory(sessionManager, input) {
358
524
  const branch = sessionManager.getBranch();
359
525
  let assistantIndex = -1;
@@ -430,55 +596,62 @@ function resolvePiConductorThinkingLevel(model, provider, modelId, value) {
430
596
  }
431
597
  return reasoning;
432
598
  }
599
+ function resolvePiConductorSessionDir(options) {
600
+ if (options.sessionDir !== undefined)
601
+ return options.sessionDir;
602
+ if (!options.issueUrl) {
603
+ throw new Error('Pi conductor transcript storage requires issueUrl or an explicit sessionDir.');
604
+ }
605
+ return resolveEnsemblePaths({ repoRoot: options.cwd })
606
+ .canonicalPiSessionsDir(options.issueUrl);
607
+ }
433
608
  async function createPiConductorSession(agentId, options, reason) {
609
+ const sessionDir = resolvePiConductorSessionDir(options);
610
+ const piInternalExtensions = options.piInternalExtensions !== false;
434
611
  const piMcpResolution = { cwd: options.cwd };
435
612
  const mcpLifecycleState = {
436
- enabled: Object.keys(options.mcpServers ?? {}).length > 0,
613
+ enabled: piInternalExtensions && Object.keys(options.mcpServers ?? {}).length > 0,
437
614
  };
438
615
  // Validate the harness-resolved map before creating the SDK session so an
439
616
  // unsupported transport fails with an actionable Pi-specific error.
440
- if (options.mcpServers)
617
+ if (piInternalExtensions && options.mcpServers) {
441
618
  loadPiMcpConfig(options.mcpServers, piMcpResolution);
619
+ }
442
620
  const piSkills = options.piSkills !== false;
621
+ const piPromptResources = options.piPromptResources !== false;
443
622
  const resources = await loadPiResources({
444
623
  ...options,
445
624
  loadExtensions: options.piExtensions !== false,
625
+ loadPromptResources: piPromptResources,
446
626
  loadSkillContents: !piSkills,
447
627
  });
448
628
  const headlessSettingsOverrides = getPiHeadlessSettingsManagerOverrides(resources.settings);
449
629
  const authContext = await createPiConductorAuthContext(options, resources);
450
- const resolved = await resolvePiModelConfigFromResources(options, resources, authContext);
630
+ const commandAuthRequestGate = { active: false };
631
+ const resolved = await resolvePiModelConfigFromResources(options, resources, authContext, commandAuthRequestGate);
451
632
  const thinkingLevel = options.reasoning !== undefined
452
633
  ? resolvePiConductorThinkingLevel(resolved.model, resolved.provider, resolved.modelId, options.reasoning)
453
634
  : undefined;
454
635
  if (options.apiKey !== undefined) {
455
636
  await authContext.modelRuntime.setRuntimeApiKey(resolved.provider, options.apiKey);
456
637
  }
457
- const sessionDir = options.sessionDir ??
458
- resolveEnsemblePaths({ repoRoot: options.cwd }).piSessionsDir;
459
- const legacySessionDir = options.legacySessionDir ?? join(options.cwd, PI_SESSION_ROOT);
460
638
  await mkdir(sessionDir, { recursive: true, mode: 0o700 });
461
639
  await chmod(sessionDir, 0o700);
462
640
  let sessionManager;
463
641
  let previousSessionFile;
464
642
  if (reason === 'resume') {
465
- previousSessionFile = SessionManager.findById(options.cwd, agentId, sessionDir);
466
- if (!previousSessionFile && legacySessionDir !== sessionDir) {
467
- const legacyFile = SessionManager.findById(options.cwd, agentId, legacySessionDir);
468
- if (legacyFile) {
469
- await mkdir(sessionDir, { recursive: true, mode: 0o700 });
470
- const migratedFile = join(sessionDir, basename(legacyFile));
471
- try {
472
- await copyFile(legacyFile, migratedFile, constants.COPYFILE_EXCL);
473
- }
474
- catch (error) {
475
- if (!isAlreadyExists(error))
476
- throw error;
477
- }
478
- await chmod(migratedFile, 0o600);
479
- previousSessionFile = migratedFile;
643
+ const resumeCwds = [
644
+ ...new Set([options.cwd, ...(options.resumeCwds ?? [])]),
645
+ ];
646
+ const findSessionFile = (directory) => {
647
+ for (const cwd of resumeCwds) {
648
+ const sessionFile = SessionManager.findById(cwd, agentId, directory);
649
+ if (sessionFile)
650
+ return sessionFile;
480
651
  }
481
- }
652
+ return undefined;
653
+ };
654
+ previousSessionFile = findSessionFile(sessionDir);
482
655
  if (!previousSessionFile) {
483
656
  throw new Error(`Pi session not found for resume (sessionId=${agentId}, cwd=${options.cwd}, ` +
484
657
  `sessionsRoot=${sessionDir})`);
@@ -496,7 +669,7 @@ async function createPiConductorSession(agentId, options, reason) {
496
669
  // (.ensemble/pi), so use an in-memory manager and populate it only through
497
670
  // the code-level headless settings allowlist below.
498
671
  const settingsManager = SettingsManager.inMemory();
499
- if (piSkills) {
672
+ if (piSkills && piPromptResources) {
500
673
  applyPiHeadlessSettings(settingsManager, getPiSkillsDiscoverySettingsOverrides(resources.settings));
501
674
  settingsManager.setProjectTrusted(true);
502
675
  }
@@ -505,18 +678,22 @@ async function createPiConductorSession(agentId, options, reason) {
505
678
  agentDir: authContext.agentDir,
506
679
  settingsManager,
507
680
  noExtensions: true,
508
- noSkills: !piSkills,
509
- additionalSkillPaths: piSkills ? resources.skillPaths : [],
681
+ noSkills: !piSkills || !piPromptResources,
682
+ additionalSkillPaths: piSkills && piPromptResources ? resources.skillPaths : [],
510
683
  noPromptTemplates: true,
511
684
  noThemes: true,
512
685
  noContextFiles: true,
513
686
  systemPrompt: systemPromptRef.value,
514
687
  extensionFactories: [
515
- createMcpExtension({
516
- loadConfig: () => loadPiMcpConfig(mcpLifecycleState.enabled ? options.mcpServers : undefined, piMcpResolution),
517
- }),
518
- createCodemodeExtension({ mode: 'on' }),
519
- createToolSearchExtension(),
688
+ ...(piInternalExtensions
689
+ ? [
690
+ createMcpExtension({
691
+ loadConfig: () => loadPiMcpConfig(mcpLifecycleState.enabled ? options.mcpServers : undefined, piMcpResolution),
692
+ }),
693
+ createCodemodeExtension({ mode: 'on' }),
694
+ createToolSearchExtension(),
695
+ ]
696
+ : []),
520
697
  createPiSystemPromptExtension(systemPromptRef),
521
698
  ],
522
699
  });
@@ -555,11 +732,11 @@ async function createPiConductorSession(agentId, options, reason) {
555
732
  }
556
733
  },
557
734
  });
558
- await observePiMcpStartup(session, options.mcpServers, mcpStartupState);
735
+ await observePiMcpStartup(session, piInternalExtensions ? options.mcpServers : undefined, mcpStartupState);
559
736
  if (mcpStartupState.error) {
560
737
  throw new Error(mcpStartupState.error);
561
738
  }
562
- return new PiConductorAgent(session, resources, settingsManager, headlessSettingsOverrides, systemPromptRef, agentId, resolved.modelId, mcpStartupState, mcpLifecycleState, piSkills, options.onStreamText, options.onContextCompaction);
739
+ return new PiConductorAgent(session, resources, settingsManager, headlessSettingsOverrides, systemPromptRef, agentId, resolved.modelId, mcpStartupState, mcpLifecycleState, commandAuthRequestGate, piSkills, piPromptResources, options.onStreamText, options.onContextCompaction);
563
740
  }
564
741
  catch (error) {
565
742
  if (!session)
@@ -969,7 +1146,49 @@ function addConductorTokenUsage(current, usage) {
969
1146
  function isRecord(value) {
970
1147
  return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
971
1148
  }
972
- function errorMessage(error) {
1149
+ function piConductorErrorMessage(error) {
1150
+ if (isPiCommandResolutionError(error)) {
1151
+ return PI_COMMAND_RESOLUTION_ERROR_MESSAGE;
1152
+ }
973
1153
  return error instanceof Error ? error.message : String(error);
974
1154
  }
1155
+ function isPiCommandResolutionError(error) {
1156
+ if (typeof error === 'string' &&
1157
+ /failed to resolve api key|from shell command/i.test(error)) {
1158
+ return true;
1159
+ }
1160
+ if (!error || typeof error !== 'object')
1161
+ return false;
1162
+ if (error instanceof ModelsError && error.code !== 'auth')
1163
+ return false;
1164
+ const isAuthModelsError = error instanceof ModelsError && error.code === 'auth';
1165
+ const errorMessage = 'message' in error && typeof error.message === 'string' ? error.message : '';
1166
+ if (/failed to resolve api key|from shell command/i.test(errorMessage)) {
1167
+ return true;
1168
+ }
1169
+ const topLevelDetails = error;
1170
+ if (isAuthModelsError &&
1171
+ (topLevelDetails.stdout !== undefined || topLevelDetails.stderr !== undefined)) {
1172
+ return true;
1173
+ }
1174
+ const pending = [error.cause];
1175
+ const seen = new Set();
1176
+ while (pending.length > 0) {
1177
+ const cause = pending.pop();
1178
+ if (!cause || typeof cause !== 'object' || seen.has(cause))
1179
+ continue;
1180
+ seen.add(cause);
1181
+ const details = cause;
1182
+ if (isAuthModelsError &&
1183
+ (details.stdout !== undefined || details.stderr !== undefined)) {
1184
+ return true;
1185
+ }
1186
+ if (typeof details.message === 'string' &&
1187
+ /failed to resolve api key|from shell command/i.test(details.message)) {
1188
+ return true;
1189
+ }
1190
+ pending.push(details.cause);
1191
+ }
1192
+ return false;
1193
+ }
975
1194
  //# sourceMappingURL=pi-conductor-agent.js.map