@adhdev/daemon-core 0.9.82-rc.160 → 0.9.82-rc.161
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/cli-adapter-types.d.ts +14 -1
- package/dist/commands/mesh-coordinator.d.ts +72 -1
- package/dist/config/chat-history.d.ts +2 -0
- package/dist/config/mesh-config.d.ts +3 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +4923 -1411
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4975 -1476
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/coordinator-prompt.d.ts +30 -0
- package/dist/mesh/coordinator-registry.d.ts +35 -1
- package/dist/providers/cli-provider-instance.d.ts +1 -1
- package/dist/providers/contracts.d.ts +48 -0
- package/dist/providers/native-history/antigravity-cli-transcript.d.ts +1 -1
- package/dist/providers/native-history/claude-cli-transcript.d.ts +1 -1
- package/dist/providers/native-history/codex-cli-transcript.d.ts +1 -1
- package/dist/providers/native-history/dispatcher.d.ts +24 -0
- package/dist/providers/native-history/hermes-cli-transcript.d.ts +30 -0
- package/dist/providers/native-history/index.d.ts +2 -0
- package/dist/providers/spec/adapter.d.ts +56 -0
- package/dist/providers/spec/cli-adapter.d.ts +76 -0
- package/dist/providers/spec/driver.d.ts +148 -0
- package/dist/providers/spec/evaluator.d.ts +47 -0
- package/dist/providers/spec/loader.d.ts +14 -0
- package/dist/providers/spec/native-history-executor.d.ts +39 -0
- package/dist/providers/spec/route.d.ts +4 -0
- package/dist/providers/spec/schema.gen.d.ts +507 -0
- package/dist/providers/spec/types.d.ts +211 -0
- package/dist/repo-mesh-types.d.ts +33 -1
- package/dist/sessions/registry.d.ts +3 -0
- package/package.json +2 -1
- package/src/cli-adapter-types.ts +15 -1
- package/src/commands/chat-commands.ts +150 -12
- package/src/commands/cli-manager.ts +11 -0
- package/src/commands/mesh-coordinator.ts +235 -1
- package/src/commands/router.ts +238 -50
- package/src/config/chat-history.ts +11 -3
- package/src/config/mesh-config.ts +16 -1
- package/src/index.ts +19 -0
- package/src/mesh/coordinator-prompt.ts +164 -8
- package/src/mesh/coordinator-registry.ts +50 -4
- package/src/providers/cli-provider-instance.ts +8 -3
- package/src/providers/contracts.ts +53 -0
- package/src/providers/native-history/antigravity-cli-transcript.ts +2 -2
- package/src/providers/native-history/claude-cli-transcript.ts +1 -1
- package/src/providers/native-history/codex-cli-transcript.ts +1 -1
- package/src/providers/native-history/dispatcher.ts +227 -0
- package/src/providers/native-history/hermes-cli-transcript.ts +230 -0
- package/src/providers/native-history/index.ts +7 -0
- package/src/providers/provider-loader.ts +126 -3
- package/src/providers/sdk/v1/schemas/cli/provider.schema.json +13 -0
- package/src/providers/spec/adapter.ts +153 -0
- package/src/providers/spec/cli-adapter.ts +318 -0
- package/src/providers/spec/driver.ts +498 -0
- package/src/providers/spec/evaluator.ts +268 -0
- package/src/providers/spec/loader.ts +130 -0
- package/src/providers/spec/native-history-executor.ts +612 -0
- package/src/providers/spec/route.ts +51 -0
- package/src/providers/spec/schema.gen.ts +507 -0
- package/src/providers/spec/schema.json +210 -0
- package/src/providers/spec/types.ts +230 -0
- package/src/repo-mesh-types.ts +33 -1
- package/src/sessions/registry.ts +3 -0
|
@@ -8,6 +8,17 @@
|
|
|
8
8
|
* 3. How to orchestrate work across nodes
|
|
9
9
|
*
|
|
10
10
|
* The prompt is generated dynamically from the current mesh state.
|
|
11
|
+
*
|
|
12
|
+
* User customization:
|
|
13
|
+
* ~/.adhdev/coordinator-prompts/<cliType>.md — full override
|
|
14
|
+
* ~/.adhdev/coordinator-prompts/<cliType>.append.md — appended to default
|
|
15
|
+
* ~/.adhdev/coordinator-prompts/default.md — full override (any CLI)
|
|
16
|
+
* ~/.adhdev/coordinator-prompts/default.append.md — appended to default (any CLI)
|
|
17
|
+
*
|
|
18
|
+
* CLI-specific files take precedence over default.* files. The override file
|
|
19
|
+
* still gets the node/policy facts substituted via the same {{placeholders}}
|
|
20
|
+
* the daemon understands; an override that doesn't reference them just gets
|
|
21
|
+
* a static prompt, which is also fine.
|
|
11
22
|
*/
|
|
12
23
|
import type { LocalMeshEntry, RepoMeshStatus } from '../repo-mesh-types.js';
|
|
13
24
|
export interface CoordinatorPromptContext {
|
|
@@ -16,4 +27,23 @@ export interface CoordinatorPromptContext {
|
|
|
16
27
|
userInstruction?: string;
|
|
17
28
|
coordinatorCliType?: string;
|
|
18
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Compose the final coordinator prompt from four layers, in this precedence:
|
|
32
|
+
*
|
|
33
|
+
* 1. Per-launch `extraSystemPrompt` (always appended, as "## Additional
|
|
34
|
+
* Context"). Never wins as a base — it's launch-scope context.
|
|
35
|
+
* 2. Mesh-level append (`mesh.coordinator.systemPromptAppend` or the legacy
|
|
36
|
+
* `systemPromptSuffix`). Stacks after whichever base won.
|
|
37
|
+
* 3. User-file append (`~/.adhdev/coordinator-prompts/<cli>.append.md` or
|
|
38
|
+
* `default.append.md`). Also stacks; same placeholder expansion as the
|
|
39
|
+
* override path.
|
|
40
|
+
* 4. Base prompt, picked in this order:
|
|
41
|
+
* a. `mesh.coordinator.systemPromptOverride` (mesh-level override)
|
|
42
|
+
* b. user-file override (`~/.adhdev/coordinator-prompts/<cli>.md` or
|
|
43
|
+
* `default.md`)
|
|
44
|
+
* c. daemon default (assembled from identity/nodes/policy/tools/…)
|
|
45
|
+
*
|
|
46
|
+
* That layering lets a user customize prompts at three increasing scopes
|
|
47
|
+
* (machine, mesh, single launch) without losing the daemon's stock rules.
|
|
48
|
+
*/
|
|
19
49
|
export declare function buildCoordinatorSystemPrompt(ctx: CoordinatorPromptContext): string;
|
|
@@ -12,12 +12,46 @@ export interface CoordinatorRegistryEntry {
|
|
|
12
12
|
sessionId: string;
|
|
13
13
|
workspace?: string;
|
|
14
14
|
startedAt: number;
|
|
15
|
+
/** CLI type used to launch the coordinator (claude-cli / codex-cli / …). */
|
|
16
|
+
cliType?: string;
|
|
17
|
+
/** Final system prompt sent to the coordinator after all overrides, appends,
|
|
18
|
+
* and extraSystemPrompt have been applied. Surfaced via the session-info
|
|
19
|
+
* endpoint so users can audit exactly what prompt the agent saw. */
|
|
20
|
+
systemPrompt?: string;
|
|
21
|
+
/** Per-launch extraSystemPrompt the caller passed in, if any. Stored
|
|
22
|
+
* separately from `systemPrompt` so the UI can show what the user
|
|
23
|
+
* added vs. what the daemon's default template produced. */
|
|
24
|
+
extraSystemPrompt?: string;
|
|
25
|
+
/** How the prompt was actually injected (cli_arg / context_file / …). */
|
|
26
|
+
injection?: {
|
|
27
|
+
mode: string;
|
|
28
|
+
target?: string;
|
|
29
|
+
};
|
|
30
|
+
/** Path of the MCP config file the daemon wrote for this session. */
|
|
31
|
+
mcpConfigPath?: string;
|
|
15
32
|
}
|
|
16
33
|
/** Load persisted coordinator registry from disk into in-memory map. Called once on daemon boot. */
|
|
17
34
|
export declare function loadMeshCoordinatorRegistry(): void;
|
|
18
35
|
/** Register a coordinator session. Persists to disk immediately. */
|
|
19
36
|
export declare function registerMeshCoordinator(entry: CoordinatorRegistryEntry): void;
|
|
20
|
-
/** Remove a coordinator session by sessionId. Persists to disk.
|
|
37
|
+
/** Remove a coordinator session by sessionId. Persists to disk.
|
|
38
|
+
*
|
|
39
|
+
* Also best-effort strips any context_file wrapper block we wrote into
|
|
40
|
+
* the workspace at launch time (AGENTS.md / GEMINI.md), because those
|
|
41
|
+
* files are auto-loaded by their CLIs on every subsequent launch — and
|
|
42
|
+
* a wrapper block surviving the coordinator session would silently
|
|
43
|
+
* inject the coordinator system prompt into ordinary non-coordinator
|
|
44
|
+
* sessions in the same workspace, which is exactly the bug we're
|
|
45
|
+
* fixing here.
|
|
46
|
+
*
|
|
47
|
+
* Stripping rules:
|
|
48
|
+
* - Look for the start sentinel anywhere in the file; if absent,
|
|
49
|
+
* leave the file alone (user-authored content).
|
|
50
|
+
* - If the wrapper block was the only thing in the file, delete the
|
|
51
|
+
* file outright so we don't leave behind an empty AGENTS.md.
|
|
52
|
+
* - Otherwise drop only the block between the sentinels and trim a
|
|
53
|
+
* leading/trailing blank line so we don't leave dangling separators.
|
|
54
|
+
*/
|
|
21
55
|
export declare function unregisterMeshCoordinator(sessionId: string): void;
|
|
22
56
|
/** Look up a coordinator entry by session ID. Returns undefined if not registered. */
|
|
23
57
|
export declare function getCoordinatorForSession(sessionId: string): CoordinatorRegistryEntry | undefined;
|
|
@@ -94,7 +94,7 @@ export declare class CliProviderInstance implements ProviderInstance {
|
|
|
94
94
|
setPresentationMode(mode: 'terminal' | 'chat'): void;
|
|
95
95
|
getPresentationMode(): 'terminal' | 'chat';
|
|
96
96
|
getHotChatSessionState(): HotChatSessionState;
|
|
97
|
-
getSessionModalState(): SessionModalState;
|
|
97
|
+
getSessionModalState(sessionId?: string): SessionModalState;
|
|
98
98
|
updateSettings(newSettings: Record<string, any>): void;
|
|
99
99
|
onEvent(event: string, data?: any): void;
|
|
100
100
|
recordAcknowledgedUserInput(input: InputEnvelope | string): void;
|
|
@@ -305,7 +305,55 @@ export interface ProviderMeshCoordinatorConfig {
|
|
|
305
305
|
/** Copyable setup template. Supports {{meshId}}, {{adhdevMcpCommand}}, {{workspace}}, {{serverName}}. */
|
|
306
306
|
template?: string;
|
|
307
307
|
};
|
|
308
|
+
/**
|
|
309
|
+
* How the coordinator system prompt reaches the launched CLI. Replaces the
|
|
310
|
+
* old hard-coded `if (cliType === 'claude-cli') push --append-system-prompt`
|
|
311
|
+
* branches in router.ts: a new CLI now ships its injection rule in its
|
|
312
|
+
* provider.v1.json, no daemon code change needed. Users can override the
|
|
313
|
+
* rendered prompt or the injection mechanism per-provider; if omitted, no
|
|
314
|
+
* system prompt is injected (safe default — won't crash spawn with a flag
|
|
315
|
+
* the CLI doesn't recognize).
|
|
316
|
+
*/
|
|
317
|
+
systemPromptInjection?: MeshCoordinatorSystemPromptInjection;
|
|
308
318
|
}
|
|
319
|
+
/**
|
|
320
|
+
* Declarative description of how a CLI accepts a session-scoped system prompt.
|
|
321
|
+
*
|
|
322
|
+
* Modes:
|
|
323
|
+
* - cli_arg → push `flag` + prompt onto spawn args (Claude)
|
|
324
|
+
* - config_override → push `flag` + a templated key=value config override (Codex)
|
|
325
|
+
* - context_file → write prompt into a workspace markdown the CLI
|
|
326
|
+
* auto-loads as project context (Gemini, Antigravity)
|
|
327
|
+
* - env_var → expose prompt to the spawned process as $name (Hermes)
|
|
328
|
+
*
|
|
329
|
+
* The prompt text is templated with `{prompt}` (raw) or `{prompt_json}`
|
|
330
|
+
* (JSON-encoded for embedding inside config-override strings).
|
|
331
|
+
*/
|
|
332
|
+
export type MeshCoordinatorSystemPromptInjection = {
|
|
333
|
+
mode: 'cli_arg';
|
|
334
|
+
/** Spawn-args flag, e.g. '--append-system-prompt'. The prompt becomes the next argv. */
|
|
335
|
+
flag: string;
|
|
336
|
+
} | {
|
|
337
|
+
mode: 'config_override';
|
|
338
|
+
/** Spawn-args flag, e.g. '-c'. Followed by `template` with placeholders rendered. */
|
|
339
|
+
flag: string;
|
|
340
|
+
/** Template using {prompt} or {prompt_json}, e.g. 'developer_instructions={prompt_json}'. */
|
|
341
|
+
template: string;
|
|
342
|
+
} | {
|
|
343
|
+
mode: 'context_file';
|
|
344
|
+
/** Workspace-relative file path the CLI auto-loads, e.g. 'AGENTS.md' or 'GEMINI.md'. */
|
|
345
|
+
path: string;
|
|
346
|
+
/**
|
|
347
|
+
* Optional wrapper around the prompt. Use `{prompt}` placeholder. Existing
|
|
348
|
+
* wrapper-delimited blocks are replaced rather than duplicated, so re-launching
|
|
349
|
+
* a coordinator doesn't pile up copies. If omitted, the prompt is appended raw.
|
|
350
|
+
*/
|
|
351
|
+
wrapper?: string;
|
|
352
|
+
} | {
|
|
353
|
+
mode: 'env_var';
|
|
354
|
+
/** Env-var name, e.g. 'HERMES_EPHEMERAL_SYSTEM_PROMPT'. */
|
|
355
|
+
name: string;
|
|
356
|
+
};
|
|
309
357
|
export interface ProviderCompatibilityEntry {
|
|
310
358
|
ideVersion: string;
|
|
311
359
|
scriptDir: string;
|
|
@@ -86,7 +86,7 @@ export interface NativeHistorySessionMeta {
|
|
|
86
86
|
*
|
|
87
87
|
* Returns `null` when the file is missing, empty, or yields no parseable messages.
|
|
88
88
|
*/
|
|
89
|
-
export declare function readSession(sessionPath: string, sessionId?: string, workspace?: string):
|
|
89
|
+
export declare function readSession(sessionPath: string, sessionId?: string, workspace?: string): NativeHistorySession | null;
|
|
90
90
|
/**
|
|
91
91
|
* List all Antigravity CLI sessions found across all watchPath locations.
|
|
92
92
|
*
|
|
@@ -56,7 +56,7 @@ export interface NativeHistorySessionMeta {
|
|
|
56
56
|
* `sessionPath` is the absolute path to a `<uuid>.jsonl` file.
|
|
57
57
|
* Returns `null` when the file is missing, empty, or yields no parseable messages.
|
|
58
58
|
*/
|
|
59
|
-
export declare function readSession(sessionPath: string):
|
|
59
|
+
export declare function readSession(sessionPath: string): NativeHistorySession | null;
|
|
60
60
|
/**
|
|
61
61
|
* List all Claude Code sessions under the given glob-style watchPath base dir.
|
|
62
62
|
*
|
|
@@ -60,7 +60,7 @@ export interface NativeHistorySessionMeta {
|
|
|
60
60
|
* under ~/.codex/sessions/.
|
|
61
61
|
* Returns `null` when the file is missing, empty, or yields no parseable messages.
|
|
62
62
|
*/
|
|
63
|
-
export declare function readSession(sessionPath: string):
|
|
63
|
+
export declare function readSession(sessionPath: string): NativeHistorySession | null;
|
|
64
64
|
/**
|
|
65
65
|
* List all Codex CLI sessions under the given watchPath base dir.
|
|
66
66
|
*
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type ReaderId = 'claude-cli' | 'codex-cli' | 'antigravity-cli' | 'hermes-cli';
|
|
2
|
+
export interface NativeHistoryInput {
|
|
3
|
+
agentType?: string;
|
|
4
|
+
sessionId?: string;
|
|
5
|
+
providerSessionId?: string;
|
|
6
|
+
historySessionId?: string;
|
|
7
|
+
workspace?: string;
|
|
8
|
+
format?: string;
|
|
9
|
+
watchPath?: string;
|
|
10
|
+
args?: Record<string, unknown>;
|
|
11
|
+
}
|
|
12
|
+
export interface NativeHistoryResult {
|
|
13
|
+
messages: Array<{
|
|
14
|
+
role: string;
|
|
15
|
+
content: string;
|
|
16
|
+
receivedAt?: number;
|
|
17
|
+
kind?: string;
|
|
18
|
+
}>;
|
|
19
|
+
providerSessionId?: string;
|
|
20
|
+
sourcePath: string;
|
|
21
|
+
sourceMtimeMs: number;
|
|
22
|
+
nativeHistoryCoverage?: 'full' | 'partial' | 'best-effort';
|
|
23
|
+
}
|
|
24
|
+
export declare function createNativeHistoryDispatcher(reader: ReaderId): (input: NativeHistoryInput) => NativeHistoryResult | null;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface NativeHistoryMessage {
|
|
2
|
+
id: string;
|
|
3
|
+
role: 'user' | 'assistant' | 'system';
|
|
4
|
+
content: string;
|
|
5
|
+
receivedAt: number;
|
|
6
|
+
kind?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface NativeHistorySession {
|
|
9
|
+
messages: NativeHistoryMessage[];
|
|
10
|
+
providerSessionId: string;
|
|
11
|
+
source: 'provider-native';
|
|
12
|
+
sourcePath: string;
|
|
13
|
+
sourceMtimeMs: number;
|
|
14
|
+
nativeHistoryCoverage: 'full';
|
|
15
|
+
workspace?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface NativeHistorySessionMeta {
|
|
18
|
+
historySessionId: string;
|
|
19
|
+
sessionId: string;
|
|
20
|
+
sourcePath: string;
|
|
21
|
+
sourceMtimeMs: number;
|
|
22
|
+
messageCount: number;
|
|
23
|
+
firstMessageAt: number;
|
|
24
|
+
lastMessageAt: number;
|
|
25
|
+
sessionTitle?: string;
|
|
26
|
+
preview?: string;
|
|
27
|
+
workspace?: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function readSession(sessionPath: string): NativeHistorySession | null;
|
|
30
|
+
export declare function listSessions(_watchPath: string): Promise<NativeHistorySessionMeta[]>;
|
|
@@ -9,3 +9,5 @@
|
|
|
9
9
|
export { readSession as readClaudeCliSession, listSessions as listClaudeCliSessions, } from './claude-cli-transcript.js';
|
|
10
10
|
export { readSession as readCodexCliSession, listSessions as listCodexCliSessions, } from './codex-cli-transcript.js';
|
|
11
11
|
export { readSession as readAntigravityCliSession, listSessions as listAntigravityCliSessions, } from './antigravity-cli-transcript.js';
|
|
12
|
+
export { readSession as readHermesCliSession, listSessions as listHermesCliSessions, } from './hermes-cli-transcript.js';
|
|
13
|
+
export { createNativeHistoryDispatcher, type ReaderId } from './dispatcher.js';
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type PtyTransportFactory } from '../../cli-adapters/pty-transport.js';
|
|
2
|
+
export interface TerminalAdapterOpts {
|
|
3
|
+
binary: string;
|
|
4
|
+
args?: string[];
|
|
5
|
+
cwd: string;
|
|
6
|
+
env?: Record<string, string>;
|
|
7
|
+
cols?: number;
|
|
8
|
+
rows?: number;
|
|
9
|
+
/** Coalesce screen snapshots: emit on_screen_changed at most this often. */
|
|
10
|
+
screenChangeDebounceMs?: number;
|
|
11
|
+
/** tick() period. 0 disables ticks. */
|
|
12
|
+
tickIntervalMs?: number;
|
|
13
|
+
/**
|
|
14
|
+
* Optional PTY transport factory. When the daemon supplies a
|
|
15
|
+
* SessionHostPtyTransportFactory (standalone runs the PTY inside the
|
|
16
|
+
* session-host so the runtime/<sid>/snapshot endpoint can serve it),
|
|
17
|
+
* forward it here. Without it the PTY spawns locally inside the
|
|
18
|
+
* daemon process and the dashboard's terminal pane reports
|
|
19
|
+
* "Runtime terminal unavailable: Unknown session".
|
|
20
|
+
*/
|
|
21
|
+
transportFactory?: PtyTransportFactory;
|
|
22
|
+
}
|
|
23
|
+
export interface TerminalAdapterHandlers {
|
|
24
|
+
init?(info: {
|
|
25
|
+
pid: number;
|
|
26
|
+
}): void;
|
|
27
|
+
on_pty_data?(chunk: string): void;
|
|
28
|
+
on_screen_changed?(snapshot: string): void;
|
|
29
|
+
tick?(): void;
|
|
30
|
+
on_exit?(info: {
|
|
31
|
+
exitCode: number;
|
|
32
|
+
}): void;
|
|
33
|
+
}
|
|
34
|
+
export declare class TerminalAdapter {
|
|
35
|
+
private readonly opts;
|
|
36
|
+
private readonly handlers;
|
|
37
|
+
private term;
|
|
38
|
+
private pty;
|
|
39
|
+
private factory;
|
|
40
|
+
private cols;
|
|
41
|
+
private rows;
|
|
42
|
+
private screenDebounceMs;
|
|
43
|
+
private tickIntervalMs;
|
|
44
|
+
private screenTimer;
|
|
45
|
+
private tickTimer;
|
|
46
|
+
private lastScreen;
|
|
47
|
+
constructor(opts: TerminalAdapterOpts, handlers: TerminalAdapterHandlers);
|
|
48
|
+
start(): void;
|
|
49
|
+
send_keys(s: string): void;
|
|
50
|
+
resize(cols: number, rows: number): void;
|
|
51
|
+
snapshot(): string;
|
|
52
|
+
kill(): void;
|
|
53
|
+
private onChunk;
|
|
54
|
+
private computeScreen;
|
|
55
|
+
private stopTimers;
|
|
56
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { CliAdapter, CliAdapterStatus } from '../../cli-adapter-types.js';
|
|
2
|
+
import type { PtyTransportFactory } from '../../cli-adapters/pty-transport.js';
|
|
3
|
+
export declare class SpecCliAdapter implements CliAdapter {
|
|
4
|
+
readonly cliType: string;
|
|
5
|
+
readonly cliName: string;
|
|
6
|
+
readonly workingDir: string;
|
|
7
|
+
private driver;
|
|
8
|
+
private spec;
|
|
9
|
+
private lastEvent;
|
|
10
|
+
private latestState;
|
|
11
|
+
private latestModal;
|
|
12
|
+
private statusCallback;
|
|
13
|
+
private ptyDataCallback;
|
|
14
|
+
private partialResponse;
|
|
15
|
+
private exited;
|
|
16
|
+
private spawned;
|
|
17
|
+
private providerSessionId;
|
|
18
|
+
/** Wall clock at the moment spawn() ran. Used as the cutoff for
|
|
19
|
+
* native-history file selection so a prior session's transcript
|
|
20
|
+
* can't leak into this session before the agent has written its
|
|
21
|
+
* own records. */
|
|
22
|
+
private spawnedAtMs;
|
|
23
|
+
/** Env vars the daemon set on the spawned child. Mesh coordinator
|
|
24
|
+
* points hermes at a per-coordinator HERMES_HOME so the dashboard's
|
|
25
|
+
* native-history reader needs that override to find the right
|
|
26
|
+
* state.db; without it the reader sees ~/.hermes/state.db which
|
|
27
|
+
* the coordinator-launched hermes never writes to. The choice to
|
|
28
|
+
* redirect HERMES_HOME is a workaround for an unresolved hermes
|
|
29
|
+
* upstream feature gap (see hermes-agent#23130 — runtime-supplied
|
|
30
|
+
* MCP config), so this routing keeps the dashboard honest until
|
|
31
|
+
* hermes ships a runtime MCP override. */
|
|
32
|
+
private spawnedEnv;
|
|
33
|
+
constructor(specPath: string, workingDir: string, cliArgs: string[], extraEnv: Record<string, string>, transportFactory?: PtyTransportFactory);
|
|
34
|
+
spawn(): Promise<void>;
|
|
35
|
+
sendMessage(text: string): Promise<void>;
|
|
36
|
+
getStatus(): CliAdapterStatus;
|
|
37
|
+
private maybeRefreshNativeHistory;
|
|
38
|
+
getScriptParsedStatus(): unknown;
|
|
39
|
+
getPartialResponse(): string;
|
|
40
|
+
shutdown(): void;
|
|
41
|
+
cancel(): void;
|
|
42
|
+
isProcessing(): boolean;
|
|
43
|
+
isReady(): boolean;
|
|
44
|
+
setOnStatusChange(cb: () => void): void;
|
|
45
|
+
setOnPtyData(cb: (data: string) => void): void;
|
|
46
|
+
writeRaw(data: string): void;
|
|
47
|
+
resize(cols: number, rows: number): void;
|
|
48
|
+
resolveModal(buttonIndex: number): void;
|
|
49
|
+
resolveAction(data: unknown): Promise<void>;
|
|
50
|
+
isApprovalRecentlyResolved(): boolean;
|
|
51
|
+
clearHistory(): void;
|
|
52
|
+
updateRuntimeSettings(): void;
|
|
53
|
+
setServerConn(): void;
|
|
54
|
+
/**
|
|
55
|
+
* Map an invokeScript(name, args) call onto a control_bar entry.
|
|
56
|
+
*
|
|
57
|
+
* scriptName is matched against control.id. The control's action.type
|
|
58
|
+
* drives the dispatch:
|
|
59
|
+
*
|
|
60
|
+
* send_keys → click_control (e.g. stop)
|
|
61
|
+
* open_picker → click_control then resolve when extract_choices
|
|
62
|
+
* surface; choice index comes from args.choiceIndex
|
|
63
|
+
* or args.choice (string label match), defaulting to 0
|
|
64
|
+
* attach_image → attach_image dispatch; expects args.blob (data url
|
|
65
|
+
* or base64) and args.mime
|
|
66
|
+
*
|
|
67
|
+
* Callers that pass an unknown control id get a { not_found } response.
|
|
68
|
+
* No control matched, no driver call — keeps the surface honest.
|
|
69
|
+
*/
|
|
70
|
+
invokeScript(scriptName: string, args?: Record<string, unknown>): Promise<unknown>;
|
|
71
|
+
getDebugSnapshot(): unknown;
|
|
72
|
+
getRuntimeMetadata(): unknown;
|
|
73
|
+
updateRuntimeMeta(meta?: Record<string, unknown>): void;
|
|
74
|
+
refreshProviderDefinition(): void;
|
|
75
|
+
private handleEvent;
|
|
76
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { PtyTransportFactory } from '../../cli-adapters/pty-transport.js';
|
|
2
|
+
import { type TraceEntry } from './evaluator.js';
|
|
3
|
+
export type DashboardEvent = {
|
|
4
|
+
kind: 'pty_data';
|
|
5
|
+
chunk: string;
|
|
6
|
+
} | {
|
|
7
|
+
kind: 'state_changed';
|
|
8
|
+
state: {
|
|
9
|
+
id: string;
|
|
10
|
+
label: string;
|
|
11
|
+
title: string | null;
|
|
12
|
+
};
|
|
13
|
+
modal: {
|
|
14
|
+
title: string | null;
|
|
15
|
+
buttons: {
|
|
16
|
+
index: number;
|
|
17
|
+
label: string;
|
|
18
|
+
}[];
|
|
19
|
+
} | null;
|
|
20
|
+
controls: {
|
|
21
|
+
id: string;
|
|
22
|
+
label: string;
|
|
23
|
+
action_type: string;
|
|
24
|
+
}[];
|
|
25
|
+
} | {
|
|
26
|
+
kind: 'notification';
|
|
27
|
+
id: string;
|
|
28
|
+
title: string;
|
|
29
|
+
body: string;
|
|
30
|
+
} | {
|
|
31
|
+
kind: 'delegate';
|
|
32
|
+
id: string;
|
|
33
|
+
task: string;
|
|
34
|
+
} | {
|
|
35
|
+
kind: 'spec_trace';
|
|
36
|
+
entries: TraceEntry[];
|
|
37
|
+
} | {
|
|
38
|
+
kind: 'exit';
|
|
39
|
+
exit_code: number;
|
|
40
|
+
} | {
|
|
41
|
+
kind: 'spec_error';
|
|
42
|
+
errors: string[];
|
|
43
|
+
};
|
|
44
|
+
export type DashboardCommand = {
|
|
45
|
+
kind: 'send_message';
|
|
46
|
+
text: string;
|
|
47
|
+
} | {
|
|
48
|
+
kind: 'pty_write';
|
|
49
|
+
data: string;
|
|
50
|
+
} | {
|
|
51
|
+
kind: 'click_control';
|
|
52
|
+
control_id: string;
|
|
53
|
+
payload?: unknown;
|
|
54
|
+
} | {
|
|
55
|
+
kind: 'click_modal_button';
|
|
56
|
+
index: number;
|
|
57
|
+
} | {
|
|
58
|
+
kind: 'attach_image';
|
|
59
|
+
blob: string;
|
|
60
|
+
mime: string;
|
|
61
|
+
} | {
|
|
62
|
+
kind: 'resize';
|
|
63
|
+
cols: number;
|
|
64
|
+
rows: number;
|
|
65
|
+
} | {
|
|
66
|
+
kind: 'cancel';
|
|
67
|
+
} | {
|
|
68
|
+
kind: 'shutdown';
|
|
69
|
+
};
|
|
70
|
+
export interface SpecDriverOpts {
|
|
71
|
+
specPath: string;
|
|
72
|
+
workingDir: string;
|
|
73
|
+
extraEnv?: Record<string, string>;
|
|
74
|
+
cols?: number;
|
|
75
|
+
rows?: number;
|
|
76
|
+
/** Set false to skip the spec.json fs.watch. */
|
|
77
|
+
hotReload?: boolean;
|
|
78
|
+
/** Set true to forward trace entries on every state_changed. */
|
|
79
|
+
emitTrace?: boolean;
|
|
80
|
+
/** Inject the daemon's PTY transport (typically SessionHostPtyTransportFactory). */
|
|
81
|
+
transportFactory?: PtyTransportFactory;
|
|
82
|
+
/**
|
|
83
|
+
* Extra CLI args appended to spec.spawn_args. Used by the daemon to
|
|
84
|
+
* pass per-launch arguments like `--session-id <uuid>` so the agent
|
|
85
|
+
* uses the daemon's providerSessionId instead of generating its own.
|
|
86
|
+
*/
|
|
87
|
+
extraCliArgs?: string[];
|
|
88
|
+
}
|
|
89
|
+
export declare class SpecDriver {
|
|
90
|
+
private readonly opts;
|
|
91
|
+
private spec;
|
|
92
|
+
private adapter;
|
|
93
|
+
private listeners;
|
|
94
|
+
private currentStateId;
|
|
95
|
+
/** Have we ever seen the spec's idle state *after* the startup grace
|
|
96
|
+
* window? Until we do, the agent's startup banner may still be
|
|
97
|
+
* painting and any send_message we forward to the PTY will be wiped
|
|
98
|
+
* when the banner clears the screen. Queue + drain on first valid
|
|
99
|
+
* idle. */
|
|
100
|
+
private idleSeenOnce;
|
|
101
|
+
private startedAtMs;
|
|
102
|
+
private pendingSends;
|
|
103
|
+
private currentEval;
|
|
104
|
+
private pickerInProgress;
|
|
105
|
+
private delegateTimers;
|
|
106
|
+
/** Timestamp of the last time the evaluator returned busy. Used to debounce
|
|
107
|
+
* the busy → idle transition (see reevaluate). */
|
|
108
|
+
private lastBusyAt;
|
|
109
|
+
/** The exact busy state object we last saw — held alongside lastBusyAt so
|
|
110
|
+
* the hold can re-emit the same { id: 'busy', label, title } payload the
|
|
111
|
+
* dashboard already learned about. currentEval can't fill this role
|
|
112
|
+
* because the evaluator already moved past busy by the time the hold
|
|
113
|
+
* kicks in. */
|
|
114
|
+
private lastBusyState;
|
|
115
|
+
/** Timer that re-runs evaluate() once the hold window expires. Needed
|
|
116
|
+
* because the PTY stops emitting once the agent finishes; without an
|
|
117
|
+
* explicit wake-up there's nothing to trigger the busy → idle
|
|
118
|
+
* downshift. */
|
|
119
|
+
private busyExpiryTimer;
|
|
120
|
+
private specWatcher;
|
|
121
|
+
constructor(opts: SpecDriverOpts);
|
|
122
|
+
/** Subscribe to outbound events. Returns an unsubscribe fn. */
|
|
123
|
+
subscribe(listener: (ev: DashboardEvent) => void): () => void;
|
|
124
|
+
start(): void;
|
|
125
|
+
dispatch(cmd: DashboardCommand): void;
|
|
126
|
+
shutdown(): void;
|
|
127
|
+
private loadSpecOrThrow;
|
|
128
|
+
private buildAdapterOpts;
|
|
129
|
+
private armSpecWatcher;
|
|
130
|
+
private emitInitialState;
|
|
131
|
+
/** Re-arm the timer that wakes the driver up after BUSY_HOLD_MS so it
|
|
132
|
+
* can decide whether to downshift to idle. Always uses the most recent
|
|
133
|
+
* hold value so a spec hot-reload that shortens the hold takes effect
|
|
134
|
+
* on the next busy entry. Safe to call repeatedly; only the last call
|
|
135
|
+
* fires. */
|
|
136
|
+
private scheduleBusyExpiry;
|
|
137
|
+
private reevaluate;
|
|
138
|
+
private armOrCancelDelegateTimers;
|
|
139
|
+
private fireDelegate;
|
|
140
|
+
private handleSendMessage;
|
|
141
|
+
private actuallySendMessage;
|
|
142
|
+
private handleClickControl;
|
|
143
|
+
private handleClickModalButton;
|
|
144
|
+
private handleAttachImage;
|
|
145
|
+
private tryAdvancePicker;
|
|
146
|
+
private handleExit;
|
|
147
|
+
private emit;
|
|
148
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { CliSpec } from './types.js';
|
|
2
|
+
export interface ResolvedSection {
|
|
3
|
+
id: string;
|
|
4
|
+
fromLine: number;
|
|
5
|
+
toLine: number;
|
|
6
|
+
text: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ModalSnapshot {
|
|
9
|
+
title: string | null;
|
|
10
|
+
buttons: {
|
|
11
|
+
index: number;
|
|
12
|
+
label: string;
|
|
13
|
+
key: string;
|
|
14
|
+
}[];
|
|
15
|
+
}
|
|
16
|
+
export interface VisibleControl {
|
|
17
|
+
id: string;
|
|
18
|
+
label: string;
|
|
19
|
+
actionType: 'send_keys' | 'open_picker' | 'attach_image';
|
|
20
|
+
}
|
|
21
|
+
export interface FiredNotification {
|
|
22
|
+
id: string;
|
|
23
|
+
title: string;
|
|
24
|
+
body: string;
|
|
25
|
+
}
|
|
26
|
+
export interface FiredDelegate {
|
|
27
|
+
id: string;
|
|
28
|
+
task: string;
|
|
29
|
+
}
|
|
30
|
+
export interface TraceEntry {
|
|
31
|
+
kind: 'section' | 'state_match' | 'state_skip' | 'modal' | 'control' | 'notification' | 'delegate';
|
|
32
|
+
text: string;
|
|
33
|
+
}
|
|
34
|
+
export interface SpecEvaluation {
|
|
35
|
+
state: {
|
|
36
|
+
id: string;
|
|
37
|
+
label: string;
|
|
38
|
+
title: string | null;
|
|
39
|
+
};
|
|
40
|
+
modal: ModalSnapshot | null;
|
|
41
|
+
controls: VisibleControl[];
|
|
42
|
+
notifications: FiredNotification[];
|
|
43
|
+
delegates: FiredDelegate[];
|
|
44
|
+
sections: ResolvedSection[];
|
|
45
|
+
trace: TraceEntry[];
|
|
46
|
+
}
|
|
47
|
+
export declare function evaluate(spec: CliSpec, screenText: string): SpecEvaluation;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CliSpec } from './types.js';
|
|
2
|
+
export interface SpecLoadResult {
|
|
3
|
+
ok: true;
|
|
4
|
+
spec: CliSpec;
|
|
5
|
+
sourcePath: string;
|
|
6
|
+
}
|
|
7
|
+
export interface SpecLoadError {
|
|
8
|
+
ok: false;
|
|
9
|
+
errors: string[];
|
|
10
|
+
sourcePath: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function loadSpec(sourcePath: string): SpecLoadResult | SpecLoadError;
|
|
13
|
+
/** Convenience: look up a provider's spec.json next to its provider dir. */
|
|
14
|
+
export declare function resolveSpecPath(providerDir: string): string;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { NativeHistoryConfig } from './types.js';
|
|
2
|
+
export interface NativeHistoryInput {
|
|
3
|
+
agentType?: string;
|
|
4
|
+
sessionId?: string;
|
|
5
|
+
providerSessionId?: string;
|
|
6
|
+
historySessionId?: string;
|
|
7
|
+
workspace?: string;
|
|
8
|
+
/** Daemon-side wall clock at the moment the session was registered.
|
|
9
|
+
* Native-history file lookups use this as the lower bound: any file
|
|
10
|
+
* whose mtime is before the current session started can't be from
|
|
11
|
+
* this session, so it's excluded from newest-recent matching. The
|
|
12
|
+
* caller (chat-history pipeline) populates this from the session
|
|
13
|
+
* registry; specs/executor never need to know how it's sourced. */
|
|
14
|
+
sessionStartedAtMs?: number;
|
|
15
|
+
/** Env overrides the daemon set on the spawned CLI. The mesh
|
|
16
|
+
* coordinator points hermes at a per-coordinator HERMES_HOME so
|
|
17
|
+
* the hermes process writes its state.db into a tmp directory
|
|
18
|
+
* instead of ~/.hermes. expandPath consults this map before
|
|
19
|
+
* process.env so the native-history reader follows the spawned
|
|
20
|
+
* child's view of HERMES_HOME / similar overrides; without it
|
|
21
|
+
* the reader would always look at ~/.hermes and miss every
|
|
22
|
+
* coordinator-session transcript. */
|
|
23
|
+
envOverrides?: Record<string, string>;
|
|
24
|
+
args?: Record<string, unknown>;
|
|
25
|
+
}
|
|
26
|
+
export interface NativeHistoryMessage {
|
|
27
|
+
role: 'user' | 'assistant' | 'system';
|
|
28
|
+
content: string;
|
|
29
|
+
receivedAt: number;
|
|
30
|
+
kind?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface NativeHistoryResult {
|
|
33
|
+
messages: NativeHistoryMessage[];
|
|
34
|
+
providerSessionId?: string;
|
|
35
|
+
sourcePath: string;
|
|
36
|
+
sourceMtimeMs: number;
|
|
37
|
+
nativeHistoryCoverage?: 'full' | 'partial' | 'best-effort';
|
|
38
|
+
}
|
|
39
|
+
export declare function executeNativeHistory(cfg: NativeHistoryConfig, input: NativeHistoryInput): NativeHistoryResult | null;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CliProviderModule } from '../../cli-adapters/provider-cli-adapter.js';
|
|
2
|
+
import type { PtyTransportFactory } from '../../cli-adapters/pty-transport.js';
|
|
3
|
+
import type { CliAdapter } from '../../cli-adapter-types.js';
|
|
4
|
+
export declare function createCliAdapter(provider: CliProviderModule, workingDir: string, cliArgs: string[], extraEnv: Record<string, string>, transportFactory?: PtyTransportFactory): CliAdapter;
|