autotouch-cli 0.2.31__tar.gz → 0.2.32__tar.gz

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.
Files changed (24) hide show
  1. {autotouch_cli-0.2.31/autotouch_cli.egg-info → autotouch_cli-0.2.32}/PKG-INFO +36 -3
  2. {autotouch_cli-0.2.31 → autotouch_cli-0.2.32}/autotouch_cli/cli.py +28 -178
  3. autotouch_cli-0.2.31/docs/research-table/reference/autotouch-cli.md → autotouch_cli-0.2.32/autotouch_cli.egg-info/PKG-INFO +44 -2
  4. {autotouch_cli-0.2.31 → autotouch_cli-0.2.32}/autotouch_cli.egg-info/SOURCES.txt +2 -0
  5. autotouch_cli-0.2.32/autotouch_cli.egg-info/top_level.txt +2 -0
  6. autotouch_cli-0.2.32/autotouch_shared/__init__.py +1 -0
  7. autotouch_cli-0.2.32/autotouch_shared/provider_registry.py +768 -0
  8. autotouch_cli-0.2.31/PKG-INFO → autotouch_cli-0.2.32/docs/research-table/reference/autotouch-cli.md +35 -11
  9. {autotouch_cli-0.2.31 → autotouch_cli-0.2.32}/pyproject.toml +2 -2
  10. autotouch_cli-0.2.31/autotouch_cli.egg-info/top_level.txt +0 -1
  11. {autotouch_cli-0.2.31 → autotouch_cli-0.2.32}/autotouch_cli/__init__.py +0 -0
  12. {autotouch_cli-0.2.31 → autotouch_cli-0.2.32}/autotouch_cli.egg-info/dependency_links.txt +0 -0
  13. {autotouch_cli-0.2.31 → autotouch_cli-0.2.32}/autotouch_cli.egg-info/entry_points.txt +0 -0
  14. {autotouch_cli-0.2.31 → autotouch_cli-0.2.32}/autotouch_cli.egg-info/requires.txt +0 -0
  15. {autotouch_cli-0.2.31 → autotouch_cli-0.2.32}/setup.cfg +0 -0
  16. {autotouch_cli-0.2.31 → autotouch_cli-0.2.32}/tests/test_column_prompt_compiler.py +0 -0
  17. {autotouch_cli-0.2.31 → autotouch_cli-0.2.32}/tests/test_contactout_custom.py +0 -0
  18. {autotouch_cli-0.2.31 → autotouch_cli-0.2.32}/tests/test_contactout_integration.py +0 -0
  19. {autotouch_cli-0.2.31 → autotouch_cli-0.2.32}/tests/test_contactout_multi_titles.py +0 -0
  20. {autotouch_cli-0.2.31 → autotouch_cli-0.2.32}/tests/test_contactout_pipeline.py +0 -0
  21. {autotouch_cli-0.2.31 → autotouch_cli-0.2.32}/tests/test_contactout_simple.py +0 -0
  22. {autotouch_cli-0.2.31 → autotouch_cli-0.2.32}/tests/test_contactout_v2_bulk.py +0 -0
  23. {autotouch_cli-0.2.31 → autotouch_cli-0.2.32}/tests/test_lead_required_fields.py +0 -0
  24. {autotouch_cli-0.2.31 → autotouch_cli-0.2.32}/tests/test_phone_provider_pipeline.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: autotouch-cli
3
- Version: 0.2.31
3
+ Version: 0.2.32
4
4
  Summary: Autotouch Smart Table CLI
5
5
  Requires-Python: >=3.9
6
6
  Description-Content-Type: text/markdown
@@ -515,8 +515,8 @@ autotouch columns create --table-id <TABLE_ID> --data-file <payload.json>
515
515
  ```
516
516
 
517
517
  Recommendation:
518
- - For provider-backed workflow columns, start with `autotouch columns recipe`:
519
- `add_to_crm`, `sync_to_table`, `add_to_sequence`.
518
+ - For provider-backed or generated columns, start with `autotouch columns recipe`.
519
+ - Built-in recipe types now include `formatter`, `llm_enrichment`, `email_finder`, `phone_finder`, `lead_finder`, `add_to_crm`, `sync_to_table`, and `add_to_sequence`.
520
520
  - Email/phone enrichment does not require creating `add_to_crm` first.
521
521
  - `add_to_crm` is an optional, non-billable export action.
522
522
 
@@ -781,6 +781,7 @@ Notes:
781
781
  Add-to-Leads note: `companyDomain` is required; LinkedIn is optional. Each row still needs at least one usable hard identity signal (LinkedIn, email, or phone). Names and location fields are metadata only for this flow. Run is non-billable.
782
782
  If `companyDomain` is missing in the table, derive or enrich that domain column first, then rerun `add_to_crm`.
783
783
  Queued lifecycle: the CLI/API accepts the run immediately, then the backend hands off `ops -> data_io` for execution.
784
+ Creating `add_to_crm` after upstream email/phone/lead columns already finished does not replay those older updates. In that case, run `add_to_crm` explicitly or rerun the upstream source column.
784
785
 
785
786
  CRM data model expectations (recommended before `add_to_crm`):
786
787
  - Lead identity/dedupe expects `company_domain` plus one usable hard identity signal. `linkedin_url` is a strong optional signal, not a requirement.
@@ -819,6 +820,14 @@ CRM data model expectations (recommended before `add_to_crm`):
819
820
  Notes:
820
821
  - Single-destination mode uses `destinationTableId` + `columnMappings`.
821
822
  - Router mode is also supported with `config.routes[]` (top-to-bottom matching, first hit wins, default route catches unmatched rows).
823
+ - Route conditions are evaluated against the parent/source row, not against each list item.
824
+ - `autoRun: "onSourceUpdate"` means dependency-driven runs from source row inserts/updates. Saving the column does not backfill existing rows for `sync_to_table`; use an explicit column run/backfill when needed.
825
+ - List mode uses `syncMode: "list"` with `listSourceColumnId` and optional `listPath` (default `items`) to expand a canonical JSON payload like `{ "items": [...], "reasoning": "..." }`.
826
+ - In list mode:
827
+ - `sourceScope: "item"` maps fields from each `items[]` object.
828
+ - `sourceScope: "row"` maps fields from the parent/source row.
829
+ - Explicit item mappings are still recommended for renamed fields such as `name -> full_name`.
830
+ - Blank placeholder list items are skipped and do not create destination rows.
822
831
 
823
832
  ### 8) `add_to_sequence`
824
833
 
@@ -844,10 +853,33 @@ Notes:
844
853
  - `sourceLeadColumn` must be the source column key that stores lead IDs (for example `add_to_leads` or another lead-id column key from `lead_finder` output), not the provider name `add_to_crm`.
845
854
  - `sequenceId` is the target sequence workflow ID.
846
855
  - The target sequence must already be `ACTIVE` for real enrollment. Table `add_to_sequence` runs and direct `POST /api/sequences/{id}/enroll` share the same activation check.
856
+ - Common tail order after contact rows or lead IDs exist is `email_finder -> add_to_crm -> create/activate sequence -> add_to_sequence`.
857
+ - Creating `add_to_sequence` after lead IDs already exist does not replay those older updates. In that case, run `add_to_sequence` explicitly or rerun the lead-id source column.
847
858
  - `add_to_sequence` runs auto-attach `research_context.source_table_id` (and table name when available). Field selection stays implicit so sequence drafts/audience resolve favorites from current starred columns by default.
848
859
  - Star/favorite the highest-signal fields so callers can see them quickly in the sidecar during live call workflows.
849
860
  - The same favorite set is also the default AI drafting context when explicit `fieldIds` are not provided.
850
861
 
862
+ ## Example pipeline: LLM items -> contacts table -> leads -> sequence
863
+
864
+ Use this when an LLM column returns canonical JSON like `{ "items": [...] }` and each item should become a contact row.
865
+ This is an example pattern, not the default recommendation for all person discovery.
866
+ Use `lead_finder` first when the goal is explicit buyer/lead discovery with clear role targeting at scale.
867
+ Use agent `llm_enrichment` when the workflow needs custom research output, website-discovered contacts, or low-coverage / hard-to-find cases.
868
+
869
+ 1. Create the source table and import company rows.
870
+ 2. Create the LLM enrichment column that outputs `items[]`.
871
+ 3. Create `sync_to_table` in `list` mode:
872
+ - set `listSourceColumnId` to the LLM column ID
873
+ - keep item fields on `sourceScope: "item"`
874
+ - keep parent company metadata on `sourceScope: "row"`
875
+ 4. Run or wait for `sync_to_table` so the destination contacts table has rows.
876
+ 5. On the destination contacts table, create and run `email_finder`.
877
+ 6. Create `add_to_crm` on that same destination contacts table.
878
+ 7. If email finder already finished before `add_to_crm` existed, run `add_to_crm` explicitly.
879
+ 8. Create the target sequence and set it `ACTIVE`.
880
+ 9. Create `add_to_sequence` with `sourceLeadColumn` set to the lead-id column key, usually `add_to_leads`.
881
+ 10. If leads already existed before `add_to_sequence` was created, run `add_to_sequence` explicitly.
882
+
851
883
  ## Common workflow
852
884
 
853
885
  ```bash
@@ -1159,6 +1191,7 @@ Agent expectations:
1159
1191
  - `column_types` tells you which column types are runnable and which are non-billable transforms (`json_split`, `formatter_formula`).
1160
1192
  - `filtering` describes valid scope/filter semantics for estimate/run.
1161
1193
  - `automation.auto_run` describes supported auto-run modes + config field names.
1194
+ - `automation.providers` exposes provider-specific setup contracts, save-time backfill behavior, dispatch strategy, and recipe type names for formatter/LLM/finders/action columns.
1162
1195
  - `execution_policies.llm.output_contract` describes output behavior by mode:
1163
1196
  - `agent` => JSON-oriented structured output
1164
1197
  - `basic` => text or JSON (`dataType=json` for structured JSON output)
@@ -34,6 +34,10 @@ from pathlib import Path
34
34
  from typing import Any, Dict, List, Optional, Tuple
35
35
 
36
36
  import requests
37
+ from autotouch_shared.provider_registry import (
38
+ get_recipe_contract,
39
+ recipe_types,
40
+ )
37
41
 
38
42
  try:
39
43
  from pymongo import MongoClient # type: ignore
@@ -82,186 +86,24 @@ SETUP_BANNER = r"""
82
86
  AI CLI SETUP
83
87
  """
84
88
 
85
- COLUMN_RECIPE_TYPES = [
86
- "email_finder",
87
- "phone_finder",
88
- "lead_finder",
89
- "llm_enrichment",
90
- "add_to_crm",
91
- "sync_to_table",
92
- "add_to_sequence",
93
- ]
89
+ COLUMN_RECIPE_TYPES = recipe_types()
94
90
 
95
- COLUMN_CREATE_RECIPES: Dict[str, Dict[str, Any]] = {
96
- "email_finder": {
97
- "key": "work_email",
98
- "label": "Work Email",
99
- "kind": "enrichment",
100
- "dataType": "json",
101
- "origin": "email_finder",
102
- "autoRun": "never",
103
- "config": {
104
- "provider": "smart_email_finder",
105
- "strategy": "cost_optimized",
106
- "lookupStrategy": "linkedin",
107
- "linkedinOnly": True,
108
- "enableProfileFallback": False,
109
- "linkedinUrl": "linkedin_url",
110
- },
111
- },
112
- "phone_finder": {
113
- "key": "mobile_phone",
114
- "label": "Mobile Phone",
115
- "kind": "enrichment",
116
- "dataType": "json",
117
- "origin": "phone_finder",
118
- "autoRun": "never",
119
- "config": {
120
- "provider": "smart_phone_finder",
121
- "firstName": "first_name",
122
- "lastName": "last_name",
123
- "company": "company",
124
- "linkedinUrl": "linkedin_url",
125
- },
126
- },
127
- "lead_finder": {
128
- "key": "lead_contacts",
129
- "label": "Lead Contacts",
130
- "kind": "enrichment",
131
- "dataType": "json",
132
- "origin": "ai",
133
- "autoRun": "never",
134
- "config": {
135
- "provider": "lead_finder",
136
- "sourceMode": "bulk_companies",
137
- "companyDomain": "domain",
138
- "strictness": "flexible_roles",
139
- "storeAsLeads": True,
140
- "autoEnrichEmails": True,
141
- "autoEnrichPhones": False,
142
- "jobTitles": ["Head of Sales", "VP Sales"],
143
- "locations": ["United States"],
144
- "maxResults": 10,
145
- },
146
- },
147
- "llm_enrichment": {
148
- "key": "company_research",
149
- "label": "Company Research",
150
- "kind": "enrichment",
151
- "dataType": "json",
152
- "origin": "ai",
153
- "autoRun": "never",
154
- "config": {
155
- "instructions": "Research the company in this row and return JSON with icp_fit, summary, and risks.",
156
- "mode": "agent",
157
- "temperature": 0.7,
158
- "batchSize": 50,
159
- "promptSource": "generated",
160
- "useCompanyContext": True,
161
- "structuredOutput": True,
162
- "useAutoSchema": True,
163
- },
164
- },
165
- "add_to_crm": {
166
- "key": "add_to_leads",
167
- "label": "Add to Leads",
168
- "kind": "enrichment",
169
- "dataType": "json",
170
- "origin": "manual",
171
- "autoRun": "onSourceUpdate",
172
- "config": {
173
- "provider": "add_to_crm",
174
- "leadSource": "research_table_export",
175
- "fieldMappings": {
176
- "mode": "single",
177
- "linkedinUrl": "linkedin_url",
178
- "companyDomain": "domain",
179
- "firstName": "first_name",
180
- "lastName": "last_name",
181
- "title": "title",
182
- "companyName": "company",
183
- "emailAddresses": [{"column": "work_email", "type": "work"}],
184
- "phoneNumbers": [{"column": "mobile_phone", "type": "mobile"}],
185
- },
186
- "sourceColumns": [
187
- "linkedin_url",
188
- "domain",
189
- "first_name",
190
- "last_name",
191
- "title",
192
- "company",
193
- "work_email",
194
- "mobile_phone",
195
- ],
196
- },
197
- },
198
- "sync_to_table": {
199
- "key": "sync_to_table",
200
- "label": "Sync to Table",
201
- "kind": "enrichment",
202
- "dataType": "json",
203
- "origin": "manual",
204
- "autoRun": "onSourceUpdate",
205
- "config": {
206
- "provider": "sync_to_table",
207
- "destinationTableId": "<DESTINATION_TABLE_ID>",
208
- "columnMappings": [
209
- {"sourceKey": "company", "destKey": "company"},
210
- {"sourceKey": "domain", "destKey": "company_domain"},
211
- {"sourceKey": "work_email", "destKey": "work_email"},
212
- ],
213
- },
214
- },
215
- "add_to_sequence": {
216
- "key": "add_to_sequence",
217
- "label": "Add to Sequence",
218
- "kind": "enrichment",
219
- "dataType": "json",
220
- "origin": "manual",
221
- "autoRun": "onSourceUpdate",
222
- "config": {
223
- "provider": "add_to_sequence",
224
- "sequenceId": "<SEQUENCE_ID>",
225
- "sourceLeadColumn": "add_to_leads",
226
- },
227
- },
228
- }
229
91
 
230
- COLUMN_RECIPE_NOTES: Dict[str, List[str]] = {
231
- "email_finder": [
232
- "For non-LinkedIn lookup, use lookupStrategy=domain/company with firstName+lastName+domain/company fields.",
233
- ],
234
- "phone_finder": [
235
- "You can use linkedinUrl or email/workEmail/personalEmail inputs.",
236
- ],
237
- "lead_finder": [
238
- "Add more targeting keys as needed (jobFunctions, seniorities, keywords, excludeKeywords, skills).",
239
- ],
240
- "llm_enrichment": [
241
- "Recommended default for agent mode: provide instructions and let the API compile the runnable prompt.",
242
- "Generated agent-mode prompts always include company/requester context.",
243
- "Basic mode stays manual-prompt-first; write the prompt directly from the user goal/preferences.",
244
- "Runtime only injects values for placeholders the prompt explicitly references.",
245
- "Manual/basic prompts should use explicit placeholders like {{company_name}}, {{domain}}, or {{user_value_proposition}} when those values are needed.",
246
- "Agent mode is JSON-oriented.",
247
- "Basic mode can be text or JSON; JSON requires dataType=json.",
248
- ],
249
- "add_to_crm": [
250
- "companyDomain is required; LinkedIn is optional.",
251
- "Include at least one usable hard-identity mapping such as LinkedIn, email, or phone.",
252
- "If companyDomain is missing in the table, derive or enrich that domain column first.",
253
- "Add to CRM is optional and non-billable.",
254
- ],
255
- "sync_to_table": [
256
- "Use destinationTableId + columnMappings for single-destination sync.",
257
- "Router mode is supported via config.routes[] (first matching route wins).",
258
- ],
259
- "add_to_sequence": [
260
- "sourceLeadColumn should reference the source column key that stores lead IDs (for example add_to_leads), not the provider name add_to_crm.",
261
- "Set sequenceId to the target workflow sequence ID.",
262
- "The target sequence must already be ACTIVE for real enrollment.",
263
- ],
264
- }
92
+ def _build_column_recipe_catalog() -> Tuple[Dict[str, Dict[str, Any]], Dict[str, List[str]]]:
93
+ recipes: Dict[str, Dict[str, Any]] = {}
94
+ notes: Dict[str, List[str]] = {}
95
+ for recipe_type in COLUMN_RECIPE_TYPES:
96
+ contract = get_recipe_contract(recipe_type)
97
+ if not contract:
98
+ continue
99
+ payload = contract.recipe_payload_copy()
100
+ if isinstance(payload, dict):
101
+ recipes[recipe_type] = payload
102
+ notes[recipe_type] = contract.recipe_notes_list()
103
+ return recipes, notes
104
+
105
+
106
+ COLUMN_CREATE_RECIPES, COLUMN_RECIPE_NOTES = _build_column_recipe_catalog()
265
107
 
266
108
  SEQUENCE_RECIPE_TYPES = [
267
109
  "create",
@@ -3589,6 +3431,8 @@ def cmd_capabilities(args: argparse.Namespace) -> None:
3589
3431
  output_contract = llm.get("output_contract") if isinstance(llm.get("output_contract"), dict) else {}
3590
3432
  agent_contract = output_contract.get("agent") if isinstance(output_contract.get("agent"), dict) else {}
3591
3433
  basic_contract = output_contract.get("basic") if isinstance(output_contract.get("basic"), dict) else {}
3434
+ automation = data.get("automation") if isinstance(data.get("automation"), dict) else {}
3435
+ providers = automation.get("providers") if isinstance(automation.get("providers"), dict) else {}
3592
3436
 
3593
3437
  print(f"API version : {_as_display(data.get('api_version'))}")
3594
3438
  print(f"Base URL : {_as_display(data.get('base_url'))}")
@@ -3612,7 +3456,13 @@ def cmd_capabilities(args: argparse.Namespace) -> None:
3612
3456
  basic_types = basic_contract.get("supported_data_types")
3613
3457
  basic_types_str = ", ".join(str(t) for t in basic_types) if isinstance(basic_types, list) else "text/json"
3614
3458
  print(f"basic : {basic_types_str} (JSON requires dataType=json)")
3459
+ if providers:
3460
+ print("")
3461
+ print("Provider setup")
3462
+ print("--------------")
3463
+ print(", ".join(sorted(str(name) for name in providers.keys())))
3615
3464
  print("")
3465
+ print("Tip: inspect automation.providers in JSON output for provider-specific setup contracts.")
3616
3466
  print("Tip: for JSON enrichment columns, run with `--wait` and verify terminal status before `autotouch columns projections`.")
3617
3467
  return
3618
3468
 
@@ -1,3 +1,12 @@
1
+ Metadata-Version: 2.4
2
+ Name: autotouch-cli
3
+ Version: 0.2.32
4
+ Summary: Autotouch Smart Table CLI
5
+ Requires-Python: >=3.9
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: requests>=2.31.0
8
+ Requires-Dist: python-dotenv>=1.0.0
9
+
1
10
  # Autotouch CLI Reference (`autotouch`)
2
11
 
3
12
  This page documents the installable CLI for the Smart Table developer API.
@@ -506,8 +515,8 @@ autotouch columns create --table-id <TABLE_ID> --data-file <payload.json>
506
515
  ```
507
516
 
508
517
  Recommendation:
509
- - For provider-backed workflow columns, start with `autotouch columns recipe`:
510
- `add_to_crm`, `sync_to_table`, `add_to_sequence`.
518
+ - For provider-backed or generated columns, start with `autotouch columns recipe`.
519
+ - Built-in recipe types now include `formatter`, `llm_enrichment`, `email_finder`, `phone_finder`, `lead_finder`, `add_to_crm`, `sync_to_table`, and `add_to_sequence`.
511
520
  - Email/phone enrichment does not require creating `add_to_crm` first.
512
521
  - `add_to_crm` is an optional, non-billable export action.
513
522
 
@@ -772,6 +781,7 @@ Notes:
772
781
  Add-to-Leads note: `companyDomain` is required; LinkedIn is optional. Each row still needs at least one usable hard identity signal (LinkedIn, email, or phone). Names and location fields are metadata only for this flow. Run is non-billable.
773
782
  If `companyDomain` is missing in the table, derive or enrich that domain column first, then rerun `add_to_crm`.
774
783
  Queued lifecycle: the CLI/API accepts the run immediately, then the backend hands off `ops -> data_io` for execution.
784
+ Creating `add_to_crm` after upstream email/phone/lead columns already finished does not replay those older updates. In that case, run `add_to_crm` explicitly or rerun the upstream source column.
775
785
 
776
786
  CRM data model expectations (recommended before `add_to_crm`):
777
787
  - Lead identity/dedupe expects `company_domain` plus one usable hard identity signal. `linkedin_url` is a strong optional signal, not a requirement.
@@ -810,6 +820,14 @@ CRM data model expectations (recommended before `add_to_crm`):
810
820
  Notes:
811
821
  - Single-destination mode uses `destinationTableId` + `columnMappings`.
812
822
  - Router mode is also supported with `config.routes[]` (top-to-bottom matching, first hit wins, default route catches unmatched rows).
823
+ - Route conditions are evaluated against the parent/source row, not against each list item.
824
+ - `autoRun: "onSourceUpdate"` means dependency-driven runs from source row inserts/updates. Saving the column does not backfill existing rows for `sync_to_table`; use an explicit column run/backfill when needed.
825
+ - List mode uses `syncMode: "list"` with `listSourceColumnId` and optional `listPath` (default `items`) to expand a canonical JSON payload like `{ "items": [...], "reasoning": "..." }`.
826
+ - In list mode:
827
+ - `sourceScope: "item"` maps fields from each `items[]` object.
828
+ - `sourceScope: "row"` maps fields from the parent/source row.
829
+ - Explicit item mappings are still recommended for renamed fields such as `name -> full_name`.
830
+ - Blank placeholder list items are skipped and do not create destination rows.
813
831
 
814
832
  ### 8) `add_to_sequence`
815
833
 
@@ -835,10 +853,33 @@ Notes:
835
853
  - `sourceLeadColumn` must be the source column key that stores lead IDs (for example `add_to_leads` or another lead-id column key from `lead_finder` output), not the provider name `add_to_crm`.
836
854
  - `sequenceId` is the target sequence workflow ID.
837
855
  - The target sequence must already be `ACTIVE` for real enrollment. Table `add_to_sequence` runs and direct `POST /api/sequences/{id}/enroll` share the same activation check.
856
+ - Common tail order after contact rows or lead IDs exist is `email_finder -> add_to_crm -> create/activate sequence -> add_to_sequence`.
857
+ - Creating `add_to_sequence` after lead IDs already exist does not replay those older updates. In that case, run `add_to_sequence` explicitly or rerun the lead-id source column.
838
858
  - `add_to_sequence` runs auto-attach `research_context.source_table_id` (and table name when available). Field selection stays implicit so sequence drafts/audience resolve favorites from current starred columns by default.
839
859
  - Star/favorite the highest-signal fields so callers can see them quickly in the sidecar during live call workflows.
840
860
  - The same favorite set is also the default AI drafting context when explicit `fieldIds` are not provided.
841
861
 
862
+ ## Example pipeline: LLM items -> contacts table -> leads -> sequence
863
+
864
+ Use this when an LLM column returns canonical JSON like `{ "items": [...] }` and each item should become a contact row.
865
+ This is an example pattern, not the default recommendation for all person discovery.
866
+ Use `lead_finder` first when the goal is explicit buyer/lead discovery with clear role targeting at scale.
867
+ Use agent `llm_enrichment` when the workflow needs custom research output, website-discovered contacts, or low-coverage / hard-to-find cases.
868
+
869
+ 1. Create the source table and import company rows.
870
+ 2. Create the LLM enrichment column that outputs `items[]`.
871
+ 3. Create `sync_to_table` in `list` mode:
872
+ - set `listSourceColumnId` to the LLM column ID
873
+ - keep item fields on `sourceScope: "item"`
874
+ - keep parent company metadata on `sourceScope: "row"`
875
+ 4. Run or wait for `sync_to_table` so the destination contacts table has rows.
876
+ 5. On the destination contacts table, create and run `email_finder`.
877
+ 6. Create `add_to_crm` on that same destination contacts table.
878
+ 7. If email finder already finished before `add_to_crm` existed, run `add_to_crm` explicitly.
879
+ 8. Create the target sequence and set it `ACTIVE`.
880
+ 9. Create `add_to_sequence` with `sourceLeadColumn` set to the lead-id column key, usually `add_to_leads`.
881
+ 10. If leads already existed before `add_to_sequence` was created, run `add_to_sequence` explicitly.
882
+
842
883
  ## Common workflow
843
884
 
844
885
  ```bash
@@ -1150,6 +1191,7 @@ Agent expectations:
1150
1191
  - `column_types` tells you which column types are runnable and which are non-billable transforms (`json_split`, `formatter_formula`).
1151
1192
  - `filtering` describes valid scope/filter semantics for estimate/run.
1152
1193
  - `automation.auto_run` describes supported auto-run modes + config field names.
1194
+ - `automation.providers` exposes provider-specific setup contracts, save-time backfill behavior, dispatch strategy, and recipe type names for formatter/LLM/finders/action columns.
1153
1195
  - `execution_policies.llm.output_contract` describes output behavior by mode:
1154
1196
  - `agent` => JSON-oriented structured output
1155
1197
  - `basic` => text or JSON (`dataType=json` for structured JSON output)
@@ -7,6 +7,8 @@ autotouch_cli.egg-info/dependency_links.txt
7
7
  autotouch_cli.egg-info/entry_points.txt
8
8
  autotouch_cli.egg-info/requires.txt
9
9
  autotouch_cli.egg-info/top_level.txt
10
+ autotouch_shared/__init__.py
11
+ autotouch_shared/provider_registry.py
10
12
  docs/research-table/reference/autotouch-cli.md
11
13
  tests/test_column_prompt_compiler.py
12
14
  tests/test_contactout_custom.py
@@ -0,0 +1,2 @@
1
+ autotouch_cli
2
+ autotouch_shared
@@ -0,0 +1 @@
1
+ """Shared Autotouch contracts and metadata."""