@oxygen-agent/cli 1.1014.1341 → 1.1014.1420
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/command-families/tables/register-reads.js +1 -1
- package/dist/command-support.js +4 -2
- package/dist/commands/blueprints.js +10 -6
- package/dist/commands/crm.js +5 -2
- package/dist/commands/dashboards.js +4 -2
- package/dist/commands/db.js +1 -1
- package/dist/commands/directory.js +7 -4
- package/dist/commands/inbox.js +5 -2
- package/dist/commands/prompts.js +4 -2
- package/dist/commands/publishing.js +5 -2
- package/dist/commands/sequences.js +13 -4
- package/dist/commands/support.js +30 -19
- package/dist/customer-operation-commands.js +0 -42
- package/dist/http-client.js +4 -1
- package/dist/workspace-placement-commands.js +1 -1
- package/node_modules/@oxygen/shared/dist/capability-args.d.ts +19 -0
- package/node_modules/@oxygen/shared/dist/capability-args.js +73 -0
- package/node_modules/@oxygen/shared/dist/contracts/browser-operations.js +2 -1
- package/node_modules/@oxygen/shared/dist/contracts/operations.js +3 -6
- package/node_modules/@oxygen/shared/dist/contracts/table-query-raw.d.ts +5 -1
- package/node_modules/@oxygen/shared/dist/contracts/table-query-raw.js +25 -2
- package/node_modules/@oxygen/shared/dist/contracts/teammates.js +1 -1
- package/node_modules/@oxygen/shared/dist/enrollment-origin.d.ts +3 -1
- package/node_modules/@oxygen/shared/dist/enrollment-origin.js +2 -0
- package/node_modules/@oxygen/shared/dist/guided-activation.d.ts +2 -0
- package/node_modules/@oxygen/shared/dist/guided-onboarding-company-context.d.ts +9 -2
- package/node_modules/@oxygen/shared/dist/guided-trial-billing.d.ts +10 -2
- package/node_modules/@oxygen/shared/dist/guided-trial-billing.js +2 -5
- package/node_modules/@oxygen/shared/dist/knowledge-bootstrap.d.ts +6 -0
- package/node_modules/@oxygen/shared/dist/knowledge-bootstrap.js +1 -0
- package/node_modules/@oxygen/shared/dist/onboarding-plays.d.ts +68 -0
- package/node_modules/@oxygen/shared/dist/onboarding-plays.js +114 -0
- package/node_modules/@oxygen/shared/dist/onboarding-research.d.ts +37 -0
- package/node_modules/@oxygen/shared/dist/onboarding-research.js +57 -0
- package/node_modules/@oxygen/shared/dist/placement-copy.d.ts +8 -0
- package/node_modules/@oxygen/shared/dist/placement-copy.js +16 -0
- package/node_modules/@oxygen/shared/dist/product-analytics-events.d.ts +8 -0
- package/node_modules/@oxygen/shared/dist/product-analytics-events.js +8 -0
- package/node_modules/@oxygen/shared/dist/sequence-failures.js +3 -1
- package/node_modules/@oxygen/shared/dist/sequences.d.ts +30 -2
- package/node_modules/@oxygen/shared/dist/sequences.js +67 -7
- package/node_modules/@oxygen/shared/dist/teammates.d.ts +3 -1
- package/node_modules/@oxygen/shared/dist/teammates.js +4 -2
- package/node_modules/@oxygen/shared/dist/user-capability-routing.js +19 -40
- package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.generated.js +1 -1
- package/node_modules/@oxygen/shared/dist/workspace-placement.js +4 -0
- package/package.json +1 -1
- package/node_modules/@oxygen/shared/dist/contracts/guided-company-files.d.ts +0 -30
- package/node_modules/@oxygen/shared/dist/contracts/guided-company-files.js +0 -53
- package/node_modules/@oxygen/shared/dist/contracts/guided-first-sourcing.d.ts +0 -120
- package/node_modules/@oxygen/shared/dist/contracts/guided-first-sourcing.js +0 -48
- package/node_modules/@oxygen/shared/dist/contracts/guided-onboarding-journey.d.ts +0 -267
- package/node_modules/@oxygen/shared/dist/contracts/guided-onboarding-journey.js +0 -63
- package/node_modules/@oxygen/shared/dist/guided-company-files.d.ts +0 -13
- package/node_modules/@oxygen/shared/dist/guided-company-files.js +0 -14
- package/node_modules/@oxygen/shared/dist/guided-first-sourcing.d.ts +0 -116
- package/node_modules/@oxygen/shared/dist/guided-first-sourcing.js +0 -11
- package/node_modules/@oxygen/shared/dist/guided-onboarding-answers.d.ts +0 -4
- package/node_modules/@oxygen/shared/dist/guided-onboarding-answers.js +0 -42
- package/node_modules/@oxygen/shared/dist/guided-onboarding-journey.d.ts +0 -147
- package/node_modules/@oxygen/shared/dist/guided-onboarding-journey.fixture.d.ts +0 -12
- package/node_modules/@oxygen/shared/dist/guided-onboarding-journey.fixture.js +0 -83
- package/node_modules/@oxygen/shared/dist/guided-onboarding-journey.js +0 -44
- package/node_modules/@oxygen/shared/dist/guided-onboarding-prospects.d.ts +0 -7
- package/node_modules/@oxygen/shared/dist/guided-onboarding-prospects.js +0 -13
- package/node_modules/@oxygen/shared/dist/guided-onboarding-recommendations.d.ts +0 -54
- package/node_modules/@oxygen/shared/dist/guided-onboarding-recommendations.js +0 -100
- package/node_modules/@oxygen/shared/dist/guided-onboarding-signals.d.ts +0 -4
- package/node_modules/@oxygen/shared/dist/guided-onboarding-signals.js +0 -34
|
@@ -424,6 +424,54 @@ export const SEQUENCE_CRM_IDENTITY_KINDS = [
|
|
|
424
424
|
* route connected → then_id, not → else_id (no waiting).
|
|
425
425
|
*/
|
|
426
426
|
export const SEQUENCE_CONNECTION_BRANCH_CONDITIONS = ["connection_accepted", "already_connected", "open_profile"];
|
|
427
|
+
/**
|
|
428
|
+
* A boolean expression a signal-branch routes on. Two leaf kinds, plus the
|
|
429
|
+
* all/any/not combinators:
|
|
430
|
+
* - a SIGNAL leaf `{ signal, present? }` tests whether an accumulated enrollment
|
|
431
|
+
* signal is present (default) or absent (present:false).
|
|
432
|
+
* - a DATA leaf `{ has_column, present? }` tests whether the lead's source row
|
|
433
|
+
* carries a non-empty value in the named row_values column — present (default)
|
|
434
|
+
* or absent (present:false). This is the LinkedIn/Lemlist "does the lead have an
|
|
435
|
+
* email / phone / … ?" branch: route by what data you HAVE about the lead,
|
|
436
|
+
* evaluated synchronously against the enrollment's row_values (no signal, no
|
|
437
|
+
* wait). A value counts as present when it is a non-empty trimmed string, or any
|
|
438
|
+
* number / boolean; null / undefined / "" / whitespace count as absent.
|
|
439
|
+
*
|
|
440
|
+
* Lemlist condition-key → Oxygen mapping (13 keys), so a Lemlist journey ports
|
|
441
|
+
* across without a lossy translation table:
|
|
442
|
+
* hasEmailAddress → { has_column: "email" } (or any SEQUENCE_EMAIL_COLUMN_KEYS key)
|
|
443
|
+
* hasLinkedinUrl → { has_column: "<linkedin url column>" }
|
|
444
|
+
* hasPhoneNumber → { has_column: "phone" } (or any SEQUENCE_PHONE_COLUMN_KEYS key)
|
|
445
|
+
* linkedinInviteAccepted → a CONNECTION branch (condition: "connection_accepted")
|
|
446
|
+
* opened → { signal: "email_opened" }
|
|
447
|
+
* clicked → { signal: "email_clicked" }
|
|
448
|
+
* replied → { signal: "email_replied" } (or linkedin_replied / whatsapp_replied)
|
|
449
|
+
* meetingBooked → { signal: "meeting_booked" }
|
|
450
|
+
* emailsUnsubscribed → { signal: "email_unsubscribed" }
|
|
451
|
+
* aircallDone → out of scope (no Aircall integration)
|
|
452
|
+
* Negate any leaf with present:false (e.g. "no email on file" = { has_column: "email", present: false }).
|
|
453
|
+
*
|
|
454
|
+
* A CONTACT leaf `{ has_contact: "email" | "linkedin" | "phone", present? }` asks
|
|
455
|
+
* whether the step that channel needs could reach the lead, resolved exactly as
|
|
456
|
+
* dispatch resolves it: email and phone through the sequence's mapped column with
|
|
457
|
+
* the built-in fallbacks, LinkedIn through the profile captured at enrollment. It
|
|
458
|
+
* is what "If has email / LinkedIn / phone" in the builder writes, so one campaign
|
|
459
|
+
* can route every lead instead of splitting the audience by data.
|
|
460
|
+
*/
|
|
461
|
+
export const SEQUENCE_CONTACT_CHANNELS = ["email", "linkedin", "phone"];
|
|
462
|
+
/**
|
|
463
|
+
* Resolve a lead's reachable contacts the way dispatch does: the mapped email or
|
|
464
|
+
* phone column first, then the built-in keys, and LinkedIn from the profile the
|
|
465
|
+
* enrollment captured (a row value alone never makes a LinkedIn step sendable).
|
|
466
|
+
*/
|
|
467
|
+
export function sequenceLeadContacts(input) {
|
|
468
|
+
const phoneKey = input.settings?.phone_column_key ?? null;
|
|
469
|
+
return {
|
|
470
|
+
email: recipientEmailFromRow(input.rowValues, input.settings?.email_column_key ?? null) !== null,
|
|
471
|
+
linkedin: input.linkedin,
|
|
472
|
+
phone: callPhoneFromRow(input.rowValues, phoneKey) !== null || whatsAppAttendeeIdFromRow(input.rowValues, phoneKey) !== null,
|
|
473
|
+
};
|
|
474
|
+
}
|
|
427
475
|
const MAX_SEQUENCE_STEPS = 50;
|
|
428
476
|
const MAX_TEMPLATE_LENGTH = 8_000;
|
|
429
477
|
// The Premium ceiling, and only an authoring hint: the limit that actually
|
|
@@ -835,7 +883,7 @@ function collectConditionSignals(condition, out) {
|
|
|
835
883
|
// it contributes nothing — and must be handled BEFORE the not-fallthrough below
|
|
836
884
|
// (which would otherwise read condition.not off a data leaf). This is also why
|
|
837
885
|
// the native-untracked engagement-gate walker never trips on a has_column branch.
|
|
838
|
-
else if ("has_column" in condition)
|
|
886
|
+
else if ("has_column" in condition || "has_contact" in condition)
|
|
839
887
|
return;
|
|
840
888
|
else if ("all" in condition)
|
|
841
889
|
condition.all.forEach((c) => collectConditionSignals(c, out));
|
|
@@ -1810,6 +1858,13 @@ function normalizeCondition(raw, path, issues, depth = 0) {
|
|
|
1810
1858
|
return undefined;
|
|
1811
1859
|
return { has_column: key, present: raw.present === false ? false : true };
|
|
1812
1860
|
}
|
|
1861
|
+
if (raw.has_contact !== undefined) {
|
|
1862
|
+
if (typeof raw.has_contact !== "string" || !SEQUENCE_CONTACT_CHANNELS.includes(raw.has_contact)) {
|
|
1863
|
+
issues.push({ path: `${path}.has_contact`, message: `has_contact must be one of ${SEQUENCE_CONTACT_CHANNELS.join(", ")}.` });
|
|
1864
|
+
return undefined;
|
|
1865
|
+
}
|
|
1866
|
+
return { has_contact: raw.has_contact, present: raw.present === false ? false : true };
|
|
1867
|
+
}
|
|
1813
1868
|
if (Array.isArray(raw.all)) {
|
|
1814
1869
|
return { all: normalizeConditionList(raw.all, `${path}.all`, issues, depth) };
|
|
1815
1870
|
}
|
|
@@ -1820,7 +1875,7 @@ function normalizeCondition(raw, path, issues, depth = 0) {
|
|
|
1820
1875
|
const inner = normalizeCondition(raw.not, `${path}.not`, issues, depth + 1);
|
|
1821
1876
|
return inner ? { not: inner } : undefined;
|
|
1822
1877
|
}
|
|
1823
|
-
issues.push({ path, message: "condition must be {signal}, {has_column}, {all}, {any}, or {not}." });
|
|
1878
|
+
issues.push({ path, message: "condition must be {signal}, {has_column}, {has_contact}, {all}, {any}, or {not}." });
|
|
1824
1879
|
return undefined;
|
|
1825
1880
|
}
|
|
1826
1881
|
function normalizeConditionList(raw, path, issues, depth) {
|
|
@@ -2103,9 +2158,10 @@ export function sequenceTemplateVariables(definition) {
|
|
|
2103
2158
|
* `rowValues` is optional and backward-compatible: when omitted (undefined), a data
|
|
2104
2159
|
* leaf ({ has_column }) evaluates as ABSENT — present:false matches, present:true
|
|
2105
2160
|
* does not — so an existing signal-only caller is unaffected. Signal leaves ignore
|
|
2106
|
-
* rowValues entirely.
|
|
2161
|
+
* rowValues entirely. A contact leaf ({ has_contact }) reads `contacts`; without
|
|
2162
|
+
* it, the contact counts as absent.
|
|
2107
2163
|
*/
|
|
2108
|
-
export function evaluateSequenceCondition(condition, firedSignals, rowValues) {
|
|
2164
|
+
export function evaluateSequenceCondition(condition, firedSignals, rowValues, contacts) {
|
|
2109
2165
|
const fired = firedSignals instanceof Set ? firedSignals : new Set(firedSignals);
|
|
2110
2166
|
if ("signal" in condition) {
|
|
2111
2167
|
const present = fired.has(condition.signal);
|
|
@@ -2115,11 +2171,15 @@ export function evaluateSequenceCondition(condition, firedSignals, rowValues) {
|
|
|
2115
2171
|
const present = rowValueIsPresent(rowValues?.[condition.has_column]);
|
|
2116
2172
|
return condition.present === false ? !present : present;
|
|
2117
2173
|
}
|
|
2174
|
+
if ("has_contact" in condition) {
|
|
2175
|
+
const present = contacts?.[condition.has_contact] === true;
|
|
2176
|
+
return condition.present === false ? !present : present;
|
|
2177
|
+
}
|
|
2118
2178
|
if ("all" in condition)
|
|
2119
|
-
return condition.all.every((c) => evaluateSequenceCondition(c, fired, rowValues));
|
|
2179
|
+
return condition.all.every((c) => evaluateSequenceCondition(c, fired, rowValues, contacts));
|
|
2120
2180
|
if ("any" in condition)
|
|
2121
|
-
return condition.any.some((c) => evaluateSequenceCondition(c, fired, rowValues));
|
|
2122
|
-
return !evaluateSequenceCondition(condition.not, fired, rowValues);
|
|
2181
|
+
return condition.any.some((c) => evaluateSequenceCondition(c, fired, rowValues, contacts));
|
|
2182
|
+
return !evaluateSequenceCondition(condition.not, fired, rowValues, contacts);
|
|
2123
2183
|
}
|
|
2124
2184
|
/**
|
|
2125
2185
|
* Whether a lead's row_values value counts as "present" for a data-leaf branch: a
|
|
@@ -29,6 +29,8 @@ export declare function teammatesAvailableTo(env: Record<string, string | undefi
|
|
|
29
29
|
export declare const DEFAULT_TEAMMATE_SLUG = "workspace";
|
|
30
30
|
/** What an Oxygent is called until someone names it, as Grok Bot's are (Tim, 2026-10-13). */
|
|
31
31
|
export declare const NEW_TEAMMATE_NAME = "New Bot";
|
|
32
|
+
/** What the first Oxygent is called until someone renames it (Philipp, 2026-10-19). */
|
|
33
|
+
export declare const DEFAULT_TEAMMATE_NAME = "Oxygent";
|
|
32
34
|
export declare const TEAMMATE_NAME_MAX_CHARS = 80;
|
|
33
35
|
export declare const TEAMMATE_DESCRIPTION_MAX_CHARS = 500;
|
|
34
36
|
/**
|
|
@@ -79,7 +81,7 @@ export declare function teammateDefaultLook(slug: string): {
|
|
|
79
81
|
};
|
|
80
82
|
/**
|
|
81
83
|
* How an Oxygent presents: its name, what it does and its look. The first
|
|
82
|
-
* Oxygent is "
|
|
84
|
+
* Oxygent is "Oxygent" until its owner names it; nothing renames the stored
|
|
83
85
|
* Agent to get there, and the Workspace Agent's stock description is no job.
|
|
84
86
|
*/
|
|
85
87
|
export declare function teammatePresentation(agent: {
|
|
@@ -30,6 +30,8 @@ export function teammatesAvailableTo(env, viewer) {
|
|
|
30
30
|
export const DEFAULT_TEAMMATE_SLUG = "workspace";
|
|
31
31
|
/** What an Oxygent is called until someone names it, as Grok Bot's are (Tim, 2026-10-13). */
|
|
32
32
|
export const NEW_TEAMMATE_NAME = "New Bot";
|
|
33
|
+
/** What the first Oxygent is called until someone renames it (Philipp, 2026-10-19). */
|
|
34
|
+
export const DEFAULT_TEAMMATE_NAME = "Oxygent";
|
|
33
35
|
const LEGACY_WORKSPACE_AGENT_NAME = "Workspace Agent";
|
|
34
36
|
const LEGACY_WORKSPACE_AGENT_DESCRIPTION = "General organization-grounded workspace worker.";
|
|
35
37
|
export const TEAMMATE_NAME_MAX_CHARS = 80;
|
|
@@ -103,12 +105,12 @@ export function teammateDefaultLook(slug) {
|
|
|
103
105
|
}
|
|
104
106
|
/**
|
|
105
107
|
* How an Oxygent presents: its name, what it does and its look. The first
|
|
106
|
-
* Oxygent is "
|
|
108
|
+
* Oxygent is "Oxygent" until its owner names it; nothing renames the stored
|
|
107
109
|
* Agent to get there, and the Workspace Agent's stock description is no job.
|
|
108
110
|
*/
|
|
109
111
|
export function teammatePresentation(agent) {
|
|
110
112
|
const first = agent.slug === DEFAULT_TEAMMATE_SLUG;
|
|
111
|
-
const name = first && agent.name === LEGACY_WORKSPACE_AGENT_NAME ?
|
|
113
|
+
const name = first && agent.name === LEGACY_WORKSPACE_AGENT_NAME ? DEFAULT_TEAMMATE_NAME : agent.name;
|
|
112
114
|
const stored = agent.description?.trim() ?? "";
|
|
113
115
|
const description = first && stored === LEGACY_WORKSPACE_AGENT_DESCRIPTION ? "" : stored;
|
|
114
116
|
const look = teammateDefaultLook(agent.slug);
|
|
@@ -14,6 +14,19 @@ export function capabilityDiscoveryRecovery(query, surface) {
|
|
|
14
14
|
// connection routes.
|
|
15
15
|
export function inferUserCapabilityRoute(query) {
|
|
16
16
|
const normalized = query.toLowerCase().replace(/[_-]+/g, " ");
|
|
17
|
+
if (/\b(?:buy|purchase|order|provision|subscribe)\b/.test(normalized)
|
|
18
|
+
&& /\b(?:inbox(?:es)?|mailbox(?:es)?)\b/.test(normalized)
|
|
19
|
+
&& !/\b(?:buy|purchase|order)\s+(?:more\s+)?credits?\b|^\s*(?:cancel|delete|remove)\b/.test(normalized)) {
|
|
20
|
+
const route = getCapabilityRouteMatch("sending-infrastructure");
|
|
21
|
+
if (route) {
|
|
22
|
+
const recommendedTools = ["oxygen_managed_inboxes_subscribe", "oxygen_managed_inboxes_list", "oxygen_domains_list", "oxygen_managed_inboxes_add_mailboxes"];
|
|
23
|
+
const recommendedCommands = ["managed-inboxes subscribe", "managed-inboxes list", "domains list", "managed-inboxes add-inboxes"];
|
|
24
|
+
return { ...route, recommendedTools, recommendedCommands, card: { ...route.card,
|
|
25
|
+
gatewayTools: recommendedTools, gatewayCommands: recommendedCommands,
|
|
26
|
+
execution: "Inspect owned domains, existing subscriptions and occupied/pending addresses before quoting. An owned domain without a subscription uses managed-inboxes subscribe with existing_domain=true (CLI --existing-domain), with no registration charge; a managed subscription uses add-inboxes. Preserve the requested provider and total per-domain cap. Preview first; exact quote approval authorizes only its inboxes and recurring cost. Warmup is included only when warmup_activation.automatic_after_provisioning=true. Discovery buys nothing and grants no spend authority.",
|
|
27
|
+
} };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
17
30
|
if (/\breply oxygent\b/.test(normalized) || (/\brepl(?:y|ies)\b/.test(normalized) && /\bbatch(?:es)?\b/.test(normalized)
|
|
18
31
|
&& /\b(?:approv(?:al|e)|voice|writing style)\b/.test(normalized))) {
|
|
19
32
|
const route = getCapabilityRouteMatch("messages");
|
|
@@ -26,22 +39,6 @@ export function inferUserCapabilityRoute(query) {
|
|
|
26
39
|
} };
|
|
27
40
|
}
|
|
28
41
|
}
|
|
29
|
-
const journeyAction = savedOnboardingJourneyAction(normalized);
|
|
30
|
-
if (journeyAction) {
|
|
31
|
-
const route = getCapabilityRouteMatch("agents");
|
|
32
|
-
if (route) {
|
|
33
|
-
const recommendedTools = [`oxygen_onboarding_journey_${journeyAction}`, ...(journeyAction === "get" ? [] : ["oxygen_onboarding_journey_get"])];
|
|
34
|
-
const recommendedCommands = [`onboarding journey ${journeyAction}`, ...(journeyAction === "get" ? [] : ["onboarding journey get"])];
|
|
35
|
-
return { ...route, recommendedTools, recommendedCommands, card: { ...route.card,
|
|
36
|
-
owns: "Creator-private saved guided onboarding answers, current step and pause/resume position.",
|
|
37
|
-
notFor: "Human workspace invitations or permissions, destructive workspace reset, paid Oxygent Start or routine execution.",
|
|
38
|
-
gatewayTools: recommendedTools, gatewayCommands: recommendedCommands,
|
|
39
|
-
posture: journeyAction === "get" ? "read_only" : "workspace_write",
|
|
40
|
-
execution: journeyAction === "get" ? "Read saved onboarding and its current native step. This read starts no research, model, billing reconciliation or message send."
|
|
41
|
-
: "Read activationId and current revision first, then hydrate the selected native operation. Use a stable mutation_id; Back and non-destructive restart preserve resources and funding. Company answers prepare native sources and existing bounded research consent. Discovery executes nothing and supplies no approval or billing authority.",
|
|
42
|
-
} };
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
42
|
// Exact native session activation must outrank the generic Agent gateways.
|
|
46
43
|
// Discovery supplies no approval, preview token or invented session binding.
|
|
47
44
|
if (/\bagent session activate\b/.test(normalized)
|
|
@@ -108,13 +105,13 @@ export function inferUserCapabilityRoute(query) {
|
|
|
108
105
|
if (isGuidedOnboardingDiscoveryIntent(normalized)) {
|
|
109
106
|
const route = getCapabilityRouteMatch("onboarding-and-copilot");
|
|
110
107
|
if (route) {
|
|
111
|
-
const recommendedTools = ["oxygen_onboarding_activation_status"
|
|
112
|
-
const recommendedCommands = ["onboarding activation status"
|
|
108
|
+
const recommendedTools = ["oxygen_onboarding_activation_status"];
|
|
109
|
+
const recommendedCommands = ["onboarding activation status"];
|
|
113
110
|
return { ...route, recommendedTools, recommendedCommands, card: { ...route.card,
|
|
114
|
-
owns: "Creator-owned DEV guided onboarding
|
|
111
|
+
owns: "Creator-owned DEV guided onboarding activation and its native resources.",
|
|
115
112
|
notFor: "Production onboarding execution, human membership or ordinary GTM consulting.",
|
|
116
113
|
posture: "read_only", gatewayTools: recommendedTools, gatewayCommands: recommendedCommands,
|
|
117
|
-
execution: "DEV-only
|
|
114
|
+
execution: "DEV-only guided onboarding runs in the workspace Oxygent chat; unavailable in production, shadow, local or production-data workspaces. Read your activation first; its activationId binds native resources and routines. If absent, inspect the free activation-start schema and choose own_agent or oxygent. The Oxygent researches the company and proposes plays; campaigns and routines keep their own previews and approvals. This read starts no research, model, schedule or send. Ordinary GTM consulting remains with Recipes/Copilot.",
|
|
118
115
|
}, exactCommand: "oxygen onboarding activation status --json",
|
|
119
116
|
exactMcpCall: { tool: "oxygen_onboarding_activation_status", arguments: {} } };
|
|
120
117
|
}
|
|
@@ -217,26 +214,8 @@ export function inferUserCapabilityRoute(query) {
|
|
|
217
214
|
// intent is exactly what this wrapper exists to answer.
|
|
218
215
|
return getCapabilityRouteMatch("connected-integrations") ?? route;
|
|
219
216
|
}
|
|
220
|
-
/**
|
|
221
|
-
*
|
|
222
|
-
function savedOnboardingJourneyAction(query) {
|
|
223
|
-
if (!/\bonboarding\b/.test(query) || !/\b(?:journey|guided|answers?|steps?|tour)\b/.test(query)
|
|
224
|
-
|| /\b(?:invite|members?|membership|permissions?|roles?|human|colleague|coworker|delete|destroy|wipe)\b/.test(query))
|
|
225
|
-
return null;
|
|
226
|
-
if (/\b(?:pause|paused|navigation)\b/.test(query) || (/\btour\b/.test(query) && /\bresume\b/.test(query)))
|
|
227
|
-
return "navigation";
|
|
228
|
-
if (/\brestart\b/.test(query))
|
|
229
|
-
return "restart";
|
|
230
|
-
if (/\bback\b/.test(query))
|
|
231
|
-
return "back";
|
|
232
|
-
if (/\bsave\b/.test(query))
|
|
233
|
-
return "save";
|
|
234
|
-
if (/\b(?:resume|read|get)\b/.test(query))
|
|
235
|
-
return "get";
|
|
236
|
-
return null;
|
|
237
|
-
}
|
|
238
|
-
/** A saved action is handled first. This is discovery of the multi-step
|
|
239
|
-
* product journey, not generic advice about improving a GTM operation. */
|
|
217
|
+
/** Discovery of the guided DEV onboarding activation, not generic advice
|
|
218
|
+
* about improving a GTM operation. */
|
|
240
219
|
function isGuidedOnboardingDiscoveryIntent(query) {
|
|
241
220
|
if (!/\bonboarding\b/.test(query)
|
|
242
221
|
|| /\b(?:invite|members?|membership|permissions?|roles?|human|colleague|coworker|delete|destroy|wipe|paid)\b/.test(query))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const OXYGEN_BUILD_VERSION = "1.1014.
|
|
1
|
+
export declare const OXYGEN_BUILD_VERSION = "1.1014.1420";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// GENERATED by scripts/ci/version-stamp.mjs from VERSION + git history. Do not edit; do not commit.
|
|
2
|
-
export const OXYGEN_BUILD_VERSION = "1.1014.
|
|
2
|
+
export const OXYGEN_BUILD_VERSION = "1.1014.1420";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { CliHttpError } from "./cli-http-error.js";
|
|
3
|
+
import { placementCopyIssue } from "./placement-copy.js";
|
|
3
4
|
const UUID = /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/i;
|
|
4
5
|
export function workspacePlacementId(value) { return typeof value === "string" && UUID.test(value); }
|
|
5
6
|
function invalid(message) { throw new CliHttpError(400, "workspace_test_invalid", message); }
|
|
@@ -21,6 +22,9 @@ export function readWorkspacePlacementVariants(value) {
|
|
|
21
22
|
invalid("body_html must contain between one and 200,000 characters when supplied.");
|
|
22
23
|
if (/\{\{[^}]+\}\}/.test(v.subject + v.body_text + (v.body_html ?? "")))
|
|
23
24
|
invalid("Resolve personalization variables before testing the exact copy.");
|
|
25
|
+
const copyIssue = placementCopyIssue(v.subject, v.body_text, v.body_html);
|
|
26
|
+
if (copyIssue)
|
|
27
|
+
invalid(copyIssue);
|
|
24
28
|
keys.add(v.key);
|
|
25
29
|
return { key: v.key, subject: v.subject, body_text: v.body_text, ...(typeof v.body_html === "string" ? { body_html: v.body_html } : {}) };
|
|
26
30
|
});
|
package/package.json
CHANGED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { type ContractIssue } from "./schema.js";
|
|
2
|
-
import type { OperationSpec } from "./operations.js";
|
|
3
|
-
export declare const GUIDED_COMPANY_FILE_UPLOAD_CONTRACT: import("./schema.js").ContractObject<{
|
|
4
|
-
readonly revision: {
|
|
5
|
-
type: "integer";
|
|
6
|
-
description?: string;
|
|
7
|
-
minimum?: number;
|
|
8
|
-
maximum?: number;
|
|
9
|
-
"x-clamp"?: true;
|
|
10
|
-
};
|
|
11
|
-
readonly upload_id: import("./schema.js").StringProperty;
|
|
12
|
-
readonly sha256: import("./schema.js").StringProperty;
|
|
13
|
-
readonly size_bytes: {
|
|
14
|
-
type: "integer";
|
|
15
|
-
description?: string;
|
|
16
|
-
minimum?: number;
|
|
17
|
-
maximum?: number;
|
|
18
|
-
"x-clamp"?: true;
|
|
19
|
-
};
|
|
20
|
-
readonly file_name: import("./schema.js").StringProperty;
|
|
21
|
-
readonly content_type: {
|
|
22
|
-
type: "string";
|
|
23
|
-
enum: readonly ("application/pdf" | "text/markdown" | "text/plain")[];
|
|
24
|
-
description?: string;
|
|
25
|
-
};
|
|
26
|
-
readonly content_base64: import("./schema.js").StringProperty;
|
|
27
|
-
}, "content_base64" | "content_type" | "file_name" | "revision" | "sha256" | "size_bytes" | "upload_id">;
|
|
28
|
-
/** Raw wire values are never trimmed or renamed; the native owner verifies original SHA/bytes. */
|
|
29
|
-
export declare function guidedCompanyFileUploadInputIssues(args: Readonly<Record<string, unknown>>): readonly ContractIssue[];
|
|
30
|
-
export declare const GUIDED_COMPANY_FILE_UPLOAD_OPERATION: OperationSpec;
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { GUIDED_COMPANY_FILE_CONTENT_TYPES, GUIDED_COMPANY_FILE_LIMITS, guidedCompanyFileByteLimit } from "../guided-company-files.js";
|
|
2
|
-
import { enumOf, int, obj, str } from "./schema.js";
|
|
3
|
-
const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu;
|
|
4
|
-
export const GUIDED_COMPANY_FILE_UPLOAD_CONTRACT = obj({
|
|
5
|
-
revision: int({ minimum: 1 }), upload_id: str({ pattern: "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" }),
|
|
6
|
-
sha256: str({ pattern: "^[0-9a-f]{64}$" }), size_bytes: int({ minimum: 1, maximum: GUIDED_COMPANY_FILE_LIMITS.pdfBytes }),
|
|
7
|
-
file_name: str({ minLength: 1, maxLength: GUIDED_COMPANY_FILE_LIMITS.nameBytes }),
|
|
8
|
-
content_type: enumOf(GUIDED_COMPANY_FILE_CONTENT_TYPES),
|
|
9
|
-
content_base64: str({ minLength: 4, maxLength: 4 * Math.ceil(GUIDED_COMPANY_FILE_LIMITS.pdfBytes / 3) }),
|
|
10
|
-
}, { required: ["revision", "upload_id", "sha256", "size_bytes", "file_name", "content_type", "content_base64"] });
|
|
11
|
-
/** Raw wire values are never trimmed or renamed; the native owner verifies original SHA/bytes. */
|
|
12
|
-
export function guidedCompanyFileUploadInputIssues(args) {
|
|
13
|
-
const issues = [];
|
|
14
|
-
const issue = (path, message) => issues.push({ path, message });
|
|
15
|
-
for (const key of ["activationId", "upload_id"])
|
|
16
|
-
if (typeof args[key] !== "string" || !uuid.test(args[key]))
|
|
17
|
-
issue(key, "Use a native UUID.");
|
|
18
|
-
const name = args.file_name;
|
|
19
|
-
if (typeof name !== "string" || !name.trim() || /[\\/\u0000-\u001f\u007f]/u.test(name) || new TextEncoder().encode(name).length > GUIDED_COMPANY_FILE_LIMITS.nameBytes)
|
|
20
|
-
issue("file_name", "Use a filename without paths or controls, at most 255 UTF-8 bytes.");
|
|
21
|
-
const type = args.content_type;
|
|
22
|
-
if (!GUIDED_COMPANY_FILE_CONTENT_TYPES.includes(type))
|
|
23
|
-
issue("content_type", "Use UTF-8 text, Markdown or PDF.");
|
|
24
|
-
else if (typeof args.size_bytes !== "number" || args.size_bytes > guidedCompanyFileByteLimit(type))
|
|
25
|
-
issue("size_bytes", "Use at most 1500000 text bytes or 5242880 PDF bytes.");
|
|
26
|
-
const encoded = args.content_base64;
|
|
27
|
-
if (typeof encoded !== "string" || !encoded || encoded.length % 4 !== 0 || /[^A-Za-z0-9+/=]/u.test(encoded) || !/^[^=]*={0,2}$/u.test(encoded))
|
|
28
|
-
issue("content_base64", "Use canonical non-empty base64 file bytes.");
|
|
29
|
-
else {
|
|
30
|
-
try {
|
|
31
|
-
const decoded = atob(encoded);
|
|
32
|
-
if (btoa(decoded) !== encoded)
|
|
33
|
-
issue("content_base64", "Use canonical base64 without altered padding bits.");
|
|
34
|
-
if (decoded.length !== args.size_bytes)
|
|
35
|
-
issue("size_bytes", "size_bytes must equal the original decoded byte length.");
|
|
36
|
-
}
|
|
37
|
-
catch {
|
|
38
|
-
issue("content_base64", "Use valid base64 file bytes.");
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return issues;
|
|
42
|
-
}
|
|
43
|
-
export const GUIDED_COMPANY_FILE_UPLOAD_OPERATION = {
|
|
44
|
-
id: "onboarding.company_files.upload", route: "/api/cli/onboarding/activation/{activationId}/company-files", method: "POST",
|
|
45
|
-
parameters: ["activationId"], contract: GUIDED_COMPANY_FILE_UPLOAD_CONTRACT, inputCheck: guidedCompanyFileUploadInputIssues,
|
|
46
|
-
multipart: { contentField: "content_base64", nameField: "file_name", typeField: "content_type", fileField: "file",
|
|
47
|
-
fileMetadata: { sha256Field: "sha256", sizeBytesField: "size_bytes" } },
|
|
48
|
-
cli: { path: ["onboarding", "company-files", "upload"], summary: "Upload an optional company source file for your private DEV onboarding activation." },
|
|
49
|
-
mcp: { name: "oxygen_onboarding_company_files_upload", title: "Oxygen: Upload onboarding company file", pack: "agents",
|
|
50
|
-
description: "Current creator and revision only, genuine enabled DEV: upload UTF-8 text/Markdown up to 1500000 bytes or PDF up to 5242880 bytes. Supply original filename, MIME, size and SHA-256 plus stable upload_id UUID; exact retries reuse the native file. PDF retains original bytes with text unavailable. No account connection, parser, model or message send.",
|
|
51
|
-
annotations: { destructiveHint: false, openWorldHint: true, idempotentHint: true } },
|
|
52
|
-
mutates: true, spends: false,
|
|
53
|
-
};
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import { type ContractIssue } from "./schema.js";
|
|
2
|
-
import type { OperationSpec } from "./operations.js";
|
|
3
|
-
/** Matches the actual native people-search and page safety ceilings. */
|
|
4
|
-
export declare const GUIDED_FIRST_SOURCING_LIMITS: {
|
|
5
|
-
readonly targetCount: 50000;
|
|
6
|
-
readonly maxPages: 1000;
|
|
7
|
-
};
|
|
8
|
-
export declare const GUIDED_FIRST_SOURCING_BOUNDS_CONTRACT: import("./schema.js").ContractObject<{
|
|
9
|
-
readonly targetCount: {
|
|
10
|
-
type: "integer";
|
|
11
|
-
description?: string;
|
|
12
|
-
minimum?: number;
|
|
13
|
-
maximum?: number;
|
|
14
|
-
"x-clamp"?: true;
|
|
15
|
-
};
|
|
16
|
-
readonly maxPages: {
|
|
17
|
-
type: "integer";
|
|
18
|
-
description?: string;
|
|
19
|
-
minimum?: number;
|
|
20
|
-
maximum?: number;
|
|
21
|
-
"x-clamp"?: true;
|
|
22
|
-
};
|
|
23
|
-
readonly maxCredits: {
|
|
24
|
-
readonly type: "number";
|
|
25
|
-
readonly description?: string;
|
|
26
|
-
readonly minimum?: number;
|
|
27
|
-
readonly maximum?: number;
|
|
28
|
-
readonly "x-clamp"?: true;
|
|
29
|
-
readonly exclusiveMinimum: 0;
|
|
30
|
-
};
|
|
31
|
-
}, "maxCredits" | "maxPages" | "targetCount">;
|
|
32
|
-
export declare const GUIDED_FIRST_SOURCING_PREVIEW_CONTRACT: import("./schema.js").ContractObject<{
|
|
33
|
-
revision: {
|
|
34
|
-
type: "integer";
|
|
35
|
-
description?: string;
|
|
36
|
-
minimum?: number;
|
|
37
|
-
maximum?: number;
|
|
38
|
-
"x-clamp"?: true;
|
|
39
|
-
};
|
|
40
|
-
bounds: import("./schema.js").ContractObject<{
|
|
41
|
-
readonly targetCount: {
|
|
42
|
-
type: "integer";
|
|
43
|
-
description?: string;
|
|
44
|
-
minimum?: number;
|
|
45
|
-
maximum?: number;
|
|
46
|
-
"x-clamp"?: true;
|
|
47
|
-
};
|
|
48
|
-
readonly maxPages: {
|
|
49
|
-
type: "integer";
|
|
50
|
-
description?: string;
|
|
51
|
-
minimum?: number;
|
|
52
|
-
maximum?: number;
|
|
53
|
-
"x-clamp"?: true;
|
|
54
|
-
};
|
|
55
|
-
readonly maxCredits: {
|
|
56
|
-
readonly type: "number";
|
|
57
|
-
readonly description?: string;
|
|
58
|
-
readonly minimum?: number;
|
|
59
|
-
readonly maximum?: number;
|
|
60
|
-
readonly "x-clamp"?: true;
|
|
61
|
-
readonly exclusiveMinimum: 0;
|
|
62
|
-
};
|
|
63
|
-
}, "maxCredits" | "maxPages" | "targetCount">;
|
|
64
|
-
}, "bounds" | "revision">;
|
|
65
|
-
export declare const GUIDED_FIRST_SOURCING_RUN_CONTRACT: import("./schema.js").ContractObject<{
|
|
66
|
-
readonly revision: {
|
|
67
|
-
type: "integer";
|
|
68
|
-
description?: string;
|
|
69
|
-
minimum?: number;
|
|
70
|
-
maximum?: number;
|
|
71
|
-
"x-clamp"?: true;
|
|
72
|
-
};
|
|
73
|
-
readonly bounds: import("./schema.js").ContractObject<{
|
|
74
|
-
readonly targetCount: {
|
|
75
|
-
type: "integer";
|
|
76
|
-
description?: string;
|
|
77
|
-
minimum?: number;
|
|
78
|
-
maximum?: number;
|
|
79
|
-
"x-clamp"?: true;
|
|
80
|
-
};
|
|
81
|
-
readonly maxPages: {
|
|
82
|
-
type: "integer";
|
|
83
|
-
description?: string;
|
|
84
|
-
minimum?: number;
|
|
85
|
-
maximum?: number;
|
|
86
|
-
"x-clamp"?: true;
|
|
87
|
-
};
|
|
88
|
-
readonly maxCredits: {
|
|
89
|
-
readonly type: "number";
|
|
90
|
-
readonly description?: string;
|
|
91
|
-
readonly minimum?: number;
|
|
92
|
-
readonly maximum?: number;
|
|
93
|
-
readonly "x-clamp"?: true;
|
|
94
|
-
readonly exclusiveMinimum: 0;
|
|
95
|
-
};
|
|
96
|
-
}, "maxCredits" | "maxPages" | "targetCount">;
|
|
97
|
-
readonly previewId: import("./schema.js").StringProperty;
|
|
98
|
-
readonly previewDigest: import("./schema.js").StringProperty;
|
|
99
|
-
readonly approved: import("./schema.js").BooleanProperty;
|
|
100
|
-
}, "approved" | "bounds" | "previewDigest" | "previewId" | "revision">;
|
|
101
|
-
export declare const GUIDED_FIRST_SOURCING_GET_CONTRACT: import("./schema.js").ContractObject<{
|
|
102
|
-
readonly revision: {
|
|
103
|
-
type: "integer";
|
|
104
|
-
description?: string;
|
|
105
|
-
minimum?: number;
|
|
106
|
-
maximum?: number;
|
|
107
|
-
"x-clamp"?: true;
|
|
108
|
-
};
|
|
109
|
-
readonly generation: {
|
|
110
|
-
type: "integer";
|
|
111
|
-
description?: string;
|
|
112
|
-
minimum?: number;
|
|
113
|
-
maximum?: number;
|
|
114
|
-
"x-clamp"?: true;
|
|
115
|
-
};
|
|
116
|
-
readonly timezoneHint: import("./schema.js").StringProperty;
|
|
117
|
-
}, never>;
|
|
118
|
-
export declare function guidedFirstSourcingGetInputIssues(args: Readonly<Record<string, unknown>>): readonly ContractIssue[];
|
|
119
|
-
export type GuidedFirstSourcingAction = "get" | "preview" | "run";
|
|
120
|
-
export declare const GUIDED_FIRST_SOURCING_OPERATIONS: readonly OperationSpec[];
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { bool, int, num, obj, str } from "./schema.js";
|
|
2
|
-
import { isValidTimeZone } from "../zoned-time.js";
|
|
3
|
-
/** Matches the actual native people-search and page safety ceilings. */
|
|
4
|
-
export const GUIDED_FIRST_SOURCING_LIMITS = { targetCount: 50000, maxPages: 1000 };
|
|
5
|
-
export const GUIDED_FIRST_SOURCING_BOUNDS_CONTRACT = obj({
|
|
6
|
-
targetCount: int({ minimum: 1, maximum: GUIDED_FIRST_SOURCING_LIMITS.targetCount }),
|
|
7
|
-
maxPages: int({ minimum: 1, maximum: GUIDED_FIRST_SOURCING_LIMITS.maxPages }),
|
|
8
|
-
maxCredits: { ...num(), exclusiveMinimum: 0 },
|
|
9
|
-
}, { required: ["targetCount", "maxPages", "maxCredits"] });
|
|
10
|
-
const request = { revision: int({ minimum: 1 }), bounds: GUIDED_FIRST_SOURCING_BOUNDS_CONTRACT };
|
|
11
|
-
export const GUIDED_FIRST_SOURCING_PREVIEW_CONTRACT = obj(request, { required: ["revision", "bounds"] });
|
|
12
|
-
export const GUIDED_FIRST_SOURCING_RUN_CONTRACT = obj({ ...request,
|
|
13
|
-
previewId: str({ minLength: 36, maxLength: 36, pattern: "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" }),
|
|
14
|
-
previewDigest: str({ minLength: 64, maxLength: 64, pattern: "^[a-f0-9]{64}$" }), approved: bool(),
|
|
15
|
-
}, { required: ["revision", "bounds", "previewId", "previewDigest", "approved"] });
|
|
16
|
-
export const GUIDED_FIRST_SOURCING_GET_CONTRACT = obj({
|
|
17
|
-
revision: int({ minimum: 1, maximum: Number.MAX_SAFE_INTEGER }),
|
|
18
|
-
generation: int({ minimum: 1, maximum: Number.MAX_SAFE_INTEGER }),
|
|
19
|
-
timezoneHint: str({ minLength: 1, maxLength: 80, description: "Provisional saved/browser IANA hint for current nightly defaults; never confirms a schedule." }),
|
|
20
|
-
});
|
|
21
|
-
export function guidedFirstSourcingGetInputIssues(args) {
|
|
22
|
-
const fields = ["revision", "generation", "timezoneHint"];
|
|
23
|
-
if (!fields.some(field => args[field] !== undefined))
|
|
24
|
-
return [];
|
|
25
|
-
const issues = fields.filter(field => args[field] === undefined).map(path => ({ path,
|
|
26
|
-
message: "Supply revision, generation and timezoneHint together to read current routine defaults." }));
|
|
27
|
-
if (typeof args.timezoneHint !== "string" || args.timezoneHint !== args.timezoneHint.trim() || !isValidTimeZone(args.timezoneHint)) {
|
|
28
|
-
issues.push({ path: "timezoneHint", message: "Use a valid customer/browser IANA timezone suggestion; no schedule is confirmed." });
|
|
29
|
-
}
|
|
30
|
-
return issues;
|
|
31
|
-
}
|
|
32
|
-
const base = "/api/cli/onboarding/activation/{activationId}/first-sourcing";
|
|
33
|
-
export const GUIDED_FIRST_SOURCING_OPERATIONS = [
|
|
34
|
-
{ id: "onboarding.first_sourcing.get", route: base, method: "GET", contract: GUIDED_FIRST_SOURCING_GET_CONTRACT, inputCheck: guidedFirstSourcingGetInputIssues, parameters: ["activationId"],
|
|
35
|
-
cli: { path: ["onboarding", "first-sourcing", "get"], summary: "Read first sourcing; optionally quote current nightly defaults with revision, generation and timezoneHint." },
|
|
36
|
-
mcp: { name: "oxygen_onboarding_first_sourcing_get", title: "Oxygen: Read first sourcing", pack: "agents",
|
|
37
|
-
description: "Read the current creator-private DEV source, native ingestion receipt and actual settled continuation. No sourcing provider call or new sourcing; historical approval cannot authorize another pull. Verifies native billing and may reconcile an earned grant. Optional revision, generation and provisional IANA timezoneHint together return current native nightly defaults after the source transaction; no draft, schedule confirmation or approval.", annotations: { openWorldHint: true } }, mutates: true, spends: false },
|
|
38
|
-
{ id: "onboarding.first_sourcing.preview", route: `${base}/preview`, method: "POST", parameters: ["activationId"], contract: GUIDED_FIRST_SOURCING_PREVIEW_CONTRACT,
|
|
39
|
-
cli: { path: ["onboarding", "first-sourcing", "preview"], summary: "Review a bounded native first pull into First leads; no provider search." },
|
|
40
|
-
mcp: { name: "oxygen_onboarding_first_sourcing_preview", title: "Oxygen: Preview first sourcing", pack: "agents",
|
|
41
|
-
description: "Current creator and revision, confirmed company, saved audience and eligible trial/paid billing required. Bounds contain targetCount, maxPages and maxCredits. Creates/reuses the native First leads Table and persists a managed source quote. Returns previewId/digest; no provider search, model or outreach.",
|
|
42
|
-
annotations: { openWorldHint: true, destructiveHint: false } }, mutates: true, spends: false },
|
|
43
|
-
{ id: "onboarding.first_sourcing.run", route: `${base}/run`, method: "POST", parameters: ["activationId"], contract: GUIDED_FIRST_SOURCING_RUN_CONTRACT,
|
|
44
|
-
cli: { path: ["onboarding", "first-sourcing", "run"], summary: "Approve the exact stored preview and caps; enqueue or recover the same native first-source run." },
|
|
45
|
-
mcp: { name: "oxygen_onboarding_first_sourcing_run", title: "Oxygen: Run first sourcing", pack: "agents",
|
|
46
|
-
description: "approved=true plus stored previewId/previewDigest, current revision and identical bounds authorize the reviewed managed people source into the activation's First leads Table. Current native admission, billing, quote and worker fences apply. Retry/Back/Restart with the same actual source recovers its native run. No model, enrollment or outreach.",
|
|
47
|
-
annotations: { openWorldHint: true, destructiveHint: false, idempotentHint: true } }, mutates: true, spends: true },
|
|
48
|
-
];
|