@foldspace_npm/harness 0.1.27 → 0.1.28
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/CLAUDE.md +301 -188
- package/README.md +54 -36
- package/bin/ask.mjs +9 -7
- package/bin/attach.mjs +80 -51
- package/bin/badge.mjs +28 -8
- package/bin/cli.mjs +23 -6
- package/bin/inject.mjs +3 -4
- package/bin/observe.mjs +22 -6
- package/bin/published.mjs +1 -1
- package/package.json +6 -2
- package/recipes/INDEX.md +13 -8
- package/recipes/README.md +30 -7
- package/recipes/account-overview/README.md +6 -6
- package/recipes/account-overview/agent/actions/show_account_overview.ts +11 -16
- package/recipes/account-overview/agent/views/brand.ts +23 -14
- package/recipes/account-overview/agent/views/overview.ts +13 -17
- package/recipes/bottom-bar/README.md +12 -9
- package/recipes/bottom-bar/agent/bottomBar.ts +14 -12
- package/recipes/create-a-record/README.md +97 -0
- package/recipes/create-a-record/agent/actions/create_project.ts +26 -0
- package/recipes/create-a-record/agent/api/projects.ts +62 -0
- package/recipes/create-a-record/agent/projects.ts +145 -0
- package/recipes/create-a-record/agent/views/approval.ts +181 -0
- package/recipes/create-a-record/agent/views/brand.ts +35 -0
- package/recipes/create-a-record/agent/views/fields.ts +252 -0
- package/recipes/create-a-record/agent/views/project-card.ts +253 -0
- package/recipes/create-a-record/agent/views/steps.ts +92 -0
- package/recipes/create-a-record/fixtures/owners.ok.json +9 -0
- package/recipes/create-a-record/fixtures/project.created.json +1 -0
- package/recipes/create-a-record/fixtures/project.rejected.json +1 -0
- package/recipes/create-a-record/recipe.json +13 -0
- package/recipes/create-a-record/variants/actions/create_project.steps.ts +160 -0
- package/recipes/find-by-name/README.md +3 -1
- package/recipes/find-by-name/agent/actions/find_project_id.ts +1 -1
- package/recipes/lookup-then-view/README.md +73 -0
- package/recipes/lookup-then-view/agent/accounts.ts +174 -0
- package/recipes/lookup-then-view/agent/actions/find_account.ts +108 -0
- package/recipes/lookup-then-view/agent/actions/show_account_overview.ts +42 -0
- package/recipes/lookup-then-view/agent/api/accounts.ts +68 -0
- package/recipes/lookup-then-view/agent/views/account.ts +183 -0
- package/recipes/lookup-then-view/agent/views/brand.ts +35 -0
- package/recipes/lookup-then-view/agent/views/format.ts +73 -0
- package/recipes/lookup-then-view/agent/views/pick-one.ts +240 -0
- package/recipes/lookup-then-view/fixtures/accounts.json +319 -0
- package/recipes/lookup-then-view/fixtures/overview.ok.json +122 -0
- package/recipes/lookup-then-view/fixtures/overview.unsigned.json +14 -0
- package/recipes/lookup-then-view/recipe.json +11 -0
- package/recipes/lookup-then-view/studio/find_account.json +10 -0
- package/recipes/lookup-then-view/studio/show_account_overview.json +15 -0
- package/recipes/opportunities-at-risk/README.md +5 -5
- package/recipes/opportunities-at-risk/agent/actions/show_opportunities_at_risk.ts +9 -7
- package/recipes/opportunities-at-risk/agent/opportunities.ts +11 -10
- package/recipes/opportunities-at-risk/agent/views/at-risk.ts +1 -1
- package/recipes/opportunities-at-risk/agent/views/brand.ts +26 -15
- package/recipes/pick-from-a-list/README.md +7 -6
- package/recipes/pick-from-a-list/agent/actions/choose_project.ts +27 -12
- package/recipes/pick-from-a-list/agent/views/brand.ts +30 -9
- package/recipes/pick-from-a-list/agent/views/picker.ts +3 -3
- package/recipes/prepare-for-a-meeting/README.md +4 -4
- package/recipes/prepare-for-a-meeting/agent/actions/prepare_for_meeting.ts +3 -3
- package/recipes/records-list/README.md +6 -6
- package/recipes/records-list/agent/accounts.ts +3 -1
- package/recipes/records-list/agent/actions/list_accounts.ts +7 -7
- package/recipes/records-list/agent/views/brand.ts +26 -14
- package/recipes/server-rendered-forms/README.md +35 -0
- package/recipes/server-rendered-forms/agent/actions/create_record_via_form.ts +62 -0
- package/recipes/server-rendered-forms/agent/api/pages.ts +57 -0
- package/recipes/server-rendered-forms/agent/forms.ts +192 -0
- package/recipes/server-rendered-forms/fixtures/form.errors.html +9 -0
- package/recipes/server-rendered-forms/fixtures/form.html +17 -0
- package/recipes/server-rendered-forms/fixtures/list.html +9 -0
- package/recipes/server-rendered-forms/fixtures/record.html +4 -0
- package/recipes/server-rendered-forms/recipe.json +10 -0
- package/recipes/several-hosts/README.md +10 -13
- package/recipes/several-hosts/agent/hosts.ts +19 -136
- package/recipes/several-hosts/fixtures/wrong-page.json +6 -0
- package/recipes/several-hosts/recipe.json +3 -3
- package/recipes/swap-the-login-method/README.md +16 -5
- package/recipes/swap-the-login-method/agent/utils.ts +52 -3
- package/recipes/swap-the-login-method/variants/utils.cookies.ts +59 -6
- package/recipes/update-meeting-notes/README.md +10 -4
- package/recipes/update-meeting-notes/agent/actions/update_meeting_notes.ts +16 -5
- package/recipes/upload-contacts/README.md +5 -5
- package/recipes/upload-contacts/agent/actions/upload_contacts.ts +7 -7
- package/recipes/upload-contacts/agent/views/brand.ts +24 -14
- package/recipes/upload-contacts/agent/views/uploader.ts +28 -17
- package/recipes/who-is-the-user/README.md +7 -3
- package/src/ask-core.mjs +3 -3
- package/src/attach-daemon.mjs +9 -2
- package/src/attach-helpers.mjs +105 -0
- package/src/attach-preflight.mjs +34 -11
- package/src/badge-core.mjs +1 -1
- package/src/cdp-client.mjs +14 -19
- package/src/cdp-ownership.mjs +41 -20
- package/src/cli-help.mjs +7 -5
- package/src/cli-registry.mjs +65 -46
- package/src/diagnostics.mjs +1 -1
- package/src/init.mjs +5 -3
- package/src/lint/index.mjs +8 -1
- package/src/lint/rules.mjs +240 -43
- package/src/observe-core.mjs +163 -34
- package/src/region.mjs +1 -1
- package/src/runtime/agent.ts +16 -12
- package/src/runtime/http.ts +25 -15
- package/src/runtime/match.ts +1 -1
- package/src/runtime/render.ts +4 -4
- package/src/transports/README.md +2 -2
- package/src/upgrade.mjs +33 -10
- package/templates/agent-starter/CLAUDE.md +8 -5
- package/templates/agent-starter/README.md +32 -33
- package/templates/agent-starter/agent/actions/_example.ts +4 -4
- package/templates/agent-starter/agent/actions/index.ts +2 -2
- package/templates/agent-starter/agent/constants.ts +4 -3
- package/templates/agent-starter/agent/utils.ts +15 -18
- package/templates/agent-starter/claude-settings.json +1 -1
- package/templates/agent-starter/docs/app-profile.md +19 -14
- package/templates/agent-starter/foldspace.dev.json +1 -1
- package/recipes/several-hosts/fixtures/nav-record.json +0 -5
package/CLAUDE.md
CHANGED
|
@@ -4,23 +4,24 @@ You are adding an AI agent to a product that already exists. It answers
|
|
|
4
4
|
questions and *does things* inside that product, with the user's own session.
|
|
5
5
|
|
|
6
6
|
The person you are working with may not be a developer. **You run the
|
|
7
|
-
commands; they sign in
|
|
8
|
-
something they can **see on their own page**
|
|
7
|
+
commands; they sign in, talk to the agent, and say yes or no.** Their first
|
|
8
|
+
session ends at something they can **see on their own page** - not at
|
|
9
|
+
"it builds".
|
|
9
10
|
|
|
10
11
|
## Every session starts the same way
|
|
11
12
|
|
|
12
13
|
1. **Harness version.** Run `npx foldspace help --json` (from an older install,
|
|
13
14
|
`npx --yes @foldspace_npm/harness@latest upgrade --check`). If
|
|
14
|
-
`update.outdated` is true, say the
|
|
15
|
-
before upgrading**; then `foldspace upgrade --yes` and
|
|
16
|
-
upgrade as a side effect of build, attach or
|
|
17
|
-
`build` and `attach` check too:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
project `CLAUDE.md` does not contain
|
|
15
|
+
`update.outdated` is true, say which version the project is on and which is
|
|
16
|
+
newest, and **ask before upgrading**; then `npx foldspace upgrade --yes` and
|
|
17
|
+
`npm run build`. Never upgrade as a side effect of build, attach or
|
|
18
|
+
implementing an action. `build` and `attach` check too: on an older version
|
|
19
|
+
they print one line and carry on; on a version Foldspace **withdrew** they
|
|
20
|
+
refuse to run and say why. Tell the human the reason and ask to upgrade.
|
|
21
|
+
Only if they decide not to, rerun with `FOLDSPACE_SKIP_UPDATE_CHECK=1` -
|
|
22
|
+
never set it on your own. If the project `CLAUDE.md` does not contain
|
|
22
23
|
`@node_modules/@foldspace_npm/harness/CLAUDE.md`, ask before
|
|
23
|
-
`foldspace upgrade --refresh-instructions`.
|
|
24
|
+
`npx foldspace upgrade --refresh-instructions`.
|
|
24
25
|
2. **Is Foldspace connected? Check — never assume.** `region` in
|
|
25
26
|
`foldspace.dev.json` (absent means `us`) picks the server: `foldspace`, or
|
|
26
27
|
`foldspace-eu` for `eu`. Use that server's tools all session. Call its
|
|
@@ -28,8 +29,8 @@ something they can **see on their own page** — not at "it builds".
|
|
|
28
29
|
|
|
29
30
|
| Result | Do |
|
|
30
31
|
|---|---|
|
|
31
|
-
| The tool does not exist | The Foldspace MCP is not installed in this editor.
|
|
32
|
-
| It exists but fails on auth | Installed, not signed in. Only the human can approve sign-in. Tell them exactly where
|
|
32
|
+
| The tool does not exist | The Foldspace MCP is not installed in this editor. Install it yourself, the one way that fits *this* region and editor. **EU** - any editor: the MCP block on <https://foldspace.ai/eu/agents.md>, never the plugin. **US** - Claude Code with git on the machine (`command -v git`): `claude plugin marketplace add https://github.com/foldspace-ai/plugins` (already added, or added from another source, is fine), then `claude plugin install foldspace-agent-builder@foldspace-plugins`; without git, or any other editor: the MCP block on <https://foldspace.ai/agents.md>. Either way it needs sign-in: start it before your turn ends, as **Connect Foldspace tools** on that page says. **The plugin** loads when your turn ends: call `list_agents` again on the human's next message. **A server added from the MCP block** (`claude mcp add`) never reaches a chat that is already running - not on the next message, not through `/mcp`: once it is signed in, the human opens a new chat in this folder and repeats their request. `claude mcp get` saying Connected proves only the setting; the check is `list_agents` in the chat you are in |
|
|
33
|
+
| It exists but fails on auth | Installed, not signed in. Only the human can approve sign-in. Tell them exactly where - Claude Code: `/mcp` → **foldspace-eu** (**EU**) or **foldspace** (**US**; listed as `plugin:foldspace-agent-builder:foldspace` when the plugin installed it) → finish in the browser - then check again. If `/mcp` does not list that server, it was added after this chat started: the human opens a new chat in this folder, as above |
|
|
33
34
|
| It returns agents | One agent: state its name, Agent Key and product in a line and carry on; the human objects if it is wrong. Several: ask which, as a choice |
|
|
34
35
|
|
|
35
36
|
3. **Orient** — `get_agent_settings`, `list_actions`, `list_task_agents`,
|
|
@@ -80,7 +81,7 @@ current CLI contract — risk, prerequisites, effects, next step.
|
|
|
80
81
|
| Level | Reached when |
|
|
81
82
|
|---|---|
|
|
82
83
|
| **L0 · agent live** | injected, visible on the page |
|
|
83
|
-
| **L1 · read settings** | the user's own account, plan or settings
|
|
84
|
+
| **L1 · read settings** | the user's own account, plan or settings on a card, showing their real values |
|
|
84
85
|
| **L2 · look up entities** | a search from the user's own words, showing rows |
|
|
85
86
|
|
|
86
87
|
1. **Get the agent live first (L0) - with no actions.** Straight after the
|
|
@@ -103,33 +104,37 @@ current CLI contract — risk, prerequisites, effects, next step.
|
|
|
103
104
|
2. **Then offer experiences - from what you already have.** With the agent on
|
|
104
105
|
their screen, offer three or four read-only experiences as a choice,
|
|
105
106
|
recommended first. Sources: the product's own menu (`npx foldspace observe
|
|
106
|
-
menu` takes a second and moves nothing), the conversation starters seeded
|
|
107
|
-
`discover_actions`, and `docs/app-profile.md`. `discover_actions`
|
|
108
|
-
candidates, not an inventory. Do not investigate the app to build this
|
|
107
|
+
menu` takes a second and moves nothing), the conversation starters seeded
|
|
108
|
+
at sign-up, `discover_actions`, and `docs/app-profile.md`. `discover_actions`
|
|
109
|
+
gives candidates, not an inventory. Do not investigate the app to build this
|
|
109
110
|
list. Order them cheapest first: the user's own data with no parameter
|
|
110
111
|
(L1) before a search with one parameter (L2).
|
|
111
112
|
3. **Only then observe - and only what the chosen experience needs** (**Find
|
|
112
113
|
the call yourself**). Check the row count as part of that: an experience
|
|
113
114
|
on a resource the account has zero of is a demo of an empty state, so say
|
|
114
|
-
so and offer the next one.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
115
|
+
so and offer the next one. When the account has nothing to read at all,
|
|
116
|
+
say so and offer a write first: a card that creates a record and writes
|
|
117
|
+
only when the user approves it (`recipes/create-a-record`).
|
|
118
|
+
|
|
119
|
+
**Say when the test window is yours, and when it is theirs - on the page and in
|
|
120
|
+
chat.** You and the human share one Chrome window. `ask` and `observe` (except
|
|
121
|
+
`screenshot`, `pages` and `wait-login`) raise the working pill by themselves for
|
|
122
|
+
as long as they run, and drop it after. For the longer stretch - from the moment
|
|
123
|
+
you start working on their window until it is their turn - run
|
|
124
|
+
`npx foldspace badge working` (the pill at the foot of the window says Foldspace
|
|
125
|
+
is building here, leave it alone) and say so in chat: *"I'm going to work in
|
|
126
|
+
that Chrome window for a few minutes - please leave it alone until I tell you
|
|
127
|
+
it's ready."* When it is their turn, `npx foldspace badge ready` takes the pill
|
|
128
|
+
away (the small corner tag comes back) and in chat you say what to try. Nothing
|
|
129
|
+
marks their turn: they may type whatever, whenever; the pill only ever asks them
|
|
130
|
+
to wait. `npx foldspace badge label` puts the small corner tag back;
|
|
131
|
+
`npx foldspace badge off` removes it, for a screenshot they will share.
|
|
132
|
+
|
|
133
|
+
**After they have tried it, the second wave: tell Foldspace who the user is
|
|
134
|
+
(item 3 - no question, one line in chat), and offer the rest as one choice,
|
|
135
|
+
recommended first.** Once you have handed over (`badge ready`), the human has
|
|
136
|
+
asked the agent the question and - with a code project - you have taken the
|
|
137
|
+
steps in **Done, and what you say at the end**:
|
|
133
138
|
|
|
134
139
|
1. **Starters.** The sign-up starters point at actions the agent does not have
|
|
135
140
|
(clicking one answers "that action isn't available"): replace them with the
|
|
@@ -140,52 +145,70 @@ human has asked the agent the question:
|
|
|
140
145
|
project's start-up code; nothing in their source changes.
|
|
141
146
|
3. **Tell Foldspace who the user is** - `recipes/who-is-the-user`: from then on
|
|
142
147
|
their conversations and analytics stop being anonymous, and the agent has
|
|
143
|
-
real-time context about who it is talking to.
|
|
144
|
-
4. **A second experience** -
|
|
145
|
-
record
|
|
148
|
+
real-time context about who it is talking to.
|
|
149
|
+
4. **A second experience** - a read, or a write through
|
|
150
|
+
`recipes/create-a-record` - and, once a card shows records, navigation to
|
|
151
|
+
a record's page.
|
|
152
|
+
5. **No emoji in any of the agent's replies** - one line in the agent's
|
|
153
|
+
Behavior instructions (Agent Studio → **Style & Behavior** → **Behavior &
|
|
154
|
+
Responses**). No Foldspace tool writes those, so it is theirs to add: on a
|
|
155
|
+
yes, give them the line to paste and where it goes.
|
|
146
156
|
|
|
147
157
|
Not before the first experience: nothing about it needs any of these, and it
|
|
148
158
|
is silent work before the human has seen anything.
|
|
149
159
|
|
|
150
|
-
Uploads
|
|
160
|
+
Uploads and extraction are a later session.
|
|
151
161
|
|
|
152
162
|
### Ask as a choice, and ask almost nothing
|
|
153
163
|
|
|
154
164
|
Every question to the human is an **enumerated choice with the recommended
|
|
155
|
-
option first** (in Claude Code, `AskUserQuestion`). A first session
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
165
|
+
option first** (in Claude Code, `AskUserQuestion`). A first session asks only
|
|
166
|
+
this: which agent (only if several), the app's hostname, which experience
|
|
167
|
+
first - and a yes wherever this manual asks for one, such as agreeing the
|
|
168
|
+
experience (below), upgrading, publishing an action, task agent or
|
|
169
|
+
navigation, publishing their code for their users, replacing their starters or
|
|
170
|
+
adding hosts. The hostname is the only free-text question. Everything else -
|
|
171
|
+
attach mode, folder, name, level - you **state and move on**.
|
|
172
|
+
|
|
173
|
+
Then agree the experience in the human's words, before creating anything:
|
|
162
174
|
*"Show my plan and credits needs one new action on your agent and one card to
|
|
163
175
|
show it in. Sound right?"*
|
|
164
176
|
|
|
165
177
|
## One product, several hosts
|
|
166
178
|
|
|
167
|
-
Many products live on more than one host - `www.`, `
|
|
168
|
-
one domain. `init` covers
|
|
169
|
-
agent does not appear and every
|
|
179
|
+
Many products live on more than one host - `www.`, `app.`, `reports.`,
|
|
180
|
+
`admin.` under one domain. `init` covers the host it was given and that
|
|
181
|
+
host's subdomains, so on sibling hosts the agent does not appear and every
|
|
182
|
+
command refuses the window.
|
|
170
183
|
|
|
171
184
|
- **Find out at the start.** Straight after the agent is live, `npx foldspace
|
|
172
185
|
observe menu` lists the hosts the page links to that belong to the same
|
|
173
186
|
product and that this project does not cover (`otherHosts`). `observe pages`
|
|
174
187
|
and the attach log say the same when the window lands on one.
|
|
175
188
|
- **Widening is the human's yes.** Name the hosts you found and ask once:
|
|
176
|
-
*"Your product also lives on
|
|
177
|
-
|
|
178
|
-
`hosts` in `foldspace.dev.json`, `
|
|
189
|
+
*"Your product also lives on reports.… and admin.…. Add them so the agent
|
|
190
|
+
works there too?"* Then add the one pattern (`"*.example.com"`) to
|
|
191
|
+
`hosts` in `foldspace.dev.json`, then `npx foldspace attach --stop`,
|
|
192
|
+
`npm run inject`, and attach again with the same flags (**Local loop**).
|
|
179
193
|
- **Then go there yourself.** `npx foldspace observe screen --goto
|
|
180
|
-
https://
|
|
194
|
+
https://reports.example.com/` opens a screen on any host the project
|
|
181
195
|
names. Do not ask the human to click a tile you can open.
|
|
182
|
-
- **An action reads only the
|
|
183
|
-
|
|
196
|
+
- **An action reads only what the page it runs on may read** - that is the
|
|
197
|
+
browser: another host's signed-in data is refused unless that host allows
|
|
198
|
+
this one. Build each experience on the host its data lives on, and use
|
|
184
199
|
`recipes/several-hosts`: an action asked from the wrong host answers
|
|
185
200
|
`wrong_page` instead of failing.
|
|
186
|
-
- **The user
|
|
187
|
-
|
|
188
|
-
|
|
201
|
+
- **The user, their conversations and the agent's turn all carry across
|
|
202
|
+
hosts.** The SDK keeps its resume note for 60 seconds in a cookie on the
|
|
203
|
+
domain the hosts share (`example.com`), so an agent that moved the user to
|
|
204
|
+
another host in the same tab carries on there. Only the navigation's own
|
|
205
|
+
result goes with it: another action that ran in the same turn reaches the
|
|
206
|
+
agent as "not available". Build nothing for the hand-over:
|
|
207
|
+
`recipes/several-hosts` is only for the wrong-host answer above. If the
|
|
208
|
+
project already has `installResumeAcrossHosts` (a row under
|
|
209
|
+
**Workarounds** in `docs/app-profile.md`), remove it as that recipe says:
|
|
210
|
+
with both, the user sees an extra message and the agent takes a second
|
|
211
|
+
turn.
|
|
189
212
|
|
|
190
213
|
## Find the call yourself
|
|
191
214
|
|
|
@@ -210,16 +233,16 @@ and asking are not the only options — **observing is, and it comes first**:
|
|
|
210
233
|
npx foldspace observe screen --click "<a label from menu>" --match "<three to five words for the data>"
|
|
211
234
|
npx foldspace observe read "<the GET path screen returned>" # GET only: status, field names and types, row count
|
|
212
235
|
npx foldspace observe auth # only if a read came back 401 or 403
|
|
213
|
-
npx foldspace observe styles
|
|
236
|
+
npx foldspace observe styles --write # agent/views/brand.ts for the cards, before the first card
|
|
214
237
|
```
|
|
215
238
|
|
|
216
239
|
`screen` reaches a screen the way a person does and returns at most eight
|
|
217
|
-
requests, ranked by your words - field names
|
|
218
|
-
|
|
219
|
-
each header value comes from, never the value: do not go
|
|
220
|
-
storage, cookies or bundles yourself. One experience needs
|
|
221
|
-
`screen` calls is normal, five means the experience is wrong
|
|
222
|
-
product - offer the next one.
|
|
240
|
+
requests, ranked by your words - field names and row counts, **never
|
|
241
|
+
values**. `read` confirms the one you picked, with value types. `auth`
|
|
242
|
+
reports *where* each header value comes from, never the value: do not go
|
|
243
|
+
hunting through storage, cookies or bundles yourself. One experience needs
|
|
244
|
+
one screen; two `screen` calls is normal, five means the experience is wrong
|
|
245
|
+
for this product - offer the next one.
|
|
223
246
|
|
|
224
247
|
**`observe` refuses what is not provably looking**, and a refusal is an
|
|
225
248
|
answer (**Safety**): it follows links, tabs and menu openers only - a plain
|
|
@@ -231,7 +254,8 @@ and asking are not the only options — **observing is, and it comes first**:
|
|
|
231
254
|
an MFA step, a paywall, a screen that needs a record the account does not
|
|
232
255
|
have, or anything that would write.
|
|
233
256
|
|
|
234
|
-
Signing in is the human's step, because `inject` launches
|
|
257
|
+
Signing in is the human's step, because `inject` launches its own Chrome
|
|
258
|
+
profile (empty the first time; it keeps the sign-in after that).
|
|
235
259
|
Navigating is yours. **While you drive a signed-in account you are read-only:**
|
|
236
260
|
GET only, never submit a form, never click anything that creates, sends or
|
|
237
261
|
deletes.
|
|
@@ -243,11 +267,11 @@ an assumption by quoting that file later.
|
|
|
243
267
|
|
|
244
268
|
## Build: objects first, code second
|
|
245
269
|
|
|
246
|
-
Four kinds of thing. With the
|
|
270
|
+
Four kinds of thing. With the human, use **Foldspace's own words** for what
|
|
247
271
|
they will see in Agent Studio - **action**, **task agent**, **navigation**,
|
|
248
272
|
**publish** - and say what each does the first time you use it. Never invent a
|
|
249
273
|
substitute: no "skill", "ability", "capability" or "tool", and no "switch on"
|
|
250
|
-
for publish. "Skill" already means something else in Claude, and the
|
|
274
|
+
for publish. "Skill" already means something else in Claude, and the human
|
|
251
275
|
has seen "Actions" at sign-up and in Agent Studio - a new word breaks the link
|
|
252
276
|
to what they know. The action handler is yours: call it "the code behind it".
|
|
253
277
|
|
|
@@ -263,7 +287,15 @@ built-ins — no code, and a first experience can lean on them.
|
|
|
263
287
|
|
|
264
288
|
1. `create_action` as a **draft**. `generate_action_handler` works from a draft
|
|
265
289
|
schema; do not publish yet. Generating the handler from the schema is what
|
|
266
|
-
keeps the two from drifting.
|
|
290
|
+
keeps the two from drifting. **Declare every parameter fully**: type,
|
|
291
|
+
`required`, and a description that says where the value comes from. That
|
|
292
|
+
schema is what the agent reads to wire actions together by itself - a
|
|
293
|
+
required `accountId` described as "get it from `find_account`" makes it
|
|
294
|
+
call `find_account` first. Showing the one match without asking, and a
|
|
295
|
+
searchable list when there are several or none, come from `find_account`'s
|
|
296
|
+
instructions and code (`recipes/lookup-then-view`). An id marked optional,
|
|
297
|
+
or described only as "the id", leaves the agent guessing, or asking the end
|
|
298
|
+
user for an id they do not have.
|
|
267
299
|
2. Implement the handler from the observed request and response shapes. A
|
|
268
300
|
handler is `fetch` plus `runTask` and nothing more. Import helpers from
|
|
269
301
|
`agent/utils.ts` — inspect that file before writing another `fetch` or rank
|
|
@@ -273,29 +305,35 @@ built-ins — no code, and a first experience can lean on them.
|
|
|
273
305
|
|
|
274
306
|
**Task agents** are for the one LLM subtask deterministic code cannot do —
|
|
275
307
|
extraction, summarisation, classification, normalisation. Not for API calls,
|
|
276
|
-
CRUD, routing or parsing a known shape.
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
308
|
+
CRUD, routing or parsing a known shape. Create one with `create_task_agent` and
|
|
309
|
+
publish it with `publish_task_agent` (with the human's yes - it changes their
|
|
310
|
+
account); call it with `getAgent().runTask({ taskKey, data })` - published, or
|
|
311
|
+
it does not resolve. `data` carries facts, never `prompt` or `instructions`. One
|
|
312
|
+
owner per concern: extraction reads, normalisation converts, nothing does both.
|
|
280
313
|
<https://docs.foldspace.ai/user-guides/task-agents/>
|
|
281
314
|
|
|
282
|
-
**
|
|
283
|
-
|
|
284
|
-
the code
|
|
285
|
-
|
|
315
|
+
**Publishing an edit to something already live changes the live agent at
|
|
316
|
+
once** - unreviewed, and publishing the earlier version again puts back its
|
|
317
|
+
schema but not the code you changed to match the new one. Never edit it to
|
|
318
|
+
test. Stand up a `_gen2` beside it, point the code at that, and roll back by
|
|
319
|
+
pointing back. `_gen2`, never `_v2` - Foldspace owns the `v` counter. The
|
|
320
|
+
suffix is permanent.
|
|
286
321
|
|
|
287
322
|
### The moment a handler exists, prove it
|
|
288
323
|
|
|
289
324
|
Do not batch verification to the end. As soon as a handler is registered:
|
|
290
|
-
build, confirm it reached `dist/index.js`, attach
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
325
|
+
build, confirm it reached `dist/index.js`, `attach --stop` and attach again
|
|
326
|
+
with the same flags (**Local loop**), confirm `registration_ok`, and
|
|
327
|
+
`npx foldspace observe read` the endpoint it calls. Once the action is
|
|
328
|
+
published (**Ask before publishing**),
|
|
329
|
+
`npx foldspace ask "<the question>" --retry 2` and check that the reply - and
|
|
330
|
+
the card, when the action draws one - shows real data: an empty answer only
|
|
331
|
+
proves the call ran.
|
|
332
|
+
|
|
333
|
+
Say what that proved and what it did not: `observe read` proves the endpoint
|
|
334
|
+
answers for this account; only the chat turn proves the experience (gate 5).
|
|
335
|
+
Then record the endpoint, auth source, response shape and row count in
|
|
336
|
+
`docs/app-profile.md`, with how you established each.
|
|
299
337
|
|
|
300
338
|
## Reachability: everything must be published
|
|
301
339
|
|
|
@@ -305,53 +343,71 @@ with how you established each.
|
|
|
305
343
|
| Task agent | **yes** | a `taskKey` in code names it |
|
|
306
344
|
| Navigation route | **yes** | `isActive: true` |
|
|
307
345
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
346
|
+
**The agent offers only what the browser has.** The SDK tells Foldspace the
|
|
347
|
+
key of every handler loaded on the page, and the agent offers only the
|
|
348
|
+
published actions among those keys - none, on a page that loads no handlers.
|
|
349
|
+
So a published action reaches someone only when its code is in what their
|
|
350
|
+
browser loads: the test window has it while attach runs; their users get it
|
|
351
|
+
once the code is in the bundle their site loads (**Done, and what you say at
|
|
352
|
+
the end**). A handler whose key does not match the action's key is never
|
|
353
|
+
offered.
|
|
354
|
+
|
|
355
|
+
A **new** action or task agent therefore changes nothing for their users until
|
|
356
|
+
its code reaches them. A navigation route needs no code: once both switches
|
|
357
|
+
below are on, it is live for them. An **edit to anything live** changes the
|
|
358
|
+
live agent at once - an action or task agent when you publish the edit
|
|
359
|
+
(above), a navigation route as soon as `update_navigation_route` replaces it.
|
|
360
|
+
**Navigation has two switches, and both must be on.** Each route has its own
|
|
361
|
+
`isActive`, set when you create it
|
|
362
|
+
(`create_navigation_route` with `is_active: true`, or `isActive: true` per route
|
|
363
|
+
in `bulk_create_navigation_routes`). The agent has one agent-level switch, **off
|
|
364
|
+
by default on a new subscription**: `publish_navigation` turns it on,
|
|
365
|
+
`unpublish_navigation` off, and neither touches the routes' own `isActive`.
|
|
366
|
+
`list_navigation_routes` shows both. To change one route's switch later,
|
|
367
|
+
`update_navigation_route` - it replaces the whole route, so read it first
|
|
368
|
+
(`get_navigation_route`) and send every field back. Route descriptions are a
|
|
369
|
+
retrieval surface: write what the page is *for*, in the user's words, and pair
|
|
370
|
+
every parameterised route with the lookup action that produces its parameter -
|
|
371
|
+
or ship neither.
|
|
372
|
+
|
|
373
|
+
**Ask before publishing.** It changes their Foldspace account, and an edit to
|
|
374
|
+
something live - or a new navigation route - reaches their users at once.
|
|
323
375
|
|
|
324
376
|
## Local loop
|
|
325
377
|
|
|
326
378
|
```bash
|
|
327
|
-
npm run
|
|
379
|
+
npm run build
|
|
328
380
|
npm run inject
|
|
329
381
|
npx foldspace attach --daemon
|
|
330
382
|
```
|
|
331
383
|
|
|
332
384
|
`inject` launches an isolated Chrome profile and records its debug port; it
|
|
333
385
|
loads nothing. In a first session `attach` comes straight after sign-in (L0),
|
|
334
|
-
and `foldspace observe` reads the same window while it runs. `attach` prepares
|
|
335
|
-
`dist/index.js` over CDP.
|
|
336
|
-
`[lifecycle] inspect_registration:…`. An empty local
|
|
337
|
-
build` is still required so `dist/index.js` exists.
|
|
386
|
+
and `foldspace observe` reads the same window while it runs. `attach` prepares
|
|
387
|
+
the page and loads the local `dist/index.js` over CDP. Use `--daemon` so the
|
|
388
|
+
command returns after `[lifecycle] inspect_registration:…`. An empty local
|
|
389
|
+
registry is valid; `npm run build` is still required so `dist/index.js` exists.
|
|
338
390
|
|
|
339
391
|
Attach mode is read off the page, never asked: default swap when the page
|
|
340
392
|
already has the configured product and agent; `--bootstrap` when it has no
|
|
341
393
|
Foldspace SDK; `--replace` when it embeds a different product or agent, or an
|
|
342
394
|
SDK without the configured agent.
|
|
343
395
|
|
|
344
|
-
`inspect_registration:registration_ok` is the signal. On
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
it
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
396
|
+
`inspect_registration:registration_ok` is the signal. On
|
|
397
|
+
`registration_mismatch`, the attach log says what differs - another agent or
|
|
398
|
+
product on the page, or action names that do not match - and the next step:
|
|
399
|
+
take it. A running attach keeps what it started with - the actions, the hosts
|
|
400
|
+
and the flags - so after a rebuild, `attach --stop` and attach again with the
|
|
401
|
+
same flags; after a change to `hosts`, `npm run inject` between the two
|
|
402
|
+
(**One product, several hosts**). Those, a mismatch's next step, and checking
|
|
403
|
+
published code on dev (**Done, and what you say at the end**) are the only
|
|
404
|
+
reasons to stop it.
|
|
405
|
+
|
|
406
|
+
Prove a named action through the visible agent. For your own chat turn,
|
|
407
|
+
`npx foldspace ask` reports whether `execute` and `render` ran; after a turn
|
|
408
|
+
of the human's, read the attach log for the `[actions]` SDK callback and local
|
|
409
|
+
execute/render lines. Those record names, statuses, durations and parameter
|
|
410
|
+
keys only - never results or error bodies.
|
|
355
411
|
|
|
356
412
|
## Done, and what you say at the end
|
|
357
413
|
|
|
@@ -420,17 +476,26 @@ Do not report success without all six:
|
|
|
420
476
|
action the captured registry includes it.
|
|
421
477
|
5. The action behaves against the real workflow: `[actions]
|
|
422
478
|
local-handler:execute` in the attach log after `foldspace ask` (or the
|
|
423
|
-
human) exercises the visible agent. A
|
|
424
|
-
turn
|
|
425
|
-
|
|
479
|
+
human) exercises the visible agent. A card also needs
|
|
480
|
+
`local-handler:render` from a real chat turn - not a hand-built `render()`
|
|
481
|
+
call. A view card leaves the chat free: in `ask`'s screenshot the panel's
|
|
482
|
+
message box does not read "Provide input, or cancel" beside a Cancel
|
|
483
|
+
button. If it does, `render` uses `callback` or `cancel` somewhere, or the
|
|
484
|
+
handler sets `awaitUserInput` - take them out. Then ask a follow-up
|
|
485
|
+
question about the card (`foldspace ask` again): the reply quotes its
|
|
486
|
+
numbers exactly as the card shows them. If it does not, make `execute`
|
|
487
|
+
return them as the card shows them (rule 2). An input card holds the chat
|
|
488
|
+
until the user answers or closes it, unless it calls `readyState`
|
|
489
|
+
(**Agentic UI components**).
|
|
426
490
|
6. Browser evidence came from the live target, not hand-authored examples.
|
|
427
|
-
Neighbour fixtures still pass when they exist.
|
|
428
491
|
|
|
429
492
|
## Rules that are not negotiable
|
|
430
493
|
|
|
431
494
|
1. **Handlers return data only.** Never `directive`, `instructions`, or a
|
|
432
|
-
paragraph telling the
|
|
433
|
-
Never return
|
|
495
|
+
paragraph telling the agent what to say - those live in Agent Studio.
|
|
496
|
+
Never return from `execute` a token, a cookie, any other secret or
|
|
497
|
+
`ApiResult.detail` (it is for the console): the agent reads what
|
|
498
|
+
`execute` returns for an action with no card or a view card (rule 2).
|
|
434
499
|
Validate parameters and return sanitised errors.
|
|
435
500
|
**What goes back to the agent states what happened - it never says what to
|
|
436
501
|
do next.** A status is state: it succeeded, it failed and why (`reason`),
|
|
@@ -442,28 +507,36 @@ Do not report success without all six:
|
|
|
442
507
|
`success: true`, the query, zero matches - *"No <records> match
|
|
443
508
|
'<query>'."* - and it stops there.
|
|
444
509
|
2. **`render` receives `execute`'s return value**, not the input params. A
|
|
445
|
-
|
|
446
|
-
|
|
510
|
+
card built from the params passes in isolation and fails in the real chat:
|
|
511
|
+
return from `execute` every id the card needs. For a view card the agent
|
|
512
|
+
gets that same value as soon as `render` returns and answers questions
|
|
513
|
+
about the card from it, so return the facts the card shows, as it shows
|
|
514
|
+
them, and those ids - never the raw API response. To change what the
|
|
515
|
+
agent gets, change `execute`, not the card (**Agentic UI components**).
|
|
516
|
+
<https://docs.foldspace.ai/guides/in-chat-ui/>
|
|
447
517
|
3. **Say what you verified and what you assumed.** "The docs say X" and "I
|
|
448
518
|
think X" are different claims — mark which. Search `docs.foldspace.ai` over
|
|
449
519
|
MCP (`search_docs`, `fetch_page`) before asserting how the platform behaves;
|
|
450
520
|
cite the page, or say plainly that the docs do not cover it and write down
|
|
451
521
|
what you learned.
|
|
452
522
|
4. **Test through the agent, then let them.** In a first session, prove an
|
|
453
|
-
experience yourself before the
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
523
|
+
experience yourself before the human sees it: once the action is published
|
|
524
|
+
and registered, `npx foldspace ask "<the question>" --retry 2` types it into
|
|
525
|
+
the agent's panel in the test window the way a person would, and reports the
|
|
526
|
+
reply, the card's text, whether `execute` and `render` ran (from the attach
|
|
527
|
+
log) and a screenshot. A reply with no action fired is publish lag or a wrong
|
|
528
|
+
key - `--retry` covers the lag. Look at the screenshot. Only then
|
|
529
|
+
`npx foldspace badge ready` and the invitation to try it; never hand the
|
|
530
|
+
first test to the human. **Ask only what reads.** A question that makes the
|
|
531
|
+
agent create, send, change or delete something is you writing in their
|
|
532
|
+
product: ask it only in an environment the human has named as a test one, or
|
|
533
|
+
let them type it. A card that writes only when the user approves it
|
|
534
|
+
(`recipes/create-a-record`) is the exception: the question draws the card,
|
|
535
|
+
`ask` presses nothing on it, and nothing is written - check what it shows,
|
|
536
|
+
and leave its buttons to the human. The conversations you start appear in
|
|
537
|
+
their Foldspace dashboard, and that is wanted: seeing their own
|
|
538
|
+
conversations recorded is part of what they came to see. Never pass
|
|
539
|
+
`--test-mode` in a first session.
|
|
467
540
|
5. **Test mode is for an agent that already has real users.** Then `attach
|
|
468
541
|
--test-mode` keeps your conversations out of their data. It is not a safety
|
|
469
542
|
net: publishing still changes the live agent. Working on an agent in
|
|
@@ -472,9 +545,9 @@ Do not report success without all six:
|
|
|
472
545
|
tool is the exception: it takes the same dev-then-production path as a new
|
|
473
546
|
agent (**Done, and what you say at the end**).
|
|
474
547
|
6. **The human is not necessarily a developer.** Never hand over a terminal
|
|
475
|
-
line, a stack trace, or a choice between flags. Name experiences by
|
|
476
|
-
|
|
477
|
-
sign in,
|
|
548
|
+
line, a stack trace, or a choice between flags. Name experiences by
|
|
549
|
+
outcome - *"show my plan and credits"*, not `get_account_status`. Their
|
|
550
|
+
jobs: sign in, talk to the agent, and say yes or no.
|
|
478
551
|
7. **Be short.** No recap of what was established, no restating the plan before
|
|
479
552
|
each step. Progress is one line: what you are doing, the level reached, and
|
|
480
553
|
the question if there is one.
|
|
@@ -488,7 +561,7 @@ Do not report success without all six:
|
|
|
488
561
|
and the recipes first: it may be supported in a way you have not found.
|
|
489
562
|
Say "I did not find it", never "Foldspace does not support it".
|
|
490
563
|
- **Build the narrowest workaround that gives them a working experience.**
|
|
491
|
-
|
|
564
|
+
The human is never left stuck on a Foldspace limitation.
|
|
492
565
|
- **Register it, in two places.** A comment in the code where it lives -
|
|
493
566
|
what it works around, how you established that, and that it goes when the
|
|
494
567
|
fix ships - and a row under **Workarounds** in `docs/app-profile.md`:
|
|
@@ -501,8 +574,10 @@ Do not report success without all six:
|
|
|
501
574
|
|
|
502
575
|
Lint catches some of these in handler code; Agent Studio copy is on you.
|
|
503
576
|
|
|
504
|
-
- **No emoji** in
|
|
505
|
-
|
|
577
|
+
- **No emoji** in agent replies, cards or handler strings. Put that rule in
|
|
578
|
+
each new action's Studio `instructions` yourself. It reaches the agent's
|
|
579
|
+
other replies only if the human adds it to the Behavior instructions: offer
|
|
580
|
+
that in the second wave (item 5).
|
|
506
581
|
- **Never send the user out of the host app.** No "open in <product>" button,
|
|
507
582
|
no pasted URLs — navigate with a route, same tab.
|
|
508
583
|
|
|
@@ -527,26 +602,56 @@ wins**. <https://docs.foldspace.ai/customize/configuration/>
|
|
|
527
602
|
mount code is the other place it can live, their engineers' call. Offer it in
|
|
528
603
|
the second wave.
|
|
529
604
|
- **An action's `execute` never calls `setConfiguration`**: a partial call from
|
|
530
|
-
one
|
|
605
|
+
one can collapse the agent's panel to nothing.
|
|
531
606
|
|
|
532
607
|
## Agentic UI components: what bites
|
|
533
608
|
|
|
534
609
|
An Agentic UI component is the card an action draws in the chat (`render`).
|
|
535
610
|
Each of these cost a real build.
|
|
536
611
|
|
|
537
|
-
- **A
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
612
|
+
- **A card is a view card or an input card.** `render(data, host, header,
|
|
613
|
+
callback, cancel, readyState)` decides which: a `render` that uses
|
|
614
|
+
`callback`, `cancel` or `readyState` is an input card, and the agent waits
|
|
615
|
+
until the card reports through one of them; one that uses none of them is
|
|
616
|
+
a view card.
|
|
617
|
+
- **A view card shows something** - an account, a list, deals at risk -
|
|
618
|
+
with `render(data, host, header)`: no `callback`, no `cancel`. As soon as
|
|
619
|
+
`render` returns, the agent gets "Shown to the user. The user has not
|
|
620
|
+
acted on it." plus exactly what `execute` returned, and answers questions
|
|
621
|
+
about the card from that. So `execute` returns the facts the card shows,
|
|
622
|
+
as fields, and nothing else - or the failure the card draws with
|
|
623
|
+
`renderFailure`: `{ success: false, error, reason }`. To change what the
|
|
624
|
+
agent gets, change `execute`, not the card. Nothing disables a view card:
|
|
625
|
+
its tabs and links keep working.
|
|
626
|
+
- **An input card asks the user** - pick one, upload, create, approve. It
|
|
627
|
+
calls `callback` or `cancel` on every path: choosing, "none of these",
|
|
628
|
+
closing the card, failing. The agent waits until one of them runs; a path
|
|
629
|
+
that never calls back leaves it waiting until the user presses stop.
|
|
630
|
+
`callback(value)` reaches the agent as the user's answer and disables the
|
|
631
|
+
card; `callback(value, false)` keeps it usable. `cancel()` reaches the
|
|
632
|
+
agent as cancelled, with no data - when the agent needs to know how the
|
|
633
|
+
card was closed, call `callback` with that state instead, as
|
|
634
|
+
`pick-from-a-list` does (`cancelled: true`). ⚠️ The docs say the SDK also
|
|
635
|
+
draws a default "Cancel" control on a card; not yet checked in the live
|
|
636
|
+
product.
|
|
637
|
+
- **When the conversation should go on while the user decides,** an input
|
|
638
|
+
card calls `readyState(value)` (`render`'s 6th argument) once it is
|
|
639
|
+
drawn: the agent gets `value` as what is shown and carries on. ⚠️ The
|
|
640
|
+
user's answer then arrives as a follow-up turn - seen in the SDK's
|
|
641
|
+
headless mode (`mode: "HEADLESS"`, the customer's own chat UI), not yet
|
|
642
|
+
checked in the chat panel. No recipe uses it yet.
|
|
643
|
+
- **Never set `awaitUserInput`** on either kind: `render` decides whether
|
|
644
|
+
the agent waits. An input card does not need it. The chat panel ignores
|
|
645
|
+
it; in headless mode `awaitUserInput: true` on a view card leaves the
|
|
646
|
+
agent waiting for a callback that never comes, until the user presses
|
|
647
|
+
stop. Lint stops the build on `awaitUserInput: true` on a view card, and
|
|
648
|
+
warns on any other `awaitUserInput` and on a `render` that declares
|
|
649
|
+
`callback`, `cancel` or `readyState` but uses none of them (the SDK then
|
|
650
|
+
treats it as a view card).
|
|
651
|
+
- **What reaches the agent is the tool response: the data and the user's
|
|
652
|
+
choice, as fields**, with the state (chosen, declined, cancelled, failed)
|
|
653
|
+
and nothing about what to do next (**Rules**, 1). A view card needs no
|
|
654
|
+
state of its own: the SDK tells the agent it was shown.
|
|
550
655
|
- **A failed or signed-out call is never drawn as zeros.** A signed-out response
|
|
551
656
|
often looks like a valid empty one: a 200 with zeros, nulls or an empty list.
|
|
552
657
|
Before you draw numbers, check that the response belongs to a signed-in user -
|
|
@@ -559,11 +664,15 @@ Each of these cost a real build.
|
|
|
559
664
|
stays clean, and the action is recorded as succeeded. Only looking at it shows
|
|
560
665
|
the problem. Draw the empty or the failed state (`renderEmpty`,
|
|
561
666
|
`renderFailure`) - never nothing.
|
|
562
|
-
- **Totals and money are never computed by the
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
667
|
+
- **Totals and money are never computed by the agent.** Compute them in
|
|
668
|
+
code, from the API's own numbers - in `execute` for a view card, since its
|
|
669
|
+
result is what the agent gets - show them on the card, and hand the agent
|
|
670
|
+
the same figures. In the action's Studio `instructions`, tell the agent to
|
|
671
|
+
answer follow-up questions from the data it receives, to quote money and
|
|
672
|
+
counts exactly as given, never to add them up itself (a model that adds up
|
|
673
|
+
line items will eventually add them up wrong), and, right after a view
|
|
674
|
+
card, to say one short sentence at most - never repeat what the card
|
|
675
|
+
shows.
|
|
567
676
|
- **A card matches the panel, not the page.** The card sits in the agent's
|
|
568
677
|
panel - white by default (the agent's theme: `backgroundColor #FFFFFF`,
|
|
569
678
|
`primaryColor` for accents; a `darkTheme` when the panel is dark, and the
|
|
@@ -574,13 +683,8 @@ Each of these cost a real build.
|
|
|
574
683
|
imports it, and the recipes' `brand.ts` files are placeholders for it. A
|
|
575
684
|
dark app keeps its accent and font; its light-on-dark text would vanish on
|
|
576
685
|
white, so that falls back to greys and the file says so. Record where the
|
|
577
|
-
values came from in the profile's Brand section. Never copy colours by eye
|
|
578
|
-
|
|
579
|
-
- **Plain hex or rgba only.** The panel saves every card to the conversation
|
|
580
|
-
history as an image drawn with html2canvas, which cannot read `oklch()`,
|
|
581
|
-
`lab()` or `color-mix()`: one such value and the saved card is blank
|
|
582
|
-
(`data:,` where the card was). Lint flags them. Do not read the app's CSS
|
|
583
|
-
variables either - on modern apps they resolve to oklch.
|
|
686
|
+
values came from in the profile's Brand section. Never copy colours by eye:
|
|
687
|
+
that is how a dark card ends up in a white panel.
|
|
584
688
|
|
|
585
689
|
## Layout
|
|
586
690
|
|
|
@@ -602,20 +706,26 @@ Do not introduce another bundler or bundle format.
|
|
|
602
706
|
## Working examples to copy from
|
|
603
707
|
|
|
604
708
|
Before writing a handler or a card, read
|
|
605
|
-
`node_modules/@foldspace_npm/harness/recipes/INDEX.md`
|
|
606
|
-
short
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
709
|
+
`node_modules/@foldspace_npm/harness/recipes/INDEX.md` - the whole table, it is
|
|
710
|
+
short - and open the recipe closest to the outcome. Each is a finished piece of
|
|
711
|
+
a build, generalised from production builds with the customer taken out: its
|
|
712
|
+
code and its fixtures (for most card recipes, a recorded response per state).
|
|
713
|
+
Copy its `agent/` files across, then replace the placeholder path and fields
|
|
714
|
+
with what you observed. They match the harness version you have installed, and
|
|
715
|
+
every one is built and run on each release.
|
|
611
716
|
|
|
612
717
|
After the first experience → `who-is-the-user`. L2 → `find-by-name`, and
|
|
613
718
|
`pick-from-a-list` when the user has to choose. The user's records on one
|
|
614
719
|
card → `records-list`. The product lives on several hosts → `several-hosts`.
|
|
615
|
-
|
|
720
|
+
The data arrives inside the HTML, with no JSON call → `server-rendered-forms`.
|
|
721
|
+
Show one account the user names → `lookup-then-view` (start here). The app
|
|
722
|
+
already hands over the id → `account-overview`. Upload a
|
|
616
723
|
CSV of contacts → `upload-contacts`. Save a note on a meeting →
|
|
617
|
-
`update-meeting-notes`.
|
|
618
|
-
|
|
724
|
+
`update-meeting-notes`. Create one record from what the user said, edited on a
|
|
725
|
+
card before it is written → `create-a-record` (all at once, or one question per
|
|
726
|
+
card then a review - its README says which). Prepare for a meeting →
|
|
727
|
+
`prepare-for-a-meeting`. The records that need attention, with reasons →
|
|
728
|
+
`opportunities-at-risk`.
|
|
619
729
|
App does not use a bearer token → `swap-the-login-method`.
|
|
620
730
|
Each says how many production builds carry the pattern and what they learned.
|
|
621
731
|
If nothing fits — there is no L1 card recipe yet, and no navigation recipe —
|
|
@@ -632,11 +742,11 @@ not import it — fix the import when you implement.
|
|
|
632
742
|
| `publicFetch` | Unauthenticated / marketing origin | Signed-in product APIs |
|
|
633
743
|
| `getAuthToken` / `parseJwt` | `AUTH_SOURCE` is `localStorage` or `cookie` | Custom header schemes — override `apiFetch` in `utils.ts` |
|
|
634
744
|
| `rankBy` | User typed a name; API search is exact or ignored | Domain ranking (invoices, coverage, industry classification codes) |
|
|
635
|
-
| `renderLoading` / `Empty` / `Error` / `Fatal` |
|
|
745
|
+
| `renderLoading` / `Empty` / `Error` / `Fatal` | Plain states inside a card (loading, empty, error) | Branded cards - those stay in `agent/views/` |
|
|
636
746
|
| `renderFailure` | Any `{ ok: false }` from `apiFetch` — it picks the state from `reason` | You need bespoke copy for one reason; then branch on `reason` yourself |
|
|
637
747
|
| `httpFailure` / `networkFailure` | Your own `apiFetch` (custom header, cookies) — keeps the same `reason`s | The default `apiFetch` fits; then you never call these |
|
|
638
|
-
| `getAgent` | Talking to Foldspace | You need a specific instance
|
|
639
|
-
| `armAllInstances` |
|
|
748
|
+
| `getAgent` | Talking to Foldspace (the overlay instance) | You need a specific instance - then `window.foldspace.agent({ apiName, mode })`, with `apiName` the Agent Key and `mode` in capitals (`"EMBEDDED"`). `window.foldspace.agentIds()` lists the instances in lower case (`"embedded-<apiName>"`): ask only for one it lists - `agent()` creates an instance that does not exist |
|
|
749
|
+
| `armAllInstances` | Never in agent code - `attach` handles the page, and test mode is `attach --test-mode`, only for an agent that already has real users | Anywhere in `agent/`: it ships to production |
|
|
640
750
|
| `redact` / `mapWithConcurrency` | Logging tokens; batching fetches | — |
|
|
641
751
|
|
|
642
752
|
Every failed call carries a `reason` — `signed_out`, `forbidden`, `not_found`,
|
|
@@ -668,10 +778,13 @@ the package import is right.
|
|
|
668
778
|
what you wanted it for, or take a narrower read-only route. Never retry it,
|
|
669
779
|
and never try a different command hoping it gets through - not re-quoted, not
|
|
670
780
|
in pieces, not through a helper script or a subagent: a denial covers the
|
|
671
|
-
outcome, not the string. If
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
781
|
+
outcome, not the string. If the session's own checks - not the human -
|
|
782
|
+
denied a `foldspace` command other than `init`, `upgrade` or `deploy`, name
|
|
783
|
+
it and give the human the allow rule for that one command only, to add to
|
|
784
|
+
`.claude/settings.local.json` - for `observe`,
|
|
785
|
+
`Bash(npx foldspace observe:*)` - and meanwhile hand that step to them: for
|
|
786
|
+
a chat turn, that is them typing the question into the panel. `init`,
|
|
787
|
+
`upgrade` and `deploy` ask on purpose: never give an allow rule for them.
|
|
675
788
|
- **Never create an account or a key** - not in Foldspace, not in the
|
|
676
789
|
customer's product, not anywhere. If one is missing, say which and where it
|
|
677
790
|
goes. **Never widen your own access**: if something is refused for a site or
|