@oxygen-agent/cli 1.1014.69 → 1.1014.86

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.69
37
+ Version: 1.1014.86
package/dist/help.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { CommanderError, type Command } from "commander";
2
2
  import { type CliFailure } from "@oxygen/shared/cli-result";
3
- export declare function applyOxygenHelp(program: Command, binaryName: string): void;
3
+ export declare function applyOxygenHelp(program: Command, binaryName: string, docsUrl?: string): void;
4
4
  /**
5
5
  * Turn commander's own edit-distance suggester on for EVERY command.
6
6
  *
package/dist/help.js CHANGED
@@ -125,13 +125,11 @@ const HELP_GROUPS = [
125
125
  },
126
126
  ];
127
127
  const FALLBACK_HEADING = "Other commands:";
128
- export function applyOxygenHelp(program, binaryName) {
128
+ export function applyOxygenHelp(program, binaryName, docsUrl = "https://oxygen-agent.com/docs") {
129
129
  program.option("--help-full", "Show expanded command and option descriptions.");
130
130
  const configure = (command) => {
131
131
  command.configureHelp({
132
- commandDescription: (cmd) => shortHelpDescription(cmd.description()),
133
132
  subcommandDescription: (cmd) => shortHelpDescription(cmd.description()),
134
- optionDescription: (option) => shortHelpDescription(option.description),
135
133
  });
136
134
  for (const child of command.commands)
137
135
  configure(child);
@@ -171,7 +169,7 @@ export function applyOxygenHelp(program, binaryName) {
171
169
  ` 5. ${binaryName} recipes list "<goal>" --view summary --json choose a play from the Recipe catalog for your goal`,
172
170
  ` Provider inventory: ${binaryName} tools search <brand> --json, then ${binaryName} tools search --provider <provider-id> --all --json (Blitz uses blitzapi).`,
173
171
  ` Installable systems and recurring monitors: ${binaryName} blueprints list --json.`,
174
- " Docs: https://oxygen-agent.com/docs",
172
+ ` Docs: ${docsUrl}`,
175
173
  "",
176
174
  "Conventions:",
177
175
  " --json prints a stable envelope {ok, data|error, meta}; errors always print it.",
@@ -186,8 +184,12 @@ export function applyOxygenHelp(program, binaryName) {
186
184
  function shortHelpDescription(description) {
187
185
  if (process.argv.includes("--help-full"))
188
186
  return description;
189
- const first = description.split(/(?<=[.!?])\s/)[0] ?? description;
190
- return first.length <= 180 ? first : `${first.slice(0, 177)}…`;
187
+ // Summarize navigation cards only. Selected commands and option grammar stay
188
+ // complete: cutting a flag at "e.g." or dropping its mode restriction caused
189
+ // a dead end in the native blind acceptance. Never truncate inside examples.
190
+ const sentences = description.split(/(?<!\be\.g\.)(?<!\bi\.e\.)(?<=[.!?])\s+/);
191
+ const safety = /\b(?:credits?|read-only|free|preview\w*|approv\w*|purg\w*|permanent\w*|irreversible)\b|--(?:yes|now)\b/i;
192
+ return sentences.filter((sentence, index) => index === 0 || safety.test(sentence)).join(" ");
191
193
  }
192
194
  /**
193
195
  * Turn commander's own edit-distance suggester on for EVERY command.
package/dist/index.js CHANGED
@@ -1645,6 +1645,14 @@ function writeMaxCreditsHint(error) {
1645
1645
  if (reservation === null)
1646
1646
  return;
1647
1647
  const nextStep = readDetailsString(error.details, "next_step");
1648
+ // A free workspace connecting LinkedIn is sent to the plan, not to a top-up (2026-10-11).
1649
+ if (readDetailsString(error.details, "remedy") === "subscribe") {
1650
+ const upgradeUrl = readDetailsString(error.details, "upgrade_url");
1651
+ process.stderr.write(`hint: this connect reserves ${reservation} credits a month; subscribe to the Oxygen plan, then connect again\n`);
1652
+ if (upgradeUrl)
1653
+ process.stderr.write(`hint: choose a plan at ${upgradeUrl}\n`);
1654
+ return;
1655
+ }
1648
1656
  const topupUrl = readDetailsString(error.details, "topup_url");
1649
1657
  process.stderr.write(`hint: this connect reserves ${reservation} credits a month; add credits, then connect again\n`);
1650
1658
  if (nextStep)
@@ -3672,7 +3680,7 @@ export function createProgram() {
3672
3680
  .name(binaryName)
3673
3681
  .description("Revenue infrastructure for B2B startups — agent-operated GTM: tables, enrichment, sequences, workflows, CRM, knowledge. Everything here also runs in the web app and over MCP; every state-changing action returns a web_url deep-link.")
3674
3682
  .version(OXYGEN_VERSION)
3675
- .option("--pretty", "Indent JSON output (default: compact JSON).")
3683
+ .option("--pretty", "Indent JSON for human reading; omit for compact agent output.")
3676
3684
  .option("--profile <name>", "Use a stored CLI profile for this command.")
3677
3685
  .option("--org <organization>", "Use an organization id, Clerk org id, or slug for this command.");
3678
3686
  program.hook("preAction", (_root, selected) => {
@@ -7241,7 +7249,7 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
7241
7249
  });
7242
7250
  }))
7243
7251
  .addCommand(addContractFlags(new Command("query"), TABLE_QUERY_GRID_CONTRACT, {
7244
- format: { valueName: "<format>", help: "Grid cell previews/state: default 50, cap 200, optional stats/fill/window. Omit for raw filters, fields, search, sorts or offsets." },
7252
+ format: { valueName: "<format>", help: "Grid cell previews/state: default 50, cap 200, optional stats/fill/window. Omit for raw filters, fields, search, sorts or offsets. JSON cells are null; their digests live in __oxygen_grid_cell_previews. Use tables rows inspect --projection value for complete values." },
7245
7253
  max_chars: { help: "Maximum compact JSON envelope characters for raw or grid reads (1,000-100,000). Raw defaults to 12,000." },
7246
7254
  })
7247
7255
  .description("Read raw Table rows or --format grid cell previews/state with grid.nextCursor, in a JSON envelope. Use tables preview --summary-only for fill rates; tables export <table> --format table renders raw rows. Grid supports stats/fill/window options and refuses raw selectors.")
@@ -12742,6 +12750,32 @@ Examples:
12742
12750
  program
12743
12751
  .command("copilot")
12744
12752
  .description("Workspace Copilot: an attended workspace agent you drive turn by turn from the terminal. Start a session, send it messages and watch the reply stream, approve the actions it proposes, then cancel when done. Managed inference uses workspace credits; paid or external actions keep their own approval gates. Web: /copilot.")
12753
+ .addCommand(new Command("dev-eval")
12754
+ .description("DEV staff owner only: immutable USD30 campaign and supplier ledger; classify dispatches guarded inference.")
12755
+ .argument("<operation>", "create, register, readiness, ledger, effects, classify, accept-pilot or end")
12756
+ .option("--campaign-id <uuid>")
12757
+ .option("--session-id <uuid>")
12758
+ .option("--models <ids>", "Comma-separated exact priced model IDs for create")
12759
+ .option("--question-set <name>", "Pinned thesis-icp-1 or title-fit-1")
12760
+ .option("--state-json <json>", "Classifier state from the native readout")
12761
+ .option("--json", "Print a JSON envelope.")
12762
+ .action(async (operation, options) => {
12763
+ await handleAsyncAction("copilot dev-eval", options, () => {
12764
+ if (operation === "readiness")
12765
+ return requestOxygen("/api/cli/copilot/dev-eval?readiness=1");
12766
+ if (operation === "effects")
12767
+ return requestOxygen(`/api/cli/copilot/dev-eval?campaign_id=${encodeURIComponent(options.campaignId ?? "")}&session_id=${encodeURIComponent(options.sessionId ?? "")}`);
12768
+ if (operation === "ledger")
12769
+ return requestOxygen(`/api/cli/copilot/dev-eval?campaign_id=${encodeURIComponent(options.campaignId ?? "")}`);
12770
+ if (!["create", "register", "end", "classify", "accept-pilot"].includes(operation))
12771
+ throw new OxygenError("invalid_input", "Unknown campaign operation.", { exitCode: 1 });
12772
+ return requestOxygen("/api/cli/copilot/dev-eval", { method: "POST", body: {
12773
+ operation, campaign_id: options.campaignId, session_id: options.sessionId,
12774
+ ...(options.questionSet ? { question_set: options.questionSet, state: JSON.parse(options.stateJson ?? "null") } : {}),
12775
+ ...(options.models ? { models: options.models.split(",").map(value => value.trim()) } : {}),
12776
+ } });
12777
+ });
12778
+ }), { hidden: true })
12745
12779
  .addCommand(new Command("start")
12746
12780
  .description("Start an attended Workspace Copilot session on Oxygen Balanced by default. Starting is free; the next send starts inference using workspace credits. No inference budget is required. Legacy budget flags remain accepted but do not authorize or cap spend. Paid or external actions require separate approval.")
12747
12781
  .option("--budget-credits <number>", "Legacy compatibility only: accepted and clamped, but does not authorize or cap attended Copilot inference.")
@@ -12804,6 +12838,7 @@ Examples:
12804
12838
  .argument("<sessionId>", "Copilot session id.")
12805
12839
  .option("--after-seq <n>", "Only return events after this sequence number.")
12806
12840
  .option("--events <n>", "Maximum events to return.")
12841
+ .option("--wait-seconds <n>", "Native bounded event long-poll, requires --after-seq (maximum 50 seconds).")
12807
12842
  .option("--json", "Print a JSON envelope.")
12808
12843
  .action(async (sessionId, options) => {
12809
12844
  await handleAsyncAction("copilot get", options, () => {
@@ -12812,6 +12847,8 @@ Examples:
12812
12847
  const eventLimit = readPositiveInt(options.events);
12813
12848
  if (afterSeq !== undefined)
12814
12849
  query.set("after_seq", String(afterSeq));
12850
+ if (options.waitSeconds !== undefined)
12851
+ query.set("wait_seconds", String(options.waitSeconds));
12815
12852
  if (eventLimit !== undefined)
12816
12853
  query.set("event_limit", String(eventLimit));
12817
12854
  const suffix = query.toString() ? `?${query.toString()}` : "";
@@ -20549,7 +20586,7 @@ Full trigger schema: oxygen workflows schema --subject trigger --json
20549
20586
  // exist when this runs, and a family registered later fell into commander's
20550
20587
  // trailing default "Commands:" list (the 2026-09-30 blind agent read the first
20551
20588
  // 80 lines of help and missed `connectors` there).
20552
- applyOxygenHelp(program, binaryName);
20589
+ applyOxygenHelp(program, binaryName, `${defaultApiUrl().replace(/\/+$/, "")}/docs`);
20553
20590
  // Last, so every command registered above is covered.
20554
20591
  enableCommandSuggestions(program);
20555
20592
  return program;
@@ -1,6 +1,7 @@
1
1
  import { lstat, readdir, readFile } from "node:fs/promises";
2
2
  import { join, resolve } from "node:path";
3
3
  import { OxygenError } from "@oxygen/shared/cli-result";
4
+ import { WORKSPACE_SKILL_OPERATIONS } from "@oxygen/shared/contracts/workspace-skills";
4
5
  import { authorWorkspaceSkill, parseWorkspaceSkillBundle, WORKSPACE_SKILL_MAX_BYTES, WORKSPACE_SKILL_MAX_FILES, WORKSPACE_SKILL_MAX_FILE_BYTES, } from "@oxygen/shared/workspace-skills";
5
6
  import { requestOxygen } from "./http-client.js";
6
7
  /** Never follow symlinks or read binary files out of a local skill folder. */
@@ -47,11 +48,11 @@ export function registerWorkspaceSkillCommands(program, handle) {
47
48
  const skills = program.commands.find((command) => command.name() === "skills");
48
49
  const workspace = skills
49
50
  .command("workspace")
50
- .description("Create, upload, find and edit private workspace skills in Knowledge.");
51
+ .description("Create, upload, find and edit skills private to this workspace's members.");
51
52
  workspace
52
53
  .command("create")
53
- .description("Write a private workspace skill. Stores instructions; 0 credits.")
54
- .requiredOption("--name <name>", "Lowercase-hyphen skill name.")
54
+ .description(WORKSPACE_SKILL_OPERATIONS.find((operation) => operation.id === "workspace_skills.create").cli.summary)
55
+ .requiredOption("--name <name>", "Lowercase-hyphen name; stored as workspace-<name>.")
55
56
  .requiredOption("--description <text>", "When to use this skill.")
56
57
  .requiredOption("--instructions <text>", "Instructions to save.")
57
58
  .option("--json", "Print a JSON envelope.")
@@ -68,7 +69,7 @@ export function registerWorkspaceSkillCommands(program, handle) {
68
69
  }));
69
70
  workspace
70
71
  .command("import")
71
- .description("Upload SKILL.md or a skill folder from any source into private Knowledge. 0 credits.")
72
+ .description(WORKSPACE_SKILL_OPERATIONS.find((operation) => operation.id === "workspace_skills.import").cli.summary)
72
73
  .option("--file <path>", "Local SKILL.md file.")
73
74
  .option("--directory <path>", "Local skill directory including SKILL.md and supporting text files.")
74
75
  .option("--url <url>", "Import a public HTTPS SKILL.md URL. Supporting files require --directory or --files-json.")
@@ -218,7 +218,7 @@ export function columnDecisionOutputSchema(decision) {
218
218
  confidence: { type: "number", description: decision.mode === "score"
219
219
  ? "System One confidence, 0 to 1. It is separate from level probabilities, not their top share or the gap between the top two."
220
220
  : "How sure the classifier is, 0 to 1." },
221
- uncertain: { type: "boolean", description: "True when confidence is below the column's floor." },
221
+ uncertain: { type: "boolean", description: "True when confidence is below the floor, selected evidence is missing or shortened, or required grounding is unusable." },
222
222
  reason: { type: "string", description: decision.mode === "score"
223
223
  ? "Readable level, confidence, and top probabilities; it does not explain the row evidence."
224
224
  : "The answer and its confidence in words." },
@@ -33,16 +33,16 @@ const operation = (verb, method, contract, summary, manualCli = false) => ({
33
33
  });
34
34
  export const WORKSPACE_SKILL_OPERATIONS = [
35
35
  operation("list", "GET", obj({
36
- query: str(),
37
- cursor: str(),
38
- limit: int({ minimum: 1, maximum: 100 }),
39
- }), "Find private workspace-authored or imported skills by name or description. Read-only, 0 credits; next_cursor continues the list."),
36
+ query: str({ description: "Case-insensitive literal substring of the name or description; short keywords work best." }),
37
+ cursor: str({ description: "Exact next_cursor from the previous list response." }),
38
+ limit: int({ minimum: 1, maximum: 100, description: "Skills per page, from 1 to 100." }),
39
+ }), "Find skills shared with workspace members by literal substring of name or description; short keywords work best; read-only, 0 credits; next_cursor continues the list."),
40
40
  operation("get", "GET", obj({ name, file }, { required: ["name"] }), "Read a workspace skill's SKILL.md or one supporting text file, with exact content, revision and the document index. Skills are task guidance; they grant no tool, spend or approval authority."),
41
41
  operation("create", "POST", obj({
42
- name: str({ minLength: 1, maxLength: 64 }),
42
+ name: str({ minLength: 1, maxLength: 64, description: "Lowercase-hyphen name; stored as workspace-<name>." }),
43
43
  description: str({ minLength: 1, maxLength: 1_024 }),
44
44
  instructions: str({ minLength: 1, maxLength: 120_000 }),
45
- }, { required: ["name", "description", "instructions"] }), "Author a reusable private workspace skill in Knowledge from a lowercase-hyphen name, description and instructions. Creates SKILL.md; no inference, provider call or credit charge.", true),
45
+ }, { required: ["name", "description", "instructions"] }), "Create workspace-<name>, visible to this workspace's members; 0 credits, no inference/provider calls or new tool/spend/approval authority. Creates SKILL.md in Knowledge from a lowercase-hyphen name, description and instructions.", true),
46
46
  operation("import", "POST", obj({
47
47
  name: str({ maxLength: 64 }),
48
48
  files: arr(WORKSPACE_SKILL_FILE_CONTRACT, { minItems: 1, maxItems: 50 }),
@@ -51,12 +51,12 @@ export const WORKSPACE_SKILL_OPERATIONS = [
51
51
  description: "Public HTTPS SKILL.md URL (raw text or GitHub file). Choose url or files. A URL imports the entrypoint only; supply files for a complete folder.",
52
52
  }),
53
53
  source_url,
54
- }), "Import a private workspace skill: supply root SKILL.md plus supporting text files, or a public HTTPS SKILL.md URL. Preserves content and source. Create-only, atomic, at most 50 text files/1 MB. Scripts stay inert. CLI accepts --file, --directory or --url. 0 credits.", true),
54
+ }), "Import to workspace members atomically, create-only; ≤50 text files/1 MB; scripts inert; 0 credits, no inference/paid provider calls or new tool/spend/approval authority. Supply root SKILL.md plus supporting text files, or a public HTTPS SKILL.md URL. Preserves content and source. CLI accepts --file, --directory or --url.", true),
55
55
  operation("update", "POST", obj({
56
56
  name,
57
57
  file,
58
- content: str({ maxLength: 120_000 }),
59
- expected_revision: int({ minimum: 1 }),
60
- }, { required: ["name", "content", "expected_revision"] }), "Edit one workspace skill file at its exact expected revision, preserving Knowledge history. Read it first; stale revisions refuse. Defaults to SKILL.md. 0 credits."),
61
- operation("archive", "POST", obj({ name, expected_revision: int({ minimum: 1 }) }, { required: ["name", "expected_revision"] }), "Archive a workspace skill's SKILL.md at its expected revision, removing the skill from discovery while preserving files and history in Knowledge. 0 credits."),
58
+ content: str({ maxLength: 120_000, description: "Full replacement file; SKILL.md must include YAML frontmatter. Its description drives discovery." }),
59
+ expected_revision: int({ minimum: 1, description: "Revision last read; a stale revision refuses the edit." }),
60
+ }, { required: ["name", "content", "expected_revision"] }), "Replace a skill file at its expected revision; read first, stale revisions refuse; 0 credits, no inference/provider calls or new tool/spend/approval authority. Preserves Knowledge history; defaults to SKILL.md."),
61
+ operation("archive", "POST", obj({ name, expected_revision: int({ minimum: 1, description: "Entrypoint revision last read; stale revisions refuse." }) }, { required: ["name", "expected_revision"] }), "Archive a workspace skill, retaining files and Knowledge history; 0 credits, no inference/provider calls or new tool/spend/approval authority. Requires the entrypoint's expected revision; removes it from discovery."),
62
62
  ];
@@ -0,0 +1,3 @@
1
+ export declare const ICP_ACCOUNT = "Is the company this person works for a SMALL SOFTWARE STARTUP based in the UNITED STATES: roughly 2 to 50 employees, seed to Series A, selling software or a software-enabled service? Judge from the company name, domain, industry, size and location fields and from the person's own profile text (headline, about, experience). Answer true only when the evidence supports every part; a large company, a non-software business, or a non-US company is false.";
2
+ export declare const ICP_PERSONA = "Is this person a GO-TO-MARKET ENGINEER, a GROWTH or REVENUE OPERATOR (growth, revenue operations, demand generation, outbound, sales development leadership), or a FOUNDER who runs go-to-market at a startup? Judge from the title, headline and experience. Answer true only when the role clearly carries go-to-market or growth responsibility; an engineer without go-to-market scope, a finance, legal, HR or product role, or an unrelated title is false.";
3
+ export declare const TITLE_FIT = "Is this job title a go-to-market, growth, revenue-operations, sales-leadership, marketing-leadership or founder/CEO role? Answer true for titles such as GTM engineer, head of growth, growth lead, revenue operations, director of sales, VP marketing, founder, CEO, co-founder; false for engineering without go-to-market scope, finance, legal, HR, product, design, support or unrelated titles.";
@@ -0,0 +1,4 @@
1
+ // Exact source question text from scripts/evals/copilot-intent/classifier.mjs.
2
+ export const ICP_ACCOUNT = "Is the company this person works for a SMALL SOFTWARE STARTUP based in the UNITED STATES: roughly 2 to 50 employees, seed to Series A, selling software or a software-enabled service? Judge from the company name, domain, industry, size and location fields and from the person's own profile text (headline, about, experience). Answer true only when the evidence supports every part; a large company, a non-software business, or a non-US company is false.";
3
+ export const ICP_PERSONA = "Is this person a GO-TO-MARKET ENGINEER, a GROWTH or REVENUE OPERATOR (growth, revenue operations, demand generation, outbound, sales development leadership), or a FOUNDER who runs go-to-market at a startup? Judge from the title, headline and experience. Answer true only when the role clearly carries go-to-market or growth responsibility; an engineer without go-to-market scope, a finance, legal, HR or product role, or an unrelated title is false.";
4
+ export const TITLE_FIT = "Is this job title a go-to-market, growth, revenue-operations, sales-leadership, marketing-leadership or founder/CEO role? Answer true for titles such as GTM engineer, head of growth, growth lead, revenue operations, director of sales, VP marketing, founder, CEO, co-founder; false for engineering without go-to-market scope, finance, legal, HR, product, design, support or unrelated titles.";
@@ -0,0 +1,35 @@
1
+ export type DevEvalContext = Readonly<{
2
+ campaignId: string;
3
+ organizationId: string;
4
+ sessionId: string;
5
+ actionRunId?: string;
6
+ /** Installed by the authenticated host after control-DB lookup, never deserialized. */
7
+ fetch: (provider: string, operation: string, input: string | Request | URL, init: RequestInit | undefined, transport: typeof fetch) => Promise<Response>;
8
+ }>;
9
+ export declare const currentDevEvalContext: () => Readonly<{
10
+ campaignId: string;
11
+ organizationId: string;
12
+ sessionId: string;
13
+ actionRunId?: string;
14
+ /** Installed by the authenticated host after control-DB lookup, never deserialized. */
15
+ fetch: (provider: string, operation: string, input: string | Request | URL, init: RequestInit | undefined, transport: typeof fetch) => Promise<Response>;
16
+ }> | null;
17
+ export declare const withDevEvalContext: <T>(context: DevEvalContext | null, fn: () => T) => T;
18
+ export declare const initializeDevEvalRequest: () => void;
19
+ export declare const enterDevEvalContext: (context: DevEvalContext | null) => void;
20
+ export declare function devEvalRefusal(reason: string, outcome?: "not_applied" | "unknown"): never;
21
+ export declare function devEvalFetch(provider: string, operation: string, input: string | Request | URL, init: RequestInit | undefined, transport?: typeof fetch): Promise<Response>;
22
+ export declare const DEV_EVAL_SAFE_CAPABILITIES: Set<string>;
23
+ /** No approval or model-provided argument can override this trial policy. */
24
+ export declare function assertDevEvalCapability(name: string, args?: Record<string, unknown>): void;
25
+ export declare const DEV_EVAL_BLITZ_OPERATIONS: Set<string>;
26
+ export declare function assertDevEvalTool(toolId: string): void;
27
+ export declare function devEvalActionMetadata(metadata?: Record<string, unknown>): Record<string, unknown>;
28
+ export declare function assertDevEvalOrganization(organizationId: string | null | undefined): void;
29
+ /** Native queued-column entry: unsupported child engines cannot shed attribution. */
30
+ export declare function assertDevEvalColumn(column: {
31
+ kind: string;
32
+ definition: unknown;
33
+ }, actionType?: string): void;
34
+ /** Native host marker only requires a matching ledger; it never creates authority. */
35
+ export declare function assertDevEvalSessionContext(metadata: Record<string, unknown>, context: DevEvalContext | null): void;
@@ -0,0 +1,108 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { OxygenError } from "./cli-result.js";
3
+ // Mutable carrier is installed synchronously before the route awaits authentication.
4
+ // The authenticated resolver populates this carrier; enterWith after an await
5
+ // would only alter the resolver continuation and lose attribution in its caller.
6
+ const scope = new AsyncLocalStorage();
7
+ export const currentDevEvalContext = () => scope.getStore()?.context ?? null;
8
+ export const withDevEvalContext = (context, fn) => scope.run({ context }, fn);
9
+ export const initializeDevEvalRequest = () => scope.enterWith({ context: null });
10
+ export const enterDevEvalContext = (context) => {
11
+ const carrier = scope.getStore();
12
+ if (!carrier) {
13
+ if (context)
14
+ devEvalRefusal("request_carrier_missing");
15
+ return;
16
+ }
17
+ carrier.context = context;
18
+ };
19
+ export function devEvalRefusal(reason, outcome = "not_applied") {
20
+ const error = new OxygenError("dev_eval_policy_refused", `Dev campaign dispatch refused: ${reason}.`, {
21
+ details: { reason, effect_outcome: outcome, retryable: false }, exitCode: 1,
22
+ });
23
+ Object.assign(error, { retryable: false, effect_outcome: outcome });
24
+ throw error;
25
+ }
26
+ export async function devEvalFetch(provider, operation, input, init, transport = fetch) {
27
+ const context = currentDevEvalContext();
28
+ return context ? context.fetch(provider, operation, input, init, transport) : transport(input, init);
29
+ }
30
+ export const DEV_EVAL_SAFE_CAPABILITIES = new Set([
31
+ "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
+ "oxygen_columns_catalog", "oxygen_columns_deps", "oxygen_columns_add", "oxygen_columns_update", "oxygen_columns_run", "oxygen_columns_materialize",
33
+ "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",
34
+ "oxygen_people_search_preview", "oxygen_people_search_plan", "oxygen_people_search_run", "oxygen_companies_search_preview", "oxygen_companies_search_plan", "oxygen_companies_search_run",
35
+ "oxygen_sequences_list", "oxygen_sequences_create", "oxygen_sequences_draft", "oxygen_sequences_update", "oxygen_sequences_get", "oxygen_sequences_stats", "oxygen_sequences_events", "oxygen_sequences_render", "oxygen_sequences_enroll", "oxygen_sequences_enrollments", "oxygen_sequences_contacts", "oxygen_sequences_analytics"
36
+ ]);
37
+ /** No approval or model-provided argument can override this trial policy. */
38
+ export function assertDevEvalCapability(name, args = {}) {
39
+ if (!currentDevEvalContext())
40
+ return;
41
+ if (name === "oxygen_sequences_start" && args.approved === false)
42
+ return; // Native nonexecuting preview remains available.
43
+ if (!DEV_EVAL_SAFE_CAPABILITIES.has(name))
44
+ devEvalRefusal("capability_not_trial_safe");
45
+ if (args.then !== undefined || args.chain !== undefined)
46
+ devEvalRefusal("unsupported_chain_dispatch");
47
+ if (typeof args.max_credits === "number" && (!Number.isFinite(args.max_credits) || args.max_credits > 200))
48
+ devEvalRefusal("declared_credit_ceiling_too_large");
49
+ if (/(send|start|resume|publish|purchase|invite|mailbox|warmup|domain|crm|composio|custom_http|sandbox|infra|workflows|agents_)/i.test(name)) {
50
+ devEvalRefusal("forbidden_effect");
51
+ }
52
+ if (/sequence/i.test(name)) {
53
+ if (!/^oxygen_sequences_(list|create|draft|update|get|stats|events|render|enroll|enrollments|contacts|analytics)$/.test(name))
54
+ devEvalRefusal("sequence_effect_not_draft");
55
+ const inspect = (value) => {
56
+ if (!value || typeof value !== "object")
57
+ return;
58
+ for (const [key, child] of Object.entries(value)) {
59
+ if (/sender|mailbox|account.*id|emailConnection|emailProviderCampaign/i.test(key) && child !== null && child !== undefined && child !== "" && !(Array.isArray(child) && child.length === 0))
60
+ devEvalRefusal("draft_requires_no_sender");
61
+ if (/^(status|state)$/.test(key) && child !== "draft" && child !== undefined && child !== null)
62
+ devEvalRefusal("sequence_requires_draft");
63
+ inspect(child);
64
+ }
65
+ };
66
+ if (args.enroll === true || args.auto_start === true)
67
+ devEvalRefusal("draft_creation_with_automatic_enrollment");
68
+ inspect(args);
69
+ }
70
+ }
71
+ export const DEV_EVAL_BLITZ_OPERATIONS = new Set(["company_search", "people_search", "employee_search", "waterfall_person_search", "waterfall_icp_search", "person_enrich", "person_profile_enrich", "company_profile_enrich", "domain_to_linkedin", "company_enrich", "key_info"]);
72
+ export function assertDevEvalTool(toolId) {
73
+ if (!currentDevEvalContext())
74
+ return;
75
+ if (!toolId.startsWith("blitzapi.") || !DEV_EVAL_BLITZ_OPERATIONS.has(toolId.slice(9)))
76
+ devEvalRefusal("data_provider_or_operation");
77
+ }
78
+ export function devEvalActionMetadata(metadata = {}) {
79
+ const { dev_eval: _untrusted, ...rest } = metadata;
80
+ const context = currentDevEvalContext();
81
+ return context ? { ...rest, dev_eval: { campaignId: context.campaignId,
82
+ organizationId: context.organizationId, sessionId: context.sessionId } } : rest;
83
+ }
84
+ export function assertDevEvalOrganization(organizationId) {
85
+ const context = currentDevEvalContext();
86
+ if (context && context.organizationId !== organizationId)
87
+ devEvalRefusal("organization_attribution_mismatch");
88
+ }
89
+ /** Native queued-column entry: unsupported child engines cannot shed attribution. */
90
+ export function assertDevEvalColumn(column, actionType = "tool_column") {
91
+ if (!currentDevEvalContext())
92
+ return;
93
+ if (actionType !== "tool_column" || !['ai', 'research', 'tool', 'formula'].includes(column.kind))
94
+ devEvalRefusal('unsupported_column_child_engine');
95
+ const definition = column.definition;
96
+ if (definition?.mode === 'composio' || definition?.mode === 'custom_http')
97
+ devEvalRefusal('forbidden_column_transport');
98
+ if (column.kind === 'tool')
99
+ assertDevEvalTool(String(definition?.toolId ?? ''));
100
+ }
101
+ /** Native host marker only requires a matching ledger; it never creates authority. */
102
+ export function assertDevEvalSessionContext(metadata, context) {
103
+ const marker = metadata.dev_eval;
104
+ if (!marker)
105
+ return;
106
+ if (typeof marker !== 'object' || !context || marker.campaignId !== context.campaignId)
107
+ devEvalRefusal('registered_session_context_unavailable');
108
+ }
@@ -1 +1 @@
1
- export declare const OXYGEN_BUILD_VERSION = "1.1014.69";
1
+ export declare const OXYGEN_BUILD_VERSION = "1.1014.86";
@@ -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.69";
2
+ export const OXYGEN_BUILD_VERSION = "1.1014.86";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxygen-agent/cli",
3
- "version": "1.1014.69",
3
+ "version": "1.1014.86",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",