@4fu/dsh-arbor-driver 0.0.0-stage → 2026.1110.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 +234 -2
- package/assistant-stream.d.ts +47 -0
- package/assistant-stream.js +98 -0
- package/driver.d.ts +212 -0
- package/driver.js +885 -0
- package/inbox.d.ts +55 -0
- package/inbox.js +160 -0
- package/index.d.ts +18 -0
- package/index.js +14 -0
- package/package.json +74 -4
- package/registry.d.ts +57 -0
- package/registry.js +95 -0
- package/relay.d.ts +51 -0
- package/relay.js +144 -0
- package/runtime-context.d.ts +49 -0
- package/runtime-context.js +86 -0
- package/session-agent.d.ts +142 -0
- package/session-agent.js +467 -0
package/relay.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The relay: arbor's journal onto the DSH session log.
|
|
3
|
+
*
|
|
4
|
+
* A window's own record lives in the arbor journal, and the user-visible one
|
|
5
|
+
* lives in the DSH session log. The driver mirrors the second from the first:
|
|
6
|
+
* while a turn runs, every tool call is recorded in the journal before it runs
|
|
7
|
+
* and its result after, and DSH appends the matching `tool/call` +
|
|
8
|
+
* `tool/result` pair to the session log. A process that stops in between
|
|
9
|
+
* leaves the journal holding a call the log never saw.
|
|
10
|
+
*
|
|
11
|
+
* On resume the relay reads the session's window chain out of the journal and
|
|
12
|
+
* appends the `tool/call` events the log is missing, so DSH's own
|
|
13
|
+
* `interruptedTurnClosers` can then close them with an outcome-unknown result
|
|
14
|
+
* instead of a not-started one — the model is told the call may have had side
|
|
15
|
+
* effects, which is the truth.
|
|
16
|
+
*
|
|
17
|
+
* The cursor is the DSH session log itself: the journal's calls are matched,
|
|
18
|
+
* in order, against the calls the log's assistant messages asked for, and
|
|
19
|
+
* whatever the log already carries is left alone. There is no second cursor to
|
|
20
|
+
* keep in step, and nothing is replayed twice.
|
|
21
|
+
*/
|
|
22
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
23
|
+
import { SessionSeq } from '@deepseek-ai/dsh-session';
|
|
24
|
+
import { MAX_PAGE_LIMIT } from '@4fu/arbor-interfaces';
|
|
25
|
+
/** Split a journal `tool_call` body (`name {args}`) into its two parts. */
|
|
26
|
+
function splitCall(body) {
|
|
27
|
+
const at = body.indexOf(' ');
|
|
28
|
+
if (at <= 0)
|
|
29
|
+
return undefined;
|
|
30
|
+
return { name: body.slice(0, at), args: body.slice(at + 1) };
|
|
31
|
+
}
|
|
32
|
+
/** Whether two argument strings carry the same JSON value. */
|
|
33
|
+
function sameArgs(left, right) {
|
|
34
|
+
try {
|
|
35
|
+
return isDeepStrictEqual(JSON.parse(left), JSON.parse(right));
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The tool calls one window chain recorded, in journal order.
|
|
43
|
+
*
|
|
44
|
+
* The chain is every window of one session: the first, its continuations, and
|
|
45
|
+
* the one a restart opened for it. A `tool_result` names the `tool_call` it
|
|
46
|
+
* answers in its refs, which is how a call is known to be settled.
|
|
47
|
+
*/
|
|
48
|
+
export async function windowToolCalls(journal, windows) {
|
|
49
|
+
const chain = new Set(windows);
|
|
50
|
+
const calls = [];
|
|
51
|
+
const index = new Map();
|
|
52
|
+
for (let offset = 0;; offset += MAX_PAGE_LIMIT) {
|
|
53
|
+
const page = await journal.list({ kinds: ['tool_call', 'tool_result'], limit: MAX_PAGE_LIMIT, offset });
|
|
54
|
+
for (const event of page.items) {
|
|
55
|
+
if (event.window === null || !chain.has(event.window))
|
|
56
|
+
continue;
|
|
57
|
+
if (event.kind === 'tool_call') {
|
|
58
|
+
const split = splitCall(event.body ?? '');
|
|
59
|
+
if (split === undefined)
|
|
60
|
+
continue;
|
|
61
|
+
index.set(event.ref, calls.length);
|
|
62
|
+
calls.push({ ...split, settled: false });
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
const answered = event.refs[0];
|
|
66
|
+
if (answered === undefined)
|
|
67
|
+
continue;
|
|
68
|
+
const at = index.get(answered);
|
|
69
|
+
if (at !== undefined)
|
|
70
|
+
calls[at] = { ...calls[at], settled: true };
|
|
71
|
+
}
|
|
72
|
+
if (page.items.length < MAX_PAGE_LIMIT)
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
return calls;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The `tool/call` events the arbor journal recorded and this log lost.
|
|
79
|
+
*
|
|
80
|
+
* Arbor runs the calls it recorded, in order, so they are the prefix of the
|
|
81
|
+
* calls the log's assistant messages asked for; a mismatch stops the replay
|
|
82
|
+
* rather than guessing. Calls the log already answers, and calls an
|
|
83
|
+
* interrupted attempt left behind (which arbor never ran), are left out.
|
|
84
|
+
*/
|
|
85
|
+
export function relayToolCalls(log, calls) {
|
|
86
|
+
const asked = [];
|
|
87
|
+
const answered = new Set();
|
|
88
|
+
const started = new Set();
|
|
89
|
+
for (const event of log) {
|
|
90
|
+
switch (event.type) {
|
|
91
|
+
case 'assistant/message':
|
|
92
|
+
// An interrupted attempt's blocks were never run, so they are not part
|
|
93
|
+
// of the order arbor's calls follow.
|
|
94
|
+
if (event.data.interrupted === true)
|
|
95
|
+
break;
|
|
96
|
+
for (const block of event.data.message.content) {
|
|
97
|
+
if (block.type !== 'tool-call')
|
|
98
|
+
continue;
|
|
99
|
+
asked.push({
|
|
100
|
+
callId: block.id,
|
|
101
|
+
name: block.name,
|
|
102
|
+
arguments: block.arguments,
|
|
103
|
+
turn: event.data.turn,
|
|
104
|
+
step: event.data.step,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
break;
|
|
108
|
+
case 'tool/call':
|
|
109
|
+
started.add(event.data.callId);
|
|
110
|
+
break;
|
|
111
|
+
case 'tool/result':
|
|
112
|
+
answered.add(event.data.message.toolCallId);
|
|
113
|
+
break;
|
|
114
|
+
default:
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const last = log.at(-1);
|
|
119
|
+
const relayed = [];
|
|
120
|
+
let seq = last?.seq ?? 0;
|
|
121
|
+
const time = last?.time ?? Date.now();
|
|
122
|
+
for (const [position, call] of calls.entries()) {
|
|
123
|
+
const wanted = asked[position];
|
|
124
|
+
if (wanted === undefined)
|
|
125
|
+
break;
|
|
126
|
+
if (wanted.name !== call.name || !sameArgs(wanted.arguments, call.args))
|
|
127
|
+
break;
|
|
128
|
+
// A call the log answers is complete, and one it has already recorded as
|
|
129
|
+
// started needs only the result DSH's own repair supplies: replaying
|
|
130
|
+
// either would duplicate an event the log already holds.
|
|
131
|
+
if (answered.has(wanted.callId) || started.has(wanted.callId))
|
|
132
|
+
continue;
|
|
133
|
+
const data = {
|
|
134
|
+
turn: wanted.turn,
|
|
135
|
+
step: wanted.step,
|
|
136
|
+
callId: wanted.callId,
|
|
137
|
+
name: wanted.name,
|
|
138
|
+
arguments: wanted.arguments,
|
|
139
|
+
};
|
|
140
|
+
seq += 1;
|
|
141
|
+
relayed.push({ type: 'tool/call', seq: SessionSeq(seq), time, data });
|
|
142
|
+
}
|
|
143
|
+
return relayed;
|
|
144
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-session runtime-context snapshot.
|
|
3
|
+
*
|
|
4
|
+
* Arbor's system prompt is frozen per epoch and shared by every window of that
|
|
5
|
+
* epoch, so a fact that belongs to one session cannot be written into a section:
|
|
6
|
+
* a section would tell every other session in the epoch the wrong value. The
|
|
7
|
+
* official loop solves the same problem with a snapshot message it appends at
|
|
8
|
+
* every step and replaces when it changes
|
|
9
|
+
* (`@deepseek-ai/dsh-agent-loop` RuntimeContextProjection), and this is that
|
|
10
|
+
* projection for the arbor path: the same source kind, the same "none" line,
|
|
11
|
+
* and the same rule that a message which is not exactly one text block is never
|
|
12
|
+
* treated as a retained snapshot, so a snapshot a window received before a
|
|
13
|
+
* restart is recognised as one on replay.
|
|
14
|
+
*
|
|
15
|
+
* What it carries is deliberately narrow — see
|
|
16
|
+
* {@link snapshotContextNames} in `@4fu/dsh-arbor-prompt`. Only a third party's
|
|
17
|
+
* runtime context travels here, because nothing else can carry per-session
|
|
18
|
+
* state from outside the framework. The approval policy and plan mode are
|
|
19
|
+
* written into the `<harness>` section instead, in dsh-arbor's own wording, and
|
|
20
|
+
* reach a running window as a `prompt_delta` carrying only the changed lines;
|
|
21
|
+
* the paths are arbor's own `<workspace>` section's.
|
|
22
|
+
*/
|
|
23
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
24
|
+
import type { ContextFormed } from '@deepseek-ai/dsh-llm';
|
|
25
|
+
import type { Session, UserMessage } from '@deepseek-ai/dsh-session';
|
|
26
|
+
declare module '@deepseek-ai/dsh-llm' {
|
|
27
|
+
interface MessageSourceMap {
|
|
28
|
+
'runtime-context': {
|
|
29
|
+
kind: 'runtime-context';
|
|
30
|
+
} & ContextFormed;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** Tracks the last retained runtime-context snapshot without owning its commit. */
|
|
34
|
+
export declare class RuntimeContextProjection {
|
|
35
|
+
/** `undefined` means no snapshot ever existed; `null` means none is retained. */
|
|
36
|
+
private retained;
|
|
37
|
+
/**
|
|
38
|
+
* Restore projection state once, then follow authoritative session events.
|
|
39
|
+
* @param ctx - agent-scoped event context
|
|
40
|
+
* @param session - session receiving projected messages
|
|
41
|
+
*/
|
|
42
|
+
constructor(ctx: Context, session: Session);
|
|
43
|
+
/**
|
|
44
|
+
* Create an uncommitted snapshot only when the retained value differs.
|
|
45
|
+
* @param current - fully rendered dynamic context
|
|
46
|
+
* @returns a candidate user message, or `undefined` when no update is needed
|
|
47
|
+
*/
|
|
48
|
+
project(current: string): UserMessage | undefined;
|
|
49
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-session runtime-context snapshot.
|
|
3
|
+
*
|
|
4
|
+
* Arbor's system prompt is frozen per epoch and shared by every window of that
|
|
5
|
+
* epoch, so a fact that belongs to one session cannot be written into a section:
|
|
6
|
+
* a section would tell every other session in the epoch the wrong value. The
|
|
7
|
+
* official loop solves the same problem with a snapshot message it appends at
|
|
8
|
+
* every step and replaces when it changes
|
|
9
|
+
* (`@deepseek-ai/dsh-agent-loop` RuntimeContextProjection), and this is that
|
|
10
|
+
* projection for the arbor path: the same source kind, the same "none" line,
|
|
11
|
+
* and the same rule that a message which is not exactly one text block is never
|
|
12
|
+
* treated as a retained snapshot, so a snapshot a window received before a
|
|
13
|
+
* restart is recognised as one on replay.
|
|
14
|
+
*
|
|
15
|
+
* What it carries is deliberately narrow — see
|
|
16
|
+
* {@link snapshotContextNames} in `@4fu/dsh-arbor-prompt`. Only a third party's
|
|
17
|
+
* runtime context travels here, because nothing else can carry per-session
|
|
18
|
+
* state from outside the framework. The approval policy and plan mode are
|
|
19
|
+
* written into the `<harness>` section instead, in dsh-arbor's own wording, and
|
|
20
|
+
* reach a running window as a `prompt_delta` carrying only the changed lines;
|
|
21
|
+
* the paths are arbor's own `<workspace>` section's.
|
|
22
|
+
*/
|
|
23
|
+
import { createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
24
|
+
/** The fixed text an empty snapshot carries, so a cleared one supersedes too. */
|
|
25
|
+
const CLEARED = 'Current runtime context: none. Earlier runtime-context snapshots no longer apply.';
|
|
26
|
+
/** Whether one message is a snapshot this projection owns. */
|
|
27
|
+
function isOwned(message) {
|
|
28
|
+
return message.source.kind === 'runtime-context';
|
|
29
|
+
}
|
|
30
|
+
/** The text of a message that is exactly one text block, else `undefined`. */
|
|
31
|
+
function textOf(message) {
|
|
32
|
+
const [block] = message.content;
|
|
33
|
+
return message.content.length === 1 && block?.type === 'text' ? block.text : undefined;
|
|
34
|
+
}
|
|
35
|
+
/** Whether one event replaces a surface node the retained snapshot lives in. */
|
|
36
|
+
function isReplacementSurfaceEvent(event) {
|
|
37
|
+
return event.type === 'system/message' || event.type === 'user/message';
|
|
38
|
+
}
|
|
39
|
+
/** Tracks the last retained runtime-context snapshot without owning its commit. */
|
|
40
|
+
export class RuntimeContextProjection {
|
|
41
|
+
/** `undefined` means no snapshot ever existed; `null` means none is retained. */
|
|
42
|
+
retained;
|
|
43
|
+
/**
|
|
44
|
+
* Restore projection state once, then follow authoritative session events.
|
|
45
|
+
* @param ctx - agent-scoped event context
|
|
46
|
+
* @param session - session receiving projected messages
|
|
47
|
+
*/
|
|
48
|
+
constructor(ctx, session) {
|
|
49
|
+
const surface = new Set(session.surface.nodes);
|
|
50
|
+
for (const event of session.snapshotEvents().toReversed()) {
|
|
51
|
+
if (event.type !== 'user/message' || !isOwned(event.data))
|
|
52
|
+
continue;
|
|
53
|
+
this.retained ??= null;
|
|
54
|
+
if (surface.has(event.seq)) {
|
|
55
|
+
this.retained = { seq: event.seq, text: textOf(event.data) ?? '' };
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
ctx.on('session/event', (subject, event) => {
|
|
60
|
+
if (subject !== session)
|
|
61
|
+
return;
|
|
62
|
+
if (event.type === 'user/message' && isOwned(event.data)) {
|
|
63
|
+
this.retained = { seq: event.seq, text: textOf(event.data) ?? '' };
|
|
64
|
+
}
|
|
65
|
+
else if (this.retained && isReplacementSurfaceEvent(event) && event.sourceEventSeqs?.includes(this.retained.seq) === true) {
|
|
66
|
+
this.retained = null;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Create an uncommitted snapshot only when the retained value differs.
|
|
72
|
+
* @param current - fully rendered dynamic context
|
|
73
|
+
* @returns a candidate user message, or `undefined` when no update is needed
|
|
74
|
+
*/
|
|
75
|
+
project(current) {
|
|
76
|
+
if (this.retained === undefined && current.length === 0)
|
|
77
|
+
return;
|
|
78
|
+
const snapshot = current.length === 0 ? CLEARED : current;
|
|
79
|
+
if (this.retained?.text === snapshot)
|
|
80
|
+
return;
|
|
81
|
+
return createUserMessage({
|
|
82
|
+
content: [{ type: 'text', text: snapshot }],
|
|
83
|
+
source: { kind: 'runtime-context' },
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The arbor session agent: one DSH user session driven through one arbor
|
|
3
|
+
* window.
|
|
4
|
+
*
|
|
5
|
+
* It implements the full `Agent` contract that
|
|
6
|
+
* `packages/core/agent/src/runtime-types.ts` augments onto `Agent` — the same
|
|
7
|
+
* surface `ReactLoopAgent` implements — but the model call and every tool call
|
|
8
|
+
* run through the arbor window instead of the in-process loop:
|
|
9
|
+
*
|
|
10
|
+
* - `followup()`/`steer()`/`inject()` splice the durable inbox and wake the
|
|
11
|
+
* driver, exactly like the official loop (inbox.ts in dsh-agent-loop).
|
|
12
|
+
* - The driver runs turns and steps over the arbor window: `turn/start`,
|
|
13
|
+
* `step/start`, `user/message`, streamed `agent/assistant-stream` frames,
|
|
14
|
+
* `assistant/message`, `ctx.tools.execute` through arbor's tool path,
|
|
15
|
+
* `step/end`, `agent/turn-stopping`, `turn/end`.
|
|
16
|
+
* - Model requests are derived from the session log (`session.deriveMessages()`)
|
|
17
|
+
* exactly like `ReactLoopAgent.buildRequest`.
|
|
18
|
+
*/
|
|
19
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
20
|
+
import type { Agent, AgentCancelCause, AgentOptions, AgentStatus, CancelOptions, InboxTarget, ModelSelection, ModelSelectionProjectionState } from '@deepseek-ai/dsh-agent';
|
|
21
|
+
import { AgentModel } from '@deepseek-ai/dsh-agent';
|
|
22
|
+
import type { InboxState } from '@deepseek-ai/dsh-agent';
|
|
23
|
+
import type { StreamChunk, TokenUsage } from '@deepseek-ai/dsh-llm';
|
|
24
|
+
import type { ToolCallBlock } from '@deepseek-ai/dsh-llm';
|
|
25
|
+
import type { Session, SessionId, UserMessage } from '@deepseek-ai/dsh-session';
|
|
26
|
+
import type { Scope } from '@deepseek-ai/dsh-scope';
|
|
27
|
+
import type { ToolResult, WindowEvent } from '@4fu/arbor-interfaces';
|
|
28
|
+
import type { WindowRef } from '@4fu/arbor-interfaces';
|
|
29
|
+
import { ArborInbox } from './inbox.js';
|
|
30
|
+
/** One model call's inputs, supplied by the DSH-side session agent. */
|
|
31
|
+
export interface ModelCallInput {
|
|
32
|
+
/** The window whose frozen epoch this call runs under. */
|
|
33
|
+
readonly window: WindowRef;
|
|
34
|
+
/** The session agent whose log the request is derived from. */
|
|
35
|
+
readonly agent: Agent;
|
|
36
|
+
readonly selection: Readonly<ModelSelection>;
|
|
37
|
+
readonly maxTokens?: number;
|
|
38
|
+
readonly position: {
|
|
39
|
+
turn: number;
|
|
40
|
+
step: number;
|
|
41
|
+
};
|
|
42
|
+
readonly signal: AbortSignal;
|
|
43
|
+
/** One streamed chunk, for the durable attempt and the window's live output. */
|
|
44
|
+
readonly onChunk: (chunk: StreamChunk) => void;
|
|
45
|
+
}
|
|
46
|
+
/** One model call's settled facts. */
|
|
47
|
+
export interface ModelCallOutcome {
|
|
48
|
+
readonly usage?: TokenUsage;
|
|
49
|
+
readonly finish: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The arbor window this session drives. Implemented by the driver, which owns
|
|
53
|
+
* the Arbor host; the agent never imports arbor-core directly. Every method
|
|
54
|
+
* that touches the window takes its ref, because the session's window changes
|
|
55
|
+
* over its life: arbor ends a window that goes quiet and the next turn drives
|
|
56
|
+
* the window that follows it.
|
|
57
|
+
*/
|
|
58
|
+
export interface ArborWindowBridge {
|
|
59
|
+
/** Open the window on first use and return its ref. */
|
|
60
|
+
ensureWindow(request?: string): Promise<WindowRef>;
|
|
61
|
+
/** The system prompt rendered for this window's epoch. */
|
|
62
|
+
systemPrompt(window: WindowRef): string;
|
|
63
|
+
/** One model call through DSH's `ctx.llm`. */
|
|
64
|
+
modelCall(input: ModelCallInput): Promise<ModelCallOutcome>;
|
|
65
|
+
/** Run one model-produced tool call through arbor's tool path into `ctx.tools.execute`. */
|
|
66
|
+
runToolCall(call: ToolCallBlock, position: {
|
|
67
|
+
turn: number;
|
|
68
|
+
step: number;
|
|
69
|
+
}): Promise<ToolResult>;
|
|
70
|
+
/** Publish one window event to the arbor window. */
|
|
71
|
+
publish(window: WindowRef, event: WindowEvent): void;
|
|
72
|
+
/** Write a step's sends after its tool results have been appended. */
|
|
73
|
+
flushSends(): void;
|
|
74
|
+
/** The turn's work settled: the window's run ends and it goes idle. */
|
|
75
|
+
finishRun(window: WindowRef): void;
|
|
76
|
+
/** Release the session's hold on its window. */
|
|
77
|
+
close(): Promise<void>;
|
|
78
|
+
}
|
|
79
|
+
/** One DSH session driven through one arbor window. */
|
|
80
|
+
export declare class ArborSessionAgent implements Agent {
|
|
81
|
+
readonly model: AgentModel;
|
|
82
|
+
readonly inbox: ArborInbox;
|
|
83
|
+
readonly scope: Scope;
|
|
84
|
+
readonly ctx: Context;
|
|
85
|
+
private readonly dispatch;
|
|
86
|
+
private bridge;
|
|
87
|
+
private phase;
|
|
88
|
+
private activityDone;
|
|
89
|
+
private assistantStreamRevision;
|
|
90
|
+
private assistantAttemptCounter;
|
|
91
|
+
private systemPromptLogged;
|
|
92
|
+
private readonly loopCtx;
|
|
93
|
+
readonly id: SessionId;
|
|
94
|
+
readonly options: AgentOptions;
|
|
95
|
+
readonly session: Session;
|
|
96
|
+
/** The window the live step drives, so the turn can end its run. */
|
|
97
|
+
private currentWindow;
|
|
98
|
+
/**
|
|
99
|
+
* The session's runtime-context snapshot: the working directory and any
|
|
100
|
+
* third-party runtime context, appended when it changes and never rewritten
|
|
101
|
+
* into the frozen prefix.
|
|
102
|
+
*/
|
|
103
|
+
private readonly runtimeContext;
|
|
104
|
+
constructor(loopCtx: Context, id: SessionId, options: AgentOptions, session: Session, projections: {
|
|
105
|
+
readonly modelSelection: () => ModelSelectionProjectionState;
|
|
106
|
+
readonly inbox: () => InboxState;
|
|
107
|
+
}, lastTurn: number);
|
|
108
|
+
/** Attach the arbor window bridge; the driver calls this once the host exists. */
|
|
109
|
+
attachBridge(bridge: ArborWindowBridge): void;
|
|
110
|
+
get status(): AgentStatus;
|
|
111
|
+
/** The turn/step position the live model step logs its tool events at. */
|
|
112
|
+
currentLogPosition(): {
|
|
113
|
+
turn: number;
|
|
114
|
+
step: number;
|
|
115
|
+
} | undefined;
|
|
116
|
+
/** The live turn's abort signal, for tool execution. */
|
|
117
|
+
currentSignal(): AbortSignal | undefined;
|
|
118
|
+
private setPhase;
|
|
119
|
+
send(message: UserMessage, target: InboxTarget, wakeup: boolean): void;
|
|
120
|
+
followup(input: UserMessage): void;
|
|
121
|
+
steer(input: UserMessage): void;
|
|
122
|
+
inject(input: UserMessage): void;
|
|
123
|
+
cancel(cause: AgentCancelCause, options?: CancelOptions): void;
|
|
124
|
+
runMaintenance<T>(job: (signal: AbortSignal) => Promise<T>): Promise<T>;
|
|
125
|
+
private wakeDriver;
|
|
126
|
+
whenIdle(): Promise<void>;
|
|
127
|
+
private throwError;
|
|
128
|
+
private kick;
|
|
129
|
+
private turn;
|
|
130
|
+
/**
|
|
131
|
+
* The session's runtime-context snapshot for this step, or `undefined` when
|
|
132
|
+
* nothing changed since the last one.
|
|
133
|
+
*
|
|
134
|
+
* DSH's own assembly supplies the text, so the wording stays DSH's: the
|
|
135
|
+
* snapshot is the one the official loop would have appended for this session.
|
|
136
|
+
* Only a third party's context is carried — the paths are arbor's own
|
|
137
|
+
* `<workspace>` section's, and the policies are `<harness>`'s.
|
|
138
|
+
*/
|
|
139
|
+
private runtimeContextSnapshot;
|
|
140
|
+
/** One model call plus the tool executions it requested. */
|
|
141
|
+
private step;
|
|
142
|
+
}
|