@arnilo/prism 0.0.1 → 0.0.3
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/CHANGELOG.md +19 -2
- package/README.md +17 -7
- package/dist/agent-definitions.d.ts +12 -0
- package/dist/agent-definitions.js +131 -0
- package/dist/agent-loops.d.ts +14 -0
- package/dist/agent-loops.js +161 -0
- package/dist/agents.js +263 -76
- package/dist/cache-helpers.d.ts +28 -0
- package/dist/cache-helpers.js +73 -0
- package/dist/cli-runner.d.ts +38 -2
- package/dist/cli-runner.js +167 -5
- package/dist/compaction.js +2 -0
- package/dist/config.js +47 -12
- package/dist/contracts.d.ts +581 -6
- package/dist/contracts.js +41 -1
- package/dist/contribution-parsing.d.ts +19 -0
- package/dist/contribution-parsing.js +124 -0
- package/dist/contributions.d.ts +13 -3
- package/dist/contributions.js +96 -20
- package/dist/extensions.js +3 -0
- package/dist/index.d.ts +19 -9
- package/dist/index.js +10 -4
- package/dist/input.d.ts +7 -1
- package/dist/input.js +52 -11
- package/dist/instruction-injection.d.ts +28 -0
- package/dist/instruction-injection.js +55 -0
- package/dist/manifests.d.ts +1 -1
- package/dist/manifests.js +3 -3
- package/dist/models.d.ts +4 -1
- package/dist/models.js +5 -2
- package/dist/node/agent-definitions.d.ts +98 -0
- package/dist/node/agent-definitions.js +389 -0
- package/dist/node/contribution-discovery.d.ts +17 -0
- package/dist/node/contribution-discovery.js +163 -0
- package/dist/node/instruction-injectors.d.ts +32 -0
- package/dist/node/instruction-injectors.js +72 -0
- package/dist/node/session-store-jsonl.d.ts +1 -1
- package/dist/node/session-store-jsonl.js +42 -4
- package/dist/node/system-project-prompts.d.ts +30 -0
- package/dist/node/system-project-prompts.js +53 -0
- package/dist/provider-events.d.ts +3 -1
- package/dist/provider-events.js +34 -0
- package/dist/provider-request-policy.js +15 -1
- package/dist/providers/openai-compatible.js +1 -1
- package/dist/providers.d.ts +6 -2
- package/dist/providers.js +15 -1
- package/dist/redaction.d.ts +2 -1
- package/dist/redaction.js +3 -0
- package/dist/registry-options.d.ts +5 -0
- package/dist/registry-options.js +5 -0
- package/dist/rpc.d.ts +6 -2
- package/dist/rpc.js +71 -13
- package/dist/session-stores.d.ts +3 -1
- package/dist/session-stores.js +67 -6
- package/dist/skills.d.ts +4 -1
- package/dist/skills.js +3 -1
- package/dist/system-prompts.js +6 -2
- package/dist/testing/compaction-conformance.d.ts +17 -0
- package/dist/testing/compaction-conformance.js +61 -0
- package/dist/testing/extension-conformance.d.ts +26 -0
- package/dist/testing/extension-conformance.js +55 -0
- package/dist/testing/provider-conformance.d.ts +7 -0
- package/dist/testing/provider-conformance.js +18 -31
- package/dist/testing/session-store-conformance.d.ts +20 -0
- package/dist/testing/session-store-conformance.js +92 -0
- package/dist/testing/tool-conformance.d.ts +39 -0
- package/dist/testing/tool-conformance.js +79 -0
- package/dist/tools.d.ts +7 -2
- package/dist/tools.js +50 -13
- package/docs/agent-definitions.md +251 -0
- package/docs/agent-events.md +199 -0
- package/docs/agent-loops.md +217 -0
- package/docs/agent-session-runtime.md +20 -8
- package/docs/cli-rpc.md +39 -4
- package/docs/coding-agent-tools.md +208 -0
- package/docs/compaction-and-retry.md +2 -2
- package/docs/compaction-conformance.md +76 -0
- package/docs/compaction-llm.md +6 -3
- package/docs/compaction-observational-memory.md +4 -4
- package/docs/configuration-and-manifests.md +6 -1
- package/docs/context-and-skills.md +79 -6
- package/docs/contribution-discovery.md +149 -0
- package/docs/contribution-registries.md +9 -6
- package/docs/credentials-and-redaction.md +2 -0
- package/docs/customization.md +191 -0
- package/docs/database-persistence.md +407 -0
- package/docs/extension-authoring.md +193 -0
- package/docs/extension-conformance.md +80 -0
- package/docs/extensions.md +6 -0
- package/docs/host-security.md +141 -0
- package/docs/index.md +41 -19
- package/docs/input-and-prompt-assembly.md +19 -3
- package/docs/instruction-injection.md +183 -0
- package/docs/migration.md +201 -0
- package/docs/model-registry.md +122 -0
- package/docs/node-jsonl-session-store.md +5 -4
- package/docs/performance.md +127 -0
- package/docs/provider-caching.md +206 -0
- package/docs/provider-conformance.md +32 -5
- package/docs/provider-layer.md +51 -11
- package/docs/provider-packages.md +65 -5
- package/docs/provider-request-policies.md +113 -0
- package/docs/providers/kimi.md +22 -0
- package/docs/providers/neuralwatt.md +388 -0
- package/docs/providers/openai-compatible.md +1 -0
- package/docs/providers/openai.md +21 -0
- package/docs/providers/opencode-go.md +31 -3
- package/docs/providers/openrouter.md +29 -0
- package/docs/providers/zai.md +17 -0
- package/docs/public-contracts.md +87 -12
- package/docs/release-and-install.md +79 -27
- package/docs/runs-and-usage.md +236 -0
- package/docs/session-store-conformance.md +78 -0
- package/docs/session-stores-and-branching.md +10 -6
- package/docs/session-stores.md +126 -0
- package/docs/settings-auth-trust-security.md +18 -4
- package/docs/structured-output.md +247 -0
- package/docs/system-prompts.md +104 -2
- package/docs/tool-conformance.md +87 -0
- package/docs/tools.md +65 -8
- package/package.json +36 -2
package/dist/cli-runner.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Readable, Writable } from "node:stream";
|
|
2
|
-
import type { AgentSession } from "./contracts.js";
|
|
2
|
+
import type { AgentSession, ContributionFileKind, InstructionInjector, Skill, SystemPromptContribution } from "./contracts.js";
|
|
3
|
+
import type { AgentBundle } from "./node/agent-definitions.js";
|
|
3
4
|
import { type RpcSessionFactory } from "./rpc.js";
|
|
4
5
|
export type CliMode = "print" | "json" | "rpc";
|
|
5
6
|
export interface CliOptions {
|
|
@@ -17,6 +18,36 @@ export interface CliOptions {
|
|
|
17
18
|
readonly compact?: number;
|
|
18
19
|
readonly maxToolRounds?: number;
|
|
19
20
|
readonly help: boolean;
|
|
21
|
+
readonly discover: boolean;
|
|
22
|
+
readonly discoverKinds: readonly ContributionFileKind[];
|
|
23
|
+
readonly noDiscovery: boolean;
|
|
24
|
+
/** Parsed flag: `--agents-config <path>` points at the app config root holding
|
|
25
|
+
* `agents/<name>/AGENT.md` bundles. Opt-in; default runs never scan it. */
|
|
26
|
+
readonly agentsConfig?: string;
|
|
27
|
+
/** Runtime-populated (not a parsed flag): agent bundles discovered by `--agents-config`
|
|
28
|
+
* via {@link discoverAgentBundles}. Host-owned resolution. */
|
|
29
|
+
readonly discoveredAgents: readonly AgentBundle[];
|
|
30
|
+
/** Runtime-populated (not a parsed flag): skills discovered by `--discover`,
|
|
31
|
+
* threaded into `createSession` so the bootstrap can build a SkillRegistry. */
|
|
32
|
+
readonly discoveredSkills: readonly Skill[];
|
|
33
|
+
/** Runtime-populated (not a parsed flag): instruction injectors discovered by
|
|
34
|
+
* `--discover`, selectable by name via `--instruction` (Task 8). */
|
|
35
|
+
readonly discoveredInjectors: readonly InstructionInjector[];
|
|
36
|
+
/** Parsed flag: `--instruction <name>` (repeatable). `false` disables injectors. */
|
|
37
|
+
readonly instructions: readonly string[];
|
|
38
|
+
/** Parsed flag: `--injector-file <path>` (repeatable); markdown → static injector. */
|
|
39
|
+
readonly injectorFiles: readonly string[];
|
|
40
|
+
/** Runtime-populated: `--instruction`/`--injector-file` resolved to live injectors. */
|
|
41
|
+
readonly resolvedInstructionInjectors: readonly InstructionInjector[];
|
|
42
|
+
/** Parsed flag: `--no-agents-md` / `--no-system-md` skip the corresponding auto-load. */
|
|
43
|
+
readonly noAgentsMd: boolean;
|
|
44
|
+
readonly noSystemMd: boolean;
|
|
45
|
+
/** Parsed flag: `--agents-md-file <path>` / `--system-md-file <path>` override read paths. */
|
|
46
|
+
readonly agentsMdFile?: string;
|
|
47
|
+
readonly systemMdFile?: string;
|
|
48
|
+
/** Runtime-populated: AGENTS.md/SYSTEM.md layers loaded from disk (print/json modes only;
|
|
49
|
+
* RPC is host-owned). Threaded into `AgentConfig.systemPrompt` by `defaultCreateSession`. */
|
|
50
|
+
readonly systemPromptLayers: readonly SystemPromptContribution[];
|
|
20
51
|
}
|
|
21
52
|
export interface CliRuntime {
|
|
22
53
|
readonly stdin: Readable;
|
|
@@ -24,8 +55,13 @@ export interface CliRuntime {
|
|
|
24
55
|
readonly stderr: Writable;
|
|
25
56
|
readonly createSession?: (options: CliOptions) => AgentSession;
|
|
26
57
|
readonly commands?: RpcSessionFactory["commands"];
|
|
58
|
+
/** Workspace root for `--discover`. Defaults to `process.cwd()`. */
|
|
59
|
+
readonly workspaceRoot?: string;
|
|
60
|
+
/** Optional global root for SYSTEM.md auto-load (host-controlled). No default;
|
|
61
|
+
* the CLI does not auto-touch the user's home directory. */
|
|
62
|
+
readonly globalRoot?: string;
|
|
27
63
|
}
|
|
28
|
-
export declare const usage = "Usage: prism [--mode print|json|rpc] [-p prompt] [options]\n\nOptions:\n -p, --prompt <text> Prompt to run in print/json mode\n --provider <name> Explicit provider id (mock is built in for smoke tests)\n --model <name> Explicit model name\n --session <id> Session id\n --config <path> Explicit config path (recorded, not auto-loaded)\n --resource <uri> Explicit resource URI (recorded, not auto-loaded)\n --extension <name> Explicit extension name (recorded, not auto-loaded)\n --tool <name> Explicit tool name (recorded, not auto-enabled)\n --system <text> System instructions\n --context <text> Context text\n --compact <entries> Auto-compaction threshold\n --max-tool-rounds <n> Maximum tool rounds\n -h, --help Show this help\n";
|
|
64
|
+
export declare const usage = "Usage: prism [--mode print|json|rpc] [-p prompt] [options]\n\nOptions:\n -p, --prompt <text> Prompt to run in print/json mode\n --provider <name> Explicit provider id (mock is built in for smoke tests)\n --model <name> Explicit model name\n --session <id> Session id\n --config <path> Explicit config path (recorded, not auto-loaded)\n --resource <uri> Explicit resource URI (recorded, not auto-loaded)\n --extension <name> Explicit extension name (recorded, not auto-loaded)\n --tool <name> Explicit tool name (recorded, not auto-enabled)\n --system <text> System instructions\n --context <text> Context text\n --compact <entries> Auto-compaction threshold\n --max-tool-rounds <n> Maximum tool rounds\n --discover Enable workspace contribution discovery (opt-in)\n --discover-kinds <csv> Kinds to discover (default: skill; skill,tool,context,instructions)\n --no-discovery Disable discovery even if --discover is set\n --agents-config <path> App config root holding agents/<name>/AGENT.md bundles (opt-in)\n --no-agents-md Skip auto-loading <workspaceRoot>/AGENTS.md\n --no-system-md Skip auto-loading the global SYSTEM.md layer\n --agents-md-file <path> Read AGENTS.md from <path> instead (trust-gated, source: app)\n --system-md-file <path> Read SYSTEM.md from <path> instead (source: user)\n -h, --help Show this help\n";
|
|
29
65
|
export declare function parseCliArgs(argv: readonly string[]): CliOptions;
|
|
30
66
|
export declare function runCli(argv: readonly string[], runtime: CliRuntime): Promise<number>;
|
|
31
67
|
export declare function runPromptMode(session: AgentSession, options: CliOptions, stdout: Writable, mode: "print" | "json"): Promise<void>;
|
package/dist/cli-runner.js
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
import
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { createAgent, createMockProvider, providerDone, providerTextDelta, resolveInstructionInjectors, createContributionRegistry } from "./index.js";
|
|
3
|
+
import { readFile } from "node:fs/promises";
|
|
4
|
+
import { basename, dirname } from "node:path";
|
|
5
|
+
import { createContributionRegistries, registerDiscoveredContributions } from "./contributions.js";
|
|
6
|
+
import { createSkillRegistry } from "./skills.js";
|
|
7
|
+
import { discoverContributions } from "./node/contribution-discovery.js";
|
|
8
|
+
import { discoverAgentBundles } from "./node/agent-definitions.js";
|
|
9
|
+
import { registerDiscoveredInstructionInjectors } from "./node/instruction-injectors.js";
|
|
10
|
+
import { loadSystemPromptFiles } from "./node/system-project-prompts.js";
|
|
11
|
+
import { createPathTrustPolicy } from "./node/trust.js";
|
|
2
12
|
import { runRpcServer } from "./rpc.js";
|
|
3
13
|
export const usage = `Usage: prism [--mode print|json|rpc] [-p prompt] [options]
|
|
4
14
|
|
|
@@ -15,9 +25,19 @@ Options:
|
|
|
15
25
|
--context <text> Context text
|
|
16
26
|
--compact <entries> Auto-compaction threshold
|
|
17
27
|
--max-tool-rounds <n> Maximum tool rounds
|
|
28
|
+
--discover Enable workspace contribution discovery (opt-in)
|
|
29
|
+
--discover-kinds <csv> Kinds to discover (default: skill; skill,tool,context,instructions)
|
|
30
|
+
--no-discovery Disable discovery even if --discover is set
|
|
31
|
+
--agents-config <path> App config root holding agents/<name>/AGENT.md bundles (opt-in)
|
|
32
|
+
--no-agents-md Skip auto-loading <workspaceRoot>/AGENTS.md
|
|
33
|
+
--no-system-md Skip auto-loading the global SYSTEM.md layer
|
|
34
|
+
--agents-md-file <path> Read AGENTS.md from <path> instead (trust-gated, source: app)
|
|
35
|
+
--system-md-file <path> Read SYSTEM.md from <path> instead (source: user)
|
|
18
36
|
-h, --help Show this help
|
|
19
37
|
`;
|
|
20
|
-
const valueFlags = new Set(["--prompt", "--mode", "--provider", "--model", "--session", "--config", "--resource", "--extension", "--tool", "--system", "--context", "--compact", "--max-tool-rounds"]);
|
|
38
|
+
const valueFlags = new Set(["--prompt", "--mode", "--provider", "--model", "--session", "--config", "--resource", "--extension", "--tool", "--system", "--context", "--compact", "--max-tool-rounds", "--discover-kinds", "--instruction", "--injector-file", "--agents-md-file", "--system-md-file", "--agents-config"]);
|
|
39
|
+
const boolFlags = new Set(["--discover", "--no-discovery", "--no-agents-md", "--no-system-md"]);
|
|
40
|
+
const ALL_KINDS = ["skill", "tool", "context", "instructions"];
|
|
21
41
|
export function parseCliArgs(argv) {
|
|
22
42
|
let mode = "print";
|
|
23
43
|
let prompt;
|
|
@@ -28,11 +48,21 @@ export function parseCliArgs(argv) {
|
|
|
28
48
|
let compact;
|
|
29
49
|
let maxToolRounds;
|
|
30
50
|
let help = false;
|
|
51
|
+
let discover = false;
|
|
52
|
+
let discoverKinds = ["skill"];
|
|
53
|
+
let noDiscovery = false;
|
|
54
|
+
let noAgentsMd = false;
|
|
55
|
+
let noSystemMd = false;
|
|
56
|
+
let agentsMdFile;
|
|
57
|
+
let systemMdFile;
|
|
58
|
+
let agentsConfig;
|
|
31
59
|
const config = [];
|
|
32
60
|
const resources = [];
|
|
33
61
|
const extensions = [];
|
|
34
62
|
const tools = [];
|
|
35
63
|
const context = [];
|
|
64
|
+
const instructions = [];
|
|
65
|
+
const injectorFiles = [];
|
|
36
66
|
for (let i = 0; i < argv.length; i += 1) {
|
|
37
67
|
const flag = argv[i];
|
|
38
68
|
if (flag === "-h" || flag === "--help") {
|
|
@@ -40,6 +70,23 @@ export function parseCliArgs(argv) {
|
|
|
40
70
|
continue;
|
|
41
71
|
}
|
|
42
72
|
const name = flag === "-p" ? "--prompt" : flag;
|
|
73
|
+
if (boolFlags.has(name)) {
|
|
74
|
+
switch (name) {
|
|
75
|
+
case "--discover":
|
|
76
|
+
discover = true;
|
|
77
|
+
break;
|
|
78
|
+
case "--no-discovery":
|
|
79
|
+
noDiscovery = true;
|
|
80
|
+
break;
|
|
81
|
+
case "--no-agents-md":
|
|
82
|
+
noAgentsMd = true;
|
|
83
|
+
break;
|
|
84
|
+
case "--no-system-md":
|
|
85
|
+
noSystemMd = true;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
43
90
|
if (!valueFlags.has(name))
|
|
44
91
|
throw new CliUsageError(`Unknown flag: ${flag}`);
|
|
45
92
|
const value = argv[i + 1];
|
|
@@ -88,9 +135,36 @@ export function parseCliArgs(argv) {
|
|
|
88
135
|
case "--max-tool-rounds":
|
|
89
136
|
maxToolRounds = positiveInt(value, flag);
|
|
90
137
|
break;
|
|
138
|
+
case "--discover-kinds":
|
|
139
|
+
discoverKinds = parseKinds(value, flag);
|
|
140
|
+
break;
|
|
141
|
+
case "--instruction":
|
|
142
|
+
instructions.push(value);
|
|
143
|
+
break;
|
|
144
|
+
case "--injector-file":
|
|
145
|
+
injectorFiles.push(value);
|
|
146
|
+
break;
|
|
147
|
+
case "--agents-md-file":
|
|
148
|
+
agentsMdFile = value;
|
|
149
|
+
break;
|
|
150
|
+
case "--system-md-file":
|
|
151
|
+
systemMdFile = value;
|
|
152
|
+
break;
|
|
153
|
+
case "--agents-config":
|
|
154
|
+
agentsConfig = value;
|
|
155
|
+
break;
|
|
91
156
|
}
|
|
92
157
|
}
|
|
93
|
-
return { mode, prompt, provider, model, session, config, resources, extensions, tools, system, context, compact, maxToolRounds, help };
|
|
158
|
+
return { mode, prompt, provider, model, session, config, resources, extensions, tools, system, context, compact, maxToolRounds, help, discover, discoverKinds, noDiscovery, agentsConfig, discoveredSkills: [], discoveredInjectors: [], discoveredAgents: [], instructions, injectorFiles, resolvedInstructionInjectors: [], noAgentsMd, noSystemMd, agentsMdFile, systemMdFile, systemPromptLayers: [] };
|
|
159
|
+
}
|
|
160
|
+
function parseKinds(csv, flag) {
|
|
161
|
+
const kinds = csv.split(",").map((k) => k.trim()).filter(Boolean);
|
|
162
|
+
if (kinds.length === 0)
|
|
163
|
+
throw new CliUsageError(`Invalid value for ${flag}: ${csv}`);
|
|
164
|
+
const invalid = kinds.filter((k) => !ALL_KINDS.includes(k));
|
|
165
|
+
if (invalid.length > 0)
|
|
166
|
+
throw new CliUsageError(`Invalid kinds for ${flag}: ${invalid.join(", ")}`);
|
|
167
|
+
return kinds;
|
|
94
168
|
}
|
|
95
169
|
export async function runCli(argv, runtime) {
|
|
96
170
|
let options;
|
|
@@ -112,8 +186,62 @@ export async function runCli(argv, runtime) {
|
|
|
112
186
|
}
|
|
113
187
|
if (!options.prompt)
|
|
114
188
|
throw new CliUsageError("Missing prompt: use -p or --prompt");
|
|
189
|
+
const mode = options.mode;
|
|
190
|
+
// ponytail: discovery is opt-in. --no-discovery hard-disables; default runs never touch the FS.
|
|
191
|
+
// Registering all kinds honors the plan (tool/context/agent descriptors registered),
|
|
192
|
+
// but only skills are wired into the mock agent here — other kinds are host-owned execution.
|
|
193
|
+
let discoveredSkills = options.discoveredSkills;
|
|
194
|
+
let discoveredInjectors = options.discoveredInjectors;
|
|
195
|
+
let discoveredAgents = options.discoveredAgents;
|
|
196
|
+
const workspaceRoot = runtime.workspaceRoot ?? process.cwd();
|
|
197
|
+
// ponytail: trust policy shared by the repo AGENTS.md loader and the app-config bundle scanner.
|
|
198
|
+
// --agents-config is an explicit opt-in → its configRoot is a trusted root by that act.
|
|
199
|
+
const trustedRoots = [workspaceRoot];
|
|
200
|
+
if (options.agentsConfig)
|
|
201
|
+
trustedRoots.push(options.agentsConfig);
|
|
202
|
+
if (options.agentsMdFile)
|
|
203
|
+
trustedRoots.push(dirname(options.agentsMdFile));
|
|
204
|
+
const trust = createPathTrustPolicy({ trustedRoots });
|
|
205
|
+
if (options.discover && !options.noDiscovery) {
|
|
206
|
+
const discovered = await discoverContributions({
|
|
207
|
+
kinds: options.discoverKinds,
|
|
208
|
+
workspaceRoot,
|
|
209
|
+
});
|
|
210
|
+
const registries = createContributionRegistries();
|
|
211
|
+
registerDiscoveredContributions(registries, discovered);
|
|
212
|
+
discoveredSkills = registries.skills.list();
|
|
213
|
+
// ponytail: Phase 30 — discovered instruction injectors register host-owned (no core auto-import).
|
|
214
|
+
await registerDiscoveredInstructionInjectors(registries, discovered);
|
|
215
|
+
discoveredInjectors = registries.instructionInjectors.list();
|
|
216
|
+
options = { ...options, discoveredSkills, discoveredInjectors };
|
|
217
|
+
}
|
|
218
|
+
// ponytail: Phase 34 — --agents-config discovers app-config agent bundles (envelopes only,
|
|
219
|
+
// no import()/resolve). Host owns turning a bundle into a live agent via resolveAgentBundle.
|
|
220
|
+
if (options.agentsConfig) {
|
|
221
|
+
discoveredAgents = await discoverAgentBundles({ configRoot: options.agentsConfig, trust });
|
|
222
|
+
options = { ...options, discoveredAgents };
|
|
223
|
+
}
|
|
224
|
+
// ponytail: Phase 30 — resolve --instruction names against discovered injectors (fail-closed)
|
|
225
|
+
// and load --injector-file markdown as static injectors. `--instruction false` disables all.
|
|
226
|
+
const resolvedInstructionInjectors = options.instructions.includes("false")
|
|
227
|
+
? []
|
|
228
|
+
: await resolveCliInjectors(options.instructions, options.injectorFiles, discoveredInjectors);
|
|
229
|
+
options = { ...options, resolvedInstructionInjectors };
|
|
230
|
+
// ponytail: Phase 31/34 — auto-load AGENTS.md (trust-gated, source: app) and the global
|
|
231
|
+
// SYSTEM.md layer (source: user) as systemPrompt layers composed on top of `--system`.
|
|
232
|
+
// The CLI no longer defaults globalRoot to the user's home directory; hosts pass globalRoot
|
|
233
|
+
// explicitly or use --agents-config / --system-md-file. RPC mode skips auto-load (host-owned).
|
|
234
|
+
if (options.mode !== "rpc") {
|
|
235
|
+
// ponytail: explicit --agents-md-file opt-in → trust its parent dir too so the named file passes
|
|
236
|
+
// the containment check (the user named it, so it's trusted by that act).
|
|
237
|
+
const layers = await loadSystemPromptFiles({
|
|
238
|
+
...(options.noAgentsMd ? {} : { workspaceRoot, trust, ...(options.agentsMdFile ? { agentsMdPath: options.agentsMdFile } : {}) }),
|
|
239
|
+
...(options.noSystemMd ? {} : { ...(runtime.globalRoot !== undefined ? { globalRoot: runtime.globalRoot } : {}), ...(options.systemMdFile ? { systemMdPath: options.systemMdFile } : {}) }),
|
|
240
|
+
});
|
|
241
|
+
options = { ...options, systemPromptLayers: layers };
|
|
242
|
+
}
|
|
115
243
|
const session = (runtime.createSession ?? defaultCreateSession)(options);
|
|
116
|
-
await runPromptMode(session, options, runtime.stdout,
|
|
244
|
+
await runPromptMode(session, options, runtime.stdout, mode);
|
|
117
245
|
return 0;
|
|
118
246
|
}
|
|
119
247
|
catch (error) {
|
|
@@ -147,14 +275,48 @@ function defaultCreateSession(options) {
|
|
|
147
275
|
if (options.provider !== "mock")
|
|
148
276
|
throw new CliUsageError("No provider configured. Pass --provider mock for a smoke test or embed Prism with an explicit provider.");
|
|
149
277
|
const model = { provider: "mock", model: options.model ?? "mock" };
|
|
150
|
-
return createAgent({
|
|
278
|
+
return createAgent({
|
|
279
|
+
model,
|
|
280
|
+
provider: createMockProvider([providerTextDelta("Hello"), providerDone()]),
|
|
281
|
+
instructions: options.system,
|
|
282
|
+
// ponytail: Phase 31 — file layers compose with `instructions` (base) via the existing
|
|
283
|
+
// composeSystemPrompt pipeline; rank order (user<package<app<run) is enforced inside.
|
|
284
|
+
...(options.systemPromptLayers.length > 0 ? { systemPrompt: options.systemPromptLayers } : {}),
|
|
285
|
+
// ponytail: discovered skills become selectable via RunOptions.activeSkills (set by runOptions below).
|
|
286
|
+
...(options.discoveredSkills.length > 0 ? { skills: createSkillRegistry(options.discoveredSkills) } : {}),
|
|
287
|
+
}).createSession({ id: options.session });
|
|
151
288
|
}
|
|
152
289
|
function runOptions(options) {
|
|
153
290
|
return {
|
|
154
291
|
maxToolRounds: options.maxToolRounds,
|
|
155
292
|
compaction: options.compact ? { thresholdEntries: options.compact } : undefined,
|
|
293
|
+
// ponytail: --discover is the explicit opt-in that activates discovered skills.
|
|
294
|
+
...(options.discover && !options.noDiscovery && options.discoveredSkills.length > 0
|
|
295
|
+
? { activeSkills: options.discoveredSkills.map((s) => s.name) }
|
|
296
|
+
: {}),
|
|
297
|
+
...(options.resolvedInstructionInjectors.length > 0
|
|
298
|
+
? { instructionInjectors: options.resolvedInstructionInjectors }
|
|
299
|
+
: {}),
|
|
156
300
|
};
|
|
157
301
|
}
|
|
302
|
+
// ponytail: resolve --instruction names against discovered injectors (fail-closed) and load
|
|
303
|
+
// --injector-file markdown files as static every_turn injectors. Names route through
|
|
304
|
+
// resolveInstructionInjectors so an unknown name throws (caught by runCli's error handler).
|
|
305
|
+
async function resolveCliInjectors(names, files, discovered) {
|
|
306
|
+
const registry = createContributionRegistry({ label: "instruction injector" });
|
|
307
|
+
for (const inj of discovered)
|
|
308
|
+
registry.register(inj.name, inj);
|
|
309
|
+
const byName = names.length ? resolveInstructionInjectors({ registry, names }) : [];
|
|
310
|
+
const fromFiles = [];
|
|
311
|
+
for (const path of files) {
|
|
312
|
+
const text = await readFile(path, "utf8");
|
|
313
|
+
fromFiles.push({
|
|
314
|
+
name: basename(path).replace(/\.[^.]+$/, ""),
|
|
315
|
+
apply: () => ({ instructions: text, when: "every_turn" }),
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
return [...byName, ...fromFiles];
|
|
319
|
+
}
|
|
158
320
|
function positiveInt(value, flag) {
|
|
159
321
|
const parsed = Number(value);
|
|
160
322
|
if (!Number.isInteger(parsed) || parsed < 0)
|
package/dist/compaction.js
CHANGED
|
@@ -55,6 +55,8 @@ function blockText(block) {
|
|
|
55
55
|
return block.text;
|
|
56
56
|
if (block.type === "tool_call")
|
|
57
57
|
return `[tool_call ${block.name}]`;
|
|
58
|
+
if (block.type === "tool_call_delta")
|
|
59
|
+
return `[tool_call_delta ${block.name ?? block.index}]`;
|
|
58
60
|
if (block.type === "tool_result")
|
|
59
61
|
return `[tool_result ${block.name}]`;
|
|
60
62
|
return "[image]";
|
package/dist/config.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export function isJsonObject(value) {
|
|
2
|
-
return isPlainObject(value) && isJsonValue(value);
|
|
2
|
+
return isPlainObject(value) && isJsonValue(value) && isSafeJsonValue(value);
|
|
3
3
|
}
|
|
4
4
|
export function assertJsonObject(value, label = "value") {
|
|
5
|
-
if (!
|
|
5
|
+
if (!isPlainObject(value) || !isJsonValue(value))
|
|
6
6
|
throw new Error(`${label} must be a JSON object`);
|
|
7
|
+
assertSafeJsonValue(value, label);
|
|
7
8
|
}
|
|
8
9
|
export async function loadConfigLayers(providers, context) {
|
|
9
10
|
const layers = [];
|
|
@@ -24,27 +25,32 @@ export function mergeConfigLayers(layers) {
|
|
|
24
25
|
}
|
|
25
26
|
return merged;
|
|
26
27
|
}
|
|
27
|
-
function mergeObjects(base, override) {
|
|
28
|
-
const result = { ...cloneJsonObject(base) };
|
|
28
|
+
function mergeObjects(base, override, path = "config") {
|
|
29
|
+
const result = { ...cloneJsonObject(base, path) };
|
|
29
30
|
for (const [key, value] of Object.entries(override)) {
|
|
31
|
+
const childPath = `${path}.${key}`;
|
|
32
|
+
assertSafeJsonKey(key, childPath);
|
|
30
33
|
const current = result[key];
|
|
31
34
|
result[key] = isPlainObject(current) && isPlainObject(value)
|
|
32
|
-
? mergeObjects(current, value)
|
|
33
|
-
: cloneJsonValue(value);
|
|
35
|
+
? mergeObjects(current, value, childPath)
|
|
36
|
+
: cloneJsonValue(value, childPath);
|
|
34
37
|
}
|
|
35
38
|
return result;
|
|
36
39
|
}
|
|
37
|
-
function cloneJsonObject(value) {
|
|
40
|
+
function cloneJsonObject(value, path = "value") {
|
|
38
41
|
const result = {};
|
|
39
|
-
for (const [key, entry] of Object.entries(value))
|
|
40
|
-
|
|
42
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
43
|
+
const childPath = `${path}.${key}`;
|
|
44
|
+
assertSafeJsonKey(key, childPath);
|
|
45
|
+
result[key] = cloneJsonValue(entry, childPath);
|
|
46
|
+
}
|
|
41
47
|
return result;
|
|
42
48
|
}
|
|
43
|
-
function cloneJsonValue(value) {
|
|
49
|
+
function cloneJsonValue(value, path = "value") {
|
|
44
50
|
if (Array.isArray(value))
|
|
45
|
-
return value.map(cloneJsonValue);
|
|
51
|
+
return value.map((entry, index) => cloneJsonValue(entry, `${path}[${index}]`));
|
|
46
52
|
if (isPlainObject(value))
|
|
47
|
-
return cloneJsonObject(value);
|
|
53
|
+
return cloneJsonObject(value, path);
|
|
48
54
|
return value;
|
|
49
55
|
}
|
|
50
56
|
function isJsonValue(value) {
|
|
@@ -60,6 +66,35 @@ function isJsonValue(value) {
|
|
|
60
66
|
return Object.values(value).every(isJsonValue);
|
|
61
67
|
return false;
|
|
62
68
|
}
|
|
69
|
+
function isSafeJsonValue(value) {
|
|
70
|
+
try {
|
|
71
|
+
assertSafeJsonValue(value, "value");
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function assertSafeJsonValue(value, path) {
|
|
79
|
+
if (Array.isArray(value)) {
|
|
80
|
+
value.forEach((entry, index) => assertSafeJsonValue(entry, `${path}[${index}]`));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (!isPlainObject(value))
|
|
84
|
+
return;
|
|
85
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
86
|
+
const childPath = `${path}.${key}`;
|
|
87
|
+
assertSafeJsonKey(key, childPath);
|
|
88
|
+
assertSafeJsonValue(entry, childPath);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function assertSafeJsonKey(key, path) {
|
|
92
|
+
if (!isSafeJsonKey(key))
|
|
93
|
+
throw new Error(`${path} uses forbidden JSON key: ${key}`);
|
|
94
|
+
}
|
|
95
|
+
function isSafeJsonKey(key) {
|
|
96
|
+
return key !== "__proto__" && key !== "prototype" && key !== "constructor";
|
|
97
|
+
}
|
|
63
98
|
function isPlainObject(value) {
|
|
64
99
|
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
65
100
|
return false;
|