omnius 1.0.872 → 1.0.873

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
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: package/dist/index.js
3
- size: 37742606 bytes
4
- sha256: 23d65d172c35aa564b0960158e87b739a330c73e98459fb8f64885e3e84b0ecd
3
+ size: 37773730 bytes
4
+ sha256: f762c00e6a2c7407ca37ff6c15bacffd0028cd2c2927dd91fd7db512549090c8
@@ -2055,7 +2055,7 @@ var NativeModalView = class {
2055
2055
  return consume();
2056
2056
  }
2057
2057
  if (key.name === "tab") {
2058
- if (this.spec.deferSelection || this.spec.captureKeys) this.emitAction(key.shift ? "shift-tab" : "tab");
2058
+ if (!this.spec.nativeTabNavigation && (this.spec.deferSelection || this.spec.captureKeys)) this.emitAction(key.shift ? "shift-tab" : "tab");
2059
2059
  else this.nextControl(key.shift ? -1 : 1);
2060
2060
  return consume();
2061
2061
  }
@@ -5956,12 +5956,16 @@ var NativeTabStrip = class extends BoxRenderable9 {
5956
5956
  const signature = JSON.stringify([items, activeId, busy]);
5957
5957
  if (signature === this.signature) return;
5958
5958
  this.signature = signature;
5959
+ const previousFocus = this.renderer.currentFocusedRenderable;
5960
+ const ownedFocus = Boolean(previousFocus) && [...this.buttons.values()].some((entry) => previousFocus === entry.button || previousFocus === entry.close);
5959
5961
  const ids = new Set(items.map((item) => item.id));
5960
5962
  for (const [id, entry] of this.buttons) if (!ids.has(id)) {
5961
5963
  entry.button.destroy();
5964
+ entry.close?.destroy();
5962
5965
  this.buttons.delete(id);
5963
5966
  }
5964
- for (const [index, action] of items.entries()) {
5967
+ const controls = [];
5968
+ for (const action of items) {
5965
5969
  let entry = this.buttons.get(action.id);
5966
5970
  if (!entry) {
5967
5971
  const button = new TextRenderable8(this.renderer, {
@@ -5991,35 +5995,73 @@ var NativeTabStrip = class extends BoxRenderable9 {
5991
5995
  this.reveal = button.id;
5992
5996
  });
5993
5997
  button.on("blurred", () => this.style(entry));
5998
+ if (this.options.close) {
5999
+ const close2 = new TextRenderable8(this.renderer, {
6000
+ id: `${this.options.prefix}-close:${action.id}`,
6001
+ content: "×",
6002
+ width: 1,
6003
+ height: 1,
6004
+ flexShrink: 0,
6005
+ wrapMode: "none",
6006
+ selectable: false,
6007
+ fg: this.colors.foreground,
6008
+ bg: this.colors.background,
6009
+ onMouseDown: (event) => {
6010
+ if (event.button === 0 && !entry.blocked) {
6011
+ close2.focus();
6012
+ this.options.close(entry.action.id);
6013
+ }
6014
+ event.preventDefault();
6015
+ event.stopPropagation();
6016
+ },
6017
+ onKeyDown: (key) => this.handleKey(entry, key, true)
6018
+ });
6019
+ entry.close = close2;
6020
+ close2.on("focused", () => {
6021
+ this.style(entry);
6022
+ this.scroll.scrollChildIntoView(close2.id);
6023
+ this.reveal = close2.id;
6024
+ });
6025
+ close2.on("blurred", () => this.style(entry));
6026
+ }
5994
6027
  }
5995
6028
  entry.action = action;
5996
6029
  entry.active = action.id === activeId;
5997
6030
  entry.blocked = busy || Boolean(action.disabled);
5998
6031
  entry.button.focusable = entry.active || !entry.blocked;
6032
+ if (entry.close) entry.close.focusable = !entry.blocked;
5999
6033
  const label2 = plainText(action.label);
6000
6034
  const content = ` ${label2} `;
6001
6035
  if (entry.button.plainText !== content) entry.button.content = content;
6002
6036
  this.style(entry);
6003
- if (this.scroll.getChildren()[index] !== entry.button) this.scroll.add(entry.button, index);
6037
+ controls.push(entry.button);
6038
+ if (entry.close) controls.push(entry.close);
6004
6039
  }
6005
- this.naturalWidth = Array.from(this.buttons.values()).reduce((width, entry) => width + terminalCellWidth(entry.button.plainText), 0) + Math.max(0, items.length - 1);
6006
- const focused = this.renderer.currentFocusedRenderable;
6007
- if (focused && !focused.focusable && Array.from(this.buttons.values()).some((entry) => entry.button === focused)) this.buttons.get(activeId)?.button.focus();
6040
+ for (const [index, control] of controls.entries()) if (this.scroll.getChildren()[index] !== control) this.scroll.add(control, index);
6041
+ this.naturalWidth = controls.reduce((width, control) => width + terminalCellWidth(control.plainText), 0) + Math.max(0, controls.length - 1);
6042
+ if (ownedFocus && (!previousFocus || previousFocus.isDestroyed || !previousFocus.focusable))
6043
+ (this.buttons.get(activeId)?.button ?? controls.find((control) => control.focusable))?.focus();
6008
6044
  if (activeId !== this.activeId) this.reveal = `${this.options.prefix}:${activeId}`;
6009
6045
  this.activeId = activeId;
6010
6046
  this.renderer.requestRender();
6011
6047
  }
6012
6048
  style(entry) {
6013
6049
  entry.button.attributes = (entry.active ? TextAttributes9.BOLD | TextAttributes9.UNDERLINE : entry.blocked ? TextAttributes9.DIM : 0) | (entry.button.focused ? TextAttributes9.INVERSE : 0);
6050
+ if (entry.close) entry.close.attributes = (entry.blocked || !entry.close.focused ? TextAttributes9.DIM : 0) | (entry.close.focused ? TextAttributes9.INVERSE : 0);
6014
6051
  }
6015
- handleKey(entry, key) {
6052
+ handleKey(entry, key, close2 = false) {
6016
6053
  if (key.eventType === "release") return;
6017
6054
  if (key.name === "return" || key.sequence === " ") {
6018
- if (!entry.blocked) this.options.activate(entry.action.id);
6055
+ if (!entry.blocked) {
6056
+ if (close2) this.options.close?.(entry.action.id);
6057
+ else this.options.activate(entry.action.id);
6058
+ }
6059
+ } else if (this.options.close && (key.name === "delete" || key.ctrl && key.name.toLowerCase() === "w")) {
6060
+ if (!entry.blocked) this.options.close(entry.action.id);
6019
6061
  } else if (["left", "right", "home", "end"].includes(key.name)) {
6020
- const targets = this.scroll.getChildren().filter((button) => button.focusable);
6062
+ const targets = this.scroll.getChildren().filter((button) => button.focusable && button.id.startsWith(`${this.options.prefix}:`));
6021
6063
  const index = targets.indexOf(entry.button);
6022
- const next = key.name === "home" ? 0 : key.name === "end" ? targets.length - 1 : (index + (key.name === "left" ? -1 : 1) + targets.length) % targets.length;
6064
+ const next = key.name === "home" ? 0 : key.name === "end" ? targets.length - 1 : (index + (key.name === "left" ? close2 ? 0 : -1 : 1) + targets.length) % targets.length;
6023
6065
  targets[next]?.focus();
6024
6066
  } else return;
6025
6067
  key.preventDefault();
@@ -6044,9 +6086,13 @@ var NativeTabStrip = class extends BoxRenderable9 {
6044
6086
  };
6045
6087
  applyTheme(colors) {
6046
6088
  this.colors = colors;
6047
- for (const { button } of this.buttons.values()) {
6089
+ for (const { button, close: close2 } of this.buttons.values()) {
6048
6090
  button.fg = colors.foreground;
6049
6091
  button.bg = colors.background;
6092
+ if (close2) {
6093
+ close2.fg = colors.foreground;
6094
+ close2.bg = colors.background;
6095
+ }
6050
6096
  }
6051
6097
  styleNativeScrollbars(this.scroll, colors);
6052
6098
  styleNativeSlider(this.rail, colors);
@@ -6196,7 +6242,8 @@ var NativeFrontend = class {
6196
6242
  id: "omnius-session-tabs",
6197
6243
  prefix: "session",
6198
6244
  colors,
6199
- activate: (id) => this.emitAction("session", id)
6245
+ activate: (id) => this.emitAction("session", id),
6246
+ close: (id) => this.emitAction("session-close", id)
6200
6247
  });
6201
6248
  this.sessionRow.add(this.sessions);
6202
6249
  this.newSession = new TextRenderable9(renderer2, {
@@ -6228,6 +6275,35 @@ var NativeFrontend = class {
6228
6275
  this.newSession.on("focused", styleNew);
6229
6276
  this.newSession.on("blurred", styleNew);
6230
6277
  this.sessionRow.add(this.newSession);
6278
+ this.sessionPicker = new TextRenderable9(renderer2, {
6279
+ id: "omnius-session-picker",
6280
+ content: "SESSIONS",
6281
+ width: 8,
6282
+ height: 1,
6283
+ flexShrink: 0,
6284
+ selectable: false,
6285
+ fg: colors.foreground,
6286
+ bg: colors.background,
6287
+ onMouseDown: (event) => {
6288
+ if (event.button === 0 && !this.state?.sessions?.busy) this.emitAction("session-picker");
6289
+ event.preventDefault();
6290
+ event.stopPropagation();
6291
+ },
6292
+ onKeyDown: (key) => {
6293
+ if (key.eventType !== "release" && (key.name === "return" || key.sequence === " ")) {
6294
+ if (!this.state?.sessions?.busy) this.emitAction("session-picker");
6295
+ key.preventDefault();
6296
+ key.stopPropagation();
6297
+ }
6298
+ }
6299
+ });
6300
+ this.sessionPicker.focusable = true;
6301
+ const stylePicker = () => {
6302
+ this.sessionPicker.attributes = (this.state?.sessions?.busy ? TextAttributes10.DIM : 0) | (this.sessionPicker.focused ? TextAttributes10.INVERSE : 0);
6303
+ };
6304
+ this.sessionPicker.on("focused", stylePicker);
6305
+ this.sessionPicker.on("blurred", stylePicker);
6306
+ this.sessionRow.add(this.sessionPicker);
6231
6307
  this.routeWarning = new TextRenderable9(renderer2, { id: "omnius-route-warning", content: "", width: "100%", wrapMode: "word", fg: colors.warning, attributes: TextAttributes10.BOLD | TextAttributes10.UNDERLINE, visible: false, flexShrink: 0 });
6232
6308
  this.root.add(this.routeWarning);
6233
6309
  this.taskPane = new BoxRenderable10(renderer2, { id: "omnius-tasks", width: "100%", flexDirection: "column", maxHeight: 6, flexShrink: 0, overflow: "hidden", visible: false });
@@ -6519,6 +6595,7 @@ var NativeFrontend = class {
6519
6595
  sessionRow;
6520
6596
  sessions;
6521
6597
  newSession;
6598
+ sessionPicker;
6522
6599
  taskPane;
6523
6600
  taskStrip;
6524
6601
  tasks;
@@ -6617,8 +6694,10 @@ var NativeFrontend = class {
6617
6694
  this.sessionRow.visible = Boolean(state.sessions);
6618
6695
  this.sessions.update(state.sessions?.items ?? [], state.sessions?.activeId ?? "", state.sessions?.busy);
6619
6696
  this.newSession.focusable = !state.sessions?.busy;
6620
- if (state.sessions?.busy && this.newSession.focused) this.getRenderable(`session:${state.sessions.activeId}`)?.focus();
6697
+ this.sessionPicker.focusable = !state.sessions?.busy;
6698
+ if (state.sessions?.busy && (this.newSession.focused || this.sessionPicker.focused)) this.getRenderable(`session:${state.sessions.activeId}`)?.focus();
6621
6699
  this.newSession.attributes = state.sessions?.busy ? TextAttributes10.DIM : this.newSession.focused ? TextAttributes10.INVERSE : 0;
6700
+ this.sessionPicker.attributes = state.sessions?.busy ? TextAttributes10.DIM : this.sessionPicker.focused ? TextAttributes10.INVERSE : 0;
6622
6701
  this.tasks.replace(state.taskNotes ? [] : state.tasks);
6623
6702
  this.taskPane.visible = !state.taskNotes && state.tasks.length > 0;
6624
6703
  this.taskStrip.update(state.taskNotes);
@@ -32538,6 +32538,38 @@
32538
32538
  }
32539
32539
  ]
32540
32540
  },
32541
+ {
32542
+ "id": "guide.reference-session-controls",
32543
+ "kind": "guide",
32544
+ "title": "Terminal session controls",
32545
+ "summary": "The header shows open sessions. Each tab has a separate × control. Closing a tab preserves its saved conversation and draft; closing the active tab selects a neighbour, or creates a new session when it was the last tab. Active work must finish or stop first. Failed transitions keep the original tab.",
32546
+ "keywords": [
32547
+ "reference",
32548
+ "session",
32549
+ "controls",
32550
+ "md"
32551
+ ],
32552
+ "maturity": "stable",
32553
+ "audiences": [
32554
+ "user",
32555
+ "integrator",
32556
+ "coding-agent"
32557
+ ],
32558
+ "layer": "documentation",
32559
+ "interfaces": [
32560
+ {
32561
+ "type": "file",
32562
+ "target": "docs/reference/session-controls.md"
32563
+ }
32564
+ ],
32565
+ "references": [
32566
+ {
32567
+ "type": "documentation",
32568
+ "target": "docs/reference/session-controls.md",
32569
+ "relation": "canonical-artifact"
32570
+ }
32571
+ ]
32572
+ },
32541
32573
  {
32542
32574
  "id": "guide.reference-slash-commands",
32543
32575
  "kind": "guide",
package/docs/DISCOVERY.md CHANGED
@@ -499,6 +499,7 @@ Daemon equivalents are `GET /v1/discovery/bootstrap`, `GET /v1/discovery?q=<inte
499
499
  | `guide.reference-configuration` | Configuration Reference | Configuration is layered from environment, global user settings, project settings, and active TUI state. |
500
500
  | `guide.reference-omni-analysis` | Omni media analysis | Omnius uses an explicitly configured Omni adapter for genuine audio and video comprehension. A model name containing -omni does not add audio/video execution to stock Ollama: the adapter must decode the media and run the corresponding model/projector weights. An unavailable runtime is an unavailable capability, never a reason to substitute synthetic observat |
501
501
  | `guide.reference-rest-api` | REST API Reference | This is the maintained human inventory for the supported Omnius daemon API. The machine contract is generated by packages/cli/src/api/openapi.ts and served at /openapi.json; every operation in that contract must appear here. The root README links to this inventory and stays focused on the system overview. Browser HTML routes and implementation-only compatibi |
502
+ | `guide.reference-session-controls` | Terminal session controls | The header shows open sessions. Each tab has a separate × control. Closing a tab preserves its saved conversation and draft; closing the active tab selects a neighbour, or creates a new session when it was the last tab. Active work must finish or stop first. Failed transitions keep the original tab. |
502
503
  | `guide.reference-slash-commands` | Slash Commands | This file is generated from packages/cli/src/tui/command-registry.ts. |
503
504
  | `guide.reference-task-observability` | Tasks, notes and scoped work | The native terminal's details rail includes TASKS / NOTES. It presents the active session's checklist as a hierarchy, public operational notes, and recorded runner activity. It remains available during transcript scrollback. When viewing an agent tab, the session label identifies which session owns the displayed checklist. |
504
505
  | `guide.reference-work-queue` | Mid-task intake and work queue | The native terminal's WORK / QUEUE section tracks user requests. TASKS / NOTES tracks the implementation steps, notes and observed execution layers within a task. Adding a request does not create a fake todo or replace the active objective. |
@@ -0,0 +1,11 @@
1
+ # Terminal session controls
2
+
3
+ The header shows open sessions. Each tab has a separate × control. Closing a tab preserves its saved conversation and draft; closing the active tab selects a neighbour, or creates a new session when it was the last tab. Active work must finish or stop first. Failed transitions keep the original tab.
4
+
5
+ **SESSIONS**, beside **+ NEW**, opens the saved-session catalog for the current project directory. Type to filter, use arrows to select, and press Enter to resume the original conversation. Tab and Shift+Tab traverse the filter and footer actions. Escape or an outside click closes the picker. `/sessions` and `/session` open the same picker in the native terminal.
6
+
7
+ The catalog includes saved sessions beyond the visible tab strip, including closed tabs and unsent drafts. Resuming restores the saved document and session identity through the existing session owner. It does not append a replay or request a generated summary.
8
+
9
+ **DELETE** requires a second confirmation and is available for inactive saved conversations. It removes the saved transcript, interface draft, catalog entries and current context projection. Generated media, shared model weights and operational audit records remain. Active work, pending delivery, update handoffs and concurrent writers can block removal; a failed or partial removal is reported in the modal. Close other terminal instances before deleting history they could save again.
10
+
11
+ The controls use the terminal palette and existing native modal dimming, corner animation and focus restoration. The right-side actions stay pinned while the tab strip scrolls within the remaining width.
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.872",
3
+ "version": "1.0.873",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.872",
9
+ "version": "1.0.873",
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.872",
3
+ "version": "1.0.873",
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/library.js",