@codemation/agent-skills 0.4.0 → 0.5.1

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 (48) hide show
  1. package/CHANGELOG.md +165 -0
  2. package/dist/metadata.json +358 -48
  3. package/package.json +3 -1
  4. package/skills/builder/ai-agent/SKILL.md +314 -0
  5. package/skills/builder/ai-agent/references/anti-patterns.md +24 -0
  6. package/skills/{codemation-cli → builder/cli}/SKILL.md +1 -8
  7. package/skills/builder/connect-external-systems/SKILL.md +191 -0
  8. package/skills/builder/credential-development/SKILL.md +86 -0
  9. package/skills/{codemation-credential-development → builder/credential-development}/references/credential-patterns.md +3 -3
  10. package/skills/builder/custom-node-development/SKILL.md +61 -0
  11. package/skills/builder/custom-node-development/references/credential-aware-nodes.md +52 -0
  12. package/skills/builder/custom-node-development/references/define-batch-node.md +54 -0
  13. package/skills/{codemation-custom-node-development → builder/custom-node-development}/references/define-node-per-item.md +14 -14
  14. package/skills/{codemation-custom-node-development → builder/custom-node-development}/references/node-patterns.md +33 -49
  15. package/skills/builder/document-ai/SKILL.md +167 -0
  16. package/skills/builder/execution-context/SKILL.md +436 -0
  17. package/skills/{codemation-framework-concepts → builder/framework-concepts}/SKILL.md +10 -18
  18. package/skills/builder/gmail/SKILL.md +327 -0
  19. package/skills/builder/human-in-the-loop/SKILL.md +82 -0
  20. package/skills/{codemation-mcp-capabilities → builder/mcp-capabilities}/SKILL.md +4 -11
  21. package/skills/{codemation-mcp-capabilities → builder/mcp-capabilities}/references/agent-with-mcp.ts +1 -1
  22. package/skills/builder/msgraph/SKILL.md +338 -0
  23. package/skills/builder/odoo/SKILL.md +498 -0
  24. package/skills/{codemation-plugin-development → builder/plugin-development}/SKILL.md +4 -7
  25. package/skills/{codemation-plugin-development → builder/plugin-development}/references/plugin-anatomy.md +36 -15
  26. package/skills/{codemation-plugin-development → builder/plugin-development}/references/plugin-structure.md +2 -2
  27. package/skills/builder/rest-node/SKILL.md +148 -0
  28. package/skills/builder/testing/SKILL.md +142 -0
  29. package/skills/builder/workflow-dsl/SKILL.md +493 -0
  30. package/skills/builder/workspace-files/SKILL.md +191 -0
  31. package/skills/concierge/credentials/SKILL.md +91 -0
  32. package/skills/concierge/intake-automation-playbook/SKILL.md +78 -0
  33. package/skills/concierge/scenario-invoice-to-accounting/SKILL.md +48 -0
  34. package/skills/concierge/scenario-procurement-intake/SKILL.md +58 -0
  35. package/skills/codemation-ai-agent-node/SKILL.md +0 -66
  36. package/skills/codemation-ai-agent-node/references/anti-patterns.md +0 -11
  37. package/skills/codemation-credential-development/SKILL.md +0 -57
  38. package/skills/codemation-custom-node-development/SKILL.md +0 -61
  39. package/skills/codemation-custom-node-development/references/credential-aware-nodes.md +0 -38
  40. package/skills/codemation-custom-node-development/references/define-batch-node.md +0 -38
  41. package/skills/codemation-document-scanner/SKILL.md +0 -136
  42. package/skills/codemation-workflow-dsl/SKILL.md +0 -78
  43. package/skills/codemation-workflow-dsl/references/builder-patterns.md +0 -120
  44. package/skills/codemation-workflow-dsl/references/complete-example.md +0 -263
  45. package/skills/codemation-workflow-dsl/references/workflow-testing.md +0 -194
  46. package/skills/codemation-workspace-files/SKILL.md +0 -142
  47. /package/skills/{codemation-cli → builder/cli}/references/command-map.md +0 -0
  48. /package/skills/{codemation-framework-concepts → builder/framework-concepts}/references/architecture-map.md +0 -0
@@ -0,0 +1,191 @@
1
+ ---
2
+ name: workspace-files
3
+ description: Reads and writes files in the platform's shared workspace pool from inside a running workflow — listWorkspaceFilesNode, readWorkspaceFileNode, writeWorkspaceFileNode. Use this whenever an automation needs a file the platform manages (a lookup table, a config sheet, a concierge-derived JSON) or produces a file back into the pool.
4
+ compatibility: Codemation core-nodes-workspace-files. Requires WORKSPACE_ID and BLOB_STORAGE_* env vars.
5
+ tags: workspace, files, pool, binary, storage, read, write, list, csv, json, lookup
6
+ uses: "@codemation/core-nodes-workspace-files"
7
+ ---
8
+
9
+ # Codemation Workspace Files
10
+
11
+ ## The lifecycle — how an automation gets at a platform-managed file
12
+
13
+ The workspace **file pool** is shared storage (S3-backed) that the platform manages. A user does not
14
+ hand a file to a workflow directly. The real flow is:
15
+
16
+ 1. A user **uploads a heavy file** (e.g. an Excel product catalogue) through the platform.
17
+ 2. The **concierge** has the coding agent **transform** it into a workflow-friendly shape (e.g. a flat
18
+ JSON keyed by SKU) and **stores the result** in the pool under a known filename.
19
+ 3. A **running automation reads that stored file** by filename (or pinned id) via the nodes below — and
20
+ does its work with the data.
21
+
22
+ The automation is **decoupled** from the upload: it reads whatever the current version of that filename
23
+ is, every run. Two concrete use cases:
24
+
25
+ - **Product database for lookups.** A nightly order workflow reads `products.json` from the pool to
26
+ enrich each line item with price and description — no external API call, the sheet is the source.
27
+ - **Leads router.** An inbound-lead webhook reads a `zipcode-regions.json` table from the pool, maps the
28
+ lead's zipcode to a region, and routes it to the right regional manager.
29
+
30
+ Workflows can also **write** files back into the pool (`writeWorkspaceFileNode`) when a step produces a
31
+ derived artifact other runs or the concierge should pick up — but **read is the headline pattern**.
32
+
33
+ **Bytes always flow through `item.binary`**, never as base64 on `item.json`. Read nodes stream a file's
34
+ bytes into a binary slot; you parse them in the next step. Use `workflow-dsl` for the
35
+ surrounding builder.
36
+
37
+ ## A complete read-and-use workflow
38
+
39
+ The concierge stored `products.json` in the pool. This webhook-triggered workflow reads it (latest-wins),
40
+ parses the JSON straight off the binary slot with `ctx.binary.getJson`, and looks a SKU up.
41
+
42
+ ```typescript
43
+ import { createWorkflowBuilder, WebhookTrigger, Callback } from "@codemation/core-nodes";
44
+ import type { NodeExecutionContext } from "@codemation/core";
45
+ import { readWorkspaceFileNode } from "@codemation/core-nodes-workspace-files";
46
+
47
+ type FileMeta = {
48
+ fileId: string;
49
+ filename: string;
50
+ contentType: string;
51
+ size: number;
52
+ lastModified: string;
53
+ binarySlot: string;
54
+ };
55
+ type Product = { sku: string; name: string; price: number };
56
+
57
+ export default createWorkflowBuilder({ id: "wf.product-lookup", name: "Look up product from pool" })
58
+ .trigger(new WebhookTrigger("Order in", { endpointKey: "order", methods: ["POST"] }))
59
+ // Read the newest "products.json" — a fresher upload is picked up on the next run.
60
+ .then(
61
+ readWorkspaceFileNode.create(
62
+ { filename: "products.json", binarySlot: "data" },
63
+ "Read product DB",
64
+ "read-product-db",
65
+ ),
66
+ )
67
+ // Parse the bytes from the binary slot — never base64 on item.json.
68
+ .then(
69
+ new Callback<FileMeta, { count: number; first?: Product }>(
70
+ "Parse products",
71
+ async (items, ctx: NodeExecutionContext) => {
72
+ const results: Array<{ json: { count: number; first?: Product } }> = [];
73
+ for (const item of items) {
74
+ const attachment = item.binary?.["data"];
75
+ if (!attachment) throw new Error('No binary at slot "data"');
76
+ const products = await ctx.binary.getJson<Product[]>(attachment);
77
+ results.push({ json: { count: products.length, first: products[0] } });
78
+ }
79
+ return results;
80
+ },
81
+ { id: "parse-products" },
82
+ ),
83
+ )
84
+ .build();
85
+ ```
86
+
87
+ ## Resolution modes (read)
88
+
89
+ | Mode | Config | Behaviour |
90
+ | ------------------------- | --------------------------- | -------------------------------------------------------------------------------------------------------------- |
91
+ | **latest-wins** (default) | `filename: "products.json"` | Reads the **newest** file with that name. Next upload of the same name is what the next run reads. |
92
+ | **pinned fileId** | `fileId: "abc123..."` | Reads that exact, immutable version forever — a new upload never changes it. Takes precedence over `filename`. |
93
+
94
+ Use **latest-wins** for "always use the current sheet". Use a **pinned fileId** for reproducible /
95
+ auditable runs. Discover ids with `listWorkspaceFilesNode`.
96
+
97
+ ## Binary slot handoff
98
+
99
+ `readWorkspaceFileNode` streams the file's bytes into `item.binary[binarySlot]` (default `"data"`) and
100
+ emits this metadata on `item.json`:
101
+
102
+ ```text
103
+ {
104
+ fileId: string;
105
+ filename: string;
106
+ contentType: string;
107
+ size: number; // bytes
108
+ lastModified: string; // ISO 8601
109
+ binarySlot: string; // e.g. "data"
110
+ }
111
+ ```
112
+
113
+ Read the bytes downstream via `ctx.binary.getJson<T>(attachment)` (bounded read + decode + parse, the
114
+ clean default for JSON), `ctx.binary.getBytes(attachment)` (raw `Uint8Array`), or
115
+ `ctx.binary.openReadStream(attachment)` for streaming. The bytes are never on `item.json`.
116
+
117
+ ## Node reference
118
+
119
+ Import every node from `@codemation/core-nodes-workspace-files`.
120
+
121
+ ### `listWorkspaceFilesNode` — discover what's in the pool
122
+
123
+ ```typescript
124
+ import { listWorkspaceFilesNode } from "@codemation/core-nodes-workspace-files";
125
+
126
+ const list = listWorkspaceFilesNode.create(
127
+ { filenameFilter: "products" }, // optional case-insensitive substring; omit to list all
128
+ "List product files",
129
+ "list-product-files",
130
+ );
131
+ ```
132
+
133
+ Emits one item per file (newest-first): `{ fileId, filename, contentType, size, lastModified }`. Useful
134
+ to drive a fan-out (one item per file) or to resolve a fileId for pinned reads.
135
+
136
+ ### `readWorkspaceFileNode` — read a file's bytes
137
+
138
+ ```typescript
139
+ import { readWorkspaceFileNode } from "@codemation/core-nodes-workspace-files";
140
+
141
+ const read = readWorkspaceFileNode.create(
142
+ {
143
+ filename: "products.json", // latest-wins; or use fileId for a pinned version
144
+ binarySlot: "data", // where the bytes land on item.binary — default "data"
145
+ maxBytes: 5 * 1024 * 1024, // cap before streaming — default 100 MiB
146
+ },
147
+ "Read products",
148
+ "read-products",
149
+ );
150
+ ```
151
+
152
+ Either `filename` or `fileId` must be set (`fileId` wins). Output: the metadata JSON above + bytes in
153
+ `item.binary[binarySlot]`.
154
+
155
+ ### `writeWorkspaceFileNode` — produce a file back into the pool
156
+
157
+ ```typescript
158
+ import { writeWorkspaceFileNode } from "@codemation/core-nodes-workspace-files";
159
+
160
+ const write = writeWorkspaceFileNode.create(
161
+ {
162
+ filename: "daily-summary.json", // required — the name in the pool / concierge view
163
+ binarySlot: "data", // slot holding the bytes to write — default "data"
164
+ contentType: "application/json", // stamped on the stored object — default application/octet-stream
165
+ },
166
+ "Write daily summary",
167
+ "write-daily-summary",
168
+ );
169
+ ```
170
+
171
+ Reads bytes from `item.binary[binarySlot]` and writes them under a new fileId. Output:
172
+ `{ fileId, filename, contentType, size, key, registered }`. In managed (CP-paired) mode it also registers
173
+ the file so the concierge can list/read it; until the CP register endpoint ships, `registered` is `false`
174
+ while the bytes are always written.
175
+
176
+ ## Gotchas
177
+
178
+ - **Bytes go through `item.binary`, never base64 on `item.json`.** Read nodes attach to a slot; write
179
+ nodes read from a slot. Attach bytes with `ctx.binary` (or an upstream node) before writing.
180
+ - **Read the derived file, not the raw upload.** Point workflows at the concierge-produced JSON/CSV in
181
+ the pool — not at a user's raw PDF/Excel. The transform is the concierge's job.
182
+ - **`WORKSPACE_ID` / `BLOB_STORAGE_*` must be set.** Outside a configured workspace (e.g. plain local dev
183
+ without CP integration) the storage adapter is unavailable and the node throws — guard if a workflow
184
+ may run in that mode.
185
+ - **Stable node ids.** Set an explicit `nodeId`; the engine slugifies the label otherwise, so renaming
186
+ re-keys the node.
187
+
188
+ ## Read next when needed
189
+
190
+ - `workflow-dsl` — builder, triggers, flow control, the per-item contract.
191
+ - `document-ai` — scan a file's bytes (e.g. read a PDF, then scan it).
@@ -0,0 +1,91 @@
1
+ ---
2
+ name: credentials
3
+ description: Explains how the concierge connects a credential — the slot/type/instance model and how it presents and binds API-key, OAuth2, and MCP credentials. Use whenever a workflow needs an external account connected (an API key, an OAuth login, an MCP server) before it can run.
4
+ compatibility: Concierge planning skill — conversation + binding guidance, no code.
5
+ tags: audience:concierge, credentials, oauth, mcp, api-key, binding
6
+ ---
7
+
8
+ # Codemation Credentials
9
+
10
+ ## Mental model
11
+
12
+ Four words run through everything here:
13
+
14
+ - **Credential TYPE** — a schema for one kind of connection (e.g. "Odoo API", "Gmail").
15
+ It declares `publicFields` (config, e.g. base URL) and `secretFields` (secret material, e.g.
16
+ an API key or token).
17
+ - **Credential INSTANCE** — one concrete connection the owner has set up against a type (their
18
+ actual Odoo, their actual mailbox). A type can have several instances (personal vs work Gmail).
19
+ - **SLOT** — a credential requirement a node declares. The binding key is
20
+ `(workflowId, nodeId, slotKey)`.
21
+ - **BIND** — pointing a slot at an instance, so the node authenticates with that connection at run time.
22
+
23
+ The division of labour: **the builder DECLARES and USES** slots (a node says "I need an Odoo
24
+ credential"); **the concierge BINDS** an instance into each slot. A workflow can't be activated while
25
+ any required slot has no healthy bound instance — so binding is the concierge's job to close out.
26
+
27
+ ## Forms render themselves — you collect and bind
28
+
29
+ You do **not** hand-build credential forms. The control-plane UI and the credential tools render the
30
+ fields automatically from the type's `publicFields` / `secretFields` schema served by the host. Each
31
+ field carries its own presentation: `type` (`string` / `password` / `textarea` / `json` / `boolean`),
32
+ whether it's `required`, and `visibility: "advanced"` (renders in a collapsed section for power-user
33
+ fields). You pick the right credential type, walk the owner through the rendered fields, and bind.
34
+
35
+ **Secrets go straight to the host, never into the chat.** When the owner types an API key or password,
36
+ it is submitted directly to the host as secret material — you never echo it, store it in the
37
+ conversation, or pass it through a tool argument. Public config (a base URL, a database name) is fine
38
+ in conversation; secret material is not.
39
+
40
+ ## The three presentation patterns
41
+
42
+ These are three _shapes the form takes_, not three different credential systems. Only OAuth2 is a
43
+ distinct auth flow under the hood — API-key and MCP both reduce to "render the schema, then bind".
44
+
45
+ ```text
46
+ API-key / basic-auth credential
47
+ - the type declares plain publicFields + secretFields (e.g. baseUrl + apiKey, or username + password)
48
+ - you: surface the form → owner fills it → secrets go to the host → test → bind to the slot
49
+ - the host's test() probes the real service; a "failing" result blocks activation, so resolve it first
50
+
51
+ OAuth2 credential (redirect flow)
52
+ - the type carries an oauth2 auth definition (provider, scopes, authorize/token URLs)
53
+ - there is NO secret field to type — the token comes from the redirect, not from chat
54
+ - you: surface the connection → owner clicks Connect → redirected to the provider → grants access
55
+ → the host stores the returned token as material → bind the resulting instance to the slot
56
+ - if a client id / secret field is part of publicFields, those are config, not the user's secret
57
+
58
+ MCP server credential
59
+ - an MCP server (from the control-plane registry) declares acceptedCredentialTypes — usually an
60
+ OAuth connection (e.g. Gmail). MCP is NOT its own credential kind; you bind an instance of the
61
+ accepted type exactly as above
62
+ - one instance can serve both a node (e.g. a Gmail trigger) and the MCP server in the same workflow
63
+ ```
64
+
65
+ ## How a binding gets done
66
+
67
+ ```text
68
+ 1. find the workflow's open credential slots (the builder declared them on specific nodes)
69
+ 2. for each slot, identify the credential type it accepts
70
+ 3. is there already a healthy instance of that type? → reuse it; otherwise set one up:
71
+ - API-key/basic: present the form → collect → secrets to host → test
72
+ - OAuth2 (incl. most MCP): present Connect → owner completes the redirect
73
+ 4. bind the instance to the slot
74
+ 5. confirm every required slot is bound and healthy → only then can the workflow activate
75
+ ```
76
+
77
+ ## The things owners forget — surface them
78
+
79
+ - **Connect before they expect to.** A workflow that "looks built" still won't run until its slots are
80
+ bound. Drive the owner through connecting accounts as part of finishing, not after they hit an error.
81
+ - **Reuse, don't re-ask.** If a healthy instance of the right type already exists, bind it rather than
82
+ making the owner reconnect.
83
+ - **A failing test is a blocker, not a warning.** The host tests a credential on Connect and before
84
+ activation. Treat a "failing" status as work to resolve (wrong key, bad URL), not something to skip.
85
+ - **One real test before live.** After binding, run the workflow once on real input so the owner sees
86
+ the connection actually working end to end.
87
+
88
+ ## Read next when needed
89
+
90
+ - `credential-development` — the builder side: declaring a credential type and node slots.
91
+ - `mcp-capabilities` — discover MCP server ids and their accepted credential types.
@@ -0,0 +1,78 @@
1
+ ---
2
+ name: intake-automation-playbook
3
+ description: Scopes an INTAKE automation (something arrives → gets understood → an action happens) and proactively surfaces the dimensions owners forget — traceability, duplicate-handling, human-review, testing on real data. Use whenever an owner wants to automate handling of incoming emails, messages, orders, leads, or form submissions.
4
+ compatibility: Concierge planning skill — plain-language conversation guidance, no code.
5
+ tags: audience:concierge, audience:concierge-only, intake, automation, planning
6
+ ---
7
+
8
+ # Intake automation playbook
9
+
10
+ ## Mental model
11
+
12
+ Most automations a business owner asks for are the same shape: an **intake automation**. Something
13
+ arrives → it gets understood → an action happens → the loop is closed. "Process my order emails", "new
14
+ leads into my CRM", "contact-form submissions into a spreadsheet" are all this one pattern.
15
+
16
+ Your job is not to transcribe what the owner asks — it's to recognise the pattern and **proactively fill
17
+ in the parts they don't know to ask for**. A non-technical owner describes the happy path; they rarely
18
+ mention what happens when the same email arrives twice, when the AI misreads a total, or how they'll
19
+ know it works on _their_ data. You supply that missing input.
20
+
21
+ ## When to use
22
+
23
+ The moment a request looks like intake: an incoming thing (email / message / order / lead / form /
24
+ document) that should be processed into an action. Walk the owner through the dimensions below,
25
+ **suggesting** the ones they didn't raise — as recommendations, not a checklist. Don't use this for
26
+ one-off tasks, reports with no incoming trigger, or pure data lookups.
27
+
28
+ ## The nine dimensions
29
+
30
+ The owner usually volunteers 1, 2, and 7. **You** raise the rest.
31
+
32
+ 1. **Trigger — what starts it.** Confirm the exact source (which inbox / label / endpoint).
33
+ 2. **Ingest / understand — turn messy input into clean data.** Read PDFs and free text, pull out the fields.
34
+ 3. **Match + link + report.** The one most underestimated: each company / contact / product must be
35
+ matched to the record in their system and linked — and whatever can't be matched is reported, not
36
+ silently dropped.
37
+ 4. **Human-review — a person checks before it commits, when it matters.** Pause on low-confidence or
38
+ unusual cases for the owner's OK.
39
+ 5. **Traceability / audit trail — keep the original** so a human can reconstruct what happened. This is
40
+ what makes them trust it.
41
+ 6. **Idempotency — don't process the same thing twice.** A re-sent or forwarded item must not double-enter.
42
+ 7. **Act — the actual output.** Be concrete: create the sale order, print the ticket, add the contact.
43
+ 8. **Notify / close the loop.** Reply to confirm or send a summary; mark the item handled.
44
+ 9. **Testability — the owner verifies on THEIR real data, no code.** Let them drop ~5 real items into a
45
+ test path and see exactly what it would do before switching it on. For a mailbox workflow this means a
46
+ **designated test folder** — ask for the folder/label name, resolve its id (via MCP), and pass it to
47
+ the builder. Fabricating fixtures defeats the point: the test must exercise the real processing on the
48
+ real input shape. See the `testing` skill for the principle and the integration skill (e.g. `gmail`)
49
+ for the concrete mechanism.
50
+
51
+ ## Per-vertical extensions
52
+
53
+ The nine are the universal core; each vertical adds its own concerns. Pull the matching scenario skill
54
+ if one exists (e.g. `scenario-procurement-intake`,
55
+ `scenario-invoice-to-accounting`), or reason from the same shape: what's the domain's
56
+ system-of-record (3), what's domain-specific to verify before committing (4), what counts as a
57
+ duplicate (6)? If no scenario skill exists, the nine still apply.
58
+
59
+ ## Using it in conversation — not an interrogation
60
+
61
+ - Lead with the owner's goal; weave the forgotten dimensions in as **suggestions you recommend**. Two or
62
+ three well-placed "I'd also set it up to…" beats nine blunt questions.
63
+ - Prioritise the high-value forgotten ones — **traceability, duplicate-handling, human-review, and a way
64
+ to test on real data**. These are what owners are most grateful you raised.
65
+ - Fold them into the plan you present, so it visibly covers them — that plan becomes the brief the
66
+ builder works from.
67
+
68
+ ## Anti-patterns
69
+
70
+ - **Mirroring the naive input** — building exactly the happy path, omitting traceability / dedup /
71
+ review / testing. That's the failure this skill exists to prevent.
72
+ - **Interrogating** — firing all nine as questions. Suggest and recommend instead.
73
+ - **Recognise-without-link** — "the AI read the order" isn't done. If matched entities aren't linked into
74
+ the system of record and the unmatched aren't reported, a human has to redo it.
75
+ - **Silent skips on the hard cases** — low-confidence or unmatched items must surface, never be quietly
76
+ guessed or dropped.
77
+ - **Leaking how it's built** — keep it plain-language outcomes; never mention nodes, tools, or code to
78
+ the owner.
@@ -0,0 +1,48 @@
1
+ ---
2
+ name: scenario-invoice-to-accounting
3
+ description: Plans an invoice-to-accounting automation — an incoming invoice is scanned, its vendor/amount/line-items extracted, and posted to an accounting system, with low-confidence ones held for review. Use when an owner wants supplier invoices read off and pushed into their accounting system automatically.
4
+ tags: scenario, invoice, accounting, ocr, document, confidence, audience:concierge
5
+ ---
6
+
7
+ # Invoice → accounting — scenario guidance
8
+
9
+ A business receives supplier invoices and wants the key fields read off and pushed into their
10
+ accounting system automatically — with the uncertain ones held back for a person to check. This is the
11
+ shape to plan from; build each step from the skills listed below.
12
+
13
+ ## The flow
14
+
15
+ ```text
16
+ on invoice arriving (webhook / upload / email)
17
+ → scan the document (invoice analyzer, per-field confidence)
18
+ → normalise the fields (vendor, invoice number, date, total, tax, line items)
19
+ → if any key field's confidence is below threshold → human review, else continue
20
+ → post the structured invoice to the accounting API
21
+ → keep the original document + the posted result audited
22
+ ```
23
+
24
+ ## What to ask the owner first
25
+
26
+ - How do invoices arrive — a webhook/upload, an email attachment, a watched folder?
27
+ - Which accounting system, and what's its API for creating a bill/expense? Which fields does it need?
28
+ - What confidence is "safe to auto-post" vs "send to a human"? (the scanner returns per-field confidence)
29
+ - Which fields matter — vendor, invoice number, date, total, tax, line items?
30
+ - How will they test it before going live — which ~5 real invoices?
31
+
32
+ ## Build it from these skills
33
+
34
+ - `workflow-dsl` — the builder, trigger, and flow-control spine. Start here.
35
+ - `workflow-dsl` — receive the invoice by webhook/upload; or `gmail` if it arrives by email.
36
+ - `document-ai` — the invoice analyzer with `includeConfidence` for per-field confidence.
37
+ - `workflow-dsl` — normalise the extracted fields into the shape the accounting API wants.
38
+ - `human-in-the-loop` — the confidence gate that routes uncertain invoices to review.
39
+ - `workflow-dsl` — post to the accounting API.
40
+ - `credentials` — connect the accounting API and bind it to the slot the builder declared.
41
+ - `testing` — let them dry-run on real invoices before it goes live.
42
+
43
+ ## The things owners forget — surface them
44
+
45
+ - **Gate on confidence** — don't auto-post a low-confidence amount; route it to review.
46
+ - **Idempotency** — the same invoice arriving twice must not post twice (dedupe on invoice number).
47
+ - **Traceability** — keep the original document and a record of what was posted.
48
+ - **Testability is part of done** — let them dry-run on a handful of real invoices before going live.
@@ -0,0 +1,58 @@
1
+ ---
2
+ name: scenario-procurement-intake
3
+ description: Plans an order-intake automation — order emails are read, matched into the customer's ERP, and turned into sales orders, with a human in the loop for the uncertain ones. Use when an owner wants incoming purchase orders processed into their ERP (Odoo, AFAS, Salesforce, …) automatically.
4
+ tags: scenario, procurement, order-intake, erp, gmail, ocr, audience:concierge
5
+ ---
6
+
7
+ # Procurement order-intake — scenario guidance
8
+
9
+ A dealer or distributor receives purchase orders by email — usually a PDF — and wants them turned into
10
+ sales orders in their ERP automatically and safely, with a human checking the uncertain ones. The ERP
11
+ is **whatever the owner uses** (Odoo, AFAS, Salesforce, …) — ask, don't assume. This is the shape to
12
+ plan from; build each step from the skills listed below.
13
+
14
+ ## The flow
15
+
16
+ ```text
17
+ on new mail @ "orders" label
18
+ → OCR the attachment + read the body
19
+ → [agent] router : is this really an order? (order / not-order / needs-human)
20
+ → [agent] extractor : company, buyer, PO number, line items (description, code, qty) as structured output
21
+ → erp-sync : MATCH company + contacts + products in the customer's ERP, LINK them into a new
22
+ sales order, REPORT anything unmatched — never silently drop it
23
+ → if low-confidence or unusual → human review gate, else continue
24
+ → create the sales order (header + lines for the matched items)
25
+ → reply to confirm + label the mail processed
26
+ → keep the original email + PDF audited throughout
27
+ ```
28
+
29
+ ## What to ask the owner first
30
+
31
+ - Which Gmail label do order emails land on? Are orders a PDF attachment, inline text, or both?
32
+ - **Which ERP / business system do they use** (Odoo, AFAS, Salesforce, …)? products matched by code/SKU?
33
+ existing customers to match? which company owns the order?
34
+ - The buyer: is it the sender, or a buyer address inside the mail? any company to exclude (their own)?
35
+ default country / currency?
36
+ - How will they test it before going live — which ~5 real order emails?
37
+
38
+ ## Build it from these skills
39
+
40
+ - `workflow-dsl` — the builder, trigger, and flow-control spine. Start here.
41
+ - `gmail` — trigger on the label; reply and label the mail processed.
42
+ - `document-ai` — OCR the attachment (managed, no Azure key).
43
+ - `ai-agent` — the router and the extractor (with an output schema). The agents classify and extract;
44
+ the ERP sync itself is deterministic integration work, not an agent.
45
+ - `connect-external-systems` — **how to reach the ERP**: once you know which ERP the owner uses, follow
46
+ the routing rule — a specialized node if one exists (e.g. `odoo`), else an MCP-backed agent, else
47
+ `defineRestNode`. Never hand-roll the ERP's HTTP/JSON-RPC in a Callback.
48
+ - `human-in-the-loop` — the review gate, on the live path, not beside a NoOp.
49
+ - `testing` — TestTrigger reads a label, IsTestRun guards live writes, assertions.
50
+ - `credential-development` — author or bind the ERP credential the integration nodes declare.
51
+
52
+ ## The things owners forget — surface them
53
+
54
+ - **Match → link → report** every entity (company, contacts, products): recognise it, match the ERP
55
+ record, link it, and report what didn't match for review. Recognise-only isn't enough.
56
+ - **Traceability** — keep the original email + PDF attached and audited.
57
+ - **Idempotency** — the same mail processed twice must not create a duplicate sales order.
58
+ - **Testability is part of done** — the workflow is tested on **real order emails from a designated test folder**, not fabricated data. Ask the owner for the test-folder name, resolve its Gmail label id (via MCP), and pass it to the builder in the build task. The builder will `report_flag` a gap if it's missing. See the `testing` and `gmail` skills for the mechanism.
@@ -1,66 +0,0 @@
1
- ---
2
- name: codemation-ai-agent-node
3
- description: AIAgent constructor, message shape, managed and BYOK chatModel configs, outputSchema, mcpServers. Read before writing any workflow step that calls an LLM.
4
- compatibility: Codemation core-nodes. Requires @codemation/core-nodes import.
5
- tags: agent, llm, ai
6
- uses: "@codemation/core-nodes"
7
- ---
8
-
9
- # Codemation AI Agent Node
10
-
11
- ## Mental model
12
-
13
- `AIAgent` is the single building block for any LLM step in a workflow. It receives items, runs a chat completion per item using the configured model and messages, and emits `{ output: string }` (or a parsed object when `outputSchema` is set) on its `main` port. The `chatModel` field determines whether the run consumes Codemation-managed quota (no credential needed) or a BYOK key the operator supplies. Every AIAgent emits exactly one output item per input item — it never fans out or filters.
14
-
15
- ## When to use / when NOT
16
-
17
- Use `AIAgent` when a workflow step needs an LLM call: classification, extraction, summarisation, drafting, or decision.
18
- Use a plain `Callback` instead when the logic is deterministic code — no LLM needed.
19
- Use `mcpServers` (see `codemation-mcp-capabilities`) when the agent needs tool access to external services.
20
- Read `codemation-workflow-dsl` for the surrounding workflow structure.
21
-
22
- ## Quickstart
23
-
24
- ```ts
25
- import { AIAgent, CodemationChatModelConfig } from "@codemation/core-nodes";
26
-
27
- new AIAgent({
28
- name: "Classify email",
29
- messages: [
30
- { role: "system", content: "Classify the email as spam or not-spam." },
31
- { role: "user", content: (args) => args.item.json.body as string },
32
- ],
33
- chatModel: new CodemationChatModelConfig("Claude Haiku", "anthropic/claude-haiku-4-5-20251001"),
34
- });
35
- ```
36
-
37
- For full patterns — BYOK (`OpenAIChatModelConfig`), `outputSchema`, tools, multi-step pipelines, and gmail classification — use your harness's example-discovery tool: `find_examples({ query: "AIAgent" })`.
38
-
39
- ## Decision branches & gotchas
40
-
41
- **Managed mode (default — no API key needed):** use `CodemationChatModelConfig(label, modelId)`. In managed mode the LLM broker **auto-authenticates via the workspace HMAC pairing** — no API key, no credential slot, no user setup required. This is the correct default for all managed-mode workflows. Do NOT tell managed users to "get an API key" — the broker handles authentication transparently.
42
-
43
- ```ts
44
- chatModel: new CodemationChatModelConfig("Claude Haiku", "anthropic/claude-haiku-4-5-20251001");
45
- // No credential slot created. Discover live model ids:
46
- // GET <CONTROL_PLANE_URL>/api/llm/managed-models
47
- ```
48
-
49
- **BYOK (self-hosted / non-managed only):** use `OpenAIChatModelConfig(label, modelId, slotKey)` — it creates a credential slot the operator must bind with an API key. Only use this in self-hosted deployments where no managed broker is available.
50
-
51
- **Messages:** `content` is a plain string or a function `(args: { item, itemIndex, items, ctx }) => string`. Put instructions in the `system` message, per-item data in the `user` message. Use `"assistant"` role only for few-shot examples.
52
-
53
- **Structured output:** add `outputSchema: z.object({...})` to validate and parse the response. Without it, `item.json.output` is always a plain string.
54
-
55
- **Stable node id:** if the node has a credential binding (BYOK), set an explicit `id:` on the constructor. Without it the id derives from the `name` label — renaming the label orphans the binding. See `codemation-workflow-dsl` for the full id-stability rule.
56
-
57
- **Downstream access:** the next node sees `item.json.output` as the agent's text response. Cast it via a typed `Callback<{ output: string }>`.
58
-
59
- ## Anti-patterns
60
-
61
- - Do not tell managed users to get an API key — use `CodemationChatModelConfig`; the broker authenticates automatically.
62
- - Do not use `OpenAIChatModelConfig` in managed mode — it creates an unnecessary credential slot and will prompt the user to bind a key they don't need.
63
- - Do not use `AIAgent` for deterministic logic; use `Callback` instead (cheaper, faster, no LLM billing).
64
- - Do not attempt to return multiple items from a single `AIAgent` step — it emits exactly one output per input.
65
-
66
- See `references/anti-patterns.md` for version-specific gotchas (managed model id churn, chatModel string shorthand trap).
@@ -1,11 +0,0 @@
1
- # AIAgent anti-patterns (version-specific)
2
-
3
- ## Managed model ids change between releases
4
-
5
- Do NOT hard-code managed model ids sourced from training data. The allowlisted set changes with each release.
6
- Always discover the live list via `GET <CONTROL_PLANE_URL>/api/llm/managed-models` before committing a model id.
7
-
8
- ## `chatModel` string shorthand is not supported on AIAgent
9
-
10
- `AIAgent` does not accept a plain string for `chatModel` — only `CodemationChatModelConfig` or `OpenAIChatModelConfig` instances.
11
- (The string shorthand `model: "openai:gpt-4o-mini"` works on the `.agent(...)` fluent DSL helper only.)
@@ -1,57 +0,0 @@
1
- ---
2
- name: codemation-credential-development
3
- description: Guides Codemation custom credential development with `defineCredential(...)`, typed sessions, credential testing, and node credential slots. Use when creating or updating custom credentials, credential registrations, or credential-aware custom nodes.
4
- compatibility: Designed for Codemation apps and plugins that register typed credentials.
5
- tags: credential, oauth, plugin
6
- ---
7
-
8
- # Codemation Credential Development
9
-
10
- ## Mental model
11
-
12
- A credential type is a schema + runtime adapter: it declares `public` config (e.g. OAuth client id), `secret` material (e.g. tokens), a `createSession(...)` factory that returns the typed object nodes consume, and a `test(...)` function for pre-activation validation. Nodes declare named credential slots; operators bind concrete instances to those slots in the UI. The binding key is `(workflowId, nodeId, slotKey)`.
13
-
14
- ## When to use / when NOT
15
-
16
- Use this skill for defining new credential types, wiring them into apps or plugins, and teaching nodes to request typed credential sessions.
17
- Do not use for general workflow authoring unless credential slots or runtime sessions are the core problem.
18
-
19
- ## Quickstart
20
-
21
- No standalone snippet — the full `defineCredential(...)` shape is in `references/credential-patterns.md`. Use your harness's example-discovery tool for runnable examples: `find_examples({ query: "defineCredential api-key test" })` or `find_examples({ query: "credential slot" })`.
22
-
23
- ## What `test()` does and why it matters
24
-
25
- Every credential type must implement `test(args)`. It is called:
26
-
27
- - When the operator clicks **Connect** in the credential dialog (validates before saving).
28
- - Before a workflow activates (blocks activation on failing credentials).
29
-
30
- `test()` receives the same `{ publicConfig, material }` args as `createSession()`. It must return `{ status: "healthy" | "failing", message, testedAt }`. A "failing" result blocks workflow activation and surfaces the `message` to the operator — use it to give actionable guidance ("API key is empty", "Endpoint returned 401 — key is invalid").
31
-
32
- Implement `test()` as a cheap probe against the real service when possible (e.g. a `/health` or `/me` call). At minimum, validate that required secret fields are non-empty. Do NOT call `createSession()` from inside `test()` — test independently so credential issues are caught before runtime.
33
-
34
- See the `define-credential-api-key` example for a concrete `test()` implementation: `find_examples({ query: "defineCredential api-key test" })`.
35
-
36
- ## Authoring rules
37
-
38
- 1. Start with `defineCredential(...)`.
39
- 2. Keep `public` versus `secret` fields intentional.
40
- 3. Make `createSession(...)` return the typed runtime object the node actually needs.
41
- 4. Implement `test(...)` so failure states are explicit before workflow activation.
42
- 5. Register credential types at the app or plugin boundary, not inside random workflow files.
43
-
44
- ## Decision branches & gotchas
45
-
46
- **Node integration:** helper-defined nodes declare credentials directly in the `credentials` field; class-based nodes use lower-level credential requirement APIs when needed.
47
-
48
- **Binding stability:** the `nodeId` defaults to a slug of the node's `name` label. Renaming a credential-using node's label silently changes its id and orphans the binding in the UI. To prevent this, set an explicit `id:` on credential-using node configs so the id is decoupled from the label.
49
-
50
- ## Anti-patterns
51
-
52
- - Do not hard-code secrets in node implementation — use credential slots.
53
- - Do not register credential types inside workflow files — use the app or plugin composition root.
54
-
55
- ## Read next when needed
56
-
57
- - Read `references/credential-patterns.md` for schema, registration, and slot guidance.