diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fefe85..8546845 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ - 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 intake answers accept the `deliverable` key. +- 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. ## 0.4.1 (2026-09-23) diff --git a/README.md b/README.md index 26c5241..497daca 100644 --- a/README.md +++ b/README.md @@ -470,7 +470,7 @@ Top-up rounds: when the companies checked so far yield too few contacts for the Intake: a run can pause on a `kind="question"` message whose `data` lists a few option fields (keys `company_activity`, `industry`, `geography`, `company_size`, `persona_roles`, `list_size`). Answer it with `client.prospecting.answer_intake(run.run_id, {"company_activity": IntakeAnswer(values=["sell"]), "geography": IntakeAnswer(other="Ohio")}, idempotency_key=...)`: `values` are the option values you picked and `other` is free text. `IntakeAnswer` comes from `discolike.requests`. Invalid answers return 422; an answer sent after the card was superseded returns 409. Typing free text with `message()` instead still works. -Initial planning extracts company counts and contacts per company from the brief. Omitted settings keep that inference available, falling back to 1,000 companies and 1 contact per company. Explicit settings, including explicit defaults, override the text. Targets support 1–10,000 companies and 1–10 contacts per company. Candidate and action caps default to automatic (`0`); explicit maxima are 100,000 candidates and 10,000 actions. Result pages support up to 500 rows; recent-run lists support up to 50. Approved runs expose a stable `saved_query_id` for saved results. +Initial planning extracts company counts and contacts per company from the brief. Omitted settings keep that inference available, falling back to 1,000 companies and 3 contacts per company. Explicit settings, including explicit defaults, override the text. Targets support 1–10,000 companies and 1–10 contacts per company. Candidate and action caps default to automatic (`0`); explicit maxima are 100,000 candidates and 10,000 actions. Result pages support up to 500 rows; recent-run lists support up to 50. Approved runs expose a stable `saved_query_id` for saved results. Run controls: `update_plan(run_id, ProspectingPlanSettings(plan_version=..., ...))` changes a proposed plan before approval. `contact_integration_id`, `validation_integration_id` and `search_provider_id` take an option id from the plan message's `contact_engine`, `company_check_engine` and `search_provider` (`data.contact_engine.options` and so on); omitted fields keep their choice, `search_provider_id="none"` skips web research, and `max_spend_usd` sets the most the run may spend on DiscoLike records and per-request fees, in USD at your plan's rates (`0` removes the limit; a plan with no per-record price is a 422). `max_provider_spend_usd` caps what the run may spend on your own AI and search provider keys, in USD (`0` removes it); once reached the run starts no new work and batches already running finish, so the total can end slightly above it. `target_companies` and `contacts_per_company` resize the plan, and its open limits are re-derived around them. `deliverable` is `"leads"` (find people at the companies) or `"accounts"` (return the checked companies only); `goal` is `"leads"` (keep adding rounds until the contact target is met) or `"companies"` (take `target_companies` matching companies and work only those). The same `deliverable` and `goal` are optional on `ProspectingBrief` at start, both defaulting to `"leads"` server-side, and the run's `brief` reports them. The plan is re-estimated at a new `plan_version`, and that is the version to approve; the latest plan message is rewritten in place under the same `seq`, so read it from the returned run's `messages` rather than past a `messages_after` cursor. An id outside the plan's options is a 422; a run that is not awaiting approval, a stale `plan_version`, or a run that cannot switch to `"accounts"` is a 409. With no engine chosen, contact research runs on DiscoLike Groove when you have a search provider and finds indexed contacts only when you have none. New `stop_reason` values, all plain strings: `candidate_limit` (the run reached its candidate cap; raise `max_candidates` or ask for fewer companies), `credit_limit` (the run stopped at its spending limit and kept its results), `provider_limit` (the run reached its AI provider spending limit), `companies_worked` (a `goal="companies"` run worked through the companies it was asked to take), and `candidates_exhausted` now means only that the search ran dry. A run reports `provider_cost_usd`, what it has spent so far on your AI and search provider keys as the providers report it (`None` when it recorded none; a custom AI endpoint reports no price), and `pipeline_phase`, `"companies"` while a phased run checks companies and `"people"` while it finds people at them (`None` for runs that interleave both). diff --git a/packages/discolike-cli/README.md b/packages/discolike-cli/README.md index 7c40e7e..39a5a34 100644 --- a/packages/discolike-cli/README.md +++ b/packages/discolike-cli/README.md @@ -67,7 +67,7 @@ discolike prospecting cancel RUN_ID `start` and `approve` default to pausing at checkpoints, like the web chat: a pilot check on large lists (`pilot`), a search drifting off target (`tail_quality`), candidates running out short of the target (`short`), the target being met (`target_reached`), and another round of companies when the ones checked yield too few contacts (`top_up`). Pass `--auto` to never pause (except at a `top_up` round that would pass a spending limit); a poor pilot is then sharpened once and the run continues with a notice, stopping with `pilot_failed` only if the re-pilot fit is still under 20%. If sharpening itself fails, the run continues on the original criteria. On a terminal, `wait` shows the question, any sample companies and numbered replies at a checkpoint, sends your pick or your own text, and keeps waiting. Without a terminal, or with `--no-input`, it prints the run on stdout, a `needs_input` envelope (`message`, `stop_reason`, `suggested_replies`, `sample`) on stderr, and exits 7; answer with `prospecting message --text ""` and run `wait` again. -Omit `--target-companies` and `--contacts-per-company` to infer counts from the brief (fallback 1,000 and 1; up to 10 contacts per company). Explicit values override the text. `--deliverable accounts` returns the checked companies only, with no people; `--goal companies` takes `--target-companies` matching companies and works only those instead of adding rounds until the contact target is met. Both default to `leads`. `--max-candidates` and `--max-actions` are automatic when omitted or `0`; their maxima are 100,000 and 10,000. Targets allow up to 10,000 companies, status pages up to 500 rows, and lists up to 50 runs. Work caps do not cap provider charges. +Omit `--target-companies` and `--contacts-per-company` to infer counts from the brief (fallback 1,000 and 3; up to 10 contacts per company). Explicit values override the text. `--deliverable accounts` returns the checked companies only, with no people; `--goal companies` takes `--target-companies` matching companies and works only those instead of adding rounds until the contact target is met. Both default to `leads`. `--max-candidates` and `--max-actions` are automatic when omitted or `0`; their maxima are 100,000 and 10,000. Targets allow up to 10,000 companies, status pages up to 500 rows, and lists up to 50 runs. Work caps do not cap provider charges. ### Conventions diff --git a/packages/discolike-cli/src/discolike_cli/prospecting.py b/packages/discolike-cli/src/discolike_cli/prospecting.py index 4dcf559..d226dec 100644 --- a/packages/discolike-cli/src/discolike_cli/prospecting.py +++ b/packages/discolike-cli/src/discolike_cli/prospecting.py @@ -187,7 +187,7 @@ def start_command( "--contacts-per-company", min=1, max=10, - help="Override contacts per company; otherwise inferred, default 1.", + help="Override contacts per company; otherwise inferred, default 3.", ), max_candidates: int | None = typer.Option( None, "--max-candidates", min=0, max=100000, help="Candidate work cap; omitted or 0 means automatic." diff --git a/packages/discolike/src/discolike/_generated/requests.py b/packages/discolike/src/discolike/_generated/requests.py index 6935a6b..0acb55b 100644 --- a/packages/discolike/src/discolike/_generated/requests.py +++ b/packages/discolike/src/discolike/_generated/requests.py @@ -3208,7 +3208,7 @@ class ProspectingBrief(DiscolikeRequest): ] = None exclude_domains: Annotated[list[str] | None, Field(max_length=1000, title="Exclude Domains")] = None target_companies: Annotated[int | None, Field(ge=1, le=10000, title="Target Companies")] = 1000 - contacts_per_company: Annotated[int | None, Field(ge=1, le=10, title="Contacts Per Company")] = 1 + contacts_per_company: Annotated[int | None, Field(ge=1, le=10, title="Contacts Per Company")] = 3 max_candidates: Annotated[int | None, Field(ge=0, le=100000, title="Max Candidates")] = 0 max_actions: Annotated[int | None, Field(ge=0, le=10000, title="Max Actions")] = 0 validation_integration_id: Annotated[str | None, Field(max_length=128, title="Validation Integration Id")] = None diff --git a/packages/discolike/tests/test_prospecting.py b/packages/discolike/tests/test_prospecting.py index 495258e..92481f9 100644 --- a/packages/discolike/tests/test_prospecting.py +++ b/packages/discolike/tests/test_prospecting.py @@ -417,7 +417,7 @@ def test_request_defaults_preserve_explicit_quantity_intent() -> None: explicit = ProspectingBrief( brief=implicit.brief, target_companies=25, contacts_per_company=2, max_actions=0, max_candidates=0 ) - assert (implicit.target_companies, implicit.contacts_per_company) == (1000, 1) + assert (implicit.target_companies, implicit.contacts_per_company) == (1000, 3) assert implicit.to_wire() == {"brief": implicit.brief} assert explicit.to_wire() == { "brief": implicit.brief,