@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
@@ -0,0 +1,16 @@
1
+ import type { AgentActivity } from './plugin-sdk.js';
2
+ import type { ProviderProgressUpdate } from './types.js';
3
+ /** Everything a provider reports. The turn boundary is the reporter's own and never arrives here. */
4
+ export type ReportedActivity = Exclude<AgentActivity, {
5
+ shape: 'turn';
6
+ }>;
7
+ /**
8
+ * Restates one progress update in the rail's own vocabulary.
9
+ *
10
+ * This is the one part of reporting that belongs to this host rather than to the
11
+ * SDK: `ProviderProgressUpdate` is the seam the provider adapters speak, and
12
+ * nothing outside this package has it. What happens to a report afterwards —
13
+ * how often it may travel, what supersedes it, where a turn begins and ends —
14
+ * is the rail's and lives with the client.
15
+ */
16
+ export declare function toReportedActivity(update: ProviderProgressUpdate): ReportedActivity | null;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Restates one progress update in the rail's own vocabulary.
3
+ *
4
+ * This is the one part of reporting that belongs to this host rather than to the
5
+ * SDK: `ProviderProgressUpdate` is the seam the provider adapters speak, and
6
+ * nothing outside this package has it. What happens to a report afterwards —
7
+ * how often it may travel, what supersedes it, where a turn begins and ends —
8
+ * is the rail's and lives with the client.
9
+ */
10
+ export function toReportedActivity(update) {
11
+ switch (update.type) {
12
+ case 'text':
13
+ return { shape: 'output', stream: update.stream, text: update.text };
14
+ case 'plan':
15
+ return {
16
+ shape: 'plan',
17
+ entries: update.items.map((item) => ({ label: item.label, status: item.status })),
18
+ };
19
+ case 'activity':
20
+ return { shape: 'activity', ...update.activity };
21
+ case 'compaction':
22
+ return null;
23
+ }
24
+ }
@@ -0,0 +1,4 @@
1
+ import type { ProjectionStrategy } from './types.js';
2
+ export declare const DEFAULT_PROJECTION_STRATEGY: ProjectionStrategy;
3
+ export declare function normalizeProjectionStrategy(value: string | undefined | null): ProjectionStrategy | undefined;
4
+ export declare function isProjectionStrategy(value: string | undefined | null): value is ProjectionStrategy;
@@ -0,0 +1,28 @@
1
+ export const DEFAULT_PROJECTION_STRATEGY = 'turn-thin';
2
+ export function normalizeProjectionStrategy(value) {
3
+ switch (value?.trim()) {
4
+ case 'session-brief':
5
+ case 'session-augmented':
6
+ case 'sessionized':
7
+ return 'session-brief';
8
+ case 'turn-full':
9
+ case 'inline-full':
10
+ case 'legacy':
11
+ return 'turn-full';
12
+ case 'message-only':
13
+ case 'zero-turn':
14
+ return 'message-only';
15
+ case 'turn-thin':
16
+ case 'grounded-thin':
17
+ case 'minimal-turn':
18
+ return 'turn-thin';
19
+ case 'projected-brief':
20
+ case 'file-brief':
21
+ return 'file-brief';
22
+ default:
23
+ return undefined;
24
+ }
25
+ }
26
+ export function isProjectionStrategy(value) {
27
+ return normalizeProjectionStrategy(value) !== undefined;
28
+ }
@@ -0,0 +1,44 @@
1
+ import type { ActiveSession } from '@agentclientprotocol/sdk';
2
+ import type { ProviderGenerateOptions } from '../types.js';
3
+ type AcpSessionUpdate = Awaited<ReturnType<ActiveSession['nextUpdate']>>;
4
+ export type AcpProgressNotification = Extract<AcpSessionUpdate, {
5
+ kind: 'session_update';
6
+ }>;
7
+ type AcpToolCall = Extract<AcpProgressNotification['update'], {
8
+ sessionUpdate: 'tool_call';
9
+ }>;
10
+ type AcpToolCallUpdate = Extract<AcpProgressNotification['update'], {
11
+ sessionUpdate: 'tool_call_update';
12
+ }>;
13
+ /**
14
+ * What a provider says about a tool call beside what the protocol says. Every
15
+ * field here is one the protocol has no place for, which is why each provider
16
+ * hangs it under its own namespace and why reading it is the one thing about
17
+ * this stream that cannot be written once for all three.
18
+ */
19
+ export interface AcpActivityMetadata {
20
+ description?: string;
21
+ parentId?: string;
22
+ subagent?: boolean;
23
+ reason?: string;
24
+ }
25
+ export type AcpActivityMetadataReader = (meta: AcpToolCall['_meta'] | AcpToolCallUpdate['_meta']) => AcpActivityMetadata;
26
+ /**
27
+ * Turns one ACP session's notifications into progress updates and keeps the
28
+ * turn's answer text so the caller can resolve the reply once the turn stops.
29
+ * The three providers speak the same protocol version and their streams map
30
+ * identically; what differs between them sits in provider metadata, read here
31
+ * through one injected function rather than through a per-provider branch or a
32
+ * per-provider copy of this class.
33
+ */
34
+ export declare class AcpProgressCollector {
35
+ private readonly onProgress?;
36
+ private readonly readMetadata;
37
+ private answerText;
38
+ private reasoningText;
39
+ constructor(onProgress?: ProviderGenerateOptions['onProgress'], readMetadata?: AcpActivityMetadataReader);
40
+ consume(notification: AcpProgressNotification): void;
41
+ getFinalText(): string;
42
+ private publishStream;
43
+ }
44
+ export {};
@@ -0,0 +1,130 @@
1
+ const ACTIVITY_KINDS = new Set([
2
+ 'read',
3
+ 'edit',
4
+ 'delete',
5
+ 'move',
6
+ 'search',
7
+ 'execute',
8
+ 'think',
9
+ 'fetch',
10
+ 'switch_mode',
11
+ 'other',
12
+ ]);
13
+ /** The protocol's four. Ours adds `waiting`, which no agent can report. */
14
+ const ACTIVITY_STATUSES = new Set([
15
+ 'pending',
16
+ 'in_progress',
17
+ 'completed',
18
+ 'failed',
19
+ ]);
20
+ /** For a provider that hangs nothing beside the protocol, or whose namespace nobody has read yet. */
21
+ const noProviderMetadata = () => ({});
22
+ function hasVisibleText(value) {
23
+ return typeof value === 'string' && value.trim().length > 0;
24
+ }
25
+ /** An unknown kind is not an error: the enum is closed in this protocol version and open in the next. */
26
+ function toActivityKind(kind) {
27
+ return ACTIVITY_KINDS.has(kind ?? '') ? kind : 'other';
28
+ }
29
+ /** A status outside the vocabulary leaves the entry where it stands rather than inventing a transition. */
30
+ function toActivityStatus(status) {
31
+ return ACTIVITY_STATUSES.has(status ?? '') ? status : undefined;
32
+ }
33
+ function toActivityPaths(locations) {
34
+ return locations ? locations.map((location) => location.path) : undefined;
35
+ }
36
+ /** Only the id and the title are required on the wire, so the rest of a new entry defaults here. */
37
+ function toCreatedActivity(update, metadata) {
38
+ const paths = toActivityPaths(update.locations);
39
+ return {
40
+ id: update.toolCallId,
41
+ label: update.title,
42
+ ...metadata,
43
+ kind: toActivityKind(update.kind),
44
+ status: toActivityStatus(update.status) ?? 'pending',
45
+ ...(paths ? { paths } : {}),
46
+ };
47
+ }
48
+ function toPatchedActivity(update, metadata) {
49
+ const paths = toActivityPaths(update.locations);
50
+ const status = toActivityStatus(update.status);
51
+ return {
52
+ id: update.toolCallId,
53
+ ...(typeof update.title === 'string' ? { label: update.title } : {}),
54
+ ...metadata,
55
+ ...(update.kind == null ? {} : { kind: toActivityKind(update.kind) }),
56
+ ...(status ? { status } : {}),
57
+ ...(paths ? { paths } : {}),
58
+ };
59
+ }
60
+ function toPlanItem(entry) {
61
+ return { label: entry.content, status: entry.status };
62
+ }
63
+ /**
64
+ * Turns one ACP session's notifications into progress updates and keeps the
65
+ * turn's answer text so the caller can resolve the reply once the turn stops.
66
+ * The three providers speak the same protocol version and their streams map
67
+ * identically; what differs between them sits in provider metadata, read here
68
+ * through one injected function rather than through a per-provider branch or a
69
+ * per-provider copy of this class.
70
+ */
71
+ export class AcpProgressCollector {
72
+ onProgress;
73
+ readMetadata;
74
+ answerText = '';
75
+ reasoningText = '';
76
+ constructor(onProgress, readMetadata = noProviderMetadata) {
77
+ this.onProgress = onProgress;
78
+ this.readMetadata = readMetadata;
79
+ }
80
+ consume(notification) {
81
+ const update = notification.update;
82
+ switch (update.sessionUpdate) {
83
+ case 'agent_message_chunk':
84
+ if (update.content.type !== 'text') {
85
+ return;
86
+ }
87
+ this.answerText += update.content.text;
88
+ this.publishStream('answer', this.answerText, update.messageId);
89
+ return;
90
+ case 'agent_thought_chunk':
91
+ if (update.content.type !== 'text') {
92
+ return;
93
+ }
94
+ this.reasoningText += update.content.text;
95
+ this.publishStream('reasoning', this.reasoningText, update.messageId);
96
+ return;
97
+ case 'tool_call':
98
+ this.onProgress?.({
99
+ type: 'activity',
100
+ activity: toCreatedActivity(update, this.readMetadata(update._meta)),
101
+ });
102
+ return;
103
+ case 'tool_call_update':
104
+ this.onProgress?.({
105
+ type: 'activity',
106
+ activity: toPatchedActivity(update, this.readMetadata(update._meta)),
107
+ });
108
+ return;
109
+ case 'plan':
110
+ this.onProgress?.({ type: 'plan', items: update.entries.map(toPlanItem) });
111
+ return;
112
+ default:
113
+ return;
114
+ }
115
+ }
116
+ getFinalText() {
117
+ return this.answerText.trim();
118
+ }
119
+ publishStream(stream, text, messageId) {
120
+ if (!hasVisibleText(text)) {
121
+ return;
122
+ }
123
+ this.onProgress?.({
124
+ type: 'text',
125
+ stream,
126
+ text,
127
+ ...(typeof messageId === 'string' ? { messageId } : {}),
128
+ });
129
+ }
130
+ }
@@ -1,12 +1,11 @@
1
- import type { ProviderGenerateOptions, ProviderReply } from '../types.js';
1
+ import type { ProviderGenerateOptions, ProviderProgressUpdate, ProviderReply } from '../types.js';
2
2
  export declare const HOST_CONTROL_PREFIX = "[[BORGEE_CONTROL]] ";
3
3
  export declare const AWAITING_USER_CONTROL_PREFIX = "[[BORGEE_CONTROL]] ";
4
4
  export declare function parseProviderReply(text: string): ProviderReply;
5
5
  export declare function sanitizeAwaitingUserProgressText(text: string): string;
6
6
  export declare class AwaitingUserProgressSanitizer {
7
7
  private readonly onProgress?;
8
- private lastPublished;
9
8
  constructor(onProgress?: ProviderGenerateOptions['onProgress']);
10
- publish(text: string): void;
9
+ publishStructured(update: ProviderProgressUpdate): void;
11
10
  }
12
11
  export declare function createAwaitingUserProgressHandler(onProgress?: ProviderGenerateOptions['onProgress']): ProviderGenerateOptions['onProgress'] | undefined;
@@ -165,20 +165,18 @@ export function sanitizeAwaitingUserProgressText(text) {
165
165
  }
166
166
  export class AwaitingUserProgressSanitizer {
167
167
  onProgress;
168
- lastPublished = null;
169
168
  constructor(onProgress) {
170
169
  this.onProgress = onProgress;
171
170
  }
172
- publish(text) {
171
+ publishStructured(update) {
173
172
  if (!this.onProgress) {
174
173
  return;
175
174
  }
176
- const sanitizedText = sanitizeAwaitingUserProgressText(text);
177
- if (!hasVisibleText(sanitizedText) || sanitizedText === this.lastPublished) {
175
+ if (update.type === 'text') {
176
+ this.onProgress({ ...update, text: sanitizeAwaitingUserProgressText(update.text) });
178
177
  return;
179
178
  }
180
- this.lastPublished = sanitizedText;
181
- this.onProgress({ text: sanitizedText });
179
+ this.onProgress(update);
182
180
  }
183
181
  }
184
182
  export function createAwaitingUserProgressHandler(onProgress) {
@@ -187,6 +185,6 @@ export function createAwaitingUserProgressHandler(onProgress) {
187
185
  }
188
186
  const sanitizer = new AwaitingUserProgressSanitizer(onProgress);
189
187
  return (update) => {
190
- sanitizer.publish(update.text);
188
+ sanitizer.publishStructured(update);
191
189
  };
192
190
  }
@@ -0,0 +1,14 @@
1
+ import type { AcpActivityMetadataReader } from '../acp-progress-collector.js';
2
+ /**
3
+ * Reads what Claude says about a tool call beside what the protocol says.
4
+ *
5
+ * The description is the phrasing the adapter would show a user, which for a
6
+ * shell tool is not the title: the protocol reserves the title for the raw
7
+ * command line so a client can render a shell preview.
8
+ *
9
+ * The container flag is only ever set, never cleared, so it is read as a fact
10
+ * about the call rather than as a value that could arrive false. It is stamped
11
+ * on the call that spawns a sub-agent; the calls that sub-agent then makes point
12
+ * back at it through the parent id.
13
+ */
14
+ export declare const readClaudeActivityMetadata: AcpActivityMetadataReader;
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Where Claude's adapter hangs its own account of a tool call. The protocol
3
+ * fields carry what the protocol defines; everything the adapter knows in
4
+ * addition — the phrasing it would show a user, the call that spawned this one,
5
+ * why a call never ran — lives here under its own key.
6
+ */
7
+ const NAMESPACE = 'claudeCode';
8
+ function readString(source, key) {
9
+ const value = source[key];
10
+ return typeof value === 'string' && value.trim().length > 0 ? value : undefined;
11
+ }
12
+ function readObject(value) {
13
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
14
+ ? value
15
+ : undefined;
16
+ }
17
+ /**
18
+ * Why a call ended as it did, from the two places the adapter reports it.
19
+ *
20
+ * A permission denial and an ordinary failure are different shapes, not two
21
+ * spellings of one: a denial is a nested object naming the rule or the mode that
22
+ * refused, and everything else is a flat token drawn from an open set the
23
+ * adapter itself declines to validate. They arrive on different upstream events
24
+ * and rarely meet, so the rule is fixed here rather than left to whichever is
25
+ * read first: the denial wins, because it accounts for the same refusal at a
26
+ * finer grain than the token, which collapses a human's rejection and a rule's
27
+ * into one value on the permission wire this host answers over.
28
+ *
29
+ * `toolResponse` is a general slot the adapter also fills with a running call's
30
+ * elapsed time and with hook output, so a denial is recognised by the field that
31
+ * only a denial has, never by the slot being occupied.
32
+ */
33
+ function readReason(provider) {
34
+ const response = readObject(provider.toolResponse);
35
+ if (response && readString(response, 'decisionReasonType')) {
36
+ return (readString(response, 'decisionReason')
37
+ ?? readString(response, 'message')
38
+ ?? readString(response, 'decisionReasonType'));
39
+ }
40
+ const nonExecutionKind = readString(provider, 'nonExecutionKind');
41
+ if (!nonExecutionKind) {
42
+ return undefined;
43
+ }
44
+ const feedback = readString(provider, 'userFeedback');
45
+ return feedback ? `${nonExecutionKind}: ${feedback}` : nonExecutionKind;
46
+ }
47
+ /**
48
+ * Reads what Claude says about a tool call beside what the protocol says.
49
+ *
50
+ * The description is the phrasing the adapter would show a user, which for a
51
+ * shell tool is not the title: the protocol reserves the title for the raw
52
+ * command line so a client can render a shell preview.
53
+ *
54
+ * The container flag is only ever set, never cleared, so it is read as a fact
55
+ * about the call rather than as a value that could arrive false. It is stamped
56
+ * on the call that spawns a sub-agent; the calls that sub-agent then makes point
57
+ * back at it through the parent id.
58
+ */
59
+ export const readClaudeActivityMetadata = (meta) => {
60
+ const provider = readObject(meta?.[NAMESPACE]);
61
+ if (!provider) {
62
+ return {};
63
+ }
64
+ const metadata = {};
65
+ const description = readString(provider, 'title');
66
+ if (description) {
67
+ metadata.description = description;
68
+ }
69
+ const parentId = readString(provider, 'parentToolUseId');
70
+ if (parentId) {
71
+ metadata.parentId = parentId;
72
+ }
73
+ if (provider.subagent === true) {
74
+ metadata.subagent = true;
75
+ }
76
+ const reason = readReason(provider);
77
+ if (reason) {
78
+ metadata.reason = reason;
79
+ }
80
+ return metadata;
81
+ };
@@ -1,7 +1,7 @@
1
1
  import spawn from 'cross-spawn';
2
2
  import { PROTOCOL_VERSION, client, methods, ndJsonStream } from '@agentclientprotocol/sdk';
3
3
  import { type DebugLogger } from '../../debug.js';
4
- import type { PreparedPromptContext, PreparedProviderTurnInput, ProviderGenerateOptions } from '../../types.js';
4
+ import type { PreparedProviderTurnInput, ProviderGenerateOptions } from '../../types.js';
5
5
  import type { ClaudeChannelSessionStore } from './session-store.js';
6
6
  interface ClaudeAcpRuntime {
7
7
  spawn: typeof spawn;
@@ -20,7 +20,6 @@ interface ClaudeHostedImageInputConfig {
20
20
  borgeeBaseUrl?: string;
21
21
  agentApiKey?: string;
22
22
  }
23
- export declare function buildClaudeSessionSystemPrompt(promptContext?: PreparedPromptContext): string | undefined;
24
23
  /**
25
24
  * Persistent ACP-backed client for the Claude ACP adapter.
26
25
  *