agentradar-cli 0.3.20 → 0.3.22
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.
|
@@ -30,7 +30,7 @@ Your primary objective is to reach your quotas and drive the company forward in
|
|
|
30
30
|
|
|
31
31
|
## Talking to humans
|
|
32
32
|
|
|
33
|
-
Write to humans so a 13-year-old would understand: a few short
|
|
33
|
+
Write to humans so a 13-year-old would understand: a few short lines, then one next step, not a list of ideas. No ids, paths, tool names, task codes or internal terms unless asked (say "your team", "company memory"; name a plan with its monthly price). Say "human" for a human. Say money in dollars, never cents; give every link with what it opens. Answer only what was asked, and say it once. Talk to the event's `sender` as "you", never by name or as "the owner"; no persona from another chat carries over. Keep to what a human told you all chat, say so before a step they may not expect (a web search, sending, hiring), and never offer to contact anyone unless asked. Never talk about how messages reach you. Do the obvious thing for "undo that"; if unclear, offer short choices. Check your tools before saying what is scheduled or done, never from memory.
|
|
34
34
|
|
|
35
35
|
## Work loop
|
|
36
36
|
|
package/dist/channel.js
CHANGED
|
@@ -122,6 +122,7 @@ export async function startChannel(runnerId, deps = {}) {
|
|
|
122
122
|
// Vadym 10342: an unconfirmed channel is reported to AgentRadar (runnerState channel_unconfirmed), so the owner sees
|
|
123
123
|
// the agent is not receiving messages; "ready" again once the session does run `inbox next`.
|
|
124
124
|
let unconfirmed = null;
|
|
125
|
+
let reportedAt = 0;
|
|
125
126
|
let warning;
|
|
126
127
|
let skillSync;
|
|
127
128
|
let teammateNotices;
|
|
@@ -203,13 +204,17 @@ export async function startChannel(runnerId, deps = {}) {
|
|
|
203
204
|
if (unconfirmed === "pending") {
|
|
204
205
|
await next({ runnerState: "channel_unconfirmed" });
|
|
205
206
|
unconfirmed = "reported";
|
|
207
|
+
reportedAt = Date.now();
|
|
206
208
|
}
|
|
207
209
|
if (unconfirmed === "reported" && polled())
|
|
208
210
|
unconfirmed = null;
|
|
209
211
|
// While reported, no runnerState: the stored state stands, and the poll still drains for wake signals.
|
|
210
212
|
// Receipt only: a preview still needs read_message before any reply (server gate). A "starting" poll cannot ack.
|
|
211
213
|
const ack = guarded && unacked.length && calledSince(lastSent) ? unacked : null;
|
|
212
|
-
|
|
214
|
+
// While reported, a short hold: the session's first `inbox next` clears the flag within seconds, not after the 200 s empty-queue hold.
|
|
215
|
+
// After 10 min still unconfirmed, back to the full hold (the session is likely never confirming; ~450 polls/h otherwise).
|
|
216
|
+
const fast = unconfirmed && Date.now() - reportedAt < (deps.unconfirmedFastMs ?? 600_000);
|
|
217
|
+
const response = await next({ ...(unconfirmed ? (fast ? { waitMs: 5_000 } : {}) : { runnerState: guarded ? "ready" : "starting" }), ...(ack ? { ackEventIds: ack } : {}) });
|
|
213
218
|
if (response.error || response.result?.isError)
|
|
214
219
|
throw new Error("AgentRadar refused `agentradar run $AR_CLI_RUNNER_ID inbox next`; check the runner key and its poll permission");
|
|
215
220
|
const content = response.result?.content;
|
package/dist/runner.js
CHANGED
|
@@ -450,8 +450,22 @@ export async function startVendorRunner(runnerId, home = homedir(), deps = {}) {
|
|
|
450
450
|
return false;
|
|
451
451
|
};
|
|
452
452
|
const shared = spawnChild("bash", [supervisor, envPath]);
|
|
453
|
-
|
|
453
|
+
// RUNNER-KILL-LEAVES-FEEDER: a `kill` reaches only this process, so pass it on to the supervisors, whose EXIT
|
|
454
|
+
// trap stops their feeders. Otherwise they outlive the runner and a restarted one delivers every event twice.
|
|
455
|
+
// SIGINT too: `kill -INT <pid>` reaches only the runner (Ctrl-C reaches the whole group anyway). SIGHUP (the
|
|
456
|
+
// vendor closes the terminal) only when a terminal runs it: a listener would undo a nohup start's ignored SIGHUP.
|
|
457
|
+
// The runner exits once the shared supervisor has; a second signal kills it at once.
|
|
458
|
+
const signals = deps.signals ?? process;
|
|
459
|
+
const names = (deps.hangup ?? process.stdout.isTTY === true)
|
|
460
|
+
? ["SIGTERM", "SIGINT", "SIGHUP"] : ["SIGTERM", "SIGINT"];
|
|
461
|
+
const unlisten = () => { for (const name of names)
|
|
462
|
+
signals.off(name, forward); };
|
|
463
|
+
const forward = () => { unlisten(); stop(); shared.kill("SIGTERM"); };
|
|
464
|
+
for (const name of names)
|
|
465
|
+
signals.once(name, forward);
|
|
466
|
+
shared.once("error", (error) => { unlisten(); stop(); reject(error); });
|
|
454
467
|
shared.once("exit", (code, signal) => {
|
|
468
|
+
unlisten();
|
|
455
469
|
stop();
|
|
456
470
|
if (!updating)
|
|
457
471
|
return resolvePromise(code ?? (signal ? 1 : 0));
|
package/dist/tool-run.js
CHANGED
|
@@ -19,6 +19,7 @@ export function envValue(contents, key) {
|
|
|
19
19
|
const quoted = raw.startsWith("'") && raw.endsWith("'") && raw.length >= 2;
|
|
20
20
|
return (quoted ? raw.slice(1, -1).replaceAll("'\\''", "'") : raw) || null;
|
|
21
21
|
}
|
|
22
|
+
const POSITIONAL_SUBJECT = { "feedback status": "feedback-id" };
|
|
22
23
|
/**
|
|
23
24
|
* #1871 slice 1: `agentradar <noun> [<sub-noun>] <verb> [--<argument> <value> ...]`, every path read from the server's
|
|
24
25
|
* tools/list (`_meta.cli.path`), so a new tool or a renamed path needs no CLI release. `command` is that path, or a
|
|
@@ -28,6 +29,12 @@ export function envValue(contents, key) {
|
|
|
28
29
|
export async function callToolOverCli(runnerId, command, argumentsJson, deps = {}, flags = []) {
|
|
29
30
|
const switches = new Set(flags.filter(([flag]) => ["help", "dry-run", "json"].includes(flag)).map(([flag]) => flag));
|
|
30
31
|
flags = flags.filter(([flag]) => !switches.has(flag));
|
|
32
|
+
// FEEDBACK-OUTCOME-NOTIFY-19720: the notice teaches `feedback status <ticket id>`; the one subject goes in as the flag it fills.
|
|
33
|
+
const subject = POSITIONAL_SUBJECT[command.slice(0, command.lastIndexOf(" "))];
|
|
34
|
+
if (subject) {
|
|
35
|
+
flags = [[subject, command.slice(command.lastIndexOf(" ") + 1)], ...flags];
|
|
36
|
+
command = command.slice(0, command.lastIndexOf(" "));
|
|
37
|
+
}
|
|
31
38
|
// Every request of a --json run, tools/list included, keeps the upgrade line off stderr.
|
|
32
39
|
if (switches.has("json"))
|
|
33
40
|
deps = { ...deps, json: true };
|
|
@@ -240,7 +247,7 @@ const serviceTools = (runnerId, tools) => {
|
|
|
240
247
|
/** Top-level `--help` headings (main 10-15: ~6 sections so 37 groups scan). */
|
|
241
248
|
const SECTIONS = {
|
|
242
249
|
Work: ["inbox", "threads", "tasks", "projects", "outcomes", "escalations", "interventions", "incidents", "briefs", "feedback", "me", "team", "browser", "files"],
|
|
243
|
-
Channels: ["channels", "contacts", "webhooks", "integrations", "join-links", "onboarding"],
|
|
250
|
+
Channels: ["channels", "contacts", "webhooks", "integrations", "join-links", "onboarding", "telegram"],
|
|
244
251
|
Access: ["permissions", "approvals", "secrets", "roles", "org-chart", "org"],
|
|
245
252
|
Data: ["vaults", "memory", "skills", "metrics", "model", "hosting", "subscriptions", "selftest"],
|
|
246
253
|
Money: ["spend", "wallet", "usage", "marketplace", "placement", "karma"],
|
|
@@ -790,7 +790,9 @@ export function paneInputReady(text, harness = AR_AGENT_HARNESS, readyPattern =
|
|
|
790
790
|
* at 50, where the ❯ prompt and the bypass footer still show; the spinner line "✽ Sautéing… (24s · ↓ 105 tokens)"
|
|
791
791
|
* shows at every width and turns into "✻ Worked for 13s" when the turn ends.
|
|
792
792
|
*/
|
|
793
|
-
|
|
793
|
+
// Hermes (hermes-agent 0.19.0) swaps its idle "❯" composer for "⚕ ❯ msg=interrupt · /queue · … Ctrl+C cancel" mid-turn,
|
|
794
|
+
// and text typed there interrupts the running turn, so the next event of a batch must wait for it.
|
|
795
|
+
export const paneBusy = (text) => /\besc to (?:interrupt|cancel)\b|\bmsg=interrupt\b/i.test(text ?? "")
|
|
794
796
|
|| /^\s*[^\w\s]\s+\S+…\s*\(\s*(?:\d+h\s*)?(?:\d+m\s*)?\d+s\b/m.test(text ?? "");
|
|
795
797
|
|
|
796
798
|
/**
|
|
@@ -380,10 +380,12 @@ compact_in_place() {
|
|
|
380
380
|
AR_COMPACT_JS="$(cd "$(dirname "$0")/.." && pwd)/pre-stop-compact.mjs" TMUX_BIN="${TMUX_BIN}" TMUX_SESSION="${TMUX_SESSION}" \
|
|
381
381
|
node --input-type=module -e '
|
|
382
382
|
import { spawn, spawnSync } from "node:child_process"; import { join } from "node:path";
|
|
383
|
-
const { compactBeforeStop, paneTail, tmux, sessionProjectsDir } = await import(process.env.AR_COMPACT_JS);
|
|
383
|
+
const { compactBeforeStop, paneTail, tmux, sessionProjectsDir, reportToServer } = await import(process.env.AR_COMPACT_JS);
|
|
384
384
|
const bin = process.env.TMUX_BIN, session = `=${process.env.TMUX_SESSION}:`;
|
|
385
385
|
const dir = process.env.CLAUDE_CONFIG_DIR ? join(process.env.CLAUDE_CONFIG_DIR, "projects") : sessionProjectsDir();
|
|
386
|
-
|
|
386
|
+
// A failed heal is reported as its end, or the next /compact reads as an overlap (COMPACTION-RACE-PARK, Pablo 11-07).
|
|
387
|
+
const report = (r) => reportToServer(r, process.env.AR_SESSION_DIR ? { dir: process.env.AR_SESSION_DIR } : {});
|
|
388
|
+
const r = await compactBeforeStop({ dir, session, workloadBusy: () => false, report, minTokens: 0,
|
|
387
389
|
run: (argv) => tmux(argv, { uid: -1, spawnFn: (_c, a, o) => spawn(bin, a, o) }),
|
|
388
390
|
capture: () => paneTail(session, { uid: -1, run: (_c, a, o) => spawnSync(bin, a, o) }) });
|
|
389
391
|
process.exit(r.compacted ? 0 : 1);' && break
|