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.
Files changed (163) hide show
  1. package/README.md +36 -192
  2. package/dist/application/use-cases/check-claimed-tests.js +81 -0
  3. package/dist/application/use-cases/detect-test-weakening.js +237 -0
  4. package/dist/application/use-cases/rerun-tests.js +110 -7
  5. package/dist/index.js +38 -1212
  6. package/dist/infrastructure/orchestration/artifacts-extractor.js +2 -323
  7. package/dist/presentation/cli/commands/check-command.js +178 -23
  8. package/dist/presentation/cli/commands/help-command.js +21 -285
  9. package/dist/ui.js +5 -236
  10. package/dist/utils/version.js +0 -6
  11. package/package.json +9 -38
  12. package/dist/application/services/verdict-service.js +0 -154
  13. package/dist/application/use-cases/assess-cross-vendor.js +0 -126
  14. package/dist/application/use-cases/build-cross-vendor-verdict.js +0 -118
  15. package/dist/application/use-cases/domain-trust.js +0 -110
  16. package/dist/application/use-cases/export-aat.js +0 -107
  17. package/dist/application/use-cases/generate-acceptance-criteria.js +0 -223
  18. package/dist/application/use-cases/generate-final-report.js +0 -259
  19. package/dist/application/use-cases/generate-narration.js +0 -130
  20. package/dist/application/use-cases/generate-thread-title.js +0 -181
  21. package/dist/application/use-cases/narrator-prompt-template.js +0 -158
  22. package/dist/application/use-cases/plan-auto-review.js +0 -57
  23. package/dist/application/use-cases/reconcile-audit.js +0 -100
  24. package/dist/application/use-cases/search-sessions.js +0 -70
  25. package/dist/application/use-cases/should-auto-review.js +0 -71
  26. package/dist/application/use-cases/thread-export.js +0 -245
  27. package/dist/application/use-cases/verify-claim.js +0 -456
  28. package/dist/application/use-cases/write-pass.js +0 -277
  29. package/dist/domain/audit.js +0 -11
  30. package/dist/domain/services/codebase-detector.js +0 -85
  31. package/dist/domain/task.js +0 -91
  32. package/dist/error-handler.js +0 -139
  33. package/dist/infrastructure/completion/bash-completion.js +0 -29
  34. package/dist/infrastructure/completion/fish-completion.js +0 -17
  35. package/dist/infrastructure/completion/zsh-completion.js +0 -25
  36. package/dist/infrastructure/config/config-manager.js +0 -174
  37. package/dist/infrastructure/config/product-context.js +0 -49
  38. package/dist/infrastructure/context/adapters/drawio-source.js +0 -157
  39. package/dist/infrastructure/context/adapters/figma-source.js +0 -198
  40. package/dist/infrastructure/context/adapters/github-issue-source.js +0 -160
  41. package/dist/infrastructure/context/adapters/linear-source.js +0 -190
  42. package/dist/infrastructure/context/context-resolver.js +0 -144
  43. package/dist/infrastructure/context/context-source.js +0 -114
  44. package/dist/infrastructure/context/github-api.js +0 -110
  45. package/dist/infrastructure/context/github-device-flow.js +0 -65
  46. package/dist/infrastructure/context/oauth-flow.js +0 -207
  47. package/dist/infrastructure/context/oauth-store.js +0 -122
  48. package/dist/infrastructure/context/registry-bootstrap.js +0 -95
  49. package/dist/infrastructure/context/write-pass-resolver.js +0 -106
  50. package/dist/infrastructure/cost/budget-config.js +0 -114
  51. package/dist/infrastructure/cost/cost-router.js +0 -97
  52. package/dist/infrastructure/cost/spend-aggregator.js +0 -222
  53. package/dist/infrastructure/cost/team-ledger.js +0 -355
  54. package/dist/infrastructure/discovery/discovery-log.js +0 -85
  55. package/dist/infrastructure/hooks/audit-tip-signature.js +0 -336
  56. package/dist/infrastructure/hooks/claude-sdk-hook.js +0 -1018
  57. package/dist/infrastructure/hooks/hook-mode.js +0 -87
  58. package/dist/infrastructure/hooks/mirror-tool-call-to-audit.js +0 -49
  59. package/dist/infrastructure/hooks/pre-prompt-scan.js +0 -122
  60. package/dist/infrastructure/hooks/session-analyzer.js +0 -177
  61. package/dist/infrastructure/observability/execution-tracer.js +0 -227
  62. package/dist/infrastructure/orchestration/agent-adapter.js +0 -13
  63. package/dist/infrastructure/orchestration/agent-installer.js +0 -101
  64. package/dist/infrastructure/orchestration/agents-config-seed.js +0 -68
  65. package/dist/infrastructure/orchestration/aider-adapter.js +0 -611
  66. package/dist/infrastructure/orchestration/cassette-recorder.js +0 -120
  67. package/dist/infrastructure/orchestration/claude-code-adapter.js +0 -592
  68. package/dist/infrastructure/orchestration/codex-adapter.js +0 -574
  69. package/dist/infrastructure/orchestration/conductor-adapter.js +0 -416
  70. package/dist/infrastructure/orchestration/context-handoff.js +0 -162
  71. package/dist/infrastructure/orchestration/control/codex-control-surface.js +0 -99
  72. package/dist/infrastructure/orchestration/control/control-surface.js +0 -15
  73. package/dist/infrastructure/orchestration/detect-ollama.js +0 -75
  74. package/dist/infrastructure/orchestration/failover-classifier.js +0 -151
  75. package/dist/infrastructure/orchestration/failover-config.js +0 -67
  76. package/dist/infrastructure/orchestration/failover-policy.js +0 -106
  77. package/dist/infrastructure/orchestration/gemini-adapter.js +0 -663
  78. package/dist/infrastructure/orchestration/generic-cli-adapter.js +0 -689
  79. package/dist/infrastructure/orchestration/graphify-context.js +0 -209
  80. package/dist/infrastructure/orchestration/memory-injector.js +0 -380
  81. package/dist/infrastructure/orchestration/mocks/mock-agent-repl.js +0 -388
  82. package/dist/infrastructure/orchestration/mocks/mock-codex-app-server.js +0 -73
  83. package/dist/infrastructure/orchestration/mocks/mock-synthesis.js +0 -83
  84. package/dist/infrastructure/orchestration/mocks/resolve-agent-spawn.js +0 -37
  85. package/dist/infrastructure/orchestration/opencode-adapter.js +0 -580
  86. package/dist/infrastructure/orchestration/progress-aggregator.js +0 -906
  87. package/dist/infrastructure/orchestration/readiness.js +0 -103
  88. package/dist/infrastructure/orchestration/session-manager.js +0 -1590
  89. package/dist/infrastructure/orchestration/session-persistence.js +0 -394
  90. package/dist/infrastructure/orchestration/thread-key-store.js +0 -127
  91. package/dist/infrastructure/orchestration/write-pass-store.js +0 -38
  92. package/dist/infrastructure/protocols/cloud-sync.js +0 -207
  93. package/dist/infrastructure/protocols/mcp-helpers.js +0 -25
  94. package/dist/infrastructure/protocols/mcp-server-impl.js +0 -3935
  95. package/dist/infrastructure/protocols/mcp-tools.js +0 -401
  96. package/dist/infrastructure/protocols/probe-port.js +0 -36
  97. package/dist/infrastructure/protocols/thread-phase.js +0 -105
  98. package/dist/infrastructure/protocols/ui/app-script-artifacts.js +0 -121
  99. package/dist/infrastructure/protocols/ui/app-script-audit-footer.js +0 -44
  100. package/dist/infrastructure/protocols/ui/app-script-audit-verify.js +0 -56
  101. package/dist/infrastructure/protocols/ui/app-script-composer-cost.js +0 -588
  102. package/dist/infrastructure/protocols/ui/app-script-context-sources.js +0 -168
  103. package/dist/infrastructure/protocols/ui/app-script-conversation.js +0 -67
  104. package/dist/infrastructure/protocols/ui/app-script-drawer.js +0 -115
  105. package/dist/infrastructure/protocols/ui/app-script-failover.js +0 -133
  106. package/dist/infrastructure/protocols/ui/app-script-github.js +0 -113
  107. package/dist/infrastructure/protocols/ui/app-script-observer.js +0 -104
  108. package/dist/infrastructure/protocols/ui/app-script-onboarding.js +0 -80
  109. package/dist/infrastructure/protocols/ui/app-script-overlay.js +0 -203
  110. package/dist/infrastructure/protocols/ui/app-script-recovery.js +0 -136
  111. package/dist/infrastructure/protocols/ui/app-script-render.js +0 -166
  112. package/dist/infrastructure/protocols/ui/app-script-share.js +0 -78
  113. package/dist/infrastructure/protocols/ui/app-script-sidebar.js +0 -194
  114. package/dist/infrastructure/protocols/ui/app-script-tour.js +0 -78
  115. package/dist/infrastructure/protocols/ui/app-script-wpass.js +0 -312
  116. package/dist/infrastructure/protocols/ui/app-script.js +0 -2998
  117. package/dist/infrastructure/protocols/ui/font-faces.js +0 -7
  118. package/dist/infrastructure/protocols/ui/styles.js +0 -2147
  119. package/dist/infrastructure/protocols/ui/tokens.js +0 -96
  120. package/dist/infrastructure/protocols/ui-page-v2.js +0 -320
  121. package/dist/infrastructure/protocols/verify-page.js +0 -1677
  122. package/dist/infrastructure/security/file-collection.js +0 -118
  123. package/dist/infrastructure/security/fix-code.js +0 -144
  124. package/dist/infrastructure/security/fix-secret.js +0 -89
  125. package/dist/infrastructure/security/permissions-config.js +0 -235
  126. package/dist/infrastructure/security/scan-types.js +0 -10
  127. package/dist/infrastructure/security/ship-scan.js +0 -195
  128. package/dist/infrastructure/security/trust-scorer.js +0 -724
  129. package/dist/infrastructure/security/vibe-checks.js +0 -99
  130. package/dist/infrastructure/storage/atomic-write.js +0 -86
  131. package/dist/infrastructure/storage/cross-tool-agents.js +0 -268
  132. package/dist/infrastructure/storage/project-thread-migration.js +0 -237
  133. package/dist/infrastructure/storage/project-thread-mirror.js +0 -671
  134. package/dist/infrastructure/storage/project-thread-reader.js +0 -409
  135. package/dist/infrastructure/storage/project-thread-title-backfill.js +0 -214
  136. package/dist/monitoring.js +0 -212
  137. package/dist/presentation/cli/commands/audit-command.js +0 -201
  138. package/dist/presentation/cli/commands/budget-command.js +0 -79
  139. package/dist/presentation/cli/commands/config-command.js +0 -125
  140. package/dist/presentation/cli/commands/context-source-command.js +0 -217
  141. package/dist/presentation/cli/commands/hook-command.js +0 -265
  142. package/dist/presentation/cli/commands/init-command.js +0 -590
  143. package/dist/presentation/cli/commands/memory-command.js +0 -1031
  144. package/dist/presentation/cli/commands/narrator-command.js +0 -101
  145. package/dist/presentation/cli/commands/permission-command.js +0 -105
  146. package/dist/presentation/cli/commands/protocols-builder.js +0 -254
  147. package/dist/presentation/cli/commands/protocols-command.js +0 -84
  148. package/dist/presentation/cli/commands/repo-map-builder.js +0 -247
  149. package/dist/presentation/cli/commands/report-command.js +0 -403
  150. package/dist/presentation/cli/commands/scan-command.js +0 -52
  151. package/dist/presentation/cli/commands/scan-tool-command.js +0 -110
  152. package/dist/presentation/cli/commands/search-command.js +0 -51
  153. package/dist/presentation/cli/commands/setup-command.js +0 -330
  154. package/dist/presentation/cli/commands/status-command.js +0 -614
  155. package/dist/presentation/cli/commands/team-command.js +0 -256
  156. package/dist/presentation/cli/commands/thread-command.js +0 -370
  157. package/dist/presentation/cli/commands/trust-command.js +0 -328
  158. package/dist/presentation/cli/commands/verify-command.js +0 -298
  159. package/dist/presentation/cli/error-display.js +0 -328
  160. package/dist/presentation/cli/repl.js +0 -805
  161. package/dist/presentation/cli/task-deliverable.js +0 -153
  162. package/dist/testing/cassette.js +0 -125
  163. package/dist/testing/replay-adapter.js +0 -147
@@ -1,70 +0,0 @@
1
- /**
2
- * Provable session search — REQUEST_METHOD stage 7 (Learn), and the Luca-seeded
3
- * differentiator (#19): codehere already keeps a signed, cost-attributed record
4
- * of every past session. This makes it SEARCHABLE — so a request can recover a
5
- * prior decision, failed attempt, or command instead of starting from zero.
6
- *
7
- * The wedge vs a plain history search (e.g. ctx): every hit carries PROVENANCE —
8
- * which agent did it and whether the run succeeded — because codehere's record
9
- * has it and a raw-transcript search doesn't. "Search history you can trust."
10
- *
11
- * Local-first: reads the on-disk project thread records; no server, no network.
12
- */
13
- import { listProjects, readProjectThread, } from '../../infrastructure/storage/project-thread-reader.js';
14
- /** Pure ranking — score a task against query terms (prompt weighted 2x report). */
15
- export function scoreTask(task, terms) {
16
- const prompt = String(task.prompt || '').toLowerCase();
17
- const report = String(task.finalReport?.summary || '').toLowerCase();
18
- let score = 0;
19
- for (const t of terms) {
20
- if (prompt.includes(t))
21
- score += 2;
22
- if (report.includes(t))
23
- score += 1;
24
- }
25
- let snippet = '';
26
- const hay = report || prompt;
27
- for (const t of terms) {
28
- const i = hay.indexOf(t);
29
- if (i >= 0) {
30
- const start = Math.max(0, i - 30);
31
- snippet = (start > 0 ? '…' : '') + hay.slice(start, start + 100).trim() + '…';
32
- break;
33
- }
34
- }
35
- return { score, snippet: snippet || String(task.prompt || '').slice(0, 90) };
36
- }
37
- /** Search past sessions across all projects. Ranked, top-N, with provenance. */
38
- export function searchSessions(query, opts) {
39
- const terms = String(query || '')
40
- .toLowerCase()
41
- .split(/\s+/)
42
- .filter((w) => w.length >= 2);
43
- if (!terms.length)
44
- return [];
45
- const limit = opts?.limit ?? 10;
46
- const hits = [];
47
- for (const p of listProjects()) {
48
- const thread = readProjectThread(p.slug);
49
- if (!thread)
50
- continue;
51
- for (const task of thread.tasks) {
52
- if (task.archived)
53
- continue;
54
- const { score, snippet } = scoreTask(task, terms);
55
- if (score > 0) {
56
- hits.push({
57
- sessionId: task.sessionId,
58
- agent: task.agent || '?',
59
- status: String(task.status || 'unknown'),
60
- project: p.remote || p.slug,
61
- prompt: task.prompt || '',
62
- snippet,
63
- score,
64
- });
65
- }
66
- }
67
- }
68
- return hits.sort((a, b) => b.score - a.score).slice(0, limit);
69
- }
70
- //# sourceMappingURL=search-sessions.js.map
@@ -1,71 +0,0 @@
1
- /**
2
- * Tier 2.1 · slice 1 — the pure decision: should a just-completed task get an
3
- * AUTOMATIC cross-vendor adversarial review, and by which vendor?
4
- *
5
- * Today cross-vendor proof is opportunistic: it only fires if a human happened
6
- * to run a second, different-vendor task that reviewed the first. 2.1 makes the
7
- * differentiator automatic — on completion, codehere itself spawns a DIFFERENT
8
- * vendor in refute-mode and folds its verdict into the composite (`SessionVerdict`).
9
- *
10
- * This module is ONLY the eligibility gate, kept pure so the policy is testable
11
- * without a daemon, a spawn, or real spend (the spawn + verdict wiring are later
12
- * slices). The honesty bar from assess-cross-vendor applies downstream: a real
13
- * review with a real reviewer, never a laundered green.
14
- *
15
- * The three gates, in order:
16
- * 1. cross-vendor possible — a DIFFERENT installed vendor exists to review with
17
- * (single-vendor incumbents structurally can't do this; it's the whole wedge).
18
- * 2. worth reviewing — the task actually changed something (no-op tasks have
19
- * nothing to refute; reviewing them burns spend for a guaranteed-empty proof).
20
- * 3. within budget — the review is a real spawn with real cost; never blow the
21
- * cost-bounded promise to produce a receipt.
22
- */
23
- /** Reviewer preference order — strongest-at-review first, mirrors ROUTING_RULES'
24
- * review rule (claude-code leads, then codex). Any installed different-vendor
25
- * agent is acceptable; this only decides WHICH when several are available. */
26
- // Reviewer preference favors reliably-authed, cost-bounded vendors first: Claude +
27
- // Cursor (flat subscriptions, ~free marginal) before the metered/rate-limit-prone
28
- // ones (aider/opencode on the OpenAI key, codex on a ChatGPT plan, gemini needs a
29
- // key). A runnable reviewer on a subscription you've paid for beats a "better" one
30
- // that's out of quota.
31
- const REVIEWER_PREFERENCE = ['claude-code', 'cursor', 'aider', 'opencode', 'codex', 'gemini'];
32
- /** Pick the strongest available reviewer that is NOT the writer's vendor. */
33
- function pickReviewer(writerAgent, installed) {
34
- const candidates = installed.filter((a) => a !== writerAgent && a !== 'conductor');
35
- if (candidates.length === 0)
36
- return undefined;
37
- for (const pref of REVIEWER_PREFERENCE) {
38
- if (candidates.includes(pref))
39
- return pref;
40
- }
41
- return candidates[0];
42
- }
43
- /**
44
- * Decide whether to auto-spawn a cross-vendor review. Pure; no I/O, no spend.
45
- */
46
- export function shouldAutoReview(input, thresholds = {}) {
47
- const minFiles = thresholds.minFilesChanged ?? 1;
48
- const minBudget = thresholds.minBudgetUsd ?? 0.1;
49
- const reviewerVendor = pickReviewer(input.writerAgent, input.installedAgents);
50
- if (!reviewerVendor) {
51
- return { eligible: false, reason: `No different vendor installed to review ${input.writerAgent}'s work.` };
52
- }
53
- if (input.filesChanged < minFiles) {
54
- return {
55
- eligible: false,
56
- reason: `Nothing to review — the task changed ${input.filesChanged} file(s); a cross-vendor review needs real work to refute.`,
57
- };
58
- }
59
- if (input.budgetRemainingUsd < minBudget) {
60
- return {
61
- eligible: false,
62
- reason: `Skipped to stay cost-bounded — $${input.budgetRemainingUsd.toFixed(2)} left under the cap, below the $${minBudget.toFixed(2)} a review needs.`,
63
- };
64
- }
65
- return {
66
- eligible: true,
67
- reviewerVendor,
68
- reason: `${reviewerVendor} will independently review ${input.writerAgent}'s work in refute-mode.`,
69
- };
70
- }
71
- //# sourceMappingURL=should-auto-review.js.map
@@ -1,245 +0,0 @@
1
- /**
2
- * Thread export · v0.8 S4 · signed-payload "GitHub for agent sessions"
3
- *
4
- * Produces a self-contained, Ed25519-signed bundle of one PersistedSession
5
- * that a colleague can verify and replay locally. Local-first by design:
6
- * never relayed through codehere's infrastructure · the user shares the
7
- * file (or signed URL) by their preferred channel (Slack, email, PR
8
- * description) and the recipient runs `codehere thread import <file>`.
9
- *
10
- * The signature covers a CANONICAL JSON form of the bundle body so two
11
- * machines that re-serialize the same data get the same hash. Verification
12
- * recomputes the canonical form, re-derives the hash, checks the signature
13
- * against the included public key, and returns one of:
14
- * - 'valid' · signature matches · safe to replay
15
- * - 'tampered' · signature does NOT match · refuse
16
- * - 'malformed' · bundle is structurally wrong · refuse
17
- * - 'unknown_key' · signature valid but public key not in user's trust
18
- * store · prompt user to trust+continue or refuse
19
- *
20
- * Pure logic. The CLI + HTTP routes wrap this and do I/O.
21
- */
22
- import { sign as cryptoSign, verify as cryptoVerify, generateKeyPairSync, createPrivateKey, createPublicKey, } from 'crypto';
23
- import { existsSync, readFileSync } from 'fs';
24
- import { computeEntryHash, AUDIT_FILE } from '../../infrastructure/hooks/claude-sdk-hook.js';
25
- /* ──────────────────────────────────────────────────────────────────────── */
26
- /* Canonicalization · deterministic JSON form so signatures match */
27
- /* ──────────────────────────────────────────────────────────────────────── */
28
- /**
29
- * Stable JSON: object keys sorted, no whitespace, primitives preserved.
30
- * Two machines that build the same body MUST produce the same bytes for
31
- * the signature to verify. We deliberately don't use JSON.stringify with
32
- * a replacer because Node's serializer doesn't guarantee key order across
33
- * versions/realms.
34
- */
35
- export function canonicalize(value) {
36
- if (value === null)
37
- return 'null';
38
- if (typeof value === 'number') {
39
- if (!Number.isFinite(value))
40
- throw new Error(`cannot canonicalize non-finite number: ${value}`);
41
- return JSON.stringify(value);
42
- }
43
- if (typeof value === 'string' || typeof value === 'boolean')
44
- return JSON.stringify(value);
45
- if (Array.isArray(value)) {
46
- return '[' + value.map(canonicalize).join(',') + ']';
47
- }
48
- if (typeof value === 'object') {
49
- const obj = value;
50
- const keys = Object.keys(obj).sort();
51
- const parts = keys
52
- .filter((k) => obj[k] !== undefined) // undefined → drop the key (JSON convention)
53
- .map((k) => JSON.stringify(k) + ':' + canonicalize(obj[k]));
54
- return '{' + parts.join(',') + '}';
55
- }
56
- if (value === undefined)
57
- return 'null';
58
- throw new Error(`cannot canonicalize type: ${typeof value}`);
59
- }
60
- /** Generate a fresh Ed25519 keypair · used for first-time setup. */
61
- export function generateKeypair() {
62
- const { publicKey, privateKey } = generateKeyPairSync('ed25519');
63
- return { publicKey, privateKey };
64
- }
65
- /** Sign a bundle body and produce the full ThreadBundle.
66
- * Ed25519 is NOT a hash-then-sign scheme · pass the message directly to
67
- * crypto.sign(null, msg, key) · the algorithm is implied by the key. */
68
- export function signBundle(body, keys) {
69
- const canonical = canonicalize(body);
70
- const sig = cryptoSign(null, Buffer.from(canonical, 'utf-8'), keys.privateKey);
71
- const pubRaw = keys.publicKey.export({ format: 'der', type: 'spki' });
72
- return {
73
- body,
74
- signature: sig.toString('base64'),
75
- publicKey: pubRaw.toString('base64'),
76
- alg: 'ed25519',
77
- };
78
- }
79
- /**
80
- * Verify a bundle. Returns one of:
81
- * - valid · signature matches AND the publicKey is in trustedKeys
82
- * - valid-untrusted · signature matches but the key isn't in trustedKeys
83
- * (returned with status='valid' but trusted=false)
84
- * - tampered · signature doesn't match the recomputed body bytes
85
- * - malformed · bundle structure broken
86
- */
87
- export function verifyBundle(bundle, trustedKeys = new Set()) {
88
- if (!isBundle(bundle))
89
- return { status: 'malformed', reason: 'bundle shape is invalid' };
90
- if (bundle.alg !== 'ed25519')
91
- return { status: 'malformed', reason: `unsupported alg: ${bundle.alg}` };
92
- try {
93
- const canonical = canonicalize(bundle.body);
94
- const pubBuf = Buffer.from(bundle.publicKey, 'base64');
95
- const sigBuf = Buffer.from(bundle.signature, 'base64');
96
- const pubKey = createPublicKey({ key: pubBuf, format: 'der', type: 'spki' });
97
- const ok = cryptoVerify(null, Buffer.from(canonical, 'utf-8'), pubKey, sigBuf);
98
- if (!ok)
99
- return { status: 'tampered', reason: 'signature does not match body' };
100
- const trusted = trustedKeys.has(bundle.publicKey);
101
- if (!trusted) {
102
- return { status: 'unknown_key', bundle, publicKey: bundle.publicKey };
103
- }
104
- return { status: 'valid', bundle, trusted: true };
105
- }
106
- catch (err) {
107
- return { status: 'malformed', reason: err instanceof Error ? err.message : String(err) };
108
- }
109
- }
110
- function isBundle(x) {
111
- if (typeof x !== 'object' || x === null)
112
- return false;
113
- const obj = x;
114
- if (typeof obj.signature !== 'string')
115
- return false;
116
- if (typeof obj.publicKey !== 'string')
117
- return false;
118
- if (typeof obj.alg !== 'string')
119
- return false;
120
- const body = obj.body;
121
- if (typeof body !== 'object' || body === null)
122
- return false;
123
- const b = body;
124
- if (b.schema !== 1)
125
- return false;
126
- if (b.kind !== 'thread')
127
- return false;
128
- if (typeof b.sessionId !== 'string')
129
- return false;
130
- if (typeof b.session !== 'object' || b.session === null)
131
- return false;
132
- // auditChain is optional, but if present it must be an array (the signature
133
- // already protects its contents; this just guards the shape).
134
- if (b.auditChain !== undefined && !Array.isArray(b.auditChain))
135
- return false;
136
- return true;
137
- }
138
- /* ──────────────────────────────────────────────────────────────────────── */
139
- /* Audit chain · embed (export side) + re-verify (import side) */
140
- /* ──────────────────────────────────────────────────────────────────────── */
141
- /**
142
- * Collect the audit entries for one session from the on-disk chain, preserving
143
- * each entry's original chain fields (seq/prevHash/entryHash). The export use-case
144
- * puts these on ThreadBundleBody.auditChain so the recipient can re-verify the
145
- * agent's actions. Best-effort: a missing/unreadable log yields [].
146
- */
147
- export function collectAuditEntriesForSession(sessionId, filepath = AUDIT_FILE, alsoSessionIds = []) {
148
- if (!existsSync(filepath))
149
- return [];
150
- let content;
151
- try {
152
- content = readFileSync(filepath, 'utf-8');
153
- }
154
- catch {
155
- return [];
156
- }
157
- // 1.3 (extended to audit reconciliation) · a conductor-routed task's tool
158
- // calls are keyed by the SPECIALIST's runtime sid, not the conductor task id.
159
- // Matching the delegate id too lets reconciliation see the real writes —
160
- // without it Auto tasks reconcile 'no-writes' and never earn the observed
161
- // signal, so the 2.2 composite grade stays dark for the default path.
162
- const ids = new Set([sessionId, ...alsoSessionIds]);
163
- const out = [];
164
- for (const line of content.split('\n')) {
165
- if (!line.trim())
166
- continue;
167
- try {
168
- const e = JSON.parse(line);
169
- if (ids.has(e.sessionId))
170
- out.push(e);
171
- }
172
- catch {
173
- /* skip unparseable line */
174
- }
175
- }
176
- return out;
177
- }
178
- /**
179
- * Re-verify an embedded audit chain's PER-ENTRY integrity: each chained entry's
180
- * stored entryHash must equal its recomputed hash (computeEntryHash, the exact
181
- * function the writer used). A mismatch means the entry's content was altered
182
- * after it was written → the record of what the agent did is not trustworthy.
183
- *
184
- * This is deliberately a self-hash check, not a cross-link walk: a single-session
185
- * export is a SUBSET of the global interleaved chain, so its entries' prevHash
186
- * values point at other sessions' entries that aren't present. Per-entry self-hash
187
- * + the bundle signature (signer vouches for the exact set) is the honest,
188
- * verifiable team-proof; global completeness is not claimable from a subset. See
189
- * ThreadBundleBody.auditChain for the FULL trust model (signer attestation, not
190
- * independent provenance — a malicious signer can fabricate a valid chain).
191
- *
192
- * CALLER CONTRACT: `valid` means "no tampered entry was found" — it is `true`
193
- * for an EMPTY or all-legacy set where NOTHING was cryptographically checked.
194
- * Any "✓ verified" surface MUST gate on `verified > 0`, not bare `valid`, or it
195
- * will claim verification that didn't happen.
196
- */
197
- export function verifyEmbeddedAuditChain(entries) {
198
- const check = { valid: true, total: 0, verified: 0, legacy: 0, tampered: [] };
199
- for (const entry of entries) {
200
- check.total++;
201
- const hasChain = typeof entry.seq === 'number' && typeof entry.prevHash === 'string' && typeof entry.entryHash === 'string';
202
- if (!hasChain) {
203
- check.legacy++;
204
- continue;
205
- }
206
- const recomputed = computeEntryHash(entry);
207
- if (recomputed !== entry.entryHash) {
208
- check.valid = false;
209
- check.tampered.push({
210
- seq: entry.seq,
211
- sessionId: entry.sessionId,
212
- toolName: entry.toolName,
213
- reason: 'self_hash_mismatch',
214
- });
215
- }
216
- else {
217
- check.verified++;
218
- }
219
- }
220
- return check;
221
- }
222
- /* ──────────────────────────────────────────────────────────────────────── */
223
- /* Key derivation helpers · PEM encoding for storage */
224
- /* ──────────────────────────────────────────────────────────────────────── */
225
- export function exportPrivatePem(key) {
226
- return key.export({ format: 'pem', type: 'pkcs8' }).toString();
227
- }
228
- export function exportPublicPem(key) {
229
- return key.export({ format: 'pem', type: 'spki' }).toString();
230
- }
231
- export function importPrivatePem(pem) {
232
- return createPrivateKey({ key: pem, format: 'pem', type: 'pkcs8' });
233
- }
234
- export function importPublicPem(pem) {
235
- return createPublicKey({ key: pem, format: 'pem', type: 'spki' });
236
- }
237
- /** Compute the base64 publicKey string for a KeyObject · same encoding as in bundles. */
238
- export function publicKeyToBase64(key) {
239
- return key.export({ format: 'der', type: 'spki' }).toString('base64');
240
- }
241
- /** Short fingerprint used in human-readable output. */
242
- export function shortFingerprint(publicKeyB64) {
243
- return publicKeyB64.slice(0, 8) + '...' + publicKeyB64.slice(-4);
244
- }
245
- //# sourceMappingURL=thread-export.js.map