1
>
/*---------------------------------------------------------------------------------------------
promptRegistry.ts
2
>
* Copyright (c) Microsoft Corporation. All rights reserved.
3
>
* Licensed under the MIT License. See License.txt in the project root for license information.
4
>
*--------------------------------------------------------------------------------------------*/
5
>
6
>
import type { SectionOverride, SystemMessageConfig, SystemMessageSection } from '@github/copilot-sdk';
7
>
import { copilotCliConfigSchema } from '../../../common/copilotCliConfig.js';
8
>
import type { SchemaValue } from '../../../common/agentHostSchema.js';
9
>
import type { ModelSelection } from '../../../common/state/protocol/state.js';
10
>
import { appendSystemMessageContent, COPILOT_AGENT_HOST_FILE_LINK_INSTRUCTIONS, COPILOT_AGENT_HOST_WORKSPACELESS_INSTRUCTIONS, COPILOT_AGENT_HOST_SYSTEM_MESSAGE, fullSystemPrompt, sectionOverrides } from './systemMessage.js';
11
>
import { resolveToolInstructionsOverride, toolSearchInstructionLines } from './toolInstructions.js';
12
>
13
>
type CopilotCliConfigDefinition = typeof copilotCliConfigSchema.definition;
14
>
15
>
/**
16
>
* Read-time context handed to prompt contributors so they can gate behavior on
17
>
* host configuration — the agent-host equivalent of the Copilot extension
18
>
* injecting `IConfigurationService` into a resolver.
19
>
*
20
>
* Scoped to the Copilot CLI config schema so contributors (and tests) read
21
>
* settings in a fully-typed way without depending on the whole configuration
22
>
* service.
23
>
*/
24
>
export interface IAgentHostPromptContext {
25
>
/**
26
>
* Returns the host-level value for a Copilot CLI setting, or `undefined`
27
>
* when unset. Mirrors `IAgentConfigurationService.getRootValue` bound to
28
>
* {@link copilotCliConfigSchema}.
29
>
*/
30
>
getSetting<K extends keyof CopilotCliConfigDefinition & string>(key: K): SchemaValue<CopilotCliConfigDefinition[K]> | undefined;
31
>
32
>
/**
33
>
* Returns whether a *client* tool is available in the session, addressed by
34
>
* the camelCase `toolReferenceName` the agent sees it under (e.g.
35
>
* `openBrowserPage`). Used to gate tool-specific instructions on the tool
36
>
* being present, the agent-host equivalent of the Copilot extension
37
>
* inspecting its tool set.
38
>
*
39
>
* Scope: client tools only (the forwarded workbench tools). It does NOT see
40
>
* shell tools, server-SDK tools, or MCP-provided tools — those aren't in the
41
>
* session snapshot at launch (MCP is discovered dynamically). A line that
42
>
* gates on one of those names silently resolves to `false`; broadening this
43
>
* is the context-enrichment follow-up.
44
>
*/
45
>
hasClientTool(name: string): boolean;
46
>
47
>
/** Whether deferred tool search is active for this session. */
48
>
toolSearchActive: boolean;
49
>
50
>
/**
51
>
* Whether this is a workspace-less session. When `true`, the
52
>
* resolved system message gets a scratch/repoless section (see
53
>
* {@link COPILOT_AGENT_HOST_WORKSPACELESS_INSTRUCTIONS}) telling the agent its
54
>
* working directory is a scratch dir, not a code repo. Set by the launcher
55
>
* from the session's `workspaceless` marker.
56
>
*/
57
>
workspaceless: boolean;
58
>
}
59
>
60
>
/**
61
>
* Per-model system-prompt contributor for Copilot CLI agent-host sessions.
62
>
*
63
>
* Mirrors the Copilot extension's `IAgentPrompt`, but — because the agent host
64
>
* runs in its own process and cannot use prompt-tsx — a contributor returns
65
>
* plain data the SDK accepts directly rather than prompt-tsx elements.
66
>
*
67
>
* A contributor may provide EITHER a full system-prompt override OR a set of
68
>
* section overrides. When it provides a full prompt that wins (`replace` mode);
69
>
* otherwise the section overrides are applied (`customize` mode).
70
>
*/
71
>
export interface IAgentHostPrompt {
72
>
/**
73
>
* Full system-prompt override. Resolved into `{ mode: 'replace' }`, which
74
>
* drops the SDK foundation prompt and its guardrails.
75
>
*/
76
>
resolveFullSystemPrompt?(model: ModelSelection, context: IAgentHostPromptContext): string | undefined;
77
>
78
>
/**
79
>
* Section-level overrides. Resolved into `{ mode: 'customize' }`, keeping the
80
>
* SDK foundation prompt and guardrails intact.
81
>
*/
82
>
resolveSectionOverrides?(model: ModelSelection, context: IAgentHostPromptContext): Partial<Record<SystemMessageSection, SectionOverride>> | undefined;
83
>
}
84
>
85
>
/**
86
>
* Constructor/static shape for a registered prompt contributor. Mirrors the
87
>
* Copilot extension's `IAgentPromptCtor`: a contributor matches a model either
88
>
* by a custom {@link matchesModel} predicate or by a model-id family prefix.
89
>
*/
90
>
export interface IAgentHostPromptCtor {
91
>
/** Model-id prefixes this contributor handles (e.g. `'claude'`, `'gpt-5'`). */
92
>
readonly familyPrefixes: readonly string[];
93
>
94
>
/** Optional custom matcher; takes precedence over {@link familyPrefixes}. */
95
>
matchesModel?(model: ModelSelection): boolean;
96
>
97
>
new(): IAgentHostPrompt;
98
>
}
99
>
100
>
type PromptWithMatcher = IAgentHostPromptCtor & { matchesModel: (model: ModelSelection) => boolean };
101
>
102
>
/**
103
>
* Registry of per-model system-prompt contributors for Copilot CLI agent-host
104
>
* sessions. Mirrors the Copilot extension's `PromptRegistry`: contributors
105
>
* register a model match (custom predicate or family prefix) and the session
106
>
* launcher calls {@link resolveSystemMessageConfig} when building a session.
107
>
*
108
>
* Exported as a class for isolated unit testing; a shared singleton
109
>
* ({@link agentHostPromptRegistry}) is what contributors register into and the
110
>
* launcher consumes.
111
>
*/
112
>
export class AgentHostPromptRegistry {
113
>
private readonly _promptsWithMatcher: PromptWithMatcher[] = [];
114
>
private readonly _familyPrefixList: { readonly prefix: string; readonly ctor: IAgentHostPromptCtor }[] = [];
115
>
116
>
registerPrompt(ctor: IAgentHostPromptCtor): void {
117
>
if (ctor.matchesModel) {
118
>
this._promptsWithMatcher.push(ctor as PromptWithMatcher);
119
>
}
120
>
for (const prefix of ctor.familyPrefixes) {
121
this._familyPrefixList.push({ prefix, ctor });
122
}
124
>
125
>
private _getContributor(model: ModelSelection): IAgentHostPromptCtor | undefined {
126
for (const ctor of this._promptsWithMatcher) {
127
if (ctor.matchesModel(model)) {