@flow-engineer/flow 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +167 -10
- package/coding-agents/auth-skill.md +137 -0
- package/coding-agents/context-skill.md +94 -0
- package/coding-agents/skill.md +219 -17
- package/coding-agents/test-skill.md +3 -1
- package/coding-agents/tools-skill.md +78 -1
- package/dist/cli/src/auth.d.ts +11 -0
- package/dist/cli/src/auth.js +147 -0
- package/dist/cli/src/commands.d.ts +119 -0
- package/dist/cli/src/commands.js +212 -0
- package/dist/cli/src/index.d.ts +2 -0
- package/dist/cli/src/index.js +5 -0
- package/dist/cli/src/run.d.ts +28 -0
- package/dist/cli/src/run.js +523 -0
- package/dist/cli/src/terminal.d.ts +3 -0
- package/dist/cli/src/terminal.js +135 -0
- package/dist/flow-messaging/src/client.d.ts +2 -2
- package/dist/flow-messaging/src/client.js +3 -3
- package/dist/flow-messaging/src/index.js +17 -9
- package/dist/mcp/src/client.d.ts +54 -0
- package/dist/mcp/src/client.js +175 -0
- package/dist/mcp/src/describe.d.ts +41 -0
- package/dist/mcp/src/describe.js +110 -0
- package/dist/mcp/src/index.d.ts +4 -0
- package/dist/mcp/src/index.js +7 -0
- package/dist/mcp/src/server.d.ts +70 -0
- package/dist/mcp/src/server.js +252 -0
- package/dist/mcp/src/terminal.d.ts +24 -0
- package/dist/mcp/src/terminal.js +54 -0
- package/dist/simulators/src/index.d.ts +1 -1
- package/dist/simulators/src/page.html +267 -49
- package/dist/simulators/src/server.d.ts +27 -0
- package/dist/simulators/src/server.js +264 -40
- package/dist/src/agent.d.ts +69 -3
- package/dist/src/agent.js +162 -8
- package/dist/src/channels.d.ts +6 -4
- package/dist/src/channels.js +4 -3
- package/dist/src/conversation.d.ts +17 -1
- package/dist/src/conversation.js +170 -16
- package/dist/src/events.d.ts +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +1 -1
- package/dist/src/model.d.ts +27 -0
- package/dist/src/model.js +85 -13
- package/dist/src/skills.d.ts +4 -0
- package/dist/src/skills.js +23 -0
- package/dist/src/store.d.ts +2 -0
- package/dist/src/store.js +18 -0
- package/dist/src/tools.d.ts +69 -0
- package/dist/src/tools.js +17 -1
- package/dist/src/traces.js +32 -4
- package/dist/src/types.d.ts +5 -0
- package/dist/testing/src/account.d.ts +1 -1
- package/dist/testing/src/account.js +368 -57
- package/dist/testing/src/chat.js +24 -16
- package/dist/testing/src/check.d.ts +4 -0
- package/dist/testing/src/check.js +90 -0
- package/dist/testing/src/cli.js +77 -19
- package/dist/testing/src/flow-login.d.ts +33 -0
- package/dist/testing/src/flow-login.js +30 -5
- package/dist/testing/src/new.d.ts +14 -5
- package/dist/testing/src/new.js +54 -20
- package/dist/testing/src/plugin.d.ts +4 -0
- package/dist/testing/src/plugin.js +116 -0
- package/docs/agents.md +87 -12
- package/examples/cafe/src/cli.ts +24 -0
- package/examples/cafe/src/mcp.ts +22 -0
- package/examples/cafe/src/tools.ts +13 -0
- package/flow-messaging/README.md +3 -1
- package/package.json +28 -1
package/README.md
CHANGED
|
@@ -26,23 +26,31 @@ Flow Framework makes the chat behaviour the default. You write the agent's promp
|
|
|
26
26
|
Node 22.18 or later. Your agent is TypeScript that Node runs as it is, with no build step.
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
npx @flow-engineer/flow new my-agent && cd my-agent && npm install
|
|
30
|
-
|
|
29
|
+
npx @flow-engineer/flow new my-agent --display-name "My agent" && cd my-agent && npm install
|
|
30
|
+
npx @flow-engineer/flow link # signs in; writes Flow's free trial model key and a phone link (or put your own key in .env)
|
|
31
31
|
npm start
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
`flow new <name>` makes `./<name>/` from the café example (lowercase letters, digits and dashes). Open the link `npm start` prints and text Pip, a café ordering assistant: your starting point. Pick a platform at the top (SMS, WhatsApp, iMessage, Telegram, web), and turn on **Debug** to see every tool call and how fast each message was answered. A model call that fails (a wrong or expired key, say) shows in the chat, in Debug and on the terminal. The café's `src/agent.ts` reads its model id from `CAFE_MODEL` (default: the model for the key you set, see [Models](#models)). The new folder has the coding-agent skills in `.claude/skills/` and an `AGENTS.md` pointing at them, so you can tell Claude Code, Codex or any coding agent what to build.
|
|
34
|
+
`flow new <name>` makes `./<name>/` from the café example (lowercase letters, digits and dashes); `--display-name` is what people call it, kept as `displayName` in its `package.json`, and a phone reaches it by sending `Hi <that name> (ref <code>)`. Open the link `npm start` prints and text Pip, a café ordering assistant: your starting point. Pick a platform at the top (SMS, WhatsApp, iMessage, Telegram, web), and turn on **Debug** to see every tool call and how fast each message was answered. A model call that fails (a wrong or expired key, say) shows in the chat, in Debug and on the terminal. The café's `src/agent.ts` reads its model id from `CAFE_MODEL` (default: the model for the key you set, see [Models](#models)). The new folder has the coding-agent skills in `.claude/skills/` and an `AGENTS.md` pointing at them, so you can tell Claude Code, Codex or any coding agent what to build.
|
|
35
35
|
|
|
36
36
|
### On your phone, from your laptop
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
|
-
npx @flow-engineer/flow init # the same starter in the current folder; then npm install
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
npx @flow-engineer/flow init # the same starter in the current folder; then npm install
|
|
40
|
+
npx @flow-engineer/flow link # signs in (opens the browser), writes its keys and Flow's trial model key into .env, prints a link
|
|
41
|
+
npm start # try it in the playground link it prints, then open the phone link and text the agent
|
|
42
|
+
npx @flow-engineer/flow status # what the folder has and lacks
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
Test it first in the playground (the link `npm start` prints: the test people and the channels side by side), then on your phone. `flow chat` (the agent in the terminal) and `npm test -- --synthetic` are how a coding agent checks its own work.
|
|
46
|
+
|
|
47
|
+
`flow chat` builds the agent from `src/agent.ts` and writes as its first test person, one message per line of stdin. `flow link` signs in and runs `flow setup` when the folder is not set up yet, then prints the link for `--channel telegram` (the default), `whatsapp` or `imessage`; a channel flow.engineer cannot open yet answers with what to do instead. With `FLOW_MESSAGING_KEY` in `.env`, the starter's `npm start` is on Telegram, plus WhatsApp and iMessage when `.env` has their senders, and connects to Flow Messaging by its outbound event stream, so there is no tunnel or public URL, and messages sent while it was stopped arrive when it starts again ([flow-messaging/README.md](flow-messaging/README.md)). Coding agents: [docs/agents.md](docs/agents.md) (https://flow.engineer/flow-framework/agents.md) has the exit codes and `next` steps.
|
|
48
|
+
|
|
49
|
+
**Shared test numbers and privacy.** The free trial runs on Flow's shared Telegram bot, WhatsApp number and iMessage line. A join code binds that phone to one app, so its messages go only to that app's agent; sending another app's code moves it. Trial chats are test data, kept for the account's retention (30 days by default). Do not use the trial for real customers' data: connect your own number for that.
|
|
50
|
+
|
|
51
|
+
**Stability.** 0.x: the CLI's commands, `--json` fields and exit codes in [docs/agents.md](docs/agents.md) stay compatible within 0.2.x; a breaking change comes with a minor version bump and a release note. Pin the version in `package.json` for production.
|
|
52
|
+
|
|
53
|
+
**Host it.** Coming soon: `npx @flow-engineer/flow deploy`, to keep the agent answering while the laptop sleeps.
|
|
46
54
|
|
|
47
55
|
### In a folder of your own
|
|
48
56
|
|
|
@@ -74,7 +82,7 @@ console.log(`Playground: ${url}/`);
|
|
|
74
82
|
```
|
|
75
83
|
|
|
76
84
|
```bash
|
|
77
|
-
echo "OPENROUTER_API_KEY=sk-or-..." > .env
|
|
85
|
+
npx @flow-engineer/flow setup # writes FLOW_KEY (the agent's Flow key, on Flow's trial credit), or: echo "OPENROUTER_API_KEY=sk-or-..." > .env
|
|
78
86
|
node --env-file=.env src/main.ts
|
|
79
87
|
```
|
|
80
88
|
|
|
@@ -154,6 +162,8 @@ await play.listen(buildAgent({ events: [play.events] }), 8788);
|
|
|
154
162
|
|
|
155
163
|
The playground plays only the test people you declare (`testPeople: [{ name, phone }]`) on the agent's own channels. A test person is a real person to the agent: the same identity, `resolve` and history as if they wrote from their phone, one conversation on every channel, and "Clear history" to start again. Replies go back where the person last wrote from, the playground or the real channel. The playground's Person tab moves the agent's clock (`simulators({ clock: playgroundClock() })`, with `createAgent({ now: clock.now })`), lists what is scheduled or held for them, sets their time zone and quiet hours, and sends nudges with every field the API takes. Locally it answers only on localhost, and its API only its own page, so a site you visit cannot send it messages. On a deployed agent, mount `play.handler(agent, { token })`: it refuses requests without the token (at least 32 characters), swaps `?token=` for a cookie on the playground's path, and limits wrong tries.
|
|
156
164
|
|
|
165
|
+
To test account sign-in, give `simulators` an `authentication({ channel, visitor })` hook. The person picker gains **New user · signed out**: the hook returns a sign-in URL until the application verifies the visitor, then its account `key` and channel `address`. The playground uses that account's existing conversation and keeps replies inside the simulator. Each interface authenticates independently; verified-phone channels can resolve the account directly. Existing test people keep their identity shortcut. See [setting up authentication](coding-agents/auth-skill.md#exercise-sign-in-in-the-playground).
|
|
166
|
+
|
|
157
167
|
`AGENTS.md` says what the agent is for and what it may and may not do. Leave messaging out of it: Flow Framework adds how to talk on each channel (its send tool, reactions, formatting, acknowledging first). A skill's `SKILL.md` starts with a `name` and `description`; the agent sees every description and loads a skill when it needs it (`inlineSkills: true` puts them all in the prompt instead).
|
|
158
168
|
|
|
159
169
|
## Channels and people
|
|
@@ -510,9 +520,52 @@ It sends a fixed set of synthetic messages (a typed message, a double text, a ph
|
|
|
510
520
|
|
|
511
521
|
The café's agent as an HTTP endpoint, with its two tools and one model through any OpenAI-compatible API (the same key variables as `openAICompatible`, `MODEL` for the model; `FAKE_MODEL=1` for a fixed reply and no model call): [`examples/http-agent-python`](examples/http-agent-python) (standard library only) and [`examples/http-agent-go`](examples/http-agent-go) (standard library only).
|
|
512
522
|
|
|
523
|
+
## The same tools in Claude, ChatGPT and a CLI
|
|
524
|
+
|
|
525
|
+
The tools and skills you write for the chat agent are also an MCP server, so people use them from
|
|
526
|
+
Claude, ChatGPT or any MCP host, and a command line for your organization. Each needs the official
|
|
527
|
+
MCP SDK, an optional peer installed only when you use it.
|
|
528
|
+
|
|
529
|
+
```ts
|
|
530
|
+
import { mcpServer } from "@flow-engineer/flow/mcp"; // npm i @modelcontextprotocol/server@2.3.1
|
|
531
|
+
|
|
532
|
+
const mcp = mcpServer({ name: "cafe", version: "1.0.0", tools, dir: "agent" });
|
|
533
|
+
createServer((req, res) => void mcp.node(req, res)).listen(8790); // Streamable HTTP at /mcp
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
- Every tool becomes an MCP tool with every field filled from the tool: `title`, `output` (sent as
|
|
537
|
+
structured content), `status` lines, and `hints` (`readOnly`, `destructive`, `idempotent`,
|
|
538
|
+
`openWorld`) as MCP annotations. Chat ignores all of these. Skills go as the server's
|
|
539
|
+
instructions and a `read_skill` tool.
|
|
540
|
+
- Sign-in is the host's OAuth against your own authorization server: `auth: { issuer, scopes,
|
|
541
|
+
verify }`, with `signIn: "required" | "optional" | "none"` per tool. A call that needs sign-in gets
|
|
542
|
+
HTTP 401 and the host's sign-in prompt; the person is `context.key` in the tool.
|
|
543
|
+
- Backend access stays in tool code: supply the same `credentials` provider to `createAgent` and
|
|
544
|
+
`mcpServer`, then use `context.credentials.get("backend")` for that verified account. `resolve`
|
|
545
|
+
receives a normalized `phone` only on verified phone channels; phone-first brands need no extra
|
|
546
|
+
login or permissions screen there. See `coding-agents/auth-skill.md`.
|
|
547
|
+
- Another MCP server's tools become your agent's with `mcpTools({ name, url })`, pinned to
|
|
548
|
+
`mcp.lock.json` (`npx flow mcp update`), so a change on that server never reaches your
|
|
549
|
+
customers before your tests do.
|
|
550
|
+
- `@flow-engineer/flow/cli` makes your organization's command line from the same server, on Stricli (the
|
|
551
|
+
framework Sentry's CLI uses): every tool is a command, a `defineCli` file adds command groups
|
|
552
|
+
(`acme order -h` lists `place`, `status`), arguments and columns, and every command takes `--json`,
|
|
553
|
+
`--fields`, `--dry-run` and `--yes`. `npx flow cli --server <url> -h` works for any MCP server.
|
|
554
|
+
- `npx flow check` checks it all by rule before you ship: tools that cannot be described
|
|
555
|
+
exactly, a CLI file that no longer matches the tools, skills that name chat-only tools.
|
|
556
|
+
- Every generated piece can be replaced: the instructions, any tool's descriptor, your own MCP
|
|
557
|
+
handlers (`extend`), your own commands.
|
|
558
|
+
|
|
559
|
+
The coding-agent skills in [`coding-agents/`](https://github.com/flow-engineer/flow/tree/main/coding-agents)
|
|
560
|
+
cover it end to end: `context-skill.md` builds the context the tools are written from (OpenAPI,
|
|
561
|
+
workflows, business rules, access) out of your existing codebase, and `auth-skill.md` sets up
|
|
562
|
+
sign-in for your stack (an OAuth provider, Firebase, Supabase, your own login page).
|
|
563
|
+
|
|
513
564
|
## Models
|
|
514
565
|
|
|
515
|
-
Any OpenAI-compatible API. With no `apiKey` or `baseURL`, `openAICompatible` uses the first key that has a value: `OPENROUTER_API_KEY` (OpenRouter), `OPENAI_API_KEY` (OpenAI), `ANTHROPIC_API_KEY` (Anthropic),
|
|
566
|
+
Any OpenAI-compatible API. With no `apiKey` or `baseURL`, `openAICompatible` uses the first key that has a value: `OPENROUTER_API_KEY` (OpenRouter), `OPENAI_API_KEY` (OpenAI), `ANTHROPIC_API_KEY` (Anthropic), `GEMINI_API_KEY` (Gemini), then `FLOW_KEY` (Flow); with none it stops at once and names them.
|
|
567
|
+
|
|
568
|
+
A provider key is optional: `npx @flow-engineer/flow setup` writes `FLOW_KEY`, the agent's one Flow key (shown only as its prefix and last 4 characters), which calls models through Flow on $2 of Flow trial credit per account (`npx @flow-engineer/flow status` shows what is left). A provider key in `.env` wins over it for the model; `setup --rotate key` replaces a leaked `FLOW_KEY` (the old one works 24 h more, `--now` ends it at once). `flowModel({ model })` uses Flow explicitly (default `anthropic/claude-haiku-5.5`; Flow offers a short list of models, `GET /models`); `FLOW_MODEL_URL` points it at another Flow server. When the credit is used up, a call fails with code `trial_credit_used` and the next step. `MODEL` sets the model id; each key has its own default. Pass `baseURL` and `apiKey` for any other address; a local server that needs no key needs only `baseURL`. Give `contextTokens` (the model's window) and a long chat's history is kept within it: the latest messages go to every call, older ones are left out (the agent is told).
|
|
516
569
|
|
|
517
570
|
For agents with tools we recommend **Claude Haiku 5.5** (`anthropic/claude-haiku-5.5`) or **GPT-6.1 Sol** (`openai/gpt-6.1-sol`, about 3x the cost). In our tests, GPT Luna repeated tool calls and answered twice.
|
|
518
571
|
|
|
@@ -563,6 +616,8 @@ For agents with tools we recommend **Claude Haiku 5.5** (`anthropic/claude-haiku
|
|
|
563
616
|
| `simulators/` | The playground (`@flow-engineer/flow/simulators`): SMS, WhatsApp, iMessage, Telegram and web, with each platform's rules |
|
|
564
617
|
| `flow-messaging/` | Channels on Flow Messaging (`@flow-engineer/flow/flow-messaging`): WhatsApp, Telegram and SMS through one API |
|
|
565
618
|
| `testing/` | Test file formats and the `flow` command (`@flow-engineer/flow/testing`) |
|
|
619
|
+
| `mcp/` | Tools and skills as an MCP server, and other MCP servers' tools (`@flow-engineer/flow/mcp`) |
|
|
620
|
+
| `cli/` | An organization's command line over its MCP server (`@flow-engineer/flow/cli`) |
|
|
566
621
|
| `examples/cafe/` | Pip, an ordering assistant, with tests |
|
|
567
622
|
| `coding-agents/` | Skills for coding agents |
|
|
568
623
|
|
|
@@ -609,3 +664,105 @@ The test cloud, its address and its API are unchanged, and traces keep their `ch
|
|
|
609
664
|
## License
|
|
610
665
|
|
|
611
666
|
[Apache-2.0](https://github.com/flow-engineer/flow/blob/main/LICENSE)
|
|
667
|
+
|
|
668
|
+
### Native plugins and standalone MCP
|
|
669
|
+
|
|
670
|
+
The standalone `/mcp` endpoint includes the skills list and `read_skill`. Native plugins
|
|
671
|
+
copy the agent's `agent/skills/` folder and connect to `/mcp/plugin`, which serves tools only,
|
|
672
|
+
so a host receives each skill once. Both paths share tools, auth and descriptor overrides.
|
|
673
|
+
`mcpServer({ ..., plugin: false })` disables the plugin path; `plugin: { path: "/native" }`
|
|
674
|
+
moves it. `handle.plugin` exposes its path and descriptors. Your `instructions` override
|
|
675
|
+
applies to both endpoints, and `extend` can add SDK handlers on both.
|
|
676
|
+
|
|
677
|
+
From the agent folder, run:
|
|
678
|
+
|
|
679
|
+
```sh
|
|
680
|
+
flow plugin --server https://agent.example.com/mcp --out plugins
|
|
681
|
+
flow check --plugin plugins
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
`plugins/claude/` uses Claude Code's `.claude-plugin/plugin.json` and `.mcp.json` with
|
|
685
|
+
`type: "http"`. `plugins/agent-plugins/` uses Agent Plugins 1.0.0 root `plugin.json` and
|
|
686
|
+
`mcp.json` with `type: "streamable-http"`; Codex supports this portable format.
|
|
687
|
+
Pass `--name` to override the package name. For a custom plugin route, pass its URL ending
|
|
688
|
+
in `/plugin`, or edit the generated MCP config. No token is written into either package.
|
|
689
|
+
Regenerate after changing a skill or its assets; `check --plugin` compares content hashes.
|
|
690
|
+
Generated files remain plain files in your repository: customize manifests and add components
|
|
691
|
+
alongside them. Regeneration rewrites manifests and source skill copies; keep hand-written
|
|
692
|
+
skills outside those source paths.
|
|
693
|
+
|
|
694
|
+
Formats checked 18 October 2026: [Agent Plugins 1.0.0](https://agent-plugins.org/specification),
|
|
695
|
+
[OpenAI plugin packaging](https://developers.openai.com/plugins/build/plugins), and
|
|
696
|
+
[Claude Code plugin reference](https://code.claude.com/docs/en/plugins-reference).
|
|
697
|
+
|
|
698
|
+
MCP calls emit `tool.start`/`tool.end` with surface (`mcp`, `mcp-plugin`, `cli`), client name
|
|
699
|
+
and version, session, person, duration, outcome and error. CLI calls send command and an
|
|
700
|
+
idempotency key in `_meta`; a caller can reuse its key on a retry. `defineCli`'s `version`
|
|
701
|
+
sets its client version (default `"1"`). Neither CLI arguments nor trace credentials go in `_meta`.
|
|
702
|
+
`mcpServer` uses `FLOW_FRAMEWORK_TRACES_*`, like chat agents, or explicit `traces` options;
|
|
703
|
+
`traces: false` disables export without disabling your event sinks. `await handle.flushTraces()`
|
|
704
|
+
flushes its bounded background queue. Each tool call is a root trace without a conversation;
|
|
705
|
+
arguments and results require `FLOW_FRAMEWORK_TRACES_CONTENT=1`. Legacy clients' initialization
|
|
706
|
+
metadata is retained for up to 24 hours in at most 4,096 sessions; bearer tokens are never cached.
|
|
707
|
+
|
|
708
|
+
### Local playground CLI
|
|
709
|
+
|
|
710
|
+
A local playground can run the team's actual CLI, including custom commands, OAuth, interactive
|
|
711
|
+
chat, confirmations, stdout/stderr and exit codes. Opt in on the server that runs on your computer:
|
|
712
|
+
|
|
713
|
+
```ts
|
|
714
|
+
import { cli } from "./cli.ts"; // the team's defineCli configuration
|
|
715
|
+
await play.listen(agent, 8790, { localCli: { channel: "terminal", config: cli } });
|
|
716
|
+
// For a local agent mounting its playground under /playground:
|
|
717
|
+
const handler = play.handler(agent, { token, base: "/playground", localCli: { channel: "terminal", config: cli } });
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
The named channel must be declared on the agent. Its tab opens the runner instead of simulated
|
|
721
|
+
chat. Each browser gets an independent temporary sign-in and conversation directory; the test
|
|
722
|
+
person picker does not grant it an account. Run `auth login`, open the sign-in link, then return
|
|
723
|
+
to the runner. `chat` keeps reading input until `/exit`; reload reconnects to that session.
|
|
724
|
+
**Clear output** clears the viewport; **New CLI session** ends the session and removes its local
|
|
725
|
+
credentials, without clearing the account's agent history. Sessions expire after 30 minutes
|
|
726
|
+
without browser activity, and a server restart starts fresh. Ending a session does not undo
|
|
727
|
+
work already accepted by the agent.
|
|
728
|
+
|
|
729
|
+
This runner accepts only direct loopback connections with a loopback Host and same-origin API
|
|
730
|
+
requests, even if explicitly configured on a mounted handler. It is absent by default and is
|
|
731
|
+
not enabled in the hosted test cloud. It invokes `runCli` in-process: no shell, arbitrary program
|
|
732
|
+
execution, expansion or pipelines. Quoted arguments work. Its configured endpoints stay fixed;
|
|
733
|
+
use the installed CLI for `--server`, `--token` or `skill --write`.
|
|
734
|
+
|
|
735
|
+
- [ ] **TODO: Hosted CLI runner — deferred until a security review is complete.** Review execution
|
|
736
|
+
isolation, custom-command filesystem/network access, credentials, OAuth callbacks, session cleanup
|
|
737
|
+
and abuse controls before implementing or deploying cloud support. Keep the current runner local-only.
|
|
738
|
+
|
|
739
|
+
Embedded clients can set `CliConfig.directory` to isolate credential/conversation files and
|
|
740
|
+
`CliIO.env` to avoid inheriting process tokens; `CliIO.signal` cancels sign-in and terminal HTTP
|
|
741
|
+
requests. Defaults retain the installed CLI's existing environment and config directory.
|
|
742
|
+
|
|
743
|
+
### Terminal conversations
|
|
744
|
+
|
|
745
|
+
`chat` talks directly to the agent, using the same bearer as MCP; it is not an MCP tool. `defineCli({ name: "acme", server, agent: "https://agent.example.com/agent/terminal" })`
|
|
746
|
+
overrides the terminal URL; the default is the MCP server's origin plus `/agent/terminal`.
|
|
747
|
+
`acme chat "Which orders are ready?" --json` prints one response; `acme chat` reads a TTY until
|
|
748
|
+
Ctrl-D or `/exit`. Without a TTY pass a message to `chat` (an interactive chat exits 2); sign-in failures exit 3.
|
|
749
|
+
`chat.json` is mode 600 and scoped by terminal endpoint. `--new` forgets the terminal's saved
|
|
750
|
+
reference; the agent keeps its one-person history across channels. It does not erase a person's
|
|
751
|
+
WhatsApp history. Teams can replace `chat` with their own Stricli command in `commands`.
|
|
752
|
+
|
|
753
|
+
Mount the optional terminal handler beside MCP (no model or extra dependency in the CLI):
|
|
754
|
+
|
|
755
|
+
```ts
|
|
756
|
+
import { terminalServer } from "@flow-engineer/flow/mcp";
|
|
757
|
+
const terminal = terminalServer({ agent: () => agent, auth });
|
|
758
|
+
const agent = createAgent({ ...options, channels: [...channels, terminal.channel] });
|
|
759
|
+
// Route terminal.path to terminal.fetch(request), and MCP's paths to mcp.fetch(request).
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
`terminalServer` accepts `path` and `channel` overrides. It verifies auth on every POST,
|
|
763
|
+
serializes concurrent requests per person, and returns `{ conversationId, replies }` after the
|
|
764
|
+
turn settles. The authenticated key becomes the same person that `resolve` returns on other
|
|
765
|
+
channels. A body cannot choose its own person; a conversation reference from another person is
|
|
766
|
+
refused. Its custom UI uses Markdown and exports turns as channel `cli`.
|
|
767
|
+
For your own HTTP adapters, `agent.request({ channel, key, text, conversationId? })` exposes this
|
|
768
|
+
without the handler. Supply a verified key and serialize calls for that channel and person.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flow-framework-auth
|
|
3
|
+
description: Set up verified-phone sign-in, a brand’s OAuth server or its existing login, with one account across chat, MCP and the CLI and backend credentials kept in tool code.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Sign-in for a Flow Framework agent’s tools
|
|
7
|
+
|
|
8
|
+
Read `flow-framework-context` first: `context/access.yaml` describes the brand’s accounts and how its users sign in. Keep that account system. Every interface resolves to the same stable brand user id; a phone number, provider login and CLI connection are ways to reach it, not separate accounts.
|
|
9
|
+
|
|
10
|
+
## Token authority and custody: the app mints, everyone else carries
|
|
11
|
+
|
|
12
|
+
The brand (or its identity provider) issues the credential its API trusts, controls its lifetime and refresh, and can revoke it. The end client, such as the CLI or an MCP host, receives and maintains its own connection credentials and sends them with requests. Flow is the protocol handoff, not a separate authority that turns a user id into access to the brand's API.
|
|
13
|
+
|
|
14
|
+
For a brand with ordinary login, its backend functions validate the existing session and mint or exchange the brand-authorized credential for the intended client and resource. Flow can coordinate OAuth registration, redirects, PKCE and one-time code exchange, retaining the short-lived protocol state needed for that exchange. It must not become a durable vault for the end client's brand access/refresh credentials or replace them with an independent Flow-owned permission to act as that user. Refresh and revocation stay under the brand's authority.
|
|
15
|
+
|
|
16
|
+
A messaging agent can itself be a server-side client: its channel connection credentials belong behind the brand's integration boundary, not in WhatsApp/iMessage messages or model context. Distinguish that client from the OAuth handoff service. Verified-phone sign-in can authorize an operation directly when that is the brand's account policy; it does not require an extra OAuth token or login screen.
|
|
17
|
+
|
|
18
|
+
## Choose the entry point
|
|
19
|
+
|
|
20
|
+
1. **Phone is the brand’s primary identity.** A sender on a provider-verified phone channel (WhatsApp, phone-based iMessage, SMS) is signed in. Resolve that phone to the brand’s account, creating it only when the brand supports that. Give their normal full account access. Do not add an OTP, browser login, consent screen or permission picker on top of the verified channel.
|
|
21
|
+
2. **The brand already exposes an OAuth authorization server.** Use it for MCP and CLI connections. On chat channels that need account sign-in, send a connection link to its existing login. A Google sign-in button in an app is not by itself an OAuth authorization server for MCP clients.
|
|
22
|
+
3. **The brand has an ordinary login.** Use that login as the authentication step behind Flow’s OAuth layer (or a self-hosted OAuth server). Phone OTP, email, social login, company SSO and custom login can all finish the same connection. The brand confirms the signed-in account server-side and owns credential issuance and refresh; the OAuth layer coordinates client registration, one-time codes, PKCE and the brand-authorized token exchange. Do not add a Flow grant-selection screen by default. An existing provider can have its own required screens.
|
|
23
|
+
|
|
24
|
+
A phone-first brand uses case 1 on WhatsApp and case 3 for its CLI: `auth login` opens the brand’s phone login in a browser, phone verification signs in, and OAuth returns to the CLI. The user never has to create an email account just to use another interface.
|
|
25
|
+
|
|
26
|
+
## Verified phone → account
|
|
27
|
+
|
|
28
|
+
`resolve` receives `phone` only when the channel verifies phone identities. It is normalized; `address` remains the provider’s original address. A custom web address that looks like a phone does not get this evidence. Only mark a channel `phone: true` when its provider verifies the sender and your webhook authenticates that provider.
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
const resolve = async ({ phone }) => phone ? accounts.forVerifiedPhone(phone) : null;
|
|
32
|
+
const agent = createAgent({ ...options, resolve });
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Return the brand’s account id. MCP’s `auth.verify` returns that same id as `key`, so the terminal, web and phone channels share the person’s history. Without a brand resolver, phone channels still share one `phone:<E.164>` identity; `context.key` is null. Connect other login methods to an existing account only with verified account-linking proof, never a number or email supplied in model input.
|
|
36
|
+
|
|
37
|
+
## Tool code gets backend access
|
|
38
|
+
|
|
39
|
+
Supply the same `credentials` provider to `createAgent` and `mcpServer`. It runs lazily when tool code requests a named credential or backend client, with the framework’s verified identity. The brand decides how to obtain, refresh, authorize and revoke that access. This callback can support an API token, an authenticated client, a grant reference or the brand’s own authentication mechanism.
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
const credentials = async (name, context) => {
|
|
43
|
+
if (name !== "backend" || !context.key) throw new Error("Sign in required");
|
|
44
|
+
return backend.forAccount(context.key); // checks current account/session access
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const tools = [tool({
|
|
48
|
+
name: "my_orders", description: "Your orders.", input: z.object({}),
|
|
49
|
+
run: async (_input, ctx) => {
|
|
50
|
+
const api = await ctx.credentials.get("backend");
|
|
51
|
+
return api.orders();
|
|
52
|
+
},
|
|
53
|
+
})];
|
|
54
|
+
|
|
55
|
+
const agent = createAgent({ ...options, tools, resolve, credentials });
|
|
56
|
+
const mcp = mcpServer({ ...serverOptions, tools, auth, credentials });
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
In TypeScript use `ctx.credentials!.get<YourBackendClient>("backend")`. Never take an account id from tool input as authorization. The credential accessor is non-enumerable and the framework sends neither its provider nor resolved credentials to the model, history or events. Tool authors must also keep credentials out of their results, errors and logs. Do not cache a user’s access in a process-global variable. A request-local token workaround is not necessary when backend access is resolved by verified account.
|
|
60
|
+
|
|
61
|
+
When backend access depends on the caller's actual credential, `auth.verify` may return `{ key, credentials }`. That request's credential provider overrides the default for MCP and the authenticated terminal; close over the verified token there. A verified account id alone is not permission to mint or fetch a backend token. The provider is server-side and non-enumerable; it never enters model inputs or traces.
|
|
62
|
+
|
|
63
|
+
The default is full normal user access, not global administrator access. Existing business ownership checks still apply. Optional scoped connections remain available for brands that require them; do not invent a permissions UI or restrict the brand’s normal experience by default.
|
|
64
|
+
|
|
65
|
+
## Authentication at a chat tool boundary
|
|
66
|
+
|
|
67
|
+
Configure `createAgent({ auth: { verify?, signIn } })`. Public conversation remains available while signed out. Tools use the same `signIn` metadata as MCP: `"none"` is public, `"optional"` runs with a null key when unsigned, and `"required"` (default) cannot execute until authenticated. Agents without `auth` retain their existing behavior.
|
|
68
|
+
|
|
69
|
+
`verify(context)` can recheck revocation or the current channel connection. Without it, a non-null key from trusted `resolve` is enough, including a provider-verified phone mapped to a brand account. `signIn(context)` returns `{ message, url? }`: the framework delivers this as a real channel message with the login link, records it in the transcript, and does not run the protected tool. Parallel protected calls produce one challenge per channel per turn. A custom in-chat challenge may omit the URL. Login functions receive server-issued channel/address and tool identity, never model-supplied account ids. Errors fail closed and credentials stay outside model context.
|
|
70
|
+
|
|
71
|
+
After login, resolve that channel to the canonical account key; the user can resume the request. Do not automatically replay an irreversible operation merely because login succeeded.
|
|
72
|
+
|
|
73
|
+
## OAuth on MCP and the CLI
|
|
74
|
+
|
|
75
|
+
The MCP server is a resource server, not the user’s login page. Its auth adapter verifies a token on every request and returns the brand’s user id:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
const auth = {
|
|
79
|
+
issuer: "https://login.example.com",
|
|
80
|
+
resource: "https://agent.example.com/mcp",
|
|
81
|
+
async verify(token, request) {
|
|
82
|
+
const session = await accounts.verifyAgentAccess(token, request);
|
|
83
|
+
return session ? { key: session.accountId } : null;
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Verify signature (or introspect opaque tokens), issuer, expiry, intended resource and current revocation/account status. The token must be issued for this MCP resource. Native plugin endpoints advertise their own resource too: configure the authorization server and verifier for each enabled resource. Never accept an unrelated website session token and forward it unchanged to a downstream API. Backend access is separate and resolved through `credentials`, or tools run within the brand’s backend under its existing authorization.
|
|
89
|
+
|
|
90
|
+
- Missing sign-in on a required tool returns HTTP 401 with `WWW-Authenticate`; MCP hosts discover the authorization server from protected-resource metadata. Do not return “please sign in” as a tool result and expect a host to start OAuth.
|
|
91
|
+
- `signIn: "none"` tools are public, `"optional"` can run either way, and the default is `"required"`. With no scope mapping, being signed in is enough. Brands that need scopes may configure `auth.scopes` and return granted `scopes` from `verify`.
|
|
92
|
+
- The CLI already implements browser-based authorization-code sign-in with PKCE and a loopback redirect. It stores credentials per server and the SDK refreshes them. CI can supply `<NAME>_TOKEN`. Device authorization is a separate optional flow, not an existing CLI feature.
|
|
93
|
+
- The authorization server handles registered clients and redirect validation. A third-party client’s existing authorization requirements remain its own. Flow’s first-party experiences do not need an extra permissions picker.
|
|
94
|
+
|
|
95
|
+
## Hosting choices
|
|
96
|
+
|
|
97
|
+
| Deployment | Authorization server | Brand work |
|
|
98
|
+
| --- | --- | --- |
|
|
99
|
+
| Flow-hosted, existing OAuth/OIDC server | Existing provider (for example Cognito) | Register clients/redirects, authorize the agent resource, and configure token verification/backend access. Flow does not add a broker. |
|
|
100
|
+
| Flow-hosted, ordinary/custom login | Flow's protocol bridge | Keep normal login. Add brand-owned one-time login completion, credential issuance, refresh, verification and revocation functions. The bridge relays brand-issued client credentials. |
|
|
101
|
+
| Self-hosted | Existing provider or the team's own OAuth server | Publish OAuth discovery and resource metadata, configure PKCE and client/redirect validation, implement the same brand authority boundary. No Flow service is required. |
|
|
102
|
+
|
|
103
|
+
An existing OAuth server must authorize the actual agent resource and supported clients; a website's social sign-in token alone is insufficient. Discover its authorization and token endpoints rather than assuming `/auth` and `/token`. Providers without dynamic registration need registered client configuration. The CLI accepts a configured OAuth client ID, and custom surfaces remain replaceable.
|
|
104
|
+
|
|
105
|
+
## Connecting an ordinary brand login
|
|
106
|
+
|
|
107
|
+
Keep the OAuth protocol layer reusable and the account authority in the brand. The brand integration must:
|
|
108
|
+
|
|
109
|
+
1. **Finish its existing login.** A small server-side function verifies its normal session and returns a short-lived assertion or opaque one-time code identifying the account and the pending Flow connection. Bind it to the client, intended resource, callback and nonce; exchange it for a single-use brand authorization required by credential issuance; never accept a user id merely posted by the browser.
|
|
110
|
+
2. **Provide account access.** Its backend validates a credential issued for that API, or supplies an authenticated execution path. Use separate resource credentials for MCP and downstream APIs; a UID alone never authorizes an API call.
|
|
111
|
+
3. **End access.** Account disable/deletion and connection revocation stop new requests and refresh. Signing out of one client need not disconnect the others.
|
|
112
|
+
|
|
113
|
+
For Firebase the app’s function validates the signed-in user’s Firebase session, or the authenticated provider’s phone proof, and resolves its Firebase UID. Where its API uses Firebase credentials, the intended client can exchange a brand-minted custom token for its own agent session and maintain that session; Flow’s handoff service does not retain its refresh credential. Keep the Admin SDK and its service-account keys in the brand’s backend. The old local test-account bootstrap is test setup, not a production sign-in flow.
|
|
114
|
+
|
|
115
|
+
A custom login implements the same boundary. Do not make every brand adopt one identity provider or copy an OAuth server into its agent repository.
|
|
116
|
+
|
|
117
|
+
After a verified OAuth callback has saved the chat client's connection, call
|
|
118
|
+
`await agent.signInCompleted({ channel, address, key, idempotencyKey: "login:" + connectionId })`.
|
|
119
|
+
This links the verified identity and queues an internal prompt to the agent, not a fixed user-facing
|
|
120
|
+
reply. The agent continues a request blocked by sign-in, or acknowledges naturally if nothing is
|
|
121
|
+
waiting. It does not automatically replay executed tools or unrelated failures. An anonymous
|
|
122
|
+
request blocked at the auth boundary is carried forward as user text; anonymous history stays
|
|
123
|
+
separate. Pass the originating `conversationId` when the login challenge was created inside a
|
|
124
|
+
conversation; it must belong to that channel address. The prompt targets that address even if
|
|
125
|
+
another session on the same channel wrote later. Signing in preserves no-contact rules, including
|
|
126
|
+
STOP. Use a stable unique completion ID so retries and restarts do not queue another event.
|
|
127
|
+
For playground connections pass `via: "playground"`; replies stay in the simulator. Persist pending
|
|
128
|
+
completion events and retry queueing after restart instead of relying on the browser staying open.
|
|
129
|
+
`linkIdentity` and `recordNudge` remain available for custom completion behavior.
|
|
130
|
+
|
|
131
|
+
## Exercise sign-in in the playground
|
|
132
|
+
|
|
133
|
+
Pass `authentication` to `simulators` to add **New user · signed out** to the person picker. Existing declared test people retain their current identities. The hook receives the `channel` and an opaque `visitor` issued by the playground server, and returns `{ key?, address?, label?, signInUrl? }`. Resolve that visitor's connection with the application's normal auth adapter and return the canonical account `key` only after authentication. A phone-primary brand can resolve a simulated provider-verified phone immediately; other interfaces can return the application's OAuth connection URL. Never take the key or verified phone proof from request bodies, and keep credentials out of the result.
|
|
134
|
+
|
|
135
|
+
For a new user the playground checks this hook before history, messages, events and person-specific actions. While signed out, the visitor can chat and see only their own anonymous conversation; requesting a protected tool produces the normal login message. Signing in gives access to the verified account conversation. It calls `agent.simulateVisitor` with the server-verified identity, so the account's existing conversation is shared with other interfaces and replies stay in the simulator. A new user needs no declared phone or preassigned account. Each interface needs its own proof; signing in on web does not silently authorize another CLI session.
|
|
136
|
+
|
|
137
|
+
After login return to the mounted playground with `person=visitor`; its message draft stays available. **Clear history** resets the selected visitor or authenticated account's conversation and agent memory across channels, after confirmation, while keeping its sign-in connection. **New signed-out user** replaces the browser's connection identity without clearing account history or revoking other clients. An existing authorization-server session may still allow login without re-entering a password. The operator token remains required on a deployed playground, and declared people still use the developer identity shortcut. This is a developer simulator, not a customer-facing chat endpoint.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flow-framework-context
|
|
3
|
+
description: Build the context pack (OpenAPI spec, overlay, workflows, business, data model, access rules, examples) from an organization's existing codebase, before writing a Flow Framework agent's tools. Load when starting an agent for a company that already has an app, API or database, when asked for an OpenAPI spec or "the spec", or when the tools need facts nobody has written down.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# The context pack
|
|
7
|
+
|
|
8
|
+
Tools are only as good as what you know when you write them. Before writing a Flow Framework agent's
|
|
9
|
+
tools (the `flow-framework-tools` skill), gather everything about the organization's systems into one
|
|
10
|
+
folder in the agent's repository, `context/`. It is the input for every later step: the tools,
|
|
11
|
+
the agent's skills, the sign-in (the `flow-framework-auth` skill), the tests and the CLI.
|
|
12
|
+
|
|
13
|
+
Work from the organization's own code and documents. Never invent: anything you cannot find goes
|
|
14
|
+
in `context/README.md` under "Questions for the team", phrased so someone can answer it in a line.
|
|
15
|
+
|
|
16
|
+
## What goes in it
|
|
17
|
+
|
|
18
|
+
| File | Format | What it holds |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| `context/README.md` | Markdown | The index: where each file came from (repo, path, commit, who confirmed it), environments, and the questions for the team |
|
|
21
|
+
| `context/openapi.yaml` | OpenAPI 3.1, **exactly as the team gives it** | Their API: operations, request and response schemas, errors, security schemes |
|
|
22
|
+
| `context/overlay.yaml` | [OpenAPI Overlay 1.0](https://spec.openapis.org/overlay/v1.0.0) | Everything you add to their spec, as changes applied to it: clearer descriptions, units ("amount is in paise"), which fields matter, gotchas, operations to avoid |
|
|
23
|
+
| `context/workflows.arazzo.yaml` | [Arazzo 1.0](https://spec.openapis.org/arazzo/v1.0) | How calls connect: the steps of each real task, how one step's output feeds the next, what counts as success, what to do when a step fails |
|
|
24
|
+
| `context/business.md` | Markdown | What the company does, who its customers are, the words they use, the policies a person would be told ("refunds within 30 days", "never promise delivery times") |
|
|
25
|
+
| `context/data.md` | Markdown | The entities and their ids, how they relate ("an order belongs to one store"), and which fields are personal data |
|
|
26
|
+
| `context/access.yaml` | YAML | Who may do what: for each task, whether it works signed out, needs the person signed in, needs a write, or needs the person's approval each time; customer or staff only |
|
|
27
|
+
| `context/examples/*.md` | Markdown | Real situations and what should happen, including the awkward ones (a refund after the window, an order that fails to pay) |
|
|
28
|
+
|
|
29
|
+
Never put keys, tokens, passwords or real customers' data in any of these files.
|
|
30
|
+
|
|
31
|
+
**Why their spec stays untouched:** the team will send new versions. Your knowledge lives in the
|
|
32
|
+
overlay, which is applied to each new version the same way, so nothing is lost and you can see
|
|
33
|
+
exactly which overlay entries, workflows and tools point at operations that changed.
|
|
34
|
+
|
|
35
|
+
## Find the API, by stack
|
|
36
|
+
|
|
37
|
+
Look at the repository's routes, its framework and its dependencies first, then pick the case:
|
|
38
|
+
|
|
39
|
+
- **A REST API with an OpenAPI spec.** Ask for the latest version, or find it in the repo
|
|
40
|
+
(`openapi.*`, `swagger.*`, a generator such as FastAPI, NestJS's Swagger module, springdoc,
|
|
41
|
+
drf-spectacular, rswag). Use theirs as is.
|
|
42
|
+
- **A REST API without a spec.** Draft one from the routes and handlers, with an `operationId` and a
|
|
43
|
+
plain description on every operation, request and response schemas from the code's own types,
|
|
44
|
+
and the error responses the handlers send. Put `x-chinwag-draft: true` at its top and list it in
|
|
45
|
+
the questions: the team confirms it, and from then on it is theirs.
|
|
46
|
+
- **GraphQL.** Keep the schema (`context/schema.graphql`) instead of OpenAPI, and describe the
|
|
47
|
+
queries and mutations the tasks use in the workflows file as plain steps.
|
|
48
|
+
- **Supabase.** PostgREST generates an OpenAPI spec from the database. It is no longer served with
|
|
49
|
+
the public anon key: the team exports it with a secret key or the Management API
|
|
50
|
+
(`GET /v1/projects/{ref}/database/openapi`), once, and gives you the file. Read the row-level
|
|
51
|
+
security policies (`supabase/migrations`, `pg_policies`) into `access.yaml`: they are the real
|
|
52
|
+
permission model.
|
|
53
|
+
- **Firebase (Firestore, Realtime Database, Storage).** There is no API: the database and its
|
|
54
|
+
security rules are the API. There is no OpenAPI; instead:
|
|
55
|
+
- `firestore.rules` (and `database.rules.json`, `storage.rules`) into `access.yaml`: who may read
|
|
56
|
+
and write what, condition by condition;
|
|
57
|
+
- `firestore.indexes.json`: which queries are possible;
|
|
58
|
+
- the app's models and the code that reads and writes each collection into `data.md`: the
|
|
59
|
+
collections, their fields and types, and the ids that link them;
|
|
60
|
+
- Callable or HTTP Cloud Functions are its only real endpoints: list them in `data.md` with their
|
|
61
|
+
input and output.
|
|
62
|
+
- **Several of these at once** (a REST API plus Firebase for chat, say): one section per system in
|
|
63
|
+
`data.md`, and say in the workflows which system each step uses.
|
|
64
|
+
|
|
65
|
+
## Fill the rest
|
|
66
|
+
|
|
67
|
+
- **Workflows:** one per task the agent or a host should be able to do in one call ("place an
|
|
68
|
+
order" = check stock, create the order, take payment; if payment fails, cancel the order). These
|
|
69
|
+
become the tools: a tool is a task, not an endpoint.
|
|
70
|
+
- **Business and data:** from the app's own copy (its website, help pages, emails, the UI's
|
|
71
|
+
strings), its terms and policies, and its models. Mark every personal field (names, phone
|
|
72
|
+
numbers, addresses, payment details) in `data.md`: tools return them only when a task needs them.
|
|
73
|
+
- **Access:** from the rules and middleware. For each task in the workflows, write one of
|
|
74
|
+
`signed_out`, `signed_in`, `signed_in_write` or `approval` (the person confirms each time), and
|
|
75
|
+
`staff` when only the team's staff may do it. This is what the tools' `signIn` and `hints` say.
|
|
76
|
+
- **Examples:** from support tickets, chat logs or the team's own words, with personal data
|
|
77
|
+
removed. They become test scenarios (the `flow-framework-tests` skill).
|
|
78
|
+
- **Environments:** a sandbox or test server and how to get test accounts, the rate limits, how
|
|
79
|
+
the API pages results, and whether writes take an idempotency key. In `README.md`.
|
|
80
|
+
|
|
81
|
+
## Questions for the team
|
|
82
|
+
|
|
83
|
+
Ask for what the code cannot tell you. The usual ones:
|
|
84
|
+
|
|
85
|
+
1. The latest OpenAPI spec (3.1), with an `operationId` and a description on every operation.
|
|
86
|
+
2. Request and response examples, including the errors.
|
|
87
|
+
3. The security schemes and the scopes each operation needs, and how their users sign in today.
|
|
88
|
+
4. How results are paged, the rate limits, and whether writes accept an idempotency key.
|
|
89
|
+
5. A sandbox or test server, and test accounts.
|
|
90
|
+
6. The webhooks or events they send (orders shipped, payments failed).
|
|
91
|
+
7. Which tasks customers may do themselves, which need their approval each time, and which are
|
|
92
|
+
staff only.
|
|
93
|
+
|
|
94
|
+
Then go on to the `flow-framework-auth` skill (sign-in) and the `flow-framework-tools` skill (the tools).
|