@ahpd/server 0.1.0

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.
Files changed (163) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +129 -0
  3. package/dist/automations.d.ts +4 -0
  4. package/dist/automations.d.ts.map +1 -0
  5. package/dist/automations.js +191 -0
  6. package/dist/automations.js.map +1 -0
  7. package/dist/catalog.d.ts +48 -0
  8. package/dist/catalog.d.ts.map +1 -0
  9. package/dist/catalog.js +51 -0
  10. package/dist/catalog.js.map +1 -0
  11. package/dist/changes.d.ts +4 -0
  12. package/dist/changes.d.ts.map +1 -0
  13. package/dist/changes.js +601 -0
  14. package/dist/changes.js.map +1 -0
  15. package/dist/cron.d.ts +43 -0
  16. package/dist/cron.d.ts.map +1 -0
  17. package/dist/cron.js +211 -0
  18. package/dist/cron.js.map +1 -0
  19. package/dist/git.d.ts +16 -0
  20. package/dist/git.d.ts.map +1 -0
  21. package/dist/git.js +126 -0
  22. package/dist/git.js.map +1 -0
  23. package/dist/host.d.ts +24 -0
  24. package/dist/host.d.ts.map +1 -0
  25. package/dist/host.js +5850 -0
  26. package/dist/host.js.map +1 -0
  27. package/dist/index.d.ts +36 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +34 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/listen.d.ts +3 -0
  32. package/dist/listen.d.ts.map +1 -0
  33. package/dist/listen.js +183 -0
  34. package/dist/listen.js.map +1 -0
  35. package/dist/paging.d.ts +38 -0
  36. package/dist/paging.d.ts.map +1 -0
  37. package/dist/paging.js +53 -0
  38. package/dist/paging.js.map +1 -0
  39. package/dist/paths.d.ts +15 -0
  40. package/dist/paths.d.ts.map +1 -0
  41. package/dist/paths.js +19 -0
  42. package/dist/paths.js.map +1 -0
  43. package/dist/resources.d.ts +122 -0
  44. package/dist/resources.d.ts.map +1 -0
  45. package/dist/resources.js +494 -0
  46. package/dist/resources.js.map +1 -0
  47. package/dist/rpc.d.ts +63 -0
  48. package/dist/rpc.d.ts.map +1 -0
  49. package/dist/rpc.js +208 -0
  50. package/dist/rpc.js.map +1 -0
  51. package/dist/scheduled.d.ts +48 -0
  52. package/dist/scheduled.d.ts.map +1 -0
  53. package/dist/scheduled.js +325 -0
  54. package/dist/scheduled.js.map +1 -0
  55. package/dist/terminals.d.ts +29 -0
  56. package/dist/terminals.d.ts.map +1 -0
  57. package/dist/terminals.js +346 -0
  58. package/dist/terminals.js.map +1 -0
  59. package/dist/tools.d.ts +18 -0
  60. package/dist/tools.d.ts.map +1 -0
  61. package/dist/tools.js +65 -0
  62. package/dist/tools.js.map +1 -0
  63. package/dist/types/agent.d.ts +245 -0
  64. package/dist/types/agent.d.ts.map +1 -0
  65. package/dist/types/agent.js +3 -0
  66. package/dist/types/agent.js.map +1 -0
  67. package/dist/types/automations.d.ts +164 -0
  68. package/dist/types/automations.d.ts.map +1 -0
  69. package/dist/types/automations.js +3 -0
  70. package/dist/types/automations.js.map +1 -0
  71. package/dist/types/catalog.d.ts +30 -0
  72. package/dist/types/catalog.d.ts.map +1 -0
  73. package/dist/types/catalog.js +3 -0
  74. package/dist/types/catalog.js.map +1 -0
  75. package/dist/types/changes.d.ts +272 -0
  76. package/dist/types/changes.d.ts.map +1 -0
  77. package/dist/types/changes.js +3 -0
  78. package/dist/types/changes.js.map +1 -0
  79. package/dist/types/common.d.ts +9 -0
  80. package/dist/types/common.d.ts.map +1 -0
  81. package/dist/types/common.js +3 -0
  82. package/dist/types/common.js.map +1 -0
  83. package/dist/types/host.d.ts +372 -0
  84. package/dist/types/host.d.ts.map +1 -0
  85. package/dist/types/host.js +3 -0
  86. package/dist/types/host.js.map +1 -0
  87. package/dist/types/index.d.ts +20 -0
  88. package/dist/types/index.d.ts.map +1 -0
  89. package/dist/types/index.js +8 -0
  90. package/dist/types/index.js.map +1 -0
  91. package/dist/types/listen.d.ts +48 -0
  92. package/dist/types/listen.d.ts.map +1 -0
  93. package/dist/types/listen.js +3 -0
  94. package/dist/types/listen.js.map +1 -0
  95. package/dist/types/paging.d.ts +10 -0
  96. package/dist/types/paging.d.ts.map +1 -0
  97. package/dist/types/paging.js +3 -0
  98. package/dist/types/paging.js.map +1 -0
  99. package/dist/types/probe.d.ts +46 -0
  100. package/dist/types/probe.d.ts.map +1 -0
  101. package/dist/types/probe.js +3 -0
  102. package/dist/types/probe.js.map +1 -0
  103. package/dist/types/resources.d.ts +83 -0
  104. package/dist/types/resources.d.ts.map +1 -0
  105. package/dist/types/resources.js +3 -0
  106. package/dist/types/resources.js.map +1 -0
  107. package/dist/types/rpc.d.ts +65 -0
  108. package/dist/types/rpc.d.ts.map +1 -0
  109. package/dist/types/rpc.js +3 -0
  110. package/dist/types/rpc.js.map +1 -0
  111. package/dist/types/session.d.ts +395 -0
  112. package/dist/types/session.d.ts.map +1 -0
  113. package/dist/types/session.js +3 -0
  114. package/dist/types/session.js.map +1 -0
  115. package/dist/types/terminals.d.ts +131 -0
  116. package/dist/types/terminals.d.ts.map +1 -0
  117. package/dist/types/terminals.js +3 -0
  118. package/dist/types/terminals.js.map +1 -0
  119. package/dist/types/wire.d.ts +62 -0
  120. package/dist/types/wire.d.ts.map +1 -0
  121. package/dist/types/wire.js +13 -0
  122. package/dist/types/wire.js.map +1 -0
  123. package/dist/types/worktrees.d.ts +86 -0
  124. package/dist/types/worktrees.d.ts.map +1 -0
  125. package/dist/types/worktrees.js +3 -0
  126. package/dist/types/worktrees.js.map +1 -0
  127. package/dist/worktrees.d.ts +27 -0
  128. package/dist/worktrees.d.ts.map +1 -0
  129. package/dist/worktrees.js +152 -0
  130. package/dist/worktrees.js.map +1 -0
  131. package/package.json +61 -0
  132. package/src/automations.ts +204 -0
  133. package/src/catalog.ts +53 -0
  134. package/src/changes.ts +629 -0
  135. package/src/cron.ts +251 -0
  136. package/src/git.ts +132 -0
  137. package/src/host.ts +5947 -0
  138. package/src/index.ts +40 -0
  139. package/src/listen.ts +240 -0
  140. package/src/paging.ts +56 -0
  141. package/src/paths.ts +19 -0
  142. package/src/resources.ts +513 -0
  143. package/src/rpc.ts +222 -0
  144. package/src/scheduled.ts +369 -0
  145. package/src/terminals.ts +353 -0
  146. package/src/tools.ts +64 -0
  147. package/src/types/agent.ts +255 -0
  148. package/src/types/automations.ts +164 -0
  149. package/src/types/catalog.ts +26 -0
  150. package/src/types/changes.ts +266 -0
  151. package/src/types/common.ts +9 -0
  152. package/src/types/host.ts +386 -0
  153. package/src/types/index.ts +27 -0
  154. package/src/types/listen.ts +53 -0
  155. package/src/types/paging.ts +11 -0
  156. package/src/types/probe.ts +42 -0
  157. package/src/types/resources.ts +107 -0
  158. package/src/types/rpc.ts +68 -0
  159. package/src/types/session.ts +415 -0
  160. package/src/types/terminals.ts +133 -0
  161. package/src/types/wire.ts +69 -0
  162. package/src/types/worktrees.ts +91 -0
  163. package/src/worktrees.ts +162 -0
@@ -0,0 +1,272 @@
1
+ /** What a session changed, as the protocol's changeset channel carries it. */
2
+ import type { ChangesetOperationScope as Scope, ChangesetOperationTargetKind as TargetKind, ChangesetStatus } from '@microsoft/agent-host-protocol';
3
+ /** A pointer to content the state tree does not carry. */
4
+ export interface ContentRef {
5
+ uri: string;
6
+ sizeHint?: number;
7
+ contentType?: string;
8
+ }
9
+ /**
10
+ * One file, on both sides of the change.
11
+ *
12
+ * `before` absent is a creation and `after` absent a deletion, which is how
13
+ * the protocol says both rather than carrying a status word for them.
14
+ */
15
+ export interface FileEdit {
16
+ before?: {
17
+ uri: string;
18
+ content: ContentRef;
19
+ };
20
+ after?: {
21
+ uri: string;
22
+ content: ContentRef;
23
+ };
24
+ diff?: {
25
+ added?: number;
26
+ removed?: number;
27
+ };
28
+ }
29
+ /** One row of a changeset. `id` is stable within it. */
30
+ export interface ChangesetFile {
31
+ id: string;
32
+ edit: FileEdit;
33
+ reviewed?: boolean;
34
+ }
35
+ /** What a client subscribed to a changeset URI is looking at. */
36
+ export interface ChangesetState {
37
+ /**
38
+ * Where the computation is, in the protocol's own three words.
39
+ *
40
+ * Taken from `ChangesetStatus` rather than written out. This port said
41
+ * `computing | complete | error` for the life of the project and the
42
+ * protocol says `computing | ready | error`, so every changeset ever served
43
+ * carried a status word no client could recognise - and nothing caught it,
44
+ * because a hand-copied union is checked against nothing.
45
+ */
46
+ status: `${ChangesetStatus}`;
47
+ files: ChangesetFile[];
48
+ }
49
+ /** The roll-up a catalogue row carries, so a list needs no subscription. */
50
+ export interface ChangesSummary {
51
+ files?: number;
52
+ additions?: number;
53
+ deletions?: number;
54
+ }
55
+ /**
56
+ * One scope of change a directory can be asked about.
57
+ *
58
+ * The protocol nests changesets under the session's own URI - `uncommitted`,
59
+ * `session`, `turn/<id>` - so a scope is the last part and the host composes
60
+ * the rest. Keeping it that way round means a source never has to know what a
61
+ * session is called.
62
+ */
63
+ export interface ChangesetScope {
64
+ /** The path segment, e.g. `uncommitted`. */
65
+ id: string;
66
+ /** What a client shows, e.g. `Uncommitted Changes`. */
67
+ label: string;
68
+ description?: string;
69
+ /**
70
+ * What kind of changeset this is, so a client can group and sort without
71
+ * parsing the URI. The protocol names `session`, `branch`, `uncommitted`,
72
+ * `turn` and `compare-turns`, and says a client should fall back sensibly
73
+ * on one it does not know.
74
+ */
75
+ changeKind: string;
76
+ /**
77
+ * Whether files in this changeset can be marked reviewed.
78
+ *
79
+ * A presence flag on the catalogue entry, which is what lets a client decide
80
+ * whether to draw the checkbox *before* it subscribes to anything. Review is
81
+ * not an operation: the client dispatches `changeset/filesReviewChanged` and
82
+ * the server keeps the flag.
83
+ */
84
+ reviewable?: boolean;
85
+ }
86
+ /**
87
+ * Where an operation may be invoked.
88
+ *
89
+ * The protocol's three: the whole changeset, one file in it, or a line range
90
+ * within one file. A source declares which it accepts and the host refuses an
91
+ * invocation whose target is not among them.
92
+ */
93
+ export type ChangesetOperationScope = `${Scope}`;
94
+ /** The file, or the lines of it, an operation was pointed at. */
95
+ export interface ChangesetOperationTarget {
96
+ kind: `${TargetKind}`;
97
+ /** The `ChangesetFile.id` of the row, which is a `file://` URI. */
98
+ resource: string;
99
+ /** Which side of the edit, where an operation can act on either. */
100
+ side?: 'before' | 'after';
101
+ /** Present iff `kind` is `range`. Lines are 1-based, as the protocol has them. */
102
+ range?: {
103
+ startLine: number;
104
+ startColumn?: number;
105
+ endLine: number;
106
+ endColumn?: number;
107
+ };
108
+ }
109
+ /**
110
+ * A verb a client may run against a changeset.
111
+ *
112
+ * Server-advertised, and that is the whole access model: `invokeChangesetOperation`
113
+ * carries an `operationId` that must match one this source already offered for
114
+ * this scope, so a client can ask for nothing that was not put in front of it.
115
+ *
116
+ * There is no `status` here because status is not the source's. Whether an
117
+ * operation is disabled depends on whether the session is mid-turn, and whether
118
+ * it is running depends on an invocation in flight - both of which the host
119
+ * knows and a source does not.
120
+ */
121
+ export interface ChangesetOperation {
122
+ /** Stable within the changeset, and what an invocation names. */
123
+ id: string;
124
+ /** The button. */
125
+ label: string;
126
+ /** Longer text, for a tooltip. */
127
+ description?: string;
128
+ /** The targets this operation accepts. */
129
+ scopes: ChangesetOperationScope[];
130
+ /**
131
+ * The question to ask before running it.
132
+ *
133
+ * Its presence is also how the protocol says "this is destructive": a client
134
+ * MUST show it, and SHOULD style the affirmative button as a warning.
135
+ */
136
+ confirmation?: string;
137
+ /** A hint, e.g. `git-commit` or `discard`. */
138
+ icon?: string;
139
+ /** Operations sharing one are drawn together. */
140
+ group?: string;
141
+ /**
142
+ * Whether running it writes to the working tree.
143
+ *
144
+ * What the host gates on: an operation that writes needs a write grant on the
145
+ * resource, negotiated through `resourceRequest`, and is refused with `-32009`
146
+ * until one is held. Declared here rather than inferred from the id, because
147
+ * the host cannot know what a source's verbs do.
148
+ */
149
+ writes?: boolean;
150
+ }
151
+ /** One invocation, as the host hands it to the source. */
152
+ export interface ChangesetOperationRequest {
153
+ dir: string;
154
+ session: string;
155
+ /** The scope segment, e.g. `uncommitted` or `turn/abc`. */
156
+ scope: string;
157
+ operationId: string;
158
+ /** Absent for a changeset-scoped operation. */
159
+ target?: ChangesetOperationTarget;
160
+ /**
161
+ * What the session is called, offered as a commit subject.
162
+ *
163
+ * The host's to know and not this source's: a changeset is a set of files and
164
+ * a session is a conversation, and the sentence somebody would write on a
165
+ * commit is in the second one.
166
+ */
167
+ subject?: string;
168
+ }
169
+ /** What an invocation says for itself. Thrown errors are the failure path. */
170
+ export interface ChangesetOperationResult {
171
+ /** One line for the client to show. */
172
+ message?: string;
173
+ /**
174
+ * Something to open afterwards, when the operation produced one.
175
+ *
176
+ * A `ContentRef` - a URI, a size hint, a content type - and whether the
177
+ * client should open it in a browser rather than inline. A pull request a
178
+ * commit-and-push produced is the case this exists for: the operation
179
+ * succeeded, and the useful thing about it is a page somewhere.
180
+ */
181
+ followUp?: {
182
+ content: {
183
+ uri: string;
184
+ sizeHint?: number;
185
+ contentType?: string;
186
+ nonce?: string;
187
+ };
188
+ external?: boolean;
189
+ };
190
+ }
191
+ /**
192
+ * Where a host's file changes come from.
193
+ *
194
+ * A port, like the filesystem and the shell, and for the sharpest version of
195
+ * the same reason: a diff comes from `git`, which is a binary that may not be
196
+ * installed, against a directory that may not be a repository. A host given
197
+ * none advertises no changesets, which is a true answer rather than an empty
198
+ * screen.
199
+ */
200
+ export interface ChangesetSource {
201
+ /**
202
+ * Which scopes can be answered here. Empty for a directory that has none.
203
+ *
204
+ * `session` is passed because two of the protocol's scopes are a session's
205
+ * rather than a directory's - what *this conversation* changed is not what
206
+ * the working tree looks like, and a directory with three sessions in it has
207
+ * three different answers.
208
+ */
209
+ scopes(dir: string, session: string): ChangesetScope[];
210
+ /** The state behind one of them. */
211
+ state(dir: string, session: string, scope: string): Promise<ChangesetState | undefined>;
212
+ /** The roll-up for a catalogue row, cheap enough to ask per row. */
213
+ summary(dir: string): ChangesSummary | undefined;
214
+ /**
215
+ * Content behind a ref this source minted.
216
+ *
217
+ * The `before` side of an edit is not a file on disk - it is what the file
218
+ * used to be - so it cannot be served by the filesystem port. Undefined for
219
+ * a URI this source does not own, which is how the host knows to try the
220
+ * filesystem instead.
221
+ */
222
+ read?(uri: string): Promise<{
223
+ data: string;
224
+ encoding: string;
225
+ } | undefined>;
226
+ /** Look again, answering whether anything moved. */
227
+ refresh?(dir: string): Promise<boolean>;
228
+ /**
229
+ * Mark files reviewed, or clear them.
230
+ *
231
+ * A person's bookkeeping about a diff they are reading, not a change to
232
+ * anything on disk - which is why it is the one thing here a client may
233
+ * write. Answers whether anything moved, so an idempotent toggle tells
234
+ * nobody about a state it already had.
235
+ */
236
+ review?(dir: string, session: string, scope: string, files: string[], reviewed: boolean): boolean;
237
+ /**
238
+ * A file an agent is about to change, and the same file once it has.
239
+ *
240
+ * What makes a *turn's* changeset the turn's. Git can only ever say what a
241
+ * working tree looks like now, so a turn asked about later would be handed
242
+ * every turn after it as well; capturing both sides as the tool runs is the
243
+ * only way the answer stays the turn's own.
244
+ *
245
+ * Reading the file is this source's business - it is the thing here that
246
+ * has a filesystem - and the session only says which one and when.
247
+ */
248
+ observe?(dir: string, session: string, turnId: string, path: string, phase: 'before' | 'after'): void;
249
+ /**
250
+ * The verbs this source offers on one scope, in the order to draw them.
251
+ *
252
+ * Asked per scope because the answer differs by scope: the working tree can
253
+ * be committed and a turn cannot, and what a turn changed can be put back
254
+ * because both sides of every file in it were captured.
255
+ *
256
+ * Empty is a real answer and the one to give for a scope with nothing to do
257
+ * to it. A source with no method at all advertises none anywhere, which is
258
+ * what a host serving a directory it may not write looks like.
259
+ */
260
+ operations?(dir: string, session: string, scope: string): ChangesetOperation[];
261
+ /**
262
+ * Run one.
263
+ *
264
+ * The host has already checked that `operationId` is among what this source
265
+ * offered for this scope, that the target's kind is one the operation
266
+ * accepts, and that a write grant is held where the operation says it writes.
267
+ * What is left is doing it, and throwing if it did not work - the protocol
268
+ * signals failure by rejecting the request, not by a field on the result.
269
+ */
270
+ invoke?(request: ChangesetOperationRequest): Promise<ChangesetOperationResult>;
271
+ }
272
+ //# sourceMappingURL=changes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"changes.d.ts","sourceRoot":"","sources":["../../src/types/changes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ /** What a session changed, as the protocol's changeset channel carries it. */
2
+ export {};
3
+ //# sourceMappingURL=changes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"changes.js","sourceRoot":"","sources":["../../src/types/changes.ts"],"names":[],"mappings":"AAAA,8EAA8E"}
@@ -0,0 +1,9 @@
1
+ /** Shapes shared by more than one channel. */
2
+ /**
3
+ * An object with no declared shape.
4
+ *
5
+ * Used for JSON arriving off the wire and for the agent SDK's frames, both of
6
+ * which are read field by field rather than trusted as a type.
7
+ */
8
+ export type Bag = Record<string, unknown>;
9
+ //# sourceMappingURL=common.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/types/common.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAE9C;;;;;GAKG;AACH,MAAM,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ /** Shapes shared by more than one channel. */
2
+ export {};
3
+ //# sourceMappingURL=common.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"common.js","sourceRoot":"","sources":["../../src/types/common.ts"],"names":[],"mappings":"AAAA,8CAA8C"}
@@ -0,0 +1,372 @@
1
+ /** The protocol server: channels, subscriptions and requests. */
2
+ import type { ToolDefinition } from '@microsoft/agent-host-protocol';
3
+ import type { Agent } from './agent.js';
4
+ import type { Entry, Metadata, Read, ResourceChange, WatchOptions, Watcher, Write as WriteContent } from './resources.js';
5
+ import type { Terminal, TerminalOptions } from './terminals.js';
6
+ import type { ChangesetSource } from './changes.js';
7
+ import type { Worktrees } from './worktrees.js';
8
+ import type { AutomationStore } from './automations.js';
9
+ import type { Peer, Request } from './rpc.js';
10
+ /**
11
+ * What a host can say about a directory beyond its path.
12
+ *
13
+ * Injected rather than built in. The interesting answers come from outside the
14
+ * protocol - a branch is a `git` subprocess, and `git` is a binary that may
15
+ * not be installed - and a host embedded in something that already knows them
16
+ * should not have them read a second time. A host given none says only what a
17
+ * path alone can tell it, which is the project's name.
18
+ */
19
+ export interface DirectoryFacts {
20
+ /**
21
+ * What is known about a directory now, as the session's `_meta`.
22
+ *
23
+ * Synchronous and cheap, because it is asked for every description of every
24
+ * session - a catalogue of a hundred rows asks a hundred times. Anything
25
+ * that has to be fetched is fetched by `refresh` and cached here.
26
+ *
27
+ * The keys are the protocol's: `git` is the well-known one, and anything of
28
+ * an implementation's own belongs under a namespace.
29
+ */
30
+ meta(dir: string): Record<string, unknown> | undefined;
31
+ /**
32
+ * Look again, answering whether anything actually moved.
33
+ *
34
+ * Asked once per served directory at startup and again whenever a turn
35
+ * ends. Only a true answer reaches a client, so a directory that has not
36
+ * changed costs nothing but the look.
37
+ */
38
+ refresh?(dir: string): Promise<boolean>;
39
+ }
40
+ /**
41
+ * The files a client may read through this host.
42
+ *
43
+ * A port, for the same reason `DirectoryFacts` is one: reading a directory is
44
+ * `node:fs` on one runtime and something else on another, and a host embedded
45
+ * in an editor may already have the file open. `roots` arrives per call rather
46
+ * than being captured, because a backend may learn of a directory after the
47
+ * host started and the answer has to move with it.
48
+ *
49
+ * A host given none serves no `resource*` command at all - `-32601`, the same
50
+ * answer it gives for anything else it does not have - and completes no `@`.
51
+ */
52
+ export interface ResourceStore {
53
+ /** One directory's entries. */
54
+ list(uri: string, roots: string[]): Promise<Entry[]>;
55
+ /** One file's bytes, or the range of them that was asked for. */
56
+ read(uri: string, roots: string[], wanted?: string): Promise<Read>;
57
+ /** What a URI is, without reading it. */
58
+ resolve(uri: string, roots: string[], followSymlinks?: boolean): Promise<Metadata>;
59
+ /** Paths under `base` that start with what is typed. */
60
+ complete(typed: string, base: string, roots: string[], limit?: number): Promise<string[]>;
61
+ /** Write, create or splice one file. */
62
+ write?(uri: string, roots: string[], content: WriteContent): Promise<void>;
63
+ /** Remove a file, or a directory when `recursive`. */
64
+ remove?(uri: string, roots: string[], recursive?: boolean): Promise<void>;
65
+ /** Make a directory, and the parents it needs. */
66
+ mkdir?(uri: string, roots: string[]): Promise<void>;
67
+ /** Rename, within the served directories on both ends. */
68
+ move?(source: string, destination: string, roots: string[], failIfExists?: boolean): Promise<void>;
69
+ /** Copy, within the served directories on both ends. */
70
+ copy?(source: string, destination: string, roots: string[], failIfExists?: boolean): Promise<void>;
71
+ /**
72
+ * Tell me when that changes.
73
+ *
74
+ * Optional on its own rather than with the write half: watching is a read,
75
+ * and a store may perfectly well serve bytes it cannot subscribe to - a
76
+ * remote filesystem, an archive, a fixture. A host whose store has none
77
+ * answers `-32601` for `createResourceWatch`, and the protocol's own client
78
+ * treats that as a reason to degrade rather than to fail.
79
+ *
80
+ * `onChange` is called with a *batch*, because the filesystem reports one
81
+ * event per file and a save is several: the protocol says a server coalesces
82
+ * them, and an empty batch MUST NOT be dispatched. Closing the returned
83
+ * handle is the only way to stop it - there is no dispose command, and
84
+ * `unsubscribe` is what the host turns into this call.
85
+ */
86
+ watch?(uri: string, roots: string[], options: WatchOptions, onChange: (changes: ResourceChange[]) => void): Promise<Watcher>;
87
+ }
88
+ /**
89
+ * The shells this host can open.
90
+ *
91
+ * A port, because a terminal is a subprocess: which one, and how it is
92
+ * spawned, is the runtime's business rather than the protocol's. A host given
93
+ * none serves neither `createTerminal` nor `disposeTerminal`, and says so with
94
+ * `-32601` rather than opening nothing and reporting success.
95
+ */
96
+ export interface TerminalStore {
97
+ /** Open one, in a directory the host has already checked. */
98
+ create(options: TerminalOptions): Terminal;
99
+ }
100
+ /** How to construct a host. */
101
+ export interface HostOptions {
102
+ /**
103
+ * The directory whose sessions this host serves, on the machine it runs on.
104
+ *
105
+ * Also the catalogue's scope: sessions outside it are neither listed nor
106
+ * openable.
107
+ */
108
+ path: string;
109
+ /**
110
+ * The backends this host serves.
111
+ *
112
+ * At least one, and each with a `provider` no other has. The first is what
113
+ * a client gets when it names none - which is the ordinary case, since a
114
+ * client that has read the root channel names one and one that has not
115
+ * cannot.
116
+ *
117
+ * Nothing in the host knows what any of them are. `claude()` is one that
118
+ * ships with it; anything satisfying `Agent` is another.
119
+ */
120
+ agents: Agent[];
121
+ /**
122
+ * The files a client may read, and complete an `@` into.
123
+ *
124
+ * Left out, no `resource*` command is served. `fileResources()` is the one
125
+ * that ships with this package, and the daemon uses it.
126
+ */
127
+ resources?: ResourceStore;
128
+ /**
129
+ * How to open a shell.
130
+ *
131
+ * Left out, no terminal can be created. `shellTerminals()` is the one that
132
+ * ships with this package, and the daemon uses it.
133
+ */
134
+ terminals?: TerminalStore;
135
+ /**
136
+ * Where the file changes a session made come from.
137
+ *
138
+ * Left out, no session advertises a changeset and the changes screen is
139
+ * honestly empty rather than emptily wrong. `gitChanges()` is the one that
140
+ * ships with this package, and the daemon uses it.
141
+ */
142
+ changes?: ChangesetSource;
143
+ /**
144
+ * What this host can say about the directories it serves.
145
+ *
146
+ * Left out, sessions carry their project and nothing more. `gitBranches()`
147
+ * is the one that ships with this package, and the daemon uses it.
148
+ */
149
+ directories?: DirectoryFacts;
150
+ /**
151
+ * Whether a session can be given a working tree of its own.
152
+ *
153
+ * Left out, every session runs in the folder it was pointed at and this host
154
+ * advertises no `isolation` - so a client draws no control for it, which is
155
+ * the honest form of "not offered". `gitWorktrees()` is the one that ships
156
+ * with this package, and the daemon uses it.
157
+ *
158
+ * The reason to wire it in: two agents in one repository is the ordinary
159
+ * case for a sessions server, and without this they share a working tree.
160
+ * The second turn's changeset then contains the first turn's edits, and
161
+ * discarding a file discards somebody else's work.
162
+ */
163
+ worktrees?: Worktrees;
164
+ /**
165
+ * The automations this host offers.
166
+ *
167
+ * Left out, no `ahp-automations://` channel is advertised and all three
168
+ * automation commands answer `-32601` - which is the right answer for a
169
+ * daemon that runs the sessions somebody asks for and schedules nothing.
170
+ * `memoryAutomations()` is the one that ships with this package: it holds
171
+ * definitions, runs them when asked, and holds no clock.
172
+ */
173
+ automations?: AutomationStore;
174
+ /**
175
+ * Tools this host contributes to every session it runs.
176
+ *
177
+ * The protocol's `serverTools`: tools that are the *host's* rather than a
178
+ * backend's or a client's, reported on `SessionState.serverTools` and given
179
+ * to the backend to offer the model. What they are is the host's to decide
180
+ * - `hostTools()` is the set that ships with this package - and a host that
181
+ * passes none contributes none, which is what an absent `serverTools` says.
182
+ */
183
+ tools?: HostTool[];
184
+ /** Called with one line per notable event, for a log. */
185
+ onEvent?(message: string): void;
186
+ }
187
+ /**
188
+ * One tool the host contributes, and what running it does.
189
+ *
190
+ * `definition` is what a client draws and what the model is offered;
191
+ * `run` is called when the model calls it, with the arguments it passed and
192
+ * the chat it called from. Returning a string is the answer; throwing is a
193
+ * tool that failed, and the message reaches the model.
194
+ */
195
+ export interface HostTool {
196
+ /** What the model is offered. `name` is the id it calls. */
197
+ definition: ToolDefinition;
198
+ /** What running it does. */
199
+ run(input: Record<string, unknown>, at: ToolCall): Promise<string> | string;
200
+ }
201
+ /**
202
+ * Where a host tool was called from, and what the host knows.
203
+ *
204
+ * The reason a tool is the host's rather than the backend's: an agent inside
205
+ * a session cannot see the sessions beside it or the terminals a person is
206
+ * watching, and the host can. A tool that wants neither ignores both.
207
+ */
208
+ export interface ToolCall {
209
+ /** The session channel URI the call was made in. */
210
+ session: string;
211
+ /** The chat channel URI it was made from. */
212
+ chat: string;
213
+ /** Every session this host is running, including the calling one. */
214
+ sessions(): {
215
+ uri: string;
216
+ provider: string;
217
+ title: string;
218
+ workingDirectories: string[];
219
+ }[];
220
+ /** Every terminal this host has open. */
221
+ terminals(): {
222
+ uri: string;
223
+ title: string;
224
+ cwd: string;
225
+ running: boolean;
226
+ }[];
227
+ /**
228
+ * Read a resource this host serves, as text.
229
+ *
230
+ * Including one it does not have: a URI a connected client published is
231
+ * fetched from that client, which is the only way an agent reaches a
232
+ * plugin's virtual files or an editor's unsaved buffers. Rejects when
233
+ * nothing serves it, in the words of whatever refused.
234
+ */
235
+ read(uri: string): Promise<string>;
236
+ }
237
+ /** One connected client and what it is watching. */
238
+ export interface Connection {
239
+ /** Where to write messages for this client. */
240
+ peer: Peer;
241
+ /** The identifier the client gave at `initialize`. */
242
+ clientId: string;
243
+ /**
244
+ * Channel URIs this client subscribed to.
245
+ *
246
+ * Per connection: two clients can watch one channel, and dropping one must
247
+ * not stop the other's stream.
248
+ */
249
+ watching: Set<string>;
250
+ /**
251
+ * Resource access this client has been granted, as `read:<uri>` / `write:<uri>`.
252
+ *
253
+ * Per connection and never per host: `resourceRequest` is a negotiation
254
+ * between two peers, and a grant one client talked its way into is not one
255
+ * every other client on the port inherits. Emptied when the connection goes,
256
+ * because it goes with the set.
257
+ */
258
+ grants: Set<string>;
259
+ /**
260
+ * Tokens this client pushed, by protected resource identifier.
261
+ *
262
+ * Per connection for the same reason grants are, and the specification says
263
+ * so outright: authentication status is per connection, each client
264
+ * authenticating independently. A token one client offered is theirs, spent
265
+ * only on sessions they ask for, and gone when they hang up.
266
+ *
267
+ * Which is also why an automation that fires with nobody connected has
268
+ * none: it is the host's own work rather than any client's, and it runs on
269
+ * the credentials the daemon was started with.
270
+ */
271
+ tokens: Map<string, string>;
272
+ /**
273
+ * Channels this client named in a shape of its own, by the channel they mean.
274
+ *
275
+ * A client may address a chat by a URI this host did not mint - see
276
+ * `chatFor` - and it then expects to be answered about *that* URI: its
277
+ * subscription is keyed by the string it sent, and an action arriving under
278
+ * any other name belongs to a channel it is not watching. So the spelling is
279
+ * remembered per connection and every notification is addressed back the way
280
+ * it was asked for.
281
+ */
282
+ aliases: Map<string, string>;
283
+ }
284
+ /**
285
+ * The clients connected to this host, as places a resource can come from.
286
+ *
287
+ * The protocol is symmetrical about `resource*`: the ten methods a client
288
+ * calls on a host are the ten a host may call on a client, with the same
289
+ * params and the same results, and the receiver decides whether to allow the
290
+ * operation whichever way round it went. What that is *for* is a client that
291
+ * publishes something the host has no way to reach - a plugin's virtual
292
+ * files, an editor's unsaved buffers, a filesystem provider - and addresses
293
+ * it as `<scheme>://<clientId>/…`.
294
+ *
295
+ * So this is not a port handed in: it is built out of the connections a host
296
+ * already has, and a URI naming one of them is answered by that client rather
297
+ * than by the host's own filesystem.
298
+ */
299
+ export interface Clients {
300
+ /** Every client currently connected, by the id it gave at `initialize`. */
301
+ ids(): string[];
302
+ /**
303
+ * The client a URI belongs to, if a connected one publishes it.
304
+ *
305
+ * `<scheme>://<clientId>/…`, which is how the reference host addresses one.
306
+ * `file:` is never a client's, and neither is any `ahp-` channel scheme -
307
+ * those are this protocol's own and their authority is not a client id.
308
+ */
309
+ owner(uri: string): string | undefined;
310
+ /** Read a file the client serves. */
311
+ read(client: string, uri: string, encoding?: string): Promise<unknown>;
312
+ /** List a directory the client serves. */
313
+ list(client: string, uri: string): Promise<unknown>;
314
+ /** Ask the client what a URI actually is. */
315
+ resolve(client: string, uri: string): Promise<unknown>;
316
+ /** Write a file the client serves. */
317
+ write(client: string, uri: string, content: {
318
+ data: string;
319
+ encoding?: string;
320
+ create?: boolean;
321
+ overwrite?: boolean;
322
+ }): Promise<unknown>;
323
+ /** Remove one. */
324
+ remove(client: string, uri: string, recursive?: boolean): Promise<unknown>;
325
+ /** Move one. Both URIs must be the same client's. */
326
+ move(client: string, source: string, destination: string, failIfExists?: boolean): Promise<unknown>;
327
+ /** Copy one. Both URIs must be the same client's. */
328
+ copy(client: string, source: string, destination: string, failIfExists?: boolean): Promise<unknown>;
329
+ /** Make a directory. */
330
+ mkdir(client: string, uri: string): Promise<unknown>;
331
+ /** Ask to watch one, and get back the channel the client will report on. */
332
+ watch(client: string, uri: string, options?: Record<string, unknown>): Promise<unknown>;
333
+ /** Ask the client for access to one of its resources. */
334
+ request(client: string, uri: string, access: {
335
+ read?: boolean;
336
+ write?: boolean;
337
+ }): Promise<unknown>;
338
+ }
339
+ /** A protocol server. One host serves many connections. */
340
+ export interface Host {
341
+ /**
342
+ * Take a new client and return what answers it.
343
+ *
344
+ * The result's `handle` answers requests; its `close` must be called when
345
+ * the connection drops, or the client's subscriptions leak.
346
+ */
347
+ accept(peer: Peer): {
348
+ /** Answer one request from this client. */
349
+ handle(request: Request): Promise<unknown>;
350
+ /** Drop this client's subscriptions and state. */
351
+ close(): void;
352
+ };
353
+ /** How many clients are currently connected. */
354
+ connections(): number;
355
+ /**
356
+ * The connected clients, as places a resource can come from.
357
+ *
358
+ * Used by this host to answer a `resource*` command naming a URI a client
359
+ * published, and exposed so an embedder can read one directly.
360
+ */
361
+ clients: Clients;
362
+ /**
363
+ * Replace the tools this host contributes.
364
+ *
365
+ * Full replacement, which is what `session/serverToolsChanged` means, and
366
+ * every running session is told. Sessions started after this get the new
367
+ * set; the ones already running get it on their next turn, because a
368
+ * backend is offered its tools when its process starts.
369
+ */
370
+ setTools(tools: HostTool[]): void;
371
+ }
372
+ //# sourceMappingURL=host.d.ts.map