@4fu/arbor-interfaces 0.1.0 → 2026.1106.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 +8 -5
- package/agent.d.ts +59 -5
- package/agent.js +3 -3
- package/blocks.d.ts +12 -0
- package/events.d.ts +1 -1
- package/hooks.d.ts +36 -2
- package/index.d.ts +5 -5
- package/notes.d.ts +2 -0
- package/package.json +2 -2
- package/plugin.d.ts +29 -4
- package/plugin.js +4 -4
- 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 +22 -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.
|
|
@@ -118,7 +120,8 @@ window gets, and how a continuation keeps working in it, are in
|
|
|
118
120
|
| [`@4fu/arbor-journal-sqlite`](packages/journal-sqlite) | the default `Journal` and `BlobStore` on SQLite |
|
|
119
121
|
| [`@4fu/arbor-notes-sqlite`](packages/notes-sqlite) | the default `NoteStore` on SQLite, beside the journal |
|
|
120
122
|
| [`@4fu/arbor-segmenter-jieba`](packages/segmenter-jieba) | the default `Segmenter`: jieba's dictionary segmentation for the word index, optional |
|
|
121
|
-
| [`@4fu/arbor-workspace-dir`](packages/workspace-dir) | the
|
|
123
|
+
| [`@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
|
+
| [`@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
125
|
| [`@4fu/arbor-environment-dir`](packages/environment-dir) | the default `Environment`: one `/env` directory, a revision per item, validation and deltas |
|
|
123
126
|
| [`@4fu/arbor-supervisor-process`](packages/supervisor-process) | the default `Supervisor`: one host process per service, restart with backoff, logs |
|
|
124
127
|
| [`@4fu/arbor-judge-jev`](packages/judge-jev) | the default `Judge`: Jev, TypeSafe AI's System One classifier, answering the dispatch decisions |
|
|
@@ -151,7 +154,7 @@ deployment can see what it would be taking on before it adopts one.
|
|
|
151
154
|
| [docs/coordination.md](docs/coordination.md) | the board, expectations, deliveries and broadcasts, staying current, effects |
|
|
152
155
|
| [docs/environment.md](docs/environment.md) | skills, MCP servers and services: using, changing, reload, built-in skills, deltas |
|
|
153
156
|
| [docs/memory.md](docs/memory.md) | notes, the active set, anchors, links |
|
|
154
|
-
| [docs/workspaces.md](docs/workspaces.md) | per-window
|
|
157
|
+
| [docs/workspaces.md](docs/workspaces.md) | the shared folder and per-window checkouts: isolation, continuity, what the agent does |
|
|
155
158
|
| [docs/tools.md](docs/tools.md) | direct tools and code mode modules |
|
|
156
159
|
| [docs/prompting.md](docs/prompting.md) | how every model-facing text is written: layers, notes, deliveries, errors, judge questions, terms |
|
|
157
160
|
| [docs/release.md](docs/release.md) | calendar versioning, the manual release steps, how a package is published |
|
package/agent.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
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";
|
|
@@ -80,6 +81,12 @@ export interface ContinuationInfo {
|
|
|
80
81
|
/** What the host inserts into a running window at a tool-call boundary. */
|
|
81
82
|
export interface Delivery {
|
|
82
83
|
kind: "inserted_task" | "conversation_update" | "notice" | "broadcast" | "window_message" | "environment_delta"
|
|
84
|
+
/**
|
|
85
|
+
* A plugin's prompt section changed since this window's prefix was built:
|
|
86
|
+
* the lines that changed, and nothing that did not
|
|
87
|
+
* (docs/prompting.md#plugin-sections-that-change).
|
|
88
|
+
*/
|
|
89
|
+
| "prompt_delta"
|
|
83
90
|
/** A host usage reminder, appended to an input (docs/windows.md#usage-reminders). */
|
|
84
91
|
| "reminder";
|
|
85
92
|
/** The window it comes from, for a notice, a broadcast or a window message. */
|
|
@@ -102,13 +109,40 @@ export interface WindowSession {
|
|
|
102
109
|
pause(): Promise<void>;
|
|
103
110
|
resume(): Promise<void>;
|
|
104
111
|
usage(): TokenUsage;
|
|
105
|
-
/** Tool calls, results and
|
|
112
|
+
/** Tool calls, results and what the model is writing, as they happen. */
|
|
106
113
|
events(): AsyncIterable<WindowEvent>;
|
|
107
114
|
/** Resolves when the window ends, with why. */
|
|
108
115
|
outcome(): Promise<WindowOutcome>;
|
|
109
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* What a window is writing while it runs, one delta at a time
|
|
119
|
+
* (docs/windows.md#streaming).
|
|
120
|
+
*
|
|
121
|
+
* Streams are best-effort and ephemeral: they are not journaled, and the
|
|
122
|
+
* journal's durable events stay the only record. A backend that cannot report
|
|
123
|
+
* deltas never emits them, and a consumer that misses one keeps going - the
|
|
124
|
+
* next delta carries what stands so far.
|
|
125
|
+
*/
|
|
126
|
+
export type StreamEvent =
|
|
127
|
+
/** Assistant text, as the model writes it. */
|
|
128
|
+
{
|
|
129
|
+
kind: "text_delta";
|
|
130
|
+
delta: string;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* One tool call's arguments, as the model writes them. `call.args` is what
|
|
134
|
+
* has been decoded so far and is best-effort: a call still being written is
|
|
135
|
+
* not valid JSON yet, so a string argument holds the characters written so
|
|
136
|
+
* far - which is what makes `send`'s content visible while the model is
|
|
137
|
+
* still writing it (docs/tools.md#direct-tools). The durable `tool_call`
|
|
138
|
+
* event carries the complete arguments, once.
|
|
139
|
+
*/
|
|
140
|
+
| {
|
|
141
|
+
kind: "tool_call_delta";
|
|
142
|
+
call: ToolCall;
|
|
143
|
+
};
|
|
110
144
|
/** What a backend streams while a window runs. */
|
|
111
|
-
export type WindowEvent = {
|
|
145
|
+
export type WindowEvent = StreamEvent | {
|
|
112
146
|
kind: "tool_call";
|
|
113
147
|
call: ToolCall;
|
|
114
148
|
} | {
|
|
@@ -153,6 +187,14 @@ export interface AgentBackend {
|
|
|
153
187
|
* reports nothing is never limited.
|
|
154
188
|
*/
|
|
155
189
|
rateLimits?(): Promise<RateLimitState> | RateLimitState;
|
|
190
|
+
/**
|
|
191
|
+
* Direct tools the backend injects from outside arbor: the tools an outside
|
|
192
|
+
* harness registered, which a host embedded in that harness hands in here
|
|
193
|
+
* (docs/tools.md#external-tools). They are declared to the model and called
|
|
194
|
+
* in the turn like any other direct tool. Read once, before the first window
|
|
195
|
+
* opens.
|
|
196
|
+
*/
|
|
197
|
+
externalTools?(): readonly ExternalTool[] | Promise<readonly ExternalTool[]>;
|
|
156
198
|
/** Open one window and start its first turn. */
|
|
157
199
|
open(request: OpenWindowRequest): Promise<WindowSession>;
|
|
158
200
|
}
|
|
@@ -162,6 +204,12 @@ export interface DirectToolSpec {
|
|
|
162
204
|
/** What it does and when to use it; side effects. 1 to 4 sentences. */
|
|
163
205
|
description: string;
|
|
164
206
|
parameters: Record<string, ParameterSpec>;
|
|
207
|
+
/**
|
|
208
|
+
* Who declares it, when it is not the core's own: a plugin's name, or a
|
|
209
|
+
* backend's. The tools list says where such a tool comes from, so the model
|
|
210
|
+
* knows whose it is (docs/tools.md#external-tools).
|
|
211
|
+
*/
|
|
212
|
+
source?: string;
|
|
165
213
|
}
|
|
166
214
|
/** Called from a script in `code_exec` (docs/tools.md#modules). */
|
|
167
215
|
export interface ModuleSpec {
|
|
@@ -193,6 +241,12 @@ export interface ToolResult {
|
|
|
193
241
|
ok: boolean;
|
|
194
242
|
/** Stable fields, bounded, in the core's error shape when it failed. */
|
|
195
243
|
content: string;
|
|
244
|
+
/**
|
|
245
|
+
* The tool that was called, as the prompt names it. A refusal carries it so
|
|
246
|
+
* the error line starts with the tool name; the catalog fills it on the way
|
|
247
|
+
* out, so a group that leaves it out still names the call.
|
|
248
|
+
*/
|
|
249
|
+
tool?: string;
|
|
196
250
|
error?: {
|
|
197
251
|
rule: string;
|
|
198
252
|
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
|
@@ -151,7 +151,7 @@ export interface TraceNode {
|
|
|
151
151
|
/** A target a window is behind on. */
|
|
152
152
|
export interface TargetGap {
|
|
153
153
|
target: Target;
|
|
154
|
-
/** The newest event on the target. */
|
|
154
|
+
/** The newest event on the target another window wrote after this one opened. */
|
|
155
155
|
latest: number;
|
|
156
156
|
/** The newest event on the target the window has seen. */
|
|
157
157
|
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
|
@@ -12,18 +12,18 @@ export type { BlockValue, Block, BlockKind, BlockFieldSpec, BlockKindSpec, TextT
|
|
|
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
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";
|
|
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";
|
|
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 } 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/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": "
|
|
3
|
+
"version": "2026.1106.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": "
|
|
69
|
+
"@4fu/arbor-errors": "2026.1106.1"
|
|
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 } 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,13 @@ 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
|
+
tools?: readonly ExternalTool[];
|
|
48
73
|
hooks?: Hooks;
|
|
49
74
|
promptSection?: PromptSection;
|
|
50
75
|
/** 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/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,8 +4,9 @@
|
|
|
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";
|
|
11
12
|
import type { EventRef, WindowRef } from "./refs.ts";
|
|
@@ -31,3 +32,22 @@ export interface ToolGroup {
|
|
|
31
32
|
call(request: ToolCall, context: CallContext): Promise<ToolResult>;
|
|
32
33
|
}
|
|
33
34
|
export type { DirectToolSpec, FunctionSpec, ModuleSpec, ParameterSpec, ToolCall, ToolResult };
|
|
35
|
+
/**
|
|
36
|
+
* One direct tool declared from outside the core: a plugin's own, or a
|
|
37
|
+
* backend's injection of the tools an outside harness registered
|
|
38
|
+
* (docs/tools.md#external-tools).
|
|
39
|
+
*
|
|
40
|
+
* One shape for both, so a plugin and a backend share one path instead of two:
|
|
41
|
+
* the host registers what it is handed, and from there the tool is a direct
|
|
42
|
+
* tool like any other - declared to the model under its own name, and called
|
|
43
|
+
* through the same hooks, journaling, scrub and result cut as the core's own.
|
|
44
|
+
* Nothing about it is special-cased after registration.
|
|
45
|
+
*/
|
|
46
|
+
export interface ExternalTool {
|
|
47
|
+
readonly name: string;
|
|
48
|
+
/** What it does and when to use it; side effects. 1 to 4 sentences. */
|
|
49
|
+
readonly description: string;
|
|
50
|
+
readonly parameters: Record<string, ParameterSpec>;
|
|
51
|
+
/** Run one call. The same path a core tool takes. */
|
|
52
|
+
execute(request: ToolCall, context: CallContext): Promise<ToolResult>;
|
|
53
|
+
}
|
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
|
}
|