@borgee/agents-host 0.2.73 → 0.2.84

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 (50) hide show
  1. package/README.md +17 -30
  2. package/dist/agents-host-supervisor.d.ts +1 -0
  3. package/dist/agents-host-supervisor.js +3 -1
  4. package/dist/agents-host.d.ts +6 -4
  5. package/dist/agents-host.js +117 -16
  6. package/dist/background-runs.d.ts +23 -0
  7. package/dist/background-runs.js +153 -0
  8. package/dist/chat/chat-control-plane.d.ts +6 -1
  9. package/dist/chat/sdk-chat-control-plane.d.ts +4 -2
  10. package/dist/chat/sdk-chat-control-plane.js +7 -0
  11. package/dist/context/claude-file-brief.js +3 -3
  12. package/dist/context/injection.d.ts +18 -12
  13. package/dist/context/injection.js +53 -53
  14. package/dist/context/prompt.js +10 -10
  15. package/dist/context/resolved-working-folder.d.ts +3 -0
  16. package/dist/context/resolved-working-folder.js +106 -0
  17. package/dist/context/turn-preparation.js +1 -1
  18. package/dist/local-config.d.ts +7 -0
  19. package/dist/local-config.js +158 -12
  20. package/dist/managed-daemon.js +42 -35
  21. package/dist/plugin-sdk.js +179 -9
  22. package/dist/plugin-sdk.js.map +3 -3
  23. package/dist/policy/authorization-audit.d.ts +1 -1
  24. package/dist/policy/copilot-permission.d.ts +9 -0
  25. package/dist/policy/copilot-permission.js +120 -1
  26. package/dist/progress-to-activity.d.ts +1 -1
  27. package/dist/progress-to-activity.js +1 -0
  28. package/dist/providers/claude/adapter.d.ts +1 -1
  29. package/dist/providers/claude/adapter.js +7 -0
  30. package/dist/providers/claude/cli-client.js +4 -3
  31. package/dist/providers/codex/adapter.d.ts +1 -1
  32. package/dist/providers/codex/adapter.js +7 -0
  33. package/dist/providers/codex/cli-client.js +8 -7
  34. package/dist/providers/codex/project-doc.js +6 -6
  35. package/dist/providers/copilot/adapter.d.ts +6 -2
  36. package/dist/providers/copilot/adapter.js +27 -1
  37. package/dist/providers/copilot/cli-client.d.ts +37 -10
  38. package/dist/providers/copilot/cli-client.js +751 -122
  39. package/dist/providers/copilot/sdk-session.d.ts +149 -0
  40. package/dist/providers/copilot/sdk-session.js +981 -0
  41. package/dist/providers/create-provider.js +33 -0
  42. package/dist/providers/provider-adapter.d.ts +16 -1
  43. package/dist/providers/provider-adapter.js +13 -0
  44. package/dist/types.d.ts +32 -11
  45. package/package.json +3 -2
  46. package/skills/borgee-agent/references/task-properties.md +3 -3
  47. package/skills/borgee-agent/scripts/borgee-agent.mjs +1 -1
  48. package/skills/borgee-agent/scripts/borgee-agent.py +1 -1
  49. package/dist/context/resolved-workspace.d.ts +0 -3
  50. package/dist/context/resolved-workspace.js +0 -106
@@ -0,0 +1,153 @@
1
+ import { createHash } from 'node:crypto';
2
+ const STOP_UNSUPPORTED_REASON = 'This provider does not support targeted background-run cancellation';
3
+ const MAX_TRACKED_RUNS = 128;
4
+ export class BackgroundRunTracker {
5
+ report;
6
+ options;
7
+ now;
8
+ mintRunId;
9
+ runs = new Map();
10
+ runsById = new Map();
11
+ constructor(report, options, now = Date.now, mintRunId = stableRunId) {
12
+ this.report = report;
13
+ this.options = options;
14
+ this.now = now;
15
+ this.mintRunId = mintRunId;
16
+ }
17
+ observe(channelId, update) {
18
+ if (!this.options.lifecycleSupported || update.type !== 'background_run')
19
+ return;
20
+ const key = `${channelId}\0${update.run.providerRunId}`;
21
+ const held = this.runs.get(key);
22
+ const state = runState(update.run.state);
23
+ const stopSupported = update.run.stopSupported &&
24
+ this.options.targetedCancellationSupported &&
25
+ this.options.cancel !== undefined;
26
+ const executionChanged = held !== undefined &&
27
+ update.run.startedAt !== undefined &&
28
+ update.run.startedAt !== held.startedAt;
29
+ const startsNewExecution = !held || executionChanged || (isTerminal(held.state) && !isTerminal(state));
30
+ if (held && isTerminal(held.state) && isTerminal(state) && !startsNewExecution)
31
+ return;
32
+ const startedAt = startsNewExecution
33
+ ? (update.run.startedAt ?? this.now())
34
+ : (update.run.startedAt ?? held.startedAt);
35
+ const run = startsNewExecution
36
+ ? {
37
+ channelId,
38
+ providerRunId: update.run.providerRunId,
39
+ runId: this.mintRunId(channelId, update.run.providerRunId, startedAt),
40
+ label: update.run.label,
41
+ state,
42
+ startedAt,
43
+ finishedAt: update.run.finishedAt,
44
+ reason: update.run.reason,
45
+ stopSupported,
46
+ }
47
+ : {
48
+ ...held,
49
+ label: update.run.label,
50
+ state,
51
+ startedAt,
52
+ finishedAt: update.run.finishedAt,
53
+ reason: update.run.reason,
54
+ stopSupported,
55
+ };
56
+ if (isTerminal(state) && run.finishedAt === undefined) {
57
+ run.finishedAt = this.now();
58
+ }
59
+ if (held && held.runId !== run.runId) {
60
+ this.runsById.delete(held.runId);
61
+ }
62
+ this.runs.delete(key);
63
+ this.runs.set(key, run);
64
+ this.runsById.set(run.runId, run);
65
+ while (this.runs.size > MAX_TRACKED_RUNS) {
66
+ const oldestKey = this.runs.keys().next().value;
67
+ if (typeof oldestKey !== 'string') {
68
+ break;
69
+ }
70
+ const oldest = this.runs.get(oldestKey);
71
+ this.runs.delete(oldestKey);
72
+ if (oldest) {
73
+ this.runsById.delete(oldest.runId);
74
+ }
75
+ }
76
+ this.publish(run);
77
+ }
78
+ async stop(channelId, runId) {
79
+ if (!this.options.lifecycleSupported ||
80
+ !this.options.targetedCancellationSupported ||
81
+ !this.options.cancel) {
82
+ return {
83
+ outcome: 'unsupported',
84
+ reason: STOP_UNSUPPORTED_REASON,
85
+ };
86
+ }
87
+ const run = this.runsById.get(runId);
88
+ if (!run || run.channelId !== channelId || isTerminal(run.state)) {
89
+ return { outcome: 'not_found' };
90
+ }
91
+ if (!run.stopSupported) {
92
+ return {
93
+ outcome: 'unsupported',
94
+ reason: STOP_UNSUPPORTED_REASON,
95
+ };
96
+ }
97
+ if (!(await this.options.cancel(channelId, run.providerRunId))) {
98
+ return { outcome: 'not_found' };
99
+ }
100
+ const current = this.runsById.get(runId);
101
+ if (current && current.channelId === channelId && !isTerminal(current.state)) {
102
+ current.state = 'stopping';
103
+ this.publish(current);
104
+ }
105
+ return { outcome: 'stopped' };
106
+ }
107
+ publish(run) {
108
+ this.report(run.channelId, {
109
+ shape: 'background_run',
110
+ runId: run.runId,
111
+ label: run.label,
112
+ state: run.state,
113
+ startedAt: run.startedAt,
114
+ ...(run.finishedAt !== undefined ? { finishedAt: run.finishedAt } : {}),
115
+ ...(run.reason ? { reason: run.reason } : {}),
116
+ stopSupported: run.stopSupported,
117
+ ...(!run.stopSupported ? { stopUnsupportedReason: STOP_UNSUPPORTED_REASON } : {}),
118
+ });
119
+ }
120
+ }
121
+ function runState(status) {
122
+ switch (status) {
123
+ case 'running':
124
+ return 'running';
125
+ case 'waiting':
126
+ return 'waiting';
127
+ case 'completed':
128
+ return 'completed';
129
+ case 'failed':
130
+ return 'failed';
131
+ case 'cancelled':
132
+ return 'cancelled';
133
+ case 'unknown':
134
+ return 'unknown';
135
+ }
136
+ }
137
+ function isTerminal(state) {
138
+ return (state === 'completed' ||
139
+ state === 'failed' ||
140
+ state === 'cancelled' ||
141
+ state === 'unknown');
142
+ }
143
+ function stableRunId(channelId, providerRunId, startedAt) {
144
+ const digest = createHash('sha256')
145
+ .update(channelId)
146
+ .update('\0')
147
+ .update(providerRunId)
148
+ .update('\0')
149
+ .update(String(startedAt))
150
+ .digest('hex')
151
+ .slice(0, 32);
152
+ return `run_${digest}`;
153
+ }
@@ -1,7 +1,8 @@
1
- import type { ReportTurnActivityInput, StopTurnHandler, TurnActivityReporter } from '../plugin-sdk.js';
1
+ import type { AgentActivity, ReportTurnActivityInput, StopBackgroundRunHandler, StopTurnHandler, TurnActivityReporter } from '../plugin-sdk.js';
2
2
  import type { ChannelSummary, ChannelHistoryEntry, ChannelMessageEvent, CreateTaskInput, DirectoryUser, MeResponseUser, PostMessageInput, PostedMessage, ReadChannelHistoryInput, Task, UpdateTaskInput } from '../types.js';
3
3
  export interface ChatControlPlane {
4
4
  onStopTurn?(handler: StopTurnHandler | undefined): void;
5
+ onStopBackgroundRun?(handler: StopBackgroundRunHandler | undefined): void;
5
6
  connect(onMessage: (message: ChannelMessageEvent) => void): Promise<void>;
6
7
  close(): Promise<void>;
7
8
  postMessage(input: PostMessageInput): Promise<PostedMessage>;
@@ -10,6 +11,10 @@ export interface ChatControlPlane {
10
11
  startTyping(channelId: string): () => void;
11
12
  /** Opens one turn's report on the activity rail. Fire-and-forget: nothing recovers what it sends. */
12
13
  reportTurnActivity(input: ReportTurnActivityInput): TurnActivityReporter;
14
+ reportActivity?(input: {
15
+ channelId: string;
16
+ activity: AgentActivity;
17
+ }): void;
13
18
  getMe(): Promise<MeResponseUser>;
14
19
  listUsers(): Promise<DirectoryUser[]>;
15
20
  listChannels(): Promise<ChannelSummary[]>;
@@ -1,7 +1,7 @@
1
- import { type BorgeePluginClient, type BorgeePluginOptions, type InboundMessageEvent, type ReportTurnActivityInput, type StopTurnHandler, type TurnActivityReporter } from '../plugin-sdk.js';
1
+ import { type BorgeePluginClient, type BorgeePluginOptions, type InboundMessageEvent, type ReportTurnActivityInput, type StopTurnHandler, type StopBackgroundRunHandler, type TurnActivityReporter } from '../plugin-sdk.js';
2
2
  import type { ChannelSummary, ChannelHistoryEntry, ChannelMessageEvent, CreateTaskInput, DirectoryUser, MeResponseUser, PostMessageInput, PostedMessage, ReadChannelHistoryInput, Task, UpdateTaskInput } from '../types.js';
3
3
  import type { ChatControlPlane } from './chat-control-plane.js';
4
- type PluginClientLike = Pick<BorgeePluginClient, 'agentId' | 'close' | 'connect' | 'createTask' | 'deleteMessage' | 'editMessage' | 'getMe' | 'getTask' | 'listChannels' | 'listTasks' | 'listUsers' | 'on' | 'onStopTurn' | 'readHistory' | 'reportTurnActivity' | 'sendMessage' | 'startTyping' | 'updateTask' | 'setTaskProperty' | 'deleteTaskProperty'>;
4
+ type PluginClientLike = Pick<BorgeePluginClient, 'agentId' | 'close' | 'connect' | 'createTask' | 'deleteMessage' | 'editMessage' | 'getMe' | 'getTask' | 'listChannels' | 'listTasks' | 'listUsers' | 'on' | 'onStopTurn' | 'onStopBackgroundRun' | 'reportActivity' | 'readHistory' | 'reportTurnActivity' | 'sendMessage' | 'startTyping' | 'updateTask' | 'setTaskProperty' | 'deleteTaskProperty'>;
5
5
  type PluginClientFactory = (options: BorgeePluginOptions) => PluginClientLike;
6
6
  type SdkChatControlPlaneOptions = Pick<BorgeePluginOptions, 'pluginId'>;
7
7
  /**
@@ -17,9 +17,11 @@ export declare class SdkChatControlPlane implements ChatControlPlane {
17
17
  private me;
18
18
  constructor(baseUrl: string, apiKey: string, createClient?: PluginClientFactory, options?: SdkChatControlPlaneOptions);
19
19
  onStopTurn(handler: StopTurnHandler | undefined): void;
20
+ onStopBackgroundRun(handler: StopBackgroundRunHandler | undefined): void;
20
21
  connect(onMessage: (message: ChannelMessageEvent) => void): Promise<void>;
21
22
  close(): Promise<void>;
22
23
  postMessage(input: PostMessageInput): Promise<PostedMessage>;
24
+ reportActivity(input: Parameters<BorgeePluginClient['reportActivity']>[0]): void;
23
25
  editMessage(messageId: string, content: string): Promise<void>;
24
26
  deleteMessage(messageId: string): Promise<void>;
25
27
  startTyping(channelId: string): () => void;
@@ -17,6 +17,9 @@ export class SdkChatControlPlane {
17
17
  onStopTurn(handler) {
18
18
  this.client.onStopTurn(handler);
19
19
  }
20
+ onStopBackgroundRun(handler) {
21
+ this.client.onStopBackgroundRun?.(handler);
22
+ }
20
23
  async connect(onMessage) {
21
24
  this.unsubscribe = this.client.on('message', (event) => {
22
25
  const message = mapInboundToChannelMessage(event);
@@ -50,6 +53,7 @@ export class SdkChatControlPlane {
50
53
  this.unsubscribe?.();
51
54
  this.unsubscribe = null;
52
55
  this.client.onStopTurn(undefined);
56
+ this.client.onStopBackgroundRun?.(undefined);
53
57
  await this.client.close();
54
58
  }
55
59
  async postMessage(input) {
@@ -60,6 +64,9 @@ export class SdkChatControlPlane {
60
64
  });
61
65
  return { messageId: sent.messageId };
62
66
  }
67
+ reportActivity(input) {
68
+ this.client.reportActivity(input);
69
+ }
63
70
  async editMessage(messageId, content) {
64
71
  await this.client.editMessage({ messageId, body: content });
65
72
  }
@@ -1,6 +1,6 @@
1
1
  import { buildCollaborationCapabilityDeclarationSummaryLines, } from './collaboration-capabilities-diagnostics.js';
2
2
  import { MAIN_SESSION_DELEGATION_LINES } from './main-session-delegation.js';
3
- import { buildResolvedWorkspaceGuidanceLines } from './resolved-workspace.js';
3
+ import { buildResolvedWorkingFolderGuidanceLines } from './resolved-working-folder.js';
4
4
  import { buildSkillManualLines, buildSkillManualReadLine } from './skill-manual.js';
5
5
  import { PARENT_CHANNEL_TASK_BOUNDARY_LINES } from './task-channel-boundary.js';
6
6
  function buildStableSkillLines(context) {
@@ -60,8 +60,8 @@ export function buildClaudeFileBrief(context) {
60
60
  '',
61
61
  ...sessionDelegationLines,
62
62
  ...(() => {
63
- const workspaceLines = buildResolvedWorkspaceGuidanceLines(context);
64
- return workspaceLines.length > 0 ? ['', ...workspaceLines] : [];
63
+ const workingFolderLines = buildResolvedWorkingFolderGuidanceLines(context);
64
+ return workingFolderLines.length > 0 ? ['', ...workingFolderLines] : [];
65
65
  })(),
66
66
  ...(() => {
67
67
  const capabilityLines = buildCollaborationCapabilityDeclarationSummaryLines(context?.collaborationCapabilities);
@@ -1,5 +1,5 @@
1
1
  import type { ChatControlPlane } from '../chat/chat-control-plane.js';
2
- import type { AttentionSnapshot, ProjectionStrategy, CollaborationCapabilityDeclaration, CollaborationOutcomeSnapshot, CompactionSnapshot, LocalhostGatewayBootstrapMetadata, MissedCollaborationDiagnostic, ProviderKind, ProviderCollaborationContext, ResolvedWorkspaceContext, RuntimeSurface, SkillRuntimeBootstrapMetadata, TaskThreadCollaborationContract, TaskAssignmentThreadContext } from '../types.js';
2
+ import type { AttentionSnapshot, ProjectionStrategy, CollaborationCapabilityDeclaration, CollaborationOutcomeSnapshot, CompactionSnapshot, LocalhostGatewayBootstrapMetadata, MissedCollaborationDiagnostic, ProviderKind, ProviderCollaborationContext, ResolvedWorkingFolderContext, RuntimeSurface, SkillRuntimeBootstrapMetadata, TaskThreadCollaborationContract, TaskAssignmentThreadContext } from '../types.js';
3
3
  export interface SkillRuntimeBootstrapPayload {
4
4
  skillDirectoryPath: string;
5
5
  nodeCliPath: string;
@@ -18,7 +18,13 @@ export interface ChannelContextPayload {
18
18
  skillRuntime?: SkillRuntimeBootstrapPayload;
19
19
  localhostGateway?: LocalhostGatewayBootstrapMetadata;
20
20
  taskAssignmentContext?: TaskAssignmentThreadContext;
21
- resolvedWorkspace?: ResolvedWorkspaceContext;
21
+ /**
22
+ * Serialized into `context.json` and echoed by the gateway bootstrap response, so the key name is
23
+ * observable outside this process: `cleanup --purge` reads payloads written by whichever version
24
+ * happened to run last, and a payload whose key it does not recognize is skipped, silently leaving
25
+ * the directory it names on disk.
26
+ */
27
+ resolvedWorkspace?: ResolvedWorkingFolderContext;
22
28
  }
23
29
  export interface PreparedChannelContext {
24
30
  directoryPath: string;
@@ -29,7 +35,7 @@ export interface PreparedChannelContext {
29
35
  gatewayCredentialPath?: string;
30
36
  skillRuntime?: SkillRuntimeBootstrapMetadata;
31
37
  localhostGateway?: LocalhostGatewayBootstrapMetadata;
32
- resolvedWorkspace?: ResolvedWorkspaceContext;
38
+ resolvedWorkingFolder?: ResolvedWorkingFolderContext;
33
39
  }
34
40
  export declare class ChannelContextPreparationError extends Error {
35
41
  readonly partialContext: PreparedChannelContext;
@@ -109,7 +115,7 @@ interface FileChannelContextStoreOptions {
109
115
  skillRuntimeEnabled?: boolean;
110
116
  skillAssetResolver?: SkillAssetResolver;
111
117
  localhostGateway?: LocalhostGatewayContextPublisher;
112
- workspaceCollectionRootDir?: string;
118
+ workingFolderCollectionRootDir?: string;
113
119
  taskReader?: Pick<ChatControlPlane, 'getTask' | 'listChannels' | 'listTasks' | 'listUsers'>;
114
120
  taskResolutionTimeoutMs?: number;
115
121
  allowCrossAgentIndependentWorkspaceHandoff?: boolean;
@@ -128,13 +134,13 @@ export declare function resolveChannelContextDirectory(stateRootDir: string, cha
128
134
  export declare function resolveChannelContextPayloadPath(stateRootDir: string, channelId: string): string;
129
135
  export declare function resolveTaskAssignmentStateDirectory(stateRootDir: string, channelId: string): string;
130
136
  export declare function resolveTaskAssignmentStatePath(stateRootDir: string, channelId: string): string;
131
- export declare function resolveTaskWorkspaceRootDirectory(startupWorkspaceRootDir: string): string;
132
- export declare function resolveManagedWorkspaceCollectionRoot(workspaceCollectionRootDir: string | undefined, env?: NodeJS.ProcessEnv, resolvedHomeDir?: string): string;
133
- export declare function resolveChannelWorkspaceRootDirectory(workspaceCollectionRootDir: string, channelId: string): string;
134
- export declare function resolveChannelWorkspaceDirectory(workspaceCollectionRootDir: string, channelId: string): string;
135
- export declare function resolveTaskIsolatedWorkspaceDirectory(workspaceCollectionRootDir: string, channelId: string, taskId: string): string;
136
- export declare const resolveTaskWorkspaceDirectory: typeof resolveTaskIsolatedWorkspaceDirectory;
137
- export declare function resolveTaskThreadScratchWorkspaceDirectory(workspaceCollectionRootDir: string, channelId: string): string;
137
+ export declare function resolveTaskWorkingFolderRootDirectory(channelRootDir: string): string;
138
+ export declare function resolveManagedWorkingFolderCollectionRoot(workingFolderCollectionRootDir: string | undefined, env?: NodeJS.ProcessEnv, resolvedHomeDir?: string): string;
139
+ export declare function resolveChannelWorkingFolderRootDirectory(workingFolderCollectionRootDir: string, channelId: string): string;
140
+ export declare function resolveChannelWorkingFolderDirectory(workingFolderCollectionRootDir: string, channelId: string): string;
141
+ export declare function resolveTaskIsolatedWorkingFolderDirectory(workingFolderCollectionRootDir: string, channelId: string, taskId: string): string;
142
+ export declare const resolveTaskWorkingFolderDirectory: typeof resolveTaskIsolatedWorkingFolderDirectory;
143
+ export declare function resolveTaskThreadScratchWorkingFolderDirectory(workingFolderCollectionRootDir: string, channelId: string): string;
138
144
  export declare function resolveChannelGatewayCredentialPath(stateRootDir: string, channelId: string): string;
139
145
  export declare function resolveGatewayCredentialPathFromPayloadPath(payloadPath: string): string;
140
146
  export declare function resolveClaudeProjectedBriefPathFromPayloadPath(payloadPath: string): string;
@@ -146,7 +152,7 @@ export declare class FileChannelContextStore implements ChannelContextStore {
146
152
  private readonly skillRuntimeEnabled;
147
153
  private readonly skillAssetResolver;
148
154
  private readonly localhostGateway?;
149
- private readonly workspaceCollectionRootDir;
155
+ private readonly workingFolderCollectionRootDir;
150
156
  private readonly taskReader?;
151
157
  private readonly taskResolutionTimeoutMs;
152
158
  private readonly allowCrossAgentIndependentWorkspaceHandoff;
@@ -12,12 +12,12 @@ const TASK_ASSIGNMENT_STATE_FILENAME = 'context.json';
12
12
  const CHANNEL_GATEWAY_CREDENTIAL_FILENAME = '.borgee-agent-gateway.json';
13
13
  const CLAUDE_PROJECTED_BRIEF_FILENAME = 'CLAUDE.md';
14
14
  const DEFAULT_HOME_BORGEE_DIRNAME = '.borgee';
15
- const CHANNEL_WORKSPACE_COLLECTION_DIRNAME = 'channels';
16
- const CHANNEL_WORKSPACE_DIRNAME = 'workspace';
17
- const TASK_ISOLATED_WORKSPACE_COLLECTION_DIRNAME = 'tasks';
18
- const TASK_THREAD_SCRATCH_WORKSPACE_DIRNAME = 'scratch';
15
+ const CHANNEL_WORKING_FOLDER_COLLECTION_DIRNAME = 'channels';
16
+ const CHANNEL_WORKING_FOLDER_DIRNAME = 'workspace';
17
+ const TASK_ISOLATED_WORKING_FOLDER_COLLECTION_DIRNAME = 'tasks';
18
+ const TASK_THREAD_SCRATCH_WORKING_FOLDER_DIRNAME = 'scratch';
19
19
  const TASK_EXECUTION_LOCAL_DIRECTORY_PROPERTY_KEY = 'execution.local_directory';
20
- const DEFAULT_TASK_WORKSPACE_RESOLUTION_TIMEOUT_MS = 1_500;
20
+ const DEFAULT_TASK_WORKING_FOLDER_RESOLUTION_TIMEOUT_MS = 1_500;
21
21
  export class ChannelContextPreparationError extends Error {
22
22
  partialContext;
23
23
  constructor(message, partialContext, options) {
@@ -113,7 +113,7 @@ function buildPromptContextForClaudeProjectedBrief(preparedContext) {
113
113
  skillRuntime: preparedContext.skillRuntime,
114
114
  localhostGateway: preparedContext.localhostGateway,
115
115
  taskAssignmentContext: preparedContext.payload.taskAssignmentContext,
116
- resolvedWorkspace: preparedContext.resolvedWorkspace,
116
+ resolvedWorkingFolder: preparedContext.resolvedWorkingFolder,
117
117
  runtimeSurface: preparedContext.payload.runtimeSurface,
118
118
  collaborationOutcome: preparedContext.payload.collaborationOutcome,
119
119
  attentionSnapshot: preparedContext.payload.attentionSnapshot,
@@ -246,17 +246,17 @@ async function readExistingTaskAssignmentContext(statePath, fileSystem) {
246
246
  return undefined;
247
247
  }
248
248
  }
249
- function buildChannelResolvedWorkspace(workspaceCollectionRootDir, channelId) {
249
+ function buildChannelResolvedWorkingFolder(workingFolderCollectionRootDir, channelId) {
250
250
  return {
251
251
  authority: 'channel',
252
252
  owningChannelId: channelId,
253
- rootPath: resolveChannelWorkspaceDirectory(workspaceCollectionRootDir, channelId),
253
+ rootPath: resolveChannelWorkingFolderDirectory(workingFolderCollectionRootDir, channelId),
254
254
  };
255
255
  }
256
- function buildTaskThreadScratchResolvedWorkspace(workspaceCollectionRootDir, channelId, reason = 'missing-or-invalid-execution-target') {
256
+ function buildTaskThreadScratchResolvedWorkingFolder(workingFolderCollectionRootDir, channelId, reason = 'missing-or-invalid-execution-target') {
257
257
  return {
258
258
  authority: 'task-thread-scratch',
259
- rootPath: resolveTaskThreadScratchWorkspaceDirectory(workspaceCollectionRootDir, channelId),
259
+ rootPath: resolveTaskThreadScratchWorkingFolderDirectory(workingFolderCollectionRootDir, channelId),
260
260
  reason,
261
261
  };
262
262
  }
@@ -312,23 +312,23 @@ async function isExistingDirectory(fileSystem, path) {
312
312
  return false;
313
313
  }
314
314
  }
315
- async function resolveTaskThreadWorkspaceContext(params) {
316
- const channelWorkspace = buildChannelResolvedWorkspace(params.workspaceCollectionRootDir, params.channelId);
317
- const discussionOnlyWorkspace = buildTaskThreadScratchResolvedWorkspace(params.workspaceCollectionRootDir, params.channelId);
315
+ async function resolveTaskThreadWorkingFolderContext(params) {
316
+ const channelWorkingFolder = buildChannelResolvedWorkingFolder(params.workingFolderCollectionRootDir, params.channelId);
317
+ const discussionOnlyWorkingFolder = buildTaskThreadScratchResolvedWorkingFolder(params.workingFolderCollectionRootDir, params.channelId);
318
318
  const taskAssignmentContext = params.taskAssignmentContext;
319
319
  const currentTaskId = taskAssignmentContext?.currentTaskId?.trim();
320
320
  if (taskAssignmentContext?.active !== true) {
321
- return { taskAssignmentContext, resolvedWorkspace: channelWorkspace };
321
+ return { taskAssignmentContext, resolvedWorkingFolder: channelWorkingFolder };
322
322
  }
323
323
  if (!currentTaskId || !params.taskReader) {
324
- return { taskAssignmentContext, resolvedWorkspace: discussionOnlyWorkspace };
324
+ return { taskAssignmentContext, resolvedWorkingFolder: discussionOnlyWorkingFolder };
325
325
  }
326
326
  try {
327
- const task = await withDeadline(findTaskForThread(params.taskReader, params.channelId, currentTaskId), params.taskResolutionTimeoutMs, `workspace resolution timed out for task ${currentTaskId}`);
327
+ const task = await withDeadline(findTaskForThread(params.taskReader, params.channelId, currentTaskId), params.taskResolutionTimeoutMs, `working folder resolution timed out for task ${currentTaskId}`);
328
328
  if (!task) {
329
329
  return {
330
330
  taskAssignmentContext,
331
- resolvedWorkspace: discussionOnlyWorkspace,
331
+ resolvedWorkingFolder: discussionOnlyWorkingFolder,
332
332
  };
333
333
  }
334
334
  const executionLocalDirectory = task.properties[TASK_EXECUTION_LOCAL_DIRECTORY_PROPERTY_KEY];
@@ -337,7 +337,7 @@ async function resolveTaskThreadWorkspaceContext(params) {
337
337
  || !(await isExistingDirectory(params.fileSystem, executionLocalDirectory))) {
338
338
  return {
339
339
  taskAssignmentContext,
340
- resolvedWorkspace: discussionOnlyWorkspace,
340
+ resolvedWorkingFolder: discussionOnlyWorkingFolder,
341
341
  };
342
342
  }
343
343
  if (!params.allowCrossAgentIndependentWorkspaceHandoff
@@ -348,12 +348,12 @@ async function resolveTaskThreadWorkspaceContext(params) {
348
348
  })) {
349
349
  return {
350
350
  taskAssignmentContext,
351
- resolvedWorkspace: buildTaskThreadScratchResolvedWorkspace(params.workspaceCollectionRootDir, params.channelId, 'cross-agent-independent-workspace-disabled'),
351
+ resolvedWorkingFolder: buildTaskThreadScratchResolvedWorkingFolder(params.workingFolderCollectionRootDir, params.channelId, 'cross-agent-independent-workspace-disabled'),
352
352
  };
353
353
  }
354
354
  return {
355
355
  taskAssignmentContext,
356
- resolvedWorkspace: {
356
+ resolvedWorkingFolder: {
357
357
  authority: 'task-execution-target',
358
358
  taskId: task.id,
359
359
  rootPath: executionLocalDirectory,
@@ -363,7 +363,7 @@ async function resolveTaskThreadWorkspaceContext(params) {
363
363
  catch (error) {
364
364
  return {
365
365
  taskAssignmentContext,
366
- resolvedWorkspace: discussionOnlyWorkspace,
366
+ resolvedWorkingFolder: discussionOnlyWorkingFolder,
367
367
  };
368
368
  }
369
369
  }
@@ -383,32 +383,32 @@ export function resolveTaskAssignmentStateDirectory(stateRootDir, channelId) {
383
383
  export function resolveTaskAssignmentStatePath(stateRootDir, channelId) {
384
384
  return join(resolveTaskAssignmentStateDirectory(stateRootDir, channelId), TASK_ASSIGNMENT_STATE_FILENAME);
385
385
  }
386
- export function resolveTaskWorkspaceRootDirectory(startupWorkspaceRootDir) {
387
- return join(resolve(startupWorkspaceRootDir), TASK_ISOLATED_WORKSPACE_COLLECTION_DIRNAME);
386
+ export function resolveTaskWorkingFolderRootDirectory(channelRootDir) {
387
+ return join(resolve(channelRootDir), TASK_ISOLATED_WORKING_FOLDER_COLLECTION_DIRNAME);
388
388
  }
389
- export function resolveManagedWorkspaceCollectionRoot(workspaceCollectionRootDir, env = process.env, resolvedHomeDir = homedir()) {
390
- const explicitRoot = workspaceCollectionRootDir?.trim();
389
+ export function resolveManagedWorkingFolderCollectionRoot(workingFolderCollectionRootDir, env = process.env, resolvedHomeDir = homedir()) {
390
+ const explicitRoot = workingFolderCollectionRootDir?.trim();
391
391
  if (explicitRoot) {
392
392
  return resolve(explicitRoot);
393
393
  }
394
394
  const home = env.HOME?.trim() || resolvedHomeDir.trim();
395
395
  if (!home) {
396
- throw new Error('Unable to resolve a user home directory for agents-host workspaces');
396
+ throw new Error('Unable to resolve a user home directory for agents-host working folders');
397
397
  }
398
- return join(resolve(home), DEFAULT_HOME_BORGEE_DIRNAME, CHANNEL_WORKSPACE_COLLECTION_DIRNAME);
398
+ return join(resolve(home), DEFAULT_HOME_BORGEE_DIRNAME, CHANNEL_WORKING_FOLDER_COLLECTION_DIRNAME);
399
399
  }
400
- export function resolveChannelWorkspaceRootDirectory(workspaceCollectionRootDir, channelId) {
401
- return join(resolve(workspaceCollectionRootDir), encodeChannelPathSegment(channelId));
400
+ export function resolveChannelWorkingFolderRootDirectory(workingFolderCollectionRootDir, channelId) {
401
+ return join(resolve(workingFolderCollectionRootDir), encodeChannelPathSegment(channelId));
402
402
  }
403
- export function resolveChannelWorkspaceDirectory(workspaceCollectionRootDir, channelId) {
404
- return join(resolveChannelWorkspaceRootDirectory(workspaceCollectionRootDir, channelId), CHANNEL_WORKSPACE_DIRNAME);
403
+ export function resolveChannelWorkingFolderDirectory(workingFolderCollectionRootDir, channelId) {
404
+ return join(resolveChannelWorkingFolderRootDirectory(workingFolderCollectionRootDir, channelId), CHANNEL_WORKING_FOLDER_DIRNAME);
405
405
  }
406
- export function resolveTaskIsolatedWorkspaceDirectory(workspaceCollectionRootDir, channelId, taskId) {
407
- return join(resolveTaskWorkspaceRootDirectory(resolveChannelWorkspaceRootDirectory(workspaceCollectionRootDir, channelId)), encodeChannelPathSegment(taskId), CHANNEL_WORKSPACE_DIRNAME);
406
+ export function resolveTaskIsolatedWorkingFolderDirectory(workingFolderCollectionRootDir, channelId, taskId) {
407
+ return join(resolveTaskWorkingFolderRootDirectory(resolveChannelWorkingFolderRootDirectory(workingFolderCollectionRootDir, channelId)), encodeChannelPathSegment(taskId), CHANNEL_WORKING_FOLDER_DIRNAME);
408
408
  }
409
- export const resolveTaskWorkspaceDirectory = resolveTaskIsolatedWorkspaceDirectory;
410
- export function resolveTaskThreadScratchWorkspaceDirectory(workspaceCollectionRootDir, channelId) {
411
- return join(resolveChannelWorkspaceRootDirectory(workspaceCollectionRootDir, channelId), TASK_THREAD_SCRATCH_WORKSPACE_DIRNAME, CHANNEL_WORKSPACE_DIRNAME);
409
+ export const resolveTaskWorkingFolderDirectory = resolveTaskIsolatedWorkingFolderDirectory;
410
+ export function resolveTaskThreadScratchWorkingFolderDirectory(workingFolderCollectionRootDir, channelId) {
411
+ return join(resolveChannelWorkingFolderRootDirectory(workingFolderCollectionRootDir, channelId), TASK_THREAD_SCRATCH_WORKING_FOLDER_DIRNAME, CHANNEL_WORKING_FOLDER_DIRNAME);
412
412
  }
413
413
  export function resolveChannelGatewayCredentialPath(stateRootDir, channelId) {
414
414
  return join(resolveChannelContextDirectory(stateRootDir, channelId), CHANNEL_GATEWAY_CREDENTIAL_FILENAME);
@@ -509,7 +509,7 @@ export class FileChannelContextStore {
509
509
  skillRuntimeEnabled;
510
510
  skillAssetResolver;
511
511
  localhostGateway;
512
- workspaceCollectionRootDir;
512
+ workingFolderCollectionRootDir;
513
513
  taskReader;
514
514
  taskResolutionTimeoutMs;
515
515
  allowCrossAgentIndependentWorkspaceHandoff;
@@ -520,9 +520,9 @@ export class FileChannelContextStore {
520
520
  this.skillRuntimeEnabled = options.skillRuntimeEnabled ?? false;
521
521
  this.skillAssetResolver = options.skillAssetResolver ?? new PackageSkillAssetResolver(import.meta.url, this.fileSystem);
522
522
  this.localhostGateway = options.localhostGateway;
523
- this.workspaceCollectionRootDir = resolveManagedWorkspaceCollectionRoot(options.workspaceCollectionRootDir, options.env, options.resolvedHomeDir);
523
+ this.workingFolderCollectionRootDir = resolveManagedWorkingFolderCollectionRoot(options.workingFolderCollectionRootDir, options.env, options.resolvedHomeDir);
524
524
  this.taskReader = options.taskReader;
525
- this.taskResolutionTimeoutMs = options.taskResolutionTimeoutMs ?? DEFAULT_TASK_WORKSPACE_RESOLUTION_TIMEOUT_MS;
525
+ this.taskResolutionTimeoutMs = options.taskResolutionTimeoutMs ?? DEFAULT_TASK_WORKING_FOLDER_RESOLUTION_TIMEOUT_MS;
526
526
  this.allowCrossAgentIndependentWorkspaceHandoff
527
527
  = options.allowCrossAgentIndependentWorkspaceHandoff ?? true;
528
528
  this.resolveStableAgentId = options.resolveStableAgentId;
@@ -571,9 +571,9 @@ export class FileChannelContextStore {
571
571
  incomingMessageType: input.incomingMessageType,
572
572
  incomingContent: input.incomingContent,
573
573
  }, existingTaskAssignmentContext);
574
- const workspaceResolution = await resolveTaskThreadWorkspaceContext({
574
+ const workingFolderResolution = await resolveTaskThreadWorkingFolderContext({
575
575
  channelId: input.channelId,
576
- workspaceCollectionRootDir: this.workspaceCollectionRootDir,
576
+ workingFolderCollectionRootDir: this.workingFolderCollectionRootDir,
577
577
  taskAssignmentContext,
578
578
  taskReader: this.taskReader,
579
579
  taskResolutionTimeoutMs: this.taskResolutionTimeoutMs,
@@ -581,8 +581,8 @@ export class FileChannelContextStore {
581
581
  resolveStableAgentId: this.resolveStableAgentId,
582
582
  fileSystem: this.fileSystem,
583
583
  });
584
- const resolvedWorkspace = workspaceResolution.resolvedWorkspace;
585
- const runtimeTaskAssignmentContext = workspaceResolution.taskAssignmentContext ?? taskAssignmentContext;
584
+ const resolvedWorkingFolder = workingFolderResolution.resolvedWorkingFolder;
585
+ const runtimeTaskAssignmentContext = workingFolderResolution.taskAssignmentContext ?? taskAssignmentContext;
586
586
  const persistedTaskAssignmentContext = input.taskAssignmentContextOverridePersistence === 'ephemeral'
587
587
  ? existingTaskAssignmentContext
588
588
  : runtimeTaskAssignmentContext;
@@ -591,12 +591,12 @@ export class FileChannelContextStore {
591
591
  collaboration: input.collaboration,
592
592
  taskAssignmentContext: runtimeTaskAssignmentContext,
593
593
  });
594
- const payload = buildChannelContextPayload(input.channelId, runtimeSurface, input.collaborationOutcome, input.attentionSnapshot, input.compactionSnapshot, input.taskThreadCollaborationContract, input.collaborationCapabilities, input.missedCollaborationDiagnostic, skillRuntime, localhostGateway, persistedTaskAssignmentContext || resolvedWorkspace
594
+ const payload = buildChannelContextPayload(input.channelId, runtimeSurface, input.collaborationOutcome, input.attentionSnapshot, input.compactionSnapshot, input.taskThreadCollaborationContract, input.collaborationCapabilities, input.missedCollaborationDiagnostic, skillRuntime, localhostGateway, persistedTaskAssignmentContext || resolvedWorkingFolder
595
595
  ? {
596
596
  ...(persistedTaskAssignmentContext
597
597
  ? { taskAssignmentContext: persistedTaskAssignmentContext }
598
598
  : {}),
599
- resolvedWorkspace,
599
+ resolvedWorkspace: resolvedWorkingFolder,
600
600
  }
601
601
  : undefined);
602
602
  const gatewayCredential = buildBorgeeAgentGatewayCredential(input.channelId, issuedLocalhostGateway);
@@ -610,7 +610,7 @@ export class FileChannelContextStore {
610
610
  gatewayCredentialPath: gatewayCredential ? gatewayCredentialPath : undefined,
611
611
  skillRuntime,
612
612
  localhostGateway,
613
- resolvedWorkspace,
613
+ resolvedWorkingFolder,
614
614
  };
615
615
  const shouldProjectClaudeBrief = input.provider === 'claude' && input.projectionStrategy === 'file-brief';
616
616
  const claudeProjectedBriefContent = shouldProjectClaudeBrief
@@ -626,8 +626,8 @@ export class FileChannelContextStore {
626
626
  try {
627
627
  await this.fileSystem.mkdir(directoryPath, { recursive: true, mode: 0o700 });
628
628
  await this.fileSystem.mkdir(resolveTaskAssignmentStateDirectory(this.stateRootDir, input.channelId), { recursive: true, mode: 0o700 });
629
- if (resolvedWorkspace.authority !== 'task-execution-target') {
630
- await this.fileSystem.mkdir(resolvedWorkspace.rootPath, { recursive: true, mode: 0o700 });
629
+ if (resolvedWorkingFolder.authority !== 'task-execution-target') {
630
+ await this.fileSystem.mkdir(resolvedWorkingFolder.rootPath, { recursive: true, mode: 0o700 });
631
631
  }
632
632
  await pruneGatewayCredentialSidecars(this.fileSystem, directoryPath, gatewayCredential ? CHANNEL_GATEWAY_CREDENTIAL_FILENAME : undefined);
633
633
  if (!claudeProjectedBriefContent) {
@@ -675,9 +675,9 @@ export class FileChannelContextStore {
675
675
  directoryPath,
676
676
  payload: {
677
677
  ...payload,
678
- ...(resolvedWorkspace.authority === 'task-execution-target' && runtimeTaskAssignmentContext?.active === true
678
+ ...(resolvedWorkingFolder.authority === 'task-execution-target' && runtimeTaskAssignmentContext?.active === true
679
679
  ? {
680
- resolvedWorkspace: buildTaskThreadScratchResolvedWorkspace(this.workspaceCollectionRootDir, input.channelId),
680
+ resolvedWorkspace: buildTaskThreadScratchResolvedWorkingFolder(this.workingFolderCollectionRootDir, input.channelId),
681
681
  }
682
682
  : {}),
683
683
  },
@@ -687,9 +687,9 @@ export class FileChannelContextStore {
687
687
  gatewayCredentialPath: gatewayCredentialWritten ? gatewayCredentialPath : undefined,
688
688
  skillRuntime: payloadWritten ? skillRuntime : undefined,
689
689
  localhostGateway: payloadWritten ? localhostGateway : undefined,
690
- resolvedWorkspace: resolvedWorkspace.authority === 'task-execution-target' && runtimeTaskAssignmentContext?.active === true
691
- ? buildTaskThreadScratchResolvedWorkspace(this.workspaceCollectionRootDir, input.channelId)
692
- : resolvedWorkspace,
690
+ resolvedWorkingFolder: resolvedWorkingFolder.authority === 'task-execution-target' && runtimeTaskAssignmentContext?.active === true
691
+ ? buildTaskThreadScratchResolvedWorkingFolder(this.workingFolderCollectionRootDir, input.channelId)
692
+ : resolvedWorkingFolder,
693
693
  };
694
694
  throw new ChannelContextPreparationError('failed to persist channel context payload', partialContext, { cause: error });
695
695
  }