karajan-code 4.31.1 → 4.33.0
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/package.json +1 -1
- package/src/agents/aider-agent.js +2 -12
- package/src/agents/base-agent.js +9 -20
- package/src/agents/claude-agent.js +2 -12
- package/src/agents/codex-agent.js +2 -12
- package/src/agents/dead-models.js +74 -0
- package/src/agents/gemini-agent.js +2 -12
- package/src/agents/model-errors.js +35 -0
- package/src/agents/opencode-agent.js +2 -12
- package/src/brain/agent-error-classifier.js +19 -1
- package/src/brain/role-fallback-chain.js +100 -0
- package/src/brain/with-brain-recovery.js +59 -3
- package/src/checks/action-pins.js +131 -0
- package/src/checks/project-checks.js +11 -0
- package/src/checks/repo-state.js +89 -0
- package/src/cli/advanced-commands.js +3 -0
- package/src/cli/register-meta.js +14 -0
- package/src/cli/register-pipeline.js +15 -1
- package/src/commands/bootstrap.js +131 -0
- package/src/commands/check.js +5 -0
- package/src/commands/code.js +57 -4
- package/src/commands/env.js +2 -1
- package/src/commands/go.js +9 -10
- package/src/commands/harden.js +3 -1
- package/src/commands/review-gate.js +28 -5
- package/src/environment/panel.js +52 -0
- package/src/environment/playbook.js +7 -6
- package/src/harden/config-templates.js +7 -1
- package/src/harden/guidelines-engine.js +7 -3
- package/src/harden/guidelines-templates.js +60 -10
- package/src/harden/hook-templates.js +4 -1
- package/src/privacy/diff-scope.js +58 -0
- package/src/privacy/scan.js +18 -0
- package/src/prompts/card-context.js +57 -0
- package/src/prompts/session-context.js +68 -0
- package/src/review/one-shot-review.js +5 -0
- package/src/review/ui-evidence.js +65 -0
- package/src/roles/agent-role.js +11 -2
- package/src/start/project-script.js +84 -0
package/src/roles/agent-role.js
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import { BaseRole } from "./base-role.js";
|
|
17
17
|
import { createAgent as defaultCreateAgent } from "../agents/index.js";
|
|
18
18
|
import { withBrainRecovery } from "../brain/with-brain-recovery.js";
|
|
19
|
+
import { buildRoleFallbackChain } from "../brain/role-fallback-chain.js";
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Silence timeout (ms) for an agent subprocess, from
|
|
@@ -154,10 +155,18 @@ export class AgentRole extends BaseRole {
|
|
|
154
155
|
// reviewer usa reviewTask) también funcionan: el wrapper sólo necesita
|
|
155
156
|
// un objeto con la firma `runTask(args) → { ok, output, error, exitCode }`.
|
|
156
157
|
const result = await withBrainRecovery({
|
|
157
|
-
agent: { runTask: (args) => agent[this.agentMethod](args), provider },
|
|
158
|
+
agent: { runTask: (args) => agent[this.agentMethod](args), provider, model: this.config?.roles?.[this.name]?.model ?? null },
|
|
158
159
|
taskArgs: runArgs,
|
|
159
160
|
role: this.name,
|
|
160
161
|
provider,
|
|
162
|
+
// KJC-TSK-0859: the chain the user declared under roles.<role>.fallback.
|
|
163
|
+
// The schema and the walker existed since KJC-TSK-0415; nobody built it,
|
|
164
|
+
// so every declared chain was dead config. This is the single point that
|
|
165
|
+
// covers every role inheriting from AgentRole.
|
|
166
|
+
fallback: buildRoleFallbackChain({
|
|
167
|
+
config: this.config, role: this.name, agentMethod: this.agentMethod,
|
|
168
|
+
createAgentFn: this._createAgent, logger: this.logger,
|
|
169
|
+
}),
|
|
161
170
|
emitter: this.emitter,
|
|
162
171
|
logger: this.logger,
|
|
163
172
|
// KJC: sessionState lets withBrainRecovery persist a hibernating run
|
|
@@ -171,7 +180,7 @@ export class AgentRole extends BaseRole {
|
|
|
171
180
|
const recoveryNote = result.recovery?.class ? ` [${result.recovery.class}]` : "";
|
|
172
181
|
return {
|
|
173
182
|
ok: false,
|
|
174
|
-
result: { error: result.error || result.output || `${this.name} failed${recoveryNote}`, provider, recovery: result.recovery, action: result.action, standbyFile: result.standbyFile || null },
|
|
183
|
+
result: { error: result.error || result.output || `${this.name} failed${recoveryNote}`, provider, recovery: result.recovery, action: result.action, standbyFile: result.standbyFile || null, tried: result.tried || null },
|
|
175
184
|
summary: `${this.name} failed${recoveryNote}: ${result.recovery?.message || result.error || "unknown error"}`,
|
|
176
185
|
usage: result.usage
|
|
177
186
|
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BOOT-C (KJC-TSK-0862) — the project's start script.
|
|
3
|
+
*
|
|
4
|
+
* From the Anthropic write-up on harnesses for long-running agents, and from
|
|
5
|
+
* the cold-start map: a session burns tokens working out how the project is
|
|
6
|
+
* launched, and worse, starts building on a tree that was already broken
|
|
7
|
+
* without knowing it. kj verifies the method; nobody verified that the
|
|
8
|
+
* application runs.
|
|
9
|
+
*
|
|
10
|
+
* kj declares the CONTRACT and checks it. The project writes the script,
|
|
11
|
+
* because kj cannot know how a project it has never seen is launched, and
|
|
12
|
+
* generating one from stack detection would produce the decorative artefact
|
|
13
|
+
* this codebase already rejects elsewhere (a config without its tool).
|
|
14
|
+
*
|
|
15
|
+
* It reports, it does not block: a badly written script must not stop work
|
|
16
|
+
* over something unrelated to the card. It earns the right to block when it
|
|
17
|
+
* proves reliable, the same path Sonar and the RAG took.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { spawn } from "node:child_process";
|
|
21
|
+
import { existsSync } from "node:fs";
|
|
22
|
+
import { join } from "node:path";
|
|
23
|
+
|
|
24
|
+
export const DEFAULT_START_SCRIPT = ".karajan/start.sh";
|
|
25
|
+
export const DEFAULT_TIMEOUT_MS = 120_000;
|
|
26
|
+
export const MAX_OUTPUT_BYTES = 64 * 1024;
|
|
27
|
+
|
|
28
|
+
export const START_SCRIPT_CONTRACT = [
|
|
29
|
+
"El guion de arranque levanta lo que haya que levantar y corre un humo de referencia.",
|
|
30
|
+
"Sale con 0 si el proyecto arranca y el humo pasa; con otro código si no.",
|
|
31
|
+
"No modifica el árbol ni depende de credenciales: solo responde si esto funciona ahora mismo.",
|
|
32
|
+
].join(" ");
|
|
33
|
+
|
|
34
|
+
/** Where the start script lives: the project's config decides, with a default. */
|
|
35
|
+
export function startScriptPath(projectDir, config = {}) {
|
|
36
|
+
return join(projectDir, config.start_script || DEFAULT_START_SCRIPT);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @returns {Promise<{status: "absent"|"ok"|"broken"|"timeout", exitCode?: number, output?: string, contract?: string, path: string}>}
|
|
41
|
+
*/
|
|
42
|
+
export function runStartScript({ projectDir, config = {}, timeoutMs = DEFAULT_TIMEOUT_MS } = {}) {
|
|
43
|
+
const script = startScriptPath(projectDir, config);
|
|
44
|
+
if (!existsSync(script)) {
|
|
45
|
+
return Promise.resolve({ status: "absent", path: script, contract: START_SCRIPT_CONTRACT });
|
|
46
|
+
}
|
|
47
|
+
return new Promise((resolve) => {
|
|
48
|
+
const ownGroup = process.platform !== "win32";
|
|
49
|
+
const child = spawn("sh", [script], { cwd: projectDir, detached: ownGroup });
|
|
50
|
+
let output = "";
|
|
51
|
+
let truncated = false;
|
|
52
|
+
let timedOut = false;
|
|
53
|
+
// Bounded on purpose (catch de codex): a verbose or runaway script would
|
|
54
|
+
// otherwise eat the CLI's memory for the whole timeout. The LAST bytes are
|
|
55
|
+
// the ones worth keeping — that is where a failure explains itself.
|
|
56
|
+
const collect = (buf) => {
|
|
57
|
+
output += String(buf);
|
|
58
|
+
if (output.length > MAX_OUTPUT_BYTES) {
|
|
59
|
+
output = output.slice(-MAX_OUTPUT_BYTES);
|
|
60
|
+
truncated = true;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
child.stdout?.on("data", collect);
|
|
64
|
+
child.stderr?.on("data", collect);
|
|
65
|
+
// Kill the GROUP, not just the shell (catch de codex): this script's whole
|
|
66
|
+
// job is to START things, so the dev server it launched would keep its port
|
|
67
|
+
// long after we reported a timeout.
|
|
68
|
+
const killTree = () => {
|
|
69
|
+
try {
|
|
70
|
+
if (ownGroup && child.pid) process.kill(-child.pid, "SIGKILL");
|
|
71
|
+
else child.kill("SIGKILL");
|
|
72
|
+
} catch { /* ya no está */ }
|
|
73
|
+
};
|
|
74
|
+
// A script that hangs is not a verdict: it is a script that hangs.
|
|
75
|
+
const timer = setTimeout(() => { timedOut = true; killTree(); }, timeoutMs);
|
|
76
|
+
child.on("error", (err) => { clearTimeout(timer); resolve({ status: "broken", path: script, exitCode: null, output: `${output}${err.message}`, truncated }); });
|
|
77
|
+
// Resolve on CLOSE, never before: the answer comes once the tree is down.
|
|
78
|
+
child.on("close", (code) => {
|
|
79
|
+
clearTimeout(timer);
|
|
80
|
+
if (timedOut) resolve({ status: "timeout", path: script, output, truncated });
|
|
81
|
+
else resolve({ status: code === 0 ? "ok" : "broken", exitCode: code, path: script, output, truncated });
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|