@borgee/agents-host 0.2.101 → 0.2.110

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.
Files changed (84) hide show
  1. package/README.md +77 -5
  2. package/dist/agents-host.d.ts +8 -0
  3. package/dist/agents-host.js +355 -23
  4. package/dist/catalog-worker.js +21 -0
  5. package/dist/catalog-worker.js.map +7 -0
  6. package/dist/chat/chat-control-plane.d.ts +7 -2
  7. package/dist/chat/normalized-input.d.ts +9 -0
  8. package/dist/chat/normalized-input.js +24 -0
  9. package/dist/chat/sdk-chat-control-plane.d.ts +18 -3
  10. package/dist/chat/sdk-chat-control-plane.js +44 -9
  11. package/dist/cli-args.d.ts +1 -1
  12. package/dist/cli-args.js +13 -4
  13. package/dist/config.d.ts +3 -1
  14. package/dist/config.js +25 -0
  15. package/dist/context/injection.d.ts +16 -0
  16. package/dist/context/injection.js +37 -21
  17. package/dist/context/prompt.d.ts +12 -0
  18. package/dist/context/prompt.js +25 -0
  19. package/dist/context/turn-preparation.d.ts +2 -1
  20. package/dist/context/turn-preparation.js +15 -0
  21. package/dist/gateway/localhost-gateway.js +33 -8
  22. package/dist/hosted-turn-content.js +3 -2
  23. package/dist/local-config.js +34 -1
  24. package/dist/managed-daemon.js +15 -0
  25. package/dist/native-environment.d.ts +2 -0
  26. package/dist/native-environment.js +9 -0
  27. package/dist/native-protocol-types/codec.d.ts +49 -0
  28. package/dist/native-protocol-types/commands.d.ts +71 -0
  29. package/dist/native-protocol-types/compatibility.d.ts +5 -0
  30. package/dist/native-protocol-types/cursor.d.ts +7 -0
  31. package/dist/native-protocol-types/envelope.d.ts +1105 -0
  32. package/dist/native-protocol-types/file-changes.d.ts +20 -0
  33. package/dist/native-protocol-types/history.d.ts +487 -0
  34. package/dist/native-protocol-types/index.d.ts +18 -0
  35. package/dist/native-protocol-types/interactions.d.ts +619 -0
  36. package/dist/native-protocol-types/messages.d.ts +2482 -0
  37. package/dist/native-protocol-types/public-validation.d.ts +10 -0
  38. package/dist/native-protocol-types/remote-host-uplink.d.ts +67 -0
  39. package/dist/native-protocol-types/resources.d.ts +126 -0
  40. package/dist/native-protocol-types/session-settings.d.ts +22 -0
  41. package/dist/native-protocol-types/snapshot.d.ts +575 -0
  42. package/dist/native-protocol-types/timeline.d.ts +1503 -0
  43. package/dist/native-protocol-types/tool-result.d.ts +26 -0
  44. package/dist/native-protocol-types/uplink.d.ts +56 -0
  45. package/dist/native-protocol-types/version.d.ts +5 -0
  46. package/dist/native-provider-types/commands.d.ts +51 -0
  47. package/dist/native-provider-types/control.d.ts +192 -0
  48. package/dist/native-provider-types/file-changes.d.ts +9 -0
  49. package/dist/native-provider-types/index.d.ts +8 -0
  50. package/dist/native-provider-types/interactions.d.ts +7 -0
  51. package/dist/native-provider-types/observation.d.ts +209 -0
  52. package/dist/native-provider-types/provider.d.ts +124 -0
  53. package/dist/native-provider-types/session-settings.d.ts +17 -0
  54. package/dist/native-provider-types/testing.d.ts +27 -0
  55. package/dist/native-provider-types/tool-result.d.ts +21 -0
  56. package/dist/native-providers.d.ts +21 -0
  57. package/dist/native-providers.js +18402 -0
  58. package/dist/native-providers.js.map +7 -0
  59. package/dist/plugin-sdk.js +10640 -225
  60. package/dist/plugin-sdk.js.map +4 -4
  61. package/dist/policy/copilot-permission.js +1 -8
  62. package/dist/providers/copilot/sdk-session.js +8 -1
  63. package/dist/providers/create-provider.js +20 -0
  64. package/dist/providers/normalized/adapter.d.ts +107 -0
  65. package/dist/providers/normalized/adapter.js +1650 -0
  66. package/dist/providers/normalized/control-policy.d.ts +5 -0
  67. package/dist/providers/normalized/control-policy.js +12 -0
  68. package/dist/providers/normalized/interactions.d.ts +3 -0
  69. package/dist/providers/normalized/interactions.js +39 -0
  70. package/dist/providers/normalized/legacy-input-ownership.d.ts +34 -0
  71. package/dist/providers/normalized/legacy-input-ownership.js +123 -0
  72. package/dist/providers/normalized/session-management.d.ts +67 -0
  73. package/dist/providers/normalized/session-management.js +334 -0
  74. package/dist/providers/normalized/session-ownership.d.ts +3 -0
  75. package/dist/providers/normalized/session-ownership.js +90 -0
  76. package/dist/providers/provider-adapter.d.ts +43 -1
  77. package/dist/providers/provider-adapter.js +8 -0
  78. package/dist/types.d.ts +34 -9
  79. package/dist/vendor/agent-provider-codex/LICENSE +211 -0
  80. package/dist/vendor/agent-provider-codex/NOTICE +13 -0
  81. package/package.json +22 -13
  82. package/skills/borgee-agent/SKILL.md +1 -1
  83. package/skills/borgee-agent/scripts/borgee-agent.mjs +24 -3
  84. package/skills/borgee-agent/scripts/borgee-agent.py +28 -2
@@ -1,3 +1,4 @@
1
+ import { readNormalizedInputText, readDurableInputText } from './chat/normalized-input.js';
1
2
  import { randomUUID } from 'node:crypto';
2
3
  import { chmod, link, mkdir, readFile, readdir, rename, unlink, writeFile } from 'node:fs/promises';
3
4
  import { dirname, join, resolve } from 'node:path';
@@ -7,6 +8,8 @@ import { toReportedActivity } from './progress-to-activity.js';
7
8
  import { BackgroundRunTracker } from './background-runs.js';
8
9
  import { ExecutionDeliveryJournal, ExecutionTelemetryReporter, } from './execution-telemetry.js';
9
10
  import { startTypingLease } from './typing-lease.js';
11
+ import { recordProtocolTrace, NormalizedActivityPublisher, NormalizedExecutionTelemetry, sessionControlResponse, parseSessionControlRequest } from './plugin-sdk.js';
12
+ import { respondToHostedInteraction } from './providers/normalized/interactions.js';
10
13
  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';
11
14
  import { buildNormalizedAttentionSnapshot, parsePersistedAttentionSnapshot, } from './context/attention.js';
12
15
  import { mergeCompactionSnapshot, parsePersistedCompactionSnapshot, } from './context/compaction.js';
@@ -15,7 +18,7 @@ import { createLocalhostGatewayController, LOCALHOST_GATEWAY_COLLABORATION_TARGE
15
18
  import { extractTaskIdFromTaskAssignmentContent, resolveTaskAssignmentStatePath, } from './context/injection.js';
16
19
  import { AuthorizationAuditSink, } from './policy/authorization-audit.js';
17
20
  import { createProvider } from './providers/create-provider.js';
18
- import { ProviderTurnCancelledError } from './providers/provider-adapter.js';
21
+ import { ProviderInputRejectedError, ProviderTurnCancelledError } from './providers/provider-adapter.js';
19
22
  import { resolveAttentionStatePath, resolveCompactionStatePath, resolveExecutionDeliveryJournalPath, resolveExecutionTelemetryOutboxPath, resolveSharedProtocolKickoffDecisionPath, resolveSharedProtocolStatusPath, } from './state-paths.js';
20
23
  import { appendVisibleMention as appendVisibleBodyMention, extractVisibleMentionIds as extractCanonicalMentionIds, } from './visible-mentions.js';
21
24
  import { buildHostedIncomingContentText, buildHostedTurnContentParts, extractHostedMessageAttachments, normalizeHostedMessageAttachment, } from './hosted-turn-content.js';
@@ -118,6 +121,18 @@ class DefaultHostRuntime {
118
121
  error: summarizeError(error),
119
122
  });
120
123
  },
124
+ providerOutputMode: () => this.provider.outputMode ?? 'legacy',
125
+ connectionLiveness: () => config.agent.provider === 'codex' && this.provider.outputMode === 'normalized'
126
+ ? { probeIntervalMs: 30_000, pongTimeoutMs: 10_000, connectTimeoutMs: 30_000 }
127
+ : undefined,
128
+ normalizedActivitySync: () => this.provider.outputMode === 'normalized' && !!this.provider.syncNormalizedActivity,
129
+ sessionControl: () => this.provider.outputMode === 'normalized' && !!this.provider.controlSession,
130
+ sessionBindings: () => this.provider.outputMode === 'normalized' && !!this.provider.setSessionPublication,
131
+ sessionRelations: () => this.provider.outputMode === 'normalized' && !!this.provider.describeSession,
132
+ sessionManagement: () => this.provider.outputMode === 'normalized' && this.provider.supportsSessionManagement === true,
133
+ sessionDirectory: () => this.provider.outputMode === 'normalized' && !!this.provider.listSessions,
134
+ sessionObservation: () => this.provider.outputMode === 'normalized' && !!this.provider.observeSession,
135
+ sessionInputOperations: () => this.provider.outputMode === 'normalized' ? this.provider.sessionInputOperations ?? [] : [],
121
136
  });
122
137
  this.gateway = createLocalhostGatewayController({
123
138
  gateEnabled: localhostGatewayGateEnabled,
@@ -141,6 +156,12 @@ class DefaultHostRuntime {
141
156
  stateRootDir: config.stateRootDir,
142
157
  agentApiKey: config.agent.agentApiKey,
143
158
  resolveStableAgentId: deps.resolveStableAgentId,
159
+ providerOutputMode: config.providerOutputMode,
160
+ nativeProviderCommand: config.nativeProviderCommand,
161
+ sessionWorkspaceRoot: config.sessionWorkspaceRoot,
162
+ codexConnectionMode: config.codexConnectionMode,
163
+ codexSharedSocketPath: config.codexSharedSocketPath,
164
+ codexTrustShared: config.codexTrustShared,
144
165
  claudeCommand: config.claudeCommand,
145
166
  claudeArgs: config.claudeArgs,
146
167
  codexCommand: config.codexCommand,
@@ -158,16 +179,19 @@ class DefaultHostRuntime {
158
179
  });
159
180
  this.ensureStateRoot = deps.ensureStateRoot ?? ensurePrivateStateRoot;
160
181
  }
161
- async start(onMessage, onStopTurn, onStopBackgroundRun, onActivityTransportReady) {
182
+ async start(onMessage, onStopTurn, onStopBackgroundRun, onActivityTransportReady, onSessionControl) {
162
183
  await this.ensureStateRoot(this.config.stateRootDir);
163
184
  await this.gateway.start();
164
185
  try {
186
+ if (this.provider.outputMode === 'normalized')
187
+ this.controlPlane.onServerRequest?.((action, params, context) => action === 'session_control' && onSessionControl && context ? onSessionControl(parseSessionControlRequest(params), context) : respondToHostedInteraction(this.provider, action, params, context));
165
188
  this.controlPlane.onStopTurn?.(onStopTurn);
166
189
  this.controlPlane.onStopBackgroundRun?.(onStopBackgroundRun);
167
190
  this.controlPlane.onActivityTransportReady?.(onActivityTransportReady);
168
191
  await this.controlPlane.connect(onMessage);
169
192
  }
170
193
  catch (error) {
194
+ this.controlPlane.onServerRequest?.(undefined);
171
195
  this.controlPlane.onStopTurn?.(undefined);
172
196
  this.controlPlane.onStopBackgroundRun?.(undefined);
173
197
  this.controlPlane.onActivityTransportReady?.(undefined);
@@ -178,6 +202,7 @@ class DefaultHostRuntime {
178
202
  async stop() {
179
203
  let thrown;
180
204
  try {
205
+ this.controlPlane.onServerRequest?.(undefined);
181
206
  this.controlPlane.onStopTurn?.(undefined);
182
207
  this.controlPlane.onStopBackgroundRun?.(undefined);
183
208
  this.controlPlane.onActivityTransportReady?.(undefined);
@@ -294,6 +319,8 @@ const MAX_AUTONOMOUS_REPLY_DEDUPLICATION_KEYS = 256;
294
319
  */
295
320
  export class AgentsHost {
296
321
  config;
322
+ executionTelemetry;
323
+ normalizedStateOwners = new Map();
297
324
  runtime;
298
325
  provider;
299
326
  borgee;
@@ -408,6 +435,15 @@ export class AgentsHost {
408
435
  try {
409
436
  await this.executionDeliveryJournal.load();
410
437
  this.provider.onAutonomousReply?.((reply) => this.dispatchAutonomousReply(reply));
438
+ this.provider.setSessionPublication?.({
439
+ supported: () => this.started && !this.controlPlaneClosed && this.borgee.sessionBindingsSupported === true,
440
+ publish: (channelId, activity) => {
441
+ if (!this.started || this.controlPlaneClosed || !this.borgee.sessionBindingsSupported || !this.borgee.reportActivity)
442
+ return false;
443
+ this.borgee.reportActivity({ channelId, activity });
444
+ return true;
445
+ },
446
+ });
411
447
  await this.runtime.start((message) => {
412
448
  if (!this.started) {
413
449
  return;
@@ -417,8 +453,24 @@ export class AgentsHost {
417
453
  return;
418
454
  }
419
455
  this.dispatchMessage(message);
420
- }, async (channelId) => this.stopTurn(channelId), async (channelId, runId) => this.backgroundRuns.stop(channelId, runId), () => this.backgroundRuns.replay());
456
+ }, async (channelId) => this.stopTurn(channelId), async (channelId, runId) => this.backgroundRuns.stop(channelId, runId), () => { this.provider.publishSessionBindings?.(); this.backgroundRuns.replay(); }, (request, context) => this.handleSessionControl(request, context));
421
457
  const agentId = await this.ensureSelfAgentId();
458
+ if (this.provider.outputMode === 'normalized' && this.borgee.reportExecutionTelemetryFrame && this.borgee.onExecutionTelemetryAck) {
459
+ try {
460
+ this.executionTelemetry = new NormalizedExecutionTelemetry({
461
+ outboxPath: join(this.config.stateRootDir, 'execution-telemetry.json'),
462
+ scope: { serverUrl: this.config.borgeeBaseUrl, agentId },
463
+ transport: {
464
+ report: frame => this.borgee.reportExecutionTelemetryFrame(frame),
465
+ onAck: handler => this.borgee.onExecutionTelemetryAck(handler),
466
+ onReady: handler => this.borgee.onExecutionTransportReady?.(handler) ?? (() => { }),
467
+ },
468
+ });
469
+ }
470
+ catch (error) {
471
+ this.logger.error('normalized accounting unavailable', { error: summarizeError(error) });
472
+ }
473
+ }
422
474
  bufferingMessages = false;
423
475
  while (bufferedMessages.length > 0) {
424
476
  const pendingMessages = bufferedMessages.splice(0, bufferedMessages.length);
@@ -478,6 +530,9 @@ export class AgentsHost {
478
530
  runtimeStopError = error;
479
531
  }
480
532
  await Promise.allSettled([...this.activeTurns]);
533
+ this.executionTelemetry?.dispose();
534
+ this.executionTelemetry = undefined;
535
+ this.normalizedStateOwners.clear();
481
536
  await Promise.allSettled(pendingCompactionSnapshotWrites);
482
537
  this.compactionSnapshotWriteQueueByChannel.clear();
483
538
  if (runtimeStopError) {
@@ -497,6 +552,9 @@ export class AgentsHost {
497
552
  });
498
553
  }
499
554
  dispatchMessage(message) {
555
+ // Self echoes must not occupy the channel queue before explicit control admission.
556
+ if (this.selfAgentId && String(message.user_id ?? message.sender_id ?? '') === this.selfAgentId && message.message_type !== 'task_assignment')
557
+ return;
500
558
  const executionId = message.execution_grant?.executionId;
501
559
  const existing = executionId
502
560
  ? this.executionDispatches.get(executionId)
@@ -505,9 +563,15 @@ export class AgentsHost {
505
563
  this.trackActiveTurn(existing);
506
564
  return;
507
565
  }
508
- const run = () => this.collaborationEnabled
509
- ? this.handleCollaborationMessage(message)
510
- : this.enqueueChannelTurn(message.channel_id, () => this.handleLegacyMessage(message));
566
+ const run = () => message.sessionTarget
567
+ ? this.handleSelectedSessionMessage(message)
568
+ : this.collaborationEnabled
569
+ ? this.handleCollaborationMessage(message)
570
+ : this.enqueueChannelTurn(message.channel_id, () => this.handleLegacyMessage(message));
571
+ if (this.provider.outputMode === 'normalized') {
572
+ this.trackActiveTurn(run());
573
+ return;
574
+ }
511
575
  const work = executionId
512
576
  ? this.runClaimedExecution(message, run)
513
577
  : this.runMessageWithExecutionTelemetry(message, run).then((result) => {
@@ -625,6 +689,17 @@ export class AgentsHost {
625
689
  ...usage,
626
690
  }));
627
691
  }
692
+ async handleSelectedSessionMessage(msg) {
693
+ if (!msg.sessionTarget || !msg.sourceCurrent?.())
694
+ return;
695
+ const normalized = await this.normalizeMessage(msg);
696
+ if (!normalized || !msg.sourceCurrent())
697
+ return;
698
+ await this.executeTurn(msg, normalized.authorId, normalized.content, {
699
+ incomingParts: normalized.incomingParts,
700
+ selectedSession: { ...msg.sessionTarget, sourceCurrent: msg.sourceCurrent },
701
+ });
702
+ }
628
703
  dispatchAutonomousReply(reply) {
629
704
  if (!this.started
630
705
  || this.controlPlaneClosed
@@ -2453,6 +2528,254 @@ export class AgentsHost {
2453
2528
  : {}),
2454
2529
  });
2455
2530
  }
2531
+ async handleSessionControl(request, context) {
2532
+ const response = (status, reason) => sessionControlResponse(request, status, reason);
2533
+ if (!this.started || this.controlPlaneClosed || context.signal.aborted || Date.now() >= request.deadline_at)
2534
+ return response('unconfirmed', 'session_control_expired');
2535
+ if (this.provider.outputMode !== 'normalized' || !this.provider.controlSession)
2536
+ return response('unsupported', 'session_control_not_supported');
2537
+ if (request.operation.kind === 'send_input')
2538
+ return this.handleNativeInput(request, context);
2539
+ if (this.borgee.sessionBindingsSupported)
2540
+ return this.handleRetainedControl(request, context);
2541
+ if (request.operation.kind === 'list_pending_inputs') {
2542
+ if (!this.provider.supportsSessionInput || !context.isConnectionCurrent)
2543
+ return response('unsupported', 'native_queue_inventory_not_supported');
2544
+ const connected = () => this.started && !this.controlPlaneClosed && this.selfAgentId === request.agent_id && context.isConnectionCurrent();
2545
+ if (!connected())
2546
+ return response('unconfirmed', 'session_control_expired');
2547
+ try {
2548
+ return (await this.provider.controlSession(request, { signal: context.signal, isConnectionCurrent: connected })).response;
2549
+ }
2550
+ catch {
2551
+ return response('unconfirmed', 'native_queue_inventory_unavailable');
2552
+ }
2553
+ }
2554
+ if (request.operation.kind === 'list_commands') {
2555
+ try {
2556
+ return (await this.provider.controlSession(request, { signal: context.signal })).response;
2557
+ }
2558
+ catch {
2559
+ return response('unconfirmed', 'command_directory_unavailable');
2560
+ }
2561
+ }
2562
+ const channelId = request.channel_id;
2563
+ const state = this.ensureCollaborationChannelState(channelId);
2564
+ if (state.processor || state.running || state.activeTurn || state.queue.length || this.channelTurnQueues.has(channelId))
2565
+ return response('rejected', 'session_busy');
2566
+ let finish;
2567
+ const ownership = new Promise(resolve => { finish = resolve; });
2568
+ state.processor = ownership;
2569
+ state.running = true;
2570
+ this.channelTurnQueues.set(channelId, ownership);
2571
+ let execution;
2572
+ let active;
2573
+ let retained = false;
2574
+ const release = () => {
2575
+ execution?.close();
2576
+ if (state.activeTurn === active)
2577
+ state.activeTurn = undefined;
2578
+ state.running = false;
2579
+ if (state.processor === ownership)
2580
+ state.processor = undefined;
2581
+ if (this.channelTurnQueues.get(channelId) === ownership)
2582
+ this.channelTurnQueues.delete(channelId);
2583
+ finish();
2584
+ };
2585
+ this.trackActiveTurn(ownership);
2586
+ try {
2587
+ if (request.operation.kind === 'execute_command') {
2588
+ if (!request.admission)
2589
+ return response('rejected', 'command_admission_required');
2590
+ const admission = request.admission;
2591
+ active = { generation: ++state.generation, turnExecutionId: admission.input_message_id, superseded: false, collaborationMessagesSent: 0, targetCooldowns: new Map() };
2592
+ state.activeTurn = active;
2593
+ const inputText = await readDurableInputText(this.borgee, { channelId, inputMessageId: admission.input_message_id, createdAt: admission.input_created_at });
2594
+ if (context.signal.aborted || Date.now() >= request.deadline_at)
2595
+ return response('unconfirmed', 'session_control_expired');
2596
+ execution = this.beginNormalizedExecution({ channelId, taskId: admission.input_message_id, inputMessageId: admission.input_message_id, inputText, activeTurn: active });
2597
+ }
2598
+ const result = await this.provider.controlSession(request, {
2599
+ signal: context.signal, execution: execution?.options,
2600
+ onCompletion: pending => {
2601
+ retained = true;
2602
+ const owner = active;
2603
+ const completion = pending.then(async (reply) => {
2604
+ if (!reply || !hasVisibleText(reply.text) || !(await this.canAcceptCompletedTurnOutput(channelId, owner)))
2605
+ return;
2606
+ await this.postTurnReply({ channelId, body: reply.text, inputMessageId: request.admission?.input_message_id, replyToId: request.admission?.input_message_id, taskId: request.admission.input_message_id, activeTurn: owner });
2607
+ }).catch(error => { this.logger.error('native command completion unavailable', { channelId, error: summarizeError(error) }); }).finally(release);
2608
+ this.trackActiveTurn(completion);
2609
+ },
2610
+ });
2611
+ return result.response;
2612
+ }
2613
+ catch (error) {
2614
+ this.logger.error('native session control unavailable', { channelId, error: summarizeError(error) });
2615
+ return response('unconfirmed', 'native_confirmation_unavailable');
2616
+ }
2617
+ finally {
2618
+ if (!retained)
2619
+ release();
2620
+ }
2621
+ }
2622
+ async handleRetainedControl(request, context) {
2623
+ const response = (status, reason) => sessionControlResponse(request, status, reason);
2624
+ const connected = () => this.started && !this.controlPlaneClosed && this.selfAgentId === request.agent_id && !!context.isConnectionCurrent?.();
2625
+ let execution;
2626
+ let canPublish = () => false;
2627
+ let retained = false;
2628
+ try {
2629
+ const admission = request.admission;
2630
+ let inputText = null;
2631
+ if (request.operation.kind === 'execute_command') {
2632
+ if (!admission)
2633
+ return response('rejected', 'command_admission_required');
2634
+ inputText = await readDurableInputText(this.borgee, { channelId: request.channel_id, inputMessageId: admission.input_message_id, createdAt: admission.input_created_at });
2635
+ }
2636
+ if (!connected() || context.signal.aborted || Date.now() >= request.deadline_at)
2637
+ return response('unconfirmed', 'session_control_expired');
2638
+ const result = await this.provider.controlSession(request, {
2639
+ signal: context.signal, connectionSignal: context.connectionSignal, isConnectionCurrent: connected,
2640
+ activateExecution: nativeCurrent => {
2641
+ if (!admission)
2642
+ throw new Error('Native command has no admitted input.');
2643
+ canPublish = () => connected() && nativeCurrent();
2644
+ execution = this.beginNormalizedExecution({ channelId: request.channel_id, taskId: admission.input_message_id, inputMessageId: admission.input_message_id, inputText, publicationGuard: canPublish });
2645
+ return execution.options;
2646
+ },
2647
+ onCompletion: pending => {
2648
+ retained = true;
2649
+ const completion = pending.then(async (reply) => {
2650
+ if (!reply || !hasVisibleText(reply.text) || !canPublish())
2651
+ return;
2652
+ await this.postTurnReply({ channelId: request.channel_id, body: reply.text, inputMessageId: admission.input_message_id, replyToId: admission.input_message_id, taskId: admission.input_message_id });
2653
+ }).catch(error => this.logger.error('native command completion unavailable', { channelId: request.channel_id, error: summarizeError(error) })).finally(() => execution?.close());
2654
+ this.trackActiveTurn(completion);
2655
+ },
2656
+ });
2657
+ return result.response;
2658
+ }
2659
+ catch (error) {
2660
+ this.logger.error('native session control unavailable', { channelId: request.channel_id, error: summarizeError(error) });
2661
+ return response('unconfirmed', 'native_confirmation_unavailable');
2662
+ }
2663
+ finally {
2664
+ if (!retained)
2665
+ execution?.close();
2666
+ }
2667
+ }
2668
+ async handleNativeInput(request, context) {
2669
+ const operation = request.operation;
2670
+ const admission = request.admission;
2671
+ const response = (status, reason) => sessionControlResponse(request, status, reason);
2672
+ if (operation.kind !== 'send_input' || !admission || !context.isConnectionCurrent || !this.provider.supportsSessionInput)
2673
+ return response('unsupported', 'native_input_not_supported');
2674
+ const connected = () => this.started && !this.controlPlaneClosed && this.selfAgentId === request.agent_id && context.isConnectionCurrent();
2675
+ let execution;
2676
+ let canPublish = () => false;
2677
+ let retained = false;
2678
+ try {
2679
+ const inputText = await readDurableInputText(this.borgee, { channelId: request.channel_id, inputMessageId: admission.input_message_id, createdAt: admission.input_created_at });
2680
+ if (inputText !== operation.text || !connected() || context.signal.aborted)
2681
+ return response('unconfirmed', 'native_input_admission_unavailable');
2682
+ const result = await this.provider.controlSession(request, {
2683
+ signal: context.signal, connectionSignal: context.connectionSignal, isConnectionCurrent: connected,
2684
+ activateExecution: async (nativeCurrent) => {
2685
+ canPublish = () => connected() && nativeCurrent();
2686
+ if (!canPublish())
2687
+ throw new Error('Native queued input connection ownership ended.');
2688
+ execution = this.beginNormalizedExecution({ channelId: request.channel_id, taskId: admission.input_message_id, inputMessageId: admission.input_message_id, inputText, publicationGuard: canPublish });
2689
+ return execution.options;
2690
+ },
2691
+ onCompletion: pending => {
2692
+ retained = true;
2693
+ const completion = pending.then(async (reply) => {
2694
+ if (!reply || !hasVisibleText(reply.text) || !canPublish())
2695
+ return;
2696
+ await this.postTurnReply({ channelId: request.channel_id, body: reply.text, inputMessageId: admission.input_message_id, replyToId: admission.input_message_id, taskId: admission.input_message_id });
2697
+ }).catch(error => this.logger.error('native input completion unavailable', { channelId: request.channel_id, error: summarizeError(error) })).finally(() => execution?.close());
2698
+ this.trackActiveTurn(completion);
2699
+ },
2700
+ });
2701
+ return result.response;
2702
+ }
2703
+ catch (error) {
2704
+ this.logger.error('native input confirmation unavailable', { channelId: request.channel_id, error: summarizeError(error) });
2705
+ return response('unconfirmed', 'native_confirmation_unavailable');
2706
+ }
2707
+ finally {
2708
+ if (!retained)
2709
+ execution?.close();
2710
+ }
2711
+ }
2712
+ async postTurnReply(input, source) {
2713
+ const message = { channelId: input.channelId, body: input.body, replyToId: input.replyToId ?? input.activeTurn?.delivery?.replyToId };
2714
+ const posted = source ? await this.borgee.postMessage(message, source) : await this.borgee.postMessage(message);
2715
+ recordProtocolTrace({ stage: 'host.delivery', kind: 'correlation', correlation: { channelId: input.channelId, agentId: this.selfAgentId ?? undefined, inputMessageId: input.inputMessageId, taskId: input.taskId, turnExecutionId: input.activeTurn?.turnExecutionId ?? undefined, outputMessageId: posted.messageId }, payload: { delivery: 'posted' } });
2716
+ return posted;
2717
+ }
2718
+ beginNormalizedExecution(input) {
2719
+ const { channelId, taskId, activeTurn } = input;
2720
+ let execution;
2721
+ try {
2722
+ execution = this.executionTelemetry?.bind({ bindingId: taskId, channelId, inputMessageId: input.inputMessageId, grant: input.grant });
2723
+ }
2724
+ catch (error) {
2725
+ this.logger.error('normalized accounting binding unavailable', { error: summarizeError(error) });
2726
+ }
2727
+ let publisher;
2728
+ let statePublisher;
2729
+ let stateClosed = false;
2730
+ const owner = Symbol('normalized-state');
2731
+ const stateOwnerKey = input.publicationGuard ? JSON.stringify([channelId, taskId]) : channelId;
2732
+ this.normalizedStateOwners.set(stateOwnerKey, owner);
2733
+ const canPublishState = () => {
2734
+ const current = this.collaborationChannels.get(channelId)?.activeTurn;
2735
+ return !stateClosed && this.started && !this.controlPlaneClosed && this.normalizedStateOwners.get(stateOwnerKey) === owner && (input.publicationGuard ? input.publicationGuard() : !activeTurn?.superseded && (!current || current.generation === activeTurn?.generation));
2736
+ };
2737
+ return {
2738
+ options: {
2739
+ normalizedTaskId: taskId, normalizedInputMessageId: input.inputMessageId, normalizedInputText: input.inputText,
2740
+ normalizedStateSink: input.silent ? undefined : {
2741
+ snapshot: (snapshot, bindingId, timelineEpoch) => {
2742
+ if (!canPublishState())
2743
+ return false;
2744
+ statePublisher ??= new NormalizedActivityPublisher({ client: { reportActivity: value => { this.borgee.reportActivity?.(value); } }, channelId, taskId, agentId: snapshot.payload.id });
2745
+ statePublisher.publish(snapshot, Date.now(), snapshot.payload.providerId, snapshot.payload.capabilities, bindingId, timelineEpoch);
2746
+ return true;
2747
+ },
2748
+ sessionActivity: activity => { if (!canPublishState() || !this.borgee.reportActivity)
2749
+ return false; this.borgee.reportActivity({ channelId, activity }); return true; },
2750
+ observe: (observation, agentId, capabilities, bindingId, timelineEpoch) => {
2751
+ if (observation.event.type !== 'runtime_updated' || !canPublishState() || this.borgee.sessionBindingsSupported)
2752
+ return;
2753
+ statePublisher ??= new NormalizedActivityPublisher({ client: { reportActivity: value => { this.borgee.reportActivity?.(value); } }, channelId, taskId, agentId });
2754
+ statePublisher.observe(observation, capabilities, bindingId, timelineEpoch);
2755
+ },
2756
+ close: () => { stateClosed = true; statePublisher?.close(); if (this.normalizedStateOwners.get(stateOwnerKey) === owner)
2757
+ this.normalizedStateOwners.delete(stateOwnerKey); },
2758
+ },
2759
+ onNormalizedObservation: (event, agentId, observation, capabilities, bindingId, timelineEpoch, sessionPublished) => {
2760
+ if (input.publicationGuard && !input.publicationGuard())
2761
+ return;
2762
+ try {
2763
+ execution?.observe(event, observation?.occurredAt);
2764
+ }
2765
+ catch (error) {
2766
+ this.logger.error('normalized accounting rejected', { error: summarizeError(error) });
2767
+ }
2768
+ const acceptsOutput = !input.silent && this.canAcceptTurnOutput(channelId, activeTurn?.generation);
2769
+ recordProtocolTrace({ stage: 'host.dispatch', kind: 'correlation', correlation: { channelId, agentId, inputMessageId: input.inputMessageId, taskId, turnExecutionId: activeTurn?.turnExecutionId ?? undefined }, payload: { eventType: event.type }, outcome: acceptsOutput ? 'forwarded' : 'suppressed' });
2770
+ if (sessionPublished || !acceptsOutput || event.type === 'runtime_updated' && this.borgee.sessionBindingsSupported)
2771
+ return;
2772
+ publisher ??= new NormalizedActivityPublisher({ client: { reportActivity: value => { this.borgee.reportActivity?.(value); } }, channelId, taskId, agentId });
2773
+ publisher.observe(observation ?? event, capabilities, bindingId, timelineEpoch);
2774
+ },
2775
+ },
2776
+ close: () => { publisher?.close(); execution?.close(); },
2777
+ };
2778
+ }
2456
2779
  async executeTurn(msg, authorId, content, options) {
2457
2780
  const channelId = msg.channel_id;
2458
2781
  const activeTurn = options?.activeTurn;
@@ -2460,6 +2783,8 @@ export class AgentsHost {
2460
2783
  const taskThreadContext = options?.taskThreadContext ?? { state: 'inactive' };
2461
2784
  const silentTurn = options?.silent === true;
2462
2785
  const deferDelivery = options?.deferDelivery === true;
2786
+ const selectedSourceCurrent = options?.selectedSession?.sourceCurrent;
2787
+ const selectedWriteSource = selectedSourceCurrent ? { isConnectionCurrent: selectedSourceCurrent } : undefined;
2463
2788
  const collaborationTurnMode = activeTurn?.protocol
2464
2789
  ? 'protocol-managed'
2465
2790
  : options?.kickoff
@@ -2478,7 +2803,7 @@ export class AgentsHost {
2478
2803
  };
2479
2804
  const turnStartedAt = Date.now();
2480
2805
  this.logger.debug('starting turn', turnContext);
2481
- const executionTelemetry = msg.execution_grant
2806
+ const executionTelemetry = this.provider.outputMode !== 'normalized' && msg.execution_grant
2482
2807
  ? this.executionTelemetryByExecutionId.get(msg.execution_grant.executionId)
2483
2808
  : undefined;
2484
2809
  let executionSucceeded = false;
@@ -2488,6 +2813,11 @@ export class AgentsHost {
2488
2813
  let visibleReplyBody;
2489
2814
  let activityReporter;
2490
2815
  let typingLease;
2816
+ const normalizedTaskId = turnExecutionId ?? randomUUID();
2817
+ recordProtocolTrace({ stage: 'host.dispatch', kind: 'correlation',
2818
+ correlation: { channelId, agentId: this.selfAgentId ?? undefined, inputMessageId: msg.message_id, taskId: normalizedTaskId, turnExecutionId: turnExecutionId ?? undefined },
2819
+ payload: { silentTurn } });
2820
+ let normalizedExecution;
2491
2821
  try {
2492
2822
  if (!silentTurn) {
2493
2823
  typingLease = startTypingLease(() => this.borgee.reportTyping(channelId));
@@ -2516,6 +2846,7 @@ export class AgentsHost {
2516
2846
  activityReporter = silentTurn
2517
2847
  ? undefined
2518
2848
  : this.beginTurnActivity(channelId, activeTurn?.generation);
2849
+ normalizedExecution = this.provider.outputMode === 'normalized' ? this.beginNormalizedExecution({ channelId, taskId: normalizedTaskId, inputMessageId: msg.message_id, inputText: await readNormalizedInputText(this.borgee, msg), grant: msg.execution_grant, activeTurn, silent: silentTurn, ...(selectedSourceCurrent ? { publicationGuard: selectedSourceCurrent } : {}) }) : undefined;
2519
2850
  await this.ensureCompactionSnapshotLoaded(channelId);
2520
2851
  const compactionSnapshot = this.compactionSnapshotByChannel.get(channelId);
2521
2852
  if (executionTelemetry) {
@@ -2539,6 +2870,7 @@ export class AgentsHost {
2539
2870
  incomingParts,
2540
2871
  incomingEventKind: msg.type,
2541
2872
  incomingMessageType: msg.message_type,
2873
+ ...(options?.selectedSession ? { selectedSession: options.selectedSession } : {}),
2542
2874
  collaboration: turnExecutionId || options?.kickoff
2543
2875
  ? {
2544
2876
  enabled: this.collaborationEnabled,
@@ -2561,7 +2893,8 @@ export class AgentsHost {
2561
2893
  ? {
2562
2894
  taskAssignmentContextOverride: {
2563
2895
  active: true,
2564
- ...(taskThreadContext.state === 'active' && taskThreadContext.currentTaskId
2896
+ ...((taskThreadContext.state === 'active'
2897
+ || taskThreadContext.state === 'recovered') && taskThreadContext.currentTaskId
2565
2898
  ? { currentTaskId: taskThreadContext.currentTaskId }
2566
2899
  : {}),
2567
2900
  },
@@ -2570,8 +2903,9 @@ export class AgentsHost {
2570
2903
  : { taskAssignmentContextOverridePersistence: 'ephemeral' }),
2571
2904
  }
2572
2905
  : {}),
2573
- }, observeProviderProgress || executionTelemetry
2906
+ }, observeProviderProgress || executionTelemetry || this.provider.outputMode === 'normalized'
2574
2907
  ? {
2908
+ ...normalizedExecution?.options,
2575
2909
  ...(executionTelemetry
2576
2910
  ? {
2577
2911
  onUsage: (usage) => {
@@ -2595,7 +2929,7 @@ export class AgentsHost {
2595
2929
  return;
2596
2930
  }
2597
2931
  }
2598
- if (!this.canAcceptTurnOutput(channelId, activeTurn?.generation)) {
2932
+ if ((selectedSourceCurrent && !selectedSourceCurrent()) || !this.canAcceptTurnOutput(channelId, activeTurn?.generation)) {
2599
2933
  return;
2600
2934
  }
2601
2935
  if (update.type === 'text'
@@ -2610,7 +2944,7 @@ export class AgentsHost {
2610
2944
  return;
2611
2945
  }
2612
2946
  const reportedActivity = toReportedActivity(update);
2613
- if (reportsProviderActivity && reportedActivity) {
2947
+ if (this.provider.outputMode !== 'normalized' && reportsProviderActivity && reportedActivity) {
2614
2948
  activityReporter?.progress(reportedActivity);
2615
2949
  }
2616
2950
  if (!supportsProviderProgress ||
@@ -2642,7 +2976,7 @@ export class AgentsHost {
2642
2976
  observedAt: Date.now(),
2643
2977
  turnExecutionId: activeTurn?.turnExecutionId ?? undefined,
2644
2978
  });
2645
- if (!(await this.canAcceptCompletedTurnOutput(channelId, activeTurn))) {
2979
+ if ((selectedSourceCurrent && !selectedSourceCurrent()) || !(await this.canAcceptCompletedTurnOutput(channelId, activeTurn))) {
2646
2980
  if (activeTurn?.turnExecutionId) {
2647
2981
  this.clearCollaborationDraft(channelId, {
2648
2982
  generation: activeTurn.generation,
@@ -2694,11 +3028,7 @@ export class AgentsHost {
2694
3028
  };
2695
3029
  }
2696
3030
  if (this.started && !this.controlPlaneClosed && hasVisibleText(publicReplyText)) {
2697
- const posted = await this.borgee.postMessage({
2698
- channelId,
2699
- body: visibleReplyBody,
2700
- replyToId: activeTurn?.delivery?.replyToId,
2701
- });
3031
+ const posted = await this.postTurnReply({ channelId, body: visibleReplyBody, inputMessageId: msg.message_id, taskId: normalizedTaskId, activeTurn }, selectedWriteSource);
2702
3032
  this.logger.debug('completed turn', {
2703
3033
  ...turnContext,
2704
3034
  durationMs: Date.now() - turnStartedAt,
@@ -2756,6 +3086,10 @@ export class AgentsHost {
2756
3086
  if (executionTelemetryStartFailed) {
2757
3087
  throw error;
2758
3088
  }
3089
+ if (error instanceof ProviderInputRejectedError && !silentTurn && !deferDelivery && (!selectedSourceCurrent || selectedSourceCurrent()) && this.canAcceptTurnOutput(channelId, activeTurn?.generation)) {
3090
+ const posted = await this.postTurnReply({ channelId, body: error.publicReplyText, replyToId: activeTurn?.delivery?.replyToId, inputMessageId: msg.message_id, taskId: normalizedTaskId, activeTurn }, selectedWriteSource);
3091
+ return { reply: { text: error.publicReplyText }, publicReplyText: error.publicReplyText, visibleReplyBody: error.publicReplyText, deliveredMessageId: posted.messageId, deliveryState: 'posted', stale: false, completed: false };
3092
+ }
2759
3093
  if (error instanceof ProviderTurnCancelledError) {
2760
3094
  const cancelledReplyText = error.publicReplyText?.trim();
2761
3095
  if (!silentTurn &&
@@ -2763,16 +3097,13 @@ export class AgentsHost {
2763
3097
  typeof cancelledReplyText === 'string' &&
2764
3098
  cancelledReplyText.length > 0 &&
2765
3099
  this.started &&
2766
- !this.controlPlaneClosed) {
3100
+ !this.controlPlaneClosed &&
3101
+ (!selectedSourceCurrent || selectedSourceCurrent())) {
2767
3102
  reply = { text: cancelledReplyText };
2768
3103
  publicReplyText = cancelledReplyText;
2769
3104
  visibleReplyBody = this.resolveVisibleTurnBody(activeTurn, reply, publicReplyText);
2770
3105
  try {
2771
- const posted = await this.borgee.postMessage({
2772
- channelId,
2773
- body: visibleReplyBody,
2774
- replyToId: activeTurn?.delivery?.replyToId,
2775
- });
3106
+ const posted = await this.postTurnReply({ channelId, body: visibleReplyBody, replyToId: activeTurn?.delivery?.replyToId, inputMessageId: msg.message_id, taskId: normalizedTaskId, activeTurn }, selectedWriteSource);
2776
3107
  this.logger.debug('posted cancelled turn feedback', {
2777
3108
  ...turnContext,
2778
3109
  durationMs: Date.now() - turnStartedAt,
@@ -2838,6 +3169,7 @@ export class AgentsHost {
2838
3169
  }
2839
3170
  typingLease?.stop();
2840
3171
  activityReporter?.end();
3172
+ normalizedExecution?.close();
2841
3173
  }
2842
3174
  }
2843
3175
  /**
@@ -0,0 +1,21 @@
1
+ import { createRequire as __nodeCreateRequire } from 'node:module';
2
+ const require = __nodeCreateRequire(import.meta.url);
3
+
4
+ // ../agent-provider-claude/src/catalog-worker.ts
5
+ import { getSessionInfo, getSessionMessages, listSessions, listSubagents, getSubagentMessages } from "@borgee/claude-native-sdk";
6
+ var [operation, sessionId, childId] = process.argv.slice(2);
7
+ try {
8
+ const result = operation === "list" ? await listSessions() : operation === "info" && sessionId ? await getSessionInfo(sessionId) : operation === "messages" && sessionId ? await getSessionMessages(sessionId) : operation === "child-messages" && sessionId && childId ? await getSubagentMessages(sessionId, childId) : operation === "children" && sessionId ? await readChildren(sessionId) : (() => {
9
+ throw new Error("Unknown catalog operation.");
10
+ })();
11
+ process.stdout.write(JSON.stringify(result ?? null));
12
+ } catch {
13
+ process.stderr.write("Claude native catalog could not be read.\n");
14
+ process.exitCode = 1;
15
+ }
16
+ async function readChildren(parentId) {
17
+ const children = [];
18
+ for (const id of await listSubagents(parentId)) children.push({ id, messages: await getSubagentMessages(parentId, id) });
19
+ return children;
20
+ }
21
+ //# sourceMappingURL=catalog-worker.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../agent-provider-claude/src/catalog-worker.ts"],
4
+ "sourcesContent": ["import { getSessionInfo, getSessionMessages, listSessions, listSubagents, getSubagentMessages } from '@anthropic-ai/claude-agent-sdk';\n\nconst [operation, sessionId, childId] = process.argv.slice(2);\ntry {\n const result = operation === 'list' ? await listSessions() : operation === 'info' && sessionId ? await getSessionInfo(sessionId)\n : operation === 'messages' && sessionId ? await getSessionMessages(sessionId)\n : operation === 'child-messages' && sessionId && childId ? await getSubagentMessages(sessionId, childId)\n : operation === 'children' && sessionId ? await readChildren(sessionId)\n : (() => { throw new Error('Unknown catalog operation.'); })();\n process.stdout.write(JSON.stringify(result ?? null));\n} catch {\n process.stderr.write('Claude native catalog could not be read.\\n');\n process.exitCode = 1;\n}\n\nasync function readChildren(parentId: string) {\n const children = [];\n for (const id of await listSubagents(parentId)) children.push({ id, messages: await getSubagentMessages(parentId, id) });\n return children;\n}\n"],
5
+ "mappings": ";;;;AAAA,SAAS,gBAAgB,oBAAoB,cAAc,eAAe,2BAA2B;AAErG,IAAM,CAAC,WAAW,WAAW,OAAO,IAAI,QAAQ,KAAK,MAAM,CAAC;AAC5D,IAAI;AACF,QAAM,SAAS,cAAc,SAAS,MAAM,aAAa,IAAI,cAAc,UAAU,YAAY,MAAM,eAAe,SAAS,IAC3H,cAAc,cAAc,YAAY,MAAM,mBAAmB,SAAS,IAC1E,cAAc,oBAAoB,aAAa,UAAU,MAAM,oBAAoB,WAAW,OAAO,IACrG,cAAc,cAAc,YAAY,MAAM,aAAa,SAAS,KACnE,MAAM;AAAE,UAAM,IAAI,MAAM,4BAA4B;AAAA,EAAG,GAAG;AAC/D,UAAQ,OAAO,MAAM,KAAK,UAAU,UAAU,IAAI,CAAC;AACrD,QAAQ;AACN,UAAQ,OAAO,MAAM,4CAA4C;AACjE,UAAQ,WAAW;AACrB;AAEA,eAAe,aAAa,UAAkB;AAC5C,QAAM,WAAW,CAAC;AAClB,aAAW,MAAM,MAAM,cAAc,QAAQ,EAAG,UAAS,KAAK,EAAE,IAAI,UAAU,MAAM,oBAAoB,UAAU,EAAE,EAAE,CAAC;AACvH,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -1,12 +1,16 @@
1
- import type { AgentActivity, ExecutionTelemetryFrame, ReportTaskFinishedInput, ReportTurnActivityInput, StopBackgroundRunHandler, StopTurnHandler, TurnActivityReporter } from '../plugin-sdk.js';
1
+ import type { AgentActivity, ExecutionTelemetryFrame, ServerRequestHandler, ReportTaskFinishedInput, ReportTurnActivityInput, StopBackgroundRunHandler, StopTurnHandler, TurnActivityReporter } from '../plugin-sdk.js';
2
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
+ reportExecutionTelemetryFrame?(frame: ExecutionTelemetryFrame): void;
5
+ onExecutionTelemetryAck?(handler: (ack: import('../plugin-sdk.js').ExecutionTelemetryAck) => void): () => void;
6
+ onExecutionTransportReady?(handler: () => void): () => void;
7
+ onServerRequest?(handler: ServerRequestHandler | undefined): void;
4
8
  onStopTurn?(handler: StopTurnHandler | undefined): void;
5
9
  onStopBackgroundRun?(handler: StopBackgroundRunHandler | undefined): void;
6
10
  onActivityTransportReady?(handler: (() => void) | undefined): void;
7
11
  connect(onMessage: (message: ChannelMessageEvent) => void): Promise<void>;
8
12
  close(): Promise<void>;
9
- postMessage(input: PostMessageInput): Promise<PostedMessage>;
13
+ postMessage(input: PostMessageInput, source?: Pick<import('../plugin-sdk.js').ServerRequestContext, 'isConnectionCurrent'>): Promise<PostedMessage>;
10
14
  editMessage(messageId: string, content: string): Promise<void>;
11
15
  deleteMessage(messageId: string): Promise<void>;
12
16
  reportTyping(channelId: string): void;
@@ -14,6 +18,7 @@ export interface ChatControlPlane {
14
18
  reportTurnActivity(input: ReportTurnActivityInput): TurnActivityReporter;
15
19
  reportExecutionTelemetry?(frame: ExecutionTelemetryFrame): Promise<void>;
16
20
  reportTaskFinished?(input: ReportTaskFinishedInput): void;
21
+ readonly sessionBindingsSupported?: boolean;
17
22
  reportActivity?(input: {
18
23
  channelId: string;
19
24
  activity: AgentActivity;