@flowapt/flowiq-cli 0.6.5 → 0.6.7
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 +29 -2
- package/TEAM-GUIDE.md +14 -4
- package/package.json +1 -1
- package/src/commands/agent-config.js +14 -2
- package/src/commands/agent-test.js +12 -2
- package/src/index.js +2 -0
package/README.md
CHANGED
|
@@ -187,8 +187,8 @@ flowiq ct list
|
|
|
187
187
|
|
|
188
188
|
- **Always `--dry-run` first** on a production agent — it prints added/removed/changed tool names before you commit.
|
|
189
189
|
- **Destructive pushes are blocked by default**: a push that removes a tool, disables one, or sends an empty `tools[]` (wiping everything) writes nothing and shows you exactly what it would strip — re-run with `--confirm` if intentional. Additive/no-op pushes are unaffected.
|
|
190
|
-
- Server-side validation is strict: tool `name` (`^[a-zA-Z0-9_-]{1,64}$`, no duplicates), non-empty `description`, `http(s)` `endpoint`, method `GET/POST/PUT/PATCH/DELETE`, object-typed `parameters`/`
|
|
191
|
-
- **Warnings (non-blocking):**
|
|
190
|
+
- Server-side validation is strict: tool `name` (`^[a-zA-Z0-9_-]{1,64}$`, no duplicates or built-in collisions), non-empty `description`, `http(s)` `endpoint`, method `GET/POST/PUT/PATCH/DELETE`, scalar headers, object-typed `parameters`/`injected_parameters`, valid auth/channels, timeout and response caps. Recursive schemas support strict nested objects, typed arrays, enums and numeric/string/item limits. Unknown tool/schema keys are rejected before anything writes.
|
|
191
|
+
- **Warnings (non-blocking):** an object with properties but no required fields. Unknown `{{placeholder}}` values are hard errors; injected parameters may additionally reference a declared top-level model parameter. Known runtime values include `organization_id`, `contact_id`, `agent_id`, `contact_whatsapp_id`, `contact_full_name`, `whatsapp_message_id`, `text`, `unique_message_id`, `unix_timestamp`, `operation_idempotency_key`, `supabase_anon_key`, and `openai_api_key`.
|
|
192
192
|
- **Retailer gateway credential:** `{{retailer_tools_internal_key}}` is super-admin-only and resolves only as the `x-api-key` value for `https://express.chatcart.io/retailer-tools/*` (or loopback in local tests). Validation and runtime both reject putting it in a body or sending it to any other host.
|
|
193
193
|
- `--agent` + filenames behave like `prompts`/`knowledge`; the file carries `agent_id`, so `push` targets the agent it was pulled from.
|
|
194
194
|
|
|
@@ -1227,6 +1227,8 @@ flowiq agent config <organization_id> --use-settings-prompt --model gpt-5.6-luna
|
|
|
1227
1227
|
--rename Zara --tool woo_order_build=true --tool view_cart_tool=true --discount true
|
|
1228
1228
|
flowiq agent config <organization_id> --test-contact-number 27000000001 --test-contact-name "QA Bot"
|
|
1229
1229
|
flowiq agent config <organization_id> --model gpt-5.6-luna --reasoning-effort high
|
|
1230
|
+
flowiq agent config <organization_id> --disable-base-tool get_product_info
|
|
1231
|
+
flowiq agent config <organization_id> --enable-base-tool get_product_info
|
|
1230
1232
|
```
|
|
1231
1233
|
|
|
1232
1234
|
Settable: `settings.use_settings_prompt`, `settings.model`,
|
|
@@ -1240,6 +1242,10 @@ the tool-flag columns (`woo_order_build`, `woo_tip_field`, `woo_order_note_field
|
|
|
1240
1242
|
(`settings.test_contact_number` / `settings.test_contact_name`). Anything else is
|
|
1241
1243
|
rejected; every change is reported before → after.
|
|
1242
1244
|
|
|
1245
|
+
`--disable-base-tool` / `--enable-base-tool` can be repeated. They manage only the
|
|
1246
|
+
allowlisted discovery/commerce built-ins; opt-out, human handover and channel-send
|
|
1247
|
+
tools cannot be disabled.
|
|
1248
|
+
|
|
1243
1249
|
**`--tool product_lookup=true` (added 11 Aug 2026).** Turns on the `product_lookup`
|
|
1244
1250
|
tool: a typo-tolerant **pg_trgm fuzzy match on `product_title`** (plus badge/tag
|
|
1245
1251
|
search), as opposed to `get_product_info`, which is **semantic**. This matters far
|
|
@@ -1327,6 +1333,22 @@ flowiq test cleanup <organization_id> --confirm # delete the stress contact
|
|
|
1327
1333
|
`--agent <id>` targets a specific (non-active) agent. `test stress cleanup` is
|
|
1328
1334
|
destructive and requires `--confirm`.
|
|
1329
1335
|
|
|
1336
|
+
**Scenario pack shape.** `{scenarios:[{id, title, turns:[{text, expect?, expectNot?}]}]}`.
|
|
1337
|
+
`expect` / `expectNot` are case-insensitive substring checks (informational —
|
|
1338
|
+
reported and summed, never a hard fail) and accept **either a single string or an
|
|
1339
|
+
array of strings**:
|
|
1340
|
+
|
|
1341
|
+
```json
|
|
1342
|
+
{ "text": "What investment options do I have?",
|
|
1343
|
+
"expect": "depend on your fund rules",
|
|
1344
|
+
"expectNot": ["Destiny offers", "guaranteed return"] }
|
|
1345
|
+
```
|
|
1346
|
+
|
|
1347
|
+
Before v0.6.7 a bare string was iterated character by character, so
|
|
1348
|
+
`"expect": "fund rules"` reported ten single-letter checks that all trivially
|
|
1349
|
+
passed — a wrong pass/fail signal that still looked like a real result. Write
|
|
1350
|
+
either form now.
|
|
1351
|
+
|
|
1330
1352
|
### Guide — `flowiq guide`
|
|
1331
1353
|
|
|
1332
1354
|
Read the bundled docs in the terminal — no digging through node_modules.
|
|
@@ -1378,3 +1400,8 @@ interactive terminal. Silence it with `FLOWIQ_NO_UPDATE_CHECK=1`.
|
|
|
1378
1400
|
|
|
1379
1401
|
UNLICENSED. Internal staff tool — install requires a valid `fiq_staff_…`
|
|
1380
1402
|
key issued by a FlowIQ super-admin.
|
|
1403
|
+
|
|
1404
|
+
ChatCart rollout: Pick n Pay uses retailer `pnp` and the private connection link.
|
|
1405
|
+
Enable its gateway before adding it to the agent tools. Cart retries use
|
|
1406
|
+
`{{operation_idempotency_key}}`; configure `timeout_ms` up to 30000 and
|
|
1407
|
+
`max_response_bytes` up to 1048576 for bounded batch results.
|
package/TEAM-GUIDE.md
CHANGED
|
@@ -98,10 +98,12 @@ When you see it, run `npm i -g @flowapt/flowiq-cli` — a stale version also mea
|
|
|
98
98
|
| See an org's agents / create one | `flowiq agent list <org_id>` / `flowiq agent create <org_id> --name "…"` — a new agent arrives ready to work (gpt-5.6-luna, high reasoning, prompt switched ON); no follow-up `agent config` needed |
|
|
99
99
|
| Change agent model / tool flags | `flowiq agent config <org_id> --model … --tool view_cart_tool=true` |
|
|
100
100
|
| Set the house model + reasoning tier | `flowiq agent config <org_id> --model gpt-5.6-luna --reasoning-effort high` |
|
|
101
|
+
| Remove a conflicting built-in tool from one agent | `flowiq agent config <org_id> --disable-base-tool get_product_info` (repeatable; safety/delivery tools cannot be disabled) |
|
|
101
102
|
| Agent says an in-stock product "isn't showing" | `flowiq agent config <org_id> --tool collapse_product_variants=true` — the search cap counts VARIANT rows until this is on |
|
|
102
103
|
| Agent can't quote ANY price / "I can't pull the live menu" | `flowiq agent config <org_id> --tool product_lookup=true` — name-based fuzzy lookup that works without embeddings or a live OpenAI key (semantic `get_product_info` needs both) |
|
|
103
104
|
| Talk to the live agent safely (no real WhatsApp ever sent) | `flowiq test send <org_id> "hi, do you sell X?"` |
|
|
104
105
|
| Set which contact `flowiq test` uses (use a FAKE number!) | `flowiq agent config <org_id> --test-contact-number 27000000001 --test-contact-name "QA Bot"` |
|
|
106
|
+
| Prove a batch of fixes actually landed, in one run | `flowiq test scenario <org_id> ./pack.json` — pack is `{scenarios:[{id,title,turns:[{text,expect?,expectNot?}]}]}`. `expect`/`expectNot` are case-insensitive substring checks and take **a single string OR an array**. (Before v0.6.7 a bare string was checked letter by letter, so it always "passed" — if you wrote packs on an older version, re-run them.) |
|
|
105
107
|
| **Make short links for a campaign** (with UTM tracking) | `flowiq links shorten <org_id> --url "https://shop.co.za/product/x" --campaign 13Aug_Seeds --content 13Aug_Seeds` (dry run) → `… --commit`. `utm_source`/`utm_medium` are set for you; campaign + content must be given together |
|
|
106
108
|
| Short links for every URL in a message | `flowiq links shorten <org_id> --file ./message.txt --campaign X --content X --commit` |
|
|
107
109
|
| Check which links a campaign has, and their clicks | `flowiq links list <org_id> --campaign 13Aug_Seeds` |
|
|
@@ -179,12 +181,15 @@ flowiq ct push <slug>
|
|
|
179
181
|
```
|
|
180
182
|
|
|
181
183
|
Custom tools define real HTTP calls the agent can execute, so the server
|
|
182
|
-
validates
|
|
183
|
-
|
|
184
|
-
|
|
184
|
+
validates names, URLs, methods and parameter shapes. Unknown keys and unresolved
|
|
185
|
+
`{{placeholders}}` block the write; an
|
|
186
|
+
object with no required fields remains a warning because it can be intentional.
|
|
187
|
+
Nested object/array schemas are strict and supported. For ChatCart retailer tools, use
|
|
185
188
|
`{{retailer_tools_internal_key}}` only as the `x-api-key` auth value on the
|
|
186
189
|
trusted `express.chatcart.io/retailer-tools/*` gateway; org/contact identity is
|
|
187
|
-
injected server-side and mutations use `{{
|
|
190
|
+
injected server-side and mutations use `{{operation_idempotency_key}}` for stable,
|
|
191
|
+
operation-scoped idempotency. Batch independent product requests in one
|
|
192
|
+
`search_retailer_products.queries` array (1–12) instead of repeated search calls.
|
|
188
193
|
|
|
189
194
|
### Example: tag a segment of contacts (Advanced Tagging)
|
|
190
195
|
|
|
@@ -325,3 +330,8 @@ Three things worth knowing:
|
|
|
325
330
|
| "Organization has no active_whatsapp_agent" | Target the agent directly: `--agent <id>` (ids from `flowiq agent list <org_id>`) |
|
|
326
331
|
| Login browser page says the code expired | Codes live 10 minutes — just re-run `flowiq auth login` |
|
|
327
332
|
| Pushed the wrong thing | Everything is pull→push, so re-pull an older copy if you have one, or check with Matt — server logs record every push with who/what/when |
|
|
333
|
+
|
|
334
|
+
ChatCart rollout: Pick n Pay uses retailer `pnp` and the private connection link.
|
|
335
|
+
Enable its gateway before adding it to the agent tools. Cart retries use
|
|
336
|
+
`{{operation_idempotency_key}}`; configure `timeout_ms` up to 30000 and
|
|
337
|
+
`max_response_bytes` up to 1048576 for bounded batch results.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flowapt/flowiq-cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.7",
|
|
4
4
|
"description": "Command-line tool for FlowIQ staff: round-trip agent prompts, questionnaires, fine-tuning, pin-board tasks, webhooks, templates, agent-updates, chat exports, and live agent testing without ever touching service-role credentials.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -23,6 +23,12 @@ export function collectTool(val, acc) {
|
|
|
23
23
|
return acc;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
export function collectToolName(val, acc) {
|
|
27
|
+
acc = acc || [];
|
|
28
|
+
acc.push(val);
|
|
29
|
+
return acc;
|
|
30
|
+
}
|
|
31
|
+
|
|
26
32
|
function printSnapshot(title, snap) {
|
|
27
33
|
console.log(` ${title}:`);
|
|
28
34
|
for (const [k, v] of Object.entries(snap)) {
|
|
@@ -47,6 +53,12 @@ export async function config(orgId, opts = {}) {
|
|
|
47
53
|
if (Object.keys(settings).length) body.settings = settings;
|
|
48
54
|
if (opts.rename !== undefined) body.name = opts.rename;
|
|
49
55
|
if (opts.discount !== undefined) body.discount_enabled = parseBool(opts.discount, "--discount");
|
|
56
|
+
if (opts.disableBaseTool?.length || opts.enableBaseTool?.length) {
|
|
57
|
+
body.base_tool_changes = {
|
|
58
|
+
disable: opts.disableBaseTool || [],
|
|
59
|
+
enable: opts.enableBaseTool || [],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
50
62
|
|
|
51
63
|
if (opts.tool && opts.tool.length) {
|
|
52
64
|
const flags = {};
|
|
@@ -63,7 +75,7 @@ export async function config(orgId, opts = {}) {
|
|
|
63
75
|
}
|
|
64
76
|
|
|
65
77
|
const hasWrite =
|
|
66
|
-
body.settings || body.name !== undefined || body.tool_flags || body.discount_enabled !== undefined;
|
|
78
|
+
body.settings || body.name !== undefined || body.tool_flags || body.base_tool_changes || body.discount_enabled !== undefined;
|
|
67
79
|
|
|
68
80
|
// No write flags → just show current config.
|
|
69
81
|
if (!hasWrite) {
|
|
@@ -76,7 +88,7 @@ export async function config(orgId, opts = {}) {
|
|
|
76
88
|
}
|
|
77
89
|
console.log(`${resp.organization_name} → agent ${resp.agent_id}${resp.is_active === false ? " [NON-active]" : ""}`);
|
|
78
90
|
printSnapshot("current", resp.current);
|
|
79
|
-
console.log("\n(pass --use-settings-prompt / --model / --reasoning-effort / --rename / --tool / --discount / --test-contact-number / --test-contact-name to change)");
|
|
91
|
+
console.log("\n(pass --use-settings-prompt / --model / --reasoning-effort / --rename / --tool / --disable-base-tool / --enable-base-tool / --discount / --test-contact-number / --test-contact-name to change)");
|
|
80
92
|
return;
|
|
81
93
|
}
|
|
82
94
|
|
|
@@ -45,11 +45,21 @@ async function clearConv(orgId, agent) {
|
|
|
45
45
|
return http.post("agent-test", { action: "clear", organization_id: orgId, agent_id: agent });
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
// A bare string is ONE needle, not a list of characters. Iterating a string
|
|
49
|
+
// with for..of yields its characters, so a pack written as
|
|
50
|
+
// `"expect": "fund rules"` used to report ten single-letter checks that all
|
|
51
|
+
// trivially passed (and, for expectNot, all trivially failed) — a wrong
|
|
52
|
+
// pass/fail signal that still looked like a real result.
|
|
53
|
+
function needles(v) {
|
|
54
|
+
if (v == null) return [];
|
|
55
|
+
return Array.isArray(v) ? v : [v];
|
|
56
|
+
}
|
|
57
|
+
|
|
48
58
|
function checkExpectations(turn, replyText) {
|
|
49
59
|
const results = [];
|
|
50
60
|
const hay = (replyText || "").toLowerCase();
|
|
51
|
-
for (const e of turn.expect
|
|
52
|
-
for (const e of turn.expectNot
|
|
61
|
+
for (const e of needles(turn.expect)) results.push({ kind: "expect", needle: e, ok: hay.includes(String(e).toLowerCase()) });
|
|
62
|
+
for (const e of needles(turn.expectNot)) results.push({ kind: "expectNot", needle: e, ok: !hay.includes(String(e).toLowerCase()) });
|
|
53
63
|
return results;
|
|
54
64
|
}
|
|
55
65
|
|
package/src/index.js
CHANGED
|
@@ -328,6 +328,8 @@ export function run(argv) {
|
|
|
328
328
|
.option("--discount [bool]", "agent.discount.enabled (true if bare)")
|
|
329
329
|
.option("--test-contact-number <number>", "settings.test_contact_number — the contact `flowiq test` uses (use a FAKE number; \"\" clears it → synthetic fallback)")
|
|
330
330
|
.option("--test-contact-name <name>", "settings.test_contact_name — display name for the test contact (\"\" clears it)")
|
|
331
|
+
.option("--disable-base-tool <name>", "hide a disableable built-in tool from this agent (repeatable)", agentConfigCmd.collectToolName, [])
|
|
332
|
+
.option("--enable-base-tool <name>", "restore a previously disabled built-in tool (repeatable)", agentConfigCmd.collectToolName, [])
|
|
331
333
|
.action((orgId, opts) => agentConfigCmd.config(orgId, opts));
|
|
332
334
|
|
|
333
335
|
// agent-updates (pending client change-requests + chat context; pull + resolve)
|