@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.
Files changed (121) hide show
  1. package/CHANGELOG.md +19 -2
  2. package/README.md +17 -7
  3. package/dist/agent-definitions.d.ts +12 -0
  4. package/dist/agent-definitions.js +131 -0
  5. package/dist/agent-loops.d.ts +14 -0
  6. package/dist/agent-loops.js +161 -0
  7. package/dist/agents.js +263 -76
  8. package/dist/cache-helpers.d.ts +28 -0
  9. package/dist/cache-helpers.js +73 -0
  10. package/dist/cli-runner.d.ts +38 -2
  11. package/dist/cli-runner.js +167 -5
  12. package/dist/compaction.js +2 -0
  13. package/dist/config.js +47 -12
  14. package/dist/contracts.d.ts +581 -6
  15. package/dist/contracts.js +41 -1
  16. package/dist/contribution-parsing.d.ts +19 -0
  17. package/dist/contribution-parsing.js +124 -0
  18. package/dist/contributions.d.ts +13 -3
  19. package/dist/contributions.js +96 -20
  20. package/dist/extensions.js +3 -0
  21. package/dist/index.d.ts +19 -9
  22. package/dist/index.js +10 -4
  23. package/dist/input.d.ts +7 -1
  24. package/dist/input.js +52 -11
  25. package/dist/instruction-injection.d.ts +28 -0
  26. package/dist/instruction-injection.js +55 -0
  27. package/dist/manifests.d.ts +1 -1
  28. package/dist/manifests.js +3 -3
  29. package/dist/models.d.ts +4 -1
  30. package/dist/models.js +5 -2
  31. package/dist/node/agent-definitions.d.ts +98 -0
  32. package/dist/node/agent-definitions.js +389 -0
  33. package/dist/node/contribution-discovery.d.ts +17 -0
  34. package/dist/node/contribution-discovery.js +163 -0
  35. package/dist/node/instruction-injectors.d.ts +32 -0
  36. package/dist/node/instruction-injectors.js +72 -0
  37. package/dist/node/session-store-jsonl.d.ts +1 -1
  38. package/dist/node/session-store-jsonl.js +42 -4
  39. package/dist/node/system-project-prompts.d.ts +30 -0
  40. package/dist/node/system-project-prompts.js +53 -0
  41. package/dist/provider-events.d.ts +3 -1
  42. package/dist/provider-events.js +34 -0
  43. package/dist/provider-request-policy.js +15 -1
  44. package/dist/providers/openai-compatible.js +1 -1
  45. package/dist/providers.d.ts +6 -2
  46. package/dist/providers.js +15 -1
  47. package/dist/redaction.d.ts +2 -1
  48. package/dist/redaction.js +3 -0
  49. package/dist/registry-options.d.ts +5 -0
  50. package/dist/registry-options.js +5 -0
  51. package/dist/rpc.d.ts +6 -2
  52. package/dist/rpc.js +71 -13
  53. package/dist/session-stores.d.ts +3 -1
  54. package/dist/session-stores.js +67 -6
  55. package/dist/skills.d.ts +4 -1
  56. package/dist/skills.js +3 -1
  57. package/dist/system-prompts.js +6 -2
  58. package/dist/testing/compaction-conformance.d.ts +17 -0
  59. package/dist/testing/compaction-conformance.js +61 -0
  60. package/dist/testing/extension-conformance.d.ts +26 -0
  61. package/dist/testing/extension-conformance.js +55 -0
  62. package/dist/testing/provider-conformance.d.ts +7 -0
  63. package/dist/testing/provider-conformance.js +18 -31
  64. package/dist/testing/session-store-conformance.d.ts +20 -0
  65. package/dist/testing/session-store-conformance.js +92 -0
  66. package/dist/testing/tool-conformance.d.ts +39 -0
  67. package/dist/testing/tool-conformance.js +79 -0
  68. package/dist/tools.d.ts +7 -2
  69. package/dist/tools.js +50 -13
  70. package/docs/agent-definitions.md +251 -0
  71. package/docs/agent-events.md +199 -0
  72. package/docs/agent-loops.md +217 -0
  73. package/docs/agent-session-runtime.md +20 -8
  74. package/docs/cli-rpc.md +39 -4
  75. package/docs/coding-agent-tools.md +208 -0
  76. package/docs/compaction-and-retry.md +2 -2
  77. package/docs/compaction-conformance.md +76 -0
  78. package/docs/compaction-llm.md +6 -3
  79. package/docs/compaction-observational-memory.md +4 -4
  80. package/docs/configuration-and-manifests.md +6 -1
  81. package/docs/context-and-skills.md +79 -6
  82. package/docs/contribution-discovery.md +149 -0
  83. package/docs/contribution-registries.md +9 -6
  84. package/docs/credentials-and-redaction.md +2 -0
  85. package/docs/customization.md +191 -0
  86. package/docs/database-persistence.md +407 -0
  87. package/docs/extension-authoring.md +193 -0
  88. package/docs/extension-conformance.md +80 -0
  89. package/docs/extensions.md +6 -0
  90. package/docs/host-security.md +141 -0
  91. package/docs/index.md +41 -19
  92. package/docs/input-and-prompt-assembly.md +19 -3
  93. package/docs/instruction-injection.md +183 -0
  94. package/docs/migration.md +201 -0
  95. package/docs/model-registry.md +122 -0
  96. package/docs/node-jsonl-session-store.md +5 -4
  97. package/docs/performance.md +127 -0
  98. package/docs/provider-caching.md +206 -0
  99. package/docs/provider-conformance.md +32 -5
  100. package/docs/provider-layer.md +51 -11
  101. package/docs/provider-packages.md +65 -5
  102. package/docs/provider-request-policies.md +113 -0
  103. package/docs/providers/kimi.md +22 -0
  104. package/docs/providers/neuralwatt.md +388 -0
  105. package/docs/providers/openai-compatible.md +1 -0
  106. package/docs/providers/openai.md +21 -0
  107. package/docs/providers/opencode-go.md +31 -3
  108. package/docs/providers/openrouter.md +29 -0
  109. package/docs/providers/zai.md +17 -0
  110. package/docs/public-contracts.md +87 -12
  111. package/docs/release-and-install.md +79 -27
  112. package/docs/runs-and-usage.md +236 -0
  113. package/docs/session-store-conformance.md +78 -0
  114. package/docs/session-stores-and-branching.md +10 -6
  115. package/docs/session-stores.md +126 -0
  116. package/docs/settings-auth-trust-security.md +18 -4
  117. package/docs/structured-output.md +247 -0
  118. package/docs/system-prompts.md +104 -2
  119. package/docs/tool-conformance.md +87 -0
  120. package/docs/tools.md +65 -8
  121. package/package.json +36 -2
package/dist/contracts.js CHANGED
@@ -1,2 +1,42 @@
1
- export {};
1
+ export const SESSION_ENTRY_KINDS = [
2
+ "message",
3
+ "event",
4
+ "summary",
5
+ "metadata",
6
+ "model_change",
7
+ "label",
8
+ "custom",
9
+ "compaction",
10
+ ];
11
+ const SESSION_ENTRY_KIND_SET = new Set(SESSION_ENTRY_KINDS);
12
+ export const SESSION_ENTRY_SCHEMA_VERSION = 1;
13
+ export function isSessionEntryKind(value) {
14
+ return typeof value === "string" && SESSION_ENTRY_KIND_SET.has(value);
15
+ }
16
+ /** Stable error code carried by `SessionAppendConflictError`. */
17
+ export const SESSION_APPEND_CONFLICT_CODE = "session_append_conflict";
18
+ /**
19
+ * Thrown when `SessionStore.append` rejects an entry under `SessionAppendOptions`
20
+ * (dangling/stale `expectedParentId`, stricter adapter CAS failure, or duplicate
21
+ * idempotency key for the same parent). Recognize via the stable `code` and
22
+ * `isSessionAppendConflict`, not message text.
23
+ */
24
+ export class SessionAppendConflictError extends Error {
25
+ conflict;
26
+ code = SESSION_APPEND_CONFLICT_CODE;
27
+ constructor(conflict) {
28
+ const detail = conflict.idempotencyDuplicate
29
+ ? `idempotency key already used`
30
+ : conflict.currentLeafId !== undefined
31
+ ? `expected parent ${conflict.expectedParentId ?? "<none>"} does not match current leaf ${conflict.currentLeafId}`
32
+ : `expected parent ${conflict.expectedParentId ?? "<none>"} is unavailable`;
33
+ super(`session append conflict: ${detail}`);
34
+ this.conflict = conflict;
35
+ this.name = "SessionAppendConflictError";
36
+ }
37
+ }
38
+ /** Type guard keyed off the stable `code` (works across bundles; not message text). */
39
+ export function isSessionAppendConflict(error) {
40
+ return error instanceof Error && error.code === SESSION_APPEND_CONFLICT_CODE;
41
+ }
2
42
  //# sourceMappingURL=contracts.js.map
@@ -0,0 +1,19 @@
1
+ import type { Skill } from "./contracts.js";
2
+ import type { ManifestContributionDeclaration } from "./manifests.js";
3
+ /** Split a markdown file with `---` frontmatter into a parsed map and body.
4
+ * Exported so Node-side loaders (e.g. agent bundle resolution) can reuse the
5
+ * same frontmatter parser without duplicating it. */
6
+ export declare function splitFrontmatter(text: string, path: string): {
7
+ front: Map<string, unknown>;
8
+ body: string;
9
+ };
10
+ /** Parse a `SKILL.md` into a {@link Skill}. Frontmatter keys map to `name`
11
+ * (required), `description`, `toolNames` (comma- or YAML-list); the markdown
12
+ * body becomes `instructions`. `context` declared in a file is surfaced as
13
+ * metadata only — file-declared context providers need a `module`; Phase 30
14
+ * owns instruction injection and context wiring. */
15
+ export declare function parseSkillFile(text: string, path: string): Skill;
16
+ /** Parse a per-agent `AGENT.md` bundle frontmatter file into a manifest-referenced declaration.
17
+ * Only `name`/`metadata` surface here; full agent resolution is Phase 33's
18
+ * `resolveAgentDefinition`. The file path is recorded as `resource`. */
19
+ export declare function parseAgentFile(text: string, path: string): ManifestContributionDeclaration;
@@ -0,0 +1,124 @@
1
+ /** Frontmatter split + scalar/simple-list parser for `SKILL.md` and agent bundle
2
+ * markdown files. Stdlib `String` parsing only — no YAML dependency, no Markdown
3
+ * AST, no `node:*` import. Unknown frontmatter keys are tolerated (collected into
4
+ * metadata, not fatal). Core is fs-free: callers pass file text already read. */
5
+ const FENCE = "---";
6
+ /** Split a markdown file with `---` frontmatter into a parsed map and body.
7
+ * Exported so Node-side loaders (e.g. agent bundle resolution) can reuse the
8
+ * same frontmatter parser without duplicating it. */
9
+ export function splitFrontmatter(text, path) {
10
+ const lines = text.split(/\r?\n/);
11
+ if (lines.length === 0 || lines[0].trim() !== FENCE) {
12
+ return { front: new Map(), body: text };
13
+ }
14
+ let end = -1;
15
+ for (let i = 1; i < lines.length; i++) {
16
+ if (lines[i].trim() === FENCE || lines[i].trim() === "...") {
17
+ end = i;
18
+ break;
19
+ }
20
+ }
21
+ if (end === -1)
22
+ throw new Error(`Malformed frontmatter in ${path}: unterminated fence`);
23
+ const frontLines = lines.slice(1, end);
24
+ const body = lines.slice(end + 1).join("\n");
25
+ return { front: parseFrontBlock(frontLines), body };
26
+ }
27
+ function parseFrontBlock(lines) {
28
+ const out = new Map();
29
+ let i = 0;
30
+ while (i < lines.length) {
31
+ const line = lines[i];
32
+ if (line.trim() === "") {
33
+ i++;
34
+ continue;
35
+ }
36
+ const colon = line.indexOf(":");
37
+ if (colon === -1) {
38
+ i++;
39
+ continue;
40
+ }
41
+ const key = line.slice(0, colon).trim();
42
+ const value = line.slice(colon + 1).trim();
43
+ if (value === "") {
44
+ // block list: collect following ` - item` lines
45
+ const items = [];
46
+ while (i + 1 < lines.length && /^\s*-\s+/.test(lines[i + 1])) {
47
+ items.push(lines[i + 1].replace(/^\s*-\s+/, "").trim());
48
+ i++;
49
+ }
50
+ out.set(key, items);
51
+ }
52
+ else if (value.startsWith("[") && value.endsWith("]")) {
53
+ out.set(key, value.slice(1, -1).split(",").map((s) => s.trim()).filter((s) => s.length > 0));
54
+ }
55
+ else {
56
+ out.set(key, value);
57
+ }
58
+ i++;
59
+ }
60
+ return out;
61
+ }
62
+ // ponytail: core is fs-free — derive the fallback skill name from the path via a
63
+ // plain string split instead of `node:path`. Expects `.../<name>/SKILL.md`.
64
+ function parentDirName(path) {
65
+ const parts = path.split(/[/\\]+/).filter(Boolean);
66
+ return parts.length >= 2 ? parts[parts.length - 2] : parts[parts.length - 1] ?? "";
67
+ }
68
+ const NAME_RE = /^[A-Za-z0-9 _-]+$/;
69
+ /** Parse a `SKILL.md` into a {@link Skill}. Frontmatter keys map to `name`
70
+ * (required), `description`, `toolNames` (comma- or YAML-list); the markdown
71
+ * body becomes `instructions`. `context` declared in a file is surfaced as
72
+ * metadata only — file-declared context providers need a `module`; Phase 30
73
+ * owns instruction injection and context wiring. */
74
+ export function parseSkillFile(text, path) {
75
+ const { front, body } = splitFrontmatter(text, path);
76
+ const name = String(front.get("name") ?? "").trim() || parentDirName(path);
77
+ if (!NAME_RE.test(name)) {
78
+ throw new Error(`Invalid skill name in ${path}: ${JSON.stringify(name)}`);
79
+ }
80
+ const description = front.has("description") ? String(front.get("description")) : undefined;
81
+ const toolNamesValue = front.get("toolNames") ?? front.get("tools");
82
+ const toolNames = Array.isArray(toolNamesValue) ? toolNamesValue : undefined;
83
+ const metadata = {};
84
+ // ponytail: `context` from a file SKILL.md lands here as metadata; not wired into a live ContextProvider (needs a module). Phase 30.
85
+ for (const [key, value] of front) {
86
+ if (key !== "name" && key !== "description" && key !== "toolNames" && key !== "tools") {
87
+ metadata[key] = value;
88
+ }
89
+ }
90
+ const skill = {
91
+ name: name.replace(/\s+/g, "-"),
92
+ description,
93
+ instructions: body.replace(/^\n+/, ""),
94
+ ...(toolNames ? { toolNames } : {}),
95
+ ...(Object.keys(metadata).length > 0 ? { metadata } : {}),
96
+ };
97
+ return skill;
98
+ }
99
+ /** Parse a per-agent `AGENT.md` bundle frontmatter file into a manifest-referenced declaration.
100
+ * Only `name`/`metadata` surface here; full agent resolution is Phase 33's
101
+ * `resolveAgentDefinition`. The file path is recorded as `resource`. */
102
+ export function parseAgentFile(text, path) {
103
+ const { front } = splitFrontmatter(text, path);
104
+ const name = String(front.get("name") ?? "").trim() || parentDirName(path);
105
+ if (!NAME_RE.test(name)) {
106
+ throw new Error(`Invalid agent name in ${path}: ${JSON.stringify(name)}`);
107
+ }
108
+ // ponytail: full resolution is Phase 33's resolveAgentDefinition. Only name/metadata surface here.
109
+ const metadata = {};
110
+ for (const [key, value] of front) {
111
+ if (key !== "name")
112
+ metadata[key] = value;
113
+ }
114
+ const declaration = {
115
+ kind: "agent",
116
+ name: name.replace(/\s+/g, "-"),
117
+ resource: path,
118
+ ...(Object.keys(metadata).length > 0 ? { metadata } : {}),
119
+ };
120
+ return declaration;
121
+ }
122
+ // Self-check: the smallest thing that fails if parsing breaks.
123
+ // Run with: node -e "import('./dist/contribution-parsing.js').then(m=>console.log(m.parseSkillFile('---\nname: x\ntoolNames:\n - a\n---\nhi', '/s/x/SKILL.md').name))"
124
+ //# sourceMappingURL=contribution-parsing.js.map
@@ -1,13 +1,14 @@
1
- import type { AgentDefinition, AuthMethod, CommandDefinition, CompactionStrategy, ContextProvider, CredentialResolver, InputBuilder, PromptBuilder, ProviderPackage, ProviderRequestPolicy, ResourceLoader, RetryPolicy, SettingsProvider, Skill, StoreFactory, SystemPromptContribution, ToolDefinition } from "./contracts.js";
1
+ import type { AgentDefinition, AuthMethod, CommandDefinition, CompactionStrategy, ContextProvider, CredentialResolver, DiscoveredContribution, InputBuilder, InstructionInjector, PromptBuilder, ProviderPackage, ProviderRequestPolicy, ResourceLoader, RetryPolicy, SettingsProvider, Skill, StoreFactory, SystemPromptContribution, ToolDefinition } from "./contracts.js";
2
2
  import { type ModelRegistry } from "./models.js";
3
3
  import { type ProviderRegistry } from "./providers.js";
4
+ import { type DuplicateRegistrationOptions } from "./registry-options.js";
4
5
  export interface ContributionRegistry<T> {
5
6
  register(key: string, contribution: T): void;
6
7
  get(key: string): T | undefined;
7
8
  resolve(key: string): T;
8
9
  list(): readonly T[];
9
10
  }
10
- export interface ContributionRegistryOptions {
11
+ export interface ContributionRegistryOptions extends DuplicateRegistrationOptions {
11
12
  readonly label?: string;
12
13
  }
13
14
  export declare function createContributionRegistry<T>(options?: ContributionRegistryOptions): ContributionRegistry<T>;
@@ -31,5 +32,14 @@ export interface ContributionRegistries {
31
32
  readonly authMethods: ContributionRegistry<AuthMethod>;
32
33
  readonly providerRequestPolicies: ContributionRegistry<ProviderRequestPolicy>;
33
34
  readonly systemPromptContributions: ContributionRegistry<SystemPromptContribution>;
35
+ readonly instructionInjectors: ContributionRegistry<InstructionInjector>;
34
36
  }
35
- export declare function createContributionRegistries(): ContributionRegistries;
37
+ export interface ContributionRegistriesOptions extends DuplicateRegistrationOptions {
38
+ }
39
+ export declare function createContributionRegistries(options?: ContributionRegistriesOptions): ContributionRegistries;
40
+ /** Register discovered contributions into the given registries. Inert: skill
41
+ * kinds register a fully realized {@link Skill}; tool/context/instructions
42
+ * kinds register **descriptor-only** entries whose executable behavior is
43
+ * host-owned. Performs NO `import()`. Last-write-wins per `(kind, name)`
44
+ * (workspace discovery already won the merge; calling twice is idempotent). */
45
+ export declare function registerDiscoveredContributions(registries: ContributionRegistries, contributions: readonly DiscoveredContribution[]): void;
@@ -1,10 +1,12 @@
1
1
  import { createModelRegistry } from "./models.js";
2
2
  import { createProviderRegistry } from "./providers.js";
3
+ import { assertCanRegister } from "./registry-options.js";
3
4
  export function createContributionRegistry(options = {}) {
4
5
  const label = options.label ?? "contribution";
5
6
  const byKey = new Map();
6
7
  return {
7
8
  register(key, contribution) {
9
+ assertCanRegister(byKey, key, label, key, options.duplicate);
8
10
  byKey.set(key, contribution);
9
11
  },
10
12
  get(key) {
@@ -21,27 +23,101 @@ export function createContributionRegistry(options = {}) {
21
23
  },
22
24
  };
23
25
  }
24
- export function createContributionRegistries() {
26
+ export function createContributionRegistries(options = {}) {
27
+ const registryOptions = (label) => ({ label, duplicate: options.duplicate });
25
28
  return {
26
- providers: createProviderRegistry(),
27
- models: createModelRegistry(),
28
- tools: createContributionRegistry({ label: "tool" }),
29
- contextProviders: createContributionRegistry({ label: "context provider" }),
30
- skills: createContributionRegistry({ label: "skill" }),
31
- commands: createContributionRegistry({ label: "command" }),
32
- agents: createContributionRegistry({ label: "agent" }),
33
- inputBuilders: createContributionRegistry({ label: "input builder" }),
34
- promptBuilders: createContributionRegistry({ label: "prompt builder" }),
35
- compactionStrategies: createContributionRegistry({ label: "compaction strategy" }),
36
- retryPolicies: createContributionRegistry({ label: "retry policy" }),
37
- storeFactories: createContributionRegistry({ label: "store factory" }),
38
- resourceLoaders: createContributionRegistry({ label: "resource loader" }),
39
- settingsProviders: createContributionRegistry({ label: "settings provider" }),
40
- credentialResolvers: createContributionRegistry({ label: "credential resolver" }),
41
- providerPackages: createContributionRegistry({ label: "provider package" }),
42
- authMethods: createContributionRegistry({ label: "auth method" }),
43
- providerRequestPolicies: createContributionRegistry({ label: "provider request policy" }),
44
- systemPromptContributions: createContributionRegistry({ label: "system prompt contribution" }),
29
+ providers: createProviderRegistry([], options),
30
+ models: createModelRegistry([], options),
31
+ tools: createContributionRegistry(registryOptions("tool")),
32
+ contextProviders: createContributionRegistry(registryOptions("context provider")),
33
+ skills: createContributionRegistry(registryOptions("skill")),
34
+ commands: createContributionRegistry(registryOptions("command")),
35
+ agents: createContributionRegistry(registryOptions("agent")),
36
+ inputBuilders: createContributionRegistry(registryOptions("input builder")),
37
+ promptBuilders: createContributionRegistry(registryOptions("prompt builder")),
38
+ compactionStrategies: createContributionRegistry(registryOptions("compaction strategy")),
39
+ retryPolicies: createContributionRegistry(registryOptions("retry policy")),
40
+ storeFactories: createContributionRegistry(registryOptions("store factory")),
41
+ resourceLoaders: createContributionRegistry(registryOptions("resource loader")),
42
+ settingsProviders: createContributionRegistry(registryOptions("settings provider")),
43
+ credentialResolvers: createContributionRegistry(registryOptions("credential resolver")),
44
+ providerPackages: createContributionRegistry(registryOptions("provider package")),
45
+ authMethods: createContributionRegistry(registryOptions("auth method")),
46
+ providerRequestPolicies: createContributionRegistry(registryOptions("provider request policy")),
47
+ systemPromptContributions: createContributionRegistry(registryOptions("system prompt contribution")),
48
+ instructionInjectors: createContributionRegistry(registryOptions("instruction injector")),
49
+ };
50
+ }
51
+ /** Register discovered contributions into the given registries. Inert: skill
52
+ * kinds register a fully realized {@link Skill}; tool/context/instructions
53
+ * kinds register **descriptor-only** entries whose executable behavior is
54
+ * host-owned. Performs NO `import()`. Last-write-wins per `(kind, name)`
55
+ * (workspace discovery already won the merge; calling twice is idempotent). */
56
+ export function registerDiscoveredContributions(registries, contributions) {
57
+ for (const contribution of contributions) {
58
+ switch (contribution.kind) {
59
+ case "skill": {
60
+ if (contribution.skill)
61
+ registries.skills.register(contribution.skill.name, contribution.skill);
62
+ break;
63
+ }
64
+ case "tool": {
65
+ registries.tools.register(contribution.name, descriptorTool(contribution));
66
+ break;
67
+ }
68
+ case "context": {
69
+ registries.contextProviders.register(contribution.name, descriptorContextProvider(contribution));
70
+ break;
71
+ }
72
+ case "instructions": {
73
+ registries.systemPromptContributions.register(contribution.name, descriptorInstructions(contribution));
74
+ break;
75
+ }
76
+ }
77
+ }
78
+ }
79
+ function discoverMetadata(contribution) {
80
+ const decl = contribution.declaration;
81
+ return {
82
+ discovered: true,
83
+ origin: contribution.origin,
84
+ path: contribution.path,
85
+ ...(decl?.module ? { module: decl.module } : {}),
86
+ ...(decl?.exportName ? { exportName: decl.exportName } : {}),
87
+ ...(decl?.resource ? { resource: decl.resource } : {}),
88
+ };
89
+ }
90
+ // ponytail: descriptor-only tool — no import(), no execution. Host owns resolving declaration.module.
91
+ // ToolDefinition has no metadata slot; discovery provenance rides on the DiscoveredContribution envelope.
92
+ function descriptorTool(contribution) {
93
+ const name = contribution.name;
94
+ return {
95
+ name,
96
+ description: `Discovered tool ${name}; host-owned execution`,
97
+ execute: () => {
98
+ throw new Error(`Discovered tool ${name} requires host execution (declaration.module not loaded)`);
99
+ },
100
+ };
101
+ }
102
+ function descriptorContextProvider(contribution) {
103
+ const name = contribution.name;
104
+ return {
105
+ name,
106
+ resolve: () => {
107
+ throw new Error(`Discovered context provider ${name} requires host execution (declaration.module not loaded)`);
108
+ },
109
+ // ponytail: ContextProvider has no metadata field; discovery provenance rides on declaration.resource/module via the DiscoveredContribution envelope.
110
+ };
111
+ }
112
+ // ponytail: core is fs-free; cannot read declaration.resource here. text is empty until the host
113
+ // lifts the resource into actual prompt text (Phase 30 instruction injection).
114
+ function descriptorInstructions(contribution) {
115
+ return {
116
+ id: contribution.name,
117
+ source: "package",
118
+ mode: "append",
119
+ text: "",
120
+ metadata: discoverMetadata(contribution),
45
121
  };
46
122
  }
47
123
  //# sourceMappingURL=contributions.js.map
@@ -108,6 +108,9 @@ export function createExtensionKernel(options = {}) {
108
108
  registerSystemPromptContribution(contribution) {
109
109
  registries.systemPromptContributions.register(systemPromptContributionKey(contribution), contribution);
110
110
  },
111
+ registerInstructionInjector(injector) {
112
+ registries.instructionInjectors.register(injector.name, injector);
113
+ },
111
114
  };
112
115
  return {
113
116
  registries,
package/dist/index.d.ts CHANGED
@@ -1,13 +1,18 @@
1
1
  export type * from "./contracts.js";
2
+ export { isSessionEntryKind, SESSION_APPEND_CONFLICT_CODE, SESSION_ENTRY_KINDS, SESSION_ENTRY_SCHEMA_VERSION, SessionAppendConflictError, isSessionAppendConflict } from "./contracts.js";
2
3
  export { createAgent, createAgentSession } from "./agents.js";
4
+ export { applyCacheControl, cacheHitRate, cacheSavings, cacheUsageReport, mapCacheRetention, sanitizeCacheKey } from "./cache-helpers.js";
5
+ export type { ApplyCacheControlOptions, CacheControlledContentBlock, CacheControlledMessage, CacheControlValue, CacheUsageReport } from "./cache-helpers.js";
6
+ export { resolveAgentDefinition } from "./agent-definitions.js";
7
+ export { parseSkillFile, parseAgentFile } from "./contribution-parsing.js";
3
8
  export { assertJsonObject, isJsonObject, loadConfigLayers, mergeConfigLayers } from "./config.js";
4
9
  export type { ConfigLayer, ConfigLoadContext, ConfigProvider } from "./config.js";
5
10
  export { createDefaultCompactionStrategy, isCompactionEntryData } from "./compaction.js";
6
11
  export type { DefaultCompactionStrategyOptions } from "./compaction.js";
7
12
  export { createDefaultRetryPolicy, isTransientErrorInfo, waitForRetry } from "./retry.js";
8
13
  export type { DefaultRetryPolicyOptions } from "./retry.js";
9
- export { createContributionRegistries, createContributionRegistry } from "./contributions.js";
10
- export type { ContributionRegistries, ContributionRegistry, ContributionRegistryOptions } from "./contributions.js";
14
+ export { createContributionRegistries, createContributionRegistry, registerDiscoveredContributions } from "./contributions.js";
15
+ export type { ContributionRegistries, ContributionRegistriesOptions, ContributionRegistry, ContributionRegistryOptions } from "./contributions.js";
11
16
  export { createChainedCredentialResolver, createEnvCredentialResolver, createExplicitCredentialResolver, createMemoryCredentialStore, refreshOAuthCredential, resolveCredentialValue } from "./credentials.js";
12
17
  export { createExtensionEventBus, createExtensionKernel } from "./extensions.js";
13
18
  export type { ExtensionErrorPolicy, ExtensionEventBus, ExtensionEventHandler, ExtensionKernel, ExtensionKernelOptions } from "./extensions.js";
@@ -18,7 +23,7 @@ export { createProviderRequestPolicyChain, createSessionCachePolicy, mergeProvid
18
23
  export type { SessionCachePolicyOptions } from "./provider-request-policy.js";
19
24
  export { composeSystemPrompt, mergeSystemPromptConfig } from "./system-prompts.js";
20
25
  export type { ComposeSystemPromptOptions } from "./system-prompts.js";
21
- export type { ModelRegistry } from "./models.js";
26
+ export type { ModelRegistry, ModelRegistryOptions } from "./models.js";
22
27
  export { definePrismManifest, parsePrismManifest } from "./manifests.js";
23
28
  export type { ManifestContributionDeclaration, ManifestContributionKind, ManifestResourceDeclaration, PrismManifest } from "./manifests.js";
24
29
  export { loadJsonResource, loadManifestResource, loadTextResource } from "./resources.js";
@@ -32,16 +37,21 @@ export { createMemorySessionStore, createSessionEntry, getSessionBranchEntries,
32
37
  export type { CreateSessionEntryOptions, SessionBranch, SessionBranchOptions, SessionContextSnapshot } from "./session-stores.js";
33
38
  export type { MockProviderOptions } from "./mock-provider.js";
34
39
  export { providerContentDelta, providerDone, providerError, providerTextDelta, providerThinkingDelta, providerToolCall, providerToolCallDelta, providerUsage, toolCallContent, } from "./provider-events.js";
35
- export { createProviderRegistry } from "./providers.js";
36
- export type { ProviderRegistry } from "./providers.js";
37
- export { createSecretRedactor, errorToErrorInfo, redactAgentEvent, redactMessage, redactProviderRequest, redactSecrets, redactSessionEntry } from "./redaction.js";
40
+ export type { ProviderResolver } from "./contracts.js";
41
+ export { createProviderRegistry, createProviderResolver } from "./providers.js";
42
+ export type { ProviderRegistry, ProviderRegistryOptions } from "./providers.js";
43
+ export { createSecretRedactor, errorToErrorInfo, redactAgentEvent, redactMessage, redactProviderRequest, redactRunLedgerRecord, redactSecrets, redactSessionEntry } from "./redaction.js";
38
44
  export type { SecretRedactor } from "./redaction.js";
39
45
  export { assertPermission, assertTrusted, checkPermission, createStaticPermissionPolicy, createStaticTrustPolicy, denialToErrorInfo, isTrusted, PermissionDeniedError, TrustDeniedError } from "./security.js";
40
46
  export type { PermissionDecision, PermissionPolicy, PermissionRequest, TrustDecision, TrustPolicy, TrustRequest } from "./security.js";
41
47
  export { createSkillRegistry, resolveActiveSkills } from "./skills.js";
42
- export type { ResolveActiveSkillsOptions } from "./skills.js";
48
+ export type { ResolveActiveSkillsOptions, SkillRegistryOptions } from "./skills.js";
49
+ export { resolveInstructionInjectors, runInstructionInjectors } from "./instruction-injection.js";
50
+ export type { ResolveInstructionInjectorsOptions } from "./instruction-injection.js";
43
51
  export { createToolRegistry, dispatchToolCall, filterTools } from "./tools.js";
44
- export type { DispatchToolCallOptions, ToolFilter, ToolFilterInput, ToolValidator } from "./tools.js";
52
+ export type { DispatchToolCallOptions, ToolFilter, ToolFilterInput, ToolRegistryOptions, ToolValidator } from "./tools.js";
53
+ export type { DuplicateRegistrationOptions, DuplicateRegistrationPolicy } from "./registry-options.js";
54
+ export { generateValidateReviseLoop, isAgentLoopOptions, resolveLoop, singleShotLoop } from "./agent-loops.js";
45
55
  export declare const name = "prism";
46
- export declare const version = "0.0.1";
56
+ export declare const version = "0.0.3";
47
57
  export declare const description = "Agent harness for AI providers, agents, sessions, and tools.";
package/dist/index.js CHANGED
@@ -1,8 +1,12 @@
1
+ export { isSessionEntryKind, SESSION_APPEND_CONFLICT_CODE, SESSION_ENTRY_KINDS, SESSION_ENTRY_SCHEMA_VERSION, SessionAppendConflictError, isSessionAppendConflict } from "./contracts.js";
1
2
  export { createAgent, createAgentSession } from "./agents.js";
3
+ export { applyCacheControl, cacheHitRate, cacheSavings, cacheUsageReport, mapCacheRetention, sanitizeCacheKey } from "./cache-helpers.js";
4
+ export { resolveAgentDefinition } from "./agent-definitions.js";
5
+ export { parseSkillFile, parseAgentFile } from "./contribution-parsing.js";
2
6
  export { assertJsonObject, isJsonObject, loadConfigLayers, mergeConfigLayers } from "./config.js";
3
7
  export { createDefaultCompactionStrategy, isCompactionEntryData } from "./compaction.js";
4
8
  export { createDefaultRetryPolicy, isTransientErrorInfo, waitForRetry } from "./retry.js";
5
- export { createContributionRegistries, createContributionRegistry } from "./contributions.js";
9
+ export { createContributionRegistries, createContributionRegistry, registerDiscoveredContributions } from "./contributions.js";
6
10
  export { createChainedCredentialResolver, createEnvCredentialResolver, createExplicitCredentialResolver, createMemoryCredentialStore, refreshOAuthCredential, resolveCredentialValue } from "./credentials.js";
7
11
  export { createExtensionEventBus, createExtensionKernel } from "./extensions.js";
8
12
  export { createModelRegistry } from "./models.js";
@@ -17,12 +21,14 @@ export { assembleProviderInput, createDefaultInputBuilder, createDefaultPromptBu
17
21
  export { createMockProvider } from "./mock-provider.js";
18
22
  export { createMemorySessionStore, createSessionEntry, getSessionBranchEntries, listSessionBranches, rebuildSessionContext } from "./session-stores.js";
19
23
  export { providerContentDelta, providerDone, providerError, providerTextDelta, providerThinkingDelta, providerToolCall, providerToolCallDelta, providerUsage, toolCallContent, } from "./provider-events.js";
20
- export { createProviderRegistry } from "./providers.js";
21
- export { createSecretRedactor, errorToErrorInfo, redactAgentEvent, redactMessage, redactProviderRequest, redactSecrets, redactSessionEntry } from "./redaction.js";
24
+ export { createProviderRegistry, createProviderResolver } from "./providers.js";
25
+ export { createSecretRedactor, errorToErrorInfo, redactAgentEvent, redactMessage, redactProviderRequest, redactRunLedgerRecord, redactSecrets, redactSessionEntry } from "./redaction.js";
22
26
  export { assertPermission, assertTrusted, checkPermission, createStaticPermissionPolicy, createStaticTrustPolicy, denialToErrorInfo, isTrusted, PermissionDeniedError, TrustDeniedError } from "./security.js";
23
27
  export { createSkillRegistry, resolveActiveSkills } from "./skills.js";
28
+ export { resolveInstructionInjectors, runInstructionInjectors } from "./instruction-injection.js";
24
29
  export { createToolRegistry, dispatchToolCall, filterTools } from "./tools.js";
30
+ export { generateValidateReviseLoop, isAgentLoopOptions, resolveLoop, singleShotLoop } from "./agent-loops.js";
25
31
  export const name = "prism";
26
- export const version = "0.0.1";
32
+ export const version = "0.0.3";
27
33
  export const description = "Agent harness for AI providers, agents, sessions, and tools.";
28
34
  //# sourceMappingURL=index.js.map
package/dist/input.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- import type { ContentBlock, ContextBlock, ContextProvider, ContextResolutionContext, InputBuilder, InputBuildContext, JsonObject, Message, ModelConfig, PromptBuilder, PromptBuildRequest, ProviderRequest, ProviderRequestOptions, ResourceLoader, Skill, ToolDefinition, ToolResult } from "./contracts.js";
1
+ import type { ContentBlock, ContextBlock, ContextProvider, ContextResolutionContext, InputBuilder, InstructionInjector, InputBuildContext, JsonObject, Message, ModelConfig, PromptBuilder, PromptBuildRequest, ProviderRequest, ProviderRequestOptions, ResourceLoader, Skill, ToolDefinition, ToolResult } from "./contracts.js";
2
2
  import type { MiddlewareRegistry } from "./middleware.js";
3
+ import type { SecretRedactor } from "./redaction.js";
3
4
  export type AgentInput = string | Message | readonly Message[];
4
5
  export interface PromptInstruction {
5
6
  readonly text: string;
@@ -25,6 +26,8 @@ export interface DefaultInputBuildContext extends InputBuildContext {
25
26
  readonly resourceLoader?: ResourceLoader;
26
27
  readonly toolResults?: readonly ToolResult[];
27
28
  readonly middleware?: MiddlewareRegistry;
29
+ readonly instructionInjectors?: readonly InstructionInjector[];
30
+ readonly turn?: number;
28
31
  }
29
32
  export interface DefaultInputBuilder extends InputBuilder {
30
33
  build(input: AgentInput, context?: DefaultInputBuildContext): Promise<readonly Message[]>;
@@ -33,6 +36,7 @@ export interface ResolveContextOptions extends Omit<ContextResolutionContext, "m
33
36
  readonly messages: readonly Message[];
34
37
  readonly providers?: readonly ContextProvider[];
35
38
  readonly middleware?: MiddlewareRegistry;
39
+ readonly injectedBlocks?: readonly ContextBlock[];
36
40
  }
37
41
  export interface DefaultPromptBuilder extends PromptBuilder {
38
42
  build(request: PromptBuildRequest): Promise<readonly Message[]>;
@@ -42,6 +46,7 @@ export interface PromptTemplateOptions {
42
46
  }
43
47
  export interface AssembleProviderInputOptions extends DefaultInputBuildContext {
44
48
  readonly model: ModelConfig;
49
+ readonly redactor?: SecretRedactor;
45
50
  readonly input: AgentInput;
46
51
  readonly inputBuilder?: InputBuilder;
47
52
  readonly contextProviders?: readonly ContextProvider[];
@@ -55,3 +60,4 @@ export declare function resolveContextProviders(options: ResolveContextOptions):
55
60
  export declare function createDefaultPromptBuilder(): DefaultPromptBuilder;
56
61
  export declare function renderPromptTemplate(template: string, variables: JsonObject, options?: PromptTemplateOptions): string;
57
62
  export declare function assembleProviderInput(options: AssembleProviderInputOptions): Promise<ProviderRequest>;
63
+ export declare function inputMessages(input: AgentInput): Message[];
package/dist/input.js CHANGED
@@ -1,17 +1,24 @@
1
+ import { redactMessage } from "./redaction.js";
1
2
  import { loadTextResource } from "./resources.js";
3
+ import { composeSystemPrompt } from "./system-prompts.js";
4
+ import { runInstructionInjectors } from "./instruction-injection.js";
2
5
  export function createDefaultInputBuilder() {
3
6
  return {
4
7
  name: "default-input",
5
8
  async build(input, context = {}) {
6
- const messages = [];
7
- messages.push(...instructionMessages(context.systemInstructions, "System instruction"));
8
- messages.push(...instructionMessages(context.developerInstructions, "Developer instruction"));
9
- messages.push(...customInstructionMessages(context.instructions));
10
- messages.push(...summaryMessages(context.summaries));
11
- messages.push(...(context.history ?? []));
12
- messages.push(...inputMessages(input));
13
- messages.push(...await attachmentMessages(context));
14
- messages.push(...toolResultMessages(context.toolResults));
9
+ const groups = {
10
+ instructions: [
11
+ ...instructionMessages(context.systemInstructions, "System instruction"),
12
+ ...instructionMessages(context.developerInstructions, "Developer instruction"),
13
+ ...customInstructionMessages(context.instructions),
14
+ ],
15
+ summaries: summaryMessages(context.summaries),
16
+ history: [...(context.history ?? [])],
17
+ input: inputMessages(input),
18
+ attachments: await attachmentMessages(context),
19
+ toolResults: toolResultMessages(context.toolResults),
20
+ };
21
+ const messages = flattenInputGroups(groups, context.inputLayout ?? "legacy");
15
22
  return context.middleware ? context.middleware.run("input_assembly", messages) : messages;
16
23
  },
17
24
  };
@@ -28,6 +35,9 @@ export async function resolveContextProviders(options) {
28
35
  signal: options.signal,
29
36
  }));
30
37
  }
38
+ // ponytail: injector blocks after host+skill provider blocks (split by origin if per-block ordering matters).
39
+ if (options.injectedBlocks)
40
+ blocks.push(...options.injectedBlocks);
31
41
  return options.middleware ? options.middleware.run("context", blocks) : blocks;
32
42
  }
33
43
  export function createDefaultPromptBuilder() {
@@ -61,10 +71,31 @@ export async function assembleProviderInput(options) {
61
71
  metadata: options.metadata,
62
72
  signal: options.signal,
63
73
  };
64
- const messages = await inputBuilder.build(options.input, { ...options, ...baseContext });
74
+ // ponytail: Phase 30 — run injectors once per turn inside the assembler so when/predicate/turn
75
+ // filter against loop-local turn. Instructions layer via composeSystemPrompt (no parallel prompt
76
+ // code); contextBlocks merge via resolveContextProviders (middleware still runs).
77
+ const turn = options.turn ?? 1;
78
+ const injectorContribs = options.instructionInjectors?.length
79
+ ? runInstructionInjectors(options.instructionInjectors, {
80
+ sessionId: options.sessionId ?? "",
81
+ runId: options.runId ?? "",
82
+ turn,
83
+ // Runtime history is already redacted; input is redacted here before injector code sees it.
84
+ input: inputMessages(options.input).map((message) => redactMessage(message, options.redactor)),
85
+ history: options.history ?? [],
86
+ metadata: options.metadata ?? {},
87
+ signal: options.signal ?? new AbortController().signal,
88
+ })
89
+ : { instructions: [], contextBlocks: [] };
90
+ const systemInstructions = injectorContribs.instructions.length
91
+ ? composeSystemPrompt(injectorContribs.instructions, { base: options.systemInstructions })
92
+ : options.systemInstructions;
93
+ const buildContext = { ...options, ...baseContext, systemInstructions };
94
+ const messages = await inputBuilder.build(options.input, buildContext);
65
95
  const context = await resolveContextProviders({
66
96
  providers: options.contextProviders,
67
97
  messages,
98
+ injectedBlocks: injectorContribs.contextBlocks.length ? injectorContribs.contextBlocks : undefined,
68
99
  middleware: options.middleware,
69
100
  ...baseContext,
70
101
  });
@@ -97,7 +128,15 @@ export async function assembleProviderInput(options) {
97
128
  signal: options.signal,
98
129
  };
99
130
  }
100
- function inputMessages(input) {
131
+ function flattenInputGroups(groups, layout) {
132
+ switch (layout) {
133
+ case "cache_aware":
134
+ return [...groups.instructions, ...groups.attachments, ...groups.summaries, ...groups.history, ...groups.toolResults, ...groups.input];
135
+ case "legacy":
136
+ return [...groups.instructions, ...groups.summaries, ...groups.history, ...groups.input, ...groups.attachments, ...groups.toolResults];
137
+ }
138
+ }
139
+ export function inputMessages(input) {
101
140
  if (typeof input === "string")
102
141
  return [textMessage("user", input)];
103
142
  if ("role" in input)
@@ -191,6 +230,8 @@ function blockText(block) {
191
230
  return JSON.stringify(part.result ?? part.error ?? null);
192
231
  if (part.type === "tool_call")
193
232
  return `${part.name}(${JSON.stringify(part.arguments)})`;
233
+ if (part.type === "tool_call_delta")
234
+ return `${part.name ?? "tool"}(${part.argumentsText ?? ""})`;
194
235
  return part.url ?? part.mimeType ?? "[image]";
195
236
  }).join("\n");
196
237
  }
@@ -0,0 +1,28 @@
1
+ import type { ContributionRegistry } from "./contributions.js";
2
+ import type { ContextBlock, InstructionContext, InstructionInjector, SystemPromptContribution } from "./contracts.js";
3
+ /** Run each selected injector against `ctx`, returning the matching contributions as
4
+ * `package`-source `SystemPromptContribution[]` (for `composeSystemPrompt`) and
5
+ * `ContextBlock[]` (for the context merge). Aborts on `ctx.signal`. Non-matching
6
+ * injectors contribute nothing; only `instructions`/`contextBlocks` are honored —
7
+ * injectors cannot register tools, skills, or permissions. */
8
+ export declare function runInstructionInjectors(injectors: readonly InstructionInjector[], ctx: InstructionContext): {
9
+ readonly instructions: readonly SystemPromptContribution[];
10
+ readonly contextBlocks: readonly ContextBlock[];
11
+ };
12
+ export interface ResolveInstructionInjectorsOptions {
13
+ /** Explicit injector list (e.g. from AgentConfig/RunOptions). Returned as-is when `names` is absent. */
14
+ readonly configured?: readonly InstructionInjector[];
15
+ /** Registry to resolve `names` against. */
16
+ readonly registry?: ContributionRegistry<InstructionInjector>;
17
+ /** Names resolved against `registry` (fail closed on miss); ignored when `configured` is set. */
18
+ readonly names?: readonly string[];
19
+ }
20
+ /**
21
+ * Resolve the effective injector list for a run. Mirrors {@link resolveActiveSkills}
22
+ * fail-closed name resolution. When `configured` is provided it wins (RunOptions
23
+ * supplies an explicit list); otherwise names are resolved against `registry`.
24
+ * An unknown name throws `Unknown instruction injector: <name>`.
25
+ *
26
+ * ponytail: no toolNames enforcement — injectors grant no tools (unlike skills).
27
+ */
28
+ export declare function resolveInstructionInjectors(options: ResolveInstructionInjectorsOptions): readonly InstructionInjector[];