@moxxy/cli 0.5.1 → 0.5.5

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,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { z as z$1, defineProvider, definePlugin, defineTool, MoxxyError, writeFileAtomic, asTurnId, defineMode, asPluginId, defineChannel, defineTunnelProvider, createMutex, defineWorkflowExecutor, toFriendlyError, estimateTextTokens, classifyHttpStatus, createStuckLoopDetector, runCompactionIfNeeded, runElisionIfNeeded, collectProviderStream, usageEventFields, isContextOverflowError, buildSystemPromptWithSkills, projectMessages, defineCompactor, defineCacheStrategy, denyByDefaultResolver, createAllowListResolver, zodToJsonSchema, asToolCallId, dispatchToolCall, runSingleShotTurn, bearerTokenMatches, estimateContextTokens, readRequestBody, moxxyPath, defineEmbedder, skillFrontmatterSchema, asSkillId, getInstallHint, moxxyHome, defineTranscriber, summarizeTokensByModel, migrateModeName, createDeferredPermissionResolver, classifyNetworkError, addModelTotals, ISOLATION_RANK, moxxyPackageSchema, defineCommand, createCallbackResolver, autoAllowResolver, asSessionId, defineViewRenderer, DEFAULT_VIEW_TAGS, evaluateToolRule, summarizeSessionTokensFromEvents, asEventId } from '@moxxy/sdk';
3
+ import { z as z$1, defineProvider, definePlugin, defineTool, MoxxyError, writeFileAtomic, asTurnId, defineMode, asPluginId, defineChannel, defineTunnelProvider, createMutex, defineWorkflowExecutor, toFriendlyError, estimateTextTokens, classifyHttpStatus, createStuckLoopDetector, runCompactionIfNeeded, runElisionIfNeeded, collectProviderStream, usageEventFields, isContextOverflowError, emitRequestsAndDetectStuck, executeToolUses, buildSystemPromptWithSkills, projectMessages, defineCompactor, defineCacheStrategy, denyByDefaultResolver, createAllowListResolver, zodToJsonSchema, runSingleShotTurn, bearerTokenMatches, estimateContextTokens, readRequestBody, moxxyPath, defineEmbedder, skillFrontmatterSchema, asSkillId, getInstallHint, moxxyHome, defineTranscriber, summarizeTokensByModel, migrateModeName, createDeferredPermissionResolver, classifyNetworkError, addModelTotals, ISOLATION_RANK, moxxyPackageSchema, defineCommand, createCallbackResolver, autoAllowResolver, asSessionId, asToolCallId, defineViewRenderer, DEFAULT_VIEW_TAGS, evaluateToolRule, summarizeSessionTokensFromEvents, asEventId } from '@moxxy/sdk';
4
4
  import * as fs37 from 'fs';
5
5
  import fs37__default, { existsSync, promises, ReadStream, readFileSync, statSync, readdirSync, mkdirSync, writeFileSync, unlinkSync, watch, createReadStream } from 'fs';
6
6
  import * as path3 from 'path';
@@ -92262,6 +92262,7 @@ init_dist();
92262
92262
  async function buildSession(args) {
92263
92263
  const userPolicyPath = args.config.permissions?.policyPath ?? path3.join(os5.homedir(), ".moxxy", "permissions.json");
92264
92264
  const permissionEngine = await PermissionEngine.load(userPolicyPath);
92265
+ const effectiveSessionId = args.resumeSessionId ?? args.sessionId;
92265
92266
  let restoredEvents = [];
92266
92267
  if (args.resumeSessionId) {
92267
92268
  try {
@@ -92275,6 +92276,12 @@ async function buildSession(args) {
92275
92276
  cause: err
92276
92277
  });
92277
92278
  }
92279
+ } else if (args.sessionId) {
92280
+ try {
92281
+ restoredEvents = await restoreEvents(args.sessionId);
92282
+ } catch {
92283
+ restoredEvents = [];
92284
+ }
92278
92285
  }
92279
92286
  const userPluginsDir2 = path3.join(os5.homedir(), ".moxxy", "plugins");
92280
92287
  return new Session({
@@ -92290,7 +92297,7 @@ async function buildSession(args) {
92290
92297
  pluginDiscoveryPaths: [userPluginsDir2, path3.join(userPluginsDir2, "node_modules")],
92291
92298
  ...args.isPluginDisabled ? { isPluginDisabled: args.isPluginDisabled } : {},
92292
92299
  ...args.secretResolver ? { secretResolver: args.secretResolver } : {},
92293
- ...args.resumeSessionId ? { sessionId: args.resumeSessionId } : {},
92300
+ ...effectiveSessionId ? { sessionId: effectiveSessionId } : {},
92294
92301
  // Seed restored events directly into the log so subscribers don't
92295
92302
  // re-fire side effects for historical events. New appends from this
92296
92303
  // point onward fire subscribers normally (and the persistence
@@ -105571,7 +105578,11 @@ async function* runDefaultMode(ctx) {
105571
105578
  continue;
105572
105579
  }
105573
105580
  reactiveCompactions = 0;
105574
- const stuck = yield* emitRequestsAndDetectStuck(ctx, toolUses, detector);
105581
+ const stuck = yield* emitRequestsAndDetectStuck(ctx, toolUses, detector, {
105582
+ abortedResultMessage: "default mode loop aborted (stuck pattern) before this call ran",
105583
+ nearHint: "against the same target (only volatile args like maxBytes varied)",
105584
+ fatalMessage: ({ toolName, count, how }) => `default mode loop aborted \u2014 detected stuck pattern: tool "${toolName}" called ${count} times ${how}. The model is likely looping on the same call; reset or rephrase.`
105585
+ });
105575
105586
  if (stuck)
105576
105587
  return;
105577
105588
  if (text || stopReason === "end_turn" || toolUses.length === 0) {
@@ -105599,79 +105610,6 @@ async function* runDefaultMode(ctx) {
105599
105610
  message: `default mode loop exceeded maxIterations (${maxIterations})`
105600
105611
  });
105601
105612
  }
105602
- async function* emitRequestsAndDetectStuck(ctx, toolUses, detector) {
105603
- const emitted = [];
105604
- for (const t2 of toolUses) {
105605
- yield await ctx.emit({
105606
- type: "tool_call_requested",
105607
- sessionId: ctx.sessionId,
105608
- turnId: ctx.turnId,
105609
- source: "model",
105610
- callId: asToolCallId(t2.id),
105611
- name: t2.name,
105612
- input: t2.input
105613
- });
105614
- emitted.push(t2);
105615
- const sig = detector.record(t2.name, t2.input);
105616
- if (sig.stuck) {
105617
- for (const r2 of emitted) {
105618
- yield await ctx.emit({
105619
- type: "tool_result",
105620
- sessionId: ctx.sessionId,
105621
- turnId: ctx.turnId,
105622
- source: "tool",
105623
- callId: asToolCallId(r2.id),
105624
- ok: false,
105625
- error: { kind: "aborted", message: "default mode loop aborted (stuck pattern) before this call ran" }
105626
- });
105627
- }
105628
- const how = sig.kind === "near" ? "against the same target (only volatile args like maxBytes varied)" : "with identical input";
105629
- yield await ctx.emit({
105630
- type: "error",
105631
- sessionId: ctx.sessionId,
105632
- turnId: ctx.turnId,
105633
- source: "system",
105634
- kind: "fatal",
105635
- message: `default mode loop aborted \u2014 detected stuck pattern: tool "${t2.name}" called ${sig.count} times ${how}. The model is likely looping on the same call; reset or rephrase.`
105636
- });
105637
- return true;
105638
- }
105639
- }
105640
- return false;
105641
- }
105642
- async function* executeToolUses(ctx, toolUses, iteration) {
105643
- const unresolved = new Set(toolUses.map((t2) => t2.id));
105644
- for (const t2 of toolUses) {
105645
- if (ctx.signal.aborted) {
105646
- for (const orphanId of unresolved) {
105647
- yield await ctx.emit({
105648
- type: "tool_result",
105649
- sessionId: ctx.sessionId,
105650
- turnId: ctx.turnId,
105651
- source: "tool",
105652
- callId: asToolCallId(orphanId),
105653
- ok: false,
105654
- error: { kind: "aborted", message: "turn aborted before tool ran" }
105655
- });
105656
- }
105657
- unresolved.clear();
105658
- yield await ctx.emit({
105659
- type: "abort",
105660
- sessionId: ctx.sessionId,
105661
- turnId: ctx.turnId,
105662
- source: "system",
105663
- reason: "signal aborted during tool execution"
105664
- });
105665
- return true;
105666
- }
105667
- try {
105668
- yield* dispatchToolCall(ctx, t2, iteration);
105669
- } finally {
105670
- unresolved.delete(t2.id);
105671
- }
105672
- }
105673
- return false;
105674
- }
105675
105613
  function buildMessages(ctx) {
105676
105614
  const systemPrompt = buildSystemPromptWithSkills(ctx.systemPrompt, ctx.skills.list());
105677
105615
  return projectMessages(ctx, { ...systemPrompt ? { systemPrompt } : {} });
@@ -105907,7 +105845,22 @@ async function* runGoalMode(ctx) {
105907
105845
  });
105908
105846
  return;
105909
105847
  }
105910
- const stuck = yield* emitRequestsAndDetectStuck2(ctx, toolUses, detector);
105848
+ const stuck = yield* emitRequestsAndDetectStuck(ctx, toolUses, detector, {
105849
+ abortedResultMessage: "goal mode aborted (stuck pattern) before this call ran",
105850
+ nearHint: "against the same target (only volatile args varied)",
105851
+ extraOnStuck: ({ toolName, count, kind: kind3 }) => [
105852
+ {
105853
+ type: "plugin_event",
105854
+ sessionId: ctx.sessionId,
105855
+ turnId: ctx.turnId,
105856
+ source: "plugin",
105857
+ pluginId: GOAL_PLUGIN_ID,
105858
+ subtype: "goal_stuck",
105859
+ payload: { tool: toolName, count, kind: kind3 }
105860
+ }
105861
+ ],
105862
+ fatalMessage: ({ toolName, count, how }) => `goal mode aborted \u2014 stuck pattern: tool "${toolName}" called ${count} times ${how}. The model is looping on the same call; send another message to redirect it.`
105863
+ });
105911
105864
  if (stuck)
105912
105865
  return;
105913
105866
  if (text || stopReason === "end_turn" || toolUses.length === 0) {
@@ -105945,7 +105898,7 @@ async function* runGoalMode(ctx) {
105945
105898
  continue;
105946
105899
  }
105947
105900
  noop3 = 0;
105948
- const exited = yield* executeToolUses2(goalCtx, toolUses, iteration);
105901
+ const exited = yield* executeToolUses(goalCtx, toolUses, iteration);
105949
105902
  if (exited)
105950
105903
  return;
105951
105904
  const terminal = detectGoalTerminal(ctx.log.slice(), toolUses);
@@ -106023,88 +105976,6 @@ function composeSystemPrompts(user, layer) {
106023
105976
 
106024
105977
  ${user}`;
106025
105978
  }
106026
- async function* emitRequestsAndDetectStuck2(ctx, toolUses, detector) {
106027
- const emitted = [];
106028
- for (const t2 of toolUses) {
106029
- yield await ctx.emit({
106030
- type: "tool_call_requested",
106031
- sessionId: ctx.sessionId,
106032
- turnId: ctx.turnId,
106033
- source: "model",
106034
- callId: asToolCallId(t2.id),
106035
- name: t2.name,
106036
- input: t2.input
106037
- });
106038
- emitted.push(t2);
106039
- const sig = detector.record(t2.name, t2.input);
106040
- if (sig.stuck) {
106041
- for (const r2 of emitted) {
106042
- yield await ctx.emit({
106043
- type: "tool_result",
106044
- sessionId: ctx.sessionId,
106045
- turnId: ctx.turnId,
106046
- source: "tool",
106047
- callId: asToolCallId(r2.id),
106048
- ok: false,
106049
- error: { kind: "aborted", message: "goal mode aborted (stuck pattern) before this call ran" }
106050
- });
106051
- }
106052
- const how = sig.kind === "near" ? "against the same target (only volatile args varied)" : "with identical input";
106053
- yield await ctx.emit({
106054
- type: "plugin_event",
106055
- sessionId: ctx.sessionId,
106056
- turnId: ctx.turnId,
106057
- source: "plugin",
106058
- pluginId: GOAL_PLUGIN_ID,
106059
- subtype: "goal_stuck",
106060
- payload: { tool: t2.name, count: sig.count, kind: sig.kind }
106061
- });
106062
- yield await ctx.emit({
106063
- type: "error",
106064
- sessionId: ctx.sessionId,
106065
- turnId: ctx.turnId,
106066
- source: "system",
106067
- kind: "fatal",
106068
- message: `goal mode aborted \u2014 stuck pattern: tool "${t2.name}" called ${sig.count} times ${how}. The model is looping on the same call; send another message to redirect it.`
106069
- });
106070
- return true;
106071
- }
106072
- }
106073
- return false;
106074
- }
106075
- async function* executeToolUses2(ctx, toolUses, iteration) {
106076
- const unresolved = new Set(toolUses.map((t2) => t2.id));
106077
- for (const t2 of toolUses) {
106078
- if (ctx.signal.aborted) {
106079
- for (const orphanId of unresolved) {
106080
- yield await ctx.emit({
106081
- type: "tool_result",
106082
- sessionId: ctx.sessionId,
106083
- turnId: ctx.turnId,
106084
- source: "tool",
106085
- callId: asToolCallId(orphanId),
106086
- ok: false,
106087
- error: { kind: "aborted", message: "turn aborted before tool ran" }
106088
- });
106089
- }
106090
- unresolved.clear();
106091
- yield await ctx.emit({
106092
- type: "abort",
106093
- sessionId: ctx.sessionId,
106094
- turnId: ctx.turnId,
106095
- source: "system",
106096
- reason: "signal aborted during tool execution"
106097
- });
106098
- return true;
106099
- }
106100
- try {
106101
- yield* dispatchToolCall(ctx, t2, iteration);
106102
- } finally {
106103
- unresolved.delete(t2.id);
106104
- }
106105
- }
106106
- return false;
106107
- }
106108
105979
 
106109
105980
  // ../mode-goal/dist/index.js
106110
105981
  var goalMode = defineMode({
@@ -111346,28 +111217,54 @@ function buildSubagentsPlugin(opts = {}) {
111346
111217
  });
111347
111218
  }
111348
111219
  buildSubagentsPlugin();
111220
+
111221
+ // ../plugin-plugins-admin/dist/shared.js
111222
+ var NPM_NAME_RE = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
111223
+ function assertSafeNpmSpec(spec) {
111224
+ const trimmed = spec.trim();
111225
+ if (trimmed.length === 0) {
111226
+ throw new Error("plugin spec must be a non-empty package name, git spec, or path");
111227
+ }
111228
+ if (trimmed.startsWith("-")) {
111229
+ throw new Error(`refusing plugin spec "${spec}": leading "-" would be parsed by npm as an option, not a package`);
111230
+ }
111231
+ return trimmed;
111232
+ }
111233
+ function diffSnapshot2(before, after) {
111234
+ const out = {};
111235
+ for (const key of ["tools", "agents", "providers", "modes", "compactors", "channels"]) {
111236
+ const b3 = new Set(before[key]);
111237
+ const added = after[key].filter((n2) => !b3.has(n2));
111238
+ if (added.length > 0)
111239
+ out[key] = added;
111240
+ }
111241
+ return out;
111242
+ }
111243
+
111244
+ // ../plugin-plugins-admin/dist/install.js
111349
111245
  function userPluginsDir() {
111350
111246
  return moxxyPath("plugins");
111351
111247
  }
111352
- var NPM_NAME_RE = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
111353
111248
  var VERSION_RE = /^[0-9a-z.~^*<=>-]+$/i;
111354
111249
  async function installPluginPackage(opts) {
111250
+ const spec = assertSafeNpmSpec(opts.packageName);
111355
111251
  const dir = userPluginsDir();
111356
111252
  await ensurePackageJson(dir);
111357
- const { exitCode, stderr } = await runNpm(["install", "--prefix", dir, "--no-fund", "--no-audit", "--save", opts.packageName], opts.signal);
111253
+ const { exitCode, stderr } = await runNpm(["install", "--prefix", dir, "--no-fund", "--no-audit", "--save", spec], opts.signal);
111358
111254
  if (exitCode !== 0) {
111359
111255
  throw new Error(`npm install failed (exit ${exitCode}): ${truncate9(stderr, 400)}`);
111360
111256
  }
111361
- return { installed: opts.packageName, dir };
111257
+ return { installed: spec, dir };
111362
111258
  }
111363
111259
  async function removePluginPackage(opts) {
111260
+ const spec = assertSafeNpmSpec(opts.packageName);
111364
111261
  const dir = userPluginsDir();
111365
111262
  await ensurePackageJson(dir);
111366
- const { exitCode, stderr } = await runNpm(["uninstall", "--prefix", dir, "--no-fund", "--no-audit", "--save", opts.packageName], opts.signal);
111263
+ const { exitCode, stderr } = await runNpm(["uninstall", "--prefix", dir, "--no-fund", "--no-audit", "--save", spec], opts.signal);
111367
111264
  if (exitCode !== 0) {
111368
111265
  throw new Error(`npm uninstall failed (exit ${exitCode}): ${truncate9(stderr, 400)}`);
111369
111266
  }
111370
- return { removed: opts.packageName, dir };
111267
+ return { removed: spec, dir };
111371
111268
  }
111372
111269
  function buildInstallPluginTool(deps) {
111373
111270
  return defineTool({
@@ -111484,26 +111381,15 @@ function runNpm(args, signal) {
111484
111381
  });
111485
111382
  });
111486
111383
  }
111487
- function diffSnapshot2(before, after) {
111488
- const out = {};
111489
- for (const key of ["tools", "agents", "providers", "modes", "compactors", "channels"]) {
111490
- const b3 = new Set(before[key]);
111491
- const added = after[key].filter((n2) => !b3.has(n2));
111492
- if (added.length > 0)
111493
- out[key] = added;
111494
- }
111495
- return out;
111496
- }
111497
111384
  function truncate9(s2, n2) {
111498
111385
  return s2.length <= n2 ? s2 : s2.slice(0, n2 - 1) + "\u2026";
111499
111386
  }
111500
- var NPM_NAME_RE2 = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
111501
111387
  function buildDisablePluginTool(deps) {
111502
111388
  return defineTool({
111503
111389
  name: "disable_plugin",
111504
111390
  description: "Disable (unplug) a registered moxxy plugin \u2014 a default builtin or an installed one \u2014 by package name. Persists `plugins[<name>].enabled=false` to ~/.moxxy/config.yaml and unloads it from the running session, so its tools / agents / providers / modes / channels disappear immediately and it stays off across restarts. Reverse with enable_plugin. Use when the user wants to turn a plugin off without uninstalling it.",
111505
111391
  inputSchema: z$1.object({
111506
- packageName: z$1.string().min(1).refine((s2) => NPM_NAME_RE2.test(s2), {
111392
+ packageName: z$1.string().min(1).refine((s2) => NPM_NAME_RE.test(s2), {
111507
111393
  message: "must be a valid moxxy package name (e.g. @moxxy/plugin-browser)"
111508
111394
  }).describe("Plugin package name to disable, e.g. @moxxy/plugin-browser.")
111509
111395
  }),
@@ -111512,7 +111398,7 @@ function buildDisablePluginTool(deps) {
111512
111398
  const before = deps.snapshot();
111513
111399
  await deps.setEnabled(packageName, false);
111514
111400
  const after = deps.snapshot();
111515
- return { disabled: packageName, unregistered: diffSnapshot3(after, before) };
111401
+ return { disabled: packageName, unregistered: diffSnapshot2(after, before) };
111516
111402
  }
111517
111403
  });
111518
111404
  }
@@ -111521,7 +111407,7 @@ function buildEnablePluginTool(deps) {
111521
111407
  name: "enable_plugin",
111522
111408
  description: "Enable (plug back in) a previously-disabled moxxy plugin by package name. Persists `plugins[<name>].enabled=true` to ~/.moxxy/config.yaml and loads it into the running session (re-registering a default or re-discovering an installed plugin), so its contributions reappear immediately. Reverse with disable_plugin. Use when the user wants to turn a disabled plugin back on.",
111523
111409
  inputSchema: z$1.object({
111524
- packageName: z$1.string().min(1).refine((s2) => NPM_NAME_RE2.test(s2), {
111410
+ packageName: z$1.string().min(1).refine((s2) => NPM_NAME_RE.test(s2), {
111525
111411
  message: "must be a valid moxxy package name (e.g. @moxxy/plugin-browser)"
111526
111412
  }).describe("Plugin package name to enable, e.g. @moxxy/plugin-browser.")
111527
111413
  }),
@@ -111530,20 +111416,10 @@ function buildEnablePluginTool(deps) {
111530
111416
  const before = deps.snapshot();
111531
111417
  await deps.setEnabled(packageName, true);
111532
111418
  const after = deps.snapshot();
111533
- return { enabled: packageName, registered: diffSnapshot3(before, after) };
111419
+ return { enabled: packageName, registered: diffSnapshot2(before, after) };
111534
111420
  }
111535
111421
  });
111536
111422
  }
111537
- function diffSnapshot3(before, after) {
111538
- const out = {};
111539
- for (const key of ["tools", "agents", "providers", "modes", "compactors", "channels"]) {
111540
- const b3 = new Set(before[key]);
111541
- const added = after[key].filter((n2) => !b3.has(n2));
111542
- if (added.length > 0)
111543
- out[key] = added;
111544
- }
111545
- return out;
111546
- }
111547
111423
 
111548
111424
  // ../plugin-plugins-admin/dist/catalog.js
111549
111425
  var INSTALLABLE_PLUGIN_CATALOG = [
@@ -118322,6 +118198,7 @@ async function setupSessionWithConfig(opts) {
118322
118198
  config,
118323
118199
  resolver: opts.resolver,
118324
118200
  resumeSessionId: opts.resumeSessionId,
118201
+ sessionId: opts.sessionId,
118325
118202
  logger,
118326
118203
  isPluginDisabled: (pkg) => disabledPackages.has(pkg),
118327
118204
  // Surface vault secrets to tool handlers as `ctx.getSecret(name)`. The
@@ -123548,9 +123425,11 @@ async function runServeStatus(except, all) {
123548
123425
  return 0;
123549
123426
  }
123550
123427
  async function runServeForeground(argv, except, all) {
123428
+ const stickySessionId = process.env["MOXXY_SESSION_ID"]?.trim();
123551
123429
  const setup = await bootSessionWithConfig(argv, {
123552
123430
  skipKeyPrompt: true,
123553
- tolerateNoProvider: true
123431
+ tolerateNoProvider: true,
123432
+ ...stickySessionId ? { sessionId: stickySessionId } : {}
123554
123433
  });
123555
123434
  const { session, vault, config, scheduler, webhooks } = setup;
123556
123435
  const setupHandle = { scheduler, webhooks };