zelari-code 2.53.0 → 2.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6537,9 +6537,9 @@ var init_schemas = __esm({
6537
6537
  };
6538
6538
  doc.write(`const input = payload.value;`);
6539
6539
  const ids = /* @__PURE__ */ Object.create(null);
6540
- let counter = 0;
6540
+ let counter2 = 0;
6541
6541
  for (const key of normalized.keys) {
6542
- ids[key] = `key_${counter++}`;
6542
+ ids[key] = `key_${counter2++}`;
6543
6543
  }
6544
6544
  doc.write(`const newResult = {};`);
6545
6545
  for (const key of normalized.keys) {
@@ -21738,9 +21738,51 @@ function buildSystemPrompt(agent, options) {
21738
21738
 
21739
21739
  ${volatile}`;
21740
21740
  }
21741
+ function resolvePromptLayout(env = process.env) {
21742
+ if (promptLayoutCache === void 0) {
21743
+ promptLayoutCache = env[PROMPT_LAYOUT_ENV]?.trim().toLowerCase() === "legacy" ? "legacy" : "trailing";
21744
+ }
21745
+ return promptLayoutCache;
21746
+ }
21747
+ function resetPromptLayoutCache() {
21748
+ promptLayoutCache = void 0;
21749
+ }
21750
+ function wrapTrailingContext(volatile) {
21751
+ const body = volatile.trim();
21752
+ if (lastTrailingRender?.volatile === body)
21753
+ return lastTrailingRender.body;
21754
+ const wrapped = body ? `${TRAILING_CONTEXT_OPEN_TAG}
21755
+ ${body}
21756
+ ${TRAILING_CONTEXT_CLOSE_TAG}` : "";
21757
+ lastTrailingRender = { volatile: body, body: wrapped };
21758
+ return wrapped;
21759
+ }
21760
+ function trailingContextFromSplit(split) {
21761
+ return wrapTrailingContext(split.volatile);
21762
+ }
21763
+ function isTrailingContextContent(content) {
21764
+ const trimmed = content.trim();
21765
+ return trimmed.startsWith(TRAILING_CONTEXT_OPEN_TAG) && trimmed.endsWith(TRAILING_CONTEXT_CLOSE_TAG);
21766
+ }
21767
+ function trailingContextMessagesFromSplit(split) {
21768
+ const body = trailingContextFromSplit(split);
21769
+ return body ? [{ role: "user", content: body }] : [];
21770
+ }
21771
+ function assembleRequestMessages(input) {
21772
+ const layout = input.layout ?? resolvePromptLayout();
21773
+ const systemMessages = systemMessagesFromSplit(input.split, {
21774
+ includeVolatile: layout === "legacy"
21775
+ });
21776
+ const trailingMessages = layout === "legacy" ? [] : trailingContextMessagesFromSplit(input.split);
21777
+ return {
21778
+ messages: [...systemMessages, ...input.history, ...trailingMessages, ...input.turn],
21779
+ systemCount: systemMessages.length,
21780
+ trailingCount: trailingMessages.length
21781
+ };
21782
+ }
21741
21783
  function systemMessagesFromSplit(split, opts) {
21742
21784
  const stable = split.stable.trim();
21743
- const volatile = split.volatile.trim();
21785
+ const volatile = opts?.includeVolatile ? split.volatile.trim() : "";
21744
21786
  if (!stable && !volatile)
21745
21787
  return [];
21746
21788
  if (opts?.singleSystem) {
@@ -21754,12 +21796,16 @@ function systemMessagesFromSplit(split, opts) {
21754
21796
  msgs.push({ role: "system", content: volatile });
21755
21797
  return msgs;
21756
21798
  }
21799
+ var PROMPT_LAYOUT_ENV, TRAILING_CONTEXT_OPEN_TAG, TRAILING_CONTEXT_CLOSE_TAG, promptLayoutCache, lastTrailingRender;
21757
21800
  var init_systemPromptBuilder = __esm({
21758
21801
  "packages/core/dist/agents/systemPromptBuilder.js"() {
21759
21802
  "use strict";
21760
21803
  init_promptModules();
21761
21804
  init_secrecyPolicy();
21762
21805
  init_skills();
21806
+ PROMPT_LAYOUT_ENV = "ZELARI_PROMPT_LAYOUT";
21807
+ TRAILING_CONTEXT_OPEN_TAG = "<context-update>";
21808
+ TRAILING_CONTEXT_CLOSE_TAG = "</context-update>";
21763
21809
  }
21764
21810
  });
21765
21811
 
@@ -22137,10 +22183,14 @@ __export(skills_exports, {
22137
22183
  KRAKEN_SELECTION_PLAYBOOK_MODULE: () => KRAKEN_SELECTION_PLAYBOOK_MODULE,
22138
22184
  LANGUAGE_POLICY_MODULE_TYPE: () => LANGUAGE_POLICY_MODULE_TYPE,
22139
22185
  NATIVE_TOOL_PROTOCOL_MODULE: () => NATIVE_TOOL_PROTOCOL_MODULE,
22186
+ PROMPT_LAYOUT_ENV: () => PROMPT_LAYOUT_ENV,
22140
22187
  SINGLE_AGENT_IDENTITY_MODULE: () => SINGLE_AGENT_IDENTITY_MODULE,
22141
22188
  SKILL_CATALOG: () => SKILL_CATALOG,
22142
22189
  TOOL_DEFINITIONS: () => TOOL_DEFINITIONS,
22190
+ TRAILING_CONTEXT_CLOSE_TAG: () => TRAILING_CONTEXT_CLOSE_TAG,
22191
+ TRAILING_CONTEXT_OPEN_TAG: () => TRAILING_CONTEXT_OPEN_TAG,
22143
22192
  VAULT_TOOL_DEFINITIONS: () => VAULT_TOOL_DEFINITIONS,
22193
+ assembleRequestMessages: () => assembleRequestMessages,
22144
22194
  buildCustomParameters: () => buildCustomParameters,
22145
22195
  buildLanguageDirective: () => buildLanguageDirective,
22146
22196
  buildLanguagePolicyModule: () => buildLanguagePolicyModule,
@@ -22164,20 +22214,26 @@ __export(skills_exports, {
22164
22214
  getSkillById: () => getSkillById,
22165
22215
  getSkillMetadata: () => getSkillMetadata,
22166
22216
  getSkillsByCategory: () => getSkillsByCategory,
22217
+ isTrailingContextContent: () => isTrailingContextContent,
22167
22218
  isValidTool: () => isValidTool,
22168
22219
  listCodingSkills: () => listCodingSkills,
22169
22220
  listSkills: () => listSkills,
22170
22221
  registerCodingSkill: () => registerCodingSkill,
22171
22222
  registerCustomTool: () => registerCustomTool,
22172
22223
  registerSkill: () => registerSkill,
22224
+ resetPromptLayoutCache: () => resetPromptLayoutCache,
22173
22225
  resolveAgentSkills: () => resolveAgentSkills,
22226
+ resolvePromptLayout: () => resolvePromptLayout,
22174
22227
  resolveResponseLanguage: () => resolveResponseLanguage,
22175
22228
  resolveSkillDependencies: () => resolveSkillDependencies,
22176
22229
  setWorkspaceStubs: () => setWorkspaceStubs,
22177
22230
  systemMessagesFromSplit: () => systemMessagesFromSplit,
22231
+ trailingContextFromSplit: () => trailingContextFromSplit,
22232
+ trailingContextMessagesFromSplit: () => trailingContextMessagesFromSplit,
22178
22233
  unregisterCustomTool: () => unregisterCustomTool,
22179
22234
  unregisterSkill: () => unregisterSkill,
22180
- validateCodingSkillRequires: () => validateCodingSkillRequires
22235
+ validateCodingSkillRequires: () => validateCodingSkillRequires,
22236
+ wrapTrailingContext: () => wrapTrailingContext
22181
22237
  });
22182
22238
  var init_skills2 = __esm({
22183
22239
  "packages/core/dist/skills/index.js"() {
@@ -36997,6 +37053,7 @@ __export(council_exports, {
36997
37053
  NFR_KEYWORDS: () => NFR_KEYWORDS,
36998
37054
  NON_RETRY_AGENTS: () => NON_RETRY_AGENTS,
36999
37055
  OUTPUT_QUALITY_DIRECTIVE: () => OUTPUT_QUALITY_DIRECTIVE,
37056
+ PROMPT_LAYOUT_ENV: () => PROMPT_LAYOUT_ENV,
37000
37057
  PROMPT_MODULES: () => PROMPT_MODULES,
37001
37058
  PROPRIETARY_REFUSAL_TEXT: () => PROPRIETARY_REFUSAL_TEXT,
37002
37059
  PROPRIETARY_SECRECY_MARKER: () => PROPRIETARY_SECRECY_MARKER,
@@ -37005,6 +37062,8 @@ __export(council_exports, {
37005
37062
  STRUCTURED_REASONING_DIRECTIVE: () => STRUCTURED_REASONING_DIRECTIVE,
37006
37063
  TIER_RANK: () => TIER_RANK,
37007
37064
  TOOL_USE_PROTOCOL_DIRECTIVE: () => TOOL_USE_PROTOCOL_DIRECTIVE,
37065
+ TRAILING_CONTEXT_CLOSE_TAG: () => TRAILING_CONTEXT_CLOSE_TAG,
37066
+ TRAILING_CONTEXT_OPEN_TAG: () => TRAILING_CONTEXT_OPEN_TAG,
37008
37067
  TURN_COMPLETION_MODULE: () => TURN_COMPLETION_MODULE,
37009
37068
  UnknownMemberError: () => UnknownMemberError,
37010
37069
  applyCompletionRetry: () => applyCompletionRetry,
@@ -37013,6 +37072,7 @@ __export(council_exports, {
37013
37072
  applyInlineJsAutofix: () => applyInlineJsAutofix,
37014
37073
  applyMotionAutofix: () => applyMotionAutofix,
37015
37074
  applyRetryIfMissing: () => applyRetryIfMissing,
37075
+ assembleRequestMessages: () => assembleRequestMessages,
37016
37076
  auditDegradedBanner: () => auditDegradedBanner,
37017
37077
  auditSynthesisTiers: () => auditSynthesisTiers,
37018
37078
  buildCouncilCompletion: () => buildCouncilCompletion,
@@ -37054,6 +37114,7 @@ __export(council_exports, {
37054
37114
  getToolDescriptions: () => getToolDescriptions,
37055
37115
  hasInteractiveClarification: () => hasInteractiveClarification,
37056
37116
  isAnswerLeak: () => isAnswerLeak,
37117
+ isTrailingContextContent: () => isTrailingContextContent,
37057
37118
  isVerifyToolCheckSkipped: () => isVerifyToolCheckSkipped,
37058
37119
  jaccardSimilarity: () => jaccardSimilarity,
37059
37120
  lintSynthesisHonesty: () => lintSynthesisHonesty,
@@ -37071,7 +37132,9 @@ __export(council_exports, {
37071
37132
  recallLessons: () => recallLessons,
37072
37133
  renderSkillMarkdown: () => renderSkillMarkdown,
37073
37134
  replayChairmanTextTools: () => replayChairmanTextTools,
37135
+ resetPromptLayoutCache: () => resetPromptLayoutCache,
37074
37136
  resolveCouncilRunMode: () => resolveCouncilRunMode,
37137
+ resolvePromptLayout: () => resolvePromptLayout,
37075
37138
  resolveRoleSystemPrompt: () => resolveRoleSystemPrompt,
37076
37139
  resolveVerifyRetryTool: () => resolveVerifyRetryTool,
37077
37140
  restrictImplementationWrites: () => restrictImplementationWrites,
@@ -37094,8 +37157,11 @@ __export(council_exports, {
37094
37157
  taskMatchesNfrKeywords: () => taskMatchesNfrKeywords,
37095
37158
  tierAtLeast: () => tierAtLeast,
37096
37159
  tokenizeForSignature: () => tokenizeForSignature,
37160
+ trailingContextFromSplit: () => trailingContextFromSplit,
37161
+ trailingContextMessagesFromSplit: () => trailingContextMessagesFromSplit,
37097
37162
  verifyCitations: () => verifyCitations,
37098
37163
  warnIfNfrSpecMissing: () => warnIfNfrSpecMissing,
37164
+ wrapTrailingContext: () => wrapTrailingContext,
37099
37165
  writeCouncilCompletion: () => writeCouncilCompletion,
37100
37166
  writeVerificationReport: () => writeVerificationReport
37101
37167
  });
@@ -40038,7 +40104,7 @@ var CORE_VERSION;
40038
40104
  var init_version = __esm({
40039
40105
  "packages/core/dist/version.js"() {
40040
40106
  "use strict";
40041
- CORE_VERSION = "2.53.0";
40107
+ CORE_VERSION = "2.55.0";
40042
40108
  }
40043
40109
  });
40044
40110
 
@@ -40196,6 +40262,7 @@ __export(dist_exports, {
40196
40262
  OUTPUT_QUALITY_DIRECTIVE: () => OUTPUT_QUALITY_DIRECTIVE,
40197
40263
  ObserverBus: () => ObserverBus,
40198
40264
  PROFILE_RESOURCE_POLICIES: () => PROFILE_RESOURCE_POLICIES,
40265
+ PROMPT_LAYOUT_ENV: () => PROMPT_LAYOUT_ENV,
40199
40266
  PROMPT_MODULES: () => PROMPT_MODULES,
40200
40267
  PROPRIETARY_REFUSAL_TEXT: () => PROPRIETARY_REFUSAL_TEXT,
40201
40268
  PROPRIETARY_SECRECY_MARKER: () => PROPRIETARY_SECRECY_MARKER,
@@ -40243,6 +40310,8 @@ __export(dist_exports, {
40243
40310
  TOOL_CALL_TRUNCATED_RECOVERY_USER: () => TOOL_CALL_TRUNCATED_RECOVERY_USER,
40244
40311
  TOOL_DEFINITIONS: () => TOOL_DEFINITIONS,
40245
40312
  TOOL_USE_PROTOCOL_DIRECTIVE: () => TOOL_USE_PROTOCOL_DIRECTIVE,
40313
+ TRAILING_CONTEXT_CLOSE_TAG: () => TRAILING_CONTEXT_CLOSE_TAG,
40314
+ TRAILING_CONTEXT_OPEN_TAG: () => TRAILING_CONTEXT_OPEN_TAG,
40246
40315
  TURN_COMPLETION_MODULE: () => TURN_COMPLETION_MODULE,
40247
40316
  TaskConstraintSchema: () => TaskConstraintSchema,
40248
40317
  TaskContractConflictError: () => TaskContractConflictError,
@@ -40273,6 +40342,7 @@ __export(dist_exports, {
40273
40342
  applyMotionAutofix: () => applyMotionAutofix,
40274
40343
  applyRetryIfMissing: () => applyRetryIfMissing,
40275
40344
  applyTaskContractUpdate: () => applyTaskContractUpdate,
40345
+ assembleRequestMessages: () => assembleRequestMessages,
40276
40346
  auditDegradedBanner: () => auditDegradedBanner,
40277
40347
  auditSynthesisTiers: () => auditSynthesisTiers,
40278
40348
  budgetPressure: () => budgetPressure,
@@ -40434,6 +40504,7 @@ __export(dist_exports, {
40434
40504
  isSettled: () => isSettled,
40435
40505
  isStatusTheaterUnit: () => isStatusTheaterUnit,
40436
40506
  isSteerControlEvent: () => isSteerControlEvent,
40507
+ isTrailingContextContent: () => isTrailingContextContent,
40437
40508
  isValidTool: () => isValidTool,
40438
40509
  isVerificationReserveProtected: () => isVerificationReserveProtected,
40439
40510
  isVerifyParallelEnabled: () => isVerifyParallelEnabled,
@@ -40510,6 +40581,7 @@ __export(dist_exports, {
40510
40581
  renderSkillMarkdown: () => renderSkillMarkdown,
40511
40582
  renderSteers: () => renderSteers,
40512
40583
  replayChairmanTextTools: () => replayChairmanTextTools,
40584
+ resetPromptLayoutCache: () => resetPromptLayoutCache,
40513
40585
  resolveAgentSkills: () => resolveAgentSkills,
40514
40586
  resolveCommandConcurrency: () => resolveCommandConcurrency,
40515
40587
  resolveCouncilRunMode: () => resolveCouncilRunMode,
@@ -40519,6 +40591,7 @@ __export(dist_exports, {
40519
40591
  resolveMaxTentacles: () => resolveMaxTentacles,
40520
40592
  resolvePlanTimeoutMs: () => resolvePlanTimeoutMs,
40521
40593
  resolveProfile: () => resolveProfile,
40594
+ resolvePromptLayout: () => resolvePromptLayout,
40522
40595
  resolveRequestSnapshotMode: () => resolveRequestSnapshotMode,
40523
40596
  resolveResponseLanguage: () => resolveResponseLanguage,
40524
40597
  resolveRoleSystemPrompt: () => resolveRoleSystemPrompt,
@@ -40574,6 +40647,8 @@ __export(dist_exports, {
40574
40647
  toolManifestHash: () => toolManifestHash,
40575
40648
  toolMatches: () => toolMatches,
40576
40649
  topoLevels: () => topoLevels,
40650
+ trailingContextFromSplit: () => trailingContextFromSplit,
40651
+ trailingContextMessagesFromSplit: () => trailingContextMessagesFromSplit,
40577
40652
  unregisterCustomTool: () => unregisterCustomTool,
40578
40653
  unregisterSkill: () => unregisterSkill,
40579
40654
  usageFromLedger: () => usageFromLedger,
@@ -40590,6 +40665,7 @@ __export(dist_exports, {
40590
40665
  weaknessFromVerdict: () => weaknessFromVerdict,
40591
40666
  weaknessScoreFromText: () => weaknessScoreFromText,
40592
40667
  wrapLegacyStream: () => wrapLegacyStream,
40668
+ wrapTrailingContext: () => wrapTrailingContext,
40593
40669
  writeCouncilCompletion: () => writeCouncilCompletion,
40594
40670
  writeVerificationReport: () => writeVerificationReport
40595
40671
  });
@@ -43803,12 +43879,12 @@ function loadHandle(rootDir) {
43803
43879
  }
43804
43880
  const {
43805
43881
  tasks: rawTasks,
43806
- counter,
43882
+ counter: counter2,
43807
43883
  schemaVersion: _schemaVersion,
43808
43884
  ...rootFields
43809
43885
  } = parsed;
43810
43886
  const tasks = (Array.isArray(rawTasks) ? rawTasks : []).map(normalizeTask).filter((t) => typeof t.id === "string" && t.id.length > 0);
43811
- const numericCounter = typeof counter === "number" && Number.isFinite(counter) && counter >= 0 ? Math.floor(counter) : 0;
43887
+ const numericCounter = typeof counter2 === "number" && Number.isFinite(counter2) && counter2 >= 0 ? Math.floor(counter2) : 0;
43812
43888
  return { rootDir, tasks, counter: numericCounter, rootFields };
43813
43889
  }
43814
43890
  function saveHandle(rootDir, handle) {
@@ -47010,14 +47086,14 @@ function parseRecordLine(line) {
47010
47086
  }
47011
47087
  if (typeof raw !== "object" || raw === null) return null;
47012
47088
  const r = raw;
47013
- const num = (v) => typeof v === "number" && Number.isFinite(v);
47089
+ const num2 = (v) => typeof v === "number" && Number.isFinite(v);
47014
47090
  const str2 = (v) => typeof v === "string";
47015
47091
  const strOrNull = (v) => v === null || typeof v === "string";
47016
- const numOrNull = (v) => v === null || num(v);
47017
- if (!num(r.ts) || !str2(r.repo) || r.repo === "" || !str2(r.role)) return null;
47092
+ const numOrNull = (v) => v === null || num2(v);
47093
+ if (!num2(r.ts) || !str2(r.repo) || r.repo === "" || !str2(r.role)) return null;
47018
47094
  if (!strOrNull(r.model) || !strOrNull(r.provider) || !strOrNull(r.language)) return null;
47019
47095
  if (!REPUTATION_OUTCOMES.includes(r.outcome)) return null;
47020
- if (typeof r.firstPass !== "boolean" || !num(r.repairCount) || r.repairCount < 0) return null;
47096
+ if (typeof r.firstPass !== "boolean" || !num2(r.repairCount) || r.repairCount < 0) return null;
47021
47097
  if (!numOrNull(r.costUsd) || !numOrNull(r.latencyMs)) return null;
47022
47098
  return {
47023
47099
  ts: r.ts,
@@ -54905,6 +54981,7 @@ function parseCachedPromptTokens(usage) {
54905
54981
  if (!usage || typeof usage !== "object") return 0;
54906
54982
  const candidates = [
54907
54983
  usage.prompt_tokens_details?.cached_tokens,
54984
+ usage.input_tokens_details?.cached_tokens,
54908
54985
  usage.prompt_cache_hit_tokens
54909
54986
  ];
54910
54987
  for (const c of candidates) {
@@ -55011,6 +55088,8 @@ function positiveEnvInt(name) {
55011
55088
  return Number.isFinite(n) && n > 0 ? n : void 0;
55012
55089
  }
55013
55090
  function openaiCompatibleProvider(config2) {
55091
+ const sessionDeepSeekThinking = resolveDeepSeekThinking();
55092
+ const sessionToolChoice = "auto";
55014
55093
  return async function* (params) {
55015
55094
  const capabilities = capabilitiesFor(params.model, config2.providerId);
55016
55095
  const streamTimeouts = resolveStreamTimeouts(capabilities);
@@ -55068,7 +55147,7 @@ function openaiCompatibleProvider(config2) {
55068
55147
  }
55069
55148
  const thinkingSpec = config2.thinking ?? "auto";
55070
55149
  if (config2.providerId === "deepseek" && thinkingSpec === "auto") {
55071
- const thinking = resolveDeepSeekThinking();
55150
+ const thinking = sessionDeepSeekThinking;
55072
55151
  if (thinking.thinking) body.thinking = { type: thinking.thinking };
55073
55152
  if (thinking.reasoningEffort) body.reasoning_effort = thinking.reasoningEffort;
55074
55153
  } else if (thinkingSpec !== "auto") {
@@ -55093,7 +55172,7 @@ function openaiCompatibleProvider(config2) {
55093
55172
  }));
55094
55173
  const recoveryAttempt = generation?.recoveryAttempt ?? 1;
55095
55174
  const forceRecoveryTool = generation?.toolChoice === "required" && capabilities.buildRecovery.forceToolChoice && recoveryAttempt <= capabilities.buildRecovery.maxForcedTurns;
55096
- body.tool_choice = forceRecoveryTool ? "required" : "auto";
55175
+ body.tool_choice = forceRecoveryTool ? "required" : sessionToolChoice;
55097
55176
  if (config2.providerId === "glm") body.tool_stream = true;
55098
55177
  }
55099
55178
  const headers3 = {
@@ -56665,15 +56744,30 @@ function withRollingCacheBreakpoint(msg, cacheControl) {
56665
56744
  }
56666
56745
  return msg;
56667
56746
  }
56747
+ function trailingContextIndex(messages) {
56748
+ for (let i = messages.length - 1; i >= 0; i--) {
56749
+ const content = messages[i].content;
56750
+ if (typeof content === "string" && isTrailingContextContent(content)) return i;
56751
+ }
56752
+ return -1;
56753
+ }
56668
56754
  function anthropicMessagesProvider(config2) {
56669
56755
  return async function* (params) {
56670
56756
  const { systemParts, rest } = splitMessages(params.messages);
56671
56757
  const ttlPref = resolvePromptCacheTtl();
56672
56758
  const cacheControl = ttlPref === "1h" ? { type: "ephemeral", ttl: "1h" } : { type: "ephemeral" };
56759
+ const trailingIdx = trailingContextIndex(rest);
56760
+ const rollingIndices = /* @__PURE__ */ new Set();
56761
+ if (rest.length > 0) {
56762
+ if (trailingIdx >= 0) rollingIndices.add(trailingIdx);
56763
+ rollingIndices.add(rest.length - 1);
56764
+ }
56673
56765
  const body = {
56674
56766
  model: params.model,
56675
56767
  max_tokens: 16384,
56676
- messages: rest.length > 0 ? rest.map((m, i) => i === rest.length - 1 ? withRollingCacheBreakpoint(m, cacheControl) : m) : rest,
56768
+ messages: rest.length > 0 ? rest.map(
56769
+ (m, i) => rollingIndices.has(i) ? withRollingCacheBreakpoint(m, cacheControl) : m
56770
+ ) : rest,
56677
56771
  stream: true
56678
56772
  };
56679
56773
  if (systemParts.length > 0) {
@@ -56804,11 +56898,11 @@ function anthropicMessagesProvider(config2) {
56804
56898
  } else if (type === "message_start") {
56805
56899
  const usage = ev.message?.usage;
56806
56900
  if (usage) {
56807
- const num = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0;
56901
+ const num2 = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0;
56808
56902
  startUsage = {
56809
- inputTokens: num(usage.input_tokens),
56810
- cacheReadTokens: num(usage.cache_read_input_tokens),
56811
- cacheCreationTokens: num(usage.cache_creation_input_tokens)
56903
+ inputTokens: num2(usage.input_tokens),
56904
+ cacheReadTokens: num2(usage.cache_read_input_tokens),
56905
+ cacheCreationTokens: num2(usage.cache_creation_input_tokens)
56812
56906
  };
56813
56907
  }
56814
56908
  } else if (type === "message_delta") {
@@ -56854,6 +56948,7 @@ var ANTHROPIC_VERSION, ANTHROPIC_BETA, ANTHROPIC_BETA_EXTENDED_CACHE_TTL;
56854
56948
  var init_anthropic = __esm({
56855
56949
  "src/cli/provider/anthropic.ts"() {
56856
56950
  "use strict";
56951
+ init_skills2();
56857
56952
  init_openai_compatible();
56858
56953
  init_chatStats();
56859
56954
  init_thinking();
@@ -57043,7 +57138,8 @@ function chatgptResponsesProvider(config2) {
57043
57138
  usage: {
57044
57139
  promptTokens: usage.input_tokens ?? usage.prompt_tokens ?? 0,
57045
57140
  completionTokens: usage.output_tokens ?? usage.completion_tokens ?? 0,
57046
- totalTokens: usage.total_tokens ?? (usage.input_tokens ?? 0) + (usage.output_tokens ?? 0)
57141
+ totalTokens: usage.total_tokens ?? (usage.input_tokens ?? 0) + (usage.output_tokens ?? 0),
57142
+ ...parseCachedPromptTokens(usage) > 0 ? { cachedPromptTokens: parseCachedPromptTokens(usage) } : {}
57047
57143
  }
57048
57144
  };
57049
57145
  }
@@ -57068,6 +57164,7 @@ var init_chatgpt = __esm({
57068
57164
  "use strict";
57069
57165
  init_openai_compatible();
57070
57166
  init_thinking();
57167
+ init_openai_compatible();
57071
57168
  }
57072
57169
  });
57073
57170
 
@@ -57270,7 +57367,8 @@ function responsesApiProvider(config2) {
57270
57367
  usage: {
57271
57368
  promptTokens: usage.input_tokens ?? usage.prompt_tokens ?? 0,
57272
57369
  completionTokens: usage.output_tokens ?? usage.completion_tokens ?? 0,
57273
- totalTokens: usage.total_tokens ?? (usage.input_tokens ?? 0) + (usage.output_tokens ?? 0)
57370
+ totalTokens: usage.total_tokens ?? (usage.input_tokens ?? 0) + (usage.output_tokens ?? 0),
57371
+ ...parseCachedPromptTokens(usage) > 0 ? { cachedPromptTokens: parseCachedPromptTokens(usage) } : {}
57274
57372
  }
57275
57373
  };
57276
57374
  }
@@ -57298,6 +57396,7 @@ var init_responsesApi = __esm({
57298
57396
  init_chatgpt();
57299
57397
  init_thinking();
57300
57398
  init_capabilities();
57399
+ init_openai_compatible();
57301
57400
  RETRYABLE_STATUSES2 = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
57302
57401
  MAX_RETRIES2 = (() => {
57303
57402
  const raw = process.env.ZELARI_PROVIDER_MAX_RETRIES;
@@ -59931,6 +60030,45 @@ var init_metrics3 = __esm({
59931
60030
  }
59932
60031
  });
59933
60032
 
60033
+ // src/cli/budget/messageUsage.ts
60034
+ function counter(value) {
60035
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.round(value) : 0;
60036
+ }
60037
+ function recordMessageUsage(input) {
60038
+ try {
60039
+ const promptTokens = counter(input.promptTokens);
60040
+ const completionTokens = counter(input.completionTokens);
60041
+ const cachedPromptTokens = Math.min(counter(input.cachedPromptTokens), promptTokens);
60042
+ const model = input.model ?? "";
60043
+ const record2 = {
60044
+ kind: "message",
60045
+ ts: input.ts ?? Date.now(),
60046
+ ...input.sessionId ? { sessionId: input.sessionId } : {},
60047
+ ...input.provider ? { provider: input.provider } : {},
60048
+ ...model ? { model } : {},
60049
+ promptTokens,
60050
+ completionTokens,
60051
+ cachedPromptTokens,
60052
+ costUsd: calculateCost(model, promptTokens, completionTokens, cachedPromptTokens)
60053
+ };
60054
+ getMetricsLogger().record(record2);
60055
+ } catch {
60056
+ }
60057
+ }
60058
+ async function flushMessageUsage() {
60059
+ try {
60060
+ await getMetricsLogger().flush();
60061
+ } catch {
60062
+ }
60063
+ }
60064
+ var init_messageUsage = __esm({
60065
+ "src/cli/budget/messageUsage.ts"() {
60066
+ "use strict";
60067
+ init_metrics3();
60068
+ init_modelPricing();
60069
+ }
60070
+ });
60071
+
59934
60072
  // src/cli/kraken/turnRuntime.ts
59935
60073
  var WRITE_TOOLS3, KrakenTurnRuntime;
59936
60074
  var init_turnRuntime = __esm({
@@ -69114,7 +69252,7 @@ async function runAgentMissionSlice(deps) {
69114
69252
  deps.ragContext
69115
69253
  );
69116
69254
  async function runPass(messages, passId) {
69117
- const counter = createWriteCounter();
69255
+ const counter2 = createWriteCounter();
69118
69256
  const harness = new AgentHarness({
69119
69257
  model: deps.model,
69120
69258
  provider,
@@ -69135,7 +69273,7 @@ async function runAgentMissionSlice(deps) {
69135
69273
  let completionTokens = 0;
69136
69274
  try {
69137
69275
  for await (const event of harness.run()) {
69138
- counter.onEvent(event);
69276
+ counter2.onEvent(event);
69139
69277
  if (deps.onEvent) await deps.onEvent(event);
69140
69278
  if (event.type === "message_delta" && typeof event.delta === "string") {
69141
69279
  text += event.delta;
@@ -69167,8 +69305,8 @@ async function runAgentMissionSlice(deps) {
69167
69305
  }
69168
69306
  return {
69169
69307
  text,
69170
- successfulWrites: counter.state.successfulWrites,
69171
- emittedWrites: counter.state.emittedWrites,
69308
+ successfulWrites: counter2.state.successfulWrites,
69309
+ emittedWrites: counter2.state.emittedWrites,
69172
69310
  errored: errored2,
69173
69311
  messages: harness.getMessages(),
69174
69312
  promptTokens,
@@ -73125,6 +73263,7 @@ async function runOneTurn(opts, provider, model, providerStream, extras) {
73125
73263
  }));
73126
73264
  const toolNames = tools.map((t) => t.name);
73127
73265
  let systemMessages;
73266
+ let wireSplit;
73128
73267
  let languageDirectiveContent;
73129
73268
  try {
73130
73269
  languageDirectiveContent = buildLanguagePolicyModuleFor(opts.task).content;
@@ -73220,7 +73359,8 @@ async function runOneTurn(opts, provider, model, providerStream, extras) {
73220
73359
  }
73221
73360
  }
73222
73361
  );
73223
- systemMessages = systemMessagesFromSplit(split);
73362
+ systemMessages = systemMessagesFromSplit(split, { includeVolatile: true });
73363
+ wireSplit = split;
73224
73364
  } catch {
73225
73365
  systemMessages = [
73226
73366
  {
@@ -73235,6 +73375,7 @@ async function runOneTurn(opts, provider, model, providerStream, extras) {
73235
73375
  ].join("\n")
73236
73376
  }
73237
73377
  ];
73378
+ wireSplit = { stable: systemMessages[0].content, volatile: "" };
73238
73379
  }
73239
73380
  await spine.beginResourceTurn();
73240
73381
  const modelContext = await buildModelContext({
@@ -73322,6 +73463,16 @@ async function runOneTurn(opts, provider, model, providerStream, extras) {
73322
73463
  for await (const event of harness.run()) {
73323
73464
  progressRuntime.observe(event);
73324
73465
  spine.observe(event);
73466
+ if (event.type === "message_end" && event.usage) {
73467
+ recordMessageUsage({
73468
+ sessionId: passSessionId,
73469
+ provider,
73470
+ model,
73471
+ promptTokens: event.usage.promptTokens,
73472
+ completionTokens: event.usage.completionTokens,
73473
+ cachedPromptTokens: event.usage.cachedPromptTokens ?? 0
73474
+ });
73475
+ }
73325
73476
  if (event.type === "message_start") {
73326
73477
  scrub.reset();
73327
73478
  }
@@ -73370,6 +73521,7 @@ async function runOneTurn(opts, provider, model, providerStream, extras) {
73370
73521
  };
73371
73522
  }
73372
73523
  const buildProgress = readBuildProgress();
73524
+ await flushMessageUsage();
73373
73525
  return {
73374
73526
  finalReason,
73375
73527
  exitCode,
@@ -73389,15 +73541,18 @@ async function runOneTurn(opts, provider, model, providerStream, extras) {
73389
73541
  }
73390
73542
  });
73391
73543
  progressRuntime.beginTurn();
73392
- const initialMessages = [
73393
- ...systemMessages,
73394
- ...historySeed,
73395
- {
73396
- role: "user",
73397
- content: effectiveTask,
73398
- ...opts.images && opts.images.length > 0 ? { images: opts.images } : {}
73399
- }
73400
- ];
73544
+ const initialMessages = assembleRequestMessages({
73545
+ split: wireSplit,
73546
+ history: historySeed,
73547
+ turn: [
73548
+ {
73549
+ role: "user",
73550
+ content: effectiveTask,
73551
+ ...opts.images && opts.images.length > 0 ? { images: opts.images } : {}
73552
+ }
73553
+ ],
73554
+ layout: resolvePromptLayout()
73555
+ }).messages;
73401
73556
  let pass = await runSinglePass(initialMessages, sessionId2);
73402
73557
  let strictExit = 0;
73403
73558
  const verifierReviewDeps = {
@@ -73450,11 +73605,17 @@ async function runOneTurn(opts, provider, model, providerStream, extras) {
73450
73605
  "[zelari-code --headless] Kraken BUILD: required checks unresolved \u2014 forcing repair pass\n"
73451
73606
  );
73452
73607
  }
73453
- const withSystem = [
73454
- ...systemMessages,
73455
- ...pass.messages.filter((m) => m.role !== "system"),
73456
- { role: "user", content: repairPrompt }
73457
- ];
73608
+ const withSystem = assembleRequestMessages({
73609
+ split: wireSplit,
73610
+ history: [],
73611
+ turn: [
73612
+ ...pass.messages.filter(
73613
+ (m) => m.role !== "system" && !isTrailingContextContent(m.content)
73614
+ ),
73615
+ { role: "user", content: repairPrompt }
73616
+ ],
73617
+ layout: resolvePromptLayout()
73618
+ }).messages;
73458
73619
  progressRuntime.beginPass(true);
73459
73620
  markRepairTriggered();
73460
73621
  const repair = await runSinglePass(withSystem, `${sessionId2}-check-repair`);
@@ -73599,6 +73760,7 @@ var init_runOneTurn = __esm({
73599
73760
  init_verifierLifecycle();
73600
73761
  init_modelContextBuilder();
73601
73762
  init_metrics3();
73763
+ init_messageUsage();
73602
73764
  init_headlessSpine();
73603
73765
  init_harnessStateEmit();
73604
73766
  init_runtime();
@@ -79268,6 +79430,76 @@ var init_contextGrowthSummary = __esm({
79268
79430
  }
79269
79431
  });
79270
79432
 
79433
+ // src/cli/budget/cacheHitReport.ts
79434
+ function billable(record2) {
79435
+ return typeof record2.promptTokens === "number" && record2.promptTokens > 0;
79436
+ }
79437
+ function num(value) {
79438
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
79439
+ }
79440
+ function summarizeCacheHits(records, window = 500) {
79441
+ const usable = records.filter(billable);
79442
+ if (usable.length === 0) return null;
79443
+ const tail2 = usable.slice(-window);
79444
+ const rows = /* @__PURE__ */ new Map();
79445
+ let promptTokens = 0;
79446
+ let cachedPromptTokens = 0;
79447
+ for (const record2 of tail2) {
79448
+ const provider = record2.provider ?? "unknown";
79449
+ const model = record2.model ?? "unknown";
79450
+ const prompt = num(record2.promptTokens);
79451
+ const cached2 = Math.min(num(record2.cachedPromptTokens), prompt);
79452
+ promptTokens += prompt;
79453
+ cachedPromptTokens += cached2;
79454
+ const key = `${provider}::${model}`;
79455
+ const row = rows.get(key) ?? {
79456
+ provider,
79457
+ model,
79458
+ messages: 0,
79459
+ promptTokens: 0,
79460
+ cachedPromptTokens: 0,
79461
+ hitRate: 0
79462
+ };
79463
+ row.messages += 1;
79464
+ row.promptTokens += prompt;
79465
+ row.cachedPromptTokens += cached2;
79466
+ row.hitRate = row.promptTokens > 0 ? row.cachedPromptTokens / row.promptTokens : 0;
79467
+ rows.set(key, row);
79468
+ }
79469
+ return {
79470
+ messages: tail2.length,
79471
+ promptTokens,
79472
+ cachedPromptTokens,
79473
+ hitRate: promptTokens > 0 ? cachedPromptTokens / promptTokens : 0,
79474
+ byModel: [...rows.values()].sort((a, b) => b.promptTokens - a.promptTokens)
79475
+ };
79476
+ }
79477
+ function fmtTokens(n) {
79478
+ return n.toLocaleString("en-US");
79479
+ }
79480
+ function fmtRate(rate) {
79481
+ return `${(rate * 100).toFixed(1)}%`;
79482
+ }
79483
+ function formatCacheHitSummary(summary, top = 3) {
79484
+ const lines = [
79485
+ `messages ${summary.messages} \xB7 prompt ${fmtTokens(summary.promptTokens)} tokens \xB7 cached ${fmtTokens(summary.cachedPromptTokens)} \xB7 hit ${fmtRate(summary.hitRate)}`
79486
+ ];
79487
+ const rows = summary.byModel.slice(0, top);
79488
+ if (rows.length > 0) {
79489
+ lines.push(
79490
+ "top models: " + rows.map(
79491
+ (r) => `${r.provider}/${r.model} ${fmtRate(r.hitRate)} (${r.messages} msg \xB7 ${fmtTokens(r.promptTokens)} prompt)`
79492
+ ).join(" \xB7 ")
79493
+ );
79494
+ }
79495
+ return lines;
79496
+ }
79497
+ var init_cacheHitReport = __esm({
79498
+ "src/cli/budget/cacheHitReport.ts"() {
79499
+ "use strict";
79500
+ }
79501
+ });
79502
+
79271
79503
  // src/cli/utils/doctor.ts
79272
79504
  var doctor_exports = {};
79273
79505
  __export(doctor_exports, {
@@ -79559,6 +79791,24 @@ async function checkContextGrowth() {
79559
79791
  return WARN(`metrics unreadable: ${err instanceof Error ? err.message : String(err)}`);
79560
79792
  }
79561
79793
  }
79794
+ async function checkPromptCache() {
79795
+ try {
79796
+ const logger = getMetricsLogger();
79797
+ const records = await readMetrics(logger.filePath);
79798
+ const summary = summarizeCacheHits(
79799
+ records.filter((r) => r.kind === "message")
79800
+ );
79801
+ if (!summary) {
79802
+ return WARN(
79803
+ `no data: sessions running the new per-message telemetry are needed for the baseline (${logger.filePath})`
79804
+ );
79805
+ }
79806
+ const lines = formatCacheHitSummary(summary);
79807
+ return OK(lines.join("\n\n "));
79808
+ } catch (err) {
79809
+ return WARN(`metrics unreadable: ${err instanceof Error ? err.message : String(err)}`);
79810
+ }
79811
+ }
79562
79812
  function firstBlockingRed(entries) {
79563
79813
  return entries.find((e) => !e.ok && e.severity === "critical") ?? null;
79564
79814
  }
@@ -79633,6 +79883,10 @@ function buildDoctorChecks(pkg, pkgName) {
79633
79883
  // Informational aggregate over metrics.jsonl: tool round-trips,
79634
79884
  // intermediate tool bytes, history@request size, cache-hit tokens.
79635
79885
  { name: "context growth", run: () => checkContextGrowth() },
79886
+ // --- prompt-cache metrics (cache-hit-rate plan M1.2) ---
79887
+ // Provider-verified cache hit% over the `kind: 'message'` rows. WARN-only
79888
+ // ("no data") until sessions run the M1.1 telemetry.
79889
+ { name: "prompt cache", run: () => checkPromptCache() },
79636
79890
  // --- optional tool plugins (v1.5.0) ---
79637
79891
  // Playwright / eslint / ruff / LSP servers. WARN-only (never critical):
79638
79892
  // these power edge features that degrade silently when absent. Surfaced
@@ -79714,6 +79968,7 @@ var init_doctor = __esm({
79714
79968
  init_prereqChecks();
79715
79969
  init_metrics3();
79716
79970
  init_contextGrowthSummary();
79971
+ init_cacheHitReport();
79717
79972
  init_updater();
79718
79973
  require3 = createRequire3(import.meta.url);
79719
79974
  __dirname3 = path112.dirname(fileURLToPath3(import.meta.url));
@@ -80776,12 +81031,111 @@ var init_turnAdapter = __esm({
80776
81031
  });
80777
81032
 
80778
81033
  // src/cli/acp/framing.ts
80779
- function createFrameWriter(sink, onError) {
81034
+ function createDualWireDecoder(format) {
81035
+ const fmt = format ?? { mode: "ndjson" };
81036
+ let buf = Buffer.alloc(0);
81037
+ let locked = false;
81038
+ const lock = (mode) => {
81039
+ if (locked) return;
81040
+ locked = true;
81041
+ fmt.mode = mode;
81042
+ };
81043
+ const step = (out) => {
81044
+ while (buf.length > 0 && (buf[0] === 10 || buf[0] === 13)) buf = buf.subarray(1);
81045
+ if (buf.length === 0) return false;
81046
+ if (buf.subarray(0, HEADER_PREFIX.length).toString("latin1").toLowerCase() === HEADER_PREFIX) {
81047
+ const sep5 = buf.indexOf("\r\n\r\n");
81048
+ if (sep5 === -1) {
81049
+ if (buf.length > MAX_PENDING_BYTES2) {
81050
+ out.warnings.push("runaway header block without \\r\\n\\r\\n (dropped)");
81051
+ buf = Buffer.alloc(0);
81052
+ return true;
81053
+ }
81054
+ return false;
81055
+ }
81056
+ const header = buf.subarray(0, sep5).toString("latin1");
81057
+ const m = /content-length:\s*(\d+)/i.exec(header);
81058
+ if (!m) {
81059
+ out.warnings.push(
81060
+ `frame without a usable Content-Length: ${JSON.stringify(header.slice(0, 80))}`
81061
+ );
81062
+ buf = buf.subarray(sep5 + 4);
81063
+ return true;
81064
+ }
81065
+ const bodyStart = sep5 + 4;
81066
+ const bodyEnd = bodyStart + Number(m[1]);
81067
+ if (buf.length < bodyEnd) return false;
81068
+ const body = buf.subarray(bodyStart, bodyEnd).toString("utf8");
81069
+ buf = buf.subarray(bodyEnd);
81070
+ try {
81071
+ out.messages.push(JSON.parse(body));
81072
+ lock("lsp-frame");
81073
+ } catch {
81074
+ out.warnings.push(`unparseable frame body (dropped): ${JSON.stringify(body.slice(0, 80))}`);
81075
+ }
81076
+ return true;
81077
+ }
81078
+ const nl = buf.indexOf(10);
81079
+ if (nl === -1) {
81080
+ if (buf.length > MAX_PENDING_BYTES2) {
81081
+ out.warnings.push("runaway line without a newline (dropped)");
81082
+ buf = Buffer.alloc(0);
81083
+ return true;
81084
+ }
81085
+ return false;
81086
+ }
81087
+ const line = buf.subarray(0, nl).toString("utf8").replace(/\r+$/, "");
81088
+ buf = buf.subarray(nl + 1);
81089
+ if (line.trim().length === 0) return true;
81090
+ try {
81091
+ out.messages.push(JSON.parse(line));
81092
+ lock("ndjson");
81093
+ } catch {
81094
+ out.warnings.push(`unparseable JSON line (dropped): ${JSON.stringify(line.slice(0, 80))}`);
81095
+ }
81096
+ return true;
81097
+ };
81098
+ const drain = () => {
81099
+ const out = { messages: [], warnings: [] };
81100
+ while (step(out)) {
81101
+ }
81102
+ return out;
81103
+ };
81104
+ return {
81105
+ push(chunk) {
81106
+ const data = typeof chunk === "string" ? Buffer.from(chunk, "utf8") : Buffer.from(chunk);
81107
+ buf = buf.length === 0 ? data : Buffer.concat([buf, data]);
81108
+ return drain();
81109
+ },
81110
+ /** EOF: salvage a trailing NDJSON line that lacks the final newline. */
81111
+ flush() {
81112
+ const out = drain();
81113
+ if (buf.length === 0) return out;
81114
+ const line = buf.toString("utf8").trim();
81115
+ buf = Buffer.alloc(0);
81116
+ if (line.length > 0) {
81117
+ try {
81118
+ out.messages.push(JSON.parse(line));
81119
+ lock("ndjson");
81120
+ } catch {
81121
+ out.warnings.push(
81122
+ `unparseable JSON line at EOF (dropped): ${JSON.stringify(line.slice(0, 80))}`
81123
+ );
81124
+ }
81125
+ }
81126
+ return out;
81127
+ }
81128
+ };
81129
+ }
81130
+ function createFrameWriter(sink, onError, format) {
80780
81131
  const write = sink.write.bind(sink);
80781
81132
  return {
80782
81133
  write(message) {
80783
81134
  try {
80784
- write(encodeMessage(message));
81135
+ write(
81136
+ format && format.mode === "ndjson" ? `${JSON.stringify(message)}
81137
+ ` : encodeMessage(message)
81138
+ );
80785
81139
  } catch (err) {
80786
81140
  try {
80787
81141
  onError?.(err instanceof Error ? err.message : String(err));
@@ -80805,7 +81159,7 @@ function malformedFrameWarnings(chunk) {
80805
81159
  }
80806
81160
  }
80807
81161
  function attachFrameReader(stream, options) {
80808
- const parser = createMessageParser();
81162
+ const parser = createDualWireDecoder(options.format);
80809
81163
  let closed = false;
80810
81164
  const report = (detail) => {
80811
81165
  try {
@@ -80813,36 +81167,43 @@ function attachFrameReader(stream, options) {
80813
81167
  } catch {
80814
81168
  }
80815
81169
  };
81170
+ const deliver = (message) => {
81171
+ try {
81172
+ options.onMessage(message);
81173
+ } catch (err) {
81174
+ report(`consumer error: ${err instanceof Error ? err.message : String(err)}`);
81175
+ }
81176
+ };
80816
81177
  const finish2 = () => {
80817
81178
  if (closed) return;
80818
81179
  closed = true;
81180
+ try {
81181
+ const rest = parser.flush();
81182
+ for (const warning of rest.warnings) report(warning);
81183
+ for (const message of rest.messages) deliver(message);
81184
+ } catch {
81185
+ }
80819
81186
  try {
80820
81187
  options.onEof();
80821
81188
  } catch {
80822
81189
  }
80823
81190
  };
80824
81191
  const onData = (chunk) => {
80825
- for (const warning of malformedFrameWarnings(chunk)) report(warning);
80826
- let messages;
81192
+ for (const warning of malformedFrameWarnings(chunk.toString("utf8"))) report(warning);
81193
+ let decoded;
80827
81194
  try {
80828
- messages = parser.push(chunk);
81195
+ decoded = parser.push(chunk);
80829
81196
  } catch (err) {
80830
81197
  report(`decoder error: ${err instanceof Error ? err.message : String(err)}`);
80831
81198
  return;
80832
81199
  }
80833
- for (const message of messages) {
80834
- try {
80835
- options.onMessage(message);
80836
- } catch (err) {
80837
- report(`consumer error: ${err instanceof Error ? err.message : String(err)}`);
80838
- }
80839
- }
81200
+ for (const warning of decoded.warnings) report(warning);
81201
+ for (const message of decoded.messages) deliver(message);
80840
81202
  };
80841
81203
  const onError = (err) => {
80842
81204
  report(`stdin error: ${err.message}`);
80843
81205
  finish2();
80844
81206
  };
80845
- stream.setEncoding("utf8");
80846
81207
  stream.on("data", onData);
80847
81208
  stream.on("end", finish2);
80848
81209
  stream.on("close", finish2);
@@ -80857,10 +81218,13 @@ function attachFrameReader(stream, options) {
80857
81218
  }
80858
81219
  };
80859
81220
  }
81221
+ var MAX_PENDING_BYTES2, HEADER_PREFIX;
80860
81222
  var init_framing = __esm({
80861
81223
  "src/cli/acp/framing.ts"() {
80862
81224
  "use strict";
80863
81225
  init_protocol();
81226
+ MAX_PENDING_BYTES2 = 4 * 1024 * 1024;
81227
+ HEADER_PREFIX = "content-length:";
80864
81228
  }
80865
81229
  });
80866
81230
 
@@ -80887,12 +81251,15 @@ function startAcpServer(deps) {
80887
81251
  `));
80888
81252
  const sessions = /* @__PURE__ */ new Map();
80889
81253
  let closed = false;
81254
+ const format = { mode: "ndjson" };
80890
81255
  const writer = createFrameWriter(
80891
81256
  deps.output ?? process.stdout,
80892
- (message) => log(`[zelari-code acp] stdout write failed: ${message}`)
81257
+ (message) => log(`[zelari-code acp] stdout write failed: ${message}`),
81258
+ format
80893
81259
  );
80894
81260
  const shutdownWaiters = [];
80895
81261
  const reader = attachFrameReader(input, {
81262
+ format,
80896
81263
  onMessage: (raw) => {
80897
81264
  void handleMessage(raw);
80898
81265
  },
@@ -81116,7 +81483,7 @@ function parseAcpFlags(argv) {
81116
81483
  return out;
81117
81484
  }
81118
81485
  function acpHelpText() {
81119
- return 'zelari-code acp \u2014 Agent Client Protocol server on stdio (for editors)\n\nSpeaks JSON-RPC 2.0 over stdin/stdout with LSP-style framing\n(`Content-Length: <bytes>\\r\\n\\r\\n<json>`). Point an ACP-capable editor\n(e.g. Zed) at this command as a custom agent server.\n\nMethods (implemented subset):\n initialize protocolVersion + agent capabilities\n session/new { cwd } -> { sessionId }\n session/prompt { sessionId, prompt: [{type:"text",text}] }\n -> { stopReason } when the turn ends\n session/cancel { sessionId }\n\nNotifications sent to the client: session/update with\nagent_message_chunk, tool_call and tool_call_update (see\nsrc/cli/acp/protocol.ts for the exact subset and its non-goals).\n\nOptions:\n --cwd <path> Fallback workspace when a client omits cwd\n (default: current directory)\n --provider <id> Provider override (default: the active provider)\n --model <id> Model override (default: the provider default)\n --help, -h Print this help and exit\n\nTurns run through the same headless dispatch as `--headless`\n(kraken by default), one turn at a time; stdin EOF shuts down cleanly.\n';
81486
+ return 'zelari-code acp \u2014 Agent Client Protocol server on stdio (for editors)\n\nSpeaks JSON-RPC 2.0 over stdin/stdout as newline-delimited JSON\n(the ACP stdio transport, as spoken by Zed). LSP-style frames\n(`Content-Length: <bytes>\\r\\n\\r\\n<json>`) are also accepted and\nmirrored on output. Point an ACP-capable editor at this command as\na custom agent server.\n\nMethods (implemented subset):\n initialize protocolVersion + agent capabilities\n session/new { cwd } -> { sessionId }\n session/prompt { sessionId, prompt: [{type:"text",text}] }\n -> { stopReason } when the turn ends\n session/cancel { sessionId }\n\nNotifications sent to the client: session/update with\nagent_message_chunk, tool_call and tool_call_update (see\nsrc/cli/acp/protocol.ts for the exact subset and its non-goals).\n\nOptions:\n --cwd <path> Fallback workspace when a client omits cwd\n (default: current directory)\n --provider <id> Provider override (default: the active provider)\n --model <id> Model override (default: the provider default)\n --help, -h Print this help and exit\n\nTurns run through the same headless dispatch as `--headless`\n(kraken by default), one turn at a time; stdin EOF shuts down cleanly.\n';
81120
81487
  }
81121
81488
  async function runAcpCommand(opts = {}) {
81122
81489
  if (opts.help === true) {
@@ -84729,6 +85096,7 @@ ${lines.join("\n")}`;
84729
85096
  init_harness();
84730
85097
  init_observationStore();
84731
85098
  init_metrics3();
85099
+ init_messageUsage();
84732
85100
  init_modelPricing();
84733
85101
  init_openai_compatible();
84734
85102
  init_resolveStream();
@@ -84937,6 +85305,8 @@ function useChatTurn(params) {
84937
85305
  let memoryAutoWrite = false;
84938
85306
  let historySeedLen = 0;
84939
85307
  let systemPrefixLen = 0;
85308
+ let trailingSeedLen = 0;
85309
+ let seedMessages;
84940
85310
  let turnSucceeded = false;
84941
85311
  try {
84942
85312
  const anchored = maybeAnchorShortAnswer(userText);
@@ -85268,6 +85638,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
85268
85638
  volatileSessionBlock,
85269
85639
  composedWorkspace
85270
85640
  ].filter(Boolean).join("\n\n");
85641
+ const promptLayout = resolvePromptLayout();
85271
85642
  let systemMessages = [];
85272
85643
  let lastStableHash;
85273
85644
  try {
@@ -85295,7 +85666,16 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
85295
85666
  ragContext: void 0
85296
85667
  });
85297
85668
  lastStableHash = hashStablePrompt(split.stable);
85298
- systemMessages = systemMessagesFromSplit(split);
85669
+ const assembled = assembleRequestMessages({
85670
+ split,
85671
+ history: historyForModel,
85672
+ turn: [{ role: "user", content: effectiveUserText }],
85673
+ layout: promptLayout
85674
+ });
85675
+ seedMessages = assembled.messages;
85676
+ systemMessages = assembled.messages.slice(0, assembled.systemCount);
85677
+ systemPrefixLen = assembled.systemCount;
85678
+ trailingSeedLen = assembled.trailingCount;
85299
85679
  } catch {
85300
85680
  const languageModule = buildLanguagePolicyModuleFor(userText);
85301
85681
  const fallback = [
@@ -85309,8 +85689,10 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
85309
85689
  ].join("\n");
85310
85690
  lastStableHash = hashStablePrompt(fallback);
85311
85691
  systemMessages = [{ role: "system", content: fallback }];
85692
+ seedMessages = void 0;
85693
+ trailingSeedLen = 0;
85312
85694
  }
85313
- systemPrefixLen = systemMessages.length;
85695
+ if (seedMessages === void 0) systemPrefixLen = systemMessages.length;
85314
85696
  const perTurnEnv = envNumber(process.env.ZELARI_MAX_TOOL_CALLS, {
85315
85697
  default: 25,
85316
85698
  min: 1
@@ -85329,7 +85711,11 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
85329
85711
  // hardcode "openai-compatible" (the transport family) so snapshots
85330
85712
  // and telemetry mislabeled deepseek/glm/minimax routing.
85331
85713
  provider: envConfig.providerId,
85332
- messages: [
85714
+ // M2.1: the seed assembled by the layout helper when the split prompt
85715
+ // built (trailing layout = [stable system][history][trailing
85716
+ // context][user]); the degraded fallback keeps the plain
85717
+ // [system][history][user] shape.
85718
+ messages: seedMessages ?? [
85333
85719
  ...systemMessages,
85334
85720
  // v1.8.0: shared rolling history (agent/council/zelari) so short
85335
85721
  // answers bind to prior ---QUESTION--- blocks. Possibly empty
@@ -85466,6 +85852,14 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
85466
85852
  totalTokens: event.usage.totalTokens,
85467
85853
  cachedPromptTokens: event.usage.cachedPromptTokens
85468
85854
  });
85855
+ recordMessageUsage({
85856
+ sessionId: sessionId2,
85857
+ provider: envConfig.providerId,
85858
+ model: envConfig.model,
85859
+ promptTokens: event.usage.promptTokens,
85860
+ completionTokens: event.usage.completionTokens,
85861
+ cachedPromptTokens: event.usage.cachedPromptTokens ?? 0
85862
+ });
85469
85863
  }
85470
85864
  if (streamContent) {
85471
85865
  const sealed = streamScrub.finalize(streamContent);
@@ -85659,7 +86053,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
85659
86053
  const h = harnessRef.current;
85660
86054
  if (h && turnSucceeded) {
85661
86055
  const all = h.getMessages();
85662
- const seedLen = systemPrefixLen + historySeedLen + 1;
86056
+ const seedLen = systemPrefixLen + historySeedLen + trailingSeedLen + 1;
85663
86057
  if (all.length > seedLen) {
85664
86058
  appendMessages(
85665
86059
  all.slice(seedLen).map((m) => {