@4fu/arbor-interfaces 0.0.0-stage → 2026.1106.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/LICENSE +21 -0
- package/README.md +187 -2
- package/agent.d.ts +202 -0
- package/agent.js +11 -0
- package/blocks.d.ts +56 -0
- package/blocks.js +11 -0
- package/channel.d.ts +61 -0
- package/channel.js +11 -0
- package/effects.d.ts +42 -0
- package/effects.js +11 -0
- package/environment.d.ts +109 -0
- package/environment.js +16 -0
- package/events.d.ts +162 -0
- package/events.js +4 -0
- package/hooks.d.ts +141 -0
- package/hooks.js +10 -0
- package/index.d.ts +29 -0
- package/index.js +10 -0
- package/journal.d.ts +87 -0
- package/journal.js +16 -0
- package/judge.d.ts +206 -0
- package/judge.js +13 -0
- package/notes.d.ts +215 -0
- package/notes.js +5 -0
- package/package.json +69 -4
- package/plugin.d.ts +52 -0
- package/plugin.js +13 -0
- package/refs.d.ts +49 -0
- package/refs.js +9 -0
- package/scheduler.d.ts +132 -0
- package/scheduler.js +21 -0
- package/settings.d.ts +64 -0
- package/settings.js +9 -0
- package/supervisor.d.ts +84 -0
- package/supervisor.js +11 -0
- package/task.d.ts +46 -0
- package/task.js +10 -0
- package/tools.d.ts +33 -0
- package/tools.js +10 -0
- package/windows.d.ts +81 -0
- package/windows.js +8 -0
- package/workspace.d.ts +113 -0
- package/workspace.js +9 -0
package/scheduler.d.ts
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `Scheduler` interface: insertion timing, backpressure and scheduled
|
|
3
|
+
* tasks (docs/architecture.md#interfaces, docs/dispatch.md).
|
|
4
|
+
*
|
|
5
|
+
* The model sees only input blocks; it never manages timing. The rules below
|
|
6
|
+
* decide when the host calls the backend's steering:
|
|
7
|
+
*
|
|
8
|
+
* - Protection period: when a window starts on a target, only inputs for that
|
|
9
|
+
* target enter during it; inputs for the same target do not restart it.
|
|
10
|
+
* Environment deltas and conversation updates are not held back.
|
|
11
|
+
* - After it: an input for the current target that is still being batched goes
|
|
12
|
+
* in first; then the others, by priority and age.
|
|
13
|
+
* - No insertion after the final usage reminder; waiting inputs are dispatched
|
|
14
|
+
* again.
|
|
15
|
+
* - Paused: nothing is inserted.
|
|
16
|
+
*
|
|
17
|
+
* Backpressure: there is no cap on windows. How many run at once is bounded by
|
|
18
|
+
* the backend, so when it reports rate limits new windows wait in a queue
|
|
19
|
+
* ordered by priority and age.
|
|
20
|
+
*/
|
|
21
|
+
import type { Delivery } from "./agent.ts";
|
|
22
|
+
import type { TaskInput } from "./task.ts";
|
|
23
|
+
import type { Target, TaskRef } from "./refs.ts";
|
|
24
|
+
import type { WindowRuntimeState } from "./windows.ts";
|
|
25
|
+
/** One input waiting to enter a window. */
|
|
26
|
+
export interface QueuedInput {
|
|
27
|
+
id: string;
|
|
28
|
+
kind: Delivery["kind"];
|
|
29
|
+
targets: readonly Target[];
|
|
30
|
+
/** Higher goes first. */
|
|
31
|
+
priority: number;
|
|
32
|
+
/** When it was queued. */
|
|
33
|
+
time: number;
|
|
34
|
+
/** Environment deltas and conversation updates are never held back. */
|
|
35
|
+
unfiltered?: boolean;
|
|
36
|
+
}
|
|
37
|
+
export interface InsertionRequest {
|
|
38
|
+
state: WindowRuntimeState;
|
|
39
|
+
queued: readonly QueuedInput[];
|
|
40
|
+
now: number;
|
|
41
|
+
}
|
|
42
|
+
export interface InsertionPlan {
|
|
43
|
+
/** Steer in now, at the next tool-call boundary. */
|
|
44
|
+
insert: readonly QueuedInput[];
|
|
45
|
+
/** Queue for after the run ends. */
|
|
46
|
+
followUp: readonly QueuedInput[];
|
|
47
|
+
/** Keep waiting. */
|
|
48
|
+
hold: readonly QueuedInput[];
|
|
49
|
+
/** Dispatch again: the window is past its final reminder. */
|
|
50
|
+
redispatch: readonly QueuedInput[];
|
|
51
|
+
}
|
|
52
|
+
export interface RateLimitState {
|
|
53
|
+
limited: boolean;
|
|
54
|
+
retryAfterMs?: number;
|
|
55
|
+
}
|
|
56
|
+
export interface AdmissionRequest {
|
|
57
|
+
running: number;
|
|
58
|
+
queued: readonly TaskInput[];
|
|
59
|
+
rateLimit?: RateLimitState;
|
|
60
|
+
now: number;
|
|
61
|
+
}
|
|
62
|
+
export interface AdmissionPlan {
|
|
63
|
+
/** How many new windows may open now. */
|
|
64
|
+
open: number;
|
|
65
|
+
/** Whether the rest must wait in the queue. */
|
|
66
|
+
wait: boolean;
|
|
67
|
+
/** When to ask again, in ms, while the backend is limited. */
|
|
68
|
+
retryAfterMs?: number;
|
|
69
|
+
reason?: string;
|
|
70
|
+
}
|
|
71
|
+
/** A daily skip period, `HH:MM` to `HH:MM`. */
|
|
72
|
+
export interface SkipPeriod {
|
|
73
|
+
from: string;
|
|
74
|
+
to: string;
|
|
75
|
+
}
|
|
76
|
+
export interface ScheduledTaskInput {
|
|
77
|
+
name?: string;
|
|
78
|
+
/** The first firing, in epoch milliseconds. */
|
|
79
|
+
firstAt: number;
|
|
80
|
+
repeat?: {
|
|
81
|
+
everyMs: number;
|
|
82
|
+
} | {
|
|
83
|
+
dailyAt: string;
|
|
84
|
+
};
|
|
85
|
+
skipDaily?: readonly SkipPeriod[];
|
|
86
|
+
targets?: readonly Target[];
|
|
87
|
+
task: Omit<TaskInput, "kind">;
|
|
88
|
+
}
|
|
89
|
+
export interface ScheduledTask {
|
|
90
|
+
ref: TaskRef;
|
|
91
|
+
name?: string;
|
|
92
|
+
firstAt: number;
|
|
93
|
+
nextAt: number;
|
|
94
|
+
lastAt?: number;
|
|
95
|
+
repeat?: {
|
|
96
|
+
everyMs: number;
|
|
97
|
+
} | {
|
|
98
|
+
dailyAt: string;
|
|
99
|
+
};
|
|
100
|
+
skipDaily?: readonly SkipPeriod[];
|
|
101
|
+
targets: readonly Target[];
|
|
102
|
+
paused: boolean;
|
|
103
|
+
/** What a firing dispatches, so a restart rebuilds the schedule. */
|
|
104
|
+
task?: Omit<TaskInput, "kind">;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* What a schedule's update may change: anything it was created with. A
|
|
108
|
+
* schedule's ref, its record of when it last fired and whether it is paused
|
|
109
|
+
* stay the host's own.
|
|
110
|
+
*/
|
|
111
|
+
export type ScheduledTaskUpdate = Partial<ScheduledTaskInput>;
|
|
112
|
+
export interface Scheduler {
|
|
113
|
+
/** Which waiting inputs may enter a running window now. */
|
|
114
|
+
insertion(request: InsertionRequest): Promise<InsertionPlan>;
|
|
115
|
+
/** How many new windows may open now, given the backend's rate limits. */
|
|
116
|
+
admission(request: AdmissionRequest): Promise<AdmissionPlan>;
|
|
117
|
+
schedule(input: ScheduledTaskInput): Promise<TaskRef>;
|
|
118
|
+
/**
|
|
119
|
+
* Change a recorded schedule: its name, its first time, its repeat rule, its
|
|
120
|
+
* skip periods, its targets or the task a firing dispatches. Returns the
|
|
121
|
+
* changed schedule, or null when the ref names none.
|
|
122
|
+
*/
|
|
123
|
+
update(ref: TaskRef, changes: ScheduledTaskUpdate): Promise<ScheduledTask | null>;
|
|
124
|
+
cancel(ref: TaskRef): Promise<void>;
|
|
125
|
+
list(): Promise<readonly ScheduledTask[]>;
|
|
126
|
+
/** Rebuild the scheduled tasks after a restart, from the journal's record. */
|
|
127
|
+
restore?(tasks: readonly ScheduledTask[]): Promise<void>;
|
|
128
|
+
/** Stop the loop, so a host that closes leaves no timer behind. */
|
|
129
|
+
close?(): void;
|
|
130
|
+
/** The tasks whose time has come, in the order they should be dispatched. */
|
|
131
|
+
due(now: number): Promise<readonly TaskInput[]>;
|
|
132
|
+
}
|
package/scheduler.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `Scheduler` interface: insertion timing, backpressure and scheduled
|
|
3
|
+
* tasks (docs/architecture.md#interfaces, docs/dispatch.md).
|
|
4
|
+
*
|
|
5
|
+
* The model sees only input blocks; it never manages timing. The rules below
|
|
6
|
+
* decide when the host calls the backend's steering:
|
|
7
|
+
*
|
|
8
|
+
* - Protection period: when a window starts on a target, only inputs for that
|
|
9
|
+
* target enter during it; inputs for the same target do not restart it.
|
|
10
|
+
* Environment deltas and conversation updates are not held back.
|
|
11
|
+
* - After it: an input for the current target that is still being batched goes
|
|
12
|
+
* in first; then the others, by priority and age.
|
|
13
|
+
* - No insertion after the final usage reminder; waiting inputs are dispatched
|
|
14
|
+
* again.
|
|
15
|
+
* - Paused: nothing is inserted.
|
|
16
|
+
*
|
|
17
|
+
* Backpressure: there is no cap on windows. How many run at once is bounded by
|
|
18
|
+
* the backend, so when it reports rate limits new windows wait in a queue
|
|
19
|
+
* ordered by priority and age.
|
|
20
|
+
*/
|
|
21
|
+
export {};
|
package/settings.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings: identity and general rules, judge modes, thresholds, model tiers
|
|
3
|
+
* and the limits the framework does enforce (docs/environment.md,
|
|
4
|
+
* docs/dispatch.md, docs/windows.md).
|
|
5
|
+
*
|
|
6
|
+
* The model may change all of it except the value of a secret. A setting names
|
|
7
|
+
* a secret and never holds its value.
|
|
8
|
+
*/
|
|
9
|
+
import type { JudgeDecisionKind, JudgeMode } from "./judge.ts";
|
|
10
|
+
export interface Settings {
|
|
11
|
+
/** `<identity>`; omitted when empty, with no default sentence. */
|
|
12
|
+
identity?: string;
|
|
13
|
+
/** `<agent>`: the general rules, one per line. */
|
|
14
|
+
generalRules?: readonly string[];
|
|
15
|
+
judge?: {
|
|
16
|
+
/** Each decision is `on` by default; each can be `off` or `shadow`. */
|
|
17
|
+
modes?: Partial<Record<JudgeDecisionKind, JudgeMode>>;
|
|
18
|
+
/** The bounded share of the active-set score the judge may add. */
|
|
19
|
+
activeSetJudgeShare?: number;
|
|
20
|
+
/** How many candidate windows the route decision may score. At most 8. */
|
|
21
|
+
maxCandidates?: number;
|
|
22
|
+
/**
|
|
23
|
+
* What each configured tier is for, as the `model tier` question reads it:
|
|
24
|
+
* one line of text per tier name (docs/dispatch.md#model-tiers). A tier
|
|
25
|
+
* named here with no text falls back to the judge's own description of it.
|
|
26
|
+
*/
|
|
27
|
+
tierCriteria?: Record<string, string>;
|
|
28
|
+
};
|
|
29
|
+
/** Each tier maps to a model from the backend's catalog. */
|
|
30
|
+
modelTiers?: Record<string, string>;
|
|
31
|
+
/** The tier a new window gets when the judge does not decide. */
|
|
32
|
+
defaultTier?: string;
|
|
33
|
+
window?: {
|
|
34
|
+
/** How long an ended run stays open for a related input. 300 s. */
|
|
35
|
+
idleSeconds?: number;
|
|
36
|
+
/** The context window's token budget. */
|
|
37
|
+
contextTokens?: number;
|
|
38
|
+
/** Tokens held back from the model. */
|
|
39
|
+
reserveTokens?: number;
|
|
40
|
+
/** How long a window protects the target it started on. 60 s. */
|
|
41
|
+
protectionSeconds?: number;
|
|
42
|
+
};
|
|
43
|
+
snapshot?: {
|
|
44
|
+
/** The system prompt and memory snapshot rebuild at most this often. 10 min. */
|
|
45
|
+
minIntervalMs?: number;
|
|
46
|
+
};
|
|
47
|
+
notes?: {
|
|
48
|
+
/** How many notes the active set may hold. */
|
|
49
|
+
activeCount?: number;
|
|
50
|
+
/** The active set's token budget, which bounds every prefix. */
|
|
51
|
+
activeTokenBudget?: number;
|
|
52
|
+
};
|
|
53
|
+
schedule?: {
|
|
54
|
+
/** Spread colliding repeating tasks apart, and their jitter. Off by default. */
|
|
55
|
+
spreadColliding?: boolean;
|
|
56
|
+
jitterMs?: number;
|
|
57
|
+
};
|
|
58
|
+
workspaces?: {
|
|
59
|
+
/** How long a chain's directory is kept after its last window ended, before the host reclaims it. */
|
|
60
|
+
retentionMs?: number;
|
|
61
|
+
};
|
|
62
|
+
/** The names of the secrets a configuration may use. Never their values. */
|
|
63
|
+
secrets?: readonly string[];
|
|
64
|
+
}
|
package/settings.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings: identity and general rules, judge modes, thresholds, model tiers
|
|
3
|
+
* and the limits the framework does enforce (docs/environment.md,
|
|
4
|
+
* docs/dispatch.md, docs/windows.md).
|
|
5
|
+
*
|
|
6
|
+
* The model may change all of it except the value of a secret. A setting names
|
|
7
|
+
* a secret and never holds its value.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
package/supervisor.d.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `Supervisor` interface: run services - start, health checks, restart with
|
|
3
|
+
* backoff, logs, quotas (docs/architecture.md#interfaces,
|
|
4
|
+
* docs/environment.md#services).
|
|
5
|
+
*
|
|
6
|
+
* A service is an application that keeps running after the window that set it
|
|
7
|
+
* up has ended. The framework gives supervision, a directory of its own, logs
|
|
8
|
+
* and secrets injected by name; the agent writes the application and
|
|
9
|
+
* `svc.json`, and decides when to stop or remove it.
|
|
10
|
+
*/
|
|
11
|
+
import type { Ref } from "./refs.ts";
|
|
12
|
+
import type { Page } from "./events.ts";
|
|
13
|
+
import type { ArborErrorInfo } from "@4fu/arbor-errors";
|
|
14
|
+
export type ServiceState = "starting" | "running" | "unhealthy" | "stopped" | "failed";
|
|
15
|
+
/** One service's configuration, from its `svc.json`. */
|
|
16
|
+
export interface ServiceSpec {
|
|
17
|
+
name: string;
|
|
18
|
+
command: string;
|
|
19
|
+
args?: readonly string[];
|
|
20
|
+
ports?: readonly number[];
|
|
21
|
+
/** The health check `reload` waits for before the item is installed. */
|
|
22
|
+
healthCheck?: {
|
|
23
|
+
path?: string;
|
|
24
|
+
port?: number;
|
|
25
|
+
intervalMs?: number;
|
|
26
|
+
timeoutMs?: number;
|
|
27
|
+
};
|
|
28
|
+
/** The names of the secrets it uses; values are injected by name. */
|
|
29
|
+
secrets?: readonly string[];
|
|
30
|
+
restart?: {
|
|
31
|
+
maxRetries?: number;
|
|
32
|
+
backoffMs?: number;
|
|
33
|
+
};
|
|
34
|
+
/** Resource quotas the supervisor applies. */
|
|
35
|
+
quota?: {
|
|
36
|
+
memoryBytes?: number;
|
|
37
|
+
cpuShares?: number;
|
|
38
|
+
};
|
|
39
|
+
env?: Record<string, string>;
|
|
40
|
+
/**
|
|
41
|
+
* The directory the service owns, which the command runs in and which no
|
|
42
|
+
* window ends or reclaims. The environment forks it from the draft.
|
|
43
|
+
*/
|
|
44
|
+
dir?: string;
|
|
45
|
+
}
|
|
46
|
+
export interface ServiceHandle {
|
|
47
|
+
name: string;
|
|
48
|
+
revision: number;
|
|
49
|
+
state: ServiceState;
|
|
50
|
+
}
|
|
51
|
+
export interface ServiceStatus {
|
|
52
|
+
name: string;
|
|
53
|
+
revision: number;
|
|
54
|
+
state: ServiceState;
|
|
55
|
+
restarts: number;
|
|
56
|
+
since: number;
|
|
57
|
+
ports: readonly number[];
|
|
58
|
+
lastError?: ArborErrorInfo;
|
|
59
|
+
/** The events that recorded its state changes. */
|
|
60
|
+
refs: readonly Ref[];
|
|
61
|
+
}
|
|
62
|
+
export interface LogOptions {
|
|
63
|
+
/** Follow the log's tail. */
|
|
64
|
+
tail?: number;
|
|
65
|
+
since?: number;
|
|
66
|
+
limit?: number;
|
|
67
|
+
}
|
|
68
|
+
export interface LogPage extends Page<string> {
|
|
69
|
+
name: string;
|
|
70
|
+
}
|
|
71
|
+
export interface Supervisor {
|
|
72
|
+
/** Start a service and wait for its health check. */
|
|
73
|
+
start(spec: ServiceSpec): Promise<ServiceHandle>;
|
|
74
|
+
status(name: string): Promise<ServiceStatus>;
|
|
75
|
+
stop(name: string): Promise<void>;
|
|
76
|
+
restart(name: string): Promise<ServiceHandle>;
|
|
77
|
+
logs(name: string, options?: LogOptions): Promise<LogPage>;
|
|
78
|
+
/**
|
|
79
|
+
* Report every state change, so the host records it in the journal and runs
|
|
80
|
+
* its hooks (docs/environment.md#services). Optional: an implementation that
|
|
81
|
+
* reports no changes leaves service failures to `status`.
|
|
82
|
+
*/
|
|
83
|
+
onStateChange?(handler: (status: ServiceStatus) => void | Promise<void>): void;
|
|
84
|
+
}
|
package/supervisor.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `Supervisor` interface: run services - start, health checks, restart with
|
|
3
|
+
* backoff, logs, quotas (docs/architecture.md#interfaces,
|
|
4
|
+
* docs/environment.md#services).
|
|
5
|
+
*
|
|
6
|
+
* A service is an application that keeps running after the window that set it
|
|
7
|
+
* up has ended. The framework gives supervision, a directory of its own, logs
|
|
8
|
+
* and secrets injected by name; the agent writes the application and
|
|
9
|
+
* `svc.json`, and decides when to stop or remove it.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
package/task.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `TaskSource` interface: turn outside events into tasks, batching them
|
|
3
|
+
* (docs/architecture.md#interfaces, docs/dispatch.md#tasks).
|
|
4
|
+
*
|
|
5
|
+
* A task is one unit of input: a batch of outside events, a scheduled firing,
|
|
6
|
+
* or a service event. Batching is the source's business - a chat source
|
|
7
|
+
* debounces per conversation, so several messages sent in a row become one
|
|
8
|
+
* task. The host makes no tasks of its own for maintenance.
|
|
9
|
+
*/
|
|
10
|
+
import type { Block } from "./blocks.ts";
|
|
11
|
+
import type { OutsideEvent, Unsubscribe } from "./channel.ts";
|
|
12
|
+
import type { EventRef, Ref, Target, TaskRef } from "./refs.ts";
|
|
13
|
+
export type TaskKind = "batch" | "scheduled" | "service";
|
|
14
|
+
/** What a source hands the host. The host assigns the ref. */
|
|
15
|
+
export interface TaskInput {
|
|
16
|
+
kind: TaskKind;
|
|
17
|
+
/** Why it is a task, for the board and the judge. */
|
|
18
|
+
reason: string;
|
|
19
|
+
targets: readonly Target[];
|
|
20
|
+
time: number;
|
|
21
|
+
content: readonly Block[];
|
|
22
|
+
/** The outside events it batches. */
|
|
23
|
+
events?: readonly OutsideEvent[];
|
|
24
|
+
/** The source that made it. */
|
|
25
|
+
source?: string;
|
|
26
|
+
/** Higher goes first when insertion timing orders waiting inputs. */
|
|
27
|
+
priority?: number;
|
|
28
|
+
/**
|
|
29
|
+
* The batch names the agent directly, so `filter` always passes it. The
|
|
30
|
+
* source knows this; the host does not guess it (docs/dispatch.md#routing).
|
|
31
|
+
*/
|
|
32
|
+
explicit?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface Task extends TaskInput {
|
|
35
|
+
ref: TaskRef;
|
|
36
|
+
/** The events the batch was made of, once they are in the journal. */
|
|
37
|
+
eventRefs: readonly EventRef[];
|
|
38
|
+
refs: readonly Ref[];
|
|
39
|
+
}
|
|
40
|
+
export interface TaskSource {
|
|
41
|
+
readonly id: string;
|
|
42
|
+
/** Turn outside events into tasks, batched the way this source batches. */
|
|
43
|
+
toTasks(events: readonly OutsideEvent[]): readonly TaskInput[];
|
|
44
|
+
/** Push source. The host may poll instead. */
|
|
45
|
+
subscribe?(handler: (events: readonly OutsideEvent[]) => void | Promise<void>): Unsubscribe;
|
|
46
|
+
}
|
package/task.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `TaskSource` interface: turn outside events into tasks, batching them
|
|
3
|
+
* (docs/architecture.md#interfaces, docs/dispatch.md#tasks).
|
|
4
|
+
*
|
|
5
|
+
* A task is one unit of input: a batch of outside events, a scheduled firing,
|
|
6
|
+
* or a service event. Batching is the source's business - a chat source
|
|
7
|
+
* debounces per conversation, so several messages sent in a row become one
|
|
8
|
+
* task. The host makes no tasks of its own for maintenance.
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
package/tools.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `ToolGroup` interface: a set of direct tools and code mode modules
|
|
3
|
+
* (docs/architecture.md#interfaces, docs/tools.md).
|
|
4
|
+
*
|
|
5
|
+
* Decisions and plain commands are direct tools, declared to the model and
|
|
6
|
+
* called in the turn. Everything else is a module function, called from a
|
|
7
|
+
* script in `code_exec`. Plugins add no tools of their own; what the model can
|
|
8
|
+
* do with a plugin is an MCP server and skills in `/env`.
|
|
9
|
+
*/
|
|
10
|
+
import type { DirectToolSpec, FunctionSpec, ModuleSpec, ParameterSpec, ToolCall, ToolResult } from "./agent.ts";
|
|
11
|
+
import type { EventRef, WindowRef } from "./refs.ts";
|
|
12
|
+
import type { WorkspaceMount } from "./workspace.ts";
|
|
13
|
+
/** What a call is made in. The service layer fills it; a group never reaches past it. */
|
|
14
|
+
export interface CallContext {
|
|
15
|
+
window: WindowRef;
|
|
16
|
+
task?: string;
|
|
17
|
+
/** The window's mounted workspace, when it has one. */
|
|
18
|
+
workspace?: WorkspaceMount;
|
|
19
|
+
/** The environment revision frozen when the window opened. */
|
|
20
|
+
envRevision?: string;
|
|
21
|
+
/** The event this call is recorded at. */
|
|
22
|
+
call?: EventRef;
|
|
23
|
+
}
|
|
24
|
+
export interface ToolGroup {
|
|
25
|
+
readonly id: string;
|
|
26
|
+
/** The direct tools this group declares to the model. */
|
|
27
|
+
tools(): readonly DirectToolSpec[];
|
|
28
|
+
/** The modules this group contributes, one line each in the prompt. */
|
|
29
|
+
modules(): readonly ModuleSpec[];
|
|
30
|
+
/** Run one call. The same path as a direct tool: same hooks, same errors. */
|
|
31
|
+
call(request: ToolCall, context: CallContext): Promise<ToolResult>;
|
|
32
|
+
}
|
|
33
|
+
export type { DirectToolSpec, FunctionSpec, ModuleSpec, ParameterSpec, ToolCall, ToolResult };
|
package/tools.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `ToolGroup` interface: a set of direct tools and code mode modules
|
|
3
|
+
* (docs/architecture.md#interfaces, docs/tools.md).
|
|
4
|
+
*
|
|
5
|
+
* Decisions and plain commands are direct tools, declared to the model and
|
|
6
|
+
* called in the turn. Everything else is a module function, called from a
|
|
7
|
+
* script in `code_exec`. Plugins add no tools of their own; what the model can
|
|
8
|
+
* do with a plugin is an MCP server and skills in `/env`.
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
package/windows.d.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Window shapes (docs/windows.md) and the live view the host keeps of them
|
|
3
|
+
* (docs/coordination.md#the-board).
|
|
4
|
+
*
|
|
5
|
+
* A window is one context window: a model run over one prompt. Windows are
|
|
6
|
+
* disposable; none of them owns a conversation, a file or a task.
|
|
7
|
+
*/
|
|
8
|
+
import type { Block } from "./blocks.ts";
|
|
9
|
+
import type { Ref, Target, TaskRef, WindowRef } from "./refs.ts";
|
|
10
|
+
export type WindowState = "running" | "idle" | "ended" | "paused";
|
|
11
|
+
/** What the host knows about a live window, for the board and for dispatch. */
|
|
12
|
+
export interface WindowRuntimeState {
|
|
13
|
+
window: WindowRef;
|
|
14
|
+
state: WindowState;
|
|
15
|
+
/** When the window opened. */
|
|
16
|
+
startedAt: number;
|
|
17
|
+
/** The task it was opened for. */
|
|
18
|
+
task?: TaskRef;
|
|
19
|
+
/** The targets it has touched. */
|
|
20
|
+
targets: readonly Target[];
|
|
21
|
+
/** The target it is working on now, for the protection period. */
|
|
22
|
+
currentTarget?: Target;
|
|
23
|
+
/** Insertion is paused. */
|
|
24
|
+
paused: boolean;
|
|
25
|
+
/** The protection period's end, in epoch milliseconds. */
|
|
26
|
+
protectionUntil: number | null;
|
|
27
|
+
/** The final usage reminder has been sent, so nothing more is inserted. */
|
|
28
|
+
finalReminderSent: boolean;
|
|
29
|
+
usage: TokenUsage;
|
|
30
|
+
}
|
|
31
|
+
export interface TokenUsage {
|
|
32
|
+
/** Tokens used in this window. */
|
|
33
|
+
used: number;
|
|
34
|
+
/** The context window's token budget. */
|
|
35
|
+
limit: number;
|
|
36
|
+
/** Tokens held back from the model. */
|
|
37
|
+
reserve: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A handoff: the cost of ending a window with unfinished work. Each part
|
|
41
|
+
* carries refs; the details belong in notes and the journal.
|
|
42
|
+
*/
|
|
43
|
+
export interface Handoff {
|
|
44
|
+
topics: readonly string[];
|
|
45
|
+
progress: readonly string[];
|
|
46
|
+
pending: readonly string[];
|
|
47
|
+
agreements: readonly string[];
|
|
48
|
+
refs: readonly Ref[];
|
|
49
|
+
}
|
|
50
|
+
/** One line on the board, per live window. */
|
|
51
|
+
export interface BoardLine {
|
|
52
|
+
window: WindowRef;
|
|
53
|
+
/** The first task's title, until the model sets a line with `board.set`. */
|
|
54
|
+
doing: string;
|
|
55
|
+
targets: readonly Target[];
|
|
56
|
+
/** What the window expects to be routed to it. */
|
|
57
|
+
expects: readonly Expectation[];
|
|
58
|
+
state: WindowState;
|
|
59
|
+
usage: TokenUsage;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* What a window expects to arrive, so the input reaches it instead of a new
|
|
63
|
+
* window. An exact expectation is a rule; a described one joins the window's
|
|
64
|
+
* profile for the judge.
|
|
65
|
+
*/
|
|
66
|
+
export interface Expectation {
|
|
67
|
+
kind: "exact" | "described";
|
|
68
|
+
/** An outside item, thread or whole conversation, for an exact expectation. */
|
|
69
|
+
item?: Target;
|
|
70
|
+
/** One sentence, for a described expectation. */
|
|
71
|
+
description?: string;
|
|
72
|
+
ref: Ref;
|
|
73
|
+
}
|
|
74
|
+
/** One block entering a window, in the shape the prompt layout uses. */
|
|
75
|
+
export interface InputBlock {
|
|
76
|
+
kind: string;
|
|
77
|
+
window?: WindowRef;
|
|
78
|
+
time: number;
|
|
79
|
+
refs: readonly Ref[];
|
|
80
|
+
content: readonly Block[];
|
|
81
|
+
}
|
package/windows.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Window shapes (docs/windows.md) and the live view the host keeps of them
|
|
3
|
+
* (docs/coordination.md#the-board).
|
|
4
|
+
*
|
|
5
|
+
* A window is one context window: a model run over one prompt. Windows are
|
|
6
|
+
* disposable; none of them owns a conversation, a file or a task.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
package/workspace.d.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `Workspace` interface: a directory per window (docs/workspaces.md).
|
|
3
|
+
*
|
|
4
|
+
* A workspace is a plain directory, not bound to any repository: the framework
|
|
5
|
+
* gives isolation and continuity, and the agent does the rest. The default is
|
|
6
|
+
* a plain directory under one root, which needs nothing a host does not
|
|
7
|
+
* already have and runs the same way on Windows, Linux and macOS.
|
|
8
|
+
*/
|
|
9
|
+
import type { TextTable } from "./blocks.ts";
|
|
10
|
+
import type { WindowRef, WorkspaceRef } from "./refs.ts";
|
|
11
|
+
import type { ToolGroup } from "./tools.ts";
|
|
12
|
+
/** The result of one command run in a mounted workspace. */
|
|
13
|
+
export interface ExecResult {
|
|
14
|
+
exitCode: number;
|
|
15
|
+
stdout: string;
|
|
16
|
+
stderr: string;
|
|
17
|
+
/** The streams were cut to fit a bound. An implementation that never cuts them reports `false`. */
|
|
18
|
+
truncated: boolean;
|
|
19
|
+
}
|
|
20
|
+
/** One file made visible inside a mounted workspace's execution environment. */
|
|
21
|
+
export interface StagedFile {
|
|
22
|
+
/** The path under the prefix, such as `skills/deploy/SKILL.md`. */
|
|
23
|
+
path: string;
|
|
24
|
+
/** The file's text. */
|
|
25
|
+
text: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A mounted workspace: a path in the host's filesystem, released when done.
|
|
29
|
+
*
|
|
30
|
+
* `path` is where the workspace's files are. It is a host path for an
|
|
31
|
+
* implementation that mounts a directory on the host, and the mounted
|
|
32
|
+
* directory of an implementation that owns its own execution environment, so a
|
|
33
|
+
* command runs there through `exec`, not on the host.
|
|
34
|
+
*/
|
|
35
|
+
export interface WorkspaceMount {
|
|
36
|
+
ref: WorkspaceRef;
|
|
37
|
+
path: string;
|
|
38
|
+
/**
|
|
39
|
+
* Run one command in the workspace's directory. Present when the
|
|
40
|
+
* implementation owns the execution environment; a host-directory
|
|
41
|
+
* implementation leaves it out and the caller runs the command itself in
|
|
42
|
+
* `path`.
|
|
43
|
+
*/
|
|
44
|
+
exec?(command: string): Promise<ExecResult>;
|
|
45
|
+
/**
|
|
46
|
+
* Read one file in the workspace as UTF-8 text, or `null` when it does not
|
|
47
|
+
* exist. Present when the implementation can hand a file to the host; an
|
|
48
|
+
* implementation whose `path` is not a host path implements it over its own
|
|
49
|
+
* transport. A caller falls back to reading `path` on the host when it is
|
|
50
|
+
* absent.
|
|
51
|
+
*/
|
|
52
|
+
readFile?(path: string): Promise<string | null>;
|
|
53
|
+
/**
|
|
54
|
+
* Write one file in the workspace as UTF-8 text, creating its directory.
|
|
55
|
+
* The same fallback rule as `readFile` applies.
|
|
56
|
+
*/
|
|
57
|
+
writeFile?(path: string, text: string): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Make files visible inside the execution environment, and answer the path
|
|
60
|
+
* they are visible at - which may be `prefix` itself, or somewhere else the
|
|
61
|
+
* implementation owns.
|
|
62
|
+
*
|
|
63
|
+
* For the shared environment, which a window reads at `/env` and which lives
|
|
64
|
+
* on the host (docs/environment.md#layout): an implementation whose `path` is
|
|
65
|
+
* not a host path stages the files into its own execution environment, and
|
|
66
|
+
* the caller rewrites the prefix onto what this answers. A host-directory
|
|
67
|
+
* implementation leaves it out, because its `path` already reaches the
|
|
68
|
+
* directory.
|
|
69
|
+
*
|
|
70
|
+
* What is staged is not the workspace's own: it sits outside its tree. An
|
|
71
|
+
* implementation stages it read-only, because a change goes through the
|
|
72
|
+
* environment's own draft and `reload`.
|
|
73
|
+
*/
|
|
74
|
+
stage?(prefix: string, files: readonly StagedFile[]): Promise<string>;
|
|
75
|
+
release(): Promise<void>;
|
|
76
|
+
}
|
|
77
|
+
export interface WorkspaceQuota {
|
|
78
|
+
bytes: number;
|
|
79
|
+
used: number;
|
|
80
|
+
files: number;
|
|
81
|
+
}
|
|
82
|
+
export interface Workspace {
|
|
83
|
+
/**
|
|
84
|
+
* Create the window's own directory, on first use. A window that never
|
|
85
|
+
* touches files has none.
|
|
86
|
+
*/
|
|
87
|
+
create(window: WindowRef): Promise<WorkspaceRef>;
|
|
88
|
+
/** Mount a workspace, read-write, at its own path. */
|
|
89
|
+
mount(ref: WorkspaceRef): Promise<WorkspaceMount>;
|
|
90
|
+
/** The workspace's size and file count, for a quota a deployment sets. */
|
|
91
|
+
quota(workspace: WorkspaceRef): Promise<WorkspaceQuota>;
|
|
92
|
+
/** Reclaim the workspace after the retention period. */
|
|
93
|
+
reclaim(workspace: WorkspaceRef): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* The workspace a window's chain already has, or `undefined` when it has
|
|
96
|
+
* none - the window never touched files, or the directory was reclaimed. The
|
|
97
|
+
* host asks for it to reclaim a chain's directory once its last window has
|
|
98
|
+
* ended and the retention period has passed, which a restart has to survive
|
|
99
|
+
* (docs/workspaces.md#lifecycle).
|
|
100
|
+
*/
|
|
101
|
+
refOf(window: WindowRef): Promise<WorkspaceRef | undefined>;
|
|
102
|
+
/**
|
|
103
|
+
* What this implementation adds to the catalog: tool groups built from the
|
|
104
|
+
* table the host renders with.
|
|
105
|
+
*
|
|
106
|
+
* A capability the core does not name arrives here. An implementation that
|
|
107
|
+
* can show another window's workspace declares its own module function or
|
|
108
|
+
* tool and its own prompt text this way, so the core knows nothing about it;
|
|
109
|
+
* a host whose implementation contributes nothing shows the model nothing
|
|
110
|
+
* about it (docs/workspaces.md, docs/architecture.md#plugins).
|
|
111
|
+
*/
|
|
112
|
+
groups?: (table: TextTable) => readonly ToolGroup[];
|
|
113
|
+
}
|
package/workspace.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `Workspace` interface: a directory per window (docs/workspaces.md).
|
|
3
|
+
*
|
|
4
|
+
* A workspace is a plain directory, not bound to any repository: the framework
|
|
5
|
+
* gives isolation and continuity, and the agent does the rest. The default is
|
|
6
|
+
* a plain directory under one root, which needs nothing a host does not
|
|
7
|
+
* already have and runs the same way on Windows, Linux and macOS.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|