@davesheffer/hunch 1.31.0 → 1.32.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/README.md +3 -6
- package/dist/cli/automaticReviewMemory.d.ts +13 -0
- package/dist/cli/dna.d.ts +2 -0
- package/dist/cli/index.d.ts +16 -0
- package/dist/cli/index.js +602 -43
- package/dist/cli/integrations.d.ts +2 -0
- package/dist/cli/integrations.js +5 -2
- package/dist/cli/invocation.d.ts +35 -0
- package/dist/cli/preflight.d.ts +1 -0
- package/dist/cli/reviewMemory.d.ts +4 -0
- package/dist/cli/reviewMemoryProvider.d.ts +14 -0
- package/dist/cli/serve.d.ts +2 -0
- package/dist/cli/taskReport.d.ts +6 -0
- package/dist/cli/taskReport.js +124 -0
- package/dist/cli/update.d.ts +14 -0
- package/dist/client/state.d.ts +248 -0
- package/dist/client/state.js +2 -0
- package/dist/constitution/adapters.d.ts +29 -0
- package/dist/constitution/behaviorAttestationBinding.d.ts +3 -0
- package/dist/constitution/behaviorEvaluator.d.ts +8 -0
- package/dist/constitution/behaviorProof.d.ts +9 -0
- package/dist/constitution/behaviorWorkspace.d.ts +25 -0
- package/dist/constitution/bootstrap.d.ts +30 -0
- package/dist/constitution/canonical.d.ts +15 -0
- package/dist/constitution/card.d.ts +58 -0
- package/dist/constitution/compiler.d.ts +52 -0
- package/dist/constitution/composition.d.ts +10 -0
- package/dist/constitution/corpus.d.ts +7 -0
- package/dist/constitution/correctionPolicyMaterializer.d.ts +57 -0
- package/dist/constitution/delta.d.ts +4 -0
- package/dist/constitution/disposition.d.ts +20 -0
- package/dist/constitution/evaluator.d.ts +47 -0
- package/dist/constitution/experiment.d.ts +466 -0
- package/dist/constitution/experimentRunner.d.ts +7 -0
- package/dist/constitution/g2.d.ts +209 -0
- package/dist/constitution/g2BehaviorAttestation.d.ts +45 -0
- package/dist/constitution/g2BehaviorCandidates.d.ts +123 -0
- package/dist/constitution/g2BehaviorDependencies.d.ts +83 -0
- package/dist/constitution/g2BehaviorMaterialization.d.ts +77 -0
- package/dist/constitution/g2BehaviorPolicyMaterializer.d.ts +48 -0
- package/dist/constitution/g2CandidateAttestation.d.ts +43 -0
- package/dist/constitution/g2Candidates.d.ts +80 -0
- package/dist/constitution/g2Drills.d.ts +33 -0
- package/dist/constitution/g3.d.ts +311 -0
- package/dist/constitution/g3Conformance.d.ts +33 -0
- package/dist/constitution/lifecycle.d.ts +25 -0
- package/dist/constitution/mutation.d.ts +15 -0
- package/dist/constitution/nodeTestEvidence.d.ts +13 -0
- package/dist/constitution/plan.d.ts +18 -0
- package/dist/constitution/policyRuntime.d.ts +5 -0
- package/dist/constitution/proof.d.ts +11 -0
- package/dist/constitution/repairPolicies.d.ts +34 -0
- package/dist/constitution/replacementFreeGit.d.ts +14 -0
- package/dist/constitution/replay.d.ts +40 -0
- package/dist/constitution/replayCache.d.ts +14 -0
- package/dist/constitution/replayWorker.d.ts +1 -0
- package/dist/constitution/repository.d.ts +129 -0
- package/dist/constitution/safeCheckout.d.ts +9 -0
- package/dist/constitution/schema.d.ts +1763 -0
- package/dist/constitution/scorecard.d.ts +219 -0
- package/dist/constitution/service.d.ts +398 -0
- package/dist/constitution/shadow.d.ts +54 -0
- package/dist/constitution/sourceMutation.d.ts +15 -0
- package/dist/constitution/staticGraphBaseline.d.ts +13 -0
- package/dist/constitution/structural.d.ts +64 -0
- package/dist/core/agenthook.d.ts +63 -0
- package/dist/core/agenthook.js +1 -0
- package/dist/core/automaticReviewMemory.d.ts +60 -0
- package/dist/core/autoreview.d.ts +66 -0
- package/dist/core/canonicalOrder.d.ts +3 -0
- package/dist/core/capturetoken.d.ts +19 -0
- package/dist/core/changeIdentity.d.ts +22 -0
- package/dist/core/changeProof.d.ts +14 -0
- package/dist/core/checkreport.d.ts +140 -0
- package/dist/core/commitrepair.d.ts +171 -0
- package/dist/core/commitrepair.js +238 -0
- package/dist/core/compare.d.ts +21 -0
- package/dist/core/config.d.ts +20 -0
- package/dist/core/conformance.d.ts +35 -0
- package/dist/core/constraintmatch.d.ts +54 -0
- package/dist/core/correction.d.ts +42 -0
- package/dist/core/correctionStage.d.ts +224 -0
- package/dist/core/declarationClusters.d.ts +149 -0
- package/dist/core/delivery.d.ts +110 -0
- package/dist/core/docanchors.d.ts +32 -0
- package/dist/core/docscan.d.ts +26 -0
- package/dist/core/drift.d.ts +13 -0
- package/dist/core/drift.js +31 -1
- package/dist/core/dupdetect.d.ts +43 -0
- package/dist/core/escalations.d.ts +129 -0
- package/dist/core/escalations.js +165 -0
- package/dist/core/events.d.ts +29 -0
- package/dist/core/evidenceMap.d.ts +164 -0
- package/dist/core/externalImports.d.ts +8 -0
- package/dist/core/format.d.ts +13 -0
- package/dist/core/glob.d.ts +9 -0
- package/dist/core/groundingLag.d.ts +84 -0
- package/dist/core/groundingLag.js +16 -2
- package/dist/core/groundingMerge.d.ts +15 -0
- package/dist/core/groundingMerge.js +95 -0
- package/dist/core/hookcache.d.ts +10 -0
- package/dist/core/hookpolicy.d.ts +47 -0
- package/dist/core/ids.d.ts +36 -0
- package/dist/core/importReview.d.ts +30 -0
- package/dist/core/io.d.ts +23 -0
- package/dist/core/jsonc.d.ts +4 -0
- package/dist/core/landscapeAdoption.d.ts +66 -0
- package/dist/core/landscapeDelivery.d.ts +76 -0
- package/dist/core/memorylog.d.ts +37 -0
- package/dist/core/migrate.d.ts +28 -0
- package/dist/core/outcomeExperience.d.ts +89 -0
- package/dist/core/overlaySafety.d.ts +24 -0
- package/dist/core/overlaySafety.js +7 -1
- package/dist/core/paths.d.ts +31 -0
- package/dist/core/pipeline.d.ts +289 -0
- package/dist/core/premises.d.ts +43 -0
- package/dist/core/provenance.d.ts +21 -0
- package/dist/core/publication.d.ts +54 -0
- package/dist/core/refrepair.d.ts +24 -0
- package/dist/core/refrepair.js +1 -1
- package/dist/core/relativeImports.d.ts +11 -0
- package/dist/core/repair.d.ts +44 -0
- package/dist/core/repairqueue.d.ts +40 -0
- package/dist/core/repairqueue.js +133 -0
- package/dist/core/reviewMemory.d.ts +40 -0
- package/dist/core/reviewqueue.d.ts +54 -0
- package/dist/core/safeRepoFile.d.ts +20 -0
- package/dist/core/served.d.ts +57 -0
- package/dist/core/served.js +14 -0
- package/dist/core/stateContract.d.ts +979 -0
- package/dist/core/stateContract.js +87 -4
- package/dist/core/stateDelivery.d.ts +78 -0
- package/dist/core/stateRecords.d.ts +383 -0
- package/dist/core/stateRecords.js +16 -1
- package/dist/core/stats.d.ts +88 -0
- package/dist/core/strictgate.d.ts +33 -0
- package/dist/core/taskReport.d.ts +226 -0
- package/dist/core/taskReport.js +514 -0
- package/dist/core/taskReportCapture.d.ts +8 -0
- package/dist/core/taskReportCapture.js +36 -0
- package/dist/core/taskReportEvidence.d.ts +26 -0
- package/dist/core/taskReportEvidence.js +268 -0
- package/dist/core/taskReportHook.d.ts +20 -0
- package/dist/core/taskReportHook.js +76 -0
- package/dist/core/taskReportPaths.d.ts +3 -0
- package/dist/core/taskReportPaths.js +23 -0
- package/dist/core/taskReportPublic.d.ts +31 -0
- package/dist/core/taskReportPublic.js +45 -0
- package/dist/core/taskReportRender.d.ts +6 -0
- package/dist/core/taskReportRender.js +97 -0
- package/dist/core/topics.d.ts +73 -0
- package/dist/core/types.d.ts +1239 -0
- package/dist/core/version.d.ts +10 -0
- package/dist/eval/guards.d.ts +45 -0
- package/dist/eval/harness.d.ts +58 -0
- package/dist/extractors/adrImport.d.ts +59 -0
- package/dist/extractors/comments.d.ts +7 -0
- package/dist/extractors/correctionSources.d.ts +11 -0
- package/dist/extractors/diff.d.ts +40 -0
- package/dist/extractors/git.d.ts +304 -0
- package/dist/extractors/git.js +208 -20
- package/dist/extractors/helm.d.ts +31 -0
- package/dist/extractors/indexer.d.ts +55 -0
- package/dist/extractors/landscapeDiscovery.d.ts +41 -0
- package/dist/extractors/languages.d.ts +77 -0
- package/dist/extractors/nativeTreeSitter.d.ts +16 -0
- package/dist/extractors/parse.d.ts +54 -0
- package/dist/extractors/php.d.ts +26 -0
- package/dist/extractors/repoSource.d.ts +53 -0
- package/dist/extractors/testreport.d.ts +36 -0
- package/dist/integrations/ciAction.d.ts +8 -0
- package/dist/integrations/claudeConfig.d.ts +44 -0
- package/dist/integrations/claudemd.d.ts +13 -0
- package/dist/integrations/claudemd.js +11 -2
- package/dist/integrations/gitignore.d.ts +16 -0
- package/dist/integrations/gitignore.js +7 -0
- package/dist/integrations/health.d.ts +74 -0
- package/dist/integrations/hooks.d.ts +41 -0
- package/dist/integrations/hooks.js +92 -66
- package/dist/integrations/madrExport.d.ts +39 -0
- package/dist/integrations/madrManifest.d.ts +69 -0
- package/dist/integrations/mergeDriver.d.ts +3 -0
- package/dist/integrations/mergeDriver.js +25 -8
- package/dist/integrations/probe.d.ts +4 -0
- package/dist/integrations/providers.d.ts +103 -0
- package/dist/integrations/scaffold.d.ts +29 -0
- package/dist/integrations/sync.d.ts +30 -0
- package/dist/integrations/sync.js +3 -2
- package/dist/integrations/team.d.ts +70 -0
- package/dist/integrations/worktree.d.ts +8 -0
- package/dist/mcp/roots.d.ts +16 -0
- package/dist/mcp/server.d.ts +36 -0
- package/dist/mcp/server.js +114 -25
- package/dist/mcp/taskReportTools.d.ts +3 -0
- package/dist/mcp/taskReportTools.js +106 -0
- package/dist/serve/app.d.ts +36 -0
- package/dist/serve/app.js +20 -0
- package/dist/serve/config.d.ts +100 -0
- package/dist/serve/writelock.d.ts +22 -0
- package/dist/store/changeLedger.d.ts +132 -0
- package/dist/store/changeLedger.js +40 -9
- package/dist/store/compact.d.ts +33 -0
- package/dist/store/db.d.ts +9 -0
- package/dist/store/embedder.d.ts +32 -0
- package/dist/store/hunchStore.d.ts +624 -0
- package/dist/store/hunchStore.js +13 -5
- package/dist/store/jsonStore.d.ts +125 -0
- package/dist/store/jsonStore.js +15 -0
- package/dist/store/merge.d.ts +39 -0
- package/dist/store/privateMigrate.d.ts +24 -0
- package/dist/store/replay.d.ts +56 -0
- package/dist/store/schema.d.ts +17 -0
- package/dist/store/stateBinding.d.ts +145 -0
- package/dist/store/stateBinding.js +110 -15
- package/dist/store/stateCapture.d.ts +15 -0
- package/dist/store/stateCapture.js +145 -0
- package/dist/synthesis/cliAdapter.d.ts +22 -0
- package/dist/synthesis/initiator.d.ts +13 -0
- package/dist/synthesis/provider.d.ts +307 -0
- package/dist/synthesis/synthesize.d.ts +117 -0
- package/dist/synthesis/tripwires.d.ts +9 -0
- package/dist/taskReports.d.ts +76 -0
- package/dist/taskReports.js +67 -0
- package/dist/wiki/adopt.d.ts +22 -0
- package/dist/wiki/graph.d.ts +87 -0
- package/dist/wiki/wiki.d.ts +242 -0
- package/package.json +6 -5
- package/server.json +2 -2
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { type DiffAnalysis } from "../extractors/diff.js";
|
|
2
|
+
import type { Decision } from "../core/types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Run a command, optionally feeding `input` to its stdin, and resolve its
|
|
5
|
+
* stdout. Uses spawn (not execFile) so we can:
|
|
6
|
+
* 1. Pass untrusted content (the prompt/diff) via STDIN, never as an argv
|
|
7
|
+
* element — so a `shell:true` resolution can't shell-interpret it.
|
|
8
|
+
* 2. Resolve Windows shims: the npm `claude` is a `.cmd`/`.ps1`, which
|
|
9
|
+
* `execFile` (CreateProcess, *.exe only) cannot launch → it threw ENOENT
|
|
10
|
+
* and made the CLI provider look unavailable on Windows. `shell:true` on
|
|
11
|
+
* win32 routes through cmd.exe so the shim resolves. Safe here because
|
|
12
|
+
* every argv we pass is a trusted, space-free flag (the prompt is stdin).
|
|
13
|
+
*/
|
|
14
|
+
export declare function pexecIn(cmd: string, args: string[], opts?: {
|
|
15
|
+
input?: string;
|
|
16
|
+
env?: NodeJS.ProcessEnv;
|
|
17
|
+
cwd?: string;
|
|
18
|
+
timeout?: number;
|
|
19
|
+
maxBuffer?: number;
|
|
20
|
+
}): Promise<{
|
|
21
|
+
stdout: string;
|
|
22
|
+
}>;
|
|
23
|
+
export interface DecisionDraft {
|
|
24
|
+
title: string;
|
|
25
|
+
context: string;
|
|
26
|
+
decision: string;
|
|
27
|
+
consequences: string[];
|
|
28
|
+
alternatives_rejected: string[];
|
|
29
|
+
confidence: number;
|
|
30
|
+
source: string;
|
|
31
|
+
samples?: number;
|
|
32
|
+
agreement?: number;
|
|
33
|
+
grounded?: number;
|
|
34
|
+
pruned?: number;
|
|
35
|
+
verifyOutcome?: "applied" | "unavailable" | "failed";
|
|
36
|
+
fellBackTo?: string;
|
|
37
|
+
fallbackReason?: string;
|
|
38
|
+
}
|
|
39
|
+
/** A skeptical audit of a DecisionDraft against the commit it was derived from.
|
|
40
|
+
* `grounded` is 0..1 (how well decision+consequences are supported by the diff);
|
|
41
|
+
* the lists name draft entries the evidence does NOT support (likely hallucinated).
|
|
42
|
+
* Used to PRUNE unsupported alternatives/consequences and to LOWER confidence —
|
|
43
|
+
* never to raise it or arm enforcement (auto stays advisory; dec_9a2f2fe72a). */
|
|
44
|
+
export interface VerifyVerdict {
|
|
45
|
+
grounded: number;
|
|
46
|
+
unsupported_alternatives: string[];
|
|
47
|
+
unsupported_claims: string[];
|
|
48
|
+
}
|
|
49
|
+
export interface BugDraft {
|
|
50
|
+
title: string;
|
|
51
|
+
symptom: string;
|
|
52
|
+
root_cause: string;
|
|
53
|
+
severity: "low" | "medium" | "high" | "critical";
|
|
54
|
+
confidence: number;
|
|
55
|
+
source: string;
|
|
56
|
+
fellBackTo?: string;
|
|
57
|
+
fallbackReason?: string;
|
|
58
|
+
}
|
|
59
|
+
/** A harness judgment of one auto-drafted decision, used by `hunch auto-review`
|
|
60
|
+
* to decide keep vs delete. `relevant` = does this record a real, reusable design
|
|
61
|
+
* choice worth keeping (not noise, not a mechanical restatement)? `duplicate_of`
|
|
62
|
+
* names an existing decision id this merely restates. The judge NEVER approves —
|
|
63
|
+
* approval stays gated on the Critic's grounding (dec_a466655539); this only adds
|
|
64
|
+
* the "is it worth keeping at all" signal the deterministic layers can't express. */
|
|
65
|
+
export interface RelevanceVerdict {
|
|
66
|
+
relevant: boolean;
|
|
67
|
+
/** 0..1 confidence in the relevance call. */
|
|
68
|
+
confidence: number;
|
|
69
|
+
/** id of an existing decision this draft duplicates, or null. */
|
|
70
|
+
duplicate_of: string | null;
|
|
71
|
+
/** one-line justification (for the review plan / audit trail). */
|
|
72
|
+
reason: string;
|
|
73
|
+
}
|
|
74
|
+
/** A decision already in the store, reduced to what the relevance judge needs. */
|
|
75
|
+
export interface ExistingDecisionRef {
|
|
76
|
+
id: string;
|
|
77
|
+
title: string;
|
|
78
|
+
decision: string;
|
|
79
|
+
}
|
|
80
|
+
export interface CommitInput {
|
|
81
|
+
subject: string;
|
|
82
|
+
body: string;
|
|
83
|
+
files: string[];
|
|
84
|
+
diff: string;
|
|
85
|
+
/** structured "what changed" — lets even the no-LLM path be informative */
|
|
86
|
+
analysis?: DiffAnalysis;
|
|
87
|
+
}
|
|
88
|
+
export interface FailureInput {
|
|
89
|
+
test: string;
|
|
90
|
+
message: string;
|
|
91
|
+
recentDiff: string;
|
|
92
|
+
suspects: string[];
|
|
93
|
+
}
|
|
94
|
+
export interface SynthProvider {
|
|
95
|
+
readonly name: string;
|
|
96
|
+
available(): Promise<boolean>;
|
|
97
|
+
draftDecision(input: CommitInput): Promise<DecisionDraft>;
|
|
98
|
+
draftBug(input: FailureInput): Promise<BugDraft>;
|
|
99
|
+
/** Optional skeptical audit of a draft against its commit (the Critic pass).
|
|
100
|
+
* Only LLM-backed providers implement it; the deterministic provider
|
|
101
|
+
* and the bare ensemble omit it, so callers must feature-detect. */
|
|
102
|
+
verifyDecision?(input: CommitInput, draft: DecisionDraft): Promise<VerifyVerdict>;
|
|
103
|
+
/** Optional free-form grounded prose (the wiki's Overview section). LLM-backed
|
|
104
|
+
* providers implement it through their normal guarded transport, so callers
|
|
105
|
+
* must feature-detect and degrade to a deterministic template when absent. */
|
|
106
|
+
draftProse?(prompt: string): Promise<string>;
|
|
107
|
+
/** Optional harness judgment of an auto-drafted decision's relevance, for
|
|
108
|
+
* `hunch auto-review`. Only LLM-backed providers implement it, so callers
|
|
109
|
+
* feature-detect. */
|
|
110
|
+
judgeDraft?(draft: Decision, existing: ExistingDecisionRef[]): Promise<RelevanceVerdict>;
|
|
111
|
+
}
|
|
112
|
+
/** Every selectable synthesis mode. `auto` is a preference value rather than a
|
|
113
|
+
* provider: it resolves the invocation's origin without choosing by availability.
|
|
114
|
+
* "openai-compat" is the opt-in local/self-hosted HTTP provider (Ollama, vLLM,
|
|
115
|
+
* LM Studio, ...) — not a subscription, but explicitly selectable like one. */
|
|
116
|
+
export declare const SYNTH_PROVIDER_NAMES: readonly ["claude-cli", "codex-cli", "cursor-agent", "kimi-cli", "openai-compat", "deterministic"];
|
|
117
|
+
export declare const SYNTH_PREFERENCES: readonly ["auto", "claude-cli", "codex-cli", "cursor-agent", "kimi-cli", "openai-compat", "deterministic"];
|
|
118
|
+
export type SynthProviderName = (typeof SYNTH_PROVIDER_NAMES)[number];
|
|
119
|
+
export type SynthPreference = (typeof SYNTH_PREFERENCES)[number];
|
|
120
|
+
export interface ProviderStatus {
|
|
121
|
+
name: SynthProviderName;
|
|
122
|
+
label: string;
|
|
123
|
+
subscription: string | null;
|
|
124
|
+
available: boolean;
|
|
125
|
+
}
|
|
126
|
+
export interface ProviderResolution {
|
|
127
|
+
provider: SynthProvider;
|
|
128
|
+
/** Why this provider was chosen. `ambiguous` is intentionally deterministic. */
|
|
129
|
+
source: "environment" | "local" | "single-available" | "ambiguous" | "none" | "unavailable-preference" | "initiator" | "unavailable-initiator" | "unknown-initiator";
|
|
130
|
+
initiator?: string | null;
|
|
131
|
+
preference: SynthPreference;
|
|
132
|
+
statuses: ProviderStatus[];
|
|
133
|
+
}
|
|
134
|
+
export interface ProviderSelectionOptions {
|
|
135
|
+
/** Repo root used for the gitignored, per-user `.hunch/local.json` preference. */
|
|
136
|
+
root?: string;
|
|
137
|
+
/** Injectable for tests; defaults to process.env. */
|
|
138
|
+
env?: NodeJS.ProcessEnv;
|
|
139
|
+
/** Injectable for tests; normal callers use the built-in CLI registry. */
|
|
140
|
+
providers?: readonly SynthProvider[];
|
|
141
|
+
/** Explicit local CLI adapter config; never loaded automatically from repository content. */
|
|
142
|
+
cliConfig?: string;
|
|
143
|
+
}
|
|
144
|
+
type PromptOutput = "json" | "text";
|
|
145
|
+
declare abstract class PromptSynthProvider implements SynthProvider {
|
|
146
|
+
abstract readonly name: string;
|
|
147
|
+
abstract available(): Promise<boolean>;
|
|
148
|
+
protected abstract run(prompt: string, output?: PromptOutput): Promise<string>;
|
|
149
|
+
/** Run a CLI with the prompt on stdin, stripping API-key env vars so the tool
|
|
150
|
+
* falls through to its SUBSCRIPTION credentials. Shared by codex/cursor. */
|
|
151
|
+
protected runCli(bin: string, args: string[], stripEnv: string[], prompt: string, timeoutMs?: number): Promise<string>;
|
|
152
|
+
draftDecision(input: CommitInput): Promise<DecisionDraft>;
|
|
153
|
+
draftBug(input: FailureInput): Promise<BugDraft>;
|
|
154
|
+
/** Grounded prose for the wiki. Uses text mode rather than the structured JSON
|
|
155
|
+
* mode required by the record mappers. Throws on empty output so the caller
|
|
156
|
+
* falls back to its deterministic template page. */
|
|
157
|
+
draftProse(prompt: string): Promise<string>;
|
|
158
|
+
/** The Critic pass: audit a draft against its commit through the provider's
|
|
159
|
+
* guarded transport.
|
|
160
|
+
* Throws on unusable output so verifyDecisionSafe degrades to the un-audited
|
|
161
|
+
* draft (a verifier failure must never lose the draft — dec_18a81c8291). */
|
|
162
|
+
verifyDecision(input: CommitInput, draft: DecisionDraft): Promise<VerifyVerdict>;
|
|
163
|
+
/** Judge whether an auto-drafted decision is worth keeping (for auto-review).
|
|
164
|
+
* Uses the provider's guarded transport. Throws on unusable
|
|
165
|
+
* output so the caller can degrade to a keep-for-human verdict. */
|
|
166
|
+
judgeDraft(draft: Decision, existing: ExistingDecisionRef[]): Promise<RelevanceVerdict>;
|
|
167
|
+
}
|
|
168
|
+
export declare function safeModel(v: string | undefined, fallback: string): string;
|
|
169
|
+
export declare function safeModel(v: string | undefined, fallback: undefined): string | undefined;
|
|
170
|
+
/** Build the non-interactive Codex invocation used from Hunch's neutral temp
|
|
171
|
+
* directory. Codex normally refuses to start outside a trusted Git repository;
|
|
172
|
+
* the explicit skip flag preserves that neutral-cwd isolation without loading a
|
|
173
|
+
* target repo's agent rules or MCP configuration. */
|
|
174
|
+
export declare function codexExecArgs(model?: string): string[];
|
|
175
|
+
export declare function safeTimeout(v: string | undefined, fallback: number): number;
|
|
176
|
+
export declare function safeMaxTokens(v: string | undefined, fallback: number): number;
|
|
177
|
+
export declare function meteredHostsAllowed(env?: NodeJS.ProcessEnv): boolean;
|
|
178
|
+
export declare class OpenAICompatProvider extends PromptSynthProvider {
|
|
179
|
+
readonly name = "openai-compat";
|
|
180
|
+
available(): Promise<boolean>;
|
|
181
|
+
protected run(prompt: string, output?: PromptOutput): Promise<string>;
|
|
182
|
+
}
|
|
183
|
+
/** Best-effort: does the configured openai-compat endpoint look like Ollama with
|
|
184
|
+
* an UNSET num_ctx? Returns
|
|
185
|
+
* an advisory warning string when so, or null when the endpoint isn't reachable,
|
|
186
|
+
* doesn't look like Ollama's /api/show shape, or already has num_ctx set — this
|
|
187
|
+
* is diagnostics only, never thrown, never blocking. Deliberately does NOT try to
|
|
188
|
+
* report the model's effective context length: modern Ollama defaults may come
|
|
189
|
+
* from server configuration or VRAM tiers, and model_info keys are not a stable
|
|
190
|
+
* parse target. We therefore report only the observed fact — whether num_ctx is
|
|
191
|
+
* pinned in the model — without guessing an effective token count. */
|
|
192
|
+
export declare function probeOllamaNumCtx(baseUrl: string, model: string): Promise<string | null>;
|
|
193
|
+
export declare class DeterministicProvider implements SynthProvider {
|
|
194
|
+
readonly name = "deterministic";
|
|
195
|
+
available(): Promise<boolean>;
|
|
196
|
+
draftDecision(input: CommitInput): Promise<DecisionDraft>;
|
|
197
|
+
draftBug(input: FailureInput): Promise<BugDraft>;
|
|
198
|
+
}
|
|
199
|
+
/** Extract the final assistant message from `codex exec --json` output (newline-
|
|
200
|
+
* delimited JSON events). Codex tags assistant turns as `item.type ==="agent_message"`,
|
|
201
|
+
* but it ALSO emits `item.text` for reasoning and may append trailing events — so we
|
|
202
|
+
* prefer the last AGENT message and only fall back to the last any-text when none is
|
|
203
|
+
* tagged. If nothing parses, hand the raw output to the mapper (→ it finds the JSON
|
|
204
|
+
* draft or throws → deterministic fallback). Tolerant by design: drift degrades, never crashes. */
|
|
205
|
+
export declare function extractCodexText(out: string): string;
|
|
206
|
+
/** Test-only: clears the availability memoization cache so a test that toggles
|
|
207
|
+
* env vars mid-process (e.g. HUNCH_SYNTH_BASE_URL) isn't served a stale result
|
|
208
|
+
* cached by an earlier call in the same process. Never call from production code. */
|
|
209
|
+
export declare function __resetAvailabilityCacheForTests(): void;
|
|
210
|
+
/** "ollama" is accepted as an alias for "openai-compat" — the provider is not
|
|
211
|
+
* Ollama-specific (it speaks the OpenAI chat-completions format any self-hosted
|
|
212
|
+
* server can implement), but Ollama is the most common self-hosted target and
|
|
213
|
+
* users reach for that name first. Applied to the HUNCH_SYNTH_PROVIDER env var in
|
|
214
|
+
* resolveSynthesisProvider below, and exported so the `hunch provider <name>` CLI
|
|
215
|
+
* command (index.ts) normalizes it the same way before validating/persisting a
|
|
216
|
+
* local preference — the two paths must agree, or a user who sets one and reads
|
|
217
|
+
* the other back gets a confusing "unknown provider" message for a name that
|
|
218
|
+
* actually works. */
|
|
219
|
+
export declare function normalizeProviderName(v: string | undefined): string | undefined;
|
|
220
|
+
/** Read a per-user, gitignored choice. Invalid/missing local state is treated as auto;
|
|
221
|
+
* `writeSynthesisPreference` refuses to overwrite malformed data so this forgiveness
|
|
222
|
+
* never destroys someone else's local settings. */
|
|
223
|
+
export declare function readSynthesisPreference(root: string): SynthPreference;
|
|
224
|
+
/** Persist the user's provider choice only in `.hunch/local.json`, which is never a
|
|
225
|
+
* repository policy. That means each developer controls their own subscription spend. */
|
|
226
|
+
export declare function writeSynthesisPreference(root: string, preference: SynthPreference): void;
|
|
227
|
+
/** Respect offline mode, then bind to invocation origin. Explicit terminal preferences
|
|
228
|
+
* apply only without an agent origin; installed executables never select an account. */
|
|
229
|
+
export declare function resolveSynthesisProvider(opts?: ProviderSelectionOptions): Promise<ProviderResolution>;
|
|
230
|
+
/** The provider used by normal synthesis. See `resolveSynthesisProvider` for a
|
|
231
|
+
* diagnosable result with the selection source and every candidate's availability. */
|
|
232
|
+
export declare function selectProvider(opts?: ProviderSelectionOptions): Promise<SynthProvider>;
|
|
233
|
+
/** Only the resolved origin-bound worker. No cross-account fan-out. */
|
|
234
|
+
export declare function selectWorkers(opts?: ProviderSelectionOptions): Promise<SynthProvider[]>;
|
|
235
|
+
/** Reconcile N worker drafts into one. DETERMINISTIC (no second LLM call): the richest
|
|
236
|
+
* draft is the spine; alternatives/consequences are unioned; confidence is AGREEMENT-
|
|
237
|
+
* WEIGHTED and CAPPED at 0.78 — below STRICT_MIN_CONFIDENCE (0.8) — so an ensemble
|
|
238
|
+
* auto-draft can never arm enforcement. `samples`/`agreement` ride along as advisory
|
|
239
|
+
* telemetry for `hunch review` (not schema-bound). */
|
|
240
|
+
export declare function mergeDecisionDrafts(drafts: DecisionDraft[]): DecisionDraft;
|
|
241
|
+
export declare class EnsembleProvider implements SynthProvider {
|
|
242
|
+
private readonly workers;
|
|
243
|
+
readonly name = "ensemble";
|
|
244
|
+
private readonly samples;
|
|
245
|
+
constructor(workers: SynthProvider[], opts?: {
|
|
246
|
+
samples?: number;
|
|
247
|
+
});
|
|
248
|
+
available(): Promise<boolean>;
|
|
249
|
+
/** Production selection supplies one origin-bound worker with N samples.
|
|
250
|
+
* Direct callers can also supply multiple workers subject to origin checks. */
|
|
251
|
+
private decisionTasks;
|
|
252
|
+
draftDecision(input: CommitInput): Promise<DecisionDraft>;
|
|
253
|
+
draftBug(input: FailureInput): Promise<BugDraft>;
|
|
254
|
+
}
|
|
255
|
+
/** Build the Deep-Synthesis provider, or null if no LLM provider is available
|
|
256
|
+
* (the caller then falls back to the normal single-provider path). `samples` sets
|
|
257
|
+
* the self-consistency depth for the single-provider case. */
|
|
258
|
+
export declare function selectEnsemble(opts?: ProviderSelectionOptions & {
|
|
259
|
+
samples?: number;
|
|
260
|
+
}): Promise<EnsembleProvider | null>;
|
|
261
|
+
/** Pick a provider to run the Critic pass — the same resolved provider normal
|
|
262
|
+
* synthesis would use (subscription CLI or the opt-in openai-compat endpoint),
|
|
263
|
+
* honoring the same env/local-preference/auto policy. Returns null when that
|
|
264
|
+
* resolves to the deterministic fallback — verification then no-ops and the
|
|
265
|
+
* un-audited draft stands (graceful degradation; dec_18a81c8291). */
|
|
266
|
+
export declare function selectVerifier(opts?: ProviderSelectionOptions): Promise<SynthProvider | null>;
|
|
267
|
+
/** Map model text → RelevanceVerdict, or null when nothing usable parses (→ the
|
|
268
|
+
* caller keeps the draft for a human). Tolerant of missing/loose fields. */
|
|
269
|
+
export declare function relevanceFromText(text: string): RelevanceVerdict | null;
|
|
270
|
+
/** Map model text → VerifyVerdict, or null when nothing usable parses (→ the caller
|
|
271
|
+
* keeps the un-audited draft). Tolerant of arrays-as-strings and missing fields. */
|
|
272
|
+
export declare function verdictFromText(text: string): VerifyVerdict | null;
|
|
273
|
+
/** Apply a verdict to a draft: drop unsupported alternatives (so they never scaffold
|
|
274
|
+
* tripwires) and consequences, and scale confidence DOWN by grounding. Confidence is
|
|
275
|
+
* clamped so it can only fall — verification never arms a stronger claim than the
|
|
276
|
+
* draft already made (R2). Records `grounded` as advisory telemetry. */
|
|
277
|
+
export declare function applyVerdict(draft: DecisionDraft, v: VerifyVerdict): DecisionDraft;
|
|
278
|
+
/** Run the Critic pass and apply it, degrading to the un-audited draft when the
|
|
279
|
+
* provider can't verify (deterministic / no CLI) or the call keeps failing. Never
|
|
280
|
+
* throws. Marks the OUTCOME on the draft (applied / unavailable / failed) so the
|
|
281
|
+
* degradation is visible in telemetry instead of silent. Retries once: under --deep
|
|
282
|
+
* the Critic call stacks after sampling, and a single transient failure on that
|
|
283
|
+
* extra call shouldn't drop the audit. */
|
|
284
|
+
export declare function verifyDecisionSafe(verifier: SynthProvider | null, input: CommitInput, draft: DecisionDraft): Promise<DecisionDraft>;
|
|
285
|
+
/** Every parseable top-level JSON object in arbitrary model text, in order. */
|
|
286
|
+
export declare function extractJsonObjects(text: string): Record<string, unknown>[];
|
|
287
|
+
/** Convenience: the FIRST parseable top-level object, or null. (The mappers below
|
|
288
|
+
* do their own content-based selection; this is just a generic accessor.) */
|
|
289
|
+
export declare function extractJson(text: string): Record<string, unknown> | null;
|
|
290
|
+
/** Map model text → DecisionDraft, or null when there's nothing usable to keep.
|
|
291
|
+
* Null (not a hollow draft) is the signal for the caller to fall back to the
|
|
292
|
+
* deterministic provider — we only claim "llm_draft" when the LLM actually
|
|
293
|
+
* produced substance. The model is asked for ONE object; if it emits several (a
|
|
294
|
+
* template/example plus the answer), take the FIRST with real (non-placeholder)
|
|
295
|
+
* substance — robust whether the junk leads or trails the answer. */
|
|
296
|
+
export declare function decisionDraftFromText(text: string, fallbackTitle: string): DecisionDraft | null;
|
|
297
|
+
/** Map model text → BugDraft, or null. A root_cause is the LLM's full value-add;
|
|
298
|
+
* a deliberate non-"medium" severity is worth keeping on its own (it carries the
|
|
299
|
+
* LLM's classification into the bug record rather than the deterministic "medium").
|
|
300
|
+
* Pick the BEST candidate object — most substantiated (root-caused) then most
|
|
301
|
+
* severe — NOT the positionally first/last, so a trailing low-severity recap can't
|
|
302
|
+
* downgrade a real critical finding. Without a root_cause the draft is labeled
|
|
303
|
+
* honestly as partial at lower confidence; constraint promotion is gated on a real
|
|
304
|
+
* root_cause downstream (see shouldPromoteConstraint), so a bare severity label
|
|
305
|
+
* preserves its signal in the record without auto-minting an invariant. */
|
|
306
|
+
export declare function bugDraftFromText(text: string, fallbackTitle: string, fallbackSymptom: string): BugDraft | null;
|
|
307
|
+
export {};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The learning loop's write step (DESIGN.md §4, §5 "write_back").
|
|
3
|
+
* - syncCommit: commit diff -> Claude/heuristic -> Decision draft -> Hunch
|
|
4
|
+
* - recordFailure: failing test -> suspect ranking -> Bug draft -> Hunch
|
|
5
|
+
*
|
|
6
|
+
* Idempotent: a Decision id is derived from (commit, title) so re-running sync on
|
|
7
|
+
* the same commit updates rather than duplicates. New records are LOW-confidence
|
|
8
|
+
* and `proposed` until confirmed — advisory and cheap to discard.
|
|
9
|
+
*/
|
|
10
|
+
import type { HunchStore } from "../store/hunchStore.js";
|
|
11
|
+
import { type DiffAnalysis } from "../extractors/diff.js";
|
|
12
|
+
import { type SynthProvider, type DecisionDraft, type BugDraft, type CommitInput, type FailureInput } from "./provider.js";
|
|
13
|
+
import type { Decision, Bug, Constraint } from "../core/types.js";
|
|
14
|
+
import type { TestReport } from "../extractors/testreport.js";
|
|
15
|
+
export interface SyncResult {
|
|
16
|
+
status: "written" | "skipped";
|
|
17
|
+
reason?: string;
|
|
18
|
+
decision?: Decision;
|
|
19
|
+
provider?: string;
|
|
20
|
+
}
|
|
21
|
+
/** Trivial-subject commits (merge/revert/bump/format/...) are noise UNLESS the body
|
|
22
|
+
* carries real content — a squash/PR description often lands there, not on the
|
|
23
|
+
* subject. Gated on body length ALONE (not the full isSignificant() heuristic): a
|
|
24
|
+
* large auto-generated reformat or dependency-bump diff with no narrative must stay
|
|
25
|
+
* skipped even though it would trip isSignificant()'s line/file/structural checks. */
|
|
26
|
+
export declare function isTrivialSubject(meta: {
|
|
27
|
+
subject: string;
|
|
28
|
+
body: string;
|
|
29
|
+
}): boolean;
|
|
30
|
+
/** Is a commit substantive enough to spend a paid LLM synthesis call on? Pure and
|
|
31
|
+
* deterministic. Any structural change (symbol/dependency delta), non-trivial
|
|
32
|
+
* churn, several files, OR an explanatory commit body signals a real decision
|
|
33
|
+
* worth the model. Everything below (typo/tweak/one-liner with no message) falls
|
|
34
|
+
* to the free deterministic draft — shallower but honestly low-confidence.
|
|
35
|
+
* `files` is whatever the caller is synthesizing from (code and/or markdown, per
|
|
36
|
+
* isSubstantive) — the line/file-count checks are format-agnostic. */
|
|
37
|
+
export declare function isSignificant(meta: {
|
|
38
|
+
body: string;
|
|
39
|
+
}, a: DiffAnalysis, files: string[]): boolean;
|
|
40
|
+
/** Capture a Decision from a commit. Defaults to HEAD. Pass `{ force: true }` to
|
|
41
|
+
* re-synthesize a commit that already has an (auto-drafted) decision. */
|
|
42
|
+
export declare function syncCommit(store: HunchStore, root: string, sha?: string, opts?: {
|
|
43
|
+
force?: boolean;
|
|
44
|
+
private?: boolean;
|
|
45
|
+
home?: "public" | "private";
|
|
46
|
+
localOnly?: boolean;
|
|
47
|
+
deep?: boolean;
|
|
48
|
+
verify?: boolean;
|
|
49
|
+
samples?: number;
|
|
50
|
+
}): Promise<SyncResult>;
|
|
51
|
+
export interface FailureResult {
|
|
52
|
+
status: "written";
|
|
53
|
+
bug: Bug;
|
|
54
|
+
constraint?: Constraint;
|
|
55
|
+
provider: string;
|
|
56
|
+
touchedHomes: Array<"public" | "private">;
|
|
57
|
+
}
|
|
58
|
+
/** Capture a Bug from a test failure. Suspects are ranked churn×recency×fan-in. */
|
|
59
|
+
export declare function recordFailure(store: HunchStore, root: string, failure: {
|
|
60
|
+
test: string;
|
|
61
|
+
message: string;
|
|
62
|
+
recentDiff?: string;
|
|
63
|
+
}, opts?: {
|
|
64
|
+
private?: boolean;
|
|
65
|
+
}): Promise<FailureResult>;
|
|
66
|
+
export interface CapturedFailure {
|
|
67
|
+
bug: Bug;
|
|
68
|
+
constraint?: Constraint;
|
|
69
|
+
}
|
|
70
|
+
export interface TestRunCapture {
|
|
71
|
+
results: CapturedFailure[];
|
|
72
|
+
/** Bugs resolved because their test now passes. */
|
|
73
|
+
fixed: Bug[];
|
|
74
|
+
/** True when the output wasn't recognized TAP/spec and we captured one coarse bug. */
|
|
75
|
+
fallback: boolean;
|
|
76
|
+
touchedHomes: Array<"public" | "private">;
|
|
77
|
+
}
|
|
78
|
+
/** Orchestrate one `hunch test` run into graph writes: capture each failing test
|
|
79
|
+
* as a Bug (recordFailure → suspects / recurrence / Constraint promotion), and
|
|
80
|
+
* resolve any open Bug whose test now passes. Kept free of console I/O so the
|
|
81
|
+
* whole capture→resolve→promote loop is unit-testable. Does NOT reindex — the
|
|
82
|
+
* caller does, once. `status` is the runner's exit code (null if unknown). */
|
|
83
|
+
export declare function captureTestRun(store: HunchStore, root: string, input: {
|
|
84
|
+
report: TestReport;
|
|
85
|
+
status: number | null;
|
|
86
|
+
cmd: string;
|
|
87
|
+
output: string;
|
|
88
|
+
private?: boolean;
|
|
89
|
+
}): Promise<TestRunCapture>;
|
|
90
|
+
/** Whether a bug should auto-promote a regression Constraint (a do-not-break
|
|
91
|
+
* invariant). A recurrence always does. Otherwise it must be high/critical AND
|
|
92
|
+
* substantiated by a real root cause — a bare severity label with no analysis
|
|
93
|
+
* (e.g. an LLM "test_failure+llm_partial" draft) keeps its severity on the bug
|
|
94
|
+
* record for human review but must not silently mint an invariant from thin air. */
|
|
95
|
+
export declare function shouldPromoteConstraint(severity: Bug["severity"], rootCause: string, isRecurrence: boolean): boolean;
|
|
96
|
+
/** Turn a bug into a regression constraint scoped to its files.
|
|
97
|
+
*
|
|
98
|
+
* Severity policy mirrors buildCorrectionConstraint's scope-footgun guard, for the
|
|
99
|
+
* same reason: a BLOCKING constraint DENIES edits under strict firmness, and neither
|
|
100
|
+
* input here is human-confirmed. `bug.severity` is whatever the synthesis provider's
|
|
101
|
+
* enum emitted (provenance "derived", an LLM label no human saw), and `affected_files`
|
|
102
|
+
* is EMPTY whenever suspect ranking resolves nothing — the common case for a failure
|
|
103
|
+
* that names no known symbol. Left unguarded those compose into the worst outcome:
|
|
104
|
+
* one model-labeled "critical" failure with no resolvable suspects mints
|
|
105
|
+
* scope ["**"] + severity "blocking", a repo-wide deny on every subsequent edit.
|
|
106
|
+
*
|
|
107
|
+
* So blocking requires BOTH a real file scope AND deterministic corroboration — a
|
|
108
|
+
* recurrence, meaning the graph itself already saw and closed this symptom. Everything
|
|
109
|
+
* else lands as a warning: still surfaced at edit time and in CI, and still promotable
|
|
110
|
+
* by a human via `hunch record-constraint`, but never an automatic deny.
|
|
111
|
+
*
|
|
112
|
+
* Exported for the same reason as shouldPromoteConstraint — it is policy, and policy
|
|
113
|
+
* should be provable without spinning up a provider. */
|
|
114
|
+
export declare function shouldBlockOnPromotion(severity: Bug["severity"], affectedFiles: string[], isRecurrence: boolean): boolean;
|
|
115
|
+
export declare function salientTerms(text: string): Set<string>;
|
|
116
|
+
export declare function draftDecisionSafe(provider: SynthProvider, input: CommitInput): Promise<DecisionDraft>;
|
|
117
|
+
export declare function draftBugSafe(provider: SynthProvider, input: FailureInput): Promise<BugDraft>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RejectedTripwire } from "../core/types.js";
|
|
2
|
+
/** Scaffold one draft tripwire per rejected alternative. Scope = directory globs of
|
|
3
|
+
* the decision's related files. `forbids` is best-effort from the prose: known repo
|
|
4
|
+
* dependencies named in the text, plus backticked identifiers as candidate symbols.
|
|
5
|
+
* Empty `forbids` is fine — the tripwire is then inert until a human fills it in. */
|
|
6
|
+
export declare function draftTripwires(alternatives: string[], relatedFiles: string[], knownDeps: string[]): RejectedTripwire[];
|
|
7
|
+
/** External dependency names declared in the repo's package.json (every section),
|
|
8
|
+
* used to recognise a dep named in a rejected-alternative sentence. */
|
|
9
|
+
export declare function knownRepoDeps(root: string): string[];
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { type ReportClaim, type ReportRecord, type LessonReference } from "./core/taskReport.js";
|
|
2
|
+
import { runReportCheck } from "./core/taskReportEvidence.js";
|
|
3
|
+
import type { DeliveryEnvelope } from "./core/delivery.js";
|
|
4
|
+
export type { TaskReport, ReportTask, ReportClaim, ReportCheck, ReportConformance, ReportSave, ReportDurability, ReportRefusal, ReportRecord, TaskDelivery, LessonReference, LessonHistory } from "./core/taskReport.js";
|
|
5
|
+
export { TASK_REPORT_SCHEMA, TaskIdSchema } from "./core/taskReport.js";
|
|
6
|
+
export { renderTaskReport, renderTaskReportHtml } from "./core/taskReportRender.js";
|
|
7
|
+
/** Create this only after authorizing a local repository/worktree. A task ID is
|
|
8
|
+
* a correlation reference, never an access token. Remote adapters must enforce
|
|
9
|
+
* their own principal/partition boundary before reaching this local API. */
|
|
10
|
+
export declare function createTaskReporter(root: string): {
|
|
11
|
+
/** Omit identity for a fresh task, or supply the harness's stable task AND
|
|
12
|
+
* execution attempt for idempotent retries. Raw identities are not retained. */
|
|
13
|
+
start(title: string, identity?: {
|
|
14
|
+
task: string;
|
|
15
|
+
attempt: string;
|
|
16
|
+
}): {
|
|
17
|
+
task_id: string;
|
|
18
|
+
scope: string;
|
|
19
|
+
title: string;
|
|
20
|
+
started_at: string;
|
|
21
|
+
finished_at: string | null;
|
|
22
|
+
state: "open" | "completed" | "interrupted";
|
|
23
|
+
};
|
|
24
|
+
/** The caller supplies the exact envelope it issued plus snapshots of the
|
|
25
|
+
* included revisions. Return the occurrence with the context to the agent.
|
|
26
|
+
* Issuance alone does not prove the model applied or even attended to it. */
|
|
27
|
+
delivered(taskId: string, envelope: DeliveryEnvelope, records: ReportRecord[], occurrenceId?: string): string;
|
|
28
|
+
applied(taskId: string, claim: ReportClaim): string;
|
|
29
|
+
/** Runs locally as argv, without a shell. Only use commands authorized by
|
|
30
|
+
* the task owner. This API does not accept remote claimed-success receipts. */
|
|
31
|
+
verify(taskId: string, command: string[], label: string, options?: Parameters<typeof runReportCheck>[5]): Promise<{
|
|
32
|
+
label: string;
|
|
33
|
+
command: string[];
|
|
34
|
+
exit_code: number | null;
|
|
35
|
+
output_hash: string;
|
|
36
|
+
before_snapshot: string | null;
|
|
37
|
+
after_snapshot: string | null;
|
|
38
|
+
snapshot_limitations: string[];
|
|
39
|
+
timed_out: boolean;
|
|
40
|
+
source: "local-command-runner";
|
|
41
|
+
check_id?: string | undefined;
|
|
42
|
+
cancelled?: boolean | undefined;
|
|
43
|
+
}>;
|
|
44
|
+
/** Hunch evaluates each delivered lesson's declared rule against the changed
|
|
45
|
+
* files. Deterministic and local; the harness supplies no verdict. */
|
|
46
|
+
conform(taskId: string): {
|
|
47
|
+
source: "local-rule-check";
|
|
48
|
+
record_id: string;
|
|
49
|
+
kind: "decisions" | "constraints";
|
|
50
|
+
content_hash: string;
|
|
51
|
+
rule: "constraint-forbids" | "decision-conformance";
|
|
52
|
+
outcome: "satisfied" | "violated" | "not-exercised" | "unavailable";
|
|
53
|
+
files: string[];
|
|
54
|
+
snapshot: string | null;
|
|
55
|
+
detail: string;
|
|
56
|
+
}[];
|
|
57
|
+
finish(taskId: string, outcome?: "completed" | "interrupted"): {
|
|
58
|
+
report: import("./core/taskReport.js").TaskReport;
|
|
59
|
+
contribution_card: string | null;
|
|
60
|
+
};
|
|
61
|
+
report: (taskId: string) => import("./core/taskReport.js").TaskReport;
|
|
62
|
+
history: () => {
|
|
63
|
+
task_id: string;
|
|
64
|
+
scope: string;
|
|
65
|
+
title: string;
|
|
66
|
+
started_at: string;
|
|
67
|
+
finished_at: string | null;
|
|
68
|
+
state: "open" | "completed" | "interrupted";
|
|
69
|
+
}[];
|
|
70
|
+
lesson: (reference: LessonReference, options?: {
|
|
71
|
+
limit?: number;
|
|
72
|
+
before?: number;
|
|
73
|
+
}) => import("./core/taskReport.js").LessonHistory;
|
|
74
|
+
html: (taskId: string, publicOnly?: boolean) => string;
|
|
75
|
+
};
|
|
76
|
+
export type TaskReporter = ReturnType<typeof createTaskReporter>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/** Provider-neutral, local engine integration. The authorized harness owns
|
|
2
|
+
* lifecycle and display; neither operation depends on model compliance. */
|
|
3
|
+
import { realpathSync } from "node:fs";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { finishReportTask, listReportTasks, readTaskReport, readLessonHistory, recordReportClaim, recordTaskDelivery, reportHash, reportPresentationEnabled, startReportTask } from "./core/taskReport.js";
|
|
6
|
+
import { reportSourceSnapshot, runReportCheck, runReportConformance } from "./core/taskReportEvidence.js";
|
|
7
|
+
import { renderTaskReport, writeTaskReportHtml } from "./core/taskReportRender.js";
|
|
8
|
+
import { hunchPaths } from "./core/paths.js";
|
|
9
|
+
import { HunchStore } from "./store/hunchStore.js";
|
|
10
|
+
export { TASK_REPORT_SCHEMA, TaskIdSchema } from "./core/taskReport.js";
|
|
11
|
+
export { renderTaskReport, renderTaskReportHtml } from "./core/taskReportRender.js";
|
|
12
|
+
const IdentitySchema = z.object({ task: z.string().min(1).max(1024), attempt: z.string().min(1).max(1024) }).strict();
|
|
13
|
+
/** Create this only after authorizing a local repository/worktree. A task ID is
|
|
14
|
+
* a correlation reference, never an access token. Remote adapters must enforce
|
|
15
|
+
* their own principal/partition boundary before reaching this local API. */
|
|
16
|
+
export function createTaskReporter(root) {
|
|
17
|
+
const scope = realpathSync(root);
|
|
18
|
+
const report = (taskId) => readTaskReport(scope, taskId, reportSourceSnapshot(scope).hash);
|
|
19
|
+
return {
|
|
20
|
+
/** Omit identity for a fresh task, or supply the harness's stable task AND
|
|
21
|
+
* execution attempt for idempotent retries. Raw identities are not retained. */
|
|
22
|
+
start(title, identity) {
|
|
23
|
+
const key = identity === undefined ? undefined : IdentitySchema.parse(identity);
|
|
24
|
+
const id = key ? `htask_${reportHash([scope, "hunch.harness-task/1", key]).slice(7, 31)}` : undefined;
|
|
25
|
+
return startReportTask(scope, title, id);
|
|
26
|
+
},
|
|
27
|
+
/** The caller supplies the exact envelope it issued plus snapshots of the
|
|
28
|
+
* included revisions. Return the occurrence with the context to the agent.
|
|
29
|
+
* Issuance alone does not prove the model applied or even attended to it. */
|
|
30
|
+
delivered(taskId, envelope, records, occurrenceId) {
|
|
31
|
+
return recordTaskDelivery(scope, taskId, envelope, records, occurrenceId);
|
|
32
|
+
},
|
|
33
|
+
applied(taskId, claim) { return recordReportClaim(scope, taskId, claim); },
|
|
34
|
+
/** Runs locally as argv, without a shell. Only use commands authorized by
|
|
35
|
+
* the task owner. This API does not accept remote claimed-success receipts. */
|
|
36
|
+
verify(taskId, command, label, options) {
|
|
37
|
+
return runReportCheck(scope, taskId, command, label, 120_000, options);
|
|
38
|
+
},
|
|
39
|
+
/** Hunch evaluates each delivered lesson's declared rule against the changed
|
|
40
|
+
* files. Deterministic and local; the harness supplies no verdict. */
|
|
41
|
+
conform(taskId) {
|
|
42
|
+
const store = new HunchStore(hunchPaths(scope));
|
|
43
|
+
try {
|
|
44
|
+
return runReportConformance(scope, store, taskId);
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
store.close();
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
finish(taskId, outcome = "completed") {
|
|
51
|
+
if (outcome === "completed") {
|
|
52
|
+
try {
|
|
53
|
+
this.conform(taskId);
|
|
54
|
+
}
|
|
55
|
+
catch { /* the report's unknowns disclose it */ }
|
|
56
|
+
}
|
|
57
|
+
finishReportTask(scope, taskId, outcome);
|
|
58
|
+
const result = report(taskId);
|
|
59
|
+
return { report: result, contribution_card: reportPresentationEnabled(scope) ? renderTaskReport(result) : null };
|
|
60
|
+
},
|
|
61
|
+
report,
|
|
62
|
+
history: () => listReportTasks(scope),
|
|
63
|
+
lesson: (reference, options) => readLessonHistory(scope, reference, options),
|
|
64
|
+
html: (taskId, publicOnly = false) => writeTaskReportHtml(scope, taskId, publicOnly),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=taskReports.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Decision } from "../core/types.js";
|
|
2
|
+
import type { RepoDoc } from "../core/docscan.js";
|
|
3
|
+
/** Page slug for an adopted doc: full rel path, kebab-cased ("docs/api-v2.md" →
|
|
4
|
+
* "docs-api-v2"). Kebab-casing can collide across DIFFERENT rels ("docs/api-v2.md"
|
|
5
|
+
* vs "docs-api/v2.md"), so a `taken` set disambiguates with a short content hash
|
|
6
|
+
* of the rel — deterministic, and wikiStatus is the single caller that assigns
|
|
7
|
+
* slugs (renderers receive the resulting paths; they never re-derive). */
|
|
8
|
+
export declare function adoptedSlug(rel: string, taken: Set<string>): string;
|
|
9
|
+
/** The freshness hash of an adopted copy: source content + what the graph
|
|
10
|
+
* currently says for each pinned topic (everything the renderer quotes,
|
|
11
|
+
* including rejected alternatives) + the grade's issues. Any of these moving
|
|
12
|
+
* re-heals the copy; the copy's own bytes are never an input. */
|
|
13
|
+
export declare function adoptionHash(content: string, decisions: readonly Decision[], doc: RepoDoc): string;
|
|
14
|
+
/** Prompt for the optional PROSE-HEAL tier (dec roadmap.adoption-prose-heal):
|
|
15
|
+
* a subscription-CLI rewrite of what the stale doc SHOULD say now. The output
|
|
16
|
+
* is garnish on the deterministic skeleton — never hashed, never a substitute
|
|
17
|
+
* for the graph corrections rendered below it. */
|
|
18
|
+
export declare function adoptProsePrompt(doc: RepoDoc, content: string, decisions: readonly Decision[]): string;
|
|
19
|
+
/** Render the wiki-managed copy of a stale doc, healed against the graph.
|
|
20
|
+
* `reconciled` (optional, LLM prose-heal) slots in under the banner; the
|
|
21
|
+
* deterministic corrections below remain the authoritative layer. */
|
|
22
|
+
export declare function renderAdoptedDoc(doc: RepoDoc, content: string, decisions: readonly Decision[], reconciled?: string | null): string;
|