@borgee/agents-host 0.2.35 → 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 (74) hide show
  1. package/README.md +23 -27
  2. package/dist/agents-host.d.ts +28 -5
  3. package/dist/agents-host.js +274 -236
  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 +6 -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 +44 -9
  15. package/dist/context/injection.js +360 -53
  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 +475 -53
  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 +14 -0
  24. package/dist/context/skill-manual.js +21 -0
  25. package/dist/context/turn-preparation.d.ts +8 -2
  26. package/dist/context/turn-preparation.js +68 -17
  27. package/dist/gateway/localhost-gateway.js +18 -8
  28. package/dist/hosted-turn-content.d.ts +15 -0
  29. package/dist/hosted-turn-content.js +50 -0
  30. package/dist/managed-daemon.d.ts +3 -2
  31. package/dist/managed-daemon.js +198 -37
  32. package/dist/plugin-sdk.js +276 -359
  33. package/dist/plugin-sdk.js.map +4 -4
  34. package/dist/progress-to-activity.d.ts +16 -0
  35. package/dist/progress-to-activity.js +24 -0
  36. package/dist/projection-strategy-values.d.ts +4 -0
  37. package/dist/projection-strategy-values.js +28 -0
  38. package/dist/providers/acp-progress-collector.d.ts +44 -0
  39. package/dist/providers/acp-progress-collector.js +130 -0
  40. package/dist/providers/awaiting-user.d.ts +2 -3
  41. package/dist/providers/awaiting-user.js +5 -7
  42. package/dist/providers/claude/activity-metadata.d.ts +14 -0
  43. package/dist/providers/claude/activity-metadata.js +81 -0
  44. package/dist/providers/claude/adapter.d.ts +3 -1
  45. package/dist/providers/claude/adapter.js +10 -0
  46. package/dist/providers/claude/cli-client.d.ts +9 -1
  47. package/dist/providers/claude/cli-client.js +270 -126
  48. package/dist/providers/codex/adapter.d.ts +3 -1
  49. package/dist/providers/codex/adapter.js +10 -0
  50. package/dist/providers/codex/cli-client.d.ts +10 -2
  51. package/dist/providers/codex/cli-client.js +88 -104
  52. package/dist/providers/codex/project-doc.js +22 -37
  53. package/dist/providers/copilot/adapter.d.ts +3 -1
  54. package/dist/providers/copilot/adapter.js +10 -0
  55. package/dist/providers/copilot/cli-client.d.ts +9 -1
  56. package/dist/providers/copilot/cli-client.js +68 -85
  57. package/dist/providers/create-provider.d.ts +3 -1
  58. package/dist/providers/create-provider.js +36 -9
  59. package/dist/providers/provider-adapter.d.ts +35 -0
  60. package/dist/providers/provider-adapter.js +44 -1
  61. package/dist/state-paths.d.ts +10 -2
  62. package/dist/state-paths.js +25 -6
  63. package/dist/task-thread-resolution.d.ts +3 -2
  64. package/dist/types.d.ts +163 -8
  65. package/package.json +2 -2
  66. package/skills/borgee-agent/SKILL.md +127 -38
  67. package/skills/borgee-agent/references/errors.md +38 -0
  68. package/skills/borgee-agent/references/task-properties.md +33 -0
  69. package/skills/borgee-agent/scripts/borgee-agent.mjs +553 -0
  70. package/skills/borgee-agent/scripts/borgee-agent.py +547 -0
  71. package/dist/durable-cursor-store.d.ts +0 -5
  72. package/dist/durable-cursor-store.js +0 -7
  73. package/skills/borgee-agent/borgee-agent.mjs +0 -562
  74. package/skills/borgee-agent/borgee-agent.py +0 -469
@@ -0,0 +1,64 @@
1
+ function buildWorkspaceRootLine(context) {
2
+ const resolvedWorkspace = context.resolvedWorkspace;
3
+ if (!resolvedWorkspace) {
4
+ return undefined;
5
+ }
6
+ if (resolvedWorkspace.authority === 'task') {
7
+ return `Writable task-isolated workspace root for this task thread: ${resolvedWorkspace.rootPath}.`;
8
+ }
9
+ if (context.taskAssignmentContext?.active === true) {
10
+ return `Writable workspace root inherited from this task thread's channel: ${resolvedWorkspace.rootPath}.`;
11
+ }
12
+ return `Writable workspace root for this channel: ${resolvedWorkspace.rootPath}.`;
13
+ }
14
+ function buildWorkspaceModeLine(context) {
15
+ if (context.taskAssignmentContext?.active !== true || !context.resolvedWorkspace) {
16
+ return undefined;
17
+ }
18
+ if (context.resolvedWorkspace.authority === 'task') {
19
+ return 'This task thread runs in a task-isolated workspace instead of inheriting the channel workspace.';
20
+ }
21
+ return 'This task thread inherits the channel workspace rather than using a task-isolated workspace.';
22
+ }
23
+ function buildWorkspaceLocalityLine(context) {
24
+ if (!context.resolvedWorkspace) {
25
+ return undefined;
26
+ }
27
+ if (context.resolvedWorkspace.authority === 'task') {
28
+ return 'This writable workspace is local to agents-host for this task thread. It does not imply that the target repository has already been checked out there.';
29
+ }
30
+ if (context.taskAssignmentContext?.active === true) {
31
+ return 'This writable workspace is local to agents-host for this task thread because the task inherits its channel workspace. It does not imply that the target repository has already been checked out there.';
32
+ }
33
+ return 'This writable workspace is local to agents-host for this channel. It does not imply that the target repository has already been checked out there.';
34
+ }
35
+ function buildCopilotCwdLine(context) {
36
+ if (!context.resolvedWorkspace) {
37
+ return undefined;
38
+ }
39
+ if (context.resolvedWorkspace.authority === 'task') {
40
+ return 'GitHub Copilot runs this turn with that task-isolated workspace as its real cwd.';
41
+ }
42
+ if (context.taskAssignmentContext?.active === true) {
43
+ return 'GitHub Copilot runs this turn with that inherited channel workspace as its real cwd.';
44
+ }
45
+ return 'GitHub Copilot runs this turn with that workspace as its real cwd.';
46
+ }
47
+ export function buildResolvedWorkspaceGuidanceLines(context, provider) {
48
+ if (!context?.resolvedWorkspace) {
49
+ return [];
50
+ }
51
+ const lines = [buildWorkspaceRootLine(context), buildWorkspaceModeLine(context)]
52
+ .filter((line) => line != null);
53
+ if (provider === 'copilot') {
54
+ const copilotCwdLine = buildCopilotCwdLine(context);
55
+ if (copilotCwdLine) {
56
+ lines.push(copilotCwdLine);
57
+ }
58
+ }
59
+ const localityLine = buildWorkspaceLocalityLine(context);
60
+ if (localityLine) {
61
+ lines.push(localityLine);
62
+ }
63
+ return lines;
64
+ }
@@ -0,0 +1,14 @@
1
+ import type { SkillRuntimeBootstrapMetadata } from '../types.js';
2
+ export declare function buildSkillManualReadLine(skillRuntime: SkillRuntimeBootstrapMetadata): string;
3
+ /**
4
+ * What every model-facing surface says about the packaged CLI: what it is, where its manual is, and
5
+ * that the manual has to be opened before acting. Nothing else about the CLI is stated on a host
6
+ * surface — the manual is self-contained, so the invocation grammar, the command inventory, the
7
+ * limits and the credential-file rules have one home that cannot drift from the implementations.
8
+ *
9
+ * The authorization sentence is written against the credential file rather than against "this turn"
10
+ * because the surfaces disagree about scope: the per-turn prompt is turn-scoped while Claude's
11
+ * session append and Codex's projected document are session-scoped, and only the per-turn prompt
12
+ * knows whether a credential file exists.
13
+ */
14
+ export declare function buildSkillManualLines(skillRuntime: SkillRuntimeBootstrapMetadata): string[];
@@ -0,0 +1,21 @@
1
+ export function buildSkillManualReadLine(skillRuntime) {
2
+ return `Read its manual at ${skillRuntime.skillMarkdownPath} before you act on this channel; it defines both the session-level coordination and delegation contract and the CLI invocation.`;
3
+ }
4
+ /**
5
+ * What every model-facing surface says about the packaged CLI: what it is, where its manual is, and
6
+ * that the manual has to be opened before acting. Nothing else about the CLI is stated on a host
7
+ * surface — the manual is self-contained, so the invocation grammar, the command inventory, the
8
+ * limits and the credential-file rules have one home that cannot drift from the implementations.
9
+ *
10
+ * The authorization sentence is written against the credential file rather than against "this turn"
11
+ * because the surfaces disagree about scope: the per-turn prompt is turn-scoped while Claude's
12
+ * session append and Codex's projected document are session-scoped, and only the per-turn prompt
13
+ * knows whether a credential file exists.
14
+ */
15
+ export function buildSkillManualLines(skillRuntime) {
16
+ return [
17
+ "A packaged local CLI reads this Borgee channel and acts on its tasks: channel history, visible participants, this channel's tasks and their properties, and short auxiliary mentions.",
18
+ buildSkillManualReadLine(skillRuntime),
19
+ 'Every command the manual documents is already authorized on a turn whose prompt names a gateway credential file. Run them directly and do not ask the user for permission first.',
20
+ ];
21
+ }
@@ -1,9 +1,15 @@
1
1
  import { type DebugLogger } from '../debug.js';
2
- import type { PreparedProviderTurnInput, ProviderInput } from '../types.js';
2
+ import type { ProjectionStrategy, PreparedProviderTurnInput, ProviderInput } from '../types.js';
3
3
  import { type ChannelContextStore } from './injection.js';
4
+ type ResolveProjectionStrategy = (input: ProviderInput) => Promise<ProjectionStrategy>;
4
5
  export declare class ProviderTurnPreparer {
5
6
  private readonly channelContextStore?;
6
7
  private readonly logger;
7
- constructor(channelContextStore?: ChannelContextStore | undefined, logger?: DebugLogger);
8
+ private readonly resolveProjectionStrategy;
9
+ constructor(channelContextStore?: ChannelContextStore | undefined, logger?: DebugLogger, options?: {
10
+ projectionStrategy?: ProjectionStrategy;
11
+ resolveProjectionStrategy?: ResolveProjectionStrategy;
12
+ });
8
13
  prepare(input: ProviderInput): Promise<PreparedProviderTurnInput>;
9
14
  }
15
+ export {};
@@ -1,6 +1,8 @@
1
1
  import { HostLogger, summarizeError } from '../debug.js';
2
- import { ChannelContextPreparationError, } from './injection.js';
3
- import { buildPrompt } from './prompt.js';
2
+ import { DEFAULT_PROJECTION_STRATEGY } from '../projection-strategy-values.js';
3
+ import { buildHostedIncomingContentText, buildHostedTurnContentParts } from '../hosted-turn-content.js';
4
+ import { buildRuntimeSurfaceForTurn, ChannelContextPreparationError, } from './injection.js';
5
+ import { buildClaudeSessionPromptAppend, buildClaudeTurnPrompt, buildPrompt } from './prompt.js';
4
6
  function providerLabel(provider) {
5
7
  if (provider === 'copilot') {
6
8
  return 'Copilot';
@@ -15,26 +17,36 @@ function toPromptContext(channelContext, input) {
15
17
  && !input.collaboration
16
18
  && !input.collaborationOutcome
17
19
  && !input.attentionSnapshot
20
+ && !input.compactionSnapshot
18
21
  && !input.collaborationCapabilities
19
22
  && !input.missedCollaborationDiagnostic
20
23
  && !input.taskThreadCollaborationContract) {
21
24
  return undefined;
22
25
  }
26
+ const runtimeSurface = channelContext?.payload.runtimeSurface ?? buildRuntimeSurfaceForTurn({
27
+ localhostGateway: channelContext?.payload.localhostGateway ?? channelContext?.localhostGateway,
28
+ collaboration: input.collaboration,
29
+ taskAssignmentContext: channelContext?.payload.taskAssignmentContext,
30
+ });
23
31
  return {
24
32
  ...(channelContext
25
33
  ? {
26
34
  channelContextPayloadPath: channelContext.payloadPath,
27
- gatewayAuthPath: channelContext.gatewayAuthPath,
35
+ claudeProjectedBriefPath: channelContext.claudeProjectedBriefPath,
36
+ claudeProjectedBriefHash: channelContext.claudeProjectedBriefHash,
37
+ gatewayCredentialPath: channelContext.gatewayCredentialPath,
28
38
  skillRuntime: channelContext.skillRuntime,
29
39
  localhostGateway: channelContext.localhostGateway,
30
40
  taskAssignmentContext: channelContext.payload.taskAssignmentContext,
31
- taskWorkspace: channelContext.taskWorkspace,
41
+ resolvedWorkspace: channelContext.resolvedWorkspace,
32
42
  }
33
43
  : {}),
44
+ runtimeSurface,
34
45
  collaborationTurnExecutionId: input.collaboration?.turnExecutionId,
35
46
  collaborationTurnMode: input.collaboration?.turnMode,
36
47
  collaborationOutcome: input.collaborationOutcome ?? channelContext?.payload.collaborationOutcome,
37
48
  attentionSnapshot: input.attentionSnapshot ?? channelContext?.payload.attentionSnapshot,
49
+ compactionSnapshot: input.compactionSnapshot ?? channelContext?.payload.compactionSnapshot,
38
50
  ...(() => {
39
51
  const collaborationCapabilities = input.collaborationCapabilities ?? channelContext?.payload.collaborationCapabilities;
40
52
  return collaborationCapabilities ? { collaborationCapabilities } : {};
@@ -70,16 +82,30 @@ function resolveProviderSessionRouting(input) {
70
82
  export class ProviderTurnPreparer {
71
83
  channelContextStore;
72
84
  logger;
73
- constructor(channelContextStore, logger = new HostLogger()) {
85
+ resolveProjectionStrategy;
86
+ constructor(channelContextStore, logger = new HostLogger(), options = {}) {
74
87
  this.channelContextStore = channelContextStore;
75
88
  this.logger = logger;
89
+ this.resolveProjectionStrategy = options.resolveProjectionStrategy
90
+ ?? (async () => options.projectionStrategy ?? DEFAULT_PROJECTION_STRATEGY);
76
91
  }
77
92
  async prepare(input) {
93
+ const incomingParts = input.incomingParts ?? buildHostedTurnContentParts({
94
+ text: input.incomingContent,
95
+ });
96
+ const incomingContent = incomingParts.length > 0
97
+ ? buildHostedIncomingContentText(incomingParts)
98
+ : input.incomingContent;
99
+ const projectionStrategy = input.provider === 'claude'
100
+ ? await this.resolveProjectionStrategy(input)
101
+ : undefined;
78
102
  let channelContext;
79
103
  if (this.channelContextStore) {
80
104
  try {
81
105
  const prepareInput = {
82
106
  channelId: input.channelId,
107
+ provider: input.provider,
108
+ projectionStrategy,
83
109
  collaboration: input.collaboration
84
110
  ? {
85
111
  ...input.collaboration,
@@ -89,6 +115,7 @@ export class ProviderTurnPreparer {
89
115
  : undefined,
90
116
  collaborationOutcome: input.collaborationOutcome,
91
117
  attentionSnapshot: input.attentionSnapshot,
118
+ compactionSnapshot: input.compactionSnapshot,
92
119
  collaborationCapabilities: input.collaborationCapabilities,
93
120
  missedCollaborationDiagnostic: input.missedCollaborationDiagnostic,
94
121
  ...(input.taskThreadCollaborationContract
@@ -97,8 +124,8 @@ export class ProviderTurnPreparer {
97
124
  ...(input.incomingMessageType !== undefined
98
125
  ? { incomingMessageType: input.incomingMessageType }
99
126
  : {}),
100
- ...(input.incomingMessageType !== undefined && input.incomingContent !== undefined
101
- ? { incomingContent: input.incomingContent }
127
+ ...(input.incomingMessageType !== undefined && incomingContent !== undefined
128
+ ? { incomingContent }
102
129
  : {}),
103
130
  };
104
131
  channelContext = await this.channelContextStore.prepare(prepareInput);
@@ -115,19 +142,43 @@ export class ProviderTurnPreparer {
115
142
  }
116
143
  const promptContext = toPromptContext(channelContext, input);
117
144
  return {
145
+ agentName: input.agentName,
118
146
  channelId: input.channelId,
147
+ incomingContent,
148
+ incomingParts,
119
149
  incomingEventKind: input.incomingEventKind,
120
150
  incomingMessageType: input.incomingMessageType,
121
- prompt: buildPrompt({
122
- agentName: input.agentName,
123
- provider: input.provider,
124
- channelId: input.channelId,
125
- incomingAuthorId: input.incomingAuthorId,
126
- incomingContent: input.incomingContent,
127
- incomingEventKind: input.incomingEventKind,
128
- incomingMessageType: input.incomingMessageType,
129
- promptContext,
130
- }),
151
+ prompt: input.provider === 'claude'
152
+ ? buildClaudeTurnPrompt({
153
+ agentName: input.agentName,
154
+ channelId: input.channelId,
155
+ incomingAuthorId: input.incomingAuthorId,
156
+ incomingContent,
157
+ incomingEventKind: input.incomingEventKind,
158
+ incomingMessageType: input.incomingMessageType,
159
+ promptContext,
160
+ promptStrategy: projectionStrategy,
161
+ })
162
+ : buildPrompt({
163
+ agentName: input.agentName,
164
+ provider: input.provider,
165
+ channelId: input.channelId,
166
+ incomingAuthorId: input.incomingAuthorId,
167
+ incomingContent,
168
+ incomingEventKind: input.incomingEventKind,
169
+ incomingMessageType: input.incomingMessageType,
170
+ promptContext,
171
+ }),
172
+ ...(input.provider === 'claude'
173
+ ? {
174
+ claudeSessionPromptAppend: buildClaudeSessionPromptAppend({
175
+ agentName: input.agentName,
176
+ promptContext,
177
+ promptStrategy: projectionStrategy,
178
+ }),
179
+ projectionStrategy,
180
+ }
181
+ : {}),
131
182
  promptContext,
132
183
  providerSessionRouting: resolveProviderSessionRouting(input),
133
184
  };
@@ -655,8 +655,14 @@ class LoopbackLocalhostGatewayController {
655
655
  this.recordAudit('authorized', 200, decision.path, method ?? 'PUT', decision.binding);
656
656
  }
657
657
  async loadCurrentThreadTask(binding) {
658
+ // Only a task-thread binding has a current task at all. Without that context the scan below
659
+ // would list every visible channel's tasks to conclude what the binding already says, so the
660
+ // parent-channel answer is given here instead of after an O(channels x tasks) sweep.
661
+ if (binding.payload?.taskAssignmentContext?.active !== true) {
662
+ throw new GatewayHttpError(404, { error: 'not_found' }, 'not-found');
663
+ }
658
664
  let task;
659
- const preferredTaskId = binding.payload?.taskAssignmentContext?.currentTaskId;
665
+ const preferredTaskId = binding.payload.taskAssignmentContext.currentTaskId;
660
666
  try {
661
667
  task = await findTaskForThread(this.controlPlane, binding.channelId, preferredTaskId);
662
668
  }
@@ -899,17 +905,21 @@ function mapGatewayControlPlaneError(error, fallbackReason) {
899
905
  case 'bpp.channel_id_required':
900
906
  case 'bpp.task_id_required':
901
907
  return new GatewayHttpError(400, { error: 'bad_request' }, 'bad-request');
902
- // An unregistered key or an oversized value is the caller's own mistake.
903
- // Falling through to 502 would tell the agent the server is broken, and it
904
- // would retry the same bad call instead of correcting it.
908
+ // An unregistered key, an oversized value or a status outside the enum is the
909
+ // caller's own mistake. Falling through to 502 would tell the agent the server
910
+ // is broken, and it would retry the same bad call instead of correcting it.
905
911
  case 'bpp.task_property_key_unknown':
906
912
  return new GatewayHttpError(400, { error: 'unknown_property_key' }, 'bad-request');
913
+ case 'bpp.task_property_value_invalid':
914
+ return new GatewayHttpError(400, { error: 'invalid_property_value' }, 'bad-request');
907
915
  case 'bpp.task_property_value_too_long':
908
916
  return new GatewayHttpError(400, { error: 'property_value_too_long' }, 'bad-request');
909
- // A distinct body, not the bare `not_found` the other routes use: the CLI
910
- // reads that one on a current-task request as "the thread's task could not
911
- // be resolved" and tells the agent to pass --task-id, which is the wrong
912
- // correction for a key that simply was not set.
917
+ case 'bpp.task_invalid_status':
918
+ return new GatewayHttpError(400, { error: 'invalid_status' }, 'bad-request');
919
+ // A distinct body, not the bare `not_found` the other routes use: on a
920
+ // current-task property request `not_found` means the thread's own task
921
+ // could not be resolved, and "pass the task id" is the wrong correction
922
+ // for a key that simply was not set.
913
923
  case 'bpp.task_property_not_found':
914
924
  return new GatewayHttpError(404, { error: 'property_not_found' }, 'not-found');
915
925
  default:
@@ -0,0 +1,15 @@
1
+ import type { HostedMessageAttachment, HostedTurnContentPart } from './types.js';
2
+ type CanonicalHostedMessageAttachment = HostedMessageAttachment & {
3
+ kind: NonNullable<HostedMessageAttachment['kind']>;
4
+ };
5
+ export declare function normalizeHostedMessageAttachment(attachment: HostedMessageAttachment): CanonicalHostedMessageAttachment;
6
+ export declare function classifyHostedAttachmentKind(contentType: string | undefined): Extract<HostedTurnContentPart, {
7
+ type: 'image' | 'file';
8
+ }>['type'];
9
+ export declare function buildHostedTurnContentParts(input: {
10
+ text?: string;
11
+ attachments?: readonly HostedMessageAttachment[];
12
+ }): HostedTurnContentPart[];
13
+ export declare function extractHostedMessageAttachments(parts: readonly HostedTurnContentPart[]): HostedMessageAttachment[];
14
+ export declare function buildHostedIncomingContentText(parts: readonly HostedTurnContentPart[]): string;
15
+ export {};
@@ -0,0 +1,50 @@
1
+ export function normalizeHostedMessageAttachment(attachment) {
2
+ return {
3
+ url: attachment.url,
4
+ ...(attachment.filename ? { filename: attachment.filename } : {}),
5
+ contentType: attachment.contentType,
6
+ kind: attachment.kind ?? classifyHostedAttachmentKind(attachment.contentType),
7
+ ...(attachment.sizeBytes !== undefined ? { sizeBytes: attachment.sizeBytes } : {}),
8
+ };
9
+ }
10
+ export function classifyHostedAttachmentKind(contentType) {
11
+ return contentType?.toLowerCase().startsWith('image/') ? 'image' : 'file';
12
+ }
13
+ export function buildHostedTurnContentParts(input) {
14
+ const text = input.text?.trim() ?? '';
15
+ const attachments = input.attachments ?? [];
16
+ const parts = [];
17
+ if (text.length > 0) {
18
+ parts.push({ type: 'text', text });
19
+ }
20
+ for (const attachment of attachments) {
21
+ const normalizedAttachment = normalizeHostedMessageAttachment(attachment);
22
+ parts.push({
23
+ type: normalizedAttachment.kind,
24
+ attachment: normalizedAttachment,
25
+ });
26
+ }
27
+ return parts;
28
+ }
29
+ export function extractHostedMessageAttachments(parts) {
30
+ return parts.flatMap((part) => ('attachment' in part ? [normalizeHostedMessageAttachment(part.attachment)] : []));
31
+ }
32
+ export function buildHostedIncomingContentText(parts) {
33
+ const text = parts
34
+ .flatMap((part) => (part.type === 'text' ? [part.text] : []))
35
+ .join('\n')
36
+ .trim();
37
+ const nonTextParts = parts.filter((part) => part.type === 'image' || part.type === 'file');
38
+ if (nonTextParts.length === 0) {
39
+ return text;
40
+ }
41
+ const summaryLines = [
42
+ `[Attachment metadata was received for ${nonTextParts.length} attachment${nonTextParts.length === 1 ? '' : 's'}]`,
43
+ ...nonTextParts.map((part, index) => {
44
+ const filename = part.attachment.filename?.trim();
45
+ return `${index + 1}. ${part.type};${filename ? ` filename: ${filename};` : ''} content type: ${part.attachment.contentType}`;
46
+ }),
47
+ ];
48
+ const summary = summaryLines.join('\n');
49
+ return text ? `${text}\n\n${summary}` : summary;
50
+ }
@@ -95,6 +95,7 @@ export interface ManagedAgentsHostDaemonDeps {
95
95
  materialize?: (rootPath: string, spec: LocalConfigGenerateSpec) => Promise<unknown>;
96
96
  logger?: Pick<Console, 'log' | 'error'>;
97
97
  logPath?: string;
98
+ platform?: NodeJS.Platform;
98
99
  }
99
100
  export interface DescribeManagedSpecOptions {
100
101
  serverUrl: string;
@@ -192,7 +193,7 @@ export declare function applyManagedSpec(options: ApplyManagedSpecOptions, deps?
192
193
  export declare class ManagedAgentsHostDaemon {
193
194
  private readonly rootPath;
194
195
  private readonly hostConfigPath;
195
- private readonly socketPath;
196
+ private readonly endpoint;
196
197
  private readonly createSupervisor;
197
198
  private readonly supervisor;
198
199
  private readonly loadSpec;
@@ -204,7 +205,7 @@ export declare class ManagedAgentsHostDaemon {
204
205
  private requestQueue;
205
206
  private started;
206
207
  private ready;
207
- private ownsSocket;
208
+ private ownsEndpoint;
208
209
  private shutdownRequested;
209
210
  private ownedSocketInode;
210
211
  constructor(rootPath: string, debug?: boolean, deps?: ManagedAgentsHostDaemonDeps);