zelari-code 1.5.4 → 1.6.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.
@@ -18592,13 +18592,33 @@ var init_AgentHarness = __esm({
18592
18592
  this.maxQueuedIterations = config2.maxQueuedIterations ?? 3;
18593
18593
  this.maxToolLoopIterations = config2.maxToolLoopIterations ?? 30;
18594
18594
  }
18595
+ /**
18596
+ * Snapshot of the live transcript (`this.config.messages`) as mutated
18597
+ * across `run()` iterations — the seed passed at construction plus any
18598
+ * assistant turns (with `toolCalls`) and tool results the loop appended.
18599
+ *
18600
+ * Used by the single-agent chat loop to carry rolling history across
18601
+ * turns: after a run completes, the caller reads the tail of this array
18602
+ * (the assistant/tool messages produced this turn) and feeds it back as
18603
+ * the seed for the next turn, so the model sees its own prior question
18604
+ * when the user answers with a short reply.
18605
+ *
18606
+ * The returned reference is the live array — callers MUST treat it as
18607
+ * read-only (do not mutate). Copy (`[...harness.getMessages()]`) before
18608
+ * retaining across runs.
18609
+ *
18610
+ * @since v1.6.0
18611
+ */
18612
+ getMessages() {
18613
+ return this.config.messages;
18614
+ }
18595
18615
  /**
18596
18616
  * Member identity fields (memberId + memberName) to merge into every
18597
18617
  * event payload. Returns an empty object when the run is a direct
18598
18618
  * user prompt (no member context), so call sites can spread it
18599
18619
  * unconditionally.
18600
18620
  *
18601
- * @since 0.5.0
18621
+ * @since v0.5.0
18602
18622
  */
18603
18623
  memberFields() {
18604
18624
  return {
@@ -29324,7 +29344,7 @@ function checkBundle() {
29324
29344
  }
29325
29345
  }
29326
29346
  function checkRuntimeDeps() {
29327
- const required2 = ["react", "react-dom", "ink", "ink-text-input", "zod"];
29347
+ const required2 = ["react", "ink", "ink-text-input", "zod"];
29328
29348
  const missing = [];
29329
29349
  for (const dep of required2) {
29330
29350
  try {
@@ -29336,7 +29356,7 @@ function checkRuntimeDeps() {
29336
29356
  }
29337
29357
  if (missing.length === 0) {
29338
29358
  return OK(
29339
- "runtime deps resolvable (react, react-dom, ink, ink-text-input, zod)"
29359
+ "runtime deps resolvable (react, ink, ink-text-input, zod)"
29340
29360
  );
29341
29361
  }
29342
29362
  return FAIL(
@@ -33050,6 +33070,14 @@ init_keyStore();
33050
33070
  init_toolRegistry();
33051
33071
  init_skills2();
33052
33072
 
33073
+ // packages/core/dist/events/index.js
33074
+ init_events();
33075
+
33076
+ // packages/core/dist/index.js
33077
+ init_harness();
33078
+ init_council();
33079
+ init_skills2();
33080
+
33053
33081
  // src/cli/hooks/messageHelpers.ts
33054
33082
  function appendSystem(setMessages, content, ts = Date.now()) {
33055
33083
  setMessages((prev2) => [
@@ -33135,6 +33163,61 @@ function finalizeStreamingAssistant(setMessages) {
33135
33163
  });
33136
33164
  }
33137
33165
 
33166
+ // src/cli/hooks/historyCompaction.ts
33167
+ var COMPACT_MARKER = "[history] Earlier turns were compacted to stay within the context budget.";
33168
+ function resolveMaxMessages(opts) {
33169
+ const raw = process.env.ZELARI_HISTORY_TURNS;
33170
+ const parsed = raw ? Number.parseInt(raw, 10) : 6;
33171
+ const envTurns = Number.isFinite(parsed) && parsed >= 0 ? parsed : 6;
33172
+ const turns = opts?.maxMessages ? Math.ceil(opts.maxMessages / 4) : envTurns;
33173
+ if (turns <= 0) return 0;
33174
+ return turns * 4;
33175
+ }
33176
+ function findValidCutIndex(messages, naiveCut) {
33177
+ let cut = naiveCut;
33178
+ while (cut < messages.length) {
33179
+ const kept = messages.slice(cut);
33180
+ const declared = /* @__PURE__ */ new Set();
33181
+ for (const m of kept) {
33182
+ if (m.role === "assistant" && m.toolCalls) {
33183
+ for (const tc of m.toolCalls) declared.add(tc.id);
33184
+ }
33185
+ }
33186
+ let moved = false;
33187
+ for (let k = 0; k < kept.length; k++) {
33188
+ const m = kept[k];
33189
+ if (m.role === "tool" && m.toolCallId && !declared.has(m.toolCallId)) {
33190
+ for (let j = cut - 1; j >= 0; j--) {
33191
+ const prev2 = messages[j];
33192
+ if (prev2.role === "assistant" && prev2.toolCalls && prev2.toolCalls.some((tc) => tc.id === m.toolCallId)) {
33193
+ cut = j;
33194
+ moved = true;
33195
+ break;
33196
+ }
33197
+ }
33198
+ break;
33199
+ }
33200
+ }
33201
+ if (!moved) break;
33202
+ }
33203
+ return cut;
33204
+ }
33205
+ function compactHistory(messages, opts) {
33206
+ const maxMessages = resolveMaxMessages(opts);
33207
+ if (maxMessages === 0) return [];
33208
+ if (messages.length <= maxMessages * 2) return messages;
33209
+ const naiveCut = messages.length - maxMessages;
33210
+ const cut = findValidCutIndex(messages, naiveCut);
33211
+ const dropped = cut;
33212
+ if (dropped === 0) return messages;
33213
+ const kept = messages.slice(cut);
33214
+ const summary = {
33215
+ role: "system",
33216
+ content: `${COMPACT_MARKER} ${dropped} earlier message(s) dropped.`
33217
+ };
33218
+ return [summary, ...kept];
33219
+ }
33220
+
33138
33221
  // src/cli/hooks/chatStats.ts
33139
33222
  function computeSessionStatsDelta(realUsage, userText, assistantContent, model, prev2) {
33140
33223
  const promptTokens = realUsage ? realUsage.promptTokens : Math.ceil(userText.length / 4);
@@ -33160,16 +33243,22 @@ function useChatTurn(params) {
33160
33243
  setSessionActive,
33161
33244
  setSessionStats,
33162
33245
  setLive,
33163
- liveRef
33246
+ liveRef,
33247
+ setPicker
33164
33248
  } = params;
33165
33249
  const harnessRef = useRef4(null);
33166
33250
  const [queueCount, setQueueCount] = useState6(0);
33251
+ const historyRef = useRef4([]);
33167
33252
  const useLiveModel = !!(setLive && liveRef);
33168
33253
  const dispatchPrompt = useCallback2(
33169
33254
  async (userText, opts) => {
33170
33255
  let envConfig;
33171
33256
  let harness;
33257
+ let historySeedLen = 0;
33258
+ let turnSucceeded = false;
33172
33259
  try {
33260
+ historyRef.current = compactHistory(historyRef.current);
33261
+ historySeedLen = historyRef.current.length;
33173
33262
  envConfig = await providerFromEnv();
33174
33263
  if (!envConfig) {
33175
33264
  const active = resolveActiveProvider();
@@ -33328,6 +33417,10 @@ function useChatTurn(params) {
33328
33417
  provider: "openai-compatible",
33329
33418
  messages: [
33330
33419
  { role: "system", content: systemPrompt },
33420
+ // v1.6.0: seed prior turns so the model sees its own last
33421
+ // question when the user answers briefly. historyRef.current
33422
+ // is compacted above (possibly empty if ZELARI_HISTORY_TURNS=0).
33423
+ ...historyRef.current,
33331
33424
  { role: "user", content: userText }
33332
33425
  ],
33333
33426
  tools: toolRegistry.toOpenAITools().map((t) => ({
@@ -33458,10 +33551,53 @@ function useChatTurn(params) {
33458
33551
  }
33459
33552
  }
33460
33553
  }
33554
+ turnSucceeded = true;
33461
33555
  } finally {
33462
33556
  flushStreaming();
33463
33557
  if (useLiveModel) finalizeStreaming(setMessages, setLive);
33464
33558
  else finalizeStreamingAssistant(setMessages);
33559
+ try {
33560
+ const h = harnessRef.current;
33561
+ if (h && turnSucceeded) {
33562
+ const all = h.getMessages();
33563
+ const seedLen = 1 + historySeedLen + 1;
33564
+ if (all.length > seedLen) {
33565
+ historyRef.current = historyRef.current.concat(
33566
+ all.slice(seedLen)
33567
+ );
33568
+ }
33569
+ }
33570
+ } catch {
33571
+ }
33572
+ if (turnSucceeded && setPicker && assistantContent) {
33573
+ try {
33574
+ const clar = parseClarificationRequest(assistantContent);
33575
+ if (clar && clar.choices && clar.choices.length >= 2) {
33576
+ const cleaned = cleanAgentContent(assistantContent);
33577
+ if (cleaned !== assistantContent) {
33578
+ setMessages((prev2) => {
33579
+ const next = [...prev2];
33580
+ for (let i = next.length - 1; i >= 0; i--) {
33581
+ if (next[i].role === "assistant") {
33582
+ next[i] = { ...next[i], content: cleaned };
33583
+ break;
33584
+ }
33585
+ }
33586
+ return next;
33587
+ });
33588
+ }
33589
+ setPicker({
33590
+ kind: "clarification",
33591
+ title: clar.question,
33592
+ items: clar.choices.map((c) => ({ value: c, label: c })),
33593
+ onAnswer: (value) => {
33594
+ void dispatchPrompt(value);
33595
+ }
33596
+ });
33597
+ }
33598
+ } catch {
33599
+ }
33600
+ }
33465
33601
  harnessRef.current = null;
33466
33602
  setQueueCount(0);
33467
33603
  setBusy(false);
@@ -36404,7 +36540,11 @@ function App() {
36404
36540
  setSessionActive: session.setSessionActive,
36405
36541
  setSessionStats,
36406
36542
  setLive: session.setLive,
36407
- liveRef: session.liveRef
36543
+ liveRef: session.liveRef,
36544
+ // v1.6.0: lets dispatchPrompt open a SelectList when the agent poses a
36545
+ // ---QUESTION--- clarifying block, so the user picks from the offered
36546
+ // choices instead of typing (and the answer binds via rolling history).
36547
+ setPicker
36408
36548
  });
36409
36549
  const onNewSession = useCallback5((id) => {
36410
36550
  void session.writerRef.current?.close();
@@ -36445,8 +36585,12 @@ function App() {
36445
36585
  });
36446
36586
  const onPickerSelect = useCallback5((value) => {
36447
36587
  if (!picker) return;
36448
- const cmd = `${picker.commandPrefix} ${value}`;
36449
36588
  setPicker(null);
36589
+ if (picker.kind === "clarification") {
36590
+ picker.onAnswer?.(value);
36591
+ return;
36592
+ }
36593
+ const cmd = `${picker.commandPrefix} ${value}`;
36450
36594
  void handleSubmit(cmd);
36451
36595
  }, [picker, handleSubmit]);
36452
36596
  const onPickerCancel = useCallback5(() => setPicker(null), []);