@moxxy/cli 0.13.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -1,10 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { z as z$1, createMutex, defineTunnelProvider, isCliTunnelAvailable, definePlugin, defineProvider, defineTool, MoxxyError, writeFileAtomic, asTurnId, defineMode, asPluginId, defineCommand, defineChannel, spawnCliTunnel, defineWorkflowExecutor, toFriendlyError, estimateTextTokens, classifyHttpStatus, createStuckLoopDetector, runCompactionIfNeeded, runElisionIfNeeded, collectProviderStream, usageEventFields, isContextOverflowError, emitRequestsAndDetectStuck, executeToolUses, buildSystemPromptWithSkills, projectMessages, defineCompactor, defineCacheStrategy, denyByDefaultResolver, createAllowListResolver, moxxyPath, moxxyHome, zodToJsonSchema, fileDiffSummary, runSingleShotTurn, bearerTokenMatches, resolveChannelToken, rotateChannelToken, defineSurface, estimateContextTokens as estimateContextTokens$1, readRequestBody, isFileDiffDisplay, MOXXY_WS_SUBPROTOCOL, renderFrontmatter, defineEmbedder, migrateModeName, bearerGuard, tokenFromWsProtocolHeader, skillFrontmatterSchema, asSkillId, getInstallHint, parseFrontmatterFile, createDeferredPermissionResolver, writeFileAtomicSync, encodeLoginPrompt, defineTranscriber, summarizeTokensByModel, countNodes, moxxyPackageSchema, classifyNetworkError, addModelTotals, createJsonFileStore, ISOLATION_RANK, fileDiffVerb, parseFrontmatter, createCallbackResolver, autoAllowResolver, asSessionId, asToolCallId, defineViewRenderer, DEFAULT_VIEW_TAGS, assertNever, isSafeViewUrl, evaluateToolRule, summarizeSessionTokensFromEvents, toDiffRows, diffGutterNo, computeElisionState, toolResultStubbed, toolResultStub, toolResultBytes, conversationalStubbed, conversationalStub, asEventId } from '@moxxy/sdk';
3
+ import { z as z$1, createMutex, defineTunnelProvider, definePlugin, defineProvider, defineTool, MoxxyError, asTurnId, defineMode, asPluginId, defineCommand, defineChannel, defineWorkflowExecutor, toFriendlyError, estimateTextTokens, classifyHttpStatus, createStuckLoopDetector, runCompactionIfNeeded, runElisionIfNeeded, collectProviderStream, usageEventFields, isContextOverflowError, emitRequestsAndDetectStuck, executeToolUses, buildSystemPromptWithSkills, projectMessages, defineCompactor, defineCacheStrategy, denyByDefaultResolver, createAllowListResolver, zodToJsonSchema, fileDiffSummary, runSingleShotTurn, defineSurface, runManualCompaction, isFileDiffDisplay, renderFrontmatter, defineEmbedder, migrateModeName, skillFrontmatterSchema, asSkillId, getInstallHint, parseFrontmatterFile, createDeferredPermissionResolver, encodeLoginPrompt, defineTranscriber, summarizeTokensByModel, countNodes, moxxyPackageSchema, classifyNetworkError, addModelTotals, createJsonFileStore, ISOLATION_RANK, MOXXY_PCM16_24KHZ_MIME, fileDiffVerb, parseFrontmatter, createCallbackResolver, autoAllowResolver, asSessionId, asToolCallId, defineViewRenderer, DEFAULT_VIEW_TAGS, isSafeViewUrl, evaluateToolRule, summarizeSessionTokensFromEvents, toDiffRows, diffGutterNo, computeElisionState, toolResultStubbed, toolResultStub, toolResultBytes, conversationalStubbed, conversationalStub, asEventId } from '@moxxy/sdk';
4
4
  import * as fs32 from 'fs';
5
- import fs32__default, { existsSync, promises, ReadStream, mkdirSync, statSync, readdirSync, writeFileSync, readFileSync, chmodSync, unlinkSync, watch, createReadStream } from 'fs';
5
+ import fs32__default, { existsSync, promises, ReadStream, mkdirSync, statSync, readdirSync, writeFileSync, readFileSync, unlinkSync, chmodSync, watch, createReadStream } from 'fs';
6
6
  import * as path3 from 'path';
7
7
  import path3__default, { join, dirname, basename } from 'path';
8
+ import { isCliTunnelAvailable, writeFileAtomic, spawnCliTunnel, moxxyPath, moxxyHome, bearerTokenMatches, resolveChannelToken, rotateChannelToken, readRequestBody, MOXXY_WS_SUBPROTOCOL, bearerGuard, tokenFromWsProtocolHeader, writeFileAtomicSync } from '@moxxy/sdk/server';
8
9
  import { z } from 'zod';
9
10
  import * as os5 from 'os';
10
11
  import os5__default, { homedir, tmpdir, userInfo } from 'os';
@@ -2966,6 +2967,8 @@ var init_workflow_executors = __esm({
2966
2967
  };
2967
2968
  }
2968
2969
  });
2970
+
2971
+ // ../core/dist/requirements.js
2969
2972
  function issue(requirement, code, message) {
2970
2973
  return {
2971
2974
  requirement,
@@ -2984,9 +2987,10 @@ function activeModeName(modes) {
2984
2987
  return null;
2985
2988
  }
2986
2989
  }
2987
- var RequirementRegistry;
2990
+ var ALWAYS_ACTIVE, RequirementRegistry, TARGET_DESCRIPTORS;
2988
2991
  var init_requirements = __esm({
2989
2992
  "../core/dist/requirements.js"() {
2993
+ ALWAYS_ACTIVE = () => true;
2990
2994
  RequirementRegistry = class {
2991
2995
  opts;
2992
2996
  runtime = /* @__PURE__ */ new Map();
@@ -3049,52 +3053,54 @@ var init_requirements = __esm({
3049
3053
  return null;
3050
3054
  }
3051
3055
  targetInfo(kind3, name) {
3052
- switch (kind3) {
3053
- case "plugin": {
3054
- const plugin5 = this.plugins.get(name);
3055
- return plugin5 ? { kind: kind3, name, version: plugin5.version, active: true } : null;
3056
- }
3057
- case "provider": {
3058
- const def = this.opts.providers.list().find((p3) => p3.name === name);
3059
- return def ? { kind: kind3, name, active: this.opts.providers.getActiveName() === name } : null;
3060
- }
3061
- case "tool": {
3062
- const def = this.opts.tools.get(name);
3063
- return def ? { kind: kind3, name, active: true } : null;
3064
- }
3065
- case "transcriber": {
3066
- const def = this.opts.transcribers.list().find((t2) => t2.name === name);
3067
- return def ? { kind: kind3, name, active: this.opts.transcribers.getActiveName() === name } : null;
3068
- }
3069
- case "synthesizer": {
3070
- const def = this.opts.synthesizers.list().find((s2) => s2.name === name);
3071
- return def ? { kind: kind3, name, active: this.opts.synthesizers.getActiveName() === name } : null;
3072
- }
3073
- case "mode": {
3074
- const def = this.opts.modes.list().find((m3) => m3.name === name);
3075
- return def ? { kind: kind3, name, active: activeModeName(this.opts.modes) === name } : null;
3076
- }
3077
- case "compactor": {
3078
- const def = this.opts.compactors.list().find((c2) => c2.name === name);
3079
- return def ? { kind: kind3, name, active: this.opts.compactors.getActive()?.name === name } : null;
3080
- }
3081
- case "channel": {
3082
- const def = this.opts.channels.get(name);
3083
- return def ? { kind: kind3, name, active: true } : null;
3084
- }
3085
- case "agent": {
3086
- const def = this.opts.agents.get(name);
3087
- return def ? { kind: kind3, name, active: true } : null;
3088
- }
3089
- case "command": {
3090
- const def = this.opts.commands.get(name);
3091
- return def ? { kind: kind3, name, active: true } : null;
3092
- }
3093
- case "runtime":
3094
- return null;
3095
- default:
3096
- return assertNever(kind3);
3056
+ if (kind3 === "plugin") {
3057
+ const plugin5 = this.plugins.get(name);
3058
+ return plugin5 ? { kind: kind3, name, version: plugin5.version, active: true } : null;
3097
3059
  }
3060
+ if (kind3 === "runtime")
3061
+ return null;
3062
+ const descriptor = TARGET_DESCRIPTORS[kind3];
3063
+ if (!descriptor.present(this.opts, name))
3064
+ return null;
3065
+ return { kind: kind3, name, active: descriptor.active(this.opts, name) };
3066
+ }
3067
+ };
3068
+ TARGET_DESCRIPTORS = {
3069
+ provider: {
3070
+ present: (opts, name) => opts.providers.list().some((p3) => p3.name === name),
3071
+ active: (opts, name) => opts.providers.getActiveName() === name
3072
+ },
3073
+ tool: {
3074
+ present: (opts, name) => opts.tools.get(name) !== void 0,
3075
+ active: ALWAYS_ACTIVE
3076
+ },
3077
+ transcriber: {
3078
+ present: (opts, name) => opts.transcribers.list().some((t2) => t2.name === name),
3079
+ active: (opts, name) => opts.transcribers.getActiveName() === name
3080
+ },
3081
+ synthesizer: {
3082
+ present: (opts, name) => opts.synthesizers.list().some((s2) => s2.name === name),
3083
+ active: (opts, name) => opts.synthesizers.getActiveName() === name
3084
+ },
3085
+ mode: {
3086
+ present: (opts, name) => opts.modes.list().some((m3) => m3.name === name),
3087
+ active: (opts, name) => activeModeName(opts.modes) === name
3088
+ },
3089
+ compactor: {
3090
+ present: (opts, name) => opts.compactors.list().some((c2) => c2.name === name),
3091
+ active: (opts, name) => opts.compactors.getActive()?.name === name
3092
+ },
3093
+ channel: {
3094
+ present: (opts, name) => opts.channels.get(name) !== void 0,
3095
+ active: ALWAYS_ACTIVE
3096
+ },
3097
+ agent: {
3098
+ present: (opts, name) => opts.agents.get(name) !== void 0,
3099
+ active: ALWAYS_ACTIVE
3100
+ },
3101
+ command: {
3102
+ present: (opts, name) => opts.commands.get(name) !== void 0,
3103
+ active: ALWAYS_ACTIVE
3098
3104
  }
3099
3105
  };
3100
3106
  }
@@ -85505,12 +85511,9 @@ function handleCollabEvent(e3, ref, root) {
85505
85511
  root.push(block2);
85506
85512
  return;
85507
85513
  }
85508
- let block = ref.current;
85509
- if (!block) {
85510
- block = newCollabBlock(e3.id, atMs);
85511
- ref.current = block;
85512
- root.push(block);
85513
- }
85514
+ const block = ref.current;
85515
+ if (!block)
85516
+ return;
85514
85517
  switch (e3.subtype) {
85515
85518
  case "collab_fallback_sequential":
85516
85519
  block.fallbackReason = String(p3.reason ?? "");
@@ -87762,7 +87765,7 @@ function useVoiceInput(opts) {
87762
87765
  const pcm = await recording.stop();
87763
87766
  const transcriber = resolveCodexTranscriber(session);
87764
87767
  const result = await transcriber.transcribe(pcm, {
87765
- mimeType: MOXXY_PCM16_24KHZ_MIME2
87768
+ mimeType: MOXXY_PCM16_24KHZ_MIME
87766
87769
  });
87767
87770
  const text = result.text.trim();
87768
87771
  if (!text) {
@@ -87869,13 +87872,12 @@ function formatVoiceError(err) {
87869
87872
  return `voice: ${message}`;
87870
87873
  return `voice: ${message || "failed"}`;
87871
87874
  }
87872
- var import_react51, CODEX_TRANSCRIBER_NAME, MOXXY_PCM16_24KHZ_MIME2;
87875
+ var import_react51, CODEX_TRANSCRIBER_NAME;
87873
87876
  var init_use_voice_input = __esm({
87874
87877
  "../plugin-cli/dist/session/use-voice-input.js"() {
87875
87878
  import_react51 = __toESM(require_react());
87876
87879
  init_voice_input();
87877
87880
  CODEX_TRANSCRIBER_NAME = "openai-codex-transcribe";
87878
- MOXXY_PCM16_24KHZ_MIME2 = "audio/x-moxxy-pcm16-24khz";
87879
87881
  }
87880
87882
  });
87881
87883
 
@@ -88165,13 +88167,17 @@ function startCollab(deps, arg) {
88165
88167
  deps.submitPrompt(objective);
88166
88168
  }
88167
88169
  function openModePicker(deps, arg = "") {
88168
- const modes = deps.session.modes.list();
88170
+ const modes = deps.session.modes.list().filter((m3) => !COLLAB_HIDDEN_MODES.has(m3.name));
88169
88171
  if (modes.length === 0) {
88170
88172
  deps.setSystemNotice("no modes registered");
88171
88173
  return;
88172
88174
  }
88173
88175
  const target = arg.trim().toLowerCase();
88174
88176
  if (target) {
88177
+ if (COLLAB_HIDDEN_MODES.has(target)) {
88178
+ deps.setSystemNotice("Use /collab <goal> to run a collaborative team (only one runs at a time).");
88179
+ return;
88180
+ }
88175
88181
  const match = modes.find((m3) => m3.name.toLowerCase() === target);
88176
88182
  if (match) {
88177
88183
  try {
@@ -88197,7 +88203,7 @@ function openModePicker(deps, arg = "") {
88197
88203
  function truncate5(s2, n2) {
88198
88204
  return s2.length <= n2 ? s2 : s2.slice(0, n2 - 1) + "\u2026";
88199
88205
  }
88200
- var PLUGIN_KIND_TAB, OTHER_TAB, PLUGIN_TAB_ORDER;
88206
+ var PLUGIN_KIND_TAB, OTHER_TAB, PLUGIN_TAB_ORDER, COLLAB_HIDDEN_MODES;
88201
88207
  var init_run_slash = __esm({
88202
88208
  async "../plugin-cli/dist/session/run-slash.js"() {
88203
88209
  init_dist();
@@ -88212,6 +88218,11 @@ var init_run_slash = __esm({
88212
88218
  };
88213
88219
  OTHER_TAB = { id: "others", label: "Others" };
88214
88220
  PLUGIN_TAB_ORDER = ["providers", "modes", "channels", "tools", "others"];
88221
+ COLLAB_HIDDEN_MODES = /* @__PURE__ */ new Set([
88222
+ "collaborative",
88223
+ "collab-architect",
88224
+ "collab-peer"
88225
+ ]);
88215
88226
  }
88216
88227
  });
88217
88228
 
@@ -128479,7 +128490,7 @@ var ChatCompletionStream = class _ChatCompletionStream extends AbstractChatCompl
128479
128490
  arguments_delta: toolCallDelta.function?.arguments ?? ""
128480
128491
  });
128481
128492
  } else {
128482
- assertNever3(toolCallSnapshot?.type);
128493
+ assertNever2(toolCallSnapshot?.type);
128483
128494
  }
128484
128495
  }
128485
128496
  }
@@ -128504,7 +128515,7 @@ var ChatCompletionStream = class _ChatCompletionStream extends AbstractChatCompl
128504
128515
  parsed_arguments: isAutoParsableTool(inputTool) ? inputTool.$parseRaw(toolCallSnapshot.function.arguments) : inputTool?.function.strict ? JSON.parse(toolCallSnapshot.function.arguments) : null
128505
128516
  });
128506
128517
  } else {
128507
- assertNever3(toolCallSnapshot.type);
128518
+ assertNever2(toolCallSnapshot.type);
128508
128519
  }
128509
128520
  }, _ChatCompletionStream_emitContentDoneEvents = function _ChatCompletionStream_emitContentDoneEvents2(choiceSnapshot) {
128510
128521
  const state = __classPrivateFieldGet15(this, _ChatCompletionStream_instances, "m", _ChatCompletionStream_getChoiceEventState).call(this, choiceSnapshot);
@@ -128784,7 +128795,7 @@ ${str(snapshot)}`);
128784
128795
  function str(x4) {
128785
128796
  return JSON.stringify(x4);
128786
128797
  }
128787
- function assertNever3(_x) {
128798
+ function assertNever2(_x) {
128788
128799
  }
128789
128800
 
128790
128801
  // ../../node_modules/.pnpm/openai@4.104.0_encoding@0.1.13_ws@8.20.0_zod@3.25.76/node_modules/openai/lib/ChatCompletionStreamingRunner.mjs
@@ -133481,9 +133492,6 @@ var localPlugin = definePlugin({
133481
133492
  version: "0.0.0",
133482
133493
  providers: [localProviderDef]
133483
133494
  });
133484
-
133485
- // ../plugin-stt-whisper/dist/audio.js
133486
- var MOXXY_PCM16_24KHZ_MIME = "audio/x-moxxy-pcm16-24khz";
133487
133495
  var WHISPER_FILENAME_BY_MIME = {
133488
133496
  "audio/ogg": "audio.ogg",
133489
133497
  "audio/opus": "audio.opus",
@@ -136258,7 +136266,7 @@ async function reclaimStaleSocket(socketPath) {
136258
136266
 
136259
136267
  // ../runner/dist/server.js
136260
136268
  init_dist();
136261
- var RUNNER_PROTOCOL_VERSION = 8;
136269
+ var RUNNER_PROTOCOL_VERSION = 9;
136262
136270
  var MIN_COMPATIBLE_PROTOCOL_VERSION = 1;
136263
136271
  var RunnerMethod = {
136264
136272
  /** client->server: handshake; returns the initial info snapshot. */
@@ -136281,6 +136289,8 @@ var RunnerMethod = {
136281
136289
  SetResolver: "setResolver",
136282
136290
  /** client->server: switch the active mode. */
136283
136291
  ModeSetActive: "mode.setActive",
136292
+ /** client->server: set the session's reasoning/thinking effort (v9). */
136293
+ SessionSetReasoning: "session.setReasoning",
136284
136294
  /** client->server: switch the active provider (server resolves credentials). */
136285
136295
  ProviderSetActive: "provider.setActive",
136286
136296
  /** client->server: enable/disable a provider (v7; persists to preferences). */
@@ -136397,6 +136407,9 @@ var setResolverParamsSchema = z.object({
136397
136407
  approval: z.boolean().optional()
136398
136408
  });
136399
136409
  var modeSetActiveParamsSchema = z.object({ name: z.string() });
136410
+ var sessionSetReasoningParamsSchema = z.object({
136411
+ effort: z.enum(["off", "low", "medium", "high"])
136412
+ });
136400
136413
  var providerSetActiveParamsSchema = z.object({
136401
136414
  name: z.string(),
136402
136415
  config: z.record(z.unknown()).optional()
@@ -136726,6 +136739,12 @@ function handleModeSetActive(ctx, raw) {
136726
136739
  ctx.session.modes.setActive(name);
136727
136740
  return {};
136728
136741
  }
136742
+ function handleSessionSetReasoning(ctx, raw) {
136743
+ const { effort } = sessionSetReasoningParamsSchema.parse(raw);
136744
+ ctx.session.reasoning = effort === "off" ? void 0 : { effort };
136745
+ ctx.broadcastInfo();
136746
+ return {};
136747
+ }
136729
136748
  async function handlePermissionAddAllow(ctx, raw) {
136730
136749
  const { name, reason } = permissionAddAllowParamsSchema.parse(raw);
136731
136750
  await ctx.session.permissions.addAllow({ name, ...reason ? { reason } : {} });
@@ -136846,6 +136865,7 @@ var RunnerServer = class {
136846
136865
  peer.handle(RunnerMethod.SessionReset, () => this.handleSessionReset());
136847
136866
  peer.handle(RunnerMethod.SetResolver, (raw) => this.handleSetResolver(client, raw));
136848
136867
  peer.handle(RunnerMethod.ModeSetActive, (raw) => handleModeSetActive(ctx, raw));
136868
+ peer.handle(RunnerMethod.SessionSetReasoning, (raw) => handleSessionSetReasoning(ctx, raw));
136849
136869
  peer.handle(RunnerMethod.ProviderSetActive, (raw) => handleProviderSetActive(ctx, raw));
136850
136870
  peer.handle(RunnerMethod.ProviderSetEnabled, (raw) => handleProviderSetEnabled(ctx, raw));
136851
136871
  peer.handle(RunnerMethod.ProviderRefreshReady, () => handleProviderRefreshReady(ctx));
@@ -137282,6 +137302,10 @@ function makeProviderAdminView(ctx) {
137282
137302
  configure: async (name, patch) => {
137283
137303
  requireServerProtocol(7, "Configuring a provider");
137284
137304
  await peer.request(RunnerMethod.ProviderConfigure, { name, patch });
137305
+ },
137306
+ setReasoning: async (effort) => {
137307
+ requireServerProtocol(9, "Setting reasoning effort");
137308
+ await peer.request(RunnerMethod.SessionSetReasoning, { effort });
137285
137309
  }
137286
137310
  };
137287
137311
  }
@@ -139079,6 +139103,57 @@ async function integrate(input) {
139079
139103
  }
139080
139104
  return { merged, conflicts, resolvedByOwnership, stagingBranch: branchName, promoted };
139081
139105
  }
139106
+ var COLLAB_LOCK_PATH = join(homedir(), ".moxxy", "collab", "active.lock");
139107
+ function collabLockPath() {
139108
+ return process.env.MOXXY_COLLAB_LOCK || COLLAB_LOCK_PATH;
139109
+ }
139110
+ function readRaw() {
139111
+ try {
139112
+ return JSON.parse(readFileSync(collabLockPath(), "utf8"));
139113
+ } catch {
139114
+ return null;
139115
+ }
139116
+ }
139117
+ function isAlive(pid) {
139118
+ try {
139119
+ process.kill(pid, 0);
139120
+ return true;
139121
+ } catch (err) {
139122
+ return err.code === "EPERM";
139123
+ }
139124
+ }
139125
+ function readActiveCollab() {
139126
+ const info = readRaw();
139127
+ if (!info)
139128
+ return null;
139129
+ if (!isAlive(info.pid)) {
139130
+ try {
139131
+ unlinkSync(collabLockPath());
139132
+ } catch {
139133
+ }
139134
+ return null;
139135
+ }
139136
+ return info;
139137
+ }
139138
+ function tryAcquireCollabLock(args) {
139139
+ mkdirSync(dirname(collabLockPath()), { recursive: true });
139140
+ const existing = readActiveCollab();
139141
+ if (existing && existing.sessionId !== args.sessionId) {
139142
+ return { ok: false, holder: existing };
139143
+ }
139144
+ const info = { pid: process.pid, ...args };
139145
+ writeFileSync(collabLockPath(), JSON.stringify(info));
139146
+ return { ok: true };
139147
+ }
139148
+ function releaseCollabLock(sessionId) {
139149
+ const info = readRaw();
139150
+ if (info && info.sessionId === sessionId) {
139151
+ try {
139152
+ unlinkSync(collabLockPath());
139153
+ } catch {
139154
+ }
139155
+ }
139156
+ }
139082
139157
 
139083
139158
  // ../mode-collaborative/dist/collab-loop.js
139084
139159
  var POLL_MS = 500;
@@ -139097,47 +139172,56 @@ async function* runCollaborative(ctx, deps) {
139097
139172
  yield await ctx.emit(assistant(ctx, "Collaborative mode needs a task to work on."));
139098
139173
  return;
139099
139174
  }
139100
- const runId = collabRunId(String(ctx.sessionId), String(ctx.turnId));
139101
- mkdirSync(collabRunDir(runId), { recursive: true });
139102
- const { installed: gitInstalled2, repo: gitRepo } = await detectGit(cwd2);
139103
- const parallel = cfg.concurrency === "parallel" && gitRepo;
139104
- if (!parallel) {
139105
- yield await ctx.emit(plugin4(ctx, "collab_fallback_sequential", {
139106
- reason: !gitInstalled2 ? "git is not installed \u2014 running agents sequentially in your workspace" : !gitRepo ? "this folder is not a git repository \u2014 running agents sequentially in your workspace" : "sequential mode selected"
139107
- }));
139108
- }
139109
- let baseSha = "";
139110
- if (parallel) {
139111
- const base2 = await resolveBase(cwd2);
139112
- baseSha = base2.baseSha;
139175
+ const lock = tryAcquireCollabLock({ sessionId: String(ctx.sessionId), task, startedAtMs: Date.now() });
139176
+ if (!lock.ok) {
139177
+ yield await ctx.emit(plugin4(ctx, "collab_blocked", { reason: "already-running", holderTask: lock.holder.task }));
139178
+ yield await ctx.emit(assistant(ctx, `A collaboration is already running ("${lock.holder.task}"). Only one runs at a time to save resources \u2014 stop it first, then start again.`));
139179
+ return;
139113
139180
  }
139181
+ const runId = collabRunId(String(ctx.sessionId), String(ctx.turnId));
139114
139182
  const worktrees = /* @__PURE__ */ new Map();
139115
- const architectEntry = {
139116
- id: ARCHITECT_AGENT_ID,
139117
- name: "Architect",
139118
- role: "architect",
139119
- subtask: task
139120
- };
139121
- const hub = await createCollaborationHub({
139122
- socketPath: hubSocketPath(runId),
139123
- task,
139124
- roster: [architectEntry],
139125
- peerReader: peerReaderFor(worktrees, baseSha)
139126
- });
139127
- registerActiveHub(String(ctx.sessionId), hub);
139128
- const unsubscribe = hub.subscribe((e3) => {
139129
- void ctx.emit(toCollabEvent(ctx, e3));
139130
- });
139131
- const supervisorOpts = {
139132
- runId,
139133
- hubSocket: hub.socketPath,
139134
- coordinatorSessionId: String(ctx.sessionId),
139135
- parentTask: task,
139136
- ...cfg.defaultModel ? { defaultModel: cfg.defaultModel } : {},
139137
- signal: ctx.signal
139138
- };
139139
- const supervisor = (deps.createSupervisor ?? ((o2) => new PeerSupervisor(o2)))(supervisorOpts, hub);
139183
+ let hub = null;
139184
+ let supervisor = null;
139185
+ let unsubscribe = null;
139140
139186
  try {
139187
+ mkdirSync(collabRunDir(runId), { recursive: true });
139188
+ const { installed: gitInstalled2, repo: gitRepo } = await detectGit(cwd2);
139189
+ const parallel = cfg.concurrency === "parallel" && gitRepo;
139190
+ if (!parallel) {
139191
+ yield await ctx.emit(plugin4(ctx, "collab_fallback_sequential", {
139192
+ reason: !gitInstalled2 ? "git is not installed \u2014 running agents sequentially in your workspace" : !gitRepo ? "this folder is not a git repository \u2014 running agents sequentially in your workspace" : "sequential mode selected"
139193
+ }));
139194
+ }
139195
+ let baseSha = "";
139196
+ if (parallel) {
139197
+ const base2 = await resolveBase(cwd2, { snapshotDirty: true });
139198
+ baseSha = base2.baseSha;
139199
+ }
139200
+ const architectEntry = {
139201
+ id: ARCHITECT_AGENT_ID,
139202
+ name: "Architect",
139203
+ role: "architect",
139204
+ subtask: task
139205
+ };
139206
+ hub = await createCollaborationHub({
139207
+ socketPath: hubSocketPath(runId),
139208
+ task,
139209
+ roster: [architectEntry],
139210
+ peerReader: peerReaderFor(worktrees, baseSha)
139211
+ });
139212
+ registerActiveHub(String(ctx.sessionId), hub);
139213
+ unsubscribe = hub.subscribe((e3) => {
139214
+ void ctx.emit(toCollabEvent(ctx, e3));
139215
+ });
139216
+ const supervisorOpts = {
139217
+ runId,
139218
+ hubSocket: hub.socketPath,
139219
+ coordinatorSessionId: String(ctx.sessionId),
139220
+ parentTask: task,
139221
+ ...cfg.defaultModel ? { defaultModel: cfg.defaultModel } : {},
139222
+ signal: ctx.signal
139223
+ };
139224
+ supervisor = (deps.createSupervisor ?? ((o2) => new PeerSupervisor(o2)))(supervisorOpts, hub);
139141
139225
  yield await ctx.emit(plugin4(ctx, "collab_started", { task, parallel, gitInstalled: gitInstalled2, gitRepo }));
139142
139226
  supervisor.spawn({ entry: architectEntry, cwd: cwd2, mode: COLLAB_ARCHITECT_MODE_NAME });
139143
139227
  yield await ctx.emit(plugin4(ctx, "collab_agent_spawned", { id: ARCHITECT_AGENT_ID, role: "architect" }));
@@ -139235,10 +139319,14 @@ ${summaryBlock}${mergeNote ? `
139235
139319
  ${mergeNote}` : ""}`));
139236
139320
  yield await ctx.emit(plugin4(ctx, "collab_completed", { done: doneIds, total: roster.length }));
139237
139321
  } finally {
139238
- await supervisor.shutdownAll("collaboration complete");
139239
- unsubscribe();
139322
+ if (supervisor)
139323
+ await supervisor.shutdownAll("collaboration complete");
139324
+ if (unsubscribe)
139325
+ unsubscribe();
139240
139326
  unregisterActiveHub(String(ctx.sessionId));
139241
- await hub.close();
139327
+ if (hub)
139328
+ await hub.close();
139329
+ releaseCollabLock(String(ctx.sessionId));
139242
139330
  }
139243
139331
  }
139244
139332
  function lastUserPromptText(ctx) {
@@ -144062,6 +144150,7 @@ var optionalWorkspace = z.string().min(1).max(256).optional();
144062
144150
  var MAX_AUDIO_BASE64 = 4e7;
144063
144151
  var MAX_INLINE_ATTACHMENT_CONTENT = 12e6;
144064
144152
  var commandName = z.string().min(1).max(64).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/, "invalid command name");
144153
+ var appId = z.string().min(1).max(64).regex(/^[a-z][a-z0-9-]*$/, "invalid app id");
144065
144154
  var workflowName = z.string().min(1).max(200).refine((s2) => !s2.includes("..") && !s2.includes("/") && !s2.includes("\\"), "invalid workflow name");
144066
144155
  var ipcInputSchemas = {
144067
144156
  // No-arg, but spawns a child process (npm install) — pin the payload to
@@ -144168,9 +144257,36 @@ var ipcInputSchemas = {
144168
144257
  path: z.string().max(4096).optional()
144169
144258
  }),
144170
144259
  "settings.fetchProviderModels": z.object({ provider: providerName }),
144260
+ // Session config mutation — pin the effort to the known enum so a renderer
144261
+ // can't push an arbitrary string through to the runner / provider request.
144262
+ "settings.setReasoning": z.object({
144263
+ workspaceId: optionalWorkspace,
144264
+ effort: z.enum(["off", "low", "medium", "high"])
144265
+ }),
144171
144266
  "settings.writeSkill": z.object({ name: skillName, body: z.string().max(1e6) }),
144172
144267
  "settings.readSkill": z.object({ name: skillName }),
144173
144268
  "settings.deleteSkill": z.object({ name: skillName }),
144269
+ // Desktop apps: appId keys the per-app install dir + a network download, so
144270
+ // pin it to a non-traversing slug. (pickDocument is no-arg → see below.)
144271
+ "apps.status": z.object({ appId }),
144272
+ "apps.install": z.object({ appId }),
144273
+ "apps.uninstall": z.object({ appId }),
144274
+ // Anonymizer: parseDocument reads a file (bound the path), saveRedacted writes
144275
+ // one (bound name + cap content so a renderer can't OOM main). pickDocument
144276
+ // takes nothing — pin it to "nothing" so no args can be smuggled across.
144277
+ "anonymizer.pickDocument": z.undefined(),
144278
+ "anonymizer.parseDocument": z.object({ path: z.string().min(1).max(4096) }),
144279
+ // A drag-dropped doc: the renderer sends the dropped file's BYTES (base64),
144280
+ // not a path — so there's no arbitrary-file-read to gate, only a size to cap.
144281
+ // ~67 MB of base64 ≈ 50 MB of file, matching the picker's practical ceiling.
144282
+ "anonymizer.parseDocumentBytes": z.object({
144283
+ name: z.string().min(1).max(255),
144284
+ dataBase64: z.string().min(1).max(67e6)
144285
+ }),
144286
+ "anonymizer.saveRedacted": z.object({
144287
+ suggestedName: z.string().min(1).max(255),
144288
+ content: z.string().max(2e7)
144289
+ }),
144174
144290
  "desks.create": z.object({ name: z.string().min(1).max(200), cwd: z.string().min(1).max(4096) }),
144175
144291
  // Mirror desks.create's name bounds — rename writes the name into the desks
144176
144292
  // JSON, so an unbounded string would let a renderer bloat the state file.
@@ -146562,37 +146678,25 @@ async function compactSession(session) {
146562
146678
  if (events.length === 0) {
146563
146679
  return { kind: "text", text: "nothing to compact: event log is empty" };
146564
146680
  }
146565
- const providerCtxWindow = resolveActiveContextWindow(s2);
146566
146681
  const provider = safe(() => s2.providers?.getActive()) ?? void 0;
146567
146682
  const model = provider?.models[0]?.id;
146683
+ const contextWindow = provider?.models[0]?.contextWindow;
146568
146684
  try {
146569
- const result = await compactor.compact(events, {
146570
- log: s2.log.asReader ? s2.log.asReader() : s2.log,
146571
- budget: {
146572
- contextWindow: providerCtxWindow,
146573
- estimatedTokens: estimateContextTokens$1(s2.log.asReader ? s2.log.asReader() : s2.log),
146574
- reserveForOutput: 0
146575
- },
146576
- signal: s2.signal ?? new AbortController().signal,
146685
+ const result = await runManualCompaction({
146686
+ compactor,
146687
+ log: s2.log,
146688
+ signal: s2.signal,
146577
146689
  ...provider ? { provider } : {},
146578
- ...model ? { model } : {}
146690
+ ...model !== void 0 ? { model } : {},
146691
+ ...contextWindow !== void 0 ? { contextWindow } : {},
146692
+ ...s2.id !== void 0 ? { sessionId: s2.id } : {}
146579
146693
  });
146580
- if (result.tokensSaved <= 0 || result.summary.trim().length === 0) {
146694
+ if (!result.compacted) {
146581
146695
  return { kind: "text", text: "nothing to compact yet" };
146582
146696
  }
146583
- const lastEvent = events[events.length - 1];
146584
- const emittable = {
146585
- sessionId: s2.id ?? lastEvent?.sessionId,
146586
- turnId: lastEvent?.turnId,
146587
- source: "compactor",
146588
- ...result
146589
- };
146590
- await s2.log.append(emittable);
146591
- const [fromSeq, toSeq] = result.replacedRange;
146592
- const compactedEvents = events.filter((e3) => e3.seq >= fromSeq && e3.seq <= toSeq).length;
146593
146697
  return {
146594
146698
  kind: "text",
146595
- text: `context compacted: ${formatCount2(compactedEvents)} ${plural2(compactedEvents, "event")}, ~${formatTokenCount2(result.tokensSaved)} tokens saved`
146699
+ text: `context compacted: ${formatCount2(result.eventsCompacted)} ${plural2(result.eventsCompacted, "event")}, ~${formatTokenCount2(result.tokensSaved)} tokens saved`
146596
146700
  };
146597
146701
  } catch (err) {
146598
146702
  return {
@@ -146601,17 +146705,6 @@ async function compactSession(session) {
146601
146705
  };
146602
146706
  }
146603
146707
  }
146604
- function resolveActiveContextWindow(s2) {
146605
- try {
146606
- const provider = s2.providers?.getActive();
146607
- if (!provider)
146608
- return Number.MAX_SAFE_INTEGER;
146609
- const window2 = provider.models[0]?.contextWindow;
146610
- return window2 && window2 > 0 ? window2 : Number.MAX_SAFE_INTEGER;
146611
- } catch {
146612
- return Number.MAX_SAFE_INTEGER;
146613
- }
146614
- }
146615
146708
  function formatCount2(value) {
146616
146709
  return new Intl.NumberFormat("en-US").format(value);
146617
146710
  }
@@ -147136,7 +147229,7 @@ var computerControlPlugin = definePlugin({
147136
147229
  tools: [...computerControlTools]
147137
147230
  });
147138
147231
 
147139
- // src/setup/voice-admin-plugin.ts
147232
+ // ../plugin-voice-admin/dist/index.js
147140
147233
  init_dist();
147141
147234
  function buildVoiceAdminPlugin(session) {
147142
147235
  const voiceNames = () => [
@@ -147170,9 +147263,7 @@ function buildVoiceAdminPlugin(session) {
147170
147263
  return { active: "system" };
147171
147264
  }
147172
147265
  if (!session.synthesizers.has(synthesizer)) {
147173
- throw new Error(
147174
- `No synthesizer named "${synthesizer}". Available: ${voiceNames().join(", ")}.`
147175
- );
147266
+ throw new Error(`No synthesizer named "${synthesizer}". Available: ${voiceNames().join(", ")}.`);
147176
147267
  }
147177
147268
  session.synthesizers.setActive(synthesizer);
147178
147269
  return { active: synthesizer };
@@ -147610,6 +147701,8 @@ var stepSchema = z.object({
147610
147701
  needs: z.array(z.string().min(1)).default([]),
147611
147702
  when: z.string().min(1).optional(),
147612
147703
  onError: z.enum(["fail", "continue", "retry"]).default("fail"),
147704
+ // `retries` only takes effect when `onError: 'retry'`; with 'fail'/'continue'
147705
+ // the step runs exactly one attempt (see runStep in executor/steps.ts).
147613
147706
  retries: z.number().int().min(0).max(3).default(0),
147614
147707
  label: z.string().max(60).optional(),
147615
147708
  format: z.enum(["json", "plain"]).optional(),
@@ -148519,7 +148612,7 @@ function buildRunResult(ctx, status, ok, extra) {
148519
148612
  };
148520
148613
  }
148521
148614
  async function runStep(step, scope, ctx) {
148522
- const attempts = 1 + Math.max(0, step.retries);
148615
+ const attempts = step.onError === "retry" ? 1 + Math.max(0, step.retries) : 1;
148523
148616
  let lastError = "";
148524
148617
  for (let attempt = 0; attempt < attempts; attempt++) {
148525
148618
  if (ctx.deps.signal.aborted)
@@ -149040,7 +149133,7 @@ ${userMessage.trim()}${FINALIZE_REPLY_SUFFIX}`;
149040
149133
  var DAG_EXECUTOR_NAME = "dag";
149041
149134
  var dagExecutor = defineWorkflowExecutor({
149042
149135
  name: DAG_EXECUTOR_NAME,
149043
- description: "DAG runner: steps with settled dependencies are scheduled in waves of up to `concurrency` ready steps, then executed sequentially within each wave (no overlap yet \u2014 `concurrency` caps the batch size, not wall-clock latency).",
149136
+ description: "DAG runner: steps with settled dependencies are scheduled in waves of up to `concurrency` ready steps, then executed sequentially within each wave (no overlap \u2014 `concurrency` caps the batch size drained per pass, not wall-clock latency).",
149044
149137
  run: runExecutor
149045
149138
  });
149046
149139
 
@@ -149107,7 +149200,7 @@ A workflow is a DAG of steps. Schema:
149107
149200
  - args: templated args object for tool/workflow steps
149108
149201
  - needs: [ <upstream step ids> ] (defines the DAG; omit only for true sources)
149109
149202
  - when (optional, legacy): simple guards only \u2014 '{{ steps.x.output }} is not empty'. Do NOT use when for semantic decisions (use condition/switch).
149110
- - onError (optional): fail | continue | retry ; retries (optional, 0-3)
149203
+ - onError (optional): fail | continue | retry ; retries (optional, 0-3 \u2014 only applies when onError is retry; fail/continue always run exactly one attempt)
149111
149204
 
149112
149205
  Operator data \u2014 two ways: declare a value the operator can supply UP FRONT as an \`inputs\` field (filled in before Run). To PAUSE mid-run and ask a question whose answer depends on earlier steps, set \`awaitInput: true\` on a prompt or skill step: the workflow pauses, surfaces the step's prompt to the operator, and resumes with their reply once they answer. Prefer \`inputs\` for known-up-front values; use \`awaitInput\` only for genuinely mid-run questions.
149113
149206