@ozzylabs/feedradar 0.1.6 → 0.1.8
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/README.md +2 -1
- package/dist/agents/_boundary.d.ts +74 -1
- package/dist/agents/_boundary.d.ts.map +1 -1
- package/dist/agents/_boundary.js +152 -0
- package/dist/agents/_boundary.js.map +1 -1
- package/dist/claude-skills/dismiss/SKILL.md +18 -12
- package/dist/claude-skills/research/SKILL.md +21 -1
- package/dist/claude-skills/review/SKILL.md +23 -1
- package/dist/claude-skills/update/SKILL.md +24 -2
- package/dist/cli/_commit-path.d.ts +33 -0
- package/dist/cli/_commit-path.d.ts.map +1 -0
- package/dist/cli/_commit-path.js +43 -0
- package/dist/cli/_commit-path.js.map +1 -0
- package/dist/cli/dismiss.d.ts +38 -7
- package/dist/cli/dismiss.d.ts.map +1 -1
- package/dist/cli/dismiss.js +239 -54
- package/dist/cli/dismiss.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +7 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/items.d.ts +44 -0
- package/dist/cli/items.d.ts.map +1 -0
- package/dist/cli/items.js +288 -0
- package/dist/cli/items.js.map +1 -0
- package/dist/cli/research.d.ts +21 -0
- package/dist/cli/research.d.ts.map +1 -1
- package/dist/cli/research.js +360 -54
- package/dist/cli/research.js.map +1 -1
- package/dist/cli/review.d.ts +23 -0
- package/dist/cli/review.d.ts.map +1 -1
- package/dist/cli/review.js +462 -2
- package/dist/cli/review.js.map +1 -1
- package/dist/cli/source.d.ts.map +1 -1
- package/dist/cli/source.js +18 -0
- package/dist/cli/source.js.map +1 -1
- package/dist/cli/triage.d.ts +136 -0
- package/dist/cli/triage.d.ts.map +1 -0
- package/dist/cli/triage.js +1110 -0
- package/dist/cli/triage.js.map +1 -0
- package/dist/cli/undismiss.d.ts +30 -0
- package/dist/cli/undismiss.d.ts.map +1 -0
- package/dist/cli/undismiss.js +133 -0
- package/dist/cli/undismiss.js.map +1 -0
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +429 -141
- package/dist/cli/update.js.map +1 -1
- package/dist/cli/workflow/generate-combined-with-triage.d.ts +163 -0
- package/dist/cli/workflow/generate-combined-with-triage.d.ts.map +1 -0
- package/dist/cli/workflow/generate-combined-with-triage.js +582 -0
- package/dist/cli/workflow/generate-combined-with-triage.js.map +1 -0
- package/dist/cli/workflow.d.ts +6 -5
- package/dist/cli/workflow.d.ts.map +1 -1
- package/dist/cli/workflow.js +13 -8
- package/dist/cli/workflow.js.map +1 -1
- package/dist/core/feeds/json-api.d.ts +5 -2
- package/dist/core/feeds/json-api.d.ts.map +1 -1
- package/dist/core/feeds/json-api.js +99 -13
- package/dist/core/feeds/json-api.js.map +1 -1
- package/dist/core/feeds/types.d.ts +26 -0
- package/dist/core/feeds/types.d.ts.map +1 -1
- package/dist/core/recipes.d.ts.map +1 -1
- package/dist/core/recipes.js +6 -0
- package/dist/core/recipes.js.map +1 -1
- package/dist/core/transitions.d.ts +30 -0
- package/dist/core/transitions.d.ts.map +1 -0
- package/dist/core/transitions.js +103 -0
- package/dist/core/transitions.js.map +1 -0
- package/dist/core/triage/adapter.d.ts +80 -0
- package/dist/core/triage/adapter.d.ts.map +1 -0
- package/dist/core/triage/adapter.js +128 -0
- package/dist/core/triage/adapter.js.map +1 -0
- package/dist/core/triage/index.d.ts +105 -0
- package/dist/core/triage/index.d.ts.map +1 -0
- package/dist/core/triage/index.js +246 -0
- package/dist/core/triage/index.js.map +1 -0
- package/dist/core/triage/prompt.d.ts +30 -0
- package/dist/core/triage/prompt.d.ts.map +1 -0
- package/dist/core/triage/prompt.js +157 -0
- package/dist/core/triage/prompt.js.map +1 -0
- package/dist/core/triage/response.d.ts +114 -0
- package/dist/core/triage/response.d.ts.map +1 -0
- package/dist/core/triage/response.js +188 -0
- package/dist/core/triage/response.js.map +1 -0
- package/dist/gemini-commands/research.toml +1 -1
- package/dist/gemini-commands/review.toml +1 -1
- package/dist/gemini-commands/update.toml +1 -1
- package/dist/recipes/aws-whats-new.yaml +36 -1
- package/dist/recipes/dev-to.yaml +24 -0
- package/dist/schemas/item.d.ts +151 -5
- package/dist/schemas/item.d.ts.map +1 -1
- package/dist/schemas/item.js +164 -4
- package/dist/schemas/item.js.map +1 -1
- package/dist/schemas/recipe.d.ts +11 -1
- package/dist/schemas/recipe.d.ts.map +1 -1
- package/dist/schemas/recipe.js +10 -1
- package/dist/schemas/recipe.js.map +1 -1
- package/dist/schemas/source.d.ts +65 -4
- package/dist/schemas/source.d.ts.map +1 -1
- package/dist/schemas/source.js +65 -3
- package/dist/schemas/source.js.map +1 -1
- package/dist/skills/research/SKILL.md +57 -1
- package/dist/skills/review/SKILL.md +65 -1
- package/dist/skills/update/SKILL.md +54 -1
- package/dist/templates/agents/AGENTS.md +30 -0
- package/dist/templates/workflows/combined-with-triage.template.yaml.tmpl +132 -0
- package/package.json +1 -1
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { AgentId } from "../../schemas/research.js";
|
|
2
|
+
/**
|
|
3
|
+
* Triage channel adapter (ADR-0018 §W4 / §W-E-2).
|
|
4
|
+
*
|
|
5
|
+
* Separated from `src/agents/AgentAdapter` (research / review / update) on
|
|
6
|
+
* purpose: the triage prompt shape is different (JSON response, no file
|
|
7
|
+
* writes), and folding it into the existing interface would either pollute
|
|
8
|
+
* the contract or force per-adapter `triage()` no-ops. ADR-0018 §W4
|
|
9
|
+
* recommends "別 channel" so this module owns the triage spawn.
|
|
10
|
+
*
|
|
11
|
+
* Defaults shell out to the same agent CLIs (`claude`, `codex`, `gemini`,
|
|
12
|
+
* `copilot`). Tests inject `TriageRunner` instead of spawning a real CLI.
|
|
13
|
+
*
|
|
14
|
+
* Rate-limit policy (W-E-2):
|
|
15
|
+
*
|
|
16
|
+
* The runner classifies its return value into one of `ok` / `rate-limited`
|
|
17
|
+
* / `error` so the orchestrator can apply per-class retry policy:
|
|
18
|
+
*
|
|
19
|
+
* - `ok`: agent returned 0 exit, stdout has the JSON array — bubble up.
|
|
20
|
+
* - `rate-limited`: agent stderr / stdout looks like 429 / 503 — exponential
|
|
21
|
+
* backoff (1s, 2s, 4s, ... cap 60s, max 3 retries). After exhaustion,
|
|
22
|
+
* the orchestrator demotes affected items to `triaged_unsure` with reason
|
|
23
|
+
* `"rate-limited"`.
|
|
24
|
+
* - `error`: any other non-zero exit — bubble up to the orchestrator which
|
|
25
|
+
* applies the global fallback path (all items → `triaged_unsure`,
|
|
26
|
+
* `fallback: true`).
|
|
27
|
+
*
|
|
28
|
+
* The 429 / 503 classifier is intentionally permissive (substring match on
|
|
29
|
+
* the error text). Cheap-model CLIs format rate-limit errors in
|
|
30
|
+
* idiosyncratic ways (HTTP status code in JSON envelope, plain "Too Many
|
|
31
|
+
* Requests" in stderr, etc.) and a precise regex would miss variants
|
|
32
|
+
* silently — false positives here just trigger a backoff retry, which is
|
|
33
|
+
* harmless.
|
|
34
|
+
*/
|
|
35
|
+
export interface TriageRunInput {
|
|
36
|
+
agent: AgentId;
|
|
37
|
+
prompt: string;
|
|
38
|
+
cwd: string;
|
|
39
|
+
}
|
|
40
|
+
export type TriageRunStatus = "ok" | "rate-limited" | "error";
|
|
41
|
+
export interface TriageRunResult {
|
|
42
|
+
status: TriageRunStatus;
|
|
43
|
+
/** Raw agent stdout (used for JSON extraction on `ok`, debug on others). */
|
|
44
|
+
stdout: string;
|
|
45
|
+
/** Raw agent stderr (used for error reporting + rate-limit classification). */
|
|
46
|
+
stderr: string;
|
|
47
|
+
/** Exit code from the spawned CLI; `0` on `ok`, non-zero otherwise. */
|
|
48
|
+
exitCode: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Function shape for the agent-CLI runner. The default implementation
|
|
52
|
+
* shells out to the real `claude` / `codex` / `gemini` / `copilot` CLIs.
|
|
53
|
+
* Tests inject a fake so end-to-end coverage does not require an
|
|
54
|
+
* authenticated CLI on PATH.
|
|
55
|
+
*/
|
|
56
|
+
export type TriageRunner = (input: TriageRunInput) => Promise<TriageRunResult>;
|
|
57
|
+
/**
|
|
58
|
+
* Cheap heuristic that flags a CLI failure as rate-limited.
|
|
59
|
+
*
|
|
60
|
+
* Exported so the orchestrator (`index.ts`) and tests can rely on the same
|
|
61
|
+
* classifier. Matches on substrings rather than exact codes because the
|
|
62
|
+
* cheap-model CLIs format rate-limit errors inconsistently — they all
|
|
63
|
+
* include one of the listed needles somewhere in stdout / stderr.
|
|
64
|
+
*/
|
|
65
|
+
export declare function looksLikeRateLimit(text: string): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Default runner: spawn the per-agent CLI and capture stdout / stderr.
|
|
68
|
+
*
|
|
69
|
+
* Returns `{ status: "ok" }` only on exit code 0. Non-zero exit triggers
|
|
70
|
+
* the rate-limit classifier; if the heuristic matches, we surface
|
|
71
|
+
* `rate-limited` so the orchestrator's exponential backoff can engage,
|
|
72
|
+
* otherwise we surface `error` so the global fallback path runs.
|
|
73
|
+
*
|
|
74
|
+
* `ENOENT` (CLI not on PATH) propagates as `{ status: "error" }` rather
|
|
75
|
+
* than a thrown exception so the orchestrator's fallback path handles it
|
|
76
|
+
* the same way as any other CLI failure — triage shouldn't kill the
|
|
77
|
+
* workflow just because the user hasn't installed an optional CLI.
|
|
78
|
+
*/
|
|
79
|
+
export declare function runTriageAgentCli(input: TriageRunInput): Promise<TriageRunResult>;
|
|
80
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/core/triage/adapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,cAAc,GAAG,OAAO,CAAC;AAE9D,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,eAAe,CAAC;IACxB,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,cAAc,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;AAE/E;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAcxD;AAgDD;;;;;;;;;;;;GAYG;AACH,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CA2CvF"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
/**
|
|
3
|
+
* Cheap heuristic that flags a CLI failure as rate-limited.
|
|
4
|
+
*
|
|
5
|
+
* Exported so the orchestrator (`index.ts`) and tests can rely on the same
|
|
6
|
+
* classifier. Matches on substrings rather than exact codes because the
|
|
7
|
+
* cheap-model CLIs format rate-limit errors inconsistently — they all
|
|
8
|
+
* include one of the listed needles somewhere in stdout / stderr.
|
|
9
|
+
*/
|
|
10
|
+
export function looksLikeRateLimit(text) {
|
|
11
|
+
const haystack = text.toLowerCase();
|
|
12
|
+
return [
|
|
13
|
+
"429",
|
|
14
|
+
"rate limit",
|
|
15
|
+
"rate-limit",
|
|
16
|
+
"rate_limited",
|
|
17
|
+
"too many requests",
|
|
18
|
+
"503",
|
|
19
|
+
"service unavailable",
|
|
20
|
+
"quota exceeded",
|
|
21
|
+
"resource_exhausted",
|
|
22
|
+
"resource exhausted",
|
|
23
|
+
].some((needle) => haystack.includes(needle));
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Per-agent CLI invocation matrix. The triage channel reuses the same
|
|
27
|
+
* binaries as the research channel (claude-code → `claude`, etc.) but with
|
|
28
|
+
* triage-specific flags:
|
|
29
|
+
*
|
|
30
|
+
* - All adapters pass the prompt as the first argument and read stdin for
|
|
31
|
+
* the trigger to start (we keep stdin empty — the prompt itself contains
|
|
32
|
+
* the full request because the response shape is just a JSON array, not a
|
|
33
|
+
* file write).
|
|
34
|
+
* - We launch each CLI in the equivalent "non-interactive, full-permission"
|
|
35
|
+
* mode that the research adapter uses, so the user does not need to
|
|
36
|
+
* re-authorize tools. See the individual `src/agents/*.ts` files for the
|
|
37
|
+
* rationale on each flag set.
|
|
38
|
+
*
|
|
39
|
+
* For the cheap-model channel intent (gemini-2.5-flash-lite,
|
|
40
|
+
* claude-haiku-4-5), the model selection is left to the agent CLI's own
|
|
41
|
+
* config — ADR-0018 §W4 explicitly leaves "which model the triage channel
|
|
42
|
+
* routes to" as adapter-internal, not schema-modeled. Users who want a
|
|
43
|
+
* specific cheap model set it via `gemini config set model …` (or
|
|
44
|
+
* equivalent) on their workstation.
|
|
45
|
+
*/
|
|
46
|
+
function buildSpawnArgs(agent, prompt) {
|
|
47
|
+
switch (agent) {
|
|
48
|
+
case "claude-code":
|
|
49
|
+
return {
|
|
50
|
+
command: "claude",
|
|
51
|
+
args: ["-p", prompt, "--output-format", "text", "--permission-mode", "bypassPermissions"],
|
|
52
|
+
};
|
|
53
|
+
case "gemini-cli":
|
|
54
|
+
return {
|
|
55
|
+
command: "gemini",
|
|
56
|
+
args: ["-p", prompt, "-y", "--skip-trust", "--output-format", "text"],
|
|
57
|
+
};
|
|
58
|
+
case "codex-cli":
|
|
59
|
+
return {
|
|
60
|
+
command: "codex",
|
|
61
|
+
args: ["exec", "--dangerously-bypass-approvals-and-sandbox", prompt],
|
|
62
|
+
};
|
|
63
|
+
case "copilot":
|
|
64
|
+
return {
|
|
65
|
+
command: "copilot",
|
|
66
|
+
args: ["-p", prompt, "--allow-all-paths", "--allow-all-tools"],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Default runner: spawn the per-agent CLI and capture stdout / stderr.
|
|
72
|
+
*
|
|
73
|
+
* Returns `{ status: "ok" }` only on exit code 0. Non-zero exit triggers
|
|
74
|
+
* the rate-limit classifier; if the heuristic matches, we surface
|
|
75
|
+
* `rate-limited` so the orchestrator's exponential backoff can engage,
|
|
76
|
+
* otherwise we surface `error` so the global fallback path runs.
|
|
77
|
+
*
|
|
78
|
+
* `ENOENT` (CLI not on PATH) propagates as `{ status: "error" }` rather
|
|
79
|
+
* than a thrown exception so the orchestrator's fallback path handles it
|
|
80
|
+
* the same way as any other CLI failure — triage shouldn't kill the
|
|
81
|
+
* workflow just because the user hasn't installed an optional CLI.
|
|
82
|
+
*/
|
|
83
|
+
export async function runTriageAgentCli(input) {
|
|
84
|
+
const { command, args } = buildSpawnArgs(input.agent, input.prompt);
|
|
85
|
+
return new Promise((resolve) => {
|
|
86
|
+
let child;
|
|
87
|
+
try {
|
|
88
|
+
child = spawn(command, args, { cwd: input.cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
92
|
+
resolve({ status: "error", stdout: "", stderr: `spawn failed: ${message}`, exitCode: -1 });
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
let stdout = "";
|
|
96
|
+
let stderr = "";
|
|
97
|
+
child.stdout?.on("data", (chunk) => {
|
|
98
|
+
stdout += chunk.toString();
|
|
99
|
+
});
|
|
100
|
+
child.stderr?.on("data", (chunk) => {
|
|
101
|
+
stderr += chunk.toString();
|
|
102
|
+
});
|
|
103
|
+
child.on("error", (err) => {
|
|
104
|
+
// ENOENT, EACCES, etc. — surface as `error` so the orchestrator's
|
|
105
|
+
// global fallback runs. Storing the formatted message in stderr lets
|
|
106
|
+
// the audit log capture what went wrong.
|
|
107
|
+
resolve({
|
|
108
|
+
status: "error",
|
|
109
|
+
stdout,
|
|
110
|
+
stderr: `${stderr}\nspawn error: ${err.message}`.trim(),
|
|
111
|
+
exitCode: -1,
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
child.on("close", (code) => {
|
|
115
|
+
const exitCode = code ?? 0;
|
|
116
|
+
if (exitCode === 0) {
|
|
117
|
+
resolve({ status: "ok", stdout, stderr, exitCode });
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const combined = `${stderr}\n${stdout}`;
|
|
121
|
+
const status = looksLikeRateLimit(combined) ? "rate-limited" : "error";
|
|
122
|
+
resolve({ status, stdout, stderr, exitCode });
|
|
123
|
+
});
|
|
124
|
+
// Close stdin immediately — the prompt is on argv, not stdin.
|
|
125
|
+
child.stdin?.end();
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../src/core/triage/adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { Item, TriageDecision } from "../../schemas/item.js";
|
|
2
|
+
import type { SourceTriagePolicy } from "../../schemas/source.js";
|
|
3
|
+
import { type TriageRunner } from "./adapter.js";
|
|
4
|
+
/**
|
|
5
|
+
* Public API for the triage channel (ADR-0018 PR-2).
|
|
6
|
+
*
|
|
7
|
+
* `triageItems(items, options)` runs every supplied `detected` item through
|
|
8
|
+
* the configured agent CLI and returns a `Map<itemId, TriageDecision>`
|
|
9
|
+
* suitable for the PR-3 CLI to merge into `items/<id>.yaml > triage:`.
|
|
10
|
+
*
|
|
11
|
+
* The contract is:
|
|
12
|
+
*
|
|
13
|
+
* - **Every input item gets a decision.** Hallucinated ids, agent omission,
|
|
14
|
+
* parse errors, and total fallbacks all resolve to `decision: "unsure"`
|
|
15
|
+
* entries. The caller can rely on `result.decisions.size === items.length`
|
|
16
|
+
* for the common case (we keep the invariant even when the agent omits
|
|
17
|
+
* ids — the orchestrator fills the gap).
|
|
18
|
+
* - **`fallback: true` only when the agent itself failed end-to-end.** A
|
|
19
|
+
* per-item demotion (low confidence, digest without group) is NOT a
|
|
20
|
+
* fallback; only "agent CLI down, all items unsure" sets the flag.
|
|
21
|
+
* - **Soft-fail on rate limit (W-E-2).** Persistent 429 / 503 after
|
|
22
|
+
* exponential backoff demotes affected items to `unsure` with reason
|
|
23
|
+
* `"rate-limited"` rather than crashing the orchestrator. The workflow
|
|
24
|
+
* continues so subsequent sources / commands run.
|
|
25
|
+
* - **Audit log (W-E-3).** When `auditLog` is supplied, the full request +
|
|
26
|
+
* raw response + parsed decisions are appended as JSONL — one line per
|
|
27
|
+
* triage call. Default off, so `radar triage` with no flag has the same
|
|
28
|
+
* storage footprint as before.
|
|
29
|
+
*/
|
|
30
|
+
export interface TriageItemsOptions {
|
|
31
|
+
/** Per-source policy block. Drives prompt rules + confidence threshold. */
|
|
32
|
+
policy: SourceTriagePolicy;
|
|
33
|
+
/**
|
|
34
|
+
* Agent identifier to use for the triage call. Almost always `policy.agent`;
|
|
35
|
+
* exposed as a separate field so the CLI can override via
|
|
36
|
+
* `--agent <id>` for ad-hoc retries without mutating the source YAML.
|
|
37
|
+
*/
|
|
38
|
+
agent: string;
|
|
39
|
+
/** Working directory for the spawned CLI. Defaults to `process.cwd()`. */
|
|
40
|
+
cwd?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Optional runner injection. Production code uses the default
|
|
43
|
+
* `runTriageAgentCli`; tests inject a fake so the suite does not depend
|
|
44
|
+
* on `claude` / `gemini` / `codex` / `copilot` being on PATH.
|
|
45
|
+
*/
|
|
46
|
+
runner?: TriageRunner;
|
|
47
|
+
/**
|
|
48
|
+
* When set, raw request / response / parsed decisions are appended to
|
|
49
|
+
* this path as JSONL (one record per `triageItems()` call). Off by
|
|
50
|
+
* default — see ADR-0018 §W-E-3 for the storage-cost rationale.
|
|
51
|
+
*/
|
|
52
|
+
auditLog?: string;
|
|
53
|
+
/** Override the ISO timestamp stamped on every decision. For test determinism. */
|
|
54
|
+
now?: () => string;
|
|
55
|
+
/**
|
|
56
|
+
* Maximum retry attempts for rate-limited (429 / 503) responses. Default
|
|
57
|
+
* 3 — matches ADR-0018 §W-E-2's recommendation. Each retry waits
|
|
58
|
+
* `min(initialDelayMs * 2^(attempt-1), maxDelayMs)`.
|
|
59
|
+
*/
|
|
60
|
+
maxRetries?: number;
|
|
61
|
+
/** Initial backoff delay (ms). Default 1000. */
|
|
62
|
+
initialDelayMs?: number;
|
|
63
|
+
/** Backoff cap (ms). Default 60_000. */
|
|
64
|
+
maxDelayMs?: number;
|
|
65
|
+
/** Sleep override (ms → Promise). Defaults to `setTimeout`. Test seam. */
|
|
66
|
+
sleep?: (ms: number) => Promise<void>;
|
|
67
|
+
}
|
|
68
|
+
export interface TriageResult {
|
|
69
|
+
/**
|
|
70
|
+
* One `TriageDecision` per input item, keyed by `item.id`. Includes items
|
|
71
|
+
* the agent omitted entirely (those carry `decision: "unsure"` + reason
|
|
72
|
+
* `"agent-omitted"`). Size equals `items.length` post-call.
|
|
73
|
+
*/
|
|
74
|
+
decisions: Map<string, TriageDecision>;
|
|
75
|
+
/**
|
|
76
|
+
* `true` when the full-fallback path was hit (agent down, total parse
|
|
77
|
+
* failure, all retries exhausted with no usable entries). Per-item
|
|
78
|
+
* demotions (low confidence / hallucinated id) do NOT flip this flag.
|
|
79
|
+
*/
|
|
80
|
+
fallback: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Free-form warnings for the operator. Mirrors the response parser's
|
|
83
|
+
* `warnings[]` plus orchestrator-level notes (retry exhaustion, audit
|
|
84
|
+
* log write failure, etc.).
|
|
85
|
+
*/
|
|
86
|
+
errors: string[];
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Triage the supplied items via the configured agent CLI.
|
|
90
|
+
*
|
|
91
|
+
* The function is the single entry point for PR-3's `radar triage` CLI and
|
|
92
|
+
* for any future caller (workflow generator, integration tests). It returns
|
|
93
|
+
* a `TriageResult` with one decision per input item plus a `fallback` flag
|
|
94
|
+
* and warning list.
|
|
95
|
+
*
|
|
96
|
+
* Empty input is handled as a no-op (no spawn) and returns
|
|
97
|
+
* `{ decisions: empty, fallback: false, errors: [] }` so callers can pass
|
|
98
|
+
* the result of a `filter()` chain without guarding for length.
|
|
99
|
+
*/
|
|
100
|
+
export declare function triageItems(items: Item[], options: TriageItemsOptions): Promise<TriageResult>;
|
|
101
|
+
export { looksLikeRateLimit, runTriageAgentCli, type TriageRunInput, type TriageRunner, type TriageRunResult, type TriageRunStatus, } from "./adapter.js";
|
|
102
|
+
export type { BuildTriagePromptOptions } from "./prompt.js";
|
|
103
|
+
export { buildTriagePrompt } from "./prompt.js";
|
|
104
|
+
export { type AgentEntry, type ParseTriageResponseResult, parseTriageResponse, TriageResponseParseError, type ValidatedTriageEntry, } from "./response.js";
|
|
105
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/triage/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAqB,KAAK,YAAY,EAAwB,MAAM,cAAc,CAAC;AAI1F;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,MAAM,WAAW,kBAAkB;IACjC,2EAA2E;IAC3E,MAAM,EAAE,kBAAkB,CAAC;IAC3B;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wCAAwC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACvC;;;;OAIG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AA8FD;;;;;;;;;;;GAWG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,IAAI,EAAE,EACb,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CA+KvB;AAED,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,eAAe,GACrB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAI5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EACL,KAAK,UAAU,EACf,KAAK,yBAAyB,EAC9B,mBAAmB,EACnB,wBAAwB,EACxB,KAAK,oBAAoB,GAC1B,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { appendFile, mkdir } from "node:fs/promises";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { runTriageAgentCli } from "./adapter.js";
|
|
4
|
+
import { buildTriagePrompt } from "./prompt.js";
|
|
5
|
+
import { parseTriageResponse, TriageResponseParseError } from "./response.js";
|
|
6
|
+
const DEFAULT_MAX_RETRIES = 3;
|
|
7
|
+
const DEFAULT_INITIAL_DELAY_MS = 1_000;
|
|
8
|
+
const DEFAULT_MAX_DELAY_MS = 60_000;
|
|
9
|
+
function defaultSleep(ms) {
|
|
10
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
11
|
+
}
|
|
12
|
+
function defaultNow() {
|
|
13
|
+
return new Date().toISOString();
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Build a fallback `TriageDecision` for an item the agent did not classify
|
|
17
|
+
* (omission, hallucinated id rejected, parse failure, full fallback, etc.).
|
|
18
|
+
* Centralised so every fallback path stamps the same shape.
|
|
19
|
+
*/
|
|
20
|
+
function buildFallbackDecision(agent, reason, triagedAt) {
|
|
21
|
+
return {
|
|
22
|
+
decision: "unsure",
|
|
23
|
+
confidence: 0,
|
|
24
|
+
reason,
|
|
25
|
+
agent,
|
|
26
|
+
triagedAt,
|
|
27
|
+
feedback: [],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Append a JSONL audit record. Failures here are non-fatal: an unwritable
|
|
32
|
+
* audit log path should not kill the triage workflow. We surface the
|
|
33
|
+
* failure as a `result.errors[]` entry instead so the operator can see it
|
|
34
|
+
* in the CLI output.
|
|
35
|
+
*/
|
|
36
|
+
async function appendAuditLog(path, record) {
|
|
37
|
+
try {
|
|
38
|
+
await mkdir(dirname(path), { recursive: true });
|
|
39
|
+
await appendFile(path, `${JSON.stringify(record)}\n`, "utf8");
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
44
|
+
return `audit log write failed (${path}): ${message}`;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Invoke the agent runner with exponential-backoff retry on `rate-limited`
|
|
49
|
+
* results. Returns the final `TriageRunResult` (which may itself be
|
|
50
|
+
* `rate-limited` if every retry hit the cap) plus the number of attempts
|
|
51
|
+
* made (for the audit log).
|
|
52
|
+
*/
|
|
53
|
+
async function runWithRetry(runner, agent, prompt, cwd, maxRetries, initialDelayMs, maxDelayMs, sleep) {
|
|
54
|
+
let attempt = 0;
|
|
55
|
+
let result = {
|
|
56
|
+
status: "error",
|
|
57
|
+
stdout: "",
|
|
58
|
+
stderr: "no attempts",
|
|
59
|
+
exitCode: -1,
|
|
60
|
+
};
|
|
61
|
+
while (attempt <= maxRetries) {
|
|
62
|
+
attempt++;
|
|
63
|
+
result = await runner({ agent: agent, prompt, cwd });
|
|
64
|
+
if (result.status !== "rate-limited" || attempt > maxRetries) {
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
const delay = Math.min(initialDelayMs * 2 ** (attempt - 1), maxDelayMs);
|
|
68
|
+
await sleep(delay);
|
|
69
|
+
}
|
|
70
|
+
return { result, attempts: attempt };
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Triage the supplied items via the configured agent CLI.
|
|
74
|
+
*
|
|
75
|
+
* The function is the single entry point for PR-3's `radar triage` CLI and
|
|
76
|
+
* for any future caller (workflow generator, integration tests). It returns
|
|
77
|
+
* a `TriageResult` with one decision per input item plus a `fallback` flag
|
|
78
|
+
* and warning list.
|
|
79
|
+
*
|
|
80
|
+
* Empty input is handled as a no-op (no spawn) and returns
|
|
81
|
+
* `{ decisions: empty, fallback: false, errors: [] }` so callers can pass
|
|
82
|
+
* the result of a `filter()` chain without guarding for length.
|
|
83
|
+
*/
|
|
84
|
+
export async function triageItems(items, options) {
|
|
85
|
+
const errors = [];
|
|
86
|
+
const decisions = new Map();
|
|
87
|
+
const now = options.now ?? defaultNow;
|
|
88
|
+
const triagedAt = now();
|
|
89
|
+
const cwd = options.cwd ?? process.cwd();
|
|
90
|
+
const runner = options.runner ?? runTriageAgentCli;
|
|
91
|
+
const sleep = options.sleep ?? defaultSleep;
|
|
92
|
+
const maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
93
|
+
const initialDelayMs = options.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS;
|
|
94
|
+
const maxDelayMs = options.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;
|
|
95
|
+
if (items.length === 0) {
|
|
96
|
+
return { decisions, fallback: false, errors };
|
|
97
|
+
}
|
|
98
|
+
const promptOptions = { items, policy: options.policy };
|
|
99
|
+
const prompt = buildTriagePrompt(promptOptions);
|
|
100
|
+
let runResult;
|
|
101
|
+
let attempts = 0;
|
|
102
|
+
try {
|
|
103
|
+
const retryOutcome = await runWithRetry(runner, options.agent, prompt, cwd, maxRetries, initialDelayMs, maxDelayMs, sleep);
|
|
104
|
+
runResult = retryOutcome.result;
|
|
105
|
+
attempts = retryOutcome.attempts;
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
109
|
+
runResult = {
|
|
110
|
+
status: "error",
|
|
111
|
+
stdout: "",
|
|
112
|
+
stderr: `runner threw: ${message}`,
|
|
113
|
+
exitCode: -1,
|
|
114
|
+
};
|
|
115
|
+
attempts = 1;
|
|
116
|
+
}
|
|
117
|
+
// --- Failure paths ---------------------------------------------------
|
|
118
|
+
// Rate-limited after exhausting retries → soft-fail with per-item reason.
|
|
119
|
+
if (runResult.status === "rate-limited") {
|
|
120
|
+
errors.push(`triage agent persistently rate-limited after ${attempts} attempt(s); falling back to unsure (rate-limited)`);
|
|
121
|
+
for (const item of items) {
|
|
122
|
+
decisions.set(item.id, buildFallbackDecision(options.agent, "rate-limited", triagedAt));
|
|
123
|
+
}
|
|
124
|
+
if (options.auditLog) {
|
|
125
|
+
const auditErr = await appendAuditLog(options.auditLog, {
|
|
126
|
+
ts: triagedAt,
|
|
127
|
+
agent: options.agent,
|
|
128
|
+
attempts,
|
|
129
|
+
status: "rate-limited",
|
|
130
|
+
itemIds: items.map((i) => i.id),
|
|
131
|
+
request: prompt,
|
|
132
|
+
response: runResult.stdout,
|
|
133
|
+
stderr: runResult.stderr,
|
|
134
|
+
exitCode: runResult.exitCode,
|
|
135
|
+
fallback: true,
|
|
136
|
+
rateLimited: true,
|
|
137
|
+
});
|
|
138
|
+
if (auditErr)
|
|
139
|
+
errors.push(auditErr);
|
|
140
|
+
}
|
|
141
|
+
return { decisions, fallback: true, errors };
|
|
142
|
+
}
|
|
143
|
+
// Hard error (CLI down, non-zero exit unrelated to rate-limit, runner threw)
|
|
144
|
+
if (runResult.status === "error") {
|
|
145
|
+
const tail = (runResult.stderr || runResult.stdout || "(no output)").trim();
|
|
146
|
+
errors.push(`triage agent CLI failed (exit ${runResult.exitCode}): ${tail}`);
|
|
147
|
+
for (const item of items) {
|
|
148
|
+
decisions.set(item.id, buildFallbackDecision(options.agent, "agent CLI failure", triagedAt));
|
|
149
|
+
}
|
|
150
|
+
if (options.auditLog) {
|
|
151
|
+
const auditErr = await appendAuditLog(options.auditLog, {
|
|
152
|
+
ts: triagedAt,
|
|
153
|
+
agent: options.agent,
|
|
154
|
+
attempts,
|
|
155
|
+
status: "error",
|
|
156
|
+
itemIds: items.map((i) => i.id),
|
|
157
|
+
request: prompt,
|
|
158
|
+
response: runResult.stdout,
|
|
159
|
+
stderr: runResult.stderr,
|
|
160
|
+
exitCode: runResult.exitCode,
|
|
161
|
+
fallback: true,
|
|
162
|
+
});
|
|
163
|
+
if (auditErr)
|
|
164
|
+
errors.push(auditErr);
|
|
165
|
+
}
|
|
166
|
+
return { decisions, fallback: true, errors };
|
|
167
|
+
}
|
|
168
|
+
// --- Happy / partial path ---------------------------------------------
|
|
169
|
+
try {
|
|
170
|
+
const parsed = parseTriageResponse(runResult.stdout, items, options.policy);
|
|
171
|
+
errors.push(...parsed.warnings);
|
|
172
|
+
for (const item of items) {
|
|
173
|
+
const entry = parsed.entries.get(item.id);
|
|
174
|
+
if (entry === undefined) {
|
|
175
|
+
decisions.set(item.id, buildFallbackDecision(options.agent, "agent-omitted", triagedAt));
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
decisions.set(item.id, {
|
|
179
|
+
decision: entry.decision,
|
|
180
|
+
confidence: entry.confidence,
|
|
181
|
+
reason: entry.reason,
|
|
182
|
+
group: entry.group,
|
|
183
|
+
agent: options.agent,
|
|
184
|
+
triagedAt,
|
|
185
|
+
feedback: [],
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
if (options.auditLog) {
|
|
189
|
+
const auditErr = await appendAuditLog(options.auditLog, {
|
|
190
|
+
ts: triagedAt,
|
|
191
|
+
agent: options.agent,
|
|
192
|
+
attempts,
|
|
193
|
+
status: "ok",
|
|
194
|
+
itemIds: items.map((i) => i.id),
|
|
195
|
+
request: prompt,
|
|
196
|
+
response: runResult.stdout,
|
|
197
|
+
stderr: runResult.stderr,
|
|
198
|
+
exitCode: runResult.exitCode,
|
|
199
|
+
decisions: Object.fromEntries(items
|
|
200
|
+
.map((item) => [item.id, decisions.get(item.id)])
|
|
201
|
+
.filter((pair) => pair[1] !== undefined)),
|
|
202
|
+
fallback: false,
|
|
203
|
+
});
|
|
204
|
+
if (auditErr)
|
|
205
|
+
errors.push(auditErr);
|
|
206
|
+
}
|
|
207
|
+
return { decisions, fallback: false, errors };
|
|
208
|
+
}
|
|
209
|
+
catch (err) {
|
|
210
|
+
// Total parse failure → all-unsure fallback (still fills every item id).
|
|
211
|
+
const message = err instanceof TriageResponseParseError
|
|
212
|
+
? err.message
|
|
213
|
+
: err instanceof Error
|
|
214
|
+
? err.message
|
|
215
|
+
: String(err);
|
|
216
|
+
errors.push(`triage response parse failed: ${message}`);
|
|
217
|
+
for (const item of items) {
|
|
218
|
+
decisions.set(item.id, buildFallbackDecision(options.agent, "response parse failure", triagedAt));
|
|
219
|
+
}
|
|
220
|
+
if (options.auditLog) {
|
|
221
|
+
const auditErr = await appendAuditLog(options.auditLog, {
|
|
222
|
+
ts: triagedAt,
|
|
223
|
+
agent: options.agent,
|
|
224
|
+
attempts,
|
|
225
|
+
status: "parse-error",
|
|
226
|
+
itemIds: items.map((i) => i.id),
|
|
227
|
+
request: prompt,
|
|
228
|
+
response: runResult.stdout,
|
|
229
|
+
stderr: runResult.stderr,
|
|
230
|
+
exitCode: runResult.exitCode,
|
|
231
|
+
fallback: true,
|
|
232
|
+
parseError: message,
|
|
233
|
+
});
|
|
234
|
+
if (auditErr)
|
|
235
|
+
errors.push(auditErr);
|
|
236
|
+
}
|
|
237
|
+
return { decisions, fallback: true, errors };
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
export { looksLikeRateLimit, runTriageAgentCli, } from "./adapter.js";
|
|
241
|
+
// Re-export internal modules so PR-3 (CLI) can import them without reaching
|
|
242
|
+
// into deeper paths. Keeping the public surface narrow: only the orchestrator
|
|
243
|
+
// API, the prompt builder, the response parser, and the adapter types.
|
|
244
|
+
export { buildTriagePrompt } from "./prompt.js";
|
|
245
|
+
export { parseTriageResponse, TriageResponseParseError, } from "./response.js";
|
|
246
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/triage/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Item } from "../../schemas/item.js";
|
|
2
|
+
import type { SourceTriagePolicy } from "../../schemas/source.js";
|
|
3
|
+
export interface BuildTriagePromptOptions {
|
|
4
|
+
items: Item[];
|
|
5
|
+
policy: SourceTriagePolicy;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Build the full triage prompt sent to the agent CLI.
|
|
9
|
+
*
|
|
10
|
+
* Structure (in order):
|
|
11
|
+
*
|
|
12
|
+
* 1. Opening directives: role statement + the two boundary-marker rules
|
|
13
|
+
* (don't follow `<untrusted_item>` instructions, treat `<policy>` as
|
|
14
|
+
* classification axes, not commands).
|
|
15
|
+
* 2. `<policy>` block: the user-supplied `policy.rules` verbatim. The
|
|
16
|
+
* surrounding tag is the boundary; the rules text itself is **not**
|
|
17
|
+
* edited or sanitized (consistent with ADR-0009's stance on
|
|
18
|
+
* untrusted-but-readable content).
|
|
19
|
+
* 3. `<items>` block: one `<untrusted_item>` block per input item.
|
|
20
|
+
* 4. Output format spec: the JSON schema the agent must emit, plus the
|
|
21
|
+
* `confidenceThreshold` so the agent has the option to self-downgrade
|
|
22
|
+
* to `unsure` before the response parser does (cheap-model agents often
|
|
23
|
+
* do this when reminded).
|
|
24
|
+
*
|
|
25
|
+
* The prompt is intentionally pure (no I/O, no clock reads) so tests can
|
|
26
|
+
* assert byte-stable output. The triage-time timestamp is stamped later by
|
|
27
|
+
* the response parser, not the prompt.
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildTriagePrompt({ items, policy }: BuildTriagePromptOptions): string;
|
|
30
|
+
//# sourceMappingURL=prompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../../src/core/triage/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AA8FlE,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,MAAM,EAAE,kBAAkB,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,wBAAwB,GAAG,MAAM,CA8CrF"}
|