@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.
Files changed (106) hide show
  1. package/README.md +2 -1
  2. package/dist/agents/_boundary.d.ts +74 -1
  3. package/dist/agents/_boundary.d.ts.map +1 -1
  4. package/dist/agents/_boundary.js +152 -0
  5. package/dist/agents/_boundary.js.map +1 -1
  6. package/dist/claude-skills/dismiss/SKILL.md +18 -12
  7. package/dist/claude-skills/research/SKILL.md +21 -1
  8. package/dist/claude-skills/review/SKILL.md +23 -1
  9. package/dist/claude-skills/update/SKILL.md +24 -2
  10. package/dist/cli/_commit-path.d.ts +33 -0
  11. package/dist/cli/_commit-path.d.ts.map +1 -0
  12. package/dist/cli/_commit-path.js +43 -0
  13. package/dist/cli/_commit-path.js.map +1 -0
  14. package/dist/cli/dismiss.d.ts +38 -7
  15. package/dist/cli/dismiss.d.ts.map +1 -1
  16. package/dist/cli/dismiss.js +239 -54
  17. package/dist/cli/dismiss.js.map +1 -1
  18. package/dist/cli/index.d.ts.map +1 -1
  19. package/dist/cli/index.js +7 -1
  20. package/dist/cli/index.js.map +1 -1
  21. package/dist/cli/items.d.ts +44 -0
  22. package/dist/cli/items.d.ts.map +1 -0
  23. package/dist/cli/items.js +288 -0
  24. package/dist/cli/items.js.map +1 -0
  25. package/dist/cli/research.d.ts +21 -0
  26. package/dist/cli/research.d.ts.map +1 -1
  27. package/dist/cli/research.js +360 -54
  28. package/dist/cli/research.js.map +1 -1
  29. package/dist/cli/review.d.ts +23 -0
  30. package/dist/cli/review.d.ts.map +1 -1
  31. package/dist/cli/review.js +462 -2
  32. package/dist/cli/review.js.map +1 -1
  33. package/dist/cli/source.d.ts.map +1 -1
  34. package/dist/cli/source.js +18 -0
  35. package/dist/cli/source.js.map +1 -1
  36. package/dist/cli/triage.d.ts +136 -0
  37. package/dist/cli/triage.d.ts.map +1 -0
  38. package/dist/cli/triage.js +1110 -0
  39. package/dist/cli/triage.js.map +1 -0
  40. package/dist/cli/undismiss.d.ts +30 -0
  41. package/dist/cli/undismiss.d.ts.map +1 -0
  42. package/dist/cli/undismiss.js +133 -0
  43. package/dist/cli/undismiss.js.map +1 -0
  44. package/dist/cli/update.d.ts.map +1 -1
  45. package/dist/cli/update.js +429 -141
  46. package/dist/cli/update.js.map +1 -1
  47. package/dist/cli/workflow/generate-combined-with-triage.d.ts +163 -0
  48. package/dist/cli/workflow/generate-combined-with-triage.d.ts.map +1 -0
  49. package/dist/cli/workflow/generate-combined-with-triage.js +582 -0
  50. package/dist/cli/workflow/generate-combined-with-triage.js.map +1 -0
  51. package/dist/cli/workflow.d.ts +6 -5
  52. package/dist/cli/workflow.d.ts.map +1 -1
  53. package/dist/cli/workflow.js +13 -8
  54. package/dist/cli/workflow.js.map +1 -1
  55. package/dist/core/feeds/json-api.d.ts +5 -2
  56. package/dist/core/feeds/json-api.d.ts.map +1 -1
  57. package/dist/core/feeds/json-api.js +99 -13
  58. package/dist/core/feeds/json-api.js.map +1 -1
  59. package/dist/core/feeds/types.d.ts +26 -0
  60. package/dist/core/feeds/types.d.ts.map +1 -1
  61. package/dist/core/recipes.d.ts.map +1 -1
  62. package/dist/core/recipes.js +6 -0
  63. package/dist/core/recipes.js.map +1 -1
  64. package/dist/core/transitions.d.ts +30 -0
  65. package/dist/core/transitions.d.ts.map +1 -0
  66. package/dist/core/transitions.js +103 -0
  67. package/dist/core/transitions.js.map +1 -0
  68. package/dist/core/triage/adapter.d.ts +80 -0
  69. package/dist/core/triage/adapter.d.ts.map +1 -0
  70. package/dist/core/triage/adapter.js +128 -0
  71. package/dist/core/triage/adapter.js.map +1 -0
  72. package/dist/core/triage/index.d.ts +105 -0
  73. package/dist/core/triage/index.d.ts.map +1 -0
  74. package/dist/core/triage/index.js +246 -0
  75. package/dist/core/triage/index.js.map +1 -0
  76. package/dist/core/triage/prompt.d.ts +30 -0
  77. package/dist/core/triage/prompt.d.ts.map +1 -0
  78. package/dist/core/triage/prompt.js +157 -0
  79. package/dist/core/triage/prompt.js.map +1 -0
  80. package/dist/core/triage/response.d.ts +114 -0
  81. package/dist/core/triage/response.d.ts.map +1 -0
  82. package/dist/core/triage/response.js +188 -0
  83. package/dist/core/triage/response.js.map +1 -0
  84. package/dist/gemini-commands/research.toml +1 -1
  85. package/dist/gemini-commands/review.toml +1 -1
  86. package/dist/gemini-commands/update.toml +1 -1
  87. package/dist/recipes/aws-whats-new.yaml +36 -1
  88. package/dist/recipes/dev-to.yaml +24 -0
  89. package/dist/schemas/item.d.ts +151 -5
  90. package/dist/schemas/item.d.ts.map +1 -1
  91. package/dist/schemas/item.js +164 -4
  92. package/dist/schemas/item.js.map +1 -1
  93. package/dist/schemas/recipe.d.ts +11 -1
  94. package/dist/schemas/recipe.d.ts.map +1 -1
  95. package/dist/schemas/recipe.js +10 -1
  96. package/dist/schemas/recipe.js.map +1 -1
  97. package/dist/schemas/source.d.ts +65 -4
  98. package/dist/schemas/source.d.ts.map +1 -1
  99. package/dist/schemas/source.js +65 -3
  100. package/dist/schemas/source.js.map +1 -1
  101. package/dist/skills/research/SKILL.md +57 -1
  102. package/dist/skills/review/SKILL.md +65 -1
  103. package/dist/skills/update/SKILL.md +54 -1
  104. package/dist/templates/agents/AGENTS.md +30 -0
  105. package/dist/templates/workflows/combined-with-triage.template.yaml.tmpl +132 -0
  106. 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":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AA+D3C;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACpC,OAAO;QACL,KAAK;QACL,YAAY;QACZ,YAAY;QACZ,cAAc;QACd,mBAAmB;QACnB,KAAK;QACL,qBAAqB;QACrB,gBAAgB;QAChB,oBAAoB;QACpB,oBAAoB;KACrB,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;AAChD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,SAAS,cAAc,CAAC,KAAc,EAAE,MAAc;IACpD,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,aAAa;YAChB,OAAO;gBACL,OAAO,EAAE,QAAQ;gBACjB,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,EAAE,mBAAmB,EAAE,mBAAmB,CAAC;aAC1F,CAAC;QACJ,KAAK,YAAY;YACf,OAAO;gBACL,OAAO,EAAE,QAAQ;gBACjB,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,CAAC;aACtE,CAAC;QACJ,KAAK,WAAW;YACd,OAAO;gBACL,OAAO,EAAE,OAAO;gBAChB,IAAI,EAAE,CAAC,MAAM,EAAE,4CAA4C,EAAE,MAAM,CAAC;aACrE,CAAC;QACJ,KAAK,SAAS;YACZ,OAAO;gBACL,OAAO,EAAE,SAAS;gBAClB,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,mBAAmB,EAAE,mBAAmB,CAAC;aAC/D,CAAC;IACN,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,KAAqB;IAC3D,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACpE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,KAA+B,CAAC;QACpC,IAAI,CAAC;YACH,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QACpF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,iBAAiB,OAAO,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;YAC3F,OAAO;QACT,CAAC;QACD,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACzC,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC7B,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACzC,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC7B,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACxB,kEAAkE;YAClE,qEAAqE;YACrE,yCAAyC;YACzC,OAAO,CAAC;gBACN,MAAM,EAAE,OAAO;gBACf,MAAM;gBACN,MAAM,EAAE,GAAG,MAAM,kBAAkB,GAAG,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE;gBACvD,QAAQ,EAAE,CAAC,CAAC;aACb,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACzB,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,CAAC;YAC3B,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;gBACnB,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;gBACpD,OAAO;YACT,CAAC;YACD,MAAM,QAAQ,GAAG,GAAG,MAAM,KAAK,MAAM,EAAE,CAAC;YACxC,MAAM,MAAM,GAAoB,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC;YACxF,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;QACH,8DAA8D;QAC9D,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -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"}