@gajae-code/coding-agent 0.12.1 → 0.12.4

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 (66) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/types/config/settings-schema.d.ts +2 -2
  3. package/dist/types/gjc-runtime/ralplan-review-conflicts.d.ts +114 -0
  4. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +5 -3
  5. package/dist/types/gjc-runtime/ultragoal-evidence.d.ts +5 -1
  6. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +8 -2
  7. package/dist/types/modes/components/irc-sidebar.d.ts +16 -0
  8. package/dist/types/modes/controllers/command-controller.d.ts +1 -1
  9. package/dist/types/modes/controllers/event-controller.d.ts +1 -1
  10. package/dist/types/modes/controllers/extension-ui-controller.d.ts +2 -1
  11. package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
  12. package/dist/types/modes/interactive-mode.d.ts +9 -0
  13. package/dist/types/modes/theme/theme.d.ts +3 -1
  14. package/dist/types/modes/types.d.ts +32 -0
  15. package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -1
  16. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +3 -2
  17. package/dist/types/session/agent-session.d.ts +2 -0
  18. package/dist/types/session/internal/managed-session-storage.d.ts +22 -3
  19. package/dist/types/task/index.d.ts +4 -0
  20. package/dist/types/task/output-manager.d.ts +1 -0
  21. package/dist/types/task/receipt.d.ts +2 -0
  22. package/dist/types/tools/index.d.ts +2 -0
  23. package/package.json +8 -8
  24. package/scripts/dogfood-ralplan-review-conflicts.ts +272 -0
  25. package/scripts/generate-sdk-operation-inventory.ts +2 -0
  26. package/scripts/run-acp-conformance.ts +5 -0
  27. package/src/async/job-manager.ts +22 -20
  28. package/src/config/provider-ranking.ts +1 -0
  29. package/src/config/settings-schema.ts +2 -2
  30. package/src/debug/index.ts +42 -17
  31. package/src/defaults/gjc/skills/ralplan/SKILL.md +9 -7
  32. package/src/defaults/gjc/skills/ultragoal/SKILL.md +22 -5
  33. package/src/defaults/gjc/skills/ultragoal/validation-batch-contracts.md +2 -1
  34. package/src/gjc-runtime/launch-worktree.ts +103 -1
  35. package/src/gjc-runtime/ledger-event-renderer.ts +1 -0
  36. package/src/gjc-runtime/ralplan-review-conflicts.ts +472 -0
  37. package/src/gjc-runtime/ralplan-runtime.ts +80 -10
  38. package/src/gjc-runtime/ultragoal-evidence.ts +110 -49
  39. package/src/gjc-runtime/ultragoal-runtime.ts +467 -22
  40. package/src/gjc-runtime/workflow-manifest.generated.json +29 -0
  41. package/src/gjc-runtime/workflow-manifest.ts +17 -2
  42. package/src/internal-urls/docs-index.generated.ts +2 -2
  43. package/src/modes/DESIGN.md +53 -0
  44. package/src/modes/components/irc-sidebar.ts +30 -1
  45. package/src/modes/components/tool-status-header.ts +0 -1
  46. package/src/modes/controllers/command-controller.ts +38 -34
  47. package/src/modes/controllers/event-controller.ts +27 -16
  48. package/src/modes/controllers/extension-ui-controller.ts +186 -34
  49. package/src/modes/controllers/selector-controller.ts +115 -22
  50. package/src/modes/interactive-mode.ts +189 -62
  51. package/src/modes/jobs-observer.ts +1 -1
  52. package/src/modes/theme/theme.ts +11 -9
  53. package/src/modes/types.ts +88 -0
  54. package/src/sdk/broker/lifecycle.ts +18 -13
  55. package/src/sdk/bus/chat-daemon-control.ts +4 -3
  56. package/src/sdk/bus/telegram-daemon-contract.ts +3 -2
  57. package/src/sdk/protocol/operation-inventory.generated.json +11 -0
  58. package/src/sdk/session.ts +1 -0
  59. package/src/session/agent-session.ts +22 -1
  60. package/src/session/internal/managed-session-scope.ts +158 -107
  61. package/src/session/internal/managed-session-storage.ts +445 -57
  62. package/src/session/session-storage.ts +29 -44
  63. package/src/task/index.ts +257 -31
  64. package/src/task/output-manager.ts +24 -14
  65. package/src/task/receipt.ts +11 -1
  66. package/src/tools/index.ts +2 -0
package/CHANGELOG.md CHANGED
@@ -1,4 +1,42 @@
1
1
  # Changelog
2
+
3
+ ## [Unreleased]
4
+
5
+ ## [0.12.4] - 2026-07-30
6
+
7
+ ## [0.12.3] - 2026-07-30
8
+
9
+ ### Fixed
10
+
11
+ - The release cut no longer deletes previously released changelog headings whose body is empty. `releasedChangelogContent` ran `removeEmptyVersionEntries` before transforming `## [Unreleased]`, so cutting a release replaced the prior empty semver heading instead of inserting the new one above it — v0.12.2 dropped the `## [0.12.1] - 2026-07-29` heading from four package changelogs. Released sections are now immutable history and are always preserved.
12
+
13
+ ## [0.12.2] - 2026-07-30
14
+
15
+ ### Added
16
+
17
+ - `gjc ultragoal quality-gate init` scaffolds a multi-surface quality-gate template (`--surface` repeatable, `--out` required) so agents can fill evidence once and use read-only `quality-gate validate` multi-error diagnostics instead of discovering missing fields one checkpoint at a time (#3474).
18
+ - Ralplan can persist a typed `disposition` stage (`ralplan.review_conflicts.v1`) when Architect and Critic prescribe incompatible actions on the same plan target. Writes fail closed until every conflict has an explicit disposition, and source receipts must resolve against the same-pass Architect/Critic rows in the run index (#2902).
19
+ - Published bounded, redacted, hash-bound sealed perf-corpus memory evidence and an output-free replay notebook. The authenticated analysis identifies sustained heap growth on the `agent-session` and `tui` surfaces while keeping RSS/native allocation and p95 claims explicitly out of scope.
20
+ - The interactive terminal’s responsive IRC/todo work-lane contract now covers exact narrow/wide geometry, requested versus effective IRC visibility, direct-root pin ordering, todo lane bounds, remapped IRC toggles, and live composer shortcut hints.
21
+ - Managed-session startup now preserves bounded Windows ACL and identity failure classifications in path-redacted recovery guidance without broadening permissions, elevation, or unsafe fallback.
22
+
23
+ ### Fixed
24
+
25
+ - Terminal input now normalizes Option/Meta navigation and psmux modified-Enter encodings through the native key parser, keeping legacy, Kitty CSI-u, and modifyOtherKeys behavior consistent.
26
+ - Ultragoal CLI replay no longer executes model-authored test source or trusts `replaySafe: true` as arbitrary command authority. Runtime replay is limited to the pinned Bun runtime for `--version` and literal `-e "console.log(...)"`; shells, interpreter code strings, path-qualified executables, tests, install/publish/network/git mutation commands, and arbitrary argv are rejected. Replay cwd/artifact files are realpath-confined, ambiguous rows fail closed, stdout and stderr are checked, and POSIX timeout cleanup signals the process group. Structured test-report fallback remains deliberately unsupported pending a separate trusted-provenance design (#3533).
27
+ - Ultragoal CLI replay evidence now reads the replay file referenced by an `executorQa.artifactRefs` entry whose `kind` is `cli-replay`. Inline, nested, and file-backed replay forms are disambiguated explicitly; mixed or malformed rows fail closed (#3533).
28
+
29
+ - Broker artifact cleanup no longer promotes a non-empty `cleanup_pending` quarantine to transcript-phase completion. The broker advances only when the retained quarantine is root-only/empty or when the lower layer returns `artifacts_removed`, so artifact bytes cannot vanish behind a success receipt (#3489).
30
+ - `gjc --worktree` / `gjc -w` launch no longer crashes with a raw uncaught `EEXIST` when the worktree bucket directory (`<repo>.gajae-code-worktrees`) is a broken symbolic link to unmounted or offloaded cold storage. The launch distinguishes dangling links and non-directory entries from valid directory symlinks or Windows junctions, reclassifies mkdir races, avoids disclosing raw link targets or unsafe shell commands, and never deletes or replaces an obstructing entry.
31
+ - POSIX parent identity reproof/fsync is now centralized before every promotable artifact-phase result, preventing a crash-window where a rename is lost after durable retirement is recorded (#3489).
32
+ - Artifact retirement, planned paths, retained authority, and transcript retry in both managed reconciliation and deletion now bind to the newest published `pendingEvidence` attempt, preventing stranded detached transcripts at paths absent from the newest receipt after a crash (#3489).
33
+ - Detached task receipts for in-memory parent sessions no longer advertise dead `agent://` output URIs. TaskTool allocates a session-lifetime durable artifact root under the process temp directory, persists child outputs there, authorizes parent and same-session descendants for scoped resolution, and omits the URI entirely when durable allocation fails (#3471).
34
+ - Managed-session replacement and cleanup now bind Windows destination mutation to exact native identity, keep lock acquisition/release retryable without reviving lost ownership, and report retained artifact payloads as `cleanup_pending` until only the verified root remains.
35
+ - Resuming a session no longer crashes with an unhandled rejection when another session transition is already running. The session picker dispatches resume through a void-returning callback, and `handleResumeSession` had no re-entrancy guard, so a second selection (or a resume issued while compaction, handoff, or a fork was in flight) reached `switchSession` and the `{ code: "busy" }` transition error rejected a promise nobody awaited. Resume now ignores an overlapping request with a status message, reports a busy transition as status, and still propagates every other failure. The progress lease is released on all paths.
36
+ - The interactive `Working…` indicator now remains visible and explicitly labels owner-scoped detached background work across foreground completion, provider errors, pending-submission aborts, and job completion, without resurrecting after TUI disposal (#3479).
37
+ - Activity-indicator suspension now detaches and restores the exact owned loader instead of stopping foreign transition UI; optimistic pre-init prompts still show and clear their spinner, context clear retains its eager teardown contract, and resume cancellation preserves transient state until session mutation actually begins.
38
+ - Activity-indicator stop and suspension helpers now fail safely for lightweight controller contexts with absent or partial status rails, while full interactive contexts retain exact loader detach/restore ownership.
39
+
2
40
  ## [0.12.1] - 2026-07-29
3
41
 
4
42
  ### Fixed
@@ -10,6 +48,7 @@
10
48
  - Managed-session startup failures now include their bounded preparation classification (and path-free native durability diagnostic when available), so Windows launch crashes no longer collapse to an unactionable generic error while filesystem paths and raw OS messages remain redacted (#3383).
11
49
  - Single-model sessions now rotate immediately to another stored provider credential after a content-free quota or rate-limit failure, without requiring a synthetic model fallback chain. Credential rotation is replay-safe for content-free failures regardless of extension lifecycle participation, and traverses the full credential pool independent of `retry.maxRetries` (#3491).
12
50
  - External credential discovery now follows `CLAUDE_CONFIG_DIR` and `CODEX_HOME` instead of always reading `~/.claude` and `~/.codex`, so importing from an account switcher (or any relocated Claude Code / Codex CLI config root) picks up the account the launching shell selected. Both variables resolve through the credential env trust boundary and must be absolute; redacted summaries name the variable, never the resolved path.
51
+ - The `acp_conformance` CI job runs again. The pinned upstream `acpx` checkout resolves its own imports (`@agentclientprotocol/sdk`, `zod`) from its own tree, but its dependencies were never installed, so the corpus runner aborted with `Cannot find module 'zod/v4'` before executing a single case. The checkout is now installed after provenance verification, and the reused warm cache still skips the reinstall.
13
52
 
14
53
  ### Added
15
54
 
@@ -994,8 +994,8 @@ export declare const SETTINGS_SCHEMA: {
994
994
  readonly default: true;
995
995
  readonly ui: {
996
996
  readonly tab: "appearance";
997
- readonly label: "Status Line Action Hints";
998
- readonly description: "Show contextual keyboard shortcuts in the status line";
997
+ readonly label: "Composer Shortcut Hints";
998
+ readonly description: "Show contextual keyboard shortcuts in the composer placeholder";
999
999
  };
1000
1000
  };
1001
1001
  readonly "statusLine.leftSegments": {
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Typed ralplan review conflicts and dispositions (#2902).
3
+ *
4
+ * Architect and Critic findings remain free-form in their stage markdown, but
5
+ * the join/revision path can record machine-checkable findings against stable
6
+ * plan targets. Incompatible actions on the same target produce open conflicts
7
+ * that block a clean join until an explicit disposition is recorded.
8
+ */
9
+ export declare const RALPLAN_REVIEW_CONFLICTS_SCHEMA: "ralplan.review_conflicts.v1";
10
+ export type ReviewAction = "add" | "remove" | "change" | "clarify";
11
+ export type ReviewRole = "architect" | "critic";
12
+ export type ReviewSeverity = "info" | "watch" | "block";
13
+ export type DispositionChoice = "accept_architect" | "accept_critic" | "synthesize" | "defer_user" | "reject_both";
14
+ export interface ReviewSourceReceipt {
15
+ stage: "architect" | "critic";
16
+ stageN: number;
17
+ path: string;
18
+ sha256: string;
19
+ }
20
+ export interface ReviewFinding {
21
+ findingId: string;
22
+ targetId: string;
23
+ action: ReviewAction;
24
+ severity: ReviewSeverity;
25
+ evidence: string;
26
+ sourceRole: ReviewRole;
27
+ sourceReceipt: ReviewSourceReceipt;
28
+ proposedOwner?: string;
29
+ }
30
+ export interface ReviewConflict {
31
+ conflictId: string;
32
+ targetId: string;
33
+ findingIds: [string, string];
34
+ actions: [ReviewAction, ReviewAction];
35
+ sourceRoles: [ReviewRole, ReviewRole];
36
+ status: "open" | "dispositioned";
37
+ }
38
+ export interface ConflictDisposition {
39
+ conflictId: string;
40
+ choice: DispositionChoice;
41
+ rationale: string;
42
+ decisionOwner: string;
43
+ affectedSections: string[];
44
+ dispositionedAt?: string;
45
+ }
46
+ export interface ReviewConflictDocument {
47
+ schema: typeof RALPLAN_REVIEW_CONFLICTS_SCHEMA;
48
+ plannerStageN: number;
49
+ findings: ReviewFinding[];
50
+ conflicts: ReviewConflict[];
51
+ dispositions: ConflictDisposition[];
52
+ }
53
+ export interface JoinGateResult {
54
+ ok: boolean;
55
+ openConflictIds: string[];
56
+ missingDispositionIds: string[];
57
+ orphanDispositionIds: string[];
58
+ message: string;
59
+ }
60
+ /** True when two actions on the same target cannot both remain. */
61
+ export declare function actionsAreIncompatible(left: ReviewAction, right: ReviewAction): boolean;
62
+ /**
63
+ * Derive open conflicts from typed findings. Only cross-role incompatible pairs
64
+ * on the same targetId are conflicts (Architect remove vs Critic add, etc.).
65
+ */
66
+ export declare function detectReviewConflicts(findings: readonly ReviewFinding[]): ReviewConflict[];
67
+ /** Mark conflicts dispositioned when a matching disposition exists. */
68
+ export declare function applyDispositions(conflicts: readonly ReviewConflict[], dispositions: readonly ConflictDisposition[]): ReviewConflict[];
69
+ /**
70
+ * Join gate: clean only when every derived conflict has an explicit disposition
71
+ * with rationale and decision owner, and no orphan dispositions reference unknown
72
+ * conflicts.
73
+ */
74
+ export declare function evaluateReviewJoinGate(findings: readonly ReviewFinding[], dispositions: readonly ConflictDisposition[], precomputedConflicts?: readonly ReviewConflict[]): JoinGateResult;
75
+ /** Strip optional markdown fence and parse JSON. */
76
+ export declare function parseReviewConflictJson(raw: string): unknown;
77
+ /**
78
+ * Authoritative provenance required by `gjc ralplan --write --stage disposition`.
79
+ *
80
+ * Without this, a disposition document can claim arbitrary path/hash strings and
81
+ * join the wrong Architect/Critic pass (#3013 adversarial review).
82
+ */
83
+ export interface IndexedReviewArtifact {
84
+ path: string;
85
+ sha256: string;
86
+ }
87
+ export interface DispositionProvenanceContext {
88
+ /** CLI `--stage_n` for this disposition write; must equal `plannerStageN`. */
89
+ expectedStageN: number;
90
+ /**
91
+ * Persisted Architect/Critic (and other) stage artifacts from this run's
92
+ * `index.jsonl`, keyed by `${stage}\u0000${stageN}`.
93
+ */
94
+ indexedArtifacts: ReadonlyMap<string, IndexedReviewArtifact>;
95
+ }
96
+ /** Stable map key for a staged artifact identity in the run index. */
97
+ export declare function reviewArtifactIndexKey(stage: string, stageN: number): string;
98
+ /**
99
+ * Cross-check every finding's source receipt against the CLI stage number and
100
+ * the run's persisted Architect/Critic artifact index. Fail closed on mismatch
101
+ * or spoofed path/hash attestations.
102
+ */
103
+ export declare function assertDispositionProvenance(doc: ReviewConflictDocument, provenance: DispositionProvenanceContext): void;
104
+ /**
105
+ * Parse and validate a disposition-stage document. Re-derives conflicts from
106
+ * findings when omitted, then fails closed unless every conflict is dispositioned
107
+ * (or findings produce zero conflicts and dispositions are empty).
108
+ *
109
+ * When `provenance` is provided (CLI write path), also enforces authoritative
110
+ * same-pass receipt checks against the run index.
111
+ */
112
+ export declare function parseReviewConflictDocument(raw: string | unknown, provenance?: DispositionProvenanceContext): ReviewConflictDocument;
113
+ /** Canonical JSON serialization for disposition-stage artifacts. */
114
+ export declare function serializeReviewConflictDocument(doc: ReviewConflictDocument): string;
@@ -14,9 +14,11 @@ import { type RalplanIndexRow } from "./ledger-event-renderer";
14
14
  * 2. **Artifact write**: `gjc ralplan --write --stage <type> --stage_n <N>
15
15
  * (--artifact <path-or-string> | --artifact-env GJC_RALPLAN_ARTIFACT)
16
16
  * [--run-id <id>] [--session-id <id>] [--lane-verdict <token>] [--json]` persists Planner / Architect
17
- * / Critic / revision / post-interview / ADR / final markdown under `.gjc/plans/ralplan/<run-id>/`, maintains
18
- * an `index.jsonl` audit log, copies `final` stages to `pending-approval.md`, and advances
19
- * the HUD chip to reflect the latest persisted stage.
17
+ * / Critic / disposition / revision / post-interview / ADR / final artifacts under
18
+ * `.gjc/plans/ralplan/<run-id>/`, maintains an `index.jsonl` audit log, copies `final`
19
+ * stages to `pending-approval.md`, and advances the HUD chip to reflect the latest
20
+ * persisted stage. Disposition stage artifacts are fail-closed JSON documents that
21
+ * record typed review conflicts with authoritative same-pass source receipts (#2902).
20
22
  */
21
23
  export interface RalplanCommandResult {
22
24
  status: number;
@@ -17,12 +17,16 @@ export declare function validateStructuralArtifact(cwd: string, row: JsonObject,
17
17
  live: boolean;
18
18
  }): Promise<boolean>;
19
19
  export declare function isAllowedGitReplayCommand(args: readonly string[]): boolean;
20
+ export declare function resolveCliReplayCommand(command: string[], options?: {
21
+ compiled?: boolean;
22
+ }): string[];
20
23
  export declare function readCliReplayRecord(cwd: string, row: JsonObject, fieldName: string): Promise<JsonObject | null>;
21
24
  export interface ReplayProcessHandle {
22
25
  readonly exited: Promise<number>;
26
+ readonly pid?: number;
23
27
  kill(signal?: number | NodeJS.Signals): void;
24
28
  }
25
- export declare function waitForReplayProcessWithTimeout(process: ReplayProcessHandle, timeoutMs: number, graceMs?: number): Promise<number>;
29
+ export declare function waitForReplayProcessWithTimeout(handle: ReplayProcessHandle, timeoutMs: number, graceMs?: number): Promise<number>;
26
30
  export declare function validateReplayExemptFallback(cwd: string, record: JsonObject, fieldName: string, artifactRefs: Map<string, JsonObject>, options: {
27
31
  surfaceFamily: SurfaceFamily;
28
32
  live: boolean;
@@ -315,13 +315,19 @@ export declare function isSubstantiveEvidence(value: unknown): boolean;
315
315
  export declare function hasTypedVerifiedReceipt(value: unknown): boolean;
316
316
  export declare function hasExistingNonEmptyArtifact(cwd: string, value: unknown): Promise<boolean>;
317
317
  export declare function readArtifactBytes(cwd: string, row: JsonObject, fieldName: string): Promise<Buffer | null>;
318
- import { validateArtifactProof, validateCliReplay, validateLiveSurfaceProofPresence, validateReplayExemptFallback, validateStructuralArtifact, validateSurfaceStructuralRequirement, waitForReplayProcessWithTimeout } from "./ultragoal-evidence";
318
+ import { resolveCliReplayCommand, validateArtifactProof, validateCliReplay, validateLiveSurfaceProofPresence, validateReplayExemptFallback, validateStructuralArtifact, validateSurfaceStructuralRequirement, waitForReplayProcessWithTimeout } from "./ultragoal-evidence";
319
319
  export type { ReplayProcessHandle } from "./ultragoal-evidence";
320
- export { validateArtifactProof, validateCliReplay, validateLiveSurfaceProofPresence, validateReplayExemptFallback, validateStructuralArtifact, validateSurfaceStructuralRequirement, waitForReplayProcessWithTimeout, };
320
+ export { resolveCliReplayCommand, validateArtifactProof, validateCliReplay, validateLiveSurfaceProofPresence, validateReplayExemptFallback, validateStructuralArtifact, validateSurfaceStructuralRequirement, waitForReplayProcessWithTimeout, };
321
321
  export declare function validateExecutorQaRedTeamEvidenceForReview(cwd: string, executorQa: Record<string, unknown>, options?: {
322
322
  mode?: "review";
323
323
  changeSet?: UltragoalChangeSet;
324
324
  }): Promise<void>;
325
+ /**
326
+ * Scaffold a schema-shaped quality-gate template for selected surfaces (#3474).
327
+ * The template is intentionally incomplete for live artifact proofs so `quality-gate
328
+ * validate` can report remaining evidence gaps in one pass after the author fills paths.
329
+ */
330
+ export declare function buildQualityGateInitTemplate(surfaces: readonly string[]): JsonObject;
325
331
  /**
326
332
  * Read-only quality-gate validation (#3474). Applies exactly the same rules as
327
333
  * `checkpoint --status complete` — including deferred-vs-boundary gate selection and
@@ -32,6 +32,22 @@ export declare function computeIrcSplitWidths(width: number): {
32
32
  separatorWidth: number;
33
33
  rightWidth: number;
34
34
  };
35
+ /**
36
+ * Resolves the single shared work-lane geometry. The left lane is used by both
37
+ * transcript and todo content whenever the IRC lane is effective.
38
+ */
39
+ export declare function computeIrcWorkLaneWidths(width: number, sidebarVisible: boolean): {
40
+ leftWidth: number;
41
+ separatorWidth: number;
42
+ rightWidth: number;
43
+ };
44
+ export declare class IrcLeftLaneComponent implements Component {
45
+ private readonly content;
46
+ private readonly isSidebarVisible;
47
+ constructor(content: Component, isSidebarVisible: (width: number) => boolean);
48
+ render(width: number): string[];
49
+ invalidate(): void;
50
+ }
35
51
  type SemanticLine = Readonly<{
36
52
  kind: "header" | "body" | "elision" | "blank";
37
53
  text: string;
@@ -3,7 +3,7 @@ import { type ProviderDetails, type UsageReport } from "@gajae-code/ai";
3
3
  import type { KeybindingsManager } from "../../config/keybindings";
4
4
  import type { CompactOptions } from "../../extensibility/extensions/types";
5
5
  import { theme } from "../../modes/theme/theme";
6
- import type { InteractiveModeContext } from "../../modes/types";
6
+ import { type InteractiveModeContext } from "../../modes/types";
7
7
  export declare function buildHelpMarkdown(keybindings: Pick<KeybindingsManager, "getAccessibleDisplayString">): string;
8
8
  export declare class CommandController {
9
9
  #private;
@@ -1,5 +1,5 @@
1
1
  import { type Component } from "@gajae-code/tui";
2
- import type { InteractiveModeContext } from "../../modes/types";
2
+ import { type InteractiveModeContext } from "../../modes/types";
3
3
  import type { AgentSessionEvent } from "../../session/agent-session";
4
4
  /** Test-only performance counters for advisory baseline tests. */
5
5
  export declare const __eventControllerPerfCounters: {
@@ -2,7 +2,7 @@ import type { Component, TUI } from "@gajae-code/tui";
2
2
  import { KeybindingsManager } from "../../config/keybindings";
3
3
  import type { ExtensionUIContext, ExtensionUIDialogOptions, ExtensionWidgetContent, ExtensionWidgetOptions, TerminalInputHandler } from "../../extensibility/extensions";
4
4
  import { type Theme } from "../../modes/theme/theme";
5
- import type { InteractiveModeContext } from "../../modes/types";
5
+ import { type InteractiveModeContext } from "../../modes/types";
6
6
  export declare class ExtensionUiController {
7
7
  #private;
8
8
  private ctx;
@@ -79,5 +79,6 @@ export declare class ExtensionUiController {
79
79
  addExtensionTerminalInputListener(handler: TerminalInputHandler): () => void;
80
80
  clearHookWidgets(): void;
81
81
  clearExtensionTerminalInputListeners(): void;
82
+ dispose(): void;
82
83
  showExtensionError(extensionPath: string, error: string): void;
83
84
  }
@@ -1,7 +1,7 @@
1
1
  import type { Component, SlashCommand } from "@gajae-code/tui";
2
2
  import type { ModelProfileConfig } from "../../config/models-config-schema";
3
3
  import { type Settings } from "../../config/settings";
4
- import type { InteractiveModeContext, OAuthSelectorOptions } from "../../modes/types";
4
+ import { type InteractiveModeContext, type OAuthSelectorOptions } from "../../modes/types";
5
5
  import { getNotificationConfig, maskToken } from "../../sdk/bus/config";
6
6
  import { proposedTelegramIdentity, reconcileCommittedTelegramConfiguration, removeTelegramConfiguration, saveTelegramInactive } from "../../sdk/bus/notification-orchestration";
7
7
  import { buildNotificationStatusReport, checkNotificationHealth, recoverNotifications, sanitizeDiagnostic, sendNotificationTest } from "../../sdk/bus/notification-service";
@@ -39,7 +39,9 @@ export declare const DEFAULT_COMPOSER_PLACEHOLDER: string;
39
39
  export declare function getComposerPlaceholder(keybindings: Pick<KeybindingsManager, "getDisplayString">, context: KeyDisplayContext, options: {
40
40
  readonly busy: boolean;
41
41
  readonly busyPromptMode: "steer" | "queue";
42
+ readonly showActionHints: boolean;
42
43
  }): string;
44
+ export declare function resolveActivityIndicatorMessage(foregroundActive: boolean, activeBackgroundTasks: number, foregroundMessage: string): string | undefined;
43
45
  export declare function getWelcomeTranscriptReservedRows(chatContainer: Container, width: number): number;
44
46
  export type WelcomeBannerSettingMode = "auto" | "unicode" | "square" | "ascii";
45
47
  export declare function resolveWelcomeLogoMode(mode: WelcomeBannerSettingMode, env?: Record<string, string | undefined>, platform?: NodeJS.Platform): WelcomeLogoMode;
@@ -156,6 +158,8 @@ export declare class InteractiveMode implements InteractiveModeContext {
156
158
  commitPetPreviewMode(mode: PetMode): boolean;
157
159
  restoreComposer(): void;
158
160
  rebuildChatFromMessages(policy: TranscriptRebuildPolicy): void;
161
+ isStopped(): boolean;
162
+ onStop(callback: () => void): () => void;
159
163
  stop(): void;
160
164
  shutdown(): Promise<void>;
161
165
  checkShutdownRequested(): Promise<void>;
@@ -169,7 +173,12 @@ export declare class InteractiveMode implements InteractiveModeContext {
169
173
  }): void;
170
174
  showError(message: string): void;
171
175
  showWarning(message: string): void;
176
+ syncActivityIndicator(): void;
172
177
  ensureLoadingAnimation(): void;
178
+ stopLoadingAnimation(options?: {
179
+ restoreBackground?: boolean;
180
+ }): void;
181
+ suspendActivityIndicator(): () => void;
173
182
  setWorkingMessage(message?: string): void;
174
183
  applyPendingWorkingMessage(): void;
175
184
  showNewVersionNotification(newVersion: string): void;
@@ -193,7 +193,9 @@ export declare var theme: Theme;
193
193
  /** Get the name of the currently active theme. */
194
194
  export declare function getCurrentThemeName(): string | undefined;
195
195
  export declare function initTheme(enableWatcher?: boolean, symbolPreset?: SymbolPreset, colorBlindMode?: boolean, darkTheme?: string, lightTheme?: string): Promise<void>;
196
- export declare function setTheme(name: string, enableWatcher?: boolean): Promise<{
196
+ export declare function setTheme(name: string, enableWatcher?: boolean, options?: {
197
+ shouldApply?: () => boolean;
198
+ }): Promise<{
197
199
  success: boolean;
198
200
  error?: string;
199
201
  }>;
@@ -49,6 +49,29 @@ export type ComposerSubmissionOptions = Readonly<{
49
49
  editor: CustomEditor;
50
50
  }>;
51
51
  export declare function canApplyComposerSubmission(options: ComposerSubmissionOptions | undefined, editor: CustomEditor): boolean;
52
+ type PartialActivityStatusContainer = Partial<Pick<Container, "children" | "clear" | "detachChild" | "addChild">>;
53
+ export declare function stopInteractiveActivityIndicator(ctx: {
54
+ loadingAnimation?: Loader;
55
+ statusContainer?: PartialActivityStatusContainer;
56
+ stopLoadingAnimation?: (options?: {
57
+ restoreBackground?: boolean;
58
+ }) => void;
59
+ }, options?: {
60
+ restoreBackground?: boolean;
61
+ }): void;
62
+ export declare function clearInteractiveActivityLoaders(ctx: Pick<InteractiveModeContext, "autoCompactionLoader" | "autoCompactionEscapeHandler" | "retryLoader" | "retryCountdownTimer" | "retryEscapeHandler" | "retryEscapePrimed" | "editor">): void;
63
+ export declare function suspendInteractiveActivityIndicator(ctx: {
64
+ loadingAnimation?: Loader;
65
+ statusContainer?: PartialActivityStatusContainer;
66
+ stopLoadingAnimation?: (options?: {
67
+ restoreBackground?: boolean;
68
+ }) => void;
69
+ syncActivityIndicator?: () => void;
70
+ suspendActivityIndicator?: () => () => void;
71
+ }): () => void;
72
+ export declare function syncInteractiveActivityIndicator(ctx: {
73
+ syncActivityIndicator?: () => void;
74
+ }): void;
52
75
  export type TodoStatus = "pending" | "in_progress" | "completed" | "abandoned";
53
76
  export type TodoItem = {
54
77
  content: string;
@@ -139,6 +162,8 @@ export interface InteractiveModeContext {
139
162
  init(): Promise<void>;
140
163
  shutdown(): Promise<void>;
141
164
  checkShutdownRequested(): Promise<void>;
165
+ isStopped?(): boolean;
166
+ onStop(callback: () => void): () => void;
142
167
  setToolUIContext(uiContext: ExtensionUIContext, hasUI: boolean): void;
143
168
  initializeHookRunner(uiContext: ExtensionUIContext, hasUI: boolean): void;
144
169
  createBackgroundUiContext(): ExtensionUIContext;
@@ -161,6 +186,11 @@ export interface InteractiveModeContext {
161
186
  setWorkingMessage(message?: string): void;
162
187
  applyPendingWorkingMessage(): void;
163
188
  ensureLoadingAnimation(): void;
189
+ syncActivityIndicator(): void;
190
+ suspendActivityIndicator(): () => void;
191
+ stopLoadingAnimation(options?: {
192
+ restoreBackground?: boolean;
193
+ }): void;
164
194
  /**
165
195
  * Commit a pet mode through the shared result-returning policy: capability
166
196
  * is rechecked immediately before mutation and the preference persists only
@@ -223,6 +253,7 @@ export interface InteractiveModeContext {
223
253
  updateEditorTopBorder(): void;
224
254
  updateEditorBorderColor(): void;
225
255
  rebuildChatFromMessages(policy: TranscriptRebuildPolicy): void;
256
+ updateEditorChrome(): void;
226
257
  setTodos(todos: TodoItem[] | TodoPhase[]): void;
227
258
  reloadTodos(): Promise<void>;
228
259
  toggleTodoExpansion(): void;
@@ -344,3 +375,4 @@ export interface OAuthSelectorOptions {
344
375
  trigger?: "bare-login";
345
376
  externalCredentialDiscover?: CredentialAutoImportOptions["discover"];
346
377
  }
378
+ export {};
@@ -19,7 +19,8 @@ export type ChatDaemonAction = "stop" | "reload";
19
19
  * chat daemons after notification configuration parsing changes. Generation 15
20
20
  * applies the current notification configuration directly when starting chat
21
21
  * daemon transports. Generation 16 applies Telegram sound-policy configuration
22
- * through shared notification parsing.
22
+ * through shared notification parsing. Generation 17 binds managed-session
23
+ * replacement to exact native filesystem authority.
23
24
  */
24
25
  export declare const CHAT_DAEMON_GENERATIONS: Readonly<Record<ChatDaemonKind, number>>;
25
26
  export declare function chatDaemonGeneration(kind: ChatDaemonKind): number;
@@ -44,9 +44,10 @@ export declare const NOTIFICATION_PROTOCOL_VERSION = 3;
44
44
  * 33 adds action-bound multi-select state rendering and replay-safe option
45
45
  * snapshots. Generation 34 converts non-photo image formats (including WebP)
46
46
  * into Telegram-compatible photo uploads when possible. Generation 35 adds
47
- * user-created topic adoption (forum-topic folder picker).
47
+ * user-created topic adoption (forum-topic folder picker). Generation 36 binds
48
+ * managed-session replacement to exact native filesystem authority.
48
49
  */
49
- export declare const DAEMON_GENERATION = 35;
50
+ export declare const DAEMON_GENERATION = 36;
50
51
  /**
51
52
  * Serving-compatibility boundary for daemon lifecycle requests. Epoch 1 covers
52
53
  * all builds published before this field existed; epoch 2 covered generation 29;
@@ -633,6 +633,7 @@ export declare class AgentSession {
633
633
  * separately inside `sendCustomMessage`. */
634
634
  enqueueCustomMessageDisplay(text: string, mode: "steer" | "followUp"): string;
635
635
  getAgentId(): string | undefined;
636
+ registerToolSessionCleanup(cleanup: () => Promise<void> | void): () => void;
636
637
  getAsyncJobSnapshot(options?: {
637
638
  recentLimit?: number;
638
639
  }): AsyncJobSnapshot | null;
@@ -1386,6 +1387,7 @@ export declare class AgentSession {
1386
1387
  */
1387
1388
  switchSession(sessionPath: string, options?: {
1388
1389
  transition?: SessionSwitchEvent["transition"];
1390
+ onTransitionMutationStarted?: () => void;
1389
1391
  }): Promise<boolean>;
1390
1392
  /**
1391
1393
  * Create a branch from a specific entry.
@@ -7,6 +7,7 @@ export declare const MANAGED_ARTIFACT_MAX_TOTAL_BYTES: number;
7
7
  export declare const MANAGED_ARTIFACT_COPY_BATCH_SIZE = 256;
8
8
  export declare class ManagedPublishError extends Error {
9
9
  readonly classification: "destination_conflict" | "atomic_unavailable" | "invalid_request" | "durability_not_provable" | "identity_mismatch" | "io_error" | "durability_failed";
10
+ readonly code: ManagedPublishError["classification"];
10
11
  readonly diagnostic: string;
11
12
  constructor(classification: ManagedPublishError["classification"], outcome: NativePublishOutcome);
12
13
  }
@@ -20,6 +21,12 @@ export interface ManagedStorageLock {
20
21
  assertOwned(): void;
21
22
  release(): Promise<void>;
22
23
  }
24
+ export interface ManagedStorageLockSync {
25
+ path: string;
26
+ attemptId: string;
27
+ assertOwned(): void;
28
+ release(): void;
29
+ }
23
30
  export interface ManagedFileSnapshot {
24
31
  bytes: Buffer;
25
32
  identity: {
@@ -31,6 +38,13 @@ export interface ManagedFileSnapshot {
31
38
  sha256: string;
32
39
  };
33
40
  }
41
+ export declare const MANAGED_SESSION_SECURITY_CODES: readonly ["acl_denied", "acl_io_error", "acl_present", "acl_malformed", "acl_unknown", "acl_unavailable", "acl_apply_failed", "acl_verify_failed", "not_found", "not_directory", "network_unsupported", "reparse_point", "identity_unavailable", "identity_mismatch", "owner_mismatch", "mode_mismatch", "io_error"];
42
+ export type ManagedSessionSecurityCode = (typeof MANAGED_SESSION_SECURITY_CODES)[number];
43
+ export declare class ManagedSessionSecurityError extends Error {
44
+ readonly code: ManagedSessionSecurityCode;
45
+ constructor(code: ManagedSessionSecurityCode);
46
+ }
47
+ export declare function managedSessionSecurityCode(error: unknown): ManagedSessionSecurityCode | undefined;
34
48
  export declare function validateNativeSecurityResult(value: unknown, operation: "apply" | "verify", kind: "directory" | "file"): NativeOwnerOnlySecurityResult;
35
49
  /** Captured configured-root authority for managed paths only. */
36
50
  export interface ManagedDirectoryRoot {
@@ -105,13 +119,18 @@ export declare function captureManagedFileNoFollow(pathname: string): ManagedFil
105
119
  /** Atomically publishes bytes without replacing an existing destination. */
106
120
  export declare function publishManagedFileNoReplace(destination: string, bytes: Uint8Array, assertOwned?: () => void, root?: ManagedDirectoryRoot, policy?: ManagedSessionSecurityPolicy): Promise<void>;
107
121
  export declare function publishManagedFileNoReplaceSync(destination: string, bytes: Uint8Array, root?: ManagedDirectoryRoot, policy?: ManagedSessionSecurityPolicy): void;
108
- /** Replace one managed regular file while retaining the secured staging fd through publication. */
109
- export declare function replaceManagedFileSync(destination: string, bytes: Uint8Array, root: ManagedDirectoryRoot, policy?: ManagedSessionSecurityPolicy): void;
110
- export declare function replaceManagedFile(destination: string, bytes: Uint8Array, root: ManagedDirectoryRoot, policy?: ManagedSessionSecurityPolicy): Promise<void>;
122
+ /**
123
+ * Atomically replace a managed file only when the destination is still the
124
+ * exact captured object. Unlike `replaceManagedFileSync`, this never performs
125
+ * an unconditional pathname rename.
126
+ */
127
+ export declare function replaceManagedFileExactSync(destination: string, bytes: Uint8Array, expected: Pick<ManagedFileSnapshot, "identity">, root: ManagedDirectoryRoot): void;
111
128
  /** Copy the exact bytes captured from one no-follow source descriptor. */
112
129
  export declare function copyManagedFileNoReplace(source: string, destination: string, snapshot?: ManagedFileSnapshot, root?: ManagedDirectoryRoot, policy?: ManagedSessionSecurityPolicy): Promise<void>;
113
130
  /** Acquire a lease lock with bounded wait, heartbeats, conservative stale reclaim, and fencing. */
114
131
  export declare function acquireManagedLock(locksDirectory: string, name: string, root?: ManagedDirectoryRoot, policy?: ManagedSessionSecurityPolicy): Promise<ManagedStorageLock>;
132
+ /** Acquire a synchronous lease lock for a short cross-process critical section. */
133
+ export declare function acquireManagedLockSync(locksDirectory: string, name: string, root?: ManagedDirectoryRoot, policy?: ManagedSessionSecurityPolicy): ManagedStorageLockSync;
115
134
  export interface ManagedArtifactTreeLimits {
116
135
  maxFiles?: number;
117
136
  maxTotalBytes?: number;
@@ -32,6 +32,10 @@ export declare class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskT
32
32
  readonly strict = true;
33
33
  readonly loadMode = "discoverable";
34
34
  readonly renderResult: typeof renderResult;
35
+ /**
36
+ * Session-lifetime durable artifact state is shared atomically by every
37
+ * TaskTool created for the same parent ToolSession.
38
+ */
35
39
  get parameters(): TaskToolSchemaInstance;
36
40
  renderCall(args: unknown, options: Parameters<typeof renderTaskCall>[1], theme: Theme): import("@gajae-code/tui").Component;
37
41
  /** Dynamic description that reflects current disabled-agent settings */
@@ -10,6 +10,7 @@ export declare class AgentOutputManager {
10
10
  #private;
11
11
  constructor(getArtifactsDir: () => string | null, options?: {
12
12
  parentPrefix?: string;
13
+ getAuthorizedArtifactsDirs?: () => readonly string[];
13
14
  });
14
15
  /**
15
16
  * Allocate a unique ID with numeric prefix.
@@ -50,6 +50,8 @@ export interface TaskResultReceipt {
50
50
  sizeBytes: number;
51
51
  lineCount: number;
52
52
  sha256?: string;
53
+ /** Output remains readable for the parent session lifetime (and same-session descendants). */
54
+ durability?: "session";
53
55
  };
54
56
  outputUnavailable?: boolean;
55
57
  review?: {
@@ -219,6 +219,8 @@ export interface ToolSession {
219
219
  getAuthorizedArtifactsDirs?: () => readonly string[];
220
220
  /** Get the ArtifactManager backing this session (shared across parent + subagents). */
221
221
  getArtifactManager?: () => ArtifactManager | null;
222
+ /** Register teardown work owned by the current logical session. */
223
+ registerSessionCleanup?: (cleanup: () => Promise<void> | void) => () => void;
222
224
  /** Allocate a new artifact path and ID for session-scoped truncated output. */
223
225
  allocateOutputArtifact?: (toolType: string) => Promise<{
224
226
  id?: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@gajae-code/coding-agent",
4
- "version": "0.12.1",
4
+ "version": "0.12.4",
5
5
  "description": "Gajae Code CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://gajae-code.com",
7
7
  "author": "Yeachan-Heo and Gajae Code Contributors",
@@ -53,13 +53,13 @@
53
53
  "@agentclientprotocol/sdk": "1.3.0",
54
54
  "@babel/parser": "^7.29.3",
55
55
  "@mozilla/readability": "^0.6.0",
56
- "@gajae-code/stats": "0.12.1",
57
- "@gajae-code/agent-core": "0.12.1",
58
- "@gajae-code/ai": "0.12.1",
59
- "@gajae-code/bridge-client": "0.12.1",
60
- "@gajae-code/natives": "0.12.1",
61
- "@gajae-code/tui": "0.12.1",
62
- "@gajae-code/utils": "0.12.1",
56
+ "@gajae-code/stats": "0.12.4",
57
+ "@gajae-code/agent-core": "0.12.4",
58
+ "@gajae-code/ai": "0.12.4",
59
+ "@gajae-code/bridge-client": "0.12.4",
60
+ "@gajae-code/natives": "0.12.4",
61
+ "@gajae-code/tui": "0.12.4",
62
+ "@gajae-code/utils": "0.12.4",
63
63
  "@puppeteer/browsers": "^2.13.0",
64
64
  "@types/turndown": "5.0.6",
65
65
  "@xterm/headless": "^6.0.0",