@adhdev/daemon-core 0.9.54 → 0.9.56
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/dist/boot/daemon-lifecycle.d.ts +5 -0
- package/dist/cli-adapters/provider-cli-adapter.d.ts +9 -0
- package/dist/cli-adapters/provider-cli-config.d.ts +1 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +24 -0
- package/dist/commands/handler.d.ts +5 -0
- package/dist/git/git-commands.d.ts +53 -0
- package/dist/git/git-types.d.ts +1 -1
- package/dist/git/index.d.ts +2 -0
- package/dist/git/turn-snapshot-tracker.d.ts +16 -0
- package/dist/index.js +249 -18
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +248 -18
- package/dist/index.mjs.map +1 -1
- package/dist/providers/contracts.d.ts +2 -0
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/boot/daemon-lifecycle.ts +6 -0
- package/src/cli-adapters/provider-cli-adapter.ts +62 -4
- package/src/cli-adapters/provider-cli-config.d.ts +1 -0
- package/src/cli-adapters/provider-cli-config.ts +2 -0
- package/src/cli-adapters/provider-cli-shared.d.ts +2 -0
- package/src/cli-adapters/provider-cli-shared.ts +8 -0
- package/src/commands/handler.ts +16 -0
- package/src/git/git-commands.ts +209 -12
- package/src/git/git-types.ts +2 -1
- package/src/git/index.ts +3 -0
- package/src/git/turn-snapshot-tracker.ts +31 -0
- package/src/providers/contracts.d.ts +8 -0
- package/src/providers/contracts.ts +2 -0
- package/src/providers/provider-schema.ts +1 -0
|
@@ -52,6 +52,11 @@ export interface DaemonInitConfig {
|
|
|
52
52
|
statusInstanceId?: string;
|
|
53
53
|
statusVersion?: string;
|
|
54
54
|
statusDaemonMode?: boolean;
|
|
55
|
+
/** Fired before send_chat is dispatched — used for turn snapshot hooks */
|
|
56
|
+
onBeforeSendChat?: (params: {
|
|
57
|
+
workspace: string;
|
|
58
|
+
sessionId: string;
|
|
59
|
+
}) => void;
|
|
55
60
|
}
|
|
56
61
|
export interface DaemonComponents {
|
|
57
62
|
providerLoader: ProviderLoader;
|
|
@@ -94,6 +94,12 @@ export declare class ProviderCliAdapter implements CliAdapter {
|
|
|
94
94
|
private accumulatedRawBuffer;
|
|
95
95
|
/** Current visible terminal screen snapshot */
|
|
96
96
|
private terminalScreen;
|
|
97
|
+
private static readonly MAX_RESPONSE_BUFFER;
|
|
98
|
+
private static readonly MAX_RECENT_OUTPUT_BUFFER;
|
|
99
|
+
private responseBufferDroppedChars;
|
|
100
|
+
private recentOutputDroppedChars;
|
|
101
|
+
private accumulatedBufferDroppedChars;
|
|
102
|
+
private accumulatedRawBufferDroppedChars;
|
|
97
103
|
/** Max accumulated buffer size. Sized to comfortably hold a single long
|
|
98
104
|
* Hermes turn (tool calls + reasoning + final bubble) without the
|
|
99
105
|
* rolling window pushing the turn's ╭─ opening line out of view. */
|
|
@@ -111,6 +117,8 @@ export declare class ProviderCliAdapter implements CliAdapter {
|
|
|
111
117
|
private readonly providerResolutionMeta;
|
|
112
118
|
private static readonly FINISH_RETRY_DELAY_MS;
|
|
113
119
|
private static readonly MAX_FINISH_RETRIES;
|
|
120
|
+
private getBufferState;
|
|
121
|
+
private recordBoundedAppendDrop;
|
|
114
122
|
private buildCommittedMessagesActivitySignature;
|
|
115
123
|
private syncMessageViews;
|
|
116
124
|
getLastCommittedMessageActivityAt(): number;
|
|
@@ -136,6 +144,7 @@ export declare class ProviderCliAdapter implements CliAdapter {
|
|
|
136
144
|
private readonly sendDelayMs;
|
|
137
145
|
private readonly sendKey;
|
|
138
146
|
private readonly submitStrategy;
|
|
147
|
+
private readonly requirePromptEchoBeforeSubmit;
|
|
139
148
|
private static readonly SCRIPT_STATUS_DEBOUNCE_MS;
|
|
140
149
|
constructor(provider: CliProviderModule, workingDir: string, extraArgs?: string[], transportFactory?: PtyTransportFactory);
|
|
141
150
|
/** Inject CLI scripts after construction (e.g. when resolved by ProviderLoader) */
|
|
@@ -27,6 +27,7 @@ export interface ResolvedCliAdapterConfig {
|
|
|
27
27
|
sendDelayMs: number;
|
|
28
28
|
sendKey: string;
|
|
29
29
|
submitStrategy: 'wait_for_echo' | 'immediate';
|
|
30
|
+
requirePromptEchoBeforeSubmit: boolean;
|
|
30
31
|
providerResolutionMeta: ProviderResolutionMeta;
|
|
31
32
|
}
|
|
32
33
|
export declare function resolveCliAdapterConfig(provider: CliProviderModule): ResolvedCliAdapterConfig;
|
|
@@ -22,6 +22,28 @@ export interface CliSessionStatus {
|
|
|
22
22
|
} | null;
|
|
23
23
|
errorMessage?: string;
|
|
24
24
|
errorReason?: string;
|
|
25
|
+
bufferState?: {
|
|
26
|
+
responseBuffer?: {
|
|
27
|
+
truncated: boolean;
|
|
28
|
+
droppedChars: number;
|
|
29
|
+
maxChars: number;
|
|
30
|
+
};
|
|
31
|
+
recentOutputBuffer?: {
|
|
32
|
+
truncated: boolean;
|
|
33
|
+
droppedChars: number;
|
|
34
|
+
maxChars: number;
|
|
35
|
+
};
|
|
36
|
+
accumulatedBuffer?: {
|
|
37
|
+
truncated: boolean;
|
|
38
|
+
droppedChars: number;
|
|
39
|
+
maxChars: number;
|
|
40
|
+
};
|
|
41
|
+
accumulatedRawBuffer?: {
|
|
42
|
+
truncated: boolean;
|
|
43
|
+
droppedChars: number;
|
|
44
|
+
maxChars: number;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
25
47
|
}
|
|
26
48
|
export interface ParsedSession {
|
|
27
49
|
status: string;
|
|
@@ -123,6 +145,8 @@ export interface CliProviderModule {
|
|
|
123
145
|
sendDelayMs?: number;
|
|
124
146
|
sendKey?: string;
|
|
125
147
|
submitStrategy?: 'wait_for_echo' | 'immediate';
|
|
148
|
+
/** Require the typed prompt to be visible on the PTY screen before sending Enter. */
|
|
149
|
+
requirePromptEchoBeforeSubmit?: boolean;
|
|
126
150
|
/** Allow sending another prompt while the CLI is still generating so users can intervene mid-turn. */
|
|
127
151
|
allowInputDuringGeneration?: boolean;
|
|
128
152
|
/** When provider-owned, daemon treats provider parser output as canonical transcript authority. */
|
|
@@ -34,6 +34,11 @@ export interface CommandContext {
|
|
|
34
34
|
onProviderSettingChanged?: (providerType: string, key: string, value: any) => Promise<void> | void;
|
|
35
35
|
onProviderSourceConfigChanged?: () => Promise<void> | void;
|
|
36
36
|
gitCommandServices?: GitCommandServices;
|
|
37
|
+
/** Fired synchronously before send_chat is dispatched; fire-and-forget for callers */
|
|
38
|
+
onBeforeSendChat?: (params: {
|
|
39
|
+
workspace: string;
|
|
40
|
+
sessionId: string;
|
|
41
|
+
}) => void;
|
|
37
42
|
}
|
|
38
43
|
/**
|
|
39
44
|
* Shared helpers interface — passed to sub-module command functions
|
|
@@ -22,6 +22,16 @@ export interface GitLogResult extends GitRepoIdentity {
|
|
|
22
22
|
truncated: boolean;
|
|
23
23
|
lastCheckedAt: number;
|
|
24
24
|
}
|
|
25
|
+
export interface GitCheckpointResult extends GitRepoIdentity {
|
|
26
|
+
commit: string;
|
|
27
|
+
message: string;
|
|
28
|
+
lastCheckedAt: number;
|
|
29
|
+
}
|
|
30
|
+
export interface GitStashPushResult extends GitRepoIdentity {
|
|
31
|
+
stashRef: string;
|
|
32
|
+
message: string;
|
|
33
|
+
lastCheckedAt: number;
|
|
34
|
+
}
|
|
25
35
|
export interface GitCommandServices {
|
|
26
36
|
getStatus?: (params: {
|
|
27
37
|
workspace: string;
|
|
@@ -53,6 +63,33 @@ export interface GitCommandServices {
|
|
|
53
63
|
since?: string;
|
|
54
64
|
until?: string;
|
|
55
65
|
}) => Promise<GitLogResult> | GitLogResult;
|
|
66
|
+
checkpoint?: (params: {
|
|
67
|
+
workspace: string;
|
|
68
|
+
message: string;
|
|
69
|
+
includeUntracked?: boolean;
|
|
70
|
+
}) => Promise<GitCheckpointResult> | GitCheckpointResult;
|
|
71
|
+
stashPush?: (params: {
|
|
72
|
+
workspace: string;
|
|
73
|
+
message: string;
|
|
74
|
+
includeUntracked?: boolean;
|
|
75
|
+
}) => Promise<GitStashPushResult> | GitStashPushResult;
|
|
76
|
+
stashPop?: (params: {
|
|
77
|
+
workspace: string;
|
|
78
|
+
stashRef?: string;
|
|
79
|
+
}) => Promise<void>;
|
|
80
|
+
checkoutFiles?: (params: {
|
|
81
|
+
workspace: string;
|
|
82
|
+
paths: string[];
|
|
83
|
+
}) => Promise<{
|
|
84
|
+
checkedOut: string[];
|
|
85
|
+
}>;
|
|
86
|
+
getRemoteUrl?: (params: {
|
|
87
|
+
workspace: string;
|
|
88
|
+
remote?: string;
|
|
89
|
+
}) => Promise<{
|
|
90
|
+
remoteUrl: string;
|
|
91
|
+
remote: string;
|
|
92
|
+
}>;
|
|
56
93
|
}
|
|
57
94
|
type GitCommandFailure = {
|
|
58
95
|
success: false;
|
|
@@ -77,6 +114,22 @@ type GitCommandSuccess = {
|
|
|
77
114
|
} | {
|
|
78
115
|
success: true;
|
|
79
116
|
log: GitLogResult;
|
|
117
|
+
} | {
|
|
118
|
+
success: true;
|
|
119
|
+
checkpoint: GitCheckpointResult;
|
|
120
|
+
} | {
|
|
121
|
+
success: true;
|
|
122
|
+
stash: GitStashPushResult;
|
|
123
|
+
} | {
|
|
124
|
+
success: true;
|
|
125
|
+
stashPopped: true;
|
|
126
|
+
} | {
|
|
127
|
+
success: true;
|
|
128
|
+
checkedOut: string[];
|
|
129
|
+
} | {
|
|
130
|
+
success: true;
|
|
131
|
+
remoteUrl: string;
|
|
132
|
+
remote: string;
|
|
80
133
|
};
|
|
81
134
|
export type GitCommandResult = GitCommandSuccess | GitCommandFailure;
|
|
82
135
|
export declare function createDefaultGitCommandServices(): GitCommandServices;
|
package/dist/git/git-types.d.ts
CHANGED
|
@@ -110,4 +110,4 @@ export interface GitWorkspaceUpdate {
|
|
|
110
110
|
seq: number;
|
|
111
111
|
timestamp: number;
|
|
112
112
|
}
|
|
113
|
-
export type GitCommandName = 'git_status' | 'git_diff_summary' | 'git_diff_file' | 'git_snapshot_create' | 'git_snapshot_compare' | 'git_log' | 'git_checkpoint' | 'git_stash_push' | 'git_stash_pop' | 'git_checkout_files';
|
|
113
|
+
export type GitCommandName = 'git_status' | 'git_diff_summary' | 'git_diff_file' | 'git_snapshot_create' | 'git_snapshot_compare' | 'git_log' | 'git_checkpoint' | 'git_stash_push' | 'git_stash_pop' | 'git_checkout_files' | 'git_remote_url';
|
package/dist/git/index.d.ts
CHANGED
|
@@ -12,3 +12,5 @@ export { createGitWorkspaceMonitor, DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS, GitW
|
|
|
12
12
|
export type { GitWorkspaceCacheEntry, GitWorkspaceMonitorOptions, GitWorkspaceSubscription, GitWorkspaceUpdateListener, NormalizedWorkspaceGitSubscriptionParams, NormalizeGitWorkspaceSubscriptionOptions, } from './git-monitor.js';
|
|
13
13
|
export { createDefaultGitCommandServices, handleGitCommand, isGitCommandName } from './git-commands.js';
|
|
14
14
|
export type { GitCommandResult, GitCommandServices, GitFileDiff, GitLogEntry, GitLogResult, } from './git-commands.js';
|
|
15
|
+
export { TurnSnapshotTracker } from './turn-snapshot-tracker.js';
|
|
16
|
+
export type { TurnCompletedCallback } from './turn-snapshot-tracker.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tracks agent session status transitions and fires snapshot callbacks on turn completion.
|
|
3
|
+
* "Busy" = streaming | waiting_approval
|
|
4
|
+
* "Completed" = idle | error (transition from busy)
|
|
5
|
+
*/
|
|
6
|
+
export type TurnCompletedCallback = (params: {
|
|
7
|
+
sessionId: string;
|
|
8
|
+
workspace: string;
|
|
9
|
+
}) => void;
|
|
10
|
+
export declare class TurnSnapshotTracker {
|
|
11
|
+
private lastStatus;
|
|
12
|
+
private onTurnCompleted;
|
|
13
|
+
constructor(onTurnCompleted: TurnCompletedCallback);
|
|
14
|
+
record(sessionId: string, status: string, workspace: string | null | undefined): void;
|
|
15
|
+
forget(sessionId: string): void;
|
|
16
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1942,6 +1942,7 @@ function resolveCliAdapterConfig(provider) {
|
|
|
1942
1942
|
sendDelayMs: typeof provider.sendDelayMs === "number" ? Math.max(0, provider.sendDelayMs) : 0,
|
|
1943
1943
|
sendKey: typeof provider.sendKey === "string" && provider.sendKey.length > 0 ? provider.sendKey : "\r",
|
|
1944
1944
|
submitStrategy: provider.submitStrategy === "immediate" ? "immediate" : "wait_for_echo",
|
|
1945
|
+
requirePromptEchoBeforeSubmit: provider.requirePromptEchoBeforeSubmit === true,
|
|
1945
1946
|
providerResolutionMeta: {
|
|
1946
1947
|
type: provider.type,
|
|
1947
1948
|
name: provider.name,
|
|
@@ -2207,6 +2208,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
2207
2208
|
this.sendDelayMs = resolvedConfig.sendDelayMs;
|
|
2208
2209
|
this.sendKey = resolvedConfig.sendKey;
|
|
2209
2210
|
this.submitStrategy = resolvedConfig.submitStrategy;
|
|
2211
|
+
this.requirePromptEchoBeforeSubmit = resolvedConfig.requirePromptEchoBeforeSubmit;
|
|
2210
2212
|
this.providerResolutionMeta = resolvedConfig.providerResolutionMeta;
|
|
2211
2213
|
this.cliScripts = provider.scripts || {};
|
|
2212
2214
|
const scriptNames = listCliScriptNames(this.cliScripts);
|
|
@@ -2303,6 +2305,12 @@ var init_provider_cli_adapter = __esm({
|
|
|
2303
2305
|
accumulatedRawBuffer = "";
|
|
2304
2306
|
/** Current visible terminal screen snapshot */
|
|
2305
2307
|
terminalScreen = new TerminalScreen(24, 80);
|
|
2308
|
+
static MAX_RESPONSE_BUFFER = 8e3;
|
|
2309
|
+
static MAX_RECENT_OUTPUT_BUFFER = 1e3;
|
|
2310
|
+
responseBufferDroppedChars = 0;
|
|
2311
|
+
recentOutputDroppedChars = 0;
|
|
2312
|
+
accumulatedBufferDroppedChars = 0;
|
|
2313
|
+
accumulatedRawBufferDroppedChars = 0;
|
|
2306
2314
|
/** Max accumulated buffer size. Sized to comfortably hold a single long
|
|
2307
2315
|
* Hermes turn (tool calls + reasoning + final bubble) without the
|
|
2308
2316
|
* rolling window pushing the turn's ╭─ opening line out of view. */
|
|
@@ -2320,6 +2328,23 @@ var init_provider_cli_adapter = __esm({
|
|
|
2320
2328
|
providerResolutionMeta;
|
|
2321
2329
|
static FINISH_RETRY_DELAY_MS = 300;
|
|
2322
2330
|
static MAX_FINISH_RETRIES = 2;
|
|
2331
|
+
getBufferState() {
|
|
2332
|
+
const build = (droppedChars, maxChars) => droppedChars > 0 ? { truncated: true, droppedChars, maxChars } : void 0;
|
|
2333
|
+
const responseBuffer = build(this.responseBufferDroppedChars, _ProviderCliAdapter.MAX_RESPONSE_BUFFER);
|
|
2334
|
+
const recentOutputBuffer = build(this.recentOutputDroppedChars, _ProviderCliAdapter.MAX_RECENT_OUTPUT_BUFFER);
|
|
2335
|
+
const accumulatedBuffer = build(this.accumulatedBufferDroppedChars, _ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
|
|
2336
|
+
const accumulatedRawBuffer = build(this.accumulatedRawBufferDroppedChars, _ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
|
|
2337
|
+
if (!responseBuffer && !recentOutputBuffer && !accumulatedBuffer && !accumulatedRawBuffer) return void 0;
|
|
2338
|
+
return {
|
|
2339
|
+
...responseBuffer ? { responseBuffer } : {},
|
|
2340
|
+
...recentOutputBuffer ? { recentOutputBuffer } : {},
|
|
2341
|
+
...accumulatedBuffer ? { accumulatedBuffer } : {},
|
|
2342
|
+
...accumulatedRawBuffer ? { accumulatedRawBuffer } : {}
|
|
2343
|
+
};
|
|
2344
|
+
}
|
|
2345
|
+
recordBoundedAppendDrop(previousLength, appendedLength, nextLength) {
|
|
2346
|
+
return Math.max(0, previousLength + appendedLength - nextLength);
|
|
2347
|
+
}
|
|
2323
2348
|
buildCommittedMessagesActivitySignature() {
|
|
2324
2349
|
const last = this.committedMessages[this.committedMessages.length - 1];
|
|
2325
2350
|
return [
|
|
@@ -2503,6 +2528,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
2503
2528
|
sendDelayMs;
|
|
2504
2529
|
sendKey;
|
|
2505
2530
|
submitStrategy;
|
|
2531
|
+
requirePromptEchoBeforeSubmit;
|
|
2506
2532
|
static SCRIPT_STATUS_DEBOUNCE_MS = 3e3;
|
|
2507
2533
|
/** Inject CLI scripts after construction (e.g. when resolved by ProviderLoader) */
|
|
2508
2534
|
setCliScripts(scripts) {
|
|
@@ -2684,7 +2710,9 @@ var init_provider_cli_adapter = __esm({
|
|
|
2684
2710
|
this.scheduleStartupSettleCheck();
|
|
2685
2711
|
}
|
|
2686
2712
|
if (this.isWaitingForResponse && cleanData) {
|
|
2687
|
-
|
|
2713
|
+
const previousResponseLen = this.responseBuffer.length;
|
|
2714
|
+
this.responseBuffer = appendBoundedText(this.responseBuffer, cleanData, _ProviderCliAdapter.MAX_RESPONSE_BUFFER);
|
|
2715
|
+
this.responseBufferDroppedChars += this.recordBoundedAppendDrop(previousResponseLen, cleanData.length, this.responseBuffer.length);
|
|
2688
2716
|
}
|
|
2689
2717
|
if (cleanData.trim()) {
|
|
2690
2718
|
if (this.serverConn) {
|
|
@@ -2693,14 +2721,19 @@ var init_provider_cli_adapter = __esm({
|
|
|
2693
2721
|
this.logBuffer.push({ message: cleanData.trim(), level: "info" });
|
|
2694
2722
|
}
|
|
2695
2723
|
}
|
|
2724
|
+
const prevRecentLen = this.recentOutputBuffer.length;
|
|
2696
2725
|
const prevAccumulatedLen = this.accumulatedBuffer.length;
|
|
2697
2726
|
const prevAccumulatedRawLen = this.accumulatedRawBuffer.length;
|
|
2698
|
-
this.recentOutputBuffer = appendBoundedText(this.recentOutputBuffer, cleanData,
|
|
2727
|
+
this.recentOutputBuffer = appendBoundedText(this.recentOutputBuffer, cleanData, _ProviderCliAdapter.MAX_RECENT_OUTPUT_BUFFER);
|
|
2699
2728
|
this.accumulatedBuffer = appendBoundedText(this.accumulatedBuffer, cleanData, _ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
|
|
2700
2729
|
this.accumulatedRawBuffer = appendBoundedText(this.accumulatedRawBuffer, rawData, _ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
|
|
2730
|
+
const droppedRecent = this.recordBoundedAppendDrop(prevRecentLen, cleanData.length, this.recentOutputBuffer.length);
|
|
2731
|
+
const droppedClean = this.recordBoundedAppendDrop(prevAccumulatedLen, cleanData.length, this.accumulatedBuffer.length);
|
|
2732
|
+
const droppedRaw = this.recordBoundedAppendDrop(prevAccumulatedRawLen, rawData.length, this.accumulatedRawBuffer.length);
|
|
2733
|
+
this.recentOutputDroppedChars += droppedRecent;
|
|
2734
|
+
this.accumulatedBufferDroppedChars += droppedClean;
|
|
2735
|
+
this.accumulatedRawBufferDroppedChars += droppedRaw;
|
|
2701
2736
|
if (this.currentTurnScope) {
|
|
2702
|
-
const droppedClean = prevAccumulatedLen + cleanData.length - this.accumulatedBuffer.length;
|
|
2703
|
-
const droppedRaw = prevAccumulatedRawLen + rawData.length - this.accumulatedRawBuffer.length;
|
|
2704
2737
|
if (droppedClean > 0) {
|
|
2705
2738
|
this.currentTurnScope.bufferStart = Math.max(0, this.currentTurnScope.bufferStart - droppedClean);
|
|
2706
2739
|
}
|
|
@@ -3530,13 +3563,15 @@ var init_provider_cli_adapter = __esm({
|
|
|
3530
3563
|
effectiveModal = parsedModal;
|
|
3531
3564
|
}
|
|
3532
3565
|
}
|
|
3566
|
+
const bufferState = this.getBufferState();
|
|
3533
3567
|
return {
|
|
3534
3568
|
status: effectiveStatus,
|
|
3535
3569
|
messages: [...this.committedMessages],
|
|
3536
3570
|
workingDir: this.workingDir,
|
|
3537
3571
|
activeModal: effectiveModal,
|
|
3538
3572
|
errorMessage: this.parseErrorMessage || void 0,
|
|
3539
|
-
errorReason: this.parseErrorMessage ? "parse_error" : void 0
|
|
3573
|
+
errorReason: this.parseErrorMessage ? "parse_error" : void 0,
|
|
3574
|
+
...bufferState ? { bufferState } : {}
|
|
3540
3575
|
};
|
|
3541
3576
|
}
|
|
3542
3577
|
seedCommittedMessages(messages) {
|
|
@@ -3718,10 +3753,12 @@ var init_provider_cli_adapter = __esm({
|
|
|
3718
3753
|
messages: hydratedMessages,
|
|
3719
3754
|
activeModal: parsed.activeModal ?? this.activeModal,
|
|
3720
3755
|
providerSessionId: typeof parsed.providerSessionId === "string" ? parsed.providerSessionId : void 0,
|
|
3756
|
+
...this.getBufferState() ? { bufferState: this.getBufferState() } : {},
|
|
3721
3757
|
...this.providerOwnsTranscript() ? { transcriptAuthority: "provider", coverage: this.shouldUseFullProviderTranscriptContext() ? "full" : "tail" } : {}
|
|
3722
3758
|
};
|
|
3723
3759
|
} else {
|
|
3724
3760
|
const messages = [...this.committedMessages];
|
|
3761
|
+
const bufferState = this.getBufferState();
|
|
3725
3762
|
result = {
|
|
3726
3763
|
id: "cli_session",
|
|
3727
3764
|
status: this.currentStatus,
|
|
@@ -3732,7 +3769,8 @@ var init_provider_cli_adapter = __esm({
|
|
|
3732
3769
|
index: typeof message.index === "number" ? message.index : index,
|
|
3733
3770
|
receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
|
|
3734
3771
|
})),
|
|
3735
|
-
activeModal: this.activeModal
|
|
3772
|
+
activeModal: this.activeModal,
|
|
3773
|
+
...bufferState ? { bufferState } : {}
|
|
3736
3774
|
};
|
|
3737
3775
|
}
|
|
3738
3776
|
const hasVisibleAssistantMessage = Array.isArray(result?.messages) && result.messages.some((message) => message?.role === "assistant" && typeof message?.content === "string" && message.content.trim());
|
|
@@ -4023,6 +4061,22 @@ var init_provider_cli_adapter = __esm({
|
|
|
4023
4061
|
}
|
|
4024
4062
|
}
|
|
4025
4063
|
if (elapsed >= state.maxEchoWaitMs) {
|
|
4064
|
+
const diagnostic = {
|
|
4065
|
+
elapsed,
|
|
4066
|
+
maxEchoWaitMs: state.maxEchoWaitMs,
|
|
4067
|
+
submitDelayMs: state.submitDelayMs,
|
|
4068
|
+
promptSnippet: state.normalizedPromptSnippet,
|
|
4069
|
+
requirePromptEchoBeforeSubmit: this.requirePromptEchoBeforeSubmit,
|
|
4070
|
+
screenText: summarizeCliTraceText(screenText, 1e3)
|
|
4071
|
+
};
|
|
4072
|
+
this.recordTrace("submit_echo_missing", diagnostic);
|
|
4073
|
+
if (this.requirePromptEchoBeforeSubmit) {
|
|
4074
|
+
const message = `${this.cliName} prompt echo was not observed on the PTY screen before submit`;
|
|
4075
|
+
LOG.warn("CLI", `[${this.cliType}] ${message} elapsed=${elapsed}ms maxEchoWaitMs=${state.maxEchoWaitMs} screen=${JSON.stringify(diagnostic.screenText).slice(0, 240)}`);
|
|
4076
|
+
completion.rejectOnce(new Error(message));
|
|
4077
|
+
return;
|
|
4078
|
+
}
|
|
4079
|
+
LOG.warn("CLI", `[${this.cliType}] prompt echo was not observed before submit; sending submit key anyway elapsed=${elapsed}ms maxEchoWaitMs=${state.maxEchoWaitMs}`);
|
|
4026
4080
|
this.submitSendKey(state, completion);
|
|
4027
4081
|
return;
|
|
4028
4082
|
}
|
|
@@ -4487,6 +4541,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
4487
4541
|
sendDelayMs: this.sendDelayMs,
|
|
4488
4542
|
sendKey: this.sendKey,
|
|
4489
4543
|
submitStrategy: this.submitStrategy,
|
|
4544
|
+
requirePromptEchoBeforeSubmit: this.requirePromptEchoBeforeSubmit,
|
|
4490
4545
|
submitPendingUntil: this.submitPendingUntil,
|
|
4491
4546
|
responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
|
|
4492
4547
|
resizeSuppressUntil: this.resizeSuppressUntil,
|
|
@@ -4574,6 +4629,7 @@ __export(index_exports, {
|
|
|
4574
4629
|
ProviderLoader: () => ProviderLoader,
|
|
4575
4630
|
STANDALONE_CDP_SCAN_INTERVAL_MS: () => STANDALONE_CDP_SCAN_INTERVAL_MS,
|
|
4576
4631
|
SessionHostPtyTransportFactory: () => SessionHostPtyTransportFactory,
|
|
4632
|
+
TurnSnapshotTracker: () => TurnSnapshotTracker,
|
|
4577
4633
|
VersionArchive: () => VersionArchive,
|
|
4578
4634
|
appendRecentActivity: () => appendRecentActivity,
|
|
4579
4635
|
buildAssistantChatMessage: () => buildAssistantChatMessage,
|
|
@@ -5585,13 +5641,8 @@ var GIT_COMMAND_NAMES = /* @__PURE__ */ new Set([
|
|
|
5585
5641
|
"git_checkpoint",
|
|
5586
5642
|
"git_stash_push",
|
|
5587
5643
|
"git_stash_pop",
|
|
5588
|
-
"git_checkout_files"
|
|
5589
|
-
|
|
5590
|
-
var MUTATING_COMMAND_NAMES = /* @__PURE__ */ new Set([
|
|
5591
|
-
"git_checkpoint",
|
|
5592
|
-
"git_stash_push",
|
|
5593
|
-
"git_stash_pop",
|
|
5594
|
-
"git_checkout_files"
|
|
5644
|
+
"git_checkout_files",
|
|
5645
|
+
"git_remote_url"
|
|
5595
5646
|
]);
|
|
5596
5647
|
var SNAPSHOT_REASONS = /* @__PURE__ */ new Set([
|
|
5597
5648
|
"session_baseline",
|
|
@@ -5632,7 +5683,12 @@ function createDefaultGitCommandServices() {
|
|
|
5632
5683
|
turnId
|
|
5633
5684
|
}),
|
|
5634
5685
|
compareSnapshots: ({ beforeSnapshotId, afterSnapshotId }) => defaultSnapshotStore.compare(beforeSnapshotId, afterSnapshotId),
|
|
5635
|
-
getLog: ({ workspace, limit, path: filePath, since, until }) => getGitLog(workspace, { limit, path: filePath, since, until })
|
|
5686
|
+
getLog: ({ workspace, limit, path: filePath, since, until }) => getGitLog(workspace, { limit, path: filePath, since, until }),
|
|
5687
|
+
checkpoint: async ({ workspace, message, includeUntracked = false }) => gitCheckpoint(workspace, message, includeUntracked),
|
|
5688
|
+
stashPush: async ({ workspace, message, includeUntracked = false }) => gitStashPush(workspace, message, includeUntracked),
|
|
5689
|
+
stashPop: async ({ workspace, stashRef }) => gitStashPop(workspace, stashRef),
|
|
5690
|
+
checkoutFiles: async ({ workspace, paths }) => gitCheckoutFiles(workspace, paths),
|
|
5691
|
+
getRemoteUrl: async ({ workspace, remote = "origin" }) => gitGetRemoteUrl(workspace, remote)
|
|
5636
5692
|
};
|
|
5637
5693
|
}
|
|
5638
5694
|
var defaultGitCommandServices = createDefaultGitCommandServices();
|
|
@@ -5696,9 +5752,6 @@ async function handleGitCommand(command, args, services = defaultGitCommandServi
|
|
|
5696
5752
|
if (!isGitCommandName(command)) {
|
|
5697
5753
|
return failure("invalid_args", `Unknown Git command: ${command}`);
|
|
5698
5754
|
}
|
|
5699
|
-
if (MUTATING_COMMAND_NAMES.has(command)) {
|
|
5700
|
-
return failure("invalid_args", `${command} is not implemented in daemon-core read-only Git routing`);
|
|
5701
|
-
}
|
|
5702
5755
|
const workspaceResult = validateWorkspace2(args);
|
|
5703
5756
|
if ("success" in workspaceResult) return workspaceResult;
|
|
5704
5757
|
const { workspace } = workspaceResult;
|
|
@@ -5758,10 +5811,153 @@ async function handleGitCommand(command, args, services = defaultGitCommandServi
|
|
|
5758
5811
|
}));
|
|
5759
5812
|
return "success" in log ? log : { success: true, log };
|
|
5760
5813
|
}
|
|
5814
|
+
case "git_checkpoint": {
|
|
5815
|
+
if (!services.checkpoint) return serviceNotImplemented(command);
|
|
5816
|
+
const msg = validateMutatingMessage(args?.message);
|
|
5817
|
+
if (typeof msg !== "string") return msg;
|
|
5818
|
+
const includeUntracked = Boolean(args?.includeUntracked);
|
|
5819
|
+
const checkpoint = await runService(() => services.checkpoint({ workspace, message: msg, includeUntracked }));
|
|
5820
|
+
return "success" in checkpoint ? checkpoint : { success: true, checkpoint };
|
|
5821
|
+
}
|
|
5822
|
+
case "git_stash_push": {
|
|
5823
|
+
if (!services.stashPush) return serviceNotImplemented(command);
|
|
5824
|
+
const msg = validateMutatingMessage(args?.message);
|
|
5825
|
+
if (typeof msg !== "string") return msg;
|
|
5826
|
+
const includeUntracked = Boolean(args?.includeUntracked);
|
|
5827
|
+
const stash = await runService(() => services.stashPush({ workspace, message: msg, includeUntracked }));
|
|
5828
|
+
return "success" in stash ? stash : { success: true, stash };
|
|
5829
|
+
}
|
|
5830
|
+
case "git_stash_pop": {
|
|
5831
|
+
if (!services.stashPop) return serviceNotImplemented(command);
|
|
5832
|
+
const stashRef = optionalString(args?.stashRef);
|
|
5833
|
+
if (stashRef !== void 0 && !/^stash@\{\d+\}$/.test(stashRef)) {
|
|
5834
|
+
return failure("invalid_args", "stashRef must match stash@{N} format");
|
|
5835
|
+
}
|
|
5836
|
+
const popResult = await runService(() => services.stashPop({ workspace, stashRef }));
|
|
5837
|
+
if (popResult !== void 0 && "success" in popResult) return popResult;
|
|
5838
|
+
return { success: true, stashPopped: true };
|
|
5839
|
+
}
|
|
5840
|
+
case "git_checkout_files": {
|
|
5841
|
+
if (!services.checkoutFiles) return serviceNotImplemented(command);
|
|
5842
|
+
const paths = args?.paths;
|
|
5843
|
+
if (!Array.isArray(paths) || paths.length === 0) {
|
|
5844
|
+
return failure("invalid_args", "paths must be a non-empty array");
|
|
5845
|
+
}
|
|
5846
|
+
if (paths.length > 50) {
|
|
5847
|
+
return failure("invalid_args", "paths array exceeds maximum of 50 entries");
|
|
5848
|
+
}
|
|
5849
|
+
const checkoutResult = await runService(() => services.checkoutFiles({ workspace, paths }));
|
|
5850
|
+
return "success" in checkoutResult ? checkoutResult : { success: true, checkedOut: checkoutResult.checkedOut };
|
|
5851
|
+
}
|
|
5852
|
+
case "git_remote_url": {
|
|
5853
|
+
if (!services.getRemoteUrl) return serviceNotImplemented(command);
|
|
5854
|
+
const remote = typeof args?.remote === "string" && args.remote.trim() ? args.remote.trim() : "origin";
|
|
5855
|
+
const remoteResult = await runService(() => services.getRemoteUrl({ workspace, remote }));
|
|
5856
|
+
if ("success" in remoteResult) return remoteResult;
|
|
5857
|
+
return { success: true, remoteUrl: remoteResult.remoteUrl, remote: remoteResult.remote };
|
|
5858
|
+
}
|
|
5761
5859
|
default:
|
|
5762
5860
|
return failure("invalid_args", `Unknown Git command: ${command}`);
|
|
5763
5861
|
}
|
|
5764
5862
|
}
|
|
5863
|
+
function validateMutatingMessage(value) {
|
|
5864
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
5865
|
+
return failure("invalid_args", "message must be a non-empty string");
|
|
5866
|
+
}
|
|
5867
|
+
const msg = value.trim();
|
|
5868
|
+
if (msg.length > 200) {
|
|
5869
|
+
return failure("invalid_args", "message must be 200 characters or fewer");
|
|
5870
|
+
}
|
|
5871
|
+
return msg;
|
|
5872
|
+
}
|
|
5873
|
+
async function gitCheckpoint(workspace, message, includeUntracked) {
|
|
5874
|
+
const repo = await resolveGitRepository(workspace);
|
|
5875
|
+
const repoRoot = repo.repoRoot;
|
|
5876
|
+
const statusResult = await getGitRepoStatus(workspace);
|
|
5877
|
+
if (statusResult.hasConflicts) {
|
|
5878
|
+
throw new GitCommandError("conflict", "Repository has conflicts \u2014 resolve before checkpointing");
|
|
5879
|
+
}
|
|
5880
|
+
const addArgs = includeUntracked ? ["-A"] : ["-u"];
|
|
5881
|
+
await runGit(repo, ["add", ...addArgs], { cwd: repoRoot });
|
|
5882
|
+
const fullMsg = `adhdev: checkpoint ${message}`;
|
|
5883
|
+
let commitSha;
|
|
5884
|
+
try {
|
|
5885
|
+
await runGit(repo, ["commit", "-m", fullMsg], { cwd: repoRoot });
|
|
5886
|
+
const revResult = await runGit(repo, ["rev-parse", "HEAD"], { cwd: repoRoot });
|
|
5887
|
+
commitSha = revResult.stdout.trim();
|
|
5888
|
+
} catch (err) {
|
|
5889
|
+
const output = (err?.stdout || "") + (err?.stderr || "");
|
|
5890
|
+
if (/nothing to commit/i.test(output)) {
|
|
5891
|
+
throw new GitCommandError("git_command_failed", "Nothing to commit");
|
|
5892
|
+
}
|
|
5893
|
+
throw err;
|
|
5894
|
+
}
|
|
5895
|
+
return {
|
|
5896
|
+
workspace: repo.workspace,
|
|
5897
|
+
repoRoot,
|
|
5898
|
+
isGitRepo: true,
|
|
5899
|
+
commit: commitSha,
|
|
5900
|
+
message: fullMsg,
|
|
5901
|
+
lastCheckedAt: Date.now()
|
|
5902
|
+
};
|
|
5903
|
+
}
|
|
5904
|
+
async function gitStashPush(workspace, message, includeUntracked) {
|
|
5905
|
+
const repo = await resolveGitRepository(workspace);
|
|
5906
|
+
const repoRoot = repo.repoRoot;
|
|
5907
|
+
const stashArgs = ["stash", "push", "-m", message];
|
|
5908
|
+
if (includeUntracked) stashArgs.push("--include-untracked");
|
|
5909
|
+
const result = await runGit(repo, stashArgs, { cwd: repoRoot });
|
|
5910
|
+
if (/No local changes to save/i.test(result.stdout + result.stderr)) {
|
|
5911
|
+
throw new GitCommandError("git_command_failed", "Nothing to stash");
|
|
5912
|
+
}
|
|
5913
|
+
return {
|
|
5914
|
+
workspace: repo.workspace,
|
|
5915
|
+
repoRoot,
|
|
5916
|
+
isGitRepo: true,
|
|
5917
|
+
stashRef: "stash@{0}",
|
|
5918
|
+
message,
|
|
5919
|
+
lastCheckedAt: Date.now()
|
|
5920
|
+
};
|
|
5921
|
+
}
|
|
5922
|
+
async function gitStashPop(workspace, stashRef) {
|
|
5923
|
+
const repo = await resolveGitRepository(workspace);
|
|
5924
|
+
const repoRoot = repo.repoRoot;
|
|
5925
|
+
const popArgs = stashRef ? ["stash", "pop", stashRef] : ["stash", "pop"];
|
|
5926
|
+
await runGit(repo, popArgs, { cwd: repoRoot });
|
|
5927
|
+
}
|
|
5928
|
+
async function gitCheckoutFiles(workspace, paths) {
|
|
5929
|
+
const repo = await resolveGitRepository(workspace);
|
|
5930
|
+
const repoRoot = repo.repoRoot;
|
|
5931
|
+
const normalizedPaths = [];
|
|
5932
|
+
for (const p of paths) {
|
|
5933
|
+
if (typeof p !== "string" || !p.trim() || p.includes("\0")) {
|
|
5934
|
+
throw new GitCommandError("invalid_args", `Invalid path: ${String(p)}`);
|
|
5935
|
+
}
|
|
5936
|
+
if (path3.isAbsolute(p)) {
|
|
5937
|
+
throw new GitCommandError("invalid_args", `Path must be repository-relative, not absolute: ${p}`);
|
|
5938
|
+
}
|
|
5939
|
+
const normalized = path3.normalize(p.trim()).split(path3.sep).join("/");
|
|
5940
|
+
if (normalized.startsWith("../") || normalized === "..") {
|
|
5941
|
+
throw new GitCommandError("path_outside_repo", `Path is outside repository root: ${p}`);
|
|
5942
|
+
}
|
|
5943
|
+
const absolutePath = path3.resolve(repoRoot, normalized);
|
|
5944
|
+
if (!isPathInside(repoRoot, absolutePath)) {
|
|
5945
|
+
throw new GitCommandError("path_outside_repo", `Path is outside repository root: ${p}`);
|
|
5946
|
+
}
|
|
5947
|
+
normalizedPaths.push(normalized);
|
|
5948
|
+
}
|
|
5949
|
+
await runGit(repo, ["checkout", "--", ...normalizedPaths], { cwd: repoRoot });
|
|
5950
|
+
return { checkedOut: normalizedPaths };
|
|
5951
|
+
}
|
|
5952
|
+
async function gitGetRemoteUrl(workspace, remote) {
|
|
5953
|
+
const repo = await resolveGitRepository(workspace);
|
|
5954
|
+
const result = await runGit(repo, ["remote", "get-url", remote], { cwd: repo.repoRoot });
|
|
5955
|
+
const remoteUrl = result.stdout.trim();
|
|
5956
|
+
if (!remoteUrl) {
|
|
5957
|
+
throw new GitCommandError("git_command_failed", `Remote '${remote}' has no URL`);
|
|
5958
|
+
}
|
|
5959
|
+
return { remoteUrl, remote };
|
|
5960
|
+
}
|
|
5765
5961
|
function formatOptionalGitLogRangeArg(flag, value) {
|
|
5766
5962
|
return value ? [`${flag}=${value}`] : [];
|
|
5767
5963
|
}
|
|
@@ -5820,6 +6016,27 @@ function validateGitLogPath(repoRoot, filePath) {
|
|
|
5820
6016
|
return normalized;
|
|
5821
6017
|
}
|
|
5822
6018
|
|
|
6019
|
+
// src/git/turn-snapshot-tracker.ts
|
|
6020
|
+
var BUSY_STATUSES = /* @__PURE__ */ new Set(["streaming", "waiting_approval"]);
|
|
6021
|
+
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["idle", "error"]);
|
|
6022
|
+
var TurnSnapshotTracker = class {
|
|
6023
|
+
lastStatus = /* @__PURE__ */ new Map();
|
|
6024
|
+
onTurnCompleted;
|
|
6025
|
+
constructor(onTurnCompleted) {
|
|
6026
|
+
this.onTurnCompleted = onTurnCompleted;
|
|
6027
|
+
}
|
|
6028
|
+
record(sessionId, status, workspace) {
|
|
6029
|
+
const prev = this.lastStatus.get(sessionId);
|
|
6030
|
+
this.lastStatus.set(sessionId, status);
|
|
6031
|
+
if (workspace && prev && BUSY_STATUSES.has(prev) && TERMINAL_STATUSES.has(status)) {
|
|
6032
|
+
this.onTurnCompleted({ sessionId, workspace });
|
|
6033
|
+
}
|
|
6034
|
+
}
|
|
6035
|
+
forget(sessionId) {
|
|
6036
|
+
this.lastStatus.delete(sessionId);
|
|
6037
|
+
}
|
|
6038
|
+
};
|
|
6039
|
+
|
|
5823
6040
|
// src/index.ts
|
|
5824
6041
|
init_config();
|
|
5825
6042
|
|
|
@@ -14707,6 +14924,16 @@ var DaemonCommandHandler = class {
|
|
|
14707
14924
|
return result;
|
|
14708
14925
|
}
|
|
14709
14926
|
}
|
|
14927
|
+
if (cmd === "send_chat" && this._ctx.onBeforeSendChat) {
|
|
14928
|
+
const sessionId = this._currentRoute.session?.sessionId;
|
|
14929
|
+
const workspace = sessionId ? this._ctx.instanceManager?.getInstance(sessionId)?.getState?.()?.workspace : void 0;
|
|
14930
|
+
if (workspace && sessionId) {
|
|
14931
|
+
try {
|
|
14932
|
+
this._ctx.onBeforeSendChat({ workspace, sessionId });
|
|
14933
|
+
} catch {
|
|
14934
|
+
}
|
|
14935
|
+
}
|
|
14936
|
+
}
|
|
14710
14937
|
try {
|
|
14711
14938
|
result = await this.dispatch(cmd, args);
|
|
14712
14939
|
this.logCommandEnd(cmd, result, startedAt);
|
|
@@ -17942,6 +18169,7 @@ var KNOWN_PROVIDER_FIELDS = /* @__PURE__ */ new Set([
|
|
|
17942
18169
|
"sendDelayMs",
|
|
17943
18170
|
"sendKey",
|
|
17944
18171
|
"submitStrategy",
|
|
18172
|
+
"requirePromptEchoBeforeSubmit",
|
|
17945
18173
|
"timeouts",
|
|
17946
18174
|
"disableUpstream"
|
|
17947
18175
|
]);
|
|
@@ -29461,6 +29689,7 @@ async function initDaemonComponents(config) {
|
|
|
29461
29689
|
providerLoader,
|
|
29462
29690
|
instanceManager,
|
|
29463
29691
|
sessionRegistry,
|
|
29692
|
+
gitCommandServices: createDefaultGitCommandServices(),
|
|
29464
29693
|
onProviderSettingChanged: async (providerType) => {
|
|
29465
29694
|
await refreshProviderAvailability(providerType);
|
|
29466
29695
|
config.onStatusChange?.();
|
|
@@ -29468,7 +29697,8 @@ async function initDaemonComponents(config) {
|
|
|
29468
29697
|
onProviderSourceConfigChanged: async () => {
|
|
29469
29698
|
await refreshProviderAvailability();
|
|
29470
29699
|
config.onStatusChange?.();
|
|
29471
|
-
}
|
|
29700
|
+
},
|
|
29701
|
+
onBeforeSendChat: config.onBeforeSendChat
|
|
29472
29702
|
});
|
|
29473
29703
|
agentStreamManager = new DaemonAgentStreamManager(
|
|
29474
29704
|
LOG.forComponent("AgentStream").asLogFn(),
|
|
@@ -29620,6 +29850,7 @@ async function shutdownDaemonComponents(components) {
|
|
|
29620
29850
|
ProviderLoader,
|
|
29621
29851
|
STANDALONE_CDP_SCAN_INTERVAL_MS,
|
|
29622
29852
|
SessionHostPtyTransportFactory,
|
|
29853
|
+
TurnSnapshotTracker,
|
|
29623
29854
|
VersionArchive,
|
|
29624
29855
|
appendRecentActivity,
|
|
29625
29856
|
buildAssistantChatMessage,
|