@oxygen-agent/cli 1.1014.1035 → 1.1014.1132

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 (77) hide show
  1. package/README.md +1 -1
  2. package/dist/command-manifest.js +1 -0
  3. package/dist/commands/columns.js +1 -1
  4. package/dist/commands/deliverability.js +2 -0
  5. package/dist/commands/sequences.js +30 -2
  6. package/dist/commands/viewers.js +1 -2
  7. package/dist/credentials.js +1 -1
  8. package/dist/customer-operation-commands.js +62 -3
  9. package/dist/help.js +1 -1
  10. package/dist/index.d.ts +0 -97
  11. package/dist/index.js +0 -98
  12. package/dist/workspace-placement-commands.d.ts +3 -0
  13. package/dist/workspace-placement-commands.js +42 -0
  14. package/node_modules/@oxygen/shared/dist/agent-sessions.d.ts +6 -1
  15. package/node_modules/@oxygen/shared/dist/agent-sessions.js +18 -6
  16. package/node_modules/@oxygen/shared/dist/agents-enabled.d.ts +8 -9
  17. package/node_modules/@oxygen/shared/dist/agents-enabled.js +12 -8
  18. package/node_modules/@oxygen/shared/dist/capability-discovery.js +24 -6
  19. package/node_modules/@oxygen/shared/dist/column-autofill.js +2 -2
  20. package/node_modules/@oxygen/shared/dist/contracts/agent-sessions.js +1 -1
  21. package/node_modules/@oxygen/shared/dist/contracts/customer-operations.js +1 -1
  22. package/node_modules/@oxygen/shared/dist/contracts/guided-activation.js +5 -5
  23. package/node_modules/@oxygen/shared/dist/contracts/guided-company-files.d.ts +30 -0
  24. package/node_modules/@oxygen/shared/dist/contracts/guided-company-files.js +53 -0
  25. package/node_modules/@oxygen/shared/dist/contracts/guided-first-sourcing.d.ts +120 -0
  26. package/node_modules/@oxygen/shared/dist/contracts/guided-first-sourcing.js +48 -0
  27. package/node_modules/@oxygen/shared/dist/contracts/guided-onboarding-journey.d.ts +267 -0
  28. package/node_modules/@oxygen/shared/dist/contracts/guided-onboarding-journey.js +63 -0
  29. package/node_modules/@oxygen/shared/dist/contracts/operations.d.ts +6 -0
  30. package/node_modules/@oxygen/shared/dist/contracts/operations.js +9 -4
  31. package/node_modules/@oxygen/shared/dist/contracts/table-query-raw.d.ts +2 -0
  32. package/node_modules/@oxygen/shared/dist/contracts/table-query-raw.js +3 -0
  33. package/node_modules/@oxygen/shared/dist/contracts/teammates.js +9 -8
  34. package/node_modules/@oxygen/shared/dist/data-suppliers.d.ts +32 -2
  35. package/node_modules/@oxygen/shared/dist/data-suppliers.js +44 -4
  36. package/node_modules/@oxygen/shared/dist/email-tracking-token.d.ts +2 -0
  37. package/node_modules/@oxygen/shared/dist/email-tracking-token.js +1 -0
  38. package/node_modules/@oxygen/shared/dist/feature-gates.d.ts +3 -4
  39. package/node_modules/@oxygen/shared/dist/feature-gates.js +3 -4
  40. package/node_modules/@oxygen/shared/dist/file-import.d.ts +46 -0
  41. package/node_modules/@oxygen/shared/dist/file-import.js +138 -0
  42. package/node_modules/@oxygen/shared/dist/guided-activation.d.ts +8 -0
  43. package/node_modules/@oxygen/shared/dist/guided-company-files.d.ts +13 -0
  44. package/node_modules/@oxygen/shared/dist/guided-company-files.js +14 -0
  45. package/node_modules/@oxygen/shared/dist/guided-first-sourcing.d.ts +116 -0
  46. package/node_modules/@oxygen/shared/dist/guided-first-sourcing.js +11 -0
  47. package/node_modules/@oxygen/shared/dist/guided-onboarding-answers.d.ts +4 -0
  48. package/node_modules/@oxygen/shared/dist/guided-onboarding-answers.js +42 -0
  49. package/node_modules/@oxygen/shared/dist/guided-onboarding-company-context.d.ts +22 -0
  50. package/node_modules/@oxygen/shared/dist/guided-onboarding-company-context.js +38 -0
  51. package/node_modules/@oxygen/shared/dist/guided-onboarding-journey.d.ts +147 -0
  52. package/node_modules/@oxygen/shared/dist/guided-onboarding-journey.fixture.d.ts +12 -0
  53. package/node_modules/@oxygen/shared/dist/guided-onboarding-journey.fixture.js +83 -0
  54. package/node_modules/@oxygen/shared/dist/guided-onboarding-journey.js +44 -0
  55. package/node_modules/@oxygen/shared/dist/guided-onboarding-prospects.d.ts +7 -0
  56. package/node_modules/@oxygen/shared/dist/guided-onboarding-prospects.js +13 -0
  57. package/node_modules/@oxygen/shared/dist/guided-onboarding-signals.d.ts +4 -0
  58. package/node_modules/@oxygen/shared/dist/guided-onboarding-signals.js +34 -0
  59. package/node_modules/@oxygen/shared/dist/guided-trial-billing.d.ts +26 -0
  60. package/node_modules/@oxygen/shared/dist/guided-trial-billing.js +19 -0
  61. package/node_modules/@oxygen/shared/dist/hosted-ai.js +108 -118
  62. package/node_modules/@oxygen/shared/dist/knowledge-links.d.ts +29 -0
  63. package/node_modules/@oxygen/shared/dist/knowledge-links.js +84 -1
  64. package/node_modules/@oxygen/shared/dist/linkedin-sequences.d.ts +5 -14
  65. package/node_modules/@oxygen/shared/dist/linkedin-sequences.js +22 -5
  66. package/node_modules/@oxygen/shared/dist/managed-scraper-backend.d.ts +18 -0
  67. package/node_modules/@oxygen/shared/dist/managed-scraper-backend.js +17 -0
  68. package/node_modules/@oxygen/shared/dist/teammates.d.ts +7 -5
  69. package/node_modules/@oxygen/shared/dist/teammates.js +11 -20
  70. package/node_modules/@oxygen/shared/dist/user-capability-routing.js +88 -0
  71. package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -1
  72. package/node_modules/@oxygen/shared/dist/version.generated.js +1 -1
  73. package/node_modules/@oxygen/shared/dist/workspace-placement-view.d.ts +150 -0
  74. package/node_modules/@oxygen/shared/dist/workspace-placement-view.js +102 -0
  75. package/node_modules/@oxygen/shared/dist/workspace-placement.d.ts +84 -0
  76. package/node_modules/@oxygen/shared/dist/workspace-placement.js +107 -0
  77. package/package.json +1 -1
@@ -0,0 +1,147 @@
1
+ /** Full DEV onboarding, approved by Philipp on 2026-10-16.
2
+ * Browser-safe vocabulary. Native owners, never navigation or caller assertions,
3
+ * establish research results, billing eligibility and resource authority. */
4
+ export declare const GUIDED_ONBOARDING_JOURNEY_VERSION: 1;
5
+ export declare const GUIDED_ONBOARDING_PART1_STEPS: readonly ["company_context", "company_research", "company_review", "audience_company_size", "audience_markets", "audience_industries", "audience_personas", "signals", "prospects", "trial"];
6
+ export declare const GUIDED_ONBOARDING_PART2_STEPS: readonly ["welcome", "daily_rhythm", "linkedin", "campaign", "sequence", "routines", "tour"];
7
+ export declare const GUIDED_ONBOARDING_STEPS: readonly ["company_context", "company_research", "company_review", "audience_company_size", "audience_markets", "audience_industries", "audience_personas", "signals", "prospects", "trial", "welcome", "daily_rhythm", "linkedin", "campaign", "sequence", "routines", "tour", "complete"];
8
+ export type GuidedOnboardingStep = typeof GUIDED_ONBOARDING_STEPS[number];
9
+ export type GuidedOnboardingPart = "part1" | "part2" | "complete";
10
+ export declare function guidedOnboardingPart(step: GuidedOnboardingStep): GuidedOnboardingPart;
11
+ export type GuidedOnboardingChoice = {
12
+ values: string[];
13
+ };
14
+ export type GuidedOnboardingSignal = {
15
+ mode: "none" | "selected";
16
+ values: string[];
17
+ };
18
+ export declare const GUIDED_ONBOARDING_TECHNOLOGY_RELATIONSHIPS: readonly ["uses", "replaces", "integrates_with"];
19
+ export type GuidedOnboardingTechnologyRelationship = typeof GUIDED_ONBOARDING_TECHNOLOGY_RELATIONSHIPS[number];
20
+ export type GuidedOnboardingTechnology = {
21
+ name: string;
22
+ relationship: GuidedOnboardingTechnologyRelationship;
23
+ };
24
+ export type GuidedOnboardingTechnologySignal = {
25
+ mode: "none" | "selected";
26
+ values: GuidedOnboardingTechnology[];
27
+ };
28
+ export type GuidedOnboardingAnswers = {
29
+ company_context?: {
30
+ website: string | null;
31
+ name: string;
32
+ description: string;
33
+ context_links: string[];
34
+ file_ids: string[];
35
+ };
36
+ /** Saving a statement alone is not its native confirmation receipt. */
37
+ company_review?: {
38
+ statement: string;
39
+ profile_revision: string;
40
+ };
41
+ audience_company_size?: GuidedOnboardingChoice;
42
+ audience_markets?: GuidedOnboardingChoice;
43
+ audience_industries?: GuidedOnboardingChoice;
44
+ audience_personas?: GuidedOnboardingChoice;
45
+ signals?: {
46
+ hiring: GuidedOnboardingSignal;
47
+ technology: GuidedOnboardingTechnologySignal;
48
+ };
49
+ prospects?: {
50
+ decision: "continue" | "refine";
51
+ feedback: string;
52
+ };
53
+ /** Connect is an intent. Only the native sender owner can prove a connection. */
54
+ linkedin?: {
55
+ choice: "connect" | "skip";
56
+ };
57
+ /** A draft remains a valid outcome on the optional LinkedIn branch. */
58
+ sequence?: {
59
+ decision: "review" | "keep_draft";
60
+ };
61
+ tour?: {
62
+ visited: Array<"overview" | "leads" | "sourcing" | "sequence">;
63
+ };
64
+ };
65
+ export type GuidedOnboardingResearchStatus = "not_started" | "running" | "completed" | "partial" | "unavailable" | "failed" | "credits_exhausted";
66
+ export type GuidedOnboardingResearch = {
67
+ status: GuidedOnboardingResearchStatus;
68
+ run_id: string | null;
69
+ knowledge_page_ids: string[];
70
+ /** Fixed customer-safe code only. Provider diagnostics remain in their owner. */
71
+ fault: {
72
+ code: string;
73
+ retryable: boolean;
74
+ } | null;
75
+ };
76
+ export type GuidedOnboardingProspect = {
77
+ row_id: string;
78
+ person_key: string;
79
+ name: string;
80
+ title: string;
81
+ company: string;
82
+ linkedin_url: string | null;
83
+ sources: Array<{
84
+ url: string;
85
+ title: string;
86
+ }>;
87
+ fit: {
88
+ rationale: string;
89
+ criteria: string[];
90
+ };
91
+ };
92
+ export type GuidedOnboardingProspectLimitation = {
93
+ field: string;
94
+ code: "unsupported_filter";
95
+ blocking: boolean;
96
+ message: string;
97
+ values: string[];
98
+ suggestions: string[];
99
+ };
100
+ export type GuidedOnboardingProspects = {
101
+ status: "not_started" | "running" | "ready" | "partial" | "empty" | "unavailable" | "failed" | "credits_exhausted";
102
+ table_id: string | null;
103
+ ingestion_run_ids: string[];
104
+ /** Up to five native source-backed people; an empty array is never success. */
105
+ people: GuidedOnboardingProspect[];
106
+ /** Absent in legacy receipts means unknown, never proof of supported filters.
107
+ * Current D projections supply actual structured diagnostics, including []. */
108
+ limitations?: GuidedOnboardingProspectLimitation[];
109
+ fault: {
110
+ code: string;
111
+ retryable: boolean;
112
+ } | null;
113
+ };
114
+ /** Read projection from native billing, not an accepted browser mutation field.
115
+ * I owns the actual test Checkout/subscription/grant producer and its signatures. */
116
+ export type GuidedOnboardingEligibility = {
117
+ state: "unverified" | "checkout_required" | "checkout_pending" | "eligible" | "unavailable";
118
+ basis: "paid" | "trial" | null;
119
+ subscription_id: string | null;
120
+ observed_at: string | null;
121
+ trial_ends_at: string | null;
122
+ };
123
+ export type GuidedOnboardingJourney = {
124
+ version: typeof GUIDED_ONBOARDING_JOURNEY_VERSION;
125
+ /** Increments on non-destructive restart; activation revision guards writes. */
126
+ generation: number;
127
+ step: GuidedOnboardingStep;
128
+ completed_steps: GuidedOnboardingStep[];
129
+ /** Presentation state is authoritative and independent of deduplicated history. */
130
+ navigation: {
131
+ paused: boolean;
132
+ tour_active_stop: "overview" | "leads" | "sourcing" | "sequence" | null;
133
+ };
134
+ answers: GuidedOnboardingAnswers;
135
+ /** Native confirmation binds the actual typed company profile revision. */
136
+ company_confirmation: {
137
+ profile_revision: string;
138
+ statement_digest: string;
139
+ confirmed_at: string;
140
+ } | null;
141
+ research: GuidedOnboardingResearch;
142
+ prospects: GuidedOnboardingProspects;
143
+ };
144
+ /** Free initialization; creates no research, chat turn, subscription or resource. */
145
+ export declare function createGuidedOnboardingJourney(): GuidedOnboardingJourney;
146
+ /** Old saved v1 records have no current-stop evidence. Never guess from visited[]. */
147
+ export declare function normalizeSavedGuidedOnboardingJourney(journey: GuidedOnboardingJourney): GuidedOnboardingJourney;
@@ -0,0 +1,12 @@
1
+ import { type GuidedOnboardingJourney, type GuidedOnboardingEligibility } from "./guided-onboarding-journey.js";
2
+ /** Synthetic component/test data only. Never substitute this for hosted receipts. */
3
+ export declare const GUIDED_ONBOARDING_FIXTURE_NAMES: readonly ["part1_initial", "research_running", "company_review", "audience_question", "technology_relationships", "prospects_ready", "prospects_partial", "prospects_empty", "prospects_unavailable", "prospects_failed", "research_failed", "credits_exhausted", "checkout_pending", "billing_unavailable", "part2_trial", "part2_paid", "linkedin_skipped", "tour_paused_back", "complete"];
4
+ export type GuidedOnboardingFixtureName = typeof GUIDED_ONBOARDING_FIXTURE_NAMES[number];
5
+ export type GuidedOnboardingFixture = {
6
+ fixture_only: true;
7
+ label: string;
8
+ activation_revision: number;
9
+ journey: GuidedOnboardingJourney;
10
+ eligibility: GuidedOnboardingEligibility;
11
+ };
12
+ export declare function createGuidedOnboardingFixture(name: GuidedOnboardingFixtureName): GuidedOnboardingFixture;
@@ -0,0 +1,83 @@
1
+ import { createGuidedOnboardingJourney, GUIDED_ONBOARDING_STEPS, } from "./guided-onboarding-journey.js";
2
+ /** Synthetic component/test data only. Never substitute this for hosted receipts. */
3
+ export const GUIDED_ONBOARDING_FIXTURE_NAMES = [
4
+ "part1_initial", "research_running", "company_review", "audience_question", "technology_relationships",
5
+ "prospects_ready", "prospects_partial", "prospects_empty", "prospects_unavailable", "prospects_failed", "research_failed",
6
+ "credits_exhausted", "checkout_pending", "billing_unavailable", "part2_trial", "part2_paid", "linkedin_skipped", "tour_paused_back", "complete",
7
+ ];
8
+ export function createGuidedOnboardingFixture(name) {
9
+ const fixture = {
10
+ fixture_only: true, label: `Synthetic onboarding fixture: ${name}`, activation_revision: 7,
11
+ journey: createGuidedOnboardingJourney(),
12
+ eligibility: { state: "checkout_required", basis: null, subscription_id: null, observed_at: "2026-10-16T12:00:00.000Z", trial_ends_at: null },
13
+ };
14
+ const { journey, eligibility } = fixture;
15
+ if (name === "research_running") {
16
+ journey.step = "company_research";
17
+ journey.research = { status: "running", run_id: "fixture-research-run", knowledge_page_ids: [], fault: null };
18
+ }
19
+ else if (name === "company_review") {
20
+ journey.step = "company_review";
21
+ journey.answers.company_review = { statement: "Synthetic company statement for panel tests.", profile_revision: "3" };
22
+ journey.research = { status: "completed", run_id: "fixture-research-run", knowledge_page_ids: ["fixture-company-page"], fault: null };
23
+ }
24
+ else if (name === "audience_question") {
25
+ journey.step = "audience_markets";
26
+ journey.answers.audience_company_size = { values: ["11–50"] };
27
+ journey.answers.audience_markets = { values: ["Germany", "Custom test market"] };
28
+ }
29
+ else if (name === "technology_relationships") {
30
+ journey.step = "signals";
31
+ journey.answers.signals = {
32
+ hiring: { mode: "selected", values: ["Revenue Operations Manager"] },
33
+ technology: { mode: "selected", values: [
34
+ { name: "Apollo", relationship: "replaces" },
35
+ { name: "Apollo", relationship: "integrates_with" },
36
+ ] },
37
+ };
38
+ }
39
+ else if (name.startsWith("prospects_")) {
40
+ journey.step = "prospects";
41
+ journey.prospects.status = name === "prospects_ready" ? "ready" : name === "prospects_partial" ? "partial"
42
+ : name === "prospects_unavailable" ? "unavailable" : name === "prospects_failed" ? "failed" : "empty";
43
+ journey.prospects.table_id = "fixture-prospects-table";
44
+ if (name === "prospects_ready" || name === "prospects_partial") {
45
+ journey.prospects.ingestion_run_ids = ["fixture-prospects-ingestion"];
46
+ journey.prospects.people = Array.from({ length: name === "prospects_ready" ? 5 : 2 }, (_, index) => ({
47
+ row_id: `fixture-person-${index + 1}`, person_key: `fixture-key-${index + 1}`,
48
+ name: `Example Buyer ${index + 1}`, title: "Example sales leader", company: "Example Company",
49
+ linkedin_url: null, sources: [{ url: "https://example.test/source", title: "Synthetic source — not provider evidence" }],
50
+ fit: { rationale: "Synthetic fit explanation for layout tests.", criteria: ["example persona"] },
51
+ }));
52
+ }
53
+ }
54
+ else if (name === "research_failed" || name === "credits_exhausted") {
55
+ journey.step = "company_research";
56
+ journey.research.status = name === "research_failed" ? "failed" : "credits_exhausted";
57
+ journey.research.fault = { code: name === "research_failed" ? "research_unavailable" : "insufficient_credits", retryable: name === "research_failed" };
58
+ }
59
+ else if (name === "checkout_pending" || name === "billing_unavailable") {
60
+ journey.step = "trial";
61
+ eligibility.state = name === "checkout_pending" ? "checkout_pending" : "unavailable";
62
+ }
63
+ else if (["part2_trial", "part2_paid", "linkedin_skipped", "tour_paused_back", "complete"].includes(name)) {
64
+ journey.step = name === "complete" ? "complete" : name === "linkedin_skipped" ? "campaign" : name === "tour_paused_back" ? "tour" : "welcome";
65
+ eligibility.state = "eligible";
66
+ eligibility.basis = name === "part2_paid" ? "paid" : "trial";
67
+ eligibility.subscription_id = "fixture-subscription";
68
+ eligibility.trial_ends_at = eligibility.basis === "trial" ? "2026-10-23T12:00:00.000Z" : null;
69
+ if (name === "linkedin_skipped")
70
+ journey.answers.linkedin = { choice: "skip" };
71
+ }
72
+ const index = GUIDED_ONBOARDING_STEPS.indexOf(journey.step);
73
+ journey.completed_steps = GUIDED_ONBOARDING_STEPS.slice(0, index);
74
+ if (index > GUIDED_ONBOARDING_STEPS.indexOf("company_review")) {
75
+ journey.answers.company_review = { statement: "Synthetic reviewed company statement.", profile_revision: "3" };
76
+ journey.company_confirmation = { profile_revision: "3", statement_digest: "a".repeat(64), confirmed_at: "2026-10-16T12:00:00.000Z" };
77
+ }
78
+ if (name === "tour_paused_back") {
79
+ journey.answers.tour = { visited: ["overview", "leads", "sourcing"] };
80
+ journey.navigation = { paused: true, tour_active_stop: "leads" };
81
+ }
82
+ return fixture;
83
+ }
@@ -0,0 +1,44 @@
1
+ /** Full DEV onboarding, approved by Philipp on 2026-10-16.
2
+ * Browser-safe vocabulary. Native owners, never navigation or caller assertions,
3
+ * establish research results, billing eligibility and resource authority. */
4
+ export const GUIDED_ONBOARDING_JOURNEY_VERSION = 1;
5
+ export const GUIDED_ONBOARDING_PART1_STEPS = [
6
+ "company_context", "company_research", "company_review",
7
+ "audience_company_size", "audience_markets", "audience_industries", "audience_personas",
8
+ "signals", "prospects", "trial",
9
+ ];
10
+ export const GUIDED_ONBOARDING_PART2_STEPS = [
11
+ "welcome", "daily_rhythm", "linkedin", "campaign", "sequence", "routines", "tour",
12
+ ];
13
+ export const GUIDED_ONBOARDING_STEPS = [
14
+ ...GUIDED_ONBOARDING_PART1_STEPS, ...GUIDED_ONBOARDING_PART2_STEPS, "complete",
15
+ ];
16
+ export function guidedOnboardingPart(step) {
17
+ if (step === "complete")
18
+ return "complete";
19
+ if (GUIDED_ONBOARDING_PART1_STEPS.includes(step))
20
+ return "part1";
21
+ if (GUIDED_ONBOARDING_PART2_STEPS.includes(step))
22
+ return "part2";
23
+ throw new TypeError("Unknown onboarding step.");
24
+ }
25
+ export const GUIDED_ONBOARDING_TECHNOLOGY_RELATIONSHIPS = ["uses", "replaces", "integrates_with"];
26
+ /** Free initialization; creates no research, chat turn, subscription or resource. */
27
+ export function createGuidedOnboardingJourney() {
28
+ return {
29
+ version: GUIDED_ONBOARDING_JOURNEY_VERSION, generation: 1, step: "company_context",
30
+ completed_steps: [], navigation: { paused: false, tour_active_stop: null }, answers: {}, company_confirmation: null,
31
+ research: { status: "not_started", run_id: null, knowledge_page_ids: [], fault: null },
32
+ prospects: { status: "not_started", table_id: null, ingestion_run_ids: [], people: [], fault: null },
33
+ };
34
+ }
35
+ /** Old saved v1 records have no current-stop evidence. Never guess from visited[]. */
36
+ export function normalizeSavedGuidedOnboardingJourney(journey) {
37
+ if (journey.navigation === undefined)
38
+ return { ...journey, navigation: { paused: false, tour_active_stop: null } };
39
+ const navigation = journey.navigation;
40
+ if (!navigation || typeof navigation.paused !== "boolean" || (navigation.tour_active_stop !== null
41
+ && !["overview", "leads", "sourcing", "sequence"].includes(navigation.tour_active_stop)))
42
+ throw new TypeError("Invalid saved onboarding navigation.");
43
+ return journey;
44
+ }
@@ -0,0 +1,7 @@
1
+ import type { GuidedOnboardingProspectLimitation, GuidedOnboardingProspects } from "./guided-onboarding-journey.js";
2
+ /** Bind actual D result.targeting.limitations, never infer support from success,
3
+ * an empty people list, a legacy absence, or a general fault. Preserve native
4
+ * result status/fit/source/ingestion truth and the compatible fault unchanged. */
5
+ export declare function projectGuidedOnboardingProspects(prospects: GuidedOnboardingProspects, limitations: unknown): GuidedOnboardingProspects & {
6
+ limitations: GuidedOnboardingProspectLimitation[];
7
+ };
@@ -0,0 +1,13 @@
1
+ import { OxygenError } from "./cli-result.js";
2
+ import { arr, bool, enumOf, obj, str, validateContract } from "./contracts/schema.js";
3
+ const limitationsContract = obj({ limitations: arr(obj({ field: str({ minLength: 1 }), code: enumOf(["unsupported_filter"]),
4
+ blocking: bool(), message: str({ minLength: 1 }), values: arr(str()), suggestions: arr(str()) }, { required: ["field", "code", "blocking", "message", "values", "suggestions"] })) }, { required: ["limitations"] });
5
+ /** Bind actual D result.targeting.limitations, never infer support from success,
6
+ * an empty people list, a legacy absence, or a general fault. Preserve native
7
+ * result status/fit/source/ingestion truth and the compatible fault unchanged. */
8
+ export function projectGuidedOnboardingProspects(prospects, limitations) {
9
+ const checked = validateContract(limitationsContract, { limitations }, { unknown: "reject", overMax: "reject" });
10
+ if (!checked.ok)
11
+ throw new OxygenError("onboarding_preview_diagnostics_invalid", "The preview could not describe its supported targeting. Review your saved criteria before retrying.");
12
+ return { ...prospects, limitations: checked.value.limitations.map(item => ({ ...item, values: [...item.values], suggestions: [...item.suggestions] })) };
13
+ }
@@ -0,0 +1,4 @@
1
+ import type { GuidedOnboardingAnswers } from "./guided-onboarding-journey.js";
2
+ /** Same product with distinct relationships is intentional, not a duplicate.
3
+ * This normalizes saved intent, never claims that a provider supports a filter. */
4
+ export declare function normalizeGuidedOnboardingSignals(value: unknown): NonNullable<GuidedOnboardingAnswers["signals"]>;
@@ -0,0 +1,34 @@
1
+ import { OxygenError } from "./cli-result.js";
2
+ import { validateContract } from "./contracts/schema.js";
3
+ import { GUIDED_ONBOARDING_SIGNALS_CONTRACT } from "./contracts/guided-onboarding-journey.js";
4
+ const fail = () => {
5
+ throw new OxygenError("invalid_onboarding_answer", "Choose hiring roles and technologies with uses, replaces or integrates_with, or explicitly choose none.", { exitCode: 2 });
6
+ };
7
+ const displayName = (name) => name.normalize("NFKC").trim().replace(/\s+/gu, " ");
8
+ /** Same product with distinct relationships is intentional, not a duplicate.
9
+ * This normalizes saved intent, never claims that a provider supports a filter. */
10
+ export function normalizeGuidedOnboardingSignals(value) {
11
+ const parsed = validateContract(GUIDED_ONBOARDING_SIGNALS_CONTRACT, value, { unknown: "reject", overMax: "reject" });
12
+ if (!parsed.ok)
13
+ return fail();
14
+ const { hiring, technology } = parsed.value;
15
+ if ((hiring.mode === "none") !== (hiring.values.length === 0)
16
+ || (technology.mode === "none") !== (technology.values.length === 0))
17
+ return fail();
18
+ const roles = hiring.values.map(displayName);
19
+ if (roles.some(role => !role))
20
+ return fail();
21
+ const seen = new Set();
22
+ const technologies = [];
23
+ for (const item of technology.values) {
24
+ const name = displayName(item.name);
25
+ if (!name)
26
+ return fail();
27
+ const key = JSON.stringify([name.toLowerCase(), item.relationship]);
28
+ if (seen.has(key))
29
+ continue;
30
+ seen.add(key);
31
+ technologies.push({ name, relationship: item.relationship });
32
+ }
33
+ return { hiring: { mode: hiring.mode, values: roles }, technology: { mode: technology.mode, values: technologies } };
34
+ }
@@ -0,0 +1,26 @@
1
+ /** DEV onboarding offer. Existing subscription terms and production acquisition stay intact. */
2
+ export declare const GUIDED_TRIAL_OFFER: {
3
+ readonly key: "guided_onboarding_v2";
4
+ readonly plan: "oxygen_49";
5
+ readonly currency: "usd";
6
+ readonly trial_days: 7;
7
+ readonly trial_credits: 2000;
8
+ readonly amount_due_now_cents: 0;
9
+ readonly monthly_price_cents: 4900;
10
+ readonly card_required: true;
11
+ readonly early_charge_allowed: false;
12
+ };
13
+ export declare function guidedTrialAcquisitionEnabled(env?: Record<string, string | undefined>): boolean;
14
+ export declare function isGuidedTrialSubscription(metadata: Record<string, unknown> | null | undefined): boolean;
15
+ export type GuidedTrialBilling = {
16
+ offer: typeof GUIDED_TRIAL_OFFER;
17
+ eligible: boolean;
18
+ checkout_allowed: boolean;
19
+ state: "trial" | "paid" | "checkout_required" | "trial_unavailable" | "subscription_pending";
20
+ billing_owner_organization_id: string;
21
+ subscription_id: string | null;
22
+ trial_ends_at: string | null;
23
+ cancellation_scheduled: boolean;
24
+ spendable_credits: number;
25
+ funded_work_paused: boolean;
26
+ };
@@ -0,0 +1,19 @@
1
+ import { guidedActivationEnabled } from "./guided-activation.js";
2
+ /** DEV onboarding offer. Existing subscription terms and production acquisition stay intact. */
3
+ export const GUIDED_TRIAL_OFFER = {
4
+ key: "guided_onboarding_v2",
5
+ plan: "oxygen_49",
6
+ currency: "usd",
7
+ trial_days: 7,
8
+ trial_credits: 2_000,
9
+ amount_due_now_cents: 0,
10
+ monthly_price_cents: 4_900,
11
+ card_required: true,
12
+ early_charge_allowed: false,
13
+ };
14
+ export function guidedTrialAcquisitionEnabled(env = process.env) {
15
+ return guidedActivationEnabled(env);
16
+ }
17
+ export function isGuidedTrialSubscription(metadata) {
18
+ return metadata?.oxygen_trial_offer === GUIDED_TRIAL_OFFER.key;
19
+ }
@@ -22,6 +22,101 @@
22
22
  * can re-source the catalog without dragging the barrel in.
23
23
  */
24
24
  import { copilotAudience, isCopilotFlagOn } from "./copilot-access.js";
25
+ /**
26
+ * The Copilot's tiers, which an Oxygent's own chat runs at. Hoisted out of the
27
+ * registry so the Agent use case can share the same spec objects.
28
+ */
29
+ const COPILOT_MODEL_SPECS = {
30
+ /**
31
+ * "Oxygen Auto" -- the earlier default, retained as an explicit choice.
32
+ * Its conversation thread stays fixed for its whole life. It is NOT a router: the
33
+ * model never changes mid-session, because an OpenRouter prompt cache is
34
+ * model-scoped and a mid-turn switch would turn a ~66% cache HIT rate into a
35
+ * cache WRITE on a fresh model, ten times a turn. Models only change below
36
+ * this thread, where the context is fresh -- see COPILOT_ERRAND_LEVEL.
37
+ *
38
+ * Before the 2026-10-10 Sol refresh, Auto ran Kimi K3. Its
39
+ * `reasoningEffort: "medium"` made that tier worth shipping.
40
+ * Until 2026-09-21 Kimi was sent with NO `reasoning` field at
41
+ * all, on a registry comment asserting K3 was max-reasoning-only. Measured
42
+ * against the live API on the same prompt and tool schema, that claim was
43
+ * false and the omission was the expensive case:
44
+ *
45
+ * omitted -> 412 reasoning tokens, $0.00779, 10.0s
46
+ * low -> 206, $0.00566, 9.2s
47
+ * medium -> 124, $0.00342, 4.8s <- 56% cheaper, 2.1x faster
48
+ * high -> 528, $0.01075, 14.8s
49
+ *
50
+ * All four returned the same answer. Reasoning was 38% of output tokens on a
51
+ * surface where output is ~61% of the bill.
52
+ */
53
+ auto: {
54
+ model: "openai/gpt-6.1-sol",
55
+ displayName: "Oxygen Auto",
56
+ // Approved 2026-10-10 for NEW sessions. Existing sessions retain their
57
+ // persisted resolved_model; Kimi's priced spec survives in the legacy map.
58
+ fallbackModels: ["anthropic/claude-sonnet-5.5"],
59
+ reasoningEffort: "medium",
60
+ estPromptUsdPerM: 2,
61
+ estCompletionUsdPerM: 10,
62
+ // Reasoning tokens count against this cap, so 8,192 left a long tool call
63
+ // (a full sequence draft, a wide table write) to be cut off mid-argument,
64
+ // which fails the turn on the second time. Credit holds are sized from the
65
+ // expected output (billing.ts), so this moves only the conservative clamp.
66
+ maxOutputTokens: 16_384,
67
+ },
68
+ low: {
69
+ // The errand model: sub-agents, compaction summaries and auto-titles. A
70
+ // child builds its own Agent with its own messages, so it starts cold on
71
+ // any model -- which is why a switch is free HERE and nowhere above it.
72
+ //
73
+ // This was deepseek/deepseek-v4.1-flash for about an hour, chosen for
74
+ // being newer and measured correct on a one-tool arithmetic probe. Probed
75
+ // again against the REAL runtime tool schemas from tools.ts
76
+ // (capability_search + capability_run, nested `args`, optional approval
77
+ // fields) under the same provider.require_parameters=true the transport
78
+ // always sends, it returned `finish_reason: "error"` with zero tool calls
79
+ // on 1 of 5 attempts, every one served by the Relace endpoint. v4-flash
80
+ // went 5/5 clean on StreamLake, is CHEAPER on both axes, and is already
81
+ // what auto-titles run on -- so the newer model was worse on reliability
82
+ // and on price, and the only thing recommending it was its version number.
83
+ //
84
+ // A child that emits no tool call returns nothing for what it spent, and
85
+ // the transport does not retry `finish_reason: "error"` on the primary
86
+ // (classifyModelError sends it straight to the next model), so a 20%
87
+ // error rate here is a 20% double round trip on every delegation.
88
+ model: "deepseek/deepseek-v4-flash",
89
+ displayName: "Oxygen Fast",
90
+ // v4.1-flash earns its place HERE: it works 4 times in 5 and it is a
91
+ // genuinely different endpoint on a different provider, which is the one
92
+ // thing a fallback has to be.
93
+ fallbackModels: ["deepseek/deepseek-v4.1-flash"],
94
+ reasoningEffort: "low",
95
+ estPromptUsdPerM: 0.09,
96
+ estCompletionUsdPerM: 0.18,
97
+ maxOutputTokens: 8192,
98
+ },
99
+ medium: {
100
+ model: "anthropic/claude-haiku-5.5",
101
+ displayName: "Oxygen Balanced",
102
+ fallbackModels: ["deepseek/deepseek-v4.1-flash"],
103
+ reasoningEffort: "medium",
104
+ estPromptUsdPerM: 0.1,
105
+ estCompletionUsdPerM: 0.5,
106
+ // A conversation tier (the default since #5225), so Auto's cap (see Auto).
107
+ maxOutputTokens: 16_384,
108
+ },
109
+ high: {
110
+ model: "openai/gpt-6.1-sol",
111
+ displayName: "Oxygen Max",
112
+ fallbackModels: ["anthropic/claude-sonnet-5.5"],
113
+ reasoningEffort: "medium",
114
+ estPromptUsdPerM: 2,
115
+ estCompletionUsdPerM: 10,
116
+ // A conversation tier, so Auto's cap (see Auto).
117
+ maxOutputTokens: 16_384,
118
+ },
119
+ };
25
120
  export const HOSTED_AI_MODEL_REGISTRY = {
26
121
  /**
27
122
  * Historical calibration of the September AI-column tiers, re-picked
@@ -112,124 +207,20 @@ export const HOSTED_AI_MODEL_REGISTRY = {
112
207
  maxOutputTokens: 4096,
113
208
  },
114
209
  },
115
- copilot: {
116
- /**
117
- * "Oxygen Auto" -- the earlier default, retained as an explicit choice.
118
- * Its conversation thread stays fixed for its whole life. It is NOT a router: the
119
- * model never changes mid-session, because an OpenRouter prompt cache is
120
- * model-scoped and a mid-turn switch would turn a ~66% cache HIT rate into a
121
- * cache WRITE on a fresh model, ten times a turn. Models only change below
122
- * this thread, where the context is fresh -- see COPILOT_ERRAND_LEVEL.
123
- *
124
- * Before the 2026-10-10 Sol refresh, Auto ran Kimi K3. Its
125
- * `reasoningEffort: "medium"` made that tier worth shipping.
126
- * Until 2026-09-21 Kimi was sent with NO `reasoning` field at
127
- * all, on a registry comment asserting K3 was max-reasoning-only. Measured
128
- * against the live API on the same prompt and tool schema, that claim was
129
- * false and the omission was the expensive case:
130
- *
131
- * omitted -> 412 reasoning tokens, $0.00779, 10.0s
132
- * low -> 206, $0.00566, 9.2s
133
- * medium -> 124, $0.00342, 4.8s <- 56% cheaper, 2.1x faster
134
- * high -> 528, $0.01075, 14.8s
135
- *
136
- * All four returned the same answer. Reasoning was 38% of output tokens on a
137
- * surface where output is ~61% of the bill.
138
- */
139
- auto: {
140
- model: "openai/gpt-6.1-sol",
141
- displayName: "Oxygen Auto",
142
- // Approved 2026-10-10 for NEW sessions. Existing sessions retain their
143
- // persisted resolved_model; Kimi's priced spec survives in the legacy map.
144
- fallbackModels: ["anthropic/claude-sonnet-5.5"],
145
- reasoningEffort: "medium",
146
- estPromptUsdPerM: 2,
147
- estCompletionUsdPerM: 10,
148
- // Reasoning tokens count against this cap, so 8,192 left a long tool call
149
- // (a full sequence draft, a wide table write) to be cut off mid-argument,
150
- // which fails the turn on the second time. Credit holds are sized from the
151
- // expected output (billing.ts), so this moves only the conservative clamp.
152
- maxOutputTokens: 16_384,
153
- },
154
- low: {
155
- // The errand model: sub-agents, compaction summaries and auto-titles. A
156
- // child builds its own Agent with its own messages, so it starts cold on
157
- // any model -- which is why a switch is free HERE and nowhere above it.
158
- //
159
- // This was deepseek/deepseek-v4.1-flash for about an hour, chosen for
160
- // being newer and measured correct on a one-tool arithmetic probe. Probed
161
- // again against the REAL runtime tool schemas from tools.ts
162
- // (capability_search + capability_run, nested `args`, optional approval
163
- // fields) under the same provider.require_parameters=true the transport
164
- // always sends, it returned `finish_reason: "error"` with zero tool calls
165
- // on 1 of 5 attempts, every one served by the Relace endpoint. v4-flash
166
- // went 5/5 clean on StreamLake, is CHEAPER on both axes, and is already
167
- // what auto-titles run on -- so the newer model was worse on reliability
168
- // and on price, and the only thing recommending it was its version number.
169
- //
170
- // A child that emits no tool call returns nothing for what it spent, and
171
- // the transport does not retry `finish_reason: "error"` on the primary
172
- // (classifyModelError sends it straight to the next model), so a 20%
173
- // error rate here is a 20% double round trip on every delegation.
174
- model: "deepseek/deepseek-v4-flash",
175
- displayName: "Oxygen Fast",
176
- // v4.1-flash earns its place HERE: it works 4 times in 5 and it is a
177
- // genuinely different endpoint on a different provider, which is the one
178
- // thing a fallback has to be.
179
- fallbackModels: ["deepseek/deepseek-v4.1-flash"],
180
- reasoningEffort: "low",
181
- estPromptUsdPerM: 0.09,
182
- estCompletionUsdPerM: 0.18,
183
- maxOutputTokens: 8192,
184
- },
185
- medium: {
186
- model: "anthropic/claude-haiku-5.5",
187
- displayName: "Oxygen Balanced",
188
- fallbackModels: ["deepseek/deepseek-v4.1-flash"],
189
- reasoningEffort: "medium",
190
- estPromptUsdPerM: 0.1,
191
- estCompletionUsdPerM: 0.5,
192
- // A conversation tier (the default since #5225), so Auto's cap (see Auto).
193
- maxOutputTokens: 16_384,
194
- },
195
- high: {
196
- model: "openai/gpt-6.1-sol",
197
- displayName: "Oxygen Max",
198
- fallbackModels: ["anthropic/claude-sonnet-5.5"],
199
- reasoningEffort: "medium",
200
- estPromptUsdPerM: 2,
201
- estCompletionUsdPerM: 10,
202
- // A conversation tier, so Auto's cap (see Auto).
203
- maxOutputTokens: 16_384,
204
- },
205
- },
210
+ copilot: COPILOT_MODEL_SPECS,
211
+ /**
212
+ * An Oxygent's background tasks and routines run on the very models its chat
213
+ * does, so one tier means one model wherever the Oxygent works (Philipp,
214
+ * 2026-10-17). Until then Agents kept their September tiers (DeepSeek V4
215
+ * Flash / V4 Pro / Kimi K2.6), which the 2026-10-10 refresh left out of scope.
216
+ * Shared by reference, so a Copilot tier refresh moves Agents with it;
217
+ * `OXYGEN_AGENT_MODEL_*` still overrides Agents alone. Retired ids keep
218
+ * their tier label through LEGACY_MANAGED_AI_COLUMN_MODELS.
219
+ */
206
220
  agent: {
207
- low: {
208
- model: "deepseek/deepseek-v4-flash",
209
- displayName: "Oxygen Fast",
210
- fallbackModels: [],
211
- reasoningEffort: "medium",
212
- estPromptUsdPerM: 0.09,
213
- estCompletionUsdPerM: 0.18,
214
- maxOutputTokens: 8192,
215
- },
216
- medium: {
217
- model: "deepseek/deepseek-v4-pro",
218
- displayName: "Oxygen Balanced",
219
- fallbackModels: ["deepseek/deepseek-v4-flash"],
220
- reasoningEffort: "high",
221
- estPromptUsdPerM: 0.435,
222
- estCompletionUsdPerM: 0.87,
223
- maxOutputTokens: 8192,
224
- },
225
- high: {
226
- model: "moonshotai/kimi-k2.6",
227
- displayName: "Oxygen Max",
228
- fallbackModels: ["deepseek/deepseek-v4-pro"],
229
- estPromptUsdPerM: 0.66,
230
- estCompletionUsdPerM: 3.41,
231
- maxOutputTokens: 8192,
232
- },
221
+ low: COPILOT_MODEL_SPECS.low,
222
+ medium: COPILOT_MODEL_SPECS.medium,
223
+ high: COPILOT_MODEL_SPECS.high,
233
224
  },
234
225
  };
235
226
  /** Persisted conversations keep their original pricing, effort and fallbacks. */
@@ -449,7 +440,6 @@ export const IMAGE_INPUT_MODELS = new Set([
449
440
  "anthropic/claude-sonnet-5.5",
450
441
  "anthropic/claude-haiku-5.5",
451
442
  "moonshotai/kimi-k3",
452
- "moonshotai/kimi-k2.6",
453
443
  "deepseek/deepseek-v4.1-flash",
454
444
  "google/gemini-3.8-flash",
455
445
  ]);