@gajae-code/coding-agent 0.4.5 → 0.5.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 (185) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/dist/types/async/job-manager.d.ts +26 -0
  3. package/dist/types/cli/args.d.ts +1 -0
  4. package/dist/types/cli/list-models.d.ts +6 -0
  5. package/dist/types/commands/gc.d.ts +26 -0
  6. package/dist/types/commands/harness.d.ts +3 -0
  7. package/dist/types/config/file-lock-gc.d.ts +5 -0
  8. package/dist/types/config/file-lock.d.ts +7 -0
  9. package/dist/types/config/model-profile-activation.d.ts +11 -2
  10. package/dist/types/config/model-profiles.d.ts +7 -0
  11. package/dist/types/config/model-registry.d.ts +3 -0
  12. package/dist/types/config/model-resolver.d.ts +2 -0
  13. package/dist/types/config/models-config-schema.d.ts +30 -0
  14. package/dist/types/config/settings-schema.d.ts +4 -3
  15. package/dist/types/coordinator/contract.d.ts +1 -1
  16. package/dist/types/defaults/gjc/extensions/grok-build/index.d.ts +1 -0
  17. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/index.d.ts +1 -0
  18. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.d.ts +25 -0
  19. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/payload/sanitize.d.ts +27 -0
  20. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/billing.d.ts +8 -0
  21. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.d.ts +5 -0
  22. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.d.ts +10 -0
  23. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/usage.d.ts +2 -0
  24. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/shared/base-url.d.ts +2 -0
  25. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/shared/errors.d.ts +38 -0
  26. package/dist/types/defaults/gjc-grok-cli.d.ts +5 -0
  27. package/dist/types/extensibility/extensions/index.d.ts +1 -0
  28. package/dist/types/extensibility/extensions/prefix-command-bridge.d.ts +35 -0
  29. package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +103 -0
  30. package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +2 -0
  31. package/dist/types/gjc-runtime/deep-interview-state.d.ts +112 -0
  32. package/dist/types/gjc-runtime/gc-render.d.ts +6 -0
  33. package/dist/types/gjc-runtime/gc-runtime.d.ts +134 -0
  34. package/dist/types/gjc-runtime/ledger-event-renderer.d.ts +68 -0
  35. package/dist/types/gjc-runtime/team-gc.d.ts +7 -0
  36. package/dist/types/gjc-runtime/team-runtime.d.ts +5 -1
  37. package/dist/types/gjc-runtime/tmux-common.d.ts +14 -0
  38. package/dist/types/gjc-runtime/tmux-gc.d.ts +7 -0
  39. package/dist/types/gjc-runtime/tmux-sessions.d.ts +13 -0
  40. package/dist/types/harness-control-plane/gc-adapter.d.ts +3 -0
  41. package/dist/types/harness-control-plane/owner.d.ts +8 -1
  42. package/dist/types/harness-control-plane/receipt-spool.d.ts +19 -0
  43. package/dist/types/harness-control-plane/state-machine.d.ts +6 -1
  44. package/dist/types/harness-control-plane/storage.d.ts +20 -0
  45. package/dist/types/harness-control-plane/types.d.ts +4 -0
  46. package/dist/types/hindsight/mental-models.d.ts +5 -5
  47. package/dist/types/modes/components/hook-selector.d.ts +7 -1
  48. package/dist/types/modes/components/model-selector.d.ts +1 -12
  49. package/dist/types/modes/controllers/command-controller.d.ts +1 -0
  50. package/dist/types/modes/rpc/rpc-client.d.ts +2 -2
  51. package/dist/types/modes/rpc/rpc-mode.d.ts +16 -1
  52. package/dist/types/modes/rpc/rpc-types.d.ts +4 -1
  53. package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +13 -0
  54. package/dist/types/modes/shared/agent-wire/session-registry.d.ts +25 -0
  55. package/dist/types/modes/shared/agent-wire/unattended-action-policy.d.ts +2 -0
  56. package/dist/types/sdk.d.ts +5 -0
  57. package/dist/types/session/agent-session.d.ts +3 -1
  58. package/dist/types/session/blob-store.d.ts +59 -4
  59. package/dist/types/session/session-manager.d.ts +24 -6
  60. package/dist/types/session/streaming-output.d.ts +3 -2
  61. package/dist/types/session/tool-choice-queue.d.ts +6 -0
  62. package/dist/types/skill-state/workflow-hud.d.ts +14 -0
  63. package/dist/types/task/receipt.d.ts +1 -0
  64. package/dist/types/task/types.d.ts +7 -0
  65. package/dist/types/thinking-metadata.d.ts +16 -0
  66. package/dist/types/thinking.d.ts +3 -12
  67. package/dist/types/tools/ask.d.ts +15 -1
  68. package/dist/types/tools/index.d.ts +2 -0
  69. package/dist/types/tools/resolve.d.ts +0 -10
  70. package/dist/types/tools/subagent.d.ts +6 -0
  71. package/dist/types/utils/tool-choice.d.ts +14 -1
  72. package/package.json +7 -7
  73. package/src/async/job-manager.ts +52 -0
  74. package/src/cli/args.ts +3 -0
  75. package/src/cli/auth-broker-cli.ts +1 -0
  76. package/src/cli/list-models.ts +13 -1
  77. package/src/cli.ts +9 -4
  78. package/src/commands/gc.ts +22 -0
  79. package/src/commands/harness.ts +43 -5
  80. package/src/commands/launch.ts +2 -2
  81. package/src/commands/session.ts +3 -1
  82. package/src/config/file-lock-gc.ts +181 -0
  83. package/src/config/file-lock.ts +14 -0
  84. package/src/config/model-profile-activation.ts +15 -3
  85. package/src/config/model-profiles.ts +264 -56
  86. package/src/config/model-resolver.ts +9 -6
  87. package/src/config/models-config-schema.ts +1 -0
  88. package/src/config/settings-schema.ts +6 -3
  89. package/src/coordinator/contract.ts +1 -0
  90. package/src/coordinator-mcp/server.ts +513 -26
  91. package/src/cursor.ts +16 -2
  92. package/src/defaults/gjc/agent.models.grok-cli.yml +36 -0
  93. package/src/defaults/gjc/extensions/grok-build/index.ts +1 -0
  94. package/src/defaults/gjc/extensions/grok-build/package.json +7 -0
  95. package/src/defaults/gjc/extensions/grok-cli-vendor/biome.json +39 -0
  96. package/src/defaults/gjc/extensions/grok-cli-vendor/package.json +8 -0
  97. package/src/defaults/gjc/extensions/grok-cli-vendor/src/index.ts +1 -0
  98. package/src/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.ts +155 -0
  99. package/src/defaults/gjc/extensions/grok-cli-vendor/src/payload/sanitize.ts +361 -0
  100. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/billing.ts +57 -0
  101. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +99 -0
  102. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.ts +50 -0
  103. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/usage.ts +56 -0
  104. package/src/defaults/gjc/extensions/grok-cli-vendor/src/shared/base-url.ts +36 -0
  105. package/src/defaults/gjc/extensions/grok-cli-vendor/src/shared/errors.ts +44 -0
  106. package/src/defaults/gjc/skills/deep-interview/SKILL.md +131 -113
  107. package/src/defaults/gjc/skills/deep-interview/lateral-review-panel.md +49 -0
  108. package/src/defaults/gjc/skills/team/SKILL.md +3 -2
  109. package/src/defaults/gjc/skills/ultragoal/SKILL.md +8 -2
  110. package/src/defaults/gjc-defaults.ts +7 -0
  111. package/src/defaults/gjc-grok-cli.ts +22 -0
  112. package/src/export/html/index.ts +13 -9
  113. package/src/extensibility/extensions/index.ts +1 -0
  114. package/src/extensibility/extensions/prefix-command-bridge.ts +128 -0
  115. package/src/gjc-runtime/deep-interview-recorder.ts +417 -0
  116. package/src/gjc-runtime/deep-interview-runtime.ts +18 -26
  117. package/src/gjc-runtime/deep-interview-state.ts +324 -0
  118. package/src/gjc-runtime/gc-render.ts +70 -0
  119. package/src/gjc-runtime/gc-runtime.ts +403 -0
  120. package/src/gjc-runtime/ledger-event-renderer.ts +164 -0
  121. package/src/gjc-runtime/ralplan-runtime.ts +58 -7
  122. package/src/gjc-runtime/state-renderer.ts +12 -3
  123. package/src/gjc-runtime/state-runtime.ts +46 -29
  124. package/src/gjc-runtime/team-gc.ts +49 -0
  125. package/src/gjc-runtime/team-runtime.ts +211 -8
  126. package/src/gjc-runtime/tmux-common.ts +29 -0
  127. package/src/gjc-runtime/tmux-gc.ts +176 -0
  128. package/src/gjc-runtime/tmux-sessions.ts +68 -12
  129. package/src/gjc-runtime/ultragoal-runtime.ts +517 -41
  130. package/src/gjc-runtime/workflow-manifest.generated.json +27 -1
  131. package/src/gjc-runtime/workflow-manifest.ts +16 -1
  132. package/src/harness-control-plane/gc-adapter.ts +184 -0
  133. package/src/harness-control-plane/owner.ts +89 -27
  134. package/src/harness-control-plane/receipt-spool.ts +128 -0
  135. package/src/harness-control-plane/state-machine.ts +27 -6
  136. package/src/harness-control-plane/storage.ts +93 -0
  137. package/src/harness-control-plane/types.ts +4 -0
  138. package/src/hindsight/mental-models.ts +17 -16
  139. package/src/internal-urls/docs-index.generated.ts +14 -8
  140. package/src/main.ts +7 -2
  141. package/src/modes/components/assistant-message.ts +26 -14
  142. package/src/modes/components/diff.ts +97 -0
  143. package/src/modes/components/hook-selector.ts +19 -0
  144. package/src/modes/components/model-selector.ts +370 -181
  145. package/src/modes/components/status-line/segments.ts +1 -1
  146. package/src/modes/components/tool-execution.ts +30 -13
  147. package/src/modes/controllers/command-controller.ts +25 -6
  148. package/src/modes/controllers/extension-ui-controller.ts +3 -0
  149. package/src/modes/controllers/selector-controller.ts +34 -42
  150. package/src/modes/rpc/rpc-client.ts +3 -2
  151. package/src/modes/rpc/rpc-mode.ts +187 -39
  152. package/src/modes/rpc/rpc-types.ts +5 -2
  153. package/src/modes/shared/agent-wire/command-dispatch.ts +279 -257
  154. package/src/modes/shared/agent-wire/command-validation.ts +11 -0
  155. package/src/modes/shared/agent-wire/deep-interview-gate.ts +30 -1
  156. package/src/modes/shared/agent-wire/session-registry.ts +109 -0
  157. package/src/modes/shared/agent-wire/unattended-action-policy.ts +24 -0
  158. package/src/modes/shared/agent-wire/unattended-run-controller.ts +23 -3
  159. package/src/modes/shared/agent-wire/unattended-session.ts +16 -1
  160. package/src/sdk.ts +46 -5
  161. package/src/secrets/obfuscator.ts +102 -27
  162. package/src/session/agent-session.ts +179 -25
  163. package/src/session/blob-store.ts +148 -6
  164. package/src/session/session-manager.ts +311 -60
  165. package/src/session/streaming-output.ts +185 -122
  166. package/src/session/tool-choice-queue.ts +23 -0
  167. package/src/setup/hermes/templates/operator-instructions.v1.md +7 -1
  168. package/src/skill-state/workflow-hud.ts +106 -10
  169. package/src/slash-commands/builtin-registry.ts +3 -2
  170. package/src/task/executor.ts +78 -6
  171. package/src/task/receipt.ts +5 -0
  172. package/src/task/render.ts +21 -1
  173. package/src/task/types.ts +8 -0
  174. package/src/thinking-metadata.ts +51 -0
  175. package/src/thinking.ts +26 -46
  176. package/src/tools/ask.ts +56 -1
  177. package/src/tools/bash.ts +1 -1
  178. package/src/tools/index.ts +2 -0
  179. package/src/tools/job.ts +3 -2
  180. package/src/tools/monitor.ts +36 -1
  181. package/src/tools/resolve.ts +93 -18
  182. package/src/tools/subagent-render.ts +9 -0
  183. package/src/tools/subagent.ts +26 -2
  184. package/src/utils/edit-mode.ts +1 -1
  185. package/src/utils/tool-choice.ts +45 -16
@@ -0,0 +1,103 @@
1
+ import { type DeepInterviewEstablishedFact, type DeepInterviewRoundRecord, type DeepInterviewStateEnvelope, type DeepInterviewTriggerMetadata } from "./deep-interview-state";
2
+ export * from "./deep-interview-state";
3
+ /**
4
+ * Runtime-owned deep-interview round recorder (conflict-aware scoring support).
5
+ *
6
+ * Ownership boundary (per the approved consensus plan): this module owns durable
7
+ * round-record semantics — stable identity, append-or-merge, lifecycle, compact
8
+ * reads, replay detection, and the pure scored-transition validator. Callers such
9
+ * as the `ask` tool only resolve an answer and invoke these helpers; they never
10
+ * compute state paths, merge records, or write `.gjc` files directly. All writes
11
+ * go through the sanctioned state-writer (`writeWorkflowEnvelopeAtomic`).
12
+ */
13
+ export interface DeepInterviewAnswerInput {
14
+ interviewId?: string;
15
+ round: number;
16
+ round_id?: string;
17
+ questionId?: string;
18
+ questionText: string;
19
+ component?: string;
20
+ dimension?: string;
21
+ ambiguity?: number;
22
+ selectedOptions?: string[];
23
+ customInput?: string;
24
+ }
25
+ export interface DeepInterviewScoringInput {
26
+ interviewId?: string;
27
+ round: number;
28
+ round_id?: string;
29
+ questionId?: string;
30
+ scores: Record<string, number>;
31
+ ambiguity: number;
32
+ triggers?: DeepInterviewTriggerMetadata[];
33
+ }
34
+ export type AppendOrMergeAction = "created" | "noop" | "replaced";
35
+ export interface AppendOrMergeResult {
36
+ rounds: DeepInterviewRoundRecord[];
37
+ action: AppendOrMergeAction;
38
+ record: DeepInterviewRoundRecord;
39
+ }
40
+ export interface DeepInterviewCompactState {
41
+ threshold?: number;
42
+ threshold_source?: string;
43
+ current_ambiguity?: number;
44
+ topology_summary?: {
45
+ active: number;
46
+ deferred: number;
47
+ components: string[];
48
+ };
49
+ established_facts: DeepInterviewEstablishedFact[];
50
+ unresolved_triggers: DeepInterviewTriggerMetadata[];
51
+ recent_scored_rounds: DeepInterviewRoundRecord[];
52
+ pending_shells: DeepInterviewRoundRecord[];
53
+ }
54
+ export interface TransitionValidationResult {
55
+ ok: boolean;
56
+ violations: string[];
57
+ }
58
+ export declare function buildAnswerShell(input: DeepInterviewAnswerInput, now?: string): DeepInterviewRoundRecord;
59
+ /**
60
+ * Append-or-merge by `round_key`. Exactly one record per key:
61
+ * - no existing record -> append (`created`);
62
+ * - identical question_hash + answer_hash -> deterministic no-op (`noop`);
63
+ * - same key, different hashes -> deterministic replacement of the prior shell
64
+ * (`replaced`); the prior answer for that key is superseded and lifecycle resets.
65
+ */
66
+ export declare function appendOrMergeRound(rounds: readonly DeepInterviewRoundRecord[], shell: DeepInterviewRoundRecord): AppendOrMergeResult;
67
+ /**
68
+ * Merge scoring output into the existing record for the derived key, transitioning
69
+ * it to `scored`. Never appends a second record for the same key; if no shell exists
70
+ * yet (scoring without a prior ask), a scored record is created so data is not lost.
71
+ */
72
+ export declare function enrichRoundWithScoring(rounds: readonly DeepInterviewRoundRecord[], input: DeepInterviewScoringInput, now?: string): {
73
+ rounds: DeepInterviewRoundRecord[];
74
+ record: DeepInterviewRoundRecord;
75
+ };
76
+ /**
77
+ * Bidirectional invariant: if `next` carries an `active` trigger, the affected
78
+ * dimension must not improve and overall ambiguity must rise vs the prior scored
79
+ * round. `disputed`/`unresolved` triggers are exempt but must carry a rationale.
80
+ */
81
+ export declare function validateDeepInterviewScoredTransition(prior: DeepInterviewRoundRecord | undefined, next: DeepInterviewRoundRecord): TransitionValidationResult;
82
+ /** Back-compat wrapper: normalize a deep-interview envelope to its canonical nested shape. */
83
+ export declare function ensureDeepInterviewStateShape(value: unknown): DeepInterviewStateEnvelope;
84
+ export declare function projectCompactState(value: unknown, options?: {
85
+ lastN?: number;
86
+ }): DeepInterviewCompactState;
87
+ /** Record an `answered` shell for one round (append-or-merge by durable key). */
88
+ export declare function appendOrMergeDeepInterviewRound(cwd: string, statePath: string, input: DeepInterviewAnswerInput, options?: {
89
+ sessionId?: string;
90
+ }): Promise<{
91
+ action: AppendOrMergeAction;
92
+ record: DeepInterviewRoundRecord;
93
+ }>;
94
+ /** Merge scoring output into the same round record, transitioning to `scored`. */
95
+ export declare function enrichDeepInterviewRoundScoring(cwd: string, statePath: string, input: DeepInterviewScoringInput, options?: {
96
+ sessionId?: string;
97
+ }): Promise<{
98
+ record: DeepInterviewRoundRecord;
99
+ }>;
100
+ /** Compact projection so callers read a slice instead of the full transcript. */
101
+ export declare function readDeepInterviewStateCompact(statePath: string, options?: {
102
+ lastN?: number;
103
+ }): Promise<DeepInterviewCompactState>;
@@ -1,3 +1,4 @@
1
+ export * from "./deep-interview-recorder";
1
2
  /**
2
3
  * Native implementation of `gjc deep-interview`.
3
4
  *
@@ -12,6 +13,7 @@ export interface DeepInterviewCommandResult {
12
13
  stdout?: string;
13
14
  stderr?: string;
14
15
  }
16
+ export declare function deepInterviewStatePath(cwd: string, sessionId: string | undefined): string;
15
17
  export interface ResolvedDeepInterviewSpecWriteArgs {
16
18
  stage: "final";
17
19
  slug: string;
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Pure, dependency-free foundation for deep-interview state shape.
3
+ *
4
+ * Ownership boundary (per the approved consensus plan): this leaf module owns the
5
+ * canonical persisted shape (interview data nested under `state`), durable round
6
+ * identity/hashing, lossless legacy normalization, and the deep-interview-specific
7
+ * envelope/round merge used by every writer (`deep-interview-recorder`,
8
+ * `state-runtime` write/reconcile, seed, and handoff). It MUST NOT import the
9
+ * active-state, state-writer, CLI runtime, or filesystem so it stays cycle-free and
10
+ * trivially testable.
11
+ */
12
+ export type DeepInterviewRoundLifecycle = "answered" | "pending_scoring" | "scored";
13
+ export type DeepInterviewTriggerKind = "A" | "B" | "C" | "D";
14
+ /** `active` triggers must satisfy the bidirectional invariant; disputed/unresolved are exempt with rationale. */
15
+ export type DeepInterviewTriggerStatus = "active" | "disputed" | "unresolved";
16
+ export interface DeepInterviewEstablishedFact {
17
+ id: string;
18
+ statement: string;
19
+ round: number;
20
+ component?: string;
21
+ dimension?: string;
22
+ evidence?: string;
23
+ disputed: boolean;
24
+ }
25
+ export interface DeepInterviewTriggerMetadata {
26
+ kind: DeepInterviewTriggerKind;
27
+ name: string;
28
+ status: DeepInterviewTriggerStatus;
29
+ component: string;
30
+ dimension: string;
31
+ priorDimensionScore?: number;
32
+ newDimensionScore?: number;
33
+ priorAmbiguity?: number;
34
+ newAmbiguity?: number;
35
+ evidence?: string;
36
+ contradictedFactId?: string;
37
+ /** Required when status is `disputed` or `unresolved` to exempt the invariant. */
38
+ rationale?: string;
39
+ }
40
+ export interface DeepInterviewRoundRecord {
41
+ round_key: string;
42
+ round_id?: string;
43
+ round: number;
44
+ question_id?: string;
45
+ question_text?: string;
46
+ question_hash: string;
47
+ answer_hash: string;
48
+ selected_options?: string[];
49
+ custom_input?: string;
50
+ component?: string;
51
+ dimension?: string;
52
+ ambiguity_at_ask?: number;
53
+ lifecycle: DeepInterviewRoundLifecycle;
54
+ answered_at: string;
55
+ scored_at?: string;
56
+ scores?: Record<string, number>;
57
+ ambiguity?: number;
58
+ triggers?: DeepInterviewTriggerMetadata[];
59
+ }
60
+ export interface DeepInterviewStateEnvelope {
61
+ threshold?: number;
62
+ threshold_source?: string;
63
+ state?: Record<string, unknown>;
64
+ [key: string]: unknown;
65
+ }
66
+ export declare function hashContent(value: string): string;
67
+ export declare function questionHash(questionText: string): string;
68
+ export declare function answerHash(selectedOptions: string[] | undefined, customInput: string | undefined): string;
69
+ /**
70
+ * Durable round identity. Prefer `interview_id + round_id`; fall back to
71
+ * `interview_id + round + question.id` when no caller-supplied `round_id` exists.
72
+ */
73
+ export declare function deriveRoundKey(interviewId: string | undefined, input: {
74
+ round_id?: string;
75
+ round: number;
76
+ questionId?: string;
77
+ }): string;
78
+ /**
79
+ * Canonicalize a deep-interview envelope: interview data nested under `state`,
80
+ * legacy flattened fields hoisted in losslessly, transcript duplicates removed
81
+ * from the top level, and `rounds`/`established_facts` guaranteed to be arrays.
82
+ *
83
+ * Idempotent: a canonical envelope is returned unchanged in shape. Never deletes
84
+ * unknown envelope or nested fields, and never mutates the input.
85
+ */
86
+ export declare function normalizeDeepInterviewEnvelope(value: unknown): DeepInterviewStateEnvelope;
87
+ /**
88
+ * Lossless, idempotent merge of two round arrays.
89
+ *
90
+ * - Records sharing a durable key (`round_key`, or synthesized from
91
+ * `round_id`/`question_id`) merge into one, preferring scored over answered.
92
+ * - Records without any durable identity are preserved verbatim; an exact
93
+ * duplicate is skipped so repeated writes stay idempotent, but distinct records
94
+ * are never collapsed.
95
+ *
96
+ * Deliberate refinement of the approved plan: rather than mutating opaque legacy
97
+ * records with synthetic `legacy:<index>` keys, they are preserved verbatim with
98
+ * exact-duplicate dedupe. This satisfies the plan's intent (lossless, idempotent,
99
+ * never collapse distinct rounds) without rewriting user-supplied round objects,
100
+ * and keeps free-form extension preservation intact. Recorder-produced records
101
+ * always carry a `round_key`, so the synthetic path is unnecessary in practice.
102
+ */
103
+ export declare function mergeDeepInterviewRounds(existing: readonly Record<string, unknown>[], incoming: readonly Record<string, unknown>[]): Record<string, unknown>[];
104
+ /**
105
+ * Deep-interview-specific envelope merge. Unlike the generic shallow null-delete
106
+ * merge, this keeps interview data nested under `state`, never deletes `state`,
107
+ * and merges `rounds` losslessly by durable key so a partial write (e.g. a
108
+ * scoring update) cannot drop recorder-written transcript history.
109
+ */
110
+ export declare function mergeDeepInterviewEnvelope(existing: unknown, incoming: unknown, options?: {
111
+ replace?: boolean;
112
+ }): DeepInterviewStateEnvelope;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Text rendering for `gjc gc` reports. JSON output is produced directly in
3
+ * `gc-runtime.ts`; this module owns the human-readable grouped report.
4
+ */
5
+ import type { GcReport } from "./gc-runtime";
6
+ export declare function buildGcReportText(report: GcReport): string;
@@ -0,0 +1,134 @@
1
+ /**
2
+ * `gjc gc` runtime — a global, liveness-only, dry-run-by-default garbage
3
+ * collector for stale GJC session/PID records.
4
+ *
5
+ * Design (see .gjc/plans/ralplan/2026-06-13-1347-954f/pending-approval.md):
6
+ * - This module is an ORCHESTRATOR only. It owns the shared PID probe, the
7
+ * report/exit-code policy, and text/JSON rendering. It must NOT parse private
8
+ * store layouts directly; every store is reached through an injectable
9
+ * `GcStoreAdapter` that lives next to its store owner.
10
+ * - Liveness-only and fail-closed: only `ESRCH` (no such process) is `dead`
11
+ * (removable). `process.kill(pid, 0)` success, `EPERM`, and any unknown probe
12
+ * error all mean KEEP — a live process is never signalled or killed.
13
+ * - Dry-run by default: nothing is deleted unless `--prune`/`--force`.
14
+ */
15
+ export type GcStore = "harness_leases" | "team_workers" | "file_locks" | "tmux_sessions" | "registry_entries";
16
+ export declare const GC_STORES: readonly GcStore[];
17
+ /** Why a probed pid is kept instead of treated as dead. */
18
+ export type GcPidKeepReason = "alive" | "eperm" | "unknown";
19
+ export interface GcPidProbeResult {
20
+ /** `dead` only on ESRCH; `keep` for alive/eperm/unknown (fail-closed). */
21
+ status: "dead" | "keep";
22
+ reason?: GcPidKeepReason;
23
+ error?: string;
24
+ }
25
+ /** Single shared liveness contract threaded through every classifier + prune path. */
26
+ export type GcPidProbe = (pid: number) => GcPidProbeResult;
27
+ export type GcPidStatus = "dead" | "alive" | "eperm" | "unknown" | "none";
28
+ export type GcAction = "none" | "would_remove" | "removed" | "remove_failed" | "skipped";
29
+ export interface GcRecord {
30
+ store: GcStore;
31
+ /** Stable identifier: session id, lock dir path, worker id, tmux name, registry session id. */
32
+ id: string;
33
+ path?: string;
34
+ root?: string;
35
+ pid?: number;
36
+ pid_status?: GcPidStatus;
37
+ /** Store-specific classification label (e.g. "dead", "live", "unclassified", "terminal_lifecycle"). */
38
+ status: string;
39
+ stale: boolean;
40
+ removable: boolean;
41
+ action: GcAction;
42
+ reason: string;
43
+ detail?: string;
44
+ error?: string;
45
+ removed?: boolean;
46
+ }
47
+ export interface GcError {
48
+ store: GcStore;
49
+ scope: string;
50
+ message: string;
51
+ }
52
+ export interface GcCollectResult {
53
+ records: GcRecord[];
54
+ errors: GcError[];
55
+ }
56
+ export interface GcPruneOutcome {
57
+ removed: boolean;
58
+ error?: string;
59
+ /** Set when a removable record was skipped at prune time (e.g. TOCTOU became live). */
60
+ skipped?: string;
61
+ }
62
+ export interface GcContext {
63
+ probe: GcPidProbe;
64
+ force: boolean;
65
+ env: NodeJS.ProcessEnv;
66
+ cwd: string;
67
+ }
68
+ /**
69
+ * A store-owned GC adapter. `collect` discovers + classifies (using the shared
70
+ * probe) without mutating anything. `prune` removes a single record, and MUST
71
+ * re-validate / re-probe immediately before any destructive action.
72
+ */
73
+ export interface GcStoreAdapter {
74
+ store: GcStore;
75
+ collect(ctx: GcContext): Promise<GcCollectResult>;
76
+ prune(record: GcRecord, ctx: GcContext): Promise<GcPruneOutcome>;
77
+ }
78
+ export interface GcCounts {
79
+ discovered: number;
80
+ stale: number;
81
+ alive: number;
82
+ eperm: number;
83
+ unknown: number;
84
+ terminal_lifecycle: number;
85
+ unclassified: number;
86
+ would_remove: number;
87
+ removed: number;
88
+ failed: number;
89
+ errors: number;
90
+ by_store: Record<GcStore, {
91
+ discovered: number;
92
+ stale: number;
93
+ would_remove: number;
94
+ removed: number;
95
+ failed: number;
96
+ }>;
97
+ }
98
+ export interface GcReport {
99
+ dry_run: boolean;
100
+ stores: Record<GcStore, GcRecord[]>;
101
+ counts: GcCounts;
102
+ errors: GcError[];
103
+ }
104
+ export interface GcRunResult {
105
+ stdout: string;
106
+ stderr: string;
107
+ status: number;
108
+ }
109
+ /**
110
+ * The shared, fail-closed PID probe. ESRCH => dead/removable; success => alive;
111
+ * EPERM => kept (owned by another user); any other error => kept as unknown.
112
+ */
113
+ export declare const gcPidProbe: GcPidProbe;
114
+ /** Map a `GcPidProbe` onto the harness lease probe shape (`"alive"|"dead"|"eperm"`). */
115
+ export declare function gcProbeToLeasePidStatus(probe: GcPidProbe): (pid: number) => "alive" | "dead" | "eperm";
116
+ /** Translate a probe result into a record-friendly pid status label. */
117
+ export declare function gcPidStatusLabel(result: GcPidProbeResult): Exclude<GcPidStatus, "none">;
118
+ /**
119
+ * Collect every store's records (catching hard discovery errors per adapter),
120
+ * then optionally prune removable records with per-record revalidation.
121
+ */
122
+ export declare function collectGcReport(adapters: GcStoreAdapter[], ctx: GcContext, prune: boolean): Promise<GcReport>;
123
+ /**
124
+ * Exit-code policy:
125
+ * - usage/parse error => 2
126
+ * - hard discovery errors => 1 (both modes)
127
+ * - prune mode with a failed intended removal => 1
128
+ * - otherwise => 0
129
+ */
130
+ export declare function computeExitCode(report: GcReport): number;
131
+ export declare function runGjcGcCommand(argv: string[], cwd?: string, env?: NodeJS.ProcessEnv, adapters?: GcStoreAdapter[]): Promise<GcRunResult>;
132
+ export declare function gcHelpText(): string;
133
+ /** Lazily assemble the real store adapters (kept lazy to avoid import cycles). */
134
+ export declare function defaultGcAdapters(): Promise<GcStoreAdapter[]>;
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Pure parse + summarize for ledger-backed skill observability.
3
+ *
4
+ * Workflow progress for ultragoal/ralplan cannot be observed via subagent tool
5
+ * events (those skills persist through `bash`-backed `gjc` CLI calls whose tool
6
+ * `details` carry no structured payload). The durable source of truth is the
7
+ * append-only ledgers:
8
+ * - ultragoal: `.gjc/ultragoal/ledger.jsonl`
9
+ * - ralplan: `.gjc/plans/ralplan/<run-id>/index.jsonl`
10
+ *
11
+ * This module is I/O-free: callers read the files and pass lines or already-parsed
12
+ * rows. It feeds the compact HUD chip builders in `skill-state/workflow-hud.ts`
13
+ * via the runtime sync paths. Display-string helpers stay theme-free.
14
+ */
15
+ /** Minimal projection of an ultragoal ledger row used for the HUD chip. */
16
+ export interface UltragoalLedgerEventLite {
17
+ /** Normalized from the row's `event` field, or `type` for reconcile rows. */
18
+ event: string;
19
+ goalId?: string;
20
+ status?: string;
21
+ timestamp?: string;
22
+ }
23
+ /**
24
+ * Coerce an already-parsed ledger row into the lite shape. Accepts both the
25
+ * `event`-keyed vocabulary (plan_created, goal_started, goal_checkpointed,
26
+ * steering_accepted/rejected, review_blockers_recorded) and the `type`-keyed
27
+ * reconcile-failure row (`type: "reconcile_failed"`). Returns undefined when no
28
+ * event/type discriminator is present.
29
+ */
30
+ export declare function coerceUltragoalLedgerEvent(row: Record<string, unknown>): UltragoalLedgerEventLite | undefined;
31
+ /** Parse a single ultragoal ledger JSONL line; undefined for blank/malformed lines. */
32
+ export declare function parseUltragoalLedgerLine(line: string): UltragoalLedgerEventLite | undefined;
33
+ /** The most recent event, or undefined when the ledger is empty. */
34
+ export declare function latestUltragoalLedgerEvent(events: readonly UltragoalLedgerEventLite[]): UltragoalLedgerEventLite | undefined;
35
+ /**
36
+ * Best-effort latest event from raw ledger text: parses line-by-line and skips
37
+ * blank/malformed rows so a torn or hand-edited ledger never throws on the HUD
38
+ * path. Strict receipt consumers should keep using the validating reader.
39
+ */
40
+ export declare function latestUltragoalLedgerEventFromText(text: string): UltragoalLedgerEventLite | undefined;
41
+ /** Minimal projection of a ralplan `index.jsonl` row. */
42
+ export interface RalplanIndexRow {
43
+ stage: string;
44
+ stageN?: number;
45
+ }
46
+ /** Parse a single ralplan index JSONL line; undefined for blank/malformed lines. */
47
+ export declare function parseRalplanIndexLine(line: string): RalplanIndexRow | undefined;
48
+ export interface RalplanIndexSummary {
49
+ /** Number of consensus iterations (planner/revision boundaries), >= 0. */
50
+ iteration: number;
51
+ /** Stage names present in the current (latest) iteration, in append order. */
52
+ currentStages: string[];
53
+ }
54
+ /**
55
+ * Derive iteration count and current-iteration stage presence from index rows.
56
+ *
57
+ * `stage_n` is NOT used as the iteration key: it is stored verbatim per row and a
58
+ * single planner/architect/critic pass can span multiple stage_n values. Instead,
59
+ * a `planner` or `revision` row opens a new iteration and subsequent rows attach
60
+ * to it. No verdict is derived here (index rows carry none).
61
+ */
62
+ export declare function summarizeRalplanIndex(rows: readonly RalplanIndexRow[]): RalplanIndexSummary;
63
+ /**
64
+ * Compact, theme-free presence string for the ralplan `stages` chip, e.g.
65
+ * `P·A·C`. Collapses past `cap` with a "… N more" suffix. Returns undefined when
66
+ * there are no stages.
67
+ */
68
+ export declare function formatRalplanStagePresence(stages: readonly string[], cap?: number): string | undefined;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * GC adapter for team workers (`.gjc/state/team/<name>/workers/<id>/` heartbeat
3
+ * + lifecycle). Liveness-only: numeric PID status dominates lifecycle/heartbeat
4
+ * signals.
5
+ */
6
+ import type { GcStoreAdapter } from "./gc-runtime";
7
+ export declare const teamWorkersGcAdapter: GcStoreAdapter;
@@ -1,4 +1,5 @@
1
1
  import type { WorkflowHudSummary } from "../skill-state/active-state";
2
+ import type { GcPidProbe, GcRecord } from "./gc-runtime";
2
3
  export type GjcTeamPhase = "starting" | "running" | "awaiting_integration" | "complete" | "failed" | "cancelled";
3
4
  export type GjcTeamTaskStatus = "pending" | "blocked" | "in_progress" | "completed" | "failed";
4
5
  export type GjcWorkerStatusState = "idle" | "working" | "blocked" | "done" | "failed" | "draining" | "unknown";
@@ -272,10 +273,13 @@ export interface GjcWorkerIntegrationAttemptRequestResult {
272
273
  }
273
274
  export declare const GJC_TEAM_API_OPERATIONS: readonly ["send-message", "broadcast", "mailbox-list", "mailbox-mark-delivered", "mailbox-mark-notified", "notification-list", "notification-read", "notification-replay", "notification-mark-pane-attempt", "worker-startup-ack", "create-task", "read-task", "list-tasks", "update-task", "claim-task", "transition-task-status", "transition-task", "release-task-claim", "read-config", "read-manifest", "read-worker-status", "update-worker-status", "read-worker-heartbeat", "recover-stale-claims", "update-worker-heartbeat", "write-worker-inbox", "write-worker-identity", "append-event", "read-events", "read-traces", "await-event", "write-shutdown-request", "read-shutdown-ack", "read-monitor-snapshot", "write-monitor-snapshot", "read-task-approval", "write-task-approval"];
274
275
  export declare function resolveGjcTeamStateRoot(cwd?: string, env?: NodeJS.ProcessEnv): string;
276
+ /** @internal */
277
+ export declare function listTeamWorkerGcRecords(teamRoot: string, probe: GcPidProbe): Promise<GcRecord[]>;
278
+ /** @internal */
279
+ export declare function pruneTeamWorkerGcRecord(record: GcRecord, probe: GcPidProbe): Promise<boolean>;
275
280
  export declare function persistGjcTeamModeStateSummary(snapshot: GjcTeamSnapshot, cwd?: string): Promise<void>;
276
281
  export declare function recoverGjcTeamStaleClaims(teamName: string, cwd?: string, env?: NodeJS.ProcessEnv): Promise<GjcTeamLivenessRecoveryResult>;
277
282
  type GjcTeamTaskMetadataInput = Partial<Pick<GjcTeamTask, "owner" | "lane" | "required_role" | "allowed_roles" | "depends_on" | "blocked_by">>;
278
- export declare function resolveGjcTmuxCommand(env?: NodeJS.ProcessEnv): string;
279
283
  export declare function resolveGjcWorkerCommand(cwd?: string, env?: NodeJS.ProcessEnv): string;
280
284
  export type GjcWorkerCheckpointClassification = {
281
285
  kind: "clean";
@@ -21,6 +21,20 @@ export interface TmuxCommandResult {
21
21
  export type TmuxCommandRunner = (args: string[]) => TmuxCommandResult;
22
22
  export declare function envDisabled(value: string | undefined): boolean;
23
23
  export declare function resolveGjcTmuxCommand(env?: NodeJS.ProcessEnv): string;
24
+ /**
25
+ * Build the exact-session target for tmux *option* commands
26
+ * (`show-options` / `set-option`) and `display-message -t`.
27
+ *
28
+ * Session-scoped commands such as `kill-session` / `attach-session` resolve a
29
+ * bare exact target (`=NAME`), but tmux 3.6a refuses to resolve a bare `=NAME`
30
+ * for option/display commands. Appending the empty window separator (`=NAME:`)
31
+ * keeps the exact-session match while giving tmux the window-qualified target
32
+ * those commands require. See gajae-code#580.
33
+ */
34
+ export declare function buildGjcTmuxExactOptionTarget(sessionName: string): string;
35
+ export declare const GJC_TMUX_UNTAGGED_REASON = "gjc_tmux_session_untagged";
36
+ export declare function buildGjcTmuxUntaggedSessionHint(tmuxCommand: string): string;
37
+ export declare function buildGjcTmuxUntaggedSessionError(sessionName: string, tmuxCommand: string): string;
24
38
  export declare function sanitizeTmuxToken(value: string): string;
25
39
  export declare function buildGjcTmuxSessionSlug(value: string): string;
26
40
  export declare function buildGjcTmuxSessionName(env?: NodeJS.ProcessEnv, context?: {
@@ -0,0 +1,7 @@
1
+ /**
2
+ * GC adapter for gjc-tagged tmux sessions. Stale iff `@gjc-project` path is gone
3
+ * OR `@gjc-branch` has no live git worktree. Removal is a spec-authorized
4
+ * destructive `kill-session`, gated by exact-target re-read + revalidation.
5
+ */
6
+ import type { GcStoreAdapter } from "./gc-runtime";
7
+ export declare const tmuxSessionsGcAdapter: GcStoreAdapter;
@@ -9,9 +9,22 @@ export interface GjcTmuxSessionStatus {
9
9
  branchSlug?: string;
10
10
  project?: string;
11
11
  }
12
+ export interface GjcTmuxSessionTagsForGc {
13
+ profile?: string;
14
+ project?: string;
15
+ branch?: string;
16
+ }
17
+ export interface GjcTmuxSessionsForGc {
18
+ tagged: GjcTmuxSessionStatus[];
19
+ untagged: string[];
20
+ }
12
21
  export declare function listGjcTmuxSessions(env?: NodeJS.ProcessEnv): GjcTmuxSessionStatus[];
22
+ /** @internal */
23
+ export declare function listTmuxSessionsForGc(env?: NodeJS.ProcessEnv): GjcTmuxSessionsForGc;
13
24
  export declare function findGjcTmuxSessionByBranch(branch: string, env?: NodeJS.ProcessEnv, project?: string | null): GjcTmuxSessionStatus | undefined;
14
25
  export declare function statusGjcTmuxSession(sessionName: string, env?: NodeJS.ProcessEnv): GjcTmuxSessionStatus;
15
26
  export declare function createGjcTmuxSession(env?: NodeJS.ProcessEnv): GjcTmuxSessionStatus;
27
+ /** @internal */
28
+ export declare function readTmuxSessionTagsForGc(sessionName: string, env?: NodeJS.ProcessEnv): GjcTmuxSessionTagsForGc;
16
29
  export declare function removeGjcTmuxSession(sessionName: string, env?: NodeJS.ProcessEnv): GjcTmuxSessionStatus;
17
30
  export declare function attachGjcTmuxSession(sessionName: string, env?: NodeJS.ProcessEnv): never;
@@ -0,0 +1,3 @@
1
+ import { type GcStoreAdapter } from "../gjc-runtime/gc-runtime";
2
+ export declare const harnessLeasesGcAdapter: GcStoreAdapter;
3
+ export declare const registryEntriesGcAdapter: GcStoreAdapter;
@@ -11,7 +11,7 @@
11
11
  * Stateless `gjc harness` CLI calls reach the owner via {@link resolveOwner} + the endpoint.
12
12
  */
13
13
  import { type FinalizeChecks, type ValidationCommandSpec } from "./finalize";
14
- import type { HarnessRpc } from "./rpc-adapter";
14
+ import { type HarnessRpc } from "./rpc-adapter";
15
15
  import { type SessionLease } from "./session-lease";
16
16
  export interface OwnerOptions {
17
17
  root: string;
@@ -44,3 +44,10 @@ export interface ResolvedOwner {
44
44
  }
45
45
  /** Determine whether a live owner currently holds the session (for CLI routing). */
46
46
  export declare function resolveOwner(root: string, sessionId: string): Promise<ResolvedOwner>;
47
+ /**
48
+ * Owner liveness for verbs that do not route to the owner (e.g. `classify`): a routable owner
49
+ * has a live lease and a socket endpoint. This is the same lease/socket probe `observe` uses to
50
+ * decide routing, so non-routing verbs derive `ownerLive` consistently instead of assuming the
51
+ * owner is gone (which would misclassify a live owner as vanished/restart-clean).
52
+ */
53
+ export declare function resolveOwnerLive(root: string, sessionId: string): Promise<boolean>;
@@ -0,0 +1,19 @@
1
+ import type { ReceiptEnvelope } from "./receipts";
2
+ export declare const RECEIPT_SPOOL_DIR_ENV = "GJC_RECEIPT_SPOOL_DIR";
3
+ export declare const RECEIPT_SPOOL_FILENAME = "spool.jsonl";
4
+ export declare const RECEIPT_SPOOL_CURSOR_WIDTH = 12;
5
+ export interface ReceiptSpoolRecord {
6
+ cursor: string;
7
+ envelope: ReceiptEnvelope<unknown>;
8
+ }
9
+ export interface ReceiptSpoolAppendResult {
10
+ cursor: string;
11
+ path: string;
12
+ }
13
+ export declare function withReceiptSpoolDir<T>(spoolDir: string, fn: () => Promise<T>): Promise<T>;
14
+ export declare function resolveReceiptSpoolDir(env?: NodeJS.ProcessEnv): string | undefined;
15
+ export declare function receiptSpoolPath(spoolDir: string): string;
16
+ export declare function formatReceiptSpoolCursor(cursor: bigint): string;
17
+ export declare function readHighestReceiptSpoolCursor(spoolDir: string): Promise<bigint>;
18
+ export declare function appendReceiptToSpool(spoolDir: string, envelope: ReceiptEnvelope<unknown>): Promise<ReceiptSpoolAppendResult>;
19
+ export declare function appendReceiptToConfiguredSpool(envelope: ReceiptEnvelope<unknown>, env?: NodeJS.ProcessEnv): Promise<ReceiptSpoolAppendResult | undefined>;
@@ -9,11 +9,16 @@ import type { HarnessLifecycle, NextAllowedAction, PrimitiveResponse, SessionSta
9
9
  export declare function isTerminal(lifecycle: HarnessLifecycle): boolean;
10
10
  export declare function canTransition(from: HarnessLifecycle, to: HarnessLifecycle): boolean;
11
11
  export declare function assertTransition(from: HarnessLifecycle, to: HarnessLifecycle): void;
12
+ export interface NextAllowedActionsOptions {
13
+ /** Additional live-owner/RPC readiness gate for submit, e.g. rpc-not-idle. */
14
+ submitUnavailableReason?: string | null;
15
+ }
16
+ export declare function submitUnavailableReason(lifecycle: HarnessLifecycle, ownerLive: boolean, gateReason?: string | null): string | null;
12
17
  /**
13
18
  * Derive the permitted next actions for a session given its lifecycle and whether
14
19
  * a live owner currently holds the lease.
15
20
  */
16
- export declare function nextAllowedActions(lifecycle: HarnessLifecycle, ownerLive: boolean): NextAllowedAction[];
21
+ export declare function nextAllowedActions(lifecycle: HarnessLifecycle, ownerLive: boolean, options?: NextAllowedActionsOptions): NextAllowedAction[];
17
22
  export declare function buildStateView(state: SessionState, ownerLive: boolean): SessionStateView;
18
23
  /** Build the universal contract response carried by every primitive. */
19
24
  export declare function buildResponse<E extends Record<string, unknown>>(state: SessionState, ownerLive: boolean, evidence: E, ok?: boolean): PrimitiveResponse<E>;
@@ -1,8 +1,28 @@
1
1
  import type { EventEnvelope, ReceiptFamily, SessionState } from "./types";
2
+ interface HarnessRootRegistryEntry {
3
+ root: string;
4
+ updatedAt: string;
5
+ }
6
+ export interface HarnessRootRegistryForGc {
7
+ sessionId: string;
8
+ roots: HarnessRootRegistryEntry[];
9
+ }
10
+ export interface HarnessRootRegistryListingForGc {
11
+ sessionId: string;
12
+ file: string;
13
+ roots: HarnessRootRegistryEntry[];
14
+ error?: string;
15
+ }
2
16
  interface ResolveHarnessSessionRootOptions {
3
17
  expectedWorkspace?: string;
4
18
  }
5
19
  export declare function canonicalWorkspacePath(workspace: string): string;
20
+ /** @internal */
21
+ export declare function listHarnessRootRegistriesForGc(env?: NodeJS.ProcessEnv): Promise<HarnessRootRegistryListingForGc[]>;
22
+ /** @internal */
23
+ export declare function rewriteHarnessRootRegistryForGc(file: string, registry: HarnessRootRegistryForGc): Promise<void>;
24
+ /** @internal */
25
+ export declare function removeHarnessRootRegistryFileForGc(file: string): Promise<void>;
6
26
  export declare const MAX_UNIX_SOCKET_PATH_BYTES = 100;
7
27
  export declare function controlSocketPath(root: string, sessionId: string, env?: NodeJS.ProcessEnv): string;
8
28
  export declare class StorageError extends Error {
@@ -138,6 +138,10 @@ export interface Observation {
138
138
  rpcLive?: boolean;
139
139
  /** ISO timestamp of the most recent RPC frame the owner observed, if any. */
140
140
  rpcLastFrameAt?: string | null;
141
+ /** True only when owner/rpc/lifecycle gates indicate a prompt can be submitted now. */
142
+ readyForSubmit?: boolean;
143
+ /** Present when readyForSubmit is false; mirrors submit's nextAllowedActions reason. */
144
+ submitUnavailableReason?: string | null;
141
145
  }
142
146
  /** Input to the deterministic recovery classifier. */
143
147
  export interface ClassifyInput {
@@ -108,12 +108,12 @@ export declare function summarizeMentalModel(model: MentalModelSummary): string;
108
108
  * snapshot only; the diff is computed locally for display purposes.
109
109
  *
110
110
  * This is intentionally minimal — for "what changed" at a glance, not for a
111
- * full structural diff. Each side is capped at `MAX_LCS_LINES` lines BEFORE
112
- * the O(n*m) LCS table is built so a long curated model can never hang the
113
- * TUI; output is then capped at `maxLines` so the rendered diff stays
114
- * readable. The cap is signalled inline.
111
+ * full structural diff. Each side is capped at `MAX_LCS_LINES` lines before
112
+ * the Hunt-Szymanski LCS pass so a long curated model can never hang the TUI;
113
+ * output is then capped at `maxLines` so the rendered diff stays readable. The
114
+ * cap is signalled inline.
115
115
  */
116
- /** Hard cap on input line count per side before LCS. Keeps the O(n*m) table tractable. */
116
+ /** Hard cap on input line count per side before LCS. Keeps worst-case repeated-line matching bounded. */
117
117
  export declare const MAX_LCS_LINES = 1000;
118
118
  export declare function diffMentalModelContent(previous: string | null, current: string, maxLines?: number): string;
119
119
  /** Awaited only by the first-turn race in `beforeAgentStartPrompt`. */