@livx.cc/agentx 0.99.39 → 0.99.41

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.
@@ -287,6 +287,8 @@ function makeWebFetchTool(options = {}) {
287
287
  return {
288
288
  name: "WebFetch",
289
289
  description: "Fetch an http/https URL and return its readable text (HTML is stripped to text). Use to read docs or web pages. Returns the status line then up to ~100k chars of content.",
290
+ // Declared so a transport above the loop sizes its deadline around this one (see AgentTool.maxDurationMs).
291
+ maxDurationMs: timeoutMs,
290
292
  parameters: { type: "object", required: ["url"], properties: { url: { type: "string", description: "absolute http(s) URL" } } },
291
293
  async run({ url }) {
292
294
  const doFetch = options.fetch ?? globalThis.fetch;
@@ -564,6 +566,11 @@ async function nodeSpawn() {
564
566
  if (!_spawn) _spawn = (await import("child_process")).spawn;
565
567
  return _spawn;
566
568
  }
569
+ function formatJobExit(n) {
570
+ return `[background job ${n.id} ${n.status}${n.exitCode != null ? ` exit ${n.exitCode}` : ""}] \`${n.command}\`
571
+ ` + (n.tail ? `${n.tail}
572
+ ` : "(no output)\n") + `Read the full output with ShellOutput({id:"${n.id}"}).`;
573
+ }
567
574
  var ShellJobRegistry = class {
568
575
  constructor(cfg) {
569
576
  this.cfg = cfg;
@@ -592,12 +599,14 @@ var ShellJobRegistry = class {
592
599
  job.status = "error";
593
600
  append(`
594
601
  [error] ${err?.message ?? err}`);
602
+ this.notifyExit(id, job);
595
603
  }
596
604
  });
597
605
  proc.on("close", (code) => {
598
606
  if (job.status === "running") {
599
607
  job.status = "exited";
600
608
  job.exitCode = code ?? void 0;
609
+ this.notifyExit(id, job);
601
610
  }
602
611
  });
603
612
  } catch (e) {
@@ -607,6 +616,29 @@ var ShellJobRegistry = class {
607
616
  this.jobs.set(id, job);
608
617
  return id;
609
618
  }
619
+ /** Fire `onExit` at most once per job, with the tail so the model can act without a second round-trip. */
620
+ notified = /* @__PURE__ */ new Set();
621
+ notifyExit(id, job) {
622
+ if (this.notified.has(id) || !this.cfg.onExit) return;
623
+ this.notified.add(id);
624
+ try {
625
+ this.cfg.onExit({ id, command: job.command, status: job.status, exitCode: job.exitCode, tail: clean(job.buf).slice(-4e3) });
626
+ } catch {
627
+ }
628
+ }
629
+ /**
630
+ * Wire (or rewire) the completion callback AFTER construction. A host that only gets its agent handle
631
+ * once the Agent is constructed (the library's own `fullAgentOptions` preset, any embedder) could not
632
+ * pass `onExit` up front, so background completions were silently REPL-only. Set it here instead.
633
+ */
634
+ setOnExit(fn) {
635
+ this.cfg.onExit = fn;
636
+ }
637
+ /** Whether a finished job will actually be reported to the model. The Shell tool's description reads
638
+ * this so it can't promise a completion notice on a host that discards it. */
639
+ get notifiesOnExit() {
640
+ return !!this.cfg.onExit;
641
+ }
610
642
  /** Current tail output for a job (null = no such job). */
611
643
  output(id) {
612
644
  return this.jobs.get(id)?.buf ?? (this.jobs.has(id) ? "" : null);
@@ -639,9 +671,37 @@ var ShellJobRegistry = class {
639
671
  function makeRealShellTool(options) {
640
672
  const defaultTimeoutMs = options.timeoutMs ?? 12e4;
641
673
  const maxTimeoutMs = Math.max(options.maxTimeoutMs ?? 6e5, defaultTimeoutMs);
674
+ const backgroundDoc = () => {
675
+ const base = "Set `background:true` for long-running processes (servers, watchers) and for anything that already timed out in the foreground \u2014 returns a job id immediately; poll with ShellOutput/ShellStatus, stop with ShellKill. ";
676
+ const notify = options.registry?.notifiesOnExit ? "Its completion is reported back to you when it finishes." : "NOTE: nothing will tell you when it finishes \u2014 poll ShellOutput/ShellStatus yourself, or you will never see its result.";
677
+ const survival = options.jobsSurviveRun === false ? "The job does NOT survive the end of this run, so do not stop while you still need its result." : "It keeps running across turns.";
678
+ return `${base}${notify} ${survival}`;
679
+ };
642
680
  return {
643
681
  name: "Shell",
644
- description: `Run a shell command via /bin/sh in the working directory. Executes any installed binary \u2014 ls, cat, grep, git, bun, node, curl, scripts, etc. Returns combined stdout+stderr; non-zero exits are prefixed \`[exit N]\`. Runs non-interactively with no terminal (stdin is /dev/null): commands that prompt for input fail fast rather than hang \u2014 for privileged actions use a non-interactive flag (e.g. \`sudo -n\`), or ask the user to run the command themselves. Each command is killed after ${defaultTimeoutMs}ms (result \`[exit 124]\` with whatever output it produced) \u2014 pass \`timeoutMs\` (max ${maxTimeoutMs}) for a legitimately slower command, and always bound network commands yourself (e.g. \`curl -m 10\`). Set \`background:true\` for long-running processes (servers, watchers) \u2014 returns a job id immediately; poll with ShellOutput, stop with ShellKill.`,
682
+ // Rebind for an isolated child agent (git worktree): same policy/env/timeouts, new cwd. The
683
+ // `registry` is deliberately dropped — it is bound to the PARENT's cwd (and, in hosts like
684
+ // shraga-ee, to the parent's session), so a background job started from the child would run
685
+ // outside the child's isolation and report into the parent. Background stays a parent capability
686
+ // (`makeShellJobTools`' companions drop out of an isolated child entirely — see their `withCwd`).
687
+ // Only `run` is rebuilt: everything else is carried over from THIS instance, because a host may
688
+ // have mutated the tool AFTER construction (shraga-ee renames `Shell` -> `Bash` and its system
689
+ // prompt says `Bash` everywhere). Rebuilding from `options` alone silently dropped that rename,
690
+ // so a worktree child advertised `Shell` while being told to call `Bash` — an unknown-tool error
691
+ // on its first call, and a hard failure for an agentType def whose allowlist names `Bash`.
692
+ // `description` is the ONE thing taken from the rebound tool instead: it is derived from the
693
+ // options and states whether `background:true` works — the rebind drops the registry, so carrying
694
+ // the parent's text over would advertise a background capability the child does not have. (The
695
+ // spread also collapses the getter below to a plain value, which is why this override is explicit.)
696
+ withCwd(cwd) {
697
+ const rebound = makeRealShellTool({ ...options, cwd, registry: void 0 });
698
+ return { ...this, description: rebound.description, run: rebound.run, withCwd: rebound.withCwd };
699
+ },
700
+ get description() {
701
+ return `Run a shell command via /bin/sh in the working directory. Executes any installed binary \u2014 ls, cat, grep, git, bun, node, curl, scripts, etc. Returns combined stdout+stderr; non-zero exits are prefixed \`[exit N]\`. Runs non-interactively with no terminal (stdin is /dev/null): commands that prompt for input fail fast rather than hang \u2014 for privileged actions use a non-interactive flag (e.g. \`sudo -n\`), or ask the user to run the command themselves. Each command is killed after ${defaultTimeoutMs}ms (result \`[exit 124]\` with whatever output it produced) \u2014 pass \`timeoutMs\` (max ${maxTimeoutMs}) for a legitimately slower command, and always bound network commands yourself (e.g. \`curl -m 10\`). ` + backgroundDoc();
702
+ },
703
+ // Declared so transports above the agent loop can size their own deadline with headroom (see AgentTool.maxDurationMs).
704
+ maxDurationMs: maxTimeoutMs,
645
705
  parameters: {
646
706
  type: "object",
647
707
  required: ["command"],
@@ -743,15 +803,18 @@ function makeRealShellTool(options) {
743
803
  };
744
804
  }
745
805
  function reasonFor(timedOut, timeoutMs, body) {
746
- const head = timedOut ? `[exit 124] timed out after ${timeoutMs}ms (killed)` : "[exit 130] cancelled (killed)";
806
+ const head = timedOut ? `[exit 124] timed out after ${timeoutMs}ms (killed). Re-running this command unchanged will time out again. Either re-run it with background:true (returns a job id immediately; poll with ShellOutput/ShellStatus, stop with ShellKill) or narrow it so it can finish \u2014 bound it, scope it, or ask for less.` : "[exit 130] cancelled (killed)";
747
807
  return body ? `${head}
808
+ Partial output before the kill:
748
809
  ${body}` : head;
749
810
  }
750
811
  var NO_JOB = (id) => `Error: no background job '${id}'. Use ShellStatus with no id to list jobs, or start one with Shell({background:true}).`;
751
812
  function makeShellJobTools(registry) {
752
813
  const idParam = { type: "object", properties: { id: { type: "string", description: "the job id from Shell({background:true})" } } };
814
+ const dropOnRebind = { withCwd: () => void 0 };
753
815
  return [
754
816
  {
817
+ ...dropOnRebind,
755
818
  name: "ShellOutput",
756
819
  description: "Read the accumulated output (tail) of a background Shell job by id.",
757
820
  parameters: { type: "object", required: ["id"], properties: { id: { type: "string" } } },
@@ -764,6 +827,7 @@ ${clean(out) || "(no output yet)"}`;
764
827
  }
765
828
  },
766
829
  {
830
+ ...dropOnRebind,
767
831
  name: "ShellStatus",
768
832
  description: "Status of a background Shell job (running/exited/killed + exit code). Omit `id` to list all jobs.",
769
833
  parameters: idParam,
@@ -777,6 +841,7 @@ ${clean(out) || "(no output yet)"}`;
777
841
  }
778
842
  },
779
843
  {
844
+ ...dropOnRebind,
780
845
  name: "ShellKill",
781
846
  description: "Stop a running background Shell job by id (SIGTERM).",
782
847
  parameters: { type: "object", required: ["id"], properties: { id: { type: "string" } } },
@@ -788,6 +853,7 @@ ${clean(out) || "(no output yet)"}`;
788
853
  }
789
854
  export {
790
855
  ShellJobRegistry,
856
+ formatJobExit,
791
857
  makeRealShellTool,
792
858
  makeShellJobTools
793
859
  };