@borgee/agents-host 0.2.44 → 0.2.56

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 (56) hide show
  1. package/README.md +23 -27
  2. package/dist/agents-host.d.ts +26 -5
  3. package/dist/agents-host.js +163 -192
  4. package/dist/chat/chat-control-plane.d.ts +3 -0
  5. package/dist/chat/sdk-chat-control-plane.d.ts +4 -3
  6. package/dist/chat/sdk-chat-control-plane.js +3 -0
  7. package/dist/cli.js +1 -5
  8. package/dist/compatibility-gates.d.ts +4 -0
  9. package/dist/compatibility-gates.js +18 -1
  10. package/dist/context/claude-file-brief.d.ts +2 -0
  11. package/dist/context/claude-file-brief.js +83 -0
  12. package/dist/context/compaction.d.ts +20 -0
  13. package/dist/context/compaction.js +59 -0
  14. package/dist/context/injection.d.ts +39 -6
  15. package/dist/context/injection.js +317 -26
  16. package/dist/context/main-session-delegation.d.ts +1 -1
  17. package/dist/context/projection-strategy.d.ts +24 -0
  18. package/dist/context/projection-strategy.js +90 -0
  19. package/dist/context/prompt.d.ts +16 -1
  20. package/dist/context/prompt.js +456 -22
  21. package/dist/context/resolved-workspace.d.ts +2 -0
  22. package/dist/context/resolved-workspace.js +64 -0
  23. package/dist/context/skill-manual.d.ts +1 -0
  24. package/dist/context/skill-manual.js +4 -1
  25. package/dist/context/turn-preparation.d.ts +8 -2
  26. package/dist/context/turn-preparation.js +56 -14
  27. package/dist/gateway/localhost-gateway.js +2 -0
  28. package/dist/managed-daemon.js +122 -9
  29. package/dist/plugin-sdk.js +276 -359
  30. package/dist/plugin-sdk.js.map +4 -4
  31. package/dist/progress-to-activity.d.ts +16 -0
  32. package/dist/progress-to-activity.js +24 -0
  33. package/dist/projection-strategy-values.d.ts +4 -0
  34. package/dist/projection-strategy-values.js +28 -0
  35. package/dist/providers/acp-progress-collector.d.ts +44 -0
  36. package/dist/providers/acp-progress-collector.js +130 -0
  37. package/dist/providers/awaiting-user.d.ts +2 -3
  38. package/dist/providers/awaiting-user.js +5 -7
  39. package/dist/providers/claude/activity-metadata.d.ts +14 -0
  40. package/dist/providers/claude/activity-metadata.js +81 -0
  41. package/dist/providers/claude/cli-client.d.ts +1 -2
  42. package/dist/providers/claude/cli-client.js +190 -117
  43. package/dist/providers/codex/cli-client.js +3 -83
  44. package/dist/providers/codex/project-doc.js +12 -11
  45. package/dist/providers/copilot/cli-client.js +3 -83
  46. package/dist/providers/create-provider.d.ts +2 -0
  47. package/dist/providers/create-provider.js +20 -4
  48. package/dist/state-paths.d.ts +1 -1
  49. package/dist/state-paths.js +3 -3
  50. package/dist/task-thread-resolution.d.ts +3 -2
  51. package/dist/types.d.ts +130 -6
  52. package/package.json +2 -2
  53. package/skills/borgee-agent/SKILL.md +9 -1
  54. package/skills/borgee-agent/references/task-properties.md +5 -2
  55. package/dist/durable-cursor-store.d.ts +0 -5
  56. package/dist/durable-cursor-store.js +0 -7
@@ -2,6 +2,7 @@ import { Readable, Writable } from 'node:stream';
2
2
  import spawn from 'cross-spawn';
3
3
  import { PROTOCOL_VERSION, client, methods, ndJsonStream, } from '@agentclientprotocol/sdk';
4
4
  import { HostLogger, summarizeChildStderr, summarizeError } from '../../debug.js';
5
+ import { AcpProgressCollector } from '../acp-progress-collector.js';
5
6
  import { resolveCopilotPermissionResponse } from '../../policy/copilot-permission.js';
6
7
  import { IDLE_BACKEND_SHUTDOWN_DISABLED_MS, IdleBackendShutdownScheduler, } from '../idle-backend-shutdown.js';
7
8
  const SESSION_TAINTED_ERRORS = new WeakSet();
@@ -36,9 +37,6 @@ const DEFAULT_SESSION_CAPABILITIES = {
36
37
  loadSession: false,
37
38
  resumeSession: false,
38
39
  };
39
- function hasVisibleText(value) {
40
- return typeof value === 'string' && value.trim().length > 0;
41
- }
42
40
  function isAbsoluteHttpUrl(value) {
43
41
  return value.startsWith('http://') || value.startsWith('https://');
44
42
  }
@@ -61,84 +59,6 @@ function isSameOrigin(left, right) {
61
59
  function asImagePart(part) {
62
60
  return part.type === 'image' ? part : null;
63
61
  }
64
- function formatToolProgress(title, status) {
65
- const normalizedTitle = hasVisibleText(title) ? title.trim() : null;
66
- switch (status) {
67
- case 'completed':
68
- return normalizedTitle ? `Completed ${normalizedTitle}` : 'Completed tool call';
69
- case 'failed':
70
- return normalizedTitle ? `Failed ${normalizedTitle}` : 'Tool call failed';
71
- case 'pending':
72
- case 'in_progress':
73
- case undefined:
74
- case null:
75
- return normalizedTitle ? `Running ${normalizedTitle}…` : 'Running tool…';
76
- default:
77
- return normalizedTitle ? `${status} ${normalizedTitle}` : status;
78
- }
79
- }
80
- function formatPlanProgress(entries) {
81
- const current = entries.find((entry) => entry.status === 'in_progress') ??
82
- entries.find((entry) => entry.status === 'pending') ??
83
- entries[0];
84
- return hasVisibleText(current?.content) ? `Plan: ${current.content.trim()}` : null;
85
- }
86
- class CopilotProgressCollector {
87
- onProgress;
88
- publicText = '';
89
- lastPublished = null;
90
- toolTitles = new Map();
91
- constructor(onProgress) {
92
- this.onProgress = onProgress;
93
- }
94
- consume(update) {
95
- switch (update.update.sessionUpdate) {
96
- case 'agent_message_chunk':
97
- if (update.update.content.type !== 'text') {
98
- return;
99
- }
100
- this.publicText += update.update.content.text;
101
- this.publish(this.publicText);
102
- return;
103
- case 'tool_call':
104
- this.toolTitles.set(update.update.toolCallId, update.update.title);
105
- this.publishFallback(formatToolProgress(update.update.title, update.update.status));
106
- return;
107
- case 'tool_call_update': {
108
- const nextTitle = update.update.title ?? this.toolTitles.get(update.update.toolCallId);
109
- if (hasVisibleText(nextTitle)) {
110
- this.toolTitles.set(update.update.toolCallId, nextTitle);
111
- }
112
- this.publishFallback(formatToolProgress(nextTitle, update.update.status));
113
- return;
114
- }
115
- case 'plan':
116
- this.publishFallback(formatPlanProgress(update.update.entries.map((entry) => ({
117
- content: entry.content,
118
- status: entry.status,
119
- }))));
120
- return;
121
- default:
122
- return;
123
- }
124
- }
125
- getFinalText() {
126
- return this.publicText.trim();
127
- }
128
- publishFallback(text) {
129
- if (hasVisibleText(this.publicText)) {
130
- return;
131
- }
132
- this.publish(text);
133
- }
134
- publish(text) {
135
- if (!this.onProgress || !hasVisibleText(text) || text === this.lastPublished) {
136
- return;
137
- }
138
- this.lastPublished = text;
139
- this.onProgress({ text });
140
- }
141
- }
142
62
  function createDeferredTurn(channelId, preparedTurn, sessionPersistence, options) {
143
63
  let settled = false;
144
64
  let resolvePromise;
@@ -647,7 +567,7 @@ export class CopilotCliClient {
647
567
  }
648
568
  }
649
569
  async resolveSessionCwd(promptContext) {
650
- return promptContext?.taskWorkspace?.rootPath ?? this.runtime.cwd;
570
+ return promptContext?.resolvedWorkspace?.rootPath ?? this.runtime.cwd;
651
571
  }
652
572
  async buildPromptInput(turn) {
653
573
  const imageParts = turn.incomingParts.map(asImagePart).filter((part) => part !== null);
@@ -707,7 +627,7 @@ export class CopilotCliClient {
707
627
  const promptFailure = new Promise((_, reject) => {
708
628
  void promptPromise.catch((error) => reject(markSessionTainted(error)));
709
629
  });
710
- const collector = new CopilotProgressCollector(options?.onProgress);
630
+ const collector = new AcpProgressCollector(options?.onProgress);
711
631
  for (;;) {
712
632
  let update;
713
633
  try {
@@ -1,5 +1,6 @@
1
1
  import type { DebugLogger } from '../debug.js';
2
2
  import type { ProviderRuntimeConfig } from '../types.js';
3
+ import type { ChatControlPlane } from '../chat/chat-control-plane.js';
3
4
  import { type ProviderAdapter } from './provider-adapter.js';
4
5
  import type { LocalhostGatewayContextPublisher } from '../context/injection.js';
5
6
  import type { AuthorizationAuditSinkLike } from '../policy/authorization-audit.js';
@@ -7,6 +8,7 @@ interface CreateProviderOptions {
7
8
  compatibilityGates?: ReadonlySet<string>;
8
9
  localhostGateway?: LocalhostGatewayContextPublisher;
9
10
  authorizationAuditSink?: AuthorizationAuditSinkLike;
11
+ controlPlane?: Pick<ChatControlPlane, 'getTask' | 'listChannels' | 'listTasks'>;
10
12
  }
11
13
  export declare function createProvider(config: ProviderRuntimeConfig, debugLogger?: DebugLogger, options?: CreateProviderOptions): ProviderAdapter;
12
14
  export {};
@@ -1,7 +1,8 @@
1
1
  import { FileChannelContextStore } from '../context/injection.js';
2
+ import { ServerProjectionStrategyResolver } from '../context/projection-strategy.js';
2
3
  import { createProviderConnectionsSessionStore } from '../connections-state-store.js';
3
4
  import { ProviderTurnPreparer } from '../context/turn-preparation.js';
4
- import { CONNECTIONS_STATE_LAYER_COMPATIBILITY_GATE, CONTEXT_INJECTION_COMPATIBILITY_GATE, CODEX_PROVIDER_COMPATIBILITY_GATE, COPILOT_PROVIDER_V2_COMPATIBILITY_GATE, INTERNAL_PROVIDER_IMPLEMENTATIONS_ENV, LOCALHOST_GATEWAY_COMPATIBILITY_GATE, parseInternalProviderImplementationOverrides, POLICY_AUDIT_ENFORCEMENT_COMPATIBILITY_GATE, resolveInternalPolicyMode, resolveInternalCompatibilityGates, SKILL_RUNTIME_COMPATIBILITY_GATE, } from '../compatibility-gates.js';
5
+ import { CONNECTIONS_STATE_LAYER_COMPATIBILITY_GATE, CONTEXT_INJECTION_COMPATIBILITY_GATE, CODEX_PROVIDER_COMPATIBILITY_GATE, COPILOT_PROVIDER_V2_COMPATIBILITY_GATE, INTERNAL_PROVIDER_IMPLEMENTATIONS_ENV, LOCALHOST_GATEWAY_COMPATIBILITY_GATE, parseInternalProviderImplementationOverrides, POLICY_AUDIT_ENFORCEMENT_COMPATIBILITY_GATE, resolveInternalPolicyMode, resolveInternalCompatibilityGates, resolveInternalProjectionStrategy, SKILL_RUNTIME_COMPATIBILITY_GATE, } from '../compatibility-gates.js';
5
6
  import { resolveIdleBackendShutdownMs } from './idle-backend-shutdown.js';
6
7
  import { ClaudeCliClient } from './claude/cli-client.js';
7
8
  import { ClaudeProviderAdapter } from './claude/adapter.js';
@@ -52,7 +53,6 @@ class CliBackedProviderV2 {
52
53
  }
53
54
  class CopilotProviderV2 extends CliBackedProviderV2 {
54
55
  }
55
- const TASK_WORKSPACE_ROOT_DIR_ENV = 'AGENTS_HOST_INTERNAL_TASK_WORKSPACE_ROOT_DIR';
56
56
  const PROVIDER_V2_COMPATIBILITY_GATES = {
57
57
  copilot: COPILOT_PROVIDER_V2_COMPATIBILITY_GATE,
58
58
  };
@@ -131,13 +131,29 @@ export function createProvider(config, debugLogger, options = {}) {
131
131
  const channelContextStore = contextInjectionGateEnabled
132
132
  ? new FileChannelContextStore(config.stateRootDir, {
133
133
  skillRuntimeEnabled: skillRuntimeGateEnabled,
134
- taskWorkspaceRootDir: process.env[TASK_WORKSPACE_ROOT_DIR_ENV],
135
134
  localhostGateway: skillRuntimeGateEnabled && localhostGatewayGateEnabled
136
135
  ? options.localhostGateway
137
136
  : undefined,
137
+ taskReader: options.controlPlane,
138
138
  })
139
139
  : undefined;
140
- const turnPreparer = new ProviderTurnPreparer(channelContextStore, debugLogger);
140
+ const resolveInternalProjection = () => resolveInternalProjectionStrategy();
141
+ const projectionStrategyResolver = new ServerProjectionStrategyResolver({
142
+ baseUrl: config.borgeeBaseUrl,
143
+ agentApiKey: config.agentApiKey,
144
+ resolveStableAgentId: config.resolveStableAgentId,
145
+ fallback: resolveInternalProjection,
146
+ logger: debugLogger,
147
+ taskReader: options.controlPlane,
148
+ });
149
+ const turnPreparer = new ProviderTurnPreparer(channelContextStore, debugLogger, {
150
+ resolveProjectionStrategy: async (input) => {
151
+ if (input.provider !== 'claude') {
152
+ return resolveInternalProjection();
153
+ }
154
+ return projectionStrategyResolver.resolve(input.channelId);
155
+ },
156
+ });
141
157
  switch (config.provider) {
142
158
  case 'claude': {
143
159
  return createClaudeProvider(config, debugLogger, connectionsStateGateEnabled, turnPreparer, idleBackendShutdownMs);
@@ -20,11 +20,11 @@ export declare function resolveManagedDaemonLogPath(rootPath: string): string;
20
20
  export declare function resolveManagedRuntimeSettingsPath(rootPath: string): string;
21
21
  export declare function resolveManagedBootstrapLockPath(rootPath: string): string;
22
22
  export declare function resolveLocalConfigAgentStateRoot(rootPath: string, agentKey: string): string;
23
- export declare function resolveAgentCursorPath(stateRootDir: string, agentId: string): string;
24
23
  export declare function resolveSharedProtocolKickoffDecisionPath(stateRootDir: string, channelId: string, anchorMessageId: string): string;
25
24
  export declare function resolveSharedProtocolStatusPath(stateRootDir: string, channelId: string, anchorMessageId: string): string;
26
25
  export declare function resolveConnectionsStatePath(stateRootDir: string): string;
27
26
  export declare function resolveAttentionStatePath(stateRootDir: string, channelId: string): string;
27
+ export declare function resolveCompactionStatePath(stateRootDir: string, channelId: string): string;
28
28
  export declare function resolveAuthorizationAuditPath(stateRootDir: string): string;
29
29
  export declare function resolvePreviousAuthorizationAuditPath(stateRootDir: string): string;
30
30
  export declare function resolveClaudeSessionMapPath(stateRootDir: string, agentId: string): string;
@@ -129,9 +129,6 @@ export function resolveManagedBootstrapLockPath(rootPath) {
129
129
  export function resolveLocalConfigAgentStateRoot(rootPath, agentKey) {
130
130
  return join(resolveManagedStateRoot(rootPath), `${sanitizeStateRootLabel(agentKey)}-${hashStateRootKey(agentKey)}`);
131
131
  }
132
- export function resolveAgentCursorPath(stateRootDir, agentId) {
133
- return join(stateRootDir, `bpp-cursor-${encodeSegment(agentId)}.json`);
134
- }
135
132
  export function resolveSharedProtocolKickoffDecisionPath(stateRootDir, channelId, anchorMessageId) {
136
133
  return join(dirname(resolve(stateRootDir)), 'protocol-kickoff-decisions', `${encodeSegment(channelId)}--${encodeSegment(anchorMessageId)}.json`);
137
134
  }
@@ -144,6 +141,9 @@ export function resolveConnectionsStatePath(stateRootDir) {
144
141
  export function resolveAttentionStatePath(stateRootDir, channelId) {
145
142
  return join(stateRootDir, 'attention-state', `${encodeSegment(channelId)}.json`);
146
143
  }
144
+ export function resolveCompactionStatePath(stateRootDir, channelId) {
145
+ return join(stateRootDir, 'compaction-state', `${encodeSegment(channelId)}.json`);
146
+ }
147
147
  export function resolveAuthorizationAuditPath(stateRootDir) {
148
148
  return join(stateRootDir, 'authorization-audit.jsonl');
149
149
  }
@@ -1,10 +1,11 @@
1
1
  import type { ChatControlPlane } from './chat/chat-control-plane.js';
2
2
  import type { Task } from './types.js';
3
+ type TaskThreadResolutionControlPlane = Pick<ChatControlPlane, 'getTask' | 'listChannels' | 'listTasks'>;
3
4
  interface WaitForTaskForThreadOptions {
4
5
  preferredTaskId?: string;
5
6
  attempts?: number;
6
7
  delayMs?: number;
7
8
  }
8
- export declare function findTaskForThread(controlPlane: ChatControlPlane, threadId: string, preferredTaskId?: string): Promise<Task | null>;
9
- export declare function waitForTaskForThread(controlPlane: ChatControlPlane, threadId: string, options?: WaitForTaskForThreadOptions): Promise<Task | null>;
9
+ export declare function findTaskForThread(controlPlane: TaskThreadResolutionControlPlane, threadId: string, preferredTaskId?: string): Promise<Task | null>;
10
+ export declare function waitForTaskForThread(controlPlane: TaskThreadResolutionControlPlane, threadId: string, options?: WaitForTaskForThreadOptions): Promise<Task | null>;
10
11
  export {};
package/dist/types.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export type ProviderKind = 'claude' | 'codex' | 'copilot';
2
+ export type ProjectionStrategy = 'session-brief' | 'turn-full' | 'message-only' | 'turn-thin' | 'file-brief';
2
3
  export interface ProviderCommandConfig {
3
4
  claudeCommand: string;
4
5
  claudeArgs: string[];
@@ -167,7 +168,7 @@ export interface CollaborationDraftSnapshot {
167
168
  }
168
169
  export type ProviderCollaborationTurnMode = 'ordinary' | 'silent-kickoff' | 'protocol-managed';
169
170
  export type CollaborationOutcomeResponseState = 'responded' | 'blocked' | 'superseded' | 'failed';
170
- export type CollaborationOutcomeDeliveryState = 'posted' | 'draft-finalized' | 'not-delivered' | 'delivery-failed';
171
+ export type CollaborationOutcomeDeliveryState = 'posted' | 'not-delivered' | 'delivery-failed';
171
172
  export type CollaborationOutcomeWakeState = 'waiting' | 'suppressed-agent' | 'resumed-human';
172
173
  export interface CollaborationOutcomeBlockedDetails {
173
174
  question: string;
@@ -211,6 +212,22 @@ export interface AttentionSnapshot {
211
212
  turnExecutionId?: string;
212
213
  claimContext?: AttentionClaimContext;
213
214
  }
215
+ export type ProviderCompactionStage = 'started' | 'completed' | 'failed';
216
+ /**
217
+ * Shared host-side projection only. This is the latest host-observed compaction
218
+ * lifecycle snapshot surfaced by a provider adapter; it is not provider- or
219
+ * model-authoritative truth about retained prompt content.
220
+ */
221
+ export interface CompactionSnapshot {
222
+ source: 'host-observed';
223
+ provider: ProviderKind;
224
+ stage: ProviderCompactionStage;
225
+ observedAt: number;
226
+ turnExecutionId?: string;
227
+ usedTokens?: number;
228
+ contextWindowTokens?: number;
229
+ failureReason?: string;
230
+ }
214
231
  export type TaskThreadCollaborationTurnRole = 'assignment' | 'continuation';
215
232
  export type TaskThreadMainResultContract = 'ordinary-final-reply-in-thread';
216
233
  export type TaskThreadAuxiliaryRouteContract = 'optional-auxiliary-send-or-escalation-only';
@@ -233,6 +250,7 @@ export interface TaskThreadCollaborationContract {
233
250
  export interface CollaborationCapabilityDeclaration {
234
251
  collaborationOutcome?: true;
235
252
  attentionSnapshot?: true;
253
+ compactionSnapshot?: true;
236
254
  taskThreadCollaborationContract?: true;
237
255
  missedCollaborationDiagnostic?: true;
238
256
  recoveryExplanation?: 'runtime-local-only';
@@ -264,6 +282,7 @@ export interface ProviderInput {
264
282
  collaboration?: ProviderCollaborationContext;
265
283
  collaborationOutcome?: CollaborationOutcomeSnapshot;
266
284
  attentionSnapshot?: AttentionSnapshot;
285
+ compactionSnapshot?: CompactionSnapshot;
267
286
  taskThreadCollaborationContract?: TaskThreadCollaborationContract;
268
287
  collaborationCapabilities?: CollaborationCapabilityDeclaration;
269
288
  missedCollaborationDiagnostic?: MissedCollaborationDiagnostic;
@@ -310,17 +329,52 @@ export interface TaskAssignmentThreadContext {
310
329
  active: true;
311
330
  currentTaskId?: string;
312
331
  }
313
- export interface TaskWorkspaceContext {
314
- currentTaskId: string;
332
+ export interface ChannelResolvedWorkspaceContext {
333
+ authority: 'channel';
334
+ owningChannelId: string;
315
335
  rootPath: string;
316
336
  }
337
+ export interface TaskResolvedWorkspaceContext {
338
+ authority: 'task';
339
+ taskId: string;
340
+ rootPath: string;
341
+ }
342
+ export type ResolvedWorkspaceContext = ChannelResolvedWorkspaceContext | TaskResolvedWorkspaceContext;
343
+ export interface RuntimeSurface {
344
+ schemaVersion: 1;
345
+ turnMode: ProviderCollaborationTurnMode;
346
+ gateway: {
347
+ available: boolean;
348
+ readOnlyRoutesAuthorized: boolean;
349
+ inspectBeforeAnswer: boolean;
350
+ };
351
+ task: {
352
+ currentThread: 'parent-channel' | 'task-assignment-thread';
353
+ parentChannelTaskCollection: 'available' | 'parent-channel-only';
354
+ currentTaskShorthand: 'requires-task-id' | 'persisted-current-task' | 'fallback-current-task';
355
+ currentTaskIdPersisted: boolean;
356
+ currentTaskFallback: 'disallowed' | 'visible-parent-channel-thread-scan';
357
+ };
358
+ collaboration: {
359
+ participantLookup: 'unavailable' | 'available';
360
+ draftRead: 'unavailable' | 'available';
361
+ auxiliarySend: 'unavailable' | 'available';
362
+ };
363
+ response: {
364
+ mainVisibleReply: 'model-visible-reply' | 'host-visible-protocol-reply' | 'no-visible-reply';
365
+ auxiliaryMessageUsage: 'unavailable' | 'targeted-escalation-only';
366
+ };
367
+ }
317
368
  export interface PreparedPromptContext {
318
369
  channelContextPayloadPath?: string;
370
+ claudeProjectedBriefPath?: string;
371
+ claudeProjectedBriefHash?: string;
319
372
  gatewayCredentialPath?: string;
320
373
  collaborationTurnExecutionId?: string;
321
374
  collaborationTurnMode?: ProviderCollaborationTurnMode;
322
375
  collaborationOutcome?: CollaborationOutcomeSnapshot;
323
376
  attentionSnapshot?: AttentionSnapshot;
377
+ compactionSnapshot?: CompactionSnapshot;
324
378
  taskThreadCollaborationContract?: TaskThreadCollaborationContract;
325
379
  collaborationCapabilities?: CollaborationCapabilityDeclaration;
326
380
  missedCollaborationDiagnostic?: MissedCollaborationDiagnostic;
@@ -330,19 +384,25 @@ export interface PreparedPromptContext {
330
384
  skillRuntime?: SkillRuntimeBootstrapMetadata;
331
385
  localhostGateway?: LocalhostGatewayBootstrapMetadata;
332
386
  taskAssignmentContext?: TaskAssignmentThreadContext;
333
- taskWorkspace?: TaskWorkspaceContext;
387
+ resolvedWorkspace?: ResolvedWorkspaceContext;
388
+ runtimeSurface?: RuntimeSurface;
334
389
  }
335
390
  export interface PreparedProviderSessionRouting {
336
391
  key: string;
337
392
  persistence: 'persistent' | 'ephemeral';
338
393
  }
339
394
  export interface PreparedProviderTurnInput {
395
+ agentName?: string;
340
396
  channelId: string;
341
397
  incomingContent: string;
342
398
  incomingParts: HostedTurnContentPart[];
343
399
  incomingEventKind?: string;
344
400
  incomingMessageType?: string;
345
401
  prompt: string;
402
+ /** Claude-only session append content for ACP systemPrompt.append. */
403
+ claudeSessionPromptAppend?: string;
404
+ /** Shared projection-strategy selector used for provider prompt/session experiments. */
405
+ projectionStrategy?: ProjectionStrategy;
346
406
  promptContext?: PreparedPromptContext;
347
407
  providerSessionRouting?: PreparedProviderSessionRouting;
348
408
  }
@@ -417,9 +477,73 @@ export interface UpdateTaskInput {
417
477
  title?: string;
418
478
  description?: string;
419
479
  }
420
- export interface ProviderProgressUpdate {
421
- text: string;
480
+ /** The protocol's ten tool kinds, treated as open: an unknown value is not an error. */
481
+ export type ProgressActivityKind = 'read' | 'edit' | 'delete' | 'move' | 'search' | 'execute' | 'think' | 'fetch' | 'switch_mode' | 'other';
482
+ /** The protocol's four statuses, plus the one it lacks: stopped, waiting on a human. */
483
+ export type ProgressActivityStatus = 'pending' | 'in_progress' | 'waiting' | 'completed' | 'failed';
484
+ /**
485
+ * One entry in an agent's timeline of work. Identity is minted by the producer:
486
+ * a producer that can correlate later progress to an earlier event reuses the
487
+ * id and the entry is patched in place; one that cannot mints a fresh id and
488
+ * entries accumulate. An absent field means unchanged; `paths` replaces.
489
+ */
490
+ export interface ProgressActivity {
491
+ id: string;
492
+ /** The protocol's title. For a shell tool this is the raw command line. */
493
+ label?: string;
494
+ /** The provider's human-readable phrasing, when it offers one. */
495
+ description?: string;
496
+ kind?: ProgressActivityKind;
497
+ status?: ProgressActivityStatus;
498
+ /** Bounded one-line detail: the command, the file, the query. Never raw output. */
499
+ detail?: string;
500
+ /** Files this activity touched. Replaces the previous list. */
501
+ paths?: string[];
502
+ /** The activity that spawned this one. */
503
+ parentId?: string;
504
+ /**
505
+ * Whether this activity is a container for other work rather than work of its
506
+ * own. A reader nests under it, and cannot arrive at that by noticing it has
507
+ * children: a sub-agent's children can be reported before it is, and a
508
+ * sub-agent that has spawned nothing yet is still a sub-agent.
509
+ */
510
+ subagent?: boolean;
511
+ /** Why a terminal activity ended as it did. An open set upstream, so never an enum. */
512
+ reason?: string;
422
513
  }
514
+ /** Plan entries have no identity: the whole list is replaced on every update. */
515
+ export interface ProgressPlanItem {
516
+ label: string;
517
+ status: 'pending' | 'in_progress' | 'completed';
518
+ }
519
+ export interface ProviderCompactionProgress {
520
+ provider: ProviderKind;
521
+ stage: ProviderCompactionStage;
522
+ usedTokens?: number;
523
+ contextWindowTokens?: number;
524
+ failureReason?: string;
525
+ }
526
+ /**
527
+ * A plan, a tool call and a stream of text are three meanings with three
528
+ * cardinalities and three update rules, so they travel as three shapes rather
529
+ * than as one entry with a category field. Text is cumulative rather than a
530
+ * delta, so a consumer that misses one update recovers on the next.
531
+ */
532
+ export type ProviderProgressUpdate = {
533
+ type: 'text';
534
+ stream: 'answer' | 'reasoning';
535
+ text: string;
536
+ messageId?: string;
537
+ } | {
538
+ type: 'activity';
539
+ activity: ProgressActivity;
540
+ } | {
541
+ type: 'plan';
542
+ items: ProgressPlanItem[];
543
+ } | {
544
+ type: 'compaction';
545
+ compaction: ProviderCompactionProgress;
546
+ };
423
547
  export interface ProviderGenerateOptions {
424
548
  onProgress?: (update: ProviderProgressUpdate) => void;
425
549
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@borgee/agents-host",
3
- "version": "0.2.44",
3
+ "version": "0.2.56",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -35,7 +35,7 @@
35
35
  "tsx": "^4.20.5",
36
36
  "typescript": "^5.9.3",
37
37
  "vitest": "^4.1.5",
38
- "@borgee/plugin-sdk": "0.4.0"
38
+ "@borgee/plugin-sdk": "0.10.0"
39
39
  },
40
40
  "scripts": {
41
41
  "predev": "pnpm --filter @borgee/plugin-sdk build",
@@ -11,6 +11,14 @@ This turn is running as an agent inside a Borgee channel. The packaged CLI is th
11
11
 
12
12
  Every command below is already authorized on a turn whose prompt names a gateway credential file. Run it directly. Never ask the user for permission first, and never end a turn having asked to read instead of reading.
13
13
 
14
+ ## Session contract
15
+
16
+ This session is the coordination surface for the work that belongs to this Borgee thread: read the channel, answer the human here, break the work down, and keep task state current here.
17
+
18
+ Delegate the heavy execution to workers rather than running everything inline in this session. Research, code edits, long builds and test runs, and broad repository sweeps belong in delegated workers so a newly arriving message can still be handled promptly here.
19
+
20
+ Delegation changes who executes, not who owns. The work still belongs to this thread, and this session still reports the result here itself.
21
+
14
22
  ## When to use it
15
23
 
16
24
  - You are asked what was said here, who is here, or what you are supposed to be working on: read it, do not guess.
@@ -56,7 +64,7 @@ Everything below writes that leading interpreter and script path as `borgee-agen
56
64
 
57
65
  On `task get`, `task update`, `task set-property` and `task delete-property` the task id is an optional leading positional. Inside a task thread, omitting it addresses this thread's own task: the host's published binding names it, and where the host published none the gateway scans the parent channel's visible tasks for this thread instead — so pass the id explicitly when that scan cannot land on a single task. `task list` and `task create` are refused inside a task thread; they belong to the parent channel. In a parent channel every one of those ids is required, and omitting it answers `not_found`.
58
66
 
59
- The keys `task set-property` and `task delete-property` accept are a closed set, and a value has a size limit: see `references/task-properties.md`.
67
+ The keys `task set-property` and `task delete-property` accept are a closed set, and a value has a size limit: see `references/task-properties.md`. In this slice, `workspace.mode` is the formal task-workspace switch: `inherit` keeps a task thread on its channel workspace, while `isolated` moves that task thread onto its own task workspace.
60
68
 
61
69
  ## What is available on a turn
62
70
 
@@ -1,12 +1,14 @@
1
1
  # Task properties
2
2
 
3
- A task property associates a task with something that lives outside it. Use one to record what a reader would otherwise have to hunt for in the thread — the pull request that implements the task, the issue it came from.
3
+ A task property associates a task with something that lives outside it, or with a closed task-level runtime choice. Use one to record what a reader would otherwise have to hunt for in the thread — the pull request that implements the task, the issue it came from, or the task workspace mode it should run with.
4
4
 
5
5
  Read them back with `task get`: every task response carries a `properties` object, `{}` when the task has none.
6
6
 
7
7
  ```
8
8
  borgee-agent --gateway <path> task set-property <task-id> --key link.pr --value https://github.com/org/repo/pull/12
9
9
  borgee-agent --gateway <path> task delete-property <task-id> --key link.pr
10
+ borgee-agent --gateway <path> task set-property <task-id> --key workspace.mode --value isolated
11
+ borgee-agent --gateway <path> task delete-property <task-id> --key workspace.mode
10
12
  ```
11
13
 
12
14
  The task id is omitted only inside that task's own thread, where the request resolves to the thread's task.
@@ -19,6 +21,7 @@ The key set is closed; writing an unregistered key is rejected with `unknown_pro
19
21
  | --- | --- |
20
22
  | `link.pr` | The pull request that implements this task. Set it as soon as the PR exists, not at the end. |
21
23
  | `link.issue` | The issue or ticket the task originates from. |
24
+ | `workspace.mode` | The task workspace binding for task threads. `inherit` keeps the task on its channel workspace. `isolated` switches it to its task-isolated workspace. Deleting the key returns to the default `inherit` behavior. |
22
25
  | `agent.session_id` | Do not write this. It is registered, so a write is accepted and lands — overwriting the host's record of which provider session worked this task. The host writes it itself after each turn. |
23
26
 
24
27
  ## One key per call
@@ -27,4 +30,4 @@ Each call writes exactly one key, and that is what makes it safe to write a prop
27
30
 
28
31
  ## Value
29
32
 
30
- A value is a plain string of at most 8 KiB; a longer one is rejected with `property_value_too_long`. It is a reference — a URL, an identifier — never a document.
33
+ A value is a plain string of at most 8 KiB; a longer one is rejected with `property_value_too_long`. It is a reference — a URL, an identifier — never a document. Some registered keys are closed enums: `workspace.mode` accepts only `inherit` or `isolated`.
@@ -1,5 +0,0 @@
1
- import { type CursorStore } from './plugin-sdk.js';
2
- export interface DurableCursorStoreOptions {
3
- stateRootDir: string;
4
- }
5
- export declare function createDurableCursorStore(options: DurableCursorStoreOptions): CursorStore;
@@ -1,7 +0,0 @@
1
- import { FileCursorStore } from './plugin-sdk.js';
2
- import { resolveAgentCursorPath } from './state-paths.js';
3
- export function createDurableCursorStore(options) {
4
- return new FileCursorStore({
5
- resolvePath: (agentId) => resolveAgentCursorPath(options.stateRootDir, agentId),
6
- });
7
- }