@tangle-network/agent-interface 0.43.1 → 0.45.0
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 +8 -1
- package/dist/agent-candidate-code-schema.d.ts +1 -0
- package/dist/agent-candidate-execution-plan-schema.d.ts +28 -6
- package/dist/agent-candidate-execution-plan-schema.js +1 -0
- package/dist/agent-candidate-profile-schema.d.ts +2 -0
- package/dist/agent-candidate-promotion-schema.d.ts +83 -20
- package/dist/agent-candidate-receipt-schema.d.ts +13 -4
- package/dist/agent-candidate-schema.d.ts +3 -0
- package/dist/agent-candidate.d.ts +12 -1
- package/dist/agent-execution-preparation.d.ts +7 -4
- package/dist/agent-profile-materialization.d.ts +6 -3
- package/dist/agent-profile-materialization.js +11 -2
- package/dist/agent-profile.d.ts +74 -4
- package/dist/agent-profile.js +17 -1
- package/dist/environment-provider.d.ts +4 -1
- package/dist/environment-provider.js +9 -1
- package/dist/harness-capabilities.d.ts +33 -4
- package/dist/harness-capabilities.js +78 -3
- package/dist/harness.d.ts +6 -1
- package/dist/harness.js +1 -0
- package/dist/profile-diff.d.ts +1 -0
- package/dist/profile-diff.js +9 -0
- package/dist/profile-schema.d.ts +11 -0
- package/dist/profile-schema.js +19 -0
- package/package.json +1 -1
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The
|
|
2
|
+
* The 30 canonical AgentProfile leaves that can affect one execution.
|
|
3
3
|
*
|
|
4
4
|
* Compound parents such as `model`, `prompt`, and `resources` are deliberately
|
|
5
5
|
* absent. A producer must report the exact requested leaf instead of claiming
|
|
6
|
-
* a parent while silently dropping one of its children.
|
|
6
|
+
* a parent while silently dropping one of its children. `systemPrompt` and
|
|
7
|
+
* `appendSystemPrompt` are separate leaves for the same reason: a backend that
|
|
8
|
+
* can only add text must report the requested replacement as unsatisfied rather
|
|
9
|
+
* than acknowledge it.
|
|
7
10
|
*/
|
|
8
11
|
export const AGENT_PROFILE_MATERIALIZATION_AXES = [
|
|
9
12
|
"name",
|
|
@@ -11,6 +14,7 @@ export const AGENT_PROFILE_MATERIALIZATION_AXES = [
|
|
|
11
14
|
"version",
|
|
12
15
|
"tags",
|
|
13
16
|
"systemPrompt",
|
|
17
|
+
"appendSystemPrompt",
|
|
14
18
|
"instructions",
|
|
15
19
|
"modelDefault",
|
|
16
20
|
"modelSmall",
|
|
@@ -68,6 +72,11 @@ const AXIS_DESCRIPTORS = [
|
|
|
68
72
|
rootPath: "/prompt/systemPrompt",
|
|
69
73
|
value: (profile) => profile.prompt?.systemPrompt,
|
|
70
74
|
},
|
|
75
|
+
{
|
|
76
|
+
axis: "appendSystemPrompt",
|
|
77
|
+
rootPath: "/prompt/appendSystemPrompt",
|
|
78
|
+
value: (profile) => profile.prompt?.appendSystemPrompt,
|
|
79
|
+
},
|
|
71
80
|
{
|
|
72
81
|
axis: "instructions",
|
|
73
82
|
rootPath: "/prompt/instructions",
|
package/dist/agent-profile.d.ts
CHANGED
|
@@ -131,14 +131,58 @@ export interface AgentProfileModelHints {
|
|
|
131
131
|
}
|
|
132
132
|
/**
|
|
133
133
|
* Prompt shaping for an agent.
|
|
134
|
+
*
|
|
135
|
+
* Replacement and addition are two different intents against the same channel
|
|
136
|
+
* and are never interchangeable. A backend that can only do one of them must
|
|
137
|
+
* refuse the other rather than substituting it, which is why
|
|
138
|
+
* {@link AgentProfileCapabilities.systemPrompt} carries a separate bit for each.
|
|
139
|
+
*
|
|
140
|
+
* Setting `systemPrompt` and `appendSystemPrompt` together is legal and ordered:
|
|
141
|
+
* the replacement is installed first and the addition composes on top of it, so
|
|
142
|
+
* the effective prompt is `systemPrompt` then `appendSystemPrompt`. The pair is
|
|
143
|
+
* deliberately allowed because {@link mergeAgentProfiles} composes the two
|
|
144
|
+
* fields independently — refusing it would let two individually valid profiles
|
|
145
|
+
* merge into an invalid one.
|
|
134
146
|
*/
|
|
135
147
|
export interface AgentProfilePrompt {
|
|
136
148
|
/**
|
|
137
|
-
*
|
|
149
|
+
* REPLACE the harness's own system prompt with this text.
|
|
150
|
+
*
|
|
151
|
+
* The harness's built-in prompt is DELETED, not extended: the model stops
|
|
152
|
+
* receiving the tool descriptions, output conventions, refusal rules, and
|
|
153
|
+
* workflow scaffolding it was tuned against, so behavior can move far beyond
|
|
154
|
+
* the words written here. Supply a prompt that stands on its own.
|
|
155
|
+
*
|
|
156
|
+
* Honored only where {@link AgentProfileSystemPromptCapability.replace} is
|
|
157
|
+
* true — a harness that exposes a real replacement control (`pi
|
|
158
|
+
* --system-prompt <file>` with context files, skills, and prompt templates
|
|
159
|
+
* off; gemini `.gemini/system.md` with `GEMINI_SYSTEM_MD=1`). A backend that
|
|
160
|
+
* can only add text must reject this field. Folding it into an addition is a
|
|
161
|
+
* silent semantic downgrade: the instructions the caller asked to delete stay
|
|
162
|
+
* in force, and nothing in the result says so.
|
|
138
163
|
*/
|
|
139
164
|
systemPrompt?: string;
|
|
140
165
|
/**
|
|
141
|
-
*
|
|
166
|
+
* ADD this text to the harness's own system prompt, which stays intact.
|
|
167
|
+
*
|
|
168
|
+
* The model keeps everything it was tuned against and receives this on top,
|
|
169
|
+
* in the same privileged position as the system prompt. Maps to claude-code
|
|
170
|
+
* `--append-system-prompt`, and to a leading `role: "system"` message on
|
|
171
|
+
* harnesses that take a message list.
|
|
172
|
+
*
|
|
173
|
+
* Distinct from {@link AgentProfilePrompt.instructions}, which harnesses
|
|
174
|
+
* materialize into their lower-privilege project-instruction surface.
|
|
175
|
+
*
|
|
176
|
+
* Honored only where {@link AgentProfileSystemPromptCapability.append} is
|
|
177
|
+
* true.
|
|
178
|
+
*/
|
|
179
|
+
appendSystemPrompt?: string;
|
|
180
|
+
/**
|
|
181
|
+
* Additional instruction lines composed into the agent's project-instruction
|
|
182
|
+
* surface — the harness's `AGENTS.md` / `CLAUDE.md`-style files or its own
|
|
183
|
+
* caller-instruction preamble. Lower privilege than
|
|
184
|
+
* {@link AgentProfilePrompt.appendSystemPrompt} and placed wherever the
|
|
185
|
+
* harness keeps caller instructions rather than in the system prompt.
|
|
142
186
|
*/
|
|
143
187
|
instructions?: string[];
|
|
144
188
|
}
|
|
@@ -326,12 +370,36 @@ export interface AgentProfile {
|
|
|
326
370
|
* Helper for declaring typed profiles in application code.
|
|
327
371
|
*/
|
|
328
372
|
export declare function defineAgentProfile<T extends AgentProfile>(profile: T): T;
|
|
373
|
+
/**
|
|
374
|
+
* What a backend can do to the harness's system prompt.
|
|
375
|
+
*
|
|
376
|
+
* Two independent bits, because most harnesses can do exactly one of them. A
|
|
377
|
+
* single boolean cannot separate "I delete the built-in prompt and install
|
|
378
|
+
* yours" from "I keep the built-in prompt and add yours to it", so a caller
|
|
379
|
+
* reading it has no way to tell whether a requested replacement will actually
|
|
380
|
+
* happen. Neither bit implies the other: declare each from what the backend's
|
|
381
|
+
* materialization really does, not from whether it accepts the field.
|
|
382
|
+
*/
|
|
383
|
+
export interface AgentProfileSystemPromptCapability {
|
|
384
|
+
/**
|
|
385
|
+
* The backend honors {@link AgentProfilePrompt.systemPrompt} by deleting the
|
|
386
|
+
* harness's own system prompt and installing the caller's. `false` means a
|
|
387
|
+
* profile carrying `systemPrompt` must be REFUSED — never quietly added to
|
|
388
|
+
* the built-in prompt instead.
|
|
389
|
+
*/
|
|
390
|
+
replace: boolean;
|
|
391
|
+
/**
|
|
392
|
+
* The backend honors {@link AgentProfilePrompt.appendSystemPrompt} by keeping
|
|
393
|
+
* the harness's own system prompt and adding the caller's text to it.
|
|
394
|
+
*/
|
|
395
|
+
append: boolean;
|
|
396
|
+
}
|
|
329
397
|
/**
|
|
330
398
|
* Capabilities describing how a backend interprets AgentProfile.
|
|
331
399
|
*/
|
|
332
400
|
export interface AgentProfileCapabilities {
|
|
333
401
|
namedProfiles: boolean;
|
|
334
|
-
systemPrompt:
|
|
402
|
+
systemPrompt: AgentProfileSystemPromptCapability;
|
|
335
403
|
instructions: boolean;
|
|
336
404
|
tools: boolean;
|
|
337
405
|
permissions: boolean;
|
|
@@ -374,7 +442,9 @@ export interface AgentProfileValidationResult {
|
|
|
374
442
|
/**
|
|
375
443
|
* Merge two public AgentProfile values.
|
|
376
444
|
*
|
|
377
|
-
* Overlay fields win on conflicts.
|
|
445
|
+
* Overlay fields win on conflicts. Additive fields compose instead: array-like
|
|
446
|
+
* instruction sets are concatenated, and `prompt.appendSystemPrompt` values are
|
|
447
|
+
* joined base-first with a blank line between them.
|
|
378
448
|
*/
|
|
379
449
|
export declare function mergeAgentProfiles(base: AgentProfile | undefined, overlay: AgentProfile | undefined): AgentProfile | undefined;
|
|
380
450
|
export {};
|
package/dist/agent-profile.js
CHANGED
|
@@ -72,6 +72,19 @@ function mergeStringArrays(base, overlay) {
|
|
|
72
72
|
return undefined;
|
|
73
73
|
return [...(base ?? []), ...(overlay ?? [])];
|
|
74
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Additive prompt text composes instead of overwriting: an overlay that adds
|
|
77
|
+
* one line must not delete what the base added. `systemPrompt` keeps
|
|
78
|
+
* overlay-wins semantics because two replacements cannot both apply, while two
|
|
79
|
+
* additions always can. An explicitly empty addition contributes no separator.
|
|
80
|
+
*/
|
|
81
|
+
function mergeAppendedSystemPrompts(base, overlay) {
|
|
82
|
+
if (base === undefined || base === "")
|
|
83
|
+
return overlay ?? base;
|
|
84
|
+
if (overlay === undefined || overlay === "")
|
|
85
|
+
return base;
|
|
86
|
+
return `${base}\n\n${overlay}`;
|
|
87
|
+
}
|
|
75
88
|
function mergeRecord(base, overlay) {
|
|
76
89
|
if (!base && !overlay)
|
|
77
90
|
return undefined;
|
|
@@ -88,7 +101,9 @@ function mergeOptionalArrays(base, overlay) {
|
|
|
88
101
|
/**
|
|
89
102
|
* Merge two public AgentProfile values.
|
|
90
103
|
*
|
|
91
|
-
* Overlay fields win on conflicts.
|
|
104
|
+
* Overlay fields win on conflicts. Additive fields compose instead: array-like
|
|
105
|
+
* instruction sets are concatenated, and `prompt.appendSystemPrompt` values are
|
|
106
|
+
* joined base-first with a blank line between them.
|
|
92
107
|
*/
|
|
93
108
|
export function mergeAgentProfiles(base, overlay) {
|
|
94
109
|
if (!base && !overlay)
|
|
@@ -97,6 +112,7 @@ export function mergeAgentProfiles(base, overlay) {
|
|
|
97
112
|
? {
|
|
98
113
|
...(base?.prompt ?? {}),
|
|
99
114
|
...(overlay?.prompt ?? {}),
|
|
115
|
+
appendSystemPrompt: mergeAppendedSystemPrompts(base?.prompt?.appendSystemPrompt, overlay?.prompt?.appendSystemPrompt),
|
|
100
116
|
instructions: mergeStringArrays(base?.prompt?.instructions, overlay?.prompt?.instructions),
|
|
101
117
|
}
|
|
102
118
|
: undefined;
|
|
@@ -617,7 +617,10 @@ export interface AgentEnvironmentCapabilities {
|
|
|
617
617
|
export declare const AgentEnvironmentCapabilitiesSchema: z.ZodObject<{
|
|
618
618
|
profile: z.ZodObject<{
|
|
619
619
|
namedProfiles: z.ZodBoolean;
|
|
620
|
-
systemPrompt: z.
|
|
620
|
+
systemPrompt: z.ZodObject<{
|
|
621
|
+
replace: z.ZodBoolean;
|
|
622
|
+
append: z.ZodBoolean;
|
|
623
|
+
}, z.core.$strict>;
|
|
621
624
|
instructions: z.ZodBoolean;
|
|
622
625
|
tools: z.ZodBoolean;
|
|
623
626
|
permissions: z.ZodBoolean;
|
|
@@ -7,7 +7,15 @@ import { InteractionCapabilitiesSchema, } from "./interaction.js";
|
|
|
7
7
|
*/
|
|
8
8
|
const AgentProfileCapabilitiesSchema = z.strictObject({
|
|
9
9
|
namedProfiles: z.boolean(),
|
|
10
|
-
|
|
10
|
+
/*
|
|
11
|
+
* Both bits are required with no default: a document that omits either one,
|
|
12
|
+
* or sends a bare boolean, fails validation instead of being read as
|
|
13
|
+
* replacement-supported, which for every append-only backend is false.
|
|
14
|
+
*/
|
|
15
|
+
systemPrompt: z.strictObject({
|
|
16
|
+
replace: z.boolean(),
|
|
17
|
+
append: z.boolean(),
|
|
18
|
+
}),
|
|
11
19
|
instructions: z.boolean(),
|
|
12
20
|
tools: z.boolean(),
|
|
13
21
|
permissions: z.boolean(),
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { type ReasoningEffort } from "./agent-profile.js";
|
|
1
|
+
import { type AgentProfileSystemPromptCapability, type ReasoningEffort } from "./agent-profile.js";
|
|
2
2
|
import type { HarnessType } from "./harness.js";
|
|
3
3
|
/**
|
|
4
4
|
* The unified harness capability layer — the single source of truth for:
|
|
5
|
-
* 1. harness ↔ model compatibility (which models a harness can run),
|
|
6
|
-
* 2. reasoning-effort support (which thinking levels a harness/model expresses)
|
|
5
|
+
* 1. harness ↔ model compatibility (which models a harness can run),
|
|
6
|
+
* 2. reasoning-effort support (which thinking levels a harness/model expresses), and
|
|
7
|
+
* 3. system-prompt intents (whether a harness can replace its own prompt, add to it, or neither).
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
+
* All are facets of the same question — "what can this (harness, model) pair actually do" — and
|
|
9
10
|
* apply to BOTH harness-backed systems (vendor-locked CLIs like claude-code/codex/kimi) AND
|
|
10
11
|
* router-backed systems (opencode, cli-base: any model the router serves). Lifted here so the
|
|
11
12
|
* cli-bridge backends, the sandbox UI pickers, and the router all read one truth instead of each
|
|
@@ -66,3 +67,31 @@ export declare function harnessHonorsModel(harness: HarnessType): boolean;
|
|
|
66
67
|
export declare function harnessHonorsEffort(harness: HarnessType): boolean;
|
|
67
68
|
/** Whether the harness honors BOTH chat selectors — i.e. the model and effort pickers are live. */
|
|
68
69
|
export declare function harnessHonorsSelectors(harness: HarnessType): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Which system-prompt intents a harness honors THROUGH A WORKSPACE — the value an adapter that
|
|
72
|
+
* lowers a profile to files, env vars, and CLI flags and then hands the result to a launcher it
|
|
73
|
+
* does not own should declare as {@link AgentProfileCapabilities.systemPrompt}. That is the shape
|
|
74
|
+
* of every caller today (the cli-bridge and tangle providers both forward a plan), which is why
|
|
75
|
+
* this answer depends on the harness alone.
|
|
76
|
+
*
|
|
77
|
+
* It is NOT the whole truth for an adapter that starts the harness itself, because one control in
|
|
78
|
+
* the table above lives outside any workspace: opencode's `agent.<name>.prompt` really does replace
|
|
79
|
+
* its built-in prompt, but it binds to the single agent whoever starts the server selects. A plan
|
|
80
|
+
* cannot name that agent, so `opencode` reads `replace: false` here — while an adapter that writes
|
|
81
|
+
* opencode's server config AND picks the primary agent (`sdk-provider-opencode`) does honor
|
|
82
|
+
* replacement, and declares `replace: true` for itself. The capability is a property of the
|
|
83
|
+
* (harness, executor) pair; this function answers it for the plan-forwarding executor.
|
|
84
|
+
*
|
|
85
|
+
* Do not widen the table to close that gap: a harness-keyed `true` would promise the intent to
|
|
86
|
+
* every plan-forwarding caller, and those callers cannot deliver it. An executor that owns a
|
|
87
|
+
* launcher control states so where it binds it — `materializeProfile`'s `binds` option in
|
|
88
|
+
* `@tangle-network/agent-profile-materialize`, which turns the plan's refusal into a binding that
|
|
89
|
+
* executor must then apply.
|
|
90
|
+
*
|
|
91
|
+
* Pass `undefined` when the harness is not known at declaration time: the answer is then
|
|
92
|
+
* `{ replace: false, append: false }`, because an adapter that cannot name its harness cannot
|
|
93
|
+
* promise either intent, and `false` means "refuse" rather than "silently substitute the other".
|
|
94
|
+
* An adapter that forwards a profile to some other layer must still declare what that layer's
|
|
95
|
+
* harness really does — being able to put the field on the wire is not the same as honoring it.
|
|
96
|
+
*/
|
|
97
|
+
export declare function harnessSystemPromptIntents(harness: HarnessType | undefined): AgentProfileSystemPromptCapability;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { REASONING_EFFORTS, } from "./agent-profile.js";
|
|
2
2
|
/**
|
|
3
3
|
* The unified harness capability layer — the single source of truth for:
|
|
4
|
-
* 1. harness ↔ model compatibility (which models a harness can run),
|
|
5
|
-
* 2. reasoning-effort support (which thinking levels a harness/model expresses)
|
|
4
|
+
* 1. harness ↔ model compatibility (which models a harness can run),
|
|
5
|
+
* 2. reasoning-effort support (which thinking levels a harness/model expresses), and
|
|
6
|
+
* 3. system-prompt intents (whether a harness can replace its own prompt, add to it, or neither).
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
+
* All are facets of the same question — "what can this (harness, model) pair actually do" — and
|
|
8
9
|
* apply to BOTH harness-backed systems (vendor-locked CLIs like claude-code/codex/kimi) AND
|
|
9
10
|
* router-backed systems (opencode, cli-base: any model the router serves). Lifted here so the
|
|
10
11
|
* cli-bridge backends, the sandbox UI pickers, and the router all read one truth instead of each
|
|
@@ -124,6 +125,9 @@ export function snapHarnessToModel(harness, modelId) {
|
|
|
124
125
|
* and silently replaced with the default rather than rejected — so the set must not overstate.
|
|
125
126
|
* - pi: `--thinking` accepts `off|minimal|low|medium|high|xhigh|max`; canonical `none` maps to
|
|
126
127
|
* `off` and `ultracode` to `max`.
|
|
128
|
+
* - prime: the prime fork of the pi line accepts the same `--thinking` set
|
|
129
|
+
* (`off|minimal|low|medium|high|xhigh|max`); canonical `none` maps to `off` and `ultracode` to
|
|
130
|
+
* `max`.
|
|
127
131
|
* - openclaw: `--thinking` accepts `off|minimal|low|medium|high|xhigh|max` (and `adaptive`, which
|
|
128
132
|
* defers the choice rather than naming a rung); canonical `none` maps to `off` and `ultracode`
|
|
129
133
|
* to `max`.
|
|
@@ -134,6 +138,7 @@ const harnessReasoningEffortsOverride = {
|
|
|
134
138
|
codex: ["none", "minimal", "low", "medium", "high", "xhigh", "ultracode"],
|
|
135
139
|
"claude-code": ["low", "medium", "high", "xhigh", "ultracode"],
|
|
136
140
|
pi: ["none", "minimal", "low", "medium", "high", "xhigh", "ultracode"],
|
|
141
|
+
prime: ["none", "minimal", "low", "medium", "high", "xhigh", "ultracode"],
|
|
137
142
|
openclaw: [
|
|
138
143
|
"none",
|
|
139
144
|
"minimal",
|
|
@@ -219,3 +224,73 @@ export function harnessHonorsEffort(harness) {
|
|
|
219
224
|
export function harnessHonorsSelectors(harness) {
|
|
220
225
|
return harnessHonorsModel(harness) && harnessHonorsEffort(harness);
|
|
221
226
|
}
|
|
227
|
+
// ── System-prompt intents (which prompt channel the harness actually owns) ────
|
|
228
|
+
/**
|
|
229
|
+
* The system-prompt intents a harness's own controls can execute, measured by reading the request
|
|
230
|
+
* each installed CLI sends — NOT taken from its help text:
|
|
231
|
+
*
|
|
232
|
+
* - claude-code 2.1.222 and pi 0.83.0 own both. `--system-prompt` drops the built-in prompt from
|
|
233
|
+
* the request (27,673 B → the caller's bytes on claude-code, 2,582 B → the caller's on pi);
|
|
234
|
+
* `--append-system-prompt` leaves it in place and adds the caller's text after it.
|
|
235
|
+
* - codex 0.146.0 owns replacement only: the `model_instructions_file` config key becomes the
|
|
236
|
+
* request's entire instructions text. It has no additive control — its AGENTS.md lands in a
|
|
237
|
+
* developer/user message, not the system channel.
|
|
238
|
+
* - gemini 0.26.0 owns replacement only: `.gemini/system.md` under `GEMINI_SYSTEM_MD=1` replaces
|
|
239
|
+
* the base prompt. Its one additive path is GEMINI.md memory, which IS the `instructions`
|
|
240
|
+
* surface, so an addition lowered there would be byte-indistinguishable from `instructions`.
|
|
241
|
+
* - opencode 1.17.18 owns addition only THROUGH A WORKSPACE: config-declared `instructions[]`
|
|
242
|
+
* files compose into the same single `role: "system"` message as its built-in prompt, which
|
|
243
|
+
* stays in place. Its replacement control (`agent.<name>.prompt`) binds to one agent chosen at
|
|
244
|
+
* launch, which a workspace plan cannot guarantee — but an executor that selects that agent
|
|
245
|
+
* can, so `replace: false` here is the plan-forwarding answer, not opencode's ceiling.
|
|
246
|
+
*
|
|
247
|
+
* Every other harness owns NEITHER, including the ones whose prompt path is a `role: "system"` chat
|
|
248
|
+
* message: that message is flattened into the user turn before the CLI sees it, so it is not a
|
|
249
|
+
* system-prompt channel at all — honoring an intent through it would put the caller's text in
|
|
250
|
+
* ordinary user content while the harness's own prompt ran unchanged. A harness with no entry
|
|
251
|
+
* refuses both, so one added later cannot inherit a capability by omission.
|
|
252
|
+
*/
|
|
253
|
+
const harnessSystemPromptControls = {
|
|
254
|
+
"claude-code": { replace: true, append: true },
|
|
255
|
+
pi: { replace: true, append: true },
|
|
256
|
+
prime: { replace: true, append: true },
|
|
257
|
+
codex: { replace: true, append: false },
|
|
258
|
+
gemini: { replace: true, append: false },
|
|
259
|
+
opencode: { replace: false, append: true },
|
|
260
|
+
};
|
|
261
|
+
const noSystemPromptControls = {
|
|
262
|
+
replace: false,
|
|
263
|
+
append: false,
|
|
264
|
+
};
|
|
265
|
+
/**
|
|
266
|
+
* Which system-prompt intents a harness honors THROUGH A WORKSPACE — the value an adapter that
|
|
267
|
+
* lowers a profile to files, env vars, and CLI flags and then hands the result to a launcher it
|
|
268
|
+
* does not own should declare as {@link AgentProfileCapabilities.systemPrompt}. That is the shape
|
|
269
|
+
* of every caller today (the cli-bridge and tangle providers both forward a plan), which is why
|
|
270
|
+
* this answer depends on the harness alone.
|
|
271
|
+
*
|
|
272
|
+
* It is NOT the whole truth for an adapter that starts the harness itself, because one control in
|
|
273
|
+
* the table above lives outside any workspace: opencode's `agent.<name>.prompt` really does replace
|
|
274
|
+
* its built-in prompt, but it binds to the single agent whoever starts the server selects. A plan
|
|
275
|
+
* cannot name that agent, so `opencode` reads `replace: false` here — while an adapter that writes
|
|
276
|
+
* opencode's server config AND picks the primary agent (`sdk-provider-opencode`) does honor
|
|
277
|
+
* replacement, and declares `replace: true` for itself. The capability is a property of the
|
|
278
|
+
* (harness, executor) pair; this function answers it for the plan-forwarding executor.
|
|
279
|
+
*
|
|
280
|
+
* Do not widen the table to close that gap: a harness-keyed `true` would promise the intent to
|
|
281
|
+
* every plan-forwarding caller, and those callers cannot deliver it. An executor that owns a
|
|
282
|
+
* launcher control states so where it binds it — `materializeProfile`'s `binds` option in
|
|
283
|
+
* `@tangle-network/agent-profile-materialize`, which turns the plan's refusal into a binding that
|
|
284
|
+
* executor must then apply.
|
|
285
|
+
*
|
|
286
|
+
* Pass `undefined` when the harness is not known at declaration time: the answer is then
|
|
287
|
+
* `{ replace: false, append: false }`, because an adapter that cannot name its harness cannot
|
|
288
|
+
* promise either intent, and `false` means "refuse" rather than "silently substitute the other".
|
|
289
|
+
* An adapter that forwards a profile to some other layer must still declare what that layer's
|
|
290
|
+
* harness really does — being able to put the field on the wire is not the same as honoring it.
|
|
291
|
+
*/
|
|
292
|
+
export function harnessSystemPromptIntents(harness) {
|
|
293
|
+
if (!harness)
|
|
294
|
+
return noSystemPromptControls;
|
|
295
|
+
return harnessSystemPromptControls[harness] ?? noSystemPromptControls;
|
|
296
|
+
}
|
package/dist/harness.d.ts
CHANGED
|
@@ -16,8 +16,12 @@ import { z } from "zod";
|
|
|
16
16
|
*
|
|
17
17
|
* `forge` (tailcallhq/forgecode) and `cursor` (cursor-agent) are multi-provider CLI harnesses with
|
|
18
18
|
* no vendor lock, so they carry no entry in the capability tables and resolve as router-backed.
|
|
19
|
+
*
|
|
20
|
+
* `prime` (PrimeIntellect-ai/prime-agent) is the prime fork of the pi line — the RLM
|
|
21
|
+
* IPython-kernel harness. It is a distinct harness from `pi`: the fork's wire protocol has
|
|
22
|
+
* diverged (its daemon rejects pi-line clients), so the two are not interchangeable at run time.
|
|
19
23
|
*/
|
|
20
|
-
export type HarnessType = "claude-code" | "nanoclaw" | "codex" | "opencode" | "kimi-code" | "pi" | "gemini" | "hermes" | "openclaw" | "amp" | "factory-droids" | "forge" | "cursor" | "acp" | "cli-base";
|
|
24
|
+
export type HarnessType = "claude-code" | "nanoclaw" | "codex" | "opencode" | "kimi-code" | "pi" | "prime" | "gemini" | "hermes" | "openclaw" | "amp" | "factory-droids" | "forge" | "cursor" | "acp" | "cli-base";
|
|
21
25
|
/** Runtime validator for {@link HarnessType}. Kept in lockstep with the type by the drift guard below. */
|
|
22
26
|
export declare const harnessTypeSchema: z.ZodEnum<{
|
|
23
27
|
"claude-code": "claude-code";
|
|
@@ -26,6 +30,7 @@ export declare const harnessTypeSchema: z.ZodEnum<{
|
|
|
26
30
|
opencode: "opencode";
|
|
27
31
|
"kimi-code": "kimi-code";
|
|
28
32
|
pi: "pi";
|
|
33
|
+
prime: "prime";
|
|
29
34
|
gemini: "gemini";
|
|
30
35
|
hermes: "hermes";
|
|
31
36
|
openclaw: "openclaw";
|
package/dist/harness.js
CHANGED
package/dist/profile-diff.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export type AgentProfileDiffAxis = "identity" | (typeof agentProfileDiffProperty
|
|
|
4
4
|
export type AgentProfileRemoveList = true | readonly string[];
|
|
5
5
|
export interface AgentProfilePromptRemoval {
|
|
6
6
|
systemPrompt?: true;
|
|
7
|
+
appendSystemPrompt?: true;
|
|
7
8
|
instructions?: AgentProfileRemoveList;
|
|
8
9
|
}
|
|
9
10
|
export interface AgentProfileResourceRemoval {
|
package/dist/profile-diff.js
CHANGED
|
@@ -21,6 +21,13 @@ void _agentProfileDiffPropertyAxesAreExhaustive;
|
|
|
21
21
|
export function defineAgentProfileDiff(diff) {
|
|
22
22
|
return diff;
|
|
23
23
|
}
|
|
24
|
+
const agentProfilePromptDiffPropertyAxes = [
|
|
25
|
+
"systemPrompt",
|
|
26
|
+
"appendSystemPrompt",
|
|
27
|
+
"instructions",
|
|
28
|
+
];
|
|
29
|
+
const _agentProfilePromptDiffPropertyAxesAreExhaustive = true;
|
|
30
|
+
void _agentProfilePromptDiffPropertyAxesAreExhaustive;
|
|
24
31
|
const agentProfileResourceDiffPropertyAxes = [
|
|
25
32
|
"files",
|
|
26
33
|
"tools",
|
|
@@ -223,6 +230,8 @@ function applyRemoval(profile, remove) {
|
|
|
223
230
|
const prompt = { ...next.prompt };
|
|
224
231
|
if (remove.prompt.systemPrompt)
|
|
225
232
|
prompt.systemPrompt = undefined;
|
|
233
|
+
if (remove.prompt.appendSystemPrompt)
|
|
234
|
+
prompt.appendSystemPrompt = undefined;
|
|
226
235
|
prompt.instructions = removeValues(prompt.instructions, remove.prompt.instructions);
|
|
227
236
|
next.prompt = Object.values(prompt).some((value) => value !== undefined)
|
|
228
237
|
? prompt
|
package/dist/profile-schema.d.ts
CHANGED
|
@@ -135,8 +135,15 @@ export declare const agentProfileModelHintsSchema: z.ZodObject<{
|
|
|
135
135
|
}>>;
|
|
136
136
|
metadata: z.ZodOptional<z.ZodType<Record<string, unknown>, unknown, z.core.$ZodTypeInternals<Record<string, unknown>, unknown>>>;
|
|
137
137
|
}, z.core.$strict>;
|
|
138
|
+
/**
|
|
139
|
+
* Replacement and addition are separate, independently optional fields, and the
|
|
140
|
+
* pair is admitted on purpose: the effective prompt is `systemPrompt` followed
|
|
141
|
+
* by `appendSystemPrompt`. No cross-field refinement rejects the combination,
|
|
142
|
+
* because {@link mergeAgentProfiles} can produce it from two valid profiles.
|
|
143
|
+
*/
|
|
138
144
|
export declare const agentProfilePromptSchema: z.ZodObject<{
|
|
139
145
|
systemPrompt: z.ZodOptional<z.ZodString>;
|
|
146
|
+
appendSystemPrompt: z.ZodOptional<z.ZodString>;
|
|
140
147
|
instructions: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
141
148
|
}, z.core.$strict>;
|
|
142
149
|
export declare const agentProfilePublicConfigValueSchema: z.ZodObject<{
|
|
@@ -244,6 +251,7 @@ export declare const agentProfileConnectionSchema: z.ZodObject<{
|
|
|
244
251
|
}, z.core.$strict>;
|
|
245
252
|
export declare const agentProfilePromptRemovalSchema: z.ZodObject<{
|
|
246
253
|
systemPrompt: z.ZodOptional<z.ZodLiteral<true>>;
|
|
254
|
+
appendSystemPrompt: z.ZodOptional<z.ZodLiteral<true>>;
|
|
247
255
|
instructions: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodArray<z.ZodString>]>>;
|
|
248
256
|
}, z.core.$strict>;
|
|
249
257
|
export declare const agentProfileResourceRemovalSchema: z.ZodObject<{
|
|
@@ -260,6 +268,7 @@ export declare const agentProfileDiffRemovalSchema: z.ZodObject<{
|
|
|
260
268
|
tags: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodArray<z.ZodString>]>>;
|
|
261
269
|
prompt: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodObject<{
|
|
262
270
|
systemPrompt: z.ZodOptional<z.ZodLiteral<true>>;
|
|
271
|
+
appendSystemPrompt: z.ZodOptional<z.ZodLiteral<true>>;
|
|
263
272
|
instructions: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodArray<z.ZodString>]>>;
|
|
264
273
|
}, z.core.$strict>]>>;
|
|
265
274
|
model: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodArray<z.ZodString>]>>;
|
|
@@ -296,6 +305,7 @@ export declare const agentProfileSchema: z.ZodObject<{
|
|
|
296
305
|
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
297
306
|
prompt: z.ZodOptional<z.ZodObject<{
|
|
298
307
|
systemPrompt: z.ZodOptional<z.ZodString>;
|
|
308
|
+
appendSystemPrompt: z.ZodOptional<z.ZodString>;
|
|
299
309
|
instructions: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
300
310
|
}, z.core.$strict>>;
|
|
301
311
|
model: z.ZodOptional<z.ZodObject<{
|
|
@@ -320,6 +330,7 @@ export declare const agentProfileSchema: z.ZodObject<{
|
|
|
320
330
|
opencode: "opencode";
|
|
321
331
|
"kimi-code": "kimi-code";
|
|
322
332
|
pi: "pi";
|
|
333
|
+
prime: "prime";
|
|
323
334
|
gemini: "gemini";
|
|
324
335
|
hermes: "hermes";
|
|
325
336
|
openclaw: "openclaw";
|
package/dist/profile-schema.js
CHANGED
|
@@ -98,8 +98,15 @@ export const agentProfileModelHintsSchema = z.strictObject({
|
|
|
98
98
|
reasoningEffort: reasoningEffortSchema.optional(),
|
|
99
99
|
metadata: ownPropertyRecordSchema(z.unknown()).optional(),
|
|
100
100
|
});
|
|
101
|
+
/**
|
|
102
|
+
* Replacement and addition are separate, independently optional fields, and the
|
|
103
|
+
* pair is admitted on purpose: the effective prompt is `systemPrompt` followed
|
|
104
|
+
* by `appendSystemPrompt`. No cross-field refinement rejects the combination,
|
|
105
|
+
* because {@link mergeAgentProfiles} can produce it from two valid profiles.
|
|
106
|
+
*/
|
|
101
107
|
export const agentProfilePromptSchema = z.strictObject({
|
|
102
108
|
systemPrompt: z.string().optional(),
|
|
109
|
+
appendSystemPrompt: z.string().optional(),
|
|
103
110
|
instructions: z.array(z.string()).optional(),
|
|
104
111
|
});
|
|
105
112
|
const controlCharacterPattern = /[\u0000-\u001f\u007f]/;
|
|
@@ -245,6 +252,7 @@ export const agentProfileConnectionSchema = z.strictObject({
|
|
|
245
252
|
const removeListSchema = z.union([z.literal(true), z.array(z.string().min(1))]);
|
|
246
253
|
export const agentProfilePromptRemovalSchema = z.strictObject({
|
|
247
254
|
systemPrompt: z.literal(true).optional(),
|
|
255
|
+
appendSystemPrompt: z.literal(true).optional(),
|
|
248
256
|
instructions: removeListSchema.optional(),
|
|
249
257
|
});
|
|
250
258
|
export const agentProfileResourceRemovalSchema = z.strictObject({
|
|
@@ -347,6 +355,17 @@ export const agentProfileDiffSchema = z.strictObject({
|
|
|
347
355
|
});
|
|
348
356
|
const _agentProfileSchemaMatchesInterface = true;
|
|
349
357
|
void _agentProfileSchemaMatchesInterface;
|
|
358
|
+
// Plain assignability cannot see a missing OPTIONAL field: an object type
|
|
359
|
+
// without `x?` is assignable to one with it, in both directions. Since nearly
|
|
360
|
+
// every profile field is optional, comparing the same shapes with optionality
|
|
361
|
+
// removed is what actually catches a field added to one side only — and a field
|
|
362
|
+
// missing from this strict schema means valid profiles get rejected at runtime.
|
|
363
|
+
// Applied at the top level and again inside the prompt, whose two intents are
|
|
364
|
+
// distinct enough that losing one is a silent semantic change, not a parse error.
|
|
365
|
+
const _agentProfileSchemaFieldsMatchInterface = true;
|
|
366
|
+
void _agentProfileSchemaFieldsMatchInterface;
|
|
367
|
+
const _agentProfilePromptSchemaFieldsMatchInterface = true;
|
|
368
|
+
void _agentProfilePromptSchemaFieldsMatchInterface;
|
|
350
369
|
export const capabilitySchema = z.strictObject({
|
|
351
370
|
id: z.string().min(1),
|
|
352
371
|
definition: agentProfileSchema,
|