codehere 1.0.1 → 1.0.2
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 +36 -192
- package/dist/application/use-cases/check-claimed-tests.js +81 -0
- package/dist/application/use-cases/detect-test-weakening.js +237 -0
- package/dist/application/use-cases/rerun-tests.js +110 -7
- package/dist/index.js +38 -1212
- package/dist/infrastructure/orchestration/artifacts-extractor.js +2 -323
- package/dist/presentation/cli/commands/check-command.js +178 -23
- package/dist/presentation/cli/commands/help-command.js +21 -285
- package/dist/ui.js +5 -236
- package/dist/utils/version.js +0 -6
- package/package.json +9 -38
- package/dist/application/services/verdict-service.js +0 -154
- package/dist/application/use-cases/assess-cross-vendor.js +0 -126
- package/dist/application/use-cases/build-cross-vendor-verdict.js +0 -118
- package/dist/application/use-cases/domain-trust.js +0 -110
- package/dist/application/use-cases/export-aat.js +0 -107
- package/dist/application/use-cases/generate-acceptance-criteria.js +0 -223
- package/dist/application/use-cases/generate-final-report.js +0 -259
- package/dist/application/use-cases/generate-narration.js +0 -130
- package/dist/application/use-cases/generate-thread-title.js +0 -181
- package/dist/application/use-cases/narrator-prompt-template.js +0 -158
- package/dist/application/use-cases/plan-auto-review.js +0 -57
- package/dist/application/use-cases/reconcile-audit.js +0 -100
- package/dist/application/use-cases/search-sessions.js +0 -70
- package/dist/application/use-cases/should-auto-review.js +0 -71
- package/dist/application/use-cases/thread-export.js +0 -245
- package/dist/application/use-cases/verify-claim.js +0 -456
- package/dist/application/use-cases/write-pass.js +0 -277
- package/dist/domain/audit.js +0 -11
- package/dist/domain/services/codebase-detector.js +0 -85
- package/dist/domain/task.js +0 -91
- package/dist/error-handler.js +0 -139
- package/dist/infrastructure/completion/bash-completion.js +0 -29
- package/dist/infrastructure/completion/fish-completion.js +0 -17
- package/dist/infrastructure/completion/zsh-completion.js +0 -25
- package/dist/infrastructure/config/config-manager.js +0 -174
- package/dist/infrastructure/config/product-context.js +0 -49
- package/dist/infrastructure/context/adapters/drawio-source.js +0 -157
- package/dist/infrastructure/context/adapters/figma-source.js +0 -198
- package/dist/infrastructure/context/adapters/github-issue-source.js +0 -160
- package/dist/infrastructure/context/adapters/linear-source.js +0 -190
- package/dist/infrastructure/context/context-resolver.js +0 -144
- package/dist/infrastructure/context/context-source.js +0 -114
- package/dist/infrastructure/context/github-api.js +0 -110
- package/dist/infrastructure/context/github-device-flow.js +0 -65
- package/dist/infrastructure/context/oauth-flow.js +0 -207
- package/dist/infrastructure/context/oauth-store.js +0 -122
- package/dist/infrastructure/context/registry-bootstrap.js +0 -95
- package/dist/infrastructure/context/write-pass-resolver.js +0 -106
- package/dist/infrastructure/cost/budget-config.js +0 -114
- package/dist/infrastructure/cost/cost-router.js +0 -97
- package/dist/infrastructure/cost/spend-aggregator.js +0 -222
- package/dist/infrastructure/cost/team-ledger.js +0 -355
- package/dist/infrastructure/discovery/discovery-log.js +0 -85
- package/dist/infrastructure/hooks/audit-tip-signature.js +0 -336
- package/dist/infrastructure/hooks/claude-sdk-hook.js +0 -1018
- package/dist/infrastructure/hooks/hook-mode.js +0 -87
- package/dist/infrastructure/hooks/mirror-tool-call-to-audit.js +0 -49
- package/dist/infrastructure/hooks/pre-prompt-scan.js +0 -122
- package/dist/infrastructure/hooks/session-analyzer.js +0 -177
- package/dist/infrastructure/observability/execution-tracer.js +0 -227
- package/dist/infrastructure/orchestration/agent-adapter.js +0 -13
- package/dist/infrastructure/orchestration/agent-installer.js +0 -101
- package/dist/infrastructure/orchestration/agents-config-seed.js +0 -68
- package/dist/infrastructure/orchestration/aider-adapter.js +0 -611
- package/dist/infrastructure/orchestration/cassette-recorder.js +0 -120
- package/dist/infrastructure/orchestration/claude-code-adapter.js +0 -592
- package/dist/infrastructure/orchestration/codex-adapter.js +0 -574
- package/dist/infrastructure/orchestration/conductor-adapter.js +0 -416
- package/dist/infrastructure/orchestration/context-handoff.js +0 -162
- package/dist/infrastructure/orchestration/control/codex-control-surface.js +0 -99
- package/dist/infrastructure/orchestration/control/control-surface.js +0 -15
- package/dist/infrastructure/orchestration/detect-ollama.js +0 -75
- package/dist/infrastructure/orchestration/failover-classifier.js +0 -151
- package/dist/infrastructure/orchestration/failover-config.js +0 -67
- package/dist/infrastructure/orchestration/failover-policy.js +0 -106
- package/dist/infrastructure/orchestration/gemini-adapter.js +0 -663
- package/dist/infrastructure/orchestration/generic-cli-adapter.js +0 -689
- package/dist/infrastructure/orchestration/graphify-context.js +0 -209
- package/dist/infrastructure/orchestration/memory-injector.js +0 -380
- package/dist/infrastructure/orchestration/mocks/mock-agent-repl.js +0 -388
- package/dist/infrastructure/orchestration/mocks/mock-codex-app-server.js +0 -73
- package/dist/infrastructure/orchestration/mocks/mock-synthesis.js +0 -83
- package/dist/infrastructure/orchestration/mocks/resolve-agent-spawn.js +0 -37
- package/dist/infrastructure/orchestration/opencode-adapter.js +0 -580
- package/dist/infrastructure/orchestration/progress-aggregator.js +0 -906
- package/dist/infrastructure/orchestration/readiness.js +0 -103
- package/dist/infrastructure/orchestration/session-manager.js +0 -1590
- package/dist/infrastructure/orchestration/session-persistence.js +0 -394
- package/dist/infrastructure/orchestration/thread-key-store.js +0 -127
- package/dist/infrastructure/orchestration/write-pass-store.js +0 -38
- package/dist/infrastructure/protocols/cloud-sync.js +0 -207
- package/dist/infrastructure/protocols/mcp-helpers.js +0 -25
- package/dist/infrastructure/protocols/mcp-server-impl.js +0 -3935
- package/dist/infrastructure/protocols/mcp-tools.js +0 -401
- package/dist/infrastructure/protocols/probe-port.js +0 -36
- package/dist/infrastructure/protocols/thread-phase.js +0 -105
- package/dist/infrastructure/protocols/ui/app-script-artifacts.js +0 -121
- package/dist/infrastructure/protocols/ui/app-script-audit-footer.js +0 -44
- package/dist/infrastructure/protocols/ui/app-script-audit-verify.js +0 -56
- package/dist/infrastructure/protocols/ui/app-script-composer-cost.js +0 -588
- package/dist/infrastructure/protocols/ui/app-script-context-sources.js +0 -168
- package/dist/infrastructure/protocols/ui/app-script-conversation.js +0 -67
- package/dist/infrastructure/protocols/ui/app-script-drawer.js +0 -115
- package/dist/infrastructure/protocols/ui/app-script-failover.js +0 -133
- package/dist/infrastructure/protocols/ui/app-script-github.js +0 -113
- package/dist/infrastructure/protocols/ui/app-script-observer.js +0 -104
- package/dist/infrastructure/protocols/ui/app-script-onboarding.js +0 -80
- package/dist/infrastructure/protocols/ui/app-script-overlay.js +0 -203
- package/dist/infrastructure/protocols/ui/app-script-recovery.js +0 -136
- package/dist/infrastructure/protocols/ui/app-script-render.js +0 -166
- package/dist/infrastructure/protocols/ui/app-script-share.js +0 -78
- package/dist/infrastructure/protocols/ui/app-script-sidebar.js +0 -194
- package/dist/infrastructure/protocols/ui/app-script-tour.js +0 -78
- package/dist/infrastructure/protocols/ui/app-script-wpass.js +0 -312
- package/dist/infrastructure/protocols/ui/app-script.js +0 -2998
- package/dist/infrastructure/protocols/ui/font-faces.js +0 -7
- package/dist/infrastructure/protocols/ui/styles.js +0 -2147
- package/dist/infrastructure/protocols/ui/tokens.js +0 -96
- package/dist/infrastructure/protocols/ui-page-v2.js +0 -320
- package/dist/infrastructure/protocols/verify-page.js +0 -1677
- package/dist/infrastructure/security/file-collection.js +0 -118
- package/dist/infrastructure/security/fix-code.js +0 -144
- package/dist/infrastructure/security/fix-secret.js +0 -89
- package/dist/infrastructure/security/permissions-config.js +0 -235
- package/dist/infrastructure/security/scan-types.js +0 -10
- package/dist/infrastructure/security/ship-scan.js +0 -195
- package/dist/infrastructure/security/trust-scorer.js +0 -724
- package/dist/infrastructure/security/vibe-checks.js +0 -99
- package/dist/infrastructure/storage/atomic-write.js +0 -86
- package/dist/infrastructure/storage/cross-tool-agents.js +0 -268
- package/dist/infrastructure/storage/project-thread-migration.js +0 -237
- package/dist/infrastructure/storage/project-thread-mirror.js +0 -671
- package/dist/infrastructure/storage/project-thread-reader.js +0 -409
- package/dist/infrastructure/storage/project-thread-title-backfill.js +0 -214
- package/dist/monitoring.js +0 -212
- package/dist/presentation/cli/commands/audit-command.js +0 -201
- package/dist/presentation/cli/commands/budget-command.js +0 -79
- package/dist/presentation/cli/commands/config-command.js +0 -125
- package/dist/presentation/cli/commands/context-source-command.js +0 -217
- package/dist/presentation/cli/commands/hook-command.js +0 -265
- package/dist/presentation/cli/commands/init-command.js +0 -590
- package/dist/presentation/cli/commands/memory-command.js +0 -1031
- package/dist/presentation/cli/commands/narrator-command.js +0 -101
- package/dist/presentation/cli/commands/permission-command.js +0 -105
- package/dist/presentation/cli/commands/protocols-builder.js +0 -254
- package/dist/presentation/cli/commands/protocols-command.js +0 -84
- package/dist/presentation/cli/commands/repo-map-builder.js +0 -247
- package/dist/presentation/cli/commands/report-command.js +0 -403
- package/dist/presentation/cli/commands/scan-command.js +0 -52
- package/dist/presentation/cli/commands/scan-tool-command.js +0 -110
- package/dist/presentation/cli/commands/search-command.js +0 -51
- package/dist/presentation/cli/commands/setup-command.js +0 -330
- package/dist/presentation/cli/commands/status-command.js +0 -614
- package/dist/presentation/cli/commands/team-command.js +0 -256
- package/dist/presentation/cli/commands/thread-command.js +0 -370
- package/dist/presentation/cli/commands/trust-command.js +0 -328
- package/dist/presentation/cli/commands/verify-command.js +0 -298
- package/dist/presentation/cli/error-display.js +0 -328
- package/dist/presentation/cli/repl.js +0 -805
- package/dist/presentation/cli/task-deliverable.js +0 -153
- package/dist/testing/cassette.js +0 -125
- package/dist/testing/replay-adapter.js +0 -147
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Hook mode detection — Pattern 20 backend.
|
|
3
|
-
*
|
|
4
|
-
* Reads ~/.claude/settings.json and reports which posture the safety
|
|
5
|
-
* hook is in. Mode is encoded as flags on the configured command:
|
|
6
|
-
*
|
|
7
|
-
* off — no codehere scan-tool entry in PreToolUse
|
|
8
|
-
* log-only — `codehere scan-tool --log-only`
|
|
9
|
-
* advisory — DETECTION ONLY (LOW #20, atomic audit): getHookMode
|
|
10
|
-
* recognizes a `--advisory` flag for forward-compat, but
|
|
11
|
-
* scan-tool implements no advisory posture — a user writing
|
|
12
|
-
* --advisory gets the DEFAULT block behavior while the nav
|
|
13
|
-
* indicator would read 'advisory'. Don't advertise the flag.
|
|
14
|
-
* block — `codehere scan-tool` (default critical/high block)
|
|
15
|
-
* OR `codehere scan-tool --block-all` (all severity levels)
|
|
16
|
-
*
|
|
17
|
-
* Read-only by design — this module never WRITES the config. Mode
|
|
18
|
-
* switching belongs in `codehere hook install/uninstall` so users go
|
|
19
|
-
* through the same path that re-validates the hook surface.
|
|
20
|
-
*/
|
|
21
|
-
import { existsSync, readFileSync } from 'fs';
|
|
22
|
-
import { join } from 'path';
|
|
23
|
-
import { homedir } from 'os';
|
|
24
|
-
/**
|
|
25
|
-
* Resolve at call time, not at import.
|
|
26
|
-
*
|
|
27
|
-
* This was a module-level const, which froze the path to whatever HOME happened to be when the
|
|
28
|
-
* module first loaded. For a one-shot CLI that is harmless — HOME does not move mid-process —
|
|
29
|
-
* but it made the safety-mode read untestable against an isolated home, and the test that
|
|
30
|
-
* covered it compensated by writing to the developer's REAL ~/.claude/settings.json instead.
|
|
31
|
-
* A safety feature whose only test edits the user's live safety config is not covered, it is
|
|
32
|
-
* gambled with, and on CI (no ~/.claude at all) it failed outright.
|
|
33
|
-
*
|
|
34
|
-
* A function costs one stat call per invocation and makes the module honest under any HOME.
|
|
35
|
-
*/
|
|
36
|
-
const settingsPath = () => join(homedir(), '.claude', 'settings.json');
|
|
37
|
-
/**
|
|
38
|
-
* Detect the current safety-hook mode from ~/.claude/settings.json.
|
|
39
|
-
* Defensive against missing / malformed settings — anything we can't
|
|
40
|
-
* parse cleanly returns 'off' (the most-conservative read).
|
|
41
|
-
*/
|
|
42
|
-
export function getHookMode() {
|
|
43
|
-
if (!existsSync(settingsPath()))
|
|
44
|
-
return 'off';
|
|
45
|
-
let parsed;
|
|
46
|
-
try {
|
|
47
|
-
parsed = JSON.parse(readFileSync(settingsPath(), 'utf-8'));
|
|
48
|
-
}
|
|
49
|
-
catch {
|
|
50
|
-
return 'off';
|
|
51
|
-
}
|
|
52
|
-
const hooks = (parsed.hooks ?? {});
|
|
53
|
-
const preToolUse = (hooks.PreToolUse ?? []);
|
|
54
|
-
if (!Array.isArray(preToolUse) || preToolUse.length === 0)
|
|
55
|
-
return 'off';
|
|
56
|
-
let bestMatch = 'off';
|
|
57
|
-
for (const entry of preToolUse) {
|
|
58
|
-
if (!entry?.hooks)
|
|
59
|
-
continue;
|
|
60
|
-
for (const hk of entry.hooks) {
|
|
61
|
-
const cmd = hk?.command;
|
|
62
|
-
if (typeof cmd !== 'string')
|
|
63
|
-
continue;
|
|
64
|
-
if (!cmd.includes('codehere scan-tool'))
|
|
65
|
-
continue;
|
|
66
|
-
// Earliest match wins block > advisory > log-only — once a stricter
|
|
67
|
-
// mode is found we keep it. (block is the operational default;
|
|
68
|
-
// log-only is opt-in.)
|
|
69
|
-
if (cmd.includes('--log-only')) {
|
|
70
|
-
if (bestMatch === 'off')
|
|
71
|
-
bestMatch = 'log-only';
|
|
72
|
-
}
|
|
73
|
-
else if (cmd.includes('--advisory')) {
|
|
74
|
-
if (bestMatch === 'off' || bestMatch === 'log-only')
|
|
75
|
-
bestMatch = 'advisory';
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
// No flag OR --block-all: both render as 'block' in the UI.
|
|
79
|
-
// Block-all is stricter but the user-visible posture ("safety
|
|
80
|
-
// hook actively blocks risky calls") is the same.
|
|
81
|
-
return 'block';
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return bestMatch;
|
|
86
|
-
}
|
|
87
|
-
//# sourceMappingURL=hook-mode.js.map
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cross-vendor tool-call audit mirror — #110 slice 2 (Codex first).
|
|
3
|
-
*
|
|
4
|
-
* When non-Claude adapters emit a tool-call event, we can't BLOCK
|
|
5
|
-
* (no synchronous pre-tool intercept yet) but we CAN mirror the
|
|
6
|
-
* call into the audit log post-hoc. Closes the audit-trail-parity
|
|
7
|
-
* gap: every adapter's tool calls land in the same hook-events.jsonl
|
|
8
|
-
* the per-tool Claude hook writes to.
|
|
9
|
-
*
|
|
10
|
-
* Future slices (per-adapter blocking) replace the post-hoc mirror
|
|
11
|
-
* with pre-call intercepts. For now: visibility today, blocking next.
|
|
12
|
-
*/
|
|
13
|
-
import { scanPayload, calculateRiskScore, appendAuditLog, AUDIT_FILE } from './claude-sdk-hook.js';
|
|
14
|
-
import { signAuditTip } from './audit-tip-signature.js';
|
|
15
|
-
export function mirrorToolCallToAudit(rec) {
|
|
16
|
-
// Run the same pattern bank the per-tool hook uses, so post-hoc
|
|
17
|
-
// findings show up in the audit feed with the same severity calibration.
|
|
18
|
-
const findings = scanPayload(rec.toolInput);
|
|
19
|
-
const riskScore = calculateRiskScore(findings);
|
|
20
|
-
const truncated = rec.toolInput.length > 500 ? rec.toolInput.slice(0, 500) + '…' : rec.toolInput;
|
|
21
|
-
const entry = {
|
|
22
|
-
timestamp: new Date().toISOString(),
|
|
23
|
-
sessionId: rec.sessionId,
|
|
24
|
-
toolName: rec.toolName,
|
|
25
|
-
toolInput: JSON.stringify({
|
|
26
|
-
agent: rec.agent,
|
|
27
|
-
input: truncated,
|
|
28
|
-
cwd: rec.cwd ?? null,
|
|
29
|
-
inputLength: rec.toolInput.length,
|
|
30
|
-
mirrored: true, // marks post-hoc vs the synchronous Claude hook
|
|
31
|
-
}),
|
|
32
|
-
riskScore,
|
|
33
|
-
blocked: false, // post-hoc — can't block, only log
|
|
34
|
-
findings: findings.length,
|
|
35
|
-
criticalFindings: findings.filter((f) => f.severity === 'critical').length,
|
|
36
|
-
// R1.5 · the mirror runs IN the daemon — stamp the provenance it promises.
|
|
37
|
-
writer: 'daemon',
|
|
38
|
-
};
|
|
39
|
-
try {
|
|
40
|
-
appendAuditLog(entry);
|
|
41
|
-
// R1.3 · sign-at-write — the daemon's mirror is a daemon append; advance
|
|
42
|
-
// the signed tip per write (sub-ms, fail-open).
|
|
43
|
-
signAuditTip(AUDIT_FILE);
|
|
44
|
-
}
|
|
45
|
-
catch {
|
|
46
|
-
// Audit-write failures don't propagate — the agent's already running.
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
//# sourceMappingURL=mirror-tool-call-to-audit.js.map
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pre-prompt safety scan — cross-vendor parity, task #110.
|
|
3
|
-
*
|
|
4
|
-
* The substrate the FAQ landing claims for v0.5.x → v0.6: "per-tool
|
|
5
|
-
* blocking parity for Codex / Aider / OpenCode / Gemini." Today, only
|
|
6
|
-
* Claude Code has a per-tool PreToolUse hook. The other 4 adapters
|
|
7
|
-
* spawn raw — every Bash/Edit they make goes unscanned.
|
|
8
|
-
*
|
|
9
|
-
* Closing that gap fully needs per-adapter pre-tool intercepts (huge
|
|
10
|
-
* scope per-adapter — different IPC + lifecycle for each CLI). This
|
|
11
|
-
* module ships the FIRST cross-vendor slice: scan the user's PROMPT
|
|
12
|
-
* before spawn. Catches the prompt-side leaks (API key in prompt,
|
|
13
|
-
* injection-pattern poisoning, credential paste) for ALL 5 adapters
|
|
14
|
-
* with one piece of code.
|
|
15
|
-
*
|
|
16
|
-
* Posture follows getHookMode():
|
|
17
|
-
* block → high+ severity findings refuse spawn (throw)
|
|
18
|
-
* advisory → findings logged + warning event, spawn proceeds
|
|
19
|
-
* log-only → findings logged, spawn proceeds, no warning
|
|
20
|
-
* off → no scan, spawn proceeds
|
|
21
|
-
*
|
|
22
|
-
* Every scan writes one audit-log entry with synthetic toolName
|
|
23
|
-
* 'pre_prompt' so the audit feed + hash chain pick it up
|
|
24
|
-
* without surface changes.
|
|
25
|
-
*/
|
|
26
|
-
import { scanPayload, calculateRiskScore, appendAuditLog } from './claude-sdk-hook.js';
|
|
27
|
-
import { getHookMode } from './hook-mode.js';
|
|
28
|
-
import { filterAllowed } from '../security/permissions-config.js';
|
|
29
|
-
/**
|
|
30
|
-
* Scan a user prompt before spawn. Always writes one audit-log entry.
|
|
31
|
-
* Returns allowed=false ONLY when mode === 'block' AND there's a
|
|
32
|
-
* high-or-critical finding. Other modes always allow but still log.
|
|
33
|
-
*/
|
|
34
|
-
export function scanPromptBeforeSpawn(input) {
|
|
35
|
-
const rawFindings = scanPayload(input.prompt);
|
|
36
|
-
// P18 slice 1 — filter against the cwd's permissions.yaml allow-list.
|
|
37
|
-
// Allow-listed findings are dropped from the BLOCK calculation but
|
|
38
|
-
// still recorded in the audit-log allowed-list field so a forensic
|
|
39
|
-
// reviewer can see what was suppressed and why.
|
|
40
|
-
const { surviving, allowed } = input.cwd
|
|
41
|
-
? filterAllowed(rawFindings, input.cwd)
|
|
42
|
-
: { surviving: rawFindings, allowed: [] };
|
|
43
|
-
const findings = surviving;
|
|
44
|
-
const riskScore = calculateRiskScore(findings);
|
|
45
|
-
const mode = (() => {
|
|
46
|
-
try {
|
|
47
|
-
return getHookMode();
|
|
48
|
-
}
|
|
49
|
-
catch {
|
|
50
|
-
return 'off';
|
|
51
|
-
}
|
|
52
|
-
})();
|
|
53
|
-
// Severity gate: only critical/high findings can refuse a spawn.
|
|
54
|
-
// Medium/low findings get logged but never block — they're flagged
|
|
55
|
-
// for visibility, not enforcement. Matches the per-tool hook's
|
|
56
|
-
// existing severity-to-action policy.
|
|
57
|
-
const hardFindings = findings.filter((f) => f.severity === 'critical' || f.severity === 'high');
|
|
58
|
-
const wouldBlock = hardFindings.length > 0;
|
|
59
|
-
const blocked = mode === 'block' && wouldBlock;
|
|
60
|
-
// Audit entry — written for every scan, regardless of mode. Synthetic
|
|
61
|
-
// toolName 'pre_prompt' lets audit-feed filters group these
|
|
62
|
-
// separately from per-tool entries (existing chip filters on tool).
|
|
63
|
-
const entry = {
|
|
64
|
-
timestamp: new Date().toISOString(),
|
|
65
|
-
sessionId: input.sessionId,
|
|
66
|
-
toolName: 'pre_prompt',
|
|
67
|
-
// Truncate the prompt for the audit record — full prompts can be
|
|
68
|
-
// 10K+ chars and we don't want the audit log to balloon.
|
|
69
|
-
toolInput: JSON.stringify({
|
|
70
|
-
agent: input.agent,
|
|
71
|
-
prompt: input.prompt.length > 500 ? input.prompt.slice(0, 500) + '…' : input.prompt,
|
|
72
|
-
cwd: input.cwd ?? null,
|
|
73
|
-
promptLength: input.prompt.length,
|
|
74
|
-
// P18 slice 1 — record what got allow-listed for forensic audit.
|
|
75
|
-
// Empty array when no permissions.yaml exists in the cwd.
|
|
76
|
-
...(allowed.length > 0
|
|
77
|
-
? {
|
|
78
|
-
allowedByPermissions: allowed.map((a) => ({
|
|
79
|
-
category: a.finding.category,
|
|
80
|
-
matched: a.finding.matched,
|
|
81
|
-
reason: a.entry.reason ?? '(no reason given)',
|
|
82
|
-
})),
|
|
83
|
-
}
|
|
84
|
-
: {}),
|
|
85
|
-
}),
|
|
86
|
-
riskScore,
|
|
87
|
-
blocked,
|
|
88
|
-
findings: findings.length,
|
|
89
|
-
criticalFindings: findings.filter((f) => f.severity === 'critical').length,
|
|
90
|
-
// Mode-driven override marker so the audit reader knows WHY a
|
|
91
|
-
// would-block finding wasn't enforced. Reuses the existing
|
|
92
|
-
// OverrideReason 'log_only_mode' for log-only AND advisory + off
|
|
93
|
-
// (all three skip the block; 'log_only_mode' is the closest
|
|
94
|
-
// shared semantic — the entry's mode field can be added as a new
|
|
95
|
-
// OverrideReason later without breaking the audit chain).
|
|
96
|
-
...(wouldBlock && !blocked ? { overrideReason: 'log_only_mode' } : {}),
|
|
97
|
-
};
|
|
98
|
-
try {
|
|
99
|
-
appendAuditLog(entry);
|
|
100
|
-
}
|
|
101
|
-
catch {
|
|
102
|
-
// Audit-write failure should NOT itself block a spawn — that would
|
|
103
|
-
// be a denial-of-service vector (fill the log dir → no spawns).
|
|
104
|
-
// The audit infra has its own failure-mode logging.
|
|
105
|
-
}
|
|
106
|
-
// Compose a block reason from the worst findings. Used by the
|
|
107
|
-
// session-manager to throw an actionable error.
|
|
108
|
-
let blockReason = '';
|
|
109
|
-
if (blocked) {
|
|
110
|
-
const top = hardFindings.slice(0, 3);
|
|
111
|
-
const summary = top.map((f) => `[${f.severity}] ${f.description}`).join('; ');
|
|
112
|
-
blockReason = `pre-prompt scan blocked: ${summary}`;
|
|
113
|
-
}
|
|
114
|
-
return {
|
|
115
|
-
allowed: !blocked,
|
|
116
|
-
blocked,
|
|
117
|
-
riskScore,
|
|
118
|
-
findings,
|
|
119
|
-
blockReason,
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
//# sourceMappingURL=pre-prompt-scan.js.map
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Session Analyzer
|
|
3
|
-
*
|
|
4
|
-
* Detects multi-agent amplification patterns in the hook audit log.
|
|
5
|
-
*
|
|
6
|
-
* When Claude Code spawns sub-agents via the Task tool, each agent makes
|
|
7
|
-
* additional tool calls. One compromised instruction can amplify into
|
|
8
|
-
* many downstream tool calls — the "17.2x amplification" risk.
|
|
9
|
-
*
|
|
10
|
-
* This analyzer groups audit events by session and detects:
|
|
11
|
-
* 1. High tool call volume per session (potential amplification)
|
|
12
|
-
* 2. Sessions with blocked calls (injection attempts)
|
|
13
|
-
* 3. Sessions with unusual tool mix (e.g., many Bash calls in a row)
|
|
14
|
-
*/
|
|
15
|
-
/**
|
|
16
|
-
* Group audit entries by session and compute per-session stats.
|
|
17
|
-
*/
|
|
18
|
-
export function analyzeSessions(entries) {
|
|
19
|
-
const bySession = new Map();
|
|
20
|
-
for (const entry of entries) {
|
|
21
|
-
const sid = entry.sessionId || 'unknown';
|
|
22
|
-
if (!bySession.has(sid))
|
|
23
|
-
bySession.set(sid, []);
|
|
24
|
-
bySession.get(sid).push(entry);
|
|
25
|
-
}
|
|
26
|
-
const summaries = [];
|
|
27
|
-
for (const [sessionId, sessionEntries] of bySession) {
|
|
28
|
-
const sorted = [...sessionEntries].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
29
|
-
const first = sorted[0];
|
|
30
|
-
const last = sorted[sorted.length - 1];
|
|
31
|
-
const durationMs = new Date(last.timestamp).getTime() - new Date(first.timestamp).getTime();
|
|
32
|
-
const toolMix = {};
|
|
33
|
-
let blocked = 0;
|
|
34
|
-
let critical = 0;
|
|
35
|
-
for (const e of sorted) {
|
|
36
|
-
toolMix[e.toolName] = (toolMix[e.toolName] ?? 0) + 1;
|
|
37
|
-
if (e.blocked)
|
|
38
|
-
blocked++;
|
|
39
|
-
if (e.criticalFindings > 0)
|
|
40
|
-
critical++;
|
|
41
|
-
}
|
|
42
|
-
// Risk heuristics. The signal we care about is amplification — a runaway
|
|
43
|
-
// or injected session doing things the user didn't watch. Volume ALONE is
|
|
44
|
-
// weak signal: a normal attended dev session easily runs 40+ calls with
|
|
45
|
-
// 20+ Bash (git, build, test, curl) and is perfectly clean. Crying "medium"
|
|
46
|
-
// at that is the false alarm UX_OBSERVATIONS.md P1 warns against, and it
|
|
47
|
-
// erodes trust in the real alerts. So:
|
|
48
|
-
// - any blocked call or critical finding is genuine security signal
|
|
49
|
-
// - pure volume only flags at a high bar (the code's own ">50" threshold)
|
|
50
|
-
// - Bash-heavy only flags when ALSO high-volume (raised 10→20, +calls>40)
|
|
51
|
-
const bashCount = toolMix['Bash'] ?? 0;
|
|
52
|
-
const callCount = sorted.length;
|
|
53
|
-
let risk = 'low';
|
|
54
|
-
if ((blocked > 0 && callCount > 20) || critical > 0)
|
|
55
|
-
risk = 'high';
|
|
56
|
-
else if (blocked > 0)
|
|
57
|
-
risk = 'medium';
|
|
58
|
-
else if (callCount > 50 || (bashCount > 20 && callCount > 40))
|
|
59
|
-
risk = 'medium';
|
|
60
|
-
summaries.push({
|
|
61
|
-
sessionId,
|
|
62
|
-
firstSeen: first.timestamp,
|
|
63
|
-
lastSeen: last.timestamp,
|
|
64
|
-
totalCalls: callCount,
|
|
65
|
-
blockedCalls: blocked,
|
|
66
|
-
criticalCalls: critical,
|
|
67
|
-
toolMix,
|
|
68
|
-
amplificationRisk: risk,
|
|
69
|
-
durationMs,
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
return summaries.sort((a, b) => new Date(b.lastSeen).getTime() - new Date(a.lastSeen).getTime());
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Identify sessions with amplification risk worth flagging.
|
|
76
|
-
*/
|
|
77
|
-
export function detectAmplificationAlerts(sessions) {
|
|
78
|
-
const alerts = [];
|
|
79
|
-
for (const session of sessions) {
|
|
80
|
-
if (session.amplificationRisk === 'low')
|
|
81
|
-
continue;
|
|
82
|
-
let message;
|
|
83
|
-
const risk = session.amplificationRisk;
|
|
84
|
-
// rc.17 framing: don't accuse "injection" by default — that wording scares
|
|
85
|
-
// users on perfectly normal long attended sessions. Reserve the strong
|
|
86
|
-
// language for cases where the *blocked-call ratio* is high (real signal
|
|
87
|
-
// of prompts trying things they shouldn't), not just call volume.
|
|
88
|
-
// UX_OBSERVATIONS.md P1.
|
|
89
|
-
const blockRatio = session.totalCalls > 0 ? session.blockedCalls / session.totalCalls : 0;
|
|
90
|
-
if (session.blockedCalls > 0 && session.totalCalls > 20 && blockRatio > 0.05) {
|
|
91
|
-
message = `${session.totalCalls} tool calls, ${session.blockedCalls} blocked (${(blockRatio * 100).toFixed(0)}%) — high block ratio, review for injection`;
|
|
92
|
-
}
|
|
93
|
-
else if (session.blockedCalls > 0 && session.totalCalls > 20) {
|
|
94
|
-
message = `${session.totalCalls} tool calls, ${session.blockedCalls} blocked — long session, review if unattended`;
|
|
95
|
-
}
|
|
96
|
-
else if (session.totalCalls > 50) {
|
|
97
|
-
message = `${session.totalCalls} tool calls in one session — high density, review if unattended`;
|
|
98
|
-
}
|
|
99
|
-
else if ((session.toolMix['Bash'] ?? 0) > 10) {
|
|
100
|
-
message = `${session.toolMix['Bash']} Bash calls in one session — elevated tool-mix`;
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
message = `${session.totalCalls} calls, ${session.blockedCalls} blocked — worth a glance`;
|
|
104
|
-
}
|
|
105
|
-
alerts.push({
|
|
106
|
-
sessionId: session.sessionId,
|
|
107
|
-
message,
|
|
108
|
-
callCount: session.totalCalls,
|
|
109
|
-
blockedCount: session.blockedCalls,
|
|
110
|
-
risk: risk === 'high' ? 'high' : 'medium',
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
return alerts.slice(0, 5); // top 5 alerts
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Real coding tools — the ones a genuine host terminal session uses. Synthetic
|
|
117
|
-
* audit-event types (WritePass.intent, pre_prompt, etc.) and seed fixtures only
|
|
118
|
-
* carry these internal names, so requiring a real tool filters them out.
|
|
119
|
-
*/
|
|
120
|
-
export const REAL_HOST_TOOLS = new Set([
|
|
121
|
-
'Bash',
|
|
122
|
-
'Edit',
|
|
123
|
-
'Write',
|
|
124
|
-
'MultiEdit',
|
|
125
|
-
'Read',
|
|
126
|
-
'Glob',
|
|
127
|
-
'Grep',
|
|
128
|
-
'Task',
|
|
129
|
-
'NotebookEdit',
|
|
130
|
-
'WebFetch',
|
|
131
|
-
]);
|
|
132
|
-
/**
|
|
133
|
-
* Is this a genuine host coding session worth surfacing — not a seed fixture or
|
|
134
|
-
* a stale log entry? Requires at least one real coding tool and recent activity.
|
|
135
|
-
* Surfacing stale/synthetic sessions as "running in your terminal" is the
|
|
136
|
-
* screen-lie the no-screen-lies rule bans.
|
|
137
|
-
*/
|
|
138
|
-
export function isRealHostSession(s, nowMs = Date.now(), recencyMs = 48 * 60 * 60 * 1000) {
|
|
139
|
-
const hasRealTool = Object.keys(s.toolMix).some((t) => REAL_HOST_TOOLS.has(t));
|
|
140
|
-
if (!hasRealTool)
|
|
141
|
-
return false;
|
|
142
|
-
const last = new Date(s.lastSeen).getTime();
|
|
143
|
-
if (!Number.isFinite(last))
|
|
144
|
-
return false;
|
|
145
|
-
return last >= nowMs - recencyMs;
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Plain-English preview of a tool call for the host-session thread timeline.
|
|
149
|
-
* The hook stores toolInput as a (sometimes truncated) JSON string. Extract the
|
|
150
|
-
* meaningful field — the bash command, the file path — never dump raw JSON at
|
|
151
|
-
* the user. Falls back to a regex when the JSON is cut off mid-string.
|
|
152
|
-
*/
|
|
153
|
-
export function previewFromToolInput(toolInput, max = 140) {
|
|
154
|
-
if (!toolInput)
|
|
155
|
-
return '';
|
|
156
|
-
let out;
|
|
157
|
-
try {
|
|
158
|
-
const inp = JSON.parse(toolInput);
|
|
159
|
-
out = String(inp.command ?? inp.file_path ?? inp.path ?? inp.pattern ?? inp.url ?? '');
|
|
160
|
-
}
|
|
161
|
-
catch {
|
|
162
|
-
const m = toolInput.match(/"(?:command|file_path|path|pattern|url)"\s*:\s*"((?:[^"\\]|\\.)*)/);
|
|
163
|
-
out = m ? m[1].replace(/\\"/g, '"').replace(/\\n/g, ' ') : '';
|
|
164
|
-
}
|
|
165
|
-
return out.slice(0, max);
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Format session duration for display.
|
|
169
|
-
*/
|
|
170
|
-
export function formatDuration(ms) {
|
|
171
|
-
if (ms < 60_000)
|
|
172
|
-
return `${Math.round(ms / 1000)}s`;
|
|
173
|
-
if (ms < 3_600_000)
|
|
174
|
-
return `${Math.round(ms / 60_000)}m`;
|
|
175
|
-
return `${Math.round(ms / 3_600_000)}h`;
|
|
176
|
-
}
|
|
177
|
-
//# sourceMappingURL=session-analyzer.js.map
|
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Infrastructure: Execution Tracer
|
|
3
|
-
* Tracks every LLM call, tool invocation, and operation for reproducibility
|
|
4
|
-
*
|
|
5
|
-
* Clean Architecture: Infrastructure Layer
|
|
6
|
-
* Strategic: Critical for production reliability and debugging
|
|
7
|
-
*/
|
|
8
|
-
import { randomUUID } from 'crypto';
|
|
9
|
-
import { writeFileSync, mkdirSync, existsSync, readFileSync } from 'fs';
|
|
10
|
-
import { join, dirname } from 'path';
|
|
11
|
-
import { fileURLToPath } from 'url';
|
|
12
|
-
import { getVersion } from '../../utils/version.js';
|
|
13
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
-
const __dirname = dirname(__filename);
|
|
15
|
-
/**
|
|
16
|
-
* Execution Tracer
|
|
17
|
-
* Tracks all operations for debugging and reproducibility
|
|
18
|
-
*/
|
|
19
|
-
export class ExecutionTracer {
|
|
20
|
-
currentRun = null;
|
|
21
|
-
tracesDir;
|
|
22
|
-
constructor() {
|
|
23
|
-
// Store traces in agent/data/traces/
|
|
24
|
-
const agentDir = join(__dirname, '../../..');
|
|
25
|
-
this.tracesDir = join(agentDir, 'data', 'traces');
|
|
26
|
-
if (!existsSync(this.tracesDir)) {
|
|
27
|
-
mkdirSync(this.tracesDir, { recursive: true });
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Start a new run context
|
|
32
|
-
*/
|
|
33
|
-
startRun(operation, context) {
|
|
34
|
-
const runId = randomUUID();
|
|
35
|
-
this.currentRun = {
|
|
36
|
-
runId,
|
|
37
|
-
startTime: Date.now(),
|
|
38
|
-
operation,
|
|
39
|
-
query: context?.query,
|
|
40
|
-
codebaseId: context?.codebaseId,
|
|
41
|
-
userAgent: context?.userAgent || 'codehere-cli',
|
|
42
|
-
version: getVersion(),
|
|
43
|
-
traces: [],
|
|
44
|
-
};
|
|
45
|
-
return runId;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* End the current run and save to disk
|
|
49
|
-
*/
|
|
50
|
-
endRun() {
|
|
51
|
-
if (!this.currentRun) {
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
this.currentRun.endTime = Date.now();
|
|
55
|
-
// Save run to disk
|
|
56
|
-
const runFilePath = join(this.tracesDir, `${this.currentRun.runId}.json`);
|
|
57
|
-
writeFileSync(runFilePath, JSON.stringify(this.currentRun, null, 2), 'utf-8');
|
|
58
|
-
this.currentRun = null;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Trace an operation
|
|
62
|
-
*/
|
|
63
|
-
trace(operation) {
|
|
64
|
-
if (!this.currentRun) {
|
|
65
|
-
// If no run context, create a temporary one
|
|
66
|
-
this.startRun('auto-trace');
|
|
67
|
-
}
|
|
68
|
-
const traceId = randomUUID();
|
|
69
|
-
const startTime = Date.now();
|
|
70
|
-
const trace = {
|
|
71
|
-
traceId,
|
|
72
|
-
runId: this.currentRun.runId,
|
|
73
|
-
operationType: operation.operationType,
|
|
74
|
-
operationName: operation.operationName,
|
|
75
|
-
timestamp: startTime,
|
|
76
|
-
duration: operation.duration,
|
|
77
|
-
input: this.sanitizeInput(operation.input),
|
|
78
|
-
output: this.sanitizeOutput(operation.output),
|
|
79
|
-
error: operation.error
|
|
80
|
-
? {
|
|
81
|
-
message: operation.error.message,
|
|
82
|
-
stack: operation.error.stack,
|
|
83
|
-
code: operation.error.code,
|
|
84
|
-
}
|
|
85
|
-
: undefined,
|
|
86
|
-
metadata: operation.metadata,
|
|
87
|
-
};
|
|
88
|
-
this.currentRun.traces.push(trace);
|
|
89
|
-
return traceId;
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Get run by ID (for reproducibility)
|
|
93
|
-
*/
|
|
94
|
-
getRun(runId) {
|
|
95
|
-
const runFilePath = join(this.tracesDir, `${runId}.json`);
|
|
96
|
-
if (!existsSync(runFilePath)) {
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
try {
|
|
100
|
-
const content = readFileSync(runFilePath, 'utf-8');
|
|
101
|
-
return JSON.parse(content);
|
|
102
|
-
}
|
|
103
|
-
catch (error) {
|
|
104
|
-
console.error(`Failed to load run ${runId}:`, error);
|
|
105
|
-
return null;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* List recent runs
|
|
110
|
-
*/
|
|
111
|
-
/**
|
|
112
|
-
* Reproduce a run (replay operations in sequence)
|
|
113
|
-
* Enhanced: Actually replays operations with state restoration
|
|
114
|
-
*/
|
|
115
|
-
async reproduceRun(runId, options) {
|
|
116
|
-
const run = this.getRun(runId);
|
|
117
|
-
if (!run) {
|
|
118
|
-
throw new Error(`Run ${runId} not found`);
|
|
119
|
-
}
|
|
120
|
-
// Create new run context for reproduction
|
|
121
|
-
const reproductionRunId = this.startRun(`reproduce:${run.operation}`, {
|
|
122
|
-
query: run.query,
|
|
123
|
-
codebaseId: run.codebaseId,
|
|
124
|
-
});
|
|
125
|
-
// Log reproduction start
|
|
126
|
-
console.log(`Reproducing run ${runId}...`);
|
|
127
|
-
console.log(`Operation: ${run.operation}`);
|
|
128
|
-
console.log(`Query: ${run.query || 'N/A'}`);
|
|
129
|
-
console.log(`Traces: ${run.traces.length}`);
|
|
130
|
-
console.log(`Step-by-step: ${options?.stepByStep || false}`);
|
|
131
|
-
// Replay each trace in sequence
|
|
132
|
-
for (let index = 0; index < run.traces.length; index++) {
|
|
133
|
-
const trace = run.traces[index];
|
|
134
|
-
console.log(`\n[${index + 1}/${run.traces.length}] ${trace.operationType}: ${trace.operationName}`);
|
|
135
|
-
if (trace.error) {
|
|
136
|
-
console.log(` ! Original error: ${trace.error.message}`);
|
|
137
|
-
// Replay error trace
|
|
138
|
-
this.trace({
|
|
139
|
-
operationType: trace.operationType,
|
|
140
|
-
operationName: trace.operationName,
|
|
141
|
-
input: trace.input,
|
|
142
|
-
output: trace.output,
|
|
143
|
-
error: new Error(trace.error.message),
|
|
144
|
-
metadata: trace.metadata,
|
|
145
|
-
duration: trace.duration,
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
else {
|
|
149
|
-
// Replay successful trace
|
|
150
|
-
this.trace({
|
|
151
|
-
operationType: trace.operationType,
|
|
152
|
-
operationName: trace.operationName,
|
|
153
|
-
input: trace.input,
|
|
154
|
-
output: trace.output,
|
|
155
|
-
metadata: trace.metadata,
|
|
156
|
-
duration: trace.duration,
|
|
157
|
-
});
|
|
158
|
-
// For tool invocations, attempt to replay if input/output available
|
|
159
|
-
if (trace.operationType === 'tool_invocation' && trace.input && trace.output) {
|
|
160
|
-
console.log(` ✓ Replayed: ${trace.operationName}`);
|
|
161
|
-
if (trace.metadata?.filepath) {
|
|
162
|
-
console.log(` File: ${trace.metadata.filepath}`);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
// Step-by-step mode: wait for user confirmation
|
|
167
|
-
if (options?.stepByStep && index < run.traces.length - 1) {
|
|
168
|
-
// In a real CLI, we'd use readline to wait for user input
|
|
169
|
-
// For now, just log that step-by-step is enabled
|
|
170
|
-
console.log(` [Press Enter to continue to next step...]`);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
console.log(`\n✓ Reproduction complete. New run ID: ${reproductionRunId}`);
|
|
174
|
-
this.endRun();
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Get current run ID
|
|
178
|
-
*/
|
|
179
|
-
getCurrentRunId() {
|
|
180
|
-
return this.currentRun?.runId || null;
|
|
181
|
-
}
|
|
182
|
-
/**
|
|
183
|
-
* Sanitize input for storage (remove sensitive data, large objects)
|
|
184
|
-
*/
|
|
185
|
-
sanitizeInput(input) {
|
|
186
|
-
if (!input)
|
|
187
|
-
return input;
|
|
188
|
-
// Limit size and remove sensitive fields
|
|
189
|
-
const sanitized = JSON.parse(JSON.stringify(input, (key, value) => {
|
|
190
|
-
// Remove API keys and secrets
|
|
191
|
-
if (typeof key === 'string' &&
|
|
192
|
-
(key.toLowerCase().includes('key') ||
|
|
193
|
-
key.toLowerCase().includes('secret') ||
|
|
194
|
-
key.toLowerCase().includes('password'))) {
|
|
195
|
-
return '[REDACTED]';
|
|
196
|
-
}
|
|
197
|
-
// Limit string length
|
|
198
|
-
if (typeof value === 'string' && value.length > 10000) {
|
|
199
|
-
return value.substring(0, 10000) + '...[truncated]';
|
|
200
|
-
}
|
|
201
|
-
// Limit array/object depth
|
|
202
|
-
return value;
|
|
203
|
-
}));
|
|
204
|
-
// Limit total size
|
|
205
|
-
const serialized = JSON.stringify(sanitized);
|
|
206
|
-
if (serialized.length > 100000) {
|
|
207
|
-
return { _truncated: true, _size: serialized.length };
|
|
208
|
-
}
|
|
209
|
-
return sanitized;
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* Sanitize output for storage
|
|
213
|
-
*/
|
|
214
|
-
sanitizeOutput(output) {
|
|
215
|
-
if (!output)
|
|
216
|
-
return output;
|
|
217
|
-
// Limit size
|
|
218
|
-
const serialized = JSON.stringify(output);
|
|
219
|
-
if (serialized.length > 50000) {
|
|
220
|
-
return { _truncated: true, _size: serialized.length, _preview: serialized.substring(0, 1000) };
|
|
221
|
-
}
|
|
222
|
-
return output;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
// Singleton instance
|
|
226
|
-
export const executionTracer = new ExecutionTracer();
|
|
227
|
-
//# sourceMappingURL=execution-tracer.js.map
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AgentAdapter — F2 foundation.
|
|
3
|
-
*
|
|
4
|
-
* Uniform interface over every AI coding agent codehere can spawn and manage.
|
|
5
|
-
* One adapter per backend: ClaudeCodeAdapter, CodexAdapter, AiderAdapter, …
|
|
6
|
-
*
|
|
7
|
-
* The interface intentionally carries **optional fields for later PM-UX layers**
|
|
8
|
-
* so we don't force a breaking change when v0.4/v0.5 ship progress roll-up,
|
|
9
|
-
* final report, intervention, and project scope. Unused today, ignored by the
|
|
10
|
-
* current renderer. See memory/project_manager_ux_v1.md for the full shape.
|
|
11
|
-
*/
|
|
12
|
-
export {};
|
|
13
|
-
//# sourceMappingURL=agent-adapter.js.map
|