@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
package/dist/agents-host.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ChatControlPlane } from './chat/chat-control-plane.js';
|
|
2
2
|
import { type AgentsHostRuntimeOptions } from './debug.js';
|
|
3
|
+
import { ExecutionDeliveryJournal } from './execution-telemetry.js';
|
|
3
4
|
import { type ProviderAdapter } from './providers/provider-adapter.js';
|
|
4
5
|
import type { AgentsHostConfig } from './types.js';
|
|
5
6
|
/**
|
|
@@ -13,6 +14,7 @@ export declare class AgentsHost {
|
|
|
13
14
|
private readonly borgee;
|
|
14
15
|
private readonly backgroundRuns;
|
|
15
16
|
private readonly logger;
|
|
17
|
+
private readonly executionDeliveryJournal;
|
|
16
18
|
private readonly contextInjectionEnabled;
|
|
17
19
|
private readonly collaborationEnabled;
|
|
18
20
|
private readonly collaborationOutcomeModelEnabled;
|
|
@@ -20,6 +22,8 @@ export declare class AgentsHost {
|
|
|
20
22
|
private readonly taskThreadCollaborationContractEnabled;
|
|
21
23
|
private readonly collaborationCapabilitiesDiagnosticsEnabled;
|
|
22
24
|
private readonly activeTurns;
|
|
25
|
+
private readonly executionTelemetryByExecutionId;
|
|
26
|
+
private readonly executionDispatches;
|
|
23
27
|
private readonly awaitingUserByChannel;
|
|
24
28
|
private readonly collaborationOutcomeByChannel;
|
|
25
29
|
/**
|
|
@@ -49,6 +53,7 @@ export declare class AgentsHost {
|
|
|
49
53
|
private controlPlaneClosed;
|
|
50
54
|
constructor(config: AgentsHostConfig, deps?: {
|
|
51
55
|
borgee?: ChatControlPlane;
|
|
56
|
+
executionDeliveryJournal?: ExecutionDeliveryJournal;
|
|
52
57
|
provider?: ProviderAdapter;
|
|
53
58
|
runtimeOptions?: AgentsHostRuntimeOptions;
|
|
54
59
|
});
|
|
@@ -56,6 +61,9 @@ export declare class AgentsHost {
|
|
|
56
61
|
stop(): Promise<void>;
|
|
57
62
|
private trackActiveTurn;
|
|
58
63
|
private dispatchMessage;
|
|
64
|
+
private runClaimedExecution;
|
|
65
|
+
private runMessageWithExecutionTelemetry;
|
|
66
|
+
private createExecutionTelemetryReporter;
|
|
59
67
|
private dispatchAutonomousReply;
|
|
60
68
|
private handleLegacyMessage;
|
|
61
69
|
private handleCollaborationMessage;
|
package/dist/agents-host.js
CHANGED
|
@@ -5,6 +5,8 @@ import { SdkChatControlPlane } from './chat/sdk-chat-control-plane.js';
|
|
|
5
5
|
import { HostLogger, summarizeError } from './debug.js';
|
|
6
6
|
import { toReportedActivity } from './progress-to-activity.js';
|
|
7
7
|
import { BackgroundRunTracker } from './background-runs.js';
|
|
8
|
+
import { ExecutionDeliveryJournal, ExecutionTelemetryReporter, } from './execution-telemetry.js';
|
|
9
|
+
import { startTypingLease } from './typing-lease.js';
|
|
8
10
|
import { ATTENTION_FOLLOW_SEMANTICS_COMPATIBILITY_GATE, COLLABORATION_CAPABILITIES_DIAGNOSTICS_COMPATIBILITY_GATE, COLLABORATION_OUTCOME_MODEL_COMPATIBILITY_GATE, COLLABORATION_SKILL_FIRST_COMPATIBILITY_GATE, CONTEXT_INJECTION_COMPATIBILITY_GATE, LOCALHOST_GATEWAY_COMPATIBILITY_GATE, POLICY_AUDIT_ENFORCEMENT_COMPATIBILITY_GATE, resolveInternalPolicyMode, resolveInternalCompatibilityGates, SKILL_RUNTIME_COMPATIBILITY_GATE, TASK_THREAD_COLLABORATION_CONTRACT_COMPATIBILITY_GATE, TOKEN_BINDING_COMPATIBILITY_GATE, } from './compatibility-gates.js';
|
|
9
11
|
import { buildNormalizedAttentionSnapshot, parsePersistedAttentionSnapshot, } from './context/attention.js';
|
|
10
12
|
import { mergeCompactionSnapshot, parsePersistedCompactionSnapshot, } from './context/compaction.js';
|
|
@@ -14,7 +16,7 @@ import { extractTaskIdFromTaskAssignmentContent, resolveTaskAssignmentStatePath,
|
|
|
14
16
|
import { AuthorizationAuditSink, } from './policy/authorization-audit.js';
|
|
15
17
|
import { createProvider } from './providers/create-provider.js';
|
|
16
18
|
import { ProviderTurnCancelledError } from './providers/provider-adapter.js';
|
|
17
|
-
import { resolveAttentionStatePath, resolveCompactionStatePath, resolveSharedProtocolKickoffDecisionPath, resolveSharedProtocolStatusPath, } from './state-paths.js';
|
|
19
|
+
import { resolveAttentionStatePath, resolveCompactionStatePath, resolveExecutionDeliveryJournalPath, resolveExecutionTelemetryOutboxPath, resolveSharedProtocolKickoffDecisionPath, resolveSharedProtocolStatusPath, } from './state-paths.js';
|
|
18
20
|
import { appendVisibleMention as appendVisibleBodyMention, extractVisibleMentionIds as extractCanonicalMentionIds, } from './visible-mentions.js';
|
|
19
21
|
import { buildHostedIncomingContentText, buildHostedTurnContentParts, extractHostedMessageAttachments, normalizeHostedMessageAttachment, } from './hosted-turn-content.js';
|
|
20
22
|
import { waitForTaskForThread } from './task-thread-resolution.js';
|
|
@@ -110,6 +112,12 @@ class DefaultHostRuntime {
|
|
|
110
112
|
deps.borgee ??
|
|
111
113
|
new SdkChatControlPlane(config.borgeeBaseUrl, config.agent.agentApiKey, undefined, {
|
|
112
114
|
pluginId: `agents-host:${config.agent.provider}`,
|
|
115
|
+
executionTelemetryOutboxPath: resolveExecutionTelemetryOutboxPath(config.stateRootDir),
|
|
116
|
+
onExecutionTelemetryError: (error) => {
|
|
117
|
+
deps.logger.error('execution telemetry transport failed', {
|
|
118
|
+
error: summarizeError(error),
|
|
119
|
+
});
|
|
120
|
+
},
|
|
113
121
|
});
|
|
114
122
|
this.gateway = createLocalhostGatewayController({
|
|
115
123
|
gateEnabled: localhostGatewayGateEnabled,
|
|
@@ -291,6 +299,7 @@ export class AgentsHost {
|
|
|
291
299
|
borgee;
|
|
292
300
|
backgroundRuns;
|
|
293
301
|
logger;
|
|
302
|
+
executionDeliveryJournal;
|
|
294
303
|
contextInjectionEnabled;
|
|
295
304
|
collaborationEnabled;
|
|
296
305
|
collaborationOutcomeModelEnabled;
|
|
@@ -298,6 +307,8 @@ export class AgentsHost {
|
|
|
298
307
|
taskThreadCollaborationContractEnabled;
|
|
299
308
|
collaborationCapabilitiesDiagnosticsEnabled;
|
|
300
309
|
activeTurns = new Set();
|
|
310
|
+
executionTelemetryByExecutionId = new Map();
|
|
311
|
+
executionDispatches = new Map();
|
|
301
312
|
awaitingUserByChannel = new Map();
|
|
302
313
|
collaborationOutcomeByChannel = new Map();
|
|
303
314
|
/**
|
|
@@ -328,6 +339,9 @@ export class AgentsHost {
|
|
|
328
339
|
constructor(config, deps) {
|
|
329
340
|
this.config = config;
|
|
330
341
|
this.logger = new HostLogger(deps?.runtimeOptions);
|
|
342
|
+
this.executionDeliveryJournal =
|
|
343
|
+
deps?.executionDeliveryJournal ??
|
|
344
|
+
new ExecutionDeliveryJournal(resolveExecutionDeliveryJournalPath(config.stateRootDir));
|
|
331
345
|
const compatibilityGates = resolveInternalCompatibilityGates();
|
|
332
346
|
const contextInjectionGateEnabled = compatibilityGates.has(CONTEXT_INJECTION_COMPATIBILITY_GATE);
|
|
333
347
|
this.contextInjectionEnabled = contextInjectionGateEnabled;
|
|
@@ -368,6 +382,14 @@ export class AgentsHost {
|
|
|
368
382
|
cancel: this.provider.cancelBackgroundRun
|
|
369
383
|
? (channelId, providerRunId) => (this.provider.cancelBackgroundRun(channelId, providerRunId))
|
|
370
384
|
: undefined,
|
|
385
|
+
onFinished: (channelId, run) => {
|
|
386
|
+
this.borgee.reportTaskFinished?.({
|
|
387
|
+
channelId,
|
|
388
|
+
taskId: run.runId,
|
|
389
|
+
outcome: run.state,
|
|
390
|
+
...(run.state === 'failed' ? { reason: 'unknown' } : {}),
|
|
391
|
+
});
|
|
392
|
+
},
|
|
371
393
|
});
|
|
372
394
|
}
|
|
373
395
|
async start() {
|
|
@@ -384,6 +406,7 @@ export class AgentsHost {
|
|
|
384
406
|
const bufferedMessages = [];
|
|
385
407
|
let bufferingMessages = true;
|
|
386
408
|
try {
|
|
409
|
+
await this.executionDeliveryJournal.load();
|
|
387
410
|
this.provider.onAutonomousReply?.((reply) => this.dispatchAutonomousReply(reply));
|
|
388
411
|
await this.runtime.start((message) => {
|
|
389
412
|
if (!this.started) {
|
|
@@ -463,16 +486,145 @@ export class AgentsHost {
|
|
|
463
486
|
}
|
|
464
487
|
trackActiveTurn(task) {
|
|
465
488
|
this.activeTurns.add(task);
|
|
466
|
-
void task
|
|
489
|
+
void task
|
|
490
|
+
.catch((error) => {
|
|
491
|
+
this.logger.error('dispatched turn failed', {
|
|
492
|
+
error: summarizeError(error),
|
|
493
|
+
});
|
|
494
|
+
})
|
|
495
|
+
.finally(() => {
|
|
467
496
|
this.activeTurns.delete(task);
|
|
468
497
|
});
|
|
469
498
|
}
|
|
470
499
|
dispatchMessage(message) {
|
|
471
|
-
const
|
|
500
|
+
const executionId = message.execution_grant?.executionId;
|
|
501
|
+
const existing = executionId
|
|
502
|
+
? this.executionDispatches.get(executionId)
|
|
503
|
+
: undefined;
|
|
504
|
+
if (existing) {
|
|
505
|
+
this.trackActiveTurn(existing);
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
const run = () => this.collaborationEnabled
|
|
472
509
|
? this.handleCollaborationMessage(message)
|
|
473
510
|
: this.enqueueChannelTurn(message.channel_id, () => this.handleLegacyMessage(message));
|
|
511
|
+
const work = executionId
|
|
512
|
+
? this.runClaimedExecution(message, run)
|
|
513
|
+
: this.runMessageWithExecutionTelemetry(message, run).then((result) => {
|
|
514
|
+
if (result.error)
|
|
515
|
+
throw result.error;
|
|
516
|
+
});
|
|
517
|
+
const task = executionId
|
|
518
|
+
? work.finally(() => {
|
|
519
|
+
this.executionDispatches.delete(executionId);
|
|
520
|
+
})
|
|
521
|
+
: work;
|
|
522
|
+
if (executionId)
|
|
523
|
+
this.executionDispatches.set(executionId, task);
|
|
474
524
|
this.trackActiveTurn(task);
|
|
475
525
|
}
|
|
526
|
+
async runClaimedExecution(message, run) {
|
|
527
|
+
const grant = message.execution_grant;
|
|
528
|
+
const claim = await this.executionDeliveryJournal.claim(grant.executionId);
|
|
529
|
+
if (claim === 'completed')
|
|
530
|
+
return;
|
|
531
|
+
if (claim === 'recovering') {
|
|
532
|
+
if (this.borgee.reportExecutionTelemetry) {
|
|
533
|
+
let reporter = this.executionTelemetryByExecutionId.get(grant.executionId);
|
|
534
|
+
const restoredReporter = !reporter;
|
|
535
|
+
reporter ??= this.createExecutionTelemetryReporter(grant);
|
|
536
|
+
this.executionTelemetryByExecutionId.set(grant.executionId, reporter);
|
|
537
|
+
await reporter.start();
|
|
538
|
+
if (restoredReporter) {
|
|
539
|
+
const persistedUsage = await this.executionDeliveryJournal.readUsage(grant.executionId);
|
|
540
|
+
if (persistedUsage) {
|
|
541
|
+
const { provider, ...usage } = persistedUsage;
|
|
542
|
+
await reporter.usage(provider, usage);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
reporter.noteFailure();
|
|
546
|
+
let finishError;
|
|
547
|
+
try {
|
|
548
|
+
await reporter.finish(false);
|
|
549
|
+
}
|
|
550
|
+
catch (error) {
|
|
551
|
+
finishError = error;
|
|
552
|
+
}
|
|
553
|
+
if (reporter.allRequiredFramesDurable) {
|
|
554
|
+
await this.executionDeliveryJournal.complete(grant.executionId);
|
|
555
|
+
this.executionTelemetryByExecutionId.delete(grant.executionId);
|
|
556
|
+
}
|
|
557
|
+
if (finishError)
|
|
558
|
+
throw finishError;
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
throw new Error('Execution telemetry is unavailable for interrupted delivery recovery');
|
|
562
|
+
}
|
|
563
|
+
const result = await this.runMessageWithExecutionTelemetry(message, run);
|
|
564
|
+
if (result.allRequiredFramesDurable) {
|
|
565
|
+
await this.executionDeliveryJournal.complete(grant.executionId);
|
|
566
|
+
}
|
|
567
|
+
if (result.error)
|
|
568
|
+
throw result.error;
|
|
569
|
+
}
|
|
570
|
+
async runMessageWithExecutionTelemetry(message, run) {
|
|
571
|
+
const grant = message.execution_grant;
|
|
572
|
+
const reporter = grant && this.borgee.reportExecutionTelemetry
|
|
573
|
+
? this.executionTelemetryByExecutionId.get(grant.executionId) ??
|
|
574
|
+
this.createExecutionTelemetryReporter(grant)
|
|
575
|
+
: undefined;
|
|
576
|
+
if (grant && !reporter) {
|
|
577
|
+
return {
|
|
578
|
+
allRequiredFramesDurable: false,
|
|
579
|
+
error: new Error('Execution telemetry is unavailable for admitted delivery'),
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
if (grant && reporter) {
|
|
583
|
+
this.executionTelemetryByExecutionId.set(grant.executionId, reporter);
|
|
584
|
+
}
|
|
585
|
+
let runError;
|
|
586
|
+
try {
|
|
587
|
+
await run();
|
|
588
|
+
}
|
|
589
|
+
catch (error) {
|
|
590
|
+
reporter?.noteFailure();
|
|
591
|
+
runError = error;
|
|
592
|
+
}
|
|
593
|
+
let finishError;
|
|
594
|
+
if (reporter) {
|
|
595
|
+
try {
|
|
596
|
+
await reporter.finish();
|
|
597
|
+
}
|
|
598
|
+
catch (error) {
|
|
599
|
+
finishError = error;
|
|
600
|
+
this.logger.error('execution telemetry finish failed', {
|
|
601
|
+
error: summarizeError(error),
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
finally {
|
|
605
|
+
if (reporter.allRequiredFramesDurable &&
|
|
606
|
+
grant &&
|
|
607
|
+
this.executionTelemetryByExecutionId.get(grant.executionId) === reporter) {
|
|
608
|
+
this.executionTelemetryByExecutionId.delete(grant.executionId);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
const error = runError && finishError
|
|
613
|
+
? new AggregateError([runError, finishError], 'Execution and telemetry finalization both failed')
|
|
614
|
+
: runError ?? finishError;
|
|
615
|
+
return {
|
|
616
|
+
allRequiredFramesDurable: reporter?.allRequiredFramesDurable ?? grant === undefined,
|
|
617
|
+
...(error ? { error } : {}),
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
createExecutionTelemetryReporter(grant) {
|
|
621
|
+
return new ExecutionTelemetryReporter(grant, {
|
|
622
|
+
reportExecutionTelemetry: (frame) => this.borgee.reportExecutionTelemetry(frame),
|
|
623
|
+
}, Date.now, (provider, usage) => this.executionDeliveryJournal.recordUsage(grant.executionId, {
|
|
624
|
+
provider,
|
|
625
|
+
...usage,
|
|
626
|
+
}));
|
|
627
|
+
}
|
|
476
628
|
dispatchAutonomousReply(reply) {
|
|
477
629
|
if (!this.started
|
|
478
630
|
|| this.controlPlaneClosed
|
|
@@ -2314,17 +2466,6 @@ export class AgentsHost {
|
|
|
2314
2466
|
? 'silent-kickoff'
|
|
2315
2467
|
: undefined;
|
|
2316
2468
|
const turnExecutionId = activeTurn?.turnExecutionId ?? null;
|
|
2317
|
-
const stopTyping = silentTurn ? () => { } : this.borgee.startTyping(channelId);
|
|
2318
|
-
const collaborationGrounding = await this.buildCollaborationGrounding(authorId, options?.incomingAuthorKind, activeTurn, options?.kickoff);
|
|
2319
|
-
const publishesCollaborationDraft = !silentTurn &&
|
|
2320
|
-
!deferDelivery &&
|
|
2321
|
-
this.collaborationEnabled &&
|
|
2322
|
-
turnExecutionId != null &&
|
|
2323
|
-
activeTurn?.protocol === undefined;
|
|
2324
|
-
const supportsProviderProgress = this.provider.capabilities?.progressUpdates?.support === 'supported';
|
|
2325
|
-
const supportsBackgroundRunLifecycle = this.provider.capabilities?.backgroundRuns?.lifecycle === 'supported';
|
|
2326
|
-
const reportsProviderActivity = !silentTurn && supportsProviderProgress;
|
|
2327
|
-
const observeProviderProgress = supportsProviderProgress || supportsBackgroundRunLifecycle;
|
|
2328
2469
|
const turnContext = {
|
|
2329
2470
|
eventType: msg.type,
|
|
2330
2471
|
channelId,
|
|
@@ -2337,26 +2478,58 @@ export class AgentsHost {
|
|
|
2337
2478
|
};
|
|
2338
2479
|
const turnStartedAt = Date.now();
|
|
2339
2480
|
this.logger.debug('starting turn', turnContext);
|
|
2481
|
+
const executionTelemetry = msg.execution_grant
|
|
2482
|
+
? this.executionTelemetryByExecutionId.get(msg.execution_grant.executionId)
|
|
2483
|
+
: undefined;
|
|
2484
|
+
let executionSucceeded = false;
|
|
2485
|
+
let executionTelemetryStartFailed = false;
|
|
2340
2486
|
let reply;
|
|
2341
2487
|
let publicReplyText;
|
|
2342
2488
|
let visibleReplyBody;
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
? this.collaborationOutcomeByChannel.get(channelId)
|
|
2346
|
-
: undefined;
|
|
2347
|
-
const missedCollaborationDiagnostic = this.resolveMissedCollaborationDiagnosticForTurn(channelId);
|
|
2348
|
-
const collaborationCapabilities = this.resolveCollaborationCapabilityDeclarationForTurn(taskThreadContext);
|
|
2349
|
-
const incomingParts = options?.incomingParts
|
|
2350
|
-
?? buildHostedTurnContentParts({
|
|
2351
|
-
text: String(msg.content ?? msg.body ?? content),
|
|
2352
|
-
attachments: msg.attachments,
|
|
2353
|
-
});
|
|
2354
|
-
const activityReporter = silentTurn
|
|
2355
|
-
? undefined
|
|
2356
|
-
: this.beginTurnActivity(channelId, activeTurn?.generation);
|
|
2357
|
-
await this.ensureCompactionSnapshotLoaded(channelId);
|
|
2358
|
-
const compactionSnapshot = this.compactionSnapshotByChannel.get(channelId);
|
|
2489
|
+
let activityReporter;
|
|
2490
|
+
let typingLease;
|
|
2359
2491
|
try {
|
|
2492
|
+
if (!silentTurn) {
|
|
2493
|
+
typingLease = startTypingLease(() => this.borgee.reportTyping(channelId));
|
|
2494
|
+
}
|
|
2495
|
+
const collaborationGrounding = await this.buildCollaborationGrounding(authorId, options?.incomingAuthorKind, activeTurn, options?.kickoff);
|
|
2496
|
+
const publishesCollaborationDraft = !silentTurn &&
|
|
2497
|
+
!deferDelivery &&
|
|
2498
|
+
this.collaborationEnabled &&
|
|
2499
|
+
turnExecutionId != null &&
|
|
2500
|
+
activeTurn?.protocol === undefined;
|
|
2501
|
+
const supportsProviderProgress = this.provider.capabilities?.progressUpdates?.support === 'supported';
|
|
2502
|
+
const supportsBackgroundRunLifecycle = this.provider.capabilities?.backgroundRuns?.lifecycle === 'supported';
|
|
2503
|
+
const reportsProviderActivity = !silentTurn && supportsProviderProgress;
|
|
2504
|
+
const observeProviderProgress = supportsProviderProgress || supportsBackgroundRunLifecycle;
|
|
2505
|
+
const taskThreadCollaborationContract = this.resolveTaskThreadCollaborationContractForTurn(msg, taskThreadContext);
|
|
2506
|
+
const collaborationOutcome = this.collaborationOutcomeModelEnabled
|
|
2507
|
+
? this.collaborationOutcomeByChannel.get(channelId)
|
|
2508
|
+
: undefined;
|
|
2509
|
+
const missedCollaborationDiagnostic = this.resolveMissedCollaborationDiagnosticForTurn(channelId);
|
|
2510
|
+
const collaborationCapabilities = this.resolveCollaborationCapabilityDeclarationForTurn(taskThreadContext);
|
|
2511
|
+
const incomingParts = options?.incomingParts
|
|
2512
|
+
?? buildHostedTurnContentParts({
|
|
2513
|
+
text: String(msg.content ?? msg.body ?? content),
|
|
2514
|
+
attachments: msg.attachments,
|
|
2515
|
+
});
|
|
2516
|
+
activityReporter = silentTurn
|
|
2517
|
+
? undefined
|
|
2518
|
+
: this.beginTurnActivity(channelId, activeTurn?.generation);
|
|
2519
|
+
await this.ensureCompactionSnapshotLoaded(channelId);
|
|
2520
|
+
const compactionSnapshot = this.compactionSnapshotByChannel.get(channelId);
|
|
2521
|
+
if (executionTelemetry) {
|
|
2522
|
+
try {
|
|
2523
|
+
await executionTelemetry.start();
|
|
2524
|
+
}
|
|
2525
|
+
catch (error) {
|
|
2526
|
+
executionTelemetryStartFailed = true;
|
|
2527
|
+
this.logger.error('execution telemetry start failed', {
|
|
2528
|
+
error: summarizeError(error),
|
|
2529
|
+
});
|
|
2530
|
+
throw error;
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2360
2533
|
reply = await this.provider.generateReply({
|
|
2361
2534
|
agentName: this.config.agent.agentName,
|
|
2362
2535
|
provider: this.config.agent.provider,
|
|
@@ -2397,42 +2570,61 @@ export class AgentsHost {
|
|
|
2397
2570
|
: { taskAssignmentContextOverridePersistence: 'ephemeral' }),
|
|
2398
2571
|
}
|
|
2399
2572
|
: {}),
|
|
2400
|
-
}, observeProviderProgress
|
|
2573
|
+
}, observeProviderProgress || executionTelemetry
|
|
2401
2574
|
? {
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
return;
|
|
2411
|
-
}
|
|
2412
|
-
}
|
|
2413
|
-
if (!this.canAcceptTurnOutput(channelId, activeTurn?.generation)) {
|
|
2414
|
-
return;
|
|
2575
|
+
...(executionTelemetry
|
|
2576
|
+
? {
|
|
2577
|
+
onUsage: (usage) => {
|
|
2578
|
+
void executionTelemetry.usage(this.config.agent.provider, usage);
|
|
2579
|
+
},
|
|
2580
|
+
onExecutionContinuation: (completion) => {
|
|
2581
|
+
executionTelemetry.hold(completion);
|
|
2582
|
+
},
|
|
2415
2583
|
}
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2584
|
+
: {}),
|
|
2585
|
+
...(observeProviderProgress
|
|
2586
|
+
? {
|
|
2587
|
+
onProgress: (update) => {
|
|
2588
|
+
if (supportsBackgroundRunLifecycle) {
|
|
2589
|
+
this.backgroundRuns.observe(channelId, update);
|
|
2590
|
+
if (update.type === 'activity' && update.activity.background === true) {
|
|
2591
|
+
this.borgee.reportActivity?.({
|
|
2592
|
+
channelId,
|
|
2593
|
+
activity: { shape: 'activity', ...update.activity },
|
|
2594
|
+
});
|
|
2595
|
+
return;
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
if (!this.canAcceptTurnOutput(channelId, activeTurn?.generation)) {
|
|
2599
|
+
return;
|
|
2600
|
+
}
|
|
2601
|
+
if (update.type === 'text'
|
|
2602
|
+
&& update.stream === 'answer'
|
|
2603
|
+
&& hasVisibleText(update.text)) {
|
|
2604
|
+
typingLease?.renew();
|
|
2605
|
+
}
|
|
2606
|
+
if (supportsProviderProgress && update.type === 'compaction') {
|
|
2607
|
+
this.noteCompactionProgress(channelId, update.compaction, {
|
|
2608
|
+
turnExecutionId: activeTurn?.turnExecutionId ?? undefined,
|
|
2609
|
+
});
|
|
2610
|
+
return;
|
|
2611
|
+
}
|
|
2612
|
+
const reportedActivity = toReportedActivity(update);
|
|
2613
|
+
if (reportsProviderActivity && reportedActivity) {
|
|
2614
|
+
activityReporter?.progress(reportedActivity);
|
|
2615
|
+
}
|
|
2616
|
+
if (!supportsProviderProgress ||
|
|
2617
|
+
update.type !== 'text' ||
|
|
2618
|
+
update.stream !== 'answer') {
|
|
2619
|
+
return;
|
|
2620
|
+
}
|
|
2621
|
+
if (!publishesCollaborationDraft) {
|
|
2622
|
+
return;
|
|
2623
|
+
}
|
|
2624
|
+
this.publishCollaborationDraft(channelId, activeTurn, update.text);
|
|
2625
|
+
},
|
|
2433
2626
|
}
|
|
2434
|
-
|
|
2435
|
-
},
|
|
2627
|
+
: {}),
|
|
2436
2628
|
}
|
|
2437
2629
|
: undefined);
|
|
2438
2630
|
publicReplyText = resolvePublicReplyText(reply);
|
|
@@ -2460,6 +2652,7 @@ export class AgentsHost {
|
|
|
2460
2652
|
return { reply, stale: true, completed: false };
|
|
2461
2653
|
}
|
|
2462
2654
|
if (silentTurn) {
|
|
2655
|
+
executionSucceeded = true;
|
|
2463
2656
|
this.logger.debug('completed turn', {
|
|
2464
2657
|
...turnContext,
|
|
2465
2658
|
durationMs: Date.now() - turnStartedAt,
|
|
@@ -2480,6 +2673,7 @@ export class AgentsHost {
|
|
|
2480
2673
|
};
|
|
2481
2674
|
}
|
|
2482
2675
|
if (deferDelivery) {
|
|
2676
|
+
executionSucceeded = true;
|
|
2483
2677
|
this.logger.debug('completed turn', {
|
|
2484
2678
|
...turnContext,
|
|
2485
2679
|
durationMs: Date.now() - turnStartedAt,
|
|
@@ -2516,6 +2710,7 @@ export class AgentsHost {
|
|
|
2516
2710
|
if (publishesCollaborationDraft) {
|
|
2517
2711
|
this.finalizeCollaborationDraft(channelId, activeTurn, posted.messageId, visibleReplyBody);
|
|
2518
2712
|
}
|
|
2713
|
+
executionSucceeded = true;
|
|
2519
2714
|
return {
|
|
2520
2715
|
reply,
|
|
2521
2716
|
deliveredMessageId: posted.messageId,
|
|
@@ -2539,6 +2734,7 @@ export class AgentsHost {
|
|
|
2539
2734
|
turnExecutionId: activeTurn.turnExecutionId,
|
|
2540
2735
|
});
|
|
2541
2736
|
}
|
|
2737
|
+
executionSucceeded = true;
|
|
2542
2738
|
return {
|
|
2543
2739
|
reply,
|
|
2544
2740
|
deliveredMessageId: null,
|
|
@@ -2557,6 +2753,9 @@ export class AgentsHost {
|
|
|
2557
2753
|
turnExecutionId: activeTurn.turnExecutionId,
|
|
2558
2754
|
});
|
|
2559
2755
|
}
|
|
2756
|
+
if (executionTelemetryStartFailed) {
|
|
2757
|
+
throw error;
|
|
2758
|
+
}
|
|
2560
2759
|
if (error instanceof ProviderTurnCancelledError) {
|
|
2561
2760
|
const cancelledReplyText = error.publicReplyText?.trim();
|
|
2562
2761
|
if (!silentTurn &&
|
|
@@ -2634,7 +2833,10 @@ export class AgentsHost {
|
|
|
2634
2833
|
};
|
|
2635
2834
|
}
|
|
2636
2835
|
finally {
|
|
2637
|
-
|
|
2836
|
+
if (executionTelemetry) {
|
|
2837
|
+
executionTelemetry.noteAttempt(executionSucceeded);
|
|
2838
|
+
}
|
|
2839
|
+
typingLease?.stop();
|
|
2638
2840
|
activityReporter?.end();
|
|
2639
2841
|
}
|
|
2640
2842
|
}
|
|
@@ -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;
|
package/dist/background-runs.js
CHANGED
|
@@ -99,6 +99,10 @@ export class BackgroundRunTracker {
|
|
|
99
99
|
historySize -= 1;
|
|
100
100
|
}
|
|
101
101
|
this.publish(run);
|
|
102
|
+
if (isConcreteTerminal(state) &&
|
|
103
|
+
(startsNewExecution || !held || !isConcreteTerminal(held.state))) {
|
|
104
|
+
this.options.onFinished?.(channelId, { runId: run.runId, state });
|
|
105
|
+
}
|
|
102
106
|
}
|
|
103
107
|
replay() {
|
|
104
108
|
for (const run of this.runs.values()) {
|
|
@@ -166,10 +170,10 @@ function runState(status) {
|
|
|
166
170
|
}
|
|
167
171
|
}
|
|
168
172
|
function isTerminal(state) {
|
|
169
|
-
return (state === '
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
+
return (isConcreteTerminal(state) || state === 'unknown');
|
|
174
|
+
}
|
|
175
|
+
function isConcreteTerminal(state) {
|
|
176
|
+
return state === 'completed' || state === 'failed' || state === 'cancelled';
|
|
173
177
|
}
|
|
174
178
|
function stableRunId(channelId, providerRunId, startedAt, executionId) {
|
|
175
179
|
const digest = createHash('sha256')
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { AgentActivity, ReportTurnActivityInput, StopBackgroundRunHandler, StopTurnHandler, TurnActivityReporter } from '../plugin-sdk.js';
|
|
2
|
-
import type { ChannelSummary, ChannelHistoryEntry, ChannelMessageEvent, CreateTaskInput, DirectoryUser, MeResponseUser, PostMessageInput, PostedMessage, ReadChannelHistoryInput, Task, UpdateTaskInput } from '../types.js';
|
|
1
|
+
import type { AgentActivity, ExecutionTelemetryFrame, ReportTaskFinishedInput, ReportTurnActivityInput, StopBackgroundRunHandler, StopTurnHandler, TurnActivityReporter } from '../plugin-sdk.js';
|
|
2
|
+
import type { ChannelSummary, ChannelFileEntry, ChannelFileContent, PublishChannelFileInput, 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;
|
|
@@ -9,15 +9,26 @@ export interface ChatControlPlane {
|
|
|
9
9
|
postMessage(input: PostMessageInput): Promise<PostedMessage>;
|
|
10
10
|
editMessage(messageId: string, content: string): Promise<void>;
|
|
11
11
|
deleteMessage(messageId: string): Promise<void>;
|
|
12
|
-
|
|
12
|
+
reportTyping(channelId: string): void;
|
|
13
13
|
/** Opens one turn's report on the activity rail. Fire-and-forget: nothing recovers what it sends. */
|
|
14
14
|
reportTurnActivity(input: ReportTurnActivityInput): TurnActivityReporter;
|
|
15
|
+
reportExecutionTelemetry?(frame: ExecutionTelemetryFrame): Promise<void>;
|
|
16
|
+
reportTaskFinished?(input: ReportTaskFinishedInput): void;
|
|
15
17
|
reportActivity?(input: {
|
|
16
18
|
channelId: string;
|
|
17
19
|
activity: AgentActivity;
|
|
18
20
|
}): void;
|
|
19
21
|
getMe(): Promise<MeResponseUser>;
|
|
20
22
|
listUsers(): Promise<DirectoryUser[]>;
|
|
23
|
+
listChannelFiles(input: {
|
|
24
|
+
channelId: string;
|
|
25
|
+
}): Promise<ChannelFileEntry[]>;
|
|
26
|
+
readChannelFile(input: {
|
|
27
|
+
channelId: string;
|
|
28
|
+
path: string;
|
|
29
|
+
textOnly: boolean;
|
|
30
|
+
}): Promise<ChannelFileContent>;
|
|
31
|
+
publishChannelFile(input: PublishChannelFileInput): Promise<ChannelFileEntry>;
|
|
21
32
|
listChannels(): Promise<ChannelSummary[]>;
|
|
22
33
|
readChannelHistory(input: ReadChannelHistoryInput): Promise<ChannelHistoryEntry[]>;
|
|
23
34
|
createTask(input: CreateTaskInput): Promise<Task>;
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import { type BorgeePluginClient, type BorgeePluginOptions, type InboundMessageEvent, type ReportTurnActivityInput, type StopTurnHandler, type StopBackgroundRunHandler, type TurnActivityReporter } from '../plugin-sdk.js';
|
|
2
|
-
import
|
|
1
|
+
import { type BorgeePluginClient, type BorgeePluginOptions, type ExecutionTelemetryFrame, type InboundMessageEvent, type ReportTaskFinishedInput, type ReportTurnActivityInput, type StopTurnHandler, type StopBackgroundRunHandler, type TurnActivityReporter } from '../plugin-sdk.js';
|
|
2
|
+
import { type ExecutionTelemetryOutboxPolicy } from '../execution-telemetry.js';
|
|
3
|
+
import type { ChannelSummary, ChannelFileEntry, ChannelFileContent, PublishChannelFileInput, ChannelHistoryEntry, ChannelMessageEvent, CreateTaskInput, DirectoryUser, MeResponseUser, PostMessageInput, PostedMessage, ReadChannelHistoryInput, Task, UpdateTaskInput } from '../types.js';
|
|
3
4
|
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' | '
|
|
5
|
+
type PluginClientLike = Pick<BorgeePluginClient, 'agentId' | 'close' | 'connect' | 'createTask' | 'deleteMessage' | 'editMessage' | 'getMe' | 'getTask' | 'listChannelFiles' | 'readChannelFile' | 'publishChannelFile' | 'listChannels' | 'listTasks' | 'listUsers' | 'on' | 'onStopTurn' | 'onStopBackgroundRun' | 'reportActivity' | 'readHistory' | 'reportTurnActivity' | 'reportTyping' | 'sendMessage' | 'updateTask' | 'setTaskProperty' | 'deleteTaskProperty'> & Partial<Pick<BorgeePluginClient, 'connectionState' | 'reportExecutionTelemetry' | 'reportTaskFinished'>>;
|
|
5
6
|
type PluginClientFactory = (options: BorgeePluginOptions) => PluginClientLike;
|
|
6
|
-
type SdkChatControlPlaneOptions = Pick<BorgeePluginOptions, 'pluginId'
|
|
7
|
+
type SdkChatControlPlaneOptions = Pick<BorgeePluginOptions, 'pluginId'> & {
|
|
8
|
+
executionTelemetryCapacityWaitMs?: number;
|
|
9
|
+
executionTelemetryOutboxPath?: string;
|
|
10
|
+
executionTelemetryOutboxPolicy?: Partial<ExecutionTelemetryOutboxPolicy>;
|
|
11
|
+
onExecutionTelemetryError?: (error: unknown) => void;
|
|
12
|
+
};
|
|
7
13
|
/**
|
|
8
14
|
* Thin adapter over `@borgee/plugin-sdk` (the same BPP/`/ws/plugin` SDK used by
|
|
9
15
|
* the OpenClaw plugin) that implements the minimal `ChatControlPlane` surface
|
|
@@ -14,8 +20,14 @@ export declare class SdkChatControlPlane implements ChatControlPlane {
|
|
|
14
20
|
private pendingMessages;
|
|
15
21
|
private unsubscribe;
|
|
16
22
|
private stateUnsubscribe;
|
|
23
|
+
private telemetryAckUnsubscribe;
|
|
24
|
+
private executionTelemetryRetry?;
|
|
17
25
|
private activityTransportReady;
|
|
26
|
+
private readonly executionTelemetryOutbox;
|
|
27
|
+
private readonly onExecutionTelemetryError;
|
|
18
28
|
private connected;
|
|
29
|
+
private transportOnline;
|
|
30
|
+
private readonly executionTelemetryCapacityWaitMs;
|
|
19
31
|
private me;
|
|
20
32
|
constructor(baseUrl: string, apiKey: string, createClient?: PluginClientFactory, options?: SdkChatControlPlaneOptions);
|
|
21
33
|
onStopTurn(handler: StopTurnHandler | undefined): void;
|
|
@@ -25,12 +37,26 @@ export declare class SdkChatControlPlane implements ChatControlPlane {
|
|
|
25
37
|
close(): Promise<void>;
|
|
26
38
|
postMessage(input: PostMessageInput): Promise<PostedMessage>;
|
|
27
39
|
reportActivity(input: Parameters<BorgeePluginClient['reportActivity']>[0]): void;
|
|
40
|
+
reportTaskFinished(input: ReportTaskFinishedInput): void;
|
|
28
41
|
editMessage(messageId: string, content: string): Promise<void>;
|
|
29
42
|
deleteMessage(messageId: string): Promise<void>;
|
|
30
|
-
|
|
43
|
+
reportTyping(channelId: string): void;
|
|
31
44
|
reportTurnActivity(input: ReportTurnActivityInput): TurnActivityReporter;
|
|
45
|
+
reportExecutionTelemetry(frame: ExecutionTelemetryFrame): Promise<void>;
|
|
46
|
+
private flushExecutionTelemetry;
|
|
47
|
+
private scheduleExecutionTelemetryRetry;
|
|
48
|
+
private cancelExecutionTelemetryRetry;
|
|
32
49
|
getMe(): Promise<MeResponseUser>;
|
|
33
50
|
listUsers(): Promise<DirectoryUser[]>;
|
|
51
|
+
listChannelFiles(input: {
|
|
52
|
+
channelId: string;
|
|
53
|
+
}): Promise<ChannelFileEntry[]>;
|
|
54
|
+
readChannelFile(input: {
|
|
55
|
+
channelId: string;
|
|
56
|
+
path: string;
|
|
57
|
+
textOnly: boolean;
|
|
58
|
+
}): Promise<ChannelFileContent>;
|
|
59
|
+
publishChannelFile(input: PublishChannelFileInput): Promise<ChannelFileEntry>;
|
|
34
60
|
readChannelHistory(input: ReadChannelHistoryInput): Promise<ChannelHistoryEntry[]>;
|
|
35
61
|
listChannels(): Promise<ChannelSummary[]>;
|
|
36
62
|
createTask(input: CreateTaskInput): Promise<Task>;
|