Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,8 @@ firecrawl agent <job-id> --wait
| `--schema-file <path>` | Path to JSON schema file for structured output |
| `--max-credits <number>` | Maximum credits to spend (job fails if exceeded) |
| `--webhook <url-or-json>` | Webhook URL or configuration |
| `--thread <threadId>` | Continue an existing thread with this prompt as the next turn |
| `--mode <mode>` | `extract` returns structured data; `chat` returns a text message |
| `--alexandria` | Let the agent call Alexandria providers (implied by the flags below); `--no-alexandria` keeps it off them |
| `--toolkits <slugs>` | Comma-separated provider slugs the agent may use (up to 5; default: the whole catalog) |
| `--max-calls <n>` | Most provider calls the agent may make this turn (1-30) |
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "firecrawl-cli",
"version": "1.26.2",
"version": "1.26.3",
"publishConfig": {
"tag": "latest"
},
Expand Down
2 changes: 1 addition & 1 deletion skills/firecrawl-agent/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ A run uses connected Alexandria data providers only when it starts with an Alexa
firecrawl agent "find the head of sales at <company>" --toolkits apollo,crunchbase --wait --json -o .firecrawl/contacts.json
```

With `--require-approval` (needs `--mode chat`), a run can end on a `pendingApproval` instead of making a paid call. Ask the user, then answer it on the same thread with `--thread <threadId> --mode chat --approve <approvalId>` (or `--decline <approvalId>`). A `terms` approval only continues after an organization admin has accepted the provider's terms in the Firecrawl dashboard; approving does not accept them.
With `--require-approval` (needs `--mode chat`), a run can end on a `pendingApproval` instead of making a paid call. Ask the user, then answer it on the same thread with `firecrawl agent "<follow-up prompt>" --thread <threadId> --mode chat --approve <approvalId>` (or `--decline <approvalId>`). A `terms` approval only continues once the provider's terms are accepted for the organization, either in the dashboard or by showing the user `firecrawl alexandria terms show <provider>` and, only after they explicitly agree, running `firecrawl alexandria terms accept <provider> --terms-version <version> --digest <digest> --confirm` with the version and digest it returned. Approving does not accept them.

## Job IDs

Expand Down
58 changes: 56 additions & 2 deletions src/__tests__/alexandria-beta.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1138,7 +1138,7 @@ it('sends no exchange without an Alexandria flag', async () => {
expect(requests[0].body).not.toHaveProperty('exchange');
});

it('rejects approval flags the API cannot honor before calling it', async () => {
it('rejects Alexandria flags the API cannot honor before calling it', async () => {
const both = await cli([
'agent',
'Go ahead.',
Expand All @@ -1152,13 +1152,63 @@ it('rejects approval flags the API cannot honor before calling it', async () =>
expect(both.code).toBe(1);
expect(both.stderr).toContain('use --approve or --decline, not both');

const alwaysWithoutApprove = await cli([
'agent',
'Keep going.',
'--thread',
THREAD_ID,
'--always',
]);
expect(alwaysWithoutApprove.code).toBe(1);
expect(alwaysWithoutApprove.stderr).toContain(
'--call-ids and --always only apply with --approve'
);

const declineWithCallIds = await cli([
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
'agent',
'Never mind.',
'--thread',
THREAD_ID,
'--decline',
APPROVAL_ID,
'--call-ids=',
]);
expect(declineWithCallIds.code).toBe(1);
expect(declineWithCallIds.stderr).toContain(
'--call-ids and --always only apply with --approve'
);

const noThread = await cli(['agent', 'Go ahead.', '--decline', APPROVAL_ID]);
expect(noThread.code).toBe(1);
expect(noThread.stderr).toContain('pass that thread with --thread');

const noChat = await cli(['agent', 'Find contacts.', '--require-approval']);
expect(noChat.code).toBe(1);
expect(noChat.stderr).toContain('--require-approval needs --mode chat');

for (const value of ['12o', '0', '31', '2.5']) {
const maxCalls = await cli([
'agent',
'Find contacts.',
'--max-calls',
value,
]);
expect(maxCalls.code).toBe(1);
expect(maxCalls.stderr).toContain(
'--max-calls must be a whole number from 1 to 30'
);
}

const toolkits = await cli([
'agent',
'Find contacts.',
'--toolkits',
'a,b,c,d,e,f',
]);
expect(toolkits.code).toBe(1);
expect(toolkits.stderr).toContain(
'--toolkits takes at most 5 provider slugs'
);
expect(requests).toHaveLength(0);
});

Expand Down Expand Up @@ -1266,7 +1316,11 @@ it('shows a pending approval and how to answer it', async () => {
};
responseFor = undefined;
const terms = await cli(['agent', RUN_ID]);
expect(terms.stdout).toContain('accept them in the Firecrawl dashboard');
expect(terms.stdout).toContain('Approving does not accept terms.');
expect(terms.stdout).toContain(
' - Crunchbase: https://example.com/terms/crunchbase'
);
expect(terms.stdout).toContain('firecrawl alexandria terms show crunchbase');
expect(terms.stdout).toContain(`--approve ${APPROVAL_ID}`);
});

Expand Down
7 changes: 5 additions & 2 deletions src/commands/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -523,10 +523,13 @@ function pendingApprovalLines(
const lines = [`Pending approval ${approval.id}: ${approval.reason}`];
if (approval.kind === 'terms') {
lines.push(
'Approving does not accept terms; accept them in the Firecrawl dashboard first:'
'Approving does not accept terms. Accept them first in the dashboard, or review them with terms show and, once agreed, accept the version and digest it returns with firecrawl alexandria terms accept <provider> --terms-version <version> --digest <digest> --confirm:'
);
for (const gate of approval.terms) {
lines.push(` - ${gate.name}: ${gate.url}`);
lines.push(
` - ${gate.name}: ${gate.url}`,
` firecrawl alexandria terms show ${gate.provider}`
);
}
} else {
for (const call of approval.calls) {
Expand Down
33 changes: 29 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1710,8 +1710,7 @@ function createAgentCommand(): Command {
)
.option(
'--max-calls <n>',
'Most provider calls the agent may make this turn (1-30)',
parseInt
'Most provider calls the agent may make this turn (1-30)'
)
.option(
'--require-approval',
Expand Down Expand Up @@ -1767,6 +1766,15 @@ function createAgentCommand(): Command {
);
process.exit(1);
}
if (
(options.callIds !== undefined || options.always) &&
!options.approve
) {
console.error(
'Error: --call-ids and --always only apply with --approve.'
);
process.exit(1);
}
if ((options.approve || options.decline) && !options.thread) {
console.error(
"Error: --approve and --decline answer a thread's pending approval; pass that thread with --thread."
Expand Down Expand Up @@ -1826,10 +1834,27 @@ function createAgentCommand(): Command {
process.exit(1);
}

const toolkits = parseCommaList(options.toolkits);
if (toolkits && toolkits.length > 5) {
console.error('Error: --toolkits takes at most 5 provider slugs.');
process.exit(1);
}
const maxCalls =
options.maxCalls === undefined ? undefined : Number(options.maxCalls);
if (
maxCalls !== undefined &&
!(Number.isInteger(maxCalls) && maxCalls >= 1 && maxCalls <= 30)
) {
console.error(
'Error: --max-calls must be a whole number from 1 to 30.'
);
process.exit(1);
}

const exchange: AgentExchangeOptions = {
enabled: options.alexandria,
toolkits: parseCommaList(options.toolkits),
maxCalls: options.maxCalls,
toolkits,
maxCalls,
requireApproval: options.requireApproval,
approve: options.approve && {
approvalId: options.approve,
Expand Down
Loading