@gajae-code/coding-agent 0.13.0 → 0.13.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.
- package/CHANGELOG.md +18 -0
- package/dist/types/config/file-lock.d.ts +2 -0
- package/dist/types/config/keybindings.d.ts +2 -2
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/models-config-schema.d.ts +6 -0
- package/dist/types/config/settings-schema.d.ts +17 -5
- package/dist/types/config/settings.d.ts +10 -1
- package/dist/types/edit/streaming.d.ts +5 -5
- package/dist/types/eval/py/kernel.d.ts +2 -2
- package/dist/types/eval/py/runtime.d.ts +5 -1
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +3 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +3 -1
- package/dist/types/modes/components/model-selector.d.ts +3 -2
- package/dist/types/runtime/process-lifecycle.d.ts +12 -31
- package/dist/types/sdk/broker/identity.d.ts +2 -1
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +17 -1
- package/dist/types/sdk/broker/lifecycle.d.ts +11 -6
- package/dist/types/sdk/broker/startup-failure.d.ts +32 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -1
- package/dist/types/sdk/bus/conversation-store.d.ts +1 -0
- package/dist/types/sdk/bus/index.d.ts +4 -0
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +10 -1
- package/dist/types/sdk/bus/reconciliation-store.d.ts +2 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +11 -2
- package/dist/types/sdk/bus/telegram-daemon.d.ts +17 -1
- package/dist/types/sdk/host/query/cursor.d.ts +4 -0
- package/dist/types/sdk/host/query/handlers.d.ts +6 -0
- package/dist/types/sdk/host/session-runtime.d.ts +7 -6
- package/dist/types/sdk/host/websocket-transport.d.ts +1 -1
- package/dist/types/sdk/index.d.ts +1 -0
- package/dist/types/sdk/turn-result.d.ts +37 -0
- package/dist/types/session/agent-session.d.ts +8 -11
- package/dist/types/session/blob-store.d.ts +6 -3
- package/dist/types/slash-commands/helpers/fast-status-report.d.ts +12 -3
- package/dist/types/task/executor.d.ts +4 -7
- package/dist/types/tools/index.d.ts +1 -1
- package/dist/types/tools/read.d.ts +1 -1
- package/dist/types/tools/search.d.ts +1 -1
- package/dist/types/utils/edit-mode.d.ts +69 -0
- package/package.json +8 -8
- package/scripts/generate-hotkeys-docs.ts +8 -6
- package/src/commands/sdk.ts +23 -2
- package/src/config/file-lock.ts +25 -5
- package/src/config/keybindings.ts +4 -2
- package/src/config/model-registry.ts +20 -0
- package/src/config/models-config-schema.ts +1 -0
- package/src/config/settings-schema.ts +29 -22
- package/src/config/settings.ts +42 -10
- package/src/coordinator-mcp/server.ts +84 -106
- package/src/edit/index.ts +5 -19
- package/src/edit/renderer.ts +3 -2
- package/src/edit/streaming.ts +28 -15
- package/src/eval/py/executor.ts +108 -10
- package/src/eval/py/kernel.ts +86 -12
- package/src/eval/py/runtime.ts +200 -16
- package/src/exec/bash-executor.ts +36 -37
- package/src/gjc-runtime/gc-runtime.ts +25 -3
- package/src/gjc-runtime/managed-owner-supervisor.ts +27 -1
- package/src/gjc-runtime/session-state-sidecar.ts +110 -4
- package/src/gjc-runtime/tmux-sessions.ts +34 -5
- package/src/internal-urls/docs-index.generated.ts +6 -6
- package/src/modes/acp/acp-agent.ts +10 -2
- package/src/modes/components/model-selector.ts +28 -8
- package/src/modes/controllers/plan-mode-controller.ts +3 -3
- package/src/modes/controllers/selector-controller.ts +4 -2
- package/src/runtime/process-lifecycle.ts +126 -27
- package/src/runtime-mcp/transports/stdio.ts +11 -6
- package/src/sdk/acp/adapter.ts +18 -10
- package/src/sdk/broker/broker.ts +55 -4
- package/src/sdk/broker/ensure.ts +10 -7
- package/src/sdk/broker/identity.ts +10 -0
- package/src/sdk/broker/lifecycle-ledger.ts +56 -6
- package/src/sdk/broker/lifecycle.ts +53 -10
- package/src/sdk/broker/session-index.ts +28 -11
- package/src/sdk/broker/startup-failure.ts +101 -0
- package/src/sdk/broker/transport.ts +1 -0
- package/src/sdk/bus/chat-daemon-control.ts +4 -3
- package/src/sdk/bus/chat-daemon-runtime.ts +8 -1
- package/src/sdk/bus/conversation-store.ts +171 -50
- package/src/sdk/bus/index.ts +101 -47
- package/src/sdk/bus/kind-aware-reconciliation.ts +52 -2
- package/src/sdk/bus/notification-service.ts +3 -3
- package/src/sdk/bus/reconciliation-store.ts +16 -0
- package/src/sdk/bus/telegram-daemon-contract.ts +11 -2
- package/src/sdk/bus/telegram-daemon.ts +131 -29
- package/src/sdk/bus/topic-registry.ts +38 -9
- package/src/sdk/cli/session-cli.ts +20 -7
- package/src/sdk/client/discovery.ts +23 -2
- package/src/sdk/host/query/cursor.ts +4 -0
- package/src/sdk/host/query/handlers.ts +99 -100
- package/src/sdk/host/sdk-surface-parity.test.ts +1 -1
- package/src/sdk/host/session-runtime.test.ts +98 -1
- package/src/sdk/host/session-runtime.ts +57 -15
- package/src/sdk/host/websocket-transport.lifecycle.test.ts +192 -2
- package/src/sdk/host/websocket-transport.ts +54 -30
- package/src/sdk/index.ts +1 -0
- package/src/sdk/mcp/server.ts +19 -6
- package/src/sdk/protocol/operation-inventory.generated.json +1 -1
- package/src/sdk/protocol/operation-registry.ts +8 -12
- package/src/sdk/session.ts +5 -14
- package/src/sdk/turn-result.ts +53 -0
- package/src/session/agent-session.ts +112 -69
- package/src/session/blob-store.ts +91 -21
- package/src/session/session-manager.ts +408 -81
- package/src/session/session-storage.ts +68 -10
- package/src/slash-commands/helpers/fast-status-report.ts +24 -8
- package/src/task/executor.ts +25 -8
- package/src/task/index.ts +0 -2
- package/src/tools/index.ts +1 -1
- package/src/tools/read.ts +12 -11
- package/src/tools/search.ts +7 -7
- package/src/utils/edit-mode.ts +180 -9
- package/src/web/search/providers/insane.ts +55 -1
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
* Preserves Q26 admit/first-terminal/capacity/TTL semantics; indexes and caps are per-kind.
|
|
4
4
|
*/
|
|
5
5
|
import type { PromptReconciliationStatus, SdkPromptTerminalOutcome, TurnPromptReconciliation } from "../prompt-status";
|
|
6
|
+
import { type TurnResultContent, type TurnResultPage } from "../turn-result";
|
|
6
7
|
import { type PromptCorrelation } from "./prompt-reconciliation";
|
|
7
8
|
import type { ReconciliationKind, ReconciliationStore } from "./reconciliation-store";
|
|
8
9
|
export type { ReconciliationKind };
|
|
9
10
|
export interface KindCorrelation extends PromptCorrelation {
|
|
10
11
|
kind: ReconciliationKind;
|
|
12
|
+
content?: TurnResultContent;
|
|
11
13
|
}
|
|
12
14
|
export interface KindAwareReconciliation {
|
|
13
15
|
admit(kind: ReconciliationKind, clientRef?: string): void;
|
|
@@ -17,21 +19,28 @@ export interface KindAwareReconciliation {
|
|
|
17
19
|
}): Promise<void>;
|
|
18
20
|
noteTransition(kind: ReconciliationKind, correlation: PromptCorrelation | undefined, frame: {
|
|
19
21
|
type: "agent_start" | "agent_end";
|
|
22
|
+
content?: TurnResultContent;
|
|
20
23
|
} | {
|
|
21
24
|
type: "agent_failed";
|
|
22
25
|
error: unknown;
|
|
26
|
+
content?: TurnResultContent;
|
|
23
27
|
}): Promise<void>;
|
|
24
28
|
claimPendingOutcome(correlation: PromptCorrelation, outcome: SdkPromptTerminalOutcome): Promise<SdkPromptTerminalOutcome>;
|
|
25
29
|
finalizePromptOutcome(correlation: PromptCorrelation, outcome?: SdkPromptTerminalOutcome, recordError?: {
|
|
26
30
|
code: string;
|
|
27
31
|
message: string;
|
|
28
|
-
}): Promise<void>;
|
|
32
|
+
}, content?: unknown): Promise<void>;
|
|
29
33
|
peekPendingOutcome(correlation: PromptCorrelation): SdkPromptTerminalOutcome | undefined;
|
|
30
34
|
lookup(kind: ReconciliationKind, selector: {
|
|
31
35
|
commandId?: string;
|
|
32
36
|
turnId?: string;
|
|
33
37
|
clientRef?: string;
|
|
34
38
|
}): TurnPromptReconciliation;
|
|
39
|
+
lookupResult(kind: ReconciliationKind, selector: {
|
|
40
|
+
commandId?: string;
|
|
41
|
+
turnId?: string;
|
|
42
|
+
clientRef?: string;
|
|
43
|
+
}): TurnResultPage;
|
|
35
44
|
cleanup(): void;
|
|
36
45
|
activeCount(kind: ReconciliationKind): number;
|
|
37
46
|
/** Hydrate from durable store (call once at session host start). */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PromptReconciliationStatus, SdkPromptTerminalOutcome } from "../prompt-status";
|
|
2
|
+
import { type TurnResultContent } from "../turn-result";
|
|
2
3
|
import type { PromptCorrelation } from "./prompt-reconciliation";
|
|
3
4
|
export declare const RECONCILIATION_STORE_VERSION = 1;
|
|
4
5
|
export declare const RECONCILIATION_SESSION_ID_PATTERN: RegExp;
|
|
@@ -19,6 +20,7 @@ export interface DurableReconciliationRecord extends PromptCorrelation {
|
|
|
19
20
|
pendingOutcome?: SdkPromptTerminalOutcome;
|
|
20
21
|
/** Skill-only safe token; never skill args bodies. */
|
|
21
22
|
skillName?: string;
|
|
23
|
+
content?: TurnResultContent;
|
|
22
24
|
}
|
|
23
25
|
export interface ReconciliationStoreDocument {
|
|
24
26
|
version: typeof RECONCILIATION_STORE_VERSION;
|
|
@@ -93,9 +93,18 @@ export declare const NOTIFICATION_PROTOCOL_VERSION = 3;
|
|
|
93
93
|
* the exact grace deadline when a failed archive publication rolls back.
|
|
94
94
|
* Generation 59 publishes the owner's attached OPEN-socket count in the
|
|
95
95
|
* heartbeat sidecar so `gjc notify health` can report a live daemon that is
|
|
96
|
-
* attached to no registered endpoint (#4128).
|
|
96
|
+
* attached to no registered endpoint (#4128). Generation 60 contains transient
|
|
97
|
+
* heartbeat-sidecar publication failures: a Windows EPERM/EACCES/EBUSY on the
|
|
98
|
+
* sidecar rename is retried bounded under the ownership-lock fence and an
|
|
99
|
+
* exhausted publication keeps the daemon alive as `publish_failed` instead of
|
|
100
|
+
* escaping as an uncaught exception, while only a proven state/lock mismatch
|
|
101
|
+
* (`not_owner`) stops the owner (#4200). Generation 61 keeps authenticated
|
|
102
|
+
* same-session transports attached while predecessor archival converges and
|
|
103
|
+
* settles non-forum close responses. Generation 62 rejects replay-gap
|
|
104
|
+
* authority claims that do not match the requested cursor, answer bounds,
|
|
105
|
+
* and retained replay suffix.
|
|
97
106
|
*/
|
|
98
|
-
export declare const DAEMON_GENERATION =
|
|
107
|
+
export declare const DAEMON_GENERATION = 62;
|
|
99
108
|
/**
|
|
100
109
|
* Serving-compatibility boundary for daemon lifecycle requests. Epoch 5
|
|
101
110
|
* requires the complete generation-36 topic authority contract, so older
|
|
@@ -296,6 +296,20 @@ export declare function readOwnerFreshnessSnapshot(input: {
|
|
|
296
296
|
settings: Settings;
|
|
297
297
|
fs?: TelegramDaemonFs;
|
|
298
298
|
}): Promise<OwnerFreshnessSnapshot>;
|
|
299
|
+
/** Outcome of a steady heartbeat sidecar renewal. */
|
|
300
|
+
export type OwnerHeartbeatSidecarRenewal =
|
|
301
|
+
/** The sidecar was published under a still-matching ownership lock. */
|
|
302
|
+
"renewed"
|
|
303
|
+
/** This process no longer matches the persisted state/lock; stop renewing. */
|
|
304
|
+
| "not_owner"
|
|
305
|
+
/**
|
|
306
|
+
* Ownership still held, but the sidecar publication failed (e.g. a transient
|
|
307
|
+
* Windows EPERM/EBUSY while another process holds the destination). The
|
|
308
|
+
* daemon must stay alive and publish again on the next cycle (#4200); a
|
|
309
|
+
* persistently failing publication eventually stales the heartbeat for
|
|
310
|
+
* outside observers, whose takeover then surfaces here as `not_owner`.
|
|
311
|
+
*/
|
|
312
|
+
| "publish_failed";
|
|
299
313
|
/** Marker-free steady heartbeat renewal. The final lock reread fences a stale writer. */
|
|
300
314
|
export declare function renewOwnerHeartbeatSidecar(input: {
|
|
301
315
|
settings: Settings;
|
|
@@ -311,7 +325,7 @@ export declare function renewOwnerHeartbeatSidecar(input: {
|
|
|
311
325
|
* rather than falsely claiming zero attachments.
|
|
312
326
|
*/
|
|
313
327
|
attachedEndpoints?: number;
|
|
314
|
-
}): Promise<
|
|
328
|
+
}): Promise<OwnerHeartbeatSidecarRenewal>;
|
|
315
329
|
export interface NotificationRootRegistration {
|
|
316
330
|
root?: string;
|
|
317
331
|
managed?: boolean;
|
|
@@ -946,6 +960,8 @@ interface SessionSocket {
|
|
|
946
960
|
pingTimer: ReturnType<typeof setInterval> | undefined;
|
|
947
961
|
/** Correlation id for the startup replay barrier. */
|
|
948
962
|
replayId: string;
|
|
963
|
+
/** Sequence cursor sent by the current startup replay request. */
|
|
964
|
+
replaySinceSeq: number;
|
|
949
965
|
/** Queues live frames until startup replay is applied. */
|
|
950
966
|
replayPending: boolean;
|
|
951
967
|
replayQueue: Record<string, unknown>[];
|
|
@@ -44,6 +44,12 @@ export interface SessionSurface {
|
|
|
44
44
|
turnId?: string;
|
|
45
45
|
clientRef?: string;
|
|
46
46
|
}): unknown | Promise<unknown>;
|
|
47
|
+
getTurnResult?(selector: {
|
|
48
|
+
kind: "prompt" | "skill";
|
|
49
|
+
clientRef?: string;
|
|
50
|
+
commandId?: string;
|
|
51
|
+
turnId?: string;
|
|
52
|
+
}): unknown | Promise<unknown>;
|
|
47
53
|
/** Q27 effective model-profile catalog from the live session registry. */
|
|
48
54
|
getModelProfiles?(): unknown[] | Promise<unknown[]>;
|
|
49
55
|
/**
|
|
@@ -92,6 +92,11 @@ export interface InvocationReconciliation {
|
|
|
92
92
|
turnId?: string;
|
|
93
93
|
clientRef?: string;
|
|
94
94
|
}): unknown;
|
|
95
|
+
lookupResult(kind: InvocationKind, selector: {
|
|
96
|
+
commandId?: string;
|
|
97
|
+
turnId?: string;
|
|
98
|
+
clientRef?: string;
|
|
99
|
+
}): unknown;
|
|
95
100
|
hydrate(): Promise<void>;
|
|
96
101
|
}
|
|
97
102
|
export declare function createInvocationReconciliation(options?: {
|
|
@@ -112,12 +117,8 @@ export interface SdkSurfaceFactoryOptions {
|
|
|
112
117
|
configOverrides?: ReadonlyMap<string, unknown>;
|
|
113
118
|
/** Session settings; used for model-usage preferences in profile-limit resolution. */
|
|
114
119
|
settings?: Settings;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
turnId?: string;
|
|
118
|
-
clientRef?: string;
|
|
119
|
-
}) => unknown;
|
|
120
|
-
skillStatusLookup?: (selector: {
|
|
120
|
+
turnResultLookup?: (selector: {
|
|
121
|
+
kind: "prompt" | "skill";
|
|
121
122
|
commandId?: string;
|
|
122
123
|
turnId?: string;
|
|
123
124
|
clientRef?: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import type { SessionSdkTransport } from "./session-runtime";
|
|
3
3
|
export interface SdkWebSocketTransportDependencies {
|
|
4
|
-
readonly filesystem?: Pick<typeof fs, "mkdir" | "writeFile" | "chmod" | "rm">;
|
|
4
|
+
readonly filesystem?: Pick<typeof fs, "mkdir" | "writeFile" | "chmod" | "rename" | "readFile" | "rm">;
|
|
5
5
|
readonly serve?: typeof Bun.serve;
|
|
6
6
|
}
|
|
7
7
|
export type SdkTransportLifecycleErrorCode = "endpoint_write_failed" | "endpoint_chmod_failed" | "endpoint_remove_failed" | "server_stop_failed";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** Canonical durable turn-result DTOs. */
|
|
2
|
+
export declare const TURN_RESULT_VERSION: 1;
|
|
3
|
+
export declare const TURN_RESULT_CONTENT_MAX_BYTES = 16384;
|
|
4
|
+
export declare const TURN_RESULT_ERROR_MESSAGE_MAX_LENGTH = 512;
|
|
5
|
+
export type TurnResultSelector = {
|
|
6
|
+
kind: "prompt" | "skill";
|
|
7
|
+
clientRef: string;
|
|
8
|
+
} | {
|
|
9
|
+
kind: "prompt" | "skill";
|
|
10
|
+
commandId: string;
|
|
11
|
+
turnId: string;
|
|
12
|
+
};
|
|
13
|
+
export type TurnResultStatus = "unknown" | "accepted" | "in_flight" | "terminal_ok" | "failed";
|
|
14
|
+
export interface TurnResultContent {
|
|
15
|
+
version: typeof TURN_RESULT_VERSION;
|
|
16
|
+
type: "text";
|
|
17
|
+
text: string;
|
|
18
|
+
byteLength: number;
|
|
19
|
+
truncated: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface TurnResultError {
|
|
22
|
+
code: string;
|
|
23
|
+
message: string;
|
|
24
|
+
}
|
|
25
|
+
export interface TurnResultPage {
|
|
26
|
+
status: TurnResultStatus;
|
|
27
|
+
kind?: "prompt" | "skill";
|
|
28
|
+
commandId?: string;
|
|
29
|
+
turnId?: string;
|
|
30
|
+
clientRef?: string;
|
|
31
|
+
acceptedAt?: number;
|
|
32
|
+
startedAt?: number;
|
|
33
|
+
terminalAt?: number;
|
|
34
|
+
content?: TurnResultContent;
|
|
35
|
+
error?: TurnResultError;
|
|
36
|
+
}
|
|
37
|
+
export declare function sanitizeTurnResultContent(text: unknown): TurnResultContent | undefined;
|
|
@@ -629,7 +629,7 @@ export declare class AgentSession {
|
|
|
629
629
|
/** Suspend provider state without closing it while a temporary model is active. */
|
|
630
630
|
beginTemporaryProviderSessionScope(reason: TemporaryModelReason): TemporaryProviderSessionScope;
|
|
631
631
|
/** Restore a temporary scope, unwinding any auto-owned scopes above it. */
|
|
632
|
-
restoreTemporaryProviderSessionScope(token: TemporaryProviderSessionScope): boolean
|
|
632
|
+
restoreTemporaryProviderSessionScope(token: TemporaryProviderSessionScope): Promise<boolean>;
|
|
633
633
|
/** Promote a temporary scope. The suspended provider state is permanently closed. */
|
|
634
634
|
commitTemporaryProviderSessionScope(token: TemporaryProviderSessionScope): boolean;
|
|
635
635
|
buildForkContextSeed(options: ForkContextSeedOptions): Promise<ForkContextSeed>;
|
|
@@ -1251,19 +1251,16 @@ export declare class AgentSession {
|
|
|
1251
1251
|
*/
|
|
1252
1252
|
isFastModeEnabled(): boolean;
|
|
1253
1253
|
/**
|
|
1254
|
-
* True when the configured
|
|
1255
|
-
*
|
|
1256
|
-
*
|
|
1257
|
-
* undefined. This is the canonical provider-aware fast-mode predicate.
|
|
1254
|
+
* True when the configured tier is realized as a fast-mode field on the
|
|
1255
|
+
* provider's wire protocol. Providers that silently drop unscoped priority
|
|
1256
|
+
* intent return false so UI indicators match the request that is sent.
|
|
1258
1257
|
*/
|
|
1259
|
-
isFastForProvider(provider?: string): boolean;
|
|
1258
|
+
isFastForProvider(provider?: string, supportsServiceTier?: boolean): boolean;
|
|
1260
1259
|
/**
|
|
1261
|
-
*
|
|
1262
|
-
* against
|
|
1263
|
-
* main session tier. Use this for `task.agentModelOverrides` role rows so the
|
|
1264
|
-
* ⚡ glyph reflects the tier the subagent actually runs under.
|
|
1260
|
+
* Wire-effective fast-mode predicate for task-tool subagent roles, evaluated
|
|
1261
|
+
* against `task.serviceTier` rather than the main session tier.
|
|
1265
1262
|
*/
|
|
1266
|
-
isFastForSubagentProvider(provider?: string): boolean;
|
|
1263
|
+
isFastForSubagentProvider(provider?: string, supportsServiceTier?: boolean): boolean;
|
|
1267
1264
|
/**
|
|
1268
1265
|
* True when the configured `serviceTier` resolves to `"priority"` for the
|
|
1269
1266
|
* *currently selected model's provider* AND fast mode was not auto-disabled
|
|
@@ -27,7 +27,7 @@ export declare class ResidentCacheTrustError extends Error {
|
|
|
27
27
|
readonly path: string;
|
|
28
28
|
constructor(reason: string, path: string, options?: ErrorOptions);
|
|
29
29
|
}
|
|
30
|
-
export declare function disposeVerifiedResidentCacheInstanceDir(instanceDir: string): void;
|
|
30
|
+
export declare function disposeVerifiedResidentCacheInstanceDir(instanceDir: string, parentDescriptor?: number): void;
|
|
31
31
|
/**
|
|
32
32
|
* Best-effort cleanup of abandoned resident-cache instances. The root is
|
|
33
33
|
* re-verified before scanning, work is bounded, and all failures fail closed.
|
|
@@ -84,6 +84,7 @@ export declare class BlobStore {
|
|
|
84
84
|
}
|
|
85
85
|
interface EphemeralBlobStoreOptions {
|
|
86
86
|
readonly adoptVerifiedDir?: boolean;
|
|
87
|
+
readonly disposalParentDescriptor?: number;
|
|
87
88
|
}
|
|
88
89
|
/** Resident-cache directories still tracked by this process. Test seam. */
|
|
89
90
|
export declare function trackedResidentCacheDirsForTest(): string[];
|
|
@@ -213,8 +214,10 @@ export interface CanonicalBlobEntry {
|
|
|
213
214
|
readonly path: string;
|
|
214
215
|
readonly bytes: number;
|
|
215
216
|
readonly mtimeMs: number;
|
|
216
|
-
readonly
|
|
217
|
-
readonly
|
|
217
|
+
readonly mtimeNs: bigint;
|
|
218
|
+
readonly dev: bigint;
|
|
219
|
+
readonly ino: bigint;
|
|
220
|
+
readonly nlink: bigint;
|
|
218
221
|
}
|
|
219
222
|
/** Longest possible `blob:sha256:<hash>` reference, used to bound chunked scans. */
|
|
220
223
|
export declare const BLOB_REFERENCE_MAX_LENGTH: number;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Model } from "@gajae-code/ai/core";
|
|
2
2
|
/**
|
|
3
3
|
* A single line in the `/fast status` report: a labelled model and whether fast
|
|
4
4
|
* mode is effective for it. The `fast` flag is resolved by the caller
|
|
@@ -36,7 +36,7 @@ export declare function formatFastStatusReport(args: FormatFastStatusReportArgs)
|
|
|
36
36
|
export interface FastStatusSessionLike {
|
|
37
37
|
readonly model?: Model;
|
|
38
38
|
/** Fast predicate against the main session tier (current model + `modelRoles`). */
|
|
39
|
-
isFastForProvider(provider?: string): boolean;
|
|
39
|
+
isFastForProvider(provider?: string, supportsServiceTier?: boolean): boolean;
|
|
40
40
|
/**
|
|
41
41
|
* Current-model EFFECTIVE fast state (intent minus any provider auto-disable).
|
|
42
42
|
* Used for the current-model row so it matches what the next request does.
|
|
@@ -44,10 +44,19 @@ export interface FastStatusSessionLike {
|
|
|
44
44
|
*/
|
|
45
45
|
isFastModeActive?(): boolean;
|
|
46
46
|
/** Fast predicate against the effective subagent tier (`task.agentModelOverrides` roles). */
|
|
47
|
-
isFastForSubagentProvider(provider?: string): boolean;
|
|
47
|
+
isFastForSubagentProvider(provider?: string, supportsServiceTier?: boolean): boolean;
|
|
48
48
|
resolveRoleModelWithThinking(role: string): {
|
|
49
49
|
model?: Model;
|
|
50
50
|
};
|
|
51
|
+
/** Runtime fallback position for the configured DEFAULT chain. */
|
|
52
|
+
getDefaultFallbackRuntimeState?(): {
|
|
53
|
+
chain: {
|
|
54
|
+
entries: readonly string[];
|
|
55
|
+
};
|
|
56
|
+
controller: {
|
|
57
|
+
activeIndex: number;
|
|
58
|
+
};
|
|
59
|
+
};
|
|
51
60
|
}
|
|
52
61
|
/** A role to enumerate in the report, with the tier source its subagent runs under. */
|
|
53
62
|
export interface FastStatusRoleTarget {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Runs each subagent on the main thread and forwards AgentEvents for progress tracking.
|
|
5
5
|
*/
|
|
6
6
|
import type { AgentTelemetryConfig, ThinkingLevel } from "@gajae-code/agent-core";
|
|
7
|
-
import type
|
|
7
|
+
import { type Model, type ServiceTier } from "@gajae-code/ai/core";
|
|
8
8
|
import { ModelRegistry } from "../config/model-registry";
|
|
9
9
|
import type { PromptTemplate } from "../config/prompt-templates";
|
|
10
10
|
import { Settings } from "../config/settings";
|
|
@@ -82,14 +82,11 @@ export interface ExecutorOptions {
|
|
|
82
82
|
/** Parent session's registry; shared by child sessions for IRC routing and roster visibility. */
|
|
83
83
|
agentRegistry?: AgentRegistry;
|
|
84
84
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* subagents, and a main-model fast-mode auto-disable does not clobber it.
|
|
85
|
+
* Parent service-tier intent captured when the child is spawned. Used when
|
|
86
|
+
* `task.serviceTier === "inherit"` so request serialization and reporting use
|
|
87
|
+
* the same immutable child settings snapshot.
|
|
89
88
|
*/
|
|
90
89
|
inheritedServiceTier?: ServiceTier;
|
|
91
|
-
/** Resolve whether the effective subagent tier grants fast mode for the selected provider. */
|
|
92
|
-
isFastForSubagentProvider?: (provider?: string) => boolean;
|
|
93
90
|
/** Override local:// protocol options so subagent shares parent's local:// root */
|
|
94
91
|
localProtocolOptions?: LocalProtocolOptions;
|
|
95
92
|
/**
|
|
@@ -237,7 +237,7 @@ export interface ToolSession {
|
|
|
237
237
|
/** Live service-tier intent of the parent session, inherited by `inherit` subagents. */
|
|
238
238
|
serviceTier?: ServiceTier;
|
|
239
239
|
/** Whether the effective subagent tier grants fast mode for a resolved provider. */
|
|
240
|
-
isFastForSubagentProvider?: (provider?: string) => boolean;
|
|
240
|
+
isFastForSubagentProvider?: (provider?: string, supportsServiceTier?: boolean) => boolean;
|
|
241
241
|
/** Plan mode state (if active) */
|
|
242
242
|
getPlanModeState?: () => PlanModeState | undefined;
|
|
243
243
|
/** Goal mode state (if active or paused) */
|
|
@@ -90,7 +90,7 @@ export declare class ReadTool implements AgentTool<typeof readSchema, ReadToolDe
|
|
|
90
90
|
readonly name = "read";
|
|
91
91
|
readonly label = "Read";
|
|
92
92
|
readonly loadMode = "essential";
|
|
93
|
-
|
|
93
|
+
get description(): string;
|
|
94
94
|
readonly parameters: z.ZodObject<{
|
|
95
95
|
path: z.ZodString;
|
|
96
96
|
truncation: z.ZodOptional<z.ZodEnum<{
|
|
@@ -58,7 +58,7 @@ export declare class SearchTool implements AgentTool<typeof searchSchema, Search
|
|
|
58
58
|
readonly label = "Search";
|
|
59
59
|
readonly loadMode = "discoverable";
|
|
60
60
|
readonly summary = "Search file contents using ripgrep (fast text search)";
|
|
61
|
-
|
|
61
|
+
get description(): string;
|
|
62
62
|
readonly parameters: z.ZodObject<{
|
|
63
63
|
pattern: z.ZodString;
|
|
64
64
|
paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -1,13 +1,82 @@
|
|
|
1
1
|
export type EditMode = "replace" | "patch" | "hashline" | "vim" | "apply_patch";
|
|
2
|
+
/** Setting value for `edit.mode`: an executable mode or `auto` (model-family routing). */
|
|
3
|
+
export type EditModeSetting = "auto" | EditMode;
|
|
4
|
+
/** Fallback executable mode used when automatic routing cannot identify the model. */
|
|
2
5
|
export declare const DEFAULT_EDIT_MODE: EditMode;
|
|
6
|
+
/** Default `edit.mode` setting value. */
|
|
7
|
+
export declare const DEFAULT_EDIT_MODE_SETTING: EditModeSetting;
|
|
3
8
|
export declare const EDIT_MODES: EditMode[];
|
|
9
|
+
export declare const EDIT_MODE_SETTINGS: EditModeSetting[];
|
|
4
10
|
export declare function normalizeEditMode(mode?: string | null): EditMode | undefined;
|
|
11
|
+
export type ModelEditFamily = "gpt" | "codex" | "claude" | "deepseek" | "qwen" | "minimax" | "glm" | "kimi" | "unknown";
|
|
12
|
+
/**
|
|
13
|
+
* Detect the model family from a model ID, ignoring the provider prefix so
|
|
14
|
+
* equivalent models served through different providers route identically
|
|
15
|
+
* (`openai/gpt-5.4`, `openrouter/openai/gpt-5.4`, `company-gateway/gpt-5.4`).
|
|
16
|
+
*
|
|
17
|
+
* Matching only examines the final path segment. A direct family prefix or a
|
|
18
|
+
* known provider-qualified family name is accepted, while arbitrary token
|
|
19
|
+
* substrings return `unknown` instead of guessing.
|
|
20
|
+
*/
|
|
21
|
+
export declare function detectModelEditFamily(modelId: string | undefined): ModelEditFamily;
|
|
22
|
+
/** Built-in mode for a detected family; `undefined` for `unknown`. */
|
|
23
|
+
export declare function builtinEditModeForFamily(family: ModelEditFamily): EditMode | undefined;
|
|
24
|
+
export type EditModeSource = "environment" | "model-override" | "setting" | "catalog" | "builtin-family" | "fallback";
|
|
25
|
+
export type ResolvedEditModeDetails = {
|
|
26
|
+
mode: EditMode;
|
|
27
|
+
source: EditModeSource;
|
|
28
|
+
modelId?: string;
|
|
29
|
+
family?: ModelEditFamily;
|
|
30
|
+
matchedRule?: string;
|
|
31
|
+
};
|
|
32
|
+
/** Raw `edit.modelVariants` match: pattern plus the configured (unvalidated) value. */
|
|
33
|
+
export type EditVariantMatch = {
|
|
34
|
+
pattern: string;
|
|
35
|
+
value: string;
|
|
36
|
+
};
|
|
5
37
|
export interface EditModeSettingsLike {
|
|
6
38
|
get(key: "edit.mode"): unknown;
|
|
39
|
+
/** Legacy accessor: first *valid* matching `edit.modelVariants` value. */
|
|
7
40
|
getEditVariantForModel?(model: string | undefined): EditMode | null;
|
|
41
|
+
/**
|
|
42
|
+
* Discriminated accessor: first matching `edit.modelVariants` rule with its
|
|
43
|
+
* raw value. Lets the resolver fail closed on invalid matched values
|
|
44
|
+
* instead of silently falling through to another mode.
|
|
45
|
+
*/
|
|
46
|
+
matchEditVariantForModel?(model: string | undefined): EditVariantMatch | null;
|
|
8
47
|
}
|
|
9
48
|
export interface EditModeSessionLike {
|
|
10
49
|
settings: EditModeSettingsLike;
|
|
11
50
|
getActiveModelString?: () => string | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Optional model-catalog edit recommendation; beats the built-in family
|
|
53
|
+
* mapping when present.
|
|
54
|
+
*
|
|
55
|
+
* Extension point: production sessions do not wire this yet — the model
|
|
56
|
+
* catalog does not expose an edit-protocol recommendation (`applyPatchToolType`
|
|
57
|
+
* is the OpenAI wire representation of an already-selected `apply_patch`
|
|
58
|
+
* mode, not a mode selector). Catalog metadata generation is deliberately
|
|
59
|
+
* deferred until the family-based path is proven for arbitrary custom-provider
|
|
60
|
+
* IDs; when it lands, sessions can expose this hook without a resolver change.
|
|
61
|
+
*/
|
|
62
|
+
getCatalogEditMode?: () => EditMode | undefined;
|
|
12
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Read the forced edit mode from `GJC_EDIT_VARIANT`/`PI_EDIT_VARIANT`.
|
|
66
|
+
* The environment force is the emergency kill switch and beats every other
|
|
67
|
+
* source. Invalid values fail fast; empty and `auto` mean "not forced".
|
|
68
|
+
*/
|
|
69
|
+
export declare function resolveForcedEnvEditMode(): EditMode | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* Resolve the active edit mode with provenance.
|
|
72
|
+
*
|
|
73
|
+
* Precedence:
|
|
74
|
+
* 1. `GJC_EDIT_VARIANT`/`PI_EDIT_VARIANT` environment force (invalid → throw).
|
|
75
|
+
* 2. Matching user `edit.modelVariants` rule (matched invalid → throw).
|
|
76
|
+
* 3. Explicit `edit.mode` setting when it is not `auto`.
|
|
77
|
+
* 4. Model-catalog edit recommendation, when the session exposes one.
|
|
78
|
+
* 5. Built-in model-family mapping.
|
|
79
|
+
* 6. `hashline` fallback.
|
|
80
|
+
*/
|
|
81
|
+
export declare function resolveEditModeDetails(session: EditModeSessionLike): ResolvedEditModeDetails;
|
|
13
82
|
export declare function resolveEditMode(session: EditModeSessionLike): EditMode;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@gajae-code/coding-agent",
|
|
4
|
-
"version": "0.13.
|
|
4
|
+
"version": "0.13.1",
|
|
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",
|
|
@@ -54,13 +54,13 @@
|
|
|
54
54
|
"@agentclientprotocol/sdk": "1.3.0",
|
|
55
55
|
"@babel/parser": "^7.29.3",
|
|
56
56
|
"@mozilla/readability": "^0.6.0",
|
|
57
|
-
"@gajae-code/stats": "0.13.
|
|
58
|
-
"@gajae-code/agent-core": "0.13.
|
|
59
|
-
"@gajae-code/ai": "0.13.
|
|
60
|
-
"@gajae-code/bridge-client": "0.13.
|
|
61
|
-
"@gajae-code/natives": "0.13.
|
|
62
|
-
"@gajae-code/tui": "0.13.
|
|
63
|
-
"@gajae-code/utils": "0.13.
|
|
57
|
+
"@gajae-code/stats": "0.13.1",
|
|
58
|
+
"@gajae-code/agent-core": "0.13.1",
|
|
59
|
+
"@gajae-code/ai": "0.13.1",
|
|
60
|
+
"@gajae-code/bridge-client": "0.13.1",
|
|
61
|
+
"@gajae-code/natives": "0.13.1",
|
|
62
|
+
"@gajae-code/tui": "0.13.1",
|
|
63
|
+
"@gajae-code/utils": "0.13.1",
|
|
64
64
|
"@puppeteer/browsers": "^2.13.0",
|
|
65
65
|
"@types/turndown": "5.0.6",
|
|
66
66
|
"@xterm/headless": "^6.0.0",
|
|
@@ -19,16 +19,18 @@ function defaultKeys(
|
|
|
19
19
|
bindingId: keyof typeof KEYBINDINGS,
|
|
20
20
|
): string {
|
|
21
21
|
const defaultsByPlatform = DOCUMENTATION_PLATFORMS.map(platform => {
|
|
22
|
+
let keys: string | readonly string[];
|
|
22
23
|
switch (bindingId) {
|
|
23
24
|
case "app.message.queue":
|
|
24
|
-
|
|
25
|
+
keys = defaultMessageQueueKeysForPlatform(platform);
|
|
26
|
+
break;
|
|
25
27
|
case "app.clipboard.pasteImage":
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
28
|
+
keys = defaultClipboardPasteImageKeysForPlatform(platform);
|
|
29
|
+
break;
|
|
30
|
+
default:
|
|
31
|
+
keys = value.defaultKeys;
|
|
31
32
|
}
|
|
33
|
+
return (typeof keys === "string" ? keys : keys.join(", ")) || "_(none)_";
|
|
32
34
|
});
|
|
33
35
|
const groups = new Map<string, string[]>();
|
|
34
36
|
for (const [index, keys] of defaultsByPlatform.entries()) {
|
package/src/commands/sdk.ts
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
writeSessionLifecycleReady,
|
|
25
25
|
} from "../sdk/broker/lifecycle";
|
|
26
26
|
import { processIncarnation } from "../sdk/broker/process-incarnation";
|
|
27
|
+
import { writeBrokerStartupFailureMarker } from "../sdk/broker/startup-failure";
|
|
27
28
|
import { runSdkSessionCli } from "../sdk/cli";
|
|
28
29
|
import { runSdkGuidesCli } from "../sdk/guides/cli";
|
|
29
30
|
import { type CreateLifecycleAgentSessionResult, createLifecycleAgentSession } from "../sdk/lifecycle-session";
|
|
@@ -913,8 +914,28 @@ export default class Sdk extends Command {
|
|
|
913
914
|
return policy === "disabled" ? "disabled" : "copy-retain";
|
|
914
915
|
},
|
|
915
916
|
});
|
|
916
|
-
|
|
917
|
-
|
|
917
|
+
try {
|
|
918
|
+
await broker.start();
|
|
919
|
+
} catch (error) {
|
|
920
|
+
// Surface why this detached broker failed before discovery: the caller
|
|
921
|
+
// spawns with stdio ignored, so the durable marker is the only channel.
|
|
922
|
+
await writeBrokerStartupFailureMarker(agentDir, {
|
|
923
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
924
|
+
exitCode: 1,
|
|
925
|
+
signal: null,
|
|
926
|
+
});
|
|
927
|
+
throw error;
|
|
928
|
+
}
|
|
929
|
+
if (!broker.ownsDiscovery) {
|
|
930
|
+
// Another broker owns discovery; this process exits cleanly (code 0) as
|
|
931
|
+
// the race loser. Record why so the caller can diagnose a missing winner.
|
|
932
|
+
await writeBrokerStartupFailureMarker(agentDir, {
|
|
933
|
+
reason: "Another broker owns the lock/discovery; this broker exited as the race loser.",
|
|
934
|
+
exitCode: 0,
|
|
935
|
+
signal: null,
|
|
936
|
+
});
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
918
939
|
// A live broker must not keep advertising sessions whose host process is
|
|
919
940
|
// gone; the sweep is the broker-side half of the host reaping bound.
|
|
920
941
|
const stopSweep = startBrokerDeadRegistrationSweep(broker);
|
package/src/config/file-lock.ts
CHANGED
|
@@ -8,11 +8,13 @@ export interface FileLockOptions {
|
|
|
8
8
|
staleMs?: number;
|
|
9
9
|
retries?: number;
|
|
10
10
|
retryDelayMs?: number;
|
|
11
|
+
signal?: AbortSignal;
|
|
12
|
+
onAcquired?: () => void;
|
|
11
13
|
/** Stable host identity required to safely reclaim locks on a shared volume. */
|
|
12
14
|
ownerHostId?: string;
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
const DEFAULT_OPTIONS: Required<Omit<FileLockOptions, "ownerHostId">> = {
|
|
17
|
+
const DEFAULT_OPTIONS: Required<Omit<FileLockOptions, "ownerHostId" | "signal" | "onAcquired">> = {
|
|
16
18
|
staleMs: 10_000,
|
|
17
19
|
retries: 50,
|
|
18
20
|
retryDelayMs: 100,
|
|
@@ -258,13 +260,18 @@ async function removeStaleLockForAcquire(lockPath: string, snapshot: LockStaleSn
|
|
|
258
260
|
}
|
|
259
261
|
}
|
|
260
262
|
|
|
261
|
-
async function tryAcquireLock(
|
|
263
|
+
async function tryAcquireLock(
|
|
264
|
+
lockPath: string,
|
|
265
|
+
ownerHostId?: string,
|
|
266
|
+
onAcquired?: () => void,
|
|
267
|
+
): Promise<LockInfo | null> {
|
|
262
268
|
await fs.mkdir(path.dirname(lockPath), { recursive: true });
|
|
263
269
|
const afterParentMkdir = FileLockTestHooks.afterParentMkdir;
|
|
264
270
|
if (afterParentMkdir) await afterParentMkdir(lockPath);
|
|
265
271
|
if (ownerHostId === undefined) {
|
|
266
272
|
try {
|
|
267
273
|
await fs.mkdir(lockPath);
|
|
274
|
+
onAcquired?.();
|
|
268
275
|
return await writeLockInfo(lockPath, lockInfo());
|
|
269
276
|
} catch (error) {
|
|
270
277
|
if ((error as NodeJS.ErrnoException).code === "EEXIST") return null;
|
|
@@ -279,6 +286,7 @@ async function tryAcquireLock(lockPath: string, ownerHostId?: string): Promise<L
|
|
|
279
286
|
await writeLockInfo(pendingPath, owner);
|
|
280
287
|
try {
|
|
281
288
|
await fs.rename(pendingPath, lockPath);
|
|
289
|
+
onAcquired?.();
|
|
282
290
|
return owner;
|
|
283
291
|
} catch (error) {
|
|
284
292
|
const code = (error as NodeJS.ErrnoException).code;
|
|
@@ -308,12 +316,24 @@ async function acquireLock(filePath: string, options: FileLockOptions = {}): Pro
|
|
|
308
316
|
const lockPath = getLockPath(filePath);
|
|
309
317
|
const contentionStartTimes = new Map<string, string | null>();
|
|
310
318
|
for (let attempt = 0; attempt < opts.retries; attempt++) {
|
|
311
|
-
|
|
319
|
+
if (opts.signal?.aborted) throw opts.signal.reason ?? new Error("File lock acquisition aborted");
|
|
320
|
+
const owner = await tryAcquireLock(lockPath, opts.ownerHostId, opts.onAcquired);
|
|
312
321
|
if (owner) return () => releaseLock(lockPath, owner);
|
|
313
|
-
|
|
314
322
|
const stale = await staleLockSnapshot(lockPath, opts.staleMs, opts.ownerHostId, contentionStartTimes);
|
|
315
323
|
if (await removeStaleLockForAcquire(lockPath, stale)) continue;
|
|
316
|
-
|
|
324
|
+
if (!opts.signal) {
|
|
325
|
+
await Bun.sleep(opts.retryDelayMs);
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
const { promise, resolve, reject } = Promise.withResolvers<void>();
|
|
329
|
+
const onAbort = (): void => reject(opts.signal?.reason ?? new Error("File lock acquisition aborted"));
|
|
330
|
+
opts.signal.addEventListener("abort", onAbort, { once: true });
|
|
331
|
+
void Bun.sleep(opts.retryDelayMs).then(resolve);
|
|
332
|
+
try {
|
|
333
|
+
await promise;
|
|
334
|
+
} finally {
|
|
335
|
+
opts.signal.removeEventListener("abort", onAbort);
|
|
336
|
+
}
|
|
317
337
|
}
|
|
318
338
|
throw new Error(`Failed to acquire lock for ${filePath} after ${opts.retries} attempts`);
|
|
319
339
|
}
|