@livx.cc/agentx 0.99.41 → 0.99.42
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/cli.js +58 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.js +58 -2
- package/dist/index.js.map +1 -1
- package/dist/tools.shell.d.ts +8 -0
- package/dist/tools.shell.js +58 -2
- package/dist/tools.shell.js.map +1 -1
- package/package.json +1 -1
package/dist/tools.shell.d.ts
CHANGED
|
@@ -141,6 +141,14 @@ declare class ShellJobRegistry {
|
|
|
141
141
|
command: string;
|
|
142
142
|
status: JobStatus;
|
|
143
143
|
}>;
|
|
144
|
+
/**
|
|
145
|
+
* Take over an ALREADY-RUNNING child as a background job. A foreground command that outruns its
|
|
146
|
+
* timeout is not necessarily a hung command — killing it throws away work that was nearly done and,
|
|
147
|
+
* worse, a launcher that spawned its own detached worker leaves that worker running with nothing
|
|
148
|
+
* tracking it. Adopting hands the model a handle instead: the command keeps going, its completion
|
|
149
|
+
* is reported like any other job, and `seed` carries the output produced before the handoff.
|
|
150
|
+
*/
|
|
151
|
+
adopt(command: string, proc: SpawnedProcess, seed?: string): string;
|
|
144
152
|
kill(id: string): boolean;
|
|
145
153
|
killAll(): void;
|
|
146
154
|
}
|
package/dist/tools.shell.js
CHANGED
|
@@ -650,6 +650,41 @@ var ShellJobRegistry = class {
|
|
|
650
650
|
list() {
|
|
651
651
|
return [...this.jobs].map(([id, j]) => ({ id, command: j.command, status: j.status }));
|
|
652
652
|
}
|
|
653
|
+
/**
|
|
654
|
+
* Take over an ALREADY-RUNNING child as a background job. A foreground command that outruns its
|
|
655
|
+
* timeout is not necessarily a hung command — killing it throws away work that was nearly done and,
|
|
656
|
+
* worse, a launcher that spawned its own detached worker leaves that worker running with nothing
|
|
657
|
+
* tracking it. Adopting hands the model a handle instead: the command keeps going, its completion
|
|
658
|
+
* is reported like any other job, and `seed` carries the output produced before the handoff.
|
|
659
|
+
*/
|
|
660
|
+
adopt(command, proc, seed = "") {
|
|
661
|
+
const id = `job-${++this.seq}`;
|
|
662
|
+
const max = this.cfg.maxBuffer ?? 256 * 1024;
|
|
663
|
+
const job = { command, buf: seed.slice(-max), status: "running", proc };
|
|
664
|
+
const append = (chunk) => {
|
|
665
|
+
const s = typeof chunk === "string" ? chunk : chunk?.toString?.("utf8") ?? "";
|
|
666
|
+
job.buf = (job.buf + s).slice(-max);
|
|
667
|
+
};
|
|
668
|
+
proc.stdout?.on("data", append);
|
|
669
|
+
proc.stderr?.on("data", append);
|
|
670
|
+
proc.on("error", (err) => {
|
|
671
|
+
if (job.status === "running") {
|
|
672
|
+
job.status = "error";
|
|
673
|
+
append(`
|
|
674
|
+
[error] ${err?.message ?? err}`);
|
|
675
|
+
this.notifyExit(id, job);
|
|
676
|
+
}
|
|
677
|
+
});
|
|
678
|
+
proc.on("close", (code) => {
|
|
679
|
+
if (job.status === "running") {
|
|
680
|
+
job.status = "exited";
|
|
681
|
+
job.exitCode = code ?? void 0;
|
|
682
|
+
this.notifyExit(id, job);
|
|
683
|
+
}
|
|
684
|
+
});
|
|
685
|
+
this.jobs.set(id, job);
|
|
686
|
+
return id;
|
|
687
|
+
}
|
|
653
688
|
kill(id) {
|
|
654
689
|
const j = this.jobs.get(id);
|
|
655
690
|
if (!j) return false;
|
|
@@ -698,7 +733,7 @@ function makeRealShellTool(options) {
|
|
|
698
733
|
return { ...this, description: rebound.description, run: rebound.run, withCwd: rebound.withCwd };
|
|
699
734
|
},
|
|
700
735
|
get description() {
|
|
701
|
-
return
|
|
736
|
+
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. " + (options.registry?.adopt ? `A command still running after ${defaultTimeoutMs}ms is NOT killed \u2014 it is handed to a background job and you get its id, so you can keep checking on it (\`[still running]\`). Pass \`timeoutMs\` (max ${maxTimeoutMs}) to wait longer in the foreground, and always bound network commands yourself (e.g. \`curl -m 10\`). ` : `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
737
|
},
|
|
703
738
|
// Declared so transports above the agent loop can size their own deadline with headroom (see AgentTool.maxDurationMs).
|
|
704
739
|
maxDurationMs: maxTimeoutMs,
|
|
@@ -737,9 +772,12 @@ function makeRealShellTool(options) {
|
|
|
737
772
|
else ctx.signal.addEventListener("abort", onAbort, { once: true });
|
|
738
773
|
}
|
|
739
774
|
let timedOut = false;
|
|
740
|
-
|
|
775
|
+
let onDeadline = () => {
|
|
741
776
|
timedOut = true;
|
|
742
777
|
ctl.abort();
|
|
778
|
+
};
|
|
779
|
+
const timer = setTimeout(() => {
|
|
780
|
+
onDeadline();
|
|
743
781
|
}, timeoutMs);
|
|
744
782
|
let pend = "";
|
|
745
783
|
let flushTimer = null;
|
|
@@ -770,6 +808,18 @@ function makeRealShellTool(options) {
|
|
|
770
808
|
}
|
|
771
809
|
if (ctl.signal.aborted) killGroup(proc, "SIGKILL");
|
|
772
810
|
else ctl.signal.addEventListener("abort", () => killGroup(proc, "SIGKILL"), { once: true });
|
|
811
|
+
const registry = options.registry;
|
|
812
|
+
if (registry?.adopt) {
|
|
813
|
+
onDeadline = () => {
|
|
814
|
+
timedOut = true;
|
|
815
|
+
if (settled) return;
|
|
816
|
+
flushEmit(ctx);
|
|
817
|
+
proc.stdout?.off?.("data", collect);
|
|
818
|
+
proc.stderr?.off?.("data", collect);
|
|
819
|
+
const id = registry.adopt(cmd, proc, out);
|
|
820
|
+
finish(handoffFor(timeoutMs, id, clean(out), !!registry.notifiesOnExit));
|
|
821
|
+
};
|
|
822
|
+
}
|
|
773
823
|
const collect = (chunk) => {
|
|
774
824
|
const s = typeof chunk === "string" ? chunk : chunk?.toString?.("utf8") ?? "";
|
|
775
825
|
out += s;
|
|
@@ -802,6 +852,12 @@ function makeRealShellTool(options) {
|
|
|
802
852
|
}
|
|
803
853
|
};
|
|
804
854
|
}
|
|
855
|
+
function handoffFor(timeoutMs, id, body, notifies) {
|
|
856
|
+
const head = `[still running] exceeded ${timeoutMs}ms, so it was handed to background job ${id} \u2014 NOT killed, it is still going. Check on it with ShellOutput({id:"${id}"}) / ShellStatus({id:"${id}"}), stop it with ShellKill({id:"${id}"}). ` + (notifies ? "Its completion will be reported to you, so you may continue with other work meanwhile." : "Nothing will tell you when it finishes \u2014 poll it yourself before you rely on its result.");
|
|
857
|
+
return body ? `${head}
|
|
858
|
+
Output so far:
|
|
859
|
+
${body}` : head;
|
|
860
|
+
}
|
|
805
861
|
function reasonFor(timedOut, timeoutMs, body) {
|
|
806
862
|
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)";
|
|
807
863
|
return body ? `${head}
|