@arnilo/prism 0.3.2 → 0.5.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.
Files changed (208) hide show
  1. package/CHANGELOG.md +50 -1
  2. package/README.md +42 -62
  3. package/dist/agent-run-lifecycle.js +4 -0
  4. package/dist/agent-run-state.d.ts +5 -2
  5. package/dist/agent-run-state.js +18 -8
  6. package/dist/agent-session/session/assemble.d.ts +6 -0
  7. package/dist/agent-session/session/assemble.js +391 -0
  8. package/dist/agent-session/session/persist.d.ts +28 -0
  9. package/dist/agent-session/session/persist.js +166 -0
  10. package/dist/agent-session/session/provider-round.d.ts +6 -0
  11. package/dist/agent-session/session/provider-round.js +231 -0
  12. package/dist/agent-session/session/tool-round.d.ts +31 -0
  13. package/dist/agent-session/session/tool-round.js +473 -0
  14. package/dist/agent-session/session/types.d.ts +115 -0
  15. package/dist/agent-session/session/types.js +5 -0
  16. package/dist/agent-session/session.d.ts +54 -41
  17. package/dist/agent-session/session.js +23 -1132
  18. package/dist/capture.d.ts +63 -0
  19. package/dist/capture.js +67 -0
  20. package/dist/cli-dev.d.ts +29 -0
  21. package/dist/cli-dev.js +52 -0
  22. package/dist/cli-init.d.ts +34 -3
  23. package/dist/cli-init.js +192 -24
  24. package/dist/cli-runner.d.ts +6 -2
  25. package/dist/cli-runner.js +57 -10
  26. package/dist/content.d.ts +3 -3
  27. package/dist/content.js +3 -1
  28. package/dist/contracts-core/agent.d.ts +8 -0
  29. package/dist/contracts-core/batch.d.ts +97 -0
  30. package/dist/contracts-core/batch.js +65 -0
  31. package/dist/contracts-core/content.d.ts +72 -1
  32. package/dist/contracts-core/embeddings.d.ts +30 -0
  33. package/dist/contracts-core/embeddings.js +17 -0
  34. package/dist/contracts-core/images.d.ts +60 -0
  35. package/dist/contracts-core/images.js +17 -0
  36. package/dist/contracts-core/moderation.d.ts +46 -0
  37. package/dist/contracts-core/moderation.js +34 -0
  38. package/dist/contracts-core/speech.d.ts +39 -0
  39. package/dist/contracts-core/speech.js +17 -0
  40. package/dist/contracts-core/transcription.d.ts +48 -0
  41. package/dist/contracts-core/transcription.js +17 -0
  42. package/dist/contracts-core/video.d.ts +61 -0
  43. package/dist/contracts-core/video.js +17 -0
  44. package/dist/contracts-core.d.ts +7 -0
  45. package/dist/contracts-core.js +7 -0
  46. package/dist/contracts-protocol.d.ts +18 -0
  47. package/dist/contracts-run-state.d.ts +1 -2
  48. package/dist/index.d.ts +7 -3
  49. package/dist/index.js +5 -3
  50. package/dist/input.d.ts +8 -0
  51. package/dist/input.js +4 -0
  52. package/dist/node/agent-definitions.d.ts +1 -8
  53. package/dist/node/agent-definitions.js +0 -34
  54. package/dist/node/settings.d.ts +0 -1
  55. package/dist/node/settings.js +0 -5
  56. package/dist/pinned-fetch.js +29 -3
  57. package/dist/provider-events.js +3 -4
  58. package/dist/providers/media.d.ts +1 -2
  59. package/dist/providers/media.js +1 -4
  60. package/dist/rpc.d.ts +1 -1
  61. package/dist/rpc.js +4 -4
  62. package/dist/testing/persistence-schema.d.ts +1 -1
  63. package/dist/testing/persistence-schema.js +32 -28
  64. package/dist/testing/provider-conformance.d.ts +114 -5
  65. package/dist/testing/provider-conformance.js +342 -0
  66. package/dist/testing/tool-conformance.d.ts +25 -0
  67. package/dist/testing/tool-conformance.js +128 -1
  68. package/dist/testing/tool-effect-store-conformance.d.ts +0 -1
  69. package/dist/testing/tool-effect-store-conformance.js +0 -3
  70. package/dist/thinking.d.ts +48 -9
  71. package/dist/thinking.js +134 -8
  72. package/dist/tool-search.d.ts +76 -0
  73. package/dist/tool-search.js +199 -0
  74. package/docs/0.1.0-readiness.md +3 -3
  75. package/docs/a2a.md +2 -2
  76. package/docs/acp-agent.md +1 -1
  77. package/docs/acp.md +3 -3
  78. package/docs/ag-ui-adoption.md +1 -1
  79. package/docs/ag-ui.md +1 -2
  80. package/docs/agent-definitions.md +1 -1
  81. package/docs/agent-events.md +5 -5
  82. package/docs/agent-identity.md +13 -2
  83. package/docs/audit-export.md +3 -3
  84. package/docs/batch-jobs.md +120 -0
  85. package/docs/browser-automation.md +5 -5
  86. package/docs/caveman.md +2 -2
  87. package/docs/cli-rpc.md +43 -9
  88. package/docs/coding-agent-tools.md +19 -19
  89. package/docs/coding-review-and-diagnostics.md +2 -2
  90. package/docs/coding-security.md +5 -5
  91. package/docs/coding-tools.md +82 -0
  92. package/docs/coding-workspaces.md +2 -2
  93. package/docs/compaction-and-retry.md +2 -2
  94. package/docs/compaction-llm.md +4 -4
  95. package/docs/compaction-observational-memory.md +3 -3
  96. package/docs/computer-use-linux.md +13 -2
  97. package/docs/context-and-skills.md +3 -1
  98. package/docs/conversations.md +4 -4
  99. package/docs/core.md +85 -0
  100. package/docs/credential-storage.md +12 -8
  101. package/docs/credentials-and-redaction.md +1 -1
  102. package/docs/data-classification.md +1 -1
  103. package/docs/database-persistence.md +7 -3
  104. package/docs/dev-inspector.md +103 -0
  105. package/docs/device-adapters.md +2 -2
  106. package/docs/diagrams.md +247 -0
  107. package/docs/document-reader.md +6 -6
  108. package/docs/documents.md +214 -0
  109. package/docs/embeddings.md +112 -0
  110. package/docs/enterprise-postgres-state.md +7 -7
  111. package/docs/evaluations.md +41 -7
  112. package/docs/extensions.md +3 -3
  113. package/docs/forge-integration.md +3 -3
  114. package/docs/graft.md +5 -5
  115. package/docs/guardrails.md +2 -2
  116. package/docs/host-security.md +16 -15
  117. package/docs/image-generation.md +129 -0
  118. package/docs/impeccable.md +7 -5
  119. package/docs/index.md +84 -46
  120. package/docs/indexed-code-search.md +2 -2
  121. package/docs/language-intelligence.md +4 -4
  122. package/docs/live-testing.md +126 -0
  123. package/docs/mcp-tools.md +44 -13
  124. package/docs/middleware-hooks.md +1 -1
  125. package/docs/migrate-to-0.4.md +312 -0
  126. package/docs/migrate-to-0.5.md +122 -0
  127. package/docs/migration.md +51 -1
  128. package/docs/model-registry.md +38 -0
  129. package/docs/model-routing.md +6 -6
  130. package/docs/moderation.md +117 -0
  131. package/docs/multi-agent-patterns.md +177 -0
  132. package/docs/multimodal-content.md +27 -3
  133. package/docs/obscura.md +12 -12
  134. package/docs/observability.md +32 -7
  135. package/docs/openapi-tools.md +14 -4
  136. package/docs/operations.md +11 -0
  137. package/docs/performance.md +30 -10
  138. package/docs/persistence-credentials-multimodality-primitives.md +7 -7
  139. package/docs/policy-and-audit.md +18 -8
  140. package/docs/ponytail.md +3 -3
  141. package/docs/postgres-persistence.md +5 -5
  142. package/docs/process-sessions.md +2 -2
  143. package/docs/prompt-registry.md +106 -0
  144. package/docs/provider-caching.md +36 -32
  145. package/docs/provider-conformance.md +24 -2
  146. package/docs/provider-packages.md +58 -22
  147. package/docs/provider-primitives.md +5 -5
  148. package/docs/provider-request-policies.md +1 -1
  149. package/docs/providers/ai-sdk.md +18 -6
  150. package/docs/providers/alibaba.md +10 -6
  151. package/docs/providers/anthropic.md +10 -6
  152. package/docs/providers/azure.md +20 -4
  153. package/docs/providers/bedrock.md +18 -3
  154. package/docs/providers/clinepass.md +7 -3
  155. package/docs/providers/commandcode.md +253 -0
  156. package/docs/providers/deepseek.md +7 -3
  157. package/docs/providers/google.md +8 -4
  158. package/docs/providers/hyper.md +284 -0
  159. package/docs/providers/kimi.md +7 -3
  160. package/docs/providers/neuralwatt.md +12 -8
  161. package/docs/providers/ollama.md +18 -3
  162. package/docs/providers/openai-compatible.md +5 -1
  163. package/docs/providers/openai.md +9 -5
  164. package/docs/providers/opencode-go.md +8 -4
  165. package/docs/providers/openrouter.md +8 -4
  166. package/docs/providers/vertex.md +21 -5
  167. package/docs/providers/xai.md +7 -3
  168. package/docs/providers/zai.md +7 -3
  169. package/docs/rag.md +31 -9
  170. package/docs/release-and-install.md +181 -76
  171. package/docs/resource-loading.md +1 -1
  172. package/docs/runs-and-usage.md +28 -3
  173. package/docs/server.md +94 -5
  174. package/docs/settings-auth-trust-security.md +7 -5
  175. package/docs/sheets.md +229 -0
  176. package/docs/speech.md +126 -0
  177. package/docs/sqlite-persistence.md +4 -4
  178. package/docs/supervisors.md +4 -3
  179. package/docs/thinking-and-reasoning.md +93 -60
  180. package/docs/tool-conformance.md +28 -3
  181. package/docs/tool-execution-primitives.md +8 -8
  182. package/docs/tools.md +32 -5
  183. package/docs/web-tools.md +3 -3
  184. package/docs/wiki.md +7 -7
  185. package/docs/work-artifacts-and-review.md +17 -6
  186. package/docs/work-connectors.md +4 -4
  187. package/docs/work-tools.md +5 -5
  188. package/docs/workflow-orchestration-primitives.md +35 -11
  189. package/docs/workflows.md +74 -13
  190. package/docs/working-and-semantic-memory.md +53 -5
  191. package/package.json +14 -31
  192. package/templates/README.md +23 -0
  193. package/templates/deep-research/README.md.tmpl +47 -0
  194. package/templates/deep-research/env.example.tmpl +12 -0
  195. package/templates/deep-research/gitignore.tmpl +7 -0
  196. package/templates/deep-research/manifest.json +12 -0
  197. package/templates/deep-research/package.json.tmpl +23 -0
  198. package/templates/deep-research/src/agent.ts.tmpl +81 -0
  199. package/templates/deep-research/src/index.ts.tmpl +53 -0
  200. package/templates/deep-research/src/tests/research.test.ts.tmpl +114 -0
  201. package/templates/deep-research/src/tools.ts.tmpl +86 -0
  202. package/templates/deep-research/src/types.ts.tmpl +45 -0
  203. package/templates/deep-research/src/workflow.ts.tmpl +156 -0
  204. package/templates/deep-research/tsconfig.json.tmpl +15 -0
  205. package/templates/init/manifest.json +5 -0
  206. package/templates/init/package.json.tmpl +2 -1
  207. package/templates/init/providers.json +40 -24
  208. package/docs/antigravity-agent.md +0 -207
@@ -0,0 +1,47 @@
1
+ # __PROJECT_NAME__ — Deep Research Agent
2
+
3
+ A production-grade, modular deep research agent built on **Prism**.
4
+
5
+ The agent executes an end-to-end research pipeline:
6
+ 1. **Plan:** Generates a structured research plan with targeted queries.
7
+ 2. **Search & Extract:** Fetches search results and content via `@arnilo/prism-web-tools`.
8
+ 3. **Refine (Bounded Loop):** Evaluates coverage and refines queries across bounded iterations.
9
+ 4. **Cite & Synthesize:** Synthesizes findings with verifiable, traceable source citations.
10
+ 5. **HITL Clarification:** Proposes structured clarification choices when research topics are ambiguous.
11
+
12
+ ## Quick Start
13
+
14
+ ```bash
15
+ # 1. Install dependencies
16
+ npm install
17
+
18
+ # 2. Run offline tests (100% offline with mock provider)
19
+ npm test
20
+
21
+ # 3. Start the research agent
22
+ npm start
23
+
24
+ # 4. Launch local dev inspector
25
+ npm run dev
26
+ ```
27
+
28
+ ## Architecture & Component Mapping
29
+
30
+ | Stage / Seam | Component | Documentation |
31
+ | --- | --- | --- |
32
+ | **Orchestration** | `@arnilo/prism-workflows` DAG with bounded refine loop & checkpoints | [`docs/workflows.md`](https://github.com/arniloy/prism/blob/main/docs/workflows.md) |
33
+ | **Search & Fetch** | `@arnilo/prism-web-tools` Brave/Exa search & Firecrawl fetch | [`docs/web-tools.md`](https://github.com/arniloy/prism/blob/main/docs/web-tools.md) |
34
+ | **Citations & RAG** | Attribution via `@arnilo/prism-web-tools` & `@arnilo/prism-memory` | [`docs/rag.md`](https://github.com/arniloy/prism/blob/main/docs/rag.md) |
35
+ | **HITL Clarification** | Structured decision tool with durable suspend/resume | [`docs/coding-agent-tools.md`](https://github.com/arniloy/prism/blob/main/docs/coding-agent-tools.md) |
36
+ | **Security & Trust** | Untrusted content boundaries for search & web data | [`docs/host-security.md`](https://github.com/arniloy/prism/blob/main/docs/host-security.md) |
37
+ | **Inspector** | Local loopback playground via `prism dev` | [`docs/cli-rpc.md`](https://github.com/arniloy/prism/blob/main/docs/cli-rpc.md) |
38
+
39
+ ## Live Configuration (Opt-in)
40
+
41
+ To connect real LLM providers and live web search APIs:
42
+ 1. Copy `.env.example` to `.env`:
43
+ ```bash
44
+ cp .env.example .env
45
+ ```
46
+ 2. Set your provider key (e.g. `OPENAI_API_KEY`) and search key (e.g. `BRAVE_API_KEY`).
47
+ 3. Update `src/tools.ts` or `src/agent.ts` to instantiate live providers.
@@ -0,0 +1,12 @@
1
+ # Deep Research Agent Environment Configuration
2
+ # Copy this file to .env to configure live providers and search tools.
3
+ # By default, tests and starter runs use the offline mock provider (no keys required).
4
+
5
+ # --- Model Providers (Optional) ---
6
+ # OPENAI_API_KEY=sk-...
7
+ # ANTHROPIC_API_KEY=sk-ant-...
8
+
9
+ # --- Web Search & Extraction (Optional) ---
10
+ # BRAVE_API_KEY=...
11
+ # FIRECRAWL_API_KEY=...
12
+ # EXA_API_KEY=...
@@ -0,0 +1,7 @@
1
+ node_modules/
2
+ dist/
3
+ .env
4
+ .env.local
5
+ .prism/
6
+ coverage/
7
+ *.log
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "deep-research",
3
+ "description": "Deep research agent: plan -> search -> extract -> refine loop -> citations -> HITL clarify",
4
+ "version": "0.1.0",
5
+ "tags": ["research", "workflows", "web-tools", "rag", "hitl"],
6
+ "packages": [
7
+ "@arnilo/prism",
8
+ "@arnilo/prism-web-tools",
9
+ "@arnilo/prism-memory",
10
+ "@arnilo/prism-workflows"
11
+ ]
12
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "__PROJECT_NAME__",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "build": "tsc -p tsconfig.json",
8
+ "typecheck": "tsc -p tsconfig.json --noEmit",
9
+ "test": "npm run build && node --test dist/__tests__/research.test.js",
10
+ "start": "npm run build && node dist/index.js",
11
+ "dev": "prism dev"
12
+ },
13
+ "dependencies": {
14
+ __DEPENDENCIES__
15
+ },
16
+ "devDependencies": {
17
+ "@types/node": "^22.0.0",
18
+ "typescript": "^5.7.0"
19
+ },
20
+ "engines": {
21
+ "node": ">=20"
22
+ }
23
+ }
@@ -0,0 +1,81 @@
1
+ import {
2
+ type Agent,
3
+ createAgent,
4
+ createMockProvider,
5
+ providerDone,
6
+ providerTextDelta,
7
+ } from "@arnilo/prism";
8
+ import { createResearchSearchAdapter, createResearchWebTools } from "./tools.js";
9
+
10
+ /**
11
+ * Creates the planning agent responsible for breaking down topics into search queries.
12
+ */
13
+ export function createPlannerAgent(options?: {
14
+ readonly model?: { provider: string; model: string };
15
+ readonly provider?: import("@arnilo/prism").AIProvider;
16
+ }): Agent {
17
+ const provider =
18
+ options?.provider ??
19
+ createMockProvider([
20
+ providerTextDelta(
21
+ JSON.stringify({
22
+ topic: "Prism Architecture",
23
+ queries: [
24
+ { query: "Prism workflows orchestration", rationale: "Understand DAG execution", aspect: "workflows" },
25
+ { query: "Prism web tools citations", rationale: "Check search and citation seam", aspect: "web-tools" },
26
+ ],
27
+ }),
28
+ ),
29
+ providerDone(),
30
+ ]);
31
+
32
+ return createAgent({
33
+ model: options?.model ?? { provider: "mock", model: "demo" },
34
+ provider,
35
+ instructions:
36
+ "You are a research planning agent. Given a research topic, produce a structured research plan breaking down the topic into targeted search queries.",
37
+ });
38
+ }
39
+
40
+ /**
41
+ * Creates the synthesis agent responsible for compiling findings into a cited report.
42
+ */
43
+ export function createSynthesizerAgent(options?: {
44
+ readonly model?: { provider: string; model: string };
45
+ readonly provider?: import("@arnilo/prism").AIProvider;
46
+ }): Agent {
47
+ const provider =
48
+ options?.provider ??
49
+ createMockProvider([
50
+ providerTextDelta(
51
+ "### Executive Summary\nPrism provides modular agent runtimes with durable workflows and verifiable web-tool citations.\n\n### Findings\n- Workflows execute deterministic DAG pipelines.\n- Web tools generate verifiable cryptographic citations.",
52
+ ),
53
+ providerDone(),
54
+ ]);
55
+
56
+ return createAgent({
57
+ model: options?.model ?? { provider: "mock", model: "demo" },
58
+ provider,
59
+ instructions:
60
+ "You are a research synthesis agent. Review the gathered research findings and compose a detailed, factual summary referencing the attributable citations.",
61
+ });
62
+ }
63
+
64
+ /**
65
+ * Default app agent export for `prism dev` and CLI inspector.
66
+ */
67
+ export function createAppAgent(): Agent {
68
+ const adapter = createResearchSearchAdapter();
69
+ const tools = createResearchWebTools(adapter);
70
+
71
+ return createAgent({
72
+ model: { provider: "mock", model: "demo" },
73
+ provider: createMockProvider([
74
+ providerTextDelta("Deep Research Agent ready. Ask me to research any topic."),
75
+ providerDone(),
76
+ ]),
77
+ tools,
78
+ instructions:
79
+ "You are a Deep Research Assistant. You break down topics, search the web, refine evidence, and produce cited reports.",
80
+ });
81
+ }
@@ -0,0 +1,53 @@
1
+ import { createAppAgent, createPlannerAgent, createSynthesizerAgent } from "./agent.js";
2
+ import { createClarifyTool, createResearchSearchAdapter, createResearchWebTools } from "./tools.js";
3
+ import type { ResearchFinding, ResearchPlan, ResearchReport } from "./types.js";
4
+ import { createResearchWorkflow, executeResearch } from "./workflow.js";
5
+
6
+ export {
7
+ createAppAgent,
8
+ createPlannerAgent,
9
+ createSynthesizerAgent,
10
+ createResearchSearchAdapter,
11
+ createResearchWebTools,
12
+ createClarifyTool,
13
+ createResearchWorkflow,
14
+ executeResearch,
15
+ type ResearchPlan,
16
+ type ResearchFinding,
17
+ type ResearchReport,
18
+ };
19
+
20
+ export async function main() {
21
+ const topic = process.argv.slice(2).join(" ") || "Prism Agent Architecture";
22
+ console.log(`Starting deep research on: "${topic}"\n`);
23
+
24
+ const report = await executeResearch(topic, {
25
+ onEvent: (event) => {
26
+ if (event.type === "node_started") {
27
+ console.log(` -> [${event.nodeId}] started...`);
28
+ } else if (event.type === "node_finished") {
29
+ console.log(` ✓ [${event.nodeId}] finished`);
30
+ }
31
+ },
32
+ });
33
+
34
+ console.log("\n========================================================");
35
+ console.log(`RESEARCH REPORT: ${report.topic}`);
36
+ console.log("========================================================");
37
+ console.log(`\n${report.summary}\n`);
38
+ console.log("Key Findings:");
39
+ for (const f of report.findings) {
40
+ console.log(`- [${f.citationId}] ${f.title}`);
41
+ console.log(` ${f.snippet}`);
42
+ console.log(` Source: ${f.url}\n`);
43
+ }
44
+ console.log("Citations & References:");
45
+ for (const c of report.citations) {
46
+ console.log(` [${c.citationId}] ${c.title} — ${c.url}`);
47
+ }
48
+ console.log("========================================================\n");
49
+ }
50
+
51
+ if (import.meta.url === `file://${process.argv[1]}`) {
52
+ await main();
53
+ }
@@ -0,0 +1,114 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+ import { citation, createWebTools } from "@arnilo/prism-web-tools";
4
+ import { createAppAgent, createPlannerAgent } from "../agent.js";
5
+ import { createClarifyTool, createResearchSearchAdapter, formatResearchFindings } from "../tools.js";
6
+ import { createResearchWorkflow, executeResearch } from "../workflow.js";
7
+
8
+ describe("deep-research template", () => {
9
+ it("generates research plans with structured query breakdowns", async () => {
10
+ const planner = createPlannerAgent();
11
+ const session = planner.createSession({ id: "test-plan" });
12
+ const result = await session.run("Prism architecture");
13
+
14
+ assert.equal(result.status, "succeeded");
15
+ assert.ok(result.text.length > 0);
16
+ const plan = JSON.parse(result.text);
17
+ assert.ok(Array.isArray(plan.queries));
18
+ assert.ok(plan.queries.length >= 2);
19
+ assert.ok(plan.queries[0].query);
20
+ assert.ok(plan.queries[0].aspect);
21
+ });
22
+
23
+ it("produces attributable citations for search findings", async () => {
24
+ const customUrl = "https://docs.prism.ai/workflows";
25
+ const customCitation = citation("brave", customUrl);
26
+
27
+ const adapter = createResearchSearchAdapter({
28
+ searchFn: async (query) => [
29
+ {
30
+ ...customCitation,
31
+ title: `Custom result for ${query}`,
32
+ snippet: "Deterministic snippet for verification",
33
+ retrievedAt: "2026-08-31T00:00:00.000Z",
34
+ },
35
+ ],
36
+ });
37
+
38
+ const response = await adapter.search("workflows", { count: 1 });
39
+ assert.equal(response.results.length, 1);
40
+ const finding = formatResearchFindings("workflows", response.results)[0]!;
41
+
42
+ assert.equal(finding.url, customUrl);
43
+ assert.equal(finding.citationId, customCitation.citationId);
44
+ assert.ok(finding.citationId.startsWith("web:brave:"));
45
+ assert.equal(finding.title, "Custom result for workflows");
46
+ });
47
+
48
+ it("clarify tool executes human-in-the-loop decision resolution", async () => {
49
+ let receivedQuestion = "";
50
+ let receivedChoices: readonly string[] = [];
51
+
52
+ const tool = createClarifyTool({
53
+ onClarify: async (question, choices) => {
54
+ receivedQuestion = question;
55
+ receivedChoices = choices;
56
+ return choices[1]!; // select second choice
57
+ },
58
+ });
59
+
60
+ const context = {
61
+ toolCallId: "call-clarify-1",
62
+ sessionId: "session-1",
63
+ };
64
+
65
+ const result = await tool.execute(
66
+ {
67
+ question: "Which aspect of Prism should we focus on?",
68
+ choices: ["Orchestration DAGs", "Web Tools & Search", "RAG & Citations"],
69
+ },
70
+ context,
71
+ );
72
+
73
+ assert.equal(result.name, "clarify_research_scope");
74
+ assert.equal(receivedQuestion, "Which aspect of Prism should we focus on?");
75
+ assert.equal(receivedChoices.length, 3);
76
+ const val = result.value as { question: string; selectedOption: string };
77
+ assert.equal(val.selectedOption, "Web Tools & Search");
78
+ });
79
+
80
+ it("executes the full research workflow DAG offline within bounded limits", async () => {
81
+ const events: string[] = [];
82
+ const report = await executeResearch("Autonomous Agents", {
83
+ maxIterations: 2,
84
+ onEvent: (e) => events.push(e.type),
85
+ });
86
+
87
+ assert.ok(report);
88
+ assert.equal(report.topic, "Autonomous Agents");
89
+ assert.ok(report.findings.length >= 2);
90
+ assert.ok(report.citations.length >= 1);
91
+ assert.ok(report.summary.includes("Autonomous Agents"));
92
+
93
+ // Verify all findings carry valid, attributable citation IDs matching citations list
94
+ const citationIds = new Set(report.citations.map((c) => c.citationId));
95
+ for (const finding of report.findings) {
96
+ assert.ok(finding.citationId, "Finding must have a citation ID");
97
+ assert.ok(citationIds.has(finding.citationId), `Finding citationId ${finding.citationId} must be in report citations`);
98
+ assert.ok(finding.url.startsWith("https://"), "Finding must have a valid URL");
99
+ }
100
+
101
+ // Verify workflow lifecycle events
102
+ assert.ok(events.includes("node_started"));
103
+ assert.ok(events.includes("node_finished"));
104
+ });
105
+
106
+ it("default createAppAgent boots and exposes search tools", async () => {
107
+ const appAgent = createAppAgent();
108
+ const session = appAgent.createSession({ id: "app-test" });
109
+ const result = await session.run("Hello");
110
+
111
+ assert.equal(result.status, "succeeded");
112
+ assert.match(result.text, /Deep Research Agent ready/);
113
+ });
114
+ });
@@ -0,0 +1,86 @@
1
+ import { type ToolDefinition, type ToolExecutionContext, type ToolResult } from "@arnilo/prism";
2
+ import { citation, createWebTools, type WebSearchAdapter, type WebSearchResult } from "@arnilo/prism-web-tools";
3
+ import type { ResearchFinding } from "./types.js";
4
+
5
+ /**
6
+ * Creates search tools backed by web-tools with attributable citation IDs.
7
+ */
8
+ export function createResearchSearchAdapter(options?: {
9
+ readonly searchFn?: (query: string) => Promise<readonly WebSearchResult[]>;
10
+ }): WebSearchAdapter {
11
+ return {
12
+ provider: "brave",
13
+ search: async (query: string) => {
14
+ if (options?.searchFn) {
15
+ const results = await options.searchFn(query);
16
+ return { provider: "brave", query, results, untrusted: true };
17
+ }
18
+ // Offline fallback mock results with attributable citations
19
+ const url = `https://docs.prism.ai/research/${encodeURIComponent(query.toLowerCase().replace(/\s+/g, "-"))}`;
20
+ const baseCitation = citation("brave", url);
21
+ const mockResult: WebSearchResult = {
22
+ ...baseCitation,
23
+ title: `Research findings for ${query}`,
24
+ snippet: `Verified architectural overview and technical analysis regarding ${query}.`,
25
+ retrievedAt: new Date().toISOString(),
26
+ };
27
+ return { provider: "brave", query, results: [mockResult], untrusted: true };
28
+ },
29
+ };
30
+ }
31
+
32
+
33
+ /**
34
+ * Create web tools for the research agent.
35
+ */
36
+ export function createResearchWebTools(adapter: WebSearchAdapter): readonly ToolDefinition[] {
37
+ return createWebTools({ search: adapter });
38
+ }
39
+
40
+ /**
41
+ * Normalizes raw search results into domain ResearchFindings with citations.
42
+ */
43
+ export function formatResearchFindings(query: string, results: readonly WebSearchResult[]): readonly ResearchFinding[] {
44
+ return results.map((r) => ({
45
+ query,
46
+ title: r.title ?? "Untitled Resource",
47
+ snippet: r.snippet ?? "",
48
+ url: r.url,
49
+ citationId: r.citationId,
50
+ retrievedAt: r.retrievedAt ?? new Date().toISOString(),
51
+ }));
52
+ }
53
+
54
+ /**
55
+ * Simple Human-in-the-Loop clarify tool for resolving broad research topics.
56
+ */
57
+ export function createClarifyTool(options: {
58
+ readonly onClarify?: (question: string, choices: readonly string[]) => Promise<string>;
59
+ }): ToolDefinition {
60
+ return {
61
+ name: "clarify_research_scope",
62
+ description: "Propose clarification questions to the user when a research topic is broad or ambiguous.",
63
+ parameters: {
64
+ type: "object",
65
+ properties: {
66
+ question: { type: "string" },
67
+ choices: { type: "array", items: { type: "string" }, minItems: 2 },
68
+ },
69
+ required: ["question", "choices"],
70
+ additionalProperties: false,
71
+ },
72
+ execute: async (args: Record<string, unknown>, context: ToolExecutionContext): Promise<ToolResult> => {
73
+ const question = String(args.question ?? "");
74
+ const choices = Array.isArray(args.choices) ? (args.choices as string[]) : [];
75
+ let selected = choices[0] ?? "default";
76
+ if (options.onClarify) {
77
+ selected = await options.onClarify(question, choices);
78
+ }
79
+ return {
80
+ toolCallId: context.toolCallId,
81
+ name: "clarify_research_scope",
82
+ value: { question, selectedOption: selected },
83
+ };
84
+ },
85
+ };
86
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Deep Research domain types and contracts.
3
+ */
4
+
5
+ export interface ResearchQuery {
6
+ readonly query: string;
7
+ readonly rationale: string;
8
+ readonly aspect: string;
9
+ }
10
+
11
+ export interface ResearchPlan {
12
+ readonly topic: string;
13
+ readonly queries: readonly ResearchQuery[];
14
+ readonly clarifyQuestion?: string;
15
+ readonly clarifyOptions?: readonly string[];
16
+ }
17
+
18
+ export interface ResearchFinding {
19
+ readonly query: string;
20
+ readonly title: string;
21
+ readonly snippet: string;
22
+ readonly url: string;
23
+ readonly citationId: string;
24
+ readonly retrievedAt: string;
25
+ }
26
+
27
+ export interface ResearchCitation {
28
+ readonly citationId: string;
29
+ readonly url: string;
30
+ readonly title: string;
31
+ }
32
+
33
+ export interface ResearchReport {
34
+ readonly topic: string;
35
+ readonly summary: string;
36
+ readonly findings: readonly ResearchFinding[];
37
+ readonly citations: readonly ResearchCitation[];
38
+ readonly iterations: number;
39
+ readonly completedAt: string;
40
+ }
41
+
42
+ export interface ClarifyDecision {
43
+ readonly question: string;
44
+ readonly selectedOption: string;
45
+ }
@@ -0,0 +1,156 @@
1
+ import { createSecretRedactor } from "@arnilo/prism";
2
+ import {
3
+ createMemoryWorkflowCheckpoints,
4
+ defineWorkflow,
5
+ functionNode,
6
+ runWorkflow,
7
+ type WorkflowDefinition,
8
+ type WorkflowEvent,
9
+ } from "@arnilo/prism-workflows";
10
+ import { createPlannerAgent, createSynthesizerAgent } from "./agent.js";
11
+ import { createResearchSearchAdapter, formatResearchFindings } from "./tools.js";
12
+ import type { ResearchCitation, ResearchFinding, ResearchPlan, ResearchReport } from "./types.js";
13
+
14
+ export interface ResearchWorkflowOptions {
15
+ readonly maxIterations?: number;
16
+ readonly searchAdapter?: import("@arnilo/prism-web-tools").WebSearchAdapter;
17
+ readonly onEvent?: (event: WorkflowEvent) => void;
18
+ readonly onClarify?: (question: string, choices: readonly string[]) => Promise<string>;
19
+ }
20
+
21
+ /**
22
+ * Builds the deep research workflow DAG.
23
+ */
24
+ export function createResearchWorkflow(options?: ResearchWorkflowOptions): WorkflowDefinition {
25
+ const maxIterations = options?.maxIterations ?? 2;
26
+ const searchAdapter = options?.searchAdapter ?? createResearchSearchAdapter();
27
+
28
+ const plan = functionNode({
29
+ execute: async (ctx) => {
30
+ const input = (ctx.workflowInput as { topic: string }) ?? { topic: "General Research" };
31
+ // Planning step: create initial structured plan
32
+ const planResult: ResearchPlan = {
33
+ topic: input.topic,
34
+ queries: [
35
+ { query: `${input.topic} overview and core architecture`, rationale: "Foundational concepts", aspect: "architecture" },
36
+ { query: `${input.topic} performance and security considerations`, rationale: "Production considerations", aspect: "security" },
37
+ ],
38
+ };
39
+ return planResult;
40
+ },
41
+ });
42
+
43
+ const search = functionNode({
44
+ execute: async (ctx) => {
45
+ const currentPlan = ctx.upstream.plan as ResearchPlan;
46
+ const findings: ResearchFinding[] = [];
47
+
48
+ for (const query of currentPlan.queries) {
49
+ const response = await searchAdapter.search(query.query, { count: 2, signal: ctx.signal });
50
+ const normalized = formatResearchFindings(query.query, response.results);
51
+ findings.push(...normalized);
52
+ }
53
+
54
+ return { findings };
55
+ },
56
+ });
57
+
58
+ const refine = functionNode({
59
+ execute: async (ctx) => {
60
+ const searchOutput = ctx.upstream.search as { findings: readonly ResearchFinding[] };
61
+ const currentFindings = [...searchOutput.findings];
62
+ let iterations = 1;
63
+
64
+ // Bounded refine loop: verify aspect coverage and execute additional queries if bounded budget allows
65
+ if (iterations < maxIterations && currentFindings.length < 4) {
66
+ iterations += 1;
67
+ const refineQuery = `${(ctx.upstream.plan as ResearchPlan).topic} best practices`;
68
+ const extra = await searchAdapter.search(refineQuery, { count: 1, signal: ctx.signal });
69
+ currentFindings.push(...formatResearchFindings(refineQuery, extra.results));
70
+ }
71
+
72
+ return { findings: currentFindings, iterations };
73
+ },
74
+ });
75
+
76
+ const synthesize = functionNode({
77
+ execute: async (ctx) => {
78
+ const planOutput = ctx.upstream.plan as ResearchPlan;
79
+ const refineOutput = ctx.upstream.refine as { findings: readonly ResearchFinding[]; iterations: number };
80
+ const findings = refineOutput.findings;
81
+
82
+ // Collect attributable citations
83
+ const citationsMap = new Map<string, ResearchCitation>();
84
+ for (const f of findings) {
85
+ if (!citationsMap.has(f.citationId)) {
86
+ citationsMap.set(f.citationId, {
87
+ citationId: f.citationId,
88
+ url: f.url,
89
+ title: f.title,
90
+ });
91
+ }
92
+ }
93
+
94
+ const citations = Array.from(citationsMap.values());
95
+ const summary = `Research completed on topic "${planOutput.topic}". Synthesized ${findings.length} findings across ${citations.length} verified sources.`;
96
+
97
+ const report: ResearchReport = {
98
+ topic: planOutput.topic,
99
+ summary,
100
+ findings,
101
+ citations,
102
+ iterations: refineOutput.iterations,
103
+ completedAt: new Date().toISOString(),
104
+ };
105
+
106
+ return report;
107
+ },
108
+ });
109
+
110
+ return defineWorkflow({
111
+ id: "deep-research-workflow",
112
+ revision: "1",
113
+ nodes: { plan, search, refine, synthesize },
114
+ edges: [
115
+ ["plan", "search"],
116
+ ["search", "refine"],
117
+ ["refine", "synthesize"],
118
+ ],
119
+ limits: {
120
+ maxConcurrency: 2,
121
+ maxFanOut: 4,
122
+ maxNodes: 16,
123
+ maxStateBytes: 65_536,
124
+ },
125
+ });
126
+ }
127
+
128
+ /**
129
+ * Execute the deep research workflow on a specified topic.
130
+ */
131
+ export async function executeResearch(
132
+ topic: string,
133
+ options?: ResearchWorkflowOptions,
134
+ ): Promise<ResearchReport> {
135
+ const workflow = createResearchWorkflow(options);
136
+ const redactor = createSecretRedactor([]);
137
+ const checkpoints = createMemoryWorkflowCheckpoints({ redactor });
138
+
139
+ const result = await runWorkflow(
140
+ workflow,
141
+ { topic },
142
+ {
143
+ checkpoints,
144
+ redactor,
145
+ ownership: { tenantId: "research-session" },
146
+ signal: AbortSignal.timeout(60_000),
147
+ onEvent: options?.onEvent,
148
+ },
149
+ );
150
+
151
+ if (result.status !== "completed") {
152
+ throw new Error(`Research workflow did not complete successfully. Status: ${result.status}`);
153
+ }
154
+
155
+ return result.outputs.synthesize as ResearchReport;
156
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "lib": ["ES2022"],
7
+ "outDir": "./dist",
8
+ "rootDir": "./src",
9
+ "strict": true,
10
+ "noImplicitOverride": true,
11
+ "skipLibCheck": true,
12
+ "forceConsistentCasingInFileNames": true
13
+ },
14
+ "include": ["src/**/*"]
15
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "init",
3
+ "description": "Minimal starter Prism agent with one selected provider and offline mock test",
4
+ "version": "0.1.0"
5
+ }
@@ -7,7 +7,8 @@
7
7
  "build": "tsc -p tsconfig.json",
8
8
  "typecheck": "tsc -p tsconfig.json --noEmit",
9
9
  "test": "npm run build && node --test dist/__tests__/agent.test.js",
10
- "start": "npm run build && node dist/index.js"
10
+ "start": "npm run build && node dist/index.js",
11
+ "dev": "prism dev"
11
12
  },
12
13
  "dependencies": {
13
14
  __DEPENDENCIES__