@4fu/arbor-interfaces 2026.1106.1 → 2026.1107.1
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 +6 -1
- package/agent.d.ts +129 -6
- package/events.d.ts +5 -1
- package/index.d.ts +3 -3
- package/journal.d.ts +59 -1
- package/package.json +2 -2
- package/plugin.d.ts +29 -4
- package/refs.d.ts +5 -2
- package/tools.d.ts +30 -1
- package/workspace.d.ts +24 -2
package/README.md
CHANGED
|
@@ -56,7 +56,9 @@ npm install @4fu/arbor-core @4fu/arbor-journal-sqlite @4fu/arbor-notes-sqlite \
|
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
`@4fu/arbor-plugin-http` is optional; it is the example plugin below.
|
|
59
|
-
`@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.
|
|
60
62
|
|
|
61
63
|
## A minimal host
|
|
62
64
|
|
|
@@ -120,6 +122,8 @@ window gets, and how a continuation keeps working in it, are in
|
|
|
120
122
|
| [`@4fu/arbor-journal-sqlite`](packages/journal-sqlite) | the default `Journal` and `BlobStore` on SQLite |
|
|
121
123
|
| [`@4fu/arbor-notes-sqlite`](packages/notes-sqlite) | the default `NoteStore` on SQLite, beside the journal |
|
|
122
124
|
| [`@4fu/arbor-segmenter-jieba`](packages/segmenter-jieba) | the default `Segmenter`: jieba's dictionary segmentation for the word index, optional |
|
|
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 |
|
|
123
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 |
|
|
124
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 |
|
|
125
129
|
| [`@4fu/arbor-environment-dir`](packages/environment-dir) | the default `Environment`: one `/env` directory, a revision per item, validation and deltas |
|
|
@@ -171,6 +175,7 @@ tarball would contain ([docs/release.md](docs/release.md)).
|
|
|
171
175
|
| `packages/errors` | the error shape every refusal uses: the rule and the next action |
|
|
172
176
|
| `packages/journal-sqlite` | the default `Journal` and `BlobStore` on SQLite |
|
|
173
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 |
|
|
174
179
|
| `packages/notes-sqlite` | the default `NoteStore` on SQLite, beside the journal |
|
|
175
180
|
| `packages/judge-jev` | the default `Judge`: Jev, TypeSafe AI's System One classifier, answering the dispatch decisions |
|
|
176
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
|
@@ -13,6 +13,17 @@ import type { ExternalTool } from "./tools.ts";
|
|
|
13
13
|
import type { TaskRef, Ref, WindowRef } from "./refs.ts";
|
|
14
14
|
import type { Handoff, TokenUsage, WindowState } from "./windows.ts";
|
|
15
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>;
|
|
16
27
|
/** One model the backend can run. Only models that take images are offered. */
|
|
17
28
|
export interface ModelInfo {
|
|
18
29
|
id: string;
|
|
@@ -78,7 +89,32 @@ export interface ContinuationInfo {
|
|
|
78
89
|
/** Set after the hard limit: the window was reset mid-task. */
|
|
79
90
|
midTaskReset?: boolean;
|
|
80
91
|
}
|
|
81
|
-
/**
|
|
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
|
+
*/
|
|
82
118
|
export interface Delivery {
|
|
83
119
|
kind: "inserted_task" | "conversation_update" | "notice" | "broadcast" | "window_message" | "environment_delta"
|
|
84
120
|
/**
|
|
@@ -87,6 +123,13 @@ export interface Delivery {
|
|
|
87
123
|
* (docs/prompting.md#plugin-sections-that-change).
|
|
88
124
|
*/
|
|
89
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"
|
|
90
133
|
/** A host usage reminder, appended to an input (docs/windows.md#usage-reminders). */
|
|
91
134
|
| "reminder";
|
|
92
135
|
/** The window it comes from, for a notice, a broadcast or a window message. */
|
|
@@ -99,10 +142,66 @@ export interface Delivery {
|
|
|
99
142
|
export interface WindowSession {
|
|
100
143
|
readonly window: WindowRef;
|
|
101
144
|
readonly state: WindowState;
|
|
102
|
-
/**
|
|
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
|
+
*/
|
|
103
172
|
steer(delivery: Delivery): Promise<void>;
|
|
104
|
-
/**
|
|
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
|
+
*/
|
|
105
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;
|
|
106
205
|
/** End the run. With a handoff the host opens a continuation. */
|
|
107
206
|
end(handoff?: Handoff, reason?: WindowEndReason): Promise<void>;
|
|
108
207
|
/** Pause insertion without ending the window. */
|
|
@@ -191,8 +290,14 @@ export interface AgentBackend {
|
|
|
191
290
|
* Direct tools the backend injects from outside arbor: the tools an outside
|
|
192
291
|
* harness registered, which a host embedded in that harness hands in here
|
|
193
292
|
* (docs/tools.md#external-tools). They are declared to the model and called
|
|
194
|
-
* in the turn like any other direct tool.
|
|
195
|
-
*
|
|
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.
|
|
196
301
|
*/
|
|
197
302
|
externalTools?(): readonly ExternalTool[] | Promise<readonly ExternalTool[]>;
|
|
198
303
|
/** Open one window and start its first turn. */
|
|
@@ -203,7 +308,17 @@ export interface DirectToolSpec {
|
|
|
203
308
|
name: string;
|
|
204
309
|
/** What it does and when to use it; side effects. 1 to 4 sentences. */
|
|
205
310
|
description: string;
|
|
311
|
+
/** The parameters in arbor's own declaration shape. */
|
|
206
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;
|
|
207
322
|
/**
|
|
208
323
|
* Who declares it, when it is not the core's own: a plugin's name, or a
|
|
209
324
|
* backend's. The tools list says where such a tool comes from, so the model
|
|
@@ -239,7 +354,15 @@ export interface ToolCall {
|
|
|
239
354
|
export interface ToolResult {
|
|
240
355
|
callId: string;
|
|
241
356
|
ok: boolean;
|
|
242
|
-
/**
|
|
357
|
+
/**
|
|
358
|
+
* Stable fields, bounded, in the core's error shape when it failed.
|
|
359
|
+
*
|
|
360
|
+
* A refusal carries its own wording here as well as in `error`: the rule, the
|
|
361
|
+
* message and the next action, in the one form the journal records a refusal
|
|
362
|
+
* with. So an embedding that writes its own log passes the result through as
|
|
363
|
+
* it stands instead of reassembling the three parts, and what the record
|
|
364
|
+
* holds is what the caller was handed.
|
|
365
|
+
*/
|
|
243
366
|
content: string;
|
|
244
367
|
/**
|
|
245
368
|
* The tool that was called, as the prompt names it. A refusal carries it so
|
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;
|
package/index.d.ts
CHANGED
|
@@ -11,8 +11,8 @@ 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, StreamEvent, 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
18
|
export type { ExecResult, StagedFile, OpenWorkItem, WorkspaceCheckout, WorkspaceKeep, WorkspaceMount, WorkspaceQuota, Workspace, } from "./workspace.ts";
|
|
@@ -20,7 +20,7 @@ export type { JudgeMode, JudgeDecisionKind, CandidateWindow, JudgeTask, FilterSt
|
|
|
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, ExternalTool } 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
26
|
export type { QueuedInput, InsertionRequest, InsertionPlan, RateLimitState, AdmissionRequest, AdmissionPlan, SkipPeriod, DailyRepeat, EveryRepeat, RepeatBounds, RepeatRule, WeeklyRepeat, ScheduledTaskInput, ScheduledTask, ScheduledTaskUpdate, Scheduler, } from "./scheduler.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@4fu/arbor-interfaces",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.1107.1",
|
|
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
|
@@ -16,8 +16,9 @@ import type { EffectExecutor } from "./effects.ts";
|
|
|
16
16
|
import type { EnvDraft } from "./environment.ts";
|
|
17
17
|
import type { JudgeCriteria } from "./judge.ts";
|
|
18
18
|
import type { Hooks } from "./hooks.ts";
|
|
19
|
+
import type { WindowRef } from "./refs.ts";
|
|
19
20
|
import type { TaskSource } from "./task.ts";
|
|
20
|
-
import type { ExternalTool } from "./tools.ts";
|
|
21
|
+
import type { ExternalTool, ExternalToolSource } from "./tools.ts";
|
|
21
22
|
/** Connections to the outside world. */
|
|
22
23
|
export interface PluginAdapters {
|
|
23
24
|
channels?: readonly Channel[];
|
|
@@ -30,8 +31,17 @@ export interface PromptSection {
|
|
|
30
31
|
tag: string;
|
|
31
32
|
/** The block kinds its inputs use, which the renderer maps to text. */
|
|
32
33
|
blocks: readonly BlockKindSpec[];
|
|
33
|
-
/**
|
|
34
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Its content, as blocks. The plugin hands blocks, never finished text.
|
|
36
|
+
*
|
|
37
|
+
* `window` is the window the section is rendered for, which the host names on
|
|
38
|
+
* every render: the epoch a window opens with, a delta of the changed lines
|
|
39
|
+
* while it runs. A section whose content is the same for every window ignores
|
|
40
|
+
* it; one that serves several sessions - one workspace per session, each
|
|
41
|
+
* naming the window it serves - answers for that window
|
|
42
|
+
* (docs/prompting.md#plugin-sections-that-change).
|
|
43
|
+
*/
|
|
44
|
+
content(window?: WindowRef): readonly Block[] | Promise<readonly Block[]>;
|
|
35
45
|
/**
|
|
36
46
|
* The fixed strings its own blocks write, merged into the host's text table.
|
|
37
47
|
* A plugin names no secret here, and writes no prompt text of its own: its
|
|
@@ -47,6 +57,16 @@ export interface PromptSection {
|
|
|
47
57
|
* never changes once the host has rendered it.
|
|
48
58
|
*/
|
|
49
59
|
revision?(): string | number | Promise<string | number>;
|
|
60
|
+
/**
|
|
61
|
+
* True when this section's content differs from window to window - a section
|
|
62
|
+
* that serves several sessions, one workspace per session, naming the window
|
|
63
|
+
* it serves. The host then renders it for each window instead of freezing it
|
|
64
|
+
* with the epoch, and that window's prefix is its own where this section is
|
|
65
|
+
* concerned; every other window still shares the epoch's prefix
|
|
66
|
+
* (docs/prompting.md#plugin-sections-that-change). Absent, like `false`: the
|
|
67
|
+
* section says the same thing to every window and is frozen with the epoch.
|
|
68
|
+
*/
|
|
69
|
+
perWindow?: boolean;
|
|
50
70
|
/**
|
|
51
71
|
* True when the section is material from outside the framework - a third
|
|
52
72
|
* party's prompt text, such as the sections a DeepSeek Harness plugin
|
|
@@ -68,8 +88,13 @@ export interface Plugin {
|
|
|
68
88
|
* them, and called in the turn through the same path a core tool takes
|
|
69
89
|
* (docs/tools.md#external-tools). A name a core tool or a module already
|
|
70
90
|
* declares is not registered; the host records the collision with its rule.
|
|
91
|
+
*
|
|
92
|
+
* The set may be a list or a source the host calls again on every refresh, so
|
|
93
|
+
* a plugin whose tools change while the host runs - the tools a harness
|
|
94
|
+
* around the host registered - answers with what stands now
|
|
95
|
+
* (docs/tools.md#external-tools-that-change).
|
|
71
96
|
*/
|
|
72
|
-
tools?: readonly ExternalTool[];
|
|
97
|
+
tools?: readonly ExternalTool[] | ExternalToolSource;
|
|
73
98
|
hooks?: Hooks;
|
|
74
99
|
promptSection?: PromptSection;
|
|
75
100
|
/** Extra criteria or decisions for its own inputs. */
|
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/tools.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* model can otherwise do with a plugin is an MCP server and skills in `/env`.
|
|
10
10
|
*/
|
|
11
11
|
import type { DirectToolSpec, FunctionSpec, ModuleSpec, ParameterSpec, ToolCall, ToolResult } from "./agent.ts";
|
|
12
|
+
import type { JSONSchema } from "./agent.ts";
|
|
12
13
|
import type { EventRef, WindowRef } from "./refs.ts";
|
|
13
14
|
import type { WorkspaceMount } from "./workspace.ts";
|
|
14
15
|
/** What a call is made in. The service layer fills it; a group never reaches past it. */
|
|
@@ -47,7 +48,35 @@ export interface ExternalTool {
|
|
|
47
48
|
readonly name: string;
|
|
48
49
|
/** What it does and when to use it; side effects. 1 to 4 sentences. */
|
|
49
50
|
readonly description: string;
|
|
50
|
-
|
|
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;
|
|
51
64
|
/** Run one call. The same path a core tool takes. */
|
|
52
65
|
execute(request: ToolCall, context: CallContext): Promise<ToolResult>;
|
|
53
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/workspace.d.ts
CHANGED
|
@@ -130,13 +130,30 @@ export interface Workspace {
|
|
|
130
130
|
mount(ref: WorkspaceRef): Promise<WorkspaceMount>;
|
|
131
131
|
/** The workspace's size and file count, for a quota a deployment sets. */
|
|
132
132
|
quota(workspace: WorkspaceRef): Promise<WorkspaceQuota>;
|
|
133
|
-
/**
|
|
133
|
+
/**
|
|
134
|
+
* Reclaim the workspace after the retention period.
|
|
135
|
+
*
|
|
136
|
+
* An implementation whose workspace is a directory somebody else owns - a
|
|
137
|
+
* host's own project directory, which nothing here may remove - has no honest
|
|
138
|
+
* answer to give here: it says so with `reclaimable`, below, and the host
|
|
139
|
+
* never asks.
|
|
140
|
+
*/
|
|
134
141
|
reclaim(workspace: WorkspaceRef): Promise<void>;
|
|
142
|
+
/**
|
|
143
|
+
* Whether this implementation reclaims a workspace at all. `false` for one
|
|
144
|
+
* whose directory belongs to somebody else - the user's own project, which
|
|
145
|
+
* nothing here may remove - and the host then neither asks `refOf` for a ref
|
|
146
|
+
* to reclaim nor arms a retention deadline, so nothing is reclaimed and no
|
|
147
|
+
* failure is recorded either (docs/workspaces.md#lifecycle). Absent, like
|
|
148
|
+
* `true`: the implementation owns what it created and `reclaim` removes it.
|
|
149
|
+
*/
|
|
150
|
+
reclaimable?(): boolean | Promise<boolean>;
|
|
135
151
|
/**
|
|
136
152
|
* The workspace a window's chain already has, or `undefined` when it has
|
|
137
153
|
* none - the window never touched files, or the directory was reclaimed. The
|
|
138
154
|
* host asks for it to reclaim a chain's directory once its last window has
|
|
139
|
-
* ended and the retention period has passed
|
|
155
|
+
* ended and the retention period has passed - of an implementation that
|
|
156
|
+
* reclaims at all (`reclaimable`, above) - which a restart has to survive
|
|
140
157
|
* (docs/workspaces.md#lifecycle).
|
|
141
158
|
*/
|
|
142
159
|
refOf(window: WindowRef): Promise<WorkspaceRef | undefined>;
|
|
@@ -182,6 +199,11 @@ export interface Workspace {
|
|
|
182
199
|
* method the model follows - how this implementation wants files changed -
|
|
183
200
|
* rendered in its own tag, so each implementation owns its own prompt text and
|
|
184
201
|
* a host that fell back to another implementation shows that one's instead.
|
|
202
|
+
*
|
|
203
|
+
* The host registers the section once and renders it per window, so a section
|
|
204
|
+
* that serves several sessions - one workspace per session, each naming the
|
|
205
|
+
* window it serves - answers for the window it is rendered for through
|
|
206
|
+
* `content(window)` (docs/prompting.md#plugin-sections-that-change).
|
|
185
207
|
*/
|
|
186
208
|
promptSection?(table: TextTable): PromptSection;
|
|
187
209
|
/**
|