@tangle-network/create-agent-app 0.44.36

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 (42) hide show
  1. package/README.md +9 -0
  2. package/index.mjs +183 -0
  3. package/package.json +34 -0
  4. package/template/.dev.vars.example +11 -0
  5. package/template/AGENTS.md +67 -0
  6. package/template/CLAUDE.md +6 -0
  7. package/template/CUSTOMIZE.md +83 -0
  8. package/template/KNOWLEDGE.md +73 -0
  9. package/template/README.md +41 -0
  10. package/template/_gitignore +5 -0
  11. package/template/_package.json +33 -0
  12. package/template/_tsconfig.json +16 -0
  13. package/template/_wrangler.toml +22 -0
  14. package/template/agent.config.ts +113 -0
  15. package/template/knowledge/.gitkeep +0 -0
  16. package/template/knowledge/README.md +24 -0
  17. package/template/scripts/knowledge-ingest.mjs +86 -0
  18. package/template/src/agent-app.ts +77 -0
  19. package/template/src/worker.ts +128 -0
  20. package/template/tests/agent-app.test.ts +71 -0
  21. package/template/vitest.config.ts +7 -0
  22. package/template-chat/.dev.vars.example +18 -0
  23. package/template-chat/AGENTS.md +73 -0
  24. package/template-chat/CLAUDE.md +6 -0
  25. package/template-chat/CUSTOMIZE.md +88 -0
  26. package/template-chat/README.md +50 -0
  27. package/template-chat/_gitignore +6 -0
  28. package/template-chat/_package.json +38 -0
  29. package/template-chat/_tsconfig.json +16 -0
  30. package/template-chat/_wrangler.toml +38 -0
  31. package/template-chat/agent.config.ts +73 -0
  32. package/template-chat/declarations.d.ts +9 -0
  33. package/template-chat/migrations/0001_init.sql +109 -0
  34. package/template-chat/prompts/system.md +11 -0
  35. package/template-chat/public/index.html +236 -0
  36. package/template-chat/src/chat.ts +210 -0
  37. package/template-chat/src/db/schema.ts +70 -0
  38. package/template-chat/src/env.ts +33 -0
  39. package/template-chat/src/sandbox.ts +159 -0
  40. package/template-chat/src/worker.ts +58 -0
  41. package/template-chat/tests/chat-turn.e2e.test.ts +277 -0
  42. package/template-chat/vitest.config.ts +22 -0
package/README.md ADDED
@@ -0,0 +1,9 @@
1
+ # @tangle-network/create-agent-app
2
+
3
+ Scaffold a new Tangle agent product on [`@tangle-network/agent-app`](https://github.com/tangle-network/agent-app):
4
+
5
+ ```bash
6
+ npm create @tangle-network/agent-app@latest my-agent
7
+ ```
8
+
9
+ Then `cd my-agent && pnpm install && pnpm typecheck && pnpm test`, and follow the generated `CUSTOMIZE.md` / `AGENTS.md`.
package/index.mjs ADDED
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env node
2
+ // create-agent-app — scaffold a new Tangle agent product on @tangle-network/agent-app.
3
+ //
4
+ // Dependency-light by design: Node built-ins only. The CLI copies one template
5
+ // tree verbatim (`template/` by default, `template-chat/` with `--chat`),
6
+ // substitutes a small set of `__TOKEN__` placeholders, and renames
7
+ // files whose template name would otherwise interfere with tooling (a template's
8
+ // own `package.json` must not be read by the scaffolder's package manager; a
9
+ // template `gitignore` must not be applied to the scaffolder repo). The generated
10
+ // project's DATA surface is `agent.config.ts` + `knowledge/`; its CODE surface is
11
+ // `src/` (the chat route + composer). The breadcrumb docs (AGENTS.md / CLAUDE.md /
12
+ // CUSTOMIZE.md / KNOWLEDGE.md) ship inside the project so a coding agent that opens
13
+ // it walks the trail with zero external context.
14
+
15
+ import { cp, mkdir, readFile, readdir, rename, stat, writeFile } from 'node:fs/promises'
16
+ import { existsSync } from 'node:fs'
17
+ import { dirname, join, resolve } from 'node:path'
18
+ import { fileURLToPath } from 'node:url'
19
+
20
+ const HERE = dirname(fileURLToPath(import.meta.url))
21
+ // Template variants: the default tool-loop skeleton, and `--chat` — the
22
+ // assembled multimodal chat vertical (auth + chat-store + chat-routes +
23
+ // sandbox producer + uploads + replay) from `examples/chat-app.md`.
24
+ const TEMPLATES = {
25
+ default: join(HERE, 'template'),
26
+ chat: join(HERE, 'template-chat'),
27
+ }
28
+
29
+ const { version: packageVersion } = JSON.parse(await readFile(join(HERE, 'package.json'), 'utf8'))
30
+ const AGENT_APP_RANGE = `^${packageVersion}`
31
+
32
+ // Template files renamed on materialization. A template cannot itself be named
33
+ // `package.json` / `.gitignore` / `tsconfig.json` without confusing the
34
+ // scaffolder repo's own tooling, so we prefix with `_` and restore on copy.
35
+ const RENAME = new Map([
36
+ ['_package.json', 'package.json'],
37
+ ['_gitignore', '.gitignore'],
38
+ ['_tsconfig.json', 'tsconfig.json'],
39
+ ['_wrangler.toml', 'wrangler.toml'],
40
+ ])
41
+
42
+ function parseArgs(argv) {
43
+ const args = { _: [] }
44
+ for (let i = 0; i < argv.length; i++) {
45
+ const a = argv[i]
46
+ if (a === '--name') args.name = argv[++i]
47
+ else if (a === '--agent-app-version') args.agentAppVersion = argv[++i]
48
+ else if (a === '--chat') args.template = 'chat'
49
+ else if (a === '--force') args.force = true
50
+ else if (a === '-h' || a === '--help') args.help = true
51
+ else if (!a.startsWith('-')) args._.push(a)
52
+ else throw new Error(`Unknown flag: ${a}`)
53
+ }
54
+ return args
55
+ }
56
+
57
+ function usage() {
58
+ return [
59
+ 'Usage: create-agent-app <target-dir> [options]',
60
+ '',
61
+ 'Scaffolds a new Tangle agent product on @tangle-network/agent-app.',
62
+ '',
63
+ 'Options:',
64
+ ' --chat Scaffold the multimodal chat variant instead: the',
65
+ ' assembled chat vertical (auth, thread/message store,',
66
+ ' streaming turns + replay, uploads, agent asks) with',
67
+ ' its own end-to-end test. Default: the tool-loop skeleton.',
68
+ ' --name <name> Project name (default: the target dir basename).',
69
+ ' --agent-app-version <range> @tangle-network/agent-app version (default: ' + AGENT_APP_RANGE + ').',
70
+ ' --force Write into a non-empty directory.',
71
+ ' -h, --help Show this help.',
72
+ '',
73
+ 'After scaffolding:',
74
+ ' cd <target-dir> && pnpm install',
75
+ ' pnpm typecheck && pnpm test',
76
+ ' # then follow CUSTOMIZE.md (each template ships its own fill-checklist)',
77
+ ].join('\n')
78
+ }
79
+
80
+ // Project name → a safe npm package name (lowercase, dashes, no scope chars).
81
+ function toPackageName(name) {
82
+ const cleaned = name
83
+ .trim()
84
+ .toLowerCase()
85
+ .replace(/[^a-z0-9-]+/g, '-')
86
+ .replace(/^-+|-+$/g, '')
87
+ return cleaned || 'agent-app-product'
88
+ }
89
+
90
+ function applyTokens(content, tokens) {
91
+ let out = content
92
+ for (const [key, value] of Object.entries(tokens)) {
93
+ out = out.split(`__${key}__`).join(value)
94
+ }
95
+ return out
96
+ }
97
+
98
+ // Files we run token substitution on. Binary/asset files would be copied as-is;
99
+ // the template is text-only, but we gate on extension to stay safe.
100
+ const TEXT_EXT = /\.(ts|tsx|js|mjs|cjs|json|md|toml|txt|html|css|sql)$/i
101
+ const TEXT_BASENAMES = new Set(['_gitignore', '.gitignore', '_package.json', 'package.json'])
102
+
103
+ async function walk(dir, base = dir, out = []) {
104
+ for (const entry of await readdir(dir, { withFileTypes: true })) {
105
+ const abs = join(dir, entry.name)
106
+ const rel = abs.slice(base.length + 1)
107
+ if (entry.isDirectory()) await walk(abs, base, out)
108
+ else out.push(rel)
109
+ }
110
+ return out
111
+ }
112
+
113
+ async function main() {
114
+ const args = parseArgs(process.argv.slice(2))
115
+ if (args.help || (args._.length === 0 && !args.name)) {
116
+ process.stdout.write(usage() + '\n')
117
+ process.exit(args.help ? 0 : 1)
118
+ }
119
+
120
+ const targetDir = resolve(args._[0] ?? args.name)
121
+ const projectName = args.name ?? targetDir.split(/[\\/]/).pop()
122
+ const packageName = toPackageName(projectName)
123
+ const agentAppVersion = args.agentAppVersion ?? AGENT_APP_RANGE
124
+ const templateDir = TEMPLATES[args.template ?? 'default']
125
+
126
+ if (existsSync(targetDir)) {
127
+ const entries = await readdir(targetDir).catch(() => [])
128
+ const meaningful = entries.filter((e) => e !== '.git' && e !== '.DS_Store')
129
+ if (meaningful.length > 0 && !args.force) {
130
+ throw new Error(
131
+ `Target directory not empty: ${targetDir}\n` +
132
+ `Pass --force to scaffold into it anyway (will not overwrite the .git dir).`,
133
+ )
134
+ }
135
+ }
136
+ await mkdir(targetDir, { recursive: true })
137
+
138
+ const tokens = {
139
+ PROJECT_NAME: projectName,
140
+ PACKAGE_NAME: packageName,
141
+ AGENT_APP_VERSION: agentAppVersion,
142
+ }
143
+
144
+ const files = await walk(templateDir)
145
+ for (const rel of files) {
146
+ const src = join(templateDir, rel)
147
+ // Resolve any renamed path segments (only basenames are renamed).
148
+ const parts = rel.split(/[\\/]/)
149
+ const baseName = parts[parts.length - 1]
150
+ const outName = RENAME.get(baseName) ?? baseName
151
+ parts[parts.length - 1] = outName
152
+ const dest = join(targetDir, parts.join('/'))
153
+
154
+ await mkdir(dirname(dest), { recursive: true })
155
+
156
+ const isText = TEXT_EXT.test(baseName) || TEXT_BASENAMES.has(baseName)
157
+ if (isText) {
158
+ const raw = await readFile(src, 'utf8')
159
+ await writeFile(dest, applyTokens(raw, tokens))
160
+ } else {
161
+ await cp(src, dest)
162
+ }
163
+ }
164
+
165
+ process.stdout.write(
166
+ [
167
+ `Scaffolded ${projectName} → ${targetDir}`,
168
+ '',
169
+ 'Next:',
170
+ ` cd ${targetDir}`,
171
+ ' pnpm install',
172
+ ' pnpm typecheck && pnpm test',
173
+ '',
174
+ 'Then walk CUSTOMIZE.md (the fill-checklist) and AGENTS.md (the behavior contract).',
175
+ '',
176
+ ].join('\n'),
177
+ )
178
+ }
179
+
180
+ main().catch((err) => {
181
+ process.stderr.write(`create-agent-app: ${err.message}\n`)
182
+ process.exit(1)
183
+ })
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@tangle-network/create-agent-app",
3
+ "version": "0.44.36",
4
+ "description": "Scaffold a new Tangle agent product on @tangle-network/agent-app: the tool-loop skeleton (default) or, with --chat, the assembled multimodal chat vertical (auth, chat store, streaming turns with replay, uploads, agent asks) with its own end-to-end test — plus the agent-followable breadcrumb docs (AGENTS.md / CUSTOMIZE.md).",
5
+ "keywords": [
6
+ "tangle",
7
+ "ai-agent",
8
+ "agent-framework",
9
+ "scaffold",
10
+ "create",
11
+ "cloudflare-workers"
12
+ ],
13
+ "license": "MIT",
14
+ "type": "module",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/tangle-network/agent-app.git",
18
+ "directory": "create-agent-app"
19
+ },
20
+ "bin": {
21
+ "create-agent-app": "./index.mjs"
22
+ },
23
+ "files": [
24
+ "index.mjs",
25
+ "template",
26
+ "template-chat"
27
+ ],
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "engines": {
32
+ "node": ">=20"
33
+ }
34
+ }
@@ -0,0 +1,11 @@
1
+ # Copy to .dev.vars for local `wrangler dev`. Do NOT commit .dev.vars.
2
+ # Model resolves via resolveTangleModelConfig (see src/agent-app.ts).
3
+
4
+ # Tangle Router (OpenAI-compatible, default path):
5
+ TANGLE_API_KEY=
6
+ MODEL_NAME=
7
+
8
+ # BYOK Anthropic (set MODEL_PROVIDER=anthropic to use):
9
+ # MODEL_PROVIDER=anthropic
10
+ # ANTHROPIC_API_KEY=
11
+ # ANTHROPIC_BASE_URL=https://api.anthropic.com
@@ -0,0 +1,67 @@
1
+ # AGENTS.md — you are customizing an agent-app
2
+
3
+ You are a coding agent working in a project generated by `create-agent-app`. This
4
+ project is a thin customization layer on top of `@tangle-network/agent-app` (the
5
+ shell) and the Tangle agent substrate (the engine). Walk this contract before you
6
+ touch anything. It is a checklist, not prose — follow it in order.
7
+
8
+ ## 0. Orient
9
+
10
+ - [ ] Read this file end to end.
11
+ - [ ] Read `CUSTOMIZE.md` — the ordered fill-checklist. It is your task list.
12
+ - [ ] Read `KNOWLEDGE.md` — how the build-loop vs the act-gate work.
13
+ - [ ] Run `pnpm install && pnpm typecheck && pnpm test`. Confirm green BEFORE editing.
14
+
15
+ ## 1. The one rule (layering)
16
+
17
+ > You change behavior by editing DATA and the COMPOSER — never by editing
18
+ > `@tangle-network/agent-app`.
19
+
20
+ - The shell (`@tangle-network/agent-app/*`) owns mechanism: the tool loop, the
21
+ agent→app tool side channel, the approval/proposal contract, the knowledge gate,
22
+ billing, crypto.
23
+ - The engine (`@tangle-network/agent-eval`, `agent-runtime`, `agent-integrations`)
24
+ is a peer dependency. Do not bundle it, do not fork it.
25
+ - You own: `agent.config.ts` (DATA) + `src/` (the COMPOSER + routes) + `knowledge/`
26
+ (DATA). That is the whole surface.
27
+
28
+ ## 2. DATA vs CODE — know which file you're in
29
+
30
+ - [ ] DATA → `agent.config.ts`: identity, taxonomy, knowledge requirements/sources,
31
+ integrations, ui, model. Plain values. No control flow. No imports of engine
32
+ internals. If you're writing an `if`, you're in the wrong file.
33
+ - [ ] DATA → `knowledge/`: domain documents. Content, not code. No secrets.
34
+ - [ ] CODE → `src/agent-app.ts`: the COMPOSER. Wires config + bindings into the
35
+ shell's seams. Override ONE handler here only when the preset genuinely can't
36
+ express your persistence — default to the preset.
37
+ - [ ] CODE → `src/worker.ts`: the chat route. Extend the route; recover trusted
38
+ per-turn context from real auth. Do not move domain values here.
39
+
40
+ ## 3. Invariants — fail-closed, never relax
41
+
42
+ - [ ] HUMAN-IN-THE-LOOP: every regulated proposal type (`config.taxonomy.regulatedTypes`)
43
+ is routed to a named human and CANNOT auto-execute. Keep regulated types in
44
+ `regulatedTypes`. Never downgrade a regulated action to an immediate tool.
45
+ - [ ] `regulatedTypes` ⊆ `proposalTypes`. The test enforces this; keep it green.
46
+ - [ ] GROUNDING: never fabricate a domain figure (price, coverage, clause, id). A
47
+ real record or an explicit "NOT ON FILE". The persona fragment says this — keep it.
48
+ - [ ] TRUSTED CONTEXT: `userId` / `workspaceId` / `threadId` come from the server
49
+ session, NEVER from model tool args. The model must not forge identity.
50
+ - [ ] SOURCES ALWAYS RECORDED, PROPOSALS GATED: in the knowledge loop, grounding is
51
+ never dropped; a low-confidence knowledge PROPOSAL is. Propose, don't apply.
52
+
53
+ ## 4. Verify (every change ends here)
54
+
55
+ - [ ] `pnpm typecheck` — clean. Proves `agent.config.ts` matches the shell contract.
56
+ - [ ] `pnpm test` — green. Proves the composer wiring + invariants.
57
+ - [ ] `pnpm knowledge:ingest` — enumerates your docs + sources (DRY) without error.
58
+ - [ ] For a real deploy: `pnpm dev` (wrangler) after filling `wrangler.toml`.
59
+
60
+ If any of these is red, you are not done. Do not weaken a test to pass.
61
+
62
+ ## 5. The schema floor
63
+
64
+ `agent.config.ts` is validated by the TypeScript type `AgentAppConfig` from
65
+ `@tangle-network/agent-app/config`. A non-TS agent can read the same shape from its
66
+ JSON Schema: `agentAppConfigJsonSchema` (exported from that subpath). That schema +
67
+ this file + `CUSTOMIZE.md` are the complete spec for what you may put where.
@@ -0,0 +1,6 @@
1
+ # CLAUDE.md
2
+
3
+ The behavior contract for this project lives in `AGENTS.md`. Read it first, then
4
+ walk `CUSTOMIZE.md` (the fill-checklist) and `KNOWLEDGE.md` (build-loop vs act-gate).
5
+
6
+ @AGENTS.md
@@ -0,0 +1,83 @@
1
+ # CUSTOMIZE.md — fill this project, in order
2
+
3
+ This is the trail. Walk it top to bottom. Each step is a checklist item paired with
4
+ the DISCOVERY QUESTION it answers — answer the question, then make the edit. The
5
+ whole job is filling `agent.config.ts` (DATA) and seeding `knowledge/` (DATA). You
6
+ touch `src/` only at step ⑤ and only if the preset can't express your stack.
7
+
8
+ When every box is checked and `pnpm typecheck && pnpm test && pnpm knowledge:ingest`
9
+ are green, the agent is customized.
10
+
11
+ ---
12
+
13
+ ## ① Identity — `agent.config.ts` → `identity`
14
+
15
+ Discovery: **Whose job does this agent do, in whose voice, under what hard rules?**
16
+
17
+ - [ ] Set `identity.name` to the product/agent name.
18
+ - [ ] Rewrite `identity.persona` as the real one-paragraph role + voice + remit. This
19
+ is the spine of the system prompt.
20
+ - [ ] Add hard rules + standing-workflow summaries to `systemPromptFragments`.
21
+ - [ ] Keep/edit the grounding rule and the `not-advice` disclaimer. Add any
22
+ regulatory notice your domain requires under `disclaimers`.
23
+
24
+ ## ② Taxonomy — `agent.config.ts` → `taxonomy`
25
+
26
+ Discovery: **Which actions change client state or are legally gated, so a certified
27
+ human must approve before they execute?**
28
+
29
+ - [ ] List every action the agent can PROPOSE in `proposalTypes`.
30
+ - [ ] Put the regulated/state-changing subset in `regulatedTypes`. These cannot
31
+ auto-execute — they fail-closed to the approval queue.
32
+ - [ ] Confirm `regulatedTypes` ⊆ `proposalTypes` (the test checks this).
33
+
34
+ ## ③ Knowledge requirements (the ACT gate) — `agent.config.ts` → `knowledge.requirements`
35
+
36
+ Discovery: **What is the minimum the agent must have GROUNDED before it's allowed to
37
+ propose? What facts gate the loop?**
38
+
39
+ - [ ] For each gating fact, add a `KnowledgeRequirementSpec` with a declarative
40
+ `satisfiedBy`:
41
+ - a config field is set → `{ config: 'dot.path', nonEmpty: true }`
42
+ - rows exist in a workspace-scoped table → `{ table: 'name', minRows: N, statusIn: [...] }`
43
+ - combine with `{ anyOf: [...] }` / `{ allOf: [...] }`
44
+ - [ ] Use a `derive` function ONLY for a rule the declarative form can't express.
45
+ - [ ] Pick a real `category` / `acquisitionMode` / `importance` per spec (the
46
+ autocomplete lists the allowed values).
47
+
48
+ ## ④ Domain docs + research sources — `knowledge/` + `agent.config.ts` → `knowledge.sources`
49
+
50
+ Discovery: **What does the agent need to READ to be grounded, and where does fresh
51
+ knowledge come from?**
52
+
53
+ - [ ] Drop your real domain documents into `knowledge/` (md/txt/json, one topic per
54
+ file). See `knowledge/README.md`.
55
+ - [ ] List external research sources in `knowledge.sources` (URLs, regulation feeds,
56
+ integration refs) with a `kind`.
57
+ - [ ] Tune `knowledge.loop` (`goal`, `minConfidence`, `freshness`) — see KNOWLEDGE.md.
58
+
59
+ ## ⑤ Integrations — `agent.config.ts` → `integrations.enabled` (+ `src/` only if needed)
60
+
61
+ Discovery: **Which CRMs / data sources / messaging channels does the workflow touch?**
62
+
63
+ - [ ] Add the `@tangle-network/agent-integrations` catalog kinds to `enabled`.
64
+ - [ ] Only if the house preset can't persist your data: override a single handler in
65
+ `src/agent-app.ts`. Default to the preset; do not fork the shell.
66
+
67
+ ## ⑥ Ingest — `pnpm knowledge:ingest`
68
+
69
+ Discovery: **Did the loop pick up exactly the docs and sources I expect?**
70
+
71
+ - [ ] Run `pnpm knowledge:ingest` (DRY). Confirm the listed docs + sources match.
72
+ - [ ] Wire a model-backed driver + decider, then `pnpm knowledge:ingest --run` to
73
+ drive the acquisition loop (see KNOWLEDGE.md).
74
+
75
+ ## ⑦ Verify
76
+
77
+ Discovery: **Does the customized agent hold its contract?**
78
+
79
+ - [ ] `pnpm typecheck` — clean.
80
+ - [ ] `pnpm test` — green (invariants + composer wiring).
81
+ - [ ] `pnpm knowledge:ingest` — enumerates without error.
82
+ - [ ] Fill `wrangler.toml` (D1 id, KV id, `MODEL_NAME`), run the preset migration,
83
+ then `pnpm dev` to exercise `/chat` for real.
@@ -0,0 +1,73 @@
1
+ # KNOWLEDGE.md — the build-loop and the act-gate
2
+
3
+ Two different things share the word "knowledge". Keep them straight.
4
+
5
+ ## The two loops
6
+
7
+ - BUILD LOOP (acquire) — `pnpm knowledge:ingest` → `@tangle-network/agent-app/knowledge-loop`.
8
+ Reads `knowledge/` + `agent.config.ts` `knowledge.sources`, researches, and
9
+ PROPOSES grounded knowledge pages. Source-grounded, propose-don't-apply.
10
+ - ACT GATE (block) — `knowledge.requirements` in `agent.config.ts`, scored at runtime
11
+ by the Cloudflare preset's `KnowledgeStateAccessor`. Decides whether the agent
12
+ KNOWS enough to be allowed to propose an action.
13
+
14
+ Build fills the well; the gate decides if the well is deep enough to act. They are
15
+ configured separately and run at different times (ingest is offline/Node; the gate
16
+ is per-request).
17
+
18
+ ## Build loop — how to drive it
19
+
20
+ - [ ] Put domain docs in `knowledge/` and external sources in `knowledge.sources`.
21
+ - [ ] `pnpm knowledge:ingest` (DRY) — confirms inputs without spending model calls.
22
+ - [ ] Wire a model-backed `driver` and run `--run`:
23
+
24
+ ```ts
25
+ import { createKnowledgeLoop } from '@tangle-network/agent-app/knowledge-loop'
26
+ import { config } from '../agent.config'
27
+
28
+ const loop = createKnowledgeLoop(config.knowledge, {
29
+ root: 'knowledge', // a KB layout on disk (Node only)
30
+ driver: async ({ systemPrompt, userMessage }) =>
31
+ ({ finalText: await callYourModel(systemPrompt, userMessage) }),
32
+ defaultMinConfidence: config.knowledge.loop?.minConfidence ?? 0.7,
33
+ })
34
+ const result = await loop.run()
35
+ ```
36
+
37
+ Runs in Node (it touches the filesystem). NEVER on the Worker edge path — drive it
38
+ from `scripts/knowledge-ingest.mjs`, CI, or a sandbox/delegation context.
39
+
40
+ ## Multimodal sources
41
+
42
+ The default source adapter is text. To ingest audio / video / image sources, pass a
43
+ `SourceAdapter` for that medium via `deps.adapters` — it is tried BEFORE text, so it
44
+ claims its media first:
45
+
46
+ ```ts
47
+ createKnowledgeLoop(config.knowledge, {
48
+ root: 'knowledge',
49
+ adapters: [audioSourceAdapter], // tried before the built-in text adapter
50
+ driver,
51
+ })
52
+ ```
53
+
54
+ Declare such sources in `agent.config.ts` with a `kind` your adapter recognizes
55
+ (e.g. `{ uri: 'vault://calls/2026-06.m4a', kind: 'audio' }`).
56
+
57
+ ## Tuning the gate (judges / confidence / freshness)
58
+
59
+ The build loop accepts a pluggable decider — an agentic judge OR a deterministic /
60
+ sandbox check. Defaults to a reviewer that applies a candidate's proposal only when
61
+ `confidence >= minConfidence`; below it the proposal is dropped but its SOURCES are
62
+ still recorded.
63
+
64
+ - CONFIDENCE — raise `knowledge.loop.minConfidence` to demand stronger grounding
65
+ before a proposed page is accepted; lower it to accept more, weaker candidates.
66
+ - JUDGE — pass a custom `decide` (a `KnowledgeDecider`) to replace the default
67
+ reviewer with your own judge (LLM-as-judge, lint, or sandbox verification).
68
+ - FRESHNESS — set `knowledge.loop.freshness` (e.g. `static` / `session` / `daily`);
69
+ the decider receives it to decide whether cached knowledge is still valid.
70
+
71
+ Per-requirement freshness on the ACT gate is set on each
72
+ `KnowledgeRequirementSpec.freshness` — that controls how stale a satisfied
73
+ requirement may be before the gate stops crediting it.
@@ -0,0 +1,41 @@
1
+ # __PROJECT_NAME__
2
+
3
+ A Tangle agent product scaffolded with `create-agent-app`, built on
4
+ [`@tangle-network/agent-app`](https://github.com/tangle-network/agent-app) (the
5
+ application-shell framework) + the Cloudflare preset (D1 + Drizzle + KV).
6
+
7
+ ## Layout
8
+
9
+ | Path | Surface | Edit when |
10
+ |---|---|---|
11
+ | `agent.config.ts` | DATA — identity, taxonomy, knowledge gate, integrations | defining the agent |
12
+ | `knowledge/` | DATA — domain documents the agent grounds on | adding domain knowledge |
13
+ | `src/agent-app.ts` | CODE — the composer (config + bindings → shell seams) | overriding a handler |
14
+ | `src/worker.ts` | CODE — the chat route | extending the route / auth |
15
+ | `scripts/knowledge-ingest.mjs` | the build-loop entry (`pnpm knowledge:ingest`) | ingesting knowledge |
16
+
17
+ ## Get started
18
+
19
+ ```bash
20
+ pnpm install
21
+ pnpm typecheck && pnpm test
22
+ ```
23
+
24
+ Then walk the trail:
25
+
26
+ 1. `AGENTS.md` — the behavior contract (you are customizing an agent-app).
27
+ 2. `CUSTOMIZE.md` — the ordered fill-checklist.
28
+ 3. `KNOWLEDGE.md` — build-loop vs act-gate.
29
+
30
+ ## Scripts
31
+
32
+ - `pnpm dev` — run the worker locally (fill `wrangler.toml` first).
33
+ - `pnpm typecheck` — `tsc --noEmit`.
34
+ - `pnpm test` — vitest.
35
+ - `pnpm knowledge:ingest` — enumerate + drive the knowledge acquisition loop.
36
+ - `pnpm deploy` — `wrangler deploy`.
37
+
38
+ ## Invariants
39
+
40
+ Regulated proposals are human-approved and never auto-execute. Domain figures are
41
+ grounded in real records, never fabricated. See `AGENTS.md` for the full contract.
@@ -0,0 +1,5 @@
1
+ node_modules
2
+ dist
3
+ .wrangler
4
+ .dev.vars
5
+ *.log
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "__PACKAGE_NAME__",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "wrangler dev",
8
+ "deploy": "wrangler deploy",
9
+ "typecheck": "tsc --noEmit",
10
+ "test": "vitest run",
11
+ "test:watch": "vitest",
12
+ "knowledge:ingest": "node scripts/knowledge-ingest.mjs"
13
+ },
14
+ "dependencies": {
15
+ "@tangle-network/agent-app": "__AGENT_APP_VERSION__"
16
+ },
17
+ "peerDependencies": {
18
+ "@tangle-network/agent-eval": "0.135.1",
19
+ "@tangle-network/agent-integrations": ">=0.44.0",
20
+ "@tangle-network/agent-interface": "0.36.0",
21
+ "@tangle-network/agent-runtime": "0.109.0"
22
+ },
23
+ "devDependencies": {
24
+ "@tangle-network/agent-eval": "0.135.1",
25
+ "@tangle-network/agent-integrations": "^0.44.0",
26
+ "@tangle-network/agent-interface": "0.36.0",
27
+ "@tangle-network/agent-runtime": "0.109.0",
28
+ "@types/node": "^25.6.0",
29
+ "typescript": "^5.7.0",
30
+ "vitest": "^3.0.0",
31
+ "wrangler": "^4.0.0"
32
+ }
33
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "lib": ["ES2022", "DOM"],
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "noUncheckedIndexedAccess": true,
12
+ "noEmit": true,
13
+ "types": ["node"]
14
+ },
15
+ "include": ["agent.config.ts", "src", "tests"]
16
+ }
@@ -0,0 +1,22 @@
1
+ name = "__PACKAGE_NAME__"
2
+ main = "src/worker.ts"
3
+ compatibility_date = "2025-01-01"
4
+ compatibility_flags = ["nodejs_compat"]
5
+
6
+ # The house Cloudflare stack the preset composes: D1 + KV.
7
+ # Run `wrangler d1 create __PACKAGE_NAME__` and paste the database_id; create a
8
+ # KV namespace and paste its id. Then run the preset migration (see CUSTOMIZE.md ⑥).
9
+
10
+ [[d1_databases]]
11
+ binding = "DB"
12
+ database_name = "__PACKAGE_NAME__"
13
+ database_id = "REPLACE_WITH_D1_DATABASE_ID"
14
+
15
+ [[kv_namespaces]]
16
+ binding = "VAULT"
17
+ id = "REPLACE_WITH_KV_NAMESPACE_ID"
18
+
19
+ # Model resolves from env via resolveTangleModelConfig (TANGLE_API_KEY / MODEL_NAME).
20
+ # Put secrets in .dev.vars locally; `wrangler secret put` for production.
21
+ [vars]
22
+ MODEL_NAME = "REPLACE_WITH_MODEL"