@cydm/pie 2.7.6 → 2.7.8

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.
package/dist/cli.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  logTransportLifecycle,
24
24
  migrateConfigFromAgentDir,
25
25
  truncateHeadTail
26
- } from "./chunks/chunk-GAUIKMBA.js";
26
+ } from "./chunks/chunk-E6F4BTNN.js";
27
27
  import {
28
28
  resolveCliProjectRoot
29
29
  } from "./chunks/chunk-WLFOBECN.js";
@@ -47,6 +47,7 @@ import {
47
47
  createEmptyExecutionState,
48
48
  createExecutionStateStore,
49
49
  createFileObservationState,
50
+ createHostEnvelopeProjectorRegistry,
50
51
  createPolicyEnforcedTools,
51
52
  createReadFileUnderstandingHandler,
52
53
  createSearchContextTool,
@@ -71,11 +72,13 @@ import {
71
72
  interactionAttentionResolution,
72
73
  isCompactSummaryMessage,
73
74
  isExtensionEnabled,
75
+ isHostContextBlock,
74
76
  isToolAllowedInPlanMode,
75
77
  maybeAdvanceTodoExecutionState,
76
78
  modalityForReadFileUnderstandingKind,
77
79
  pauseExecutionState,
78
80
  prepareSessionCompaction,
81
+ projectHostEnvelopes,
79
82
  registerInteractionHandler,
80
83
  requestInteraction,
81
84
  resolveExtensionIdentity,
@@ -85,8 +88,8 @@ import {
85
88
  shouldPreserveExecutionStateForUserText,
86
89
  supersedeExecutionState,
87
90
  verifyPreparedCompactionCommit
88
- } from "./chunks/chunk-YWWLRZSG.js";
89
- import "./chunks/chunk-5YWEUFLM.js";
91
+ } from "./chunks/chunk-P22SYVGO.js";
92
+ import "./chunks/chunk-W5B3OXHQ.js";
90
93
  import {
91
94
  Deref,
92
95
  EMPTY_ASSISTANT_CONTENT_ERROR,
@@ -111,7 +114,7 @@ import {
111
114
  sortToolModelCandidatesByCapability,
112
115
  streamSimple,
113
116
  type_exports
114
- } from "./chunks/chunk-ZF5FHTES.js";
117
+ } from "./chunks/chunk-2FEDB5GR.js";
115
118
  import {
116
119
  IsArray,
117
120
  IsBigInt,
@@ -53655,7 +53658,12 @@ var ModelRegistry = class {
53655
53658
  import * as fs4 from "fs";
53656
53659
  import * as path4 from "path";
53657
53660
  function getDisabledToolNames(settingsManager) {
53658
- return new Set(typeof settingsManager?.getDisabledTools === "function" ? settingsManager.getDisabledTools() : []);
53661
+ const disabled = new Set(typeof settingsManager?.getDisabledTools === "function" ? settingsManager.getDisabledTools() : []);
53662
+ if (disabled.has("bash") || disabled.has("shell")) {
53663
+ disabled.add("bash");
53664
+ disabled.add("shell");
53665
+ }
53666
+ return disabled;
53659
53667
  }
53660
53668
  function filterEnabledTools(tools, settingsManager) {
53661
53669
  const disabled = getDisabledToolNames(settingsManager);
@@ -53983,11 +53991,12 @@ var SettingsManager = class {
53983
53991
  }
53984
53992
  /** Whether a runtime tool is enabled; absent means "enabled". */
53985
53993
  isToolEnabled(name) {
53986
- return !this.getDisabledTools().includes(name);
53994
+ return !getDisabledToolNames(this).has(name);
53987
53995
  }
53988
53996
  /** Enable/disable a runtime tool by name (persisted in settings.json). */
53989
53997
  setToolEnabled(name, enabled) {
53990
- const disabled = this.getDisabledTools().filter((tool) => tool !== name);
53998
+ const isShell = name === "shell" || name === "bash";
53999
+ const disabled = this.getDisabledTools().filter((tool) => isShell ? tool !== "shell" && tool !== "bash" : tool !== name);
53991
54000
  if (!enabled) disabled.push(name);
53992
54001
  disabled.sort();
53993
54002
  const nextTools = { ...this.settings.tools ?? {} };
@@ -54427,8 +54436,8 @@ var MODE_RULES = {
54427
54436
  "File tools read and write inside the current workspace.",
54428
54437
  "~/.pie is allowlisted for Pie configuration and session support.",
54429
54438
  "Shell commands run from the workspace and return structured output, timeout, and truncation details.",
54430
- "web_research is read-only, uses provider-native web/search/fetch capabilities, and does not grant bash/curl web scraping permission.",
54431
- "Desktop control runs through the computer-use-axi skill CLI via bash: observation commands only read the screen, while act synthesizes real desktop input and goes through normal shell permission prompts.",
54439
+ "web_research is read-only, uses provider-native web/search/fetch capabilities, and does not grant shell/curl web scraping permission.",
54440
+ "Desktop control runs through the computer-use-axi skill CLI via the shell tool: observation commands only read the screen, while act synthesizes real desktop input and goes through normal shell permission prompts.",
54432
54441
  "Protected paths from extensions can block reads/writes even when tools are present."
54433
54442
  ],
54434
54443
  plan: [
@@ -54454,7 +54463,9 @@ var MODE_TITLES = {
54454
54463
  function buildPermissionsData() {
54455
54464
  return {
54456
54465
  modes: Object.fromEntries(Object.entries(MODE_RULES).map(([key, rules]) => [key, [...rules]])),
54457
- tools: Object.entries(BUILTIN_TOOL_CAPABILITY_METADATA).map(([name, meta]) => ({
54466
+ // The legacy bash policy key supports old history/settings; it is not a
54467
+ // second executable tool and must not be advertised as one here.
54468
+ tools: Object.entries(BUILTIN_TOOL_CAPABILITY_METADATA).filter(([name]) => name !== "bash").map(([name, meta]) => ({
54458
54469
  name,
54459
54470
  ...meta.riskClass ? { riskClass: meta.riskClass } : {},
54460
54471
  ...meta.permissionScope ? { permissionScope: meta.permissionScope } : {},
@@ -54478,7 +54489,7 @@ ${toolLines}
54478
54489
 
54479
54490
  Disable or narrow capabilities:
54480
54491
  - Start from a trusted cwd.
54481
- - Use selected tools or extension policy to omit write_file, edit_file, bash, or host-specific tools.
54492
+ - Use selected tools or extension policy to omit write_file, edit_file, the shell tool, or host-specific tools.
54482
54493
  - Keep API keys in environment variables or ~/.pie/models.json, not in prompts.`;
54483
54494
  }
54484
54495
 
@@ -56581,6 +56592,10 @@ function createExtensionContext(extension, cwd, uiContext, abortFn, isIdleFn, ha
56581
56592
  if (!actions?.registerProvider) throw new Error("registerProvider is not available in this host mode");
56582
56593
  return actions.registerProvider(name, config);
56583
56594
  },
56595
+ registerHostEnvelopeProjector(projector) {
56596
+ if (!actions?.registerHostEnvelopeProjector) throw new Error("registerHostEnvelopeProjector is not available in this host mode");
56597
+ return actions.registerHostEnvelopeProjector(projector);
56598
+ },
56584
56599
  async loginProvider(name) {
56585
56600
  if (!actions?.loginProvider) throw new Error("loginProvider is not available in this host mode");
56586
56601
  return actions.loginProvider(name);
@@ -64338,6 +64353,10 @@ var ExtensionRunner = class {
64338
64353
  if (!runner.actions.registerProvider) throw new Error("registerProvider is not available in this host mode");
64339
64354
  return runner.trackUnregisterHandle(runner.actions.registerProvider(name, config));
64340
64355
  },
64356
+ registerHostEnvelopeProjector: (projector) => {
64357
+ if (!runner.actions.registerHostEnvelopeProjector) throw new Error("registerHostEnvelopeProjector is not available in this host mode");
64358
+ return runner.trackUnregisterHandle(runner.actions.registerHostEnvelopeProjector(projector));
64359
+ },
64341
64360
  loginProvider: async (name) => {
64342
64361
  if (!runner.actions.loginProvider) throw new Error("loginProvider is not available in this host mode");
64343
64362
  return runner.actions.loginProvider(name);
@@ -72295,7 +72314,7 @@ ${displayLines.map((line) => theme.fg("toolOutput", line)).join("\n")}`;
72295
72314
  ${theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`)}`;
72296
72315
  }
72297
72316
  }
72298
- } else if (this.toolName === "bash") {
72317
+ } else if (this.toolName === "bash" || this.toolName === "shell") {
72299
72318
  const command = str(this.args?.command);
72300
72319
  const timeout = this.args?.timeout;
72301
72320
  const timeoutSuffix = timeout ? theme.fg("muted", ` (timeout ${timeout}s)`) : "";
@@ -76118,14 +76137,15 @@ function handleHotkeysCommand(host) {
76118
76137
  "",
76119
76138
  theme.fg("muted", "Editor shortcuts:"),
76120
76139
  " Enter Submit message",
76121
- " Shift+Enter New line",
76140
+ " Shift+Enter New line (if ineffective, use Ctrl+J)",
76141
+ " Ctrl+J New line (fallback)",
76122
76142
  ` ${theme.fg("dim", kb.getKeys("steer").join(" / "))} ${theme.fg("muted", "Queue steering message for the active turn")}`,
76123
76143
  ` ${theme.fg("dim", kb.getKeys("dequeue").join(" / "))} ${theme.fg("muted", "Restore queued messages")}`,
76124
76144
  " / Show slash commands",
76125
76145
  " Esc Cancel autocomplete",
76126
76146
  "",
76127
76147
  theme.fg("muted", "Direct mode:"),
76128
- " !!command Execute bash command (bypasses AI)",
76148
+ " !!command Execute a shell command (bypasses AI)",
76129
76149
  "",
76130
76150
  theme.bold(theme.fg("accent", "Slash Commands")),
76131
76151
  "",
@@ -76659,6 +76679,369 @@ function renderCompactionDiffPanel(target, stats) {
76659
76679
  target.safeRequestRender();
76660
76680
  }
76661
76681
 
76682
+ // src/runtime/runtime-mode-context.ts
76683
+ var RUNTIME_MODE_ENVELOPE_KIND = "pie.runtimeMode";
76684
+ var RUNTIME_MODE_ENVELOPE_VERSION = 1;
76685
+ function runtimeModeEnvelopeRegistry() {
76686
+ return sharedRuntimeModeRegistry ??= createHostEnvelopeProjectorRegistry([{
76687
+ kind: RUNTIME_MODE_ENVELOPE_KIND,
76688
+ version: RUNTIME_MODE_ENVELOPE_VERSION,
76689
+ project: (data) => buildRuntimeModeContext(runtimeModeStateFromEnvelope(data))
76690
+ }]);
76691
+ }
76692
+ var sharedRuntimeModeRegistry;
76693
+ function runtimeModeStateFromEnvelope(data) {
76694
+ return {
76695
+ planMode: data.planMode === "planning" ? "planning" : "off",
76696
+ yoloMode: data.yoloMode === true,
76697
+ filesystemScope: data.filesystemScope === "all" ? "all" : "workspace",
76698
+ pendingTransition: typeof data.pendingTransition === "string" ? data.pendingTransition : void 0,
76699
+ activeToolNames: Array.isArray(data.activeToolNames) ? data.activeToolNames.filter((name) => typeof name === "string") : [],
76700
+ planDisabledToolNames: Array.isArray(data.planDisabledToolNames) ? data.planDisabledToolNames.filter((name) => typeof name === "string") : [],
76701
+ currentDate: typeof data.currentDate === "string" ? data.currentDate : void 0,
76702
+ timeZone: typeof data.timeZone === "string" ? data.timeZone : void 0
76703
+ };
76704
+ }
76705
+ function runtimeModeEnvelopeData(state) {
76706
+ return {
76707
+ planMode: state.planMode,
76708
+ yoloMode: state.yoloMode,
76709
+ filesystemScope: state.filesystemScope,
76710
+ ...state.pendingTransition ? { pendingTransition: state.pendingTransition } : {},
76711
+ activeToolNames: [...state.activeToolNames],
76712
+ planDisabledToolNames: [...state.planDisabledToolNames],
76713
+ ...state.currentDate ? { currentDate: state.currentDate } : {},
76714
+ ...state.timeZone ? { timeZone: state.timeZone } : {}
76715
+ };
76716
+ }
76717
+ function runtimeModeEnvelopeBlock(state) {
76718
+ return {
76719
+ type: "hostContext",
76720
+ kind: RUNTIME_MODE_ENVELOPE_KIND,
76721
+ version: RUNTIME_MODE_ENVELOPE_VERSION,
76722
+ data: runtimeModeEnvelopeData(state)
76723
+ };
76724
+ }
76725
+ function isAgentMessage(value) {
76726
+ return !!value && typeof value === "object" && "role" in value;
76727
+ }
76728
+ function prependRuntimeModeBlockToPrompt(input, block) {
76729
+ if (!block) return input;
76730
+ if (typeof input === "string") {
76731
+ return [{ ...block }, { type: "text", text: input }];
76732
+ }
76733
+ if (Array.isArray(input)) {
76734
+ if (input.length > 0 && isAgentMessage(input[0])) {
76735
+ return prependBlockToMessages(input, block);
76736
+ }
76737
+ return [block, ...input];
76738
+ }
76739
+ if (isAgentMessage(input)) {
76740
+ return prependBlockToMessages([input], block)[0];
76741
+ }
76742
+ return [block, ...input];
76743
+ }
76744
+ function prependBlockToMessages(messages, block) {
76745
+ const next = [...messages];
76746
+ let target = -1;
76747
+ for (let index = next.length - 1; index >= 0; index--) {
76748
+ if (next[index]?.role === "user") {
76749
+ target = index;
76750
+ break;
76751
+ }
76752
+ }
76753
+ if (target < 0) {
76754
+ next.push({ role: "user", content: [block], timestamp: Date.now() });
76755
+ return next;
76756
+ }
76757
+ const message = next[target];
76758
+ if (Array.isArray(message.content)) {
76759
+ next[target] = { ...message, content: [block, ...message.content] };
76760
+ } else if (typeof message.content === "string") {
76761
+ next[target] = { ...message, content: [block, { type: "text", text: message.content }] };
76762
+ } else {
76763
+ next.splice(target, 0, { role: "user", content: [block], timestamp: Date.now() });
76764
+ }
76765
+ return next;
76766
+ }
76767
+ var RUNTIME_MODE_CONTEXT_MARKER = "<runtime-mode-context>";
76768
+ var RUNTIME_MODE_CONTEXT_END_MARKER = "</runtime-mode-context>";
76769
+ function buildRuntimeModeContext(state) {
76770
+ const lines = [
76771
+ RUNTIME_MODE_CONTEXT_MARKER,
76772
+ "Current runtime mode:"
76773
+ ];
76774
+ if (state.planMode === "planning") {
76775
+ lines.push(
76776
+ "- Plan mode: active (workspace read-only; plan_scratch permits session-local temporary drafts; submit the finished plan by calling plan_submit with the ordered steps for user approval)."
76777
+ );
76778
+ if (state.planDisabledToolNames.length > 0) {
76779
+ lines.push(
76780
+ `- Disabled this turn by plan mode (calls are denied): ${state.planDisabledToolNames.join(", ")}. Do not call them while planning.`
76781
+ );
76782
+ }
76783
+ } else {
76784
+ lines.push("- Plan mode: off.");
76785
+ }
76786
+ lines.push(
76787
+ `- YOLO mode: ${state.yoloMode ? "on (filesystem sandbox directory restrictions are disabled)" : "off (workspace/protected-path restrictions apply)"}.`,
76788
+ `- Filesystem scope: ${state.filesystemScope === "all" ? "all local paths may be passed directly to file tools when permitted" : "workspace paths and configured safe roots only"}.`
76789
+ );
76790
+ if (state.pendingTransition) {
76791
+ const transitionText = {
76792
+ entered_yolo: "YOLO mode was just enabled for this turn.",
76793
+ exited_yolo: "YOLO mode was just disabled; do not assume previous absolute-path access still applies.",
76794
+ entered_plan: "Plan mode was just enabled; analyze and plan without changing the workspace. Use plan_scratch for temporary drafts, then submit the finished plan by calling plan_submit with the ordered steps for user approval.",
76795
+ exited_plan: "Plan mode was just disabled; execution tools may be used when the task requires them."
76796
+ };
76797
+ lines.push(`- Transition: ${transitionText[state.pendingTransition]}`);
76798
+ }
76799
+ if (state.currentDate) {
76800
+ lines.push(`- Current date: ${state.currentDate}${state.timeZone ? ` (${state.timeZone})` : ""}.`);
76801
+ }
76802
+ if (state.activeToolNames.length > 0) {
76803
+ lines.push(`- Active tools: ${state.activeToolNames.join(", ")}.`);
76804
+ }
76805
+ lines.push(RUNTIME_MODE_CONTEXT_END_MARKER);
76806
+ return lines.join("\n");
76807
+ }
76808
+ function runtimeModeBlockKey(state) {
76809
+ return JSON.stringify({
76810
+ planMode: state.planMode,
76811
+ yoloMode: state.yoloMode,
76812
+ filesystemScope: state.filesystemScope,
76813
+ currentDate: state.currentDate ?? null,
76814
+ activeToolNames: state.activeToolNames,
76815
+ planDisabledToolNames: state.planDisabledToolNames
76816
+ });
76817
+ }
76818
+ function shouldInjectRuntimeModeBlock(lastKey, state) {
76819
+ if (lastKey === null) return true;
76820
+ return runtimeModeBlockKey(state) !== lastKey;
76821
+ }
76822
+ function runtimeModeTraceData(state) {
76823
+ return {
76824
+ yoloMode: state.yoloMode,
76825
+ planMode: state.planMode,
76826
+ filesystemScope: state.filesystemScope,
76827
+ pendingTransition: state.pendingTransition,
76828
+ currentDate: state.currentDate,
76829
+ timeZone: state.timeZone,
76830
+ activeToolCount: state.activeToolNames.length,
76831
+ planDisabledToolCount: state.planDisabledToolNames.length
76832
+ };
76833
+ }
76834
+ function currentDateInZone(timeZone) {
76835
+ const parts = new Intl.DateTimeFormat("en-US", {
76836
+ year: "numeric",
76837
+ month: "2-digit",
76838
+ day: "2-digit",
76839
+ timeZone
76840
+ }).formatToParts(/* @__PURE__ */ new Date());
76841
+ const part = (type) => parts.find((p) => p.type === type)?.value ?? "00";
76842
+ return `${part("year")}-${part("month")}-${part("day")}`;
76843
+ }
76844
+ var RUNTIME_MODE_BLOCK_START_MARKER = "<runtime-mode-context>";
76845
+ function leadingRuntimeBlock(text) {
76846
+ const withoutLeadingWhitespace = text.replace(/^[\t ]+/, "");
76847
+ if (!withoutLeadingWhitespace.startsWith(RUNTIME_MODE_BLOCK_START_MARKER)) return null;
76848
+ const markerStart = text.length - withoutLeadingWhitespace.length;
76849
+ const endIndex = text.indexOf(RUNTIME_MODE_CONTEXT_END_MARKER, markerStart);
76850
+ if (endIndex < 0) return null;
76851
+ const block = text.slice(markerStart, endIndex + RUNTIME_MODE_CONTEXT_END_MARKER.length);
76852
+ const rest = text.slice(endIndex + RUNTIME_MODE_CONTEXT_END_MARKER.length).replace(/^[\t ]*(?:\r?\n){0,2}/, "");
76853
+ return { block, rest };
76854
+ }
76855
+ function stripLeadingRuntimeBlock(text) {
76856
+ const leading = leadingRuntimeBlock(text);
76857
+ return leading ? { rest: leading.rest } : null;
76858
+ }
76859
+ function lastUserMessageCarriesEnvelopeBlock(messages, block) {
76860
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
76861
+ const message = messages[index];
76862
+ if (message?.role !== "user") continue;
76863
+ const content = message.content;
76864
+ if (!Array.isArray(content) || content.length === 0) return false;
76865
+ const first = content[0];
76866
+ return first?.type === "hostContext" && first.kind === block.kind && first.version === block.version && JSON.stringify(first.data) === JSON.stringify(block.data);
76867
+ }
76868
+ return false;
76869
+ }
76870
+ function refreshRuntimeModeBlockOnLastUserMessage(messages, block) {
76871
+ for (let i = messages.length - 1; i >= 0; i--) {
76872
+ const message = messages[i];
76873
+ if (message?.role !== "user") continue;
76874
+ const content = message.content;
76875
+ if (typeof content === "string") {
76876
+ const stripped = stripLeadingRuntimeBlock(content);
76877
+ const rest = stripped ? stripped.rest : content;
76878
+ if (!rest) {
76879
+ const copy2 = [...messages];
76880
+ copy2[i] = { ...message, content: [block] };
76881
+ return copy2;
76882
+ }
76883
+ const copy = [...messages];
76884
+ copy[i] = { ...message, content: [block, { type: "text", text: rest }] };
76885
+ return copy;
76886
+ }
76887
+ if (Array.isArray(content)) {
76888
+ const first = content[0];
76889
+ if (first?.type === "hostContext") {
76890
+ if (first === block) return messages;
76891
+ const copy2 = [...messages];
76892
+ const contentCopy = [...content];
76893
+ contentCopy[0] = block;
76894
+ copy2[i] = { ...message, content: contentCopy };
76895
+ return copy2;
76896
+ }
76897
+ let userBlocks = content;
76898
+ const firstText = content[0];
76899
+ if (firstText?.type === "text" && typeof firstText.text === "string") {
76900
+ const stripped = stripLeadingRuntimeBlock(firstText.text);
76901
+ if (stripped) {
76902
+ userBlocks = stripped.rest ? [{ type: "text", text: stripped.rest }, ...content.slice(1)] : content.slice(1);
76903
+ }
76904
+ }
76905
+ const copy = [...messages];
76906
+ copy[i] = { ...message, content: [block, ...userBlocks] };
76907
+ return copy;
76908
+ }
76909
+ return messages;
76910
+ }
76911
+ return messages;
76912
+ }
76913
+ var RuntimeModeBlockInjector = class {
76914
+ constructor(options) {
76915
+ this.options = options;
76916
+ }
76917
+ options;
76918
+ lastKey = null;
76919
+ /** Exact frozen envelope block, including its consumed one-shot transition. */
76920
+ lastFrozenBlock = null;
76921
+ /**
76922
+ * Request-view overlay. While active, the CURRENT block is re-derived on
76923
+ * every request from the live message snapshot and applied to the latest
76924
+ * user message. It is not cached into history and it ends only when a real
76925
+ * user message freezes the current block (see `nextBlock` + the transform),
76926
+ * so consecutive tool rounds after a compaction retry keep the fresh state
76927
+ * instead of losing it after one request.
76928
+ */
76929
+ overlayActive = false;
76930
+ reset() {
76931
+ this.lastKey = null;
76932
+ this.lastFrozenBlock = null;
76933
+ this.overlayActive = true;
76934
+ }
76935
+ /**
76936
+ * Arm the request-view overlay without resetting the frozen-block key. Used
76937
+ * when the runtime shape changed while a turn is still running (compaction
76938
+ * checkpoint committed, plan approval, YOLO toggle inside a tool turn): the
76939
+ * next and subsequent model requests must see the current state even though
76940
+ * no new user message has been accepted yet.
76941
+ */
76942
+ armRefresh() {
76943
+ this.overlayActive = true;
76944
+ }
76945
+ /**
76946
+ * Envelope block to prepend to the next user message, or null when
76947
+ * unchanged. Does NOT end the overlay: the block is only proof once the
76948
+ * message carrying it reaches the request transform.
76949
+ */
76950
+ nextBlock() {
76951
+ return this.freshBlock();
76952
+ }
76953
+ /**
76954
+ * Current envelope block for a request that reuses existing history
76955
+ * (overflow retry after compaction, manual-compact continue, or the tool
76956
+ * rounds that follow a mid-turn mode change). Returns the same block for
76957
+ * every request until the latest user message itself carries that block;
76958
+ * then the overlay is retired and null is returned. Derivation is per
76959
+ * request from the given snapshot; no message array is cached.
76960
+ */
76961
+ consumeRetryRefreshBlock(messages) {
76962
+ if (!this.overlayActive) return null;
76963
+ const state = this.options.getState();
76964
+ const block = runtimeModeEnvelopeBlock(state);
76965
+ const frozenCurrent = this.lastKey === runtimeModeBlockKey(state) ? this.lastFrozenBlock : null;
76966
+ if (messages && (lastUserMessageCarriesEnvelopeBlock(messages, block) || frozenCurrent !== null && lastUserMessageCarriesEnvelopeBlock(messages, frozenCurrent))) {
76967
+ this.overlayActive = false;
76968
+ return null;
76969
+ }
76970
+ return block;
76971
+ }
76972
+ freshBlock() {
76973
+ const state = this.options.getState();
76974
+ if (!shouldInjectRuntimeModeBlock(this.lastKey, state)) return null;
76975
+ this.lastKey = runtimeModeBlockKey(state);
76976
+ if (state.pendingTransition) this.options.consumeTransition();
76977
+ this.options.noteTrace(state);
76978
+ this.lastFrozenBlock = runtimeModeEnvelopeBlock(state);
76979
+ return this.lastFrozenBlock;
76980
+ }
76981
+ };
76982
+
76983
+ // src/runtime/host-envelope-registry.ts
76984
+ var sharedCliEnvelopeRegistry;
76985
+ function cliHostEnvelopeRegistry() {
76986
+ if (sharedCliEnvelopeRegistry) return sharedCliEnvelopeRegistry;
76987
+ sharedCliEnvelopeRegistry = createHostEnvelopeProjectorRegistry();
76988
+ for (const { kind, version } of runtimeModeEnvelopeRegistry().listPins()) {
76989
+ sharedCliEnvelopeRegistry.register({
76990
+ kind,
76991
+ version,
76992
+ project: (data) => runtimeModeEnvelopeRegistry().resolve(kind, version).project(data)
76993
+ });
76994
+ }
76995
+ return sharedCliEnvelopeRegistry;
76996
+ }
76997
+ function registerExternalHostEnvelopeProjector(projector) {
76998
+ cliHostEnvelopeRegistry().register(projector);
76999
+ return () => {
77000
+ };
77001
+ }
77002
+ function cliHostEnvelopeKinds() {
77003
+ return cliHostEnvelopeRegistry().listPins().map(({ kind, version }) => `${kind}@${version}`).sort();
77004
+ }
77005
+ function projectCliHostEnvelopes(messages) {
77006
+ return projectHostEnvelopes(messages, cliHostEnvelopeRegistry());
77007
+ }
77008
+ function isIntakeHostContextError(value) {
77009
+ return value.code !== void 0;
77010
+ }
77011
+ function intakeHostContextBlock(value) {
77012
+ if (!isHostContextBlock(value)) {
77013
+ return {
77014
+ ok: false,
77015
+ code: "host_context_invalid_shape",
77016
+ message: "hostContext must be { type: 'hostContext', kind: string, version: integer >= 1, data: object }"
77017
+ };
77018
+ }
77019
+ const block = value;
77020
+ const projector = cliHostEnvelopeRegistry().resolve(block.kind, block.version);
77021
+ if (!projector) {
77022
+ return {
77023
+ ok: false,
77024
+ code: "host_context_unknown_projector",
77025
+ message: `no projector for ${block.kind}@${block.version}; this build supports: ${cliHostEnvelopeKinds().join(", ")}`
77026
+ };
77027
+ }
77028
+ try {
77029
+ const text = projector.project(block.data);
77030
+ if (typeof text !== "string" || text.trim().length === 0) throw new Error("projector returned empty text");
77031
+ } catch (error) {
77032
+ return {
77033
+ ok: false,
77034
+ code: "host_context_unknown_projector",
77035
+ message: `projector for ${block.kind}@${block.version} rejected the data: ${error instanceof Error ? error.message : String(error)}`
77036
+ };
77037
+ }
77038
+ return { ok: true, block };
77039
+ }
77040
+ function intakeHostContextFailure(value) {
77041
+ const result = intakeHostContextBlock(value);
77042
+ return isIntakeHostContextError(result) ? { code: result.code, message: result.message } : void 0;
77043
+ }
77044
+
76662
77045
  // src/commands/interactive-compact-command.ts
76663
77046
  async function handleCompactCommand(host) {
76664
77047
  if (host.submitLock || host.isProcessing || host.isCompacting) {
@@ -76712,7 +77095,7 @@ async function handleCompactCommand(host) {
76712
77095
  if (beforeCompact?.summary) {
76713
77096
  summary = beforeCompact.summary;
76714
77097
  } else {
76715
- summary = await createCompactionSummary(preparation.messages, compactOptions);
77098
+ summary = await createCompactionSummary(projectCliHostEnvelopes(preparation.messages), compactOptions);
76716
77099
  }
76717
77100
  const verified = verifyPreparedCompactionCommit({
76718
77101
  preparation,
@@ -78884,9 +79267,7 @@ function queueMessage(host, text, mode, options = {}) {
78884
79267
  const clearEditor = options.clearEditor ?? true;
78885
79268
  host.sessionTrace.notePendingUserText(text);
78886
79269
  const runtimeBlock = host.runtimeModeBlockForNextMessage?.();
78887
- const content = runtimeBlock ? `${runtimeBlock}
78888
-
78889
- ${text}` : text;
79270
+ const content = runtimeBlock ? [runtimeBlock, { type: "text", text }] : [{ type: "text", text }];
78890
79271
  void host.agentSessionController.prompt(
78891
79272
  { role: "user", content, timestamp: Date.now() },
78892
79273
  {
@@ -79274,7 +79655,11 @@ When the user asks about Pie itself, its extensions, skills, TUI, model configur
79274
79655
  function buildCliSystemPrompt(options) {
79275
79656
  const tools = options.tools.map((tool) => ({
79276
79657
  name: tool.name,
79277
- description: ""
79658
+ description: "",
79659
+ // Metadata travels with the projection: the <tools> section resolves
79660
+ // policy from tool-carried metadata first, so a renamed tool cannot
79661
+ // silently disappear from the prompt.
79662
+ capabilityMetadata: tool.capabilityMetadata
79278
79663
  }));
79279
79664
  return [
79280
79665
  options.customPrompt?.trim() || "You are Pie, a helpful AI coding assistant.",
@@ -79373,7 +79758,7 @@ async function createCliAutoCompactionResult(params) {
79373
79758
  systemPrompt: params.systemPrompt,
79374
79759
  signal: params.context.signal
79375
79760
  };
79376
- const summary = await createCompactionSummary(params.context.messages, compactOptions);
79761
+ const summary = await createCompactionSummary(projectCliHostEnvelopes(params.context.messages), compactOptions);
79377
79762
  if (params.context.signal?.aborted) {
79378
79763
  return { aborted: true };
79379
79764
  }
@@ -79580,10 +79965,7 @@ function estimateMessageTokens(msg) {
79580
79965
  return estimateTokens(msg);
79581
79966
  }
79582
79967
  function buildSystemPrompt(host) {
79583
- const tools = (host.agent?.state?.tools ?? host.options.tools ?? []).filter((tool) => typeof tool.name === "string").map((tool) => ({
79584
- name: tool.name,
79585
- description: tool.description
79586
- }));
79968
+ const tools = (host.agent?.state?.tools ?? host.options.tools ?? []).filter((tool) => typeof tool.name === "string");
79587
79969
  return buildCliSystemPrompt({
79588
79970
  cwd: host.options.cwd,
79589
79971
  tools,
@@ -79593,7 +79975,7 @@ function buildSystemPrompt(host) {
79593
79975
  }
79594
79976
  function getAllToolNames(host) {
79595
79977
  if (!host.agent?.state?.tools) {
79596
- return [...CLI_FILESYSTEM_TOOL_NAMES, "bash"];
79978
+ return [...CLI_FILESYSTEM_TOOL_NAMES, "shell"];
79597
79979
  }
79598
79980
  return host.agent.state.tools.map((tool) => tool.name);
79599
79981
  }
@@ -80594,277 +80976,6 @@ function createInteractiveAgentSessionController(host) {
80594
80976
  });
80595
80977
  }
80596
80978
 
80597
- // src/runtime/runtime-mode-context.ts
80598
- function isAgentMessage(value) {
80599
- return !!value && typeof value === "object" && "role" in value;
80600
- }
80601
- function prependRuntimeModeBlockToPrompt(input, block) {
80602
- if (!block) return input;
80603
- if (typeof input === "string") {
80604
- return input.trim() ? `${block}
80605
-
80606
- ${input}` : block;
80607
- }
80608
- if (Array.isArray(input)) {
80609
- if (input.length > 0 && isAgentMessage(input[0])) {
80610
- return prependBlockToMessages(input, block);
80611
- }
80612
- return [{ type: "text", text: block }, ...input];
80613
- }
80614
- if (isAgentMessage(input)) {
80615
- return prependBlockToMessages([input], block)[0];
80616
- }
80617
- return [{ type: "text", text: block }, ...input];
80618
- }
80619
- function prependBlockToMessages(messages, block) {
80620
- const next = [...messages];
80621
- let target = -1;
80622
- for (let index = next.length - 1; index >= 0; index--) {
80623
- if (next[index]?.role === "user") {
80624
- target = index;
80625
- break;
80626
- }
80627
- }
80628
- if (target < 0) {
80629
- next.push({ role: "user", content: block, timestamp: Date.now() });
80630
- return next;
80631
- }
80632
- const message = next[target];
80633
- if (typeof message.content === "string") {
80634
- next[target] = { ...message, content: `${block}
80635
-
80636
- ${message.content}` };
80637
- } else if (Array.isArray(message.content)) {
80638
- next[target] = { ...message, content: [{ type: "text", text: block }, ...message.content] };
80639
- } else {
80640
- next.splice(target, 0, { role: "user", content: block, timestamp: Date.now() });
80641
- }
80642
- return next;
80643
- }
80644
- var RUNTIME_MODE_CONTEXT_MARKER = "<runtime-mode-context>";
80645
- var RUNTIME_MODE_CONTEXT_END_MARKER = "</runtime-mode-context>";
80646
- function buildRuntimeModeContext(state) {
80647
- const lines = [
80648
- RUNTIME_MODE_CONTEXT_MARKER,
80649
- "Current runtime mode:"
80650
- ];
80651
- if (state.planMode === "planning") {
80652
- lines.push(
80653
- "- Plan mode: active (workspace read-only; plan_scratch permits session-local temporary drafts; submit the finished plan by calling plan_submit with the ordered steps for user approval)."
80654
- );
80655
- if (state.planDisabledToolNames.length > 0) {
80656
- lines.push(
80657
- `- Disabled this turn by plan mode (calls are denied): ${state.planDisabledToolNames.join(", ")}. Do not call them while planning.`
80658
- );
80659
- }
80660
- } else {
80661
- lines.push("- Plan mode: off.");
80662
- }
80663
- lines.push(
80664
- `- YOLO mode: ${state.yoloMode ? "on (filesystem sandbox directory restrictions are disabled)" : "off (workspace/protected-path restrictions apply)"}.`,
80665
- `- Filesystem scope: ${state.filesystemScope === "all" ? "all local paths may be passed directly to file tools when permitted" : "workspace paths and configured safe roots only"}.`
80666
- );
80667
- if (state.pendingTransition) {
80668
- const transitionText = {
80669
- entered_yolo: "YOLO mode was just enabled for this turn.",
80670
- exited_yolo: "YOLO mode was just disabled; do not assume previous absolute-path access still applies.",
80671
- entered_plan: "Plan mode was just enabled; analyze and plan without changing the workspace. Use plan_scratch for temporary drafts, then submit the finished plan by calling plan_submit with the ordered steps for user approval.",
80672
- exited_plan: "Plan mode was just disabled; execution tools may be used when the task requires them."
80673
- };
80674
- lines.push(`- Transition: ${transitionText[state.pendingTransition]}`);
80675
- }
80676
- if (state.currentDate) {
80677
- lines.push(`- Current date: ${state.currentDate}${state.timeZone ? ` (${state.timeZone})` : ""}.`);
80678
- }
80679
- if (state.activeToolNames.length > 0) {
80680
- lines.push(`- Active tools: ${state.activeToolNames.join(", ")}.`);
80681
- }
80682
- lines.push(RUNTIME_MODE_CONTEXT_END_MARKER);
80683
- return lines.join("\n");
80684
- }
80685
- function runtimeModeBlockKey(state) {
80686
- return JSON.stringify({
80687
- planMode: state.planMode,
80688
- yoloMode: state.yoloMode,
80689
- filesystemScope: state.filesystemScope,
80690
- currentDate: state.currentDate ?? null,
80691
- activeToolNames: state.activeToolNames,
80692
- planDisabledToolNames: state.planDisabledToolNames
80693
- });
80694
- }
80695
- function shouldInjectRuntimeModeBlock(lastKey, state) {
80696
- if (lastKey === null) return true;
80697
- return runtimeModeBlockKey(state) !== lastKey;
80698
- }
80699
- function runtimeModeTraceData(state) {
80700
- return {
80701
- yoloMode: state.yoloMode,
80702
- planMode: state.planMode,
80703
- filesystemScope: state.filesystemScope,
80704
- pendingTransition: state.pendingTransition,
80705
- currentDate: state.currentDate,
80706
- timeZone: state.timeZone,
80707
- activeToolCount: state.activeToolNames.length,
80708
- planDisabledToolCount: state.planDisabledToolNames.length
80709
- };
80710
- }
80711
- function currentDateInZone(timeZone) {
80712
- const parts = new Intl.DateTimeFormat("en-US", {
80713
- year: "numeric",
80714
- month: "2-digit",
80715
- day: "2-digit",
80716
- timeZone
80717
- }).formatToParts(/* @__PURE__ */ new Date());
80718
- const part = (type) => parts.find((p) => p.type === type)?.value ?? "00";
80719
- return `${part("year")}-${part("month")}-${part("day")}`;
80720
- }
80721
- var RUNTIME_MODE_BLOCK_START_MARKER = "<runtime-mode-context>";
80722
- function leadingRuntimeBlock(text) {
80723
- const withoutLeadingWhitespace = text.replace(/^[\t ]+/, "");
80724
- if (!withoutLeadingWhitespace.startsWith(RUNTIME_MODE_BLOCK_START_MARKER)) return null;
80725
- const markerStart = text.length - withoutLeadingWhitespace.length;
80726
- const endIndex = text.indexOf(RUNTIME_MODE_CONTEXT_END_MARKER, markerStart);
80727
- if (endIndex < 0) return null;
80728
- const block = text.slice(markerStart, endIndex + RUNTIME_MODE_CONTEXT_END_MARKER.length);
80729
- const rest = text.slice(endIndex + RUNTIME_MODE_CONTEXT_END_MARKER.length).replace(/^[\t ]*(?:\r?\n){0,2}/, "");
80730
- return { block, rest };
80731
- }
80732
- function stripLeadingRuntimeBlock(text) {
80733
- const leading = leadingRuntimeBlock(text);
80734
- return leading ? { rest: leading.rest } : null;
80735
- }
80736
- function leadingRuntimeBlockOfMessage(message) {
80737
- if (message.role !== "user") return null;
80738
- const content = message.content;
80739
- if (typeof content === "string") return leadingRuntimeBlock(content)?.block ?? null;
80740
- if (!Array.isArray(content)) return null;
80741
- const first = content[0];
80742
- if (first?.type !== "text" || typeof first.text !== "string") return null;
80743
- return leadingRuntimeBlock(first.text)?.block ?? null;
80744
- }
80745
- function lastUserMessageCarriesRuntimeBlock(messages, block) {
80746
- for (let index = messages.length - 1; index >= 0; index -= 1) {
80747
- const message = messages[index];
80748
- if (message?.role !== "user") continue;
80749
- return leadingRuntimeBlockOfMessage(message) === block;
80750
- }
80751
- return false;
80752
- }
80753
- function applyRuntimeBlockToText(text, block) {
80754
- const split = stripLeadingRuntimeBlock(text);
80755
- const rest = split ? split.rest : text;
80756
- const next = rest.length > 0 ? `${block}
80757
-
80758
- ${rest}` : block;
80759
- return next === text ? text : next;
80760
- }
80761
- function applyRuntimeBlockToContentArray(content, block) {
80762
- const first = content[0];
80763
- if (first && typeof first === "object" && first.type === "text" && typeof first.text === "string") {
80764
- const text = first.text;
80765
- if (stripLeadingRuntimeBlock(text)) {
80766
- const nextText = applyRuntimeBlockToText(text, block);
80767
- if (nextText === text) return content;
80768
- const copy = [...content];
80769
- copy[0] = { ...first, text: nextText };
80770
- return copy;
80771
- }
80772
- }
80773
- return [{ type: "text", text: block }, ...content];
80774
- }
80775
- function refreshRuntimeModeBlockOnLastUserMessage(messages, block) {
80776
- for (let i = messages.length - 1; i >= 0; i--) {
80777
- const message = messages[i];
80778
- if (message?.role !== "user") continue;
80779
- const content = message.content;
80780
- if (typeof content === "string") {
80781
- const next = applyRuntimeBlockToText(content, block);
80782
- if (next === content) return messages;
80783
- const copy = [...messages];
80784
- copy[i] = { ...message, content: next };
80785
- return copy;
80786
- }
80787
- if (Array.isArray(content)) {
80788
- const next = applyRuntimeBlockToContentArray(content, block);
80789
- if (next === content) return messages;
80790
- const copy = [...messages];
80791
- copy[i] = { ...message, content: next };
80792
- return copy;
80793
- }
80794
- return messages;
80795
- }
80796
- return messages;
80797
- }
80798
- var RuntimeModeBlockInjector = class {
80799
- constructor(options) {
80800
- this.options = options;
80801
- }
80802
- options;
80803
- lastKey = null;
80804
- /** Exact frozen block, including its consumed one-shot transition. */
80805
- lastFrozenBlock = null;
80806
- /**
80807
- * Request-view overlay. While active, the CURRENT block is re-derived on
80808
- * every request from the live message snapshot and applied to the latest
80809
- * user message. It is not cached into history and it ends only when a real
80810
- * user message freezes the current block (see `nextBlock` + the transform),
80811
- * so consecutive tool rounds after a compaction retry keep the fresh state
80812
- * instead of losing it after one request.
80813
- */
80814
- overlayActive = false;
80815
- reset() {
80816
- this.lastKey = null;
80817
- this.lastFrozenBlock = null;
80818
- this.overlayActive = true;
80819
- }
80820
- /**
80821
- * Arm the request-view overlay without resetting the frozen-block key. Used
80822
- * when the runtime shape changed while a turn is still running (compaction
80823
- * checkpoint committed, plan approval, YOLO toggle inside a tool turn): the
80824
- * next and subsequent model requests must see the current state even though
80825
- * no new user message has been accepted yet.
80826
- */
80827
- armRefresh() {
80828
- this.overlayActive = true;
80829
- }
80830
- /**
80831
- * Block to prepend to the next user message, or null when unchanged. Does
80832
- * NOT end the overlay: the block is only proof once the message carrying it
80833
- * reaches the request transform.
80834
- */
80835
- nextBlock() {
80836
- return this.freshBlock();
80837
- }
80838
- /**
80839
- * Current block for a request that reuses existing history (overflow retry
80840
- * after compaction, manual-compact continue, or the tool rounds that follow
80841
- * a mid-turn mode change). Returns the same block for every request until
80842
- * the latest user message itself carries that block; then the overlay is
80843
- * retired and null is returned. Derivation is per request from the given
80844
- * snapshot; no message array is cached.
80845
- */
80846
- consumeRetryRefreshBlock(messages) {
80847
- if (!this.overlayActive) return null;
80848
- const state = this.options.getState();
80849
- const block = buildRuntimeModeContext(state);
80850
- const frozenCurrent = this.lastKey === runtimeModeBlockKey(state) ? this.lastFrozenBlock : null;
80851
- if (messages && (lastUserMessageCarriesRuntimeBlock(messages, block) || frozenCurrent !== null && lastUserMessageCarriesRuntimeBlock(messages, frozenCurrent))) {
80852
- this.overlayActive = false;
80853
- return null;
80854
- }
80855
- return block;
80856
- }
80857
- freshBlock() {
80858
- const state = this.options.getState();
80859
- if (!shouldInjectRuntimeModeBlock(this.lastKey, state)) return null;
80860
- this.lastKey = runtimeModeBlockKey(state);
80861
- if (state.pendingTransition) this.options.consumeTransition();
80862
- this.options.noteTrace(state);
80863
- this.lastFrozenBlock = buildRuntimeModeContext(state);
80864
- return this.lastFrozenBlock;
80865
- }
80866
- };
80867
-
80868
80979
  // src/runtime/cache-retention.ts
80869
80980
  function createCacheRetentionStreamFn(configured, getConfiguredRetention) {
80870
80981
  const base = configured ?? ((model, context, options) => streamSimple(model, context, options));
@@ -81727,6 +81838,7 @@ async function loadInteractiveExtensions(host) {
81727
81838
  host.noteRuntimeModeChanged("extension_register_provider");
81728
81839
  return unregister;
81729
81840
  },
81841
+ registerHostEnvelopeProjector: (projector) => registerExternalHostEnvelopeProjector(projector),
81730
81842
  loginProvider: async (name) => {
81731
81843
  const loggedIn = await loginExtensionProvider({
81732
81844
  provider: name,
@@ -81860,7 +81972,7 @@ var InteractiveMode = class {
81860
81972
  pendingYoloModeTransition = null;
81861
81973
  executionReminder = null;
81862
81974
  pendingRuntimeGuardAdvisory = null;
81863
- /** Freezes runtime mode blocks into user messages (cache-stable history). */
81975
+ /** Freezes runtime mode envelope blocks into user messages (cache-stable history). */
81864
81976
  runtimeModeBlocks = new RuntimeModeBlockInjector({
81865
81977
  getState: () => this.getRuntimeModeState(),
81866
81978
  consumeTransition: () => this.consumeRuntimeModeTransition(),
@@ -82037,7 +82149,7 @@ var InteractiveMode = class {
82037
82149
  });
82038
82150
  rebuildSkillAwareTools(sourceTools) {
82039
82151
  const preservedTools = sourceTools.filter(
82040
- (tool) => !CLI_FILESYSTEM_TOOL_NAMES.includes(tool.name) && tool.name !== "bash"
82152
+ (tool) => !CLI_FILESYSTEM_TOOL_NAMES.includes(tool.name) && tool.name !== "bash" && tool.name !== "shell"
82041
82153
  );
82042
82154
  const bashTool = createBashTool(this.options.cwd, { isYoloMode: () => this.yoloMode, getEnv: () => computerUseOwnerEnv(this.sessionManager.getActiveSessionId() || COMPUTER_USE_STANDALONE_OWNER, String(process.pid)) });
82043
82155
  const fileSystemToolOptions = (allowlistedDirs) => ({
@@ -82092,16 +82204,13 @@ var InteractiveMode = class {
82092
82204
  resetRuntimeModeBlocks() {
82093
82205
  this.runtimeModeBlocks.reset();
82094
82206
  }
82095
- /** Runtime mode block for the next user message; frozen into it (append-only history, cache-stable prefixes). */
82207
+ /** Runtime mode envelope block for the next user message; frozen into it (append-only history, cache-stable prefixes). */
82096
82208
  runtimeModeBlockForNextMessage() {
82097
82209
  return this.runtimeModeBlocks.nextBlock();
82098
82210
  }
82099
82211
  getPendingRuntimeModeTransition() {
82100
- if (this.pendingYoloModeTransition === "enter") return "entered_yolo";
82101
- if (this.pendingYoloModeTransition === "exit") return "exited_yolo";
82102
- if (this.pendingPlanModeTransition === "enter") return "entered_plan";
82103
- if (this.pendingPlanModeTransition === "exit") return "exited_plan";
82104
- return void 0;
82212
+ const yolo = this.pendingYoloModeTransition === "enter" ? "entered_yolo" : this.pendingYoloModeTransition === "exit" ? "exited_yolo" : void 0;
82213
+ return yolo ?? (this.pendingPlanModeTransition === "enter" ? "entered_plan" : this.pendingPlanModeTransition === "exit" ? "exited_plan" : void 0);
82105
82214
  }
82106
82215
  consumeRuntimeModeTransition() {
82107
82216
  this.pendingYoloModeTransition = null;
@@ -82190,7 +82299,7 @@ var InteractiveMode = class {
82190
82299
  async init() {
82191
82300
  const activeSession = this.sessionManager.getActiveSession();
82192
82301
  this.thinkingLevel = resolveInitialThinkingLevel({ session: activeSession, settingsManager: this.options.settingsManager });
82193
- const { Agent } = await import("./chunks/src-6WK5KYWQ.js");
82302
+ const { Agent } = await import("./chunks/src-C2CBPEUY.js");
82194
82303
  this.agent = new Agent({
82195
82304
  initialState: {
82196
82305
  systemPrompt: this.buildSystemPrompt(),
@@ -82201,14 +82310,21 @@ var InteractiveMode = class {
82201
82310
  apiKey: this.options.apiKey,
82202
82311
  sessionId: activeSession?.id,
82203
82312
  streamFn: createTransportDiagnosticsStreamFn(createCacheRetentionStreamFn(this.options.streamFn, () => this.options.settingsManager.getPromptCacheRetention()), createTraceTransportSink((type, meta) => this.sessionTrace.noteRuntimeEvent(type, meta))),
82204
- transformContext: async (messages) => applyRuntimeRequestTransform(messages, {
82205
- takeAdvisory: () => {
82206
- const advisory = this.pendingRuntimeGuardAdvisory;
82207
- this.pendingRuntimeGuardAdvisory = null;
82208
- return advisory;
82209
- },
82210
- takeRetryBlock: (snapshot) => this.runtimeModeBlocks.consumeRetryRefreshBlock(snapshot)
82211
- })
82313
+ transformContext: async (messages) => {
82314
+ const structured = applyRuntimeRequestTransform(messages, {
82315
+ takeAdvisory: () => {
82316
+ const advisory = this.pendingRuntimeGuardAdvisory;
82317
+ this.pendingRuntimeGuardAdvisory = null;
82318
+ return advisory;
82319
+ },
82320
+ takeRetryBlock: (snapshot) => this.runtimeModeBlocks.consumeRetryRefreshBlock(snapshot)
82321
+ });
82322
+ return projectHostEnvelopes(
82323
+ structured,
82324
+ cliHostEnvelopeRegistry(),
82325
+ (outcome) => this.sessionTrace.noteRuntimeEvent(outcome.degraded ? "host_envelope_degraded" : "host_envelope_projected", outcome.audit)
82326
+ );
82327
+ }
82212
82328
  });
82213
82329
  this.agentSessionController = createInteractiveAgentSessionController(this.controllerSetupHost);
82214
82330
  this.updateTerminalTitle();
@@ -82995,6 +83111,7 @@ async function loadPrintModeExtensions(params) {
82995
83111
  setSessionName: (name) => params.sessionManager.setSessionName(name),
82996
83112
  getSessionName: () => params.sessionManager.getSessionName(),
82997
83113
  registerProvider: (name, config) => params.modelRegistry.registerProvider(name, config),
83114
+ registerHostEnvelopeProjector: (projector) => registerExternalHostEnvelopeProjector(projector),
82998
83115
  loginProvider: (name) => loginExtensionProvider({
82999
83116
  provider: name,
83000
83117
  modelRegistry: params.modelRegistry,
@@ -83049,7 +83166,7 @@ async function runPrintMode(params) {
83049
83166
  `);
83050
83167
  console.log("Assistant: ");
83051
83168
  }
83052
- const { Agent: AgentClass } = await import("./chunks/src-6WK5KYWQ.js");
83169
+ const { Agent: AgentClass } = await import("./chunks/src-C2CBPEUY.js");
83053
83170
  let agent;
83054
83171
  const printModeExtensions = await loadPrintModeExtensions({
83055
83172
  cwd: runtime.cwd,
@@ -83082,7 +83199,7 @@ async function runPrintMode(params) {
83082
83199
  cwd: runtime.cwd,
83083
83200
  skillsSection: runtime.skillsSection,
83084
83201
  knowledgeSection: runtime.knowledgeSection,
83085
- tools: printModeTools.map((tool) => ({ name: tool.name, description: tool.description }))
83202
+ tools: printModeTools
83086
83203
  });
83087
83204
  const printThinkingLevel = resolveInitialThinkingLevel({ session: runtime.sessionManager.getActiveSession(), settingsManager: runtime.settingsManager });
83088
83205
  agent = new AgentClass({
@@ -83093,6 +83210,12 @@ async function runPrintMode(params) {
83093
83210
  thinkingLevel: printThinkingLevel
83094
83211
  },
83095
83212
  apiKey: runtime.apiKey,
83213
+ // Print mode restores interactive sessions verbatim (AgentSessionController
83214
+ // loads stored history on construction). Those sessions carry persisted
83215
+ // hostContext facts; project them to model-visible text or the provider
83216
+ // converters reject the request (`Unprojected hostContext block`). The
83217
+ // persisted history is never rewritten — projection builds the wire view.
83218
+ transformContext: async (messages) => projectCliHostEnvelopes(messages),
83096
83219
  // Body-free transport timeline for the main (print-mode) agent.
83097
83220
  streamFn: createTransportDiagnosticsStreamFn(params.streamFn ?? streamSimple, logTransportLifecycle)
83098
83221
  });
@@ -83336,14 +83459,17 @@ function parseInput(line) {
83336
83459
  };
83337
83460
  }
83338
83461
  if (parsed.type === "sidechat.request" && typeof parsed.requestId === "string" && typeof parsed.prompt === "string") {
83462
+ if (parsed.hostContext !== void 0 && !isHostContextBlock(parsed.hostContext)) return null;
83339
83463
  return {
83340
83464
  type: "sidechat.request",
83341
83465
  sessionId: typeof parsed.sessionId === "string" ? parsed.sessionId : void 0,
83342
83466
  requestId: parsed.requestId,
83343
- prompt: parsed.prompt
83467
+ prompt: parsed.prompt,
83468
+ hostContext: parsed.hostContext
83344
83469
  };
83345
83470
  }
83346
83471
  if ((parsed.type === "turn.request" || parsed.type === "turn.input") && typeof parsed.turnId === "string" && typeof parsed.prompt === "string") {
83472
+ if (parsed.hostContext !== void 0 && !isHostContextBlock(parsed.hostContext)) return null;
83347
83473
  const streamingBehavior = parsed.streamingBehavior;
83348
83474
  if (streamingBehavior !== void 0 && streamingBehavior !== "steer" && streamingBehavior !== "followUp") {
83349
83475
  return null;
@@ -83353,6 +83479,7 @@ function parseInput(line) {
83353
83479
  sessionId: typeof parsed.sessionId === "string" ? parsed.sessionId : void 0,
83354
83480
  turnId: parsed.turnId,
83355
83481
  prompt: parsed.prompt,
83482
+ hostContext: parsed.hostContext,
83356
83483
  streamingBehavior,
83357
83484
  cwd: typeof parsed.cwd === "string" ? parsed.cwd : void 0,
83358
83485
  metadata: isRecord3(parsed.metadata) ? parsed.metadata : void 0
@@ -83461,6 +83588,7 @@ async function loadJsonModeExtensions(params) {
83461
83588
  getRuntimeTools: () => params.getAgent()?.state.tools ?? [],
83462
83589
  resolveModelClass: (modelClass) => params.runtime.modelRegistry.resolveModelClass(modelClass),
83463
83590
  resolveModelRef: (modelRef) => params.runtime.modelRegistry.resolveModelRef(modelRef),
83591
+ registerHostEnvelopeProjector: (projector) => registerExternalHostEnvelopeProjector(projector),
83464
83592
  getMessages: () => params.getAgent()?.state.messages ?? [],
83465
83593
  getSessionMetadata: () => ({ ...params.runtime.sessionManager.getActiveSession()?.metadata ?? {} }),
83466
83594
  setSessionMetadata: (key, value) => {
@@ -83535,7 +83663,14 @@ async function runJsonEventsMode(params) {
83535
83663
  const prewarmTimeoutMs = Number(process.env.PIE_JSON_EVENTS_PREWARM_TIMEOUT_MS || 1e4);
83536
83664
  const sidechatTimeoutMs = Number(process.env.PIE_JSON_EVENTS_SIDECHAT_TIMEOUT_MS || 3e5);
83537
83665
  await prewarmRuntime();
83538
- writeEvent({ type: "agent.ready", sessionId });
83666
+ writeEvent({
83667
+ type: "agent.ready",
83668
+ sessionId,
83669
+ // Capability broadcast: the envelope kinds THIS build can project at
83670
+ // intake (embedded external kinds included), so embedders can probe
83671
+ // before their first turn instead of learning mismatches from failures.
83672
+ capabilities: { hostContextKinds: cliHostEnvelopeKinds() }
83673
+ });
83539
83674
  function trackSidechatPromise(promise) {
83540
83675
  activeSidechatPromise = promise;
83541
83676
  void promise.finally(() => {
@@ -83637,7 +83772,7 @@ async function runJsonEventsMode(params) {
83637
83772
  timestamp: startedAt,
83638
83773
  message: {
83639
83774
  role: "user",
83640
- content: message.prompt,
83775
+ content: message.hostContext ? [message.hostContext, { type: "text", text: message.prompt }] : message.prompt,
83641
83776
  timestamp: startedAt
83642
83777
  }
83643
83778
  };
@@ -83850,6 +83985,11 @@ async function runJsonEventsMode(params) {
83850
83985
  });
83851
83986
  }
83852
83987
  async function handleSidechat(message) {
83988
+ const hostContextFailure = validateIntakeHostContext(message);
83989
+ if (hostContextFailure) {
83990
+ writeSidechatFailure(message, hostContextFailure.message, hostContextFailure.code);
83991
+ return;
83992
+ }
83853
83993
  if (isSessionMismatch(message)) {
83854
83994
  writeSidechatFailure(message, sessionMismatchError(message.sessionId), "session_mismatch");
83855
83995
  return;
@@ -83905,7 +84045,7 @@ async function runJsonEventsMode(params) {
83905
84045
  return;
83906
84046
  }
83907
84047
  const parent = agent;
83908
- const { Agent: AgentClass } = await import("./chunks/src-6WK5KYWQ.js");
84048
+ const { Agent: AgentClass } = await import("./chunks/src-C2CBPEUY.js");
83909
84049
  const sideAgent = new AgentClass({
83910
84050
  initialState: {
83911
84051
  systemPrompt: parent.state.systemPrompt,
@@ -83915,6 +84055,9 @@ async function runJsonEventsMode(params) {
83915
84055
  messages: structuredClone(parent.state.messages)
83916
84056
  },
83917
84057
  apiKey: runtime.apiKey,
84058
+ // The sidechat snapshot clones the parent's stored history, including
84059
+ // persisted hostContext facts; project them like the main agent does.
84060
+ transformContext: async (messages) => projectCliHostEnvelopes(messages),
83918
84061
  // Same body-free transport timeline as the main agent: a sidechat is
83919
84062
  // an independent Responses request and previously had no record at all
83920
84063
  // when it stalled. `streamSimple` matches the host default when the
@@ -83928,7 +84071,7 @@ async function runJsonEventsMode(params) {
83928
84071
  }, sidechatTimeoutMs);
83929
84072
  timer.unref?.();
83930
84073
  try {
83931
- await sideAgent.prompt(message.prompt);
84074
+ await sideAgent.prompt(sidechatPromptBlocks(message));
83932
84075
  } finally {
83933
84076
  clearTimeout(timer);
83934
84077
  }
@@ -84259,7 +84402,7 @@ async function runJsonEventsMode(params) {
84259
84402
  if (runtime.initialModel.unconfigured || controller) {
84260
84403
  return;
84261
84404
  }
84262
- const { Agent: AgentClass } = await import("./chunks/src-6WK5KYWQ.js");
84405
+ const { Agent: AgentClass } = await import("./chunks/src-C2CBPEUY.js");
84263
84406
  if (runtimeClosing) return;
84264
84407
  const jsonModeExtensions = await loadJsonModeExtensions({ runtime, getAgent: () => agent });
84265
84408
  if (runtimeClosing) {
@@ -84286,7 +84429,7 @@ async function runJsonEventsMode(params) {
84286
84429
  skillsSection: runtime.skillsSection,
84287
84430
  knowledgeSection: runtime.knowledgeSection,
84288
84431
  appendSystemPrompt: params.developerInstructions,
84289
- tools: tools.map((tool) => ({ name: tool.name, description: tool.description }))
84432
+ tools
84290
84433
  });
84291
84434
  agent = new AgentClass({
84292
84435
  initialState: {
@@ -84296,6 +84439,10 @@ async function runJsonEventsMode(params) {
84296
84439
  thinkingLevel
84297
84440
  },
84298
84441
  apiKey: runtime.apiKey,
84442
+ // Restored interactive sessions carry persisted hostContext facts; project
84443
+ // them to model-visible text at the request boundary or the provider
84444
+ // converters reject the request (`Unprojected hostContext block`).
84445
+ transformContext: async (messages) => projectCliHostEnvelopes(messages),
84299
84446
  // Body-free transport timeline for the main (JSON-events) agent.
84300
84447
  streamFn: createJsonEventsTransportStreamFn(params.streamFn)
84301
84448
  });
@@ -84429,7 +84576,27 @@ async function runJsonEventsMode(params) {
84429
84576
  syncSessionModelMetadata(runtime.sessionManager, runtime.model, thinkingLevel);
84430
84577
  }
84431
84578
  }
84579
+ function promptInputFor(message) {
84580
+ return message.hostContext ? [message.hostContext, { type: "text", text: message.prompt }] : message.prompt;
84581
+ }
84582
+ function sidechatPromptBlocks(message) {
84583
+ return message.hostContext ? [message.hostContext, { type: "text", text: message.prompt }] : [{ type: "text", text: message.prompt }];
84584
+ }
84585
+ function validateIntakeHostContext(message) {
84586
+ return message.hostContext ? intakeHostContextFailure(message.hostContext) : void 0;
84587
+ }
84432
84588
  async function runTurn(message) {
84589
+ const hostContextFailure = validateIntakeHostContext(message);
84590
+ if (hostContextFailure) {
84591
+ writeEvent({
84592
+ type: "turn.failed",
84593
+ sessionId,
84594
+ turnId: message.turnId,
84595
+ error: hostContextFailure.message,
84596
+ ...hostContextFailure.code === "host_context_unknown_projector" ? { hostContextKinds: cliHostEnvelopeKinds() } : {}
84597
+ });
84598
+ return;
84599
+ }
84433
84600
  if (isSessionMismatch(message)) {
84434
84601
  writeEvent({
84435
84602
  type: "turn.failed",
@@ -84527,7 +84694,7 @@ async function runJsonEventsMode(params) {
84527
84694
  try {
84528
84695
  trace?.notePendingUserText(message.prompt);
84529
84696
  trace?.noteDispatchStart({ mode: "json-events" });
84530
- await controller.prompt(message.prompt, { source: "json-events" });
84697
+ await controller.prompt(promptInputFor(message), { source: "json-events" });
84531
84698
  trace?.noteDispatchSettled({ mode: "json-events" });
84532
84699
  trace?.noteWaitForIdleStart({ mode: "json-events" });
84533
84700
  await controller.waitForSettled();
@@ -84592,7 +84759,7 @@ async function runJsonEventsMode(params) {
84592
84759
  mode: message.streamingBehavior,
84593
84760
  turnId: message.turnId
84594
84761
  });
84595
- await activeController.prompt(message.prompt, {
84762
+ await activeController.prompt(promptInputFor(message), {
84596
84763
  source: "json-events",
84597
84764
  streamingBehavior: message.streamingBehavior,
84598
84765
  displayText: message.prompt,
@@ -84797,6 +84964,8 @@ Commands:
84797
84964
 
84798
84965
  Interactive Mode Key Bindings:
84799
84966
  Enter Submit message
84967
+ Shift+Enter New line; if ineffective, use Ctrl+J
84968
+ Ctrl+J New line (fallback)
84800
84969
  \\ + Enter New line (fallback)
84801
84970
  Alt+Enter Queue steering message for the active turn
84802
84971
  Ctrl+C Exit
@@ -84866,7 +85035,7 @@ async function runWithJsonEventsConsoleIsolation(fn) {
84866
85035
  }
84867
85036
  async function createCliTestStreamFnFromEnv() {
84868
85037
  if (!process.env.PIE_TEST_MOCK_STREAM_SEQUENCE) return void 0;
84869
- return (await import("./chunks/test-stream-7EH7BGY6.js")).createTestStreamFnFromSequenceEnv();
85038
+ return (await import("./chunks/test-stream-LDVUVSUP.js")).createTestStreamFnFromSequenceEnv();
84870
85039
  }
84871
85040
  function resolveJsonEventsDetailFlag() {
84872
85041
  for (let index = 0; index < process.argv.length; index += 1) {