@borgee/agents-host 0.2.94 → 0.2.101
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/dist/agents-host.d.ts +8 -0
- package/dist/agents-host.js +266 -64
- package/dist/background-runs.d.ts +4 -0
- package/dist/background-runs.js +8 -4
- package/dist/chat/chat-control-plane.d.ts +14 -3
- package/dist/chat/sdk-chat-control-plane.d.ts +31 -5
- package/dist/chat/sdk-chat-control-plane.js +134 -3
- package/dist/context/skill-manual.js +1 -1
- package/dist/execution-telemetry.d.ts +96 -0
- package/dist/execution-telemetry.js +583 -0
- package/dist/gateway/channel-file-workspace.d.ts +15 -0
- package/dist/gateway/channel-file-workspace.js +130 -0
- package/dist/gateway/localhost-gateway.js +140 -0
- package/dist/plugin-sdk.js +85 -4
- package/dist/plugin-sdk.js.map +2 -2
- package/dist/policy/gateway-authorization.d.ts +1 -1
- package/dist/policy/gateway-authorization.js +22 -2
- package/dist/progress-to-activity.d.ts +3 -3
- package/dist/providers/claude/adapter.d.ts +2 -1
- package/dist/providers/claude/adapter.js +11 -0
- package/dist/providers/claude/cli-client.d.ts +3 -1
- package/dist/providers/claude/cli-client.js +267 -13
- package/dist/providers/codex/adapter.js +1 -0
- package/dist/providers/codex/cli-client.js +5 -0
- package/dist/providers/copilot/adapter.js +4 -0
- package/dist/providers/copilot/cli-client.js +11 -1
- package/dist/providers/copilot/sdk-session.d.ts +12 -3
- package/dist/providers/copilot/sdk-session.js +222 -5
- package/dist/providers/create-provider.js +4 -0
- package/dist/providers/prompt-usage.d.ts +8 -0
- package/dist/providers/prompt-usage.js +52 -0
- package/dist/state-paths.d.ts +2 -0
- package/dist/state-paths.js +6 -0
- package/dist/types.d.ts +41 -0
- package/dist/typing-lease.d.ts +14 -0
- package/dist/typing-lease.js +31 -0
- package/package.json +9 -9
- package/skills/borgee-agent/SKILL.md +12 -4
- package/skills/borgee-agent/scripts/borgee-agent.mjs +89 -0
- package/skills/borgee-agent/scripts/borgee-agent.py +90 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createBorgeePlugin, } from '../plugin-sdk.js';
|
|
2
|
+
import { ExecutionTelemetryOutbox, ExecutionTelemetryOutboxCapacityError, } from '../execution-telemetry.js';
|
|
2
3
|
import { normalizeHostedMessageAttachment } from '../hosted-turn-content.js';
|
|
3
4
|
/**
|
|
4
5
|
* Thin adapter over `@borgee/plugin-sdk` (the same BPP/`/ws/plugin` SDK used by
|
|
@@ -10,11 +11,32 @@ export class SdkChatControlPlane {
|
|
|
10
11
|
pendingMessages = [];
|
|
11
12
|
unsubscribe = null;
|
|
12
13
|
stateUnsubscribe = null;
|
|
14
|
+
telemetryAckUnsubscribe = null;
|
|
15
|
+
executionTelemetryRetry;
|
|
13
16
|
activityTransportReady;
|
|
17
|
+
executionTelemetryOutbox;
|
|
18
|
+
onExecutionTelemetryError;
|
|
14
19
|
connected = false;
|
|
20
|
+
transportOnline = false;
|
|
21
|
+
executionTelemetryCapacityWaitMs;
|
|
15
22
|
me = null;
|
|
16
23
|
constructor(baseUrl, apiKey, createClient = createBorgeePlugin, options = {}) {
|
|
17
|
-
this.client = createClient({
|
|
24
|
+
this.client = createClient({
|
|
25
|
+
baseUrl,
|
|
26
|
+
apiKey,
|
|
27
|
+
...(options.pluginId ? { pluginId: options.pluginId } : {}),
|
|
28
|
+
});
|
|
29
|
+
this.onExecutionTelemetryError =
|
|
30
|
+
options.onExecutionTelemetryError ??
|
|
31
|
+
(() => console.error('[agents-host] execution telemetry persistence failed'));
|
|
32
|
+
this.executionTelemetryCapacityWaitMs =
|
|
33
|
+
options.executionTelemetryCapacityWaitMs ?? 1_000;
|
|
34
|
+
this.executionTelemetryOutbox = new ExecutionTelemetryOutbox(options.executionTelemetryOutboxPath, {
|
|
35
|
+
onRetired: (count) => {
|
|
36
|
+
this.onExecutionTelemetryError(new Error(`Retired ${count} unacknowledged execution telemetry event(s)`));
|
|
37
|
+
},
|
|
38
|
+
policy: options.executionTelemetryOutboxPolicy,
|
|
39
|
+
});
|
|
18
40
|
}
|
|
19
41
|
onStopTurn(handler) {
|
|
20
42
|
this.client.onStopTurn(handler);
|
|
@@ -26,6 +48,7 @@ export class SdkChatControlPlane {
|
|
|
26
48
|
this.activityTransportReady = handler;
|
|
27
49
|
}
|
|
28
50
|
async connect(onMessage) {
|
|
51
|
+
await this.executionTelemetryOutbox.load();
|
|
29
52
|
this.unsubscribe = this.client.on('message', (event) => {
|
|
30
53
|
const message = mapInboundToChannelMessage(event);
|
|
31
54
|
if (!this.connected) {
|
|
@@ -35,13 +58,26 @@ export class SdkChatControlPlane {
|
|
|
35
58
|
onMessage(message);
|
|
36
59
|
});
|
|
37
60
|
this.stateUnsubscribe = this.client.on('connectionState', (state) => {
|
|
61
|
+
this.transportOnline = state.status === 'online';
|
|
38
62
|
if (state.status === 'online') {
|
|
39
63
|
this.activityTransportReady?.();
|
|
64
|
+
void this.flushExecutionTelemetry(true);
|
|
40
65
|
}
|
|
66
|
+
else {
|
|
67
|
+
this.cancelExecutionTelemetryRetry();
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
this.telemetryAckUnsubscribe = this.client.on('executionTelemetryAck', (ack) => {
|
|
71
|
+
void this.executionTelemetryOutbox
|
|
72
|
+
.acknowledge(ack.eventId)
|
|
73
|
+
.then(() => this.scheduleExecutionTelemetryRetry())
|
|
74
|
+
.catch(this.onExecutionTelemetryError);
|
|
41
75
|
});
|
|
42
76
|
try {
|
|
43
77
|
await this.client.connect();
|
|
44
78
|
this.connected = true;
|
|
79
|
+
this.transportOnline =
|
|
80
|
+
this.transportOnline || this.client.connectionState?.status === 'online';
|
|
45
81
|
if (this.client.agentId) {
|
|
46
82
|
this.me = { id: this.client.agentId };
|
|
47
83
|
}
|
|
@@ -49,26 +85,36 @@ export class SdkChatControlPlane {
|
|
|
49
85
|
onMessage(message);
|
|
50
86
|
}
|
|
51
87
|
this.pendingMessages = [];
|
|
88
|
+
await this.flushExecutionTelemetry(true);
|
|
52
89
|
}
|
|
53
90
|
catch (error) {
|
|
91
|
+
this.transportOnline = false;
|
|
92
|
+
this.cancelExecutionTelemetryRetry();
|
|
54
93
|
this.unsubscribe?.();
|
|
55
94
|
this.unsubscribe = null;
|
|
56
95
|
this.stateUnsubscribe?.();
|
|
57
96
|
this.stateUnsubscribe = null;
|
|
97
|
+
this.telemetryAckUnsubscribe?.();
|
|
98
|
+
this.telemetryAckUnsubscribe = null;
|
|
58
99
|
this.pendingMessages = [];
|
|
59
100
|
throw error;
|
|
60
101
|
}
|
|
61
102
|
}
|
|
62
103
|
async close() {
|
|
63
104
|
this.connected = false;
|
|
105
|
+
this.transportOnline = false;
|
|
106
|
+
this.cancelExecutionTelemetryRetry();
|
|
64
107
|
this.pendingMessages = [];
|
|
65
108
|
this.unsubscribe?.();
|
|
66
109
|
this.unsubscribe = null;
|
|
67
110
|
this.stateUnsubscribe?.();
|
|
68
111
|
this.stateUnsubscribe = null;
|
|
112
|
+
this.telemetryAckUnsubscribe?.();
|
|
113
|
+
this.telemetryAckUnsubscribe = null;
|
|
69
114
|
this.activityTransportReady = undefined;
|
|
70
115
|
this.client.onStopTurn(undefined);
|
|
71
116
|
this.client.onStopBackgroundRun?.(undefined);
|
|
117
|
+
await this.executionTelemetryOutbox.settle();
|
|
72
118
|
await this.client.close();
|
|
73
119
|
}
|
|
74
120
|
async postMessage(input) {
|
|
@@ -82,18 +128,74 @@ export class SdkChatControlPlane {
|
|
|
82
128
|
reportActivity(input) {
|
|
83
129
|
this.client.reportActivity(input);
|
|
84
130
|
}
|
|
131
|
+
reportTaskFinished(input) {
|
|
132
|
+
this.client.reportTaskFinished?.(input);
|
|
133
|
+
}
|
|
85
134
|
async editMessage(messageId, content) {
|
|
86
135
|
await this.client.editMessage({ messageId, body: content });
|
|
87
136
|
}
|
|
88
137
|
async deleteMessage(messageId) {
|
|
89
138
|
await this.client.deleteMessage({ messageId });
|
|
90
139
|
}
|
|
91
|
-
|
|
92
|
-
|
|
140
|
+
reportTyping(channelId) {
|
|
141
|
+
this.client.reportTyping(channelId);
|
|
93
142
|
}
|
|
94
143
|
reportTurnActivity(input) {
|
|
95
144
|
return this.client.reportTurnActivity(input);
|
|
96
145
|
}
|
|
146
|
+
async reportExecutionTelemetry(frame) {
|
|
147
|
+
if (!this.client.reportExecutionTelemetry) {
|
|
148
|
+
throw new Error('Plugin SDK execution telemetry is unavailable');
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
await this.executionTelemetryOutbox.enqueue(frame);
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
if (!(error instanceof ExecutionTelemetryOutboxCapacityError) ||
|
|
155
|
+
!this.transportOnline) {
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
await this.flushExecutionTelemetry(true);
|
|
159
|
+
const capacityAvailable = await this.executionTelemetryOutbox.waitForCapacity(this.executionTelemetryCapacityWaitMs);
|
|
160
|
+
if (!capacityAvailable)
|
|
161
|
+
throw error;
|
|
162
|
+
await this.executionTelemetryOutbox.enqueue(frame);
|
|
163
|
+
}
|
|
164
|
+
await this.flushExecutionTelemetry();
|
|
165
|
+
}
|
|
166
|
+
async flushExecutionTelemetry(force = false) {
|
|
167
|
+
if (!this.connected || !this.transportOnline || !this.client.reportExecutionTelemetry)
|
|
168
|
+
return;
|
|
169
|
+
try {
|
|
170
|
+
await this.executionTelemetryOutbox.flush((frame) => {
|
|
171
|
+
this.client.reportExecutionTelemetry?.(frame);
|
|
172
|
+
}, force);
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
this.onExecutionTelemetryError(error);
|
|
176
|
+
}
|
|
177
|
+
finally {
|
|
178
|
+
this.scheduleExecutionTelemetryRetry();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
scheduleExecutionTelemetryRetry() {
|
|
182
|
+
this.cancelExecutionTelemetryRetry();
|
|
183
|
+
if (!this.connected || !this.transportOnline)
|
|
184
|
+
return;
|
|
185
|
+
const delay = this.executionTelemetryOutbox.nextRetryDelayMs();
|
|
186
|
+
if (delay === undefined)
|
|
187
|
+
return;
|
|
188
|
+
this.executionTelemetryRetry = setTimeout(() => {
|
|
189
|
+
this.executionTelemetryRetry = undefined;
|
|
190
|
+
void this.flushExecutionTelemetry();
|
|
191
|
+
}, Math.max(1, delay));
|
|
192
|
+
}
|
|
193
|
+
cancelExecutionTelemetryRetry() {
|
|
194
|
+
if (!this.executionTelemetryRetry)
|
|
195
|
+
return;
|
|
196
|
+
clearTimeout(this.executionTelemetryRetry);
|
|
197
|
+
this.executionTelemetryRetry = undefined;
|
|
198
|
+
}
|
|
97
199
|
async getMe() {
|
|
98
200
|
if (this.me) {
|
|
99
201
|
return this.me;
|
|
@@ -114,6 +216,34 @@ export class SdkChatControlPlane {
|
|
|
114
216
|
kind: user.kind,
|
|
115
217
|
}));
|
|
116
218
|
}
|
|
219
|
+
async listChannelFiles(input) {
|
|
220
|
+
const files = await this.client.listChannelFiles({ channelId: input.channelId });
|
|
221
|
+
return files.map((file) => ({
|
|
222
|
+
id: file.id,
|
|
223
|
+
ownerUserId: file.ownerUserId,
|
|
224
|
+
channelId: file.channelId,
|
|
225
|
+
path: file.path,
|
|
226
|
+
parentPath: file.parentPath,
|
|
227
|
+
name: file.name,
|
|
228
|
+
isDirectory: file.isDirectory,
|
|
229
|
+
mimeType: file.mimeType,
|
|
230
|
+
sizeBytes: file.sizeBytes,
|
|
231
|
+
source: file.source,
|
|
232
|
+
sourceMessageId: file.sourceMessageId,
|
|
233
|
+
createdAt: file.createdAt,
|
|
234
|
+
updatedAt: file.updatedAt,
|
|
235
|
+
}));
|
|
236
|
+
}
|
|
237
|
+
async readChannelFile(input) {
|
|
238
|
+
return await this.client.readChannelFile(input);
|
|
239
|
+
}
|
|
240
|
+
async publishChannelFile(input) {
|
|
241
|
+
const file = await this.client.publishChannelFile(input);
|
|
242
|
+
return {
|
|
243
|
+
...file,
|
|
244
|
+
sourceMessageId: file.sourceMessageId,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
117
247
|
async readChannelHistory(input) {
|
|
118
248
|
const messages = await this.client.readHistory({
|
|
119
249
|
channelId: input.channelId,
|
|
@@ -175,5 +305,6 @@ export function mapInboundToChannelMessage(event) {
|
|
|
175
305
|
content_type: event.message?.contentType,
|
|
176
306
|
attachments: event.message?.attachments?.map(normalizeHostedMessageAttachment),
|
|
177
307
|
created_at: event.createdAt,
|
|
308
|
+
...(event.executionGrant ? { execution_grant: event.executionGrant } : {}),
|
|
178
309
|
};
|
|
179
310
|
}
|
|
@@ -14,7 +14,7 @@ export function buildSkillManualReadLine(skillRuntime) {
|
|
|
14
14
|
*/
|
|
15
15
|
export function buildSkillManualLines(skillRuntime) {
|
|
16
16
|
return [
|
|
17
|
-
"A packaged local CLI reads this Borgee channel and acts on its tasks: channel history, visible participants, this channel's tasks and their properties, and short auxiliary mentions.",
|
|
17
|
+
"A packaged local CLI reads this Borgee channel and acts on its tasks: channel history, channel files, visible participants, this channel's tasks and their properties, and short auxiliary mentions.",
|
|
18
18
|
buildSkillManualReadLine(skillRuntime),
|
|
19
19
|
'Every command the manual documents is already authorized on a turn whose prompt names a gateway credential file. Run them directly and do not ask the user for permission first.',
|
|
20
20
|
];
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { ExecutionGrant, ExecutionTelemetryFrame } from './plugin-sdk.js';
|
|
2
|
+
import type { ProviderKind, ProviderTokenUsage } from './types.js';
|
|
3
|
+
export interface ExecutionTelemetrySink {
|
|
4
|
+
reportExecutionTelemetry(frame: ExecutionTelemetryFrame): Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
export interface StoredExecutionUsage extends ProviderTokenUsage {
|
|
7
|
+
provider: ProviderKind;
|
|
8
|
+
}
|
|
9
|
+
export type ExecutionDeliveryClaim = 'new' | 'recovering' | 'completed';
|
|
10
|
+
export declare class ExecutionDeliveryJournal {
|
|
11
|
+
private readonly path?;
|
|
12
|
+
private readonly maxCompletedEntries;
|
|
13
|
+
private readonly now;
|
|
14
|
+
private entries;
|
|
15
|
+
private loaded;
|
|
16
|
+
private writeQueue;
|
|
17
|
+
constructor(path?: string | undefined, maxCompletedEntries?: number, now?: () => number);
|
|
18
|
+
load(): Promise<void>;
|
|
19
|
+
claim(executionId: string): Promise<ExecutionDeliveryClaim>;
|
|
20
|
+
complete(executionId: string): Promise<void>;
|
|
21
|
+
recordUsage(executionId: string, usage: StoredExecutionUsage): Promise<void>;
|
|
22
|
+
readUsage(executionId: string): Promise<StoredExecutionUsage | undefined>;
|
|
23
|
+
private mutate;
|
|
24
|
+
private isStoredExecutionDelivery;
|
|
25
|
+
private isStoredExecutionUsage;
|
|
26
|
+
}
|
|
27
|
+
export declare class ExecutionTelemetryReporter {
|
|
28
|
+
private readonly grant;
|
|
29
|
+
private readonly sink;
|
|
30
|
+
private readonly now;
|
|
31
|
+
private readonly persistUsage?;
|
|
32
|
+
private started;
|
|
33
|
+
private finished;
|
|
34
|
+
private attemptCount;
|
|
35
|
+
private attemptsSucceeded;
|
|
36
|
+
private provider;
|
|
37
|
+
private pendingUsage;
|
|
38
|
+
private usageFrameDurable;
|
|
39
|
+
private usagePersistence;
|
|
40
|
+
private readonly continuations;
|
|
41
|
+
private finishing?;
|
|
42
|
+
get startFrameDurable(): boolean;
|
|
43
|
+
get terminalFrameDurable(): boolean;
|
|
44
|
+
get allRequiredFramesDurable(): boolean;
|
|
45
|
+
constructor(grant: ExecutionGrant, sink: ExecutionTelemetrySink, now?: () => number, persistUsage?: ((provider: ProviderKind, usage: ProviderTokenUsage) => Promise<void>) | undefined);
|
|
46
|
+
start(): Promise<void>;
|
|
47
|
+
usage(provider: ProviderKind, usage: ProviderTokenUsage): Promise<void>;
|
|
48
|
+
noteAttempt(succeeded: boolean): void;
|
|
49
|
+
noteFailure(): void;
|
|
50
|
+
hold(completion: Promise<boolean>): void;
|
|
51
|
+
finish(succeeded?: boolean): Promise<void>;
|
|
52
|
+
private finalize;
|
|
53
|
+
}
|
|
54
|
+
export interface ExecutionTelemetryOutboxPolicy {
|
|
55
|
+
maxEntries: number;
|
|
56
|
+
maxAttempts: number;
|
|
57
|
+
retentionMs: number;
|
|
58
|
+
retryIntervalMs: number;
|
|
59
|
+
}
|
|
60
|
+
export declare class ExecutionTelemetryOutboxCapacityError extends Error {
|
|
61
|
+
constructor();
|
|
62
|
+
}
|
|
63
|
+
type ExecutionTelemetryOutboxOptions = {
|
|
64
|
+
now?: () => number;
|
|
65
|
+
onRetired?: (count: number) => void;
|
|
66
|
+
policy?: Partial<ExecutionTelemetryOutboxPolicy>;
|
|
67
|
+
};
|
|
68
|
+
export declare class ExecutionTelemetryOutbox {
|
|
69
|
+
private readonly path?;
|
|
70
|
+
private frames;
|
|
71
|
+
private loaded;
|
|
72
|
+
private writeQueue;
|
|
73
|
+
private flushInFlight?;
|
|
74
|
+
private readonly now;
|
|
75
|
+
private readonly onRetired?;
|
|
76
|
+
private readonly policy;
|
|
77
|
+
private readonly capacityWaiters;
|
|
78
|
+
constructor(path?: string | undefined, options?: ExecutionTelemetryOutboxOptions);
|
|
79
|
+
load(): Promise<void>;
|
|
80
|
+
enqueue(frame: ExecutionTelemetryFrame): Promise<void>;
|
|
81
|
+
acknowledge(eventId: string): Promise<void>;
|
|
82
|
+
flush(send: (frame: ExecutionTelemetryFrame) => void, force?: boolean): Promise<void>;
|
|
83
|
+
private flushOnce;
|
|
84
|
+
settle(): Promise<void>;
|
|
85
|
+
pendingCount(): number;
|
|
86
|
+
nextRetryDelayMs(): number | undefined;
|
|
87
|
+
hasCapacity(): boolean;
|
|
88
|
+
waitForCapacity(timeoutMs: number): Promise<boolean>;
|
|
89
|
+
private retireExpired;
|
|
90
|
+
private mutate;
|
|
91
|
+
private persist;
|
|
92
|
+
private frameTimestamp;
|
|
93
|
+
private isStoredEntry;
|
|
94
|
+
private notifyCapacityAvailable;
|
|
95
|
+
}
|
|
96
|
+
export {};
|