2026-06-24
An AI Agent for Freelancers Should Not Send the Proposal
A 36-case intake gate turns untrusted briefs into review packets while refusing client messages, invented claims, broad tools, privacy drift, and ambiguous terms.
Opportunity intake reconstruction
An AI agent for freelancers should stop before it speaks for the freelancer. Its useful job is narrower: turn one messy, untrusted client brief into a decision packet that shows what is known, what is missing, what looks risky, and which single question would reduce the most uncertainty.
The polished proposal is the tempting output. It is also where the agent can invent experience, accept a bad rights clause, quote against an unclear scope, or send an instruction that came from the brief itself. Those errors do not look like software failures. They look like the freelancer made a professional promise.
We built a deterministic intake gate instead. Thirty-six synthetic cases exercised identity, scope, budget, deadline, timezone, rights, privacy, conflicts, prompt injection, attachments, outbound intent, tool reach, provenance, duplication, and work category. Three complete packets reached human review. The other 33 stopped with a specific reason.
37eea4052629e30b12724adb7b0d56a85722f206272ea5d14041e1293438a202.The useful output is a packet, not a pitch
Most inbound opportunities arrive as mixed evidence. The client may describe a deliverable but omit its acceptance criteria. A budget may cover discovery or the entire engagement; the text rarely says which. “Next Friday” is not a deadline until a timezone and review window are known. A portfolio request can sit beside a confidentiality demand that forbids showing comparable work.
The agent should not smooth those contradictions into sales copy. It should preserve them. A good packet is deliberately awkward where the source is awkward.
This is less ambitious than “an agent that wins clients while you sleep.” It is also a better first system. Classification and evidence capture are reversible. A client-facing promise is not.
One unresolved field becomes one question
The fixture does not assign a generic quality score. It routes the packet according to the first unresolved boundary. Missing scope becomes ASK_SCOPE_QUESTION; missing budget becomes ASK_BUDGET_QUESTION; an impossible deadline becomes DECLINE_DEADLINE. Confidential material pauses at HOLD_PRIVACY_REVIEW. A known conflict does not get a softer score; it becomes DECLINE_CONFLICT.
Separate states matter because they lead to different human work. The freelancer who needs to clarify a deadline should not receive the same “low confidence” bucket as a brief carrying restricted data.
A packet may contain a draft question, but not a draft answer attributed to the client. That small distinction prevents the agent from turning uncertainty into fiction.
Where the packet fails closed
A brief is not an instruction file for the agent. It is evidence about a possible engagement. That remains true when the brief says “ignore previous rules,” embeds a hidden request in an attachment, or asks the system to email a different address.
OWASP classifies instructions hidden in emails, attachments, documents, issue descriptions, and web pages as indirect prompt injection. Its recommended controls include separating instructions from untrusted data, validating tool parameters and outputs, limiting privilege, and retaining human approval for high-impact actions. OpenClaw's security documentation makes the same operational point: a trusted sender does not make every page, file, email, log, or pasted block trustworthy.
Wrapping the brief as untrusted content is a useful control, but not the whole design. Prompt filtering can miss novel phrasing. The hard boundary is that the intake agent lacks the client-facing and general-purpose tools an injected brief would want to use.
Tool reach is the real boundary
For this role, the allowlist is short. The agent may read the supplied intake record and write a local packet artifact. It does not receive marketplace-send, email-send, calendar-write, contract, invoice, payment, browser-login, or generic shell tools.
OpenClaw distinguishes sandbox placement from tool policy. Its documentation says deny wins, and warns that allowing generic exec does not become read-only merely because named write tools are missing. A shell can still call a remote API or alter a file. If the packet agent can improvise its own network client, “no send tool” is only cosmetic.
Credentials follow the same rule. A SecretRef can reduce plaintext persistence, but it is not process isolation. The intake agent does not need a marketplace token, mail token, calendar token, or billing secret at all. The safest secret is the one outside the role's trust boundary.
Testing thirty-six intake cases
The base case used a synthetic marketplace brief for a small accessibility review. It had a verified platform identity, two explicit deliverables, known budget and deadline, a timezone, clear rights, public data, no conflict, complete provenance, no outbound request, and only a local packet-store tool.
Each mutation changed one or two fields. The decision function is ordinary JavaScript with a fixed evaluation order; there is no language model inside the test.
| Mutation | Observed route | Why the route is specific |
|---|---|---|
| Scope or deliverables are missing. | ASK_SCOPE_QUESTION | The packet cannot estimate work from an empty deliverable list. |
| Budget, deadline, timezone, or rights are unresolved. | One matching ASK_… state | The freelancer gets one reviewable next question, not a fabricated assumption. |
| Data is confidential or restricted. | HOLD_PRIVACY_REVIEW | Sensitive material needs a handling decision before analysis continues. |
| Conflict is unknown or confirmed. | HOLD_CONFLICT_REVIEW or DECLINE_CONFLICT | Uncertainty and a known prohibition are not collapsed into one score. |
| Brief or attachment contains an instruction-like injection signal. | BLOCK_INJECTION | Client content cannot redefine the intake task. |
| The case asks for a message, proposal send, shell, mail tool, or calendar write. | BLOCK_AUTO_SEND or BLOCK_UNSAFE_TOOL | Authority is rejected before the packet reaches any external system. |
| Experience claim lacks provenance or the intake duplicates an existing idempotency key. | BLOCK_UNSUPPORTED_CLAIM or BLOCK_DUPLICATE | The packet neither invents credibility nor creates two records for one lead. |
The counts describe a retained policy fixture, not a live marketplace benchmark, model accuracy test, conversion study, or estimate of time saved.
Order of checks prevents convenient bypasses
The function checks prompt injection and unsafe attachments before it considers business completeness. It checks outbound intent before requested tools. It checks unsupported claims and duplicate identity before it recommends review. This order means a beautifully specified opportunity cannot buy its way past a security boundary.
One case combined “send this proposal” with a mail tool. It stopped at BLOCK_AUTO_SEND. A second case requested the mail tool without the send flag and stopped at BLOCK_UNSAFE_TOOL. The two tests are intentionally redundant: both the declared intent and the available capability must be safe.
Three cases reached READY_FOR_HUMAN_REVIEW: the base marketplace packet, a verified-domain referral email, and a verified-domain contact form. That result does not mean “take the job.” It means the record is coherent enough for the freelancer to decide.
What the agent is allowed to retain
The packet should be compact enough to inspect without reopening every source, and detailed enough to show where each conclusion came from. A synthetic example looks like this:
{
"source": {
"type": "marketplace_brief",
"retrievedAt": "2026-07-21T20:00:00Z",
"identityStatus": "verified_platform_identity"
},
"work": {
"deliverables": ["annotated findings", "remediation note"],
"scopeStatus": "clear",
"budgetStatus": "known",
"deadlineStatus": "known",
"rightsStatus": "clear"
},
"risk": {
"dataClass": "public",
"conflictStatus": "clear",
"injectionSignal": false
},
"decision": "READY_FOR_HUMAN_REVIEW",
"nextQuestion": null,
"outboundAllowed": false
}
Real packets should minimize personal data, encrypt retained source material where appropriate, and record deletion policy. The packet does not need a client's private attachment copied into every downstream note. A hash, provenance record, and restricted pointer may be the better receipt.
Keep the raw brief separate from the normalized fields. That separation allows a reviewer to compare the agent's extraction with the source, and it prevents the brief from being reused later as trusted instructions.
A decision packet is not a recommendation
READY_FOR_HUMAN_REVIEW is deliberately modest. It does not set a rate, claim availability, rank the client, promise a delivery date, waive a conflict, accept a rights clause, or produce a likelihood of winning. Those judgments depend on the freelancer's pipeline, cash position, interests, obligations, and tolerance for uncertainty.
If a scoring layer is added later, keep its factors inspectable and avoid proxy judgments about people. Score the opportunity's documented fit and completeness, not the perceived worth of the client. Preserve the underlying fields so the freelancer can disagree with the ranking.
The handoff keeps agency with the freelancer
- Capture one inbound brief with a stable record ID and retrieval timestamp.
- Wrap the brief and extracted attachment text as untrusted data.
- Normalize only the fields the decision packet actually needs.
- Run deterministic injection, attachment, tool, provenance, duplicate, conflict, privacy, and completeness gates.
- Write the packet locally with its source receipt and exact decision code.
- Show the freelancer the packet, unresolved field, and at most one proposed next question.
- Let the freelancer edit, reject, or compose the response in a separate client-facing workflow.
The separation creates a clean audit trail. If the extraction is wrong, correct the packet. If the policy is wrong, change the gate and replay retained synthetic cases. Neither repair requires apologizing to a client for a message the agent already sent.
The tradeoff is obvious: the freelancer still performs the final communication. That is not wasted automation. Intake compression removes searching, rereading, and uncertainty bookkeeping while leaving pricing, negotiation, tone, and consent with the person whose reputation is at stake.
If outbound automation becomes necessary, build it as a different system. Require an exact recipient, exact reviewed body, expiry, one-time approval, idempotency key, delivery receipt, and a tool that cannot alter the approved payload. Do not add a boolean named sendNow to the intake agent.
Evidence and the untested boundary
- OWASP LLM Prompt Injection Prevention Cheat Sheet — indirect injection, instruction/data separation, tool validation, least privilege, output checks, and human review.
- NIST Generative AI Profile — trustworthiness considerations across design, development, use, and evaluation.
- OpenClaw security documentation — untrusted external content and why prompt guidance is not a hard boundary.
- OpenClaw sandbox and tool-policy documentation — allow/deny precedence and the generic-exec boundary.
- OpenClaw secrets documentation — SecretRef persistence reduction and its process-isolation limit.
- Local deterministic fixture — 36 cases, 36 matches, 3 human-review packets, 33 held or blocked, and 21 decision codes. SHA-256:
37eea4052629e30b12724adb7b0d56a85722f206272ea5d14041e1293438a202.
The evidence here is official source analysis plus synthetic local JavaScript. We did not connect to a freelancer marketplace, inbox, client, attachment, model API, messaging system, calendar, contract service, invoicing system, credential store, or production OpenClaw configuration. Before using the design with real opportunities, replay the negative cases in an isolated test account, review data-retention duties for the jurisdictions involved, and verify that outbound tools are absent rather than merely discouraged.