@flowy-agent-store/sdk 0.1.0-beta.10
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/LICENSE +201 -0
- package/README.md +138 -0
- package/dist/index.cjs +475 -0
- package/dist/index.d.cts +248 -0
- package/dist/index.d.mts +248 -0
- package/dist/index.mjs +467 -0
- package/package.json +49 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { AppServerClient } from "@flowy-agent-store/client";
|
|
2
|
+
import { ClientCapabilities, ClientInfo, ExpertPack, InitializeResult } from "@flowy-agent-store/protocol";
|
|
3
|
+
//#region src/bin.d.ts
|
|
4
|
+
export declare function resolveAppServerBin(explicit?: string): string;
|
|
5
|
+
//#endregion
|
|
6
|
+
//#region src/export.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* The wire faces this module needs, narrowed from {@link Harness} on purpose:
|
|
9
|
+
* unit tests hand a fake, and the dependency surface stays explicit instead of
|
|
10
|
+
* quietly reaching `store` / `conversations`. A real harness satisfies this
|
|
11
|
+
* structurally (`Harness` extends `AppServerClient`, which owns `agents` /
|
|
12
|
+
* `teams` / `skills` and the two flat asset methods).
|
|
13
|
+
*/
|
|
14
|
+
interface ExportDeps {
|
|
15
|
+
agents: {
|
|
16
|
+
export(agentId: string): Promise<ExpertPack>;
|
|
17
|
+
};
|
|
18
|
+
teams: {
|
|
19
|
+
export(teamId: string, teamVersion?: string): Promise<ExpertPack>;
|
|
20
|
+
};
|
|
21
|
+
skills: {
|
|
22
|
+
files(skillId: string): Promise<{
|
|
23
|
+
files: {
|
|
24
|
+
path: string;
|
|
25
|
+
}[];
|
|
26
|
+
}>;
|
|
27
|
+
readFile(skillId: string, path: string): Promise<Uint8Array>;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* The plugin-level snapshot asset byte face (`import/asset`, doc 38). The
|
|
31
|
+
* pack names the `avatars/`, `bin/` and `scripts/` files by reference —
|
|
32
|
+
* that list is the single source of what to fetch — and this fetches one
|
|
33
|
+
* file's bytes. (The listing method `import/assets` exists on the client;
|
|
34
|
+
* materialization deliberately trusts only the pack, so the exported
|
|
35
|
+
* directory can never contain a file the pack does not name.)
|
|
36
|
+
*/
|
|
37
|
+
readImportAsset(snapshotId: string, path: string): Promise<Uint8Array>;
|
|
38
|
+
}
|
|
39
|
+
interface ExportResult {
|
|
40
|
+
/** The pack is also returned in memory: a consumer that never touches the disk needs no second call. */
|
|
41
|
+
pack: ExpertPack;
|
|
42
|
+
dir: string;
|
|
43
|
+
/** Skill names actually written (deduplicated). */
|
|
44
|
+
writtenSkills: string[];
|
|
45
|
+
/**
|
|
46
|
+
* Snapshot-relative asset paths actually written, in the pack's order. The
|
|
47
|
+
* materialized directory is therefore a complete **plugin root**: a team
|
|
48
|
+
* whose scripts resolve `PLUGIN_ROOT/bin/init_task.py` two levels above
|
|
49
|
+
* their skill finds it, and the `avatars/` references in its own skill
|
|
50
|
+
* documents resolve too (doc 38 §3.5).
|
|
51
|
+
*/
|
|
52
|
+
writtenAssets: string[];
|
|
53
|
+
/**
|
|
54
|
+
* Skills the pack **declares** but this host cannot resolve (`skill/files`
|
|
55
|
+
* rejected them). Reported honestly, never skipped in silence: the pack
|
|
56
|
+
* states declarations, resolvability is a host fact the consumer has to
|
|
57
|
+
* judge (doc `32` §5 R8, live criterion EX-008).
|
|
58
|
+
*/
|
|
59
|
+
danglingSkills: {
|
|
60
|
+
id: string;
|
|
61
|
+
error: string;
|
|
62
|
+
}[];
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Write an in-hand pack to `dir` as a real directory (docs `35` §5 and `38`
|
|
66
|
+
* §3.5). "Materialize" here means exactly that: the in-memory pack object plus
|
|
67
|
+
* the skill and asset bytes it references become files on disk; the pack data
|
|
68
|
+
* itself is unchanged.
|
|
69
|
+
*
|
|
70
|
+
* ```text
|
|
71
|
+
* <dir>/expert-pack.json # the wire pack, byte-for-byte (JSON.stringify(_, 2))
|
|
72
|
+
* <dir>/persona.md # kind === "agent" only: pack.persona.instructions
|
|
73
|
+
* <dir>/members/<id>/persona.md # kind === "team" only, one per member
|
|
74
|
+
* <dir>/skills/<name>/… # every referenced skill's files, deduplicated by id
|
|
75
|
+
* <dir>/avatars/… # pack.assets, kind === "avatar", at their snapshot paths
|
|
76
|
+
* <dir>/bin/… · scripts/… # pack.assets, kind === "script" (doc 38 D5)
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* Error semantics (doc `35` §6): the pack is fetched **before** anything is
|
|
80
|
+
* written, so a failed wire export leaves no directory behind; a `skill/files`
|
|
81
|
+
* rejection lands in {@link ExportResult.danglingSkills} and the loop
|
|
82
|
+
* continues; a `skill/file` or `import/asset` rejection after a successful
|
|
83
|
+
* listing **throws** — that is a host I/O failure, and swallowing it would
|
|
84
|
+
* produce a directory that lists files it does not contain. No rollback on
|
|
85
|
+
* mid-write failure.
|
|
86
|
+
*/
|
|
87
|
+
export declare function materializePack(deps: ExportDeps, pack: ExpertPack, dir: string): Promise<ExportResult>;
|
|
88
|
+
/**
|
|
89
|
+
* Export one single-agent expert and write it out as a directory.
|
|
90
|
+
*
|
|
91
|
+
* Same preconditions as the wire method: the expert must be installed and
|
|
92
|
+
* enabled, or the host answers `agent_not_installed` / `agent_disabled` /
|
|
93
|
+
* `preset_disabled`; a host whose `[expert_export]` table refuses the id
|
|
94
|
+
* answers `policy_denied` (the capability bit only reports the seam). Check
|
|
95
|
+
* `capabilities.expert_export` for the seam, not for this id.
|
|
96
|
+
*/
|
|
97
|
+
export declare function exportAgent(deps: ExportDeps, agentId: string, dir: string): Promise<ExportResult>;
|
|
98
|
+
/**
|
|
99
|
+
* Export one expert team and write it out as a directory, leader first.
|
|
100
|
+
*
|
|
101
|
+
* All-or-nothing stays on the server: if any member is uninstalled, disabled,
|
|
102
|
+
* or refused by policy, the whole call fails and nothing is written. A pack
|
|
103
|
+
* over 1 MiB fails with `response_too_large` rather than being truncated.
|
|
104
|
+
* `teamVersion` is an optional pin; a mismatch is `version_mismatch`.
|
|
105
|
+
*/
|
|
106
|
+
export declare function exportTeam(deps: ExportDeps, teamId: string, dir: string, teamVersion?: string): Promise<ExportResult>;
|
|
107
|
+
//#endregion
|
|
108
|
+
//#region src/readiness.d.ts
|
|
109
|
+
/**
|
|
110
|
+
* Readiness protocol (docs/agent-store/12 P0-1).
|
|
111
|
+
*
|
|
112
|
+
* The runtime prints exactly one machine-readable line on stdout after the
|
|
113
|
+
* socket is bound. Tracing logs share stdout, so the host scans lines for a
|
|
114
|
+
* JSON object with `"agent_store": "listening"` and ignores everything else.
|
|
115
|
+
*/
|
|
116
|
+
interface ReadinessInfo {
|
|
117
|
+
host: string;
|
|
118
|
+
port: number;
|
|
119
|
+
url: string;
|
|
120
|
+
protocol_version: string;
|
|
121
|
+
version: string;
|
|
122
|
+
auth: string;
|
|
123
|
+
}
|
|
124
|
+
/** Parse one stdout line; `null` when it is not the readiness line. */
|
|
125
|
+
export declare function parseReadinessLine(line: string): ReadinessInfo | null;
|
|
126
|
+
//#endregion
|
|
127
|
+
//#region src/spawn.d.ts
|
|
128
|
+
/** Fail when the runtime speaks a different wire protocol (§7 acceptance). */
|
|
129
|
+
export declare function assertProtocolCompatible(runtimeVersion: string): void;
|
|
130
|
+
interface SpawnOptions {
|
|
131
|
+
/** Explicit binary path (overrides `AGENT_STORE_BIN` / `PATH`). */
|
|
132
|
+
bin?: string;
|
|
133
|
+
/** Data directory the child owns. Defaults to a fresh temp dir (removed on `close`). */
|
|
134
|
+
dataDir?: string;
|
|
135
|
+
/** Port to bind. Defaults to `0` (OS-assigned, reported by the readiness line). */
|
|
136
|
+
port?: number;
|
|
137
|
+
/** Extra CLI args appended after the managed ones. */
|
|
138
|
+
extraArgs?: string[];
|
|
139
|
+
/**
|
|
140
|
+
* What the spawned host does with its default marketplace sources at boot
|
|
141
|
+
* (doc `37` §3.1). Sent as `AGENT_STORE_MARKET_DOWNLOAD`.
|
|
142
|
+
*
|
|
143
|
+
* - `"lazy"` — register the declared sources and download nothing; the
|
|
144
|
+
* archives wait for an explicit `market/refresh` / `refreshMarketplace()`;
|
|
145
|
+
* - `"none"` — register nothing at all, so `store/list` starts empty;
|
|
146
|
+
* - `"eager"` — fetch every declared source during boot.
|
|
147
|
+
*
|
|
148
|
+
* Omitted = the host's own policy (`download_on_start` per source, default
|
|
149
|
+
* `true`, so a declared source *is* downloaded unless it says otherwise).
|
|
150
|
+
* This is a process-scoped override: nothing is written to the user's config.
|
|
151
|
+
*/
|
|
152
|
+
marketDownload?: "eager" | "lazy" | "none";
|
|
153
|
+
/**
|
|
154
|
+
* Config file for this child only (doc `37` §3.1). Sent as
|
|
155
|
+
* `AGENT_STORE_CONFIG`.
|
|
156
|
+
*
|
|
157
|
+
* Without it the host reads the per-user `~/.agent-store/config.toml` — which
|
|
158
|
+
* a spawned, SDK-owned host should not have to share. The data directory is
|
|
159
|
+
* independent of this (`dataDir`, always explicit).
|
|
160
|
+
*/
|
|
161
|
+
configPath?: string;
|
|
162
|
+
/** How long to wait for the readiness line. Defaults to 120s (cold DB init). */
|
|
163
|
+
readyTimeoutMs?: number;
|
|
164
|
+
/**
|
|
165
|
+
* Extra environment variables, merged over the parent's `process.env`.
|
|
166
|
+
*
|
|
167
|
+
* An entry here **wins over the typed options above** (`marketDownload`,
|
|
168
|
+
* `configPath`): the raw map is the caller's most specific statement, and
|
|
169
|
+
* nothing this factory derives should silently overwrite it.
|
|
170
|
+
*/
|
|
171
|
+
env?: Record<string, string | undefined>;
|
|
172
|
+
/** Working directory for the child. Defaults to the parent's cwd. */
|
|
173
|
+
cwd?: string;
|
|
174
|
+
/** Called once when the child exits, for any reason (crash included). */
|
|
175
|
+
onExit?: (info: SpawnExitInfo) => void;
|
|
176
|
+
}
|
|
177
|
+
/** How a spawned runtime process ended. */
|
|
178
|
+
interface SpawnExitInfo {
|
|
179
|
+
/** Exit code, or `null` when the process was terminated by a signal. */
|
|
180
|
+
code: number | null;
|
|
181
|
+
/** Terminating signal, or `null` on a normal exit. */
|
|
182
|
+
signal: string | null;
|
|
183
|
+
}
|
|
184
|
+
interface SpawnedServer {
|
|
185
|
+
readonly readiness: ReadinessInfo;
|
|
186
|
+
readonly dataDir: string;
|
|
187
|
+
/**
|
|
188
|
+
* Settles when the runtime process exits — including an unexpected crash,
|
|
189
|
+
* which is otherwise indistinguishable from "still starting up". Never
|
|
190
|
+
* rejects: inspect `code` / `signal`.
|
|
191
|
+
*/
|
|
192
|
+
readonly exited: Promise<SpawnExitInfo>;
|
|
193
|
+
/** Terminate the child and remove the data dir when it was auto-created. */
|
|
194
|
+
close(): Promise<void>;
|
|
195
|
+
}
|
|
196
|
+
export declare function spawnAppServer(options?: SpawnOptions): Promise<SpawnedServer>;
|
|
197
|
+
//#endregion
|
|
198
|
+
//#region src/index.d.ts
|
|
199
|
+
export interface HarnessOptions extends SpawnOptions {
|
|
200
|
+
client: ClientInfo;
|
|
201
|
+
capabilities?: ClientCapabilities;
|
|
202
|
+
token?: string;
|
|
203
|
+
requestTimeoutMs?: number;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* A connected `AppServerClient` that **owns the runtime process it spawned**
|
|
207
|
+
* (doc `31` §5 方案 B).
|
|
208
|
+
*
|
|
209
|
+
* Every `AppServerClient` member is available directly — `conversations`,
|
|
210
|
+
* `store`, `agents`, `onNotification`, … — because a factory that returned a
|
|
211
|
+
* *wrapper* was the lie this shape removes.
|
|
212
|
+
*
|
|
213
|
+
* Named after the external reference this shape follows (doc `31` §3.2):
|
|
214
|
+
* Kimi Code's `const harness = createKimiHarness(...)` hands back a `harness`
|
|
215
|
+
* you keep for the whole life of the run, not a one-shot launcher.
|
|
216
|
+
*/
|
|
217
|
+
export interface Harness extends AppServerClient {
|
|
218
|
+
/** The child process: `readiness` / `dataDir` / `exited` / `close`. */
|
|
219
|
+
readonly server: SpawnedServer;
|
|
220
|
+
/**
|
|
221
|
+
* The handshake response captured at launch (**never null**).
|
|
222
|
+
*
|
|
223
|
+
* Not named `initializeResult`: that is a private field of the base class
|
|
224
|
+
* (`client.ts`), and the base's public `initializeInfo` is the *current*
|
|
225
|
+
* connection state — it goes back to `null` after `close()`. This member is
|
|
226
|
+
* the snapshot that proves which protocol fingerprint the session was
|
|
227
|
+
* admitted under.
|
|
228
|
+
*/
|
|
229
|
+
readonly handshake: InitializeResult;
|
|
230
|
+
/**
|
|
231
|
+
* Unsubscribe → close the transport → terminate the child → remove an
|
|
232
|
+
* auto-created data dir. Idempotent.
|
|
233
|
+
*
|
|
234
|
+
* Deliberately widens the base's synchronous `close(): void`: awaiting a
|
|
235
|
+
* void-returning call is legal, so callers holding a plain `AppServerClient`
|
|
236
|
+
* are unaffected, while a launched session needs the promise to know the
|
|
237
|
+
* process is actually gone.
|
|
238
|
+
*/
|
|
239
|
+
close(): Promise<void>;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Spawn a loopback runtime with its own data dir and return a connected,
|
|
243
|
+
* initialized client. Rejects non-loopback contact outright (P0-4): this
|
|
244
|
+
* entry point only ever dials the process it just spawned.
|
|
245
|
+
*/
|
|
246
|
+
export declare function launchHarness(options: HarnessOptions): Promise<Harness>;
|
|
247
|
+
//#endregion
|
|
248
|
+
export type { ExportDeps, ExportResult, ReadinessInfo, SpawnExitInfo, SpawnOptions, SpawnedServer };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { AppServerClient } from "@flowy-agent-store/client";
|
|
2
|
+
import { ClientCapabilities, ClientInfo, ExpertPack, InitializeResult } from "@flowy-agent-store/protocol";
|
|
3
|
+
//#region src/bin.d.ts
|
|
4
|
+
export declare function resolveAppServerBin(explicit?: string): string;
|
|
5
|
+
//#endregion
|
|
6
|
+
//#region src/export.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* The wire faces this module needs, narrowed from {@link Harness} on purpose:
|
|
9
|
+
* unit tests hand a fake, and the dependency surface stays explicit instead of
|
|
10
|
+
* quietly reaching `store` / `conversations`. A real harness satisfies this
|
|
11
|
+
* structurally (`Harness` extends `AppServerClient`, which owns `agents` /
|
|
12
|
+
* `teams` / `skills` and the two flat asset methods).
|
|
13
|
+
*/
|
|
14
|
+
interface ExportDeps {
|
|
15
|
+
agents: {
|
|
16
|
+
export(agentId: string): Promise<ExpertPack>;
|
|
17
|
+
};
|
|
18
|
+
teams: {
|
|
19
|
+
export(teamId: string, teamVersion?: string): Promise<ExpertPack>;
|
|
20
|
+
};
|
|
21
|
+
skills: {
|
|
22
|
+
files(skillId: string): Promise<{
|
|
23
|
+
files: {
|
|
24
|
+
path: string;
|
|
25
|
+
}[];
|
|
26
|
+
}>;
|
|
27
|
+
readFile(skillId: string, path: string): Promise<Uint8Array>;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* The plugin-level snapshot asset byte face (`import/asset`, doc 38). The
|
|
31
|
+
* pack names the `avatars/`, `bin/` and `scripts/` files by reference —
|
|
32
|
+
* that list is the single source of what to fetch — and this fetches one
|
|
33
|
+
* file's bytes. (The listing method `import/assets` exists on the client;
|
|
34
|
+
* materialization deliberately trusts only the pack, so the exported
|
|
35
|
+
* directory can never contain a file the pack does not name.)
|
|
36
|
+
*/
|
|
37
|
+
readImportAsset(snapshotId: string, path: string): Promise<Uint8Array>;
|
|
38
|
+
}
|
|
39
|
+
interface ExportResult {
|
|
40
|
+
/** The pack is also returned in memory: a consumer that never touches the disk needs no second call. */
|
|
41
|
+
pack: ExpertPack;
|
|
42
|
+
dir: string;
|
|
43
|
+
/** Skill names actually written (deduplicated). */
|
|
44
|
+
writtenSkills: string[];
|
|
45
|
+
/**
|
|
46
|
+
* Snapshot-relative asset paths actually written, in the pack's order. The
|
|
47
|
+
* materialized directory is therefore a complete **plugin root**: a team
|
|
48
|
+
* whose scripts resolve `PLUGIN_ROOT/bin/init_task.py` two levels above
|
|
49
|
+
* their skill finds it, and the `avatars/` references in its own skill
|
|
50
|
+
* documents resolve too (doc 38 §3.5).
|
|
51
|
+
*/
|
|
52
|
+
writtenAssets: string[];
|
|
53
|
+
/**
|
|
54
|
+
* Skills the pack **declares** but this host cannot resolve (`skill/files`
|
|
55
|
+
* rejected them). Reported honestly, never skipped in silence: the pack
|
|
56
|
+
* states declarations, resolvability is a host fact the consumer has to
|
|
57
|
+
* judge (doc `32` §5 R8, live criterion EX-008).
|
|
58
|
+
*/
|
|
59
|
+
danglingSkills: {
|
|
60
|
+
id: string;
|
|
61
|
+
error: string;
|
|
62
|
+
}[];
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Write an in-hand pack to `dir` as a real directory (docs `35` §5 and `38`
|
|
66
|
+
* §3.5). "Materialize" here means exactly that: the in-memory pack object plus
|
|
67
|
+
* the skill and asset bytes it references become files on disk; the pack data
|
|
68
|
+
* itself is unchanged.
|
|
69
|
+
*
|
|
70
|
+
* ```text
|
|
71
|
+
* <dir>/expert-pack.json # the wire pack, byte-for-byte (JSON.stringify(_, 2))
|
|
72
|
+
* <dir>/persona.md # kind === "agent" only: pack.persona.instructions
|
|
73
|
+
* <dir>/members/<id>/persona.md # kind === "team" only, one per member
|
|
74
|
+
* <dir>/skills/<name>/… # every referenced skill's files, deduplicated by id
|
|
75
|
+
* <dir>/avatars/… # pack.assets, kind === "avatar", at their snapshot paths
|
|
76
|
+
* <dir>/bin/… · scripts/… # pack.assets, kind === "script" (doc 38 D5)
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* Error semantics (doc `35` §6): the pack is fetched **before** anything is
|
|
80
|
+
* written, so a failed wire export leaves no directory behind; a `skill/files`
|
|
81
|
+
* rejection lands in {@link ExportResult.danglingSkills} and the loop
|
|
82
|
+
* continues; a `skill/file` or `import/asset` rejection after a successful
|
|
83
|
+
* listing **throws** — that is a host I/O failure, and swallowing it would
|
|
84
|
+
* produce a directory that lists files it does not contain. No rollback on
|
|
85
|
+
* mid-write failure.
|
|
86
|
+
*/
|
|
87
|
+
export declare function materializePack(deps: ExportDeps, pack: ExpertPack, dir: string): Promise<ExportResult>;
|
|
88
|
+
/**
|
|
89
|
+
* Export one single-agent expert and write it out as a directory.
|
|
90
|
+
*
|
|
91
|
+
* Same preconditions as the wire method: the expert must be installed and
|
|
92
|
+
* enabled, or the host answers `agent_not_installed` / `agent_disabled` /
|
|
93
|
+
* `preset_disabled`; a host whose `[expert_export]` table refuses the id
|
|
94
|
+
* answers `policy_denied` (the capability bit only reports the seam). Check
|
|
95
|
+
* `capabilities.expert_export` for the seam, not for this id.
|
|
96
|
+
*/
|
|
97
|
+
export declare function exportAgent(deps: ExportDeps, agentId: string, dir: string): Promise<ExportResult>;
|
|
98
|
+
/**
|
|
99
|
+
* Export one expert team and write it out as a directory, leader first.
|
|
100
|
+
*
|
|
101
|
+
* All-or-nothing stays on the server: if any member is uninstalled, disabled,
|
|
102
|
+
* or refused by policy, the whole call fails and nothing is written. A pack
|
|
103
|
+
* over 1 MiB fails with `response_too_large` rather than being truncated.
|
|
104
|
+
* `teamVersion` is an optional pin; a mismatch is `version_mismatch`.
|
|
105
|
+
*/
|
|
106
|
+
export declare function exportTeam(deps: ExportDeps, teamId: string, dir: string, teamVersion?: string): Promise<ExportResult>;
|
|
107
|
+
//#endregion
|
|
108
|
+
//#region src/readiness.d.ts
|
|
109
|
+
/**
|
|
110
|
+
* Readiness protocol (docs/agent-store/12 P0-1).
|
|
111
|
+
*
|
|
112
|
+
* The runtime prints exactly one machine-readable line on stdout after the
|
|
113
|
+
* socket is bound. Tracing logs share stdout, so the host scans lines for a
|
|
114
|
+
* JSON object with `"agent_store": "listening"` and ignores everything else.
|
|
115
|
+
*/
|
|
116
|
+
interface ReadinessInfo {
|
|
117
|
+
host: string;
|
|
118
|
+
port: number;
|
|
119
|
+
url: string;
|
|
120
|
+
protocol_version: string;
|
|
121
|
+
version: string;
|
|
122
|
+
auth: string;
|
|
123
|
+
}
|
|
124
|
+
/** Parse one stdout line; `null` when it is not the readiness line. */
|
|
125
|
+
export declare function parseReadinessLine(line: string): ReadinessInfo | null;
|
|
126
|
+
//#endregion
|
|
127
|
+
//#region src/spawn.d.ts
|
|
128
|
+
/** Fail when the runtime speaks a different wire protocol (§7 acceptance). */
|
|
129
|
+
export declare function assertProtocolCompatible(runtimeVersion: string): void;
|
|
130
|
+
interface SpawnOptions {
|
|
131
|
+
/** Explicit binary path (overrides `AGENT_STORE_BIN` / `PATH`). */
|
|
132
|
+
bin?: string;
|
|
133
|
+
/** Data directory the child owns. Defaults to a fresh temp dir (removed on `close`). */
|
|
134
|
+
dataDir?: string;
|
|
135
|
+
/** Port to bind. Defaults to `0` (OS-assigned, reported by the readiness line). */
|
|
136
|
+
port?: number;
|
|
137
|
+
/** Extra CLI args appended after the managed ones. */
|
|
138
|
+
extraArgs?: string[];
|
|
139
|
+
/**
|
|
140
|
+
* What the spawned host does with its default marketplace sources at boot
|
|
141
|
+
* (doc `37` §3.1). Sent as `AGENT_STORE_MARKET_DOWNLOAD`.
|
|
142
|
+
*
|
|
143
|
+
* - `"lazy"` — register the declared sources and download nothing; the
|
|
144
|
+
* archives wait for an explicit `market/refresh` / `refreshMarketplace()`;
|
|
145
|
+
* - `"none"` — register nothing at all, so `store/list` starts empty;
|
|
146
|
+
* - `"eager"` — fetch every declared source during boot.
|
|
147
|
+
*
|
|
148
|
+
* Omitted = the host's own policy (`download_on_start` per source, default
|
|
149
|
+
* `true`, so a declared source *is* downloaded unless it says otherwise).
|
|
150
|
+
* This is a process-scoped override: nothing is written to the user's config.
|
|
151
|
+
*/
|
|
152
|
+
marketDownload?: "eager" | "lazy" | "none";
|
|
153
|
+
/**
|
|
154
|
+
* Config file for this child only (doc `37` §3.1). Sent as
|
|
155
|
+
* `AGENT_STORE_CONFIG`.
|
|
156
|
+
*
|
|
157
|
+
* Without it the host reads the per-user `~/.agent-store/config.toml` — which
|
|
158
|
+
* a spawned, SDK-owned host should not have to share. The data directory is
|
|
159
|
+
* independent of this (`dataDir`, always explicit).
|
|
160
|
+
*/
|
|
161
|
+
configPath?: string;
|
|
162
|
+
/** How long to wait for the readiness line. Defaults to 120s (cold DB init). */
|
|
163
|
+
readyTimeoutMs?: number;
|
|
164
|
+
/**
|
|
165
|
+
* Extra environment variables, merged over the parent's `process.env`.
|
|
166
|
+
*
|
|
167
|
+
* An entry here **wins over the typed options above** (`marketDownload`,
|
|
168
|
+
* `configPath`): the raw map is the caller's most specific statement, and
|
|
169
|
+
* nothing this factory derives should silently overwrite it.
|
|
170
|
+
*/
|
|
171
|
+
env?: Record<string, string | undefined>;
|
|
172
|
+
/** Working directory for the child. Defaults to the parent's cwd. */
|
|
173
|
+
cwd?: string;
|
|
174
|
+
/** Called once when the child exits, for any reason (crash included). */
|
|
175
|
+
onExit?: (info: SpawnExitInfo) => void;
|
|
176
|
+
}
|
|
177
|
+
/** How a spawned runtime process ended. */
|
|
178
|
+
interface SpawnExitInfo {
|
|
179
|
+
/** Exit code, or `null` when the process was terminated by a signal. */
|
|
180
|
+
code: number | null;
|
|
181
|
+
/** Terminating signal, or `null` on a normal exit. */
|
|
182
|
+
signal: string | null;
|
|
183
|
+
}
|
|
184
|
+
interface SpawnedServer {
|
|
185
|
+
readonly readiness: ReadinessInfo;
|
|
186
|
+
readonly dataDir: string;
|
|
187
|
+
/**
|
|
188
|
+
* Settles when the runtime process exits — including an unexpected crash,
|
|
189
|
+
* which is otherwise indistinguishable from "still starting up". Never
|
|
190
|
+
* rejects: inspect `code` / `signal`.
|
|
191
|
+
*/
|
|
192
|
+
readonly exited: Promise<SpawnExitInfo>;
|
|
193
|
+
/** Terminate the child and remove the data dir when it was auto-created. */
|
|
194
|
+
close(): Promise<void>;
|
|
195
|
+
}
|
|
196
|
+
export declare function spawnAppServer(options?: SpawnOptions): Promise<SpawnedServer>;
|
|
197
|
+
//#endregion
|
|
198
|
+
//#region src/index.d.ts
|
|
199
|
+
export interface HarnessOptions extends SpawnOptions {
|
|
200
|
+
client: ClientInfo;
|
|
201
|
+
capabilities?: ClientCapabilities;
|
|
202
|
+
token?: string;
|
|
203
|
+
requestTimeoutMs?: number;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* A connected `AppServerClient` that **owns the runtime process it spawned**
|
|
207
|
+
* (doc `31` §5 方案 B).
|
|
208
|
+
*
|
|
209
|
+
* Every `AppServerClient` member is available directly — `conversations`,
|
|
210
|
+
* `store`, `agents`, `onNotification`, … — because a factory that returned a
|
|
211
|
+
* *wrapper* was the lie this shape removes.
|
|
212
|
+
*
|
|
213
|
+
* Named after the external reference this shape follows (doc `31` §3.2):
|
|
214
|
+
* Kimi Code's `const harness = createKimiHarness(...)` hands back a `harness`
|
|
215
|
+
* you keep for the whole life of the run, not a one-shot launcher.
|
|
216
|
+
*/
|
|
217
|
+
export interface Harness extends AppServerClient {
|
|
218
|
+
/** The child process: `readiness` / `dataDir` / `exited` / `close`. */
|
|
219
|
+
readonly server: SpawnedServer;
|
|
220
|
+
/**
|
|
221
|
+
* The handshake response captured at launch (**never null**).
|
|
222
|
+
*
|
|
223
|
+
* Not named `initializeResult`: that is a private field of the base class
|
|
224
|
+
* (`client.ts`), and the base's public `initializeInfo` is the *current*
|
|
225
|
+
* connection state — it goes back to `null` after `close()`. This member is
|
|
226
|
+
* the snapshot that proves which protocol fingerprint the session was
|
|
227
|
+
* admitted under.
|
|
228
|
+
*/
|
|
229
|
+
readonly handshake: InitializeResult;
|
|
230
|
+
/**
|
|
231
|
+
* Unsubscribe → close the transport → terminate the child → remove an
|
|
232
|
+
* auto-created data dir. Idempotent.
|
|
233
|
+
*
|
|
234
|
+
* Deliberately widens the base's synchronous `close(): void`: awaiting a
|
|
235
|
+
* void-returning call is legal, so callers holding a plain `AppServerClient`
|
|
236
|
+
* are unaffected, while a launched session needs the promise to know the
|
|
237
|
+
* process is actually gone.
|
|
238
|
+
*/
|
|
239
|
+
close(): Promise<void>;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Spawn a loopback runtime with its own data dir and return a connected,
|
|
243
|
+
* initialized client. Rejects non-loopback contact outright (P0-4): this
|
|
244
|
+
* entry point only ever dials the process it just spawned.
|
|
245
|
+
*/
|
|
246
|
+
export declare function launchHarness(options: HarnessOptions): Promise<Harness>;
|
|
247
|
+
//#endregion
|
|
248
|
+
export type { ExportDeps, ExportResult, ReadinessInfo, SpawnExitInfo, SpawnOptions, SpawnedServer };
|