@oxygen-agent/cli 1.936.1 → 1.982.3
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.
- package/README.md +1 -1
- package/dist/admin-primary-providers-render.js +9 -1
- package/dist/cli-values.d.ts +14 -0
- package/dist/cli-values.js +26 -0
- package/dist/command-manifest.js +30 -2
- package/dist/functions-commands.js +13 -5
- package/dist/help.js +2 -0
- package/dist/index.js +1509 -290
- package/dist/knowledge-repository-commands.d.ts +6 -0
- package/dist/knowledge-repository-commands.js +198 -0
- package/dist/skills.js +20 -0
- package/dist/ugc-commands.js +470 -15
- package/node_modules/@oxygen/recipe-sdk/dist/index.d.ts +2 -0
- package/node_modules/@oxygen/shared/dist/byok-connect.d.ts +11 -6
- package/node_modules/@oxygen/shared/dist/byok-connect.js +14 -6
- package/node_modules/@oxygen/shared/dist/capability-discovery.d.ts +8 -0
- package/node_modules/@oxygen/shared/dist/capability-discovery.js +152 -20
- package/node_modules/@oxygen/shared/dist/copilot-errors.js +3 -0
- package/node_modules/@oxygen/shared/dist/copilot-journeys.d.ts +19 -1
- package/node_modules/@oxygen/shared/dist/copilot-journeys.generated.d.ts +19 -0
- package/node_modules/@oxygen/shared/dist/copilot-journeys.generated.js +26 -0
- package/node_modules/@oxygen/shared/dist/copilot-journeys.js +8 -41
- package/node_modules/@oxygen/shared/dist/email-dsn.d.ts +60 -0
- package/node_modules/@oxygen/shared/dist/email-dsn.js +120 -0
- package/node_modules/@oxygen/shared/dist/email-warmup-readiness.d.ts +64 -0
- package/node_modules/@oxygen/shared/dist/email-warmup-readiness.js +90 -0
- package/node_modules/@oxygen/shared/dist/inbox-avatar-url.d.ts +28 -0
- package/node_modules/@oxygen/shared/dist/inbox-avatar-url.js +57 -0
- package/node_modules/@oxygen/shared/dist/index.d.ts +10 -0
- package/node_modules/@oxygen/shared/dist/index.js +10 -0
- package/node_modules/@oxygen/shared/dist/knowledge-bases.d.ts +74 -0
- package/node_modules/@oxygen/shared/dist/knowledge-bases.js +456 -0
- package/node_modules/@oxygen/shared/dist/knowledge-bootstrap.d.ts +56 -48
- package/node_modules/@oxygen/shared/dist/knowledge-bootstrap.js +50 -49
- package/node_modules/@oxygen/shared/dist/knowledge-repository.d.ts +22 -0
- package/node_modules/@oxygen/shared/dist/knowledge-repository.js +121 -0
- package/node_modules/@oxygen/shared/dist/knowledge-vault-markdown.d.ts +20 -0
- package/node_modules/@oxygen/shared/dist/knowledge-vault-markdown.js +155 -0
- package/node_modules/@oxygen/shared/dist/langfuse.d.ts +8 -3
- package/node_modules/@oxygen/shared/dist/langfuse.js +177 -130
- package/node_modules/@oxygen/shared/dist/llm-payload.d.ts +10 -0
- package/node_modules/@oxygen/shared/dist/llm-payload.js +54 -0
- package/node_modules/@oxygen/shared/dist/llm-usage.d.ts +11 -0
- package/node_modules/@oxygen/shared/dist/llm-usage.js +30 -0
- package/node_modules/@oxygen/shared/dist/mailbox-import.d.ts +10 -0
- package/node_modules/@oxygen/shared/dist/mailbox-import.js +53 -0
- package/node_modules/@oxygen/shared/dist/plan-limits.d.ts +8 -0
- package/node_modules/@oxygen/shared/dist/plan-limits.js +8 -0
- package/node_modules/@oxygen/shared/dist/pricing-sheet.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/pricing-sheet.js +1 -1
- package/node_modules/@oxygen/shared/dist/product-analytics-core.d.ts +98 -0
- package/node_modules/@oxygen/shared/dist/product-analytics-core.js +159 -0
- package/node_modules/@oxygen/shared/dist/product-analytics-environment.d.ts +18 -0
- package/node_modules/@oxygen/shared/dist/product-analytics-environment.js +46 -0
- package/node_modules/@oxygen/shared/dist/product-analytics-events.d.ts +116 -0
- package/node_modules/@oxygen/shared/dist/product-analytics-events.js +120 -0
- package/node_modules/@oxygen/shared/dist/recipes.d.ts +6 -0
- package/node_modules/@oxygen/shared/dist/recipes.js +23 -0
- package/node_modules/@oxygen/shared/dist/sequences.d.ts +126 -2
- package/node_modules/@oxygen/shared/dist/sequences.js +280 -4
- package/node_modules/@oxygen/shared/dist/ugc-amplification-identity.d.ts +2 -0
- package/node_modules/@oxygen/shared/dist/ugc-amplification-identity.js +24 -0
- package/node_modules/@oxygen/shared/dist/ugc.d.ts +29 -1
- package/node_modules/@oxygen/shared/dist/user-capability-routing.js +8 -1
- package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.js +3 -1
- package/node_modules/@oxygen/shared/dist/workspace-file-storage.d.ts +6 -2
- package/node_modules/@oxygen/shared/dist/workspace-file-storage.js +15 -4
- package/node_modules/@oxygen/shared/package.json +15 -0
- package/node_modules/@oxygen/workflows/dist/graph/lint.js +22 -0
- package/package.json +2 -1
|
@@ -7,6 +7,8 @@ export type RecipeToolRunOptions = {
|
|
|
7
7
|
optional?: boolean;
|
|
8
8
|
/** Pin this call to one provider account for every replay/scheduled run. */
|
|
9
9
|
connectionId?: string;
|
|
10
|
+
/** Pin the credential rail instead of automatically selecting a connected key. */
|
|
11
|
+
credentialMode?: "managed" | "user_api_key" | "user_oauth";
|
|
10
12
|
};
|
|
11
13
|
export type RecipeToolApi = {
|
|
12
14
|
run: <T = unknown>(toolId: string, payload?: Record<string, unknown>, options?: RecipeToolRunOptions) => Promise<T>;
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
*
|
|
5
5
|
* WHY THIS EXISTS. `native_provider.execution.supports_byok` says a TOOL can run on
|
|
6
6
|
* a customer's own key. It does not say the customer has any way to give us one.
|
|
7
|
-
* Those are two different facts, and
|
|
7
|
+
* Those are two different facts, and 9 of the 42 byok-capable native providers
|
|
8
8
|
* have the first without the second:
|
|
9
9
|
*
|
|
10
|
-
* apify builtwith dropleads lusha openwebninja
|
|
10
|
+
* apify builtwith dropleads lusha openwebninja peopledatalabs
|
|
11
11
|
* rocketreach theirstack wiza
|
|
12
12
|
*
|
|
13
13
|
* For those, `oxygen integrations connect <id> --api-key` does not reach
|
|
@@ -17,11 +17,16 @@
|
|
|
17
17
|
* tool is native generic-REST and `shouldRouteStaticToolThroughComposio` is false, so
|
|
18
18
|
* `resolveOptionalApiKeyCredentials` finds nothing and the call falls back to the
|
|
19
19
|
* MANAGED key — the very key that was out of credits. The command appears to work,
|
|
20
|
-
* changes nothing, and the customer is back where they started with no error.
|
|
20
|
+
* changes nothing, and the customer is back where they started with no error. The
|
|
21
|
+
* connect route now refuses each of those nine with a typed `byok_connect_unavailable`
|
|
22
|
+
* rather than a silent Composio "success"; they stay OFF this list until one gains a
|
|
23
|
+
* real native connect form.
|
|
21
24
|
*
|
|
22
|
-
* That is precisely the failure Plain T-104
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
+
* That is precisely the failure Plain T-104 hit: `parallel.search` was one of the
|
|
26
|
+
* refused tools, and a refusal message that told Mentcape to connect their own Parallel
|
|
27
|
+
* key would once have sent them down a path that silently does nothing. Parallel now has
|
|
28
|
+
* a native api-key connect (catalog.ts) and IS on this list, so that message is finally
|
|
29
|
+
* true for it — the nine above are the ones it must still never name.
|
|
25
30
|
*
|
|
26
31
|
* So a message may only offer BYOK when BOTH halves hold, and this list is the second
|
|
27
32
|
* half. It lives in @oxygen/shared because the two sides that need it cannot see each
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
*
|
|
5
5
|
* WHY THIS EXISTS. `native_provider.execution.supports_byok` says a TOOL can run on
|
|
6
6
|
* a customer's own key. It does not say the customer has any way to give us one.
|
|
7
|
-
* Those are two different facts, and
|
|
7
|
+
* Those are two different facts, and 9 of the 42 byok-capable native providers
|
|
8
8
|
* have the first without the second:
|
|
9
9
|
*
|
|
10
|
-
* apify builtwith dropleads lusha openwebninja
|
|
10
|
+
* apify builtwith dropleads lusha openwebninja peopledatalabs
|
|
11
11
|
* rocketreach theirstack wiza
|
|
12
12
|
*
|
|
13
13
|
* For those, `oxygen integrations connect <id> --api-key` does not reach
|
|
@@ -17,11 +17,16 @@
|
|
|
17
17
|
* tool is native generic-REST and `shouldRouteStaticToolThroughComposio` is false, so
|
|
18
18
|
* `resolveOptionalApiKeyCredentials` finds nothing and the call falls back to the
|
|
19
19
|
* MANAGED key — the very key that was out of credits. The command appears to work,
|
|
20
|
-
* changes nothing, and the customer is back where they started with no error.
|
|
20
|
+
* changes nothing, and the customer is back where they started with no error. The
|
|
21
|
+
* connect route now refuses each of those nine with a typed `byok_connect_unavailable`
|
|
22
|
+
* rather than a silent Composio "success"; they stay OFF this list until one gains a
|
|
23
|
+
* real native connect form.
|
|
21
24
|
*
|
|
22
|
-
* That is precisely the failure Plain T-104
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
+
* That is precisely the failure Plain T-104 hit: `parallel.search` was one of the
|
|
26
|
+
* refused tools, and a refusal message that told Mentcape to connect their own Parallel
|
|
27
|
+
* key would once have sent them down a path that silently does nothing. Parallel now has
|
|
28
|
+
* a native api-key connect (catalog.ts) and IS on this list, so that message is finally
|
|
29
|
+
* true for it — the nine above are the ones it must still never name.
|
|
25
30
|
*
|
|
26
31
|
* So a message may only offer BYOK when BOTH halves hold, and this list is the second
|
|
27
32
|
* half. It lives in @oxygen/shared because the two sides that need it cannot see each
|
|
@@ -64,6 +69,9 @@ export const NATIVE_API_KEY_CONNECT_INTEGRATION_IDS = new Set([
|
|
|
64
69
|
// rest — so the native runner does find the key.
|
|
65
70
|
"moltsets",
|
|
66
71
|
"ocean",
|
|
72
|
+
// Native api-key connect added in catalog.ts (2026-09-10): connect writes an
|
|
73
|
+
// integration_secrets api_key row the native parallel.* runner reads. T-104.
|
|
74
|
+
"parallel",
|
|
67
75
|
"predictleads",
|
|
68
76
|
"prospeo",
|
|
69
77
|
"scalelist",
|
|
@@ -23,6 +23,12 @@ export type CapabilityRouteMatch = {
|
|
|
23
23
|
card: CapabilityRouteCard;
|
|
24
24
|
recommendedTools: string[];
|
|
25
25
|
recommendedCommands: string[];
|
|
26
|
+
/**
|
|
27
|
+
* The one exact invocation for asks whose answer is a single command with a
|
|
28
|
+
* specific flag the help text alone did not teach. Present only when the
|
|
29
|
+
* intent is that narrow; `recommendedCommands[0]` is the same command.
|
|
30
|
+
*/
|
|
31
|
+
exactCommand?: string;
|
|
26
32
|
};
|
|
27
33
|
export type PrimitiveRouteMatch = CapabilityRouteMatch & {
|
|
28
34
|
card: PrimitiveRouteCard;
|
|
@@ -34,3 +40,5 @@ export declare function getCapabilityRouteMatch(id: string): CapabilityRouteMatc
|
|
|
34
40
|
export declare function inferCapabilityRoute(query: string): CapabilityRouteMatch | null;
|
|
35
41
|
export declare function inferPrimitiveRoute(query: string): PrimitiveRouteMatch | null;
|
|
36
42
|
export declare function serializeCapabilityRoute(route: CapabilityRouteMatch | null): Record<string, unknown> | null;
|
|
43
|
+
/** The exact `companies search plan` invocation for the url_recovery intent. */
|
|
44
|
+
export declare const COMPANY_URL_RECOVERY_COMMAND = "oxygen companies search plan --prompt \"Find the websites for these companies: <company names>\" --source-intent url_recovery";
|
|
@@ -56,11 +56,11 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
56
56
|
primitive: null,
|
|
57
57
|
owns: "First-run setup and interactive Workspace Copilot sessions over the shared primitive contracts.",
|
|
58
58
|
notFor: "Recurring deterministic automation or standing adaptive jobs; use Workflows or Agents.",
|
|
59
|
-
execution: "
|
|
59
|
+
execution: "Use existing onboarding context to understand the user goal and GTM bottleneck, recommend a useful next improvement, and guide its native implementation when selected.",
|
|
60
60
|
posture: "mixed",
|
|
61
61
|
gatewayTools: ["oxygen_whoami", "oxygen_recipes_list", "oxygen_blueprints_list", "oxygen_context_resolve", "oxygen_copilot_start"],
|
|
62
|
-
gatewayCommands: ["whoami", "
|
|
63
|
-
skills: ["oxygen-quickstart", "oxygen-gtm"],
|
|
62
|
+
gatewayCommands: ["whoami", "context resolve", "recipes list", "blueprints list", "copilot start"],
|
|
63
|
+
skills: ["oxygen-onboarding", "oxygen-quickstart", "oxygen-gtm"],
|
|
64
64
|
// `activation` remains an exact legacy API family for saved clients, but it
|
|
65
65
|
// has no recommended gateway and is not an intent term or first-touch path.
|
|
66
66
|
endpointSections: ["activation", "copilot", "home"],
|
|
@@ -68,7 +68,7 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
68
68
|
// users say ("next step of this workflow run"), and a 6-point multi-word hit
|
|
69
69
|
// would drag those queries here. The terms kept are the ones only a founder
|
|
70
70
|
// asking where they are in setup actually types.
|
|
71
|
-
intentTerms: ["onboard", "onboarding", "first run", "first day", "setup oxygen", "copilot", "assistant", "guided", "where am i", "next step in setup", "what do i do next"],
|
|
71
|
+
intentTerms: ["onboard", "onboarding", "first run", "first day", "setup oxygen", "copilot", "assistant", "guided", "where am i", "next step in setup", "what do i do next", "gtm consultant", "gtm planning", "improve our gtm operation"],
|
|
72
72
|
},
|
|
73
73
|
{
|
|
74
74
|
id: "billing-and-budgets",
|
|
@@ -82,7 +82,7 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
82
82
|
gatewayCommands: ["billing balance", "billing usage", "budget list"],
|
|
83
83
|
skills: ["oxygen-gtm", "oxygen-diagnostics"],
|
|
84
84
|
endpointSections: ["billing", "budget", "limits"],
|
|
85
|
-
intentTerms: ["billing", "credits", "credit balance", "usage", "spend", "cost", "budget", "subscription", "invoice", "cancel plan"],
|
|
85
|
+
intentTerms: ["billing", "credits", "credit balance", "usage", "spend", "cost", "budget", "subscription", "invoice", "cancel plan", "change plan", "upgrade", "downgrade"],
|
|
86
86
|
},
|
|
87
87
|
{
|
|
88
88
|
id: "support-and-feedback",
|
|
@@ -174,7 +174,7 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
174
174
|
gatewayCommands: ["knowledge search", "knowledge index", "knowledge page get"],
|
|
175
175
|
skills: ["oxygen-knowledge"],
|
|
176
176
|
endpointSections: ["context", "knowledge"],
|
|
177
|
-
intentTerms: ["knowledge", "wiki", "context", "icp", "positioning", "offer", "rubric", "brand", "voice", "company profile"],
|
|
177
|
+
intentTerms: ["knowledge", "wiki", "context", "icp", "ideal customer", "positioning", "offer", "rubric", "brand", "voice", "company profile", "github", "obsidian", "vault", "repository sync", "markdown import"],
|
|
178
178
|
},
|
|
179
179
|
{
|
|
180
180
|
id: "recipes",
|
|
@@ -182,13 +182,13 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
182
182
|
primitive: "recipes",
|
|
183
183
|
owns: "Versioned advisory playbooks that teach when and why to run proven GTM motions through native primitives.",
|
|
184
184
|
notFor: "A hidden runtime, mandatory bundle, or replacement for hosted Workflows.",
|
|
185
|
-
execution: "Browse
|
|
185
|
+
execution: "Browse the Recipe, apply its kit (ordered Blueprint stages, one approval, 0 credits, workflows disabled), then run the play through native primitives.",
|
|
186
186
|
posture: "workspace_write",
|
|
187
187
|
gatewayTools: ["oxygen_recipes_list", "oxygen_recipes_get"],
|
|
188
|
-
gatewayCommands: ["recipes list", "recipes show", "recipes install"],
|
|
189
|
-
skills: ["oxygen-recipes"],
|
|
188
|
+
gatewayCommands: ["recipes list", "recipes show", "recipes apply", "recipes install"],
|
|
189
|
+
skills: ["oxygen-recipes", "oxygen-playbooks"],
|
|
190
190
|
endpointSections: ["blueprints", "recipes", "templates"],
|
|
191
|
-
intentTerms: ["recipe", "playbook", "proven play", "activation play", "setup play", "journey", "day 1", "day 7", "day 30", "blueprint", "template", "what should i do"],
|
|
191
|
+
intentTerms: ["recipe", "playbook", "proven play", "activation play", "setup play", "journey", "day 1", "day 7", "day 30", "blueprint", "template", "what should i do", "inbound led outbound", "engagers into a sequence", "warm outbound from my posts", "whole motion", "end to end motion", "kit"],
|
|
192
192
|
},
|
|
193
193
|
{
|
|
194
194
|
id: "records",
|
|
@@ -210,18 +210,18 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
210
210
|
primitive: "tables",
|
|
211
211
|
owns: "Typed working datasets, rows, formulas, AI/tool/waterfall columns, reusable Functions with isolated drafts and published versions, cell state, projects, and run provenance.",
|
|
212
212
|
notFor: "Canonical CRM truth, message cadence, or an off-platform spreadsheet runtime.",
|
|
213
|
-
execution: "Create and run work in hosted OXYGEN Tables; validate a small sample before bounded paid runs.",
|
|
213
|
+
execution: "Create and run work in hosted OXYGEN Tables; validate a small sample before bounded paid runs. For standard person or company enrichment, `columns add <table> --preset person_enrich|company_enrich` (MCP oxygen_columns_add with preset) adds the maintained bundle in one call before any hand-built tool column.",
|
|
214
214
|
posture: "mixed",
|
|
215
215
|
gatewayTools: ["oxygen_tables_create", "oxygen_columns_add", "oxygen_enrich_column_preview", "oxygen_tables_link_bulk", "oxygen_callables_manage"],
|
|
216
216
|
gatewayCommands: ["tables create", "columns add", "enrich-column preview", "tables link", "functions list", "functions draft"],
|
|
217
|
-
skills: ["oxygen-gtm", "oxygen-table-tidy", "oxygen-diagnostics", "oxygen-clay-migration"],
|
|
218
|
-
endpointSections: ["action-columns", "callables", "functions", "columns", "company-enrichment", "enrich-column", "enrichment", "projects", "table-action-items", "table-action-runs", "table-ingestion-runs", "tables"],
|
|
217
|
+
skills: ["oxygen-gtm", "oxygen-table-tidy", "oxygen-diagnostics", "oxygen-clay-migration", "oxygen-linkedin-marketing"],
|
|
218
|
+
endpointSections: ["action-columns", "callables", "functions", "columns", "company-enrichment", "enrich-column", "enrichment", "projects", "supabase", "table-action-items", "table-action-runs", "table-ingestion-runs", "tables"],
|
|
219
219
|
// "link"/"join"/"connect"/"relate" route here for `tables link`. Added after a
|
|
220
220
|
// blind user eval asked for exactly "link two tables" and was routed to
|
|
221
221
|
// `tables create` / `columns add` / `enrich-column preview` — none of which
|
|
222
222
|
// do it. The agent only found the right command by grepping the raw 25k-line
|
|
223
223
|
// command manifest, which is not a discovery path a customer has.
|
|
224
|
-
intentTerms: ["table", "rows", "column", "columns", "dataset", "csv", "import", "enrich", "enrichment", "waterfall", "score", "formula", "ai column", "lookup", "link", "link tables", "join", "connect", "relate", "relationship", "function", "functions", "reusable function", "function draft", "function version", "callable"],
|
|
224
|
+
intentTerms: ["table", "spreadsheet", "rows", "column", "columns", "dataset", "csv", "import", "enrich", "enrichment", "waterfall", "score", "formula", "ai column", "lookup", "link", "link tables", "join", "connect", "relate", "relationship", "function", "functions", "reusable function", "function draft", "function version", "callable"],
|
|
225
225
|
},
|
|
226
226
|
{
|
|
227
227
|
id: "messages",
|
|
@@ -324,7 +324,7 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
324
324
|
// to workspace-access — "identity" matched the AUTH sense. The nouns a user
|
|
325
325
|
// reaches for when naming a sending identity or the dialing pool have to land
|
|
326
326
|
// on the group that actually owns `senders profiles` and `voice numbers`.
|
|
327
|
-
intentTerms: ["sequence", "campaign", "cadence", "enroll", "outreach", "linkedin message", "linkedin dm", "nurture", "follow up", "sender rotation", "stop on reply", "sender identity", "sending identity", "sender profile", "phone number", "dialing", "dialer"],
|
|
327
|
+
intentTerms: ["sequence", "campaign", "cadence", "enroll", "outreach", "linkedin message", "linkedin dm", "nurture", "follow up", "sender rotation", "stop on reply", "sender identity", "sending identity", "sender profile", "phone number", "dialing", "dialer", "call", "cold call"],
|
|
328
328
|
},
|
|
329
329
|
{
|
|
330
330
|
id: "publishing",
|
|
@@ -335,10 +335,13 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
335
335
|
execution: "Create and schedule on OXYGEN; explicit approval releases the hosted publisher.",
|
|
336
336
|
posture: "external_write",
|
|
337
337
|
gatewayTools: ["oxygen_publishing_posts_create", "oxygen_publishing_posts_list", "oxygen_publishing_posts_approve", "oxygen_ugc_get"],
|
|
338
|
-
gatewayCommands: ["publishing posts create", "publishing posts list", "publishing posts approve", "ugc programs list"],
|
|
338
|
+
gatewayCommands: ["publishing posts create", "publishing posts list", "publishing posts approve", "ugc programs list", "ugc programs duplicate", "ugc programs archive"],
|
|
339
339
|
skills: ["oxygen-linkedin-marketing", "oxygen-ugc"],
|
|
340
340
|
endpointSections: ["publishing", "ugc"],
|
|
341
|
-
|
|
341
|
+
// "remove program" / "delete program" landed on route: null in a blind eval
|
|
342
|
+
// (2026-09-10); a program is a UGC noun here, and its lifecycle verbs must
|
|
343
|
+
// resolve to the group that owns `ugc programs archive|delete|duplicate`.
|
|
344
|
+
intentTerms: ["publish", "publishing", "schedule post", "content calendar", "posting calendar", "approve post", "social publishing", "ugc", "creator program", "user generated content", "creator voice", "sponsored creator", "ugc program", "archive program", "delete program", "remove program", "duplicate program", "copy program"],
|
|
342
345
|
},
|
|
343
346
|
{
|
|
344
347
|
id: "workflows",
|
|
@@ -523,6 +526,7 @@ export function inferCapabilityRoute(query) {
|
|
|
523
526
|
card,
|
|
524
527
|
recommendedTools: recommendations.tools,
|
|
525
528
|
recommendedCommands: recommendations.commands,
|
|
529
|
+
...(recommendations.exactCommand ? { exactCommand: recommendations.exactCommand } : {}),
|
|
526
530
|
};
|
|
527
531
|
}
|
|
528
532
|
export function inferPrimitiveRoute(query) {
|
|
@@ -546,6 +550,7 @@ export function serializeCapabilityRoute(route) {
|
|
|
546
550
|
endpoint_sections: route.card.endpointSections,
|
|
547
551
|
recommended_commands: route.recommendedCommands,
|
|
548
552
|
recommended_tools: route.recommendedTools,
|
|
553
|
+
...(route.exactCommand ? { exact_command: route.exactCommand } : {}),
|
|
549
554
|
hydrate: {
|
|
550
555
|
cli: "oxygen commands get <exact-command> --json",
|
|
551
556
|
mcp: "oxygen_capabilities_schema",
|
|
@@ -555,9 +560,21 @@ export function serializeCapabilityRoute(route) {
|
|
|
555
560
|
function normalizeIntent(query) {
|
|
556
561
|
return query.toLowerCase().replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim();
|
|
557
562
|
}
|
|
563
|
+
function isLinkedInProfileWatcherIntent(query) {
|
|
564
|
+
return /\blinkedin\b/.test(query)
|
|
565
|
+
&& /\bprofiles?\b/.test(query)
|
|
566
|
+
&& /\b(watcher|watch|watching|monitor|monitoring|daily|every day|recurring)\b/.test(query)
|
|
567
|
+
&& (/\b(engagers?|engaging|engagement|reactions?|comments?|posts?)\b/.test(query) || /\bprofile watcher\b/.test(query))
|
|
568
|
+
&& !isNetNewLinkedInInitiation(query);
|
|
569
|
+
}
|
|
558
570
|
function explicitCapabilityIntent(query) {
|
|
559
571
|
if (/\b(infographic|graphic designer|render html|carousel pages|visual design|gtm flow image)\b/.test(query))
|
|
560
572
|
return ROUTE_BY_ID.get("visual-rendering") ?? null;
|
|
573
|
+
// A whole-motion ask ("set up inbound-led outbound for my posts") is a
|
|
574
|
+
// Recipe kit + playbook, not the connected-LinkedIn signals card that the
|
|
575
|
+
// "my LinkedIn … engagers" rule below would otherwise claim. Resolve it first.
|
|
576
|
+
if (isInboundLedOutboundIntent(query))
|
|
577
|
+
return ROUTE_BY_PRIMITIVE.get("recipes") ?? null;
|
|
561
578
|
// A unified sender profile is an owned Sequence identity, even when the ask
|
|
562
579
|
// names every attached channel (LinkedIn + WhatsApp + email). Resolve this
|
|
563
580
|
// before public LinkedIn research, whose generic "profile" wording would
|
|
@@ -574,6 +591,8 @@ function explicitCapabilityIntent(query) {
|
|
|
574
591
|
if (isMailboxOnboardingIntent(query)) {
|
|
575
592
|
return ROUTE_BY_ID.get("sending-infrastructure") ?? null;
|
|
576
593
|
}
|
|
594
|
+
if (isLinkedInProfileWatcherIntent(query))
|
|
595
|
+
return ROUTE_BY_PRIMITIVE.get("tables") ?? null;
|
|
577
596
|
if (isHostedWorkflowIntent(query))
|
|
578
597
|
return ROUTE_BY_PRIMITIVE.get("workflows") ?? null;
|
|
579
598
|
if (isOwnedPostCommentIntent(query))
|
|
@@ -612,6 +631,17 @@ function explicitCapabilityIntent(query) {
|
|
|
612
631
|
if (/\b(my|our|own)\b.{0,40}\blinkedin\b.{0,40}\b(post )?engagers?\b/.test(query)) {
|
|
613
632
|
return ROUTE_BY_PRIMITIVE.get("signals") ?? null;
|
|
614
633
|
}
|
|
634
|
+
// Grading addresses a row ALREADY holds — "verify these emails", "which are
|
|
635
|
+
// safe to send", "catch-all or valid" — is Tables work (the Email verification
|
|
636
|
+
// column: `columns add --capability verify_email`, previewed and run through
|
|
637
|
+
// `enrich-column`), with `verify email` as the one-shot twin. Without this
|
|
638
|
+
// rule the bare word "email" scored the Messages card, and a 2026-09-10 blind
|
|
639
|
+
// user asking "verify email deliverability safe to send" was routed to
|
|
640
|
+
// `email send`. Sits after the mailbox rules above so warm-up and sender
|
|
641
|
+
// deliverability keep their owner, and after the scheduling rule so "every
|
|
642
|
+
// Monday verify new signups" still lands on Workflows.
|
|
643
|
+
if (isEmailVerificationIntent(query))
|
|
644
|
+
return ROUTE_BY_PRIMITIVE.get("tables") ?? null;
|
|
615
645
|
if (/\b(recipe|playbook|proven play|what should i do)\b/.test(query)) {
|
|
616
646
|
return ROUTE_BY_PRIMITIVE.get("recipes") ?? null;
|
|
617
647
|
}
|
|
@@ -643,9 +673,23 @@ function explicitCapabilityIntent(query) {
|
|
|
643
673
|
|| /\b(day\s*\d+|days?\s+\d|multi[ -]step|rotate senders?|sender rotation|stop on reply|until (?:they )?reply)\b/.test(query)) {
|
|
644
674
|
return ROUTE_BY_PRIMITIVE.get("sequences") ?? null;
|
|
645
675
|
}
|
|
676
|
+
// Existing reply handling is Messages even without a channel name. Keep this
|
|
677
|
+
// after the explicit cadence/enrollment rules so reply-stop programs retain
|
|
678
|
+
// Sequences. The onboarding baseline phrased this as "follow up on replies".
|
|
679
|
+
if (/\b(follow up|triage|answer|handle)\b.{0,40}\breplies\b/.test(query)) {
|
|
680
|
+
return ROUTE_BY_PRIMITIVE.get("messages") ?? null;
|
|
681
|
+
}
|
|
646
682
|
if (/\b(reply|respond)\b.{0,30}\b(conversation|thread|email|linkedin|whatsapp|dm)\b/.test(query)) {
|
|
647
683
|
return ROUTE_BY_PRIMITIVE.get("messages") ?? null;
|
|
648
684
|
}
|
|
685
|
+
// Resolving websites / domains / LinkedIn pages for company NAMES the user
|
|
686
|
+
// already holds is the companies-search `url_recovery` intent, not a fresh
|
|
687
|
+
// sourcing search and not an enrichment column. A 2026-09-11 blind user
|
|
688
|
+
// asking "get company website URL from company name" got NO route here
|
|
689
|
+
// (null), never discovered `companies search plan`, and priced the whole
|
|
690
|
+
// company_enrich preset instead.
|
|
691
|
+
if (isCompanyUrlRecoveryIntent(query))
|
|
692
|
+
return ROUTE_BY_ID.get("sourcing-and-provider-tools") ?? null;
|
|
649
693
|
if (/\b(find emails?|find phones?|find companies|find people|source (?:leads?|a list|companies|people)|build (?:me )?a list|tam|prospect)\b/.test(query)) {
|
|
650
694
|
return ROUTE_BY_ID.get("sourcing-and-provider-tools") ?? null;
|
|
651
695
|
}
|
|
@@ -654,16 +698,43 @@ function explicitCapabilityIntent(query) {
|
|
|
654
698
|
}
|
|
655
699
|
return null;
|
|
656
700
|
}
|
|
701
|
+
/**
|
|
702
|
+
* Does `term` occur in `query` starting on a word boundary?
|
|
703
|
+
*
|
|
704
|
+
* A plain `query.includes(term)` is what sent "enrich these le**ad**s" and
|
|
705
|
+
* "build me a spre**ad**sheet" to the competitor-ad-intelligence card, and
|
|
706
|
+
* "our i**deal** customer" to CRM Records: a short term matches inside any
|
|
707
|
+
* longer word that happens to contain its letters, and nothing else scores, so
|
|
708
|
+
* the wrong card wins outright rather than narrowly.
|
|
709
|
+
*
|
|
710
|
+
* Only the START of the match is anchored, deliberately. Requiring a boundary at
|
|
711
|
+
* BOTH ends looks stricter and is wrong here, because the cards do not list
|
|
712
|
+
* their own plurals consistently — `sequence`, `workflow`, `table`, `agent` and
|
|
713
|
+
* `campaign` have no plural term beside them — so whole-word matching silently
|
|
714
|
+
* stops routing "sequences", "workflows" and "tables", which are the phrasings
|
|
715
|
+
* people actually type. Anchoring the head keeps every plural and inflection
|
|
716
|
+
* working while refusing the mid-word accidents, and it is the same
|
|
717
|
+
* word-boundary treatment v1.915.0 applied to the Tables guard this scorer sits
|
|
718
|
+
* behind.
|
|
719
|
+
*/
|
|
720
|
+
function matchesIntentTerm(query, term) {
|
|
721
|
+
for (let at = query.indexOf(term); at >= 0; at = query.indexOf(term, at + 1)) {
|
|
722
|
+
const preceding = at === 0 ? "" : query[at - 1] ?? "";
|
|
723
|
+
if (!/[a-z0-9]/.test(preceding))
|
|
724
|
+
return true;
|
|
725
|
+
}
|
|
726
|
+
return false;
|
|
727
|
+
}
|
|
657
728
|
function highestScoringRoute(query) {
|
|
658
729
|
let best = null;
|
|
659
730
|
for (const card of OXYGEN_CAPABILITY_ROUTES) {
|
|
660
731
|
let score = 0;
|
|
661
732
|
for (const term of card.intentTerms) {
|
|
662
|
-
if (query
|
|
733
|
+
if (matchesIntentTerm(query, term))
|
|
663
734
|
score += term.includes(" ") ? 6 : 2;
|
|
664
735
|
}
|
|
665
736
|
for (const term of card.negativeTerms ?? []) {
|
|
666
|
-
if (query
|
|
737
|
+
if (matchesIntentTerm(query, term))
|
|
667
738
|
score -= term.includes(" ") ? 8 : 3;
|
|
668
739
|
}
|
|
669
740
|
if (score > (best?.score ?? 0))
|
|
@@ -671,7 +742,16 @@ function highestScoringRoute(query) {
|
|
|
671
742
|
}
|
|
672
743
|
return best?.card ?? null;
|
|
673
744
|
}
|
|
745
|
+
/** The exact `companies search plan` invocation for the url_recovery intent. */
|
|
746
|
+
export const COMPANY_URL_RECOVERY_COMMAND = 'oxygen companies search plan --prompt "Find the websites for these companies: <company names>" --source-intent url_recovery';
|
|
674
747
|
function recommendationsFor(card, query) {
|
|
748
|
+
if (card.id === "sourcing-and-provider-tools" && isCompanyUrlRecoveryIntent(query)) {
|
|
749
|
+
return {
|
|
750
|
+
tools: ["oxygen_companies_search_plan", "oxygen_companies_search_run"],
|
|
751
|
+
commands: ["companies search plan", "companies search run"],
|
|
752
|
+
exactCommand: COMPANY_URL_RECOVERY_COMMAND,
|
|
753
|
+
};
|
|
754
|
+
}
|
|
675
755
|
if (card.id === "connected-linkedin" && isMutualLinkedInConnectionsIntent(query)) {
|
|
676
756
|
return {
|
|
677
757
|
tools: ["oxygen_tools_get", "oxygen_tools_run_live", "oxygen_senders_list"],
|
|
@@ -752,12 +832,27 @@ function recommendationsFor(card, query) {
|
|
|
752
832
|
};
|
|
753
833
|
}
|
|
754
834
|
if (card.primitive === "tables") {
|
|
835
|
+
if (isLinkedInProfileWatcherIntent(query)) {
|
|
836
|
+
return {
|
|
837
|
+
tools: ["oxygen_tables_watcher"],
|
|
838
|
+
commands: ["tables watcher preview", "tables watcher create", "tables watcher get", "tables watcher update", "tables watcher pause", "tables watcher resume"],
|
|
839
|
+
};
|
|
840
|
+
}
|
|
755
841
|
if (/\b(table )?(action )?runs?\b/.test(query)) {
|
|
756
842
|
return {
|
|
757
843
|
tools: ["oxygen_table_runs_get", "oxygen_table_runs_items", "oxygen_table_runs_wait", "oxygen_table_runs_retry_failed"],
|
|
758
844
|
commands: ["table-runs get", "table-runs items", "table-runs wait", "table-runs retry-failed"],
|
|
759
845
|
};
|
|
760
846
|
}
|
|
847
|
+
if (isEmailVerificationIntent(query)) {
|
|
848
|
+
// Define-without-running first (0 credits), then the free preview that
|
|
849
|
+
// prices it, then the approved run, then the one-shot for a handful of
|
|
850
|
+
// addresses that never needed a table.
|
|
851
|
+
return {
|
|
852
|
+
tools: ["oxygen_columns_add", "oxygen_enrich_column_preview", "oxygen_enrich_column_run", "oxygen_verify_email"],
|
|
853
|
+
commands: ["columns add", "enrich-column preview", "enrich-column run", "verify email"],
|
|
854
|
+
};
|
|
855
|
+
}
|
|
761
856
|
if (/\b(waterfall|enrich|enrichment|work email|mobile phone)\b/.test(query)) {
|
|
762
857
|
return {
|
|
763
858
|
tools: ["oxygen_enrich_column_preview", "oxygen_columns_add", "oxygen_enrich_column_run", "oxygen_table_runs_get"],
|
|
@@ -889,7 +984,7 @@ function recommendationsFor(card, query) {
|
|
|
889
984
|
if (card.primitive === "signals" && /\blinkedin\b.{0,40}\bengagers?\b/.test(query)) {
|
|
890
985
|
return {
|
|
891
986
|
tools: ["oxygen_engagement_list_engagers", "oxygen_signals_list", "oxygen_signals_leads_today"],
|
|
892
|
-
commands: ["engagement
|
|
987
|
+
commands: ["engagement engagers", "signals list", "signals leads-today"],
|
|
893
988
|
};
|
|
894
989
|
}
|
|
895
990
|
if (card.id === "connected-whatsapp" && /\b(account|connect|limits?|sync)\b/.test(query)) {
|
|
@@ -900,6 +995,35 @@ function recommendationsFor(card, query) {
|
|
|
900
995
|
}
|
|
901
996
|
return { tools: [...card.gatewayTools], commands: [...card.gatewayCommands] };
|
|
902
997
|
}
|
|
998
|
+
// An email-VERIFICATION ask names a grading verb next to the addresses, or the
|
|
999
|
+
// addresses next to a grade. "check my emails" is deliberately not matched
|
|
1000
|
+
// (that is an inbox), and bare "deliverability" is left to the sending rail —
|
|
1001
|
+
// only "are these emails deliverable" counts. Bare "bounce" describes an
|
|
1002
|
+
// observed delivery failure too; require predictive wording or "bounce risk".
|
|
1003
|
+
function isEmailVerificationIntent(query) {
|
|
1004
|
+
const gradeThenAddress = /\b(verif(?:y|ied|ication)|validat(?:e|ed|ion)|grade|scrub|clean)\b.{0,40}\b(e ?mails?|email addresses|addresses)\b/;
|
|
1005
|
+
const addressThenGrade = /\b(e ?mails?|addresses)\b.{0,40}\b(verif(?:y|ied|ication)|validat(?:e|ed|ion)|valid|invalid|deliverable|(?:will|would|might) bounce|bounce risk|risky|safe to (?:send|email)|catch ?all|accept ?all)\b/;
|
|
1006
|
+
return gradeThenAddress.test(query) || addressThenGrade.test(query);
|
|
1007
|
+
}
|
|
1008
|
+
// "get company website URL from company name", "find the websites for this
|
|
1009
|
+
// list of company names", "recover domains for company names" — a URL-ish
|
|
1010
|
+
// noun, a company noun, a recovery verb, and the hint that the input is NAMES
|
|
1011
|
+
// or a LIST (or that URLs are MISSING). Every leg is required so a sourcing
|
|
1012
|
+
// ask ("find companies with a website in Germany") keeps its owner. LinkedIn
|
|
1013
|
+
// page recovery is deliberately left to the LinkedIn rules above it, which
|
|
1014
|
+
// already resolve company pages through the public-research catalog.
|
|
1015
|
+
function isCompanyUrlRecoveryIntent(query) {
|
|
1016
|
+
const urlNoun = /\b(websites?|web ?sites?|website urls?|domains?|urls?|homepages?)\b/;
|
|
1017
|
+
const companyNoun = /\b(compan(?:y|ies)|accounts?|organi[sz]ations?|brands?|vendors?)\b/;
|
|
1018
|
+
const recover = /\b(recover|resolve|look ?up|lookup|missing|get|find|fill(?: in)?|add)\b/;
|
|
1019
|
+
const fromNames = /\b(names?|list|missing|these|those)\b/;
|
|
1020
|
+
// An email / phone / contact ask that merely MENTIONS domains is enrichment,
|
|
1021
|
+
// and a column / table ask is a Tables ask — both keep their owners.
|
|
1022
|
+
const otherOwner = /\b(e-?mails?|phones?|mobiles?|numbers?|contacts?|people|persons?|columns?|tables?|enrich\w*|verif\w*)\b/;
|
|
1023
|
+
return urlNoun.test(query) && companyNoun.test(query) && recover.test(query) && fromNames.test(query)
|
|
1024
|
+
&& !/\b(scrape|engagers?|comments?|reactions?|posts?)\b/.test(query)
|
|
1025
|
+
&& !otherOwner.test(query);
|
|
1026
|
+
}
|
|
903
1027
|
function isInboxAvatarIntent(query) {
|
|
904
1028
|
return /\b(avatar|profile (?:picture|photo)|headshot|hosted (?:picture|image)|mailbox (?:picture|photo))\b/.test(query);
|
|
905
1029
|
}
|
|
@@ -926,6 +1050,14 @@ function isMailboxDeleteIntent(query) {
|
|
|
926
1050
|
const scopedDetach = /\b(remove|disconnect)\b.{0,48}\b(mailbox(?:es)?|inbox(?:es)?|sender accounts?|email accounts?)\b.{0,24}\b(?:from|in|on)\b.{0,24}\b(sequence|campaign|cadence|sender profile)\b/.test(query);
|
|
927
1051
|
return mailboxScope && removal && !scopedDetach;
|
|
928
1052
|
}
|
|
1053
|
+
// The composed inbound-led outbound motion: engagers → ICP gate → capped
|
|
1054
|
+
// sequence → CRM. Hyphens are already normalised to spaces by the caller.
|
|
1055
|
+
function isInboundLedOutboundIntent(query) {
|
|
1056
|
+
return /\binbound led outbound\b/.test(query)
|
|
1057
|
+
|| /\bengagers?\b.{0,40}\b(into|to) (a |my )?(sequence|pipeline)\b/.test(query)
|
|
1058
|
+
|| /\bwarm outbound\b.{0,40}\b(posts?|engagement)\b/.test(query)
|
|
1059
|
+
|| /\b(whole|end to end|complete) (gtm )?motion\b/.test(query);
|
|
1060
|
+
}
|
|
929
1061
|
function isNetNewLinkedInInitiation(query) {
|
|
930
1062
|
const mentionsLinkedIn = /\blinkedin\b|\bdm\b/.test(query);
|
|
931
1063
|
const startsConversation = /\b(send|message|dm|contact|reach out|initiate|start)\b/.test(query)
|
|
@@ -21,6 +21,9 @@ const WORKER_STEP_TIMEOUT_MESSAGE = /\bWorker step '[^']+' exceeded \d+ms deadli
|
|
|
21
21
|
export function customerFacingCopilotError(input) {
|
|
22
22
|
const code = input.code ?? null;
|
|
23
23
|
const message = input.message ?? null;
|
|
24
|
+
if (code === "agent_response_incomplete") {
|
|
25
|
+
return { code, message: "I stopped before completing the requested work. Completed actions are saved; ask me to continue." };
|
|
26
|
+
}
|
|
24
27
|
const isTimeout = code === "worker_step_timeout" ||
|
|
25
28
|
code === COPILOT_TURN_TIMEOUT_CODE ||
|
|
26
29
|
code === COPILOT_TURN_DEADLINE_EXCEEDED_CODE ||
|
|
@@ -8,6 +8,24 @@ export type CopilotJourney = {
|
|
|
8
8
|
seedMessage: string;
|
|
9
9
|
};
|
|
10
10
|
export declare const LINKEDIN_NETWORK_COPILOT_JOURNEY_SLUG = "inbound-led-outbound";
|
|
11
|
-
export declare const COPILOT_JOURNEYS: readonly
|
|
11
|
+
export declare const COPILOT_JOURNEYS: readonly [{
|
|
12
|
+
readonly slug: "inbound-led-outbound";
|
|
13
|
+
readonly title: "Inbound-led outbound";
|
|
14
|
+
readonly description: "Turn the people who engage with your LinkedIn posts into a capped, evidence-gated warm sequence with replies routed to your CRM.";
|
|
15
|
+
readonly recipeSlugs: readonly ["inbound-led-outbound", "linkedin-network-first-motion", "founder-posting-system", "reply-to-meeting"];
|
|
16
|
+
readonly seedMessage: "I chose the inbound-led-outbound recipe. Help me set up inbound-led outbound for my LinkedIn posts: watch who engages with them every day, assess each person against my ICP, hand me the strong fits, put the possible fits into a draft LinkedIn sequence on a schedule, move replies into my CRM, and write what we learn back into my knowledge wiki. Inspect the workspace first, follow the recipe, and resume from durable evidence instead of starting over. Do not connect an account, enroll anyone, send anything, or spend credits without asking me first.";
|
|
17
|
+
}, {
|
|
18
|
+
readonly slug: "email-infra-setup";
|
|
19
|
+
readonly title: "Outbound infrastructure";
|
|
20
|
+
readonly description: "Domains, inboxes, warmup, sender posture — reviewed and approved before anything is bought or sent.";
|
|
21
|
+
readonly recipeSlugs: readonly ["email-infra-setup"];
|
|
22
|
+
readonly seedMessage: "Inspect my current sending setup (domains, inboxes, warmup, senders, caps, deliverability) and propose what's missing for a safe cold-email motion. Show quotes and costs — do not buy or change anything without my approval.";
|
|
23
|
+
}, {
|
|
24
|
+
readonly slug: "tam-sourcing";
|
|
25
|
+
readonly title: "Map your market";
|
|
26
|
+
readonly description: "Source and segment your addressable market from your ICP, starting with a bounded sample.";
|
|
27
|
+
readonly recipeSlugs: readonly ["icp-to-account-map", "outbound-pilot-50"];
|
|
28
|
+
readonly seedMessage: "Help me map my total addressable market from my ICP. Ground it in my company profile and website, source a bounded sample first, and show me coverage and credit estimates before expanding.";
|
|
29
|
+
}];
|
|
12
30
|
export type CopilotJourneySlug = (typeof COPILOT_JOURNEYS)[number]["slug"];
|
|
13
31
|
export declare function getCopilotJourney(slug: string): CopilotJourney | null;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const COPILOT_JOURNEYS_GENERATED: readonly [{
|
|
2
|
+
readonly slug: "inbound-led-outbound";
|
|
3
|
+
readonly title: "Inbound-led outbound";
|
|
4
|
+
readonly description: "Turn the people who engage with your LinkedIn posts into a capped, evidence-gated warm sequence with replies routed to your CRM.";
|
|
5
|
+
readonly recipeSlugs: readonly ["inbound-led-outbound", "linkedin-network-first-motion", "founder-posting-system", "reply-to-meeting"];
|
|
6
|
+
readonly seedMessage: "I chose the inbound-led-outbound recipe. Help me set up inbound-led outbound for my LinkedIn posts: watch who engages with them every day, assess each person against my ICP, hand me the strong fits, put the possible fits into a draft LinkedIn sequence on a schedule, move replies into my CRM, and write what we learn back into my knowledge wiki. Inspect the workspace first, follow the recipe, and resume from durable evidence instead of starting over. Do not connect an account, enroll anyone, send anything, or spend credits without asking me first.";
|
|
7
|
+
}, {
|
|
8
|
+
readonly slug: "email-infra-setup";
|
|
9
|
+
readonly title: "Outbound infrastructure";
|
|
10
|
+
readonly description: "Domains, inboxes, warmup, sender posture — reviewed and approved before anything is bought or sent.";
|
|
11
|
+
readonly recipeSlugs: readonly ["email-infra-setup"];
|
|
12
|
+
readonly seedMessage: "Inspect my current sending setup (domains, inboxes, warmup, senders, caps, deliverability) and propose what's missing for a safe cold-email motion. Show quotes and costs — do not buy or change anything without my approval.";
|
|
13
|
+
}, {
|
|
14
|
+
readonly slug: "tam-sourcing";
|
|
15
|
+
readonly title: "Map your market";
|
|
16
|
+
readonly description: "Source and segment your addressable market from your ICP, starting with a bounded sample.";
|
|
17
|
+
readonly recipeSlugs: readonly ["icp-to-account-map", "outbound-pilot-50"];
|
|
18
|
+
readonly seedMessage: "Help me map my total addressable market from my ICP. Ground it in my company profile and website, source a bounded sample first, and show me coverage and credit estimates before expanding.";
|
|
19
|
+
}];
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// GENERATED by apps/web/scripts/generate-copilot-journeys.ts from the recipe
|
|
2
|
+
// catalog (`journey:` blocks in apps/web/src/agent-skills/oxygen-recipes/recipes).
|
|
3
|
+
// Do not edit by hand — run `npm run skills:metadata -w @oxygen/web`.
|
|
4
|
+
export const COPILOT_JOURNEYS_GENERATED = [
|
|
5
|
+
{
|
|
6
|
+
slug: "inbound-led-outbound",
|
|
7
|
+
title: "Inbound-led outbound",
|
|
8
|
+
description: "Turn the people who engage with your LinkedIn posts into a capped, evidence-gated warm sequence with replies routed to your CRM.",
|
|
9
|
+
recipeSlugs: ["inbound-led-outbound", "linkedin-network-first-motion", "founder-posting-system", "reply-to-meeting"],
|
|
10
|
+
seedMessage: "I chose the inbound-led-outbound recipe. Help me set up inbound-led outbound for my LinkedIn posts: watch who engages with them every day, assess each person against my ICP, hand me the strong fits, put the possible fits into a draft LinkedIn sequence on a schedule, move replies into my CRM, and write what we learn back into my knowledge wiki. Inspect the workspace first, follow the recipe, and resume from durable evidence instead of starting over. Do not connect an account, enroll anyone, send anything, or spend credits without asking me first.",
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
slug: "email-infra-setup",
|
|
14
|
+
title: "Outbound infrastructure",
|
|
15
|
+
description: "Domains, inboxes, warmup, sender posture — reviewed and approved before anything is bought or sent.",
|
|
16
|
+
recipeSlugs: ["email-infra-setup"],
|
|
17
|
+
seedMessage: "Inspect my current sending setup (domains, inboxes, warmup, senders, caps, deliverability) and propose what's missing for a safe cold-email motion. Show quotes and costs — do not buy or change anything without my approval.",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
slug: "tam-sourcing",
|
|
21
|
+
title: "Map your market",
|
|
22
|
+
description: "Source and segment your addressable market from your ICP, starting with a bounded sample.",
|
|
23
|
+
recipeSlugs: ["icp-to-account-map", "outbound-pilot-50"],
|
|
24
|
+
seedMessage: "Help me map my total addressable market from my ICP. Ground it in my company profile and website, source a bounded sample first, and show me coverage and credit estimates before expanding.",
|
|
25
|
+
},
|
|
26
|
+
];
|
|
@@ -1,47 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
// the runtime (system-prompt grounding on the named recipes) and the web journey
|
|
3
|
-
// cards (seeding a session's first message) — the data is pure and client-safe.
|
|
4
|
-
// Each journey is a thin descriptor over EXISTING advisory catalog Recipes:
|
|
5
|
-
// journeys add no runtime behavior, and every guardrail (never enroll/send/buy/
|
|
6
|
-
// change DNS without approval) rides those recipes' own approval gates.
|
|
1
|
+
import { COPILOT_JOURNEYS_GENERATED } from "./copilot-journeys.generated.js";
|
|
7
2
|
// Stable slug for sessions deliberately launched from the matching Recipe.
|
|
8
3
|
// It is never a default: first-touch surfaces stay outcome-neutral.
|
|
9
4
|
export const LINKEDIN_NETWORK_COPILOT_JOURNEY_SLUG = "inbound-led-outbound";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
// are a thin, anonymity-capped sample by comparison. Posting is second because
|
|
18
|
-
// it CREATES the engager stream this play captures, and replies close the loop.
|
|
19
|
-
recipeSlugs: [
|
|
20
|
-
"linkedin-network-first-motion",
|
|
21
|
-
"founder-posting-system",
|
|
22
|
-
"reply-to-meeting",
|
|
23
|
-
],
|
|
24
|
-
// Deliberately free of steps, commands, table names, and caps: encoding
|
|
25
|
-
// mechanics here would fork a second, unversioned copy of the play that no
|
|
26
|
-
// catalog gate can lint (ADR 0013 §11 — a journey is a thin pointer at
|
|
27
|
-
// advisory recipes, never a hardcoded kit).
|
|
28
|
-
seedMessage: "I chose the linkedin-network-first-motion recipe. Help me turn the LinkedIn network I already have — my connections and the people who engage with my posts — into a small, safe, approval-gated outreach motion. Inspect the relevant workspace primitives, follow the recipe, and resume from durable evidence instead of starting over. Do not connect an account, enroll anyone, send anything, or spend credits without asking me first.",
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
slug: "tam-sourcing",
|
|
32
|
-
title: "Map your market",
|
|
33
|
-
description: "Source and segment your addressable market from your ICP, starting with a bounded sample.",
|
|
34
|
-
recipeSlugs: ["icp-to-account-map", "outbound-pilot-50"],
|
|
35
|
-
seedMessage: "Help me map my total addressable market from my ICP. Ground it in my company profile and website, source a bounded sample first, and show me coverage and credit estimates before expanding.",
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
slug: "email-infra-setup",
|
|
39
|
-
title: "Outbound infrastructure",
|
|
40
|
-
description: "Domains, inboxes, warmup, sender posture — reviewed and approved before anything is bought or sent.",
|
|
41
|
-
recipeSlugs: ["email-infra-setup"],
|
|
42
|
-
seedMessage: "Inspect my current sending setup (domains, inboxes, warmup, senders, caps, deliverability) and propose what's missing for a safe cold-email motion. Show quotes and costs — do not buy or change anything without my approval.",
|
|
43
|
-
},
|
|
44
|
-
];
|
|
5
|
+
// Derived from the recipe catalog (ADR 0025): every recipe carrying a
|
|
6
|
+
// `journey:` block leads one journey. The list is generated into
|
|
7
|
+
// ./copilot-journeys.generated.ts by apps/web/scripts/generate-copilot-journeys.ts
|
|
8
|
+
// (run through `npm run skills:metadata -w @oxygen/web`) and gated against the
|
|
9
|
+
// catalog by apps/web/src/lib/recipes/copilot-journeys.test.ts, because this
|
|
10
|
+
// package cannot read the markdown the catalog is served from.
|
|
11
|
+
export const COPILOT_JOURNEYS = COPILOT_JOURNEYS_GENERATED;
|
|
45
12
|
export function getCopilotJourney(slug) {
|
|
46
13
|
return COPILOT_JOURNEYS.find((journey) => journey.slug === slug) ?? null;
|
|
47
14
|
}
|