@borgee/agents-host 0.2.74 → 0.2.94

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 (82) hide show
  1. package/README.md +11 -24
  2. package/dist/agents-host.d.ts +6 -4
  3. package/dist/agents-host.js +127 -16
  4. package/dist/background-runs.d.ts +24 -0
  5. package/dist/background-runs.js +184 -0
  6. package/dist/chat/chat-control-plane.d.ts +7 -1
  7. package/dist/chat/sdk-chat-control-plane.d.ts +7 -2
  8. package/dist/chat/sdk-chat-control-plane.js +22 -0
  9. package/dist/context/claude-file-brief.js +3 -3
  10. package/dist/context/injection.d.ts +18 -12
  11. package/dist/context/injection.js +53 -53
  12. package/dist/context/prompt.js +10 -10
  13. package/dist/context/resolved-working-folder.d.ts +3 -0
  14. package/dist/context/resolved-working-folder.js +106 -0
  15. package/dist/context/turn-preparation.js +1 -1
  16. package/dist/managed-daemon.js +30 -30
  17. package/dist/plugin-sdk.js +315 -29
  18. package/dist/plugin-sdk.js.map +3 -3
  19. package/dist/policy/authorization-audit.d.ts +1 -1
  20. package/dist/policy/copilot-permission.d.ts +9 -0
  21. package/dist/policy/copilot-permission.js +120 -1
  22. package/dist/progress-to-activity.d.ts +1 -1
  23. package/dist/progress-to-activity.js +1 -0
  24. package/dist/providers/claude/adapter.d.ts +2 -1
  25. package/dist/providers/claude/adapter.js +36 -1
  26. package/dist/providers/claude/background-run-observer.d.ts +32 -0
  27. package/dist/providers/claude/background-run-observer.js +381 -0
  28. package/dist/providers/claude/cli-client.d.ts +12 -0
  29. package/dist/providers/claude/cli-client.js +453 -72
  30. package/dist/providers/claude/foreground-handoff.d.ts +4 -0
  31. package/dist/providers/claude/foreground-handoff.js +45 -0
  32. package/dist/providers/claude/task-cancellation-protocol.d.ts +14 -0
  33. package/dist/providers/claude/task-cancellation-protocol.js +21 -0
  34. package/dist/providers/codex/adapter.d.ts +1 -1
  35. package/dist/providers/codex/adapter.js +7 -0
  36. package/dist/providers/codex/cli-client.js +8 -7
  37. package/dist/providers/codex/project-doc.js +6 -6
  38. package/dist/providers/copilot/activity-metadata.d.ts +3 -0
  39. package/dist/providers/copilot/activity-metadata.js +19 -0
  40. package/dist/providers/copilot/adapter.d.ts +6 -2
  41. package/dist/providers/copilot/adapter.js +27 -1
  42. package/dist/providers/copilot/cli-client.d.ts +37 -10
  43. package/dist/providers/copilot/cli-client.js +756 -122
  44. package/dist/providers/copilot/sdk-session.d.ts +149 -0
  45. package/dist/providers/copilot/sdk-session.js +981 -0
  46. package/dist/providers/create-provider.js +33 -0
  47. package/dist/providers/provider-adapter.d.ts +24 -1
  48. package/dist/providers/provider-adapter.js +17 -0
  49. package/dist/types.d.ts +40 -11
  50. package/dist/vendor/claude-agent-acp/LICENSE +191 -0
  51. package/dist/vendor/claude-agent-acp/NOTICE +8 -0
  52. package/dist/vendor/claude-agent-acp/dist/acp-agent.d.ts +1017 -0
  53. package/dist/vendor/claude-agent-acp/dist/acp-agent.d.ts.map +1 -0
  54. package/dist/vendor/claude-agent-acp/dist/acp-agent.js +6305 -0
  55. package/dist/vendor/claude-agent-acp/dist/borgee-foreground-handoff-bridge.js +322 -0
  56. package/dist/vendor/claude-agent-acp/dist/borgee-task-cancellation-bridge.js +101 -0
  57. package/dist/vendor/claude-agent-acp/dist/borgee-task-lifecycle-bridge.js +58 -0
  58. package/dist/vendor/claude-agent-acp/dist/elicitation.d.ts +130 -0
  59. package/dist/vendor/claude-agent-acp/dist/elicitation.d.ts.map +1 -0
  60. package/dist/vendor/claude-agent-acp/dist/elicitation.js +304 -0
  61. package/dist/vendor/claude-agent-acp/dist/index.d.ts +3 -0
  62. package/dist/vendor/claude-agent-acp/dist/index.d.ts.map +1 -0
  63. package/dist/vendor/claude-agent-acp/dist/index.js +75 -0
  64. package/dist/vendor/claude-agent-acp/dist/lib.d.ts +6 -0
  65. package/dist/vendor/claude-agent-acp/dist/lib.d.ts.map +1 -0
  66. package/dist/vendor/claude-agent-acp/dist/lib.js +5 -0
  67. package/dist/vendor/claude-agent-acp/dist/settings.d.ts +68 -0
  68. package/dist/vendor/claude-agent-acp/dist/settings.d.ts.map +1 -0
  69. package/dist/vendor/claude-agent-acp/dist/settings.js +185 -0
  70. package/dist/vendor/claude-agent-acp/dist/tools.d.ts +102 -0
  71. package/dist/vendor/claude-agent-acp/dist/tools.d.ts.map +1 -0
  72. package/dist/vendor/claude-agent-acp/dist/tools.js +1000 -0
  73. package/dist/vendor/claude-agent-acp/dist/utils.d.ts +16 -0
  74. package/dist/vendor/claude-agent-acp/dist/utils.d.ts.map +1 -0
  75. package/dist/vendor/claude-agent-acp/dist/utils.js +81 -0
  76. package/dist/vendor/claude-agent-acp/package.json +85 -0
  77. package/package.json +15 -10
  78. package/skills/borgee-agent/references/task-properties.md +3 -3
  79. package/skills/borgee-agent/scripts/borgee-agent.mjs +1 -1
  80. package/skills/borgee-agent/scripts/borgee-agent.py +1 -1
  81. package/dist/context/resolved-workspace.d.ts +0 -3
  82. package/dist/context/resolved-workspace.js +0 -106
@@ -0,0 +1,16 @@
1
+ import { Readable, Writable } from "node:stream";
2
+ import { WritableStream, ReadableStream } from "node:stream/web";
3
+ import { Logger } from "./acp-agent.js";
4
+ export declare class Pushable<T> implements AsyncIterable<T> {
5
+ private queue;
6
+ private resolvers;
7
+ private done;
8
+ push(item: T): void;
9
+ end(): void;
10
+ [Symbol.asyncIterator](): AsyncIterator<T>;
11
+ }
12
+ export declare function nodeToWebWritable(nodeStream: Writable): WritableStream<Uint8Array>;
13
+ export declare function nodeToWebReadable(nodeStream: Readable): ReadableStream<Uint8Array>;
14
+ export declare function unreachable(value: never, logger?: Logger): void;
15
+ export declare function sleep(time: number): Promise<void>;
16
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAGxC,qBAAa,QAAQ,CAAC,CAAC,CAAE,YAAW,aAAa,CAAC,CAAC,CAAC;IAClD,OAAO,CAAC,KAAK,CAAW;IACxB,OAAO,CAAC,SAAS,CAA8C;IAC/D,OAAO,CAAC,IAAI,CAAS;IAErB,IAAI,CAAC,IAAI,EAAE,CAAC;IASZ,GAAG;IAQH,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC;CAgB3C;AAGD,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,QAAQ,GAAG,cAAc,CAAC,UAAU,CAAC,CAclF;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,QAAQ,GAAG,cAAc,CAAC,UAAU,CAAC,CAUlF;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,GAAE,MAAgB,QAQjE;AAED,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEjD"}
@@ -0,0 +1,81 @@
1
+ // A pushable async iterable: allows you to push items and consume them with for-await.
2
+ import { WritableStream, ReadableStream } from "node:stream/web";
3
+ // Useful for bridging push-based and async-iterator-based code.
4
+ export class Pushable {
5
+ queue = [];
6
+ resolvers = [];
7
+ done = false;
8
+ push(item) {
9
+ if (this.resolvers.length > 0) {
10
+ const resolve = this.resolvers.shift();
11
+ resolve({ value: item, done: false });
12
+ }
13
+ else {
14
+ this.queue.push(item);
15
+ }
16
+ }
17
+ end() {
18
+ this.done = true;
19
+ while (this.resolvers.length > 0) {
20
+ const resolve = this.resolvers.shift();
21
+ resolve({ value: undefined, done: true });
22
+ }
23
+ }
24
+ [Symbol.asyncIterator]() {
25
+ return {
26
+ next: () => {
27
+ if (this.queue.length > 0) {
28
+ const value = this.queue.shift();
29
+ return Promise.resolve({ value, done: false });
30
+ }
31
+ if (this.done) {
32
+ return Promise.resolve({ value: undefined, done: true });
33
+ }
34
+ return new Promise((resolve) => {
35
+ this.resolvers.push(resolve);
36
+ });
37
+ },
38
+ };
39
+ }
40
+ }
41
+ // Helper to convert Node.js streams to Web Streams
42
+ export function nodeToWebWritable(nodeStream) {
43
+ return new WritableStream({
44
+ write(chunk) {
45
+ return new Promise((resolve, reject) => {
46
+ nodeStream.write(Buffer.from(chunk), (err) => {
47
+ if (err) {
48
+ reject(err);
49
+ }
50
+ else {
51
+ resolve();
52
+ }
53
+ });
54
+ });
55
+ },
56
+ });
57
+ }
58
+ export function nodeToWebReadable(nodeStream) {
59
+ return new ReadableStream({
60
+ start(controller) {
61
+ nodeStream.on("data", (chunk) => {
62
+ controller.enqueue(new Uint8Array(chunk));
63
+ });
64
+ nodeStream.on("end", () => controller.close());
65
+ nodeStream.on("error", (err) => controller.error(err));
66
+ },
67
+ });
68
+ }
69
+ export function unreachable(value, logger = console) {
70
+ let valueAsString;
71
+ try {
72
+ valueAsString = JSON.stringify(value);
73
+ }
74
+ catch {
75
+ valueAsString = value;
76
+ }
77
+ logger.error(`Unexpected case: ${valueAsString}`);
78
+ }
79
+ export function sleep(time) {
80
+ return new Promise((resolve) => setTimeout(resolve, time));
81
+ }
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "@agentclientprotocol/claude-agent-acp",
3
+ "publishConfig": {
4
+ "access": "public"
5
+ },
6
+ "version": "0.64.0",
7
+ "description": "An ACP-compatible coding agent powered by the Claude Agent SDK (TypeScript)",
8
+ "main": "dist/lib.js",
9
+ "types": "dist/lib.d.ts",
10
+ "bin": {
11
+ "claude-agent-acp": "dist/index.js"
12
+ },
13
+ "type": "module",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/lib.d.ts",
17
+ "import": "./dist/lib.js"
18
+ },
19
+ "./*": "./*"
20
+ },
21
+ "files": [
22
+ "dist/",
23
+ "!dist/tests/",
24
+ "README.md",
25
+ "LICENSE",
26
+ "package.json"
27
+ ],
28
+ "scripts": {
29
+ "build": "tsc",
30
+ "start": "node dist/index.js",
31
+ "dev": "npm run build && npm run start",
32
+ "lint": "eslint src --ext .ts",
33
+ "lint:fix": "eslint src --ext .ts --fix",
34
+ "format": "prettier --write .",
35
+ "format:check": "prettier --check .",
36
+ "check": "npm run lint && npm run format:check",
37
+ "test": "vitest",
38
+ "test:integration": "RUN_INTEGRATION_TESTS=true vitest run",
39
+ "test:run": "vitest run",
40
+ "test:coverage": "vitest run --coverage",
41
+ "prepublishOnly": "npm run build"
42
+ },
43
+ "keywords": [
44
+ "claude",
45
+ "acp",
46
+ "agent",
47
+ "anthropic",
48
+ "typescript",
49
+ "sdk",
50
+ "code"
51
+ ],
52
+ "homepage": "https://github.com/agentclientprotocol/claude-agent-acp#readme",
53
+ "bugs": {
54
+ "url": "https://github.com/agentclientprotocol/claude-agent-acp/issues"
55
+ },
56
+ "repository": {
57
+ "type": "git",
58
+ "url": "git+https://github.com/agentclientprotocol/claude-agent-acp.git"
59
+ },
60
+ "author": "Zed Industries",
61
+ "license": "Apache-2.0",
62
+ "engines": {
63
+ "node": ">=22"
64
+ },
65
+ "dependencies": {
66
+ "@agentclientprotocol/sdk": "1.3.0",
67
+ "@anthropic-ai/claude-agent-sdk": "0.3.220",
68
+ "zod": "^3.25.0 || ^4.0.0"
69
+ },
70
+ "devDependencies": {
71
+ "@anthropic-ai/sdk": "0.115.0",
72
+ "@eslint/js": "10.0.1",
73
+ "@tsconfig/node22": "22.0.5",
74
+ "@types/node": "26.1.2",
75
+ "@typescript-eslint/eslint-plugin": "8.65.0",
76
+ "@typescript-eslint/parser": "8.65.0",
77
+ "eslint": "10.8.0",
78
+ "eslint-config-prettier": "10.1.8",
79
+ "globals": "17.8.0",
80
+ "prettier": "3.9.6",
81
+ "ts-node": "10.9.2",
82
+ "typescript": "6.0.3",
83
+ "vitest": "4.1.10"
84
+ }
85
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@borgee/agents-host",
3
- "version": "0.2.74",
3
+ "version": "0.2.94",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -22,35 +22,40 @@
22
22
  },
23
23
  "license": "MIT",
24
24
  "dependencies": {
25
- "@agentclientprotocol/claude-agent-acp": "0.64.0",
26
25
  "@agentclientprotocol/codex-acp": "1.1.7",
27
26
  "@agentclientprotocol/sdk": "1.3.0",
27
+ "@anthropic-ai/claude-agent-sdk": "0.3.220",
28
+ "@github/copilot-sdk": "1.0.11",
28
29
  "cross-spawn": "^7.0.6",
29
- "yaml": "^2.8.1"
30
+ "yaml": "^2.8.1",
31
+ "zod": "4.4.3"
30
32
  },
31
33
  "devDependencies": {
34
+ "@agentclientprotocol/claude-agent-acp": "0.64.0",
32
35
  "@types/cross-spawn": "^6.0.6",
33
36
  "@types/node": "^24.3.0",
34
37
  "esbuild": "^0.28.1",
35
38
  "tsx": "^4.20.5",
36
39
  "typescript": "^5.9.3",
37
40
  "vitest": "^4.1.5",
38
- "@borgee/plugin-sdk": "0.12.5"
41
+ "@borgee/plugin-sdk": "0.14.1"
39
42
  },
40
43
  "scripts": {
41
- "predev": "pnpm --filter @borgee/plugin-sdk build",
44
+ "predev": "pnpm --filter @borgee/plugin-sdk build && pnpm run vendor:claude",
42
45
  "dev": "tsx src/index.ts",
43
- "precli": "pnpm --filter @borgee/plugin-sdk build",
46
+ "precli": "pnpm --filter @borgee/plugin-sdk build && pnpm run vendor:claude",
44
47
  "cli": "tsx src/cli.ts",
45
48
  "prestart": "pnpm --filter @borgee/plugin-sdk build",
46
49
  "start": "node dist/index.js",
47
50
  "prebuild": "pnpm --filter @borgee/plugin-sdk build",
48
- "build": "tsc && node scripts/bundle-plugin-sdk.mjs",
51
+ "build": "tsc && node scripts/bundle-plugin-sdk.mjs && node scripts/vendor-claude-acp.mjs",
52
+ "test:package": "node --test --test-timeout=10000 scripts/packed-package-descriptor.test.mjs && pnpm run build && node scripts/test-packed-claude-lifecycle.mjs",
49
53
  "typecheck": "tsc --noEmit",
50
- "pretest": "pnpm --filter @borgee/plugin-sdk build",
54
+ "pretest": "pnpm --filter @borgee/plugin-sdk build && pnpm run vendor:claude",
51
55
  "test": "vitest run --testTimeout=10000",
52
- "pretest:e2e": "pnpm --filter @borgee/plugin-sdk build",
56
+ "pretest:e2e": "pnpm --filter @borgee/plugin-sdk build && pnpm run vendor:claude",
53
57
  "test:e2e": "vitest run --config vitest.e2e.config.ts --testTimeout=120000",
54
- "pretypecheck": "pnpm --filter @borgee/plugin-sdk build"
58
+ "pretypecheck": "pnpm --filter @borgee/plugin-sdk build",
59
+ "vendor:claude": "node scripts/vendor-claude-acp.mjs"
55
60
  }
56
61
  }
@@ -21,8 +21,8 @@ The key set is closed; writing an unregistered key is rejected with `unknown_pro
21
21
  | --- | --- |
22
22
  | `link.pr` | The pull request that implements this task. Set it as soon as the PR exists, not at the end. |
23
23
  | `link.issue` | The issue or ticket the task originates from. |
24
- | `execution.local_directory` | The explicit local directory a human selected as this task thread's execution target. New tasks default it from the immediately previous task thread in the same channel: if that previous task carried a value, the new task inherits it; if the previous task left it unset, the new task also starts unset. Hosted agents can read it, but the generic agent task-property rail must not write it. Humans set it through the existing user rail. The extra single-human-user guard applies when a task thread is about to switch into somebody's own local workspace/project directory, not as a blanket rule on every property write. A separate agents-host local policy switch governs whether a hosted agent may enter an independent workspace for a task thread that originated from another agent's task. |
25
- | `workspace.mode` | The host-managed scratch-workspace isolation hint for task threads. `inherit` keeps the scratch workspace on the shared host-managed path, while `isolated` reserves the task's own host-managed scratch workspace shape. Deleting the key returns to the default `inherit` behavior. |
24
+ | `execution.local_directory` | The explicit local directory a human selected as this task thread's execution target. New tasks default it from the immediately previous task thread in the same channel: if that previous task carried a value, the new task inherits it; if the previous task left it unset, the new task also starts unset. Hosted agents can read it, but the generic agent task-property rail must not write it. Humans set it through the existing user rail. The extra single-human-user guard applies when a task thread is about to switch into somebody's own local project directory, not as a blanket rule on every property write. A separate agents-host local policy switch governs whether a hosted agent may enter an independent working folder for a task thread that originated from another agent's task. |
25
+ | `workspace.mode` | The host-managed scratch working-folder isolation hint for task threads. `inherit` keeps the scratch working folder on the shared host-managed path, while `isolated` reserves the task's own host-managed scratch working folder shape. Deleting the key returns to the default `inherit` behavior. |
26
26
  | `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. |
27
27
 
28
28
  ## One key per call
@@ -31,4 +31,4 @@ Each call writes exactly one key, and that is what makes it safe to write a prop
31
31
 
32
32
  ## Value
33
33
 
34
- 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. `execution.local_directory` must be a non-empty absolute path string. Before a human lets a task thread switch into their own local workspace/project directory, first verify that the task's parent channel has exactly one human user; otherwise keep the thread discussion-only. If a hosted agent is trying to enter an independent workspace for a task thread that originated from another agent's task, this agents-host's local handoff policy must also allow it. Some registered keys are closed enums: `workspace.mode` accepts only `inherit` or `isolated`.
34
+ 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. `execution.local_directory` must be a non-empty absolute path string. Before a human lets a task thread switch into their own local project directory, first verify that the task's parent channel has exactly one human user; otherwise keep the thread discussion-only. If a hosted agent is trying to enter an independent working folder for a task thread that originated from another agent's task, this agents-host's local handoff policy must also allow it. Some registered keys are closed enums: `workspace.mode` accepts only `inherit` or `isolated`.
@@ -338,7 +338,7 @@ function validateCommand(parsed) {
338
338
  }
339
339
 
340
340
  // The credential path is used exactly as handed over: no walk-up, no glob, no environment lookup.
341
- // Task workspaces live inside the git repository and are not gitignored, so any search would
341
+ // Task working folders live inside the git repository and are not gitignored, so any search would
342
342
  // traverse attacker-influenceable directories where a planted file could redirect the gateway base
343
343
  // URL or point at another channel's credentials.
344
344
  async function readGatewayCredential(gatewayCredentialPath) {
@@ -300,7 +300,7 @@ def validate_command(parsed: dict[str, object]) -> None:
300
300
 
301
301
 
302
302
  # The credential path is used exactly as handed over: no walk-up, no glob, no environment lookup.
303
- # Task workspaces live inside the git repository and are not gitignored, so any search would
303
+ # Task working folders live inside the git repository and are not gitignored, so any search would
304
304
  # traverse attacker-influenceable directories where a planted file could redirect the gateway base
305
305
  # URL or point at another channel's credentials.
306
306
  def read_gateway_credential(gateway_credential_path: str) -> dict[str, str]:
@@ -1,3 +0,0 @@
1
- import type { PreparedPromptContext, ProviderKind } from '../types.js';
2
- export declare function buildResolvedWorkspaceGuidanceLines(context: PreparedPromptContext | undefined, provider?: ProviderKind): string[];
3
- export declare function isDiscussionOnlyResolvedWorkspace(context: PreparedPromptContext | undefined): boolean;
@@ -1,106 +0,0 @@
1
- function buildWorkspaceRootLine(context) {
2
- const resolvedWorkspace = context.resolvedWorkspace;
3
- if (!resolvedWorkspace) {
4
- return undefined;
5
- }
6
- if (resolvedWorkspace.authority === 'task-execution-target') {
7
- return `Explicit execution local directory for this task thread: ${resolvedWorkspace.rootPath}.`;
8
- }
9
- if (resolvedWorkspace.authority === 'task-thread-scratch') {
10
- return `Discussion-only scratch workspace for this task thread: ${resolvedWorkspace.rootPath}.`;
11
- }
12
- if (context.taskAssignmentContext?.active === true) {
13
- return `Writable workspace root inherited from this task thread's channel: ${resolvedWorkspace.rootPath}.`;
14
- }
15
- return `Writable workspace root for this channel: ${resolvedWorkspace.rootPath}.`;
16
- }
17
- function buildWorkspaceModeLine(context) {
18
- if (context.taskAssignmentContext?.active !== true || !context.resolvedWorkspace) {
19
- return undefined;
20
- }
21
- if (context.resolvedWorkspace.authority === 'task-execution-target') {
22
- return 'This task thread has a valid explicit execution.local_directory target, so execution is bound to that exact existing local directory.';
23
- }
24
- if (context.resolvedWorkspace.authority === 'task-thread-scratch') {
25
- return context.resolvedWorkspace.reason === 'cross-agent-independent-workspace-disabled'
26
- ? 'This task thread has an explicit execution.local_directory target, but this agents-host currently disables cross-agent independent-workspace handoff, so it is discussion-only.'
27
- : 'This task thread has no valid explicit execution.local_directory target, so it is discussion-only.';
28
- }
29
- return 'This task thread inherits the channel workspace rather than using a task-isolated workspace.';
30
- }
31
- function buildWorkspaceLocalityLine(context) {
32
- if (!context.resolvedWorkspace) {
33
- return undefined;
34
- }
35
- if (context.resolvedWorkspace.authority === 'task-execution-target') {
36
- return 'This is a human-selected existing local directory. The host validates it before use and never creates it.';
37
- }
38
- if (context.resolvedWorkspace.authority === 'task-thread-scratch') {
39
- return 'This scratch workspace is host-managed only for discussion turns. It is not a bound project workspace and does not imply that any target repository is checked out there.';
40
- }
41
- if (context.taskAssignmentContext?.active === true) {
42
- 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.';
43
- }
44
- 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.';
45
- }
46
- function buildCopilotCwdLine(context) {
47
- if (!context.resolvedWorkspace) {
48
- return undefined;
49
- }
50
- if (context.resolvedWorkspace.authority === 'task-execution-target') {
51
- return 'GitHub Copilot runs this turn with that exact local directory as its real cwd.';
52
- }
53
- if (context.resolvedWorkspace.authority === 'task-thread-scratch') {
54
- return 'GitHub Copilot runs this turn with that host-managed scratch workspace as its real cwd.';
55
- }
56
- if (context.taskAssignmentContext?.active === true) {
57
- return 'GitHub Copilot runs this turn with that inherited channel workspace as its real cwd.';
58
- }
59
- return 'GitHub Copilot runs this turn with that workspace as its real cwd.';
60
- }
61
- function buildDiscussionOnlyPermissionLine(context) {
62
- if (context.resolvedWorkspace?.authority !== 'task-thread-scratch') {
63
- return undefined;
64
- }
65
- return context.resolvedWorkspace.reason === 'cross-agent-independent-workspace-disabled'
66
- ? 'Filesystem and shell tool permissions stay denied in this discussion-only task thread while this agents-host keeps cross-agent independent-workspace handoff disabled.'
67
- : 'Filesystem and shell tool permissions stay denied in this discussion-only task thread until a human sets a valid execution.local_directory target.';
68
- }
69
- function buildProjectEntryPrecheckLine(context) {
70
- if (context.taskAssignmentContext?.active !== true) {
71
- return undefined;
72
- }
73
- if (context.resolvedWorkspace?.authority === 'task-execution-target') {
74
- return 'Precheck before switching this task thread into your own local workspace or project directory: the task\'s parent channel must have exactly one human user. Cross-agent independent-workspace handoff is also governed by this agents-host\'s local policy switch.';
75
- }
76
- return 'Before a human switches this task thread into their own local workspace or project directory, verify that the task\'s parent channel has exactly one human user; otherwise keep the thread discussion-only. Cross-agent independent-workspace handoff is also governed by this agents-host\'s local policy switch.';
77
- }
78
- export function buildResolvedWorkspaceGuidanceLines(context, provider) {
79
- if (!context?.resolvedWorkspace) {
80
- return [];
81
- }
82
- const lines = [buildWorkspaceRootLine(context), buildWorkspaceModeLine(context)]
83
- .filter((line) => line != null);
84
- if (provider === 'copilot') {
85
- const copilotCwdLine = buildCopilotCwdLine(context);
86
- if (copilotCwdLine) {
87
- lines.push(copilotCwdLine);
88
- }
89
- }
90
- const localityLine = buildWorkspaceLocalityLine(context);
91
- if (localityLine) {
92
- lines.push(localityLine);
93
- }
94
- const permissionLine = buildDiscussionOnlyPermissionLine(context);
95
- if (permissionLine) {
96
- lines.push(permissionLine);
97
- }
98
- const precheckLine = buildProjectEntryPrecheckLine(context);
99
- if (precheckLine) {
100
- lines.push(precheckLine);
101
- }
102
- return lines;
103
- }
104
- export function isDiscussionOnlyResolvedWorkspace(context) {
105
- return context?.resolvedWorkspace?.authority === 'task-thread-scratch';
106
- }