imprint-mcp 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/package.json +4 -2
- package/prompts/audit-agent.md +4 -2
- package/prompts/auth-compile-agent.md +149 -158
- package/prompts/build-planning.md +14 -9
- package/prompts/compile-agent.md +46 -23
- package/prompts/live-verifier-agent.md +101 -0
- package/prompts/playbook-compilation.md +8 -1
- package/prompts/tool-candidate-detection.md +9 -29
- package/src/cli.ts +83 -1
- package/src/imprint/audit.ts +219 -20
- package/src/imprint/auth-compile-agent.ts +215 -155
- package/src/imprint/auth-compile-tools.ts +300 -118
- package/src/imprint/auth-verifier.ts +167 -180
- package/src/imprint/backend-cache.ts +18 -11
- package/src/imprint/backend-ladder.ts +225 -177
- package/src/imprint/build-plan.ts +3 -8
- package/src/imprint/cdp-browser-fetch.ts +930 -83
- package/src/imprint/claude-cli-compile.ts +46 -20
- package/src/imprint/codex-cli-compile.ts +436 -139
- package/src/imprint/compile-agent-types.ts +15 -10
- package/src/imprint/compile-agent.ts +88 -28
- package/src/imprint/compile-tools.ts +228 -39
- package/src/imprint/compile-verification.ts +187 -0
- package/src/imprint/compile.ts +71 -2
- package/src/imprint/credential-store.ts +57 -44
- package/src/imprint/emit.ts +3 -1
- package/src/imprint/freeform-redact.ts +27 -6
- package/src/imprint/live-verifier.ts +1665 -0
- package/src/imprint/llm.ts +22 -3
- package/src/imprint/login.ts +107 -25
- package/src/imprint/mcp-compile-server.ts +447 -114
- package/src/imprint/mcp-live-verifier-server.ts +359 -0
- package/src/imprint/mcp-server.ts +116 -35
- package/src/imprint/probe-backends.ts +88 -7
- package/src/imprint/recording-request.ts +36 -0
- package/src/imprint/request-capture.ts +40 -17
- package/src/imprint/runtime.ts +490 -337
- package/src/imprint/teach-state.ts +36 -2
- package/src/imprint/teach.ts +302 -146
- package/src/imprint/tool-candidates.ts +8 -10
- package/src/imprint/tool-loader.ts +12 -4
- package/src/imprint/types.ts +137 -68
package/README.md
CHANGED
|
@@ -184,6 +184,8 @@ The full order is `fetch → fetch-bootstrap → cdp-replay → stealth-fetch
|
|
|
184
184
|
|
|
185
185
|
For bot-protected sites, `imprint probe-backends <site> --tool <toolName>` writes a `backends.json` preference cache so cron and MCP start from the known-good backend instead of rediscovering blocked rungs. Use `imprint probe-backends <site> --all` to refresh every tool in a multi-tool site; `imprint mcp status` reports stale or invalid backend caches before they quietly fall back to the default ladder. CDP replay records both cold and warm timings when it succeeds: a timeout-safe cold start may rank by its fast warm runtime, but a cold start above the preferred threshold stays behind cold-safe backends in durable cache order.
|
|
186
186
|
|
|
187
|
+
During `imprint teach`, the independent final verifier uses the same probe before it runs the compiler-written integration suite. Probe time is separate from the suite timeout, and verification pins the selected backend. Compiler revisions reuse that proven preference without probing; only a transport/network/browser failure can prompt a reprobe. Semantic failures return directly to the compiler. Live verifier progress and retained suite receipts are written beside the generated tool as `.live-verifier-log.jsonl` and `.live-verification-evidence.json`.
|
|
188
|
+
|
|
187
189
|
Every recording compiles to *both* `workflow.json` and `playbook.yaml`, so the ladder always has a DOM fallback.
|
|
188
190
|
|
|
189
191
|
---
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "imprint-mcp",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "Teach an AI agent how to use any website. Once. Records a real browser session + narration; generates a deterministic MCP tool plus a DOM-replay playbook fallback.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
7
|
"./runtime": "./src/imprint/runtime.ts",
|
|
8
8
|
"./types": "./src/imprint/types.ts",
|
|
9
|
-
"./backend-ladder": "./src/imprint/backend-ladder.ts"
|
|
9
|
+
"./backend-ladder": "./src/imprint/backend-ladder.ts",
|
|
10
|
+
"./compile-verification": "./src/imprint/compile-verification.ts"
|
|
10
11
|
},
|
|
11
12
|
"license": "MIT",
|
|
12
13
|
"author": "Ashay Changwani",
|
|
@@ -65,6 +66,7 @@
|
|
|
65
66
|
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
|
66
67
|
"redactum": "^1.1.0",
|
|
67
68
|
"semver": "^7.8.4",
|
|
69
|
+
"tldts": "^7.4.8",
|
|
68
70
|
"yaml": "^2.8.4",
|
|
69
71
|
"zod": "^3.24.0"
|
|
70
72
|
},
|
package/prompts/audit-agent.md
CHANGED
|
@@ -20,10 +20,11 @@ Your priority is **functional coverage, not edge cases.** A tool that returns da
|
|
|
20
20
|
|
|
21
21
|
3. **Differentially test EACH advertised parameter.** This is the core of the audit. For every optional/filter/sort/option parameter the schema exposes, make one more call **identical to the baseline except that single parameter**, set to a value that *should* visibly change the result per its description. Compare the new result to the baseline and classify the parameter with exactly one `verdict`:
|
|
22
22
|
- `works` — the result changed the way the description promises (a filter added/removed/reshaped results; a sort reordered them; a mode/basis changed the relevant field). Name the observed change in the reason.
|
|
23
|
-
- `no_op` — the
|
|
24
|
-
- `broken` — the
|
|
23
|
+
- `no_op` — there is positive evidence that the parameter is inert: use a meaningfully discriminating value and show that the response still reflects the baseline value or otherwise ignores the requested change. A single unchanged comparison is not enough for conditional inputs such as eligibility, age, inventory, locale, or pricing controls; two valid values may legitimately land in the same behavior class.
|
|
24
|
+
- `broken` — there is positive evidence that the parameter produces a wrong result: it errors because of a valid value, contradicts the requested value, or corrupts the response. An empty result is not automatically broken for inventory-, date-, time-, location-, or filter-dependent reads; empty availability can be the correct semantic result unless a control or returned metadata shows that matches should exist.
|
|
25
25
|
- `untestable` — reserve this for genuine impossibility, NOT inconvenience. Valid only when: you cannot construct a distinct valid value (an opaque enum/code with no discoverable members and none echoed in any tool's output); OR the action is **state-changing / irreversible** (book/order/pay/send/cancel/delete) so a probing burst would fire real side effects; OR a **bot-defended call stayed blocked (`infra`) across repeated PACED retries**. State which in the reason. **Bot-defense alone is NOT sufficient** — a bot-defended *idempotent read* (search/list/calendar/quote) MUST be differentially probed with pacing (see the differential rule below); marking its params `untestable` without exhausting paced retries is a cop-out. Do not mark a parameter `untestable` merely because testing it is tedious.
|
|
26
26
|
To isolate the parameter, change only that one field between the two calls. When two parameters interact (e.g. a min/max pair), test the pair together and say so in the reason.
|
|
27
|
+
For conditional parameters, choose a boundary value likely to cross a behavior class and inspect echoed request metadata as well as result data. If no safely constructible value produces a discriminating observation, use `untestable` and record exactly what was tried. Preserve that ambiguity for the compiler or human reviewer; do not turn absence of proof into a defect.
|
|
27
28
|
Promo/coupon/voucher/discount-code parameters need a **known valid code** to produce a visible discount. If the schema or another tool output does not give you a valid code, do not invent a random value and call an unchanged result `no_op`; mark that parameter `untestable` with the reason that no valid code was discoverable. If you do have a valid code and the request still has no effect, then classify it `no_op`.
|
|
28
29
|
|
|
29
30
|
4. **Judge the baseline invocation** against what the description and schema promise, with exactly one `verdict`:
|
|
@@ -43,6 +44,7 @@ Your priority is **functional coverage, not edge cases.** A tool that returns da
|
|
|
43
44
|
- Audit **every** connected tool, and within each, test **every** advertised parameter (subject to the read-type rule above). Do not skip a tool because another failed.
|
|
44
45
|
- Prefer `infra` over `tool_broken`/`broken` when the evidence points to anti-bot, rate-limiting, or network/upstream failure — a blocked request is not a code bug.
|
|
45
46
|
- Prefer `bad_params` over `tool_broken` when re-reading the schema shows your own inputs were invalid.
|
|
47
|
+
- Require affirmative evidence before assigning `no_op` or `broken`. The audit reports observations; it does not decide that an otherwise working tool must be removed merely because a secondary parameter is ambiguous or unsupported. Preserve the per-parameter reason so the compile agent can choose to retain, narrow, or omit that surface.
|
|
46
48
|
- **Chain producer-sourced values.** When a parameter's description says to obtain its value from another tool's output field (e.g. "Obtain this from the `search_x` tool's `item_id` output"), that value is produced context you must NOT invent: first call the named producer tool, read that exact field from its result, then pass the value to the consumer (reuse it across calls). Judge the consumer on that real value. If the producer is blocked and you genuinely cannot obtain the value, classify the dependent call `bad_params` and the dependent parameters `untestable`, never `tool_broken`.
|
|
47
49
|
|
|
48
50
|
## Output
|
|
@@ -1,165 +1,156 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
5. **Get the live second factor.** Call `prompt_user` with a clear message (and `options` if it's a choice), then STOP. The orchestrator collects the user's input and resumes you with it.
|
|
44
|
-
|
|
45
|
-
6. **Verify phase 2 (complete the login).** Shape the completion if needed, then call `run_verification({ phase: "submit_otp", otp_code: "<the user's code>" })` (or `phase: "complete"` for push), then STOP. On `ok`, the login finished and the session token is stored → call `done`. On failure, decide cool-off vs defect as in step 4.
|
|
46
|
-
|
|
47
|
-
7. **Finish.** Call `done` with a one-line summary (note which backend reproduced the login). Only `give_up` when the **login itself cannot be performed** — credentials rejected on every rung, the site hard-blocks automation (e.g. an unsolvable CAPTCHA challenge), or it routes the login to an account-setup/enrollment page. Never loosen a success marker to fake success.
|
|
48
|
-
|
|
49
|
-
## Persist the session token for data tools (`sessionCapture`)
|
|
50
|
-
|
|
51
|
-
The point of completing the login is a **durable token the data tools reuse without re-running auth** (they re-auth only when it expires). Cookies are persisted automatically. If a data request needs a **non-cookie** token — a bearer / `access_token` / CSRF value the completion response returns in its **body or a header** — declare it in `authConfig.sessionCapture` (same shape as a request `capture`). Its resolved value is stored as a durable `${credential.NAME}`. Ground each in the recording; don't invent them. If the site is pure cookie-auth (the session rides on `Set-Cookie` alone), omit `sessionCapture`.
|
|
52
|
-
|
|
53
|
-
## authConfig (structural — never a channel name)
|
|
54
|
-
|
|
55
|
-
Set `twoFactorType` to exactly one of:
|
|
56
|
-
- **`none`** — login completes in the initiate request(s); no second step.
|
|
57
|
-
- **`otp`** — a later request carries a short code the user got out-of-band (SMS, email, TOTP are all `otp`). Set `initiateRequestCount` (requests before that one run on `initiate`; the rest on `submit_otp`), declare an `otp_code` param, and if the completion reads a value the **initiate response returned** (e.g. a reauth `mfaId`), add a `capture` for it on the initiate request AND list its name in `twoFactorContext` (each call is stateless — this carries the token across the gap).
|
|
58
|
-
- **`push`** — one endpoint polled until its response flips (pending→approved) or a session cookie appears. Set `pollEndpoint` (+ optional `pollIntervalMs`/`maxPollAttempts`) and a `pollTerminal` capture grounded in the recorded **approved** poll (a field absent on the pending polls). Omit `pollTerminal` only to fall back to "a fresh session cookie appeared". **If the recorded poll request sends a body** (read it with `read_request` — many status endpoints require a JSON payload like `{"mfaId":"..."}` and reject an empty POST with 4xx), copy it into `pollBody` (templated: `${state.X}`/`${credential.X}`/`${param.X}`) and set `pollContentType` (and `pollMethod` if not POST) from the recorded request. A missing `pollBody` means the poll sends nothing, so an approval is never recognized.
|
|
59
|
-
|
|
60
|
-
## Replayable vs browser-minted logins
|
|
61
|
-
|
|
62
|
-
Auth runs on **cdp-replay** (a real headed browser): the verifier navigates `bootstrap.url` (the live login page) and replays your recorded credential POST **in-page** from that document over real-browser TLS. Read the credential POST with `read_request` and classify it:
|
|
63
|
-
- **Replayable** — plain form/JSON of username/password (+ static/capturable tokens). Replays directly.
|
|
64
|
-
- **Static signed/encrypted blob** — the body carries an encrypted credential blob / signature / public key the page computed at record time. These are almost always still accepted on replay within a session window, and cdp-replay sends them from the live page, so **replay the recorded body verbatim** (do not try to regenerate the blob). Capture any per-session token the *response* returns via `${state.X}` as usual.
|
|
65
|
-
- **Per-request nonce the server rejects on replay** — a value that must be minted by the page *for this exact POST* (a one-time WebCrypto challenge, a per-load reCAPTCHA token). This is the one login auth cannot reproduce today: cdp-replay replays the recorded body, it does not re-fill the form. Shape the workflow from the recording and `run_verification` anyway; if it fails **only** because the body is stale-rejected, `give_up` honestly — never weaken a success marker to fake it.
|
|
66
|
-
|
|
67
|
-
In all cases you emit **only `workflow.json`** for an authenticate tool. Do **not** write a `playbook.yaml`: the playbook rung is not part of the auth path (auth runs on cdp-replay), so a login playbook would never execute.
|
|
68
|
-
|
|
69
|
-
## Two rules that decide whether a 2FA login completes (read BEFORE writing captures)
|
|
70
|
-
|
|
71
|
-
These two patterns are the difference between a 2FA tool that works every run and one that breaks intermittently. Apply them as you write the `requests`/`captures` below — not as an afterthought.
|
|
72
|
-
|
|
73
|
-
1. **Capture from variable-order arrays by FIELD, not index.** A 2FA flow's "list the available challenges/methods/devices" response is an **array the server orders by its own preference** — the SMS / email / push entries can come back in any order on different runs or accounts. A fixed index (`challenges[0].…`) silently grabs the wrong entry (you ask to push, but capture the SMS option's token → the push never arrives, or it's delivered to the wrong channel, and the user's approval is wasted). **Select by a discriminator field instead:** `challenges[type=push].token` resolves to the FIRST array element whose `type` stringifies to `push`, regardless of position. Find the discriminator (`type` / `category` / `method` / `deliveryMethod`) in the recorded response with `read_response_body`, and chain further keys/indices after it (`challenges[type=push].options[0].token`). Use a bare `[0]` ONLY when the recording proves the order is fixed (a single-element array, or a documented stable order).
|
|
74
|
-
|
|
75
|
-
2. **Mark non-fatal steps `"optional": true`.** Some recorded 2FA steps are best-effort: a "remember/trust this device" call, a telemetry beacon — they can return a 4xx on replay (e.g. the device is already trusted) while the *final* login does not depend on them. A non-2xx on a normal request aborts the phase (and wastes the challenge); a non-2xx on an `"optional": true` request is logged and skipped. **Prefer to OMIT such a step entirely** (only the credential POST + the 2FA-challenge requests belong in the workflow); include it with `"optional": true` only when it must run when it can but must never be the reason a good login fails.
|
|
76
|
-
|
|
77
|
-
## workflow.json structure
|
|
1
|
+
# Auth compile agent
|
|
2
|
+
|
|
3
|
+
Compile the captured authentication recording into a deterministic
|
|
4
|
+
`workflow.json`, then prove the declared success path with live verification.
|
|
5
|
+
|
|
6
|
+
You decide the action boundaries, required inputs, evidence, carried state,
|
|
7
|
+
request repetition, error behavior, and completion criteria from the recording.
|
|
8
|
+
The runtime only executes the program you write.
|
|
9
|
+
|
|
10
|
+
## Process
|
|
11
|
+
|
|
12
|
+
1. Call `read_session_summary`.
|
|
13
|
+
2. Inspect relevant requests and responses with `search_requests`,
|
|
14
|
+
`read_request`, and `read_response_body`.
|
|
15
|
+
3. Write `workflow.json` and any recording-grounded
|
|
16
|
+
`request-transform.ts` needed by the requests.
|
|
17
|
+
4. Call `run_verification` with one declared action and its parameters, then
|
|
18
|
+
stop. The orchestrator runs it in a verification browser and resumes you with
|
|
19
|
+
the observed status, body preview, continuation, and next action.
|
|
20
|
+
5. Revise the artifact or continue through its actions. Use `prompt_user` only
|
|
21
|
+
when the program actually requires human input or an external user action.
|
|
22
|
+
When verification evidence warrants inspecting the existing browser page,
|
|
23
|
+
call `inspect_verification_page`; inspection never runs another auth action.
|
|
24
|
+
6. Call `done` only after a live action whose declared outcome is `success`
|
|
25
|
+
returns `ok: true`.
|
|
26
|
+
|
|
27
|
+
Live authentication happens only through `run_verification`. Do not contact the
|
|
28
|
+
site through shell commands or other tools.
|
|
29
|
+
|
|
30
|
+
Verification reuses one browser and continuation state by default. If an
|
|
31
|
+
observed result proves that state is no longer usable, you may set
|
|
32
|
+
`freshSession: true` on the next `run_verification`. This discards the prior
|
|
33
|
+
browser and continuation before running the requested action. Decide from live
|
|
34
|
+
evidence; do not encode site or authentication-channel assumptions.
|
|
35
|
+
Set `cleanSession: true` only when evidence also requires withholding stored
|
|
36
|
+
cookies and browser storage; credential values remain available.
|
|
37
|
+
|
|
38
|
+
## Auth program
|
|
39
|
+
|
|
40
|
+
Set `toolKind` to `"authenticate"`. Keep recorded network operations in the
|
|
41
|
+
ordinary top-level `requests` array. `authConfig` references those requests:
|
|
78
42
|
|
|
79
43
|
```json
|
|
80
44
|
{
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
"captures": [
|
|
99
|
-
{ "name": "pushToken", "source": "json", "path": "challenges[type=push].options[0].token" }
|
|
100
|
-
]
|
|
45
|
+
"entry": "agent_chosen_entry",
|
|
46
|
+
"actions": {
|
|
47
|
+
"agent_chosen_entry": {
|
|
48
|
+
"parameters": [],
|
|
49
|
+
"steps": [
|
|
50
|
+
{
|
|
51
|
+
"request": 0,
|
|
52
|
+
"onError": "fail"
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
"outcome": {
|
|
56
|
+
"type": "pause",
|
|
57
|
+
"next": "agent_chosen_next",
|
|
58
|
+
"evidence": ["recording_grounded_capture"],
|
|
59
|
+
"carry": ["state_needed_by_next_action"],
|
|
60
|
+
"message": "What the live result means for the caller"
|
|
61
|
+
}
|
|
101
62
|
},
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
63
|
+
"agent_chosen_next": {
|
|
64
|
+
"parameters": ["runtime_input_name"],
|
|
65
|
+
"steps": [
|
|
66
|
+
{
|
|
67
|
+
"request": 1,
|
|
68
|
+
"onError": "retry",
|
|
69
|
+
"repeat": {
|
|
70
|
+
"until": {
|
|
71
|
+
"source": "json",
|
|
72
|
+
"name": "recording_grounded_terminal",
|
|
73
|
+
"path": "recorded.path",
|
|
74
|
+
"equals": "recorded terminal value"
|
|
75
|
+
},
|
|
76
|
+
"intervalMs": 1000,
|
|
77
|
+
"maxAttempts": 30
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
],
|
|
81
|
+
"outcome": {
|
|
82
|
+
"type": "success",
|
|
83
|
+
"evidence": ["recording_grounded_terminal"]
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"persist": [],
|
|
88
|
+
"crossOriginCookieReinjection": false
|
|
119
89
|
}
|
|
120
90
|
```
|
|
121
91
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
-
|
|
134
|
-
|
|
135
|
-
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
-
|
|
143
|
-
-
|
|
144
|
-
|
|
145
|
-
-
|
|
146
|
-
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
92
|
+
The names and values above are structural examples, not required names or auth
|
|
93
|
+
semantics.
|
|
94
|
+
|
|
95
|
+
- `entry` is the action used when no action parameter is supplied.
|
|
96
|
+
- Declare a top-level string workflow parameter named `action`; set its default
|
|
97
|
+
to `entry` and its `choices` to exactly the keys of `actions` so generated
|
|
98
|
+
tools expose every compiled action.
|
|
99
|
+
- `parameters` lists scalar workflow parameters required by that action.
|
|
100
|
+
- Each step references one entry in `requests`.
|
|
101
|
+
- `onError` is `fail`, `continue`, or `retry`. `retry` requires a
|
|
102
|
+
`repeat` block so the artifact supplies the bound.
|
|
103
|
+
- `repeat.until` is a normal request capture evaluated after each successful
|
|
104
|
+
response. The step repeats until it matches or reaches the declared bound.
|
|
105
|
+
- `evidence` optionally names captures that must exist before the outcome is
|
|
106
|
+
accepted. Empty strings, `false`, and `null` are valid when the capture
|
|
107
|
+
declares an exact `equals` value. Choose evidence from the recording when it
|
|
108
|
+
adds proof beyond successful execution of the declared steps.
|
|
109
|
+
- A `pause` outcome declares the next action, the exact state names to carry,
|
|
110
|
+
and the caller-facing message. Carry every prior capture referenced as
|
|
111
|
+
`${state.X}` by the next action unless that action produces it before use.
|
|
112
|
+
- A `success` outcome is the artifact's authentication completion criterion.
|
|
113
|
+
- `persist` names captured non-cookie values that data tools consume later as
|
|
114
|
+
`${credential.NAME}`. Cookies persist automatically.
|
|
115
|
+
- Set `crossOriginCookieReinjection` only when the recording proves a
|
|
116
|
+
cross-origin response cookie must be projected into the browser jar.
|
|
117
|
+
|
|
118
|
+
## Request construction
|
|
119
|
+
|
|
120
|
+
Use the standard workflow request schema: `method`, `url`, `headers`,
|
|
121
|
+
`recordingRequestSeq`, optional string `body`, optional `captures`, optional
|
|
122
|
+
`mode: "navigate"` with bounded navigation criteria, and optional `effect`.
|
|
123
|
+
Set `recordingRequestSeq` to the exact `seq` from `read_request` that grounds
|
|
124
|
+
the request. It is required on requests whose captures appear in
|
|
125
|
+
`authConfig.persist`, so `imprint login` extracts each value only from its
|
|
126
|
+
producing recorded response.
|
|
127
|
+
|
|
128
|
+
`mode: "navigate"` performs a real top-level browser navigation for GET and
|
|
129
|
+
`application/x-www-form-urlencoded` POST requests. Use it when the recording
|
|
130
|
+
shows a document form submission whose redirects or browser-owned state must be
|
|
131
|
+
preserved. Its response exposes the browser's final URL in the synthetic
|
|
132
|
+
`x-imprint-final-url` response header. Capture that header with
|
|
133
|
+
`source: "response_header"` when a redirect URL carries state needed by a later
|
|
134
|
+
request.
|
|
135
|
+
|
|
136
|
+
Use runtime templates exactly as supported:
|
|
137
|
+
`${credential.X}`, `${param.X}`, `${state.X}`,
|
|
138
|
+
`${response[N].path}`, and `${generated.uuid|nonce|epoch_s|epoch_ms|iso8601}`.
|
|
139
|
+
|
|
140
|
+
Ground every request, capture, predicate, retry bound, navigation criterion, and
|
|
141
|
+
transform in the recording. Preserve functional headers and request encoding.
|
|
142
|
+
Do not store recorded secrets or one-time values as literals. When browser code
|
|
143
|
+
must mint coupled state, represent the recorded page navigation or write a
|
|
144
|
+
general request transform supported by live inputs; if you write
|
|
145
|
+
`request-transform.ts`, set the workflow's top-level `requestTransformModule`
|
|
146
|
+
to `"./request-transform.ts"`. Verification determines whether it works.
|
|
147
|
+
|
|
148
|
+
There is no auth playbook. Do not write, inspect, or depend on
|
|
149
|
+
`playbook.yaml`.
|
|
150
|
+
|
|
151
|
+
## Checkpoints
|
|
152
|
+
|
|
153
|
+
One checkpoint call ends the current turn. After `run_verification`,
|
|
154
|
+
`inspect_verification_page`, `prompt_user`, or `wait_for_cooldown`, stop and wait
|
|
155
|
+
for the orchestrator.
|
|
156
|
+
It will resume the same Claude or Codex session with the result.
|
|
@@ -8,7 +8,7 @@ You receive:
|
|
|
8
8
|
|
|
9
9
|
- `site`, `url`, `narration` — what the user was doing. When several captures were merged, `narration` includes `[Recording from <timestamp>] <url>` boundary lines marking where each capture begins (the same logical request may then appear once per capture, often with a different entity/token).
|
|
10
10
|
- `selectedTools[]` — the tools that WILL be compiled: `{ toolName, description, expectedOutput, requestSeqs, dependencySeqs, likelyParams }`. You must emit exactly one `perTool` entry for each.
|
|
11
|
-
- `sharedContext` — `{ loginRequestSeqs, credentialNames, tokenExtractionNotes, sharedHelperNotes,
|
|
11
|
+
- `sharedContext` — `{ loginRequestSeqs, credentialNames, tokenExtractionNotes, sharedHelperNotes, authRequestSeqs, authNotes }` from candidate detection.
|
|
12
12
|
- `ephemeralValues[]` — values that differed across two independent replays (highest-confidence signal for signing tokens / per-call state): `{ classification, originalSeq, location, producerSeq, producerPath, suggestedStateName }`. `browser_minted` with a high-entropy query-param `location` is the canonical sign of client-side URL signing → a `request-transform` module.
|
|
13
13
|
- `tokenContractHints[]` — producer→consumer opaque-token edges DETECTED DETERMINISTICALLY from the dual-pass diff: `{ consumerTool, consumerParam, consumerLocation, producerTool, producerField, producerPath }`. Each is a grounded `server_derived` value `consumerTool` sends that was produced in `producerTool`'s response. These are pre-computed for you and are AUTHORITATIVE — you MUST declare each as a `tokenParams` (consumer) + `emitsTokens` (producer) contract per rule 12. Refine the rough `consumerParam`/`producerField` names and the `shape` from the recording, but do not drop an edge. (Any edge you miss is reconciled in deterministically, but declaring it yourself lets you pick the right `shape`.)
|
|
14
14
|
- `requiredInputHints[]` — the GENERAL dependency contract DETECTED DETERMINISTICALLY from the recording: `{ consumerTool, input: { location, source, wiring, ... }, authCapture? }`. Each `input` is one thing a tool's request needs and where it comes from — `auth` (a login-minted session token → `${credential.X}`), `producer_tool` (a sibling token → param), `browser_state` (a captured `${state.X}` or, for a `referer` location, a `bootstrap.url`), `generated` (a per-call `${generated.uuid|epoch_ms|epoch_s|iso8601|nonce}`), or `static` (a page-minted app constant emitted verbatim). These are AUTHORITATIVE — copy each into the owning tool's `requiredInputs[]` (rule 13). For an `auth` input, also ensure `authTool.captures` carries its `authCapture` so the login persists it. Any input you drop is reconciled in deterministically, but declaring it yourself lets you refine the name/notes.
|
|
@@ -40,17 +40,15 @@ You receive:
|
|
|
40
40
|
"sourceSeqs": [number]
|
|
41
41
|
}
|
|
42
42
|
],
|
|
43
|
-
"authTool": { // OPTIONAL — whenever
|
|
43
|
+
"authTool": { // OPTIONAL — whenever sharedContext contains authentication requests
|
|
44
44
|
"toolName": "authenticate_<site>",
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"twoFactorType": "none" | "otp" | "push", // structural: none = login completes in the login request(s); otp = code typed back; push = poll until approved
|
|
48
|
-
"twoFactorContext": [string], // otp only: initiate-response fields the submit_otp request chains via ${state.X}
|
|
45
|
+
"credentialRequestSeqs": [number],
|
|
46
|
+
"authRequestSeqs": [number],
|
|
49
47
|
"credentialNames": ["username", "password"],
|
|
50
48
|
"captures": [
|
|
51
49
|
{ "name": "session_cookie", "source": "cookie", "locator": "cookie_name", "usedAs": "cookie" }
|
|
52
50
|
],
|
|
53
|
-
"notes": "
|
|
51
|
+
"notes": "recorded auth request/response relationships; the auth compile agent decides actions"
|
|
54
52
|
},
|
|
55
53
|
"perTool": [
|
|
56
54
|
{
|
|
@@ -89,10 +87,17 @@ You receive:
|
|
|
89
87
|
|
|
90
88
|
1. **Emit exactly one `perTool` entry per `selectedTools` entry**, using the same `toolName`. Do not invent or drop tools.
|
|
91
89
|
2. **Only hoist a shared module when ≥2 selected tools genuinely share it.** Single-use logic stays inside that tool's own parser.ts / request-transform.ts — do NOT create a `_shared/` module for it.
|
|
92
|
-
3. **`request-transform`** — URL signing or body construction shared across tools. Wire-up: the consuming tool sets `requestTransformModule: "../_shared/<name>.ts"`. Ground it in `ephemeralValues` (browser_minted, high-entropy query param) and `sourceSeqs`. The exported `transform(method, url, responses, params?)` returns the signed URL (or `{ url
|
|
90
|
+
3. **`request-transform`** — URL signing or body construction shared across tools. Wire-up: the consuming tool sets `requestTransformModule: "../_shared/<name>.ts"`. Ground it in `ephemeralValues` (browser_minted, high-entropy query param) and `sourceSeqs`. The exported `transform(method, url, responses, params?)` returns the signed URL (or `{ url?, body?, headers?, skip? }`).
|
|
93
91
|
4. **`parser-helper`** — a decoder/normalizer ≥2 tools' parsers call (e.g. a shared JSPB walker, a shared field mapper). The consuming tool's parser.ts does `import { ... } from '../_shared/<name>.ts'`. Ground it in a captured response body (`sourceSeqs`).
|
|
94
92
|
5. **`types`** — shared TypeScript interfaces used by ≥2 parsers. Type-only; no runtime behavior.
|
|
95
|
-
6. **Auth is NEVER a shared module.**
|
|
93
|
+
6. **Auth is NEVER a shared module.** When `sharedContext.authRequestSeqs` or
|
|
94
|
+
`loginRequestSeqs` is non-empty, declare one standalone
|
|
95
|
+
`authenticate_<site>` tool. Copy credential-bearing requests to
|
|
96
|
+
`credentialRequestSeqs` and all related auth requests to `authRequestSeqs`.
|
|
97
|
+
Do not prescribe phases or action boundaries; the auth compile agent derives
|
|
98
|
+
them from the recording. Data tools set `authRecipe.required: false` and
|
|
99
|
+
`dependsOnAuth: true` so they reuse the stored session. `credentialNames`
|
|
100
|
+
contains durable provisioned values only, never live one-time input.
|
|
96
101
|
7. **`exportSignatures` must be real TypeScript signatures** the builder will implement and the verifier will check for. List every public export.
|
|
97
102
|
8. **`spec` must be concrete enough to implement and test** — name the inputs, the exact output, and the `sourceSeqs` that prove it (e.g. "given the URL at seq 41 with the `sig` param stripped, regenerate `sig` to match the recorded value").
|
|
98
103
|
9. **`dependsOn` only references other `sharedModules[].path`.** No cycles.
|