@pellux/goodvibes-agent 1.0.10 → 1.0.12

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/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  Product-facing release notes for GoodVibes Agent.
4
4
 
5
+ ## 1.0.12 - 2026-06-03
6
+
7
+ - Expose command browser, reasoning-effort picker, and live process output as model-visible confirmation-gated harness UI surfaces.
8
+ - Allow confirmed run_workspace_action execution for local memory, note, persona, skill, and routine create editors through agent_local_registry with required-field validation.
9
+ - Refresh package-facing docs for current 1.0.x harness parity and keep coverage aligned with the completed user-facing and model-facing surface map.
10
+
11
+ ## 1.0.11 - 2026-06-03
12
+
13
+ - Model-visible harness parity now opens conversation search, prompt history search, slash-command mode, file picker, and nearest-block actions through confirmation-gated visible shell routes.
14
+ - Documentation now names the current UI surface inventory for 1.0.x so users and the model share the same harness map.
15
+ - Harness assertions cover the new visible surface routes and preserve the existing safety boundary.
16
+
5
17
  ## 1.0.10 - 2026-06-03
6
18
 
7
19
  - Expose the runtime activity monitor as a model-visible, confirmation-gated harness UI surface, wire the shell opener through CommandContext, and refresh package-facing docs and focused coverage for the current 1.0.x surface map.
package/README.md CHANGED
@@ -66,7 +66,7 @@ Use the workspace as the primary product surface:
66
66
 
67
67
  Inside the Agent workspace, press `/` to search every workspace action by name, category, command, or detail. This is the primary discovery path for product actions; slash commands and CLI subcommands remain power-user/scriptable mirrors.
68
68
 
69
- The model has the same harness map through `agent_harness`: it can list Agent workspace actions, list built-in panels and their workspace routes, inspect modal/overlay/picker UI surfaces, list top-level CLI mirrors, inspect fixed shortcuts and configurable keybindings, list slash commands, inspect command policy metadata, inspect model tool definitions, inspect or change Agent settings with confirmation, run concrete workspace or slash-command mirrors with confirmation, and report connected-host capability and live readiness posture. CLI mirror modes are read-only catalog and parser inspection; inside the main conversation, the model uses the returned preferred model tool, workspace action, setting mode, or confirmed slash-command mirror instead of launching hidden nested CLI processes. Panel modes expose catalog/open state and route visible panel/workspace changes through the current Agent shell bridge with confirmation. UI surface modes expose help, shortcuts, context, runtime activity, bookmarks, model/provider pickers, TTS provider/voice pickers, session/profile pickers, the panel-picker compatibility route, security/knowledge/subscription operator surfaces, settings, MCP workspace, onboarding, and Agent workspace entrypoints; `open_ui_surface` is confirmation-gated and only performs visible shell navigation. Shortcut modes expose fixed runtime/editor controls plus the live resolved keybinding table; `set_keybinding` and `reset_keybinding` write the same `keybindings.json` file the user edits and require confirmation. Workspace action discovery can inline editor field schemas with `includeParameters:true`; profile editors use the current starter-template inventory, and routine schedule editors can prefill a selected local routine from `recordId`. Selection-based local workspace actions accept a local `recordId` so the model can use the same note promotion and local registry flows as the TUI. The `connected_host` report includes route families, allowed capabilities, blocked lifecycle/non-Agent surfaces, and availability for the first-class Agent tools. The `connected_host_status` report performs a read-only live check of the connected-host status route and the isolated Agent Knowledge status route, reports SDK compatibility, token posture, endpoint bindings, route readiness, and actionable findings, and still refuses connected-host lifecycle control. The model uses those first-class tools for product workflows where they exist, such as Agent Knowledge, local memory/notes/personas/skills/routines, channel sends, notifications, reminders, media generation, work plans, and connected-host operator actions.
69
+ The model has the same harness map through `agent_harness`: it can list Agent workspace actions, list built-in panels and their workspace routes, inspect modal/overlay/picker UI surfaces, list top-level CLI mirrors, inspect fixed shortcuts and configurable keybindings, list slash commands, inspect command policy metadata, inspect model tool definitions, inspect or change Agent settings with confirmation, run concrete workspace or slash-command mirrors with confirmation, and report connected-host capability and live readiness posture. CLI mirror modes are read-only catalog and parser inspection; inside the main conversation, the model uses the returned preferred model tool, workspace action, setting mode, or confirmed slash-command mirror instead of launching hidden nested CLI processes. Panel modes expose catalog/open state and route visible panel/workspace changes through the current Agent shell bridge with confirmation. UI surface modes expose help, shortcuts, command browser, conversation search, prompt-history search, slash-command mode, file picker, block actions, context, runtime activity, live process output, bookmarks, model/provider/reasoning-effort pickers, TTS provider/voice pickers, session/profile pickers, the panel-picker compatibility route, security/knowledge/subscription operator surfaces, settings, MCP workspace, onboarding, and Agent workspace entrypoints; `open_ui_surface` is confirmation-gated and only performs visible shell navigation. Shortcut modes expose fixed runtime/editor controls plus the live resolved keybinding table; `set_keybinding` and `reset_keybinding` write the same `keybindings.json` file the user edits and require confirmation. Workspace action discovery can inline editor field schemas with `includeParameters:true`; profile editors use the current starter-template inventory, and routine schedule editors can prefill a selected local routine from `recordId`. Selection-based local workspace actions accept a local `recordId` so the model can use the same note promotion and local registry flows as the TUI. Direct local create editors for memory, notes, personas, skills, and routines can execute from submitted fields through `run_workspace_action` and `agent_local_registry` with confirmation. The `connected_host` report includes route families, allowed capabilities, blocked lifecycle/non-Agent surfaces, and availability for the first-class Agent tools. The `connected_host_status` report performs a read-only live check of the connected-host status route and the isolated Agent Knowledge status route, reports SDK compatibility, token posture, endpoint bindings, route readiness, and actionable findings, and still refuses connected-host lifecycle control. The model uses those first-class tools for product workflows where they exist, such as Agent Knowledge, local memory/notes/personas/skills/routines, channel sends, notifications, reminders, media generation, work plans, and connected-host operator actions.
70
70
 
71
71
  The setup workspace surfaces discovered Agent-local persona, skill, and routine markdown files so day-one setup can import useful behavior instead of starting from blank records. It can also create one initial scratchpad note, local persona, skill, and routine directly during first-run setup; those records stay in Agent-local registries and never write to default knowledge or non-Agent segments.
72
72
 
@@ -816508,7 +816508,7 @@ var createStyledCell = (char, overrides = {}) => ({
816508
816508
  // src/version.ts
816509
816509
  import { readFileSync } from "fs";
816510
816510
  import { join } from "path";
816511
- var _version = "1.0.10";
816511
+ var _version = "1.0.12";
816512
816512
  var _sdkVersion = "0.33.35";
816513
816513
  try {
816514
816514
  const pkg = JSON.parse(readFileSync(join(import.meta.dir, "..", "package.json"), "utf-8"));
@@ -847717,13 +847717,13 @@ init_state3();
847717
847717
 
847718
847718
  // src/input/commands/recall-shared.ts
847719
847719
  var VALID_CLASSES = ["decision", "constraint", "incident", "pattern", "fact", "risk", "runbook", "architecture", "ownership"];
847720
- var VALID_SCOPES2 = ["session", "project", "team"];
847720
+ var VALID_SCOPES = ["session", "project", "team"];
847721
847721
  var VALID_REVIEW_STATES = ["fresh", "reviewed", "stale", "contradicted"];
847722
847722
  function isValidClass(s4) {
847723
847723
  return VALID_CLASSES.includes(s4);
847724
847724
  }
847725
847725
  function isValidScope(s4) {
847726
- return VALID_SCOPES2.includes(s4);
847726
+ return VALID_SCOPES.includes(s4);
847727
847727
  }
847728
847728
  function isValidReviewState(s4) {
847729
847729
  return VALID_REVIEW_STATES.includes(s4);
@@ -847766,7 +847766,7 @@ function handleRecallSearch(args2, context) {
847766
847766
  if (isValidScope(scope))
847767
847767
  filter.scope = scope;
847768
847768
  else {
847769
- context.print(`[memory] Unknown scope "${scope}". Valid values ${VALID_SCOPES2.join(", ")}.`);
847769
+ context.print(`[memory] Unknown scope "${scope}". Valid values ${VALID_SCOPES.join(", ")}.`);
847770
847770
  return;
847771
847771
  }
847772
847772
  }
@@ -847941,7 +847941,7 @@ function handleRecallList(args2, context) {
847941
847941
  if (scopeIdx !== -1 && args2[scopeIdx + 1]) {
847942
847942
  const scope = args2[scopeIdx + 1];
847943
847943
  if (!isValidScope(scope)) {
847944
- context.print(`[memory] Unknown scope "${scope}". Valid values ${VALID_SCOPES2.join(", ")}.`);
847944
+ context.print(`[memory] Unknown scope "${scope}". Valid values ${VALID_SCOPES.join(", ")}.`);
847945
847945
  return;
847946
847946
  }
847947
847947
  filter.scope = scope;
@@ -847991,7 +847991,7 @@ async function handleRecallAdd(args2, context) {
847991
847991
  const tags = tagsRaw ? tagsRaw.split(",").map((token) => token.trim()).filter(Boolean) : [];
847992
847992
  const scope = scopeRaw && isValidScope(scopeRaw) ? scopeRaw : "project";
847993
847993
  if (scopeRaw && !isValidScope(scopeRaw)) {
847994
- context.print(`[memory] Invalid scope "${scopeRaw}". Valid values ${VALID_SCOPES2.join(", ")}.`);
847994
+ context.print(`[memory] Invalid scope "${scopeRaw}". Valid values ${VALID_SCOPES.join(", ")}.`);
847995
847995
  return;
847996
847996
  }
847997
847997
  const provenance = [];
@@ -848125,7 +848125,7 @@ function handleRecallExport(args2, context) {
848125
848125
  if (scopeIdx !== -1 && commandArgs[scopeIdx + 1]) {
848126
848126
  const scope = commandArgs[scopeIdx + 1];
848127
848127
  if (!isValidScope(scope)) {
848128
- context.print(`[memory] Unknown scope "${scope}". Valid values ${VALID_SCOPES2.join(", ")}.`);
848128
+ context.print(`[memory] Unknown scope "${scope}". Valid values ${VALID_SCOPES.join(", ")}.`);
848129
848129
  return;
848130
848130
  }
848131
848131
  filter.scope = scope;
@@ -848204,7 +848204,7 @@ function handleRecallHandoffExport(args2, context) {
848204
848204
  const scopeIdx = commandArgs.indexOf("--scope");
848205
848205
  const scopeRaw = scopeIdx !== -1 ? commandArgs[scopeIdx + 1] : "team";
848206
848206
  if (!scopeRaw || !isValidScope(scopeRaw)) {
848207
- context.print(`[memory] Unknown scope "${scopeRaw ?? ""}". Valid values ${VALID_SCOPES2.join(", ")}.`);
848207
+ context.print(`[memory] Unknown scope "${scopeRaw ?? ""}". Valid values ${VALID_SCOPES.join(", ")}.`);
848208
848208
  return;
848209
848209
  }
848210
848210
  const bundle = memory.exportBundle({ scope: scopeRaw });
@@ -848320,7 +848320,7 @@ function handleRecallPromote(args2, context) {
848320
848320
  const id = parsed.rest[0];
848321
848321
  const scope = parsed.rest[1];
848322
848322
  if (!id || !scope || !isValidScope(scope)) {
848323
- context.print(`[memory] Usage: /memory promote <id> <${VALID_SCOPES2.join("|")}> --yes`);
848323
+ context.print(`[memory] Usage: /memory promote <id> <${VALID_SCOPES.join("|")}> --yes`);
848324
848324
  return;
848325
848325
  }
848326
848326
  if (!parsed.yes) {
@@ -862165,7 +862165,7 @@ import { mkdirSync as mkdirSync64, readFileSync as readFileSync86, writeFileSync
862165
862165
  import { dirname as dirname63, resolve as resolve39 } from "path";
862166
862166
  init_state3();
862167
862167
  var VALID_CLASSES2 = ["decision", "constraint", "incident", "pattern", "fact", "risk", "runbook", "architecture", "ownership"];
862168
- var VALID_SCOPES4 = ["session", "project", "team"];
862168
+ var VALID_SCOPES3 = ["session", "project", "team"];
862169
862169
  var VALID_REVIEW_STATES3 = ["fresh", "reviewed", "stale", "contradicted"];
862170
862170
  var VALID_PROVENANCE_KINDS = ["session", "turn", "task", "event", "file"];
862171
862171
  var VALUE_OPTIONS = new Set([
@@ -862265,7 +862265,7 @@ function isMemoryClass2(value) {
862265
862265
  return VALID_CLASSES2.includes(value);
862266
862266
  }
862267
862267
  function isMemoryScope2(value) {
862268
- return VALID_SCOPES4.includes(value);
862268
+ return VALID_SCOPES3.includes(value);
862269
862269
  }
862270
862270
  function isReviewState(value) {
862271
862271
  return VALID_REVIEW_STATES3.includes(value);
@@ -862280,7 +862280,7 @@ function requireClass(value) {
862280
862280
  }
862281
862281
  function requireScope(value) {
862282
862282
  if (!value || !isMemoryScope2(value))
862283
- throw new Error(`Invalid memory scope "${value ?? ""}". Valid values ${VALID_SCOPES4.join(", ")}`);
862283
+ throw new Error(`Invalid memory scope "${value ?? ""}". Valid values ${VALID_SCOPES3.join(", ")}`);
862284
862284
  return value;
862285
862285
  }
862286
862286
  function optionalScope(value) {
@@ -880492,7 +880492,7 @@ function localRegistryArgsFromEditor(editor, fields, id) {
880492
880492
  if (editor.kind === "memory")
880493
880493
  return { domain: "memory", action: editor.mode === "update" ? "update" : "create", id, cls: read("cls"), scope: read("scope"), summary: read("summary"), detail: read("detail"), tags: splitList5(read("tags")), confidence: read("confidence"), provenance: editor.recordId ? "agent-harness-local-operation" : "agent-harness-note-promotion" };
880494
880494
  if (editor.kind === "note")
880495
- return { domain: "note", action: "update", id, title: read("title"), body: read("body"), sourceUrl: read("sourceUrl"), tags: splitList5(read("tags")), provenance: "agent-harness-local-operation" };
880495
+ return { domain: "note", action: editor.mode === "update" ? "update" : "create", id, title: read("title"), body: read("body"), sourceUrl: read("sourceUrl"), tags: splitList5(read("tags")), provenance: editor.recordId ? "agent-harness-local-operation" : "agent-harness-note-promotion" };
880496
880496
  if (editor.kind === "persona")
880497
880497
  return { domain: "persona", action: editor.mode === "update" ? "update" : "create", id, name: read("name"), description: read("description"), body: read("body"), tags: splitList5(read("tags")), triggers: splitList5(read("triggers")), activate: read("activate"), provenance: editor.recordId ? "agent-harness-local-operation" : "agent-harness-note-promotion" };
880498
880498
  if (editor.kind === "skill")
@@ -880530,6 +880530,27 @@ function describeLocalWorkspaceModelExecution(action2) {
880530
880530
  return { tool: "agent_knowledge_ingest", sourceKind: "url", requiresRecordId: true, selectedRecordDomain: "note" };
880531
880531
  return { tool: "agent_local_registry", selectedRecordDomain: "note", action: "create", requiresRecordId: true };
880532
880532
  }
880533
+ async function runLocalWorkspaceEditorAction(deps, editor, args2) {
880534
+ const fields = readFieldMap2(args2.fields);
880535
+ const missing = missingRequiredFields(editor, fields);
880536
+ if (missing.length > 0) {
880537
+ return output6({
880538
+ status: "missing_required_fields",
880539
+ missing,
880540
+ editor: describeEditor(editor)
880541
+ });
880542
+ }
880543
+ const confirmationError2 = requireConfirmed(args2, "Workspace local registry editor action");
880544
+ if (confirmationError2)
880545
+ return error51(confirmationError2);
880546
+ if (editor.kind !== "memory" && editor.kind !== "note" && editor.kind !== "persona" && editor.kind !== "skill" && editor.kind !== "routine") {
880547
+ return output6({
880548
+ status: "model_tool_required",
880549
+ editor: describeEditor(editor)
880550
+ });
880551
+ }
880552
+ return executeTool(deps.toolRegistry, "agent_local_registry", localRegistryArgsFromEditor(editor, fields, editor.recordId));
880553
+ }
880533
880554
  async function runLocalWorkspaceAction(deps, action2, args2) {
880534
880555
  if (action2.kind === "local-selection")
880535
880556
  return output6({ status: "local_selection", action: action2.id, modelExecution: describeLocalWorkspaceModelExecution(action2) });
@@ -880878,6 +880899,17 @@ function optionalPane(args2) {
880878
880899
  const pane = readString56(args2.pane);
880879
880900
  return pane === "top" || pane === "bottom" ? pane : undefined;
880880
880901
  }
880902
+ function surfaceInputText(args2) {
880903
+ return readString56(args2.prefix || args2.key || (args2.surfaceId ? args2.query : undefined) || args2.target) || undefined;
880904
+ }
880905
+ function filePickerOptions(args2) {
880906
+ const target = readString56(args2.target).toLowerCase();
880907
+ const injectMode = target === "inject" || target === "inject-mode" || target === "!@";
880908
+ return {
880909
+ injectMode,
880910
+ query: readString56(args2.prefix || args2.key || (args2.surfaceId ? args2.query : undefined)) || undefined
880911
+ };
880912
+ }
880881
880913
  function openAgentWorkspaceCategory(context, surface, categoryId, extra = {}) {
880882
880914
  if (!context.openAgentWorkspace)
880883
880915
  return routeUnavailable(surface);
@@ -881073,6 +881105,33 @@ var UI_SURFACES = [
881073
881105
  return opened(surface, { target: "main" });
881074
881106
  }
881075
881107
  },
881108
+ {
881109
+ id: "reasoning-effort-picker",
881110
+ label: "Reasoning Effort Picker",
881111
+ kind: "picker",
881112
+ summary: "Interactive reasoning-effort selector for the current main chat model when that model exposes effort levels.",
881113
+ command: "/effort",
881114
+ preferredModelRoute: "Use settings/get_setting/set_setting for provider.reasoningEffort when a concrete level is known, or run_workspace_action setup-effort with confirmation.",
881115
+ available: (context) => typeof context.openReasoningEffortPicker === "function",
881116
+ open: (context) => {
881117
+ const surface = findSurfaceById("reasoning-effort-picker");
881118
+ if (!context.openReasoningEffortPicker)
881119
+ return routeUnavailable(surface);
881120
+ const result2 = context.openReasoningEffortPicker();
881121
+ return result2.opened ? opened(surface, {
881122
+ model: result2.model,
881123
+ levels: result2.levels ?? []
881124
+ }) : {
881125
+ status: "not_opened",
881126
+ surface: surface.id,
881127
+ kind: surface.kind,
881128
+ model: result2.model,
881129
+ levels: result2.levels ?? [],
881130
+ reason: result2.reason ?? "unsupported",
881131
+ note: "The current model does not expose configurable reasoning effort levels."
881132
+ };
881133
+ }
881134
+ },
881076
881135
  {
881077
881136
  id: "tts-provider-picker",
881078
881137
  label: "TTS Provider Picker",
@@ -881187,6 +881246,155 @@ var UI_SURFACES = [
881187
881246
  return opened(surface);
881188
881247
  }
881189
881248
  },
881249
+ {
881250
+ id: "live-tail",
881251
+ label: "Live Process Output",
881252
+ kind: "modal",
881253
+ summary: "Visible live-output tail for a running process, opened by the same shell route as Enter from the runtime activity monitor.",
881254
+ command: "F2, Enter",
881255
+ preferredModelRoute: "Use this only for visible supervision of a running process output stream; use first-class model tools or confirmed commands for actual operations.",
881256
+ parameters: ["target", "query", "prefix", "key"],
881257
+ available: (context) => typeof context.openLiveTail === "function",
881258
+ open: (context, args2) => {
881259
+ const surface = findSurfaceById("live-tail");
881260
+ if (!context.openLiveTail)
881261
+ return routeUnavailable(surface);
881262
+ const target = surfaceInputText(args2);
881263
+ const result2 = context.openLiveTail(target);
881264
+ return result2.opened ? opened(surface, {
881265
+ target: target ?? "selected",
881266
+ processId: result2.processId,
881267
+ label: result2.label
881268
+ }) : {
881269
+ status: "not_opened",
881270
+ surface: surface.id,
881271
+ kind: surface.kind,
881272
+ target: target ?? "selected",
881273
+ reason: result2.reason ?? "not_found",
881274
+ note: result2.reason === "no_processes" ? "There are no running shell processes to tail." : "No running shell process matched the requested target."
881275
+ };
881276
+ }
881277
+ },
881278
+ {
881279
+ id: "conversation-search",
881280
+ label: "Conversation Search",
881281
+ kind: "overlay",
881282
+ summary: "Visible transcript search overlay opened by the same shell route as Ctrl+F.",
881283
+ command: "Ctrl+F",
881284
+ preferredModelRoute: "Use conversation/session/content modes for model-readable inspection; use this surface for visible transcript search navigation.",
881285
+ parameters: ["query", "prefix", "key"],
881286
+ available: (context) => typeof context.openConversationSearch === "function",
881287
+ open: (context, args2) => {
881288
+ const surface = findSurfaceById("conversation-search");
881289
+ if (!context.openConversationSearch)
881290
+ return routeUnavailable(surface);
881291
+ const query2 = surfaceInputText(args2);
881292
+ context.openConversationSearch(query2);
881293
+ return opened(surface, { query: query2 ?? "" });
881294
+ }
881295
+ },
881296
+ {
881297
+ id: "prompt-history-search",
881298
+ label: "Prompt History Search",
881299
+ kind: "overlay",
881300
+ summary: "Visible reverse prompt-history search opened by the same shell route as Ctrl+R.",
881301
+ command: "Ctrl+R",
881302
+ preferredModelRoute: "Use this only for visible prompt recall; accepting a result remains an explicit interactive shell action.",
881303
+ parameters: ["query", "prefix", "key"],
881304
+ available: (context) => typeof context.openPromptHistorySearch === "function",
881305
+ open: (context, args2) => {
881306
+ const surface = findSurfaceById("prompt-history-search");
881307
+ if (!context.openPromptHistorySearch)
881308
+ return routeUnavailable(surface);
881309
+ const query2 = surfaceInputText(args2);
881310
+ context.openPromptHistorySearch(query2);
881311
+ return opened(surface, { query: query2 ?? "" });
881312
+ }
881313
+ },
881314
+ {
881315
+ id: "slash-command-mode",
881316
+ label: "Slash Command Mode",
881317
+ kind: "overlay",
881318
+ summary: "Slash-command autocomplete route opened by the same shell path as typing / in an empty prompt.",
881319
+ command: "/",
881320
+ preferredModelRoute: "Use commands/command for model-readable command discovery and run_command for confirmed command execution.",
881321
+ parameters: ["query", "prefix", "key"],
881322
+ available: (context) => typeof context.openSlashCommandMode === "function",
881323
+ open: (context, args2) => {
881324
+ const surface = findSurfaceById("slash-command-mode");
881325
+ if (!context.openSlashCommandMode)
881326
+ return routeUnavailable(surface);
881327
+ const query2 = surfaceInputText(args2);
881328
+ const openedCommandMode = context.openSlashCommandMode(query2);
881329
+ return openedCommandMode ? opened(surface, { query: query2 ?? "" }) : {
881330
+ status: "not_opened",
881331
+ surface: surface.id,
881332
+ kind: surface.kind,
881333
+ query: query2 ?? "",
881334
+ note: "The current prompt contains a non-command draft, so the shell opener refused to replace it."
881335
+ };
881336
+ }
881337
+ },
881338
+ {
881339
+ id: "command-browser",
881340
+ label: "Command Browser",
881341
+ kind: "picker",
881342
+ summary: "Registry-driven searchable slash-command browser opened by /commands and /help.",
881343
+ command: "/commands",
881344
+ preferredModelRoute: "Use commands/command for model-readable slash-command discovery and run_command for confirmed command execution.",
881345
+ available: (context) => typeof context.executeCommand === "function",
881346
+ open: async (context) => {
881347
+ const surface = findSurfaceById("command-browser");
881348
+ if (!context.executeCommand)
881349
+ return routeUnavailable(surface);
881350
+ const handled = await context.executeCommand("commands", []);
881351
+ return handled ? opened(surface, { command: "/commands" }) : {
881352
+ status: "not_opened",
881353
+ surface: surface.id,
881354
+ kind: surface.kind,
881355
+ note: "The slash-command registry did not handle /commands in the current runtime."
881356
+ };
881357
+ }
881358
+ },
881359
+ {
881360
+ id: "file-picker",
881361
+ label: "File Picker",
881362
+ kind: "picker",
881363
+ summary: "Visible project file picker opened by the same prompt route as @ and !@ references.",
881364
+ command: "@",
881365
+ preferredModelRoute: "Use first-class file, workspace, or artifact tools for model operation; use this for visible file reference navigation.",
881366
+ parameters: ["target=reference|inject", "query", "prefix", "key"],
881367
+ available: (context) => typeof context.openFilePicker === "function",
881368
+ open: (context, args2) => {
881369
+ const surface = findSurfaceById("file-picker");
881370
+ if (!context.openFilePicker)
881371
+ return routeUnavailable(surface);
881372
+ const options = filePickerOptions(args2);
881373
+ const openedFilePicker = context.openFilePicker(options);
881374
+ return openedFilePicker ? opened(surface, { mode: options.injectMode ? "inject" : "reference", query: options.query ?? "" }) : routeUnavailable(surface);
881375
+ }
881376
+ },
881377
+ {
881378
+ id: "block-actions",
881379
+ label: "Block Actions",
881380
+ kind: "overlay",
881381
+ summary: "Visible nearest-block action menu opened by the same shell route as pressing Enter on an empty prompt near transcript content.",
881382
+ command: "Enter on empty prompt",
881383
+ preferredModelRoute: "Use conversation/session/content modes or confirmed slash-command mirrors for concrete block operations; use this surface for visible block-action navigation.",
881384
+ available: (context) => typeof context.openBlockActions === "function",
881385
+ open: (context) => {
881386
+ const surface = findSurfaceById("block-actions");
881387
+ if (!context.openBlockActions)
881388
+ return routeUnavailable(surface);
881389
+ const openedBlockActions = context.openBlockActions();
881390
+ return openedBlockActions ? opened(surface) : {
881391
+ status: "not_opened",
881392
+ surface: surface.id,
881393
+ kind: surface.kind,
881394
+ note: "The shell opener requires an empty prompt and a nearby rendered conversation block."
881395
+ };
881396
+ }
881397
+ },
881190
881398
  {
881191
881399
  id: "help-overlay",
881192
881400
  label: "Help Overlay",
@@ -881443,15 +881651,15 @@ function describeWorkspaceAction(category, action2, options = {}) {
881443
881651
  }
881444
881652
  function localEditorModelExecution(editorKind) {
881445
881653
  if (editorKind === "memory")
881446
- return 'Use agent_local_registry with domain:"memory" for create/update/review/stale/delete.';
881654
+ return 'run_workspace_action can execute this editor from fields through agent_local_registry domain:"memory"; agent_local_registry also supports list/search/get/review/stale/delete.';
881447
881655
  if (editorKind === "note")
881448
- return 'Use agent_local_registry with domain:"note" for create/update/review/stale/delete.';
881656
+ return 'run_workspace_action can execute this editor from fields through agent_local_registry domain:"note"; agent_local_registry also supports list/search/get/review/stale/delete.';
881449
881657
  if (editorKind === "persona")
881450
- return 'Use agent_local_registry with domain:"persona" for create/update/use/clear_active/review/stale/delete.';
881658
+ return 'run_workspace_action can execute this editor from fields through agent_local_registry domain:"persona"; agent_local_registry also supports list/search/get/use/clear_active/review/stale/delete.';
881451
881659
  if (editorKind === "skill")
881452
- return 'Use agent_local_registry with domain:"skill" for create/update/enable/disable/review/stale/delete.';
881660
+ return 'run_workspace_action can execute this editor from fields through agent_local_registry domain:"skill"; agent_local_registry also supports list/search/get/enable/disable/review/stale/delete.';
881453
881661
  if (editorKind === "routine")
881454
- return 'Use agent_local_registry with domain:"routine" for create/update/enable/disable/start/review/stale/delete.';
881662
+ return 'run_workspace_action can execute this editor from fields through agent_local_registry domain:"routine"; agent_local_registry also supports list/search/get/enable/disable/start/review/stale/delete.';
881455
881663
  if (editorKind === "learned-behavior")
881456
881664
  return "run_workspace_action can create the learned behavior from fields.";
881457
881665
  if (editorKind === "profile")
@@ -881605,6 +881813,9 @@ async function runWorkspaceEditorAction(deps, action2, editor, args2) {
881605
881813
  });
881606
881814
  }
881607
881815
  if (!isAgentWorkspaceCommandEditorKind(editor.kind)) {
881816
+ if (editor.kind === "memory" || editor.kind === "note" || editor.kind === "persona" || editor.kind === "skill" || editor.kind === "routine") {
881817
+ return runLocalWorkspaceEditorAction(deps, editor, args2);
881818
+ }
881608
881819
  return output7({
881609
881820
  status: "model_tool_required",
881610
881821
  action: action2.id,
@@ -899900,6 +900111,40 @@ function wireShellUiOpeners(options) {
899900
900111
  render();
899901
900112
  });
899902
900113
  };
900114
+ commandContext.openReasoningEffortPicker = () => {
900115
+ const currentModel = providerRegistry.getCurrentModel();
900116
+ const validLevels = currentModel.reasoningEffort ?? [];
900117
+ if (validLevels.length === 0) {
900118
+ return { opened: false, model: currentModel.displayName, levels: [], reason: "unsupported" };
900119
+ }
900120
+ const current = String(runtime3.reasoningEffort || configManager.get("provider.reasoningEffort") || "medium");
900121
+ const descriptions = {
900122
+ ...EFFORT_DESCRIPTIONS,
900123
+ medium: "Balanced speed and quality (default)"
900124
+ };
900125
+ input.openSelection("Reasoning Effort", validLevels.map((level) => ({
900126
+ id: level,
900127
+ label: level,
900128
+ detail: level === current ? `current - ${descriptions[level] ?? level}` : descriptions[level] ?? level
900129
+ })), { preSelectId: current, allowSearch: false }, (result2) => {
900130
+ if (!result2)
900131
+ return;
900132
+ const level = result2.item.id;
900133
+ runtime3.reasoningEffort = level;
900134
+ configManager.set("provider.reasoningEffort", level);
900135
+ commandContext.print([
900136
+ "Reasoning effort set",
900137
+ ` level ${level}`
900138
+ ].join(`
900139
+ `));
900140
+ render();
900141
+ });
900142
+ return {
900143
+ opened: true,
900144
+ model: currentModel.displayName,
900145
+ levels: validLevels
900146
+ };
900147
+ };
899903
900148
  commandContext.openSelection = (title, items, opts, callback) => {
899904
900149
  input.openSelection(title, items, opts, callback);
899905
900150
  };
@@ -899921,6 +900166,92 @@ function wireShellUiOpeners(options) {
899921
900166
  input.processModal.open();
899922
900167
  render();
899923
900168
  };
900169
+ commandContext.openLiveTail = (target) => {
900170
+ input.processModal.refresh();
900171
+ const entries = input.processModal.entries;
900172
+ if (entries.length === 0) {
900173
+ return { opened: false, reason: "no_processes" };
900174
+ }
900175
+ const normalizedTarget = target?.trim().toLowerCase() ?? "";
900176
+ const entry = normalizedTarget.length > 0 ? entries.find((candidate) => candidate.id.toLowerCase() === normalizedTarget || candidate.label.toLowerCase().includes(normalizedTarget)) : input.processModal.getSelected() ?? entries[0];
900177
+ if (!entry) {
900178
+ return { opened: false, reason: "not_found" };
900179
+ }
900180
+ input.modalOpened("liveTail");
900181
+ input.processModal.close();
900182
+ input.liveTailModal.open(entry);
900183
+ render();
900184
+ return { opened: true, processId: entry.id, label: entry.label };
900185
+ };
900186
+ commandContext.openConversationSearch = (query2) => {
900187
+ input.searchManager.open();
900188
+ const searchQuery = query2?.trim() ?? "";
900189
+ if (searchQuery.length > 0) {
900190
+ input.searchManager.search(searchQuery, input.getHistory());
900191
+ }
900192
+ render();
900193
+ };
900194
+ commandContext.openPromptHistorySearch = (query2) => {
900195
+ input.historySearch.open(input.prompt);
900196
+ const searchQuery = query2?.trim() ?? "";
900197
+ if (searchQuery.length > 0) {
900198
+ input.historySearch.search(searchQuery);
900199
+ }
900200
+ render();
900201
+ };
900202
+ commandContext.openSlashCommandMode = (query2) => {
900203
+ const normalizedQuery = query2?.trim().replace(/^\/+/, "") ?? "";
900204
+ if (input.prompt.length > 0 && !input.prompt.startsWith("/")) {
900205
+ return false;
900206
+ }
900207
+ input.modalOpened("command");
900208
+ input.commandMode = true;
900209
+ if (input.prompt.length === 0 || !input.prompt.startsWith("/") || normalizedQuery.length > 0) {
900210
+ input.saveUndoState();
900211
+ input.prompt = `/${normalizedQuery}`;
900212
+ input.cursorPos = input.prompt.length;
900213
+ }
900214
+ const commandQuery = input.prompt.startsWith("/") ? input.prompt.slice(1) : "";
900215
+ const hasArgs = commandQuery.includes(" ");
900216
+ if (hasArgs)
900217
+ input.autocomplete?.reset();
900218
+ else
900219
+ input.autocomplete?.update(commandQuery);
900220
+ input.ensureInputCursorVisible();
900221
+ input.syncFeedContextMutableFields();
900222
+ render();
900223
+ return true;
900224
+ };
900225
+ commandContext.openFilePicker = (options2) => {
900226
+ const injectMode = Boolean(options2?.injectMode);
900227
+ const query2 = options2?.query?.trim() ?? "";
900228
+ const marker = `${injectMode ? "!@" : "@"}${query2}`;
900229
+ const insertPos = input.cursorPos;
900230
+ input.saveUndoState();
900231
+ input.prompt = input.prompt.slice(0, insertPos) + marker + input.prompt.slice(insertPos);
900232
+ input.cursorPos = insertPos + marker.length;
900233
+ input.modalOpened("filePicker");
900234
+ input.filePicker.open(insertPos, injectMode);
900235
+ if (query2.length > 0)
900236
+ input.filePicker.setQuery(query2);
900237
+ input.ensureInputCursorVisible();
900238
+ input.syncFeedContextMutableFields();
900239
+ render();
900240
+ return true;
900241
+ };
900242
+ commandContext.openBlockActions = () => {
900243
+ if (input.prompt.trim().length > 0 || input.commandMode) {
900244
+ return false;
900245
+ }
900246
+ const nearest = conversation.findNearestBlock(input.getScrollTop());
900247
+ if (!nearest) {
900248
+ return false;
900249
+ }
900250
+ input.modalOpened("blockActions");
900251
+ input.blockActionsMenu.open(nearest);
900252
+ render();
900253
+ return true;
900254
+ };
899924
900255
  commandContext.openHelpOverlay = () => {
899925
900256
  if (!input.helpOverlayActive)
899926
900257
  input.modalOpened("help");
package/docs/README.md CHANGED
@@ -22,7 +22,7 @@ Important baseline constraints:
22
22
  - Agent depends on `@pellux/goodvibes-sdk@0.33.35`.
23
23
  - Agent connects to a GoodVibes host owned outside this product.
24
24
  - Agent does not start, stop, restart, install, uninstall, or own the connected GoodVibes host.
25
- - User-facing Agent workspace actions, built-in panels, modal/overlay/picker UI surfaces, named operator surfaces, top-level CLI mirrors, fixed shortcuts, configurable keybindings, slash commands, command policy metadata, model tools, settings, local registries, connected-host capability boundaries, and live connected-host readiness are model-visible through Agent-owned tools. Mutations and visible UI routing remain explicit and confirmation-gated.
25
+ - User-facing Agent workspace actions, built-in panels, modal/overlay/picker UI surfaces, named operator surfaces, top-level CLI mirrors, fixed shortcuts, configurable keybindings, slash commands, command policy metadata, model tools, settings, local registries, connected-host capability boundaries, and live connected-host readiness are model-visible through Agent-owned tools. Visible UI routing includes the command browser, reasoning-effort picker, live process output, runtime activity, settings, workspaces, and pickers the user can open. Local memory, note, persona, skill, and routine create editors can also run through confirmed workspace actions. Mutations and visible UI routing remain explicit and confirmation-gated.
26
26
  - Agent Knowledge uses only `/api/goodvibes-agent/knowledge/*`; there is no default knowledge or non-Agent product fallback.
27
27
  - Agent supports isolated Agent homes with `GOODVIBES_AGENT_HOME=<path>` and named profile homes with `goodvibes-agent profiles create <name> --template <starter> --yes` plus `--agent-profile <name>`.
28
28
  - Agent supports connected-host URL overrides with `--runtime-url http://host:port` or `GOODVIBES_AGENT_RUNTIME_URL=http://host:port`; these only change the Agent connection target.
@@ -44,7 +44,7 @@ After setup has been applied once, the TUI opens directly into the Agent operato
44
44
 
45
45
  Press `/` inside the Agent workspace to search every workspace action by name, category, command, or detail. Use that finder before reaching for shell commands; CLI subcommands are scriptable mirrors of these TUI workflows.
46
46
 
47
- The model can inspect and use the same harness surface through Agent-owned tools. `agent_harness` exposes workspace action discovery, built-in panel discovery/routing, modal/overlay/picker UI surface discovery/routing, top-level CLI mirror discovery, fixed shortcut and configurable keybinding discovery, keybinding mutation with confirmation, slash-command discovery, command policy metadata, command execution with confirmation, model tool discovery, settings inspection/mutation, connected-host capability inventory, and live connected-host readiness posture. CLI mirror modes are read-only catalog/parser inspection and point the model to the matching in-process tool, workspace action, setting mode, or slash-command mirror. Panel modes expose catalog/open state and route visible panel/workspace changes through the current Agent shell bridge with confirmation. UI surface modes expose help, shortcuts, context, runtime activity, bookmarks, model/provider pickers, TTS provider/voice pickers, session/profile pickers, the panel-picker compatibility route, security/knowledge/subscription operator surfaces, settings, MCP workspace, onboarding, and Agent workspace entrypoints; `open_ui_surface` is confirmation-gated and only performs visible shell navigation. Shortcut modes expose the fixed runtime/editor shortcuts and the live keybindings table; confirmed keybinding edits write the same `keybindings.json` file the user edits and reload the runtime manager. Workspace action discovery can include editor field schemas with `includeParameters:true`, including starter-template defaults for profile creation and selected-routine defaults for routine schedule promotion when `recordId` is supplied. Selection-based local workspace actions accept a local `recordId`, so the model can use the same note promotion and local registry flows as the TUI. First-class model tools cover the main product workflows directly: Agent Knowledge, Agent Knowledge ingest, Agent-local memory/notes/personas/skills/routines, operator actions, notifications, channel sends, reminders, generated media, and work plans.
47
+ The model can inspect and use the same harness surface through Agent-owned tools. `agent_harness` exposes workspace action discovery, built-in panel discovery/routing, modal/overlay/picker UI surface discovery/routing, top-level CLI mirror discovery, fixed shortcut and configurable keybinding discovery, keybinding mutation with confirmation, slash-command discovery, command policy metadata, command execution with confirmation, model tool discovery, settings inspection/mutation, connected-host capability inventory, and live connected-host readiness posture. CLI mirror modes are read-only catalog/parser inspection and point the model to the matching in-process tool, workspace action, setting mode, or slash-command mirror. Panel modes expose catalog/open state and route visible panel/workspace changes through the current Agent shell bridge with confirmation. UI surface modes expose help, shortcuts, command browser, conversation search, prompt-history search, slash-command mode, file picker, block actions, context, runtime activity, live process output, bookmarks, model/provider/reasoning-effort pickers, TTS provider/voice pickers, session/profile pickers, the panel-picker compatibility route, security/knowledge/subscription operator surfaces, settings, MCP workspace, onboarding, and Agent workspace entrypoints; `open_ui_surface` is confirmation-gated and only performs visible shell navigation. Shortcut modes expose the fixed runtime/editor shortcuts and the live keybindings table; confirmed keybinding edits write the same `keybindings.json` file the user edits and reload the runtime manager. Workspace action discovery can include editor field schemas with `includeParameters:true`, including starter-template defaults for profile creation and selected-routine defaults for routine schedule promotion when `recordId` is supplied. Selection-based local workspace actions accept a local `recordId`, so the model can use the same note promotion and local registry flows as the TUI. Direct local create editors for memory, notes, personas, skills, and routines can execute from submitted fields through `run_workspace_action` and `agent_local_registry` with confirmation. First-class model tools cover the main product workflows directly: Agent Knowledge, Agent Knowledge ingest, Agent-local memory/notes/personas/skills/routines, operator actions, notifications, channel sends, reminders, generated media, and work plans.
48
48
 
49
49
  Use the Artifacts area for concrete files and generated output: attach images to prompts, export conversations or saved sessions, ingest local source files and URL lists into Agent Knowledge, import bookmarks or browser history, inspect source records, and generate media artifacts from confirmed prompts.
50
50
 
@@ -52,7 +52,7 @@ The main Agent model has an Agent-owned harness bridge rather than generic SDK s
52
52
 
53
53
  `agent_harness` discovery modes are read-only. `summary` reports the model access map; `panels` and `panel` return the built-in panel catalog, current open/focused state, and each panel's matching Agent workspace route; `ui_surfaces` and `ui_surface` return modal, overlay, picker, and workspace entrypoints with shell-opener availability and preferred model routes; `cli_commands` and `cli_command` return top-level package CLI mirror metadata, parser output, blocked command tokens, and preferred in-process model routes; `shortcuts` returns fixed runtime/editor shortcuts plus configurable keybindings; `keybindings` and `keybinding` return the live resolved keybinding table, default bindings, custom state, and config path; `commands` and `command` return slash-command descriptions plus effect/confirmation/preferred-tool/boundary policy metadata; `workspace_actions` lists Agent workspace actions and can inline editor field schemas with `includeParameters:true`; `workspace_action` returns one action with its editor schema; `tools` can inline model tool JSON schemas with `includeParameters:true`; `settings` returns setting descriptors plus setting policy; `connected_host` returns the connected-host route families, allowed capabilities, blocked capabilities, and first-class tool availability; and `connected_host_status` performs a live read-only check of the connected-host status and Agent Knowledge status routes and reports endpoint bindings, token posture, SDK compatibility, route readiness, and findings without printing token values.
54
54
 
55
- `open_ui_surface` is a confirmation-gated visible navigation mode for the same shell surfaces the user can open: Agent workspace, settings, MCP workspace, model/provider pickers, TTS provider/voice pickers, session/profile pickers, the panel-picker compatibility route, security/knowledge/subscription operator surfaces, bookmarks, context inspector, runtime activity monitor, help, shortcuts, and onboarding. It does not perform hidden operations; use first-class model tools, settings modes, workspace actions, or confirmed slash-command mirrors for actual state changes.
55
+ `open_ui_surface` is a confirmation-gated visible navigation mode for the same shell surfaces the user can open: Agent workspace, settings, MCP workspace, model/provider/reasoning-effort pickers, TTS provider/voice pickers, session/profile pickers, the panel-picker compatibility route, security/knowledge/subscription operator surfaces, conversation search, prompt-history search, slash-command mode, command browser, file picker, block actions, bookmarks, context inspector, runtime activity monitor, live process output, help, shortcuts, and onboarding. It does not perform hidden operations; use first-class model tools, settings modes, workspace actions, or confirmed slash-command mirrors for actual state changes.
56
56
 
57
57
  `set_keybinding` and `reset_keybinding` are confirmation-gated control modes. They write the same Agent `keybindings.json` file exposed by `/keybindings`, reload the runtime keybinding manager, and leave fixed runtime/editor shortcuts read-only.
58
58
 
@@ -62,7 +62,7 @@ CLI mirror modes are catalog and parser inspection only. When the user asks the
62
62
 
63
63
  Setting writes, setting resets, keybinding writes/resets, UI surface routing, slash-command invocation, workspace-action invocation, local record deletion, channel sends, notifications, reminders, media generation, and connected-host operator mutations require explicit user request and confirmation. Secret-backed settings are stored through the secret manager, and connected-host lifecycle/listener settings remain read-only in Agent.
64
64
 
65
- Selection-based local workspace actions use the same bridge. `agent_harness` reports the required model tool for each local action; for actions that depend on the TUI selection, call `run_workspace_action` with the selected local `recordId`. Note promotion actions can prefill and create memory, personas, skills, routines, or isolated Agent Knowledge URL ingests through the matching first-class model tool. Profile creation schemas include the current runtime starter-template inventory, and routine schedule schemas prefill the selected routine when `recordId` or a `routineId` field matches a local routine.
65
+ Selection-based local workspace actions use the same bridge. `agent_harness` reports the required model tool for each local action; for actions that depend on the TUI selection, call `run_workspace_action` with the selected local `recordId`. Direct local create editors for memory, notes, personas, skills, and routines can execute from submitted `fields` through `run_workspace_action`; the harness validates required fields, requires `confirm:true` and `explicitUserRequest`, and dispatches through `agent_local_registry`. Note promotion actions can prefill and create memory, personas, skills, routines, or isolated Agent Knowledge URL ingests through the matching first-class model tool. Profile creation schemas include the current runtime starter-template inventory, and routine schedule schemas prefill the selected routine when `recordId` or a `routineId` field matches a local routine.
66
66
 
67
67
  Use first-class Agent tools before falling back to slash-command mirrors. Slash-command execution is for harness parity and scriptable mirrors, not for bypassing Agent product boundaries.
68
68
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-agent",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "private": false,
5
5
  "description": "GoodVibes personal operator assistant TUI with a proactive Agent product brain, isolated Agent Knowledge, local profiles, routines, skills, personas, and explicit build delegation.",
6
6
  "type": "module",
@@ -90,9 +90,26 @@ export interface CommandShellUiOpeners {
90
90
  openModelPickerWithTarget?: (target: import('./model-picker.ts').ModelPickerTarget) => boolean;
91
91
  openProviderModelPickerWithTarget?: (target: import('./model-picker.ts').ModelPickerTarget) => boolean;
92
92
  openProviderPicker?: () => void;
93
+ openReasoningEffortPicker?: () => {
94
+ opened: boolean;
95
+ model?: string;
96
+ levels?: readonly string[];
97
+ reason?: 'unsupported';
98
+ };
93
99
  openContextInspector?: () => void;
94
100
  openBookmarkModal?: () => void;
95
101
  openProcessModal?: () => void;
102
+ openConversationSearch?: (query?: string) => void;
103
+ openPromptHistorySearch?: (query?: string) => void;
104
+ openSlashCommandMode?: (query?: string) => boolean;
105
+ openFilePicker?: (options?: { injectMode?: boolean; query?: string }) => boolean;
106
+ openBlockActions?: () => boolean;
107
+ openLiveTail?: (target?: string) => {
108
+ opened: boolean;
109
+ processId?: string;
110
+ label?: string;
111
+ reason?: 'no_processes' | 'not_found';
112
+ };
96
113
  jumpToBookmark?: (key: string) => void;
97
114
  scrollToLine?: (line: number) => void;
98
115
  openHelpOverlay?: () => void;
@@ -4,6 +4,7 @@ import type { ConversationManager } from '../core/conversation';
4
4
  import type { CommandContext } from '../input/command-registry.ts';
5
5
  import type { InputHandler } from '../input/handler.ts';
6
6
  import type { PanelManager } from '../panels/panel-manager.ts';
7
+ import { EFFORT_DESCRIPTIONS } from '@pellux/goodvibes-sdk/platform/providers';
7
8
  import type { ProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers';
8
9
  import type { MutableRuntimeState } from '@/runtime/index.ts';
9
10
  import type { FeatureFlagManager } from '@/runtime/index.ts';
@@ -229,6 +230,45 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
229
230
  });
230
231
  };
231
232
 
233
+ commandContext.openReasoningEffortPicker = () => {
234
+ const currentModel = providerRegistry.getCurrentModel();
235
+ const validLevels = currentModel.reasoningEffort ?? [];
236
+ if (validLevels.length === 0) {
237
+ return { opened: false, model: currentModel.displayName, levels: [], reason: 'unsupported' };
238
+ }
239
+
240
+ const current = String(runtime.reasoningEffort || configManager.get('provider.reasoningEffort') || 'medium');
241
+ const descriptions: Record<string, string> = {
242
+ ...EFFORT_DESCRIPTIONS,
243
+ medium: 'Balanced speed and quality (default)',
244
+ };
245
+ input.openSelection(
246
+ 'Reasoning Effort',
247
+ validLevels.map((level) => ({
248
+ id: level,
249
+ label: level,
250
+ detail: level === current ? `current - ${descriptions[level] ?? level}` : (descriptions[level] ?? level),
251
+ })),
252
+ { preSelectId: current, allowSearch: false },
253
+ (result) => {
254
+ if (!result) return;
255
+ const level = result.item.id as 'instant' | 'low' | 'medium' | 'high';
256
+ runtime.reasoningEffort = level;
257
+ configManager.set('provider.reasoningEffort', level);
258
+ commandContext.print([
259
+ 'Reasoning effort set',
260
+ ` level ${level}`,
261
+ ].join('\n'));
262
+ render();
263
+ },
264
+ );
265
+ return {
266
+ opened: true,
267
+ model: currentModel.displayName,
268
+ levels: validLevels,
269
+ };
270
+ };
271
+
232
272
  commandContext.openSelection = (title, items, opts, callback) => {
233
273
  input.openSelection(title, items, opts, callback);
234
274
  };
@@ -255,6 +295,105 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
255
295
  render();
256
296
  };
257
297
 
298
+ commandContext.openLiveTail = (target?: string) => {
299
+ input.processModal.refresh();
300
+ const entries = input.processModal.entries;
301
+ if (entries.length === 0) {
302
+ return { opened: false, reason: 'no_processes' };
303
+ }
304
+
305
+ const normalizedTarget = target?.trim().toLowerCase() ?? '';
306
+ const entry = normalizedTarget.length > 0
307
+ ? entries.find((candidate) => (
308
+ candidate.id.toLowerCase() === normalizedTarget
309
+ || candidate.label.toLowerCase().includes(normalizedTarget)
310
+ ))
311
+ : input.processModal.getSelected() ?? entries[0];
312
+
313
+ if (!entry) {
314
+ return { opened: false, reason: 'not_found' };
315
+ }
316
+
317
+ input.modalOpened('liveTail');
318
+ input.processModal.close();
319
+ input.liveTailModal.open(entry);
320
+ render();
321
+ return { opened: true, processId: entry.id, label: entry.label };
322
+ };
323
+
324
+ commandContext.openConversationSearch = (query?: string) => {
325
+ input.searchManager.open();
326
+ const searchQuery = query?.trim() ?? '';
327
+ if (searchQuery.length > 0) {
328
+ input.searchManager.search(searchQuery, input.getHistory());
329
+ }
330
+ render();
331
+ };
332
+
333
+ commandContext.openPromptHistorySearch = (query?: string) => {
334
+ input.historySearch.open(input.prompt);
335
+ const searchQuery = query?.trim() ?? '';
336
+ if (searchQuery.length > 0) {
337
+ input.historySearch.search(searchQuery);
338
+ }
339
+ render();
340
+ };
341
+
342
+ commandContext.openSlashCommandMode = (query?: string): boolean => {
343
+ const normalizedQuery = query?.trim().replace(/^\/+/, '') ?? '';
344
+ if (input.prompt.length > 0 && !input.prompt.startsWith('/')) {
345
+ return false;
346
+ }
347
+
348
+ input.modalOpened('command');
349
+ input.commandMode = true;
350
+ if (input.prompt.length === 0 || !input.prompt.startsWith('/') || normalizedQuery.length > 0) {
351
+ input.saveUndoState();
352
+ input.prompt = `/${normalizedQuery}`;
353
+ input.cursorPos = input.prompt.length;
354
+ }
355
+
356
+ const commandQuery = input.prompt.startsWith('/') ? input.prompt.slice(1) : '';
357
+ const hasArgs = commandQuery.includes(' ');
358
+ if (hasArgs) input.autocomplete?.reset();
359
+ else input.autocomplete?.update(commandQuery);
360
+ input.ensureInputCursorVisible();
361
+ input.syncFeedContextMutableFields();
362
+ render();
363
+ return true;
364
+ };
365
+
366
+ commandContext.openFilePicker = (options): boolean => {
367
+ const injectMode = Boolean(options?.injectMode);
368
+ const query = options?.query?.trim() ?? '';
369
+ const marker = `${injectMode ? '!@' : '@'}${query}`;
370
+ const insertPos = input.cursorPos;
371
+ input.saveUndoState();
372
+ input.prompt = input.prompt.slice(0, insertPos) + marker + input.prompt.slice(insertPos);
373
+ input.cursorPos = insertPos + marker.length;
374
+ input.modalOpened('filePicker');
375
+ input.filePicker.open(insertPos, injectMode);
376
+ if (query.length > 0) input.filePicker.setQuery(query);
377
+ input.ensureInputCursorVisible();
378
+ input.syncFeedContextMutableFields();
379
+ render();
380
+ return true;
381
+ };
382
+
383
+ commandContext.openBlockActions = (): boolean => {
384
+ if (input.prompt.trim().length > 0 || input.commandMode) {
385
+ return false;
386
+ }
387
+ const nearest = conversation.findNearestBlock(input.getScrollTop());
388
+ if (!nearest) {
389
+ return false;
390
+ }
391
+ input.modalOpened('blockActions');
392
+ input.blockActionsMenu.open(nearest);
393
+ render();
394
+ return true;
395
+ };
396
+
258
397
  commandContext.openHelpOverlay = () => {
259
398
  if (!input.helpOverlayActive) input.modalOpened('help');
260
399
  input.helpOverlayActive = !input.helpOverlayActive;
@@ -172,7 +172,7 @@ function editorForOperation(context: CommandContext, operation: AgentWorkspaceLo
172
172
  function localRegistryArgsFromEditor(editor: AgentWorkspaceLocalEditor, fields: Readonly<Record<string, string>>, id?: string): Record<string, unknown> {
173
173
  const read = fieldReader(editor, fields);
174
174
  if (editor.kind === 'memory') return { domain: 'memory', action: editor.mode === 'update' ? 'update' : 'create', id, cls: read('cls'), scope: read('scope'), summary: read('summary'), detail: read('detail'), tags: splitList(read('tags')), confidence: read('confidence'), provenance: editor.recordId ? 'agent-harness-local-operation' : 'agent-harness-note-promotion' };
175
- if (editor.kind === 'note') return { domain: 'note', action: 'update', id, title: read('title'), body: read('body'), sourceUrl: read('sourceUrl'), tags: splitList(read('tags')), provenance: 'agent-harness-local-operation' };
175
+ if (editor.kind === 'note') return { domain: 'note', action: editor.mode === 'update' ? 'update' : 'create', id, title: read('title'), body: read('body'), sourceUrl: read('sourceUrl'), tags: splitList(read('tags')), provenance: editor.recordId ? 'agent-harness-local-operation' : 'agent-harness-note-promotion' };
176
176
  if (editor.kind === 'persona') return { domain: 'persona', action: editor.mode === 'update' ? 'update' : 'create', id, name: read('name'), description: read('description'), body: read('body'), tags: splitList(read('tags')), triggers: splitList(read('triggers')), activate: read('activate'), provenance: editor.recordId ? 'agent-harness-local-operation' : 'agent-harness-note-promotion' };
177
177
  if (editor.kind === 'skill') return { domain: 'skill', action: editor.mode === 'update' ? 'update' : 'create', id, name: read('name'), description: read('description'), procedure: read('procedure'), tags: splitList(read('tags')), triggers: splitList(read('triggers')), requiresEnv: splitList(read('requiresEnv')), requiresCommands: splitList(read('requiresCommands')), enabled: isAffirmative(read('enabled')), provenance: editor.recordId ? 'agent-harness-local-operation' : 'agent-harness-note-promotion' };
178
178
  return { domain: 'routine', action: editor.mode === 'update' ? 'update' : 'create', id, name: read('name'), description: read('description'), steps: read('steps'), tags: splitList(read('tags')), triggers: splitList(read('triggers')), requiresEnv: splitList(read('requiresEnv')), requiresCommands: splitList(read('requiresCommands')), enabled: isAffirmative(read('enabled')), provenance: editor.recordId ? 'agent-harness-local-operation' : 'agent-harness-note-promotion' };
@@ -206,6 +206,41 @@ export function describeLocalWorkspaceModelExecution(action: AgentWorkspaceActio
206
206
  return { tool: 'agent_local_registry', selectedRecordDomain: 'note', action: 'create', requiresRecordId: true };
207
207
  }
208
208
 
209
+ export async function runLocalWorkspaceEditorAction(
210
+ deps: AgentHarnessLocalOperationDeps,
211
+ editor: AgentWorkspaceLocalEditor,
212
+ args: AgentHarnessLocalOperationArgs,
213
+ ): Promise<HarnessResult> {
214
+ const fields = readFieldMap(args.fields);
215
+ const missing = missingRequiredFields(editor, fields);
216
+ if (missing.length > 0) {
217
+ return output({
218
+ status: 'missing_required_fields',
219
+ missing,
220
+ editor: describeEditor(editor),
221
+ });
222
+ }
223
+ const confirmationError = requireConfirmed(args, 'Workspace local registry editor action');
224
+ if (confirmationError) return error(confirmationError);
225
+ if (
226
+ editor.kind !== 'memory'
227
+ && editor.kind !== 'note'
228
+ && editor.kind !== 'persona'
229
+ && editor.kind !== 'skill'
230
+ && editor.kind !== 'routine'
231
+ ) {
232
+ return output({
233
+ status: 'model_tool_required',
234
+ editor: describeEditor(editor),
235
+ });
236
+ }
237
+ return executeTool(
238
+ deps.toolRegistry,
239
+ 'agent_local_registry',
240
+ localRegistryArgsFromEditor(editor, fields, editor.recordId),
241
+ );
242
+ }
243
+
209
244
  export async function runLocalWorkspaceAction(
210
245
  deps: AgentHarnessLocalOperationDeps,
211
246
  action: AgentWorkspaceAction,
@@ -21,7 +21,7 @@ import { blockedHarnessCliCommandTokens, describeHarnessCliCommand, listHarnessC
21
21
  import { describeHarnessKeybinding, listHarnessKeybindings, listHarnessShortcuts, resetHarnessKeybinding, setHarnessKeybinding, totalHarnessKeybindings, totalHarnessShortcuts } from './agent-harness-keybinding-metadata.ts';
22
22
  import { describeHarnessPanel, listHarnessPanels, openHarnessPanel, totalHarnessPanels } from './agent-harness-panel-metadata.ts';
23
23
  import { connectedHostStatusSummary } from './agent-harness-connected-host-status.ts';
24
- import { describeLocalWorkspaceModelExecution, runLocalWorkspaceAction } from './agent-harness-local-operations.ts';
24
+ import { describeLocalWorkspaceModelExecution, runLocalWorkspaceAction, runLocalWorkspaceEditorAction } from './agent-harness-local-operations.ts';
25
25
  import { listHarnessModelTools } from './agent-harness-model-tool-catalog.ts';
26
26
  import { AGENT_HARNESS_MODES, AGENT_HARNESS_PARAMETER_PROPERTIES } from './agent-harness-tool-schema.ts';
27
27
  import { describeHarnessUiSurface, listHarnessUiSurfaces, openHarnessUiSurface, totalHarnessUiSurfaces } from './agent-harness-ui-surface-metadata.ts';
@@ -238,11 +238,11 @@ function describeWorkspaceAction(
238
238
  }
239
239
 
240
240
  function localEditorModelExecution(editorKind: AgentWorkspaceEditorKind): string {
241
- if (editorKind === 'memory') return 'Use agent_local_registry with domain:"memory" for create/update/review/stale/delete.';
242
- if (editorKind === 'note') return 'Use agent_local_registry with domain:"note" for create/update/review/stale/delete.';
243
- if (editorKind === 'persona') return 'Use agent_local_registry with domain:"persona" for create/update/use/clear_active/review/stale/delete.';
244
- if (editorKind === 'skill') return 'Use agent_local_registry with domain:"skill" for create/update/enable/disable/review/stale/delete.';
245
- if (editorKind === 'routine') return 'Use agent_local_registry with domain:"routine" for create/update/enable/disable/start/review/stale/delete.';
241
+ if (editorKind === 'memory') return 'run_workspace_action can execute this editor from fields through agent_local_registry domain:"memory"; agent_local_registry also supports list/search/get/review/stale/delete.';
242
+ if (editorKind === 'note') return 'run_workspace_action can execute this editor from fields through agent_local_registry domain:"note"; agent_local_registry also supports list/search/get/review/stale/delete.';
243
+ if (editorKind === 'persona') return 'run_workspace_action can execute this editor from fields through agent_local_registry domain:"persona"; agent_local_registry also supports list/search/get/use/clear_active/review/stale/delete.';
244
+ if (editorKind === 'skill') return 'run_workspace_action can execute this editor from fields through agent_local_registry domain:"skill"; agent_local_registry also supports list/search/get/enable/disable/review/stale/delete.';
245
+ if (editorKind === 'routine') return 'run_workspace_action can execute this editor from fields through agent_local_registry domain:"routine"; agent_local_registry also supports list/search/get/enable/disable/start/review/stale/delete.';
246
246
  if (editorKind === 'learned-behavior') return 'run_workspace_action can create the learned behavior from fields.';
247
247
  if (editorKind === 'profile') return 'run_workspace_action dispatches the matching /agent-profile create command.';
248
248
  return 'Use the command field, editor schema, or a first-class Agent model tool when available.';
@@ -409,6 +409,15 @@ async function runWorkspaceEditorAction(
409
409
  }
410
410
 
411
411
  if (!isAgentWorkspaceCommandEditorKind(editor.kind)) {
412
+ if (
413
+ editor.kind === 'memory'
414
+ || editor.kind === 'note'
415
+ || editor.kind === 'persona'
416
+ || editor.kind === 'skill'
417
+ || editor.kind === 'routine'
418
+ ) {
419
+ return runLocalWorkspaceEditorAction(deps, editor, args);
420
+ }
412
421
  return output({
413
422
  status: 'model_tool_required',
414
423
  action: action.id,
@@ -82,6 +82,19 @@ function optionalPane(args: AgentHarnessUiSurfaceArgs): 'top' | 'bottom' | undef
82
82
  return pane === 'top' || pane === 'bottom' ? pane : undefined;
83
83
  }
84
84
 
85
+ function surfaceInputText(args: AgentHarnessUiSurfaceArgs): string | undefined {
86
+ return readString(args.prefix || args.key || (args.surfaceId ? args.query : undefined) || args.target) || undefined;
87
+ }
88
+
89
+ function filePickerOptions(args: AgentHarnessUiSurfaceArgs): { injectMode: boolean; query?: string } {
90
+ const target = readString(args.target).toLowerCase();
91
+ const injectMode = target === 'inject' || target === 'inject-mode' || target === '!@';
92
+ return {
93
+ injectMode,
94
+ query: readString(args.prefix || args.key || (args.surfaceId ? args.query : undefined)) || undefined,
95
+ };
96
+ }
97
+
85
98
  function openAgentWorkspaceCategory(
86
99
  context: CommandContext,
87
100
  surface: UiSurfaceDefinition,
@@ -299,6 +312,34 @@ const UI_SURFACES: readonly UiSurfaceDefinition[] = [
299
312
  return opened(surface, { target: 'main' });
300
313
  },
301
314
  },
315
+ {
316
+ id: 'reasoning-effort-picker',
317
+ label: 'Reasoning Effort Picker',
318
+ kind: 'picker',
319
+ summary: 'Interactive reasoning-effort selector for the current main chat model when that model exposes effort levels.',
320
+ command: '/effort',
321
+ preferredModelRoute: 'Use settings/get_setting/set_setting for provider.reasoningEffort when a concrete level is known, or run_workspace_action setup-effort with confirmation.',
322
+ available: (context) => typeof context.openReasoningEffortPicker === 'function',
323
+ open: (context) => {
324
+ const surface = findSurfaceById('reasoning-effort-picker')!;
325
+ if (!context.openReasoningEffortPicker) return routeUnavailable(surface);
326
+ const result = context.openReasoningEffortPicker();
327
+ return result.opened
328
+ ? opened(surface, {
329
+ model: result.model,
330
+ levels: result.levels ?? [],
331
+ })
332
+ : {
333
+ status: 'not_opened',
334
+ surface: surface.id,
335
+ kind: surface.kind,
336
+ model: result.model,
337
+ levels: result.levels ?? [],
338
+ reason: result.reason ?? 'unsupported',
339
+ note: 'The current model does not expose configurable reasoning effort levels.',
340
+ };
341
+ },
342
+ },
302
343
  {
303
344
  id: 'tts-provider-picker',
304
345
  label: 'TTS Provider Picker',
@@ -408,6 +449,160 @@ const UI_SURFACES: readonly UiSurfaceDefinition[] = [
408
449
  return opened(surface);
409
450
  },
410
451
  },
452
+ {
453
+ id: 'live-tail',
454
+ label: 'Live Process Output',
455
+ kind: 'modal',
456
+ summary: 'Visible live-output tail for a running process, opened by the same shell route as Enter from the runtime activity monitor.',
457
+ command: 'F2, Enter',
458
+ preferredModelRoute: 'Use this only for visible supervision of a running process output stream; use first-class model tools or confirmed commands for actual operations.',
459
+ parameters: ['target', 'query', 'prefix', 'key'],
460
+ available: (context) => typeof context.openLiveTail === 'function',
461
+ open: (context, args) => {
462
+ const surface = findSurfaceById('live-tail')!;
463
+ if (!context.openLiveTail) return routeUnavailable(surface);
464
+ const target = surfaceInputText(args);
465
+ const result = context.openLiveTail(target);
466
+ return result.opened
467
+ ? opened(surface, {
468
+ target: target ?? 'selected',
469
+ processId: result.processId,
470
+ label: result.label,
471
+ })
472
+ : {
473
+ status: 'not_opened',
474
+ surface: surface.id,
475
+ kind: surface.kind,
476
+ target: target ?? 'selected',
477
+ reason: result.reason ?? 'not_found',
478
+ note: result.reason === 'no_processes'
479
+ ? 'There are no running shell processes to tail.'
480
+ : 'No running shell process matched the requested target.',
481
+ };
482
+ },
483
+ },
484
+ {
485
+ id: 'conversation-search',
486
+ label: 'Conversation Search',
487
+ kind: 'overlay',
488
+ summary: 'Visible transcript search overlay opened by the same shell route as Ctrl+F.',
489
+ command: 'Ctrl+F',
490
+ preferredModelRoute: 'Use conversation/session/content modes for model-readable inspection; use this surface for visible transcript search navigation.',
491
+ parameters: ['query', 'prefix', 'key'],
492
+ available: (context) => typeof context.openConversationSearch === 'function',
493
+ open: (context, args) => {
494
+ const surface = findSurfaceById('conversation-search')!;
495
+ if (!context.openConversationSearch) return routeUnavailable(surface);
496
+ const query = surfaceInputText(args);
497
+ context.openConversationSearch(query);
498
+ return opened(surface, { query: query ?? '' });
499
+ },
500
+ },
501
+ {
502
+ id: 'prompt-history-search',
503
+ label: 'Prompt History Search',
504
+ kind: 'overlay',
505
+ summary: 'Visible reverse prompt-history search opened by the same shell route as Ctrl+R.',
506
+ command: 'Ctrl+R',
507
+ preferredModelRoute: 'Use this only for visible prompt recall; accepting a result remains an explicit interactive shell action.',
508
+ parameters: ['query', 'prefix', 'key'],
509
+ available: (context) => typeof context.openPromptHistorySearch === 'function',
510
+ open: (context, args) => {
511
+ const surface = findSurfaceById('prompt-history-search')!;
512
+ if (!context.openPromptHistorySearch) return routeUnavailable(surface);
513
+ const query = surfaceInputText(args);
514
+ context.openPromptHistorySearch(query);
515
+ return opened(surface, { query: query ?? '' });
516
+ },
517
+ },
518
+ {
519
+ id: 'slash-command-mode',
520
+ label: 'Slash Command Mode',
521
+ kind: 'overlay',
522
+ summary: 'Slash-command autocomplete route opened by the same shell path as typing / in an empty prompt.',
523
+ command: '/',
524
+ preferredModelRoute: 'Use commands/command for model-readable command discovery and run_command for confirmed command execution.',
525
+ parameters: ['query', 'prefix', 'key'],
526
+ available: (context) => typeof context.openSlashCommandMode === 'function',
527
+ open: (context, args) => {
528
+ const surface = findSurfaceById('slash-command-mode')!;
529
+ if (!context.openSlashCommandMode) return routeUnavailable(surface);
530
+ const query = surfaceInputText(args);
531
+ const openedCommandMode = context.openSlashCommandMode(query);
532
+ return openedCommandMode
533
+ ? opened(surface, { query: query ?? '' })
534
+ : {
535
+ status: 'not_opened',
536
+ surface: surface.id,
537
+ kind: surface.kind,
538
+ query: query ?? '',
539
+ note: 'The current prompt contains a non-command draft, so the shell opener refused to replace it.',
540
+ };
541
+ },
542
+ },
543
+ {
544
+ id: 'command-browser',
545
+ label: 'Command Browser',
546
+ kind: 'picker',
547
+ summary: 'Registry-driven searchable slash-command browser opened by /commands and /help.',
548
+ command: '/commands',
549
+ preferredModelRoute: 'Use commands/command for model-readable slash-command discovery and run_command for confirmed command execution.',
550
+ available: (context) => typeof context.executeCommand === 'function',
551
+ open: async (context) => {
552
+ const surface = findSurfaceById('command-browser')!;
553
+ if (!context.executeCommand) return routeUnavailable(surface);
554
+ const handled = await context.executeCommand('commands', []);
555
+ return handled
556
+ ? opened(surface, { command: '/commands' })
557
+ : {
558
+ status: 'not_opened',
559
+ surface: surface.id,
560
+ kind: surface.kind,
561
+ note: 'The slash-command registry did not handle /commands in the current runtime.',
562
+ };
563
+ },
564
+ },
565
+ {
566
+ id: 'file-picker',
567
+ label: 'File Picker',
568
+ kind: 'picker',
569
+ summary: 'Visible project file picker opened by the same prompt route as @ and !@ references.',
570
+ command: '@',
571
+ preferredModelRoute: 'Use first-class file, workspace, or artifact tools for model operation; use this for visible file reference navigation.',
572
+ parameters: ['target=reference|inject', 'query', 'prefix', 'key'],
573
+ available: (context) => typeof context.openFilePicker === 'function',
574
+ open: (context, args) => {
575
+ const surface = findSurfaceById('file-picker')!;
576
+ if (!context.openFilePicker) return routeUnavailable(surface);
577
+ const options = filePickerOptions(args);
578
+ const openedFilePicker = context.openFilePicker(options);
579
+ return openedFilePicker
580
+ ? opened(surface, { mode: options.injectMode ? 'inject' : 'reference', query: options.query ?? '' })
581
+ : routeUnavailable(surface);
582
+ },
583
+ },
584
+ {
585
+ id: 'block-actions',
586
+ label: 'Block Actions',
587
+ kind: 'overlay',
588
+ summary: 'Visible nearest-block action menu opened by the same shell route as pressing Enter on an empty prompt near transcript content.',
589
+ command: 'Enter on empty prompt',
590
+ preferredModelRoute: 'Use conversation/session/content modes or confirmed slash-command mirrors for concrete block operations; use this surface for visible block-action navigation.',
591
+ available: (context) => typeof context.openBlockActions === 'function',
592
+ open: (context) => {
593
+ const surface = findSurfaceById('block-actions')!;
594
+ if (!context.openBlockActions) return routeUnavailable(surface);
595
+ const openedBlockActions = context.openBlockActions();
596
+ return openedBlockActions
597
+ ? opened(surface)
598
+ : {
599
+ status: 'not_opened',
600
+ surface: surface.id,
601
+ kind: surface.kind,
602
+ note: 'The shell opener requires an empty prompt and a nearby rendered conversation block.',
603
+ };
604
+ },
605
+ },
411
606
  {
412
607
  id: 'help-overlay',
413
608
  label: 'Help Overlay',
package/src/version.ts CHANGED
@@ -6,7 +6,7 @@ import { join } from 'node:path';
6
6
  // The prebuild script updates the fallback value before compilation.
7
7
  // Uses import.meta.dir (Bun) to locate package.json relative to this file,
8
8
  // which is correct regardless of the process working directory.
9
- let _version = '1.0.10';
9
+ let _version = '1.0.12';
10
10
  let _sdkVersion = '0.33.35';
11
11
  try {
12
12
  const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8')) as {