@tangle-network/agent-profile-materialize 0.11.0 → 0.13.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/dist/index.d.ts +220 -28
- package/dist/index.js +644 -239
- package/dist/prime-fork.d.ts +192 -0
- package/dist/prime-fork.js +303 -0
- package/package.json +1 -1
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fork FACTS for prime-agent (PrimeIntellect-ai/prime-agent) — the constants,
|
|
3
|
+
* dialects and refusal reasons every executor that drives the fork needs, with
|
|
4
|
+
* no `node:fs` and no executor assumptions. The agent-dir MATERIALIZATION that
|
|
5
|
+
* consumes them lives in `./index.ts`; both consumers (cli-bridge's host-spawn
|
|
6
|
+
* backend on `--mode rpc`, sdk-provider-prime's in-sandbox adapter on
|
|
7
|
+
* `--mode acp`) read these instead of each keeping a private copy, because a
|
|
8
|
+
* fork bump must move one file, not two repositories.
|
|
9
|
+
*
|
|
10
|
+
* Every value here is verified against the pinned clone named by
|
|
11
|
+
* {@link PRIME_FORK_PIN}; `tests/prime-fork-pin.test.ts` re-asserts them
|
|
12
|
+
* against that clone when it is present, so a fork bump fails loudly in one
|
|
13
|
+
* place.
|
|
14
|
+
*/
|
|
15
|
+
/** The prime-agent clone every constant in this module was read from. */
|
|
16
|
+
export declare const PRIME_FORK_PIN: {
|
|
17
|
+
readonly repo: "PrimeIntellect-ai/prime-agent";
|
|
18
|
+
readonly version: "0.7.0";
|
|
19
|
+
readonly commit: "be9e2fa0";
|
|
20
|
+
/** Local clone used by the fork-pin test when it exists. */
|
|
21
|
+
readonly clonePath: "/home/drew/bench-cache/prime-agent";
|
|
22
|
+
/** Package root inside the clone that the file:line citations are relative to. */
|
|
23
|
+
readonly packageRoot: "packages/coding-agent/src";
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* The fork's package identity, and why it cannot be installed from the npm
|
|
27
|
+
* registry.
|
|
28
|
+
*
|
|
29
|
+
* `@earendil-works/pi-coding-agent` on registry.npmjs.org serves the UPSTREAM
|
|
30
|
+
* pi line (0.84.x); the fork's 0.7.x versions were never published there. The
|
|
31
|
+
* two are wire-incompatible — different daemon schema revision, different
|
|
32
|
+
* config dir, different agent-dir env var (see
|
|
33
|
+
* {@link PRIME_AGENT_DIR_ENV_VARS}) — so `npm install -g` under that name
|
|
34
|
+
* yields an agent none of this lowering was verified against. The fork must be
|
|
35
|
+
* built from source and exposed under a `prime-agent` command name; its own
|
|
36
|
+
* `bin` entry is `pi` (fork package.json `bin`), which is exactly how the wrong
|
|
37
|
+
* binary ends up on `PATH` under the right-looking name.
|
|
38
|
+
*/
|
|
39
|
+
export declare const PRIME_FORK_INSTALL: {
|
|
40
|
+
readonly npmName: "@earendil-works/pi-coding-agent";
|
|
41
|
+
/** The fork's own bin name — deliberately NOT the command to install it as. */
|
|
42
|
+
readonly forkBinName: "pi";
|
|
43
|
+
/** Command name an executor's binary path is expected to resolve to. */
|
|
44
|
+
readonly expectedCommandName: "prime-agent";
|
|
45
|
+
readonly sourceRepo: "https://github.com/PrimeIntellect-ai/prime-agent";
|
|
46
|
+
/** Built entrypoint to expose as `prime-agent`. */
|
|
47
|
+
readonly sourceEntrypoint: "packages/coding-agent/dist/bundle/cli.js";
|
|
48
|
+
readonly reason: string;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Env vars the agent resolves its config dir from, most-specific first.
|
|
52
|
+
*
|
|
53
|
+
* The fork derives the name from its own package identity —
|
|
54
|
+
* `ENV_AGENT_DIR = \`${envPrefix}_CODING_AGENT_DIR\`` where `envPrefix` comes
|
|
55
|
+
* from `package.json` `piConfig.name` (config.ts:485-498) — so the fork reads
|
|
56
|
+
* `PRIME_AGENT_CODING_AGENT_DIR` and upstream pi reads `PI_CODING_AGENT_DIR`.
|
|
57
|
+
* An executor that sets only the fork's name has its isolation SILENTLY
|
|
58
|
+
* disabled when the binary on PATH turns out to be upstream pi: `getAgentDir()`
|
|
59
|
+
* falls back to `homedir()/<CONFIG_DIR_NAME>` (config.ts:520-527), one shared
|
|
60
|
+
* dir — and therefore one shared self-modifying `harness_state.json` — for
|
|
61
|
+
* every session on the box. Setting BOTH names costs nothing and keeps the
|
|
62
|
+
* isolation fail-closed under either binary.
|
|
63
|
+
*/
|
|
64
|
+
export declare const PRIME_AGENT_DIR_ENV_VARS: readonly ["PRIME_AGENT_CODING_AGENT_DIR", "PI_CODING_AGENT_DIR"];
|
|
65
|
+
/** Canonical agent-dir env var for the fork itself (config.ts:498). */
|
|
66
|
+
export declare const PRIME_AGENT_DIR_ENV_VAR: "PRIME_AGENT_CODING_AGENT_DIR";
|
|
67
|
+
/** Pin `agentDir` under every name the agent could resolve its config dir from. */
|
|
68
|
+
export declare function primeAgentDirEnv(agentDir: string): Record<string, string>;
|
|
69
|
+
/**
|
|
70
|
+
* Operator knobs an executor forwards into the agent when they are set.
|
|
71
|
+
*
|
|
72
|
+
* `PRIME_AGENT_KERNEL_VENV` is load-bearing for any executor that also pins
|
|
73
|
+
* HOME: the kernel venv defaults under `homedir()/.prime/agent/kernel-venv`
|
|
74
|
+
* (kernel/bootstrap.ts:331-342), so a fresh HOME re-bootstraps it through uv on
|
|
75
|
+
* every run unless the operator's venv path is forwarded
|
|
76
|
+
* (bootstrap.ts:340 override, :358 branch).
|
|
77
|
+
*/
|
|
78
|
+
export declare const PRIME_KERNEL_ENV_PASSTHROUGH: readonly ["PRIME_AGENT_INSTALL_UV", "PRIME_AGENT_KERNEL_PYTHON", "PRIME_AGENT_KERNEL_VENV"];
|
|
79
|
+
/**
|
|
80
|
+
* Env vars an executor must NEVER forward from its own environment.
|
|
81
|
+
*
|
|
82
|
+
* These re-point the self-modifying continual-harness store OUTSIDE the agent
|
|
83
|
+
* dir (agent-session.ts:8805 hands the kernel its store location), which
|
|
84
|
+
* defeats per-session isolation without changing anything an executor inspects.
|
|
85
|
+
* Isolation is the fail-closed default; sharing state is an explicit opt-in
|
|
86
|
+
* that names a directory, never an inherited env var.
|
|
87
|
+
*/
|
|
88
|
+
export declare const PRIME_HARNESS_STATE_ENV_DENYLIST: readonly ["RLM_GLOBAL_HARNESS_STATE_DIR", "RLM_HARNESS_STATE_DIR"];
|
|
89
|
+
/**
|
|
90
|
+
* A prime-agent IPython kernel that dies is never re-provisioned headlessly —
|
|
91
|
+
* every later `ipython` call fails with one of these markers until the process
|
|
92
|
+
* is replaced. An executor that cannot recognize them either reports a generic
|
|
93
|
+
* turn failure or burns its whole timeout against a permanently dead tool.
|
|
94
|
+
*
|
|
95
|
+
* Verified in `core/kernel/index.ts`: :723 ("forked kernel exited
|
|
96
|
+
* unexpectedly"), :750 ("Kernel exited before resolving ports"), :779 ("Kernel
|
|
97
|
+
* exited during startup"), :820/:837/:1172/:1295 ("Kernel has been shut down").
|
|
98
|
+
*/
|
|
99
|
+
export declare const PRIME_KERNEL_DEAD_MARKERS: readonly string[];
|
|
100
|
+
/** The first kernel-death marker present in `text`, or undefined. */
|
|
101
|
+
export declare function matchPrimeKernelDeadMarker(text: string): string | undefined;
|
|
102
|
+
/**
|
|
103
|
+
* `--thinking` rungs the fork's argument parser accepts
|
|
104
|
+
* (cli/args.ts:62 `VALID_THINKING_LEVELS`, rejected at :175). Read from the
|
|
105
|
+
* fork itself rather than borrowed from upstream pi's measured table: the two
|
|
106
|
+
* agreeing today is a coincidence this module must not depend on.
|
|
107
|
+
*/
|
|
108
|
+
export declare const PRIME_THINKING_LEVELS: readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
109
|
+
export type PrimeThinkingLevel = (typeof PRIME_THINKING_LEVELS)[number];
|
|
110
|
+
/**
|
|
111
|
+
* Map the portable reasoning ladder onto the fork's `--thinking` value.
|
|
112
|
+
*
|
|
113
|
+
* Throws on an effort with no rung instead of returning undefined: omitting the
|
|
114
|
+
* flag runs the agent at its default thinking level while the caller believes
|
|
115
|
+
* the requested effort is in force, which is a silently dropped control.
|
|
116
|
+
*/
|
|
117
|
+
export declare function primeThinkingLevel(effort: string | null | undefined): PrimeThinkingLevel | undefined;
|
|
118
|
+
/**
|
|
119
|
+
* Why `profile.mcp` (and any hub connection that resolves to MCP) is refused.
|
|
120
|
+
*
|
|
121
|
+
* The fork's `mcpServers` settings surface IS read at startup
|
|
122
|
+
* (settings-manager.ts:98-120,219) but a user-declared server never becomes
|
|
123
|
+
* model-visible: the host McpManager registers OAuth providers and serves
|
|
124
|
+
* mcp.config/refresh host-requests only (core/mcp/mcp-manager.ts:62-124),
|
|
125
|
+
* skips stdio servers entirely (mcp-manager.ts:73), and nothing generates a
|
|
126
|
+
* skill or prompt section for one. Model-visible MCP is exactly the built-in
|
|
127
|
+
* catalog skills, which are hand-written Python `McpIntegration` subclasses
|
|
128
|
+
* (prime-agent-runtime/src/rlm/mcp_base.py:112-125; only linear and notion
|
|
129
|
+
* ship). Materializing a per-run entry would run the agent WITHOUT the
|
|
130
|
+
* requested tools while the config on disk says otherwise.
|
|
131
|
+
*/
|
|
132
|
+
export declare const PRIME_MCP_UNSUPPORTED_REASON: string;
|
|
133
|
+
/** Which half of a profile refusal this is: a fork limit, or a bad declaration. */
|
|
134
|
+
export type PrimeProfileErrorKind = "unsupported" | "invalid";
|
|
135
|
+
/**
|
|
136
|
+
* Refusal raised by the shared prime lowering, carrying the control and the
|
|
137
|
+
* reason separately so each executor can render them in its own error contract
|
|
138
|
+
* (an HTTP backend's typed `BackendError`, an SDK adapter's plain `Error`)
|
|
139
|
+
* without the reason text — which is where the fork evidence lives — being
|
|
140
|
+
* rewritten per repository.
|
|
141
|
+
*/
|
|
142
|
+
export declare class PrimeProfileError extends Error {
|
|
143
|
+
readonly kind: PrimeProfileErrorKind;
|
|
144
|
+
readonly control: string;
|
|
145
|
+
readonly reason: string;
|
|
146
|
+
constructor(kind: PrimeProfileErrorKind, control: string, reason: string);
|
|
147
|
+
}
|
|
148
|
+
/** Raise the "the fork cannot honor this control" half of the refusal. */
|
|
149
|
+
export declare function rejectPrimeControl(control: string, reason: string): never;
|
|
150
|
+
/** Raise the "this declaration is malformed" half of the refusal. */
|
|
151
|
+
export declare function rejectPrimeDeclaration(control: string, reason: string): never;
|
|
152
|
+
/**
|
|
153
|
+
* The fork's continual-harness store exists but no longer parses.
|
|
154
|
+
*
|
|
155
|
+
* The store is the agent's own self-modification state — refinements, learned
|
|
156
|
+
* prompts, subagents it created mid-session — so an executor that reseeds it
|
|
157
|
+
* from empty destroys evidence and silently mutates the thing under study. The
|
|
158
|
+
* turn is failed instead; the file is the operator's to repair or remove.
|
|
159
|
+
*/
|
|
160
|
+
export declare class PrimeHarnessStateError extends Error {
|
|
161
|
+
readonly statePath: string;
|
|
162
|
+
constructor(statePath: string, detail: string);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Strip `//` line comments and trailing commas, leaving string literals
|
|
166
|
+
* untouched — byte-for-byte the fork's own `stripJsonComments`
|
|
167
|
+
* (core/model-registry.ts), so a models.json the agent accepts is never refused
|
|
168
|
+
* by an executor and vice versa.
|
|
169
|
+
*/
|
|
170
|
+
export declare function stripPrimeJsonComments(input: string): string;
|
|
171
|
+
/**
|
|
172
|
+
* Parse a models.json with the fork's own laxness and assert the one structural
|
|
173
|
+
* invariant it requires (a `providers` object). Throws naming `sourcePath`.
|
|
174
|
+
*
|
|
175
|
+
* This is the SHAPE and DIALECT only. Producing the file is deliberately not
|
|
176
|
+
* shared: an executor holding real operator credentials copies an
|
|
177
|
+
* operator-authored file and forwards only the env var names its `apiKey`
|
|
178
|
+
* entries reference, while an in-sandbox executor synthesizes a one-provider
|
|
179
|
+
* entry with a literal key that is a proxy dummy. Same schema, opposite threat
|
|
180
|
+
* models.
|
|
181
|
+
*/
|
|
182
|
+
export declare function parsePrimeModelsJson(text: string, sourcePath: string): Record<string, unknown>;
|
|
183
|
+
/**
|
|
184
|
+
* Environment variables a models.json names as `apiKey` sources.
|
|
185
|
+
*
|
|
186
|
+
* prime resolves an `apiKey` value env-var first and LITERAL second
|
|
187
|
+
* (core/resolve-config-value.ts), so an executor that forwards exactly the
|
|
188
|
+
* named variables lets an operator keep secrets out of the file — and an
|
|
189
|
+
* executor that forwards NONE of them silently turns the variable name itself
|
|
190
|
+
* into the credential string.
|
|
191
|
+
*/
|
|
192
|
+
export declare function primeApiKeyEnvNames(modelsConfig: Record<string, unknown>): string[];
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fork FACTS for prime-agent (PrimeIntellect-ai/prime-agent) — the constants,
|
|
3
|
+
* dialects and refusal reasons every executor that drives the fork needs, with
|
|
4
|
+
* no `node:fs` and no executor assumptions. The agent-dir MATERIALIZATION that
|
|
5
|
+
* consumes them lives in `./index.ts`; both consumers (cli-bridge's host-spawn
|
|
6
|
+
* backend on `--mode rpc`, sdk-provider-prime's in-sandbox adapter on
|
|
7
|
+
* `--mode acp`) read these instead of each keeping a private copy, because a
|
|
8
|
+
* fork bump must move one file, not two repositories.
|
|
9
|
+
*
|
|
10
|
+
* Every value here is verified against the pinned clone named by
|
|
11
|
+
* {@link PRIME_FORK_PIN}; `tests/prime-fork-pin.test.ts` re-asserts them
|
|
12
|
+
* against that clone when it is present, so a fork bump fails loudly in one
|
|
13
|
+
* place.
|
|
14
|
+
*/
|
|
15
|
+
/** The prime-agent clone every constant in this module was read from. */
|
|
16
|
+
export const PRIME_FORK_PIN = {
|
|
17
|
+
repo: "PrimeIntellect-ai/prime-agent",
|
|
18
|
+
version: "0.7.0",
|
|
19
|
+
commit: "be9e2fa0",
|
|
20
|
+
/** Local clone used by the fork-pin test when it exists. */
|
|
21
|
+
clonePath: "/home/drew/bench-cache/prime-agent",
|
|
22
|
+
/** Package root inside the clone that the file:line citations are relative to. */
|
|
23
|
+
packageRoot: "packages/coding-agent/src",
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* The fork's package identity, and why it cannot be installed from the npm
|
|
27
|
+
* registry.
|
|
28
|
+
*
|
|
29
|
+
* `@earendil-works/pi-coding-agent` on registry.npmjs.org serves the UPSTREAM
|
|
30
|
+
* pi line (0.84.x); the fork's 0.7.x versions were never published there. The
|
|
31
|
+
* two are wire-incompatible — different daemon schema revision, different
|
|
32
|
+
* config dir, different agent-dir env var (see
|
|
33
|
+
* {@link PRIME_AGENT_DIR_ENV_VARS}) — so `npm install -g` under that name
|
|
34
|
+
* yields an agent none of this lowering was verified against. The fork must be
|
|
35
|
+
* built from source and exposed under a `prime-agent` command name; its own
|
|
36
|
+
* `bin` entry is `pi` (fork package.json `bin`), which is exactly how the wrong
|
|
37
|
+
* binary ends up on `PATH` under the right-looking name.
|
|
38
|
+
*/
|
|
39
|
+
export const PRIME_FORK_INSTALL = {
|
|
40
|
+
npmName: "@earendil-works/pi-coding-agent",
|
|
41
|
+
/** The fork's own bin name — deliberately NOT the command to install it as. */
|
|
42
|
+
forkBinName: "pi",
|
|
43
|
+
/** Command name an executor's binary path is expected to resolve to. */
|
|
44
|
+
expectedCommandName: "prime-agent",
|
|
45
|
+
sourceRepo: "https://github.com/PrimeIntellect-ai/prime-agent",
|
|
46
|
+
/** Built entrypoint to expose as `prime-agent`. */
|
|
47
|
+
sourceEntrypoint: "packages/coding-agent/dist/bundle/cli.js",
|
|
48
|
+
reason: "registry.npmjs.org serves the upstream pi line under @earendil-works/pi-coding-agent " +
|
|
49
|
+
"(0.84.x); the prime-agent fork's 0.7.x versions do not exist there. Installing from " +
|
|
50
|
+
"the registry yields a wire-incompatible agent with a different daemon schema, a " +
|
|
51
|
+
"different config dir, and a different agent-dir env var. Build from source and expose " +
|
|
52
|
+
"packages/coding-agent/dist/bundle/cli.js as `prime-agent`.",
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Env vars the agent resolves its config dir from, most-specific first.
|
|
56
|
+
*
|
|
57
|
+
* The fork derives the name from its own package identity —
|
|
58
|
+
* `ENV_AGENT_DIR = \`${envPrefix}_CODING_AGENT_DIR\`` where `envPrefix` comes
|
|
59
|
+
* from `package.json` `piConfig.name` (config.ts:485-498) — so the fork reads
|
|
60
|
+
* `PRIME_AGENT_CODING_AGENT_DIR` and upstream pi reads `PI_CODING_AGENT_DIR`.
|
|
61
|
+
* An executor that sets only the fork's name has its isolation SILENTLY
|
|
62
|
+
* disabled when the binary on PATH turns out to be upstream pi: `getAgentDir()`
|
|
63
|
+
* falls back to `homedir()/<CONFIG_DIR_NAME>` (config.ts:520-527), one shared
|
|
64
|
+
* dir — and therefore one shared self-modifying `harness_state.json` — for
|
|
65
|
+
* every session on the box. Setting BOTH names costs nothing and keeps the
|
|
66
|
+
* isolation fail-closed under either binary.
|
|
67
|
+
*/
|
|
68
|
+
export const PRIME_AGENT_DIR_ENV_VARS = [
|
|
69
|
+
"PRIME_AGENT_CODING_AGENT_DIR",
|
|
70
|
+
"PI_CODING_AGENT_DIR",
|
|
71
|
+
];
|
|
72
|
+
/** Canonical agent-dir env var for the fork itself (config.ts:498). */
|
|
73
|
+
export const PRIME_AGENT_DIR_ENV_VAR = PRIME_AGENT_DIR_ENV_VARS[0];
|
|
74
|
+
/** Pin `agentDir` under every name the agent could resolve its config dir from. */
|
|
75
|
+
export function primeAgentDirEnv(agentDir) {
|
|
76
|
+
const env = {};
|
|
77
|
+
for (const name of PRIME_AGENT_DIR_ENV_VARS)
|
|
78
|
+
env[name] = agentDir;
|
|
79
|
+
return env;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Operator knobs an executor forwards into the agent when they are set.
|
|
83
|
+
*
|
|
84
|
+
* `PRIME_AGENT_KERNEL_VENV` is load-bearing for any executor that also pins
|
|
85
|
+
* HOME: the kernel venv defaults under `homedir()/.prime/agent/kernel-venv`
|
|
86
|
+
* (kernel/bootstrap.ts:331-342), so a fresh HOME re-bootstraps it through uv on
|
|
87
|
+
* every run unless the operator's venv path is forwarded
|
|
88
|
+
* (bootstrap.ts:340 override, :358 branch).
|
|
89
|
+
*/
|
|
90
|
+
export const PRIME_KERNEL_ENV_PASSTHROUGH = [
|
|
91
|
+
/** "1" lets a headless first run install uv itself; "0" forbids it. */
|
|
92
|
+
"PRIME_AGENT_INSTALL_UV",
|
|
93
|
+
/** Offline escape hatch: pin the kernel venv python instead of uv-provisioning. */
|
|
94
|
+
"PRIME_AGENT_KERNEL_PYTHON",
|
|
95
|
+
/** Reuse an operator-provisioned kernel venv instead of re-bootstrapping it. */
|
|
96
|
+
"PRIME_AGENT_KERNEL_VENV",
|
|
97
|
+
];
|
|
98
|
+
/**
|
|
99
|
+
* Env vars an executor must NEVER forward from its own environment.
|
|
100
|
+
*
|
|
101
|
+
* These re-point the self-modifying continual-harness store OUTSIDE the agent
|
|
102
|
+
* dir (agent-session.ts:8805 hands the kernel its store location), which
|
|
103
|
+
* defeats per-session isolation without changing anything an executor inspects.
|
|
104
|
+
* Isolation is the fail-closed default; sharing state is an explicit opt-in
|
|
105
|
+
* that names a directory, never an inherited env var.
|
|
106
|
+
*/
|
|
107
|
+
export const PRIME_HARNESS_STATE_ENV_DENYLIST = [
|
|
108
|
+
"RLM_GLOBAL_HARNESS_STATE_DIR",
|
|
109
|
+
"RLM_HARNESS_STATE_DIR",
|
|
110
|
+
];
|
|
111
|
+
/**
|
|
112
|
+
* A prime-agent IPython kernel that dies is never re-provisioned headlessly —
|
|
113
|
+
* every later `ipython` call fails with one of these markers until the process
|
|
114
|
+
* is replaced. An executor that cannot recognize them either reports a generic
|
|
115
|
+
* turn failure or burns its whole timeout against a permanently dead tool.
|
|
116
|
+
*
|
|
117
|
+
* Verified in `core/kernel/index.ts`: :723 ("forked kernel exited
|
|
118
|
+
* unexpectedly"), :750 ("Kernel exited before resolving ports"), :779 ("Kernel
|
|
119
|
+
* exited during startup"), :820/:837/:1172/:1295 ("Kernel has been shut down").
|
|
120
|
+
*/
|
|
121
|
+
export const PRIME_KERNEL_DEAD_MARKERS = [
|
|
122
|
+
"Kernel has been shut down",
|
|
123
|
+
"Kernel exited before resolving ports",
|
|
124
|
+
"Kernel exited during startup",
|
|
125
|
+
"forked kernel exited unexpectedly",
|
|
126
|
+
];
|
|
127
|
+
/** The first kernel-death marker present in `text`, or undefined. */
|
|
128
|
+
export function matchPrimeKernelDeadMarker(text) {
|
|
129
|
+
return PRIME_KERNEL_DEAD_MARKERS.find((marker) => text.includes(marker));
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* `--thinking` rungs the fork's argument parser accepts
|
|
133
|
+
* (cli/args.ts:62 `VALID_THINKING_LEVELS`, rejected at :175). Read from the
|
|
134
|
+
* fork itself rather than borrowed from upstream pi's measured table: the two
|
|
135
|
+
* agreeing today is a coincidence this module must not depend on.
|
|
136
|
+
*/
|
|
137
|
+
export const PRIME_THINKING_LEVELS = [
|
|
138
|
+
"off",
|
|
139
|
+
"minimal",
|
|
140
|
+
"low",
|
|
141
|
+
"medium",
|
|
142
|
+
"high",
|
|
143
|
+
"xhigh",
|
|
144
|
+
"max",
|
|
145
|
+
];
|
|
146
|
+
/** Canonical portable effort → fork `--thinking` rung. */
|
|
147
|
+
const PRIME_THINKING_BY_EFFORT = {
|
|
148
|
+
none: "off",
|
|
149
|
+
minimal: "minimal",
|
|
150
|
+
low: "low",
|
|
151
|
+
medium: "medium",
|
|
152
|
+
high: "high",
|
|
153
|
+
xhigh: "xhigh",
|
|
154
|
+
// The fork carries `max` above `xhigh`, so the ceiling level reaches it.
|
|
155
|
+
ultracode: "max",
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* Map the portable reasoning ladder onto the fork's `--thinking` value.
|
|
159
|
+
*
|
|
160
|
+
* Throws on an effort with no rung instead of returning undefined: omitting the
|
|
161
|
+
* flag runs the agent at its default thinking level while the caller believes
|
|
162
|
+
* the requested effort is in force, which is a silently dropped control.
|
|
163
|
+
*/
|
|
164
|
+
export function primeThinkingLevel(effort) {
|
|
165
|
+
if (effort === undefined || effort === null || effort === "")
|
|
166
|
+
return undefined;
|
|
167
|
+
const level = PRIME_THINKING_BY_EFFORT[effort];
|
|
168
|
+
if (level === undefined) {
|
|
169
|
+
throw new PrimeProfileError("invalid", "model.reasoningEffort", `prime-agent has no --thinking rung for ${JSON.stringify(effort)} ` +
|
|
170
|
+
`(fork cli/args.ts:62 accepts ${PRIME_THINKING_LEVELS.join(", ")}).`);
|
|
171
|
+
}
|
|
172
|
+
return level;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Why `profile.mcp` (and any hub connection that resolves to MCP) is refused.
|
|
176
|
+
*
|
|
177
|
+
* The fork's `mcpServers` settings surface IS read at startup
|
|
178
|
+
* (settings-manager.ts:98-120,219) but a user-declared server never becomes
|
|
179
|
+
* model-visible: the host McpManager registers OAuth providers and serves
|
|
180
|
+
* mcp.config/refresh host-requests only (core/mcp/mcp-manager.ts:62-124),
|
|
181
|
+
* skips stdio servers entirely (mcp-manager.ts:73), and nothing generates a
|
|
182
|
+
* skill or prompt section for one. Model-visible MCP is exactly the built-in
|
|
183
|
+
* catalog skills, which are hand-written Python `McpIntegration` subclasses
|
|
184
|
+
* (prime-agent-runtime/src/rlm/mcp_base.py:112-125; only linear and notion
|
|
185
|
+
* ship). Materializing a per-run entry would run the agent WITHOUT the
|
|
186
|
+
* requested tools while the config on disk says otherwise.
|
|
187
|
+
*/
|
|
188
|
+
export const PRIME_MCP_UNSUPPORTED_REASON = "a user-declared mcpServers entry is read from the fork's settings.json at startup but never " +
|
|
189
|
+
"becomes model-visible: the host only registers OAuth/config for it (fork core/mcp/" +
|
|
190
|
+
"mcp-manager.ts:62-124), skips stdio servers entirely (mcp-manager.ts:73), and generates no " +
|
|
191
|
+
"skill or prompt section for it. Model-visible MCP is exactly the built-in Python " +
|
|
192
|
+
"McpIntegration catalog skills (prime-agent-runtime/src/rlm/mcp_base.py:112-125), which no " +
|
|
193
|
+
"per-run configuration creates — so materializing it would be a silently dropped control.";
|
|
194
|
+
/**
|
|
195
|
+
* Refusal raised by the shared prime lowering, carrying the control and the
|
|
196
|
+
* reason separately so each executor can render them in its own error contract
|
|
197
|
+
* (an HTTP backend's typed `BackendError`, an SDK adapter's plain `Error`)
|
|
198
|
+
* without the reason text — which is where the fork evidence lives — being
|
|
199
|
+
* rewritten per repository.
|
|
200
|
+
*/
|
|
201
|
+
export class PrimeProfileError extends Error {
|
|
202
|
+
kind;
|
|
203
|
+
control;
|
|
204
|
+
reason;
|
|
205
|
+
constructor(kind, control, reason) {
|
|
206
|
+
super(kind === "unsupported"
|
|
207
|
+
? `prime does not support AgentProfile ${control}: ${reason}`
|
|
208
|
+
: `prime cannot accept AgentProfile ${control}: ${reason}`);
|
|
209
|
+
this.name = "PrimeProfileError";
|
|
210
|
+
this.kind = kind;
|
|
211
|
+
this.control = control;
|
|
212
|
+
this.reason = reason;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/** Raise the "the fork cannot honor this control" half of the refusal. */
|
|
216
|
+
export function rejectPrimeControl(control, reason) {
|
|
217
|
+
throw new PrimeProfileError("unsupported", control, reason);
|
|
218
|
+
}
|
|
219
|
+
/** Raise the "this declaration is malformed" half of the refusal. */
|
|
220
|
+
export function rejectPrimeDeclaration(control, reason) {
|
|
221
|
+
throw new PrimeProfileError("invalid", control, reason);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* The fork's continual-harness store exists but no longer parses.
|
|
225
|
+
*
|
|
226
|
+
* The store is the agent's own self-modification state — refinements, learned
|
|
227
|
+
* prompts, subagents it created mid-session — so an executor that reseeds it
|
|
228
|
+
* from empty destroys evidence and silently mutates the thing under study. The
|
|
229
|
+
* turn is failed instead; the file is the operator's to repair or remove.
|
|
230
|
+
*/
|
|
231
|
+
export class PrimeHarnessStateError extends Error {
|
|
232
|
+
statePath;
|
|
233
|
+
constructor(statePath, detail) {
|
|
234
|
+
super(`prime harness state ${statePath} is not valid JSON and may hold agent self-modification ` +
|
|
235
|
+
`state; refusing to overwrite it — repair or remove the file to continue (${detail})`);
|
|
236
|
+
this.name = "PrimeHarnessStateError";
|
|
237
|
+
this.statePath = statePath;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Strip `//` line comments and trailing commas, leaving string literals
|
|
242
|
+
* untouched — byte-for-byte the fork's own `stripJsonComments`
|
|
243
|
+
* (core/model-registry.ts), so a models.json the agent accepts is never refused
|
|
244
|
+
* by an executor and vice versa.
|
|
245
|
+
*/
|
|
246
|
+
export function stripPrimeJsonComments(input) {
|
|
247
|
+
return input
|
|
248
|
+
.replace(/"(?:\\.|[^"\\])*"|\/\/[^\n]*/g, (m) => (m[0] === '"' ? m : ""))
|
|
249
|
+
.replace(/"(?:\\.|[^"\\])*"|,(\s*[}\]])/g, (m, tail) => tail !== undefined ? tail : m[0] === '"' ? m : "");
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Parse a models.json with the fork's own laxness and assert the one structural
|
|
253
|
+
* invariant it requires (a `providers` object). Throws naming `sourcePath`.
|
|
254
|
+
*
|
|
255
|
+
* This is the SHAPE and DIALECT only. Producing the file is deliberately not
|
|
256
|
+
* shared: an executor holding real operator credentials copies an
|
|
257
|
+
* operator-authored file and forwards only the env var names its `apiKey`
|
|
258
|
+
* entries reference, while an in-sandbox executor synthesizes a one-provider
|
|
259
|
+
* entry with a literal key that is a proxy dummy. Same schema, opposite threat
|
|
260
|
+
* models.
|
|
261
|
+
*/
|
|
262
|
+
export function parsePrimeModelsJson(text, sourcePath) {
|
|
263
|
+
let parsed;
|
|
264
|
+
try {
|
|
265
|
+
parsed = JSON.parse(stripPrimeJsonComments(text));
|
|
266
|
+
}
|
|
267
|
+
catch (err) {
|
|
268
|
+
throw new Error(`prime models.json ${sourcePath} is not valid JSON: ${err.message}`);
|
|
269
|
+
}
|
|
270
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
271
|
+
throw new Error(`prime models.json ${sourcePath} must be a JSON object`);
|
|
272
|
+
}
|
|
273
|
+
const providers = parsed.providers;
|
|
274
|
+
if (!providers || typeof providers !== "object" || Array.isArray(providers)) {
|
|
275
|
+
throw new Error(`prime models.json ${sourcePath} must carry a "providers" object (prime's models.json schema)`);
|
|
276
|
+
}
|
|
277
|
+
return parsed;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Environment variables a models.json names as `apiKey` sources.
|
|
281
|
+
*
|
|
282
|
+
* prime resolves an `apiKey` value env-var first and LITERAL second
|
|
283
|
+
* (core/resolve-config-value.ts), so an executor that forwards exactly the
|
|
284
|
+
* named variables lets an operator keep secrets out of the file — and an
|
|
285
|
+
* executor that forwards NONE of them silently turns the variable name itself
|
|
286
|
+
* into the credential string.
|
|
287
|
+
*/
|
|
288
|
+
export function primeApiKeyEnvNames(modelsConfig) {
|
|
289
|
+
const names = [];
|
|
290
|
+
const providers = modelsConfig.providers;
|
|
291
|
+
if (!providers || typeof providers !== "object")
|
|
292
|
+
return names;
|
|
293
|
+
for (const provider of Object.values(providers)) {
|
|
294
|
+
if (!provider || typeof provider !== "object")
|
|
295
|
+
continue;
|
|
296
|
+
const key = provider.apiKey;
|
|
297
|
+
if (typeof key !== "string" || !/^[A-Z_][A-Z0-9_]*$/.test(key))
|
|
298
|
+
continue;
|
|
299
|
+
if (!names.includes(key))
|
|
300
|
+
names.push(key);
|
|
301
|
+
}
|
|
302
|
+
return names;
|
|
303
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-profile-materialize",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Materialize an AgentProfile into the native per-harness workspace files (the one shared materializer for cli-bridge, agent-dev-container, and VB).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|