@oxygen-agent/cli 1.1014.19 → 1.1014.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/command-manifest.js +1 -1
- package/dist/help.js +21 -1
- package/dist/index.js +50 -14
- package/dist/json-output.d.ts +3 -0
- package/dist/json-output.js +33 -0
- package/dist/workspace-skill-commands.d.ts +9 -0
- package/dist/workspace-skill-commands.js +134 -0
- package/node_modules/@oxygen/shared/dist/capability-discovery.js +9 -0
- package/node_modules/@oxygen/shared/dist/contracts/operations.js +4 -3
- package/node_modules/@oxygen/shared/dist/contracts/table-query-grid.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/contracts/table-query-grid.js +7 -4
- package/node_modules/@oxygen/shared/dist/contracts/table-query-raw.d.ts +14 -0
- package/node_modules/@oxygen/shared/dist/contracts/table-query-raw.js +18 -0
- package/node_modules/@oxygen/shared/dist/contracts/workspace-skills.d.ts +6 -0
- package/node_modules/@oxygen/shared/dist/contracts/workspace-skills.js +62 -0
- package/node_modules/@oxygen/shared/dist/diagnostic-storage-lock.js +5 -1
- package/node_modules/@oxygen/shared/dist/diagnostic-storage.js +178 -63
- package/node_modules/@oxygen/shared/dist/email-health-status.d.ts +11 -0
- package/node_modules/@oxygen/shared/dist/email-health-status.js +16 -0
- package/node_modules/@oxygen/shared/dist/hosted-ai.d.ts +3 -2
- package/node_modules/@oxygen/shared/dist/hosted-ai.js +9 -6
- package/node_modules/@oxygen/shared/dist/langfuse.d.ts +4 -2
- package/node_modules/@oxygen/shared/dist/langfuse.js +123 -66
- package/node_modules/@oxygen/shared/dist/table-run-summary.d.ts +2 -0
- package/node_modules/@oxygen/shared/dist/table-run-summary.js +26 -0
- package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.generated.js +1 -1
- package/node_modules/@oxygen/shared/dist/workspace-skills.d.ts +26 -0
- package/node_modules/@oxygen/shared/dist/workspace-skills.js +102 -0
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/command-manifest.js
CHANGED
|
@@ -306,7 +306,7 @@ export function searchCommandManifest(manifest, query, requestedLimit = DEFAULT_
|
|
|
306
306
|
commands: matches.slice(0, limit).map(({ command, score }) => ({
|
|
307
307
|
name: command.name,
|
|
308
308
|
group: command.group,
|
|
309
|
-
description: command.description,
|
|
309
|
+
description: (command.description.split(/(?<=[.!?])\s/)[0] ?? command.description).slice(0, 180),
|
|
310
310
|
spends_credits: command.spends_credits,
|
|
311
311
|
mutates: command.mutates,
|
|
312
312
|
destructive: command.destructive,
|
package/dist/help.js
CHANGED
|
@@ -126,6 +126,17 @@ const HELP_GROUPS = [
|
|
|
126
126
|
];
|
|
127
127
|
const FALLBACK_HEADING = "Other commands:";
|
|
128
128
|
export function applyOxygenHelp(program, binaryName) {
|
|
129
|
+
program.option("--help-full", "Show expanded command and option descriptions.");
|
|
130
|
+
const configure = (command) => {
|
|
131
|
+
command.configureHelp({
|
|
132
|
+
commandDescription: (cmd) => shortHelpDescription(cmd.description()),
|
|
133
|
+
subcommandDescription: (cmd) => shortHelpDescription(cmd.description()),
|
|
134
|
+
optionDescription: (option) => shortHelpDescription(option.description),
|
|
135
|
+
});
|
|
136
|
+
for (const child of command.commands)
|
|
137
|
+
configure(child);
|
|
138
|
+
};
|
|
139
|
+
configure(program);
|
|
129
140
|
const headingByCommand = new Map();
|
|
130
141
|
for (const group of HELP_GROUPS) {
|
|
131
142
|
for (const name of group.commands)
|
|
@@ -140,6 +151,7 @@ export function applyOxygenHelp(program, binaryName) {
|
|
|
140
151
|
program.addHelpText("before", [
|
|
141
152
|
"",
|
|
142
153
|
"Agent quickstart:",
|
|
154
|
+
" Help is concise; --help-full or commands get <name> --json retains complete grammar.",
|
|
143
155
|
` 1. ${binaryName} login connect this terminal (or set OXYGEN_API_KEY)`,
|
|
144
156
|
` ${binaryName} whoami --summary-json confirm the active workspace and identity (0 credits)`,
|
|
145
157
|
// `login` auto-installs the skills, but an agent authenticating with
|
|
@@ -171,6 +183,12 @@ export function applyOxygenHelp(program, binaryName) {
|
|
|
171
183
|
].join("\n"));
|
|
172
184
|
program.addHelpText("afterAll", `\nExit codes: ${exitCodeLine}`);
|
|
173
185
|
}
|
|
186
|
+
function shortHelpDescription(description) {
|
|
187
|
+
if (process.argv.includes("--help-full"))
|
|
188
|
+
return description;
|
|
189
|
+
const first = description.split(/(?<=[.!?])\s/)[0] ?? description;
|
|
190
|
+
return first.length <= 180 ? first : `${first.slice(0, 177)}…`;
|
|
191
|
+
}
|
|
174
192
|
/**
|
|
175
193
|
* Turn commander's own edit-distance suggester on for EVERY command.
|
|
176
194
|
*
|
|
@@ -443,7 +461,9 @@ function buildUnknownCommandHint(parent, path, token) {
|
|
|
443
461
|
// command.
|
|
444
462
|
const prefixMatches = names.filter((name) => name.startsWith(lower));
|
|
445
463
|
const suggestion = synonym ?? (prefixMatches.length === 1 ? prefixMatches[0] : null);
|
|
446
|
-
const
|
|
464
|
+
const query = path.split(" ").slice(1).concat(token).join(" ");
|
|
465
|
+
const quotedQuery = `'${query.replace(/'/g, "'\\''")}'`;
|
|
466
|
+
const helpLine = `Find the command with \`${path.split(" ")[0]} commands search ${quotedQuery} --json\`; hydrate the selected name with \`${path.split(" ")[0]} commands get <name> --json\`.`;
|
|
447
467
|
return suggestion
|
|
448
468
|
? `Hint: \`${path}\` has no \`${token}\`. Did you mean \`${path} ${suggestion}\`? ${helpLine}`
|
|
449
469
|
: `Hint: \`${path}\` has no \`${token}\`. ${helpLine}`;
|
package/dist/index.js
CHANGED
|
@@ -20,6 +20,7 @@ import { applyOxygenHelp, argvRequestsJson, commanderUsageFailure, commandNameFo
|
|
|
20
20
|
import { homeStandupMarksSeen } from "./home-standup.js";
|
|
21
21
|
import { ensureFreshCliForApiUrl, requestOxygen } from "./http-client.js";
|
|
22
22
|
import { formatInboxNeedsReplyNotice } from "./inbox-needs-reply-notice.js";
|
|
23
|
+
import { serializeCliJson, writeExclusiveJson } from "./json-output.js";
|
|
23
24
|
import { acquireMirrorLock, clearConflictFiles, deletePageFile, emptyMirrorState, findMirrorSlugByPageId, isFileDirty, listConflictFiles, listLocalMirrors, localPageSha256, markMirrorStale, mirrorExists, pageFilePath, planMirrorPush, purgeMirror, resetMirrorForFullResync, quarantineDirtyFile, readMirrorState, releaseMirrorLock, resolveDefaultConfigDir, resolveMirrorDir, writeGeneratedIndexFile, writeGeneratedLogFile, writeMirrorState, writePageFile, } from "./knowledge-mirror.js";
|
|
24
25
|
import { registerKnowledgeRepositoryCommands } from "./knowledge-repository-commands.js";
|
|
25
26
|
import { runLocalCustomHttpColumn } from "./local-custom-http-column.js";
|
|
@@ -42,6 +43,7 @@ import { registerVisualCommands } from "./visual-commands.js";
|
|
|
42
43
|
import { WHATSAPP_ACCOUNT_FLAGS, whatsappAccountQuery } from "./whatsapp-account-flags.js";
|
|
43
44
|
import { formatWorkflowPlanLimitNotices } from "./workflow-plan-limit-notices.js";
|
|
44
45
|
import { WORKFLOW_RUN_LIST_FLAG_OVERLAY, readWorkflowRunListFlags } from "./workflow-run-list-flags.js";
|
|
46
|
+
import { registerWorkspaceSkillCommands } from "./workspace-skill-commands.js";
|
|
45
47
|
import { addProviderLimitFlags, addSuppressionListFlags, providerLimitBody, providerLimitQuery, suppressionListQuery } from "./workspace-utility-flags.js";
|
|
46
48
|
import { isRecipeDefinition } from "@oxygen/recipe-sdk";
|
|
47
49
|
import { AGENCY_DIRECTORY_REGIONS, AGENCY_DIRECTORY_SERVICES, COLLAB_GATE_KINDS, COLLAB_GATE_PROSE, COLLAB_SUBJECT_KINDS, COLLAB_SUBJECT_KINDS_PROSE, COLLAB_SUBJECT_LABELS, COLLAB_SUBJECT_PROSE, GATE_KIND_SUBJECTS, describeWorkflowStatusChange, formatCellForDisplay, formatCopilotPlanDuration, formatCopilotPlanSeconds, formatPublicBudgetScopes, SUBJECT_PATH_FORMS_PROSE, formatSubjectPath, exitCodeForOxygenError, parseSubjectPath, parseSubjectRef, parseWorkflowStatusChange, chunk, isVersionGreater, isVersionLess, KNOWLEDGE_BOOTSTRAP_MAX_CREDITS, MAX_CLI_JSON_BODY_BYTES, MAX_MCP_TOOL_NAME_LENGTH, normalizeCopilotPlanStepStatus, OXYGEN_CAPABILITY_ROUTES, OXYGEN_VERSION, OxygenError, getCapabilityRouteMatch, inferUserCapabilityRoute, parseKnowledgePageMarkdown, PLAN_LIMITS, serializeCapabilityRoute, sleep, success, TAG_KINDS_PROSE, toFailure, workflowMcpToolName, } from "@oxygen/shared";
|
|
@@ -65,6 +67,7 @@ import { SEQUENCE_LIST_PROJECTION_CONTRACT } from "@oxygen/shared/contracts/sequ
|
|
|
65
67
|
import { TABLE_FEED_UPDATE_CONTRACT, tableFeedUpdateIssues } from "@oxygen/shared/contracts/table-feed-update";
|
|
66
68
|
import { TABLE_LIST_QUERY_CONTRACT, tableListQueryParams } from "@oxygen/shared/contracts/table-list";
|
|
67
69
|
import { TABLE_QUERY_GRID_CONTRACT } from "@oxygen/shared/contracts/table-query-grid";
|
|
70
|
+
import { TABLE_QUERY_CLI_DEFAULT_LIMIT, TABLE_QUERY_CLI_DEFAULT_MAX_CHARS, tableQueryRawResultFits, tableQueryRawRecovery } from "@oxygen/shared/contracts/table-query-raw";
|
|
68
71
|
import { WHATSAPP_ACCOUNTS_READ_CONTRACT } from "@oxygen/shared/contracts/whatsapp-reads";
|
|
69
72
|
import { WORKFLOW_RUN_LIST_CONTRACT, workflowRunListQueryParams } from "@oxygen/shared/contracts/workflow-run-list";
|
|
70
73
|
import { COPILOT_ASK_USER_TOOL_NAME, normalizeCopilotQuestions } from "@oxygen/shared/copilot-questions";
|
|
@@ -79,6 +82,7 @@ import { TAG_COLORS } from "@oxygen/shared/select-options";
|
|
|
79
82
|
import { SEQUENCE_LIFECYCLES, sequenceDisplayStatus } from "@oxygen/shared/sequence-lifecycle";
|
|
80
83
|
import { lookupColumnDataType } from "@oxygen/shared/table-field-types";
|
|
81
84
|
import { FILTER_TREE_OPERATORS } from "@oxygen/shared/table-filter-operators";
|
|
85
|
+
import { tableRunSummary } from "@oxygen/shared/table-run-summary";
|
|
82
86
|
import { assertRecipeBundleSafe, assertPortableWorkflowDefinition, assertWorkflowGraphManifest, assertWorkflowManifest, buildRecipeManifest, compileWorkflowDefinition, isAnyWorkflowManifest, isRecipeManifest, isWorkflowDefinition, isWorkflowGraphManifest, isWorkflowManifest, hashPortableWorkflowGraphManifest, WORKFLOW_GRAPH_COMPILER_VERSION, WORKFLOW_GRAPH_MANIFEST_VERSION, } from "@oxygen/workflows";
|
|
83
87
|
import { Command, CommanderError, Option } from "commander";
|
|
84
88
|
import { execFileSync } from "node:child_process";
|
|
@@ -435,7 +439,7 @@ const RECIPE_ESBUILD_NODE_PATHS = [
|
|
|
435
439
|
resolve(CLI_MODULE_DIR, "../../../node_modules"),
|
|
436
440
|
];
|
|
437
441
|
function writeJson(value) {
|
|
438
|
-
process.stdout.write(
|
|
442
|
+
process.stdout.write(serializeCliJson(value, process.argv.includes("--pretty")));
|
|
439
443
|
}
|
|
440
444
|
function linkBar(coverage, width = 36) {
|
|
441
445
|
const filled = Math.max(0, Math.min(width, Math.round(coverage * width)));
|
|
@@ -3668,6 +3672,7 @@ export function createProgram() {
|
|
|
3668
3672
|
.name(binaryName)
|
|
3669
3673
|
.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.")
|
|
3670
3674
|
.version(OXYGEN_VERSION)
|
|
3675
|
+
.option("--pretty", "Indent JSON output (default: compact JSON).")
|
|
3671
3676
|
.option("--profile <name>", "Use a stored CLI profile for this command.")
|
|
3672
3677
|
.option("--org <organization>", "Use an organization id, Clerk org id, or slug for this command.");
|
|
3673
3678
|
program.hook("preAction", (_root, selected) => {
|
|
@@ -7237,10 +7242,11 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
7237
7242
|
}))
|
|
7238
7243
|
.addCommand(addContractFlags(new Command("query"), TABLE_QUERY_GRID_CONTRACT, {
|
|
7239
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." },
|
|
7245
|
+
max_chars: { help: "Maximum compact JSON envelope characters for raw or grid reads (1,000-100,000). Raw defaults to 12,000." },
|
|
7240
7246
|
})
|
|
7241
7247
|
.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.")
|
|
7242
7248
|
.argument("<table>", "Table id or slug.")
|
|
7243
|
-
.option("--limit <n>", "Raw: default
|
|
7249
|
+
.option("--limit <n>", "Raw: default 20, native cap 2000; --full restores default 100. Grid: default 50, cap 200.")
|
|
7244
7250
|
.option("--cursor <cursor>", "Pagination cursor returned by a previous query. Walks forward one page at a time; mutually exclusive with --offset.")
|
|
7245
7251
|
.option("--offset <n>", "Skip this many rows and start there — jump straight to a row without paging to it. Mutually exclusive with --cursor.")
|
|
7246
7252
|
.option("--fields <columns>", "Comma-separated column keys or ids to include.")
|
|
@@ -7250,14 +7256,21 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
7250
7256
|
.option("--formula-values <mode>", "Formula filters are refused by default because displayed formulas are evaluated live. Refresh the formula's stored cells with `columns run <table> <column> --force` (0 credits), then pass 'materialized'; filtering uses that stored snapshot and returns a freshness note. A fill-empty field a bundle adds (definition fill: \"empty\", such as Headcount or Job title) is stored, not live: its cell is the value, so 'materialized' filters it exactly.")
|
|
7251
7257
|
.option("--sort-json <json>", "Ordered sort rules, e.g. '[{\"columnKey\":\"_created_at\",\"direction\":\"desc\"}]'. Earlier rules dominate. Mutually exclusive with --filter-json.")
|
|
7252
7258
|
.option("--no-system-fields", "Omit _row_id, _created_at, and _updated_at from returned rows (included by default).")
|
|
7259
|
+
.option("--full", "Read legacy full rows without the default character limit.")
|
|
7260
|
+
.option("--output <path>", "Write full JSON for this page to a new local file; print a small receipt.")
|
|
7253
7261
|
.option("--json", "Print a JSON envelope.")
|
|
7254
7262
|
.action(async (table, options) => {
|
|
7255
|
-
await handleAsyncAction("tables query", options, () => {
|
|
7263
|
+
await handleAsyncAction("tables query", options, async () => {
|
|
7256
7264
|
const gridArgs = readContractFlags(options, TABLE_QUERY_GRID_CONTRACT);
|
|
7257
|
-
if (gridArgs.format !== "grid" && Object.keys(gridArgs).
|
|
7265
|
+
if (gridArgs.format !== "grid" && Object.keys(gridArgs).some((key) => key !== "max_chars")) {
|
|
7258
7266
|
throw new OxygenError("invalid_request", "Grid options require --format grid.", { exitCode: 1 });
|
|
7259
7267
|
}
|
|
7260
|
-
const
|
|
7268
|
+
const full = options.full || Boolean(options.output);
|
|
7269
|
+
if (full && options.format === "grid")
|
|
7270
|
+
throw new OxygenError("invalid_request", "--full/--output require raw rows; omit --format grid.", { exitCode: 2 });
|
|
7271
|
+
const limit = readPositiveInt(options.limit) ?? (options.format !== "grid" && !full ? TABLE_QUERY_CLI_DEFAULT_LIMIT : undefined);
|
|
7272
|
+
if (options.format !== "grid" && !full && gridArgs.max_chars === undefined)
|
|
7273
|
+
gridArgs.max_chars = TABLE_QUERY_CLI_DEFAULT_MAX_CHARS;
|
|
7261
7274
|
const filters = readFilterJsonOption(options.filterJson);
|
|
7262
7275
|
const filterTree = readJsonObjectOption(options.filterTreeJson);
|
|
7263
7276
|
const formulaValues = readFormulaValuesOption(options.formulaValues);
|
|
@@ -7276,7 +7289,7 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
7276
7289
|
readOption(options.search) || formulaValues || sorts?.length || (offset !== undefined && offset > 0))) {
|
|
7277
7290
|
throw new OxygenError("invalid_request", '--format grid takes only table, --limit, --cursor and optional --include-stats/--include-fill; omit --format for filters, fields, search, sorts or offsets.', { exitCode: 1 });
|
|
7278
7291
|
}
|
|
7279
|
-
|
|
7292
|
+
const data = await requestOxygen("/api/cli/tables/query", {
|
|
7280
7293
|
method: "POST",
|
|
7281
7294
|
body: {
|
|
7282
7295
|
table,
|
|
@@ -7293,6 +7306,17 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
7293
7306
|
...(options.systemFields === false ? { include_system_fields: false } : {}),
|
|
7294
7307
|
},
|
|
7295
7308
|
});
|
|
7309
|
+
// Enforce the window even against an older compatible serving API.
|
|
7310
|
+
if (options.format !== "grid" && !tableQueryRawResultFits(data, typeof gridArgs.max_chars === "number" ? gridArgs.max_chars : undefined)) {
|
|
7311
|
+
const firstRow = Array.isArray(data.rows) ? data.rows[0] : undefined;
|
|
7312
|
+
throw new OxygenError("table_query_result_too_large", "Table rows exceed the requested result window.", {
|
|
7313
|
+
details: { max_chars: gridArgs.max_chars, ...tableQueryRawRecovery(table, typeof firstRow?._row_id === "string" ? firstRow._row_id : undefined) }, exitCode: 2,
|
|
7314
|
+
});
|
|
7315
|
+
}
|
|
7316
|
+
if (!options.output)
|
|
7317
|
+
return data;
|
|
7318
|
+
const output = await writeExclusiveJson(options.output, success("tables query", data), process.argv.includes("--pretty"));
|
|
7319
|
+
return { output, returned_count: data.returned_count ?? data.returnedCount, total_count: data.total_count ?? data.totalCount, hasMore: data.hasMore, nextCursor: data.nextCursor, web_url: data.web_url };
|
|
7296
7320
|
});
|
|
7297
7321
|
}))
|
|
7298
7322
|
.addCommand(new Command("preview")
|
|
@@ -10656,11 +10680,17 @@ Examples:
|
|
|
10656
10680
|
.addCommand(new Command("functions")
|
|
10657
10681
|
.description("List every formula function (name, signature, examples), the operator grammar, and the expression_notes that a signature cannot teach (bare column keys, string escapes, no regex flags, read-time evaluation). The same language powers formula columns and --run-condition gates.")
|
|
10658
10682
|
.option("--category <category>", "Filter by category: logic, string, number, date, url_email, array, json, null_handling, cross_row.")
|
|
10683
|
+
.option("--query <text>", "Find functions by name, signature or description.")
|
|
10659
10684
|
.option("--json", "Print a JSON envelope.")
|
|
10660
10685
|
.action(async (options) => {
|
|
10661
10686
|
await handleAsyncAction("formulas functions", options, () => {
|
|
10662
10687
|
const category = readOption(options.category);
|
|
10663
|
-
const
|
|
10688
|
+
const params = new URLSearchParams();
|
|
10689
|
+
if (category)
|
|
10690
|
+
params.set("category", category);
|
|
10691
|
+
if (readOption(options.query))
|
|
10692
|
+
params.set("query", readOption(options.query));
|
|
10693
|
+
const query = params.size ? `?${params.toString()}` : "";
|
|
10664
10694
|
return requestOxygen(`/api/cli/tables/formulas/functions${query}`, { method: "GET" });
|
|
10665
10695
|
});
|
|
10666
10696
|
}))
|
|
@@ -10739,9 +10769,11 @@ Examples:
|
|
|
10739
10769
|
.addCommand(new Command("get")
|
|
10740
10770
|
.description("Get one durable table action run. For a Function run, selection.rowIds are rows of the Function's execution table, not the calling table; list the calling table's rows it wrote with `tables query <caller> --filter-tree-json` on the Function column's \"action_run_id\" path.")
|
|
10741
10771
|
.argument("<run_id>", "Table action run UUID or parent workspace run UUID.")
|
|
10772
|
+
.option("--full", "Read the full run, including stored inputs and metadata.")
|
|
10742
10773
|
.option("--json", "Print a JSON envelope.")
|
|
10743
10774
|
.action(async (runId, options) => {
|
|
10744
|
-
await handleAsyncAction("table-runs get", options, () => requestOxygen(`/api/cli/table-action-runs/${encodeURIComponent(runId)}`)
|
|
10775
|
+
await handleAsyncAction("table-runs get", options, () => requestOxygen(`/api/cli/table-action-runs/${encodeURIComponent(runId)}?view=${options.full ? "full" : "summary"}`)
|
|
10776
|
+
.then((run) => options.full ? run : tableRunSummary(run)));
|
|
10745
10777
|
}))
|
|
10746
10778
|
.addCommand(new Command("items")
|
|
10747
10779
|
.description("List row items for a durable table action run. For a Function run, list the calling table's rows it wrote with `tables query <caller> --filter-tree-json` on the Function column's \"action_run_id\" path instead of joining items.")
|
|
@@ -10766,6 +10798,7 @@ Examples:
|
|
|
10766
10798
|
.argument("<run_id>", "Table action run UUID or parent workspace run UUID.")
|
|
10767
10799
|
.option("--timeout-seconds <n>", "Maximum time to wait. Defaults to 600.")
|
|
10768
10800
|
.option("--interval-seconds <n>", "Polling interval. Defaults to 5.")
|
|
10801
|
+
.option("--full", "Return the full completed run instead of a progress summary.")
|
|
10769
10802
|
.option("--json", "Print a JSON envelope.")
|
|
10770
10803
|
.action(async (runId, options) => {
|
|
10771
10804
|
await handleAsyncAction("table-runs wait", options, () => waitForTableActionRun(runId, options));
|
|
@@ -16689,10 +16722,11 @@ Examples:
|
|
|
16689
16722
|
.option("--status <status>", "Filter by enrollment status (pending, active, waiting_connection, waiting_signal, replied, completed, stopped, failed, excluded). pending = enrolled, not started (next_due not_started); waiting_connection = invite sent, not accepted yet. Status is the lead's progress, not its invite: use --invite accepted for the leads that accepted.")
|
|
16690
16723
|
.option("--step <n>", "Only leads currently on this step (0-based index, as in the sequence's steps).")
|
|
16691
16724
|
.option("--invite <state>", "Only leads in this LinkedIn invite state: accepted (invited and connected), pending (invited, not accepted yet) or not_invited (no invite: already connected before, suppressed or email-only).")
|
|
16692
|
-
.option("--view <view>", "
|
|
16725
|
+
.option("--view <view>", "compact (default) or full (every enrollment field) (one short row per lead with next_due in words and local time).")
|
|
16693
16726
|
.option("--order <order>", "created (default) or due: soonest next_due first, then waiting leads by gives_up_at, then leads with nothing due.")
|
|
16694
16727
|
.option("--limit <n>", "Maximum enrollments to return (1-500).")
|
|
16695
16728
|
.option("--offset <n>", "Skip this many enrollments; pass next_offset from the previous page to continue.")
|
|
16729
|
+
.option("--full", "Read every enrollment field (same as --view full).")
|
|
16696
16730
|
.option("--json", "Print a JSON envelope.")
|
|
16697
16731
|
.action(async (sequence, options) => {
|
|
16698
16732
|
await handleAsyncAction("sequences enrollments", options, () => {
|
|
@@ -16706,7 +16740,7 @@ Examples:
|
|
|
16706
16740
|
const invite = readOption(options.invite);
|
|
16707
16741
|
if (invite)
|
|
16708
16742
|
params.set("invite", invite);
|
|
16709
|
-
const view = readOption(options.view);
|
|
16743
|
+
const view = options.full ? "full" : readOption(options.view) ?? "compact";
|
|
16710
16744
|
if (view)
|
|
16711
16745
|
params.set("view", view);
|
|
16712
16746
|
const order = readOption(options.order);
|
|
@@ -20456,7 +20490,7 @@ Full trigger schema: oxygen workflows schema --subject trigger --json
|
|
|
20456
20490
|
})));
|
|
20457
20491
|
program
|
|
20458
20492
|
.command("skills")
|
|
20459
|
-
.description("
|
|
20493
|
+
.description("Find and install Oxygen guides, or create and import private workspace skills with skills workspace.")
|
|
20460
20494
|
.addCommand(new Command("list")
|
|
20461
20495
|
.description("List Oxygen agent skills available from the hosted skill index.")
|
|
20462
20496
|
.option("--api-url <url>", "Oxygen app URL. Defaults to OXYGEN_API_URL or https://oxygen-agent.com.")
|
|
@@ -20508,6 +20542,7 @@ Full trigger schema: oxygen workflows schema --subject trigger --json
|
|
|
20508
20542
|
registerConnectorsCommands(program, handleAsyncAction);
|
|
20509
20543
|
registerSettingsCommands(program, handleAsyncAction);
|
|
20510
20544
|
registerCrmSupportFlags(program);
|
|
20545
|
+
registerWorkspaceSkillCommands(program, handleAsyncAction);
|
|
20511
20546
|
registerCustomerOperationCommands(program, handleAsyncAction);
|
|
20512
20547
|
registerCrmFileReadCommands(program, handleAsyncAction);
|
|
20513
20548
|
// After the register* helpers: help groups are assigned to the commands that
|
|
@@ -24314,12 +24349,12 @@ function buildGetSurfaceRunWaitConfig(params) {
|
|
|
24314
24349
|
defaultTimeoutSeconds: params.defaultTimeoutSeconds,
|
|
24315
24350
|
defaultIntervalSeconds: params.defaultIntervalSeconds,
|
|
24316
24351
|
retryTransientNetworkErrors: params.retryTransientNetworkErrors === true,
|
|
24317
|
-
fetchRun: (signal) => requestOxygen(`${params.endpoint}/${encodeURIComponent(params.runId)}`, signal ? { signal } : {}),
|
|
24352
|
+
fetchRun: (signal) => requestOxygen(`${params.endpoint}/${encodeURIComponent(params.runId)}${params.view ? `?view=${params.view}` : ""}`, signal ? { signal } : {}),
|
|
24318
24353
|
isTerminal: params.isTerminal,
|
|
24319
24354
|
shapeTerminal: (run, status, polls, elapsedMs) => {
|
|
24320
24355
|
const webUrl = readRecordString(run, "web_url");
|
|
24321
24356
|
return {
|
|
24322
|
-
[params.runKey]: run,
|
|
24357
|
+
[params.runKey]: params.view === "summary" ? tableRunSummary(run) : run,
|
|
24323
24358
|
[params.runIdKey]: readRecordString(run, "id") ?? params.runId,
|
|
24324
24359
|
status,
|
|
24325
24360
|
terminal: true,
|
|
@@ -24372,6 +24407,7 @@ function waitForTableActionRun(runId, options) {
|
|
|
24372
24407
|
return waitForCliRun(buildGetSurfaceRunWaitConfig({
|
|
24373
24408
|
runId,
|
|
24374
24409
|
endpoint: "/api/cli/table-action-runs",
|
|
24410
|
+
view: options.full ? "full" : "summary",
|
|
24375
24411
|
requestedTimeoutSeconds: options.timeoutSeconds,
|
|
24376
24412
|
requestedIntervalSeconds: options.intervalSeconds,
|
|
24377
24413
|
defaultTimeoutSeconds: TABLE_ACTION_RUN_WAIT_DEFAULT_TIMEOUT_SECONDS,
|
|
@@ -30623,7 +30659,7 @@ beginCliDiagnostics();
|
|
|
30623
30659
|
maybeScheduleBackgroundUpdate();
|
|
30624
30660
|
const program = createProgram();
|
|
30625
30661
|
installCommanderExitOverride(program);
|
|
30626
|
-
const argv = withoutHelpForUnknownCommand(program, process.argv);
|
|
30662
|
+
const argv = withoutHelpForUnknownCommand(program, process.argv.map((arg) => arg === "--help-full" ? "--help" : arg));
|
|
30627
30663
|
setCliDiagnosticOperation(commandNameForArgv(program, argv.slice(2)));
|
|
30628
30664
|
try {
|
|
30629
30665
|
await program.parseAsync(argv);
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare function serializeCliJson(value: unknown, pretty?: boolean): string;
|
|
2
|
+
/** Own only the newly created file; preserve an existing destination on every failure. */
|
|
3
|
+
export declare function writeExclusiveJson(path: string, value: unknown, pretty?: boolean): Promise<string>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { open, unlink } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { OxygenError } from "@oxygen/shared/cli-result";
|
|
4
|
+
export function serializeCliJson(value, pretty = false) {
|
|
5
|
+
return `${JSON.stringify(value, null, pretty ? 2 : undefined)}\n`;
|
|
6
|
+
}
|
|
7
|
+
/** Own only the newly created file; preserve an existing destination on every failure. */
|
|
8
|
+
export async function writeExclusiveJson(path, value, pretty = false) {
|
|
9
|
+
const destination = resolve(path);
|
|
10
|
+
const serialized = serializeCliJson(value, pretty);
|
|
11
|
+
let handle;
|
|
12
|
+
try {
|
|
13
|
+
handle = await open(destination, "wx", 0o600);
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
if (error.code === "EEXIST") {
|
|
17
|
+
throw new OxygenError("output_exists", "Output file already exists. Choose a new path.", { exitCode: 2 });
|
|
18
|
+
}
|
|
19
|
+
throw new OxygenError("output_write_failed", "Unable to create the output file. Check the path and permissions.", { exitCode: 2 });
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
await handle.writeFile(serialized, "utf8");
|
|
23
|
+
await handle.close();
|
|
24
|
+
return destination;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
await handle.close().catch(() => undefined);
|
|
28
|
+
const removed = await unlink(destination).then(() => true, () => false);
|
|
29
|
+
throw new OxygenError("output_write_failed", removed
|
|
30
|
+
? "Unable to write the output file. The partial file was removed."
|
|
31
|
+
: "Unable to write or remove the partial output file. Check the destination before retrying.", { exitCode: 2 });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { type WorkspaceSkillFile } from "@oxygen/shared/workspace-skills";
|
|
3
|
+
/** Never follow symlinks or read binary files out of a local skill folder. */
|
|
4
|
+
export declare function readWorkspaceSkillDirectory(directory: string): Promise<WorkspaceSkillFile[]>;
|
|
5
|
+
type Handle = (command: string, options: {
|
|
6
|
+
json?: boolean;
|
|
7
|
+
}, action: () => Promise<unknown>) => Promise<void>;
|
|
8
|
+
export declare function registerWorkspaceSkillCommands(program: Command, handle: Handle): void;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { lstat, readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { OxygenError } from "@oxygen/shared/cli-result";
|
|
4
|
+
import { authorWorkspaceSkill, parseWorkspaceSkillBundle, WORKSPACE_SKILL_MAX_BYTES, WORKSPACE_SKILL_MAX_FILES, WORKSPACE_SKILL_MAX_FILE_BYTES, } from "@oxygen/shared/workspace-skills";
|
|
5
|
+
import { requestOxygen } from "./http-client.js";
|
|
6
|
+
/** Never follow symlinks or read binary files out of a local skill folder. */
|
|
7
|
+
export async function readWorkspaceSkillDirectory(directory) {
|
|
8
|
+
const root = resolve(directory);
|
|
9
|
+
const files = [];
|
|
10
|
+
let bytes = 0;
|
|
11
|
+
const visit = async (relative, depth) => {
|
|
12
|
+
if (depth > 15)
|
|
13
|
+
throw new OxygenError("invalid_workspace_skill", "Skill directory nesting exceeds 15 levels.", { exitCode: 2 });
|
|
14
|
+
const path = join(root, relative);
|
|
15
|
+
const stat = await lstat(path);
|
|
16
|
+
if (stat.isSymbolicLink())
|
|
17
|
+
throw new OxygenError("invalid_workspace_skill", "Skill imports do not follow symbolic links.", { exitCode: 2 });
|
|
18
|
+
if (stat.isDirectory()) {
|
|
19
|
+
for (const child of (await readdir(path)).sort()) {
|
|
20
|
+
if ([".git", "node_modules", ".DS_Store"].includes(child))
|
|
21
|
+
continue;
|
|
22
|
+
await visit(relative ? `${relative}/${child}` : child, depth + 1);
|
|
23
|
+
}
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (!stat.isFile() ||
|
|
27
|
+
stat.size > WORKSPACE_SKILL_MAX_FILE_BYTES ||
|
|
28
|
+
files.length >= WORKSPACE_SKILL_MAX_FILES ||
|
|
29
|
+
bytes + stat.size > WORKSPACE_SKILL_MAX_BYTES) {
|
|
30
|
+
throw new OxygenError("invalid_workspace_skill", "Import supports at most 50 text files, 120 KB per file and 1 MB total.", { exitCode: 2 });
|
|
31
|
+
}
|
|
32
|
+
const buffer = await readFile(path);
|
|
33
|
+
bytes += buffer.byteLength;
|
|
34
|
+
let content;
|
|
35
|
+
try {
|
|
36
|
+
content = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(buffer);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
throw new OxygenError("invalid_workspace_skill", `File ${relative} is not UTF-8 text.`, { exitCode: 2 });
|
|
40
|
+
}
|
|
41
|
+
files.push({ path: relative, content });
|
|
42
|
+
};
|
|
43
|
+
await visit("", 0);
|
|
44
|
+
return files;
|
|
45
|
+
}
|
|
46
|
+
export function registerWorkspaceSkillCommands(program, handle) {
|
|
47
|
+
const skills = program.commands.find((command) => command.name() === "skills");
|
|
48
|
+
const workspace = skills
|
|
49
|
+
.command("workspace")
|
|
50
|
+
.description("Create, upload, find and edit private workspace skills in Knowledge.");
|
|
51
|
+
workspace
|
|
52
|
+
.command("create")
|
|
53
|
+
.description("Write a private workspace skill. Stores instructions; 0 credits.")
|
|
54
|
+
.requiredOption("--name <name>", "Lowercase-hyphen skill name.")
|
|
55
|
+
.requiredOption("--description <text>", "When to use this skill.")
|
|
56
|
+
.requiredOption("--instructions <text>", "Instructions to save.")
|
|
57
|
+
.option("--json", "Print a JSON envelope.")
|
|
58
|
+
.action((options, command) => handle("skills workspace create", { json: command.optsWithGlobals().json === true }, async () => {
|
|
59
|
+
authorWorkspaceSkill(options);
|
|
60
|
+
return requestOxygen("/api/cli/skills/workspace/create", {
|
|
61
|
+
method: "POST",
|
|
62
|
+
body: {
|
|
63
|
+
name: options.name,
|
|
64
|
+
description: options.description,
|
|
65
|
+
instructions: options.instructions,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
}));
|
|
69
|
+
workspace
|
|
70
|
+
.command("import")
|
|
71
|
+
.description("Upload SKILL.md or a skill folder from any source into private Knowledge. 0 credits.")
|
|
72
|
+
.option("--file <path>", "Local SKILL.md file.")
|
|
73
|
+
.option("--directory <path>", "Local skill directory including SKILL.md and supporting text files.")
|
|
74
|
+
.option("--url <url>", "Import a public HTTPS SKILL.md URL. Supporting files require --directory or --files-json.")
|
|
75
|
+
.option("--files-json <json>", "Inline array of {path, content}; alternative to --file or --directory.")
|
|
76
|
+
.option("--name <name>", "Destination name; otherwise use SKILL.md frontmatter name.")
|
|
77
|
+
.option("--source-url <url>", "HTTPS source provenance; content must already be downloaded.")
|
|
78
|
+
.option("--json", "Print a JSON envelope.")
|
|
79
|
+
.action((options, command) => handle("skills workspace import", { json: command.optsWithGlobals().json === true }, async () => {
|
|
80
|
+
if ([
|
|
81
|
+
options.file,
|
|
82
|
+
options.directory,
|
|
83
|
+
options.url,
|
|
84
|
+
options.filesJson,
|
|
85
|
+
].filter((value) => value !== undefined).length !== 1)
|
|
86
|
+
throw new OxygenError("invalid_workspace_skill", "Choose exactly one of --file, --directory, --url or --files-json.", { exitCode: 2 });
|
|
87
|
+
if (options.url !== undefined) {
|
|
88
|
+
if (options.sourceUrl !== undefined)
|
|
89
|
+
throw new OxygenError("invalid_workspace_skill", "--source-url is for uploaded files. A URL import records its own source.", { exitCode: 2 });
|
|
90
|
+
return requestOxygen("/api/cli/skills/workspace/import", {
|
|
91
|
+
method: "POST",
|
|
92
|
+
body: {
|
|
93
|
+
url: options.url,
|
|
94
|
+
...(options.name ? { name: options.name } : {}),
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
let files;
|
|
99
|
+
if (options.directory)
|
|
100
|
+
files = await readWorkspaceSkillDirectory(options.directory);
|
|
101
|
+
else if (options.file) {
|
|
102
|
+
const stat = await lstat(options.file);
|
|
103
|
+
if (!stat.isFile() || stat.size > WORKSPACE_SKILL_MAX_FILE_BYTES)
|
|
104
|
+
throw new OxygenError("invalid_workspace_skill", "Provide a regular SKILL.md text file of at most 120 KB.", { exitCode: 2 });
|
|
105
|
+
files = [
|
|
106
|
+
{
|
|
107
|
+
path: "SKILL.md",
|
|
108
|
+
content: new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(await readFile(options.file)),
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
try {
|
|
114
|
+
files = JSON.parse(options.filesJson);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
throw new OxygenError("invalid_workspace_skill", "--files-json must be a JSON array of {path, content}.", { exitCode: 2 });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const bundle = parseWorkspaceSkillBundle({
|
|
121
|
+
files,
|
|
122
|
+
...(options.name ? { name: options.name } : {}),
|
|
123
|
+
...(options.sourceUrl ? { source_url: options.sourceUrl } : {}),
|
|
124
|
+
});
|
|
125
|
+
return requestOxygen("/api/cli/skills/workspace/import", {
|
|
126
|
+
method: "POST",
|
|
127
|
+
body: {
|
|
128
|
+
name: bundle.name,
|
|
129
|
+
files: bundle.files,
|
|
130
|
+
...(bundle.source_url ? { source_url: bundle.source_url } : {}),
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
}));
|
|
134
|
+
}
|
|
@@ -1877,7 +1877,12 @@ function isSubscriptionRetentionIntent(query) {
|
|
|
1877
1877
|
return /\b(retention offer|win[ -]?back discount|cancellation (?:reason|feedback)|written cancellation)\b/.test(query)
|
|
1878
1878
|
|| /\b(stay|cancel|cancelling)\b/.test(query) && /\b(discount|next month|subscription reason)\b/.test(query);
|
|
1879
1879
|
}
|
|
1880
|
+
function isWorkspaceSkillIntent(query) {
|
|
1881
|
+
return /\bskills?\b/.test(query) && /\b(create|author|write|upload|import|edit|update|archive|private|workspace|custom|own|reusable)\b/.test(query);
|
|
1882
|
+
}
|
|
1880
1883
|
function explicitCapabilityIntent(query) {
|
|
1884
|
+
if (isWorkspaceSkillIntent(query))
|
|
1885
|
+
return ROUTE_BY_ID.get("knowledge-graph") ?? null;
|
|
1881
1886
|
// Importing an AI answer is GEO evidence, rather than a Table file import.
|
|
1882
1887
|
if (/\b(?:geo|aeo|ai (?:(?:search|answer) )?visibility|consumer ai answers?|answer engine optimization|generative engine optimization)\b/.test(query))
|
|
1883
1888
|
return ROUTE_BY_ID.get("geo") ?? null;
|
|
@@ -2207,6 +2212,10 @@ function isPublishingPostIntent(query) {
|
|
|
2207
2212
|
&& !(/\b(find|search|scrape|research)\b/.test(query) && /\b(about|mention|containing)\b/.test(query));
|
|
2208
2213
|
}
|
|
2209
2214
|
function recommendationsFor(card, query) {
|
|
2215
|
+
if (card.id === "knowledge-graph" && isWorkspaceSkillIntent(query)) {
|
|
2216
|
+
return { tools: ["oxygen_workspace_skills_list", "oxygen_workspace_skills_create", "oxygen_workspace_skills_import", "oxygen_workspace_skills_get", "oxygen_workspace_skills_update", "oxygen_workspace_skills_archive"],
|
|
2217
|
+
commands: ["skills workspace list", "skills workspace create", "skills workspace import", "skills workspace get", "skills workspace update", "skills workspace archive"] };
|
|
2218
|
+
}
|
|
2210
2219
|
const contactFind = card.id === "sourcing-and-provider-tools" ? oneShotContactFindingRecommendation(query) : null;
|
|
2211
2220
|
if (contactFind)
|
|
2212
2221
|
return contactFind;
|
|
@@ -13,6 +13,7 @@ import { TABLE_FEED_UPDATE_OPERATION } from "./table-feed-update.js";
|
|
|
13
13
|
import { GEO_OPERATIONS } from "./geo.js";
|
|
14
14
|
import { CUSTOMER_OPERATIONS } from "./customer-operations.js";
|
|
15
15
|
import { AGENT_SESSION_OPERATIONS } from "./agent-sessions.js";
|
|
16
|
+
import { WORKSPACE_SKILL_OPERATIONS } from "./workspace-skills.js";
|
|
16
17
|
/** The route with its `{param}` segments filled in, each URL-encoded. */
|
|
17
18
|
export function operationPath(spec, params) {
|
|
18
19
|
return spec.route.replace(/\{(\w+)\}/g, (_match, name) => {
|
|
@@ -82,6 +83,6 @@ export function operationFormData(spec, body) {
|
|
|
82
83
|
}
|
|
83
84
|
return form;
|
|
84
85
|
}
|
|
85
|
-
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];
|
|
86
|
-
export const CLI_OPERATIONS = [...CUSTOMER_OPERATIONS, ...GEO_OPERATIONS, ...BROWSER_OPERATIONS, ...AGENT_SESSION_OPERATIONS].filter((spec) => spec.generated?.cli !== false);
|
|
87
|
-
export const MCP_OPERATIONS = [...CUSTOMER_OPERATIONS, ...GEO_OPERATIONS, ...BROWSER_OPERATIONS, ...AGENT_SESSION_OPERATIONS].filter((spec) => spec.generated?.mcp !== false);
|
|
86
|
+
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, ...WORKSPACE_SKILL_OPERATIONS];
|
|
87
|
+
export const CLI_OPERATIONS = [...CUSTOMER_OPERATIONS, ...GEO_OPERATIONS, ...BROWSER_OPERATIONS, ...AGENT_SESSION_OPERATIONS, ...WORKSPACE_SKILL_OPERATIONS].filter((spec) => spec.generated?.cli !== false);
|
|
88
|
+
export const MCP_OPERATIONS = [...CUSTOMER_OPERATIONS, ...GEO_OPERATIONS, ...BROWSER_OPERATIONS, ...AGENT_SESSION_OPERATIONS, ...WORKSPACE_SKILL_OPERATIONS].filter((spec) => spec.generated?.mcp !== false);
|
|
@@ -17,7 +17,7 @@ export declare const TABLE_QUERY_GRID_CONTRACT: import("./schema.js").ContractOb
|
|
|
17
17
|
}, never>;
|
|
18
18
|
export declare const TABLE_QUERY_GRID_NATIVE_WINDOW = 10000;
|
|
19
19
|
export declare const TABLE_QUERY_GRID_BUILTIN_WINDOW = 4000;
|
|
20
|
-
export declare const TABLE_QUERY_GRID_COPILOT_WINDOW =
|
|
20
|
+
export declare const TABLE_QUERY_GRID_COPILOT_WINDOW = 50000;
|
|
21
21
|
/** The window a built-in host reads tables in; other clients have none. */
|
|
22
22
|
export declare function tableQueryBuiltinWindow(surface: string | null | undefined): number | undefined;
|
|
23
23
|
/** Measure the complete native receipt and the escaped built-in host receipt. */
|
|
@@ -12,10 +12,13 @@ export const TABLE_QUERY_GRID_NATIVE_WINDOW = 10_000;
|
|
|
12
12
|
// Deliberately below Strands' shipped 6000 default; counts the complete host
|
|
13
13
|
// object rather than only its text. This changes no adapter's outer ceiling.
|
|
14
14
|
export const TABLE_QUERY_GRID_BUILTIN_WINDOW = 4_000;
|
|
15
|
-
// The Copilot hands its model the structured result alone,
|
|
16
|
-
// characters
|
|
17
|
-
//
|
|
18
|
-
|
|
15
|
+
// The Copilot hands its model the structured result alone, and stores one over
|
|
16
|
+
// 12,000 characters for its session so the model reads only the part it needs
|
|
17
|
+
// (ADR 0013, amendments 2026-10-10 and 2026-10-11). Its window is therefore the
|
|
18
|
+
// largest page the MCP layer returns untrimmed (MAX_STRUCTURED_CONTENT_CHARS in
|
|
19
|
+
// @oxygen/mcp-server), not what fits in a conversation. One qualified row
|
|
20
|
+
// measured 4,537 on dev, so the Agent window refuses even a single row.
|
|
21
|
+
export const TABLE_QUERY_GRID_COPILOT_WINDOW = 50_000;
|
|
19
22
|
/** The window a built-in host reads tables in; other clients have none. */
|
|
20
23
|
export function tableQueryBuiltinWindow(surface) {
|
|
21
24
|
if (surface === "copilot")
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Opt-in at the API boundary; the CLI supplies these defaults. */
|
|
2
|
+
export declare const TABLE_QUERY_RAW_CONTRACT: import("./schema.js").ContractObject<{
|
|
3
|
+
readonly max_chars: {
|
|
4
|
+
type: "integer";
|
|
5
|
+
description?: string;
|
|
6
|
+
minimum?: number;
|
|
7
|
+
maximum?: number;
|
|
8
|
+
"x-clamp"?: true;
|
|
9
|
+
};
|
|
10
|
+
}, never>;
|
|
11
|
+
export declare const TABLE_QUERY_CLI_DEFAULT_LIMIT = 20;
|
|
12
|
+
export declare const TABLE_QUERY_CLI_DEFAULT_MAX_CHARS = 12000;
|
|
13
|
+
export declare function tableQueryRawResultFits(data: Record<string, unknown>, maxChars: number | undefined): boolean;
|
|
14
|
+
export declare function tableQueryRawRecovery(table: string, row?: string): Record<string, unknown>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { success } from "../cli-result.js";
|
|
2
|
+
import { int, obj } from "./schema.js";
|
|
3
|
+
/** Opt-in at the API boundary; the CLI supplies these defaults. */
|
|
4
|
+
export const TABLE_QUERY_RAW_CONTRACT = obj({
|
|
5
|
+
max_chars: int({ minimum: 1_000, maximum: 100_000, description: "Result envelope chars." }),
|
|
6
|
+
});
|
|
7
|
+
export const TABLE_QUERY_CLI_DEFAULT_LIMIT = 20;
|
|
8
|
+
export const TABLE_QUERY_CLI_DEFAULT_MAX_CHARS = 12_000;
|
|
9
|
+
export function tableQueryRawResultFits(data, maxChars) {
|
|
10
|
+
return maxChars === undefined || JSON.stringify(success("tables query", data)).length <= maxChars;
|
|
11
|
+
}
|
|
12
|
+
export function tableQueryRawRecovery(table, row) {
|
|
13
|
+
return {
|
|
14
|
+
table,
|
|
15
|
+
...(row ? { row } : {}),
|
|
16
|
+
next_step: "Reduce fields/limit. Inspect a large cell with `tables rows inspect --table <table> --row <row> --field <field> --projection value --path /row/<field>/<key>`; select a JSON Pointer and continue with value_cursor. MCP: hydrate oxygen_table_row_inspect with oxygen_capabilities_schema, then dispatch it through oxygen_capabilities_read. CLI: --full restores legacy reads; --output <path> keeps full JSON outside context.",
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { OperationSpec } from "./operations.js";
|
|
2
|
+
export declare const WORKSPACE_SKILL_FILE_CONTRACT: import("./schema.js").ContractObject<{
|
|
3
|
+
readonly path: import("./schema.js").StringProperty;
|
|
4
|
+
readonly content: import("./schema.js").StringProperty;
|
|
5
|
+
}, "content" | "path">;
|
|
6
|
+
export declare const WORKSPACE_SKILL_OPERATIONS: readonly OperationSpec[];
|