@anvia/cli 1.2.0 → 1.3.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 (53) hide show
  1. package/README.md +32 -0
  2. package/dist/chunk-ERCP4EIZ.js +607 -0
  3. package/dist/chunk-ERCP4EIZ.js.map +1 -0
  4. package/dist/cli.js +278 -74
  5. package/dist/cli.js.map +1 -1
  6. package/dist/index.d.ts +54 -1
  7. package/dist/index.js +21 -3
  8. package/dist/skills/anvia-agent/SKILL.md +59 -0
  9. package/dist/skills/anvia-agent/references/agent-options.md +83 -0
  10. package/dist/skills/anvia-agent/references/providers.md +22 -0
  11. package/dist/skills/anvia-agent/references/teams.md +84 -0
  12. package/dist/skills/anvia-agent/references/tools.md +71 -0
  13. package/dist/skills/anvia-agent/scripts/check-agent.sh +99 -0
  14. package/dist/skills/anvia-channels/SKILL.md +59 -0
  15. package/dist/skills/anvia-channels/references/adapters.md +53 -0
  16. package/dist/skills/anvia-channels/references/channel-agent.md +52 -0
  17. package/dist/skills/anvia-channels/references/delivery.md +53 -0
  18. package/dist/skills/anvia-channels/scripts/check-channels.sh +79 -0
  19. package/dist/skills/anvia-chat/SKILL.md +61 -0
  20. package/dist/skills/anvia-chat/references/react-ui.md +104 -0
  21. package/dist/skills/anvia-chat/references/server-protocol.md +53 -0
  22. package/dist/skills/anvia-chat/references/transports-state.md +71 -0
  23. package/dist/skills/anvia-chat/scripts/check-chat-boundary.sh +70 -0
  24. package/dist/skills/anvia-evals/SKILL.md +51 -0
  25. package/dist/skills/anvia-evals/references/judges.md +61 -0
  26. package/dist/skills/anvia-evals/references/metrics.md +45 -0
  27. package/dist/skills/anvia-evals/references/running.md +62 -0
  28. package/dist/skills/anvia-evals/scripts/check-evals.sh +73 -0
  29. package/dist/skills/anvia-mcp/SKILL.md +49 -0
  30. package/dist/skills/anvia-mcp/references/clients.md +73 -0
  31. package/dist/skills/anvia-mcp/references/safety.md +25 -0
  32. package/dist/skills/anvia-mcp/scripts/check-mcp.sh +73 -0
  33. package/dist/skills/anvia-pipeline/SKILL.md +52 -0
  34. package/dist/skills/anvia-pipeline/references/agents-extract.md +62 -0
  35. package/dist/skills/anvia-pipeline/references/steps-compose.md +63 -0
  36. package/dist/skills/anvia-pipeline/scripts/check-pipeline.sh +69 -0
  37. package/dist/skills/anvia-rag/SKILL.md +50 -0
  38. package/dist/skills/anvia-rag/references/graph-rag.md +115 -0
  39. package/dist/skills/anvia-rag/references/pipeline.md +81 -0
  40. package/dist/skills/anvia-rag/references/rag-tool.md +43 -0
  41. package/dist/skills/anvia-rag/references/stores.md +68 -0
  42. package/dist/skills/anvia-rag/scripts/check-rag.sh +75 -0
  43. package/dist/skills/anvia-studio/SKILL.md +45 -0
  44. package/dist/skills/anvia-studio/references/inspect.md +33 -0
  45. package/dist/skills/anvia-studio/references/observe.md +34 -0
  46. package/dist/skills/anvia-studio/references/serve.md +65 -0
  47. package/dist/skills/anvia-studio/scripts/check-studio.sh +59 -0
  48. package/dist/skills/release-notes/SKILL.md +18 -0
  49. package/dist/skills/release-notes/references/style.md +6 -0
  50. package/dist/skills/release-notes/scripts/draft.sh +22 -0
  51. package/package.json +2 -2
  52. package/dist/chunk-IH4ZNWHB.js +0 -285
  53. package/dist/chunk-IH4ZNWHB.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,21 +1,39 @@
1
1
  import {
2
2
  addRegistryItem,
3
+ bundledSkillsDirectory,
3
4
  closestRegistryItemName,
5
+ collectSkillFiles,
4
6
  createRegistryItem,
7
+ initSkills,
5
8
  initializeProject,
6
9
  inspectInstalledItems,
10
+ inspectInstalledSkills,
7
11
  isRegistryItemName,
12
+ isSkillsTarget,
8
13
  registryItemNames,
9
- updateInstalledItems
10
- } from "./chunk-IH4ZNWHB.js";
14
+ skillNames,
15
+ skillsTargetDirectory,
16
+ skillsTargetNames,
17
+ updateInstalledItems,
18
+ updateSkills
19
+ } from "./chunk-ERCP4EIZ.js";
11
20
  export {
12
21
  addRegistryItem,
22
+ bundledSkillsDirectory,
13
23
  closestRegistryItemName,
24
+ collectSkillFiles,
14
25
  createRegistryItem,
26
+ initSkills,
15
27
  initializeProject,
16
28
  inspectInstalledItems,
29
+ inspectInstalledSkills,
17
30
  isRegistryItemName,
31
+ isSkillsTarget,
18
32
  registryItemNames,
19
- updateInstalledItems
33
+ skillNames,
34
+ skillsTargetDirectory,
35
+ skillsTargetNames,
36
+ updateInstalledItems,
37
+ updateSkills
20
38
  };
21
39
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: anvia-agent
3
+ description: Build Anvia agents and tools — Agent options, tool definitions, approvals, memory, streaming, multi-agent teams, and provider wiring.
4
+ ---
5
+
6
+ # Anvia Agent Skill
7
+
8
+ Use this skill when the user wants to build or change an Anvia agent: defining
9
+ tools, configuring the `Agent`, handling approvals and streaming, adding memory,
10
+ composing specialists, or wiring a provider model.
11
+
12
+ ## Process
13
+
14
+ 1. Define tools first (`references/tools.md`) — zod schemas, approvals.
15
+ 2. Create the `Agent` (`references/agent-options.md`) — smallest option set that works.
16
+ 3. Wire the provider model (`references/providers.md`) — keep vendor SDKs in provider packages.
17
+ 4. Compose specialists when needed (`references/teams.md`) — subagents as tools, or `AgentTeam`.
18
+ 5. Run `scripts/check-agent.sh` from the app root before claiming done.
19
+
20
+ ## Minimal slice
21
+
22
+ ```ts
23
+ import { Agent } from "@anvia/core/agent";
24
+ import { createTool } from "@anvia/core/tool";
25
+ import { OpenAIClient } from "@anvia/openai";
26
+ import { z } from "zod";
27
+
28
+ const addTool = createTool({
29
+ name: "add",
30
+ description: "Add two numbers together.",
31
+ inputSchema: z.object({
32
+ x: z.number().describe("The first number."),
33
+ y: z.number().describe("The second number."),
34
+ }),
35
+ outputSchema: z.number(),
36
+ execute: (args) => args.x + args.y,
37
+ });
38
+
39
+ const client = new OpenAIClient({
40
+ baseUrl: process.env.OPENAI_BASEURL,
41
+ apiKey: process.env.OPENAI_API_KEY ?? "",
42
+ });
43
+ const agent = new Agent({
44
+ id: "agent",
45
+ model: client.completionModel({ modelId: "gpt-6-astra", api: "responses" }),
46
+ instructions: "You are a concise assistant. Use tools when useful.",
47
+ maxTurns: 2,
48
+ tools: [addTool],
49
+ });
50
+
51
+ const response = await agent.generate({ prompt: "What is 12 + 30? Use the add tool." });
52
+ if (response.type !== "response") throw new Error("Unexpected tool approval request.");
53
+ console.log(response.output);
54
+ ```
55
+
56
+ ## Output
57
+
58
+ Prefer one agent, few tools, explicit zod schemas. Point to the relevant
59
+ reference file instead of pasting its contents into chat.
@@ -0,0 +1,83 @@
1
+ # Agent Options
2
+
3
+ ```ts
4
+ const agent = new Agent({
5
+ id: "agent", // required, stable across restarts
6
+ model, // required CompletionModel from a provider package
7
+ instructions: "...", // system guidance; keep it task-specific
8
+ tools: [...], // AnyTool | ProviderTool | ToolIndex
9
+ mcpServers: [...], // MCP servers (the only path for MCP tools)
10
+ skills: skillSet, // SkillSet from loadSkills() — Agent merges its tools
11
+ context: [...], // static Document or VectorContext entries
12
+ memory: { store }, // MemoryStore for session continuity
13
+ outputSchema: schema, // zod schema for structured final output
14
+ toolChoice, // constrain or force tool use
15
+ maxTurns, // bound runaway loops (e.g. 2-4 for demos)
16
+ middlewares: [...], // AgentMiddleware for cross-cutting tool behavior
17
+ guardrails, // policy input for safety constraints
18
+ temperature, // maxTokens, retries, controls, providerOptions as needed
19
+ });
20
+ ```
21
+
22
+ ## generate vs stream
23
+
24
+ - `agent.generate({ prompt, session? })` — one shot. Returns `{ type: "response" }`,
25
+ `{ type: "interaction" }` (approval/question), or `{ type: "blocked" }` (guardrail
26
+ stop); never throws for those.
27
+ - `agent.stream({ prompt, toolConcurrency? })` — events: `tool_call`,
28
+ `tool_result`, `text_delta`, `response`. Use `toolConcurrency` when parallel
29
+ specialist calls are safe. Requires a streaming-capable model — it throws
30
+ otherwise, so fall back to `generate` for chat-only models.
31
+
32
+ ## Memory
33
+
34
+ `memory: { store }` with a `MemoryStore` (`load` / `append` / `clear` over
35
+ `Message[]`), plus a `session` (`{ sessionId, userId }`) per call:
36
+
37
+ ```ts
38
+ const agent = new Agent({ id: "agent", model, instructions: "...", memory: { store } });
39
+ await agent.generate({ prompt: "Remember my project is named Anvia.", session });
40
+ await agent.generate({ prompt: "What is my project named?", session });
41
+ ```
42
+
43
+ Compaction (`afterTokens` / `recentTokens` + token counter + compactor) is opt-in
44
+ for long sessions — add it when context growth is the problem, not upfront.
45
+ `agent.compactMemory({ session })` forces that compaction on demand.
46
+
47
+ ## Multi-agent
48
+
49
+ Specialists route by their `name` + `description` text — both optional on
50
+ `Agent`, but give them explicitly (`asTool` falls back to a generic description
51
+ when omitted). `asTool` itself requires a tool `name` and an explicit
52
+ `suspension` policy:
53
+
54
+ ```ts
55
+ const coordinator = new Agent({
56
+ id: "coordinator",
57
+ model,
58
+ instructions: "Coordinate specialist agents through tools. ...",
59
+ maxTurns: 4,
60
+ tools: [supportAgent.asTool({ name: "ask_support_agent", suspension: "reject" })],
61
+ });
62
+ ```
63
+
64
+ Keep the coordinator's instructions to routing + synthesis. Give each specialist
65
+ a short task built only from user facts. Prefer `stream` with `toolConcurrency`
66
+ for parallel delegation. When members must talk, wait, and spawn each other,
67
+ use `AgentTeam` (`references/teams.md`); for static teams/pipelines, check the
68
+ cookbook (`07_multi_agent`, `05_pipelines`) before inventing a new pattern.
69
+
70
+ ## Skills wiring
71
+
72
+ ```ts
73
+ import { loadSkills, skill } from "@anvia/core/skills";
74
+
75
+ const skills = await loadSkills(skill.local("./skills"));
76
+ const agent = new Agent({ id: "agent", model, instructions: "...", skills });
77
+ ```
78
+
79
+ `skill.local(path)` treats the path as one skill when it holds `SKILL.md`,
80
+ otherwise as a folder of skills. `loadSkills` builds the system instructions
81
+ (name + description + reference/script listing) and the four lazy
82
+ `get_skill_*` / `run_skill_script` tools — the Agent only pulls full
83
+ instructions, references, or scripts when relevant.
@@ -0,0 +1,22 @@
1
+ # Providers
2
+
3
+ Provider behavior lives in provider packages, never in `@anvia/core`:
4
+
5
+ - `@anvia/openai`, `@anvia/anthropic`, `@anvia/gemini`, `@anvia/mistral`,
6
+ `@anvia/grok` — check the owning package README for client names and model options.
7
+ - Typical shape: `new OpenAIClient({ baseUrl, apiKey })`, then
8
+ `client.completionModel({ modelId, api: "responses" })` for the Agent model.
9
+ - Embedding, transcription, and image/audio generation follow the same split:
10
+ core contracts, provider adapters in `packages/provider-*`, local embeddings
11
+ in `packages/embedding-transformers`.
12
+
13
+ ## Rules
14
+
15
+ - Never import a vendor SDK in `@anvia/core` (or in app code that claims to be
16
+ provider-agnostic) — add a provider adapter instead.
17
+ - Read credentials from the environment at the boundary
18
+ (`process.env.OPENAI_API_KEY ?? ""`); never commit keys or trace payloads.
19
+ - Tests must mock or fake provider SDKs. Ordinary package tests must not need
20
+ network access or real API keys.
21
+ - Match the package's import style: some packages use extensionless local
22
+ imports, many adapters use `.js` suffixes in source imports.
@@ -0,0 +1,84 @@
1
+ # Subagents and Teams
2
+
3
+ Two composition shapes, chosen by who decides the control flow: when you can
4
+ name the delegation graph, wire agents together yourself; when the model must
5
+ decide who to call, give it subagent tools; when instances must talk, wait, and
6
+ spawn each other, use `AgentTeam`.
7
+
8
+ ## Subagents as tools (you decide)
9
+
10
+ `agent.asTool({ name, suspension })` wraps an Agent as a callable tool of
11
+ another (see `references/agent-options.md` for the coordinator example).
12
+ Prefer this when the delegation graph is static and shallow — one coordinator
13
+ calling named specialists, optionally fanned out in parallel via
14
+ `toolConcurrency`. Each call is independent; subagents share nothing but the
15
+ model.
16
+
17
+ ## AgentTeam (the model decides)
18
+
19
+ `AgentTeam` from `@anvia/core/agent` runs a coordinator plus member agents as
20
+ one supervised runtime:
21
+
22
+ ```ts
23
+ import { Agent, AgentTeam } from "@anvia/core/agent";
24
+
25
+ const researcher = new Agent({ id: "researcher", model, instructions: "Find facts only." });
26
+ const writer = new Agent({ id: "writer", model, instructions: "Draft from findings only." });
27
+
28
+ const team = new AgentTeam({
29
+ id: "research-team", // coordinator options (id, model, instructions, tools, memory...)
30
+ model,
31
+ instructions: "Coordinate research and drafting through your member tools.",
32
+ members: [researcher, writer], // required; Agent instances, unique ids
33
+ communication: { siblings: true }, // default false
34
+ limits: { maxConcurrentAgents: 4 },
35
+ });
36
+
37
+ const outcome = await team.generate({ prompt: "Brief the team and produce a memo." });
38
+ console.log(
39
+ outcome.output,
40
+ outcome.usage,
41
+ outcome.members.map((m) => `${m.name}:${m.status}`),
42
+ );
43
+ ```
44
+
45
+ Constructor options are coordinator `AgentOptions` plus:
46
+
47
+ - `members` — required array of `Agent` instances. Member ids must be unique
48
+ and match `^[a-zA-Z0-9_-]{1,58}$` because they become spawn tool names.
49
+ - `spawning: [{ from, to }]` — additional spawn permissions between members;
50
+ the coordinator can always spawn every member.
51
+ - `communication: { siblings: true }` — allow messaging and waiting between
52
+ instances with the same parent (default false).
53
+ - `limits` — `maxDepth` (3), `maxConcurrentAgents` (4), `maxAgentInstances`
54
+ (12), `maxTotalTurns` (100), `maxBufferedEvents` (1024). Breaching a limit
55
+ raises `AgentTeamLimitError`; unread stream-event overflow cancels the run.
56
+
57
+ The runtime injects reserved tools — `spawn_<memberId>`, `send_message`,
58
+ `wait_for_agent`, `list_agents`, `cancel_agent`. A member tool with one of
59
+ those names throws at construction.
60
+
61
+ ## Running teams
62
+
63
+ - `generate({ prompt })` or `{ messages }` — exactly one. Returns the
64
+ coordinator outcome (`response` or `blocked`) plus `teamRunId`, aggregate
65
+ `usage`, and per-instance `members` summaries (status, usage, outcome).
66
+ - `stream()` — async events: `agent_queued` / `agent_started` /
67
+ `agent_waiting` / `agent_idle` / `agent_failed` / `agent_cancelled`,
68
+ `message_queued` / `message_delivered`, `interaction`, and nested
69
+ `agent_event`s (each tagged with the emitting instance). Also exposes
70
+ `textStream`, `text`, `result`, `steer(input)`, and `cancel(reason?)`.
71
+ - Interactions from any member (tool approvals, questions) must be resolved at
72
+ the team level: pass `resolveInteraction(request) => response` on the run.
73
+ Without it, an interaction fails the run with `AgentTeamInteractionError`.
74
+
75
+ ## Rules
76
+
77
+ - Keep members narrow: one task each, explicit `name` + `description`, facts
78
+ in, facts out — the coordinator routes by that text.
79
+ - One coordinator; do not nest teams inside teams. Escalating depth is what
80
+ `limits.maxDepth` is there to stop.
81
+ - A fixed sequence of agent steps is a pipeline (`.agent()` stage — see the
82
+ `anvia-pipeline` skill), not a team.
83
+ - Studio renders team runs with steer, cancel, and interaction cards — see the
84
+ `anvia-studio` skill.
@@ -0,0 +1,71 @@
1
+ # Tools
2
+
3
+ Tools are created with `createTool` from `@anvia/core/tool`. Every tool needs a
4
+ `name`, a routing `description`, a zod `inputSchema`, and an `execute`
5
+ implementation. Input and output are validated at the boundary — describe every
6
+ schema field so the model fills it correctly.
7
+
8
+ ```ts
9
+ const tool = createTool({
10
+ name: "add",
11
+ description: "Add two numbers together.",
12
+ inputSchema: z.object({
13
+ x: z.number().describe("The first number."),
14
+ y: z.number().describe("The second number."),
15
+ }),
16
+ outputSchema: z.number(), // optional; when present, execute results are parsed against it
17
+ execute: (args) => args.x + args.y,
18
+ });
19
+ ```
20
+
21
+ ## Approvals
22
+
23
+ Side-effecting tools declare `requiresApproval` — a boolean, a static
24
+ `{ reason }`, or a function of the args. Approval requests suspend the run;
25
+ resume it explicitly:
26
+
27
+ ```ts
28
+ const tool = createTool({
29
+ name: "delete_account",
30
+ description: "Delete a customer account permanently.",
31
+ inputSchema: z.object({ accountId: z.string() }),
32
+ outputSchema: z.string(),
33
+ requiresApproval: ({ accountId }) => ({ reason: `Permanently delete ${accountId}?` }),
34
+ execute: ({ accountId }) => `Deleted account ${accountId}`,
35
+ });
36
+
37
+ let result = await agent.generate({ prompt: "..." });
38
+ while (result.type === "interaction") {
39
+ // result.interaction is e.g. { type: "tool-approval", toolName, reason }
40
+ result = await agent.resume(result.continuation, {
41
+ type: "tool-approval",
42
+ approved: false,
43
+ reason: "Reject sensitive operations in this demo.",
44
+ });
45
+ }
46
+ ```
47
+
48
+ Always handle the `interaction` result shape — `generate` does not throw for
49
+ approvals. The same interaction loop applies to `createQuestionTool` prompts.
50
+
51
+ ## Built-in and dynamic tools
52
+
53
+ - `createThinkTool()` — a scratchpad reasoning tool for agents that need visible
54
+ intermediate thought.
55
+ - `createQuestionTool()` — ask the caller a question mid-run (resolves through
56
+ the interaction loop like approvals).
57
+ - `ToolIndex` — expose a large or dynamic tool set behind one entry so the Agent
58
+ resolves tools at call time instead of listing them all in the prompt.
59
+ - `createMiddleware()` — observe or map around requests, responses, and tool
60
+ input/output; prefer it over re-implementing retry or mapping inside
61
+ `execute`.
62
+ - `createHook()` (from `@anvia/core/internal/agent`) — lifecycle actions
63
+ (continue, skip, terminate, approval). Per-run tool concurrency is the
64
+ `toolConcurrency` setting, not a middleware or hook concern.
65
+
66
+ ## MCP tools
67
+
68
+ MCP tools must be registered through `Agent.mcpServers`, never smuggled in via
69
+ `Agent.skills`. Skill tooling (`get_skill_instructions`, `get_skill_reference`,
70
+ `get_skill_script`, `run_skill_script`) is created by `loadSkills` and merged by
71
+ the Agent itself — pass the `SkillSet`, not its individual tools.
@@ -0,0 +1,99 @@
1
+ #!/bin/sh
2
+ # Heuristic checks for Anvia agent/tool code in an app directory.
3
+ # Usage: sh scripts/check-agent.sh [--dir <app-root>]
4
+ # Fails with a list of violations; passes silently with "agent OK".
5
+
6
+ DIR="."
7
+ if [ "$1" = "--dir" ] && [ -n "$2" ]; then
8
+ DIR="$2"
9
+ fi
10
+
11
+ ROOTS_FOUND=0
12
+ for root in "$DIR/src" "$DIR/app" "$DIR/lib" "$DIR/server"; do
13
+ if [ -d "$root" ]; then
14
+ ROOTS_FOUND=1
15
+ break
16
+ fi
17
+ done
18
+ if [ "$ROOTS_FOUND" -eq 0 ]; then
19
+ echo "ERROR: no src/app/lib/server directory under '$DIR' — nothing was checked."
20
+ echo "Run from the app root or pass --dir <app-root>."
21
+ exit 1
22
+ fi
23
+
24
+ fail=0
25
+ violation() {
26
+ echo "VIOLATION: $1"
27
+ fail=1
28
+ }
29
+ warning() {
30
+ echo "WARNING: $1"
31
+ }
32
+
33
+ scan() {
34
+ # $1 = include pattern; prints matching files under the usual roots.
35
+ grep -rln --include="$1" -e 'new Agent(' -e 'new AgentTeam(' -e 'createTool(' -e '.asTool(' \
36
+ "$DIR/src" "$DIR/app" "$DIR/lib" "$DIR/server" 2>/dev/null
37
+ }
38
+
39
+ FILES=$( { scan '*.ts'; scan '*.tsx'; } | sort -u)
40
+
41
+ if [ -z "$FILES" ]; then
42
+ echo "agent OK (no Agent/tool code found)"
43
+ exit 0
44
+ fi
45
+
46
+ # 1. createTool requires a zod inputSchema at the boundary.
47
+ violations=$(echo "$FILES" | while IFS= read -r f; do
48
+ awk -v file="$f" '
49
+ /createTool\(/ { in_tool=1; buf=""; depth=0 }
50
+ in_tool { buf=buf "\n" $0; depth+=gsub(/\(/,"(")-gsub(/\)/,")") }
51
+ in_tool && depth<=0 && length(buf)>0 {
52
+ if (buf !~ /inputSchema/) print "VIOLATION: " file ": createTool without inputSchema (see references/tools.md)."
53
+ in_tool=0
54
+ }
55
+ ' "$f"
56
+ done)
57
+ if [ -n "$violations" ]; then
58
+ echo "$violations"
59
+ fail=1
60
+ fi
61
+
62
+ # 2. asTool requires an explicit suspension policy.
63
+ if echo "$FILES" | xargs grep -h -A4 '.asTool(' 2>/dev/null | grep -q 'asTool('; then
64
+ echo "$FILES" | xargs grep -h -A4 '.asTool(' 2>/dev/null | grep -q 'suspension' || {
65
+ violation "agent.asTool without suspension policy (see references/agent-options.md)."
66
+ }
67
+ fi
68
+
69
+ # 3. new Agent requires id and model.
70
+ if echo "$FILES" | xargs grep -h -B1 -A8 'new Agent(' 2>/dev/null | grep -q 'new Agent('; then
71
+ echo "$FILES" | xargs grep -h -B1 -A8 'new Agent(' 2>/dev/null | grep -q 'id:' || {
72
+ violation "new Agent without id (see references/agent-options.md)."
73
+ }
74
+ echo "$FILES" | xargs grep -h -B1 -A8 'new Agent(' 2>/dev/null | grep -q 'model' || {
75
+ violation "new Agent without model (see references/providers.md)."
76
+ }
77
+ fi
78
+
79
+ # 4. Sensitive tool names without requiresApproval (warning only).
80
+ if echo "$FILES" | xargs grep -l -i -e 'name: *"[^"]*\(delete\|refund\|payment\|payroll\)' 2>/dev/null | grep -q .; then
81
+ hit=$(echo "$FILES" | xargs grep -l -i -e 'name: *"[^"]*\(delete\|refund\|payment\|payroll\)' 2>/dev/null)
82
+ echo "$hit" | xargs grep -L 'requiresApproval' 2>/dev/null | grep -q . && {
83
+ warning "sensitive tool without requiresApproval (see references/tools.md)."
84
+ }
85
+ fi
86
+
87
+ # 5. AgentTeam requires a members array.
88
+ if echo "$FILES" | xargs grep -l 'new AgentTeam(' 2>/dev/null | grep -q .; then
89
+ echo "$FILES" | xargs grep -h -A10 'new AgentTeam(' 2>/dev/null | grep -q 'members' || {
90
+ violation "new AgentTeam without members (see references/teams.md)."
91
+ }
92
+ fi
93
+
94
+ if [ "$fail" -eq 0 ]; then
95
+ echo "agent OK"
96
+ exit 0
97
+ fi
98
+ echo "See skills/anvia-agent/references/ for fixes."
99
+ exit 1
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: anvia-channels
3
+ description: Connect Discord, Slack, and Telegram to Anvia agents — channel adapters, agent-backed bots, proactive delivery, sessions, streaming, and platform actions.
4
+ ---
5
+
6
+ # Anvia Channels Skill
7
+
8
+ Use this skill when the user wants an Anvia agent reachable from Discord, Slack,
9
+ or Telegram, needs proactive delivery into a channel from a worker or monitor,
10
+ or wants to handle raw platform events. The five libraries are private workspace
11
+ packages — depend on them from the channels workspace (`workspace:*`), not npm.
12
+
13
+ ## Process
14
+
15
+ 1. Wire the platform adapter (`references/adapters.md`) — one factory call per
16
+ platform; credentials from the environment, never literals.
17
+ 2. Run an agent behind it (`references/channel-agent.md`) — sessions and memory,
18
+ streaming edits, acknowledgements, slash commands, paused approvals.
19
+ 3. Send or receive without an agent (`references/delivery.md`) —
20
+ `sendChannelMessage`, long-text splitting, validation, raw event handling.
21
+ 4. Run `scripts/check-channels.sh` from the app root before claiming done.
22
+
23
+ ## Minimal slice
24
+
25
+ ```ts
26
+ import { createChannelAgent } from "@anvia/channel-agent";
27
+ import { Agent } from "@anvia/core/agent";
28
+ import { OpenAIClient } from "@anvia/openai";
29
+ import { telegram } from "@anvia/telegram";
30
+
31
+ const openai = new OpenAIClient({ apiKey: process.env.OPENAI_API_KEY ?? "" });
32
+ const agent = new Agent({
33
+ id: "telegram-assistant",
34
+ model: openai.completionModel({ modelId: "gpt-6-astra", api: "responses" }),
35
+ instructions: "You are a concise assistant. Answer in the sender's language.",
36
+ });
37
+
38
+ const channel = telegram({
39
+ token: process.env.TELEGRAM_BOT_TOKEN ?? "",
40
+ onError: (error, context) => console.error(`[telegram:${context.operation}]`, error),
41
+ });
42
+
43
+ const service = createChannelAgent({
44
+ channel,
45
+ agent,
46
+ streaming: { placeholder: "Thinking…", editIntervalMs: 750 },
47
+ acknowledge: "👀",
48
+ onError: (error, context) => console.error(`[channel-agent:${context.stage}]`, error),
49
+ });
50
+
51
+ await service.start();
52
+ process.once("SIGTERM", () => void service.stop());
53
+ ```
54
+
55
+ ## Output
56
+
57
+ Prefer one adapter, one agent, environment-read credentials, and a shutdown path
58
+ for every started service. Point to the relevant reference file instead of
59
+ pasting its contents into chat.
@@ -0,0 +1,53 @@
1
+ # Channel adapters
2
+
3
+ Three first-party adapters, each a single factory call. All factories read
4
+ credentials from the environment at the call site — never a literal.
5
+
6
+ | Platform | Factory | Input transport | Delivery |
7
+ | -------- | --------------------------------------------------- | ----------------------- | -------- |
8
+ | Telegram | `telegram({ token })` from `@anvia/telegram` | Long polling or webhook | Bot API |
9
+ | Discord | `discord({ token })` from `@anvia/discord` | Gateway | REST |
10
+ | Slack | `slack({ appToken, botToken })` from `@anvia/slack` | Socket Mode | Web API |
11
+
12
+ ## Option shapes
13
+
14
+ Each factory accepts either live credentials or an injected low-level client for
15
+ offline tests — the unions are exclusive (`token` and `api` are never both set):
16
+
17
+ - `telegram({ token, baseUrl?, fetch?, maximumAttachmentBytes?, polling?, webhook?, onError? })`
18
+ or `telegram({ api })`. `onError(error, context)` receives
19
+ `context.operation`.
20
+ - `discord({ token, messageContentIntent?, fetch?, maximumAttachmentBytes?, onError? })`
21
+ or `discord({ gateway })`. Set `messageContentIntent` when the bot needs
22
+ message content (privileged intent on Discord's side).
23
+ - `slack({ appToken, botToken, fetch?, maximumAttachmentBytes? })` or
24
+ `slack({ transport })` — app-level + bot-level tokens are both required.
25
+
26
+ Adapters also expose the classes directly (`TelegramChannel`, `DiscordChannel`,
27
+ `SlackChannel`) if you need the type; prefer the factories.
28
+
29
+ ## Capabilities
30
+
31
+ Adapters advertise what the platform supports through `channel.capabilities` —
32
+ gate optional behavior on it instead of hard-coding per-platform branches.
33
+ All three support message edits, deletion, and reactions; files and
34
+ replies/threads everywhere; native actions differ (Discord buttons, Slack Block
35
+ Kit buttons, Telegram inline keyboard); typing indicators exist on Discord and
36
+ Telegram but not Slack.
37
+
38
+ ## Addresses and events
39
+
40
+ Every conversation is a `ChannelAddress`: `{ platform, conversationId }`.
41
+ `platform` is a plain string — custom adapters choose their own.
42
+
43
+ Inbound payloads are validated at runtime and normalized into a `ChannelEvent`
44
+ union: message, message-edited, message-deleted, reaction, action, or command
45
+ events. You rarely touch raw platform payloads unless you write an adapter.
46
+
47
+ ## Custom adapters
48
+
49
+ Implement `Channel<RawEvent>` from `@anvia/channel`: a `platform` string,
50
+ optional `capabilities`, send/edit/delete/react operations, and an event
51
+ subscription that normalizes raw payloads into `ChannelEvent` values at the
52
+ boundary. Wrap any adapter with `createRateLimitedChannel(channel, options)`
53
+ to throttle outbound delivery.
@@ -0,0 +1,52 @@
1
+ # Channel agent bridge
2
+
3
+ `createChannelAgent({ channel, agent, ... })` from `@anvia/channel-agent` runs
4
+ an Anvia `Agent` behind any adapter and returns a `ChannelAgentService` with
5
+ `start()` / `stop()`. `serveChannelAgent(options)` is the shorthand that also
6
+ starts it. The executor contract is structural — anything with `generate` /
7
+ `stream` (plus optional `resume`), i.e. an `Agent` — so tests can pass a fake.
8
+
9
+ ## Options that matter
10
+
11
+ - `shouldHandle(event)` — filter inbound messages; the default
12
+ (`defaultShouldHandleChannelEvent`) drops bot/self messages and non-text
13
+ noise. Override for allowlists or group-chat rules.
14
+ - `createSession(event)` — return a `MemoryScope` to scope agent memory per
15
+ conversation or per user. Defaults to `defaultChannelAgentSession`
16
+ (per-conversation); use `channelConversationUserSession` when each sender
17
+ needs their own thread of memory, `channelConversationSession` /
18
+ `channelConversationKey` for custom keys.
19
+ - `createPrompt({ event, context })` — reshape the prompt before the agent runs
20
+ (`channelMessagePrompt` is the default builder).
21
+ - `renderOutcome({ outcome, event })` — turn the agent outcome into the reply
22
+ text or a full `ChannelMessage` (actions, attachments).
23
+ - `streaming` — `{ enabled?, editIntervalMs?, placeholder? }`; the bridge edits
24
+ one message in place as the agent streams. `placeholder: false` disables the
25
+ "Thinking…" stub.
26
+ - `acknowledge` — shorthand for a receipt reaction: `"👀"` or
27
+ `{ reaction, completeReaction?, ... }`; `false` disables.
28
+ - `commands` — handle platform slash commands; `false` (default) ignores them.
29
+ - `multimodal` — inbound attachments become prompt parts when enabled.
30
+ - `errorMessage`, `emptyResponseMessage` — user-facing fallbacks.
31
+ - `onError(error, context)` — always set it. `context.stage` is one of
32
+ `"filter" | "prepare" | "acknowledge" | "interaction" | "agent" | "delivery"`,
33
+ which tells you where a failure happened.
34
+
35
+ ## Approvals and questions
36
+
37
+ Tool approvals and agent questions pause instead of failing: the bridge stores
38
+ the pending interaction and renders platform actions (buttons/inline keyboards)
39
+ so the conversation can resume when the user taps. `interactions` controls this
40
+ — pass `false` to disable, or options with a store:
41
+
42
+ - `MemoryChannelAgentInteractionStore` — in-memory, fine for dev.
43
+ - `SqliteChannelAgentInteractionStore` — durable; pending interactions survive
44
+ restarts.
45
+ - Helpers: `renderChannelAgentInteraction`, `parseChannelAgentActionResponse`,
46
+ `channelInteractionActions`, `channelInteractionKey`.
47
+
48
+ ## Lifecycle
49
+
50
+ `start()` begins receiving; `stop()` drains and disconnects. Own the shutdown
51
+ path: stop the service, then close memory/interaction-store clients. Guard
52
+ against double-shutdown when both SIGINT and SIGTERM arrive.