ai-foreman 1.7.29 → 2.0.1

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 (156) hide show
  1. package/README.md +1 -1
  2. package/dist/adapters/claude.d.ts +23 -12
  3. package/dist/adapters/claude.js +231 -26
  4. package/dist/adapters/codex.d.ts +19 -7
  5. package/dist/adapters/codex.js +202 -19
  6. package/dist/adapters/recovering.d.ts +2 -0
  7. package/dist/adapters/recovering.js +5 -1
  8. package/dist/adapters/structured.d.ts +71 -0
  9. package/dist/adapters/structured.js +213 -0
  10. package/dist/adapters/types.d.ts +85 -9
  11. package/dist/agentRun.d.ts +2 -0
  12. package/dist/agentRun.js +2 -1
  13. package/dist/boundedTurnEvidence.d.ts +18 -0
  14. package/dist/boundedTurnEvidence.js +76 -0
  15. package/dist/branch/currentGuard.d.ts +2 -0
  16. package/dist/branch/currentGuard.js +2 -0
  17. package/dist/branch/git.js +3 -8
  18. package/dist/branch/planner.js +1 -4
  19. package/dist/branch/runner.d.ts +2 -1
  20. package/dist/branch/runner.js +10 -7
  21. package/dist/buildAdmission.js +4 -0
  22. package/dist/buildApproval.d.ts +15 -0
  23. package/dist/buildApproval.js +28 -9
  24. package/dist/buildAssignment.d.ts +1 -0
  25. package/dist/buildAssignment.js +82 -23
  26. package/dist/buildInterventions.d.ts +2 -2
  27. package/dist/buildInterventions.js +11 -4
  28. package/dist/buildLaunchGate.js +3 -2
  29. package/dist/buildRuns.d.ts +1 -0
  30. package/dist/buildRuns.js +6 -1
  31. package/dist/builderGuidanceFollowup.js +17 -6
  32. package/dist/cli/doctor.js +3 -1
  33. package/dist/cli/manager.js +22 -20
  34. package/dist/cli/start.js +85 -39
  35. package/dist/cli/tickets.d.ts +0 -8
  36. package/dist/cli/tickets.js +50 -60
  37. package/dist/continuity.d.ts +10 -16
  38. package/dist/continuity.js +223 -208
  39. package/dist/continuityOrigin.d.ts +13 -0
  40. package/dist/continuityOrigin.js +91 -0
  41. package/dist/dispatchNotSent.d.ts +4 -0
  42. package/dist/dispatchNotSent.js +10 -0
  43. package/dist/foreman.d.ts +15 -16
  44. package/dist/foreman.js +70 -278
  45. package/dist/gitExclude.d.ts +16 -0
  46. package/dist/gitExclude.js +46 -0
  47. package/dist/graph/boundary.d.ts +1 -1
  48. package/dist/graph/boundary.js +1 -1
  49. package/dist/graph/host.js +8 -19
  50. package/dist/graph/manager.d.ts +1 -1
  51. package/dist/graph/manager.js +1 -1
  52. package/dist/graph/preparation.d.ts +38 -0
  53. package/dist/graph/preparation.js +7 -6
  54. package/dist/graph/semantic.d.ts +1 -0
  55. package/dist/graph/semantic.js +4 -1
  56. package/dist/graph/session.js +7 -2
  57. package/dist/graph/turn.d.ts +3 -4
  58. package/dist/graph/turn.js +29 -41
  59. package/dist/handoffProtocol.js +2 -2
  60. package/dist/handoffs.d.ts +5 -3
  61. package/dist/handoffs.js +92 -88
  62. package/dist/hostGraphApplicability.d.ts +2 -1
  63. package/dist/hostGraphApplicability.js +17 -19
  64. package/dist/hostQaAugmentation.js +21 -4
  65. package/dist/hostQaComparison.d.ts +21 -2
  66. package/dist/hostQaComparison.js +43 -29
  67. package/dist/hostSourcePreservation.d.ts +38 -0
  68. package/dist/hostSourcePreservation.js +72 -0
  69. package/dist/humanDecision.d.ts +7 -0
  70. package/dist/humanDecision.js +11 -0
  71. package/dist/interviews.d.ts +7 -0
  72. package/dist/interviews.js +18 -1
  73. package/dist/legacyCommunicationDisplay.d.ts +2 -0
  74. package/dist/legacyCommunicationDisplay.js +2 -0
  75. package/dist/managerPacket.js +3 -3
  76. package/dist/markers.d.ts +4 -3
  77. package/dist/markers.js +4 -20
  78. package/dist/observability.js +3 -8
  79. package/dist/probeIpc.d.ts +14 -0
  80. package/dist/probeIpc.js +29 -0
  81. package/dist/qaBuildGate.d.ts +21 -0
  82. package/dist/qaBuildGate.js +60 -13
  83. package/dist/qaBuilderCoverage.js +22 -13
  84. package/dist/qaBuilderEvidence.d.ts +16 -1
  85. package/dist/qaBuilderEvidence.js +48 -8
  86. package/dist/qaContractCoverage.d.ts +2 -2
  87. package/dist/qaContractCoverage.js +4 -9
  88. package/dist/qaContractDelivery.js +34 -20
  89. package/dist/qaCoverageAssembly.d.ts +22 -0
  90. package/dist/qaCoverageAssembly.js +16 -0
  91. package/dist/qaDeliveryJournal.d.ts +2 -0
  92. package/dist/qaDeliveryJournal.js +19 -0
  93. package/dist/qaFailureDelivery.js +97 -85
  94. package/dist/qaHostTransformation.d.ts +26 -0
  95. package/dist/qaHostTransformation.js +100 -0
  96. package/dist/qaPreparation.d.ts +8 -20
  97. package/dist/qaPreparation.js +153 -254
  98. package/dist/qaPreparationInputs.js +19 -0
  99. package/dist/qaPreparationStore.d.ts +11 -0
  100. package/dist/qaPreparationStore.js +44 -4
  101. package/dist/qaPrerequisites.d.ts +2 -1
  102. package/dist/qaPrerequisites.js +21 -1
  103. package/dist/qaProtocolV2.d.ts +15 -0
  104. package/dist/qaRecovery.d.ts +0 -3
  105. package/dist/qaRecovery.js +21 -27
  106. package/dist/qaReview.js +241 -406
  107. package/dist/qaReviewEvidence.d.ts +7 -3
  108. package/dist/qaReviewEvidence.js +15 -4
  109. package/dist/qaRuntime.js +1 -1
  110. package/dist/qaSnapshot.js +76 -21
  111. package/dist/qaStructuredResponse.d.ts +18 -0
  112. package/dist/qaStructuredResponse.js +81 -0
  113. package/dist/qaTimeline.js +2 -2
  114. package/dist/qaVerificationContract.d.ts +1 -2
  115. package/dist/qaVerificationContract.js +24 -35
  116. package/dist/readinessGate.js +5 -4
  117. package/dist/recoveryOwners.d.ts +32 -0
  118. package/dist/recoveryOwners.js +87 -0
  119. package/dist/runtimePreflight.d.ts +11 -0
  120. package/dist/runtimePreflight.js +77 -0
  121. package/dist/runtimeReadiness.js +23 -15
  122. package/dist/sources/sourceRegistry.d.ts +0 -3
  123. package/dist/sources/sourceRegistry.js +0 -42
  124. package/dist/statusReporter.d.ts +2 -0
  125. package/dist/statusReporter.js +2 -0
  126. package/dist/structuredCutover.d.ts +31 -0
  127. package/dist/structuredCutover.js +510 -0
  128. package/dist/structuredCutoverFiles.d.ts +10 -0
  129. package/dist/structuredCutoverFiles.js +181 -0
  130. package/dist/structuredDispatchAuthority.d.ts +22 -0
  131. package/dist/structuredDispatchAuthority.js +110 -0
  132. package/dist/structuredDispatchRecord.d.ts +29 -0
  133. package/dist/structuredDispatchRecord.js +36 -0
  134. package/dist/structuredLiveBudget.d.ts +2 -0
  135. package/dist/structuredLiveBudget.js +23 -0
  136. package/dist/structuredOperation.d.ts +18 -0
  137. package/dist/structuredOperation.js +279 -0
  138. package/dist/supervisedStart.js +35 -21
  139. package/dist/ticketPlanning.d.ts +1 -376
  140. package/dist/ticketPlanning.js +2 -49
  141. package/dist/ticketPopulation.d.ts +0 -3
  142. package/dist/ticketPopulation.js +4 -21
  143. package/dist/ticketPopulationPageAuthority.d.ts +13 -0
  144. package/dist/ticketPopulationPageAuthority.js +30 -0
  145. package/dist/ticketPopulationPaging.d.ts +27 -0
  146. package/dist/ticketPopulationPaging.js +127 -0
  147. package/dist/tickets/renderMarkdown.d.ts +14 -0
  148. package/dist/tickets/renderMarkdown.js +39 -5
  149. package/dist/tickets/ticketLoader.js +10 -0
  150. package/dist/tickets/ticketSchema.d.ts +107 -0
  151. package/dist/tickets/ticketSchema.js +4 -1
  152. package/dist/workflowDb.d.ts +116 -3
  153. package/dist/workflowDb.js +852 -61
  154. package/dist/workflowReader.d.ts +1 -0
  155. package/dist/workflowReader.js +9 -0
  156. package/package.json +23 -7
@@ -0,0 +1,13 @@
1
+ import type Database from "better-sqlite3";
2
+ export type CheckpointOrigin = {
3
+ kind: "provider";
4
+ dispatchId: string;
5
+ publicationKind: string;
6
+ } | {
7
+ kind: "host";
8
+ eventSequence: number;
9
+ inputCheckpointDigests: string[];
10
+ };
11
+ /** Read-only replay: never opens a writer or migrates storage. */
12
+ export declare function verifyCheckpointOrigin(db: Database.Database, projectDir: string, checkpointId: number, visited?: Set<number>): void;
13
+ export declare function verifyContinuityHeads(db: Database.Database, projectDir: string, runId: string): void;
@@ -0,0 +1,91 @@
1
+ import { persistedProviderWireContract } from "rafi-spec";
2
+ import { createHash } from "node:crypto";
3
+ import { canonicalJson } from "rafi-spec";
4
+ import { graphDerivedAllowed } from "./graph/derived.js";
5
+ import { hydrateStructuredReceipt, persistedDispatchTuple, verifyPersistedDispatch } from "./structuredDispatchRecord.js";
6
+ /** Read-only replay: never opens a writer or migrates storage. */
7
+ export function verifyCheckpointOrigin(db, projectDir, checkpointId, visited = new Set()) {
8
+ if (visited.has(checkpointId))
9
+ throw new Error("Cyclic checkpoint provenance");
10
+ visited.add(checkpointId);
11
+ const checkpoint = db.prepare("SELECT * FROM continuity_checkpoints WHERE checkpoint_id=?").get(checkpointId);
12
+ const row = db.prepare("SELECT origin_json,delta_digest FROM continuity_checkpoint_origins WHERE checkpoint_id=?").get(checkpointId);
13
+ if (!checkpoint || !row || hash(checkpoint.delta_json) !== row.delta_digest)
14
+ throw new Error("Checkpoint has no verified provenance");
15
+ const origin = JSON.parse(row.origin_json);
16
+ if (origin.kind === "provider") {
17
+ const operation = db.prepare("SELECT * FROM operation_journal WHERE idempotency_key=?").get(origin.dispatchId);
18
+ if (!operation || operation.run_id !== checkpoint.run_id || operation.status !== "confirmed")
19
+ throw new Error("Checkpoint origin is not a confirmed dispatch in this run");
20
+ const intent = JSON.parse(operation.intent_json), result = JSON.parse(operation.result_json);
21
+ const tuple = persistedDispatchTuple(intent.sealedDispatch);
22
+ if (canonicalJson(intent.tuple) !== intent.sealedDispatch.tupleJson || tuple.dispatchId !== origin.dispatchId || intent.sessionRef?.role !== checkpoint.role)
23
+ throw new Error("Checkpoint origin tuple/role mismatch");
24
+ const frozen = intent.sessionRef;
25
+ if (!frozen || frozen.sessionId !== tuple.sessionId || frozen.provider !== tuple.provider || frozen.generation !== tuple.generation)
26
+ throw new Error("Checkpoint origin frozen session mismatch");
27
+ const review = db.prepare("SELECT intent_json FROM qa_turns WHERE operation_id=?").get(origin.dispatchId);
28
+ if (review && canonicalJson(JSON.parse(review.intent_json).sealedDispatch) !== canonicalJson(intent.sealedDispatch))
29
+ throw new Error("Checkpoint QA owner differs from transport intent");
30
+ const delivery = db.prepare("SELECT record_json FROM qa_delivery_turns WHERE turn_record_id=?").get(origin.dispatchId);
31
+ if (delivery && canonicalJson(JSON.parse(delivery.record_json).sealedDispatch) !== canonicalJson(intent.sealedDispatch))
32
+ throw new Error("Checkpoint remediation owner differs from transport intent");
33
+ const descriptor = persistedProviderWireContract(tuple);
34
+ if (!descriptor || result.validation !== "accepted")
35
+ throw new Error("Checkpoint origin descriptor/validation mismatch");
36
+ const read = (digest) => {
37
+ const bytes = db.prepare("SELECT content FROM content_refs WHERE digest=?").get(digest)?.content;
38
+ if (!bytes || hash(bytes) !== digest || !graphDerivedAllowed(db, projectDir, digest))
39
+ throw new Error("Checkpoint origin evidence missing, altered or revoked");
40
+ return bytes;
41
+ };
42
+ const retained = JSON.parse(read(result.responseDigest).toString());
43
+ if (retained.tupleDigest !== tuple.digest)
44
+ throw new Error("Checkpoint origin receipt tuple mismatch");
45
+ const response = verifyPersistedDispatch(descriptor.canonical, intent.sealedDispatch, hydrateStructuredReceipt(retained, read));
46
+ if (canonicalJson(response.continuity) !== canonicalJson(JSON.parse(checkpoint.delta_json)))
47
+ throw new Error("Checkpoint delta differs from its native origin");
48
+ if (checkpoint.session_ref_json) {
49
+ const session = JSON.parse(checkpoint.session_ref_json);
50
+ if (["provider", "role", "stream", "cwd", "configRoot", "workspaceIdentity", "ticketId", "deliveryUnitId"].some(field => session[field] !== frozen[field]))
51
+ throw new Error("Checkpoint scoped session differs from transport");
52
+ if (session.sessionId !== tuple.sessionId || session.generation !== tuple.generation) {
53
+ // Generation promotion is permitted only by the recorded accepted handoff.
54
+ const acceptance = db.prepare("SELECT acceptance_receipt_digest FROM handoffs WHERE run_id=? AND successor_session_id=? AND generation=? AND state='accepted'").get(checkpoint.run_id, session.sessionId, session.generation);
55
+ if (!acceptance)
56
+ throw new Error("Checkpoint session differs from its dispatch");
57
+ const receipt = JSON.parse(read(acceptance.acceptance_receipt_digest).toString());
58
+ if (receipt.runId !== checkpoint.run_id || receipt.role !== checkpoint.role || receipt.acceptanceCheckpointDigest !== checkpoint.digest
59
+ || canonicalJson(receipt.successorSessionRef) !== canonicalJson(session) || session.sessionId !== tuple.sessionId)
60
+ throw new Error("Checkpoint generation promotion lacks its exact accepted handoff lineage");
61
+ }
62
+ }
63
+ }
64
+ else if (origin.kind === "host") {
65
+ const event = db.prepare("SELECT run_id,role,payload_json FROM continuity_events WHERE sequence=?").get(origin.eventSequence);
66
+ if (!event || event.run_id !== checkpoint.run_id || event.role !== "host" || JSON.parse(event.payload_json).checkpointDeltaDigest !== row.delta_digest)
67
+ throw new Error("Host checkpoint event provenance mismatch");
68
+ for (const digest of origin.inputCheckpointDigests) {
69
+ const input = db.prepare("SELECT checkpoint_id FROM continuity_checkpoints WHERE run_id=? AND digest=?").get(checkpoint.run_id, digest);
70
+ if (!input || input.checkpoint_id >= checkpointId)
71
+ throw new Error("Host checkpoint input is unavailable or not a predecessor");
72
+ verifyCheckpointOrigin(db, projectDir, input.checkpoint_id, new Set(visited));
73
+ }
74
+ }
75
+ else
76
+ throw new Error("Unknown checkpoint provenance");
77
+ }
78
+ function hash(bytes) { return createHash("sha256").update(bytes).digest("hex"); }
79
+ export function verifyContinuityHeads(db, projectDir, runId) {
80
+ const heads = db.prepare("SELECT * FROM continuity_heads WHERE run_id=? ORDER BY role").all(runId);
81
+ const roles = heads.filter(head => head.role !== "run");
82
+ for (const head of roles) {
83
+ const checkpoint = db.prepare("SELECT checkpoint_id,event_sequence FROM continuity_checkpoints WHERE run_id=? AND role=? AND digest=?").get(runId, head.role, head.digest);
84
+ if (!checkpoint || checkpoint.event_sequence !== head.event_sequence)
85
+ throw new Error("Continuity head has no matching checkpoint");
86
+ verifyCheckpointOrigin(db, projectDir, checkpoint.checkpoint_id);
87
+ }
88
+ const run = heads.find(head => head.role === "run");
89
+ if (run && (run.digest !== hash(canonicalJson(roles.map(row => ({ role: row.role, digest: row.digest })))) || run.event_sequence !== Math.max(...roles.map(row => row.event_sequence))))
90
+ throw new Error("Run continuity head differs from its verified role heads");
91
+ }
@@ -0,0 +1,4 @@
1
+ /** Tag an error raised before any provider transport began, so the journal
2
+ * records a known unsent turn instead of an unknown outcome. */
3
+ export declare function markDispatchNotSent(error: unknown): unknown;
4
+ export declare function dispatchWasNotSent(error: unknown): boolean;
@@ -0,0 +1,10 @@
1
+ /** Tag an error raised before any provider transport began, so the journal
2
+ * records a known unsent turn instead of an unknown outcome. */
3
+ export function markDispatchNotSent(error) {
4
+ if (error && typeof error === "object")
5
+ error.dispatchNotSent = true;
6
+ return error;
7
+ }
8
+ export function dispatchWasNotSent(error) {
9
+ return Boolean(error && typeof error === "object" && error.dispatchNotSent);
10
+ }
package/dist/foreman.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { GraphPurpose } from "rafi-spec";
1
+ import type { GraphPurpose, OutputContract } from "rafi-spec";
2
2
  import type { BuilderAdapter, PermissionHandler, PermissionRequest, TurnResult } from "./adapters/types.js";
3
3
  import type { PermissionPolicy } from "./permissions/policy.js";
4
4
  import { type AnsweredProviderQuestion } from "./providerQuestions.js";
@@ -7,7 +7,7 @@ import { type QaNonconvergenceContext, type QaNonconvergenceDecision, type QaRep
7
7
  import type { SessionStrategy } from "rafi-spec";
8
8
  import type { RunObserver } from "./observability.js";
9
9
  import type { QaRecoveryPacket } from "./qaRecovery.js";
10
- /** Parsed STEP_STATUS marker from a builder's turn. */
10
+ /** A role turn's outcome, derived from its native structured result. */
11
11
  export interface StepStatus {
12
12
  kind: "done" | "blocked" | "plan_complete" | "needs_input" | "qa_pass" | "qa_fail" | "unknown";
13
13
  summary?: string;
@@ -21,17 +21,10 @@ export interface StepStatus {
21
21
  error?: string;
22
22
  }
23
23
  /**
24
- * Workflow-specific completion is opt-in. Callers may preserve a valid
25
- * structured response before the generic strict STEP_STATUS repair path runs.
24
+ * Workflow-specific completion is opt-in. Callers may interpret a valid
25
+ * structured response before the generic native result handling runs.
26
26
  */
27
27
  export type InstructionCompletion = (text: string) => StepStatus | undefined;
28
- /**
29
- * Pull the STEP_STATUS marker out of a turn's final text.
30
- * Format: `STEP_STATUS: <kind> | key="value" key="value"`.
31
- */
32
- export declare function parseStepStatus(text: string, options?: {
33
- requireFinalLine?: boolean;
34
- }): StepStatus;
35
28
  /** Heuristic: did a marker-less turn end by asking the human something? */
36
29
  export declare function looksLikeQuestion(text: string): boolean;
37
30
  export { MARKER_SPEC } from "./markers.js";
@@ -39,13 +32,13 @@ export { QA_MARKER_SPEC } from "./markers.js";
39
32
  /** Instruction sent on the first turn of a batch. */
40
33
  export declare function buildPrimer(n: number, trackerPath?: string, ticketsEnabled?: boolean, preferredTicketId?: string): string;
41
34
  /** Instruction sent on turns 2–N. */
42
- export declare function buildNextStepInstruction(i: number, n: number): string;
35
+ export declare function buildNextStepInstruction(i: number, n: number, ticketsEnabled?: boolean): string;
43
36
  /** QA review turn: ask the builder to triple-check the ticket or step it just completed. */
44
37
  export declare function buildQaInstruction(): string;
45
38
  /** Follow-up turn after qa_fail: have the builder implement the listed fixes. */
46
39
  export declare function buildQaFixInstruction(issues: string): string;
47
40
  /** Pre-flight planning turn: ask the builder to list its next N steps without implementing anything. */
48
- export declare function buildPlanningTurn(n: number, ticketsContent?: string, preferredTicketId?: string): string;
41
+ export declare function buildPlanningTurn(n: number, ticketsContent?: string, preferredTicketId?: string, ticketsEnabled?: boolean): string;
49
42
  /** Builds the permission callback: classify, log, allow or escalate. */
50
43
  export declare function createPermissionHandler(policy: PermissionPolicy, log: Log, opts?: {
51
44
  interactive?: boolean;
@@ -67,7 +60,7 @@ export interface ForemanNotificationOptions {
67
60
  desktop: boolean;
68
61
  terminalBell: boolean;
69
62
  }
70
- /** Drives one builder through a batch of N steps via the STEP_STATUS protocol. */
63
+ /** Drives one builder through contracted native operations. */
71
64
  export declare class BuildInterruptedError extends Error {
72
65
  constructor();
73
66
  }
@@ -133,14 +126,20 @@ export declare class Foreman {
133
126
  /** Send user feedback on the plan; builder responds with a revised list. Does not count toward steps. */
134
127
  sendPreflightFeedback(feedback: string): Promise<void>;
135
128
  /** Send one custom instruction through the same needs_input loop as a batch turn. */
136
- runInstruction(instruction: string, options?: {
129
+ runInstruction(instruction: string, options: {
137
130
  completion?: InstructionCompletion;
131
+ output: OutputContract;
138
132
  }): Promise<{
139
133
  result: TurnResult;
140
134
  status: StepStatus;
141
135
  }>;
136
+ /** The sole convenience path that selects the Builder's work-report contract. */
137
+ runBuilderWork(instruction: string): Promise<{
138
+ result: TurnResult;
139
+ status: StepStatus;
140
+ }>;
142
141
  /** Ask an already-active role session to propose choices for its reported blocker. */
143
- resolveBlocker(adapter: BuilderAdapter, reason: string, mode?: "builder" | "qa"): Promise<{
142
+ resolveBlocker(adapter: BuilderAdapter, reason: string, mode: "builder" | "qa", output: OutputContract): Promise<{
144
143
  result: TurnResult;
145
144
  status: StepStatus;
146
145
  }>;