Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
41ee19b
changelog: typed columns without TypeSafe now 400 at submit
yudelevi Sep 25, 2026
9d436a0
Add managed prospecting SDK and CLI workflows
yudelevi Sep 25, 2026
2daa3d1
Explain prospecting scope rejection in SDK guidance
yudelevi Sep 26, 2026
8008cfc
Clarify prospecting customer credential requirements
yudelevi Sep 26, 2026
2f4a9cc
Clarify customer-funded agent coordination
yudelevi Sep 26, 2026
498a592
Support prospecting chat approval and messages in SDK and CLI
yudelevi Sep 27, 2026
f71e90f
Surface every saved contact list on a prospecting run
yudelevi Sep 27, 2026
99000c6
Track chat_closed and the misuse stop reason on prospecting runs
yudelevi Sep 28, 2026
c80becc
Reject control-character idempotency keys and check response field ty…
yudelevi Sep 28, 2026
33150a9
Add keyset paging to prospecting.list via before
yudelevi Sep 28, 2026
37ae830
Support prospecting checkpoints in the SDK and CLI
yudelevi Sep 28, 2026
395b5e3
Resolve named enum refs in the contract check and parse the companies…
yudelevi Sep 28, 2026
6c6d593
Correct auto-mode pilot behavior: sharpen and continue, not stop
yudelevi Sep 28, 2026
9f438d9
Describe the CLI summary filters as the word match they are
yudelevi Sep 28, 2026
3785878
Keep released SDKs parsing newer API responses
yudelevi Sep 28, 2026
3db4c24
Add seeded prospecting and segment selection to the SDK and CLI
yudelevi Sep 28, 2026
39185f6
Default ProspectingBrief to 1000 companies / 1 contact each
yudelevi Sep 28, 2026
dbec50f
Document that reply_pending holds through results grouping
yudelevi Sep 29, 2026
635f2cf
Collapse the unreleased changelog to what a release adds
yudelevi Sep 29, 2026
50c9385
Cancel prospecting runs via POST /cancel, add delete
yudelevi Sep 29, 2026
3126619
Add prospecting.rename for PATCH /prospecting/runs/{run_id}
yudelevi Sep 29, 2026
273d4f2
Merge pull request #36 from discolike/feat/agentic-prospecting
yudelevi Sep 29, 2026
7cd19aa
Add prospecting.update_plan for PATCH /prospecting/runs/{run_id}/plan
yudelevi Sep 30, 2026
de47e96
Take the prospecting run limit in dollars, not records
yudelevi Sep 30, 2026
1097239
Say the prospecting spending limit covers per-call fees
yudelevi Sep 30, 2026
1305698
Drop the spending limit from the prospecting run brief
yudelevi Sep 30, 2026
3ed840b
Add prospecting.answer_intake for the guided intake card
yudelevi Sep 30, 2026
ece47f7
Say where IntakeAnswer lives and that invalid answers 422
yudelevi Sep 30, 2026
cd826a9
Resolve changelog merge of run controls and intake
yudelevi Sep 30, 2026
ee03bbf
Regenerate request models from the dev spec and declare ProspectingIn…
yudelevi Sep 30, 2026
06525d0
Track the phased prospecting contract now live on api-dev
yudelevi Oct 1, 2026
883d88a
Merge pull request #37 from discolike/feat/prospecting-run-controls-a…
yudelevi Oct 1, 2026
0566e6f
Close the Greptile gaps from the prospecting PR
yudelevi Oct 1, 2026
f8d12a7
Flag the spec dropping the intake key constraint
yudelevi Oct 1, 2026
c8d414f
Merge pull request #38 from discolike/fix/prospecting-greptile-p2
yudelevi Oct 1, 2026
3105281
Prospecting defaults to 3 contacts per company
yudelevi Oct 1, 2026
cf38015
Let prospecting rename() switch a run between Manual and Auto
yudelevi Oct 2, 2026
a7680df
Answer a prospecting contacts review and bind replies to their question
yudelevi Oct 2, 2026
dc9b7db
Send the question's seq when the CLI picks a suggested reply
yudelevi Oct 2, 2026
7dfbe10
Say a wave run looks up database contacts' emails during research
yudelevi Oct 2, 2026
9918883
Split prospecting provider spend into AI and search parts
yudelevi Oct 3, 2026
dbc1ad9
release: 0.5.0
Oct 7, 2026
f421a22
Fix root README pilot advice and contract item-type check
yudelevi Oct 7, 2026
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
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
python-version: ['3.11', '3.12', '3.13', '3.14']
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
Expand All @@ -36,7 +36,7 @@ jobs:
- resolution: highest
python-version: '3.14'
- resolution: lowest-direct
python-version: '3.10'
python-version: '3.11'
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ repos:
rev: v3.21.2
hooks:
- id: pyupgrade
args: [--py310-plus, --keep-mock]
args: [--py311-plus, --keep-mock]
- repo: https://github.com/bwhmather/ssort
rev: 0.16.0
hooks:
Expand Down
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Changelog

## 0.5.0 (2026-10-06)

- **Breaking:** Python 3.10 is no longer supported (end of life October 2026); the SDK and CLI require Python 3.11+.
- **Breaking:** the polling deadline on `Job.wait()`, `EmailJob.wait()` and `EmailBatch.results()` (sync and async) is renamed from `timeout=` to `max_wait=`, and `signup()` / `async_signup()` take `request_timeout=` instead of `timeout=`. Behavior is unchanged; the client constructor, `with_options(timeout=...)` and the CLI's `--timeout` keep their names.
- SDK: response enums such as `JobStatus` are open, so a value a newer API adds parses instead of failing. Compare against the documented values and treat anything else as unknown.
- SDK/CLI: managed prospecting. `client.prospecting` starts, reads, lists, approves, messages, renames, cancels, deletes and waits on runs (sync and async), with typed plans, chat, progress and saved contact lists (`saved_query_ids`, split into parts for large results). `ProspectingBrief.checkpoints="ask"` pauses the run at a pilot, a drifting search, a shortfall or the target for you to answer; `customer_domains` groups your customers into segments and finds lookalikes of each, picked on approval with `seed_segments`. `rename()` retitles a run in any status with `ProspectingRunUpdate(title=...)` and returns its `list()` summary; result lists it already saved keep their names. `cancel()` stops a run and keeps it; `delete()` cancels an active run, then removes it from `list()` and `get()` (charges already incurred stay). The CLI adds `prospecting start/status/list/approve/message/cancel/wait`, asks at checkpoints on a terminal, and exits 7 with the question on stderr under `--no-input`.
- SDK: `client.prospecting.update_plan(run_id, ProspectingPlanSettings(plan_version=..., ...))` (sync and async) changes a proposed plan's contact engine, company-check engine, search provider (`"none"` skips web research) and `max_spend_usd` (a USD spending limit on records and per-request fees at the plan's rates; `0` removes it) before approval, then returns the run with the re-estimated plan at a new `plan_version`. Prospecting `stop_reason` gains `candidate_limit` (candidate cap reached) and `credit_limit` (stopped at the spending limit, results kept); `candidates_exhausted` now means only that the search ran dry. `stop_reason` stays an open string. Runs without a chosen contact engine research on DiscoLike Groove, or find indexed contacts only with no search provider.
- SDK: `client.prospecting.answer_intake(run_id, answers, idempotency_key=..., summary=None)` (sync and async) answers the guided-intake card a run asks before it drafts a plan. `answers` maps a question key (`company_activity`, `industry`, `geography`, `company_size`, `persona_roles`, `list_size`) to `IntakeAnswer(values=[...], other="...")`; the card's fields are on the `kind="question"` message's `data`. `ProspectingMessageRequest.text` is now optional, so a message can carry `intake` alone; a message with neither `text` nor any `intake` answer is rejected locally, as the API rejects it.
- SDK: `ProspectingInFlight` gains `progress` (0-100 or `None`) for the running item.
- SDK/CLI: prospecting run shape and limits. `ProspectingPlanSettings` gains `target_companies`, `contacts_per_company`, `max_provider_spend_usd` (USD on your own AI and search provider keys; `0` removes it), `deliverable` (`"leads"`, or `"accounts"` for the checked companies only) and `goal` (`"leads"`, or `"companies"` to work only `target_companies` matching companies), all optional. `ProspectingBrief` takes `deliverable` and `goal` too, sent only when set, and the CLI's `prospecting start` gains `--deliverable` and `--goal`. `update_plan()` rewrites the latest plan message in place under the same `seq`, so read the plan from the run it returns. `ProspectingRun` gains `pipeline_phase` (`"companies"`, `"people"` or `None`) and `provider_cost_usd`, and its `brief` reports `deliverable` and `goal`. `CHECKPOINT_STOP_REASONS` gains `top_up`: a run asks before another round of companies (replies `continue`, `raise`, `shrink`, `finish`), and an auto run asks too when that round would pass a spending limit. `stop_reason` gains `provider_limit` and `companies_worked`. A saved contact list now carries every fit company as a row with its fit columns (`ICP Fit`, `Confidence`, `Reasoning`, `Segment`, `Customer segment`); a company with no contact has `contacts: []`, and an accounts run saves company rows only. Contacts per company go up to 10 and default to 3 when the brief gives no count, and intake answers accept the `deliverable` key.
- SDK: `ProspectingRunUpdate` takes `checkpoints` (`"ask"` or `"auto"`), and `title` is now optional, so `client.prospecting.rename(run_id, ProspectingRunUpdate(checkpoints="auto"))` switches a run between Manual and Auto mid-run. Switching to Auto while the run waits on a checkpoint answers it with continue and resumes. A mode switch needs the same plan, quota and payment method as `start()`, and raises on a finished run or while another of your runs is active (409). A title-only rename behaves as before.
- SDK/CLI: `CHECKPOINT_STOP_REASONS` gains `contacts_review`, the pause a wave run makes in both modes before looking up its researched contacts' emails (replies "Find emails", "Find more companies first", "Finish"). `ProspectingMessageRequest` takes an optional `question_seq` and the CLI's `prospecting message` gains `--question-seq`, so a suggested reply answers only the question it was picked from; omitted, the body is unchanged. "Find emails" counts only with `question_seq`, and the CLI's interactive `wait` sends it when you pick a reply by number (its `--no-input` envelope carries `question_seq` too).
- SDK: a wave run's plan message `data` may carry `email_lookup_note`: approving the plan also starts email lookups for contacts already in DiscoLike's database while web research runs, so the `contacts_review` question reports what those found and sizes only the remaining lookups. Message `data` is a plain dict, so no SDK change is needed to read it.
- SDK: `ProspectingRun` gains `provider_ai_cost_usd` and `provider_search_cost_usd`, the AI and search providers' parts of `provider_cost_usd` (`None` for runs started before the split was recorded). With DiscoLike Groove, research adds no AI cost; its web searches bill your own search provider, about two queries per researched company at that provider's per-query price, never DiscoLike credits. The plan message's `data["estimate"]` likewise gains `ai_cost_usd` and `search_cost_usd` beside the total `llm_cost_usd`; it is a plain dict, so no SDK change is needed to read them.

## 0.4.1 (2026-09-23)

- CLI: fix `ImportError: cannot import name 'Abort' from 'typer._click.exceptions'` on every command in a fresh 0.4.0 install. Typer 0.27 moved `Abort`; the CLI now imports the public `typer.Abort` and requires `typer>=0.26.1,<0.28`, since it still relies on Typer's vendored Click for its error envelope and help formatting.
Expand Down
26 changes: 25 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ uv run pytest packages/discolike/tests -q
uv run pytest packages/discolike-cli/tests -q
```

CI runs the same checks across Python 3.10–3.14.
CI runs the same checks across Python 3.11–3.14.

## Branches

Expand All @@ -42,6 +42,30 @@ models in `discolike.requests` track dev, not prod. The prod spec lags, so
both `check_contract.py` and `gen_requests.py --check` against prod stay red
until the platform deploys; don't regenerate against prod to "fix" it.

## Compatibility

Released SDK versions stay installed long after a new one ships, and they
all talk to the same live API. Keep them working:

- **Response models parse anything a newer server can send.** Models allow
extra fields (`extra="allow"`). Response enums are open
(`Literal[...] | str`), never a closed `Literal`. Response fields carry no
request-side limits (`max_length`, `ge`/`le`). Don't reuse a request model
as a response field (see `ProspectingRunBrief`). A new response field gets
a default, so the SDK still parses servers from before it existed.
- **The SDK may be looser than the spec, never stricter.** The contract check
flags only drift that breaks parsing: the SDK requiring a field the spec
makes optional, or the spec allowing a type the SDK rejects.
- **Public signatures don't change silently.** Never rename or remove a
public method, kwarg, exception, or exported name in a patch release. When
one has to go, keep the old spelling working with a `DeprecationWarning`
that names the replacement for at least one minor release, and record the
removal under **Breaking** in `CHANGELOG.md`.
- **New request fields are optional** and omitted from the wire when unset, so
a newer SDK still works against an API that hasn't deployed them yet.
- **Python support follows upstream EOL.** Drop a version once it reaches
end of life, as a **Breaking** CHANGELOG line.

## Reporting bugs

Open a GitHub issue with the package name, version, and a minimal
Expand Down
Loading
Loading