@foldspace_npm/harness 0.1.26 → 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 +402 -204
- package/README.md +84 -36
- package/bin/ask.mjs +19 -9
- package/bin/attach.mjs +91 -56
- 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 +141 -0
- 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 +16 -4
- 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 +15 -19
- package/src/cdp-ownership.mjs +41 -20
- package/src/cli-help.mjs +7 -5
- package/src/cli-registry.mjs +102 -47
- package/src/diagnostics.mjs +1 -1
- package/src/init.mjs +5 -3
- package/src/keep-focus.mjs +17 -13
- package/src/lint/index.mjs +8 -1
- package/src/lint/rules.mjs +240 -43
- package/src/observe-core.mjs +163 -34
- package/src/published-core.mjs +392 -0
- 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 +2 -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`,
|
|
@@ -38,6 +39,39 @@ something they can **see on their own page** — not at "it builds".
|
|
|
38
39
|
seeded at sign-up are often a specification someone already wrote.
|
|
39
40
|
4. **Read `docs/app-profile.md`.** It is what is already known about this app,
|
|
40
41
|
with how each fact was established.
|
|
42
|
+
5. **Does the agent already have code saved in Foldspace?** If your Foldspace
|
|
43
|
+
tools include `repo_read`, call it with command `status` now - before the
|
|
44
|
+
first build, because the first build is what the test window shows them.
|
|
45
|
+
- **A code project.** Call `repo_read` with command `instructions` and
|
|
46
|
+
follow what it returns. The project's `agent/` is the copy Foldspace
|
|
47
|
+
builds and publishes, and it may hold work someone else saved. Before you
|
|
48
|
+
build, replace this folder's `agent/` with it (`repo_read` commands `tree`
|
|
49
|
+
and `file`): delete what is there rather than copying over the top, or
|
|
50
|
+
their page shows the template instead of their agent. An action file here
|
|
51
|
+
that the project lacks is work a session never saved: keep it aside and
|
|
52
|
+
put it back after.
|
|
53
|
+
- **`NO_REPO`.** Nothing is saved yet. Follow the next step `status` gives:
|
|
54
|
+
it creates the code project in about a minute and changes nothing anyone
|
|
55
|
+
sees - say you are setting up their project, no need to ask. Get the agent
|
|
56
|
+
live in this folder meanwhile (L0 needs no code). Once `status` shows the
|
|
57
|
+
project, replace this folder's `agent/` with its `agent/` as above, before
|
|
58
|
+
you write the first action.
|
|
59
|
+
- **A refusal that names `foldspace-ai-access`.** This agent has saved
|
|
60
|
+
code that Foldspace has not opened to these tools; that is final. Tell
|
|
61
|
+
the human that Foldspace manages their agent's saved code, so this build
|
|
62
|
+
starts fresh.
|
|
63
|
+
- **A refusal that starts "Your Foldspace account cannot".** It is about
|
|
64
|
+
the signed-in account, so you cannot tell whether code is saved. Pass its
|
|
65
|
+
words on as they are - they are written for the human.
|
|
66
|
+
- **Anything else** - `status` says a project claims this agent but cannot
|
|
67
|
+
be used, or another error. Create none. Tell the human in your own words
|
|
68
|
+
that their agent's saved code cannot be used right now; never pass on the
|
|
69
|
+
text that came back - it can name things they cannot open.
|
|
70
|
+
|
|
71
|
+
After any of these three, build in this folder, never stop the session
|
|
72
|
+
over it, and tell the human once that the code stays on this computer.
|
|
73
|
+
- **No `repo_read` tool.** This folder is the only copy, so there is no code
|
|
74
|
+
project.
|
|
41
75
|
|
|
42
76
|
Before an unfamiliar harness operation, `npx foldspace help --json` is the
|
|
43
77
|
current CLI contract — risk, prerequisites, effects, next step.
|
|
@@ -47,14 +81,15 @@ current CLI contract — risk, prerequisites, effects, next step.
|
|
|
47
81
|
| Level | Reached when |
|
|
48
82
|
|---|---|
|
|
49
83
|
| **L0 · agent live** | injected, visible on the page |
|
|
50
|
-
| **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 |
|
|
51
85
|
| **L2 · look up entities** | a search from the user's own words, showing rows |
|
|
52
86
|
|
|
53
|
-
1. **Get the agent live first (L0) - with no actions.** Straight after
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
87
|
+
1. **Get the agent live first (L0) - with no actions.** Straight after the
|
|
88
|
+
steps above (with a code project, once its `agent/` is in place - step 5):
|
|
89
|
+
`npm run build`, `npm run inject`, and one ask - *"A Chrome window opened at
|
|
90
|
+
<Product>'s login. Please log in there - I'll notice when you're in."* Then
|
|
91
|
+
`npx foldspace observe wait-login`: it returns the moment they are in, so
|
|
92
|
+
nobody has to type "I've logged in". Then
|
|
58
93
|
`npx foldspace attach --daemon` - and if it answers that the page has no
|
|
59
94
|
Foldspace code, `npx foldspace attach --bootstrap --daemon` (an app that
|
|
60
95
|
never had Foldspace). An empty registry is valid. L0 is
|
|
@@ -69,33 +104,37 @@ current CLI contract — risk, prerequisites, effects, next step.
|
|
|
69
104
|
2. **Then offer experiences - from what you already have.** With the agent on
|
|
70
105
|
their screen, offer three or four read-only experiences as a choice,
|
|
71
106
|
recommended first. Sources: the product's own menu (`npx foldspace observe
|
|
72
|
-
menu` takes a second and moves nothing), the conversation starters seeded
|
|
73
|
-
`discover_actions`, and `docs/app-profile.md`. `discover_actions`
|
|
74
|
-
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
|
|
75
110
|
list. Order them cheapest first: the user's own data with no parameter
|
|
76
111
|
(L1) before a search with one parameter (L2).
|
|
77
112
|
3. **Only then observe - and only what the chosen experience needs** (**Find
|
|
78
113
|
the call yourself**). Check the row count as part of that: an experience
|
|
79
114
|
on a resource the account has zero of is a demo of an empty state, so say
|
|
80
|
-
so and offer the next one.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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**:
|
|
99
138
|
|
|
100
139
|
1. **Starters.** The sign-up starters point at actions the agent does not have
|
|
101
140
|
(clicking one answers "that action isn't available"): replace them with the
|
|
@@ -106,52 +145,70 @@ human has asked the agent the question:
|
|
|
106
145
|
project's start-up code; nothing in their source changes.
|
|
107
146
|
3. **Tell Foldspace who the user is** - `recipes/who-is-the-user`: from then on
|
|
108
147
|
their conversations and analytics stop being anonymous, and the agent has
|
|
109
|
-
real-time context about who it is talking to.
|
|
110
|
-
4. **A second experience** -
|
|
111
|
-
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.
|
|
112
156
|
|
|
113
157
|
Not before the first experience: nothing about it needs any of these, and it
|
|
114
158
|
is silent work before the human has seen anything.
|
|
115
159
|
|
|
116
|
-
Uploads
|
|
160
|
+
Uploads and extraction are a later session.
|
|
117
161
|
|
|
118
162
|
### Ask as a choice, and ask almost nothing
|
|
119
163
|
|
|
120
164
|
Every question to the human is an **enumerated choice with the recommended
|
|
121
|
-
option first** (in Claude Code, `AskUserQuestion`). A first session
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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:
|
|
128
174
|
*"Show my plan and credits needs one new action on your agent and one card to
|
|
129
175
|
show it in. Sound right?"*
|
|
130
176
|
|
|
131
177
|
## One product, several hosts
|
|
132
178
|
|
|
133
|
-
Many products live on more than one host - `www.`, `
|
|
134
|
-
one domain. `init` covers
|
|
135
|
-
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.
|
|
136
183
|
|
|
137
184
|
- **Find out at the start.** Straight after the agent is live, `npx foldspace
|
|
138
185
|
observe menu` lists the hosts the page links to that belong to the same
|
|
139
186
|
product and that this project does not cover (`otherHosts`). `observe pages`
|
|
140
187
|
and the attach log say the same when the window lands on one.
|
|
141
188
|
- **Widening is the human's yes.** Name the hosts you found and ask once:
|
|
142
|
-
*"Your product also lives on
|
|
143
|
-
|
|
144
|
-
`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**).
|
|
145
193
|
- **Then go there yourself.** `npx foldspace observe screen --goto
|
|
146
|
-
https://
|
|
194
|
+
https://reports.example.com/` opens a screen on any host the project
|
|
147
195
|
names. Do not ask the human to click a tile you can open.
|
|
148
|
-
- **An action reads only the
|
|
149
|
-
|
|
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
|
|
150
199
|
`recipes/several-hosts`: an action asked from the wrong host answers
|
|
151
200
|
`wrong_page` instead of failing.
|
|
152
|
-
- **The user
|
|
153
|
-
|
|
154
|
-
|
|
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.
|
|
155
212
|
|
|
156
213
|
## Find the call yourself
|
|
157
214
|
|
|
@@ -176,16 +233,16 @@ and asking are not the only options — **observing is, and it comes first**:
|
|
|
176
233
|
npx foldspace observe screen --click "<a label from menu>" --match "<three to five words for the data>"
|
|
177
234
|
npx foldspace observe read "<the GET path screen returned>" # GET only: status, field names and types, row count
|
|
178
235
|
npx foldspace observe auth # only if a read came back 401 or 403
|
|
179
|
-
npx foldspace observe styles
|
|
236
|
+
npx foldspace observe styles --write # agent/views/brand.ts for the cards, before the first card
|
|
180
237
|
```
|
|
181
238
|
|
|
182
239
|
`screen` reaches a screen the way a person does and returns at most eight
|
|
183
|
-
requests, ranked by your words - field names
|
|
184
|
-
|
|
185
|
-
each header value comes from, never the value: do not go
|
|
186
|
-
storage, cookies or bundles yourself. One experience needs
|
|
187
|
-
`screen` calls is normal, five means the experience is wrong
|
|
188
|
-
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.
|
|
189
246
|
|
|
190
247
|
**`observe` refuses what is not provably looking**, and a refusal is an
|
|
191
248
|
answer (**Safety**): it follows links, tabs and menu openers only - a plain
|
|
@@ -197,7 +254,8 @@ and asking are not the only options — **observing is, and it comes first**:
|
|
|
197
254
|
an MFA step, a paywall, a screen that needs a record the account does not
|
|
198
255
|
have, or anything that would write.
|
|
199
256
|
|
|
200
|
-
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).
|
|
201
259
|
Navigating is yours. **While you drive a signed-in account you are read-only:**
|
|
202
260
|
GET only, never submit a form, never click anything that creates, sends or
|
|
203
261
|
deletes.
|
|
@@ -209,11 +267,11 @@ an assumption by quoting that file later.
|
|
|
209
267
|
|
|
210
268
|
## Build: objects first, code second
|
|
211
269
|
|
|
212
|
-
Four kinds of thing. With the
|
|
270
|
+
Four kinds of thing. With the human, use **Foldspace's own words** for what
|
|
213
271
|
they will see in Agent Studio - **action**, **task agent**, **navigation**,
|
|
214
272
|
**publish** - and say what each does the first time you use it. Never invent a
|
|
215
273
|
substitute: no "skill", "ability", "capability" or "tool", and no "switch on"
|
|
216
|
-
for publish. "Skill" already means something else in Claude, and the
|
|
274
|
+
for publish. "Skill" already means something else in Claude, and the human
|
|
217
275
|
has seen "Actions" at sign-up and in Agent Studio - a new word breaks the link
|
|
218
276
|
to what they know. The action handler is yours: call it "the code behind it".
|
|
219
277
|
|
|
@@ -229,7 +287,15 @@ built-ins — no code, and a first experience can lean on them.
|
|
|
229
287
|
|
|
230
288
|
1. `create_action` as a **draft**. `generate_action_handler` works from a draft
|
|
231
289
|
schema; do not publish yet. Generating the handler from the schema is what
|
|
232
|
-
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.
|
|
233
299
|
2. Implement the handler from the observed request and response shapes. A
|
|
234
300
|
handler is `fetch` plus `runTask` and nothing more. Import helpers from
|
|
235
301
|
`agent/utils.ts` — inspect that file before writing another `fetch` or rank
|
|
@@ -239,29 +305,35 @@ built-ins — no code, and a first experience can lean on them.
|
|
|
239
305
|
|
|
240
306
|
**Task agents** are for the one LLM subtask deterministic code cannot do —
|
|
241
307
|
extraction, summarisation, classification, normalisation. Not for API calls,
|
|
242
|
-
CRUD, routing or parsing a known shape.
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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.
|
|
246
313
|
<https://docs.foldspace.ai/user-guides/task-agents/>
|
|
247
314
|
|
|
248
|
-
**
|
|
249
|
-
|
|
250
|
-
the code
|
|
251
|
-
|
|
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.
|
|
252
321
|
|
|
253
322
|
### The moment a handler exists, prove it
|
|
254
323
|
|
|
255
324
|
Do not batch verification to the end. As soon as a handler is registered:
|
|
256
|
-
build, confirm it reached `dist/index.js`, attach
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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.
|
|
265
337
|
|
|
266
338
|
## Reachability: everything must be published
|
|
267
339
|
|
|
@@ -271,52 +343,71 @@ with how you established each.
|
|
|
271
343
|
| Task agent | **yes** | a `taskKey` in code names it |
|
|
272
344
|
| Navigation route | **yes** | `isActive: true` |
|
|
273
345
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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.
|
|
289
375
|
|
|
290
376
|
## Local loop
|
|
291
377
|
|
|
292
378
|
```bash
|
|
293
|
-
npm run
|
|
379
|
+
npm run build
|
|
294
380
|
npm run inject
|
|
295
381
|
npx foldspace attach --daemon
|
|
296
382
|
```
|
|
297
383
|
|
|
298
384
|
`inject` launches an isolated Chrome profile and records its debug port; it
|
|
299
385
|
loads nothing. In a first session `attach` comes straight after sign-in (L0),
|
|
300
|
-
and `foldspace observe` reads the same window while it runs. `attach` prepares
|
|
301
|
-
`dist/index.js` over CDP.
|
|
302
|
-
`[lifecycle] inspect_registration:…`. An empty local
|
|
303
|
-
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.
|
|
304
390
|
|
|
305
391
|
Attach mode is read off the page, never asked: default swap when the page
|
|
306
392
|
already has the configured product and agent; `--bootstrap` when it has no
|
|
307
393
|
Foldspace SDK; `--replace` when it embeds a different product or agent, or an
|
|
308
394
|
SDK without the configured agent.
|
|
309
395
|
|
|
310
|
-
`inspect_registration:registration_ok` is the signal. On
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
it
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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.
|
|
320
411
|
|
|
321
412
|
## Done, and what you say at the end
|
|
322
413
|
|
|
@@ -324,7 +415,44 @@ A first session is done when the customer has seen one experience answer from
|
|
|
324
415
|
their own data, in the test window, and been invited to try it. Not when the
|
|
325
416
|
gates pass, and not when there is nothing left to improve.
|
|
326
417
|
|
|
327
|
-
|
|
418
|
+
**With a code project** (step 5 found or created one), putting it in front of
|
|
419
|
+
their users is yours to do. Once they have tried it in the test window, before
|
|
420
|
+
you offer the second wave:
|
|
421
|
+
|
|
422
|
+
1. Say *"I'm saving your changes and publishing them to dev"* - no need to
|
|
423
|
+
ask - then save with `repo_write` and publish with the `deploy` tool, the
|
|
424
|
+
way `repo_read` command `instructions` says. One exception: a dev preview
|
|
425
|
+
that comes back with no warnings means this agent loads dev for its users -
|
|
426
|
+
then ask first, as for production. In chat it is "saving your changes",
|
|
427
|
+
"publishing to dev" and "publishing for your users" - never GitHub,
|
|
428
|
+
branches, commits or tool names.
|
|
429
|
+
2. Check dev yourself. Say in chat that you are checking it in that window
|
|
430
|
+
and to leave it alone until you say it is ready: with attach stopped the
|
|
431
|
+
page draws no pill. Then `npx foldspace attach --stop` (while attach runs,
|
|
432
|
+
the page serves your local build), `npx foldspace published dev`, and
|
|
433
|
+
`npx foldspace ask "<the question>" --retry 2`. Without attach, `ask`
|
|
434
|
+
cannot see `execute` and `render`: the reply, the card text and the
|
|
435
|
+
screenshot are the proof - look at the screenshot. If it fails, go no
|
|
436
|
+
further: attach again, fix it here, then save, publish to dev and check
|
|
437
|
+
again. If it works, invite them to try it in that window (a reload takes
|
|
438
|
+
it off dev), and run `npx foldspace attach --daemon` before you build
|
|
439
|
+
anything else. If `published` says this window cannot show dev, say so in
|
|
440
|
+
one sentence, attach again, and stop at step 1.
|
|
441
|
+
3. Once the dev check passed, ask before publishing for their users:
|
|
442
|
+
*"Shall I publish it for your users?"* On a yes, publish to production. On a not yet, it stays on dev:
|
|
443
|
+
say so in one line and carry on.
|
|
444
|
+
|
|
445
|
+
Steps 2 and 3 need a site that loads this agent by itself. If you attached
|
|
446
|
+
with `--bootstrap` or `--replace`, it does not yet: do step 1, keep attach
|
|
447
|
+
running, and give the answer below for the rest - their site loading the agent
|
|
448
|
+
is the step the Foldspace team does with them.
|
|
449
|
+
|
|
450
|
+
A save refused because someone saved after you read is not final: the
|
|
451
|
+
instructions say how to redo it. Any other refusal from the Foldspace tools
|
|
452
|
+
here is final: then the answer below.
|
|
453
|
+
|
|
454
|
+
**Without a code project**, the answer to *"how do I put this in front of my
|
|
455
|
+
users?"* is:
|
|
328
456
|
|
|
329
457
|
> What you've seen runs in a test window on your machine. Putting it in front
|
|
330
458
|
> of your users is a separate step, and the Foldspace team does it with you.
|
|
@@ -332,8 +460,9 @@ If they ask *"how do I put this in front of my users?"*, the honest answer is:
|
|
|
332
460
|
> <support@foldspace.ai>. Everything built today carries over: the actions are
|
|
333
461
|
> already in your Foldspace account, and the code is in this folder.
|
|
334
462
|
|
|
335
|
-
Never run `foldspace deploy
|
|
336
|
-
|
|
463
|
+
Never run `npx foldspace deploy` - that command is for Foldspace staff only,
|
|
464
|
+
and it is not the `deploy` tool you publish a code project with. Never install
|
|
465
|
+
cloud tooling, and never ask them for cloud credentials.
|
|
337
466
|
|
|
338
467
|
## Verification gates
|
|
339
468
|
|
|
@@ -347,17 +476,26 @@ Do not report success without all six:
|
|
|
347
476
|
action the captured registry includes it.
|
|
348
477
|
5. The action behaves against the real workflow: `[actions]
|
|
349
478
|
local-handler:execute` in the attach log after `foldspace ask` (or the
|
|
350
|
-
human) exercises the visible agent. A
|
|
351
|
-
turn
|
|
352
|
-
|
|
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**).
|
|
353
490
|
6. Browser evidence came from the live target, not hand-authored examples.
|
|
354
|
-
Neighbour fixtures still pass when they exist.
|
|
355
491
|
|
|
356
492
|
## Rules that are not negotiable
|
|
357
493
|
|
|
358
494
|
1. **Handlers return data only.** Never `directive`, `instructions`, or a
|
|
359
|
-
paragraph telling the
|
|
360
|
-
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).
|
|
361
499
|
Validate parameters and return sanitised errors.
|
|
362
500
|
**What goes back to the agent states what happened - it never says what to
|
|
363
501
|
do next.** A status is state: it succeeded, it failed and why (`reason`),
|
|
@@ -369,37 +507,47 @@ Do not report success without all six:
|
|
|
369
507
|
`success: true`, the query, zero matches - *"No <records> match
|
|
370
508
|
'<query>'."* - and it stops there.
|
|
371
509
|
2. **`render` receives `execute`'s return value**, not the input params. A
|
|
372
|
-
|
|
373
|
-
|
|
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/>
|
|
374
517
|
3. **Say what you verified and what you assumed.** "The docs say X" and "I
|
|
375
518
|
think X" are different claims — mark which. Search `docs.foldspace.ai` over
|
|
376
519
|
MCP (`search_docs`, `fetch_page`) before asserting how the platform behaves;
|
|
377
520
|
cite the page, or say plainly that the docs do not cover it and write down
|
|
378
521
|
what you learned.
|
|
379
522
|
4. **Test through the agent, then let them.** In a first session, prove an
|
|
380
|
-
experience yourself before the
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
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.
|
|
394
540
|
5. **Test mode is for an agent that already has real users.** Then `attach
|
|
395
541
|
--test-mode` keeps your conversations out of their data. It is not a safety
|
|
396
542
|
net: publishing still changes the live agent. Working on an agent in
|
|
397
543
|
production - test mode, pinned action versions, what may be published - is
|
|
398
|
-
a separate playbook, not this one.
|
|
544
|
+
a separate playbook, not this one. Publishing its code with the `deploy`
|
|
545
|
+
tool is the exception: it takes the same dev-then-production path as a new
|
|
546
|
+
agent (**Done, and what you say at the end**).
|
|
399
547
|
6. **The human is not necessarily a developer.** Never hand over a terminal
|
|
400
|
-
line, a stack trace, or a choice between flags. Name experiences by
|
|
401
|
-
|
|
402
|
-
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.
|
|
403
551
|
7. **Be short.** No recap of what was established, no restating the plan before
|
|
404
552
|
each step. Progress is one line: what you are doing, the level reached, and
|
|
405
553
|
the question if there is one.
|
|
@@ -413,7 +561,7 @@ Do not report success without all six:
|
|
|
413
561
|
and the recipes first: it may be supported in a way you have not found.
|
|
414
562
|
Say "I did not find it", never "Foldspace does not support it".
|
|
415
563
|
- **Build the narrowest workaround that gives them a working experience.**
|
|
416
|
-
|
|
564
|
+
The human is never left stuck on a Foldspace limitation.
|
|
417
565
|
- **Register it, in two places.** A comment in the code where it lives -
|
|
418
566
|
what it works around, how you established that, and that it goes when the
|
|
419
567
|
fix ships - and a row under **Workarounds** in `docs/app-profile.md`:
|
|
@@ -426,8 +574,10 @@ Do not report success without all six:
|
|
|
426
574
|
|
|
427
575
|
Lint catches some of these in handler code; Agent Studio copy is on you.
|
|
428
576
|
|
|
429
|
-
- **No emoji** in
|
|
430
|
-
|
|
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).
|
|
431
581
|
- **Never send the user out of the host app.** No "open in <product>" button,
|
|
432
582
|
no pasted URLs — navigate with a route, same tab.
|
|
433
583
|
|
|
@@ -452,26 +602,56 @@ wins**. <https://docs.foldspace.ai/customize/configuration/>
|
|
|
452
602
|
mount code is the other place it can live, their engineers' call. Offer it in
|
|
453
603
|
the second wave.
|
|
454
604
|
- **An action's `execute` never calls `setConfiguration`**: a partial call from
|
|
455
|
-
one
|
|
605
|
+
one can collapse the agent's panel to nothing.
|
|
456
606
|
|
|
457
607
|
## Agentic UI components: what bites
|
|
458
608
|
|
|
459
609
|
An Agentic UI component is the card an action draws in the chat (`render`).
|
|
460
610
|
Each of these cost a real build.
|
|
461
611
|
|
|
462
|
-
- **A
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
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.
|
|
475
655
|
- **A failed or signed-out call is never drawn as zeros.** A signed-out response
|
|
476
656
|
often looks like a valid empty one: a 200 with zeros, nulls or an empty list.
|
|
477
657
|
Before you draw numbers, check that the response belongs to a signed-in user -
|
|
@@ -484,11 +664,15 @@ Each of these cost a real build.
|
|
|
484
664
|
stays clean, and the action is recorded as succeeded. Only looking at it shows
|
|
485
665
|
the problem. Draw the empty or the failed state (`renderEmpty`,
|
|
486
666
|
`renderFailure`) - never nothing.
|
|
487
|
-
- **Totals and money are never computed by the
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
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.
|
|
492
676
|
- **A card matches the panel, not the page.** The card sits in the agent's
|
|
493
677
|
panel - white by default (the agent's theme: `backgroundColor #FFFFFF`,
|
|
494
678
|
`primaryColor` for accents; a `darkTheme` when the panel is dark, and the
|
|
@@ -499,13 +683,8 @@ Each of these cost a real build.
|
|
|
499
683
|
imports it, and the recipes' `brand.ts` files are placeholders for it. A
|
|
500
684
|
dark app keeps its accent and font; its light-on-dark text would vanish on
|
|
501
685
|
white, so that falls back to greys and the file says so. Record where the
|
|
502
|
-
values came from in the profile's Brand section. Never copy colours by eye
|
|
503
|
-
|
|
504
|
-
- **Plain hex or rgba only.** The panel saves every card to the conversation
|
|
505
|
-
history as an image drawn with html2canvas, which cannot read `oklch()`,
|
|
506
|
-
`lab()` or `color-mix()`: one such value and the saved card is blank
|
|
507
|
-
(`data:,` where the card was). Lint flags them. Do not read the app's CSS
|
|
508
|
-
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.
|
|
509
688
|
|
|
510
689
|
## Layout
|
|
511
690
|
|
|
@@ -527,20 +706,26 @@ Do not introduce another bundler or bundle format.
|
|
|
527
706
|
## Working examples to copy from
|
|
528
707
|
|
|
529
708
|
Before writing a handler or a card, read
|
|
530
|
-
`node_modules/@foldspace_npm/harness/recipes/INDEX.md`
|
|
531
|
-
short
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
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.
|
|
536
716
|
|
|
537
717
|
After the first experience → `who-is-the-user`. L2 → `find-by-name`, and
|
|
538
718
|
`pick-from-a-list` when the user has to choose. The user's records on one
|
|
539
719
|
card → `records-list`. The product lives on several hosts → `several-hosts`.
|
|
540
|
-
|
|
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
|
|
541
723
|
CSV of contacts → `upload-contacts`. Save a note on a meeting →
|
|
542
|
-
`update-meeting-notes`.
|
|
543
|
-
|
|
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`.
|
|
544
729
|
App does not use a bearer token → `swap-the-login-method`.
|
|
545
730
|
Each says how many production builds carry the pattern and what they learned.
|
|
546
731
|
If nothing fits — there is no L1 card recipe yet, and no navigation recipe —
|
|
@@ -557,11 +742,11 @@ not import it — fix the import when you implement.
|
|
|
557
742
|
| `publicFetch` | Unauthenticated / marketing origin | Signed-in product APIs |
|
|
558
743
|
| `getAuthToken` / `parseJwt` | `AUTH_SOURCE` is `localStorage` or `cookie` | Custom header schemes — override `apiFetch` in `utils.ts` |
|
|
559
744
|
| `rankBy` | User typed a name; API search is exact or ignored | Domain ranking (invoices, coverage, industry classification codes) |
|
|
560
|
-
| `renderLoading` / `Empty` / `Error` / `Fatal` |
|
|
745
|
+
| `renderLoading` / `Empty` / `Error` / `Fatal` | Plain states inside a card (loading, empty, error) | Branded cards - those stay in `agent/views/` |
|
|
561
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 |
|
|
562
747
|
| `httpFailure` / `networkFailure` | Your own `apiFetch` (custom header, cookies) — keeps the same `reason`s | The default `apiFetch` fits; then you never call these |
|
|
563
|
-
| `getAgent` | Talking to Foldspace | You need a specific instance
|
|
564
|
-
| `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 |
|
|
565
750
|
| `redact` / `mapWithConcurrency` | Logging tokens; batching fetches | — |
|
|
566
751
|
|
|
567
752
|
Every failed call carries a `reason` — `signed_out`, `forbidden`, `not_found`,
|
|
@@ -571,13 +756,20 @@ building is not the session at run time: **expect `signed_out`** and let
|
|
|
571
756
|
`renderFailure` show it. `not_found` and zero rows are both `renderEmpty` with
|
|
572
757
|
the query named — never an error, never a blank card.
|
|
573
758
|
|
|
574
|
-
If the observed auth is not Bearer + `localStorage`/`cookie`,
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
759
|
+
If the observed auth is not Bearer + `localStorage`/`cookie`, copy
|
|
760
|
+
`node_modules/@foldspace_npm/harness/recipes/swap-the-login-method/agent/utils.ts`
|
|
761
|
+
over `agent/utils.ts` - or, when the browser's own cookies carry the sign-in and
|
|
762
|
+
there is no token, that recipe's `variants/utils.cookies.ts` - and set what its
|
|
763
|
+
README's **Adapt it** table lists to what you observed.
|
|
764
|
+
|
|
765
|
+
**If `agent/` came from a code project** (step 5), nothing in it may import a
|
|
766
|
+
package: Foldspace builds the published bundle with no `node_modules`, so a
|
|
767
|
+
package import passes every gate in this manual and then fails on publish.
|
|
768
|
+
Import the helpers the way the project's own `agent/utils.ts` does - from
|
|
769
|
+
`./runtime` - in a recipe file you copy too. A project with an `agent/utils/`
|
|
770
|
+
folder has its own helpers instead: change the sign-in there, and never add an
|
|
771
|
+
`agent/utils.ts` beside it, which would win the import. Without a code project,
|
|
772
|
+
the package import is right.
|
|
581
773
|
|
|
582
774
|
## Safety
|
|
583
775
|
|
|
@@ -586,10 +778,13 @@ export { apiFetch } from "./api";
|
|
|
586
778
|
what you wanted it for, or take a narrower read-only route. Never retry it,
|
|
587
779
|
and never try a different command hoping it gets through - not re-quoted, not
|
|
588
780
|
in pieces, not through a helper script or a subagent: a denial covers the
|
|
589
|
-
outcome, not the string. If
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
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.
|
|
593
788
|
- **Never create an account or a key** - not in Foldspace, not in the
|
|
594
789
|
customer's product, not anywhere. If one is missing, say which and where it
|
|
595
790
|
goes. **Never widen your own access**: if something is refused for a site or
|
|
@@ -597,5 +792,8 @@ export { apiFetch } from "./api";
|
|
|
597
792
|
- Do not commit secrets, cookies, HAR files, browser storage, or
|
|
598
793
|
`.foldspace-dev/`. Nothing transient goes in `docs/`.
|
|
599
794
|
- Local handler changes are not cloud publication.
|
|
600
|
-
- Creating or publishing Foldspace resources
|
|
795
|
+
- Creating or publishing Foldspace resources needs the human's yes - except
|
|
796
|
+
the agent's code: creating its code project, saving to it and publishing it
|
|
797
|
+
to dev, unless the dev preview comes back with no warnings (this agent then
|
|
798
|
+
loads dev for its users). Say you are doing it, then do it.
|
|
601
799
|
- Action keys and parameter names must match the published schema.
|