@adhdev/daemon-core 0.9.54 → 0.9.55
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 +1 -0
- package/dist/cli-adapters/provider-cli-config.d.ts +1 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +2 -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 +208 -12
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +207 -12
- 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 +19 -0
- 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 +2 -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;
|
|
@@ -136,6 +136,7 @@ export declare class ProviderCliAdapter implements CliAdapter {
|
|
|
136
136
|
private readonly sendDelayMs;
|
|
137
137
|
private readonly sendKey;
|
|
138
138
|
private readonly submitStrategy;
|
|
139
|
+
private readonly requirePromptEchoBeforeSubmit;
|
|
139
140
|
private static readonly SCRIPT_STATUS_DEBOUNCE_MS;
|
|
140
141
|
constructor(provider: CliProviderModule, workingDir: string, extraArgs?: string[], transportFactory?: PtyTransportFactory);
|
|
141
142
|
/** 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;
|
|
@@ -123,6 +123,8 @@ export interface CliProviderModule {
|
|
|
123
123
|
sendDelayMs?: number;
|
|
124
124
|
sendKey?: string;
|
|
125
125
|
submitStrategy?: 'wait_for_echo' | 'immediate';
|
|
126
|
+
/** Require the typed prompt to be visible on the PTY screen before sending Enter. */
|
|
127
|
+
requirePromptEchoBeforeSubmit?: boolean;
|
|
126
128
|
/** Allow sending another prompt while the CLI is still generating so users can intervene mid-turn. */
|
|
127
129
|
allowInputDuringGeneration?: boolean;
|
|
128
130
|
/** 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);
|
|
@@ -2503,6 +2505,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
2503
2505
|
sendDelayMs;
|
|
2504
2506
|
sendKey;
|
|
2505
2507
|
submitStrategy;
|
|
2508
|
+
requirePromptEchoBeforeSubmit;
|
|
2506
2509
|
static SCRIPT_STATUS_DEBOUNCE_MS = 3e3;
|
|
2507
2510
|
/** Inject CLI scripts after construction (e.g. when resolved by ProviderLoader) */
|
|
2508
2511
|
setCliScripts(scripts) {
|
|
@@ -4023,6 +4026,22 @@ var init_provider_cli_adapter = __esm({
|
|
|
4023
4026
|
}
|
|
4024
4027
|
}
|
|
4025
4028
|
if (elapsed >= state.maxEchoWaitMs) {
|
|
4029
|
+
const diagnostic = {
|
|
4030
|
+
elapsed,
|
|
4031
|
+
maxEchoWaitMs: state.maxEchoWaitMs,
|
|
4032
|
+
submitDelayMs: state.submitDelayMs,
|
|
4033
|
+
promptSnippet: state.normalizedPromptSnippet,
|
|
4034
|
+
requirePromptEchoBeforeSubmit: this.requirePromptEchoBeforeSubmit,
|
|
4035
|
+
screenText: summarizeCliTraceText(screenText, 1e3)
|
|
4036
|
+
};
|
|
4037
|
+
this.recordTrace("submit_echo_missing", diagnostic);
|
|
4038
|
+
if (this.requirePromptEchoBeforeSubmit) {
|
|
4039
|
+
const message = `${this.cliName} prompt echo was not observed on the PTY screen before submit`;
|
|
4040
|
+
LOG.warn("CLI", `[${this.cliType}] ${message} elapsed=${elapsed}ms maxEchoWaitMs=${state.maxEchoWaitMs} screen=${JSON.stringify(diagnostic.screenText).slice(0, 240)}`);
|
|
4041
|
+
completion.rejectOnce(new Error(message));
|
|
4042
|
+
return;
|
|
4043
|
+
}
|
|
4044
|
+
LOG.warn("CLI", `[${this.cliType}] prompt echo was not observed before submit; sending submit key anyway elapsed=${elapsed}ms maxEchoWaitMs=${state.maxEchoWaitMs}`);
|
|
4026
4045
|
this.submitSendKey(state, completion);
|
|
4027
4046
|
return;
|
|
4028
4047
|
}
|
|
@@ -4487,6 +4506,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
4487
4506
|
sendDelayMs: this.sendDelayMs,
|
|
4488
4507
|
sendKey: this.sendKey,
|
|
4489
4508
|
submitStrategy: this.submitStrategy,
|
|
4509
|
+
requirePromptEchoBeforeSubmit: this.requirePromptEchoBeforeSubmit,
|
|
4490
4510
|
submitPendingUntil: this.submitPendingUntil,
|
|
4491
4511
|
responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
|
|
4492
4512
|
resizeSuppressUntil: this.resizeSuppressUntil,
|
|
@@ -4574,6 +4594,7 @@ __export(index_exports, {
|
|
|
4574
4594
|
ProviderLoader: () => ProviderLoader,
|
|
4575
4595
|
STANDALONE_CDP_SCAN_INTERVAL_MS: () => STANDALONE_CDP_SCAN_INTERVAL_MS,
|
|
4576
4596
|
SessionHostPtyTransportFactory: () => SessionHostPtyTransportFactory,
|
|
4597
|
+
TurnSnapshotTracker: () => TurnSnapshotTracker,
|
|
4577
4598
|
VersionArchive: () => VersionArchive,
|
|
4578
4599
|
appendRecentActivity: () => appendRecentActivity,
|
|
4579
4600
|
buildAssistantChatMessage: () => buildAssistantChatMessage,
|
|
@@ -5585,13 +5606,8 @@ var GIT_COMMAND_NAMES = /* @__PURE__ */ new Set([
|
|
|
5585
5606
|
"git_checkpoint",
|
|
5586
5607
|
"git_stash_push",
|
|
5587
5608
|
"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"
|
|
5609
|
+
"git_checkout_files",
|
|
5610
|
+
"git_remote_url"
|
|
5595
5611
|
]);
|
|
5596
5612
|
var SNAPSHOT_REASONS = /* @__PURE__ */ new Set([
|
|
5597
5613
|
"session_baseline",
|
|
@@ -5632,7 +5648,12 @@ function createDefaultGitCommandServices() {
|
|
|
5632
5648
|
turnId
|
|
5633
5649
|
}),
|
|
5634
5650
|
compareSnapshots: ({ beforeSnapshotId, afterSnapshotId }) => defaultSnapshotStore.compare(beforeSnapshotId, afterSnapshotId),
|
|
5635
|
-
getLog: ({ workspace, limit, path: filePath, since, until }) => getGitLog(workspace, { limit, path: filePath, since, until })
|
|
5651
|
+
getLog: ({ workspace, limit, path: filePath, since, until }) => getGitLog(workspace, { limit, path: filePath, since, until }),
|
|
5652
|
+
checkpoint: async ({ workspace, message, includeUntracked = false }) => gitCheckpoint(workspace, message, includeUntracked),
|
|
5653
|
+
stashPush: async ({ workspace, message, includeUntracked = false }) => gitStashPush(workspace, message, includeUntracked),
|
|
5654
|
+
stashPop: async ({ workspace, stashRef }) => gitStashPop(workspace, stashRef),
|
|
5655
|
+
checkoutFiles: async ({ workspace, paths }) => gitCheckoutFiles(workspace, paths),
|
|
5656
|
+
getRemoteUrl: async ({ workspace, remote = "origin" }) => gitGetRemoteUrl(workspace, remote)
|
|
5636
5657
|
};
|
|
5637
5658
|
}
|
|
5638
5659
|
var defaultGitCommandServices = createDefaultGitCommandServices();
|
|
@@ -5696,9 +5717,6 @@ async function handleGitCommand(command, args, services = defaultGitCommandServi
|
|
|
5696
5717
|
if (!isGitCommandName(command)) {
|
|
5697
5718
|
return failure("invalid_args", `Unknown Git command: ${command}`);
|
|
5698
5719
|
}
|
|
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
5720
|
const workspaceResult = validateWorkspace2(args);
|
|
5703
5721
|
if ("success" in workspaceResult) return workspaceResult;
|
|
5704
5722
|
const { workspace } = workspaceResult;
|
|
@@ -5758,10 +5776,153 @@ async function handleGitCommand(command, args, services = defaultGitCommandServi
|
|
|
5758
5776
|
}));
|
|
5759
5777
|
return "success" in log ? log : { success: true, log };
|
|
5760
5778
|
}
|
|
5779
|
+
case "git_checkpoint": {
|
|
5780
|
+
if (!services.checkpoint) return serviceNotImplemented(command);
|
|
5781
|
+
const msg = validateMutatingMessage(args?.message);
|
|
5782
|
+
if (typeof msg !== "string") return msg;
|
|
5783
|
+
const includeUntracked = Boolean(args?.includeUntracked);
|
|
5784
|
+
const checkpoint = await runService(() => services.checkpoint({ workspace, message: msg, includeUntracked }));
|
|
5785
|
+
return "success" in checkpoint ? checkpoint : { success: true, checkpoint };
|
|
5786
|
+
}
|
|
5787
|
+
case "git_stash_push": {
|
|
5788
|
+
if (!services.stashPush) return serviceNotImplemented(command);
|
|
5789
|
+
const msg = validateMutatingMessage(args?.message);
|
|
5790
|
+
if (typeof msg !== "string") return msg;
|
|
5791
|
+
const includeUntracked = Boolean(args?.includeUntracked);
|
|
5792
|
+
const stash = await runService(() => services.stashPush({ workspace, message: msg, includeUntracked }));
|
|
5793
|
+
return "success" in stash ? stash : { success: true, stash };
|
|
5794
|
+
}
|
|
5795
|
+
case "git_stash_pop": {
|
|
5796
|
+
if (!services.stashPop) return serviceNotImplemented(command);
|
|
5797
|
+
const stashRef = optionalString(args?.stashRef);
|
|
5798
|
+
if (stashRef !== void 0 && !/^stash@\{\d+\}$/.test(stashRef)) {
|
|
5799
|
+
return failure("invalid_args", "stashRef must match stash@{N} format");
|
|
5800
|
+
}
|
|
5801
|
+
const popResult = await runService(() => services.stashPop({ workspace, stashRef }));
|
|
5802
|
+
if (popResult !== void 0 && "success" in popResult) return popResult;
|
|
5803
|
+
return { success: true, stashPopped: true };
|
|
5804
|
+
}
|
|
5805
|
+
case "git_checkout_files": {
|
|
5806
|
+
if (!services.checkoutFiles) return serviceNotImplemented(command);
|
|
5807
|
+
const paths = args?.paths;
|
|
5808
|
+
if (!Array.isArray(paths) || paths.length === 0) {
|
|
5809
|
+
return failure("invalid_args", "paths must be a non-empty array");
|
|
5810
|
+
}
|
|
5811
|
+
if (paths.length > 50) {
|
|
5812
|
+
return failure("invalid_args", "paths array exceeds maximum of 50 entries");
|
|
5813
|
+
}
|
|
5814
|
+
const checkoutResult = await runService(() => services.checkoutFiles({ workspace, paths }));
|
|
5815
|
+
return "success" in checkoutResult ? checkoutResult : { success: true, checkedOut: checkoutResult.checkedOut };
|
|
5816
|
+
}
|
|
5817
|
+
case "git_remote_url": {
|
|
5818
|
+
if (!services.getRemoteUrl) return serviceNotImplemented(command);
|
|
5819
|
+
const remote = typeof args?.remote === "string" && args.remote.trim() ? args.remote.trim() : "origin";
|
|
5820
|
+
const remoteResult = await runService(() => services.getRemoteUrl({ workspace, remote }));
|
|
5821
|
+
if ("success" in remoteResult) return remoteResult;
|
|
5822
|
+
return { success: true, remoteUrl: remoteResult.remoteUrl, remote: remoteResult.remote };
|
|
5823
|
+
}
|
|
5761
5824
|
default:
|
|
5762
5825
|
return failure("invalid_args", `Unknown Git command: ${command}`);
|
|
5763
5826
|
}
|
|
5764
5827
|
}
|
|
5828
|
+
function validateMutatingMessage(value) {
|
|
5829
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
5830
|
+
return failure("invalid_args", "message must be a non-empty string");
|
|
5831
|
+
}
|
|
5832
|
+
const msg = value.trim();
|
|
5833
|
+
if (msg.length > 200) {
|
|
5834
|
+
return failure("invalid_args", "message must be 200 characters or fewer");
|
|
5835
|
+
}
|
|
5836
|
+
return msg;
|
|
5837
|
+
}
|
|
5838
|
+
async function gitCheckpoint(workspace, message, includeUntracked) {
|
|
5839
|
+
const repo = await resolveGitRepository(workspace);
|
|
5840
|
+
const repoRoot = repo.repoRoot;
|
|
5841
|
+
const statusResult = await getGitRepoStatus(workspace);
|
|
5842
|
+
if (statusResult.hasConflicts) {
|
|
5843
|
+
throw new GitCommandError("conflict", "Repository has conflicts \u2014 resolve before checkpointing");
|
|
5844
|
+
}
|
|
5845
|
+
const addArgs = includeUntracked ? ["-A"] : ["-u"];
|
|
5846
|
+
await runGit(repo, ["add", ...addArgs], { cwd: repoRoot });
|
|
5847
|
+
const fullMsg = `adhdev: checkpoint ${message}`;
|
|
5848
|
+
let commitSha;
|
|
5849
|
+
try {
|
|
5850
|
+
await runGit(repo, ["commit", "-m", fullMsg], { cwd: repoRoot });
|
|
5851
|
+
const revResult = await runGit(repo, ["rev-parse", "HEAD"], { cwd: repoRoot });
|
|
5852
|
+
commitSha = revResult.stdout.trim();
|
|
5853
|
+
} catch (err) {
|
|
5854
|
+
const output = (err?.stdout || "") + (err?.stderr || "");
|
|
5855
|
+
if (/nothing to commit/i.test(output)) {
|
|
5856
|
+
throw new GitCommandError("git_command_failed", "Nothing to commit");
|
|
5857
|
+
}
|
|
5858
|
+
throw err;
|
|
5859
|
+
}
|
|
5860
|
+
return {
|
|
5861
|
+
workspace: repo.workspace,
|
|
5862
|
+
repoRoot,
|
|
5863
|
+
isGitRepo: true,
|
|
5864
|
+
commit: commitSha,
|
|
5865
|
+
message: fullMsg,
|
|
5866
|
+
lastCheckedAt: Date.now()
|
|
5867
|
+
};
|
|
5868
|
+
}
|
|
5869
|
+
async function gitStashPush(workspace, message, includeUntracked) {
|
|
5870
|
+
const repo = await resolveGitRepository(workspace);
|
|
5871
|
+
const repoRoot = repo.repoRoot;
|
|
5872
|
+
const stashArgs = ["stash", "push", "-m", message];
|
|
5873
|
+
if (includeUntracked) stashArgs.push("--include-untracked");
|
|
5874
|
+
const result = await runGit(repo, stashArgs, { cwd: repoRoot });
|
|
5875
|
+
if (/No local changes to save/i.test(result.stdout + result.stderr)) {
|
|
5876
|
+
throw new GitCommandError("git_command_failed", "Nothing to stash");
|
|
5877
|
+
}
|
|
5878
|
+
return {
|
|
5879
|
+
workspace: repo.workspace,
|
|
5880
|
+
repoRoot,
|
|
5881
|
+
isGitRepo: true,
|
|
5882
|
+
stashRef: "stash@{0}",
|
|
5883
|
+
message,
|
|
5884
|
+
lastCheckedAt: Date.now()
|
|
5885
|
+
};
|
|
5886
|
+
}
|
|
5887
|
+
async function gitStashPop(workspace, stashRef) {
|
|
5888
|
+
const repo = await resolveGitRepository(workspace);
|
|
5889
|
+
const repoRoot = repo.repoRoot;
|
|
5890
|
+
const popArgs = stashRef ? ["stash", "pop", stashRef] : ["stash", "pop"];
|
|
5891
|
+
await runGit(repo, popArgs, { cwd: repoRoot });
|
|
5892
|
+
}
|
|
5893
|
+
async function gitCheckoutFiles(workspace, paths) {
|
|
5894
|
+
const repo = await resolveGitRepository(workspace);
|
|
5895
|
+
const repoRoot = repo.repoRoot;
|
|
5896
|
+
const normalizedPaths = [];
|
|
5897
|
+
for (const p of paths) {
|
|
5898
|
+
if (typeof p !== "string" || !p.trim() || p.includes("\0")) {
|
|
5899
|
+
throw new GitCommandError("invalid_args", `Invalid path: ${String(p)}`);
|
|
5900
|
+
}
|
|
5901
|
+
if (path3.isAbsolute(p)) {
|
|
5902
|
+
throw new GitCommandError("invalid_args", `Path must be repository-relative, not absolute: ${p}`);
|
|
5903
|
+
}
|
|
5904
|
+
const normalized = path3.normalize(p.trim()).split(path3.sep).join("/");
|
|
5905
|
+
if (normalized.startsWith("../") || normalized === "..") {
|
|
5906
|
+
throw new GitCommandError("path_outside_repo", `Path is outside repository root: ${p}`);
|
|
5907
|
+
}
|
|
5908
|
+
const absolutePath = path3.resolve(repoRoot, normalized);
|
|
5909
|
+
if (!isPathInside(repoRoot, absolutePath)) {
|
|
5910
|
+
throw new GitCommandError("path_outside_repo", `Path is outside repository root: ${p}`);
|
|
5911
|
+
}
|
|
5912
|
+
normalizedPaths.push(normalized);
|
|
5913
|
+
}
|
|
5914
|
+
await runGit(repo, ["checkout", "--", ...normalizedPaths], { cwd: repoRoot });
|
|
5915
|
+
return { checkedOut: normalizedPaths };
|
|
5916
|
+
}
|
|
5917
|
+
async function gitGetRemoteUrl(workspace, remote) {
|
|
5918
|
+
const repo = await resolveGitRepository(workspace);
|
|
5919
|
+
const result = await runGit(repo, ["remote", "get-url", remote], { cwd: repo.repoRoot });
|
|
5920
|
+
const remoteUrl = result.stdout.trim();
|
|
5921
|
+
if (!remoteUrl) {
|
|
5922
|
+
throw new GitCommandError("git_command_failed", `Remote '${remote}' has no URL`);
|
|
5923
|
+
}
|
|
5924
|
+
return { remoteUrl, remote };
|
|
5925
|
+
}
|
|
5765
5926
|
function formatOptionalGitLogRangeArg(flag, value) {
|
|
5766
5927
|
return value ? [`${flag}=${value}`] : [];
|
|
5767
5928
|
}
|
|
@@ -5820,6 +5981,27 @@ function validateGitLogPath(repoRoot, filePath) {
|
|
|
5820
5981
|
return normalized;
|
|
5821
5982
|
}
|
|
5822
5983
|
|
|
5984
|
+
// src/git/turn-snapshot-tracker.ts
|
|
5985
|
+
var BUSY_STATUSES = /* @__PURE__ */ new Set(["streaming", "waiting_approval"]);
|
|
5986
|
+
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["idle", "error"]);
|
|
5987
|
+
var TurnSnapshotTracker = class {
|
|
5988
|
+
lastStatus = /* @__PURE__ */ new Map();
|
|
5989
|
+
onTurnCompleted;
|
|
5990
|
+
constructor(onTurnCompleted) {
|
|
5991
|
+
this.onTurnCompleted = onTurnCompleted;
|
|
5992
|
+
}
|
|
5993
|
+
record(sessionId, status, workspace) {
|
|
5994
|
+
const prev = this.lastStatus.get(sessionId);
|
|
5995
|
+
this.lastStatus.set(sessionId, status);
|
|
5996
|
+
if (workspace && prev && BUSY_STATUSES.has(prev) && TERMINAL_STATUSES.has(status)) {
|
|
5997
|
+
this.onTurnCompleted({ sessionId, workspace });
|
|
5998
|
+
}
|
|
5999
|
+
}
|
|
6000
|
+
forget(sessionId) {
|
|
6001
|
+
this.lastStatus.delete(sessionId);
|
|
6002
|
+
}
|
|
6003
|
+
};
|
|
6004
|
+
|
|
5823
6005
|
// src/index.ts
|
|
5824
6006
|
init_config();
|
|
5825
6007
|
|
|
@@ -14707,6 +14889,16 @@ var DaemonCommandHandler = class {
|
|
|
14707
14889
|
return result;
|
|
14708
14890
|
}
|
|
14709
14891
|
}
|
|
14892
|
+
if (cmd === "send_chat" && this._ctx.onBeforeSendChat) {
|
|
14893
|
+
const sessionId = this._currentRoute.session?.sessionId;
|
|
14894
|
+
const workspace = sessionId ? this._ctx.instanceManager?.getInstance(sessionId)?.getState?.()?.workspace : void 0;
|
|
14895
|
+
if (workspace && sessionId) {
|
|
14896
|
+
try {
|
|
14897
|
+
this._ctx.onBeforeSendChat({ workspace, sessionId });
|
|
14898
|
+
} catch {
|
|
14899
|
+
}
|
|
14900
|
+
}
|
|
14901
|
+
}
|
|
14710
14902
|
try {
|
|
14711
14903
|
result = await this.dispatch(cmd, args);
|
|
14712
14904
|
this.logCommandEnd(cmd, result, startedAt);
|
|
@@ -17942,6 +18134,7 @@ var KNOWN_PROVIDER_FIELDS = /* @__PURE__ */ new Set([
|
|
|
17942
18134
|
"sendDelayMs",
|
|
17943
18135
|
"sendKey",
|
|
17944
18136
|
"submitStrategy",
|
|
18137
|
+
"requirePromptEchoBeforeSubmit",
|
|
17945
18138
|
"timeouts",
|
|
17946
18139
|
"disableUpstream"
|
|
17947
18140
|
]);
|
|
@@ -29461,6 +29654,7 @@ async function initDaemonComponents(config) {
|
|
|
29461
29654
|
providerLoader,
|
|
29462
29655
|
instanceManager,
|
|
29463
29656
|
sessionRegistry,
|
|
29657
|
+
gitCommandServices: createDefaultGitCommandServices(),
|
|
29464
29658
|
onProviderSettingChanged: async (providerType) => {
|
|
29465
29659
|
await refreshProviderAvailability(providerType);
|
|
29466
29660
|
config.onStatusChange?.();
|
|
@@ -29468,7 +29662,8 @@ async function initDaemonComponents(config) {
|
|
|
29468
29662
|
onProviderSourceConfigChanged: async () => {
|
|
29469
29663
|
await refreshProviderAvailability();
|
|
29470
29664
|
config.onStatusChange?.();
|
|
29471
|
-
}
|
|
29665
|
+
},
|
|
29666
|
+
onBeforeSendChat: config.onBeforeSendChat
|
|
29472
29667
|
});
|
|
29473
29668
|
agentStreamManager = new DaemonAgentStreamManager(
|
|
29474
29669
|
LOG.forComponent("AgentStream").asLogFn(),
|
|
@@ -29620,6 +29815,7 @@ async function shutdownDaemonComponents(components) {
|
|
|
29620
29815
|
ProviderLoader,
|
|
29621
29816
|
STANDALONE_CDP_SCAN_INTERVAL_MS,
|
|
29622
29817
|
SessionHostPtyTransportFactory,
|
|
29818
|
+
TurnSnapshotTracker,
|
|
29623
29819
|
VersionArchive,
|
|
29624
29820
|
appendRecentActivity,
|
|
29625
29821
|
buildAssistantChatMessage,
|