@borgee/agents-host 0.2.84 → 0.2.97
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 +1 -1
- package/dist/agents-host.js +23 -5
- package/dist/background-runs.d.ts +6 -1
- package/dist/background-runs.js +56 -21
- package/dist/chat/chat-control-plane.d.ts +3 -1
- package/dist/chat/sdk-chat-control-plane.d.ts +6 -2
- package/dist/chat/sdk-chat-control-plane.js +18 -0
- package/dist/plugin-sdk.js +149 -21
- package/dist/plugin-sdk.js.map +3 -3
- package/dist/progress-to-activity.d.ts +3 -3
- package/dist/providers/claude/adapter.d.ts +3 -1
- package/dist/providers/claude/adapter.js +41 -3
- package/dist/providers/claude/background-run-observer.d.ts +32 -0
- package/dist/providers/claude/background-run-observer.js +381 -0
- package/dist/providers/claude/cli-client.d.ts +15 -1
- package/dist/providers/claude/cli-client.js +699 -69
- package/dist/providers/claude/foreground-handoff.d.ts +4 -0
- package/dist/providers/claude/foreground-handoff.js +45 -0
- package/dist/providers/claude/task-cancellation-protocol.d.ts +14 -0
- package/dist/providers/claude/task-cancellation-protocol.js +21 -0
- package/dist/providers/copilot/activity-metadata.d.ts +3 -0
- package/dist/providers/copilot/activity-metadata.js +19 -0
- package/dist/providers/copilot/cli-client.js +5 -0
- package/dist/providers/provider-adapter.d.ts +8 -0
- package/dist/providers/provider-adapter.js +4 -0
- package/dist/types.d.ts +8 -0
- package/dist/vendor/claude-agent-acp/LICENSE +191 -0
- package/dist/vendor/claude-agent-acp/NOTICE +8 -0
- package/dist/vendor/claude-agent-acp/dist/acp-agent.d.ts +1017 -0
- package/dist/vendor/claude-agent-acp/dist/acp-agent.d.ts.map +1 -0
- package/dist/vendor/claude-agent-acp/dist/acp-agent.js +6305 -0
- package/dist/vendor/claude-agent-acp/dist/borgee-foreground-handoff-bridge.js +322 -0
- package/dist/vendor/claude-agent-acp/dist/borgee-task-cancellation-bridge.js +101 -0
- package/dist/vendor/claude-agent-acp/dist/borgee-task-lifecycle-bridge.js +58 -0
- package/dist/vendor/claude-agent-acp/dist/elicitation.d.ts +130 -0
- package/dist/vendor/claude-agent-acp/dist/elicitation.d.ts.map +1 -0
- package/dist/vendor/claude-agent-acp/dist/elicitation.js +304 -0
- package/dist/vendor/claude-agent-acp/dist/index.d.ts +3 -0
- package/dist/vendor/claude-agent-acp/dist/index.d.ts.map +1 -0
- package/dist/vendor/claude-agent-acp/dist/index.js +75 -0
- package/dist/vendor/claude-agent-acp/dist/lib.d.ts +6 -0
- package/dist/vendor/claude-agent-acp/dist/lib.d.ts.map +1 -0
- package/dist/vendor/claude-agent-acp/dist/lib.js +5 -0
- package/dist/vendor/claude-agent-acp/dist/settings.d.ts +68 -0
- package/dist/vendor/claude-agent-acp/dist/settings.d.ts.map +1 -0
- package/dist/vendor/claude-agent-acp/dist/settings.js +185 -0
- package/dist/vendor/claude-agent-acp/dist/tools.d.ts +102 -0
- package/dist/vendor/claude-agent-acp/dist/tools.d.ts.map +1 -0
- package/dist/vendor/claude-agent-acp/dist/tools.js +1000 -0
- package/dist/vendor/claude-agent-acp/dist/utils.d.ts +16 -0
- package/dist/vendor/claude-agent-acp/dist/utils.d.ts.map +1 -0
- package/dist/vendor/claude-agent-acp/dist/utils.js +81 -0
- package/dist/vendor/claude-agent-acp/package.json +85 -0
- package/package.json +16 -12
package/README.md
CHANGED
|
@@ -479,7 +479,7 @@ successfully applied snapshot running.
|
|
|
479
479
|
Each Borgee channel is mapped 1:1 to a provider-native session while it stays
|
|
480
480
|
active:
|
|
481
481
|
|
|
482
|
-
- **Claude**: one persistent `@agentclientprotocol/claude-agent-acp` subprocess is shared by a single `AgentsHost`, with one ACP session per Borgee channel. Same-channel turns are serialized; different channels keep isolated ACP sessions. The real ACP session cwd now comes from the resolved channel working folder `~/.borgee/channels/<encoded-channel-id>/workspace`. Task threads only switch execution onto an explicit existing `execution.local_directory`; otherwise they stay discussion-only on `~/.borgee/channels/<encoded-thread-id>/scratch/workspace`, and ACP permission requests are denied so filesystem and shell tools stay unavailable. The host persists the channel→ACP-session map in its state root, restores same-host continuity with ACP `session/resume` when available, retries stale restores with a fresh session immediately, and injects the per-channel local context paths through Claude ACP `_meta.systemPrompt.append`.
|
|
482
|
+
- **Claude**: one persistent `@agentclientprotocol/claude-agent-acp` subprocess is shared by a single `AgentsHost`, with one ACP session per Borgee channel. Same-channel turns are serialized; different channels keep isolated ACP sessions. The real ACP session cwd now comes from the resolved channel working folder `~/.borgee/channels/<encoded-channel-id>/workspace`. Task threads only switch execution onto an explicit existing `execution.local_directory`; otherwise they stay discussion-only on `~/.borgee/channels/<encoded-thread-id>/scratch/workspace`, and ACP permission requests are denied so filesystem and shell tools stay unavailable. The host persists the channel→ACP-session map in its state root, restores same-host continuity with ACP `session/resume` when available, retries stale restores with a fresh session immediately, and injects the per-channel local context paths through Claude ACP `_meta.systemPrompt.append`. The bundled adapter advertises a versioned targeted task-stop extension backed by the Agent SDK's `Query.stopTask(taskId)`: accepting that request moves only the selected background run to `stopping`, while its provider lifecycle event decides the terminal state and the parent turn plus sibling workers continue unchanged. Custom ACP commands do not receive this capability and remain fail-closed.
|
|
483
483
|
- **Codex**: one persistent `@agentclientprotocol/codex-acp` subprocess is
|
|
484
484
|
shared by a single `AgentsHost`, with one ACP session per Borgee channel. When
|
|
485
485
|
the current turn has a materialized channel context payload, Codex refreshes a
|
package/dist/agents-host.js
CHANGED
|
@@ -150,17 +150,19 @@ class DefaultHostRuntime {
|
|
|
150
150
|
});
|
|
151
151
|
this.ensureStateRoot = deps.ensureStateRoot ?? ensurePrivateStateRoot;
|
|
152
152
|
}
|
|
153
|
-
async start(onMessage, onStopTurn, onStopBackgroundRun) {
|
|
153
|
+
async start(onMessage, onStopTurn, onStopBackgroundRun, onActivityTransportReady) {
|
|
154
154
|
await this.ensureStateRoot(this.config.stateRootDir);
|
|
155
155
|
await this.gateway.start();
|
|
156
156
|
try {
|
|
157
157
|
this.controlPlane.onStopTurn?.(onStopTurn);
|
|
158
158
|
this.controlPlane.onStopBackgroundRun?.(onStopBackgroundRun);
|
|
159
|
+
this.controlPlane.onActivityTransportReady?.(onActivityTransportReady);
|
|
159
160
|
await this.controlPlane.connect(onMessage);
|
|
160
161
|
}
|
|
161
162
|
catch (error) {
|
|
162
163
|
this.controlPlane.onStopTurn?.(undefined);
|
|
163
164
|
this.controlPlane.onStopBackgroundRun?.(undefined);
|
|
165
|
+
this.controlPlane.onActivityTransportReady?.(undefined);
|
|
164
166
|
await this.gateway.stop().catch(() => { });
|
|
165
167
|
throw error;
|
|
166
168
|
}
|
|
@@ -170,6 +172,7 @@ class DefaultHostRuntime {
|
|
|
170
172
|
try {
|
|
171
173
|
this.controlPlane.onStopTurn?.(undefined);
|
|
172
174
|
this.controlPlane.onStopBackgroundRun?.(undefined);
|
|
175
|
+
this.controlPlane.onActivityTransportReady?.(undefined);
|
|
173
176
|
await this.controlPlane.close();
|
|
174
177
|
}
|
|
175
178
|
catch (error) {
|
|
@@ -365,6 +368,14 @@ export class AgentsHost {
|
|
|
365
368
|
cancel: this.provider.cancelBackgroundRun
|
|
366
369
|
? (channelId, providerRunId) => (this.provider.cancelBackgroundRun(channelId, providerRunId))
|
|
367
370
|
: undefined,
|
|
371
|
+
onFinished: (channelId, run) => {
|
|
372
|
+
this.borgee.reportTaskFinished?.({
|
|
373
|
+
channelId,
|
|
374
|
+
taskId: run.runId,
|
|
375
|
+
outcome: run.state,
|
|
376
|
+
...(run.state === 'failed' ? { reason: 'unknown' } : {}),
|
|
377
|
+
});
|
|
378
|
+
},
|
|
368
379
|
});
|
|
369
380
|
}
|
|
370
381
|
async start() {
|
|
@@ -391,7 +402,7 @@ export class AgentsHost {
|
|
|
391
402
|
return;
|
|
392
403
|
}
|
|
393
404
|
this.dispatchMessage(message);
|
|
394
|
-
}, async (channelId) => this.stopTurn(channelId), async (channelId, runId) => this.backgroundRuns.stop(channelId, runId));
|
|
405
|
+
}, async (channelId) => this.stopTurn(channelId), async (channelId, runId) => this.backgroundRuns.stop(channelId, runId), () => this.backgroundRuns.replay());
|
|
395
406
|
const agentId = await this.ensureSelfAgentId();
|
|
396
407
|
bufferingMessages = false;
|
|
397
408
|
while (bufferedMessages.length > 0) {
|
|
@@ -642,10 +653,10 @@ export class AgentsHost {
|
|
|
642
653
|
if (this.isHumanWakeFiltered(msg, authorKind, attentionSnapshot)) {
|
|
643
654
|
return;
|
|
644
655
|
}
|
|
645
|
-
let
|
|
656
|
+
let foregroundTransition;
|
|
646
657
|
if (authorKind !== 'agent' && channelState.activeTurn) {
|
|
647
658
|
this.supersedeActiveTurn(msg.channel_id, channelState.activeTurn);
|
|
648
|
-
|
|
659
|
+
foregroundTransition = (async () => {
|
|
649
660
|
try {
|
|
650
661
|
await this.provider.interruptTurn?.(msg.channel_id);
|
|
651
662
|
}
|
|
@@ -669,7 +680,7 @@ export class AgentsHost {
|
|
|
669
680
|
attentionSnapshot,
|
|
670
681
|
taskThreadContext,
|
|
671
682
|
});
|
|
672
|
-
await
|
|
683
|
+
await foregroundTransition;
|
|
673
684
|
await this.ensureCollaborationProcessor(msg.channel_id, channelState);
|
|
674
685
|
}
|
|
675
686
|
detectProtocolKickoffCandidate(anchorMessageId, content, selfAgentId) {
|
|
@@ -2399,6 +2410,13 @@ export class AgentsHost {
|
|
|
2399
2410
|
onProgress: (update) => {
|
|
2400
2411
|
if (supportsBackgroundRunLifecycle) {
|
|
2401
2412
|
this.backgroundRuns.observe(channelId, update);
|
|
2413
|
+
if (update.type === 'activity' && update.activity.background === true) {
|
|
2414
|
+
this.borgee.reportActivity?.({
|
|
2415
|
+
channelId,
|
|
2416
|
+
activity: { shape: 'activity', ...update.activity },
|
|
2417
|
+
});
|
|
2418
|
+
return;
|
|
2419
|
+
}
|
|
2402
2420
|
}
|
|
2403
2421
|
if (!this.canAcceptTurnOutput(channelId, activeTurn?.generation)) {
|
|
2404
2422
|
return;
|
|
@@ -4,6 +4,10 @@ interface BackgroundRunTrackerOptions {
|
|
|
4
4
|
lifecycleSupported: boolean;
|
|
5
5
|
targetedCancellationSupported: boolean;
|
|
6
6
|
cancel?: (channelId: string, providerRunId: string) => Promise<boolean>;
|
|
7
|
+
onFinished?: (channelId: string, run: {
|
|
8
|
+
runId: string;
|
|
9
|
+
state: 'completed' | 'failed' | 'cancelled';
|
|
10
|
+
}) => void;
|
|
7
11
|
}
|
|
8
12
|
export declare class BackgroundRunTracker {
|
|
9
13
|
private readonly report;
|
|
@@ -12,8 +16,9 @@ export declare class BackgroundRunTracker {
|
|
|
12
16
|
private readonly mintRunId;
|
|
13
17
|
private readonly runs;
|
|
14
18
|
private readonly runsById;
|
|
15
|
-
constructor(report: (channelId: string, activity: AgentActivity) => void, options: BackgroundRunTrackerOptions, now?: () => number, mintRunId?: (channelId: string, providerRunId: string, startedAt: number) => string);
|
|
19
|
+
constructor(report: (channelId: string, activity: AgentActivity) => void, options: BackgroundRunTrackerOptions, now?: () => number, mintRunId?: (channelId: string, providerRunId: string, startedAt: number, executionId?: string) => string);
|
|
16
20
|
observe(channelId: string, update: ProviderProgressUpdate): void;
|
|
21
|
+
replay(): void;
|
|
17
22
|
stop(channelId: string, runId: string): Promise<{
|
|
18
23
|
outcome: 'stopped' | 'not_found' | 'unsupported';
|
|
19
24
|
reason?: string;
|
package/dist/background-runs.js
CHANGED
|
@@ -20,15 +20,26 @@ export class BackgroundRunTracker {
|
|
|
20
20
|
const key = `${channelId}\0${update.run.providerRunId}`;
|
|
21
21
|
const held = this.runs.get(key);
|
|
22
22
|
const state = runState(update.run.state);
|
|
23
|
+
const refinesUnknown = held?.state === 'unknown' && state !== 'unknown';
|
|
23
24
|
const stopSupported = update.run.stopSupported &&
|
|
24
25
|
this.options.targetedCancellationSupported &&
|
|
25
26
|
this.options.cancel !== undefined;
|
|
26
27
|
const executionChanged = held !== undefined &&
|
|
27
|
-
update.run.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
((update.run.executionId !== undefined &&
|
|
29
|
+
update.run.executionId !== held.executionId) ||
|
|
30
|
+
(update.run.startedAt !== undefined &&
|
|
31
|
+
update.run.startedAt !== held.startedAt));
|
|
32
|
+
const startsNewExecution = !held ||
|
|
33
|
+
executionChanged ||
|
|
34
|
+
(held.state !== 'unknown' && isTerminal(held.state) && !isTerminal(state));
|
|
35
|
+
if (held &&
|
|
36
|
+
isTerminal(held.state) &&
|
|
37
|
+
isTerminal(state) &&
|
|
38
|
+
!startsNewExecution &&
|
|
39
|
+
held.state !== state &&
|
|
40
|
+
held.state !== 'unknown') {
|
|
31
41
|
return;
|
|
42
|
+
}
|
|
32
43
|
const startedAt = startsNewExecution
|
|
33
44
|
? (update.run.startedAt ?? this.now())
|
|
34
45
|
: (update.run.startedAt ?? held.startedAt);
|
|
@@ -36,9 +47,11 @@ export class BackgroundRunTracker {
|
|
|
36
47
|
? {
|
|
37
48
|
channelId,
|
|
38
49
|
providerRunId: update.run.providerRunId,
|
|
39
|
-
runId: this.mintRunId(channelId, update.run.providerRunId, startedAt),
|
|
50
|
+
runId: this.mintRunId(channelId, update.run.providerRunId, startedAt, update.run.executionId),
|
|
51
|
+
executionId: update.run.executionId,
|
|
40
52
|
label: update.run.label,
|
|
41
53
|
state,
|
|
54
|
+
parentActivityId: update.run.parentActivityId,
|
|
42
55
|
startedAt,
|
|
43
56
|
finishedAt: update.run.finishedAt,
|
|
44
57
|
reason: update.run.reason,
|
|
@@ -48,32 +61,53 @@ export class BackgroundRunTracker {
|
|
|
48
61
|
...held,
|
|
49
62
|
label: update.run.label,
|
|
50
63
|
state,
|
|
64
|
+
parentActivityId: update.run.parentActivityId ?? held.parentActivityId,
|
|
51
65
|
startedAt,
|
|
52
|
-
finishedAt:
|
|
53
|
-
|
|
66
|
+
finishedAt: refinesUnknown
|
|
67
|
+
? update.run.finishedAt
|
|
68
|
+
: (update.run.finishedAt ?? held.finishedAt),
|
|
69
|
+
reason: refinesUnknown ? update.run.reason : (update.run.reason ?? held.reason),
|
|
54
70
|
stopSupported,
|
|
55
71
|
};
|
|
56
72
|
if (isTerminal(state) && run.finishedAt === undefined) {
|
|
57
73
|
run.finishedAt = this.now();
|
|
58
74
|
}
|
|
75
|
+
if (held &&
|
|
76
|
+
held.runId === run.runId &&
|
|
77
|
+
held.label === run.label &&
|
|
78
|
+
held.state === run.state &&
|
|
79
|
+
held.parentActivityId === run.parentActivityId &&
|
|
80
|
+
held.finishedAt === run.finishedAt &&
|
|
81
|
+
held.reason === run.reason &&
|
|
82
|
+
held.stopSupported === run.stopSupported) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
59
85
|
if (held && held.runId !== run.runId) {
|
|
60
86
|
this.runsById.delete(held.runId);
|
|
61
87
|
}
|
|
62
88
|
this.runs.delete(key);
|
|
63
89
|
this.runs.set(key, run);
|
|
64
90
|
this.runsById.set(run.runId, run);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
91
|
+
let historySize = [...this.runs.values()].filter((candidate) => isTerminal(candidate.state)).length;
|
|
92
|
+
while (historySize > MAX_TRACKED_RUNS) {
|
|
93
|
+
const oldest = [...this.runs].find(([, candidate]) => isTerminal(candidate.state));
|
|
94
|
+
if (!oldest) {
|
|
68
95
|
break;
|
|
69
96
|
}
|
|
70
|
-
|
|
71
|
-
this.
|
|
72
|
-
|
|
73
|
-
this.runsById.delete(oldest.runId);
|
|
74
|
-
}
|
|
97
|
+
this.runs.delete(oldest[0]);
|
|
98
|
+
this.runsById.delete(oldest[1].runId);
|
|
99
|
+
historySize -= 1;
|
|
75
100
|
}
|
|
76
101
|
this.publish(run);
|
|
102
|
+
if (isConcreteTerminal(state) &&
|
|
103
|
+
(startsNewExecution || !held || !isConcreteTerminal(held.state))) {
|
|
104
|
+
this.options.onFinished?.(channelId, { runId: run.runId, state });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
replay() {
|
|
108
|
+
for (const run of this.runs.values()) {
|
|
109
|
+
this.publish(run);
|
|
110
|
+
}
|
|
77
111
|
}
|
|
78
112
|
async stop(channelId, runId) {
|
|
79
113
|
if (!this.options.lifecycleSupported ||
|
|
@@ -110,6 +144,7 @@ export class BackgroundRunTracker {
|
|
|
110
144
|
runId: run.runId,
|
|
111
145
|
label: run.label,
|
|
112
146
|
state: run.state,
|
|
147
|
+
...(run.parentActivityId ? { parentId: run.parentActivityId } : {}),
|
|
113
148
|
startedAt: run.startedAt,
|
|
114
149
|
...(run.finishedAt !== undefined ? { finishedAt: run.finishedAt } : {}),
|
|
115
150
|
...(run.reason ? { reason: run.reason } : {}),
|
|
@@ -135,18 +170,18 @@ function runState(status) {
|
|
|
135
170
|
}
|
|
136
171
|
}
|
|
137
172
|
function isTerminal(state) {
|
|
138
|
-
return (state === '
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
173
|
+
return (isConcreteTerminal(state) || state === 'unknown');
|
|
174
|
+
}
|
|
175
|
+
function isConcreteTerminal(state) {
|
|
176
|
+
return state === 'completed' || state === 'failed' || state === 'cancelled';
|
|
142
177
|
}
|
|
143
|
-
function stableRunId(channelId, providerRunId, startedAt) {
|
|
178
|
+
function stableRunId(channelId, providerRunId, startedAt, executionId) {
|
|
144
179
|
const digest = createHash('sha256')
|
|
145
180
|
.update(channelId)
|
|
146
181
|
.update('\0')
|
|
147
182
|
.update(providerRunId)
|
|
148
183
|
.update('\0')
|
|
149
|
-
.update(String(startedAt))
|
|
184
|
+
.update(executionId ?? String(startedAt))
|
|
150
185
|
.digest('hex')
|
|
151
186
|
.slice(0, 32);
|
|
152
187
|
return `run_${digest}`;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type { AgentActivity, ReportTurnActivityInput, StopBackgroundRunHandler, StopTurnHandler, TurnActivityReporter } from '../plugin-sdk.js';
|
|
1
|
+
import type { AgentActivity, ReportTaskFinishedInput, ReportTurnActivityInput, StopBackgroundRunHandler, StopTurnHandler, TurnActivityReporter } from '../plugin-sdk.js';
|
|
2
2
|
import type { ChannelSummary, ChannelHistoryEntry, ChannelMessageEvent, CreateTaskInput, DirectoryUser, MeResponseUser, PostMessageInput, PostedMessage, ReadChannelHistoryInput, Task, UpdateTaskInput } from '../types.js';
|
|
3
3
|
export interface ChatControlPlane {
|
|
4
4
|
onStopTurn?(handler: StopTurnHandler | undefined): void;
|
|
5
5
|
onStopBackgroundRun?(handler: StopBackgroundRunHandler | undefined): void;
|
|
6
|
+
onActivityTransportReady?(handler: (() => void) | undefined): void;
|
|
6
7
|
connect(onMessage: (message: ChannelMessageEvent) => void): Promise<void>;
|
|
7
8
|
close(): Promise<void>;
|
|
8
9
|
postMessage(input: PostMessageInput): Promise<PostedMessage>;
|
|
@@ -11,6 +12,7 @@ export interface ChatControlPlane {
|
|
|
11
12
|
startTyping(channelId: string): () => void;
|
|
12
13
|
/** Opens one turn's report on the activity rail. Fire-and-forget: nothing recovers what it sends. */
|
|
13
14
|
reportTurnActivity(input: ReportTurnActivityInput): TurnActivityReporter;
|
|
15
|
+
reportTaskFinished?(input: ReportTaskFinishedInput): void;
|
|
14
16
|
reportActivity?(input: {
|
|
15
17
|
channelId: string;
|
|
16
18
|
activity: AgentActivity;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { type BorgeePluginClient, type BorgeePluginOptions, type InboundMessageEvent, type ReportTurnActivityInput, type StopTurnHandler, type StopBackgroundRunHandler, type TurnActivityReporter } from '../plugin-sdk.js';
|
|
1
|
+
import { type BorgeePluginClient, type BorgeePluginOptions, type InboundMessageEvent, type ReportTaskFinishedInput, type ReportTurnActivityInput, type StopTurnHandler, type StopBackgroundRunHandler, type TurnActivityReporter } from '../plugin-sdk.js';
|
|
2
2
|
import type { ChannelSummary, ChannelHistoryEntry, ChannelMessageEvent, CreateTaskInput, DirectoryUser, MeResponseUser, PostMessageInput, PostedMessage, ReadChannelHistoryInput, Task, UpdateTaskInput } from '../types.js';
|
|
3
3
|
import type { ChatControlPlane } from './chat-control-plane.js';
|
|
4
|
-
type PluginClientLike = Pick<BorgeePluginClient, 'agentId' | 'close' | 'connect' | 'createTask' | 'deleteMessage' | 'editMessage' | 'getMe' | 'getTask' | 'listChannels' | 'listTasks' | 'listUsers' | 'on' | 'onStopTurn' | 'onStopBackgroundRun' | 'reportActivity' | 'readHistory' | 'reportTurnActivity' | 'sendMessage' | 'startTyping' | 'updateTask' | 'setTaskProperty' | 'deleteTaskProperty'
|
|
4
|
+
type PluginClientLike = Pick<BorgeePluginClient, 'agentId' | 'close' | 'connect' | 'createTask' | 'deleteMessage' | 'editMessage' | 'getMe' | 'getTask' | 'listChannels' | 'listTasks' | 'listUsers' | 'on' | 'onStopTurn' | 'onStopBackgroundRun' | 'reportActivity' | 'readHistory' | 'reportTurnActivity' | 'sendMessage' | 'startTyping' | 'updateTask' | 'setTaskProperty' | 'deleteTaskProperty'> & Partial<Pick<BorgeePluginClient, 'reportTaskFinished'>>;
|
|
5
5
|
type PluginClientFactory = (options: BorgeePluginOptions) => PluginClientLike;
|
|
6
6
|
type SdkChatControlPlaneOptions = Pick<BorgeePluginOptions, 'pluginId'>;
|
|
7
7
|
/**
|
|
@@ -13,15 +13,19 @@ export declare class SdkChatControlPlane implements ChatControlPlane {
|
|
|
13
13
|
private readonly client;
|
|
14
14
|
private pendingMessages;
|
|
15
15
|
private unsubscribe;
|
|
16
|
+
private stateUnsubscribe;
|
|
17
|
+
private activityTransportReady;
|
|
16
18
|
private connected;
|
|
17
19
|
private me;
|
|
18
20
|
constructor(baseUrl: string, apiKey: string, createClient?: PluginClientFactory, options?: SdkChatControlPlaneOptions);
|
|
19
21
|
onStopTurn(handler: StopTurnHandler | undefined): void;
|
|
20
22
|
onStopBackgroundRun(handler: StopBackgroundRunHandler | undefined): void;
|
|
23
|
+
onActivityTransportReady(handler: (() => void) | undefined): void;
|
|
21
24
|
connect(onMessage: (message: ChannelMessageEvent) => void): Promise<void>;
|
|
22
25
|
close(): Promise<void>;
|
|
23
26
|
postMessage(input: PostMessageInput): Promise<PostedMessage>;
|
|
24
27
|
reportActivity(input: Parameters<BorgeePluginClient['reportActivity']>[0]): void;
|
|
28
|
+
reportTaskFinished(input: ReportTaskFinishedInput): void;
|
|
25
29
|
editMessage(messageId: string, content: string): Promise<void>;
|
|
26
30
|
deleteMessage(messageId: string): Promise<void>;
|
|
27
31
|
startTyping(channelId: string): () => void;
|
|
@@ -9,6 +9,8 @@ export class SdkChatControlPlane {
|
|
|
9
9
|
client;
|
|
10
10
|
pendingMessages = [];
|
|
11
11
|
unsubscribe = null;
|
|
12
|
+
stateUnsubscribe = null;
|
|
13
|
+
activityTransportReady;
|
|
12
14
|
connected = false;
|
|
13
15
|
me = null;
|
|
14
16
|
constructor(baseUrl, apiKey, createClient = createBorgeePlugin, options = {}) {
|
|
@@ -20,6 +22,9 @@ export class SdkChatControlPlane {
|
|
|
20
22
|
onStopBackgroundRun(handler) {
|
|
21
23
|
this.client.onStopBackgroundRun?.(handler);
|
|
22
24
|
}
|
|
25
|
+
onActivityTransportReady(handler) {
|
|
26
|
+
this.activityTransportReady = handler;
|
|
27
|
+
}
|
|
23
28
|
async connect(onMessage) {
|
|
24
29
|
this.unsubscribe = this.client.on('message', (event) => {
|
|
25
30
|
const message = mapInboundToChannelMessage(event);
|
|
@@ -29,6 +34,11 @@ export class SdkChatControlPlane {
|
|
|
29
34
|
}
|
|
30
35
|
onMessage(message);
|
|
31
36
|
});
|
|
37
|
+
this.stateUnsubscribe = this.client.on('connectionState', (state) => {
|
|
38
|
+
if (state.status === 'online') {
|
|
39
|
+
this.activityTransportReady?.();
|
|
40
|
+
}
|
|
41
|
+
});
|
|
32
42
|
try {
|
|
33
43
|
await this.client.connect();
|
|
34
44
|
this.connected = true;
|
|
@@ -43,6 +53,8 @@ export class SdkChatControlPlane {
|
|
|
43
53
|
catch (error) {
|
|
44
54
|
this.unsubscribe?.();
|
|
45
55
|
this.unsubscribe = null;
|
|
56
|
+
this.stateUnsubscribe?.();
|
|
57
|
+
this.stateUnsubscribe = null;
|
|
46
58
|
this.pendingMessages = [];
|
|
47
59
|
throw error;
|
|
48
60
|
}
|
|
@@ -52,6 +64,9 @@ export class SdkChatControlPlane {
|
|
|
52
64
|
this.pendingMessages = [];
|
|
53
65
|
this.unsubscribe?.();
|
|
54
66
|
this.unsubscribe = null;
|
|
67
|
+
this.stateUnsubscribe?.();
|
|
68
|
+
this.stateUnsubscribe = null;
|
|
69
|
+
this.activityTransportReady = undefined;
|
|
55
70
|
this.client.onStopTurn(undefined);
|
|
56
71
|
this.client.onStopBackgroundRun?.(undefined);
|
|
57
72
|
await this.client.close();
|
|
@@ -67,6 +82,9 @@ export class SdkChatControlPlane {
|
|
|
67
82
|
reportActivity(input) {
|
|
68
83
|
this.client.reportActivity(input);
|
|
69
84
|
}
|
|
85
|
+
reportTaskFinished(input) {
|
|
86
|
+
this.client.reportTaskFinished?.(input);
|
|
87
|
+
}
|
|
70
88
|
async editMessage(messageId, content) {
|
|
71
89
|
await this.client.editMessage({ messageId, body: content });
|
|
72
90
|
}
|
package/dist/plugin-sdk.js
CHANGED
|
@@ -3722,8 +3722,8 @@ var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
|
3722
3722
|
// ../sdk/plugin-ts/dist/errors.js
|
|
3723
3723
|
var BorgeeError = class extends Error {
|
|
3724
3724
|
code;
|
|
3725
|
-
constructor(message, code) {
|
|
3726
|
-
super(message);
|
|
3725
|
+
constructor(message, code, options) {
|
|
3726
|
+
super(message, options);
|
|
3727
3727
|
this.name = "BorgeeError";
|
|
3728
3728
|
this.code = code;
|
|
3729
3729
|
}
|
|
@@ -3743,6 +3743,95 @@ var PermissionDeniedError = class extends BorgeeError {
|
|
|
3743
3743
|
}
|
|
3744
3744
|
};
|
|
3745
3745
|
|
|
3746
|
+
// ../sdk/plugin-ts/dist/channel-contracts.js
|
|
3747
|
+
var isZoneId = (value) => typeof value === "string" && /^[A-Za-z0-9_-]{1,128}$/.test(value);
|
|
3748
|
+
var validateChannelPlacement = (parentId) => {
|
|
3749
|
+
if (parentId !== null && (!isZoneId(parentId) || parentId === "null")) {
|
|
3750
|
+
throw new BorgeeError("parentId is required: pass null for Default or a Zone ID", "channel.placement_invalid");
|
|
3751
|
+
}
|
|
3752
|
+
};
|
|
3753
|
+
var rejectChannelPlacementUpdate = (input) => {
|
|
3754
|
+
for (const field of ["parentId", "parent_id", "channelType", "channel_type", "kind"]) {
|
|
3755
|
+
if (Object.hasOwn(input, field)) {
|
|
3756
|
+
throw new BorgeeError(`${field} cannot be changed after channel creation`, "channel.field_immutable");
|
|
3757
|
+
}
|
|
3758
|
+
}
|
|
3759
|
+
};
|
|
3760
|
+
var validateListZonesInput = (input) => {
|
|
3761
|
+
if (!isZoneId(input.guildId)) {
|
|
3762
|
+
throw new BorgeeError("guildId must be a nonempty opaque ID", "zone.input_invalid");
|
|
3763
|
+
}
|
|
3764
|
+
if (input.limit !== void 0 && (!Number.isInteger(input.limit) || input.limit < 1 || input.limit > 50)) {
|
|
3765
|
+
throw new BorgeeError("limit must be an integer between 1 and 50", "zone.input_invalid");
|
|
3766
|
+
}
|
|
3767
|
+
if (input.after !== void 0 && !isZoneId(input.after)) {
|
|
3768
|
+
throw new BorgeeError("after must be a nonempty opaque Zone ID", "zone.input_invalid");
|
|
3769
|
+
}
|
|
3770
|
+
};
|
|
3771
|
+
|
|
3772
|
+
// ../sdk/plugin-ts/dist/bpp/channel-results.js
|
|
3773
|
+
var invalidResult = (field) => new BorgeeError(`Invalid channel result: ${field}`, "bpp.invalid_result");
|
|
3774
|
+
var record = (value, field) => {
|
|
3775
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
3776
|
+
throw invalidResult(field);
|
|
3777
|
+
return value;
|
|
3778
|
+
};
|
|
3779
|
+
var requiredString = (value, field) => {
|
|
3780
|
+
if (typeof value !== "string")
|
|
3781
|
+
throw invalidResult(field);
|
|
3782
|
+
return value;
|
|
3783
|
+
};
|
|
3784
|
+
var decodeChannel = (value) => {
|
|
3785
|
+
const channel = record(value, "channel");
|
|
3786
|
+
if (!isZoneId(channel.id))
|
|
3787
|
+
throw invalidResult("id");
|
|
3788
|
+
if (channel.parent_id !== null && !isZoneId(channel.parent_id))
|
|
3789
|
+
throw invalidResult("parent_id");
|
|
3790
|
+
return {
|
|
3791
|
+
id: channel.id,
|
|
3792
|
+
name: requiredString(channel.name, "name"),
|
|
3793
|
+
kind: requiredString(channel.kind, "kind"),
|
|
3794
|
+
parentId: channel.parent_id
|
|
3795
|
+
};
|
|
3796
|
+
};
|
|
3797
|
+
var decodeChannels = (value) => {
|
|
3798
|
+
if (!Array.isArray(value))
|
|
3799
|
+
throw invalidResult("channels");
|
|
3800
|
+
return value.map(decodeChannel);
|
|
3801
|
+
};
|
|
3802
|
+
var decodeZone = (value) => {
|
|
3803
|
+
const zone = record(value, "zone");
|
|
3804
|
+
if (!isZoneId(zone.id))
|
|
3805
|
+
throw invalidResult("zone.id");
|
|
3806
|
+
const name = requiredString(zone.name, "zone.name");
|
|
3807
|
+
if (new RegExp("^\\p{White_Space}|\\p{White_Space}$", "u").test(name) || [...name].length < 1 || [...name].length > 100 || /[\p{Cc}\p{Cs}]/u.test(name)) {
|
|
3808
|
+
throw invalidResult("zone.name");
|
|
3809
|
+
}
|
|
3810
|
+
return { id: zone.id, name };
|
|
3811
|
+
};
|
|
3812
|
+
var decodeZonePage = (value, input) => {
|
|
3813
|
+
const page = record(value, "Zone page");
|
|
3814
|
+
if (!Array.isArray(page.zones))
|
|
3815
|
+
throw invalidResult("zones");
|
|
3816
|
+
const limit = input?.limit ?? 20;
|
|
3817
|
+
if (page.zones.length > limit)
|
|
3818
|
+
throw invalidResult("zones exceed the requested limit");
|
|
3819
|
+
const zones = page.zones.map(decodeZone);
|
|
3820
|
+
let previous = input?.after;
|
|
3821
|
+
for (const zone of zones) {
|
|
3822
|
+
if (previous !== void 0 && zone.id <= previous)
|
|
3823
|
+
throw invalidResult("Zone IDs must follow the cursor in ascending order");
|
|
3824
|
+
previous = zone.id;
|
|
3825
|
+
}
|
|
3826
|
+
const nextAfter = page.next_after;
|
|
3827
|
+
if (nextAfter !== null) {
|
|
3828
|
+
if (!isZoneId(nextAfter) || zones.length !== limit || nextAfter !== zones.at(-1)?.id) {
|
|
3829
|
+
throw invalidResult("next_after must be null or the final ID of a full page");
|
|
3830
|
+
}
|
|
3831
|
+
}
|
|
3832
|
+
return { zones, nextAfter };
|
|
3833
|
+
};
|
|
3834
|
+
|
|
3746
3835
|
// ../sdk/plugin-ts/dist/bpp/bpp-transport.js
|
|
3747
3836
|
var PROTOCOL_VERSION = "bpp-1";
|
|
3748
3837
|
var CONFIG_ACK_REASONS = /* @__PURE__ */ new Set([
|
|
@@ -3929,7 +4018,13 @@ var BppTransport = class {
|
|
|
3929
4018
|
this.pending.delete(nonce);
|
|
3930
4019
|
reject(new BorgeeError(`semantic_action ${action.op} timed out`, "bpp.action_timeout"));
|
|
3931
4020
|
}, remaining);
|
|
3932
|
-
this.pending.set(nonce, {
|
|
4021
|
+
this.pending.set(nonce, {
|
|
4022
|
+
op: action.op,
|
|
4023
|
+
...action.op === "list_zones" ? { zoneInput: { guildId: action.guildId, limit: action.limit, after: action.after } } : {},
|
|
4024
|
+
resolve,
|
|
4025
|
+
reject,
|
|
4026
|
+
timer
|
|
4027
|
+
});
|
|
3933
4028
|
this.send(frame);
|
|
3934
4029
|
});
|
|
3935
4030
|
}
|
|
@@ -4159,7 +4254,13 @@ var BppTransport = class {
|
|
|
4159
4254
|
this.pending.delete(frame.nonce);
|
|
4160
4255
|
clearTimeout(p.timer);
|
|
4161
4256
|
if (frame.status === RESULT_STATUS_OK) {
|
|
4162
|
-
|
|
4257
|
+
let result;
|
|
4258
|
+
try {
|
|
4259
|
+
result = decodeActionResult(p.op, frame.payload, p.zoneInput);
|
|
4260
|
+
} catch (error) {
|
|
4261
|
+
p.reject(error);
|
|
4262
|
+
return;
|
|
4263
|
+
}
|
|
4163
4264
|
if (p.op === "get_me" && result && typeof result === "object") {
|
|
4164
4265
|
const id = result.id;
|
|
4165
4266
|
if (id)
|
|
@@ -4401,7 +4502,7 @@ var BppTransport = class {
|
|
|
4401
4502
|
};
|
|
4402
4503
|
this.send(frame);
|
|
4403
4504
|
}
|
|
4404
|
-
// sendActivity flattens the
|
|
4505
|
+
// sendActivity flattens the activity union onto one frame. The union is
|
|
4405
4506
|
// how a producer says which shape it is reporting; the wire puts the
|
|
4406
4507
|
// discriminator beside its payload with no wrapper object, matching both the
|
|
4407
4508
|
// protocol this relays and inbound_message's own `kind`.
|
|
@@ -4443,6 +4544,9 @@ var BppTransport = class {
|
|
|
4443
4544
|
frame.detail = action.activity.detail ?? "";
|
|
4444
4545
|
frame.parent_id = action.activity.parentId ?? "";
|
|
4445
4546
|
frame.subagent = action.activity.subagent ?? false;
|
|
4547
|
+
frame.background = action.activity.background;
|
|
4548
|
+
frame.background_state = action.activity.backgroundState;
|
|
4549
|
+
frame.follow_up_control = action.activity.followUpControl;
|
|
4446
4550
|
frame.reason = action.activity.reason ?? "";
|
|
4447
4551
|
if (action.activity.paths)
|
|
4448
4552
|
frame.paths = action.activity.paths;
|
|
@@ -4464,12 +4568,25 @@ var BppTransport = class {
|
|
|
4464
4568
|
frame.run_id = action.activity.runId;
|
|
4465
4569
|
frame.label = action.activity.label;
|
|
4466
4570
|
frame.run_state = action.activity.state;
|
|
4571
|
+
frame.parent_id = action.activity.parentId ?? "";
|
|
4467
4572
|
frame.started_at = action.activity.startedAt;
|
|
4468
4573
|
frame.finished_at = action.activity.finishedAt ?? 0;
|
|
4469
4574
|
frame.reason = action.activity.reason ?? "";
|
|
4470
4575
|
frame.stop_supported = action.activity.stopSupported;
|
|
4471
4576
|
frame.stop_unsupported_reason = action.activity.stopUnsupportedReason ?? "";
|
|
4472
4577
|
break;
|
|
4578
|
+
case "normalized":
|
|
4579
|
+
frame.protocol_version = action.activity.protocolVersion;
|
|
4580
|
+
frame.task_id = action.activity.taskId;
|
|
4581
|
+
frame.event = action.activity.event;
|
|
4582
|
+
break;
|
|
4583
|
+
}
|
|
4584
|
+
if (action.activity.shape === "normalized") {
|
|
4585
|
+
const ids = [frame.channel_id, frame.task_id];
|
|
4586
|
+
if (ids.some((value) => new TextEncoder().encode(value).byteLength > 256))
|
|
4587
|
+
return;
|
|
4588
|
+
if (new TextEncoder().encode(JSON.stringify(frame)).byteLength > 32768)
|
|
4589
|
+
return;
|
|
4473
4590
|
}
|
|
4474
4591
|
this.send(frame);
|
|
4475
4592
|
}
|
|
@@ -4603,15 +4720,21 @@ function encodeActionPayload(action) {
|
|
|
4603
4720
|
case "list_users":
|
|
4604
4721
|
case "list_channels":
|
|
4605
4722
|
return {};
|
|
4723
|
+
case "list_zones":
|
|
4724
|
+
validateListZonesInput(action);
|
|
4725
|
+
return { guild_id: action.guildId, limit: action.limit, after: action.after };
|
|
4606
4726
|
case "create_channel":
|
|
4727
|
+
validateChannelPlacement(action.parentId);
|
|
4607
4728
|
return {
|
|
4608
4729
|
guild_id: action.guildId,
|
|
4609
4730
|
name: action.name,
|
|
4731
|
+
parent_id: action.parentId,
|
|
4610
4732
|
topic: action.topic,
|
|
4611
4733
|
visibility: action.visibility,
|
|
4612
4734
|
member_ids: action.memberIds
|
|
4613
4735
|
};
|
|
4614
4736
|
case "update_channel":
|
|
4737
|
+
rejectChannelPlacementUpdate(action);
|
|
4615
4738
|
return {
|
|
4616
4739
|
channel_id: action.channelId,
|
|
4617
4740
|
name: action.name,
|
|
@@ -4655,7 +4778,20 @@ function encodeActionPayload(action) {
|
|
|
4655
4778
|
return {};
|
|
4656
4779
|
}
|
|
4657
4780
|
}
|
|
4658
|
-
function decodeActionResult(op, payloadJSON) {
|
|
4781
|
+
function decodeActionResult(op, payloadJSON, zoneInput) {
|
|
4782
|
+
if (op === "list_zones" || op === "list_channels" || op === "create_channel" || op === "update_channel") {
|
|
4783
|
+
let value;
|
|
4784
|
+
try {
|
|
4785
|
+
value = JSON.parse(payloadJSON);
|
|
4786
|
+
} catch (cause) {
|
|
4787
|
+
throw new BorgeeError("Invalid JSON in channel result", "bpp.invalid_result", { cause });
|
|
4788
|
+
}
|
|
4789
|
+
if (op === "list_zones")
|
|
4790
|
+
return decodeZonePage(value, zoneInput);
|
|
4791
|
+
if (op === "list_channels")
|
|
4792
|
+
return decodeChannels(value);
|
|
4793
|
+
return decodeChannel(value);
|
|
4794
|
+
}
|
|
4659
4795
|
const p = safeParseJSON(payloadJSON);
|
|
4660
4796
|
switch (op) {
|
|
4661
4797
|
case "send_message":
|
|
@@ -4695,21 +4831,6 @@ function decodeActionResult(op, payloadJSON) {
|
|
|
4695
4831
|
kind: u.kind === "agent" ? "agent" : "user"
|
|
4696
4832
|
}));
|
|
4697
4833
|
}
|
|
4698
|
-
case "list_channels": {
|
|
4699
|
-
const arr = Array.isArray(p) ? p : [];
|
|
4700
|
-
return arr.map((c) => ({
|
|
4701
|
-
id: String(c.id ?? ""),
|
|
4702
|
-
name: String(c.name ?? ""),
|
|
4703
|
-
kind: String(c.kind ?? "")
|
|
4704
|
-
}));
|
|
4705
|
-
}
|
|
4706
|
-
case "create_channel":
|
|
4707
|
-
case "update_channel":
|
|
4708
|
-
return {
|
|
4709
|
-
id: String(p.id ?? ""),
|
|
4710
|
-
name: String(p.name ?? ""),
|
|
4711
|
-
kind: String(p.kind ?? "")
|
|
4712
|
-
};
|
|
4713
4834
|
case "set_topic":
|
|
4714
4835
|
case "delete_channel":
|
|
4715
4836
|
return void 0;
|
|
@@ -5050,16 +5171,19 @@ var Client = class {
|
|
|
5050
5171
|
return await this.t.perform({ op: "list_channels" });
|
|
5051
5172
|
}
|
|
5052
5173
|
async createChannel(input) {
|
|
5174
|
+
validateChannelPlacement(input.parentId);
|
|
5053
5175
|
return await this.t.perform({
|
|
5054
5176
|
op: "create_channel",
|
|
5055
5177
|
guildId: input.guildId,
|
|
5056
5178
|
name: input.name,
|
|
5179
|
+
parentId: input.parentId,
|
|
5057
5180
|
topic: input.topic,
|
|
5058
5181
|
visibility: input.visibility,
|
|
5059
5182
|
memberIds: input.memberIds
|
|
5060
5183
|
});
|
|
5061
5184
|
}
|
|
5062
5185
|
async updateChannel(input) {
|
|
5186
|
+
rejectChannelPlacementUpdate(input);
|
|
5063
5187
|
return await this.t.perform({
|
|
5064
5188
|
op: "update_channel",
|
|
5065
5189
|
channelId: input.channelId,
|
|
@@ -5068,6 +5192,10 @@ var Client = class {
|
|
|
5068
5192
|
archived: input.archived
|
|
5069
5193
|
});
|
|
5070
5194
|
}
|
|
5195
|
+
async listZones(input) {
|
|
5196
|
+
validateListZonesInput(input);
|
|
5197
|
+
return await this.t.perform({ op: "list_zones", ...input });
|
|
5198
|
+
}
|
|
5071
5199
|
async setTopic(input) {
|
|
5072
5200
|
await this.t.perform({ op: "set_topic", channelId: input.channelId, topic: input.topic });
|
|
5073
5201
|
}
|