@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.
- package/README.md +17 -30
- package/dist/agents-host-supervisor.d.ts +1 -0
- package/dist/agents-host-supervisor.js +3 -1
- package/dist/agents-host.d.ts +6 -4
- package/dist/agents-host.js +117 -16
- package/dist/background-runs.d.ts +23 -0
- package/dist/background-runs.js +153 -0
- package/dist/chat/chat-control-plane.d.ts +6 -1
- package/dist/chat/sdk-chat-control-plane.d.ts +4 -2
- package/dist/chat/sdk-chat-control-plane.js +7 -0
- package/dist/context/claude-file-brief.js +3 -3
- package/dist/context/injection.d.ts +18 -12
- package/dist/context/injection.js +53 -53
- package/dist/context/prompt.js +10 -10
- package/dist/context/resolved-working-folder.d.ts +3 -0
- package/dist/context/resolved-working-folder.js +106 -0
- package/dist/context/turn-preparation.js +1 -1
- package/dist/local-config.d.ts +7 -0
- package/dist/local-config.js +158 -12
- package/dist/managed-daemon.js +42 -35
- package/dist/plugin-sdk.js +179 -9
- package/dist/plugin-sdk.js.map +3 -3
- package/dist/policy/authorization-audit.d.ts +1 -1
- package/dist/policy/copilot-permission.d.ts +9 -0
- package/dist/policy/copilot-permission.js +120 -1
- package/dist/progress-to-activity.d.ts +1 -1
- package/dist/progress-to-activity.js +1 -0
- package/dist/providers/claude/adapter.d.ts +1 -1
- package/dist/providers/claude/adapter.js +7 -0
- package/dist/providers/claude/cli-client.js +4 -3
- package/dist/providers/codex/adapter.d.ts +1 -1
- package/dist/providers/codex/adapter.js +7 -0
- package/dist/providers/codex/cli-client.js +8 -7
- package/dist/providers/codex/project-doc.js +6 -6
- package/dist/providers/copilot/adapter.d.ts +6 -2
- package/dist/providers/copilot/adapter.js +27 -1
- package/dist/providers/copilot/cli-client.d.ts +37 -10
- package/dist/providers/copilot/cli-client.js +751 -122
- package/dist/providers/copilot/sdk-session.d.ts +149 -0
- package/dist/providers/copilot/sdk-session.js +981 -0
- package/dist/providers/create-provider.js +33 -0
- package/dist/providers/provider-adapter.d.ts +16 -1
- package/dist/providers/provider-adapter.js +13 -0
- package/dist/types.d.ts +32 -11
- package/package.json +3 -2
- package/skills/borgee-agent/references/task-properties.md +3 -3
- package/skills/borgee-agent/scripts/borgee-agent.mjs +1 -1
- package/skills/borgee-agent/scripts/borgee-agent.py +1 -1
- package/dist/context/resolved-workspace.d.ts +0 -3
- package/dist/context/resolved-workspace.js +0 -106
|
@@ -30,9 +30,23 @@ class ProviderV2CompatibilityAdapter {
|
|
|
30
30
|
turn: preparedTurn,
|
|
31
31
|
}, options);
|
|
32
32
|
}
|
|
33
|
+
onAutonomousReply(handler) {
|
|
34
|
+
this.provider.onAutonomousReply?.(handler);
|
|
35
|
+
}
|
|
33
36
|
async cancelTurn(channelId) {
|
|
34
37
|
return this.provider.cancelTurn?.(channelId) ?? false;
|
|
35
38
|
}
|
|
39
|
+
async interruptTurn(channelId) {
|
|
40
|
+
return this.provider.interruptTurn?.(channelId) ?? false;
|
|
41
|
+
}
|
|
42
|
+
async cancelSession(channelId) {
|
|
43
|
+
return this.provider.cancelSession?.(channelId)
|
|
44
|
+
?? this.provider.cancelTurn?.(channelId)
|
|
45
|
+
?? false;
|
|
46
|
+
}
|
|
47
|
+
async cancelBackgroundRun(channelId, providerRunId) {
|
|
48
|
+
return this.provider.cancelBackgroundRun?.(channelId, providerRunId) ?? false;
|
|
49
|
+
}
|
|
36
50
|
async dispose() {
|
|
37
51
|
await this.provider.shutdown?.();
|
|
38
52
|
}
|
|
@@ -50,9 +64,28 @@ class CliBackedProviderV2 {
|
|
|
50
64
|
// session as it runs, so before this point there is nothing to report.
|
|
51
65
|
return { ...parseProviderReply(text), sessionId: this.cli.sessionIdForChannel(input.turn.channelId) };
|
|
52
66
|
}
|
|
67
|
+
onAutonomousReply(handler) {
|
|
68
|
+
this.cli.onAutonomousReply?.(handler
|
|
69
|
+
? (reply) => {
|
|
70
|
+
const parsed = parseProviderReply(reply.text);
|
|
71
|
+
if (parsed.text.trim()) {
|
|
72
|
+
handler({ ...reply, text: parsed.text });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
: undefined);
|
|
76
|
+
}
|
|
53
77
|
async cancelTurn(channelId) {
|
|
54
78
|
return this.cli.cancelTurn(channelId);
|
|
55
79
|
}
|
|
80
|
+
async interruptTurn(channelId) {
|
|
81
|
+
return this.cli.interruptTurn(channelId);
|
|
82
|
+
}
|
|
83
|
+
async cancelSession(channelId) {
|
|
84
|
+
return this.cli.cancelSession(channelId);
|
|
85
|
+
}
|
|
86
|
+
async cancelBackgroundRun(channelId, providerRunId) {
|
|
87
|
+
return this.cli.cancelBackgroundRun?.(channelId, providerRunId) ?? false;
|
|
88
|
+
}
|
|
56
89
|
async shutdown() {
|
|
57
90
|
await this.cli.dispose();
|
|
58
91
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ProviderGenerateOptions, ProviderInput, ProviderReply } from '../types.js';
|
|
1
|
+
import type { ProviderAutonomousReply, ProviderGenerateOptions, ProviderInput, ProviderReply } from '../types.js';
|
|
2
2
|
export type ProviderImageInputSupport = 'supported' | 'metadata-only' | 'not-supported';
|
|
3
3
|
export type ProviderImageTransportSupport = 'supported' | 'not-supported';
|
|
4
4
|
export type ProviderImageTransportSource = 'transport-derived' | 'runtime-derived';
|
|
@@ -24,12 +24,23 @@ export interface ProviderImageCapability {
|
|
|
24
24
|
sharedHostFallback: SharedHostAttachmentMetadataFallbackCapability;
|
|
25
25
|
transport: ProviderImageTransportCapability;
|
|
26
26
|
}
|
|
27
|
+
export interface ProviderProgressUpdatesCapability {
|
|
28
|
+
support: 'supported' | 'not-supported';
|
|
29
|
+
}
|
|
30
|
+
export interface ProviderBackgroundRunsCapability {
|
|
31
|
+
lifecycle: 'supported' | 'not-supported';
|
|
32
|
+
targetedCancellation: 'supported' | 'not-supported';
|
|
33
|
+
}
|
|
27
34
|
export interface ProviderCapabilities {
|
|
28
35
|
imageInput: ProviderImageCapability;
|
|
36
|
+
progressUpdates: ProviderProgressUpdatesCapability;
|
|
37
|
+
backgroundRuns?: ProviderBackgroundRunsCapability;
|
|
29
38
|
}
|
|
30
39
|
export declare const SHARED_HOST_ATTACHMENT_METADATA_FALLBACK: SharedHostAttachmentMetadataFallbackCapability;
|
|
31
40
|
export declare function createHostedProviderCapabilities(params: {
|
|
32
41
|
imageInputTransport: ProviderImageTransportCapability;
|
|
42
|
+
progressUpdates: ProviderProgressUpdatesCapability;
|
|
43
|
+
backgroundRuns?: ProviderBackgroundRunsCapability;
|
|
33
44
|
sharedHostFallback?: SharedHostAttachmentMetadataFallbackCapability;
|
|
34
45
|
}): ProviderCapabilities;
|
|
35
46
|
export declare const TEXT_ONLY_HOSTED_PROVIDER_CAPABILITIES: ProviderCapabilities;
|
|
@@ -42,6 +53,10 @@ export declare class ProviderTurnCancelledError extends Error {
|
|
|
42
53
|
export interface ProviderAdapter {
|
|
43
54
|
readonly capabilities: ProviderCapabilities;
|
|
44
55
|
generateReply(input: ProviderInput, options?: ProviderGenerateOptions): Promise<ProviderReply>;
|
|
56
|
+
onAutonomousReply?(handler: ((reply: ProviderAutonomousReply) => void) | undefined): void;
|
|
57
|
+
interruptTurn?(channelId: string): Promise<boolean>;
|
|
58
|
+
cancelSession?(channelId: string): Promise<boolean>;
|
|
45
59
|
cancelTurn?(channelId: string): Promise<boolean>;
|
|
60
|
+
cancelBackgroundRun?(channelId: string, providerRunId: string): Promise<boolean>;
|
|
46
61
|
dispose?(): Promise<void>;
|
|
47
62
|
}
|
|
@@ -32,6 +32,12 @@ export function createHostedProviderCapabilities(params) {
|
|
|
32
32
|
sharedHostFallback,
|
|
33
33
|
transport,
|
|
34
34
|
}),
|
|
35
|
+
progressUpdates: Object.freeze({ ...params.progressUpdates }),
|
|
36
|
+
backgroundRuns: Object.freeze({
|
|
37
|
+
lifecycle: 'not-supported',
|
|
38
|
+
targetedCancellation: 'not-supported',
|
|
39
|
+
...params.backgroundRuns,
|
|
40
|
+
}),
|
|
35
41
|
});
|
|
36
42
|
}
|
|
37
43
|
export const TEXT_ONLY_HOSTED_PROVIDER_CAPABILITIES = createHostedProviderCapabilities({
|
|
@@ -41,6 +47,13 @@ export const TEXT_ONLY_HOSTED_PROVIDER_CAPABILITIES = createHostedProviderCapabi
|
|
|
41
47
|
reason: 'transport-does-not-deliver-real-media',
|
|
42
48
|
delivery: [],
|
|
43
49
|
},
|
|
50
|
+
progressUpdates: {
|
|
51
|
+
support: 'supported',
|
|
52
|
+
},
|
|
53
|
+
backgroundRuns: {
|
|
54
|
+
lifecycle: 'not-supported',
|
|
55
|
+
targetedCancellation: 'not-supported',
|
|
56
|
+
},
|
|
44
57
|
});
|
|
45
58
|
export class ProviderTurnCancelledError extends Error {
|
|
46
59
|
publicReplyText;
|
package/dist/types.d.ts
CHANGED
|
@@ -12,8 +12,12 @@ export interface ProviderCommandConfig {
|
|
|
12
12
|
providerIdleShutdownMinutes: number;
|
|
13
13
|
/**
|
|
14
14
|
* Whether this agents-host may let a hosted agent enter an independent
|
|
15
|
-
* execution.local_directory
|
|
16
|
-
* agent. Undefined keeps the default allow behavior.
|
|
15
|
+
* execution.local_directory working folder for a task thread created by
|
|
16
|
+
* another agent. Undefined keeps the default allow behavior.
|
|
17
|
+
*
|
|
18
|
+
* The name mirrors the operator-facing `ALLOW_CROSS_AGENT_INDEPENDENT_WORKSPACE_HANDOFF`
|
|
19
|
+
* environment variable and the identically named agent YAML key, so it keeps
|
|
20
|
+
* their spelling.
|
|
17
21
|
*/
|
|
18
22
|
allowCrossAgentIndependentWorkspaceHandoff?: boolean;
|
|
19
23
|
}
|
|
@@ -337,22 +341,22 @@ export interface TaskAssignmentThreadContext {
|
|
|
337
341
|
active: true;
|
|
338
342
|
currentTaskId?: string;
|
|
339
343
|
}
|
|
340
|
-
export interface
|
|
344
|
+
export interface ChannelResolvedWorkingFolderContext {
|
|
341
345
|
authority: 'channel';
|
|
342
346
|
owningChannelId: string;
|
|
343
347
|
rootPath: string;
|
|
344
348
|
}
|
|
345
|
-
export interface
|
|
349
|
+
export interface TaskThreadScratchWorkingFolderContext {
|
|
346
350
|
authority: 'task-thread-scratch';
|
|
347
351
|
rootPath: string;
|
|
348
352
|
reason?: 'missing-or-invalid-execution-target' | 'cross-agent-independent-workspace-disabled';
|
|
349
353
|
}
|
|
350
|
-
export interface
|
|
354
|
+
export interface TaskExecutionTargetResolvedWorkingFolderContext {
|
|
351
355
|
authority: 'task-execution-target';
|
|
352
356
|
taskId: string;
|
|
353
357
|
rootPath: string;
|
|
354
358
|
}
|
|
355
|
-
export type
|
|
359
|
+
export type ResolvedWorkingFolderContext = ChannelResolvedWorkingFolderContext | TaskThreadScratchWorkingFolderContext | TaskExecutionTargetResolvedWorkingFolderContext;
|
|
356
360
|
export interface RuntimeSurface {
|
|
357
361
|
schemaVersion: 1;
|
|
358
362
|
turnMode: ProviderCollaborationTurnMode;
|
|
@@ -397,7 +401,7 @@ export interface PreparedPromptContext {
|
|
|
397
401
|
skillRuntime?: SkillRuntimeBootstrapMetadata;
|
|
398
402
|
localhostGateway?: LocalhostGatewayBootstrapMetadata;
|
|
399
403
|
taskAssignmentContext?: TaskAssignmentThreadContext;
|
|
400
|
-
|
|
404
|
+
resolvedWorkingFolder?: ResolvedWorkingFolderContext;
|
|
401
405
|
runtimeSurface?: RuntimeSurface;
|
|
402
406
|
}
|
|
403
407
|
export interface PreparedProviderSessionRouting {
|
|
@@ -536,11 +540,25 @@ export interface ProviderCompactionProgress {
|
|
|
536
540
|
contextWindowTokens?: number;
|
|
537
541
|
failureReason?: string;
|
|
538
542
|
}
|
|
543
|
+
export interface ProviderBackgroundRunProgress {
|
|
544
|
+
providerRunId: string;
|
|
545
|
+
label: string;
|
|
546
|
+
state: 'running' | 'waiting' | 'completed' | 'failed' | 'cancelled' | 'unknown';
|
|
547
|
+
startedAt?: number;
|
|
548
|
+
finishedAt?: number;
|
|
549
|
+
reason?: string;
|
|
550
|
+
stopSupported: boolean;
|
|
551
|
+
}
|
|
552
|
+
export interface ProviderAutonomousReply {
|
|
553
|
+
channelId: string;
|
|
554
|
+
eventId: string;
|
|
555
|
+
text: string;
|
|
556
|
+
}
|
|
539
557
|
/**
|
|
540
|
-
*
|
|
541
|
-
*
|
|
542
|
-
*
|
|
543
|
-
*
|
|
558
|
+
* Turn text, plans, activities, compaction, and provider-owned background runs
|
|
559
|
+
* have different identities and lifetimes, so they travel as separate shapes.
|
|
560
|
+
* Text is cumulative rather than a delta, so a consumer that misses one update
|
|
561
|
+
* recovers on the next.
|
|
544
562
|
*/
|
|
545
563
|
export type ProviderProgressUpdate = {
|
|
546
564
|
type: 'text';
|
|
@@ -556,6 +574,9 @@ export type ProviderProgressUpdate = {
|
|
|
556
574
|
} | {
|
|
557
575
|
type: 'compaction';
|
|
558
576
|
compaction: ProviderCompactionProgress;
|
|
577
|
+
} | {
|
|
578
|
+
type: 'background_run';
|
|
579
|
+
run: ProviderBackgroundRunProgress;
|
|
559
580
|
};
|
|
560
581
|
export interface ProviderGenerateOptions {
|
|
561
582
|
onProgress?: (update: ProviderProgressUpdate) => void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@borgee/agents-host",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.84",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"@agentclientprotocol/claude-agent-acp": "0.64.0",
|
|
26
26
|
"@agentclientprotocol/codex-acp": "1.1.7",
|
|
27
27
|
"@agentclientprotocol/sdk": "1.3.0",
|
|
28
|
+
"@github/copilot-sdk": "1.0.11",
|
|
28
29
|
"cross-spawn": "^7.0.6",
|
|
29
30
|
"yaml": "^2.8.1"
|
|
30
31
|
},
|
|
@@ -35,7 +36,7 @@
|
|
|
35
36
|
"tsx": "^4.20.5",
|
|
36
37
|
"typescript": "^5.9.3",
|
|
37
38
|
"vitest": "^4.1.5",
|
|
38
|
-
"@borgee/plugin-sdk": "0.12.
|
|
39
|
+
"@borgee/plugin-sdk": "0.12.8"
|
|
39
40
|
},
|
|
40
41
|
"scripts": {
|
|
41
42
|
"predev": "pnpm --filter @borgee/plugin-sdk build",
|
|
@@ -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
|
|
25
|
-
| `workspace.mode` | The host-managed scratch-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
}
|