omnius 1.0.463 → 1.0.465

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -578528,7 +578528,7 @@ function classifyThinkOutcome(raw) {
578528
578528
  }
578529
578529
  return null;
578530
578530
  }
578531
- var TOOL_SUBSETS, TOOL_AUTO_DEMOTE_TURNS, TOOL_ACTION_REASON_KEYS, TOOL_ACTION_REASON_SCOPES, TOOL_ACTION_REASON_SCHEMA, SYSTEM_PROMPT, SYSTEM_PROMPT_MEDIUM, SYSTEM_PROMPT_SMALL, VISUAL_TOOLS, AUDIO_TOOLS, SOCIAL_TOOLS, SPATIAL_TOOLS, CODE_TOOLS, AgenticRunner, OllamaAgenticBackend;
578531
+ var TOOL_SUBSETS, TOOL_AUTO_DEMOTE_TURNS, TOOL_ACTION_REASON_KEYS, TOOL_ACTION_REASON_SCOPES, READ_ONLY_ACTION_REASON_OPTIONAL_TOOLS, TOOL_ACTION_REASON_SCHEMA, SYSTEM_PROMPT, SYSTEM_PROMPT_MEDIUM, SYSTEM_PROMPT_SMALL, VISUAL_TOOLS, AUDIO_TOOLS, SOCIAL_TOOLS, SPATIAL_TOOLS, CODE_TOOLS, AgenticRunner, OllamaAgenticBackend;
578532
578532
  var init_agenticRunner = __esm({
578533
578533
  "packages/orchestrator/dist/agenticRunner.js"() {
578534
578534
  "use strict";
@@ -578633,6 +578633,25 @@ var init_agenticRunner = __esm({
578633
578633
  "completion",
578634
578634
  "other"
578635
578635
  ]);
578636
+ READ_ONLY_ACTION_REASON_OPTIONAL_TOOLS = /* @__PURE__ */ new Set([
578637
+ "file_read",
578638
+ "file_explore",
578639
+ "list_directory",
578640
+ "grep_search",
578641
+ "grep",
578642
+ "glob_find",
578643
+ "find_files",
578644
+ "todo_read",
578645
+ "memory_read",
578646
+ "memory_search",
578647
+ "semantic_map",
578648
+ "code_graph",
578649
+ "graph_query",
578650
+ "graph_traverse",
578651
+ "web_search",
578652
+ "web_fetch",
578653
+ "tool_search"
578654
+ ]);
578636
578655
  TOOL_ACTION_REASON_SCHEMA = {
578637
578656
  type: "object",
578638
578657
  description: "Compact public action contract for this exact tool call. Do not include hidden chain-of-thought; use concise task-state facts.",
@@ -579573,7 +579592,7 @@ ${parts.join("\n")}
579573
579592
  if (ts.failedApproaches.length > 0) {
579574
579593
  lines.push(`recent_failed_approaches=${ts.failedApproaches.slice(-3).join(" | ").replace(/\s+/g, " ").slice(0, 420)}`);
579575
579594
  }
579576
- lines.push("tool_call_contract=Each tool call must include action_reason {task_anchor,intent,evidence,expected_result,scope}; compact public facts only, no hidden chain-of-thought.");
579595
+ lines.push("tool_call_contract=Mutating/side-effectful tool calls require action_reason {task_anchor,intent,evidence,expected_result,scope}; read-only evidence tools may include it but must never be blocked only for missing metadata. Compact public facts only, no hidden chain-of-thought.");
579577
579596
  lines.push(`scope_contract=${Array.from(TOOL_ACTION_REASON_SCOPES).join("|")}; use targeted_patch for constrained code edits and full_file_rewrite only for deliberate whole-file replacement.`);
579578
579597
  const focus = this._focusSupervisor?.snapshot().directive ?? null;
579579
579598
  if (focus) {
@@ -584785,7 +584804,7 @@ ${blob}
584785
584804
  * size. The hash is computed over the full canonical value.
584786
584805
  */
584787
584806
  _buildExactArgsKey(args) {
584788
- return Object.entries(args ?? {}).sort(([a2], [b]) => a2.localeCompare(b)).map(([k, v]) => `${k}=${this._formatExactArgValue(v)}`).join(",");
584807
+ return Object.entries(args ?? {}).filter(([key]) => !TOOL_ACTION_REASON_KEYS.has(key)).sort(([a2], [b]) => a2.localeCompare(b)).map(([k, v]) => `${k}=${this._formatExactArgValue(v)}`).join(",");
584789
584808
  }
584790
584809
  _buildToolFingerprint(name10, args) {
584791
584810
  const canonical = this.lookupRegisteredTool(name10)?.name ?? name10;
@@ -585260,7 +585279,8 @@ Rewrite it now for ${ctx3.model}.`;
585260
585279
  const unknownKeys = providedKeys.filter((key) => !props.has(key));
585261
585280
  const aliases = this.suggestArgumentAliases(missingRequired, providedKeys, args, props);
585262
585281
  const corrected = this.buildCorrectedArgsPreview(args, props, aliases);
585263
- const siblingMatches = this.findSiblingToolSchemaMatches(toolName, providedKeys);
585282
+ const actionReasonOnlyFailure = validationError.includes("[TOOL ACTION REASON CONTRACT]");
585283
+ const siblingMatches = actionReasonOnlyFailure ? [] : this.findSiblingToolSchemaMatches(toolName, providedKeys);
585264
585284
  const lines = [
585265
585285
  `[RUNTIME TOOL ARGUMENT REPAIR]`,
585266
585286
  `Tool call failed before execution: ${toolName}`,
@@ -588680,7 +588700,7 @@ ${memoryLines.join("\n")}`
588680
588700
  const executeSingle = async (tc) => {
588681
588701
  if (this.aborted)
588682
588702
  return null;
588683
- const actionReasonForAdvisory = this._extractToolActionReason(tc.arguments ?? {});
588703
+ const actionReasonForAdvisory = this._toolActionReasonForCall(tc.name, tc.arguments ?? {});
588684
588704
  const cohortKey = this.buildArgCohortKey(tc.name, tc.arguments);
588685
588705
  const cohort = this._argCohorts.get(cohortKey);
588686
588706
  if (cohort && cohort.failure >= 3 && cohort.success === 0) {
@@ -593831,21 +593851,40 @@ ${marker}` : marker);
593831
593851
  }
593832
593852
  return matchesFresh(args);
593833
593853
  }
593834
- _requiresToolActionReason(toolName) {
593854
+ _toolActionReasonPolicy(toolName, args) {
593835
593855
  if (process.env["OMNIUS_DISABLE_TOOL_ACTION_REASON"] === "1") {
593836
- return false;
593856
+ return "disabled";
593837
593857
  }
593838
593858
  if ((process.env["VITEST"] || process.env["NODE_ENV"] === "test") && process.env["OMNIUS_REQUIRE_TOOL_ACTION_REASON"] !== "1") {
593839
- return false;
593859
+ return "disabled";
593840
593860
  }
593841
593861
  if (this.options.artifactMode === "internal")
593842
- return false;
593862
+ return "disabled";
593843
593863
  if (toolName.startsWith("__"))
593844
- return false;
593845
- return true;
593864
+ return "disabled";
593865
+ return this._toolActionReasonIsOptional(toolName, args) ? "optional" : "required";
593866
+ }
593867
+ _requiresToolActionReason(toolName, args) {
593868
+ return this._toolActionReasonPolicy(toolName, args) === "required";
593869
+ }
593870
+ _toolActionReasonIsOptional(toolName, args) {
593871
+ const resolved = this.lookupRegisteredTool(toolName);
593872
+ const canonical = resolved?.name ?? toolName;
593873
+ if (READ_ONLY_ACTION_REASON_OPTIONAL_TOOLS.has(canonical))
593874
+ return true;
593875
+ const tool = resolved?.tool;
593876
+ if (typeof tool?.isReadOnly === "function") {
593877
+ try {
593878
+ if (tool.isReadOnly(args ?? {}))
593879
+ return true;
593880
+ } catch {
593881
+ }
593882
+ }
593883
+ return false;
593846
593884
  }
593847
593885
  _withToolActionReasonSchema(toolName, parameters) {
593848
- if (!this._requiresToolActionReason(toolName)) {
593886
+ const policy = this._toolActionReasonPolicy(toolName);
593887
+ if (policy === "disabled") {
593849
593888
  return parameters ?? { type: "object", properties: {} };
593850
593889
  }
593851
593890
  const base3 = parameters && typeof parameters === "object" && !Array.isArray(parameters) ? { ...parameters } : { type: "object" };
@@ -593856,7 +593895,7 @@ ${marker}` : marker);
593856
593895
  ...base3,
593857
593896
  type: "object",
593858
593897
  properties,
593859
- required: [.../* @__PURE__ */ new Set([...required, "action_reason"])]
593898
+ required: policy === "required" ? [.../* @__PURE__ */ new Set([...required, "action_reason"])] : required
593860
593899
  };
593861
593900
  }
593862
593901
  _extractToolActionReason(args) {
@@ -593883,20 +593922,46 @@ ${marker}` : marker);
593883
593922
  };
593884
593923
  return reason;
593885
593924
  }
593925
+ _toolActionReasonForCall(toolName, args) {
593926
+ const explicit = this._extractToolActionReason(args);
593927
+ if (explicit)
593928
+ return explicit;
593929
+ if (this._toolActionReasonPolicy(toolName, args) !== "optional")
593930
+ return null;
593931
+ return this._synthesizeReadOnlyToolActionReason(toolName, args);
593932
+ }
593933
+ _synthesizeReadOnlyToolActionReason(toolName, args) {
593934
+ const canonical = this.lookupRegisteredTool(toolName)?.name ?? toolName;
593935
+ const goal = this._taskState.currentStep || this._taskState.nextAction || this._taskState.goal || this._taskState.originalGoal || "current task";
593936
+ const targets = this._extractToolTargetPaths(canonical, args).slice(0, 3);
593937
+ const targetText = targets.length > 0 ? ` for ${targets.join(", ")}` : "";
593938
+ return {
593939
+ task_anchor: String(goal).replace(/\s+/g, " ").trim().slice(0, 160),
593940
+ intent: `Run read-only ${canonical}${targetText} to gather current evidence.`,
593941
+ evidence: "Read-only metadata fallback; no project mutation is authorized by this action.",
593942
+ expected_result: `Current ${canonical} evidence for the next decision.`,
593943
+ scope: "discovery"
593944
+ };
593945
+ }
593886
593946
  _validateToolActionReason(toolName, args) {
593887
- if (!this._requiresToolActionReason(toolName))
593947
+ const policy = this._toolActionReasonPolicy(toolName, args);
593948
+ if (policy === "disabled")
593888
593949
  return null;
593889
593950
  const raw = args["action_reason"] ?? args["actionReason"] ?? args["justification"];
593890
593951
  if (!raw || typeof raw === "string" && !raw.trim()) {
593952
+ if (policy === "optional")
593953
+ return null;
593891
593954
  return [
593892
593955
  "[TOOL ACTION REASON CONTRACT]",
593893
- `Every tool call must include action_reason: { task_anchor, intent, evidence, expected_result, scope }.`,
593956
+ `This side-effectful tool call must include action_reason: { task_anchor, intent, evidence, expected_result, scope }.`,
593894
593957
  `Use compact public facts only; do not include hidden chain-of-thought.`,
593895
593958
  `scope must be one of: ${Array.from(TOOL_ACTION_REASON_SCOPES).join(", ")}.`
593896
593959
  ].join("\n");
593897
593960
  }
593898
593961
  const reason = this._extractToolActionReason(args);
593899
593962
  if (!reason) {
593963
+ if (policy === "optional")
593964
+ return null;
593900
593965
  return "[TOOL ACTION REASON CONTRACT] action_reason must be an object with compact public task-state fields.";
593901
593966
  }
593902
593967
  const missing = [
@@ -593907,9 +593972,13 @@ ${marker}` : marker);
593907
593972
  "scope"
593908
593973
  ].filter((key) => reason[key].length < 3);
593909
593974
  if (missing.length > 0) {
593975
+ if (policy === "optional")
593976
+ return null;
593910
593977
  return `[TOOL ACTION REASON CONTRACT] action_reason missing non-empty field(s): ${missing.join(", ")}.`;
593911
593978
  }
593912
593979
  if (!TOOL_ACTION_REASON_SCOPES.has(reason.scope)) {
593980
+ if (policy === "optional")
593981
+ return null;
593913
593982
  return `[TOOL ACTION REASON CONTRACT] action_reason.scope="${reason.scope}" is invalid. Use one of: ${Array.from(TOOL_ACTION_REASON_SCOPES).join(", ")}.`;
593914
593983
  }
593915
593984
  return null;
@@ -595967,7 +596036,7 @@ ${trimmedNew}`;
595967
596036
  args = JSON.parse(tc.function.arguments);
595968
596037
  } catch {
595969
596038
  }
595970
- const actionReason = this._extractToolActionReason(args);
596039
+ const actionReason = this._toolActionReasonForCall(name10, args);
595971
596040
  const argsKey = this._buildExactArgsKey(args);
595972
596041
  const fingerprint = this._buildToolFingerprint(name10, args);
595973
596042
  const prior = this._adversaryToolOutcomes.find((o2) => o2.succeeded && o2.tool === name10 && o2.fingerprint === fingerprint && o2.stateVersion === this._adversaryStateVersion && o2.turn < turn);
@@ -619070,7 +619139,10 @@ function renderTaskCompleteBox(host, data) {
619070
619139
  testsRun: data.testsRun ? [...data.testsRun] : [],
619071
619140
  provenanceAnchors: data.provenanceAnchors ? [...data.provenanceAnchors] : []
619072
619141
  };
619073
- host.registerDynamicBlock(blockId, (width) => buildBoxLines(frozen, width));
619142
+ host.registerDynamicBlock(blockId, (width) => buildBoxLines(frozen, width), {
619143
+ renderer: "task-complete",
619144
+ data: frozen
619145
+ });
619074
619146
  host.appendDynamicBlock(blockId);
619075
619147
  return blockId;
619076
619148
  }
@@ -619094,7 +619166,11 @@ function renderSessionHistoryBox(host, data) {
619094
619166
  };
619095
619167
  host.registerDynamicBlock(
619096
619168
  blockId,
619097
- (width) => buildSessionHistoryBoxLines(frozen, width)
619169
+ (width) => buildSessionHistoryBoxLines(frozen, width),
619170
+ {
619171
+ renderer: "session-history",
619172
+ data: frozen
619173
+ }
619098
619174
  );
619099
619175
  host.appendDynamicBlock(blockId);
619100
619176
  return blockId;
@@ -620137,6 +620213,7 @@ __export(render_exports, {
620137
620213
  getColorsEnabled: () => getColorsEnabled,
620138
620214
  getEmojisEnabled: () => getEmojisEnabled,
620139
620215
  getTermWidth: () => getTermWidth,
620216
+ isPersistedDynamicBlockSpec: () => isPersistedDynamicBlockSpec,
620140
620217
  pastel: () => pastel,
620141
620218
  renderAssistantText: () => renderAssistantText,
620142
620219
  renderBoxedBlock: () => renderBoxedBlock,
@@ -620150,6 +620227,7 @@ __export(render_exports, {
620150
620227
  renderInfo: () => renderInfo,
620151
620228
  renderModelList: () => renderModelList,
620152
620229
  renderModelSwitch: () => renderModelSwitch,
620230
+ renderPersistedDynamicBlockSpec: () => renderPersistedDynamicBlockSpec,
620153
620231
  renderRichHeader: () => renderRichHeader,
620154
620232
  renderSlashHelp: () => renderSlashHelp,
620155
620233
  renderSteeringIntake: () => renderSteeringIntake,
@@ -620599,6 +620677,68 @@ function applyCollapseToBox(fullLines, opts) {
620599
620677
  );
620600
620678
  return [...head, ...body, lessRow, bottom];
620601
620679
  }
620680
+ function isPersistedDynamicBlockSpec(value2) {
620681
+ if (!value2 || typeof value2 !== "object") return false;
620682
+ const renderer = value2.renderer;
620683
+ return renderer === "tool-combined" || renderer === "tool-result" || renderer === "tool-box" || renderer === "task-complete" || renderer === "session-history";
620684
+ }
620685
+ function renderPersistedDynamicBlockSpec(spec, width, id2) {
620686
+ switch (spec.renderer) {
620687
+ case "tool-combined": {
620688
+ let lines = buildCombinedToolBoxLines(
620689
+ spec.toolName,
620690
+ spec.callArgs ?? {},
620691
+ Boolean(spec.success),
620692
+ String(spec.output ?? ""),
620693
+ spec.opts ?? {},
620694
+ width
620695
+ );
620696
+ if (spec.collapse && id2) {
620697
+ ensureCollapsible(id2);
620698
+ lines = applyCollapseToBox(lines, {
620699
+ state: getCollapseState(id2),
620700
+ desc: spec.collapse,
620701
+ width
620702
+ });
620703
+ }
620704
+ return lines;
620705
+ }
620706
+ case "tool-result": {
620707
+ let lines = buildToolResultBoxLines(
620708
+ spec.toolName,
620709
+ Boolean(spec.success),
620710
+ String(spec.output ?? ""),
620711
+ spec.opts ?? {},
620712
+ width
620713
+ );
620714
+ if (spec.collapse && id2) {
620715
+ ensureCollapsible(id2);
620716
+ lines = applyCollapseToBox(lines, {
620717
+ state: getCollapseState(id2),
620718
+ desc: spec.collapse,
620719
+ width
620720
+ });
620721
+ }
620722
+ return lines;
620723
+ }
620724
+ case "tool-box": {
620725
+ let lines = buildToolBoxLines(spec.data, width);
620726
+ if (spec.collapse && id2) {
620727
+ ensureCollapsible(id2);
620728
+ lines = applyCollapseToBox(lines, {
620729
+ state: getCollapseState(id2),
620730
+ desc: spec.collapse,
620731
+ width
620732
+ });
620733
+ }
620734
+ return lines;
620735
+ }
620736
+ case "task-complete":
620737
+ return buildBoxLines(spec.data, width);
620738
+ case "session-history":
620739
+ return buildSessionHistoryBoxLines(spec.data, width);
620740
+ }
620741
+ }
620602
620742
  function buildToolContentRow(content, width, colorCode) {
620603
620743
  const border = toolColorSeq(colorCode);
620604
620744
  const reset = toolResetSeq();
@@ -620841,11 +620981,12 @@ function buildToolBoxLines(data, width) {
620841
620981
  lines.push(buildToolBottom(w, data.colorCode));
620842
620982
  return lines;
620843
620983
  }
620844
- function renderToolDynamicBlock(kind, render2, opts, collapse) {
620984
+ function renderToolDynamicBlock(kind, render2, opts, collapse, persistedSpec) {
620845
620985
  const redir = _contentWriteHook?.redirect?.();
620846
620986
  const host = opts.host !== void 0 ? opts.host : _contentWriteHook?.dynamicBlockHost?.();
620847
620987
  if (!redir && host) {
620848
620988
  const id2 = `${kind}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
620989
+ const specForPersistence = persistedSpec && collapse ? { ...persistedSpec, collapse } : persistedSpec;
620849
620990
  if (collapse) {
620850
620991
  ensureCollapsible(id2);
620851
620992
  host.registerDynamicBlock(
@@ -620854,10 +620995,11 @@ function renderToolDynamicBlock(kind, render2, opts, collapse) {
620854
620995
  state: getCollapseState(id2),
620855
620996
  desc: collapse,
620856
620997
  width
620857
- })
620998
+ }),
620999
+ specForPersistence
620858
621000
  );
620859
621001
  } else {
620860
- host.registerDynamicBlock(id2, render2);
621002
+ host.registerDynamicBlock(id2, render2, specForPersistence);
620861
621003
  }
620862
621004
  host.appendDynamicBlock(id2);
620863
621005
  return;
@@ -621426,7 +621568,16 @@ function renderToolResult(toolName, success, output, verboseOrOpts) {
621426
621568
  width
621427
621569
  ),
621428
621570
  opts,
621429
- collapseDesc
621571
+ collapseDesc,
621572
+ {
621573
+ renderer: "tool-combined",
621574
+ toolName,
621575
+ callArgs: { ...pending2.args ?? {} },
621576
+ success,
621577
+ output: frozenOutput,
621578
+ opts: { ...opts, verbose: pending2.verbose ?? opts.verbose },
621579
+ collapse: collapseDesc
621580
+ }
621430
621581
  );
621431
621582
  return;
621432
621583
  }
@@ -621435,7 +621586,15 @@ function renderToolResult(toolName, success, output, verboseOrOpts) {
621435
621586
  "tool-result",
621436
621587
  (width) => buildToolResultBoxLines(toolName, success, frozenOutput, opts, width),
621437
621588
  opts,
621438
- collapseDesc
621589
+ collapseDesc,
621590
+ {
621591
+ renderer: "tool-result",
621592
+ toolName,
621593
+ success,
621594
+ output: frozenOutput,
621595
+ opts: { ...opts },
621596
+ collapse: collapseDesc
621597
+ }
621439
621598
  );
621440
621599
  }
621441
621600
  function renderBoxedBlock(opts) {
@@ -621459,7 +621618,18 @@ function renderBoxedBlock(opts) {
621459
621618
  },
621460
621619
  width
621461
621620
  ),
621462
- opts.host === void 0 ? {} : { host: opts.host }
621621
+ opts.host === void 0 ? {} : { host: opts.host },
621622
+ void 0,
621623
+ {
621624
+ renderer: "tool-box",
621625
+ data: {
621626
+ title: opts.title,
621627
+ metrics: opts.metrics ?? "",
621628
+ body,
621629
+ colorCode: opts.colorCode ?? tuiTextDim(),
621630
+ metricsColorCode: opts.metricsColorCode
621631
+ }
621632
+ }
621463
621633
  );
621464
621634
  }
621465
621635
  function renderImageAsciiPreview(title, imagePath, ascii2, renderer) {
@@ -627926,6 +628096,7 @@ var omnius_directory_exports = {};
627926
628096
  __export(omnius_directory_exports, {
627927
628097
  OMNIUS_DIR: () => OMNIUS_DIR,
627928
628098
  buildContextRestorePrompt: () => buildContextRestorePrompt,
628099
+ buildContextRestoreSnapshot: () => buildContextRestoreSnapshot,
627929
628100
  buildHandoffPrompt: () => buildHandoffPrompt,
627930
628101
  cleanPromptForDiary: () => cleanPromptForDiary,
627931
628102
  clearTaskHandoff: () => clearTaskHandoff,
@@ -627944,6 +628115,7 @@ __export(omnius_directory_exports, {
627944
628115
  loadRecentSessions: () => loadRecentSessions,
627945
628116
  loadSessionContext: () => loadSessionContext,
627946
628117
  loadSessionHistory: () => loadSessionHistory,
628118
+ loadTuiSessionState: () => loadTuiSessionState,
627947
628119
  loadUsageHistory: () => loadUsageHistory,
627948
628120
  readIndexData: () => readIndexData,
627949
628121
  readIndexMeta: () => readIndexMeta,
@@ -627957,6 +628129,7 @@ __export(omnius_directory_exports, {
627957
628129
  saveSession: () => saveSession,
627958
628130
  saveSessionContext: () => saveSessionContext,
627959
628131
  saveSessionHistory: () => saveSessionHistory,
628132
+ saveTuiSessionState: () => saveTuiSessionState,
627960
628133
  sessionContextToHistoryBoxData: () => sessionContextToHistoryBoxData,
627961
628134
  stopOmniusGitignoreWatcher: () => stopOmniusGitignoreWatcher,
627962
628135
  updateSessionEntry: () => updateSessionEntry,
@@ -628869,9 +629042,7 @@ function selectActiveTaskAnchor(repoRoot) {
628869
629042
  return null;
628870
629043
  }
628871
629044
  }
628872
- function buildActiveTaskAnchor(repoRoot) {
628873
- const selected = selectActiveTaskAnchor(repoRoot);
628874
- if (!selected) return null;
629045
+ function formatActiveTaskAnchor(selected) {
628875
629046
  const ledger = selected.ledger;
628876
629047
  const evidence = ledger.evidence ?? [];
628877
629048
  const selectedEvidence = [...evidence.slice(0, 2), ...evidence.slice(-4)];
@@ -628905,6 +629076,134 @@ ${boardCards.join("\n")}
628905
629076
  ` : "") + `Continuation rule: when the user asks to continue, resume this active task unless the new prompt names a different target.
628906
629077
  </active-task-anchor>`;
628907
629078
  }
629079
+ function uniqueSessionIds(ids) {
629080
+ const seen = /* @__PURE__ */ new Set();
629081
+ const out = [];
629082
+ for (const raw of ids) {
629083
+ const id2 = String(raw || "").trim();
629084
+ if (!id2 || seen.has(id2)) continue;
629085
+ seen.add(id2);
629086
+ out.push(id2);
629087
+ }
629088
+ return out;
629089
+ }
629090
+ function collectRestoreSessionIds(ctx3, activeTask, historySessionId) {
629091
+ const entries = ctx3?.entries ?? [];
629092
+ const usefulEntries = entries.filter((entry) => !isManualSessionEntry(entry));
629093
+ const chronological = usefulEntries.length > 0 ? usefulEntries : entries;
629094
+ return uniqueSessionIds([
629095
+ activeTask?.ledger.runId,
629096
+ ...[...chronological].reverse().map((entry) => entry.sessionId),
629097
+ ...[...entries].reverse().map((entry) => entry.sessionId),
629098
+ historySessionId
629099
+ ]);
629100
+ }
629101
+ function childMapForTodos(todos) {
629102
+ const byId = new Set(todos.map((todo) => todo.id));
629103
+ const byParent = /* @__PURE__ */ new Map();
629104
+ for (const todo of todos) {
629105
+ if (!todo.parentId || !byId.has(todo.parentId)) continue;
629106
+ const bucket = byParent.get(todo.parentId) ?? [];
629107
+ bucket.push(todo);
629108
+ byParent.set(todo.parentId, bucket);
629109
+ }
629110
+ for (const bucket of byParent.values()) {
629111
+ bucket.sort((a2, b) => a2.createdAt - b.createdAt || a2.id.localeCompare(b.id));
629112
+ }
629113
+ return byParent;
629114
+ }
629115
+ function statusRank2(status) {
629116
+ switch (status) {
629117
+ case "in_progress":
629118
+ return 0;
629119
+ case "blocked":
629120
+ return 1;
629121
+ case "pending":
629122
+ return 2;
629123
+ case "completed":
629124
+ return 3;
629125
+ default:
629126
+ return 4;
629127
+ }
629128
+ }
629129
+ function orderedTodoTree(todos) {
629130
+ const byId = new Set(todos.map((todo) => todo.id));
629131
+ const byParent = childMapForTodos(todos);
629132
+ const roots = todos.filter((todo) => !todo.parentId || !byId.has(todo.parentId)).sort((a2, b) => statusRank2(a2.status) - statusRank2(b.status) || a2.createdAt - b.createdAt || a2.id.localeCompare(b.id));
629133
+ const out = [];
629134
+ const seen = /* @__PURE__ */ new Set();
629135
+ const visit = (todo, depth) => {
629136
+ if (seen.has(todo.id)) return;
629137
+ seen.add(todo.id);
629138
+ out.push({ todo, depth });
629139
+ for (const child of byParent.get(todo.id) ?? []) visit(child, depth + 1);
629140
+ };
629141
+ for (const root of roots) visit(root, 0);
629142
+ for (const todo of todos) visit(todo, 0);
629143
+ return out;
629144
+ }
629145
+ function formatTodoLine(todo, depth) {
629146
+ const indent2 = " ".repeat(Math.min(depth, 6));
629147
+ const blocker = todo.blocker ? ` blocker="${normalizeSessionText(todo.blocker, 120)}"` : "";
629148
+ const verify2 = todo.verifyCommand ? ` verify="${normalizeSessionText(todo.verifyCommand, 120)}"` : "";
629149
+ const artifacts = todo.declaredArtifacts && todo.declaredArtifacts.length > 0 ? ` artifacts=${todo.declaredArtifacts.slice(0, 4).map((item) => normalizeSessionText(item, 80)).join(",")}` : "";
629150
+ return `${indent2}- [${todo.status}] ${normalizeSessionText(todo.content, 180)} (id=${todo.id}${todo.parentId ? ` parent=${todo.parentId}` : ""}${blocker}${verify2}${artifacts})`;
629151
+ }
629152
+ function buildRestoredTodoBlock(sessionId, todos) {
629153
+ const byParent = childMapForTodos(todos);
629154
+ const leafTodos = todos.filter((todo) => (byParent.get(todo.id)?.length ?? 0) === 0);
629155
+ const completedLeaves = leafTodos.filter((todo) => todo.status === "completed").length;
629156
+ const activeTodos = todos.filter((todo) => todo.status === "in_progress" || todo.status === "blocked").sort((a2, b) => statusRank2(a2.status) - statusRank2(b.status) || b.updatedAt - a2.updatedAt).slice(0, 8);
629157
+ const ordered = orderedTodoTree(todos);
629158
+ const visible = ordered.slice(0, 48);
629159
+ const omitted = Math.max(0, ordered.length - visible.length);
629160
+ const activeLines = activeTodos.map((todo) => `- [${todo.status}] ${normalizeSessionText(todo.content, 180)} (id=${todo.id})`);
629161
+ const treeLines = visible.map(({ todo, depth }) => formatTodoLine(todo, depth));
629162
+ return `<restored-todo-state>
629163
+ todo_session_id=${sessionId}
629164
+ total=${todos.length}; leaf_progress=${completedLeaves}/${leafTodos.length || todos.length}
629165
+ ` + (activeLines.length > 0 ? `Active/blocked items:
629166
+ ${activeLines.join("\n")}
629167
+ ` : "") + `Todo tree:
629168
+ ${treeLines.join("\n")}${omitted > 0 ? `
629169
+ - ... ${omitted} more todo(s) in restored session` : ""}
629170
+ Continuation contract: this is the live checklist from the restored run. Continue it and update it with todo_write; do not recreate a parallel plan unless the new user request explicitly changes direction.
629171
+ </restored-todo-state>`;
629172
+ }
629173
+ function findRestoredTodoState(sessionIds) {
629174
+ for (const sessionId of sessionIds) {
629175
+ const todos = readTodos(sessionId).filter((todo) => todo && todo.id && todo.content);
629176
+ if (todos.length === 0) continue;
629177
+ return {
629178
+ sessionId,
629179
+ todos,
629180
+ block: buildRestoredTodoBlock(sessionId, todos)
629181
+ };
629182
+ }
629183
+ return null;
629184
+ }
629185
+ function buildRestoreHistoryAnchor(repoRoot) {
629186
+ const [latest] = listSessions(repoRoot);
629187
+ if (!latest?.id) return null;
629188
+ const lines = loadSessionHistory(repoRoot, latest.id) ?? [];
629189
+ const meaningfulTail = lines.map((line) => cleanSessionHistoryDisplayLine(line)).filter((line) => line && !isNoisySessionHistoryLine(line)).slice(-18).map((line) => `- ${normalizeSessionText(line, 220)}`);
629190
+ const path12 = join136(OMNIUS_DIR, SESSIONS_DIR, `${latest.id}.jsonl`);
629191
+ const statePath = join136(OMNIUS_DIR, SESSIONS_DIR, `${latest.id}${TUI_STATE_SUFFIX}`);
629192
+ const hasTuiState = existsSync122(join136(repoRoot, statePath));
629193
+ const block = `<restored-interface-history>
629194
+ latest_visual_session_id=${latest.id}
629195
+ ` + (hasTuiState ? `full_tui_state_path=${statePath}
629196
+ ` : "") + `full_transcript_path=${path12}
629197
+ recorded_lines=${lines.length}
629198
+ ` + (meaningfulTail.length > 0 ? `Recent visible transcript tail:
629199
+ ${meaningfulTail.join("\n")}
629200
+ ` : "") + `Recall contract: the full transcript artifact above is restored into the TUI scrollback and is the authoritative previous interface state. Read it only when exact prior UI/tool output is needed; otherwise use this restored state to avoid rediscovery.
629201
+ </restored-interface-history>`;
629202
+ return { sessionId: latest.id, path: path12, lineCount: lines.length, block };
629203
+ }
629204
+ function appendRestoreBlocks(base3, blocks) {
629205
+ return [base3, ...blocks].filter((part) => part && part.trim()).join("\n\n");
629206
+ }
628908
629207
  function formatSessionHistoryDisplay(ctx3) {
628909
629208
  return buildSessionHistoryBoxLines(
628910
629209
  sessionContextToHistoryBoxData(ctx3),
@@ -628930,10 +629229,15 @@ function sessionContextToHistoryBoxData(ctx3, title = "Session History") {
628930
629229
  updatedAt: ctx3?.updatedAt ?? null
628931
629230
  };
628932
629231
  }
628933
- function buildContextRestorePrompt(repoRoot) {
629232
+ function buildContextRestoreSnapshot(repoRoot) {
628934
629233
  const ctx3 = loadSessionContext(repoRoot);
628935
629234
  const handoffPrompt = buildHandoffPrompt(repoRoot);
628936
- const activeTaskAnchor = buildActiveTaskAnchor(repoRoot);
629235
+ const activeTask = selectActiveTaskAnchor(repoRoot);
629236
+ const activeTaskAnchor = activeTask ? formatActiveTaskAnchor(activeTask) : null;
629237
+ const historyAnchor = buildRestoreHistoryAnchor(repoRoot);
629238
+ const restoreSessionIds = collectRestoreSessionIds(ctx3, activeTask, historyAnchor?.sessionId);
629239
+ const restoredTodos = findRestoredTodoState(restoreSessionIds);
629240
+ const sourceSessionId = restoredTodos?.sessionId ?? restoreSessionIds[0];
628937
629241
  if (handoffPrompt) {
628938
629242
  const usefulEntries2 = (ctx3?.entries ?? []).filter((entry) => !isManualSessionEntry(entry));
628939
629243
  const baseCtx = ctx3 && ctx3.entries.length > 0 ? `
@@ -628943,13 +629247,37 @@ Recent tasks: ${(usefulEntries2.length > 0 ? usefulEntries2 : ctx3.entries).slic
628943
629247
  (e2) => `[${e2.completed ? "done" : "partial"}] ${normalizeSessionText(e2.summary || e2.task, 80)}`
628944
629248
  ).join(", ")}
628945
629249
  </session-recap>` : "";
628946
- return handoffPrompt + (activeTaskAnchor ? `
629250
+ const prompt2 = appendRestoreBlocks(
629251
+ handoffPrompt + (activeTaskAnchor ? `
628947
629252
 
628948
- ${activeTaskAnchor}` : "") + baseCtx;
629253
+ ${activeTaskAnchor}` : "") + baseCtx,
629254
+ [restoredTodos?.block, historyAnchor?.block]
629255
+ );
629256
+ return {
629257
+ prompt: prompt2,
629258
+ sourceSessionId,
629259
+ todoSessionId: restoredTodos?.sessionId,
629260
+ todoCount: restoredTodos?.todos.length ?? 0,
629261
+ historySessionId: historyAnchor?.sessionId,
629262
+ historyPath: historyAnchor?.path,
629263
+ historyLineCount: historyAnchor?.lineCount
629264
+ };
629265
+ }
629266
+ if (!ctx3 || ctx3.entries.length === 0) {
629267
+ const prompt2 = appendRestoreBlocks(activeTaskAnchor ?? "", [restoredTodos?.block, historyAnchor?.block]);
629268
+ if (!prompt2.trim()) return null;
629269
+ return {
629270
+ prompt: prompt2,
629271
+ sourceSessionId,
629272
+ todoSessionId: restoredTodos?.sessionId,
629273
+ todoCount: restoredTodos?.todos.length ?? 0,
629274
+ historySessionId: historyAnchor?.sessionId,
629275
+ historyPath: historyAnchor?.path,
629276
+ historyLineCount: historyAnchor?.lineCount
629277
+ };
628949
629278
  }
628950
- if (!ctx3 || ctx3.entries.length === 0) return activeTaskAnchor;
628951
629279
  const usefulEntries = ctx3.entries.filter((entry) => !isManualSessionEntry(entry));
628952
- const recent = (usefulEntries.length > 0 ? usefulEntries : ctx3.entries).slice(-5);
629280
+ const recent = (usefulEntries.length > 0 ? usefulEntries : ctx3.entries).slice(-20);
628953
629281
  const chronology = recent.map((e2) => {
628954
629282
  const status = e2.completed ? "done" : "partial";
628955
629283
  const summary = normalizeSessionText(e2.assistantResponse || e2.summary || e2.task, 140);
@@ -628960,7 +629288,7 @@ ${activeTaskAnchor}` : "") + baseCtx;
628960
629288
  const last2 = recent[recent.length - 1] ?? ctx3.entries[ctx3.entries.length - 1];
628961
629289
  const lastCompleted = [...usefulEntries].reverse().find((entry) => entry.completed);
628962
629290
  const latestCompleted = lastCompleted ? `Latest completed task: ${normalizeSessionText(lastCompleted.assistantResponse || lastCompleted.summary || lastCompleted.task, 180)}` : "";
628963
- const recentDialogue = recent.slice(-3).map((entry) => {
629291
+ const recentDialogue = recent.slice(-8).map((entry) => {
628964
629292
  const assistant = normalizeSessionText(entry.assistantResponse || entry.summary, 320) || "(no assistant reply captured)";
628965
629293
  const user = cleanPromptForDiary(entry.task).slice(0, 220);
628966
629294
  return `User: ${user}
@@ -628970,10 +629298,12 @@ Assistant: ${assistant}`;
628970
629298
  Provenance: ${last2.provenance} (file_read to expand)` : "";
628971
629299
  const kg = `
628972
629300
  KG summary: .omnius/context/kg-summary/latest.md (file_read to expand; legacy pointer: .omnius/context/kg-summary-latest.md)`;
628973
- return `<session-recap>
629301
+ const prompt = appendRestoreBlocks(
629302
+ `<session-recap>
628974
629303
  ` + (activeTaskAnchor ? `${activeTaskAnchor}
628975
629304
 
628976
629305
  ` : "") + `Project chronology (older to newer):
629306
+ Scope: full retained rolling window (${recent.length} entr${recent.length === 1 ? "y" : "ies"}).
628977
629307
  ${chronology.join("\n")}
628978
629308
  ` + (latestCompleted ? `
628979
629309
  ${latestCompleted}
@@ -628982,7 +629312,21 @@ Most recent exchanges (older to newer):
628982
629312
  ${recentDialogue}
628983
629313
  ` + (activeTaskAnchor ? `For continuation prompts, resume the active task anchor above; use chronology only to avoid repeating completed work.${prov}${kg}
628984
629314
  ` : `Continue from the latest exchange and do not repeat completed work.${prov}${kg}
628985
- `) + `</session-recap>`;
629315
+ `) + `</session-recap>`,
629316
+ [restoredTodos?.block, historyAnchor?.block]
629317
+ );
629318
+ return {
629319
+ prompt,
629320
+ sourceSessionId,
629321
+ todoSessionId: restoredTodos?.sessionId,
629322
+ todoCount: restoredTodos?.todos.length ?? 0,
629323
+ historySessionId: historyAnchor?.sessionId,
629324
+ historyPath: historyAnchor?.path,
629325
+ historyLineCount: historyAnchor?.lineCount
629326
+ };
629327
+ }
629328
+ function buildContextRestorePrompt(repoRoot) {
629329
+ return buildContextRestoreSnapshot(repoRoot)?.prompt ?? null;
628986
629330
  }
628987
629331
  function getLastTaskSummary(repoRoot) {
628988
629332
  const ctx3 = loadSessionContext(repoRoot);
@@ -629073,6 +629417,34 @@ function saveSessionHistory(repoRoot, sessionId, contentLines, meta) {
629073
629417
  }
629074
629418
  writeFileSync64(indexPath, JSON.stringify(index, null, 2), "utf-8");
629075
629419
  }
629420
+ function saveTuiSessionState(repoRoot, sessionId, state) {
629421
+ const sessDir = join136(repoRoot, OMNIUS_DIR, SESSIONS_DIR);
629422
+ mkdirSync76(sessDir, { recursive: true });
629423
+ const statePath = join136(sessDir, `${sessionId}${TUI_STATE_SUFFIX}`);
629424
+ const payload = {
629425
+ version: 2,
629426
+ savedAt: state.savedAt || (/* @__PURE__ */ new Date()).toISOString(),
629427
+ contentLines: Array.isArray(state.contentLines) ? state.contentLines : [],
629428
+ dynamicBlocks: state.dynamicBlocks && typeof state.dynamicBlocks === "object" ? state.dynamicBlocks : {}
629429
+ };
629430
+ writeFileSync64(statePath, JSON.stringify(payload, null, 2), "utf-8");
629431
+ }
629432
+ function loadTuiSessionState(repoRoot, sessionId) {
629433
+ const statePath = join136(repoRoot, OMNIUS_DIR, SESSIONS_DIR, `${sessionId}${TUI_STATE_SUFFIX}`);
629434
+ try {
629435
+ if (!existsSync122(statePath)) return null;
629436
+ const parsed = JSON.parse(readFileSync101(statePath, "utf-8"));
629437
+ if (parsed.version !== 2 || !Array.isArray(parsed.contentLines)) return null;
629438
+ return {
629439
+ version: 2,
629440
+ savedAt: typeof parsed.savedAt === "string" ? parsed.savedAt : (/* @__PURE__ */ new Date()).toISOString(),
629441
+ contentLines: parsed.contentLines.filter((line) => typeof line === "string"),
629442
+ dynamicBlocks: parsed.dynamicBlocks && typeof parsed.dynamicBlocks === "object" ? parsed.dynamicBlocks : {}
629443
+ };
629444
+ } catch {
629445
+ return null;
629446
+ }
629447
+ }
629076
629448
  function listSessions(repoRoot) {
629077
629449
  const indexPath = join136(repoRoot, OMNIUS_DIR, SESSIONS_DIR, SESSIONS_INDEX);
629078
629450
  try {
@@ -629098,6 +629470,8 @@ function deleteSession(repoRoot, sessionId) {
629098
629470
  try {
629099
629471
  const contentPath = join136(sessDir, `${sessionId}.jsonl`);
629100
629472
  if (existsSync122(contentPath)) unlinkSync23(contentPath);
629473
+ const statePath = join136(sessDir, `${sessionId}${TUI_STATE_SUFFIX}`);
629474
+ if (existsSync122(statePath)) unlinkSync23(statePath);
629101
629475
  if (existsSync122(indexPath)) {
629102
629476
  let index = JSON.parse(readFileSync101(indexPath, "utf-8"));
629103
629477
  index = index.filter((s2) => s2.id !== sessionId);
@@ -629305,10 +629679,11 @@ function deleteUsageRecord(kind, value2, repoRoot) {
629305
629679
  remove(join136(repoRoot, OMNIUS_DIR, USAGE_HISTORY_FILE));
629306
629680
  }
629307
629681
  }
629308
- var OMNIUS_DIR, LEGACY_DIRS, SUBDIRS, gitignoreWatchers, gitignoreRetryTimers, CONTEXT_FILES, PENDING_TASK_FILE, HANDOFF_FILE, CONTEXT_SAVE_FILE, CONTEXT_LEDGER_FILE, MAX_CONTEXT_ENTRIES, MAX_SESSION_DIARY_ENTRIES, MAX_SESSION_DIARY_DETAILED_ENTRIES, MAX_CONTEXT_LEDGER_LINES, MAX_CONTEXT_LEDGER_BYTES, SAME_TASK_REPLACE_WINDOW_MS, LOCK_TIMEOUT_MS, LOCK_RETRY_MS, LOCK_RETRY_MAX, DEICTIC_CONTINUATION_TERMS, SESSIONS_DIR, SESSIONS_INDEX, SKIP_DIRS3, HOME_SKIP_DIRS, USAGE_HISTORY_FILE, MAX_HISTORY_RECORDS;
629682
+ var OMNIUS_DIR, LEGACY_DIRS, SUBDIRS, gitignoreWatchers, gitignoreRetryTimers, CONTEXT_FILES, PENDING_TASK_FILE, HANDOFF_FILE, CONTEXT_SAVE_FILE, CONTEXT_LEDGER_FILE, MAX_CONTEXT_ENTRIES, MAX_SESSION_DIARY_ENTRIES, MAX_SESSION_DIARY_DETAILED_ENTRIES, MAX_CONTEXT_LEDGER_LINES, MAX_CONTEXT_LEDGER_BYTES, SAME_TASK_REPLACE_WINDOW_MS, LOCK_TIMEOUT_MS, LOCK_RETRY_MS, LOCK_RETRY_MAX, DEICTIC_CONTINUATION_TERMS, SESSIONS_DIR, SESSIONS_INDEX, TUI_STATE_SUFFIX, SKIP_DIRS3, HOME_SKIP_DIRS, USAGE_HISTORY_FILE, MAX_HISTORY_RECORDS;
629309
629683
  var init_omnius_directory = __esm({
629310
629684
  "packages/cli/src/tui/omnius-directory.ts"() {
629311
629685
  "use strict";
629686
+ init_dist5();
629312
629687
  init_task_complete_box();
629313
629688
  OMNIUS_DIR = ".omnius";
629314
629689
  LEGACY_DIRS = [".oa", ".open-agents"];
@@ -629368,6 +629743,7 @@ var init_omnius_directory = __esm({
629368
629743
  ]);
629369
629744
  SESSIONS_DIR = "sessions";
629370
629745
  SESSIONS_INDEX = "sessions-index.json";
629746
+ TUI_STATE_SUFFIX = ".tui-state.json";
629371
629747
  SKIP_DIRS3 = /* @__PURE__ */ new Set([
629372
629748
  "node_modules",
629373
629749
  ".git",
@@ -632050,6 +632426,7 @@ var init_status_bar = __esm({
632050
632426
  * repaint, including selection updates and scroll events.
632051
632427
  */
632052
632428
  _dynamicBlocks = /* @__PURE__ */ new Map();
632429
+ _dynamicBlockSpecs = /* @__PURE__ */ new Map();
632053
632430
  _dynamicBlockClickHandlers = /* @__PURE__ */ new Map();
632054
632431
  /** Sentinel marker — used both for scrollback storage and reflow detection. */
632055
632432
  DYNAMIC_BLOCK_MARK_PREFIX = "DYNBLOCK:";
@@ -632195,8 +632572,13 @@ var init_status_bar = __esm({
632195
632572
  * through `appendDynamicBlock` which also triggers a repaint so the
632196
632573
  * block becomes visible immediately.
632197
632574
  */
632198
- registerDynamicBlock(id2, render2) {
632575
+ registerDynamicBlock(id2, render2, persistedSpec) {
632199
632576
  this._dynamicBlocks.set(id2, render2);
632577
+ if (isPersistedDynamicBlockSpec(persistedSpec)) {
632578
+ this._dynamicBlockSpecs.set(id2, persistedSpec);
632579
+ } else {
632580
+ this._dynamicBlockSpecs.delete(id2);
632581
+ }
632200
632582
  this._dynamicBlockVersion++;
632201
632583
  this.invalidateRowCountCache();
632202
632584
  return `${this.DYNAMIC_BLOCK_MARK_PREFIX}${id2}${this.DYNAMIC_BLOCK_MARK_SUFFIX}`;
@@ -632207,6 +632589,7 @@ var init_status_bar = __esm({
632207
632589
  /** Unregister a dynamic block. Existing sentinels in scrollback become inert (rendered as empty). */
632208
632590
  unregisterDynamicBlock(id2) {
632209
632591
  this._dynamicBlocks.delete(id2);
632592
+ this._dynamicBlockSpecs.delete(id2);
632210
632593
  this._dynamicBlockClickHandlers.delete(id2);
632211
632594
  this._dynamicBlockVersion++;
632212
632595
  this.invalidateRowCountCache();
@@ -632239,6 +632622,51 @@ var init_status_bar = __esm({
632239
632622
  }
632240
632623
  return out;
632241
632624
  }
632625
+ /** Capture the main session scrollback for persistence, independent of active tab. */
632626
+ capturePersistedSessionLines(width = 100) {
632627
+ const mainView = this._agentViews.get("main");
632628
+ const lines = mainView?.contentLines ?? this._contentLines;
632629
+ return this.expandDynamicBlockSentinels(lines, width);
632630
+ }
632631
+ dynamicBlockIdFromSentinel(line) {
632632
+ if (typeof line !== "string" || !line.startsWith(this.DYNAMIC_BLOCK_MARK_PREFIX) || !line.endsWith(this.DYNAMIC_BLOCK_MARK_SUFFIX) || line.length <= this.DYNAMIC_BLOCK_MARK_PREFIX.length + this.DYNAMIC_BLOCK_MARK_SUFFIX.length) {
632633
+ return null;
632634
+ }
632635
+ return line.slice(
632636
+ this.DYNAMIC_BLOCK_MARK_PREFIX.length,
632637
+ line.length - this.DYNAMIC_BLOCK_MARK_SUFFIX.length
632638
+ );
632639
+ }
632640
+ /** Capture raw render sources for exact restart restore. */
632641
+ capturePersistedSessionState() {
632642
+ const mainView = this._agentViews.get("main");
632643
+ const contentLines = [...mainView?.contentLines ?? this._contentLines].slice(-this._contentMaxLines);
632644
+ const dynamicBlocks = {};
632645
+ for (const line of contentLines) {
632646
+ const id2 = this.dynamicBlockIdFromSentinel(line);
632647
+ if (!id2) continue;
632648
+ const spec = this._dynamicBlockSpecs.get(id2);
632649
+ if (spec) dynamicBlocks[id2] = spec;
632650
+ }
632651
+ return {
632652
+ version: 2,
632653
+ savedAt: (/* @__PURE__ */ new Date()).toISOString(),
632654
+ contentLines,
632655
+ dynamicBlocks
632656
+ };
632657
+ }
632658
+ /** Restore raw render sources and re-register their live dynamic renderers. */
632659
+ restoreMainSessionState(state) {
632660
+ for (const [id2, spec] of Object.entries(state.dynamicBlocks ?? {})) {
632661
+ if (!isPersistedDynamicBlockSpec(spec)) continue;
632662
+ this.registerDynamicBlock(
632663
+ id2,
632664
+ (width) => renderPersistedDynamicBlockSpec(spec, width, id2),
632665
+ spec
632666
+ );
632667
+ }
632668
+ this.restoreMainContentLines(Array.isArray(state.contentLines) ? state.contentLines : []);
632669
+ }
632242
632670
  /**
632243
632671
  * Append a previously-registered dynamic block's sentinel to scrollback
632244
632672
  * and trigger a repaint. The block's renderer fires immediately at the
@@ -633605,6 +634033,40 @@ var init_status_bar = __esm({
633605
634033
  this.fillContentArea();
633606
634034
  this.renderFooterAndPositionInput();
633607
634035
  }
634036
+ /**
634037
+ * Restore persisted scrollback into the main TUI view after process restart.
634038
+ *
634039
+ * Session transcripts are persisted with dynamic blocks already expanded, so
634040
+ * this intentionally restores plain rendered lines rather than stale sentinel
634041
+ * callbacks from a prior process.
634042
+ */
634043
+ restoreMainContentLines(lines) {
634044
+ const mainView = this._agentViews.get("main");
634045
+ const restored = lines.filter((line) => typeof line === "string").slice(-this._contentMaxLines);
634046
+ if (mainView) {
634047
+ mainView.contentLines.splice(0, mainView.contentLines.length, ...restored);
634048
+ mainView.scrollOffset = 0;
634049
+ this._activeViewId = "main";
634050
+ this._contentLines = mainView.contentLines;
634051
+ } else {
634052
+ this._contentLines.splice(0, this._contentLines.length, ...restored);
634053
+ }
634054
+ this._contentScrollOffset = 0;
634055
+ this._pendingTrailingBlanks = 0;
634056
+ this._inProgressLine = "";
634057
+ this.clearStreamingRepaintTimer();
634058
+ this._lastBufferedFingerprint = "";
634059
+ this._lastBufferedAt = 0;
634060
+ this._autoScroll = true;
634061
+ this.markContentMutated();
634062
+ this.clampContentScrollOffset();
634063
+ if (this.active) {
634064
+ this.fillContentArea();
634065
+ this.repaintContent();
634066
+ this.renderFooterAndPositionInput();
634067
+ this.refreshHeaderContent();
634068
+ }
634069
+ }
633608
634070
  /** Set callback for header focus changes (wired by interactive.ts to banner) */
633609
634071
  setHeaderFocusHandler(handler) {
633610
634072
  this._headerFocusHandler = handler;
@@ -659388,11 +659850,12 @@ sleep 1
659388
659850
  const prompt = ctx3.contextRestore?.();
659389
659851
  if (prompt) {
659390
659852
  const info = ctx3.contextShow?.();
659853
+ const snapshot = typeof prompt === "string" ? null : prompt;
659854
+ ctx3.setRestoredContext?.(prompt);
659391
659855
  renderInfo(
659392
- `Context restored from ${info?.entries ?? 0} saved session(s). Will be injected into your next task.`
659856
+ `Context restored from ${info?.entries ?? 0} saved session(s)${snapshot?.historyLineCount ? `; replayed ${snapshot.historyLineCount} transcript line(s)` : ""}${snapshot?.todoCount ? `; restored ${snapshot.todoCount} todo(s)` : ""}. Will be injected into your next task.`
659393
659857
  );
659394
- renderContextHistory(ctx3, "Session History");
659395
- ctx3.setRestoredContext?.(prompt);
659858
+ if (!snapshot?.historyLineCount) renderContextHistory(ctx3, "Session History");
659396
659859
  } else {
659397
659860
  renderWarning(
659398
659861
  "No saved session context found. Complete a task first, or use /context save."
@@ -738694,7 +739157,7 @@ async function runSelfImprovementCycle(repoRoot) {
738694
739157
  } catch {
738695
739158
  }
738696
739159
  }
738697
- function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce, statusBar, sudoCallback, costTracker, onComplete, taskType, contextWindowSize, modelCaps, personality, deepContext, onCompaction, emotionEngine, flowEnabled, slashCommandHandler, thinkingEnabled, askUserCallback, selfModifyEnabled, sessionMetrics, realtimeEnabled) {
739160
+ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce, statusBar, sudoCallback, costTracker, onComplete, taskType, contextWindowSize, modelCaps, personality, deepContext, onCompaction, emotionEngine, flowEnabled, slashCommandHandler, thinkingEnabled, askUserCallback, selfModifyEnabled, sessionMetrics, realtimeEnabled, restoredRunSessionId) {
738698
739161
  const voiceStyleMap = {
738699
739162
  concise: 1,
738700
739163
  balanced: 3,
@@ -738715,7 +739178,7 @@ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce
738715
739178
  if (emotionEngine) emotionEngine.autistMode = true;
738716
739179
  }
738717
739180
  const modelTier2 = getModelTier(config.model);
738718
- const sessionId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
739181
+ const sessionId = typeof restoredRunSessionId === "string" && restoredRunSessionId.trim() ? restoredRunSessionId.trim() : `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
738719
739182
  const taskMetricDescription = cleanForStorage(task).replace(/\s+/g, " ").slice(0, 160) || "task";
738720
739183
  costTracker?.startTask(taskMetricDescription);
738721
739184
  sessionMetrics?.startTask(taskMetricDescription);
@@ -740719,7 +741182,8 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
740719
741182
  toolCalls: result.toolCalls,
740720
741183
  completed: result.completed,
740721
741184
  model: config.model,
740722
- source: "task_complete"
741185
+ source: "task_complete",
741186
+ sessionId
740723
741187
  });
740724
741188
  } catch {
740725
741189
  }
@@ -740756,7 +741220,8 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
740756
741220
  toolCalls: result.toolCalls,
740757
741221
  completed: result.completed,
740758
741222
  model: config.model,
740759
- source: "task_complete"
741223
+ source: "task_complete",
741224
+ sessionId
740760
741225
  },
740761
741226
  accomplishments: extractedAccomplishments.length > 0 ? extractedAccomplishments : [
740762
741227
  `Completed task in ${result.turns} turns with ${result.toolCalls} tool calls`
@@ -740780,11 +741245,8 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
740780
741245
  }
740781
741246
  try {
740782
741247
  const historySessionId = process.env["OMNIUS_SESSION_ID"] || `session-${Date.now().toString(36)}`;
740783
- const rawContentLines = statusBar?._contentLines ?? [];
740784
- const contentLines = typeof statusBar?.expandDynamicBlockSentinels === "function" ? statusBar.expandDynamicBlockSentinels(
740785
- rawContentLines,
740786
- 100
740787
- ) : rawContentLines;
741248
+ const tuiState = typeof statusBar?.capturePersistedSessionState === "function" ? statusBar.capturePersistedSessionState() : null;
741249
+ const contentLines = typeof statusBar?.capturePersistedSessionLines === "function" ? statusBar.capturePersistedSessionLines(100) : statusBar?._contentLines ?? [];
740788
741250
  if (contentLines.length > 0) {
740789
741251
  const description = cleanPromptForDiary(task).slice(0, 240);
740790
741252
  const historyTitle = description.slice(0, 80) || void 0;
@@ -740794,6 +741256,9 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
740794
741256
  taskCount: 1,
740795
741257
  model: config.model
740796
741258
  });
741259
+ if (tuiState) {
741260
+ saveTuiSessionState(repoRoot, historySessionId, tuiState);
741261
+ }
740797
741262
  importTranscriptSession({
740798
741263
  id: `tui:${historySessionId}`,
740799
741264
  projectRoot: repoRoot,
@@ -741025,6 +741490,8 @@ async function startInteractive(config, repoPath2) {
741025
741490
  }
741026
741491
  await bootstrapMcpAndPlugins(repoRoot);
741027
741492
  let restoredSessionContext = null;
741493
+ let restoredTaskSessionId = null;
741494
+ let saveVisualSessionSnapshotRef = null;
741028
741495
  let terminalRestoredForExit = false;
741029
741496
  let interactiveExiting = false;
741030
741497
  let idleMemoryMaintenance = null;
@@ -741053,6 +741520,10 @@ async function startInteractive(config, repoPath2) {
741053
741520
  );
741054
741521
  const cleanupAndExit = (code8) => {
741055
741522
  interactiveExiting = true;
741523
+ try {
741524
+ saveVisualSessionSnapshotRef?.("process exit");
741525
+ } catch {
741526
+ }
741056
741527
  if (_shellToolRef) _shellToolRef.killAll();
741057
741528
  idleMemoryMaintenance?.stop();
741058
741529
  killAllFullSubAgents();
@@ -741569,14 +742040,14 @@ ${result.summary}`
741569
742040
  const { getLastTaskSummary: getLastTaskSummary2 } = (init_omnius_directory(), __toCommonJS(omnius_directory_exports));
741570
742041
  const taskSummary = hasTaskToResume ? getLastTaskSummary2(repoRoot) : null;
741571
742042
  const resumeMsg = taskSummary ? `v${version5} — picking up: ${taskSummary}` : `Updated to v${version5}.`;
742043
+ const restoreSnapshot = buildContextRestoreSnapshot(repoRoot);
742044
+ const replayedVisual = restoreSnapshot ? applyRestoreSnapshot(restoreSnapshot, { replayVisual: true }) : false;
741572
742045
  writeContent(() => {
741573
742046
  renderInfo(resumeMsg);
741574
- const resumePrompt = buildContextRestorePrompt(repoRoot);
741575
- if (resumePrompt) {
741576
- restoredSessionContext = resumePrompt;
742047
+ if (restoreSnapshot) {
741577
742048
  const info = loadSessionContext(repoRoot);
741578
742049
  renderInfo(
741579
- `Context restored from ${info?.entries.length ?? 0} session(s).`
742050
+ `Context restored from ${info?.entries.length ?? 0} session(s)${replayedVisual ? `; replayed ${restoreSnapshot.historyLineCount ?? 0} transcript line(s).` : "."}`
741580
742051
  );
741581
742052
  }
741582
742053
  });
@@ -742560,6 +743031,79 @@ This is an independent background session started from /background.`
742560
743031
  }
742561
743032
  }
742562
743033
  setDreamWriteContent(writeContent);
743034
+ function saveVisualSessionSnapshot(reason) {
743035
+ try {
743036
+ const historySessionId = process.env["OMNIUS_SESSION_ID"] || process.env["OMNIUS_TUI_SESSION_ID"] || `session-${Date.now().toString(36)}`;
743037
+ const tuiState = statusBar.capturePersistedSessionState();
743038
+ const contentLines = statusBar.capturePersistedSessionLines(100);
743039
+ if (contentLines.length === 0) return;
743040
+ const description = cleanPromptForDiary(lastSubmittedPrompt || lastCompletedSummary || reason).slice(0, 240) || reason;
743041
+ const historyTitle = description.slice(0, 80) || void 0;
743042
+ saveSessionHistory(repoRoot, historySessionId, contentLines, {
743043
+ name: historyTitle,
743044
+ description,
743045
+ taskCount: 1,
743046
+ model: currentConfig.model
743047
+ });
743048
+ saveTuiSessionState(repoRoot, historySessionId, tuiState);
743049
+ importTranscriptSession({
743050
+ id: `tui:${historySessionId}`,
743051
+ projectRoot: repoRoot,
743052
+ model: currentConfig.model,
743053
+ title: historyTitle,
743054
+ description,
743055
+ transcriptLines: contentLines,
743056
+ updatedAt: Date.now()
743057
+ });
743058
+ } catch {
743059
+ }
743060
+ }
743061
+ saveVisualSessionSnapshotRef = saveVisualSessionSnapshot;
743062
+ function replayRestoredVisualSession(snapshot) {
743063
+ if (!snapshot.historySessionId) return false;
743064
+ const tuiState = loadTuiSessionState(repoRoot, snapshot.historySessionId);
743065
+ if (tuiState) {
743066
+ try {
743067
+ statusBar.restoreMainSessionState(tuiState);
743068
+ return true;
743069
+ } catch {
743070
+ }
743071
+ }
743072
+ const lines = loadSessionHistory(repoRoot, snapshot.historySessionId);
743073
+ if (!lines || lines.length === 0) return false;
743074
+ try {
743075
+ statusBar.restoreMainContentLines(lines);
743076
+ return true;
743077
+ } catch {
743078
+ return false;
743079
+ }
743080
+ }
743081
+ function applyRestoreSnapshot(snapshot, options2 = {}) {
743082
+ if (options2.injectNextTask !== false) {
743083
+ restoredSessionContext = snapshot.prompt;
743084
+ }
743085
+ const restoredId = snapshot.todoSessionId || snapshot.sourceSessionId || null;
743086
+ restoredTaskSessionId = restoredId;
743087
+ if (restoredId) {
743088
+ try {
743089
+ process.env["OMNIUS_SESSION_ID"] = restoredId;
743090
+ } catch {
743091
+ }
743092
+ try {
743093
+ setTodoSessionId(restoredId);
743094
+ } catch {
743095
+ }
743096
+ try {
743097
+ setTuiTasksSession(restoredId);
743098
+ } catch {
743099
+ }
743100
+ try {
743101
+ refreshTuiTasks();
743102
+ } catch {
743103
+ }
743104
+ }
743105
+ return options2.replayVisual ? replayRestoredVisualSession(snapshot) : false;
743106
+ }
742563
743107
  async function writeContentAsync(fn) {
742564
743108
  if (!statusBar.isActive) return fn();
742565
743109
  if (isNeovimActive()) {
@@ -743335,6 +743879,7 @@ Log: ${nexusLogPath}`
743335
743879
  clearInterval(reminderDispatchTimer);
743336
743880
  reminderDispatchTimer = null;
743337
743881
  }
743882
+ saveVisualSessionSnapshot("command exit");
743338
743883
  statusBar.deactivate();
743339
743884
  if (carousel.isRunning) carousel.stop();
743340
743885
  banner.stop();
@@ -745551,7 +746096,8 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
745551
746096
  toolCalls: sessionToolCallCount,
745552
746097
  source: "manual",
745553
746098
  completed: false,
745554
- model: currentConfig.model
746099
+ model: currentConfig.model,
746100
+ sessionId: getTodoSessionId() || process.env["OMNIUS_SESSION_ID"]
745555
746101
  };
745556
746102
  saveSessionContext(repoRoot, entry);
745557
746103
  return true;
@@ -745560,10 +746106,14 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
745560
746106
  }
745561
746107
  },
745562
746108
  contextRestore() {
745563
- return buildContextRestorePrompt(repoRoot);
746109
+ return buildContextRestoreSnapshot(repoRoot);
745564
746110
  },
745565
746111
  setRestoredContext(ctx3) {
745566
- restoredSessionContext = ctx3;
746112
+ if (typeof ctx3 === "string") {
746113
+ restoredSessionContext = ctx3;
746114
+ } else {
746115
+ applyRestoreSnapshot(ctx3, { replayVisual: true });
746116
+ }
745567
746117
  },
745568
746118
  contextShow() {
745569
746119
  const ctx3 = loadSessionContext(repoRoot);
@@ -745706,28 +746256,30 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
745706
746256
  const auto = countdown <= 0;
745707
746257
  const doRestore = selectResult.confirmed && selectResult.key === "restore";
745708
746258
  if (doRestore) {
745709
- const prompt = buildContextRestorePrompt(repoRoot);
745710
- if (prompt) {
745711
- restoredSessionContext = prompt;
746259
+ const snapshot = buildContextRestoreSnapshot(repoRoot);
746260
+ if (snapshot) {
746261
+ const replayedVisual = applyRestoreSnapshot(snapshot, { replayVisual: true });
745712
746262
  const info = loadSessionContext(repoRoot);
745713
746263
  writeContent(() => {
745714
746264
  renderInfo(
745715
- auto ? `Context auto-restored from ${info?.entries.length ?? 0} session(s).` : `Context restored from ${info?.entries.length ?? 0} session(s).`
746265
+ auto ? `Context auto-restored from ${info?.entries.length ?? 0} session(s)${replayedVisual ? `; replayed ${snapshot.historyLineCount ?? 0} transcript line(s)` : ""}${snapshot.todoCount ? `; restored ${snapshot.todoCount} todo(s)` : ""}.` : `Context restored from ${info?.entries.length ?? 0} session(s)${replayedVisual ? `; replayed ${snapshot.historyLineCount ?? 0} transcript line(s)` : ""}${snapshot.todoCount ? `; restored ${snapshot.todoCount} todo(s)` : ""}.`
745716
746266
  );
745717
- if (statusBar.isActive && !isNeovimActive() && !isOverlayActive()) {
745718
- renderSessionHistoryBox(
745719
- {
745720
- registerDynamicBlock: (id2, render2) => statusBar.registerDynamicBlock(id2, render2),
745721
- appendDynamicBlock: (id2) => statusBar.appendDynamicBlock(id2)
745722
- },
745723
- sessionContextToHistoryBoxData(info, "Session History")
745724
- );
745725
- } else {
745726
- const historyDisplay = formatSessionHistoryDisplay(info);
745727
- process.stdout.write(
745728
- historyDisplay.endsWith("\n") ? historyDisplay : `${historyDisplay}
746267
+ if (!replayedVisual) {
746268
+ if (statusBar.isActive && !isNeovimActive() && !isOverlayActive()) {
746269
+ renderSessionHistoryBox(
746270
+ {
746271
+ registerDynamicBlock: (id2, render2) => statusBar.registerDynamicBlock(id2, render2),
746272
+ appendDynamicBlock: (id2) => statusBar.appendDynamicBlock(id2)
746273
+ },
746274
+ sessionContextToHistoryBoxData(info, "Session History")
746275
+ );
746276
+ } else {
746277
+ const historyDisplay = formatSessionHistoryDisplay(info);
746278
+ process.stdout.write(
746279
+ historyDisplay.endsWith("\n") ? historyDisplay : `${historyDisplay}
745729
746280
  `
745730
- );
746281
+ );
746282
+ }
745731
746283
  }
745732
746284
  });
745733
746285
  } else {
@@ -745787,7 +746339,14 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
745787
746339
  const pendingTask = loadPendingTask(repoRoot);
745788
746340
  if (pendingTask) {
745789
746341
  setTimeout(() => {
745790
- const sessionCtx = buildContextRestorePrompt(repoRoot);
746342
+ const restoreSnapshot = buildContextRestoreSnapshot(repoRoot);
746343
+ if (restoreSnapshot) {
746344
+ applyRestoreSnapshot(restoreSnapshot, {
746345
+ replayVisual: true,
746346
+ injectNextTask: false
746347
+ });
746348
+ }
746349
+ const sessionCtx = restoreSnapshot?.prompt ?? buildContextRestorePrompt(repoRoot);
745791
746350
  const resumeContext = [
745792
746351
  sessionCtx || "",
745793
746352
  `Continuing task after update: ${pendingTask.prompt}`,
@@ -745958,6 +746517,7 @@ ${result.content.slice(0, 2e3)}${result.content.length > 2e3 ? "\n[truncated]" :
745958
746517
  const quitMatch = input.trim().replace(/^\//, "").toLowerCase();
745959
746518
  if (quitMatch === "quit" || quitMatch === "exit" || quitMatch === "q") {
745960
746519
  interactiveExiting = true;
746520
+ saveVisualSessionSnapshot("quit");
745961
746521
  if (activeTask) activeTask.runner.abort();
745962
746522
  idleMemoryMaintenance?.stop();
745963
746523
  taskManager.stopAll();
@@ -745975,6 +746535,7 @@ ${result.content.slice(0, 2e3)}${result.content.length > 2e3 ? "\n[truncated]" :
745975
746535
  );
745976
746536
  if (cmdResult === "exit") {
745977
746537
  interactiveExiting = true;
746538
+ saveVisualSessionSnapshot("command exit");
745978
746539
  if (activeTask) activeTask.runner.abort();
745979
746540
  idleMemoryMaintenance?.stop();
745980
746541
  taskManager.stopAll();
@@ -746539,6 +747100,7 @@ ${formatTaskCompletionMeta(previousMetaForIntake)}`
746539
747100
  } catch {
746540
747101
  }
746541
747102
  let taskInput = fullInput;
747103
+ let taskSessionId = restoredTaskSessionId;
746542
747104
  if (restoredSessionContext) {
746543
747105
  taskInput = `${restoredSessionContext}
746544
747106
 
@@ -746547,6 +747109,7 @@ ${formatTaskCompletionMeta(previousMetaForIntake)}`
746547
747109
  NEW TASK: ${fullInput}`;
746548
747110
  restoredSessionContext = null;
746549
747111
  }
747112
+ restoredTaskSessionId = null;
746550
747113
  if (taskIntakePacket) {
746551
747114
  taskInput = `${taskIntakePacket}
746552
747115
 
@@ -746595,7 +747158,8 @@ ${taskInput}`;
746595
747158
  handleAskUser,
746596
747159
  selfModifyEnabled,
746597
747160
  sessionMetrics,
746598
- realtimeEnabled
747161
+ realtimeEnabled,
747162
+ taskSessionId
746599
747163
  );
746600
747164
  activeTask = task;
746601
747165
  _recallText = null;
@@ -746834,6 +747398,7 @@ Rationale: ${proposal.rationale}${provenanceNote}${dmnDevDiscipline(proposal.cat
746834
747398
  rl.on("close", () => {
746835
747399
  if (interactiveExiting) return;
746836
747400
  interactiveExiting = true;
747401
+ saveVisualSessionSnapshot("readline close");
746837
747402
  if (peerMesh) {
746838
747403
  peerMesh.stop().catch(() => {
746839
747404
  });
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.463",
3
+ "version": "1.0.465",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.463",
9
+ "version": "1.0.465",
10
10
  "bundleDependencies": [
11
11
  "image-to-ascii"
12
12
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.463",
3
+ "version": "1.0.465",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",