@oxygen-agent/cli 1.1014.1420 → 1.1014.1451

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 CHANGED
@@ -34,4 +34,4 @@ oxygen update
34
34
 
35
35
  For product documentation, visit https://oxygen-agent.com/docs. For support, visit https://oxygen-agent.com.
36
36
 
37
- Version: 1.1014.1420
37
+ Version: 1.1014.1451
@@ -17,6 +17,7 @@ import { registerBudgetFamily } from "../commands/budget.js";
17
17
  import { registerCallablesFamily } from "../commands/callables.js";
18
18
  import { registerCapabilitiesFamily } from "../commands/capabilities.js";
19
19
  import { registerCellsFamily } from "../commands/cells.js";
20
+ import { registerClayMigrationFamily } from "../commands/clay-migration.js";
20
21
  import { registerColumnsFamily } from "../commands/columns.js";
21
22
  import { registerCommandsFamily } from "../commands/commands.js";
22
23
  import { registerCommentsFamily } from "../commands/comments.js";
@@ -199,6 +200,7 @@ export function createProgram() {
199
200
  registerSupabaseFamily(program, handleAsyncAction);
200
201
  registerIntegrationsFamily(program, handleAsyncAction);
201
202
  registerSendersFamily(program, handleAsyncAction);
203
+ registerClayMigrationFamily(program, handleAsyncAction);
202
204
  registerConnectionsFamily(program, handleAsyncAction);
203
205
  registerFollowersFamily(program, handleAsyncAction);
204
206
  registerViewersFamily(program, handleAsyncAction);
@@ -30,6 +30,7 @@ const COMMAND_SEARCH_STOP_WORDS = new Set([
30
30
  // leaf in the tree using that verb, so the default (mutates:false) would have
31
31
  // advertised the most consequential collaboration write as a read.
32
32
  const MUTATING_VERBS = new Set([
33
+ "migration",
33
34
  "forget-whatsapp-chat",
34
35
  "start-whatsapp",
35
36
  "revoke-invite", "confirm",
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ import type { ExtractedCommandActionHandler } from "../command-support.js";
3
+ export declare function registerClayMigrationFamily(program: Command, handleAsyncAction: ExtractedCommandActionHandler): void;
@@ -0,0 +1,22 @@
1
+ import { OxygenError } from "@oxygen/shared/cli-result";
2
+ import { CLAY_MIGRATION_OPERATION } from "@oxygen/shared/contracts/clay-migration";
3
+ import { operationInputIssues } from "@oxygen/shared/contracts/operations";
4
+ import { Command } from "commander";
5
+ import { addContractFlags, readContractFlags } from "../contract-flags.js";
6
+ import { requestOxygen } from "../http-client.js";
7
+ const spec = CLAY_MIGRATION_OPERATION;
8
+ export function registerClayMigrationFamily(program, handleAsyncAction) {
9
+ const migration = addContractFlags(new Command("migration").description(spec.cli.summary), spec.contract)
10
+ .option("--json", "Print a JSON envelope.")
11
+ .action(async (_options, command) => {
12
+ const options = command.optsWithGlobals();
13
+ await handleAsyncAction("clay migration", options, () => {
14
+ const body = readContractFlags(options, spec.contract);
15
+ const issues = operationInputIssues(spec, body);
16
+ if (issues.length)
17
+ throw new OxygenError("invalid_request", issues.map(issue => issue.message).join(" "), { exitCode: 2 });
18
+ return requestOxygen("/api/cli/migrations/clay", { method: "POST", body });
19
+ });
20
+ });
21
+ program.addCommand(new Command("clay").description("Import and rebuild a Clay workspace in Oxygen.").addCommand(migration));
22
+ }
@@ -494,7 +494,7 @@ export function registerDomainsFamily(program, handleAsyncAction) {
494
494
  .addCommand(new Command("tracking")
495
495
  .description("Open/click tracking domain for native cold email: provision the tracking CNAME on the domain's Cloudflare zone and check its verification status. 0 Oxygen credits.")
496
496
  .addCommand(new Command("setup")
497
- .description("Provision the open/click tracking CNAME (<track-host> CNAME <tracking target>) on the domain's Cloudflare zone. Without --approve, previews the exact record. GATE: needs EMAIL_TRACKING_CNAME_TARGET configured + the tracking hostname attached on Vercel; send-time injection stays off until EMAIL_TRACKING_SECRET is set.")
497
+ .description("Provision the open/click tracking CNAME (<track-host> CNAME <tracking target>) on the domain's Cloudflare zone. Without --approve, previews the exact record. Open and click tracking must be enabled for your workspace (otherwise the command returns tracking_not_configured); link and pixel injection stays off until tracking_secret_configured is true.")
498
498
  .argument("<domain>", "Sending domain, such as acme.com.")
499
499
  .option("--host <host>", "Tracking subdomain (default track.<domain>); must be a subdomain of the domain.")
500
500
  .option("--approve", "Write the CNAME. Without this flag, returns a preview only.")
@@ -239,7 +239,7 @@ export const OXYGEN_CAPABILITY_ROUTES = [
239
239
  gatewayTools: ["oxygen_tables_create", "oxygen_tools_search", "oxygen_columns_add", "oxygen_columns_catalog", "oxygen_enrich_column_preview", "oxygen_tables_link_bulk", "oxygen_callables_manage", "oxygen_tables_records_import"],
240
240
  gatewayCommands: ["tables create", "tools search", "columns add", "columns catalog", "enrich-column preview", "tables link", "functions list", "functions draft", "tables records import"],
241
241
  skills: ["oxygen-tables", "oxygen-enrichment", "oxygen-table-tidy", "oxygen-diagnostics", "oxygen-clay-migration", "oxygen-signals"],
242
- endpointSections: ["action-columns", "callables", "functions", "columns", "company-enrichment", "enrich-column", "enrichment", "projects", "supabase", "table-action-items", "table-action-runs", "table-ingestion-runs", "tables"],
242
+ endpointSections: ["action-columns", "callables", "functions", "columns", "company-enrichment", "enrich-column", "enrichment", "migrations", "projects", "supabase", "table-action-items", "table-action-runs", "table-ingestion-runs", "tables"],
243
243
  // "link"/"join"/"connect"/"relate" route here for `tables link`. Added after a
244
244
  // blind user eval asked for exactly "link two tables" and was routed to
245
245
  // `tables create` / `columns add` / `enrich-column preview` — none of which
@@ -1358,6 +1358,12 @@ function routeCardFor(card, query) {
1358
1358
  notFor: "Provider credentials, enrichment, workspace billing, or staff-wide database operations.",
1359
1359
  execution: database.execution,
1360
1360
  };
1361
+ if (card.id === "tables" && isClayMigrationIntent(query))
1362
+ card = {
1363
+ ...card,
1364
+ posture: "workspace_write",
1365
+ execution: "Start oxygen_clay_migration action=connect for browser sign-in; keep its migration_id for discovery, bounded source reads, table transfers, rebuild checkpoints and coverage reports. Read oxygen-clay-migration for the native reconstruction procedure. Create destination objects through their Oxygen tools, retain source outputs and report export limitations. Imported behavior stays inactive until separately approved. External MCP uses oxygen_capabilities_call.",
1366
+ };
1361
1367
  const specializedSkill = preferredSkillForIntent(card, query);
1362
1368
  return specializedSkill ? { ...card, skills: [specializedSkill, ...card.skills.filter((skill) => skill !== specializedSkill)] } : card;
1363
1369
  }
@@ -1366,7 +1372,7 @@ function preferredSkillForIntent(card, query) {
1366
1372
  if (card.id === "tables") {
1367
1373
  if (linkedinUrlFindingRecommendation(query))
1368
1374
  return "oxygen-enrichment";
1369
- if (/\bclay\b/.test(query) && /\b(import|export|migrat\w*|mov\w*)\b/.test(query))
1375
+ if (isClayMigrationIntent(query))
1370
1376
  return "oxygen-clay-migration";
1371
1377
  if (isTableMaintenanceIntent(query))
1372
1378
  return "oxygen-table-tidy";
@@ -1387,6 +1393,9 @@ function preferredSkillForIntent(card, query) {
1387
1393
  return "oxygen-signals";
1388
1394
  return null;
1389
1395
  }
1396
+ function isClayMigrationIntent(query) {
1397
+ return /\bclay\b/.test(query) && /\b(import|export|migrat\w*|mov\w*|transfer|bring|rebuild)\b/.test(query);
1398
+ }
1390
1399
  function isTableMaintenanceIntent(query) {
1391
1400
  return /\b(tables?|columns?|folders?)\b/.test(query)
1392
1401
  && !/\b(functions?|callables?|campaigns?|tags?|workflows?|agents?)\b/.test(query)
@@ -2169,6 +2178,8 @@ function isWorkspaceSkillIntent(query) {
2169
2178
  return /\bskills?\b/.test(query) && /\b(create|author|write|upload|import|edit|update|archive|private|workspace|custom|own|reusable)\b/.test(query);
2170
2179
  }
2171
2180
  function explicitCapabilityIntent(query) {
2181
+ if (isClayMigrationIntent(query))
2182
+ return ROUTE_BY_ID.get("tables") ?? null;
2172
2183
  if (isWorkspaceSkillIntent(query))
2173
2184
  return ROUTE_BY_ID.get("knowledge-graph") ?? null;
2174
2185
  const readOnly = readInspectionRecommendation(query);
@@ -2867,6 +2878,8 @@ function recommendationsFor(card, query) {
2867
2878
  };
2868
2879
  }
2869
2880
  if (card.primitive === "tables") {
2881
+ if (isClayMigrationIntent(query))
2882
+ return { tools: ["oxygen_clay_migration"], commands: ["clay migration"], exactCommand: "oxygen clay migration --action connect --json", exactMcpCall: { tool: "oxygen_clay_migration", arguments: { action: "connect" } } };
2870
2883
  const removal = tableRowRemovalRecommendation(query);
2871
2884
  if (removal)
2872
2885
  return removal;
@@ -0,0 +1,3 @@
1
+ export declare function tagClayMigrationFailureState(error: unknown, encryptedState: string): void;
2
+ /** Consume only state attached in this process by the migration adapter. */
3
+ export declare function readClayMigrationFailureState(error: unknown): string | null;
@@ -0,0 +1,15 @@
1
+ // Rotated credentials must survive a failed source read, without becoming part
2
+ // of a customer error, operator log or serialized tool result.
3
+ const failureStates = new WeakMap();
4
+ export function tagClayMigrationFailureState(error, encryptedState) {
5
+ if (error !== null && typeof error === "object")
6
+ failureStates.set(error, encryptedState);
7
+ }
8
+ /** Consume only state attached in this process by the migration adapter. */
9
+ export function readClayMigrationFailureState(error) {
10
+ if (error === null || typeof error !== "object")
11
+ return null;
12
+ const state = failureStates.get(error) ?? null;
13
+ failureStates.delete(error);
14
+ return state;
15
+ }
@@ -0,0 +1,71 @@
1
+ import type { OperationSpec } from "./operation-request.js";
2
+ import { type ContractIssue, type Infer } from "./schema.js";
3
+ export declare const CLAY_MIGRATION_ACTIONS: readonly ["connect", "status", "discover", "read", "transfer", "checkpoint", "report", "disconnect"];
4
+ export declare const CLAY_RESOURCE_TYPES: readonly ["workspace", "folder", "workbook", "table", "audience", "workflow", "function", "claygent", "knowledge", "schedule", "campaign"];
5
+ export declare const CLAY_RESOURCE_DISPOSITIONS: readonly ["pending", "copied", "rebuilt", "needs_decision", "unsupported", "inaccessible", "excluded", "failed"];
6
+ export declare const CLAY_MIGRATION_CONTRACT: import("./schema.js").ContractObject<{
7
+ readonly action: {
8
+ type: "string";
9
+ enum: readonly ("checkpoint" | "connect" | "disconnect" | "discover" | "read" | "report" | "status" | "transfer")[];
10
+ description?: string;
11
+ };
12
+ readonly migration_id: import("./schema.js").StringProperty;
13
+ readonly source_workspace_id: import("./schema.js").StringProperty;
14
+ readonly resource_type: {
15
+ type: "string";
16
+ enum: readonly ("audience" | "campaign" | "claygent" | "folder" | "function" | "knowledge" | "schedule" | "table" | "workbook" | "workflow" | "workspace")[];
17
+ description?: string;
18
+ };
19
+ readonly resource_id: import("./schema.js").StringProperty;
20
+ readonly section: {
21
+ type: "string";
22
+ enum: readonly ("columns" | "fields" | "graph" | "metadata" | "record_ids" | "records" | "rows" | "sequence")[];
23
+ description?: string;
24
+ };
25
+ readonly path: import("./schema.js").StringProperty;
26
+ readonly text_offset: {
27
+ type: "integer";
28
+ description?: string;
29
+ minimum?: number;
30
+ maximum?: number;
31
+ "x-clamp"?: true;
32
+ };
33
+ readonly entity_type: {
34
+ type: "string";
35
+ enum: readonly ("companies" | "deals" | "people")[];
36
+ description?: string;
37
+ };
38
+ readonly record_ids: import("./schema.js").ArrayProperty<{
39
+ type: "integer";
40
+ description?: string;
41
+ minimum?: number;
42
+ maximum?: number;
43
+ "x-clamp"?: true;
44
+ }>;
45
+ readonly cursor: import("./schema.js").StringProperty;
46
+ readonly limit: {
47
+ type: "integer";
48
+ description?: string;
49
+ minimum?: number;
50
+ maximum?: number;
51
+ "x-clamp"?: true;
52
+ };
53
+ readonly target_table: import("./schema.js").StringProperty;
54
+ readonly source_cells_column: import("./schema.js").StringProperty;
55
+ readonly source_row_id_column: import("./schema.js").StringProperty;
56
+ readonly mapping: import("./schema.js").ArrayProperty<import("./schema.js").ContractObject<{
57
+ readonly source_column_id: import("./schema.js").StringProperty;
58
+ readonly target_column: import("./schema.js").StringProperty;
59
+ }, "source_column_id" | "target_column">>;
60
+ readonly disposition: {
61
+ type: "string";
62
+ enum: readonly ("copied" | "excluded" | "failed" | "inaccessible" | "needs_decision" | "pending" | "rebuilt" | "unsupported")[];
63
+ description?: string;
64
+ };
65
+ readonly target_kind: import("./schema.js").StringProperty;
66
+ readonly target_id: import("./schema.js").StringProperty;
67
+ readonly notes: import("./schema.js").StringProperty;
68
+ }, "action">;
69
+ export type ClayMigrationInput = Infer<typeof CLAY_MIGRATION_CONTRACT>;
70
+ export declare function clayMigrationInputIssues(args: Readonly<Record<string, unknown>>): ContractIssue[];
71
+ export declare const CLAY_MIGRATION_OPERATION: OperationSpec<typeof CLAY_MIGRATION_CONTRACT>;
@@ -0,0 +1,60 @@
1
+ import { arr, enumOf, int, obj, str } from "./schema.js";
2
+ export const CLAY_MIGRATION_ACTIONS = ["connect", "status", "discover", "read", "transfer", "checkpoint", "report", "disconnect"];
3
+ export const CLAY_RESOURCE_TYPES = ["workspace", "folder", "workbook", "table", "audience", "workflow", "function", "claygent", "knowledge", "schedule", "campaign"];
4
+ export const CLAY_RESOURCE_DISPOSITIONS = ["pending", "copied", "rebuilt", "needs_decision", "unsupported", "inaccessible", "excluded", "failed"];
5
+ export const CLAY_MIGRATION_CONTRACT = obj({
6
+ action: enumOf(CLAY_MIGRATION_ACTIONS),
7
+ migration_id: str({ minLength: 1, maxLength: 200 }),
8
+ source_workspace_id: str({ minLength: 1, maxLength: 200 }),
9
+ resource_type: enumOf(CLAY_RESOURCE_TYPES),
10
+ resource_id: str({ minLength: 1, maxLength: 200 }),
11
+ section: enumOf(["metadata", "columns", "rows", "graph", "fields", "record_ids", "records", "sequence"]),
12
+ path: str({ minLength: 1, maxLength: 1000, description: "Dotted path or JSON pointer into source configuration, e.g. data.0.settings." }),
13
+ text_offset: int({ minimum: 0, maximum: 8388608, description: "Continue a large scalar read at next_text_offset." }),
14
+ entity_type: enumOf(["people", "companies", "deals"]),
15
+ record_ids: arr(int({ minimum: 1 }), { minItems: 1, maxItems: 100 }),
16
+ cursor: str({ maxLength: 4000 }),
17
+ limit: int({ minimum: 1, maximum: 100 }),
18
+ target_table: str({ minLength: 1, maxLength: 200 }),
19
+ source_cells_column: str({ minLength: 1, maxLength: 200, description: "Existing JSON column preserving original Clay cells; default clay_source_cells." }),
20
+ source_row_id_column: str({ minLength: 1, maxLength: 200, description: "Existing text column for stable source row IDs; default clay_source_row_id." }),
21
+ mapping: arr(obj({ source_column_id: str({ minLength: 1, maxLength: 200 }), target_column: str({ minLength: 1, maxLength: 200 }) }, { required: ["source_column_id", "target_column"] }), { minItems: 1, maxItems: 500, description: "Source Clay column IDs or Audience field keys to existing Oxygen column keys." }),
22
+ disposition: enumOf(CLAY_RESOURCE_DISPOSITIONS),
23
+ target_kind: str({ minLength: 1, maxLength: 100 }),
24
+ target_id: str({ minLength: 1, maxLength: 200 }),
25
+ notes: str({ maxLength: 8000 }),
26
+ }, { required: ["action"] });
27
+ export function clayMigrationInputIssues(args) {
28
+ const issues = [];
29
+ const requireField = (key) => { if (args[key] === undefined)
30
+ issues.push({ path: key, message: `${key} is required for ${String(args.action)}.` }); };
31
+ if (args.action !== "connect")
32
+ requireField("migration_id");
33
+ if (["read", "transfer", "checkpoint"].includes(String(args.action)))
34
+ requireField("resource_type");
35
+ const workspaceRead = ["workspace", "knowledge"].includes(String(args.resource_type))
36
+ || (args.resource_type === "audience" && ["fields", "record_ids", "records"].includes(String(args.section)));
37
+ if (["transfer", "checkpoint"].includes(String(args.action)) || (args.action === "read" && !workspaceRead))
38
+ requireField("resource_id");
39
+ if (args.action === "transfer") {
40
+ requireField("target_table");
41
+ requireField("mapping");
42
+ if (!["table", "audience"].includes(String(args.resource_type)))
43
+ issues.push({ path: "resource_type", message: "Bulk transfer requires a Clay table or Audience; rebuild other resources with native Oxygen tools." });
44
+ }
45
+ if (args.action === "checkpoint")
46
+ requireField("disposition");
47
+ if (args.section === "records") {
48
+ requireField("entity_type");
49
+ requireField("record_ids");
50
+ }
51
+ return issues;
52
+ }
53
+ export const CLAY_MIGRATION_OPERATION = {
54
+ id: "clay.migration", route: "/api/cli/migrations/clay", method: "POST",
55
+ contract: CLAY_MIGRATION_CONTRACT, inputCheck: clayMigrationInputIssues,
56
+ generated: { cli: false, mcp: false },
57
+ cli: { path: ["clay", "migration"], summary: "Migrate a Clay workspace into Oxygen: connect, discover source configuration, inspect bounded samples, transfer table and Audience rows, checkpoint native rebuilds and reconcile coverage. Creates no paid runs or active schedules." },
58
+ mcp: { name: "oxygen_clay_migration", title: "Oxygen: Migrate from Clay", description: "Connect to Clay with browser sign-in, discover resources, read source configuration and samples, transfer table and Audience rows to existing Oxygen tables, checkpoint rebuilds and report coverage. Use native Oxygen tools to create targets and rebuild behavior. Follow cursors; report unsupported or inaccessible resources. Never activates schedules or paid runs.", pack: "integrations", annotations: { openWorldHint: true, destructiveHint: false, idempotentHint: false } },
59
+ mutates: true, spends: false,
60
+ };
@@ -17,6 +17,7 @@ import { AGENT_SESSION_OPERATIONS } from "./agent-sessions.js";
17
17
  import { REPLY_OXYGENT_OPERATIONS } from "./reply-oxygent.js";
18
18
  import { TEAMMATE_OPERATIONS } from "./teammates.js";
19
19
  import { WORKSPACE_SKILL_OPERATIONS } from "./workspace-skills.js";
20
+ import { CLAY_MIGRATION_OPERATION } from "./clay-migration.js";
20
21
  export { operationPath, operationRequest } from "./operation-request.js";
21
22
  /** The exact public grammar, including the operation's scoped route parameters. */
22
23
  export function operationInputContract(spec) {
@@ -60,6 +61,6 @@ export function operationFormData(spec, body) {
60
61
  }
61
62
  return form;
62
63
  }
63
- export const OPERATION_SPECS = [SENDER_LIMITS_SET_OPERATION, CONNECTOR_UPDATE_OPERATION, CONNECTOR_EVENTS_REVOKE_OPERATION, WORKSPACE_PAGES_GET_OPERATION, WORKSPACE_PAGES_UPDATE_OPERATION, TABLE_FEED_UPDATE_OPERATION, ...CUSTOMER_OPERATIONS, ...GEO_OPERATIONS, ...BROWSER_OPERATIONS, ...AGENT_SESSION_OPERATIONS, ...GUIDED_ACTIVATION_OPERATIONS, ...TEAMMATE_OPERATIONS, ...REPLY_OXYGENT_OPERATIONS, ...WORKSPACE_SKILL_OPERATIONS];
64
+ export const OPERATION_SPECS = [CLAY_MIGRATION_OPERATION, SENDER_LIMITS_SET_OPERATION, CONNECTOR_UPDATE_OPERATION, CONNECTOR_EVENTS_REVOKE_OPERATION, WORKSPACE_PAGES_GET_OPERATION, WORKSPACE_PAGES_UPDATE_OPERATION, TABLE_FEED_UPDATE_OPERATION, ...CUSTOMER_OPERATIONS, ...GEO_OPERATIONS, ...BROWSER_OPERATIONS, ...AGENT_SESSION_OPERATIONS, ...GUIDED_ACTIVATION_OPERATIONS, ...TEAMMATE_OPERATIONS, ...REPLY_OXYGENT_OPERATIONS, ...WORKSPACE_SKILL_OPERATIONS];
64
65
  export const CLI_OPERATIONS = [...CUSTOMER_OPERATIONS, ...GEO_OPERATIONS, ...BROWSER_OPERATIONS, ...AGENT_SESSION_OPERATIONS, ...GUIDED_ACTIVATION_OPERATIONS, ...TEAMMATE_OPERATIONS, ...REPLY_OXYGENT_OPERATIONS, ...WORKSPACE_SKILL_OPERATIONS].filter((spec) => spec.generated?.cli !== false);
65
66
  export const MCP_OPERATIONS = [...CUSTOMER_OPERATIONS, ...GEO_OPERATIONS, ...BROWSER_OPERATIONS, ...AGENT_SESSION_OPERATIONS, ...GUIDED_ACTIVATION_OPERATIONS, ...TEAMMATE_OPERATIONS, ...REPLY_OXYGENT_OPERATIONS, ...WORKSPACE_SKILL_OPERATIONS].filter((spec) => spec.generated?.mcp !== false);
@@ -28,6 +28,7 @@ export async function devEvalFetch(provider, operation, input, init, transport =
28
28
  return context ? context.fetch(provider, operation, input, init, transport) : transport(input, init);
29
29
  }
30
30
  export const DEV_EVAL_SAFE_CAPABILITIES = new Set([
31
+ "oxygen_knowledge_index", "oxygen_knowledge_search", "oxygen_knowledge_page_get",
31
32
  "oxygen_tables_list", "oxygen_tables_sources", "oxygen_tables_describe", "oxygen_tables_preview", "oxygen_tables_query", "oxygen_tables_create", "oxygen_tables_update", "oxygen_tables_rows_add", "oxygen_tables_rows_update",
32
33
  "oxygen_columns_catalog", "oxygen_columns_deps", "oxygen_columns_add", "oxygen_columns_update", "oxygen_columns_run", "oxygen_columns_materialize",
33
34
  "oxygen_enrichment_catalog", "oxygen_tools_search", "oxygen_tools_describe", "oxygen_tools_check_access", "oxygen_tools_plan_table_batch", "oxygen_tools_run_dry", "oxygen_tools_run_live",
@@ -195,7 +195,10 @@ export const HOSTED_AI_MODEL_REGISTRY = {
195
195
  reasoningEffort: "medium",
196
196
  estPromptUsdPerM: 0.1,
197
197
  estCompletionUsdPerM: 0.5,
198
- maxOutputTokens: 4096,
198
+ // Reasoning tokens count against max_tokens: at 4,096 Haiku spent 3.5-4k
199
+ // thinking and returned no answer. Copilot's cap for the same model and
200
+ // effort. Spend admission uses AI_COLUMN_TIER_COGS_USD, not this ceiling.
201
+ maxOutputTokens: 16_384,
199
202
  },
200
203
  high: {
201
204
  model: "openai/gpt-6.1-sol",
@@ -204,7 +207,8 @@ export const HOSTED_AI_MODEL_REGISTRY = {
204
207
  reasoningEffort: "medium",
205
208
  estPromptUsdPerM: 2,
206
209
  estCompletionUsdPerM: 10,
207
- maxOutputTokens: 4096,
210
+ // Reasoning counts against max_tokens, so Balanced's cap (see Balanced).
211
+ maxOutputTokens: 16_384,
208
212
  },
209
213
  },
210
214
  copilot: COPILOT_MODEL_SPECS,
@@ -279,8 +283,8 @@ export const KNOWLEDGE_BOOTSTRAP_SYNTHESIS_MODEL = {
279
283
  fallbackModels: ["openai/gpt-6-luna"],
280
284
  disableReasoning: true,
281
285
  // The profile plus the cited note of a well-documented company runs past the
282
- // Balanced tier's 4,096: on dev (2026-10-18) attio.com and lemlist.com stopped at
283
- // exactly 4,096 tokens (`finish_reason: length`), mid-note, as invalid JSON, so the
286
+ // Balanced tier's then 4,096: on dev (2026-10-18) attio.com and lemlist.com stopped
287
+ // at exactly 4,096 tokens (`finish_reason: length`), mid-note, as invalid JSON, so the
284
288
  // run ended `partial` with no research page. A shorter answer is unaffected.
285
289
  maxOutputTokens: 8192,
286
290
  // The same model ran 3x apart by provider on dev (2026-10-18): DigitalOcean and
@@ -19,12 +19,15 @@ export declare const ONBOARDING_RESEARCH_INFERENCE_MAX_CREDITS = 150;
19
19
  /**
20
20
  * Eight areas at once (offer, ICP, plays, signals, competitors, company
21
21
  * LinkedIn, people, the signing-up person), with room for one follow-up pass.
22
+ * Each researcher wraps up after 90 seconds: on the first live run the slowest
23
+ * of eight took 370 seconds and everyone waited on it (2026-10-19).
22
24
  */
23
25
  export declare const ONBOARDING_RESEARCH_SUBAGENT_LIMITS: {
24
26
  readonly maxRuns: 12;
25
27
  readonly maxParallelTasks: 8;
26
- readonly maxInferencesPerRun: 8;
27
- readonly maxCapabilityCallsPerRun: 24;
28
+ readonly maxInferencesPerRun: 6;
29
+ readonly maxCapabilityCallsPerRun: 20;
30
+ readonly concludeAfterMs: 90000;
28
31
  };
29
32
  /**
30
33
  * Read-only provider tools the research may call through `oxygen_tools_run_live`.
@@ -33,5 +36,7 @@ export declare const ONBOARDING_RESEARCH_SUBAGENT_LIMITS: {
33
36
  */
34
37
  export declare const ONBOARDING_RESEARCH_TOOL_IDS: readonly ["serper.search", "exa.search", "exa.contents", "exa.find_similar", "firecrawl.map", "firecrawl.scrape", "web.page_fetch", "scraper.linkedin_company", "scraper.linkedin_profile", "scraper.linkedin_profile_search", "scraper.linkedin_job_search", "blitzapi.employee_search", "blitzapi.person_profile_enrich", "blitzapi.company_profile_enrich", "predictleads.company_news_events", "predictleads.company_job_openings", "predictleads.company_similar_companies"];
35
38
  export declare function onboardingResearchToolAllowed(toolId: unknown): boolean;
39
+ /** The hub pages the onboarding Oxygent itself writes; research links to them and never creates them. */
40
+ export declare const ONBOARDING_RESEARCH_HUB_SLUGS: readonly ["company", "offers", "icp", "plays", "signals", "competitors", "team"];
36
41
  /** Knowledge page types a research sub-agent may not write: canonical voice and positioning stay proposal-gated. */
37
42
  export declare const ONBOARDING_RESEARCH_DENIED_PAGE_TYPES: readonly ["voice", "brand", "positioning", "message_playbook", "playbook"];
@@ -19,12 +19,15 @@ export const ONBOARDING_RESEARCH_INFERENCE_MAX_CREDITS = 150;
19
19
  /**
20
20
  * Eight areas at once (offer, ICP, plays, signals, competitors, company
21
21
  * LinkedIn, people, the signing-up person), with room for one follow-up pass.
22
+ * Each researcher wraps up after 90 seconds: on the first live run the slowest
23
+ * of eight took 370 seconds and everyone waited on it (2026-10-19).
22
24
  */
23
25
  export const ONBOARDING_RESEARCH_SUBAGENT_LIMITS = {
24
26
  maxRuns: 12,
25
27
  maxParallelTasks: 8,
26
- maxInferencesPerRun: 8,
27
- maxCapabilityCallsPerRun: 24,
28
+ maxInferencesPerRun: 6,
29
+ maxCapabilityCallsPerRun: 20,
30
+ concludeAfterMs: 90_000,
28
31
  };
29
32
  /**
30
33
  * Read-only provider tools the research may call through `oxygen_tools_run_live`.
@@ -53,5 +56,7 @@ export const ONBOARDING_RESEARCH_TOOL_IDS = [
53
56
  export function onboardingResearchToolAllowed(toolId) {
54
57
  return typeof toolId === "string" && ONBOARDING_RESEARCH_TOOL_IDS.includes(toolId);
55
58
  }
59
+ /** The hub pages the onboarding Oxygent itself writes; research links to them and never creates them. */
60
+ export const ONBOARDING_RESEARCH_HUB_SLUGS = ["company", "offers", "icp", "plays", "signals", "competitors", "team"];
56
61
  /** Knowledge page types a research sub-agent may not write: canonical voice and positioning stay proposal-gated. */
57
62
  export const ONBOARDING_RESEARCH_DENIED_PAGE_TYPES = ["voice", "brand", "positioning", "message_playbook", "playbook"];
@@ -1 +1 @@
1
- export declare const OXYGEN_BUILD_VERSION = "1.1014.1420";
1
+ export declare const OXYGEN_BUILD_VERSION = "1.1014.1451";
@@ -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.1420";
2
+ export const OXYGEN_BUILD_VERSION = "1.1014.1451";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxygen-agent/cli",
3
- "version": "1.1014.1420",
3
+ "version": "1.1014.1451",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",