@cydm/pie 2.7.7 → 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.
@@ -1,7 +1,7 @@
1
1
  import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
2
2
  import {
3
3
  createAskUserCapability
4
- } from "../../../chunks/chunk-UPGEXDEQ.js";
4
+ } from "../../../chunks/chunk-P22SYVGO.js";
5
5
  import "../../../chunks/chunk-W5B3OXHQ.js";
6
6
  import "../../../chunks/chunk-2FEDB5GR.js";
7
7
  import "../../../chunks/chunk-XXGYUYXI.js";
@@ -8,7 +8,7 @@ import {
8
8
  isPlanModeSafeCommand,
9
9
  markCompletedPlanSteps,
10
10
  restoreExecutionState
11
- } from "../../../chunks/chunk-UPGEXDEQ.js";
11
+ } from "../../../chunks/chunk-P22SYVGO.js";
12
12
  import "../../../chunks/chunk-W5B3OXHQ.js";
13
13
  import "../../../chunks/chunk-2FEDB5GR.js";
14
14
  import {
@@ -4,12 +4,12 @@ import {
4
4
  formatTransportLifecycle,
5
5
  getConfigDir,
6
6
  logTransportLifecycle
7
- } from "../../../chunks/chunk-IU5L5DED.js";
7
+ } from "../../../chunks/chunk-E6F4BTNN.js";
8
8
  import {
9
9
  SUBAGENT_SESSION_RECORD_VERSION,
10
10
  createSharedFileSystemTools,
11
11
  createSubagentCapability
12
- } from "../../../chunks/chunk-UPGEXDEQ.js";
12
+ } from "../../../chunks/chunk-P22SYVGO.js";
13
13
  import "../../../chunks/chunk-W5B3OXHQ.js";
14
14
  import "../../../chunks/chunk-2FEDB5GR.js";
15
15
  import "../../../chunks/chunk-XXGYUYXI.js";
@@ -206,7 +206,13 @@ function createSubagentSessionStore(sessionsDir, log, deps) {
206
206
  }
207
207
  }
208
208
  try {
209
- const stat = fs.statSync(lockPath);
209
+ let stat;
210
+ try {
211
+ stat = fs.statSync(lockPath);
212
+ } catch (statError) {
213
+ if (statError.code === "ENOENT") continue;
214
+ throw new Error(`Subagent session "${id}" is busy (session lease is unreadable).`);
215
+ }
210
216
  let lockContent;
211
217
  try {
212
218
  lockContent = fs.readFileSync(lockPath, "utf8");
@@ -8,7 +8,7 @@ import {
8
8
  executeManageTodoList,
9
9
  executionStateToTodos,
10
10
  restoreExecutionState
11
- } from "../../../chunks/chunk-UPGEXDEQ.js";
11
+ } from "../../../chunks/chunk-P22SYVGO.js";
12
12
  import "../../../chunks/chunk-W5B3OXHQ.js";
13
13
  import "../../../chunks/chunk-2FEDB5GR.js";
14
14
  import "../../../chunks/chunk-XXGYUYXI.js";
@@ -6,7 +6,7 @@ import {
6
6
  createSharedWebSearchTool,
7
7
  interpretShellExit,
8
8
  requestInteraction
9
- } from "./chunk-UPGEXDEQ.js";
9
+ } from "./chunk-P22SYVGO.js";
10
10
  import {
11
11
  getLogger
12
12
  } from "./chunk-2FEDB5GR.js";
@@ -2002,6 +2002,9 @@ function leadingHostContextBlock(message) {
2002
2002
  if (!isHostContextBlock(first)) return void 0;
2003
2003
  return first;
2004
2004
  }
2005
+ function projectionSeparator(projectionText) {
2006
+ return projectionText.endsWith("\n") && !projectionText.endsWith("\n\n") ? "" : "\n\n";
2007
+ }
2005
2008
  function projectHostEnvelopes(messages, registry, onProjection) {
2006
2009
  let changed = false;
2007
2010
  const next = [];
@@ -2015,12 +2018,11 @@ function projectHostEnvelopes(messages, registry, onProjection) {
2015
2018
  onProjection?.(outcome);
2016
2019
  const content = message.content;
2017
2020
  const rest = content.slice(1);
2021
+ const separator = projectionSeparator(outcome.text);
2018
2022
  const first = rest[0];
2019
2023
  let projectedContent;
2020
2024
  if (first && first.type === "text" && typeof first.text === "string") {
2021
- const merged = first.text.trim().length > 0 ? `${outcome.text}
2022
-
2023
- ${first.text}` : outcome.text;
2025
+ const merged = first.text.trim().length > 0 ? `${outcome.text}${separator}${first.text}` : outcome.text;
2024
2026
  projectedContent = [{ ...first, text: merged }, ...rest.slice(1)];
2025
2027
  } else {
2026
2028
  projectedContent = [{ type: "text", text: outcome.text }, ...rest];
@@ -19451,6 +19453,7 @@ export {
19451
19453
  generateEntryId,
19452
19454
  createHostEnvelopeProjectorRegistry,
19453
19455
  projectHostEnvelopes,
19456
+ isHostContextBlock,
19454
19457
  FileSessionStore,
19455
19458
  createSessionManager,
19456
19459
  prepareSessionCompaction,
package/dist/cli.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  logTransportLifecycle,
24
24
  migrateConfigFromAgentDir,
25
25
  truncateHeadTail
26
- } from "./chunks/chunk-IU5L5DED.js";
26
+ } from "./chunks/chunk-E6F4BTNN.js";
27
27
  import {
28
28
  resolveCliProjectRoot
29
29
  } from "./chunks/chunk-WLFOBECN.js";
@@ -72,6 +72,7 @@ import {
72
72
  interactionAttentionResolution,
73
73
  isCompactSummaryMessage,
74
74
  isExtensionEnabled,
75
+ isHostContextBlock,
75
76
  isToolAllowedInPlanMode,
76
77
  maybeAdvanceTodoExecutionState,
77
78
  modalityForReadFileUnderstandingKind,
@@ -87,7 +88,7 @@ import {
87
88
  shouldPreserveExecutionStateForUserText,
88
89
  supersedeExecutionState,
89
90
  verifyPreparedCompactionCommit
90
- } from "./chunks/chunk-UPGEXDEQ.js";
91
+ } from "./chunks/chunk-P22SYVGO.js";
91
92
  import "./chunks/chunk-W5B3OXHQ.js";
92
93
  import {
93
94
  Deref,
@@ -56591,6 +56592,10 @@ function createExtensionContext(extension, cwd, uiContext, abortFn, isIdleFn, ha
56591
56592
  if (!actions?.registerProvider) throw new Error("registerProvider is not available in this host mode");
56592
56593
  return actions.registerProvider(name, config);
56593
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
+ },
56594
56599
  async loginProvider(name) {
56595
56600
  if (!actions?.loginProvider) throw new Error("loginProvider is not available in this host mode");
56596
56601
  return actions.loginProvider(name);
@@ -64348,6 +64353,10 @@ var ExtensionRunner = class {
64348
64353
  if (!runner.actions.registerProvider) throw new Error("registerProvider is not available in this host mode");
64349
64354
  return runner.trackUnregisterHandle(runner.actions.registerProvider(name, config));
64350
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
+ },
64351
64360
  loginProvider: async (name) => {
64352
64361
  if (!runner.actions.loginProvider) throw new Error("loginProvider is not available in this host mode");
64353
64362
  return runner.actions.loginProvider(name);
@@ -76670,6 +76679,369 @@ function renderCompactionDiffPanel(target, stats) {
76670
76679
  target.safeRequestRender();
76671
76680
  }
76672
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
+
76673
77045
  // src/commands/interactive-compact-command.ts
76674
77046
  async function handleCompactCommand(host) {
76675
77047
  if (host.submitLock || host.isProcessing || host.isCompacting) {
@@ -76723,7 +77095,7 @@ async function handleCompactCommand(host) {
76723
77095
  if (beforeCompact?.summary) {
76724
77096
  summary = beforeCompact.summary;
76725
77097
  } else {
76726
- summary = await createCompactionSummary(preparation.messages, compactOptions);
77098
+ summary = await createCompactionSummary(projectCliHostEnvelopes(preparation.messages), compactOptions);
76727
77099
  }
76728
77100
  const verified = verifyPreparedCompactionCommit({
76729
77101
  preparation,
@@ -79386,7 +79758,7 @@ async function createCliAutoCompactionResult(params) {
79386
79758
  systemPrompt: params.systemPrompt,
79387
79759
  signal: params.context.signal
79388
79760
  };
79389
- const summary = await createCompactionSummary(params.context.messages, compactOptions);
79761
+ const summary = await createCompactionSummary(projectCliHostEnvelopes(params.context.messages), compactOptions);
79390
79762
  if (params.context.signal?.aborted) {
79391
79763
  return { aborted: true };
79392
79764
  }
@@ -80604,307 +80976,6 @@ function createInteractiveAgentSessionController(host) {
80604
80976
  });
80605
80977
  }
80606
80978
 
80607
- // src/runtime/runtime-mode-context.ts
80608
- var RUNTIME_MODE_ENVELOPE_KIND = "pie.runtimeMode";
80609
- var RUNTIME_MODE_ENVELOPE_VERSION = 1;
80610
- function runtimeModeEnvelopeRegistry() {
80611
- return sharedRuntimeModeRegistry ??= createHostEnvelopeProjectorRegistry([{
80612
- kind: RUNTIME_MODE_ENVELOPE_KIND,
80613
- version: RUNTIME_MODE_ENVELOPE_VERSION,
80614
- project: (data) => buildRuntimeModeContext(runtimeModeStateFromEnvelope(data))
80615
- }]);
80616
- }
80617
- var sharedRuntimeModeRegistry;
80618
- function runtimeModeStateFromEnvelope(data) {
80619
- return {
80620
- planMode: data.planMode === "planning" ? "planning" : "off",
80621
- yoloMode: data.yoloMode === true,
80622
- filesystemScope: data.filesystemScope === "all" ? "all" : "workspace",
80623
- pendingTransition: typeof data.pendingTransition === "string" ? data.pendingTransition : void 0,
80624
- activeToolNames: Array.isArray(data.activeToolNames) ? data.activeToolNames.filter((name) => typeof name === "string") : [],
80625
- planDisabledToolNames: Array.isArray(data.planDisabledToolNames) ? data.planDisabledToolNames.filter((name) => typeof name === "string") : [],
80626
- currentDate: typeof data.currentDate === "string" ? data.currentDate : void 0,
80627
- timeZone: typeof data.timeZone === "string" ? data.timeZone : void 0
80628
- };
80629
- }
80630
- function runtimeModeEnvelopeData(state) {
80631
- return {
80632
- planMode: state.planMode,
80633
- yoloMode: state.yoloMode,
80634
- filesystemScope: state.filesystemScope,
80635
- ...state.pendingTransition ? { pendingTransition: state.pendingTransition } : {},
80636
- activeToolNames: [...state.activeToolNames],
80637
- planDisabledToolNames: [...state.planDisabledToolNames],
80638
- ...state.currentDate ? { currentDate: state.currentDate } : {},
80639
- ...state.timeZone ? { timeZone: state.timeZone } : {}
80640
- };
80641
- }
80642
- function runtimeModeEnvelopeBlock(state) {
80643
- return {
80644
- type: "hostContext",
80645
- kind: RUNTIME_MODE_ENVELOPE_KIND,
80646
- version: RUNTIME_MODE_ENVELOPE_VERSION,
80647
- data: runtimeModeEnvelopeData(state)
80648
- };
80649
- }
80650
- function isAgentMessage(value) {
80651
- return !!value && typeof value === "object" && "role" in value;
80652
- }
80653
- function prependRuntimeModeBlockToPrompt(input, block) {
80654
- if (!block) return input;
80655
- if (typeof input === "string") {
80656
- return [{ ...block }, { type: "text", text: input }];
80657
- }
80658
- if (Array.isArray(input)) {
80659
- if (input.length > 0 && isAgentMessage(input[0])) {
80660
- return prependBlockToMessages(input, block);
80661
- }
80662
- return [block, ...input];
80663
- }
80664
- if (isAgentMessage(input)) {
80665
- return prependBlockToMessages([input], block)[0];
80666
- }
80667
- return [block, ...input];
80668
- }
80669
- function prependBlockToMessages(messages, block) {
80670
- const next = [...messages];
80671
- let target = -1;
80672
- for (let index = next.length - 1; index >= 0; index--) {
80673
- if (next[index]?.role === "user") {
80674
- target = index;
80675
- break;
80676
- }
80677
- }
80678
- if (target < 0) {
80679
- next.push({ role: "user", content: [block], timestamp: Date.now() });
80680
- return next;
80681
- }
80682
- const message = next[target];
80683
- if (Array.isArray(message.content)) {
80684
- next[target] = { ...message, content: [block, ...message.content] };
80685
- } else if (typeof message.content === "string") {
80686
- next[target] = { ...message, content: [block, { type: "text", text: message.content }] };
80687
- } else {
80688
- next.splice(target, 0, { role: "user", content: [block], timestamp: Date.now() });
80689
- }
80690
- return next;
80691
- }
80692
- var RUNTIME_MODE_CONTEXT_MARKER = "<runtime-mode-context>";
80693
- var RUNTIME_MODE_CONTEXT_END_MARKER = "</runtime-mode-context>";
80694
- function buildRuntimeModeContext(state) {
80695
- const lines = [
80696
- RUNTIME_MODE_CONTEXT_MARKER,
80697
- "Current runtime mode:"
80698
- ];
80699
- if (state.planMode === "planning") {
80700
- lines.push(
80701
- "- 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)."
80702
- );
80703
- if (state.planDisabledToolNames.length > 0) {
80704
- lines.push(
80705
- `- Disabled this turn by plan mode (calls are denied): ${state.planDisabledToolNames.join(", ")}. Do not call them while planning.`
80706
- );
80707
- }
80708
- } else {
80709
- lines.push("- Plan mode: off.");
80710
- }
80711
- lines.push(
80712
- `- YOLO mode: ${state.yoloMode ? "on (filesystem sandbox directory restrictions are disabled)" : "off (workspace/protected-path restrictions apply)"}.`,
80713
- `- Filesystem scope: ${state.filesystemScope === "all" ? "all local paths may be passed directly to file tools when permitted" : "workspace paths and configured safe roots only"}.`
80714
- );
80715
- if (state.pendingTransition) {
80716
- const transitionText = {
80717
- entered_yolo: "YOLO mode was just enabled for this turn.",
80718
- exited_yolo: "YOLO mode was just disabled; do not assume previous absolute-path access still applies.",
80719
- 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.",
80720
- exited_plan: "Plan mode was just disabled; execution tools may be used when the task requires them."
80721
- };
80722
- lines.push(`- Transition: ${transitionText[state.pendingTransition]}`);
80723
- }
80724
- if (state.currentDate) {
80725
- lines.push(`- Current date: ${state.currentDate}${state.timeZone ? ` (${state.timeZone})` : ""}.`);
80726
- }
80727
- if (state.activeToolNames.length > 0) {
80728
- lines.push(`- Active tools: ${state.activeToolNames.join(", ")}.`);
80729
- }
80730
- lines.push(RUNTIME_MODE_CONTEXT_END_MARKER);
80731
- return lines.join("\n");
80732
- }
80733
- function runtimeModeBlockKey(state) {
80734
- return JSON.stringify({
80735
- planMode: state.planMode,
80736
- yoloMode: state.yoloMode,
80737
- filesystemScope: state.filesystemScope,
80738
- currentDate: state.currentDate ?? null,
80739
- activeToolNames: state.activeToolNames,
80740
- planDisabledToolNames: state.planDisabledToolNames
80741
- });
80742
- }
80743
- function shouldInjectRuntimeModeBlock(lastKey, state) {
80744
- if (lastKey === null) return true;
80745
- return runtimeModeBlockKey(state) !== lastKey;
80746
- }
80747
- function runtimeModeTraceData(state) {
80748
- return {
80749
- yoloMode: state.yoloMode,
80750
- planMode: state.planMode,
80751
- filesystemScope: state.filesystemScope,
80752
- pendingTransition: state.pendingTransition,
80753
- currentDate: state.currentDate,
80754
- timeZone: state.timeZone,
80755
- activeToolCount: state.activeToolNames.length,
80756
- planDisabledToolCount: state.planDisabledToolNames.length
80757
- };
80758
- }
80759
- function currentDateInZone(timeZone) {
80760
- const parts = new Intl.DateTimeFormat("en-US", {
80761
- year: "numeric",
80762
- month: "2-digit",
80763
- day: "2-digit",
80764
- timeZone
80765
- }).formatToParts(/* @__PURE__ */ new Date());
80766
- const part = (type) => parts.find((p) => p.type === type)?.value ?? "00";
80767
- return `${part("year")}-${part("month")}-${part("day")}`;
80768
- }
80769
- var RUNTIME_MODE_BLOCK_START_MARKER = "<runtime-mode-context>";
80770
- function leadingRuntimeBlock(text) {
80771
- const withoutLeadingWhitespace = text.replace(/^[\t ]+/, "");
80772
- if (!withoutLeadingWhitespace.startsWith(RUNTIME_MODE_BLOCK_START_MARKER)) return null;
80773
- const markerStart = text.length - withoutLeadingWhitespace.length;
80774
- const endIndex = text.indexOf(RUNTIME_MODE_CONTEXT_END_MARKER, markerStart);
80775
- if (endIndex < 0) return null;
80776
- const block = text.slice(markerStart, endIndex + RUNTIME_MODE_CONTEXT_END_MARKER.length);
80777
- const rest = text.slice(endIndex + RUNTIME_MODE_CONTEXT_END_MARKER.length).replace(/^[\t ]*(?:\r?\n){0,2}/, "");
80778
- return { block, rest };
80779
- }
80780
- function stripLeadingRuntimeBlock(text) {
80781
- const leading = leadingRuntimeBlock(text);
80782
- return leading ? { rest: leading.rest } : null;
80783
- }
80784
- function lastUserMessageCarriesEnvelopeBlock(messages, block) {
80785
- for (let index = messages.length - 1; index >= 0; index -= 1) {
80786
- const message = messages[index];
80787
- if (message?.role !== "user") continue;
80788
- const content = message.content;
80789
- if (!Array.isArray(content) || content.length === 0) return false;
80790
- const first = content[0];
80791
- return first?.type === "hostContext" && first.kind === block.kind && first.version === block.version && JSON.stringify(first.data) === JSON.stringify(block.data);
80792
- }
80793
- return false;
80794
- }
80795
- function refreshRuntimeModeBlockOnLastUserMessage(messages, block) {
80796
- for (let i = messages.length - 1; i >= 0; i--) {
80797
- const message = messages[i];
80798
- if (message?.role !== "user") continue;
80799
- const content = message.content;
80800
- if (typeof content === "string") {
80801
- const stripped = stripLeadingRuntimeBlock(content);
80802
- const rest = stripped ? stripped.rest : content;
80803
- if (!rest) {
80804
- const copy2 = [...messages];
80805
- copy2[i] = { ...message, content: [block] };
80806
- return copy2;
80807
- }
80808
- const copy = [...messages];
80809
- copy[i] = { ...message, content: [block, { type: "text", text: rest }] };
80810
- return copy;
80811
- }
80812
- if (Array.isArray(content)) {
80813
- const first = content[0];
80814
- if (first?.type === "hostContext") {
80815
- if (first === block) return messages;
80816
- const copy2 = [...messages];
80817
- const contentCopy = [...content];
80818
- contentCopy[0] = block;
80819
- copy2[i] = { ...message, content: contentCopy };
80820
- return copy2;
80821
- }
80822
- let userBlocks = content;
80823
- const firstText = content[0];
80824
- if (firstText?.type === "text" && typeof firstText.text === "string") {
80825
- const stripped = stripLeadingRuntimeBlock(firstText.text);
80826
- if (stripped) {
80827
- userBlocks = stripped.rest ? [{ type: "text", text: stripped.rest }, ...content.slice(1)] : content.slice(1);
80828
- }
80829
- }
80830
- const copy = [...messages];
80831
- copy[i] = { ...message, content: [block, ...userBlocks] };
80832
- return copy;
80833
- }
80834
- return messages;
80835
- }
80836
- return messages;
80837
- }
80838
- var RuntimeModeBlockInjector = class {
80839
- constructor(options) {
80840
- this.options = options;
80841
- }
80842
- options;
80843
- lastKey = null;
80844
- /** Exact frozen envelope block, including its consumed one-shot transition. */
80845
- lastFrozenBlock = null;
80846
- /**
80847
- * Request-view overlay. While active, the CURRENT block is re-derived on
80848
- * every request from the live message snapshot and applied to the latest
80849
- * user message. It is not cached into history and it ends only when a real
80850
- * user message freezes the current block (see `nextBlock` + the transform),
80851
- * so consecutive tool rounds after a compaction retry keep the fresh state
80852
- * instead of losing it after one request.
80853
- */
80854
- overlayActive = false;
80855
- reset() {
80856
- this.lastKey = null;
80857
- this.lastFrozenBlock = null;
80858
- this.overlayActive = true;
80859
- }
80860
- /**
80861
- * Arm the request-view overlay without resetting the frozen-block key. Used
80862
- * when the runtime shape changed while a turn is still running (compaction
80863
- * checkpoint committed, plan approval, YOLO toggle inside a tool turn): the
80864
- * next and subsequent model requests must see the current state even though
80865
- * no new user message has been accepted yet.
80866
- */
80867
- armRefresh() {
80868
- this.overlayActive = true;
80869
- }
80870
- /**
80871
- * Envelope block to prepend to the next user message, or null when
80872
- * unchanged. Does NOT end the overlay: the block is only proof once the
80873
- * message carrying it reaches the request transform.
80874
- */
80875
- nextBlock() {
80876
- return this.freshBlock();
80877
- }
80878
- /**
80879
- * Current envelope block for a request that reuses existing history
80880
- * (overflow retry after compaction, manual-compact continue, or the tool
80881
- * rounds that follow a mid-turn mode change). Returns the same block for
80882
- * every request until the latest user message itself carries that block;
80883
- * then the overlay is retired and null is returned. Derivation is per
80884
- * request from the given snapshot; no message array is cached.
80885
- */
80886
- consumeRetryRefreshBlock(messages) {
80887
- if (!this.overlayActive) return null;
80888
- const state = this.options.getState();
80889
- const block = runtimeModeEnvelopeBlock(state);
80890
- const frozenCurrent = this.lastKey === runtimeModeBlockKey(state) ? this.lastFrozenBlock : null;
80891
- if (messages && (lastUserMessageCarriesEnvelopeBlock(messages, block) || frozenCurrent !== null && lastUserMessageCarriesEnvelopeBlock(messages, frozenCurrent))) {
80892
- this.overlayActive = false;
80893
- return null;
80894
- }
80895
- return block;
80896
- }
80897
- freshBlock() {
80898
- const state = this.options.getState();
80899
- if (!shouldInjectRuntimeModeBlock(this.lastKey, state)) return null;
80900
- this.lastKey = runtimeModeBlockKey(state);
80901
- if (state.pendingTransition) this.options.consumeTransition();
80902
- this.options.noteTrace(state);
80903
- this.lastFrozenBlock = runtimeModeEnvelopeBlock(state);
80904
- return this.lastFrozenBlock;
80905
- }
80906
- };
80907
-
80908
80979
  // src/runtime/cache-retention.ts
80909
80980
  function createCacheRetentionStreamFn(configured, getConfiguredRetention) {
80910
80981
  const base = configured ?? ((model, context, options) => streamSimple(model, context, options));
@@ -81767,6 +81838,7 @@ async function loadInteractiveExtensions(host) {
81767
81838
  host.noteRuntimeModeChanged("extension_register_provider");
81768
81839
  return unregister;
81769
81840
  },
81841
+ registerHostEnvelopeProjector: (projector) => registerExternalHostEnvelopeProjector(projector),
81770
81842
  loginProvider: async (name) => {
81771
81843
  const loggedIn = await loginExtensionProvider({
81772
81844
  provider: name,
@@ -82249,7 +82321,7 @@ var InteractiveMode = class {
82249
82321
  });
82250
82322
  return projectHostEnvelopes(
82251
82323
  structured,
82252
- runtimeModeEnvelopeRegistry(),
82324
+ cliHostEnvelopeRegistry(),
82253
82325
  (outcome) => this.sessionTrace.noteRuntimeEvent(outcome.degraded ? "host_envelope_degraded" : "host_envelope_projected", outcome.audit)
82254
82326
  );
82255
82327
  }
@@ -83039,6 +83111,7 @@ async function loadPrintModeExtensions(params) {
83039
83111
  setSessionName: (name) => params.sessionManager.setSessionName(name),
83040
83112
  getSessionName: () => params.sessionManager.getSessionName(),
83041
83113
  registerProvider: (name, config) => params.modelRegistry.registerProvider(name, config),
83114
+ registerHostEnvelopeProjector: (projector) => registerExternalHostEnvelopeProjector(projector),
83042
83115
  loginProvider: (name) => loginExtensionProvider({
83043
83116
  provider: name,
83044
83117
  modelRegistry: params.modelRegistry,
@@ -83137,6 +83210,12 @@ async function runPrintMode(params) {
83137
83210
  thinkingLevel: printThinkingLevel
83138
83211
  },
83139
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),
83140
83219
  // Body-free transport timeline for the main (print-mode) agent.
83141
83220
  streamFn: createTransportDiagnosticsStreamFn(params.streamFn ?? streamSimple, logTransportLifecycle)
83142
83221
  });
@@ -83380,14 +83459,17 @@ function parseInput(line) {
83380
83459
  };
83381
83460
  }
83382
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;
83383
83463
  return {
83384
83464
  type: "sidechat.request",
83385
83465
  sessionId: typeof parsed.sessionId === "string" ? parsed.sessionId : void 0,
83386
83466
  requestId: parsed.requestId,
83387
- prompt: parsed.prompt
83467
+ prompt: parsed.prompt,
83468
+ hostContext: parsed.hostContext
83388
83469
  };
83389
83470
  }
83390
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;
83391
83473
  const streamingBehavior = parsed.streamingBehavior;
83392
83474
  if (streamingBehavior !== void 0 && streamingBehavior !== "steer" && streamingBehavior !== "followUp") {
83393
83475
  return null;
@@ -83397,6 +83479,7 @@ function parseInput(line) {
83397
83479
  sessionId: typeof parsed.sessionId === "string" ? parsed.sessionId : void 0,
83398
83480
  turnId: parsed.turnId,
83399
83481
  prompt: parsed.prompt,
83482
+ hostContext: parsed.hostContext,
83400
83483
  streamingBehavior,
83401
83484
  cwd: typeof parsed.cwd === "string" ? parsed.cwd : void 0,
83402
83485
  metadata: isRecord3(parsed.metadata) ? parsed.metadata : void 0
@@ -83505,6 +83588,7 @@ async function loadJsonModeExtensions(params) {
83505
83588
  getRuntimeTools: () => params.getAgent()?.state.tools ?? [],
83506
83589
  resolveModelClass: (modelClass) => params.runtime.modelRegistry.resolveModelClass(modelClass),
83507
83590
  resolveModelRef: (modelRef) => params.runtime.modelRegistry.resolveModelRef(modelRef),
83591
+ registerHostEnvelopeProjector: (projector) => registerExternalHostEnvelopeProjector(projector),
83508
83592
  getMessages: () => params.getAgent()?.state.messages ?? [],
83509
83593
  getSessionMetadata: () => ({ ...params.runtime.sessionManager.getActiveSession()?.metadata ?? {} }),
83510
83594
  setSessionMetadata: (key, value) => {
@@ -83579,7 +83663,14 @@ async function runJsonEventsMode(params) {
83579
83663
  const prewarmTimeoutMs = Number(process.env.PIE_JSON_EVENTS_PREWARM_TIMEOUT_MS || 1e4);
83580
83664
  const sidechatTimeoutMs = Number(process.env.PIE_JSON_EVENTS_SIDECHAT_TIMEOUT_MS || 3e5);
83581
83665
  await prewarmRuntime();
83582
- 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
+ });
83583
83674
  function trackSidechatPromise(promise) {
83584
83675
  activeSidechatPromise = promise;
83585
83676
  void promise.finally(() => {
@@ -83681,7 +83772,7 @@ async function runJsonEventsMode(params) {
83681
83772
  timestamp: startedAt,
83682
83773
  message: {
83683
83774
  role: "user",
83684
- content: message.prompt,
83775
+ content: message.hostContext ? [message.hostContext, { type: "text", text: message.prompt }] : message.prompt,
83685
83776
  timestamp: startedAt
83686
83777
  }
83687
83778
  };
@@ -83894,6 +83985,11 @@ async function runJsonEventsMode(params) {
83894
83985
  });
83895
83986
  }
83896
83987
  async function handleSidechat(message) {
83988
+ const hostContextFailure = validateIntakeHostContext(message);
83989
+ if (hostContextFailure) {
83990
+ writeSidechatFailure(message, hostContextFailure.message, hostContextFailure.code);
83991
+ return;
83992
+ }
83897
83993
  if (isSessionMismatch(message)) {
83898
83994
  writeSidechatFailure(message, sessionMismatchError(message.sessionId), "session_mismatch");
83899
83995
  return;
@@ -83959,6 +84055,9 @@ async function runJsonEventsMode(params) {
83959
84055
  messages: structuredClone(parent.state.messages)
83960
84056
  },
83961
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),
83962
84061
  // Same body-free transport timeline as the main agent: a sidechat is
83963
84062
  // an independent Responses request and previously had no record at all
83964
84063
  // when it stalled. `streamSimple` matches the host default when the
@@ -83972,7 +84071,7 @@ async function runJsonEventsMode(params) {
83972
84071
  }, sidechatTimeoutMs);
83973
84072
  timer.unref?.();
83974
84073
  try {
83975
- await sideAgent.prompt(message.prompt);
84074
+ await sideAgent.prompt(sidechatPromptBlocks(message));
83976
84075
  } finally {
83977
84076
  clearTimeout(timer);
83978
84077
  }
@@ -84340,6 +84439,10 @@ async function runJsonEventsMode(params) {
84340
84439
  thinkingLevel
84341
84440
  },
84342
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),
84343
84446
  // Body-free transport timeline for the main (JSON-events) agent.
84344
84447
  streamFn: createJsonEventsTransportStreamFn(params.streamFn)
84345
84448
  });
@@ -84473,7 +84576,27 @@ async function runJsonEventsMode(params) {
84473
84576
  syncSessionModelMetadata(runtime.sessionManager, runtime.model, thinkingLevel);
84474
84577
  }
84475
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
+ }
84476
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
+ }
84477
84600
  if (isSessionMismatch(message)) {
84478
84601
  writeEvent({
84479
84602
  type: "turn.failed",
@@ -84571,7 +84694,7 @@ async function runJsonEventsMode(params) {
84571
84694
  try {
84572
84695
  trace?.notePendingUserText(message.prompt);
84573
84696
  trace?.noteDispatchStart({ mode: "json-events" });
84574
- await controller.prompt(message.prompt, { source: "json-events" });
84697
+ await controller.prompt(promptInputFor(message), { source: "json-events" });
84575
84698
  trace?.noteDispatchSettled({ mode: "json-events" });
84576
84699
  trace?.noteWaitForIdleStart({ mode: "json-events" });
84577
84700
  await controller.waitForSettled();
@@ -84636,7 +84759,7 @@ async function runJsonEventsMode(params) {
84636
84759
  mode: message.streamingBehavior,
84637
84760
  turnId: message.turnId
84638
84761
  });
84639
- await activeController.prompt(message.prompt, {
84762
+ await activeController.prompt(promptInputFor(message), {
84640
84763
  source: "json-events",
84641
84764
  streamingBehavior: message.streamingBehavior,
84642
84765
  displayText: message.prompt,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cydm/pie",
3
- "version": "2.7.7",
3
+ "version": "2.7.8",
4
4
  "description": "Pie AI Agent CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -20,7 +20,7 @@
20
20
  "test:skill-regressions": "node --test --import tsx test/bash-lifecycle.test.ts test/bash-powershell-display.test.ts test/skill-creator-runner.test.ts test/tui-use-contract.test.ts test/tui-use-runtime.test.ts test/browser-runtime.test.ts test/axi-stop-semantics.test.ts test/computer-use-axi.test.ts test/computer-evidence.test.ts test/interactive-lifecycle.test.ts",
21
21
  "test:offline:post": "node --test --import tsx test/remote-model-catalog.test.ts",
22
22
  "test:offline": "node ../../scripts/run-cli-offline.mjs",
23
- "test:offline:main": "node --test --test-concurrency=4 --import tsx test/cli-attention.test.ts test/builtin-extensions.test.ts test/core-services.test.ts test/cli.test.ts test/cli-auto-compaction.test.ts test/execution-state-manager.test.ts test/queued-steer-execution-state.test.ts test/todo-steering-ownership.test.ts test/architecture-boundaries.test.ts test/attachment-resolver.test.ts test/kimi-attachment-provider.test.ts test/document-attachment-extension.test.ts test/auto-compact-overflow.test.ts test/auto-compaction-ui.test.ts test/hierarchical-command-system.test.ts test/mcp-adapter.test.ts test/herdr-agent-state.test.ts test/extensions-enablement.test.ts test/extension-selector-shortcuts.test.ts test/skill-tool-enablement.test.ts test/message-queue-unit.test.ts test/evals-harness.test.ts test/session-trace.test.ts test/json-events-sidechat-diagnostics.test.ts test/json-events-prewarm-shutdown.test.ts test/subagent-session-store.test.ts test/runtime-mode-context.test.ts test/tool-policy.test.ts test/retry-policy.test.ts test/yolo-command.test.ts test/footer-lifecycle.test.ts test/files-utils.test.ts test/source-size-guard.test.ts test/runtime-logger.test.ts test/release-readiness.test.ts test/package-artifacts.test.ts test/init-extension-contract.test.ts test/skills-runtime.test.ts test/skills-agents-discovery.test.ts test/skills-symlink.test.ts test/model-overrides.test.ts test/auth-storage.test.ts test/extension-register-provider.test.ts test/extension-reload.test.ts test/extension-terminal-input.test.ts test/extension-session-before-events.test.ts test/extension-widgets.test.ts test/extension-render-hint.test.ts test/extension-oauth-restore.test.ts test/cross-host-parity.test.ts test/tool-name-rendering.test.ts test/session-selector.test.ts test/tree-selector-pagination.test.ts test/context-report.test.ts test/context-signals.test.ts test/plan-cache-stability.test.ts test/cache-invalidation-trace.test.ts test/cache-retention.test.ts test/trace-report.test.ts test/boot-report.test.ts test/compaction-diff.test.ts test/entry-body-and-usage.test.ts test/session-control.test.ts test/terminal-title.test.ts test/interactive-controller-title.test.ts test/todo-title.terminal.test.ts test/host-envelope-dogfood.terminal.test.ts test/plan-mode-extension.test.ts test/rendered-page-reader.test.ts test/tools-registry.test.ts test/system-prompt-contract.test.ts test/bash-yolo.test.ts test/bash-contract.test.ts test/bash-confirm-pending.test.ts test/interaction-status-lifecycle.test.ts test/bash-process-group.test.ts test/bash-artifact-sink.test.ts test/direct-bash.test.ts test/terminal-bell.test.ts test/ctrl-d-exit.test.ts test/session-display-title.test.ts test/calm-mode.test.ts test/stream-render-throttle.test.ts test/message-display-resync.test.ts test/project-root.test.ts test/guard-advisory-context.test.ts test/clear-command-usage.test.ts test/daily-agent-smoke.terminal.test.ts test/slash-menu-toggles.terminal.test.ts test/cli-args.test.ts test/extensions-configured-paths.test.ts test/extensions-loader-path.test.ts test/file-picker.test.ts test/interrupt-recovery.test.ts test/mention-insert-regression.test.ts test/mention-picker-navigation.test.ts test/pie-unity-rpc-skill.test.ts test/skill-creator-scripts.test.ts test/interactive-compact-command.test.ts test/computer-use-broker.test.ts test/computer-use-review-regressions.test.ts test/computer-use-safety.test.ts ../../scripts/node-test-diagnostics.test.js ../../scripts/offline-test-runner.test.js",
23
+ "test:offline:main": "node --test --test-concurrency=4 --import tsx test/cli-attention.test.ts test/builtin-extensions.test.ts test/core-services.test.ts test/cli.test.ts test/cli-auto-compaction.test.ts test/execution-state-manager.test.ts test/queued-steer-execution-state.test.ts test/todo-steering-ownership.test.ts test/architecture-boundaries.test.ts test/attachment-resolver.test.ts test/kimi-attachment-provider.test.ts test/document-attachment-extension.test.ts test/auto-compact-overflow.test.ts test/auto-compaction-ui.test.ts test/hierarchical-command-system.test.ts test/mcp-adapter.test.ts test/herdr-agent-state.test.ts test/extensions-enablement.test.ts test/extension-selector-shortcuts.test.ts test/skill-tool-enablement.test.ts test/message-queue-unit.test.ts test/evals-harness.test.ts test/session-trace.test.ts test/json-events-sidechat-diagnostics.test.ts test/json-events-prewarm-shutdown.test.ts test/subagent-session-store.test.ts test/runtime-mode-context.test.ts test/tool-policy.test.ts test/retry-policy.test.ts test/yolo-command.test.ts test/footer-lifecycle.test.ts test/files-utils.test.ts test/source-size-guard.test.ts test/runtime-logger.test.ts test/release-readiness.test.ts test/package-artifacts.test.ts test/init-extension-contract.test.ts test/skills-runtime.test.ts test/skills-agents-discovery.test.ts test/skills-symlink.test.ts test/model-overrides.test.ts test/auth-storage.test.ts test/extension-register-provider.test.ts test/extension-reload.test.ts test/extension-terminal-input.test.ts test/extension-session-before-events.test.ts test/extension-widgets.test.ts test/extension-render-hint.test.ts test/extension-oauth-restore.test.ts test/cross-host-parity.test.ts test/tool-name-rendering.test.ts test/session-selector.test.ts test/tree-selector-pagination.test.ts test/context-report.test.ts test/context-signals.test.ts test/plan-cache-stability.test.ts test/cache-invalidation-trace.test.ts test/cache-retention.test.ts test/trace-report.test.ts test/boot-report.test.ts test/compaction-diff.test.ts test/entry-body-and-usage.test.ts test/session-control.test.ts test/terminal-title.test.ts test/interactive-controller-title.test.ts test/todo-title.terminal.test.ts test/host-envelope-dogfood.terminal.test.ts test/host-envelope-cross-mode.test.ts test/plan-mode-extension.test.ts test/rendered-page-reader.test.ts test/tools-registry.test.ts test/system-prompt-contract.test.ts test/bash-yolo.test.ts test/bash-contract.test.ts test/bash-confirm-pending.test.ts test/interaction-status-lifecycle.test.ts test/bash-process-group.test.ts test/bash-artifact-sink.test.ts test/direct-bash.test.ts test/terminal-bell.test.ts test/ctrl-d-exit.test.ts test/session-display-title.test.ts test/calm-mode.test.ts test/stream-render-throttle.test.ts test/message-display-resync.test.ts test/project-root.test.ts test/guard-advisory-context.test.ts test/clear-command-usage.test.ts test/daily-agent-smoke.terminal.test.ts test/slash-menu-toggles.terminal.test.ts test/cli-args.test.ts test/extensions-configured-paths.test.ts test/extensions-loader-path.test.ts test/file-picker.test.ts test/interrupt-recovery.test.ts test/mention-insert-regression.test.ts test/mention-picker-navigation.test.ts test/pie-unity-rpc-skill.test.ts test/skill-creator-scripts.test.ts test/interactive-compact-command.test.ts test/computer-use-broker.test.ts test/computer-use-review-regressions.test.ts test/computer-use-safety.test.ts ../../scripts/node-test-diagnostics.test.js ../../scripts/offline-test-runner.test.js",
24
24
  "test:all": "npm run test:offline && npm run test:terminal:vitest",
25
25
  "test:terminal:vitest": "vitest run test/mention-file-select.terminal.test.ts test/tool-partial-display.terminal.test.ts test/footer-plan-mode.test.ts test/viewport-position-final.test.ts test/resume-header.terminal.test.ts --no-file-parallelism",
26
26
  "test:todo-tui": "node ../../scripts/run-with-env.mjs PIE_REAL_TODO_TUI=1 -- --test --import tsx test/todo-closure.terminal.test.ts",