@4fu/arbor-interfaces 2026.1106.0 → 2026.1107.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.
- package/README.md +14 -6
- package/agent.d.ts +177 -8
- package/agent.js +3 -3
- package/blocks.d.ts +12 -0
- package/events.d.ts +6 -2
- package/hooks.d.ts +36 -2
- package/index.d.ts +6 -6
- package/journal.d.ts +59 -1
- package/notes.d.ts +2 -0
- package/package.json +2 -2
- package/plugin.d.ts +34 -4
- package/plugin.js +4 -4
- package/refs.d.ts +5 -2
- package/scheduler.d.ts +26 -10
- package/settings.d.ts +15 -0
- package/task.d.ts +10 -1
- package/task.js +6 -0
- package/tools.d.ts +51 -2
- package/tools.js +3 -2
- package/windows.d.ts +5 -0
- package/workspace.d.ts +91 -0
package/README.md
CHANGED
|
@@ -27,9 +27,11 @@ everything that takes judgment, such as git, merges and conflicts, to the agent.
|
|
|
27
27
|
guarantees what an agent cannot guarantee for itself - the record, isolation,
|
|
28
28
|
exactly-once effects, the checks, dispatch. Everything that takes judgment,
|
|
29
29
|
such as git, merges and conflicts, is the agent's.
|
|
30
|
-
- **A
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
- **A shared workspace with per-window checkouts.** The default workspace is
|
|
31
|
+
ovnfs: one shared folder every window reads, and a window opens its own
|
|
32
|
+
copy-on-write checkout of the project it needs, works there and merges back
|
|
33
|
+
with `apply`. It falls back to a plain directory per window when ovnfs is not
|
|
34
|
+
installed. It is not a sandbox: ovnfs isolates files, not processes.
|
|
33
35
|
- **A self-changing environment.** The model adds and changes skills, MCP
|
|
34
36
|
servers and resident services itself; a `reload` validates the change and
|
|
35
37
|
broadcasts it to every live window as a delta of names and descriptions.
|
|
@@ -54,7 +56,9 @@ npm install @4fu/arbor-core @4fu/arbor-journal-sqlite @4fu/arbor-notes-sqlite \
|
|
|
54
56
|
```
|
|
55
57
|
|
|
56
58
|
`@4fu/arbor-plugin-http` is optional; it is the example plugin below.
|
|
57
|
-
`@4fu/arbor-segmenter-jieba` is an optional peer of the
|
|
59
|
+
`@4fu/arbor-segmenter-jieba` is an optional peer of the lexical package, and
|
|
60
|
+
`@4fu/arbor-search-zvec` an optional peer of the journal: absent either one, the
|
|
61
|
+
journal searches lexically and nothing else changes.
|
|
58
62
|
|
|
59
63
|
## A minimal host
|
|
60
64
|
|
|
@@ -118,7 +122,10 @@ window gets, and how a continuation keeps working in it, are in
|
|
|
118
122
|
| [`@4fu/arbor-journal-sqlite`](packages/journal-sqlite) | the default `Journal` and `BlobStore` on SQLite |
|
|
119
123
|
| [`@4fu/arbor-notes-sqlite`](packages/notes-sqlite) | the default `NoteStore` on SQLite, beside the journal |
|
|
120
124
|
| [`@4fu/arbor-segmenter-jieba`](packages/segmenter-jieba) | the default `Segmenter`: jieba's dictionary segmentation for the word index, optional |
|
|
121
|
-
| [`@4fu/arbor-
|
|
125
|
+
| [`@4fu/arbor-lexical`](packages/lexical) | the lexical search the journal and notes share: word segmentation, the FTS5 helpers and the merge order |
|
|
126
|
+
| [`@4fu/arbor-search-zvec`](packages/search-zvec) | the optional semantic channel: event text embedded and searched by meaning with zvec |
|
|
127
|
+
| [`@4fu/arbor-workspace-dir`](packages/workspace-dir) | the fallback `Workspace`: one directory per window, with a continuation working in its predecessor's directory in place |
|
|
128
|
+
| [`@4fu/arbor-workspace-ovnfs`](packages/workspace-ovnfs) | the default `Workspace` when ovnfs is installed: one shared folder, per-window copy-on-write checkouts the model opens and applies back |
|
|
122
129
|
| [`@4fu/arbor-environment-dir`](packages/environment-dir) | the default `Environment`: one `/env` directory, a revision per item, validation and deltas |
|
|
123
130
|
| [`@4fu/arbor-supervisor-process`](packages/supervisor-process) | the default `Supervisor`: one host process per service, restart with backoff, logs |
|
|
124
131
|
| [`@4fu/arbor-judge-jev`](packages/judge-jev) | the default `Judge`: Jev, TypeSafe AI's System One classifier, answering the dispatch decisions |
|
|
@@ -151,7 +158,7 @@ deployment can see what it would be taking on before it adopts one.
|
|
|
151
158
|
| [docs/coordination.md](docs/coordination.md) | the board, expectations, deliveries and broadcasts, staying current, effects |
|
|
152
159
|
| [docs/environment.md](docs/environment.md) | skills, MCP servers and services: using, changing, reload, built-in skills, deltas |
|
|
153
160
|
| [docs/memory.md](docs/memory.md) | notes, the active set, anchors, links |
|
|
154
|
-
| [docs/workspaces.md](docs/workspaces.md) | per-window
|
|
161
|
+
| [docs/workspaces.md](docs/workspaces.md) | the shared folder and per-window checkouts: isolation, continuity, what the agent does |
|
|
155
162
|
| [docs/tools.md](docs/tools.md) | direct tools and code mode modules |
|
|
156
163
|
| [docs/prompting.md](docs/prompting.md) | how every model-facing text is written: layers, notes, deliveries, errors, judge questions, terms |
|
|
157
164
|
| [docs/release.md](docs/release.md) | calendar versioning, the manual release steps, how a package is published |
|
|
@@ -168,6 +175,7 @@ tarball would contain ([docs/release.md](docs/release.md)).
|
|
|
168
175
|
| `packages/errors` | the error shape every refusal uses: the rule and the next action |
|
|
169
176
|
| `packages/journal-sqlite` | the default `Journal` and `BlobStore` on SQLite |
|
|
170
177
|
| `packages/segmenter-jieba` | the default `Segmenter`: jieba's dictionary segmentation for the word index, optional |
|
|
178
|
+
| `packages/lexical` | the lexical search the journal and notes share: word segmentation, the FTS5 helpers and the merge order |
|
|
171
179
|
| `packages/notes-sqlite` | the default `NoteStore` on SQLite, beside the journal |
|
|
172
180
|
| `packages/judge-jev` | the default `Judge`: Jev, TypeSafe AI's System One classifier, answering the dispatch decisions |
|
|
173
181
|
| `packages/workspace-dir` | the default `Workspace`: one directory per window, with a continuation working in its predecessor's directory in place, and the lookup of another window's directory it contributes to the catalog |
|
package/agent.d.ts
CHANGED
|
@@ -1,17 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The `AgentBackend` interface: run one window - send the prompt and the tools
|
|
3
|
-
* to a model, stream tool calls
|
|
4
|
-
* calls finish, report token usage, list the models
|
|
5
|
-
* their providers (docs/architecture.md#interfaces).
|
|
3
|
+
* to a model, stream tool calls and what the model is writing, accept inputs
|
|
4
|
+
* inserted once a turn's tool calls finish, report token usage, list the models
|
|
5
|
+
* it can run and log in to their providers (docs/architecture.md#interfaces).
|
|
6
6
|
*
|
|
7
7
|
* arbor uses pi-agent-core's `Agent` with its own system prompt: pi's default
|
|
8
8
|
* coding prompt, context files and skills never reach the model
|
|
9
9
|
* (docs/prompting.md#no-built-in-pi-prompt).
|
|
10
10
|
*/
|
|
11
11
|
import type { Block } from "./blocks.ts";
|
|
12
|
+
import type { ExternalTool } from "./tools.ts";
|
|
12
13
|
import type { TaskRef, Ref, WindowRef } from "./refs.ts";
|
|
13
14
|
import type { Handoff, TokenUsage, WindowState } from "./windows.ts";
|
|
14
15
|
import type { RateLimitState } from "./scheduler.ts";
|
|
16
|
+
/**
|
|
17
|
+
* One JSON Schema object, carried as it was declared.
|
|
18
|
+
*
|
|
19
|
+
* A tool declared from outside the core - a plugin's own, or a harness's
|
|
20
|
+
* through a backend - hands its parameters over in the JSON Schema that
|
|
21
|
+
* harness already speaks, and that schema is what the model reads, verbatim
|
|
22
|
+
* (docs/tools.md#external-tools). Deriving one from a summary would drop what
|
|
23
|
+
* the schema says and what the summary cannot: `enum`, `items`, `oneOf`,
|
|
24
|
+
* `additionalProperties`, a nested object's own properties.
|
|
25
|
+
*/
|
|
26
|
+
export type JSONSchema = Record<string, unknown>;
|
|
15
27
|
/** One model the backend can run. Only models that take images are offered. */
|
|
16
28
|
export interface ModelInfo {
|
|
17
29
|
id: string;
|
|
@@ -77,9 +89,47 @@ export interface ContinuationInfo {
|
|
|
77
89
|
/** Set after the hard limit: the window was reset mid-task. */
|
|
78
90
|
midTaskReset?: boolean;
|
|
79
91
|
}
|
|
80
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* What the host inserts into a running window at a tool-call boundary.
|
|
94
|
+
*
|
|
95
|
+
* ## What each kind expects from a backend
|
|
96
|
+
*
|
|
97
|
+
* Every delivery is one input block, and the host says when the model must
|
|
98
|
+
* read it by the call it hands the delivery to: `WindowSession.steer` for one
|
|
99
|
+
* that goes in at the next step boundary, `WindowSession.followUp` for one that
|
|
100
|
+
* becomes the input of the next turn. The rule is the same for every kind - the
|
|
101
|
+
* insertion rules decide, not the kind
|
|
102
|
+
* (docs/dispatch.md#insertion-timing) - with two things a backend can rely on:
|
|
103
|
+
*
|
|
104
|
+
* - **What a window can call is never held back.** A conversation update, an
|
|
105
|
+
* environment delta, a prompt delta and a tools delta always come through
|
|
106
|
+
* `steer`, in the protection period and after it, because they change what
|
|
107
|
+
* the window would otherwise do next.
|
|
108
|
+
* - **A reminder is the kind the host times itself.** While the window runs it
|
|
109
|
+
* is steered in at the next boundary; once the run has ended it is queued as
|
|
110
|
+
* a follow-up, so it is the input of the window's next turn.
|
|
111
|
+
*
|
|
112
|
+
* A backend that is not pi maps the two calls onto whatever its own loop
|
|
113
|
+
* offers - DeepSeek Harness's `agent.inject` for a step boundary and its
|
|
114
|
+
* steering for the next turn - and the rest of the contract is the same: the
|
|
115
|
+
* block is one user-role input, in order, exactly once, and the turn the
|
|
116
|
+
* window is in is never ended by it.
|
|
117
|
+
*/
|
|
81
118
|
export interface Delivery {
|
|
82
119
|
kind: "inserted_task" | "conversation_update" | "notice" | "broadcast" | "window_message" | "environment_delta"
|
|
120
|
+
/**
|
|
121
|
+
* A plugin's prompt section changed since this window's prefix was built:
|
|
122
|
+
* the lines that changed, and nothing that did not
|
|
123
|
+
* (docs/prompting.md#plugin-sections-that-change).
|
|
124
|
+
*/
|
|
125
|
+
| "prompt_delta"
|
|
126
|
+
/**
|
|
127
|
+
* The direct tools declared from outside the core changed while this window
|
|
128
|
+
* runs: a harness registered one, or a plugin's set moved. The delta names
|
|
129
|
+
* what was added and what was removed, and the window may call the new ones
|
|
130
|
+
* from its next tool call on (docs/tools.md#external-tools-that-change).
|
|
131
|
+
*/
|
|
132
|
+
| "tools_delta"
|
|
83
133
|
/** A host usage reminder, appended to an input (docs/windows.md#usage-reminders). */
|
|
84
134
|
| "reminder";
|
|
85
135
|
/** The window it comes from, for a notice, a broadcast or a window message. */
|
|
@@ -92,23 +142,106 @@ export interface Delivery {
|
|
|
92
142
|
export interface WindowSession {
|
|
93
143
|
readonly window: WindowRef;
|
|
94
144
|
readonly state: WindowState;
|
|
95
|
-
/**
|
|
145
|
+
/**
|
|
146
|
+
* Insert at the next tool-call boundary, through the backend's steering.
|
|
147
|
+
*
|
|
148
|
+
* A **step boundary** is the moment every tool call of the current turn has
|
|
149
|
+
* finished and the next model request has not gone out yet. The delivery
|
|
150
|
+
* becomes one user-role input the model reads with that request. It is never
|
|
151
|
+
* a system message in the middle of a window - some providers fold those into
|
|
152
|
+
* the leading system prompt and that breaks the cached prefix
|
|
153
|
+
* (docs/prompting.md#no-built-in-pi-prompt) - and it never rewrites the
|
|
154
|
+
* window's prefix.
|
|
155
|
+
*
|
|
156
|
+
* What a backend must do:
|
|
157
|
+
*
|
|
158
|
+
* - deliver the block in the order the host handed its deliveries over, and
|
|
159
|
+
* deliver each one exactly once;
|
|
160
|
+
* - keep the turn going: the model is not stopped, not asked to confirm, and
|
|
161
|
+
* not told the input is special - it is one more input;
|
|
162
|
+
* - start a turn when the window is idle, because that is what a related
|
|
163
|
+
* input wants (docs/dispatch.md#insertion-timing); the host only steers
|
|
164
|
+
* into a window it has not ended and has not paused, so a delivery never
|
|
165
|
+
* arrives for one of those;
|
|
166
|
+
* - resolve once the delivery is queued, not once the model has read it: the
|
|
167
|
+
* host never waits for the model, and a slow model is not a slow steer.
|
|
168
|
+
*
|
|
169
|
+
* A window that has ended refuses in the core's error shape, rule
|
|
170
|
+
* `window.ended`; the host never steers into one.
|
|
171
|
+
*/
|
|
96
172
|
steer(delivery: Delivery): Promise<void>;
|
|
97
|
-
/**
|
|
173
|
+
/**
|
|
174
|
+
* Queue for after the run ends: the delivery becomes the input of the
|
|
175
|
+
* window's next turn.
|
|
176
|
+
*
|
|
177
|
+
* What a backend must do:
|
|
178
|
+
*
|
|
179
|
+
* - hold it out of the turn the window is in - the model must not read it
|
|
180
|
+
* mid-turn, which is what separates this call from `steer`;
|
|
181
|
+
* - deliver it as the input of the next turn, in order and exactly once, when
|
|
182
|
+
* the window runs again;
|
|
183
|
+
* - keep it queued while the window sits idle, and drop it when the window
|
|
184
|
+
* ends, because an ended window is never reopened
|
|
185
|
+
* (docs/windows.md#lifecycle).
|
|
186
|
+
*
|
|
187
|
+
* The host uses this only for a delivery that belongs after the run: the
|
|
188
|
+
* reminder of a window whose run has already ended, and whatever a
|
|
189
|
+
* `Scheduler` places there (docs/dispatch.md#insertion-timing).
|
|
190
|
+
*/
|
|
98
191
|
followUp(delivery: Delivery): Promise<void>;
|
|
192
|
+
/**
|
|
193
|
+
* The direct tools this window may call changed: the host registered what a
|
|
194
|
+
* backend or a plugin declared since this window opened
|
|
195
|
+
* (docs/tools.md#external-tools-that-change).
|
|
196
|
+
*
|
|
197
|
+
* A backend that can change a running window's declared tools replaces them
|
|
198
|
+
* here, so a tool that appeared is callable in this window and one that was
|
|
199
|
+
* removed is not. A backend that cannot leaves the declaration it opened
|
|
200
|
+
* with: the window still learns the change from the `tools_delta` it is
|
|
201
|
+
* steered, and a call still runs through the host's catalog, which holds the
|
|
202
|
+
* current set. Optional; the host asks only of a session that has it.
|
|
203
|
+
*/
|
|
204
|
+
refreshTools?(tools: readonly DirectToolSpec[]): Promise<void> | void;
|
|
99
205
|
/** End the run. With a handoff the host opens a continuation. */
|
|
100
206
|
end(handoff?: Handoff, reason?: WindowEndReason): Promise<void>;
|
|
101
207
|
/** Pause insertion without ending the window. */
|
|
102
208
|
pause(): Promise<void>;
|
|
103
209
|
resume(): Promise<void>;
|
|
104
210
|
usage(): TokenUsage;
|
|
105
|
-
/** Tool calls, results and
|
|
211
|
+
/** Tool calls, results and what the model is writing, as they happen. */
|
|
106
212
|
events(): AsyncIterable<WindowEvent>;
|
|
107
213
|
/** Resolves when the window ends, with why. */
|
|
108
214
|
outcome(): Promise<WindowOutcome>;
|
|
109
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* What a window is writing while it runs, one delta at a time
|
|
218
|
+
* (docs/windows.md#streaming).
|
|
219
|
+
*
|
|
220
|
+
* Streams are best-effort and ephemeral: they are not journaled, and the
|
|
221
|
+
* journal's durable events stay the only record. A backend that cannot report
|
|
222
|
+
* deltas never emits them, and a consumer that misses one keeps going - the
|
|
223
|
+
* next delta carries what stands so far.
|
|
224
|
+
*/
|
|
225
|
+
export type StreamEvent =
|
|
226
|
+
/** Assistant text, as the model writes it. */
|
|
227
|
+
{
|
|
228
|
+
kind: "text_delta";
|
|
229
|
+
delta: string;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* One tool call's arguments, as the model writes them. `call.args` is what
|
|
233
|
+
* has been decoded so far and is best-effort: a call still being written is
|
|
234
|
+
* not valid JSON yet, so a string argument holds the characters written so
|
|
235
|
+
* far - which is what makes `send`'s content visible while the model is
|
|
236
|
+
* still writing it (docs/tools.md#direct-tools). The durable `tool_call`
|
|
237
|
+
* event carries the complete arguments, once.
|
|
238
|
+
*/
|
|
239
|
+
| {
|
|
240
|
+
kind: "tool_call_delta";
|
|
241
|
+
call: ToolCall;
|
|
242
|
+
};
|
|
110
243
|
/** What a backend streams while a window runs. */
|
|
111
|
-
export type WindowEvent = {
|
|
244
|
+
export type WindowEvent = StreamEvent | {
|
|
112
245
|
kind: "tool_call";
|
|
113
246
|
call: ToolCall;
|
|
114
247
|
} | {
|
|
@@ -153,6 +286,20 @@ export interface AgentBackend {
|
|
|
153
286
|
* reports nothing is never limited.
|
|
154
287
|
*/
|
|
155
288
|
rateLimits?(): Promise<RateLimitState> | RateLimitState;
|
|
289
|
+
/**
|
|
290
|
+
* Direct tools the backend injects from outside arbor: the tools an outside
|
|
291
|
+
* harness registered, which a host embedded in that harness hands in here
|
|
292
|
+
* (docs/tools.md#external-tools). They are declared to the model and called
|
|
293
|
+
* in the turn like any other direct tool.
|
|
294
|
+
*
|
|
295
|
+
* The set is refreshable: the host reads it again before every window opens
|
|
296
|
+
* and at every step boundary, so a tool the harness registers - or drops -
|
|
297
|
+
* while the host runs reaches the model without a restart
|
|
298
|
+
* (docs/tools.md#external-tools-that-change). A backend that wants the change
|
|
299
|
+
* to land the moment it happens rather than at the next boundary calls
|
|
300
|
+
* `Arbor.refreshExternalTools` from its own signal.
|
|
301
|
+
*/
|
|
302
|
+
externalTools?(): readonly ExternalTool[] | Promise<readonly ExternalTool[]>;
|
|
156
303
|
/** Open one window and start its first turn. */
|
|
157
304
|
open(request: OpenWindowRequest): Promise<WindowSession>;
|
|
158
305
|
}
|
|
@@ -161,7 +308,23 @@ export interface DirectToolSpec {
|
|
|
161
308
|
name: string;
|
|
162
309
|
/** What it does and when to use it; side effects. 1 to 4 sentences. */
|
|
163
310
|
description: string;
|
|
311
|
+
/** The parameters in arbor's own declaration shape. */
|
|
164
312
|
parameters: Record<string, ParameterSpec>;
|
|
313
|
+
/**
|
|
314
|
+
* The parameters as the JSON Schema an outside harness declared them, carried
|
|
315
|
+
* verbatim: `enum`, `items`, `oneOf`, `additionalProperties`, a nested
|
|
316
|
+
* object's own properties, all of it. What the model reads when the tool has
|
|
317
|
+
* one, and what the `<tools>` line and `tool_search` show
|
|
318
|
+
* (docs/tools.md#external-tools). Arbor's own tools declare none: their
|
|
319
|
+
* `parameters` are the declaration.
|
|
320
|
+
*/
|
|
321
|
+
schema?: JSONSchema;
|
|
322
|
+
/**
|
|
323
|
+
* Who declares it, when it is not the core's own: a plugin's name, or a
|
|
324
|
+
* backend's. The tools list says where such a tool comes from, so the model
|
|
325
|
+
* knows whose it is (docs/tools.md#external-tools).
|
|
326
|
+
*/
|
|
327
|
+
source?: string;
|
|
165
328
|
}
|
|
166
329
|
/** Called from a script in `code_exec` (docs/tools.md#modules). */
|
|
167
330
|
export interface ModuleSpec {
|
|
@@ -193,6 +356,12 @@ export interface ToolResult {
|
|
|
193
356
|
ok: boolean;
|
|
194
357
|
/** Stable fields, bounded, in the core's error shape when it failed. */
|
|
195
358
|
content: string;
|
|
359
|
+
/**
|
|
360
|
+
* The tool that was called, as the prompt names it. A refusal carries it so
|
|
361
|
+
* the error line starts with the tool name; the catalog fills it on the way
|
|
362
|
+
* out, so a group that leaves it out still names the call.
|
|
363
|
+
*/
|
|
364
|
+
tool?: string;
|
|
196
365
|
error?: {
|
|
197
366
|
rule: string;
|
|
198
367
|
message: string;
|
package/agent.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The `AgentBackend` interface: run one window - send the prompt and the tools
|
|
3
|
-
* to a model, stream tool calls
|
|
4
|
-
* calls finish, report token usage, list the models
|
|
5
|
-
* their providers (docs/architecture.md#interfaces).
|
|
3
|
+
* to a model, stream tool calls and what the model is writing, accept inputs
|
|
4
|
+
* inserted once a turn's tool calls finish, report token usage, list the models
|
|
5
|
+
* it can run and log in to their providers (docs/architecture.md#interfaces).
|
|
6
6
|
*
|
|
7
7
|
* arbor uses pi-agent-core's `Agent` with its own system prompt: pi's default
|
|
8
8
|
* coding prompt, context files and skills never reach the model
|
package/blocks.d.ts
CHANGED
|
@@ -23,6 +23,12 @@ export interface BlockFieldSpec {
|
|
|
23
23
|
name: string;
|
|
24
24
|
type: "string" | "strings" | "number" | "boolean" | "ref" | "refs" | "blocks";
|
|
25
25
|
required?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* True when the field names the block's own XML tag rather than content: the
|
|
28
|
+
* renderer writes it as the tag and never as a line of its own
|
|
29
|
+
* (docs/prompting.md#blocks-and-the-renderer).
|
|
30
|
+
*/
|
|
31
|
+
tag?: boolean;
|
|
26
32
|
}
|
|
27
33
|
/**
|
|
28
34
|
* A plugin's registration of a block kind: its fields, the tag the renderer
|
|
@@ -41,6 +47,12 @@ export interface BlockKindSpec {
|
|
|
41
47
|
extends?: string;
|
|
42
48
|
/** XML tag the renderer wraps one block in; omitted for an untagged block. */
|
|
43
49
|
tag?: string;
|
|
50
|
+
/**
|
|
51
|
+
* The field whose value is this block's XML tag, for a kind whose tag is
|
|
52
|
+
* chosen per instance - a plugin prompt section's delta, which renders in the
|
|
53
|
+
* tag the plugin named. Omitted for a kind whose tag is fixed.
|
|
54
|
+
*/
|
|
55
|
+
tagField?: string;
|
|
44
56
|
fields: readonly BlockFieldSpec[];
|
|
45
57
|
/** Fixed lines written before the fields, by key into the text table. */
|
|
46
58
|
lines?: readonly string[];
|
package/events.d.ts
CHANGED
|
@@ -108,7 +108,11 @@ export interface SearchHit {
|
|
|
108
108
|
kind: EventKind;
|
|
109
109
|
time: number;
|
|
110
110
|
match: MatchKind;
|
|
111
|
-
/**
|
|
111
|
+
/**
|
|
112
|
+
* Relevance in 0..1 across the channels that found the hit; higher is better.
|
|
113
|
+
* Fused with reciprocal rank fusion, so a hit two channels found outranks one
|
|
114
|
+
* only one did.
|
|
115
|
+
*/
|
|
112
116
|
score: number;
|
|
113
117
|
/** The event's opening words, for a one-line result. */
|
|
114
118
|
summary: string;
|
|
@@ -151,7 +155,7 @@ export interface TraceNode {
|
|
|
151
155
|
/** A target a window is behind on. */
|
|
152
156
|
export interface TargetGap {
|
|
153
157
|
target: Target;
|
|
154
|
-
/** The newest event on the target. */
|
|
158
|
+
/** The newest event on the target another window wrote after this one opened. */
|
|
155
159
|
latest: number;
|
|
156
160
|
/** The newest event on the target the window has seen. */
|
|
157
161
|
watermark: number;
|
package/hooks.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* built from these.
|
|
9
9
|
*/
|
|
10
10
|
import type { ArborErrorInfo } from "@4fu/arbor-errors";
|
|
11
|
-
import type { Delivery, RenderedPrompt, ToolCall, ToolResult } from "./agent.ts";
|
|
11
|
+
import type { Delivery, RenderedPrompt, StreamEvent, ToolCall, ToolResult } from "./agent.ts";
|
|
12
12
|
import type { Block } from "./blocks.ts";
|
|
13
13
|
import type { Event, EventInput } from "./events.ts";
|
|
14
14
|
import type { Effect } from "./effects.ts";
|
|
@@ -27,6 +27,11 @@ export type HookOutcome<T> = {
|
|
|
27
27
|
allow: false;
|
|
28
28
|
error: ArborErrorInfo;
|
|
29
29
|
};
|
|
30
|
+
/**
|
|
31
|
+
* What an after hook answers with: the outcome of what it observed, or nothing
|
|
32
|
+
* at all. An after hook that observes only answers with nothing.
|
|
33
|
+
*/
|
|
34
|
+
export type AfterHookOutcome<T> = HookOutcome<T> | void;
|
|
30
35
|
export interface AppendContext {
|
|
31
36
|
input: EventInput;
|
|
32
37
|
event?: Event;
|
|
@@ -67,12 +72,28 @@ export interface WindowContinueContext {
|
|
|
67
72
|
export interface ModelRequestContext {
|
|
68
73
|
window: WindowRef;
|
|
69
74
|
prompt: RenderedPrompt;
|
|
75
|
+
/**
|
|
76
|
+
* The blocks the window's first input is rendered from, and what a hook
|
|
77
|
+
* changes is what the window opens with: the host renders what comes back.
|
|
78
|
+
* The hook runs once per window, before the first turn
|
|
79
|
+
* (docs/windows.md#prompt-layout).
|
|
80
|
+
*/
|
|
70
81
|
inputs: readonly Block[];
|
|
71
82
|
}
|
|
72
83
|
export interface DeliveryContext {
|
|
73
84
|
window: WindowRef;
|
|
74
85
|
delivery: Delivery;
|
|
75
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* One delta of a window's live output, as the model writes it. What a window
|
|
89
|
+
* says to the outside is the `content` argument of the `send` direct tool, so a
|
|
90
|
+
* surface that shows a window's output live reads `tool_call_delta`
|
|
91
|
+
* (docs/windows.md#streaming).
|
|
92
|
+
*/
|
|
93
|
+
export interface WindowStreamContext {
|
|
94
|
+
window: WindowRef;
|
|
95
|
+
event: StreamEvent;
|
|
96
|
+
}
|
|
76
97
|
export interface ToolCallContext {
|
|
77
98
|
window: WindowRef;
|
|
78
99
|
call: ToolCall;
|
|
@@ -127,8 +148,21 @@ export interface Hooks {
|
|
|
127
148
|
windowContinue?(context: WindowContinueContext): Promise<HookOutcome<WindowContinueContext>> | HookOutcome<WindowContinueContext>;
|
|
128
149
|
beforeModelRequest?(context: ModelRequestContext): Promise<HookOutcome<ModelRequestContext>> | HookOutcome<ModelRequestContext>;
|
|
129
150
|
beforeDelivery?(context: DeliveryContext): Promise<HookOutcome<DeliveryContext>> | HookOutcome<DeliveryContext>;
|
|
151
|
+
/**
|
|
152
|
+
* One delta of a window's live output. Observation only, and best-effort: it
|
|
153
|
+
* can neither change nor refuse anything, it is not waited for, and a hook
|
|
154
|
+
* that throws or is slow loses its deltas rather than the window
|
|
155
|
+
* (docs/windows.md#streaming).
|
|
156
|
+
*/
|
|
157
|
+
windowStream?(context: WindowStreamContext): Promise<void> | void;
|
|
130
158
|
beforeToolCall?(context: ToolCallContext): Promise<HookOutcome<ToolCallContext>> | HookOutcome<ToolCallContext>;
|
|
131
|
-
|
|
159
|
+
/**
|
|
160
|
+
* After one call has run. The call is already made, so a refusal cannot undo
|
|
161
|
+
* it: it stops the hooks after it and leaves the result as it stands. What it
|
|
162
|
+
* answers with is used - a hook that returns a different `result` replaces
|
|
163
|
+
* what is recorded and what the model reads (docs/architecture.md#hooks).
|
|
164
|
+
*/
|
|
165
|
+
afterToolCall?(context: ToolCallContext): Promise<AfterHookOutcome<ToolCallContext>> | AfterHookOutcome<ToolCallContext>;
|
|
132
166
|
beforeEffect?(context: EffectContext): Promise<EffectOutcome> | EffectOutcome;
|
|
133
167
|
beforeNoteWrite?(context: NoteWriteContext): Promise<HookOutcome<NoteWriteContext>> | HookOutcome<NoteWriteContext>;
|
|
134
168
|
afterNoteWrite?(context: NoteWriteContext): Promise<void> | void;
|
package/index.d.ts
CHANGED
|
@@ -11,19 +11,19 @@ export type { BlockValue, Block, BlockKind, BlockFieldSpec, BlockKindSpec, TextT
|
|
|
11
11
|
/** Page bounds every read reports (docs/prompting.md). */
|
|
12
12
|
export { DEFAULT_PAGE_LIMIT, MAX_PAGE_LIMIT } from "./refs.ts";
|
|
13
13
|
export type { EventInput, Link, LinkInput, Redaction, Event, ListOptions, Page, EventPage, AroundOptions, SearchOptions, SearchHit, SearchPage, LinksOptions, RelatedItem, LinkPage, TraceOptions, TraceNode, TargetGap, JournalError, } from "./events.ts";
|
|
14
|
-
export type { BlobInput, BlobContent, BlobInfo, BlobStore, Segmenter, Journal, } from "./journal.ts";
|
|
15
|
-
export type { ModelInfo, LoginState, LoginRequest, LoginResult, RenderedPrompt, OpenWindowRequest, WindowLimits, ContinuationInfo, Delivery, WindowSession, WindowEvent, WindowEndReason, WindowOutcome, AgentBackend, DirectToolSpec, ModuleSpec, FunctionSpec, ParameterSpec, ToolCall, ToolResult, } from "./agent.ts";
|
|
14
|
+
export type { BlobInput, BlobContent, BlobInfo, BlobStore, Segmenter, SemanticFilters, SemanticFields, Embedder, SemanticIndex, Journal, } from "./journal.ts";
|
|
15
|
+
export type { ModelInfo, LoginState, LoginRequest, LoginResult, RenderedPrompt, OpenWindowRequest, WindowLimits, ContinuationInfo, Delivery, WindowSession, StreamEvent, WindowEvent, WindowEndReason, WindowOutcome, AgentBackend, DirectToolSpec, ModuleSpec, FunctionSpec, ParameterSpec, JSONSchema, ToolCall, ToolResult, } from "./agent.ts";
|
|
16
16
|
export type { WindowState, WindowRuntimeState, TokenUsage, Handoff, BoardLine, Expectation, InputBlock, } from "./windows.ts";
|
|
17
17
|
export type { NoteStatus, Anchor, NoteWriteInput, Note, NoteSummary, NoteBody, NoteRevision, NoteContext, NoteQuery, NoteConflictInfo, ActiveSetBudget, ActiveCandidate, NoteUseKind, NoteUseInput, SnapshotOptions, NotesSnapshot, NoteDelta, NoteDeltaItem, NoteStore, } from "./notes.ts";
|
|
18
|
-
export type { ExecResult, StagedFile, WorkspaceMount, WorkspaceQuota, Workspace, } from "./workspace.ts";
|
|
18
|
+
export type { ExecResult, StagedFile, OpenWorkItem, WorkspaceCheckout, WorkspaceKeep, WorkspaceMount, WorkspaceQuota, Workspace, } from "./workspace.ts";
|
|
19
19
|
export type { JudgeMode, JudgeDecisionKind, CandidateWindow, JudgeTask, FilterState, RouteState, ModelTierState, NoticeState, BroadcastState, ActiveSetState, FilterRequest, RouteRequest, ModelTierRequest, NoticeRequest, BroadcastRequest, ActiveSetRequest, JudgeRequest, FilterAnswer, RouteScore, RouteAnswer, ModelTierAnswer, WindowDecision, NoticeAnswer, BroadcastAnswer, ActiveSetAnswer, JudgeAnswer, Judge, JudgeCriteria, } from "./judge.ts";
|
|
20
20
|
export type { OutsideEvent, OutsideMessage, ChannelConnection, SendRequest, SendResult, Unsubscribe, Channel, } from "./channel.ts";
|
|
21
21
|
export type { TaskKind, TaskInput, Task, TaskSource } from "./task.ts";
|
|
22
22
|
export type { EffectKind, Effect, EffectResult, EffectExecutor } from "./effects.ts";
|
|
23
|
-
export type { CallContext, ToolGroup } from "./tools.ts";
|
|
23
|
+
export type { CallContext, ToolGroup, ExternalTool, ExternalToolSource, ExternalToolChange, } from "./tools.ts";
|
|
24
24
|
export type { EnvItemKind, EnvItem, EnvDraft, EnvValidation, EnvRevision, EnvDelta, Environment, } from "./environment.ts";
|
|
25
25
|
export type { ServiceState, ServiceSpec, ServiceHandle, ServiceStatus, LogOptions, LogPage, Supervisor, } from "./supervisor.ts";
|
|
26
|
-
export type { QueuedInput, InsertionRequest, InsertionPlan, RateLimitState, AdmissionRequest, AdmissionPlan, SkipPeriod, ScheduledTaskInput, ScheduledTask, ScheduledTaskUpdate, Scheduler, } from "./scheduler.ts";
|
|
27
|
-
export type { HookOutcome, AppendContext, DispatchContext, JudgeContext, WindowStartContext, WindowEndContext, WindowContinueContext, ModelRequestContext, DeliveryContext, ToolCallContext, EffectOutcome, EffectHold, EffectContext, NoteWriteContext, ReloadContext, ServiceStateContext, Hooks, HeldEffect, } from "./hooks.ts";
|
|
26
|
+
export type { QueuedInput, InsertionRequest, InsertionPlan, RateLimitState, AdmissionRequest, AdmissionPlan, SkipPeriod, DailyRepeat, EveryRepeat, RepeatBounds, RepeatRule, WeeklyRepeat, ScheduledTaskInput, ScheduledTask, ScheduledTaskUpdate, Scheduler, } from "./scheduler.ts";
|
|
27
|
+
export type { AfterHookOutcome, HookOutcome, AppendContext, DispatchContext, JudgeContext, WindowStartContext, WindowEndContext, WindowContinueContext, ModelRequestContext, DeliveryContext, WindowStreamContext, ToolCallContext, EffectOutcome, EffectHold, EffectContext, NoteWriteContext, ReloadContext, ServiceStateContext, Hooks, HeldEffect, } from "./hooks.ts";
|
|
28
28
|
export type { PluginAdapters, PromptSection, Plugin, } from "./plugin.ts";
|
|
29
29
|
export type { Settings } from "./settings.ts";
|
package/journal.d.ts
CHANGED
|
@@ -52,6 +52,60 @@ export interface BlobStore {
|
|
|
52
52
|
export interface Segmenter {
|
|
53
53
|
segment(text: string): string[];
|
|
54
54
|
}
|
|
55
|
+
/** The filters a semantic search applies, as the journal's own read applies them. */
|
|
56
|
+
export interface SemanticFilters {
|
|
57
|
+
kinds?: readonly string[];
|
|
58
|
+
window?: WindowRef | null;
|
|
59
|
+
afterSeq?: number;
|
|
60
|
+
beforeSeq?: number;
|
|
61
|
+
}
|
|
62
|
+
/** The scalar fields a semantic filter reads, beside an event's text. */
|
|
63
|
+
export interface SemanticFields {
|
|
64
|
+
kind?: string;
|
|
65
|
+
window?: WindowRef | null;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Embedding text into vectors. The default is a small local model that needs no
|
|
69
|
+
* key (docs/journal.md#search); a deployment replaces it with its own, which is
|
|
70
|
+
* the only way text reaches anything outside the host.
|
|
71
|
+
*/
|
|
72
|
+
export interface Embedder {
|
|
73
|
+
/** The dimension every vector this embedder returns has. */
|
|
74
|
+
readonly dimension: number;
|
|
75
|
+
/** Embed each text, in the order given. */
|
|
76
|
+
embed(texts: readonly string[]): Promise<readonly number[][]>;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The semantic channel: text search by meaning, beside the lexical indexes
|
|
80
|
+
* (docs/journal.md#search, docs/memory.md#search).
|
|
81
|
+
*
|
|
82
|
+
* A store takes one as an optional peer, the way it takes a `Segmenter`.
|
|
83
|
+
* When none is registered - the package is not installed, its vector store will
|
|
84
|
+
* not open on the platform, or no embedding model is available - search is
|
|
85
|
+
* lexical and nothing else changes. A channel that is registered but not ready
|
|
86
|
+
* yet - the model is still downloading, or the download failed and is waiting to
|
|
87
|
+
* be retried - answers `ready()` with `false`, an empty `search`, and keeps
|
|
88
|
+
* what `add` handed it until it is.
|
|
89
|
+
*
|
|
90
|
+
* The ref is the store's own: an event ref for the journal, a note id for the
|
|
91
|
+
* note store, which holds one vector per note and replaces it when the note is
|
|
92
|
+
* revised. What the channel keeps beside a ref - the fields, and the order the
|
|
93
|
+
* ref's number carries - is whatever that store's reads filter on.
|
|
94
|
+
*/
|
|
95
|
+
export interface SemanticIndex {
|
|
96
|
+
/** Embed and store one item's text, replacing what is stored for it. */
|
|
97
|
+
add(ref: Ref, text: string, fields?: SemanticFields): Promise<void>;
|
|
98
|
+
/** Forget one item's text, as a redaction or a delete does. */
|
|
99
|
+
remove(ref: Ref): Promise<void>;
|
|
100
|
+
/** The refs nearest a query, best first, already filtered. Empty until ready. */
|
|
101
|
+
search(query: string, limit: number, filters: SemanticFilters): Promise<readonly Ref[]>;
|
|
102
|
+
/** Whether the channel can answer a query now. */
|
|
103
|
+
ready(): Promise<boolean>;
|
|
104
|
+
/** Every ref the channel holds, so a caller can index what it is missing. */
|
|
105
|
+
held(): Promise<ReadonlySet<Ref>>;
|
|
106
|
+
/** Release the underlying store. */
|
|
107
|
+
close(): Promise<void>;
|
|
108
|
+
}
|
|
55
109
|
export interface Journal {
|
|
56
110
|
/** Append one event. Assigns `ref` and `seq`; a redelivered `external` id is stored once. */
|
|
57
111
|
append(input: EventInput): Promise<Event>;
|
|
@@ -63,7 +117,11 @@ export interface Journal {
|
|
|
63
117
|
list(options?: ListOptions): Promise<EventPage>;
|
|
64
118
|
/** The events around one, in the global order. */
|
|
65
119
|
around(ref: EventRef, options?: AroundOptions): Promise<Event[]>;
|
|
66
|
-
/**
|
|
120
|
+
/**
|
|
121
|
+
* Text search: exact substring hits first, then word hits and semantic hits
|
|
122
|
+
* fused by reciprocal rank, each with its ref and window
|
|
123
|
+
* (docs/journal.md#search).
|
|
124
|
+
*/
|
|
67
125
|
search(query: string, options?: SearchOptions): Promise<SearchPage>;
|
|
68
126
|
/** Related items by weight, with the call that pages the rest. */
|
|
69
127
|
links(ref: Ref, options?: LinksOptions): Promise<LinkPage>;
|
package/notes.d.ts
CHANGED
|
@@ -155,6 +155,8 @@ export interface NotesSnapshot {
|
|
|
155
155
|
limit: number;
|
|
156
156
|
nextCall: string;
|
|
157
157
|
budget: ActiveSetBudget;
|
|
158
|
+
/** What the active notes cost against `budget.tokens`, estimated. */
|
|
159
|
+
tokens: number;
|
|
158
160
|
}
|
|
159
161
|
/** What changed since an epoch: the notes written after it. */
|
|
160
162
|
export interface NoteDelta {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@4fu/arbor-interfaces",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.1107.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Every replaceable interface in arbor, and the core's own shapes.",
|
|
6
6
|
"exports": {
|
|
@@ -66,6 +66,6 @@
|
|
|
66
66
|
"access": "public"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"@4fu/arbor-errors": "2026.
|
|
69
|
+
"@4fu/arbor-errors": "2026.1107.0"
|
|
70
70
|
}
|
|
71
71
|
}
|
package/plugin.d.ts
CHANGED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
* below; every part is optional, so a plugin can be only a hook, only a skill,
|
|
6
6
|
* or a whole domain such as a chat channel or a code host.
|
|
7
7
|
*
|
|
8
|
-
* Capabilities
|
|
9
|
-
*
|
|
10
|
-
* in `/env
|
|
11
|
-
*
|
|
8
|
+
* Capabilities the model reads, changes and reinstalls itself are MCP servers
|
|
9
|
+
* and skills in `/env`. A tool whose behaviour is the plugin's own code rather
|
|
10
|
+
* than an item in `/env` is declared as an `ExternalTool` and reaches the model
|
|
11
|
+
* under its own name (docs/tools.md#external-tools).
|
|
12
12
|
*/
|
|
13
13
|
import type { Block, BlockKindSpec, TextTable } from "./blocks.ts";
|
|
14
14
|
import type { Channel } from "./channel.ts";
|
|
@@ -17,6 +17,7 @@ import type { EnvDraft } from "./environment.ts";
|
|
|
17
17
|
import type { JudgeCriteria } from "./judge.ts";
|
|
18
18
|
import type { Hooks } from "./hooks.ts";
|
|
19
19
|
import type { TaskSource } from "./task.ts";
|
|
20
|
+
import type { ExternalTool, ExternalToolSource } from "./tools.ts";
|
|
20
21
|
/** Connections to the outside world. */
|
|
21
22
|
export interface PluginAdapters {
|
|
22
23
|
channels?: readonly Channel[];
|
|
@@ -37,6 +38,23 @@ export interface PromptSection {
|
|
|
37
38
|
* blocks carry data, and the table carries the words.
|
|
38
39
|
*/
|
|
39
40
|
text?: TextTable;
|
|
41
|
+
/**
|
|
42
|
+
* The section's revision, which the plugin raises when its content changes.
|
|
43
|
+
* The host asks for it as a window runs - one cheap call per step - and
|
|
44
|
+
* renders the section again only when it moved, so a section that changes
|
|
45
|
+
* reaches a live window as a delta carrying the changed lines
|
|
46
|
+
* (docs/prompting.md#plugin-sections-that-change). Absent for a section that
|
|
47
|
+
* never changes once the host has rendered it.
|
|
48
|
+
*/
|
|
49
|
+
revision?(): string | number | Promise<string | number>;
|
|
50
|
+
/**
|
|
51
|
+
* True when the section is material from outside the framework - a third
|
|
52
|
+
* party's prompt text, such as the sections a DeepSeek Harness plugin
|
|
53
|
+
* contributes. It is marked as data, carries a lead line saying what it is,
|
|
54
|
+
* and the `<agent>` rules state that the framework's own prompt wins on
|
|
55
|
+
* conflict (docs/prompting.md#data-and-instructions).
|
|
56
|
+
*/
|
|
57
|
+
outside?: boolean;
|
|
40
58
|
}
|
|
41
59
|
export interface Plugin {
|
|
42
60
|
readonly name: string;
|
|
@@ -45,6 +63,18 @@ export interface Plugin {
|
|
|
45
63
|
mcpServers?: readonly EnvDraft[];
|
|
46
64
|
/** Skills, installed into `/env/skills` under the plugin's name. */
|
|
47
65
|
skills?: readonly EnvDraft[];
|
|
66
|
+
/**
|
|
67
|
+
* Direct tools of its own: declared to the model under the names it gives
|
|
68
|
+
* them, and called in the turn through the same path a core tool takes
|
|
69
|
+
* (docs/tools.md#external-tools). A name a core tool or a module already
|
|
70
|
+
* declares is not registered; the host records the collision with its rule.
|
|
71
|
+
*
|
|
72
|
+
* The set may be a list or a source the host calls again on every refresh, so
|
|
73
|
+
* a plugin whose tools change while the host runs - the tools a harness
|
|
74
|
+
* around the host registered - answers with what stands now
|
|
75
|
+
* (docs/tools.md#external-tools-that-change).
|
|
76
|
+
*/
|
|
77
|
+
tools?: readonly ExternalTool[] | ExternalToolSource;
|
|
48
78
|
hooks?: Hooks;
|
|
49
79
|
promptSection?: PromptSection;
|
|
50
80
|
/** Extra criteria or decisions for its own inputs. */
|
package/plugin.js
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* below; every part is optional, so a plugin can be only a hook, only a skill,
|
|
6
6
|
* or a whole domain such as a chat channel or a code host.
|
|
7
7
|
*
|
|
8
|
-
* Capabilities
|
|
9
|
-
*
|
|
10
|
-
* in `/env
|
|
11
|
-
*
|
|
8
|
+
* Capabilities the model reads, changes and reinstalls itself are MCP servers
|
|
9
|
+
* and skills in `/env`. A tool whose behaviour is the plugin's own code rather
|
|
10
|
+
* than an item in `/env` is declared as an `ExternalTool` and reaches the model
|
|
11
|
+
* under its own name (docs/tools.md#external-tools).
|
|
12
12
|
*/
|
|
13
13
|
export {};
|
package/refs.d.ts
CHANGED
|
@@ -41,8 +41,11 @@ export type Target = string;
|
|
|
41
41
|
export type EventKind = "input" | "tool_call" | "tool_result" | "send" | "effect_result" | "note_revision" | "window_start" | "window_end" | "delivery" | "environment_revision" | "service_state" | "redaction" | "schedule" | (string & {});
|
|
42
42
|
/** Link kinds between refs. The host records them; the model never declares one. */
|
|
43
43
|
export type LinkKind = "cite" | "evidence" | "range" | "origin" | "revision" | "handoff" | "continuation" | "notice" | "reply" | (string & {});
|
|
44
|
-
/**
|
|
45
|
-
|
|
44
|
+
/**
|
|
45
|
+
* How a search hit matched: an exact substring, a word, or meaning alone. A hit
|
|
46
|
+
* the semantic channel found and no lexical index did is `semantic`.
|
|
47
|
+
*/
|
|
48
|
+
export type MatchKind = "substring" | "word" | "semantic";
|
|
46
49
|
/** Page size default shared by every read that returns a page. */
|
|
47
50
|
export declare const DEFAULT_PAGE_LIMIT = 20;
|
|
48
51
|
/** Maximum page size any read returns. */
|
package/scheduler.d.ts
CHANGED
|
@@ -73,15 +73,33 @@ export interface SkipPeriod {
|
|
|
73
73
|
from: string;
|
|
74
74
|
to: string;
|
|
75
75
|
}
|
|
76
|
+
/** How a repeating schedule stops: how often it fires, and until when. */
|
|
77
|
+
export interface RepeatBounds {
|
|
78
|
+
/** How many more times it fires after the first one. */
|
|
79
|
+
times?: number;
|
|
80
|
+
/** The last moment it may fire at, in epoch milliseconds. */
|
|
81
|
+
until?: number;
|
|
82
|
+
}
|
|
83
|
+
/** A period in milliseconds. */
|
|
84
|
+
export interface EveryRepeat extends RepeatBounds {
|
|
85
|
+
everyMs: number;
|
|
86
|
+
}
|
|
87
|
+
/** A clock time a firing repeats at daily. */
|
|
88
|
+
export interface DailyRepeat extends RepeatBounds {
|
|
89
|
+
dailyAt: string;
|
|
90
|
+
}
|
|
91
|
+
/** Days of the week, 0 for Sunday, and the clock time it fires at on them. */
|
|
92
|
+
export interface WeeklyRepeat extends RepeatBounds {
|
|
93
|
+
weekdays: readonly number[];
|
|
94
|
+
at: string;
|
|
95
|
+
}
|
|
96
|
+
/** How a scheduled task repeats. */
|
|
97
|
+
export type RepeatRule = EveryRepeat | DailyRepeat | WeeklyRepeat;
|
|
76
98
|
export interface ScheduledTaskInput {
|
|
77
99
|
name?: string;
|
|
78
100
|
/** The first firing, in epoch milliseconds. */
|
|
79
101
|
firstAt: number;
|
|
80
|
-
repeat?:
|
|
81
|
-
everyMs: number;
|
|
82
|
-
} | {
|
|
83
|
-
dailyAt: string;
|
|
84
|
-
};
|
|
102
|
+
repeat?: RepeatRule;
|
|
85
103
|
skipDaily?: readonly SkipPeriod[];
|
|
86
104
|
targets?: readonly Target[];
|
|
87
105
|
task: Omit<TaskInput, "kind">;
|
|
@@ -92,11 +110,9 @@ export interface ScheduledTask {
|
|
|
92
110
|
firstAt: number;
|
|
93
111
|
nextAt: number;
|
|
94
112
|
lastAt?: number;
|
|
95
|
-
repeat
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
dailyAt: string;
|
|
99
|
-
};
|
|
113
|
+
/** How many times it has fired, so `repeat.times` survives a restart. */
|
|
114
|
+
fired?: number;
|
|
115
|
+
repeat?: RepeatRule;
|
|
100
116
|
skipDaily?: readonly SkipPeriod[];
|
|
101
117
|
targets: readonly Target[];
|
|
102
118
|
paused: boolean;
|
package/settings.d.ts
CHANGED
|
@@ -58,6 +58,21 @@ export interface Settings {
|
|
|
58
58
|
workspaces?: {
|
|
59
59
|
/** How long a chain's directory is kept after its last window ended, before the host reclaims it. */
|
|
60
60
|
retentionMs?: number;
|
|
61
|
+
/**
|
|
62
|
+
* The shared folder every window works in, for the ovnfs workspace: it can
|
|
63
|
+
* hold several projects and repositories, and it is read-only by convention.
|
|
64
|
+
* Absent leaves the plain-directory workspace's own root in place.
|
|
65
|
+
*/
|
|
66
|
+
shared?: string;
|
|
67
|
+
/** The ovnfs daemon's paths, under the host's data directory by default. */
|
|
68
|
+
ovnfs?: {
|
|
69
|
+
/** Where the daemon keeps its state. */
|
|
70
|
+
state?: string;
|
|
71
|
+
/** Where checkouts appear. */
|
|
72
|
+
mount?: string;
|
|
73
|
+
/** The ovnfs binary to run; `ovnfs` on PATH by default. */
|
|
74
|
+
bin?: string;
|
|
75
|
+
};
|
|
61
76
|
};
|
|
62
77
|
/** The names of the secrets a configuration may use. Never their values. */
|
|
63
78
|
secrets?: readonly string[];
|
package/task.d.ts
CHANGED
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
* or a service event. Batching is the source's business - a chat source
|
|
7
7
|
* debounces per conversation, so several messages sent in a row become one
|
|
8
8
|
* task. The host makes no tasks of its own for maintenance.
|
|
9
|
+
*
|
|
10
|
+
* A source claims the outside events of the source it names: the host hands it
|
|
11
|
+
* every event whose `OutsideEvent.source` is this source's `id`, and dispatches
|
|
12
|
+
* the tasks it makes of them as they stand. The host groups nothing of its own
|
|
13
|
+
* for a source's events, and never debounces on its behalf - a source that
|
|
14
|
+
* debounces keeps its own buffer.
|
|
9
15
|
*/
|
|
10
16
|
import type { Block } from "./blocks.ts";
|
|
11
17
|
import type { OutsideEvent, Unsubscribe } from "./channel.ts";
|
|
@@ -41,6 +47,9 @@ export interface TaskSource {
|
|
|
41
47
|
readonly id: string;
|
|
42
48
|
/** Turn outside events into tasks, batched the way this source batches. */
|
|
43
49
|
toTasks(events: readonly OutsideEvent[]): readonly TaskInput[];
|
|
44
|
-
/**
|
|
50
|
+
/**
|
|
51
|
+
* Push source. The host may poll instead. The host takes this one at `start`,
|
|
52
|
+
* the way it takes a channel's, and what it pushes goes through `ingest`.
|
|
53
|
+
*/
|
|
45
54
|
subscribe?(handler: (events: readonly OutsideEvent[]) => void | Promise<void>): Unsubscribe;
|
|
46
55
|
}
|
package/task.js
CHANGED
|
@@ -6,5 +6,11 @@
|
|
|
6
6
|
* or a service event. Batching is the source's business - a chat source
|
|
7
7
|
* debounces per conversation, so several messages sent in a row become one
|
|
8
8
|
* task. The host makes no tasks of its own for maintenance.
|
|
9
|
+
*
|
|
10
|
+
* A source claims the outside events of the source it names: the host hands it
|
|
11
|
+
* every event whose `OutsideEvent.source` is this source's `id`, and dispatches
|
|
12
|
+
* the tasks it makes of them as they stand. The host groups nothing of its own
|
|
13
|
+
* for a source's events, and never debounces on its behalf - a source that
|
|
14
|
+
* debounces keeps its own buffer.
|
|
9
15
|
*/
|
|
10
16
|
export {};
|
package/tools.d.ts
CHANGED
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Decisions and plain commands are direct tools, declared to the model and
|
|
6
6
|
* called in the turn. Everything else is a module function, called from a
|
|
7
|
-
* script in `code_exec`.
|
|
8
|
-
*
|
|
7
|
+
* script in `code_exec`. A plugin declares its own direct tools as
|
|
8
|
+
* `ExternalTool`s, which the host registers as a group of their own; what the
|
|
9
|
+
* model can otherwise do with a plugin is an MCP server and skills in `/env`.
|
|
9
10
|
*/
|
|
10
11
|
import type { DirectToolSpec, FunctionSpec, ModuleSpec, ParameterSpec, ToolCall, ToolResult } from "./agent.ts";
|
|
12
|
+
import type { JSONSchema } from "./agent.ts";
|
|
11
13
|
import type { EventRef, WindowRef } from "./refs.ts";
|
|
12
14
|
import type { WorkspaceMount } from "./workspace.ts";
|
|
13
15
|
/** What a call is made in. The service layer fills it; a group never reaches past it. */
|
|
@@ -31,3 +33,50 @@ export interface ToolGroup {
|
|
|
31
33
|
call(request: ToolCall, context: CallContext): Promise<ToolResult>;
|
|
32
34
|
}
|
|
33
35
|
export type { DirectToolSpec, FunctionSpec, ModuleSpec, ParameterSpec, ToolCall, ToolResult };
|
|
36
|
+
/**
|
|
37
|
+
* One direct tool declared from outside the core: a plugin's own, or a
|
|
38
|
+
* backend's injection of the tools an outside harness registered
|
|
39
|
+
* (docs/tools.md#external-tools).
|
|
40
|
+
*
|
|
41
|
+
* One shape for both, so a plugin and a backend share one path instead of two:
|
|
42
|
+
* the host registers what it is handed, and from there the tool is a direct
|
|
43
|
+
* tool like any other - declared to the model under its own name, and called
|
|
44
|
+
* through the same hooks, journaling, scrub and result cut as the core's own.
|
|
45
|
+
* Nothing about it is special-cased after registration.
|
|
46
|
+
*/
|
|
47
|
+
export interface ExternalTool {
|
|
48
|
+
readonly name: string;
|
|
49
|
+
/** What it does and when to use it; side effects. 1 to 4 sentences. */
|
|
50
|
+
readonly description: string;
|
|
51
|
+
/**
|
|
52
|
+
* The parameters in arbor's own declaration shape. Optional for a tool that
|
|
53
|
+
* declares `schema` instead: an outside harness already has a JSON Schema for
|
|
54
|
+
* its tool, and restating it in this shape would only lose what the schema
|
|
55
|
+
* says.
|
|
56
|
+
*/
|
|
57
|
+
readonly parameters?: Record<string, ParameterSpec>;
|
|
58
|
+
/**
|
|
59
|
+
* The parameters as the JSON Schema the outside harness declared them,
|
|
60
|
+
* carried verbatim (docs/tools.md#external-tools). What the model reads when
|
|
61
|
+
* the tool has one; `parameters` is the declaration for a tool that has none.
|
|
62
|
+
*/
|
|
63
|
+
readonly schema?: JSONSchema;
|
|
64
|
+
/** Run one call. The same path a core tool takes. */
|
|
65
|
+
execute(request: ToolCall, context: CallContext): Promise<ToolResult>;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Where a plugin's external tools come from: a list, or a call that answers
|
|
69
|
+
* with the current one. The host reads the source again on every refresh, so a
|
|
70
|
+
* plugin whose tools change at runtime - the tools a harness around the host
|
|
71
|
+
* registered - answers with what stands now
|
|
72
|
+
* (docs/tools.md#external-tools-that-change).
|
|
73
|
+
*/
|
|
74
|
+
export type ExternalToolSource = () => readonly ExternalTool[] | Promise<readonly ExternalTool[]>;
|
|
75
|
+
/** What one refresh of the external tools changed about one tool. */
|
|
76
|
+
export interface ExternalToolChange {
|
|
77
|
+
change: "added" | "removed";
|
|
78
|
+
/** The tool's own name, as the model calls it. */
|
|
79
|
+
name: string;
|
|
80
|
+
/** Who declares it: the plugin's name, or the backend's. */
|
|
81
|
+
source: string;
|
|
82
|
+
}
|
package/tools.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Decisions and plain commands are direct tools, declared to the model and
|
|
6
6
|
* called in the turn. Everything else is a module function, called from a
|
|
7
|
-
* script in `code_exec`.
|
|
8
|
-
*
|
|
7
|
+
* script in `code_exec`. A plugin declares its own direct tools as
|
|
8
|
+
* `ExternalTool`s, which the host registers as a group of their own; what the
|
|
9
|
+
* model can otherwise do with a plugin is an MCP server and skills in `/env`.
|
|
9
10
|
*/
|
|
10
11
|
export {};
|
package/windows.d.ts
CHANGED
|
@@ -57,6 +57,11 @@ export interface BoardLine {
|
|
|
57
57
|
expects: readonly Expectation[];
|
|
58
58
|
state: WindowState;
|
|
59
59
|
usage: TokenUsage;
|
|
60
|
+
/**
|
|
61
|
+
* The work this window still has open in its workspace, each item as the
|
|
62
|
+
* model reads it (docs/workspaces.md#open-work). Empty when it has none.
|
|
63
|
+
*/
|
|
64
|
+
openWork?: readonly string[];
|
|
60
65
|
}
|
|
61
66
|
/**
|
|
62
67
|
* What a window expects to arrive, so the input reaches it instead of a new
|
package/workspace.d.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { TextTable } from "./blocks.ts";
|
|
10
10
|
import type { WindowRef, WorkspaceRef } from "./refs.ts";
|
|
11
|
+
import type { PromptSection } from "./plugin.ts";
|
|
11
12
|
import type { ToolGroup } from "./tools.ts";
|
|
12
13
|
/** The result of one command run in a mounted workspace. */
|
|
13
14
|
export interface ExecResult {
|
|
@@ -79,6 +80,46 @@ export interface WorkspaceQuota {
|
|
|
79
80
|
used: number;
|
|
80
81
|
files: number;
|
|
81
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* One checkout a window's chain has open, for an implementation that gives a
|
|
85
|
+
* window a copy-on-write checkout rather than working in place (the ovnfs
|
|
86
|
+
* workspace, docs/workspaces.md). `dirty` is true when the checkout holds
|
|
87
|
+
* changes not yet applied back, which counts as open work at `end`.
|
|
88
|
+
*/
|
|
89
|
+
export interface WorkspaceCheckout {
|
|
90
|
+
/** The checkout's name, which begins with the window ref that opened it. */
|
|
91
|
+
name: string;
|
|
92
|
+
/** The checkout's path on the host, where a command works in it. */
|
|
93
|
+
path: string;
|
|
94
|
+
/** The checkout holds changes not yet applied back to the shared folder. */
|
|
95
|
+
dirty: boolean;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* One persistent directory an implementation keeps beyond a window's own
|
|
99
|
+
* folder: a named place whose content outlives the window that made it, the
|
|
100
|
+
* retention period and a host restart, and which a later window finds by name
|
|
101
|
+
* and uses. The plain directory workspace keeps them under one root beside the
|
|
102
|
+
* windows' own directories (docs/workspaces.md#persistent-directories).
|
|
103
|
+
*/
|
|
104
|
+
export interface WorkspaceKeep {
|
|
105
|
+
/** The name the model gave it, which is how a later window names it. */
|
|
106
|
+
name: string;
|
|
107
|
+
/** The directory's path on the host, where a command works in it. */
|
|
108
|
+
path: string;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* One piece of work a window still has open in its workspace: something it
|
|
112
|
+
* started and has not finished. The ovnfs workspace answers with its unapplied
|
|
113
|
+
* checkouts; an implementation whose work is finished in place - the plain
|
|
114
|
+
* directory workspace, which a continuation keeps working in - answers nothing
|
|
115
|
+
* (docs/workspaces.md#open-work).
|
|
116
|
+
*/
|
|
117
|
+
export interface OpenWorkItem {
|
|
118
|
+
/** The name the implementation knows it by, which is how a later window names it. */
|
|
119
|
+
name: string;
|
|
120
|
+
/** What is open about it, in the implementation's own words, when it has any. */
|
|
121
|
+
detail?: string;
|
|
122
|
+
}
|
|
82
123
|
export interface Workspace {
|
|
83
124
|
/**
|
|
84
125
|
* Create the window's own directory, on first use. A window that never
|
|
@@ -110,4 +151,54 @@ export interface Workspace {
|
|
|
110
151
|
* about it (docs/workspaces.md, docs/architecture.md#plugins).
|
|
111
152
|
*/
|
|
112
153
|
groups?: (table: TextTable) => readonly ToolGroup[];
|
|
154
|
+
/**
|
|
155
|
+
* The open checkouts of a window's chain, for `end`'s open-work check, for
|
|
156
|
+
* `workspace.list` and `workspace.path`, and as the gate reclaim uses. Absent
|
|
157
|
+
* for an implementation that keeps no checkouts of its own.
|
|
158
|
+
*/
|
|
159
|
+
checkouts?(window: WindowRef): Promise<readonly WorkspaceCheckout[]>;
|
|
160
|
+
/**
|
|
161
|
+
* The work a window still has open: what it started and has not finished.
|
|
162
|
+
* The host asks for it where a window's own state is not enough - the
|
|
163
|
+
* reminder tiers name the open items, the board shows them, `end` refuses
|
|
164
|
+
* without a handoff while any are open, and a continuation's first input
|
|
165
|
+
* names the ones its predecessor left (docs/windows.md#usage-reminders,
|
|
166
|
+
* docs/workspaces.md#open-work). Absent for an implementation that keeps
|
|
167
|
+
* nothing open.
|
|
168
|
+
*/
|
|
169
|
+
openWork?(window: WindowRef): Promise<readonly OpenWorkItem[]>;
|
|
170
|
+
/**
|
|
171
|
+
* The persistent directories a window's chain created, or every one of them
|
|
172
|
+
* when no window is named - the places whose content outlives the window, the
|
|
173
|
+
* retention period and a host restart, which a later window finds by name and
|
|
174
|
+
* uses (docs/workspaces.md#persistent-directories). Absent for an
|
|
175
|
+
* implementation that keeps none. The host asks for a chain's to name them in
|
|
176
|
+
* a continuation's first input; a window asks for all of them through the
|
|
177
|
+
* implementation's own module function.
|
|
178
|
+
*/
|
|
179
|
+
keeps?(window?: WindowRef): Promise<readonly WorkspaceKeep[]>;
|
|
180
|
+
/**
|
|
181
|
+
* The prompt section this implementation contributes, or none. It carries the
|
|
182
|
+
* method the model follows - how this implementation wants files changed -
|
|
183
|
+
* rendered in its own tag, so each implementation owns its own prompt text and
|
|
184
|
+
* a host that fell back to another implementation shows that one's instead.
|
|
185
|
+
*/
|
|
186
|
+
promptSection?(table: TextTable): PromptSection;
|
|
187
|
+
/**
|
|
188
|
+
* The environment a bash command in this window runs with, beyond the host's
|
|
189
|
+
* own, or none. The ovnfs workspace sets `ARBOR_WINDOW` and the git hook path
|
|
190
|
+
* that stamps `Arbor-Window` onto commits made in a checkout
|
|
191
|
+
* (docs/workspaces.md). Set on the command's own process, never on the host,
|
|
192
|
+
* so it does not reach an MCP server or a service.
|
|
193
|
+
*/
|
|
194
|
+
bashEnv?(window: WindowRef): Record<string, string>;
|
|
195
|
+
/**
|
|
196
|
+
* The built-in skills this implementation seeds into the environment, read
|
|
197
|
+
* only, or none. The ovnfs workspace seeds the `ovnfs` skill that documents
|
|
198
|
+
* the full CLI method.
|
|
199
|
+
*/
|
|
200
|
+
builtinSkills?(): readonly {
|
|
201
|
+
name: string;
|
|
202
|
+
source: string;
|
|
203
|
+
}[];
|
|
113
204
|
}
|