@timurproko/a1 0.1.8-dev.157 → 0.1.8-dev.214

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 (171) hide show
  1. package/README.md +32 -0
  2. package/bin/cli.js +2 -2
  3. package/bin/sync-pi-tui-proxy.js +1 -1
  4. package/bin/ui.js +18 -3
  5. package/dist/cli/dispatch.js +3 -3
  6. package/dist/cli/packages.js +1 -1
  7. package/dist/cli/version-stats.js +3 -3
  8. package/dist/composition/owned-ui.d.ts +4 -1
  9. package/dist/composition/owned-ui.js +9 -6
  10. package/dist/composition/settings-route-host.js +6 -4
  11. package/dist/contracts/agent-engine/capability-ports.d.ts +2 -2
  12. package/dist/contracts/agent-engine/domain-validation.js +22 -4
  13. package/dist/contracts/agent-engine/domain.d.ts +17 -0
  14. package/dist/contracts/owned-ui/model.d.ts +8 -0
  15. package/dist/contracts/owned-ui/validation.js +11 -0
  16. package/dist/features/launch/development-launch.js +1 -1
  17. package/dist/features/owned-ui/customization.d.ts +2 -0
  18. package/dist/features/owned-ui/customization.js +2 -0
  19. package/dist/features/owned-ui/diagnostics.d.ts +1 -0
  20. package/dist/features/owned-ui/diagnostics.js +1 -0
  21. package/dist/features/owned-ui/index.d.ts +1 -0
  22. package/dist/features/owned-ui/index.js +1 -0
  23. package/dist/features/owned-ui/project-trust-prompt.d.ts +25 -0
  24. package/dist/features/owned-ui/project-trust-prompt.js +149 -0
  25. package/dist/features/owned-ui/settings-app.js +69 -44
  26. package/dist/features/workspace/reducer.d.ts +1 -0
  27. package/dist/features/workspace/reducer.js +1 -0
  28. package/dist/features/workspace/router.d.ts +1 -0
  29. package/dist/features/workspace/router.js +1 -0
  30. package/dist/features/workspace/store.d.ts +1 -0
  31. package/dist/features/workspace/store.js +1 -0
  32. package/dist/foundation/launch-guardian/main.js +1 -1
  33. package/dist/foundation/native-host-protocol/codec.d.ts +2 -0
  34. package/dist/foundation/native-host-protocol/codec.js +2 -0
  35. package/dist/foundation/process-containment/linux-process-inspector.d.ts +1 -0
  36. package/dist/foundation/process-containment/linux-process-inspector.js +1 -0
  37. package/dist/foundation/process-containment/native-guardian-containment.d.ts +1 -0
  38. package/dist/foundation/process-containment/native-guardian-containment.js +1 -0
  39. package/dist/foundation/process-containment/windows-process-inspector.d.ts +1 -0
  40. package/dist/foundation/process-containment/windows-process-inspector.js +1 -0
  41. package/dist/foundation/protocol/client.d.ts +1 -0
  42. package/dist/foundation/protocol/client.js +1 -0
  43. package/dist/foundation/protocol/messages.d.ts +1 -0
  44. package/dist/foundation/protocol/messages.js +1 -0
  45. package/dist/foundation/release/bootstrap.js +6 -6
  46. package/dist/foundation/release/cohort-state.d.ts +1 -0
  47. package/dist/foundation/release/cohort-state.js +1 -0
  48. package/dist/foundation/release/release-gc.js +1 -1
  49. package/dist/foundation/release/release.js +1 -1
  50. package/dist/foundation/release/update-transaction.d.ts +1 -0
  51. package/dist/foundation/release/update-transaction.js +1 -0
  52. package/dist/foundation/release/update.js +12 -12
  53. package/dist/foundation/storage/control-store.d.ts +1 -0
  54. package/dist/foundation/storage/control-store.js +1 -0
  55. package/dist/foundation/structured-agent-runtime/backpressure.d.ts +1 -0
  56. package/dist/foundation/structured-agent-runtime/backpressure.js +1 -0
  57. package/dist/foundation/structured-agent-runtime/commands.d.ts +1 -0
  58. package/dist/foundation/structured-agent-runtime/commands.js +1 -0
  59. package/dist/foundation/structured-agent-runtime/reconnection.d.ts +1 -0
  60. package/dist/foundation/structured-agent-runtime/reconnection.js +1 -0
  61. package/dist/foundation/structured-agent-runtime/state.d.ts +1 -0
  62. package/dist/foundation/structured-agent-runtime/state.js +1 -0
  63. package/dist/foundation/supervision/main.js +1 -1
  64. package/dist/foundation/supervision/server.d.ts +1 -0
  65. package/dist/foundation/supervision/server.js +4 -6
  66. package/dist/integrations/pi/components/conformance.d.ts +1 -0
  67. package/dist/integrations/pi/components/conformance.js +1 -0
  68. package/dist/integrations/pi/components/shell-components.d.ts +1 -0
  69. package/dist/integrations/pi/components/shell-components.js +1 -0
  70. package/dist/integrations/pi/components/shell-editor-autocomplete.js +10 -0
  71. package/dist/integrations/pi/components/shell-footer-status.d.ts +1 -1
  72. package/dist/integrations/pi/components/shell-footer-status.js +19 -13
  73. package/dist/integrations/pi/components/shell-presenters-info.d.ts +36 -0
  74. package/dist/integrations/pi/components/shell-presenters-info.js +78 -0
  75. package/dist/integrations/pi/components/shell-presenters-transcript.d.ts +3 -36
  76. package/dist/integrations/pi/components/shell-presenters-transcript.js +88 -128
  77. package/dist/integrations/pi/components/shell-shared-facade.d.ts +11 -1
  78. package/dist/integrations/pi/components/shell-shared-facade.js +1 -1
  79. package/dist/integrations/pi/engine/adapter.d.ts +22 -4
  80. package/dist/integrations/pi/engine/adapter.js +220 -70
  81. package/dist/integrations/pi/engine/conformance.d.ts +2 -0
  82. package/dist/integrations/pi/engine/conformance.js +2 -0
  83. package/dist/integrations/pi/engine/http-dispatcher.d.ts +4 -0
  84. package/dist/integrations/pi/engine/http-dispatcher.js +25 -0
  85. package/dist/integrations/pi/engine/index.d.ts +3 -0
  86. package/dist/integrations/pi/engine/index.js +3 -0
  87. package/dist/integrations/pi/engine/model-auth-integration.d.ts +1 -0
  88. package/dist/integrations/pi/engine/model-auth-integration.js +1 -0
  89. package/dist/integrations/pi/engine/package-integration.js +1 -1
  90. package/dist/integrations/pi/engine/project-trust-preflight.d.ts +22 -0
  91. package/dist/integrations/pi/engine/project-trust-preflight.js +50 -0
  92. package/dist/integrations/pi/engine/resource-extension-integration.d.ts +1 -0
  93. package/dist/integrations/pi/engine/resource-extension-integration.js +1 -0
  94. package/dist/integrations/pi/engine/runtime-integration.d.ts +16 -1
  95. package/dist/integrations/pi/engine/runtime-integration.js +34 -4
  96. package/dist/integrations/pi/engine/session-integration.d.ts +1 -0
  97. package/dist/integrations/pi/engine/session-integration.js +2 -1
  98. package/dist/integrations/pi/engine/settings-effects.d.ts +66 -0
  99. package/dist/integrations/pi/engine/settings-effects.js +249 -0
  100. package/dist/integrations/pi/engine/settings-integration.d.ts +14 -26
  101. package/dist/integrations/pi/engine/settings-integration.js +102 -108
  102. package/dist/integrations/pi/engine/workflow-controllers.d.ts +2 -1
  103. package/dist/integrations/pi/engine/workflow-controllers.js +1 -0
  104. package/dist/integrations/pi/session-ui/clipboard-image.d.ts +11 -0
  105. package/dist/integrations/pi/session-ui/clipboard-image.js +29 -0
  106. package/dist/integrations/pi/session-ui/index.d.ts +1 -0
  107. package/dist/integrations/pi/session-ui/index.js +1 -0
  108. package/dist/integrations/pi/session-ui/prompt-chips.js +6 -2
  109. package/dist/integrations/pi/session-ui/session-shell-root.d.ts +39 -4
  110. package/dist/integrations/pi/session-ui/session-shell-root.js +215 -80
  111. package/dist/integrations/pi/session-ui/session-shell.d.ts +10 -1
  112. package/dist/integrations/pi/session-ui/session-shell.js +256 -45
  113. package/dist/integrations/pi/session-ui/session-viewport-controller.d.ts +5 -0
  114. package/dist/integrations/pi/session-ui/session-viewport-controller.js +76 -11
  115. package/dist/integrations/pi/session-ui/stream-presentation-coalescer.d.ts +29 -0
  116. package/dist/integrations/pi/session-ui/stream-presentation-coalescer.js +79 -0
  117. package/dist/integrations/pi/session-ui/system-clipboard.d.ts +9 -0
  118. package/dist/integrations/pi/session-ui/system-clipboard.js +38 -9
  119. package/dist/integrations/pi/tui-runtime/adapter.d.ts +6 -0
  120. package/dist/integrations/pi/tui-runtime/adapter.js +166 -8
  121. package/dist/integrations/pi/tui-runtime/contracts.d.ts +25 -0
  122. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +63 -0
  123. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +240 -0
  124. package/dist/integrations/pi/tui-runtime/index.d.ts +2 -0
  125. package/dist/integrations/pi/tui-runtime/index.js +2 -0
  126. package/dist/integrations/pi/tui-runtime/input-presentation-coordinator.d.ts +39 -0
  127. package/dist/integrations/pi/tui-runtime/input-presentation-coordinator.js +139 -0
  128. package/dist/native/darwin-arm64/manifest.json +1 -1
  129. package/dist/native/linux-x64/manifest.json +1 -1
  130. package/dist/native/win32-x64/manifest.json +2 -2
  131. package/dist/native/win32-x64/process-guardian.exe +0 -0
  132. package/dist/ui/apps/host.js +5 -9
  133. package/dist/ui/apps/registry.d.ts +1 -0
  134. package/dist/ui/apps/registry.js +1 -0
  135. package/dist/ui/components/dialog-panel.js +5 -3
  136. package/dist/ui/components/frame.d.ts +1 -0
  137. package/dist/ui/components/frame.js +1 -0
  138. package/dist/ui/components/index.d.ts +1 -0
  139. package/dist/ui/components/index.js +1 -0
  140. package/dist/ui/components/line-input.d.ts +1 -6
  141. package/dist/ui/components/line-input.js +4 -9
  142. package/dist/ui/components/list-view.d.ts +0 -6
  143. package/dist/ui/components/list-view.js +14 -7
  144. package/dist/ui/components/progress-status.d.ts +2 -0
  145. package/dist/ui/components/progress-status.js +4 -0
  146. package/dist/ui/components/scrollbar.js +1 -1
  147. package/dist/ui/components/shortcuts.d.ts +1 -0
  148. package/dist/ui/components/shortcuts.js +1 -0
  149. package/dist/ui/components/spans.d.ts +2 -5
  150. package/dist/ui/components/spans.js +28 -8
  151. package/dist/ui/components/surface.d.ts +0 -5
  152. package/dist/ui/components/surface.js +1 -5
  153. package/dist/ui/components/text-selection.d.ts +10 -1
  154. package/dist/ui/components/text-selection.js +96 -29
  155. package/dist/ui/components/text.js +3 -3
  156. package/dist/ui/components/transcript-viewport.d.ts +42 -0
  157. package/dist/ui/components/transcript-viewport.js +287 -39
  158. package/dist/ui/settings/sections.d.ts +6 -19
  159. package/dist/ui/settings/sections.js +17 -15
  160. package/dist/ui/settings/session.d.ts +8 -18
  161. package/dist/ui/settings/session.js +60 -52
  162. package/dist/ui/settings/store.d.ts +1 -0
  163. package/dist/ui/settings/store.js +2 -1
  164. package/docs/architecture/code-documentation.md +77 -0
  165. package/docs/architecture/project-structure.md +15 -1
  166. package/docs/architecture/ui-reference-provenance.md +23 -3
  167. package/docs/ci-release-runbook.md +98 -7
  168. package/docs/manual-owned-ui-checkpoint.md +28 -1
  169. package/docs/manual-terminal-colour-check.md +4 -3
  170. package/docs/repository-governance-live-acceptance.md +77 -0
  171. package/package.json +11 -3
@@ -1,10 +1,12 @@
1
1
  import { execFile } from "node:child_process";
2
+ import { createHash } from "node:crypto";
2
3
  import { mkdir, rm, writeFile } from "node:fs/promises";
3
4
  import { tmpdir } from "node:os";
4
5
  import { dirname, join, resolve } from "node:path";
5
6
  import { promisify } from "node:util";
6
7
  import { PRODUCT_IDENTITY } from "../../../product-identity.js";
7
- import { copyToClipboard, DefaultPackageManager, getAgentDir, ProjectTrustStore, SessionManager, } from "@earendil-works/pi-coding-agent";
8
+ import { configureOwnedHttpDispatcher } from "./http-dispatcher.js";
9
+ import { copyToClipboard, DefaultPackageManager, getAgentDir, ProjectTrustStore, SessionManager, VERSION, } from "@earendil-works/pi-coding-agent";
8
10
  import { OWNED_UI_EXTENSION_CONTRACT_VERSION, OWNED_UI_EXTENSION_RENDER_CALLBACKS, OWNED_UI_EXTENSION_UI_CALLBACKS, OWNED_UI_EXTENSION_UI_PROPERTIES, assertOwnedUiCommand, assertOwnedUiExtensionUiPort, assertOwnedUiSnapshot, } from "../../../contracts/owned-ui/index.js";
9
11
  import { PINNED_PI_SETTINGS_CALLBACKS, PINNED_PI_WORKFLOW_COMMAND_NAMES, } from "./workflows.js";
10
12
  import { createPiRuntimeIntegration } from "./runtime-integration.js";
@@ -16,7 +18,7 @@ const execFileAsync = promisify(execFile);
16
18
  * that a streaming burst never holds input, large enough that an ordinary turn is one
17
19
  * batch.
18
20
  */
19
- // Deliver at most one engine event per event-loop turn. Transcript updates can
21
+ // Performance: deliver at most one engine event per event-loop turn. Transcript updates can
20
22
  // be expensive in long sessions; a larger synchronous batch starves terminal
21
23
  // input and makes an in-progress mouse selection appear frozen.
22
24
  const EVENT_DELIVERY_BATCH = 1;
@@ -27,12 +29,14 @@ const DEFAULT_SURFACE = {
27
29
  focusedRegion: "editor",
28
30
  hardwareCursor: false,
29
31
  };
32
+ /** Owns the pinned Pi session lifecycle and translates its events into the neutral agent-engine contract. */
30
33
  export class PiEngineAdapter {
31
34
  #runtimeFactory;
32
35
  #checkPackageUpdates;
33
36
  #cwd;
34
37
  #agentDir;
35
38
  #sessionId;
39
+ #sessionPath;
36
40
  #workflowHost;
37
41
  #workflowInteraction;
38
42
  #listeners = new Set();
@@ -68,6 +72,7 @@ export class PiEngineAdapter {
68
72
  #messageBlockIds = new WeakMap();
69
73
  #messageFallbackIds = new Map();
70
74
  #toolBlockIds = new Map();
75
+ #transcriptImageAssets = new Map();
71
76
  #pendingToolUpdates = new Map();
72
77
  #toolUpdateFlush = null;
73
78
  #usageCache;
@@ -84,12 +89,16 @@ export class PiEngineAdapter {
84
89
  #extensionShutdown;
85
90
  #extensionBound = false;
86
91
  #disposed = false;
87
- /** Lists the themes installed on this machine, when the caller supplies one. */
92
+ #settingsProductMode;
93
+ #projectTrustPrompt;
94
+ #settingsIntegration;
95
+ #settingsIntegrationManager;
88
96
  #availableThemes;
89
97
  constructor(options = {}) {
90
98
  this.#cwd = options.cwd ?? process.cwd();
91
99
  this.#agentDir = options.agentDir ?? getAgentDir();
92
100
  this.#sessionId = options.sessionId ?? "owned-session-1";
101
+ this.#sessionPath = options.sessionPath;
93
102
  this.#runtimeFactory = options.createRuntime ?? createDefaultPiRuntime;
94
103
  this.#checkPackageUpdates = options.checkPackageUpdates
95
104
  ?? (options.createRuntime
@@ -97,6 +106,8 @@ export class PiEngineAdapter {
97
106
  : settingsManager => checkDefaultPiPackageUpdates(this.#cwd, this.#agentDir, settingsManager));
98
107
  this.#workflowHost = options.workflowHost ?? defaultWorkflowHost();
99
108
  this.#availableThemes = options.availableThemes ?? null;
109
+ this.#settingsProductMode = options.settingsProductMode ?? "bare";
110
+ this.#projectTrustPrompt = options.projectTrustPrompt;
100
111
  this.#workflowInteraction = { prompt: async () => null, notify() { } };
101
112
  }
102
113
  setWorkflowInteractionHost(interaction) {
@@ -111,6 +122,29 @@ export class PiEngineAdapter {
111
122
  get agentDir() {
112
123
  return this.#agentDir;
113
124
  }
125
+ resolveTranscriptImage(assetId) {
126
+ return this.#transcriptImageAssets.get(assetId) ?? null;
127
+ }
128
+ currentSessionFile() {
129
+ const value = this.#session?.sessionManager?.getSessionFile?.();
130
+ return typeof value === "string" && value.length > 0 ? value : null;
131
+ }
132
+ currentSessionResumeMetadata() {
133
+ const manager = this.#session?.sessionManager;
134
+ if (manager === undefined
135
+ || typeof manager.isPersisted !== "function"
136
+ || typeof manager.getSessionId !== "function"
137
+ || typeof manager.getSessionDir !== "function"
138
+ || typeof manager.usesDefaultSessionDir !== "function"
139
+ || manager.isPersisted() !== true
140
+ || this.currentSessionFile() === null)
141
+ return null;
142
+ const sessionId = manager.getSessionId();
143
+ const sessionDir = manager.getSessionDir();
144
+ if (!sessionId || !sessionDir)
145
+ return null;
146
+ return { sessionId, sessionDir, usesDefaultSessionDir: manager.usesDefaultSessionDir() };
147
+ }
114
148
  get disposed() {
115
149
  return this.#disposed;
116
150
  }
@@ -121,6 +155,8 @@ export class PiEngineAdapter {
121
155
  cwd: this.#cwd,
122
156
  agentDir: this.#agentDir,
123
157
  sessionId: this.#sessionId,
158
+ ...(this.#sessionPath === undefined ? {} : { sessionPath: this.#sessionPath }),
159
+ ...(this.#projectTrustPrompt === undefined ? {} : { projectTrustPrompt: this.#projectTrustPrompt }),
124
160
  }).catch(error => {
125
161
  this.#lifecycle = "failed";
126
162
  this.#addDiagnostic("error", "engine-startup", error instanceof Error ? error.message : String(error), false);
@@ -140,6 +176,7 @@ export class PiEngineAdapter {
140
176
  this.#addDiagnostic(diagnostic.type === "error" ? "error" : diagnostic.type === "warning" ? "warning" : "info", "engine-startup", diagnostic.message, diagnostic.type !== "error");
141
177
  }
142
178
  this.#bindSession(runtime.session);
179
+ await this.#announceChangelog(runtime.services.settingsManager);
143
180
  this.#lifecycle = "ready";
144
181
  this.#editor = { ...this.#editor, submitEnabled: true };
145
182
  this.#emitEvent({ type: "session-lifecycle", lifecycle: "ready", reason: null });
@@ -147,6 +184,19 @@ export class PiEngineAdapter {
147
184
  void this.#announcePackageUpdates(runtime.services.settingsManager);
148
185
  return this.view();
149
186
  }
187
+ async #announceChangelog(settingsManager) {
188
+ if (!settingsManager || typeof settingsManager.getLastChangelogVersion !== "function"
189
+ || typeof settingsManager.setLastChangelogVersion !== "function")
190
+ return;
191
+ if (settingsManager.getLastChangelogVersion() === VERSION)
192
+ return;
193
+ const markdown = await this.#workflowHost.readChangelog().catch(() => "");
194
+ if (markdown.trim().length > 0) {
195
+ this.#addDiagnostic("info", settingsManager.getCollapseChangelog() ? "changelog-collapsed" : "changelog-expanded", markdown, true);
196
+ }
197
+ settingsManager.setLastChangelogVersion(VERSION);
198
+ await settingsManager.flush();
199
+ }
150
200
  async #announcePackageUpdates(settingsManager) {
151
201
  if (process.env.PI_OFFLINE)
152
202
  return;
@@ -432,16 +482,107 @@ export class PiEngineAdapter {
432
482
  /** Settings port for the live runtime, or null before the runtime is available. */
433
483
  settingsPort() {
434
484
  const settings = this.#runtime?.services.settingsManager;
435
- if (!settings)
485
+ if (!settings || typeof settings.getCompactionEnabled !== "function")
436
486
  return null;
437
- const session = this.#runtime?.session;
438
- return new PiSettingsIntegration(settings, {
439
- ...(this.#availableThemes === null ? {} : { themes: this.#availableThemes }),
440
- thinkingLevels: () => {
441
- const levels = session?.getAvailableThinkingLevels?.();
442
- return Array.isArray(levels) ? levels.map(level => String(level)) : [];
443
- },
444
- });
487
+ if (this.#settingsIntegration === undefined || this.#settingsIntegrationManager !== settings) {
488
+ this.#settingsIntegrationManager = settings;
489
+ configureOwnedHttpDispatcher(settings.getHttpIdleTimeoutMs());
490
+ this.#settingsIntegration = new PiSettingsIntegration(settings, {
491
+ ...(this.#availableThemes === null ? {} : { themes: this.#availableThemes }),
492
+ thinkingLevels: () => {
493
+ const levels = this.#runtime?.session?.getAvailableThinkingLevels?.();
494
+ return Array.isArray(levels) ? levels.map(level => String(level)) : [];
495
+ },
496
+ productMode: this.#settingsProductMode,
497
+ });
498
+ this.#settingsIntegration.bindOwner("shell", {
499
+ enableSkillCommands: { apply: value => {
500
+ if (typeof value !== "boolean")
501
+ throw new TypeError("Skill commands value is invalid");
502
+ settings.setEnableSkillCommands(value);
503
+ } },
504
+ doubleEscapeAction: { apply: value => {
505
+ if (value !== "tree" && value !== "fork" && value !== "none")
506
+ throw new TypeError("Double-Escape action is invalid");
507
+ settings.setDoubleEscapeAction(value);
508
+ } },
509
+ treeFilterMode: { apply: value => {
510
+ if (value !== "default" && value !== "no-tools" && value !== "user-only" && value !== "labeled-only" && value !== "all")
511
+ throw new TypeError("Tree filter mode is invalid");
512
+ settings.setTreeFilterMode(value);
513
+ } },
514
+ showCacheMissNotices: { apply: value => {
515
+ if (typeof value !== "boolean")
516
+ throw new TypeError("Cache-miss notice setting is invalid");
517
+ settings.setShowCacheMissNotices(value);
518
+ } },
519
+ });
520
+ this.#settingsIntegration.bindOwner("startup", {
521
+ // Invariant: deferred application is the owner operation: the next preflight reads
522
+ // the persisted default before constructing project-backed services.
523
+ defaultProjectTrust: { apply() { } },
524
+ collapseChangelog: { apply() { } },
525
+ });
526
+ this.#settingsIntegration.bindOwner("agent", {
527
+ autoCompact: { apply: value => {
528
+ if (typeof value !== "boolean")
529
+ throw new TypeError("Auto compact value is invalid");
530
+ this.#requireWorkflowSession().setAutoCompactionEnabled(value);
531
+ } },
532
+ autoResizeImages: { apply: value => {
533
+ if (typeof value !== "boolean")
534
+ throw new TypeError("Auto-resize images value is invalid");
535
+ settings.setImageAutoResize(value);
536
+ } },
537
+ blockImages: { apply: value => {
538
+ if (typeof value !== "boolean")
539
+ throw new TypeError("Block images value is invalid");
540
+ settings.setBlockImages(value);
541
+ } },
542
+ steeringMode: { apply: value => {
543
+ if (value !== "all" && value !== "one-at-a-time")
544
+ throw new TypeError("Steering mode is invalid");
545
+ this.#requireWorkflowSession().setSteeringMode(value);
546
+ } },
547
+ followUpMode: { apply: value => {
548
+ if (value !== "all" && value !== "one-at-a-time")
549
+ throw new TypeError("Follow-up mode is invalid");
550
+ this.#requireWorkflowSession().setFollowUpMode(value);
551
+ } },
552
+ transport: { apply: value => {
553
+ if (value !== "sse" && value !== "websocket" && value !== "websocket-cached" && value !== "auto")
554
+ throw new TypeError("Transport is invalid");
555
+ this.#requireWorkflowSession().agent.transport = value;
556
+ } },
557
+ httpIdleTimeoutMs: { apply: value => {
558
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0)
559
+ throw new TypeError("HTTP idle timeout is invalid");
560
+ // Invariant: provider streaming reads the manager per request; global fetch uses
561
+ // the matching owned dispatcher and zero maps to disabled semantics.
562
+ configureOwnedHttpDispatcher(value);
563
+ settings.setHttpIdleTimeoutMs(value);
564
+ } },
565
+ thinkingLevel: { apply: value => {
566
+ if (!isThinkingLevel(value))
567
+ throw new TypeError("Thinking level is invalid");
568
+ this.#requireWorkflowSession().setThinkingLevel(value);
569
+ this.#thinkingLevel = readThinkingLevel(this.#requireWorkflowSession().thinkingLevel);
570
+ this.#emitView();
571
+ } },
572
+ warnings: { apply: value => {
573
+ if (!isRecord(value) || Object.values(value).some(flag => typeof flag !== "boolean"))
574
+ throw new TypeError("Warnings setting is invalid");
575
+ settings.setWarnings(value);
576
+ } },
577
+ });
578
+ }
579
+ return this.#settingsIntegration;
580
+ }
581
+ bindSettingsOwner(owner, handlers) {
582
+ const settings = this.settingsPort();
583
+ if (settings === null)
584
+ return () => { };
585
+ return settings.bindOwner(owner, handlers);
445
586
  }
446
587
  pinnedModelSelectorContext() {
447
588
  const session = this.#requireWorkflowSession();
@@ -718,9 +859,9 @@ export class PiEngineAdapter {
718
859
  excludeFromContext,
719
860
  };
720
861
  }
721
- applyPinnedSettingValue(callback, value) {
862
+ async applyPinnedSettingValue(callback, value) {
722
863
  try {
723
- return this.#applyPinnedSetting(callback, value, true);
864
+ return await this.#applyPinnedSetting(callback, value, true);
724
865
  }
725
866
  catch (error) {
726
867
  return workflowResult("settings", "failed", error instanceof Error ? error.message : String(error));
@@ -821,6 +962,7 @@ export class PiEngineAdapter {
821
962
  this.#toolUpdateFlush = null;
822
963
  }
823
964
  this.#pendingToolUpdates.clear();
965
+ this.#transcriptImageAssets.clear();
824
966
  this.#extensionBound = false;
825
967
  this.#extensionUi = undefined;
826
968
  this.#extensionShutdown = undefined;
@@ -846,7 +988,7 @@ export class PiEngineAdapter {
846
988
  case "settings": {
847
989
  if (!selection)
848
990
  return workflowResult(request.command, "failed", "Settings requires the owned settings controller");
849
- return this.#applyPinnedSetting(selection);
991
+ return await this.#applyPinnedSetting(selection);
850
992
  }
851
993
  case "model": {
852
994
  const reference = selection ?? argument;
@@ -1236,7 +1378,7 @@ export class PiEngineAdapter {
1236
1378
  return [];
1237
1379
  return sessionInfoOptions(await SessionManager.list(cwd, typeof sessionDir === "string" ? sessionDir : undefined));
1238
1380
  }
1239
- #applyPinnedSetting(selection, selectedValue, hasSelectedValue = false) {
1381
+ async #applyPinnedSetting(selection, selectedValue, hasSelectedValue = false) {
1240
1382
  if (!PINNED_PI_SETTINGS_CALLBACKS.includes(selection)) {
1241
1383
  return workflowResult("settings", "failed", `Unknown setting callback: ${selection}`);
1242
1384
  }
@@ -1283,29 +1425,19 @@ export class PiEngineAdapter {
1283
1425
  selectedValue = currentValues[callback];
1284
1426
  hasSelectedValue = true;
1285
1427
  }
1286
- const settings = this.#runtime?.services.settingsManager;
1287
- if (!settings)
1428
+ const port = this.settingsPort();
1429
+ if (port === null)
1288
1430
  return workflowResult("settings", "failed", "Settings are unavailable");
1289
- const session = this.#requireWorkflowSession();
1290
- if (callback === "onThinkingLevelChange") {
1291
- if (!isThinkingLevel(selectedValue))
1292
- return workflowResult("settings", "failed", "Thinking level is invalid");
1293
- session.setThinkingLevel(selectedValue);
1294
- }
1295
- else {
1296
- const key = settingKeyForCallback(callback);
1297
- if (key === null)
1298
- return workflowResult("settings", "failed", `${settingLabel(callback)} is unavailable in this runtime`);
1299
- const value = agentJsonValue(selectedValue);
1300
- if (callback === "onAutoCompactChange") {
1301
- if (typeof value !== "boolean")
1302
- return workflowResult("settings", "failed", "Auto compact value is invalid");
1303
- session.setAutoCompactionEnabled?.(value);
1304
- }
1305
- new PiSettingsIntegration(settings).writeSettingNow(key, value);
1431
+ const key = settingKeyForCallback(callback);
1432
+ if (key === null)
1433
+ return workflowResult("settings", "failed", `${settingLabel(callback)} is unavailable in this runtime`);
1434
+ const result = await port.writeSetting(key, agentJsonValue(selectedValue));
1435
+ if (result.status === "failed" || result.status === "unavailable") {
1436
+ return workflowResult("settings", "failed", result.failure ?? result.limitationReason ?? `${settingLabel(callback)} is unavailable`);
1306
1437
  }
1307
1438
  this.#emitView();
1308
- return workflowResult("settings", "completed", `${settingLabel(callback)}: ${String(selectedValue)}`);
1439
+ const suffix = result.status === "deferred" ? ` (${result.application})` : "";
1440
+ return workflowResult("settings", "completed", `${settingLabel(callback)}: ${String(selectedValue)}${suffix}`);
1309
1441
  }
1310
1442
  async #perform(command) {
1311
1443
  const runtime = this.#runtime;
@@ -1384,6 +1516,7 @@ export class PiEngineAdapter {
1384
1516
  this.#activeModel = readModel(session.model);
1385
1517
  this.#reconcileActiveModelAvailability();
1386
1518
  this.#thinkingLevel = readThinkingLevel(session.thinkingLevel);
1519
+ this.#transcriptImageAssets.clear();
1387
1520
  this.#rebuildTranscript(session.messages, "finalized");
1388
1521
  this.#unsubscribe = session.subscribe(event => this.#handlePiEvent(event));
1389
1522
  if (this.#extensionUi !== undefined)
@@ -1433,23 +1566,24 @@ export class PiEngineAdapter {
1433
1566
  this.#emitView();
1434
1567
  }
1435
1568
  }
1436
- /**
1437
- * Replaces the transcript and the index that finds a block by its identifier. Every
1438
- * lookup goes through the index, so streaming a chunk costs the same in a long session
1439
- * as in a new one.
1440
- */
1569
+ // Performance: replacing the transcript rebuilds its index so block lookup stays constant-time.
1441
1570
  #setTranscript(blocks) {
1442
1571
  this.#transcript = blocks;
1443
1572
  this.#transcriptIndex.clear();
1444
1573
  for (const [index, block] of blocks.entries())
1445
1574
  this.#transcriptIndex.set(block.id, index);
1575
+ const retainedAssets = new Set(blocks.flatMap(block => block.imageReferences?.map(reference => reference.assetId) ?? []));
1576
+ for (const assetId of this.#transcriptImageAssets.keys()) {
1577
+ if (!retainedAssets.has(assetId))
1578
+ this.#transcriptImageAssets.delete(assetId);
1579
+ }
1446
1580
  this.#transcriptSnapshot = undefined;
1447
1581
  }
1448
1582
  #transcriptBlock(id) {
1449
1583
  const index = this.#transcriptIndex.get(id);
1450
1584
  return index === undefined ? undefined : this.#transcript[index];
1451
1585
  }
1452
- /** Shows the state named by `kind`, which becomes the state a later end can clear. */
1586
+ // Invariant: the named work state is the only state a matching end may clear.
1453
1587
  #enterWorkState(kind, message) {
1454
1588
  const wasBusy = this.#lifecycle === "busy";
1455
1589
  this.#statusKind = kind;
@@ -1459,21 +1593,16 @@ export class PiEngineAdapter {
1459
1593
  this.#emitEvent({ type: "session-lifecycle", lifecycle: "busy", reason: null });
1460
1594
  this.#emitEvent({ type: "status", status: this.#status });
1461
1595
  }
1462
- /**
1463
- * Ends one named state. A state the shell is not in is left alone, so a finished
1464
- * compaction or retry cannot clear the working state it never replaced. While the run
1465
- * continues, ending either of those returns to working rather than to idle.
1466
- */
1596
+ // Invariant: ending retry or compaction cannot clear a different active work state.
1467
1597
  #endWorkState(kind) {
1468
1598
  if (this.#statusKind !== kind)
1469
1599
  return;
1470
1600
  if (this.#agentRunActive) {
1471
- this.#enterWorkState("working", "Working...");
1601
+ this.#enterWorkState("working", "Working");
1472
1602
  return;
1473
1603
  }
1474
1604
  this.#leaveWorkStates();
1475
1605
  }
1476
- /** Leaves every work state and reports the session idle. */
1477
1606
  #leaveWorkStates() {
1478
1607
  this.#statusKind = null;
1479
1608
  this.#lifecycle = "ready";
@@ -1484,7 +1613,7 @@ export class PiEngineAdapter {
1484
1613
  #handlePiEvent(event) {
1485
1614
  if (!isRecord(event) || typeof event.type !== "string")
1486
1615
  return;
1487
- // Usage moves at message and lifecycle boundaries, not with stream chunks, so the
1616
+ // Invariant: usage moves at message and lifecycle boundaries, not with stream chunks, so the
1488
1617
  // two streaming event kinds keep the memo and everything else drops it.
1489
1618
  if (event.type !== "message_update" && event.type !== "tool_execution_update")
1490
1619
  this.#usageCache = undefined;
@@ -1492,7 +1621,7 @@ export class PiEngineAdapter {
1492
1621
  case "agent_start":
1493
1622
  this.#agentRunActive = true;
1494
1623
  this.#emitEvent({ type: "agent-run-started" });
1495
- this.#enterWorkState("working", "Working...");
1624
+ this.#enterWorkState("working", "Working");
1496
1625
  return;
1497
1626
  case "message_start":
1498
1627
  this.#upsertMessageBlock(event.message, "live");
@@ -1501,7 +1630,7 @@ export class PiEngineAdapter {
1501
1630
  const delta = isRecord(event.assistantMessageEvent) && typeof event.assistantMessageEvent.delta === "string"
1502
1631
  ? event.assistantMessageEvent.delta
1503
1632
  : undefined;
1504
- // The delta is folded in before the block is stored, so a chunk is one update to
1633
+ // Invariant: the delta is folded in before the block is stored, so a chunk is one update to
1505
1634
  // one block rather than a store without the delta followed by a store with it.
1506
1635
  const blocks = this.#messageBlocks(event.message, "live", this.#transcript.length);
1507
1636
  for (const [index, block] of blocks.entries()) {
@@ -1513,7 +1642,7 @@ export class PiEngineAdapter {
1513
1642
  }
1514
1643
  case "message_end":
1515
1644
  this.#upsertMessageBlock(event.message, "finalized");
1516
- // Preserve the same semantic boundary v2 counted. Transcript block
1645
+ // Compatibility: preserve the same semantic boundary v2 counted. Transcript block
1517
1646
  // finalization is intentionally not a substitute: rebuilds, retries,
1518
1647
  // thinking parts, and tool rows can all finalize independently.
1519
1648
  if (isRecord(event.message) && event.message.role === "assistant") {
@@ -1529,7 +1658,7 @@ export class PiEngineAdapter {
1529
1658
  return;
1530
1659
  case "tool_execution_start":
1531
1660
  case "tool_execution_end": {
1532
- // The end supersedes any update still waiting on the coalescing timer.
1661
+ // Concurrency: the end supersedes any update still waiting on the coalescing timer.
1533
1662
  const toolCallId = stringValue(event.toolCallId);
1534
1663
  if (toolCallId !== undefined)
1535
1664
  this.#pendingToolUpdates.delete(toolCallId);
@@ -1550,7 +1679,7 @@ export class PiEngineAdapter {
1550
1679
  this.#rebuildTranscript(finalMessages, "finalized");
1551
1680
  else
1552
1681
  this.#setTranscript(this.#transcript.map(block => block.status === "live" ? { ...block, status: "finalized" } : block));
1553
- // Ending a turn leaves the working state, as the recorded pinned baseline does, but
1682
+ // Compatibility: ending a turn leaves the working state, as the recorded pinned baseline does, but
1554
1683
  // it leaves only that state: a compaction or retry being shown outlives the turn
1555
1684
  // that ended under it. Settlement ends the run, and with it every state — the
1556
1685
  // engine ends a turn for each continuation it makes and settles once.
@@ -1576,13 +1705,13 @@ export class PiEngineAdapter {
1576
1705
  return;
1577
1706
  }
1578
1707
  case "auto_retry_start":
1579
- this.#enterWorkState("retry", "Retrying");
1708
+ this.#enterWorkState("retry", "Retrying");
1580
1709
  return;
1581
1710
  case "auto_retry_end":
1582
1711
  this.#endWorkState("retry");
1583
1712
  return;
1584
1713
  case "compaction_start":
1585
- this.#enterWorkState("compaction", "Compacting");
1714
+ this.#enterWorkState("compaction", "Compacting");
1586
1715
  return;
1587
1716
  case "compaction_end":
1588
1717
  this.#endWorkState("compaction");
@@ -1675,7 +1804,7 @@ export class PiEngineAdapter {
1675
1804
  }
1676
1805
  }
1677
1806
  }
1678
- // An authoritative rebuild restates most of what is already there. Reusing the block
1807
+ // Performance: an authoritative rebuild restates most of what is already there. Reusing the block
1679
1808
  // that already says it keeps its revision, and with it the rows the shell rendered for
1680
1809
  // it — otherwise every turn that ends re-renders the whole session.
1681
1810
  this.#setTranscript(blocks.map(block => {
@@ -1702,6 +1831,7 @@ export class PiEngineAdapter {
1702
1831
  revision: this.#nextBlockRevision(baseId),
1703
1832
  title: "User",
1704
1833
  text: textFromContent(message.content),
1834
+ imageReferences: this.#imageReferences(message.content, "user"),
1705
1835
  payload: {
1706
1836
  role: "user",
1707
1837
  imageCount: contentImageCount(message.content),
@@ -1779,6 +1909,7 @@ export class PiEngineAdapter {
1779
1909
  revision: this.#nextBlockRevision(blockId),
1780
1910
  title: stringValue(message.toolName) ?? existing?.title ?? "Tool result",
1781
1911
  text: textFromContent(message.content),
1912
+ imageReferences: this.#imageReferences(message.content, "tool-result"),
1782
1913
  payload: {
1783
1914
  ...(existingPayload ?? {}),
1784
1915
  role: "toolResult",
@@ -1834,12 +1965,25 @@ export class PiEngineAdapter {
1834
1965
  }
1835
1966
  return blocks;
1836
1967
  }
1837
- /**
1838
- * A busy tool streams far more output chunks than a terminal frame can show, and every
1839
- * chunk restates the whole accumulated output. Keeping only the newest chunk per tool
1840
- * and applying it on a short timer bounds the per-command work by frames rather than
1841
- * by chunks, which is what keeps typed input and the spinner alive under the stream.
1842
- */
1968
+ #imageReferences(content, source) {
1969
+ if (!Array.isArray(content))
1970
+ return [];
1971
+ const references = [];
1972
+ for (const item of content) {
1973
+ if (references.length >= 16 || !isRecord(item) || item.type !== "image"
1974
+ || typeof item.data !== "string" || item.data.length === 0
1975
+ || typeof item.mimeType !== "string" || !/^image\/[a-z0-9.+-]+$/i.test(item.mimeType))
1976
+ continue;
1977
+ const byteLength = Buffer.from(item.data, "base64").byteLength;
1978
+ if (byteLength < 1 || byteLength > 20 * 1024 * 1024)
1979
+ continue;
1980
+ const assetId = `image-${createHash("sha256").update(item.mimeType).update("\0").update(item.data).digest("hex").slice(0, 24)}`;
1981
+ this.#transcriptImageAssets.set(assetId, { type: "image", data: item.data, mimeType: item.mimeType });
1982
+ references.push({ assetId, mimeType: item.mimeType, byteLength, source });
1983
+ }
1984
+ return references;
1985
+ }
1986
+ // Performance: coalescing each tool to its newest chunk bounds work by frames, not stream events.
1843
1987
  #coalesceToolExecutionUpdate(event) {
1844
1988
  const toolCallId = stringValue(event.toolCallId);
1845
1989
  if (!toolCallId)
@@ -1882,7 +2026,7 @@ export class PiEngineAdapter {
1882
2026
  toolCallId,
1883
2027
  toolName: stringValue(event.toolName) ?? "unknown",
1884
2028
  arguments: jsonSummary(event.args),
1885
- // A partial result repeats the whole accumulated output on every chunk;
2029
+ // Performance: a partial result repeats the whole accumulated output on every chunk;
1886
2030
  // summarizing it each time would cost quadratic work over the stream.
1887
2031
  result: ended ? jsonSummary(source) : { summary: "", json: null },
1888
2032
  partialResult: event.type === "tool_execution_update",
@@ -1895,7 +2039,7 @@ export class PiEngineAdapter {
1895
2039
  const index = this.#transcriptIndex.get(block.id);
1896
2040
  if (index !== undefined) {
1897
2041
  const existing = this.#transcript[index];
1898
- // Nothing to tell the shell about a block that repeats itself, and keeping the
2042
+ // Performance: nothing is emitted for a block that repeats itself, and keeping the
1899
2043
  // revision keeps the rows it already rendered.
1900
2044
  if (existing !== undefined && sameBlockContent(existing, block))
1901
2045
  return;
@@ -2012,7 +2156,7 @@ export class PiEngineAdapter {
2012
2156
  }
2013
2157
  }
2014
2158
  deliveredSinceYield += 1;
2015
- // A microtask chain runs to exhaustion before the loop turns, so a streaming
2159
+ // Concurrency: a microtask chain runs to exhaustion before the loop turns, so a streaming
2016
2160
  // burst would hold typed input, pointer reports, and timed indicators until it
2017
2161
  // drained. Yielding on a macrotask hands those their turn between batches.
2018
2162
  if (deliveredSinceYield >= EVENT_DELIVERY_BATCH && this.#eventQueue.length > 0) {
@@ -2056,7 +2200,12 @@ export async function createPiEngineAdapter(options = {}) {
2056
2200
  return adapter;
2057
2201
  }
2058
2202
  async function createDefaultPiRuntime(input) {
2059
- return createPiRuntimeIntegration({ cwd: input.cwd, agentDir: input.agentDir });
2203
+ return createPiRuntimeIntegration({
2204
+ cwd: input.cwd,
2205
+ agentDir: input.agentDir,
2206
+ ...(input.sessionPath === undefined ? {} : { sessionPath: input.sessionPath }),
2207
+ ...(input.projectTrustPrompt === undefined ? {} : { projectTrustPrompt: input.projectTrustPrompt }),
2208
+ });
2060
2209
  }
2061
2210
  async function checkDefaultPiPackageUpdates(cwd, agentDir, settingsManager) {
2062
2211
  const packageManager = new DefaultPackageManager({ cwd, agentDir, settingsManager });
@@ -2282,7 +2431,7 @@ function settingKeyForCallback(callback) {
2282
2431
  onAutoCompactChange: "autoCompact", onShowImagesChange: "showImages", onImageWidthCellsChange: "imageWidthCells",
2283
2432
  onAutoResizeImagesChange: "autoResizeImages", onBlockImagesChange: "blockImages", onEnableSkillCommandsChange: "enableSkillCommands",
2284
2433
  onSteeringModeChange: "steeringMode", onFollowUpModeChange: "followUpMode", onTransportChange: "transport",
2285
- onHttpIdleTimeoutMsChange: "httpIdleTimeoutMs", onThemeChange: "theme", onThemePreview: "theme",
2434
+ onHttpIdleTimeoutMsChange: "httpIdleTimeoutMs", onThinkingLevelChange: "thinkingLevel", onThemeChange: "theme", onThemePreview: "theme",
2286
2435
  onHideThinkingBlockChange: "hideThinkingBlock", onMermaidRenderingModeChange: "mermaidRenderingMode",
2287
2436
  onShowCacheMissNoticesChange: "showCacheMissNotices", onCollapseChangelogChange: "collapseChangelog",
2288
2437
  onEnableInstallTelemetryChange: "enableInstallTelemetry", onQuietStartupChange: "quietStartup",
@@ -2483,7 +2632,8 @@ function sameBlockContent(left, right) {
2483
2632
  && left.status === right.status
2484
2633
  && left.title === right.title
2485
2634
  && left.text === right.text
2486
- && sameValue(left.payload, right.payload);
2635
+ && sameValue(left.payload, right.payload)
2636
+ && sameValue(left.imageReferences ?? [], right.imageReferences ?? []);
2487
2637
  }
2488
2638
  function sameValue(left, right) {
2489
2639
  if (left === right)
@@ -13,6 +13,7 @@ export declare const REQUIRED_PI_CAPABILITY_OPERATIONS: Readonly<{
13
13
  readonly workflows: readonly ["workflow.route", "workflow.validate", "workflow.diagnostics"];
14
14
  readonly disposal: readonly ["subscription.dispose", "session.dispose", "services.cleanup"];
15
15
  }>;
16
+ /** Identifies one required Pi capability operation that differs from the accepted integration contract. */
16
17
  export declare class PiCapabilityCompatibilityError extends Error {
17
18
  readonly packageVersion: string;
18
19
  readonly capability: string;
@@ -29,6 +30,7 @@ export interface PiUpgradeConformanceReport {
29
30
  readonly commandSurface: readonly string[];
30
31
  readonly capabilities: readonly PiCapabilityConformanceResult[];
31
32
  }
33
+ /** Identifies the public Pi SDK stage that failed during upgrade conformance. */
32
34
  export declare class PiUpgradeConformanceError extends Error {
33
35
  readonly stage: "exports" | "services" | "session";
34
36
  constructor(stage: "exports" | "services" | "session", cause: unknown);
@@ -14,6 +14,7 @@ export const REQUIRED_PI_CAPABILITY_OPERATIONS = Object.freeze({
14
14
  workflows: ["workflow.route", "workflow.validate", "workflow.diagnostics"],
15
15
  disposal: ["subscription.dispose", "session.dispose", "services.cleanup"],
16
16
  });
17
+ /** Identifies one required Pi capability operation that differs from the accepted integration contract. */
17
18
  export class PiCapabilityCompatibilityError extends Error {
18
19
  packageVersion;
19
20
  capability;
@@ -26,6 +27,7 @@ export class PiCapabilityCompatibilityError extends Error {
26
27
  this.name = "PiCapabilityCompatibilityError";
27
28
  }
28
29
  }
30
+ /** Identifies the public Pi SDK stage that failed during upgrade conformance. */
29
31
  export class PiUpgradeConformanceError extends Error {
30
32
  stage;
31
33
  constructor(stage, cause) {
@@ -0,0 +1,4 @@
1
+ /** Apply the profile's HTTP timeout to npm undici and its fetch globals. */
2
+ export declare function configureOwnedHttpDispatcher(timeoutMs: number): void;
3
+ /** Test and diagnostics seam; reports configured semantics, not the max-int implementation detail. */
4
+ export declare function configuredOwnedHttpIdleTimeoutMs(): number | null;
@@ -0,0 +1,25 @@
1
+ // Provenance: mechanically adapted from Pi commit 914cf14
2
+ // packages/coding-agent/src/core/http-dispatcher.ts (MIT).
3
+ // Local modifications: expose only A1's owned timeout seam and retain no private Pi imports.
4
+ import { EnvHttpProxyAgent, install, setGlobalDispatcher } from "undici";
5
+ const MAX_DISABLED_TIMEOUT_MS = 2_147_483_647;
6
+ let configuredTimeoutMs = null;
7
+ /** Apply the profile's HTTP timeout to npm undici and its fetch globals. */
8
+ export function configureOwnedHttpDispatcher(timeoutMs) {
9
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 0)
10
+ throw new TypeError("HTTP idle timeout is invalid");
11
+ const effectiveTimeoutMs = timeoutMs === 0 ? MAX_DISABLED_TIMEOUT_MS : timeoutMs;
12
+ const dispatcher = new EnvHttpProxyAgent({
13
+ allowH2: false,
14
+ bodyTimeout: effectiveTimeoutMs,
15
+ headersTimeout: effectiveTimeoutMs,
16
+ connect: { autoSelectFamilyAttemptTimeout: 2_000 },
17
+ });
18
+ setGlobalDispatcher(dispatcher);
19
+ install?.();
20
+ configuredTimeoutMs = timeoutMs;
21
+ }
22
+ /** Test and diagnostics seam; reports configured semantics, not the max-int implementation detail. */
23
+ export function configuredOwnedHttpIdleTimeoutMs() {
24
+ return configuredTimeoutMs;
25
+ }
@@ -4,6 +4,9 @@ export * from "./runtime-integration.js";
4
4
  export * from "./session-integration.js";
5
5
  export * from "./model-auth-integration.js";
6
6
  export * from "./settings-integration.js";
7
+ export * from "./settings-effects.js";
8
+ export * from "./project-trust-preflight.js";
9
+ export * from "./http-dispatcher.js";
7
10
  export * from "./package-integration.js";
8
11
  export * from "./resource-extension-integration.js";
9
12
  export * from "./workflow-controllers.js";