@gajae-code/coding-agent 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +1 -1
  3. package/dist/types/async/job-manager.d.ts +26 -0
  4. package/dist/types/cli/args.d.ts +1 -0
  5. package/dist/types/cli/list-models.d.ts +6 -0
  6. package/dist/types/cli/setup-cli.d.ts +8 -1
  7. package/dist/types/commands/gc.d.ts +26 -0
  8. package/dist/types/commands/setup.d.ts +7 -0
  9. package/dist/types/config/file-lock-gc.d.ts +5 -0
  10. package/dist/types/config/file-lock.d.ts +29 -0
  11. package/dist/types/config/model-registry.d.ts +4 -0
  12. package/dist/types/config/models-config-schema.d.ts +5 -0
  13. package/dist/types/config/settings-schema.d.ts +62 -0
  14. package/dist/types/coordinator/contract.d.ts +1 -1
  15. package/dist/types/defaults/gjc/extensions/grok-build/index.d.ts +1 -0
  16. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/index.d.ts +1 -0
  17. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.d.ts +25 -0
  18. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/payload/sanitize.d.ts +27 -0
  19. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/billing.d.ts +8 -0
  20. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.d.ts +5 -0
  21. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.d.ts +10 -0
  22. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/usage.d.ts +2 -0
  23. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/shared/base-url.d.ts +2 -0
  24. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/shared/errors.d.ts +38 -0
  25. package/dist/types/defaults/gjc-grok-cli.d.ts +5 -0
  26. package/dist/types/extensibility/extensions/index.d.ts +1 -0
  27. package/dist/types/extensibility/extensions/prefix-command-bridge.d.ts +35 -0
  28. package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +103 -0
  29. package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +2 -0
  30. package/dist/types/gjc-runtime/deep-interview-state.d.ts +112 -0
  31. package/dist/types/gjc-runtime/gc-render.d.ts +6 -0
  32. package/dist/types/gjc-runtime/gc-runtime.d.ts +134 -0
  33. package/dist/types/gjc-runtime/ledger-event-renderer.d.ts +68 -0
  34. package/dist/types/gjc-runtime/state-writer.d.ts +64 -2
  35. package/dist/types/gjc-runtime/team-gc.d.ts +7 -0
  36. package/dist/types/gjc-runtime/team-runtime.d.ts +5 -0
  37. package/dist/types/gjc-runtime/tmux-common.d.ts +11 -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/gjc-runtime/ultragoal-guard.d.ts +10 -0
  41. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +29 -0
  42. package/dist/types/harness-control-plane/gc-adapter.d.ts +3 -0
  43. package/dist/types/harness-control-plane/owner.d.ts +7 -0
  44. package/dist/types/harness-control-plane/storage.d.ts +20 -0
  45. package/dist/types/modes/components/hook-selector.d.ts +7 -1
  46. package/dist/types/modes/components/provider-onboarding-selector.d.ts +1 -1
  47. package/dist/types/modes/controllers/command-controller.d.ts +1 -0
  48. package/dist/types/modes/interactive-mode.d.ts +1 -1
  49. package/dist/types/modes/rpc/rpc-mode.d.ts +72 -2
  50. package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +13 -0
  51. package/dist/types/modes/shared/agent-wire/session-registry.d.ts +25 -0
  52. package/dist/types/modes/shared/agent-wire/unattended-action-policy.d.ts +2 -0
  53. package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +10 -0
  54. package/dist/types/modes/theme/defaults/index.d.ts +302 -0
  55. package/dist/types/modes/theme/theme.d.ts +1 -0
  56. package/dist/types/modes/types.d.ts +1 -1
  57. package/dist/types/session/agent-session.d.ts +1 -1
  58. package/dist/types/session/blob-store.d.ts +39 -3
  59. package/dist/types/session/history-storage.d.ts +2 -2
  60. package/dist/types/session/session-manager.d.ts +10 -1
  61. package/dist/types/setup/credential-import.d.ts +79 -0
  62. package/dist/types/skill-state/workflow-hud.d.ts +14 -0
  63. package/dist/types/task/executor.d.ts +1 -0
  64. package/dist/types/task/render.d.ts +1 -1
  65. package/dist/types/tools/ask.d.ts +15 -1
  66. package/dist/types/tools/subagent-render.d.ts +7 -1
  67. package/dist/types/tools/subagent.d.ts +27 -0
  68. package/dist/types/tools/ultragoal-ask-guard.d.ts +5 -0
  69. package/dist/types/web/search/index.d.ts +4 -4
  70. package/dist/types/web/search/provider.d.ts +16 -20
  71. package/dist/types/web/search/providers/base.d.ts +2 -1
  72. package/dist/types/web/search/providers/openai-compatible.d.ts +9 -0
  73. package/dist/types/web/search/types.d.ts +14 -2
  74. package/package.json +7 -7
  75. package/scripts/build-binary.ts +7 -0
  76. package/src/async/job-manager.ts +52 -0
  77. package/src/cli/args.ts +5 -0
  78. package/src/cli/auth-broker-cli.ts +1 -0
  79. package/src/cli/fast-help.ts +2 -0
  80. package/src/cli/list-models.ts +13 -1
  81. package/src/cli/setup-cli.ts +138 -3
  82. package/src/cli.ts +1 -0
  83. package/src/commands/gc.ts +22 -0
  84. package/src/commands/harness.ts +7 -3
  85. package/src/commands/setup.ts +5 -1
  86. package/src/commands/ultragoal.ts +3 -1
  87. package/src/config/file-lock-gc.ts +193 -0
  88. package/src/config/file-lock.ts +66 -10
  89. package/src/config/model-profile-activation.ts +15 -3
  90. package/src/config/model-profiles.ts +39 -30
  91. package/src/config/model-registry.ts +21 -1
  92. package/src/config/models-config-schema.ts +1 -0
  93. package/src/config/settings-schema.ts +62 -0
  94. package/src/coordinator/contract.ts +1 -0
  95. package/src/coordinator-mcp/server.ts +459 -3
  96. package/src/defaults/gjc/agent.models.grok-cli.yml +36 -0
  97. package/src/defaults/gjc/extensions/grok-build/index.ts +1 -0
  98. package/src/defaults/gjc/extensions/grok-build/package.json +7 -0
  99. package/src/defaults/gjc/extensions/grok-cli-vendor/biome.json +39 -0
  100. package/src/defaults/gjc/extensions/grok-cli-vendor/package.json +8 -0
  101. package/src/defaults/gjc/extensions/grok-cli-vendor/src/index.ts +1 -0
  102. package/src/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.ts +155 -0
  103. package/src/defaults/gjc/extensions/grok-cli-vendor/src/payload/sanitize.ts +361 -0
  104. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/billing.ts +57 -0
  105. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +99 -0
  106. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.ts +50 -0
  107. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/usage.ts +56 -0
  108. package/src/defaults/gjc/extensions/grok-cli-vendor/src/shared/base-url.ts +36 -0
  109. package/src/defaults/gjc/extensions/grok-cli-vendor/src/shared/errors.ts +44 -0
  110. package/src/defaults/gjc/skills/deep-interview/SKILL.md +131 -113
  111. package/src/defaults/gjc/skills/deep-interview/lateral-review-panel.md +49 -0
  112. package/src/defaults/gjc/skills/ultragoal/SKILL.md +30 -8
  113. package/src/defaults/gjc-defaults.ts +7 -0
  114. package/src/defaults/gjc-grok-cli.ts +22 -0
  115. package/src/extensibility/extensions/index.ts +1 -0
  116. package/src/extensibility/extensions/prefix-command-bridge.ts +128 -0
  117. package/src/gjc-runtime/deep-interview-recorder.ts +457 -0
  118. package/src/gjc-runtime/deep-interview-runtime.ts +18 -26
  119. package/src/gjc-runtime/deep-interview-state.ts +324 -0
  120. package/src/gjc-runtime/gc-render.ts +70 -0
  121. package/src/gjc-runtime/gc-runtime.ts +403 -0
  122. package/src/gjc-runtime/launch-tmux.ts +3 -4
  123. package/src/gjc-runtime/ledger-event-renderer.ts +164 -0
  124. package/src/gjc-runtime/ralplan-runtime.ts +232 -19
  125. package/src/gjc-runtime/state-renderer.ts +12 -3
  126. package/src/gjc-runtime/state-runtime.ts +48 -30
  127. package/src/gjc-runtime/state-writer.ts +254 -7
  128. package/src/gjc-runtime/team-gc.ts +49 -0
  129. package/src/gjc-runtime/team-runtime.ts +179 -2
  130. package/src/gjc-runtime/tmux-common.ts +14 -0
  131. package/src/gjc-runtime/tmux-gc.ts +177 -0
  132. package/src/gjc-runtime/tmux-sessions.ts +49 -1
  133. package/src/gjc-runtime/ultragoal-guard.ts +155 -0
  134. package/src/gjc-runtime/ultragoal-runtime.ts +1239 -31
  135. package/src/gjc-runtime/workflow-manifest.generated.json +44 -0
  136. package/src/gjc-runtime/workflow-manifest.ts +12 -0
  137. package/src/harness-control-plane/gc-adapter.ts +184 -0
  138. package/src/harness-control-plane/owner.ts +14 -2
  139. package/src/harness-control-plane/rpc-adapter.ts +1 -1
  140. package/src/harness-control-plane/storage.ts +70 -0
  141. package/src/hooks/skill-state.ts +121 -2
  142. package/src/internal-urls/docs-index.generated.ts +22 -12
  143. package/src/lsp/defaults.json +1 -0
  144. package/src/main.ts +18 -3
  145. package/src/modes/acp/acp-agent.ts +4 -2
  146. package/src/modes/bridge/bridge-mode.ts +2 -1
  147. package/src/modes/components/history-search.ts +5 -2
  148. package/src/modes/components/hook-selector.ts +19 -0
  149. package/src/modes/components/model-selector.ts +51 -8
  150. package/src/modes/components/provider-onboarding-selector.ts +6 -1
  151. package/src/modes/components/status-line/segments.ts +1 -1
  152. package/src/modes/controllers/command-controller.ts +25 -6
  153. package/src/modes/controllers/extension-ui-controller.ts +3 -0
  154. package/src/modes/controllers/selector-controller.ts +81 -1
  155. package/src/modes/interactive-mode.ts +11 -1
  156. package/src/modes/rpc/rpc-mode.ts +266 -34
  157. package/src/modes/shared/agent-wire/command-dispatch.ts +281 -261
  158. package/src/modes/shared/agent-wire/deep-interview-gate.ts +30 -1
  159. package/src/modes/shared/agent-wire/host-tool-bridge.ts +3 -0
  160. package/src/modes/shared/agent-wire/session-registry.ts +109 -0
  161. package/src/modes/shared/agent-wire/unattended-action-policy.ts +24 -0
  162. package/src/modes/shared/agent-wire/unattended-run-controller.ts +23 -3
  163. package/src/modes/shared/agent-wire/unattended-session.ts +32 -2
  164. package/src/modes/theme/defaults/claude-code.json +100 -0
  165. package/src/modes/theme/defaults/codex.json +100 -0
  166. package/src/modes/theme/defaults/index.ts +6 -0
  167. package/src/modes/theme/defaults/opencode.json +102 -0
  168. package/src/modes/theme/theme.ts +2 -2
  169. package/src/modes/types.ts +1 -1
  170. package/src/prompts/agents/executor.md +5 -2
  171. package/src/sdk.ts +29 -4
  172. package/src/session/agent-session.ts +99 -19
  173. package/src/session/blob-store.ts +59 -3
  174. package/src/session/history-storage.ts +32 -11
  175. package/src/session/session-manager.ts +72 -20
  176. package/src/setup/credential-import.ts +429 -0
  177. package/src/setup/hermes/templates/operator-instructions.v1.md +7 -1
  178. package/src/skill-state/deep-interview-mutation-guard.ts +2 -1
  179. package/src/skill-state/workflow-hud.ts +106 -10
  180. package/src/slash-commands/builtin-registry.ts +3 -2
  181. package/src/task/executor.ts +16 -1
  182. package/src/task/render.ts +18 -7
  183. package/src/tools/ask.ts +59 -2
  184. package/src/tools/cron.ts +1 -1
  185. package/src/tools/job.ts +3 -2
  186. package/src/tools/monitor.ts +36 -1
  187. package/src/tools/subagent-render.ts +128 -29
  188. package/src/tools/subagent.ts +173 -9
  189. package/src/tools/ultragoal-ask-guard.ts +39 -0
  190. package/src/web/search/index.ts +25 -25
  191. package/src/web/search/provider.ts +178 -87
  192. package/src/web/search/providers/base.ts +2 -1
  193. package/src/web/search/providers/openai-compatible.ts +151 -0
  194. package/src/web/search/types.ts +47 -22
@@ -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;
@@ -1,4 +1,5 @@
1
- import * as fs from "node:fs/promises";
1
+ import type { Stats } from "node:fs";
2
+ import { type FileLockOptions } from "../config/file-lock";
2
3
  import type { SkillActiveEntry } from "../skill-state/active-state";
3
4
  import { type AuditEntry, type CanonicalGjcWorkflowSkill, type WorkflowStateMutationOwner } from "../skill-state/workflow-state-contract";
4
5
  /**
@@ -55,6 +56,12 @@ export interface StateWriterOptions {
55
56
  cwd?: string;
56
57
  receipt?: StateWriterReceiptContext;
57
58
  audit?: StateWriterAuditContext;
59
+ /**
60
+ * Cross-process lock tuning for read-modify-write paths that route through
61
+ * `withWorkflowStateLock` / `updateJsonAtomic`. Omit for the hardened
62
+ * `withFileLock` defaults.
63
+ */
64
+ lock?: FileLockOptions;
58
65
  }
59
66
  export interface DeleteIfOwnedOptions extends StateWriterOptions {
60
67
  predicate?: (current: unknown) => boolean | Promise<boolean>;
@@ -81,7 +88,7 @@ export interface GenericHardPruneTarget {
81
88
  export interface GenericHardPruneSelectorContext {
82
89
  path: string;
83
90
  category: WriterCategory | string;
84
- stat: Awaited<ReturnType<typeof fs.stat>>;
91
+ stat: Stats;
85
92
  readJson: () => Promise<unknown>;
86
93
  }
87
94
  export type GenericHardPruneSelector = (context: GenericHardPruneSelectorContext) => boolean | Promise<boolean>;
@@ -109,8 +116,63 @@ export declare function detectWorkflowEnvelopeIntegrityMismatch(filePath: string
109
116
  export declare function writeJsonAtomic(targetPath: string, value: unknown, options?: StateWriterOptions): Promise<string>;
110
117
  export declare function writeWorkflowEnvelopeAtomic(targetPath: string, value: unknown, options?: StateWriterOptions): Promise<string>;
111
118
  export declare function writeTextAtomic(targetPath: string, text: string, options?: StateWriterOptions): Promise<string>;
119
+ /**
120
+ * Serialize a read-modify-write (or any multi-step mutation) against concurrent
121
+ * writers of the same `.gjc/**` target. Uses the cross-process directory lock
122
+ * from `withFileLock`, keyed on the resolved file path, so separate CLI/agent
123
+ * processes (e.g. team-mode workers) cannot interleave one writer's read with
124
+ * another writer's write and silently drop the first mutation (issue #646).
125
+ *
126
+ * The lock is advisory: it only protects callers that route through it, so every
127
+ * read-modify-write of a given file MUST acquire this lock for the same resolved
128
+ * path. `atomicWrite`'s temp-file + rename crash-atomicity is preserved; this
129
+ * layers concurrency-atomicity on top without weakening it.
130
+ */
131
+ export declare function withWorkflowStateLock<T>(targetPath: string, fn: () => Promise<T>, options?: StateWriterOptions): Promise<T>;
112
132
  export declare function updateJsonAtomic<T = unknown>(targetPath: string, mutator: (current: T | undefined) => T | Promise<T>, options?: StateWriterOptions): Promise<string>;
113
133
  export declare function appendJsonl(targetPath: string, entry: unknown, options?: StateWriterOptions): Promise<string>;
134
+ export interface AppendJsonlIdempotentOptions extends StateWriterOptions {
135
+ /**
136
+ * Identity key for an entry. Two entries that produce the same non-`undefined`
137
+ * key are duplicates, so only the first is appended. Return `undefined` to opt a
138
+ * candidate out of dedup (it is always appended). Use `key` for the common case
139
+ * where identity reduces to a single string.
140
+ */
141
+ key?: (entry: unknown) => string | undefined;
142
+ /**
143
+ * Equivalence predicate: return `true` when `existing` already represents
144
+ * `candidate`, suppressing the append. Use when identity cannot be reduced to a
145
+ * single string key. When both `key` and `equals` are supplied, `equals` wins.
146
+ */
147
+ equals?: (candidate: unknown, existing: unknown) => boolean;
148
+ }
149
+ export interface AppendJsonlIdempotentResult {
150
+ path: string;
151
+ /** `true` when the entry was written; `false` when an equivalent entry already existed. */
152
+ appended: boolean;
153
+ /** The pre-existing entry that suppressed the append, when `appended` is `false`. */
154
+ duplicate?: unknown;
155
+ }
156
+ /**
157
+ * Append `entry` to a JSONL file only when no equivalent entry already exists —
158
+ * the shared idempotent append primitive (issue #660).
159
+ *
160
+ * `appendJsonl` is a pure append with no dedup, so every recurring "duplicate
161
+ * ledger row" bug (#638, #643, #645) had to be patched with bespoke per-call-site
162
+ * guards. This primitive centralizes the read-check-append cycle: a caller
163
+ * declares identity once via `key` or `equals` instead of re-deriving the lookup
164
+ * at each site.
165
+ *
166
+ * The read-then-append is serialized through the same cross-process workflow lock
167
+ * as `updateJsonAtomic`, so two concurrent idempotent appends cannot both observe
168
+ * "no duplicate" and both write (the #646 TOCTOU that a plain `appendJsonl`
169
+ * preceded by a manual existence check is still exposed to).
170
+ *
171
+ * Scope note: this dedups the *append* only. Call sites whose idempotency must
172
+ * also skip a coupled mutation — e.g. the plan/state rewrite in #643/#645 — still
173
+ * need a whole-operation guard; this primitive is the ledger-level half of that.
174
+ */
175
+ export declare function appendJsonlIdempotent(targetPath: string, entry: unknown, options: AppendJsonlIdempotentOptions): Promise<AppendJsonlIdempotentResult>;
114
176
  export declare function appendText(targetPath: string, text: string, options?: StateWriterOptions): Promise<string>;
115
177
  export declare function createJsonNoClobber(targetPath: string, value: unknown, options?: StateWriterOptions): Promise<string>;
116
178
  export declare function deleteIfOwned(targetPath: string, predicateOrOptions?: ((current: unknown) => boolean | Promise<boolean>) | DeleteIfOwnedOptions): Promise<DeleteResult>;
@@ -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,6 +273,10 @@ 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">>;
@@ -21,6 +21,17 @@ 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;
24
35
  export declare const GJC_TMUX_UNTAGGED_REASON = "gjc_tmux_session_untagged";
25
36
  export declare function buildGjcTmuxUntaggedSessionHint(tmuxCommand: string): string;
26
37
  export declare function buildGjcTmuxUntaggedSessionError(sessionName: string, tmuxCommand: string): string;
@@ -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;
@@ -5,6 +5,15 @@ export interface UltragoalGuardDiagnostic {
5
5
  message: string;
6
6
  goalId?: string;
7
7
  }
8
+ export interface UltragoalAskBlockDiagnostic {
9
+ active: boolean;
10
+ reason: string;
11
+ source: "absent" | "durable_state" | "durable_state_unreadable" | "ledger" | "goals_json";
12
+ goalsPath?: string;
13
+ ledgerPath?: string;
14
+ goalIds?: string[];
15
+ message: string;
16
+ }
8
17
  export interface CurrentGoalLike {
9
18
  objective: string;
10
19
  status?: string;
@@ -19,6 +28,7 @@ export declare function readUltragoalVerificationState(input: {
19
28
  cwd: string;
20
29
  currentGoal?: CurrentGoalLike | null;
21
30
  }): Promise<UltragoalGuardDiagnostic>;
31
+ export declare function isUltragoalAskBlocked(cwd: string): Promise<UltragoalAskBlockDiagnostic>;
22
32
  export declare function assertCanCompleteCurrentGoal(input: {
23
33
  cwd: string;
24
34
  currentGoal?: CurrentGoalLike | null;