@oxygen-agent/cli 1.365.3 → 1.575.19
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/column-run-notices.d.ts +11 -0
- package/dist/column-run-notices.js +37 -0
- package/dist/command-manifest.js +13 -8
- package/dist/help.js +78 -16
- package/dist/index.js +3873 -514
- package/dist/skills.js +106 -1
- package/node_modules/@oxygen/formula/dist/coerce.d.ts +8 -0
- package/node_modules/@oxygen/formula/dist/coerce.js +10 -0
- package/node_modules/@oxygen/formula/dist/evaluate.d.ts +31 -0
- package/node_modules/@oxygen/formula/dist/evaluate.js +248 -0
- package/node_modules/@oxygen/formula/dist/expression.d.ts +64 -0
- package/node_modules/@oxygen/formula/dist/expression.js +428 -0
- package/node_modules/@oxygen/formula/dist/formula-functions.d.ts +71 -0
- package/node_modules/@oxygen/formula/dist/formula-functions.js +1100 -0
- package/node_modules/@oxygen/formula/dist/index.d.ts +17 -0
- package/node_modules/@oxygen/formula/dist/index.js +17 -0
- package/node_modules/@oxygen/formula/dist/value-normalizers.d.ts +30 -0
- package/node_modules/@oxygen/formula/dist/value-normalizers.js +80 -0
- package/node_modules/@oxygen/formula/package.json +26 -0
- package/node_modules/@oxygen/recipe-sdk/dist/index.d.ts +30 -0
- package/node_modules/@oxygen/recipe-sdk/dist/index.js +2 -2
- package/node_modules/@oxygen/shared/dist/billing-anchors.d.ts +60 -0
- package/node_modules/@oxygen/shared/dist/billing-anchors.js +135 -0
- package/node_modules/@oxygen/shared/dist/billing.d.ts +101 -5
- package/node_modules/@oxygen/shared/dist/billing.js +192 -8
- package/node_modules/@oxygen/shared/dist/call-outcomes.d.ts +59 -0
- package/node_modules/@oxygen/shared/dist/call-outcomes.js +73 -0
- package/node_modules/@oxygen/shared/dist/cli-result.js +1 -0
- package/node_modules/@oxygen/shared/dist/credit-guidance.js +3 -1
- package/node_modules/@oxygen/shared/dist/crm-reply-events.d.ts +35 -0
- package/node_modules/@oxygen/shared/dist/crm-reply-events.js +31 -0
- package/node_modules/@oxygen/shared/dist/dial-guardrail-overrides.d.ts +50 -0
- package/node_modules/@oxygen/shared/dist/dial-guardrail-overrides.js +65 -0
- package/node_modules/@oxygen/shared/dist/directory.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/directory.js +1 -0
- package/node_modules/@oxygen/shared/dist/hosted-ai.d.ts +17 -1
- package/node_modules/@oxygen/shared/dist/hosted-ai.js +52 -3
- package/node_modules/@oxygen/shared/dist/index.d.ts +11 -0
- package/node_modules/@oxygen/shared/dist/index.js +15 -0
- package/node_modules/@oxygen/shared/dist/langfuse.d.ts +77 -0
- package/node_modules/@oxygen/shared/dist/langfuse.js +231 -0
- package/node_modules/@oxygen/shared/dist/linkedin-quota-denial.d.ts +31 -0
- package/node_modules/@oxygen/shared/dist/linkedin-quota-denial.js +56 -0
- package/node_modules/@oxygen/shared/dist/linkedin-sequences.d.ts +5 -4
- package/node_modules/@oxygen/shared/dist/linkedin-sequences.js +5 -4
- package/node_modules/@oxygen/shared/dist/linkedin-url.d.ts +22 -0
- package/node_modules/@oxygen/shared/dist/linkedin-url.js +7 -4
- package/node_modules/@oxygen/shared/dist/log.js +56 -4
- package/node_modules/@oxygen/shared/dist/microsoft-consent-url.d.ts +7 -0
- package/node_modules/@oxygen/shared/dist/microsoft-consent-url.js +29 -0
- package/node_modules/@oxygen/shared/dist/object-storage.d.ts +31 -0
- package/node_modules/@oxygen/shared/dist/object-storage.js +61 -0
- package/node_modules/@oxygen/shared/dist/plan-limits.d.ts +636 -0
- package/node_modules/@oxygen/shared/dist/plan-limits.js +199 -0
- package/node_modules/@oxygen/shared/dist/pricing-sheet.d.ts +89 -23
- package/node_modules/@oxygen/shared/dist/pricing-sheet.js +88 -24
- package/node_modules/@oxygen/shared/dist/sequence-crm-events.d.ts +291 -0
- package/node_modules/@oxygen/shared/dist/sequence-crm-events.js +224 -0
- package/node_modules/@oxygen/shared/dist/sequence-template.d.ts +42 -1
- package/node_modules/@oxygen/shared/dist/sequence-template.js +0 -0
- package/node_modules/@oxygen/shared/dist/sequences.d.ts +287 -24
- package/node_modules/@oxygen/shared/dist/sequences.js +940 -60
- package/node_modules/@oxygen/shared/dist/spend-safety.d.ts +70 -0
- package/node_modules/@oxygen/shared/dist/spend-safety.js +106 -0
- package/node_modules/@oxygen/shared/dist/tags.d.ts +90 -1
- package/node_modules/@oxygen/shared/dist/tags.js +126 -6
- package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.js +1 -1
- package/node_modules/@oxygen/shared/dist/workflow-trigger-metadata.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/workflow-trigger-metadata.js +4 -0
- package/node_modules/@oxygen/shared/dist/workspace-agents.d.ts +8 -7
- package/node_modules/@oxygen/shared/dist/workspace-agents.js +34 -7
- package/node_modules/@oxygen/shared/package.json +95 -0
- package/node_modules/@oxygen/workflows/dist/event-dispatch.d.ts +126 -0
- package/node_modules/@oxygen/workflows/dist/event-dispatch.js +173 -0
- package/node_modules/@oxygen/workflows/dist/graph/expression.d.ts +78 -0
- package/node_modules/@oxygen/workflows/dist/graph/expression.js +700 -0
- package/node_modules/@oxygen/workflows/dist/graph/index.d.ts +20 -0
- package/node_modules/@oxygen/workflows/dist/graph/index.js +20 -0
- package/node_modules/@oxygen/workflows/dist/graph/lint.d.ts +4 -0
- package/node_modules/@oxygen/workflows/dist/graph/lint.js +812 -0
- package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.d.ts +501 -0
- package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.js +200 -0
- package/node_modules/@oxygen/workflows/dist/graph/params.d.ts +86 -0
- package/node_modules/@oxygen/workflows/dist/graph/params.js +173 -0
- package/node_modules/@oxygen/workflows/dist/graph/remap.d.ts +48 -0
- package/node_modules/@oxygen/workflows/dist/graph/remap.js +213 -0
- package/node_modules/@oxygen/workflows/dist/graph/topology.d.ts +46 -0
- package/node_modules/@oxygen/workflows/dist/graph/topology.js +280 -0
- package/node_modules/@oxygen/workflows/dist/graph/types.d.ts +270 -0
- package/node_modules/@oxygen/workflows/dist/graph/types.js +93 -0
- package/node_modules/@oxygen/workflows/dist/index.d.ts +113 -1
- package/node_modules/@oxygen/workflows/dist/index.js +179 -13
- package/node_modules/@oxygen/workflows/dist/tool-effects.d.ts +1 -0
- package/node_modules/@oxygen/workflows/dist/tool-effects.js +19 -0
- package/node_modules/@oxygen/workflows/dist/usage-estimate.js +135 -4
- package/node_modules/@oxygen/workflows/package.json +4 -0
- package/package.json +10 -5
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { PlanTier } from "./billing.js";
|
|
2
|
+
import type { LimitsTier } from "./plan-limits.js";
|
|
3
|
+
/**
|
|
4
|
+
* Spend-safety defaults: plan-scaled ceilings applied when an autonomous
|
|
5
|
+
* spend path was armed WITHOUT an explicit cap. They restore the ADR-0011
|
|
6
|
+
* doctrine that every cron/webhook/event trigger persists a per-delivery
|
|
7
|
+
* ceiling — absence of a cap is no longer unbounded. Explicit user caps always
|
|
8
|
+
* win and are never clamped down; `null` means no default applies (enterprise
|
|
9
|
+
* runs on custom limits; free has no BYOK).
|
|
10
|
+
*
|
|
11
|
+
* Values anchor to ≥ one day-equivalent of the plan's monthly credits
|
|
12
|
+
* (monthlyCredits / 30), orders of magnitude above typical per-delivery spend,
|
|
13
|
+
* so the ceiling binds on runaway loops, not legitimate heavy runs.
|
|
14
|
+
*/
|
|
15
|
+
/** Where an effective cap came from: caller-declared or a plan-tier default. */
|
|
16
|
+
export type EffectiveCapSource = "explicit" | "trigger_default";
|
|
17
|
+
/** Workflow run trigger types that fire without a human in the loop. */
|
|
18
|
+
export declare const AUTONOMOUS_WORKFLOW_TRIGGER_TYPES: ReadonlySet<string>;
|
|
19
|
+
/**
|
|
20
|
+
* Per-run managed-credit ceiling for a LIVE workflow run fired by an autonomous
|
|
21
|
+
* trigger when neither run metadata nor the manifest declares `max_credits`.
|
|
22
|
+
*/
|
|
23
|
+
export declare const DEFAULT_TRIGGER_RUN_CREDIT_CEILING: Record<PlanTier, number | null>;
|
|
24
|
+
/**
|
|
25
|
+
* Per-delivery credit ceiling stamped onto a standing/webhook table auto-run
|
|
26
|
+
* batch when the armed configuration carries no explicit `max_credits`.
|
|
27
|
+
*/
|
|
28
|
+
export declare const DEFAULT_AUTO_RUN_BATCH_CREDIT_CEILING: Record<PlanTier, number | null>;
|
|
29
|
+
/**
|
|
30
|
+
* Row ceiling for a BYOK AI-column run whose caller gave no explicit row bound
|
|
31
|
+
* (no limit, no row_ids, selection "all"). BYOK bills the customer's own
|
|
32
|
+
* provider account, so a credit ceiling is meaningless — row count is the
|
|
33
|
+
* enforceable unit. An explicit limit (up to the 500k platform row cap) wins.
|
|
34
|
+
*/
|
|
35
|
+
export declare const DEFAULT_BYOK_COLUMN_RUN_MAX_ROWS: Record<PlanTier, number | null>;
|
|
36
|
+
/**
|
|
37
|
+
* Default per-provider DAILY call cap for BYOK provider traffic (the customer's
|
|
38
|
+
* own key). Short-window pacing defaults already exist in provider-fetch; this
|
|
39
|
+
* bounds daily volume. Warn-first: enforcement flips only after telemetry and
|
|
40
|
+
* with the self-serve override surface live. A workspace
|
|
41
|
+
* provider_rate_limit_policies row with windowSeconds=86400 overrides it.
|
|
42
|
+
*/
|
|
43
|
+
export declare const DEFAULT_BYOK_PROVIDER_DAILY_CALL_CAP: Record<PlanTier, number | null>;
|
|
44
|
+
export declare const BYOK_PROVIDER_DAILY_WINDOW_SECONDS: number;
|
|
45
|
+
/**
|
|
46
|
+
* Implicit org-level DAILY budget guard, evaluated only when the org has no
|
|
47
|
+
* explicit org-scope daily budget policy and the plan has finite positive
|
|
48
|
+
* monthly credits. Warn when one rolling day spends past WARN_PCT of the
|
|
49
|
+
* monthly grant; hard-block at BLOCK_PCT (a loop burning a whole month in a
|
|
50
|
+
* day is near-certainly runaway — this also protects large top-up balances).
|
|
51
|
+
* Setting any explicit org-daily policy fully suppresses the implicit pair.
|
|
52
|
+
*/
|
|
53
|
+
export declare const DEFAULT_ORG_DAILY_SPEND_WARN_PCT = 0.25;
|
|
54
|
+
export declare const DEFAULT_ORG_DAILY_SPEND_BLOCK_PCT = 1;
|
|
55
|
+
export declare function resolveDefaultTriggerRunCreditCeiling(tier: PlanTier): number | null;
|
|
56
|
+
export declare function resolveDefaultAutoRunBatchCreditCeiling(tier: PlanTier): number | null;
|
|
57
|
+
export declare function resolveDefaultByokColumnRunMaxRows(tier: PlanTier): number | null;
|
|
58
|
+
export declare function resolveDefaultByokProviderDailyCallCap(tier: PlanTier): number | null;
|
|
59
|
+
/**
|
|
60
|
+
* The plan tiers the spend-safety default tables above are keyed by. `limitsTier`
|
|
61
|
+
* collapses "enterprise" onto the "scale" enforcement rung, but these tables carry
|
|
62
|
+
* a distinct "enterprise" column (null = runs on custom limits) — so prefer the
|
|
63
|
+
* raw plan_tier whenever it is itself a table key, and fall back to the limits rung
|
|
64
|
+
* (always a valid PlanTier key) when plan_tier is absent or an unknown/legacy
|
|
65
|
+
* string (e.g. a Stripe price nickname). Single source of truth for the
|
|
66
|
+
* `oxygen limits` report, the org-facing provider-limits API, and the BYOK
|
|
67
|
+
* daily-cap enforcement resolver — they must agree on which column an org reads.
|
|
68
|
+
*/
|
|
69
|
+
export declare const SPEND_SAFETY_PLAN_TIERS: readonly PlanTier[];
|
|
70
|
+
export declare function resolveSpendSafetyPlanTier(planTier: string | null, limitsTier: LimitsTier): PlanTier;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/** Workflow run trigger types that fire without a human in the loop. */
|
|
2
|
+
export const AUTONOMOUS_WORKFLOW_TRIGGER_TYPES = new Set([
|
|
3
|
+
"cron",
|
|
4
|
+
"webhook",
|
|
5
|
+
"event",
|
|
6
|
+
]);
|
|
7
|
+
/**
|
|
8
|
+
* Per-run managed-credit ceiling for a LIVE workflow run fired by an autonomous
|
|
9
|
+
* trigger when neither run metadata nor the manifest declares `max_credits`.
|
|
10
|
+
*/
|
|
11
|
+
export const DEFAULT_TRIGGER_RUN_CREDIT_CEILING = {
|
|
12
|
+
free: 500,
|
|
13
|
+
starter: 5_000,
|
|
14
|
+
pro: 10_000,
|
|
15
|
+
team: 25_000,
|
|
16
|
+
scale: 250_000,
|
|
17
|
+
enterprise: null,
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Per-delivery credit ceiling stamped onto a standing/webhook table auto-run
|
|
21
|
+
* batch when the armed configuration carries no explicit `max_credits`.
|
|
22
|
+
*/
|
|
23
|
+
export const DEFAULT_AUTO_RUN_BATCH_CREDIT_CEILING = {
|
|
24
|
+
free: 500,
|
|
25
|
+
starter: 5_000,
|
|
26
|
+
pro: 10_000,
|
|
27
|
+
team: 25_000,
|
|
28
|
+
scale: 250_000,
|
|
29
|
+
enterprise: null,
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Row ceiling for a BYOK AI-column run whose caller gave no explicit row bound
|
|
33
|
+
* (no limit, no row_ids, selection "all"). BYOK bills the customer's own
|
|
34
|
+
* provider account, so a credit ceiling is meaningless — row count is the
|
|
35
|
+
* enforceable unit. An explicit limit (up to the 500k platform row cap) wins.
|
|
36
|
+
*/
|
|
37
|
+
export const DEFAULT_BYOK_COLUMN_RUN_MAX_ROWS = {
|
|
38
|
+
free: null,
|
|
39
|
+
starter: 25_000,
|
|
40
|
+
pro: 50_000,
|
|
41
|
+
team: 100_000,
|
|
42
|
+
scale: 250_000,
|
|
43
|
+
enterprise: null,
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Default per-provider DAILY call cap for BYOK provider traffic (the customer's
|
|
47
|
+
* own key). Short-window pacing defaults already exist in provider-fetch; this
|
|
48
|
+
* bounds daily volume. Warn-first: enforcement flips only after telemetry and
|
|
49
|
+
* with the self-serve override surface live. A workspace
|
|
50
|
+
* provider_rate_limit_policies row with windowSeconds=86400 overrides it.
|
|
51
|
+
*/
|
|
52
|
+
export const DEFAULT_BYOK_PROVIDER_DAILY_CALL_CAP = {
|
|
53
|
+
free: null,
|
|
54
|
+
starter: 10_000,
|
|
55
|
+
pro: 25_000,
|
|
56
|
+
team: 50_000,
|
|
57
|
+
scale: 200_000,
|
|
58
|
+
enterprise: null,
|
|
59
|
+
};
|
|
60
|
+
export const BYOK_PROVIDER_DAILY_WINDOW_SECONDS = 24 * 60 * 60;
|
|
61
|
+
/**
|
|
62
|
+
* Implicit org-level DAILY budget guard, evaluated only when the org has no
|
|
63
|
+
* explicit org-scope daily budget policy and the plan has finite positive
|
|
64
|
+
* monthly credits. Warn when one rolling day spends past WARN_PCT of the
|
|
65
|
+
* monthly grant; hard-block at BLOCK_PCT (a loop burning a whole month in a
|
|
66
|
+
* day is near-certainly runaway — this also protects large top-up balances).
|
|
67
|
+
* Setting any explicit org-daily policy fully suppresses the implicit pair.
|
|
68
|
+
*/
|
|
69
|
+
export const DEFAULT_ORG_DAILY_SPEND_WARN_PCT = 0.25;
|
|
70
|
+
export const DEFAULT_ORG_DAILY_SPEND_BLOCK_PCT = 1;
|
|
71
|
+
export function resolveDefaultTriggerRunCreditCeiling(tier) {
|
|
72
|
+
return DEFAULT_TRIGGER_RUN_CREDIT_CEILING[tier];
|
|
73
|
+
}
|
|
74
|
+
export function resolveDefaultAutoRunBatchCreditCeiling(tier) {
|
|
75
|
+
return DEFAULT_AUTO_RUN_BATCH_CREDIT_CEILING[tier];
|
|
76
|
+
}
|
|
77
|
+
export function resolveDefaultByokColumnRunMaxRows(tier) {
|
|
78
|
+
return DEFAULT_BYOK_COLUMN_RUN_MAX_ROWS[tier];
|
|
79
|
+
}
|
|
80
|
+
export function resolveDefaultByokProviderDailyCallCap(tier) {
|
|
81
|
+
return DEFAULT_BYOK_PROVIDER_DAILY_CALL_CAP[tier];
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The plan tiers the spend-safety default tables above are keyed by. `limitsTier`
|
|
85
|
+
* collapses "enterprise" onto the "scale" enforcement rung, but these tables carry
|
|
86
|
+
* a distinct "enterprise" column (null = runs on custom limits) — so prefer the
|
|
87
|
+
* raw plan_tier whenever it is itself a table key, and fall back to the limits rung
|
|
88
|
+
* (always a valid PlanTier key) when plan_tier is absent or an unknown/legacy
|
|
89
|
+
* string (e.g. a Stripe price nickname). Single source of truth for the
|
|
90
|
+
* `oxygen limits` report, the org-facing provider-limits API, and the BYOK
|
|
91
|
+
* daily-cap enforcement resolver — they must agree on which column an org reads.
|
|
92
|
+
*/
|
|
93
|
+
export const SPEND_SAFETY_PLAN_TIERS = [
|
|
94
|
+
"free",
|
|
95
|
+
"starter",
|
|
96
|
+
"pro",
|
|
97
|
+
"team",
|
|
98
|
+
"scale",
|
|
99
|
+
"enterprise",
|
|
100
|
+
];
|
|
101
|
+
export function resolveSpendSafetyPlanTier(planTier, limitsTier) {
|
|
102
|
+
if (planTier && SPEND_SAFETY_PLAN_TIERS.includes(planTier)) {
|
|
103
|
+
return planTier;
|
|
104
|
+
}
|
|
105
|
+
return limitsTier;
|
|
106
|
+
}
|
|
@@ -1,7 +1,45 @@
|
|
|
1
|
+
import type { TagColor } from "./select-options.js";
|
|
1
2
|
/** Primitive kinds that participate in the workspace tag union today. */
|
|
2
|
-
export declare const TAG_KINDS: readonly ["knowledge_page", "publishing_post", "sequence", "table", "workflow", "recipe"];
|
|
3
|
+
export declare const TAG_KINDS: readonly ["knowledge_page", "publishing_post", "sequence", "table", "workflow", "recipe", "conversation", "mailbox", "sender", "record", "domain", "project", "engagement_watch"];
|
|
3
4
|
export type TagKind = (typeof TAG_KINDS)[number];
|
|
5
|
+
/**
|
|
6
|
+
* The taggable kinds in prose, for CLI `--help` and MCP tool descriptions.
|
|
7
|
+
*
|
|
8
|
+
* Lives next to {@link TAG_KINDS} because those descriptions are the *discovery*
|
|
9
|
+
* surface: a blind-user eval (2026-07-21) found the tags help still advertising
|
|
10
|
+
* the original seven kinds, so a user reading `tags --help` had no way to learn
|
|
11
|
+
* that domains, projects, or engagement watches were taggable at all — they had
|
|
12
|
+
* to stumble on each owning group's own help. One string, one import, and
|
|
13
|
+
* `TAG_KIND_PROSE_MARKERS` fails the build/test if a new kind skips it.
|
|
14
|
+
*/
|
|
15
|
+
export declare const TAG_KINDS_PROSE: string;
|
|
16
|
+
/**
|
|
17
|
+
* The phrase each kind must contribute to {@link TAG_KINDS_PROSE}. Typed as a
|
|
18
|
+
* total map over TagKind, so adding a kind is a compile error until it is named
|
|
19
|
+
* here, and the paired test asserts the phrase actually appears in the prose.
|
|
20
|
+
*/
|
|
21
|
+
export declare const TAG_KIND_PROSE_MARKERS: Record<TagKind, string>;
|
|
4
22
|
export declare function isTagKind(value: unknown): value is TagKind;
|
|
23
|
+
/**
|
|
24
|
+
* How each kind is named when a tag's footprint is listed. Total over TagKind,
|
|
25
|
+
* so adding a kind is a compile error until it is labelled — the same guarantee
|
|
26
|
+
* {@link TAG_KIND_PROSE_MARKERS} gives the help text.
|
|
27
|
+
*
|
|
28
|
+
* `many` is sentence-form with proper nouns preserved ("CRM records"), which is
|
|
29
|
+
* what an inline count wants ("3 CRM records"). Headings run it through
|
|
30
|
+
* {@link tagKindHeading}, which only uppercases a leading lowercase letter, so
|
|
31
|
+
* "CRM records" survives intact where a naive capitalize would yield "Crm".
|
|
32
|
+
* Three hand-maintained copies of this map used to drift (the /tags page said
|
|
33
|
+
* "knowledge", the detail page and the MCP widget said "Knowledge pages").
|
|
34
|
+
*/
|
|
35
|
+
export declare const TAG_KIND_LABELS: Record<TagKind, {
|
|
36
|
+
one: string;
|
|
37
|
+
many: string;
|
|
38
|
+
}>;
|
|
39
|
+
/** Inline label for a count: `tagKindLabel("sequence", 1)` → "sequence". */
|
|
40
|
+
export declare function tagKindLabel(kind: string, count: number): string;
|
|
41
|
+
/** Section heading for a kind: "Knowledge pages", "CRM records". */
|
|
42
|
+
export declare function tagKindHeading(kind: string): string;
|
|
5
43
|
/** Hard cap per tagged item — matches the wiki's long-standing limit. */
|
|
6
44
|
export declare const MAX_TAGS_PER_ITEM = 50;
|
|
7
45
|
/**
|
|
@@ -12,3 +50,54 @@ export declare const MAX_TAGS_PER_ITEM = 50;
|
|
|
12
50
|
export declare function normalizeTagList(values: readonly unknown[]): string[];
|
|
13
51
|
/** Lenient variant: `undefined`/`null`/non-arrays normalize to `[]`. */
|
|
14
52
|
export declare function normalizeTags(value: unknown): string[];
|
|
53
|
+
/**
|
|
54
|
+
* Registry-only caps (ADR 0017). Deliberately NOT enforced by
|
|
55
|
+
* {@link normalizeTagList}: that runs on every writer's hot path, so adding a
|
|
56
|
+
* length filter there would silently drop an existing long tag the next time
|
|
57
|
+
* any row carrying it was rewritten. A longer tag stays legal to carry — it
|
|
58
|
+
* just cannot be declared.
|
|
59
|
+
*/
|
|
60
|
+
export declare const MAX_TAG_LABEL_LENGTH = 200;
|
|
61
|
+
export declare const MAX_TAG_DESCRIPTION_LENGTH = 500;
|
|
62
|
+
/**
|
|
63
|
+
* Normalize ONE label the way {@link normalizeTagList} normalizes each element,
|
|
64
|
+
* and reject what the registry cannot store: non-strings, blanks, and anything
|
|
65
|
+
* over {@link MAX_TAG_LABEL_LENGTH}. Returns `null` so callers can raise their
|
|
66
|
+
* own typed error.
|
|
67
|
+
*
|
|
68
|
+
* Note this lowercases and trims but does NOT slugify — "Q3 Outbound" becomes
|
|
69
|
+
* "q3 outbound", space intact. Any character is legal in a tag.
|
|
70
|
+
*/
|
|
71
|
+
export declare function normalizeTagLabel(value: unknown): string | null;
|
|
72
|
+
/**
|
|
73
|
+
* One tag as every read surface sees it: carrier counts unioned across stores,
|
|
74
|
+
* plus the registry annotation when the tag is declared.
|
|
75
|
+
*
|
|
76
|
+
* Canonical here rather than in the web app because two consumers used to
|
|
77
|
+
* declare it independently (`WorkspaceTagIndexEntry` server-side and
|
|
78
|
+
* `WorkspaceTagVocabularyEntry` in the TagPicker). Extra properties are
|
|
79
|
+
* structurally compatible in TypeScript, so widening one and not the other
|
|
80
|
+
* compiled silently and the picker simply never saw the new fields.
|
|
81
|
+
*
|
|
82
|
+
* `color` is the explicit OVERRIDE or null — never the derived color. Resolving
|
|
83
|
+
* `tagColorForLabel` here would force every chip render to consult the index
|
|
84
|
+
* instead of computing locally, which is the property that lets undeclared tags
|
|
85
|
+
* stay color-stable with nothing stored.
|
|
86
|
+
*/
|
|
87
|
+
export type WorkspaceTagEntry = {
|
|
88
|
+
tag: string;
|
|
89
|
+
/** Carriers only. 0 for a declared tag nothing carries yet. */
|
|
90
|
+
total: number;
|
|
91
|
+
counts: Partial<Record<TagKind, number>>;
|
|
92
|
+
/** Has a registry row. */
|
|
93
|
+
declared: boolean;
|
|
94
|
+
description: string | null;
|
|
95
|
+
color: TagColor | null;
|
|
96
|
+
pinned: boolean;
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Index ordering, shared by the web page, the CLI/MCP payload, and the picker.
|
|
100
|
+
* Pinned first matters beyond the tags page: the picker truncates its suggestion
|
|
101
|
+
* list, and a curated tag with few carriers would otherwise fall off that cliff.
|
|
102
|
+
*/
|
|
103
|
+
export declare function compareWorkspaceTagEntries(a: Pick<WorkspaceTagEntry, "tag" | "total" | "pinned">, b: Pick<WorkspaceTagEntry, "tag" | "total" | "pinned">): number;
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
// Workspace tags (Tags primitive, ADR 0010): free-form
|
|
2
|
-
// across primitives so campaign knowledge links across
|
|
3
|
-
// campaign-learnings wiki page, the source table, and
|
|
4
|
-
// can all carry `q3-outbound`.
|
|
5
|
-
// the 0009/0025 pattern); this module is the one
|
|
6
|
-
// participate, and how a tag
|
|
1
|
+
// Workspace tags (Tags primitive, ADR 0010; registry per ADR 0017): free-form
|
|
2
|
+
// lowercase labels shared across primitives so campaign knowledge links across
|
|
3
|
+
// stores — a sequence, its campaign-learnings wiki page, the source table, and
|
|
4
|
+
// the workflow that feeds it can all carry `q3-outbound`. Carrier storage stays
|
|
5
|
+
// per-store (`tags text[]` + GIN, the 0009/0025 pattern); this module is the one
|
|
6
|
+
// vocabulary: which kinds participate, and how a tag is normalized before it is
|
|
7
|
+
// written anywhere.
|
|
8
|
+
//
|
|
9
|
+
// Since ADR 0017 a tag exists iff it is DECLARED (a row in the tenant registry,
|
|
10
|
+
// `ox_context.workspace_tags`) OR CARRIED (some store's tags array holds it).
|
|
11
|
+
// The registry is an ANNOTATION table — description, color override, pinned —
|
|
12
|
+
// and never records which items carry a tag. Drift is legal in both directions.
|
|
7
13
|
/** Primitive kinds that participate in the workspace tag union today. */
|
|
8
14
|
export const TAG_KINDS = [
|
|
9
15
|
"knowledge_page",
|
|
@@ -12,10 +18,90 @@ export const TAG_KINDS = [
|
|
|
12
18
|
"table",
|
|
13
19
|
"workflow",
|
|
14
20
|
"recipe",
|
|
21
|
+
"conversation",
|
|
22
|
+
"mailbox",
|
|
23
|
+
"sender",
|
|
24
|
+
"record",
|
|
25
|
+
"domain",
|
|
26
|
+
"project",
|
|
27
|
+
"engagement_watch",
|
|
15
28
|
];
|
|
29
|
+
/**
|
|
30
|
+
* The taggable kinds in prose, for CLI `--help` and MCP tool descriptions.
|
|
31
|
+
*
|
|
32
|
+
* Lives next to {@link TAG_KINDS} because those descriptions are the *discovery*
|
|
33
|
+
* surface: a blind-user eval (2026-07-21) found the tags help still advertising
|
|
34
|
+
* the original seven kinds, so a user reading `tags --help` had no way to learn
|
|
35
|
+
* that domains, projects, or engagement watches were taggable at all — they had
|
|
36
|
+
* to stumble on each owning group's own help. One string, one import, and
|
|
37
|
+
* `TAG_KIND_PROSE_MARKERS` fails the build/test if a new kind skips it.
|
|
38
|
+
*/
|
|
39
|
+
export const TAG_KINDS_PROSE = "publishing posts, knowledge pages, sequences, tables, workflows, recipes, " +
|
|
40
|
+
"inbox conversations, CRM records, mailboxes, sender accounts, sending domains, " +
|
|
41
|
+
"projects, and engagement watches";
|
|
42
|
+
/**
|
|
43
|
+
* The phrase each kind must contribute to {@link TAG_KINDS_PROSE}. Typed as a
|
|
44
|
+
* total map over TagKind, so adding a kind is a compile error until it is named
|
|
45
|
+
* here, and the paired test asserts the phrase actually appears in the prose.
|
|
46
|
+
*/
|
|
47
|
+
export const TAG_KIND_PROSE_MARKERS = {
|
|
48
|
+
knowledge_page: "knowledge page",
|
|
49
|
+
publishing_post: "publishing post",
|
|
50
|
+
sequence: "sequence",
|
|
51
|
+
table: "table",
|
|
52
|
+
workflow: "workflow",
|
|
53
|
+
recipe: "recipe",
|
|
54
|
+
conversation: "inbox conversation",
|
|
55
|
+
mailbox: "mailbox",
|
|
56
|
+
sender: "sender account",
|
|
57
|
+
record: "CRM record",
|
|
58
|
+
domain: "sending domain",
|
|
59
|
+
project: "project",
|
|
60
|
+
engagement_watch: "engagement watch",
|
|
61
|
+
};
|
|
16
62
|
export function isTagKind(value) {
|
|
17
63
|
return typeof value === "string" && TAG_KINDS.includes(value);
|
|
18
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* How each kind is named when a tag's footprint is listed. Total over TagKind,
|
|
67
|
+
* so adding a kind is a compile error until it is labelled — the same guarantee
|
|
68
|
+
* {@link TAG_KIND_PROSE_MARKERS} gives the help text.
|
|
69
|
+
*
|
|
70
|
+
* `many` is sentence-form with proper nouns preserved ("CRM records"), which is
|
|
71
|
+
* what an inline count wants ("3 CRM records"). Headings run it through
|
|
72
|
+
* {@link tagKindHeading}, which only uppercases a leading lowercase letter, so
|
|
73
|
+
* "CRM records" survives intact where a naive capitalize would yield "Crm".
|
|
74
|
+
* Three hand-maintained copies of this map used to drift (the /tags page said
|
|
75
|
+
* "knowledge", the detail page and the MCP widget said "Knowledge pages").
|
|
76
|
+
*/
|
|
77
|
+
export const TAG_KIND_LABELS = {
|
|
78
|
+
knowledge_page: { one: "knowledge page", many: "knowledge pages" },
|
|
79
|
+
publishing_post: { one: "publishing post", many: "publishing posts" },
|
|
80
|
+
sequence: { one: "sequence", many: "sequences" },
|
|
81
|
+
table: { one: "table", many: "tables" },
|
|
82
|
+
workflow: { one: "workflow", many: "workflows" },
|
|
83
|
+
recipe: { one: "recipe", many: "recipes" },
|
|
84
|
+
conversation: { one: "conversation", many: "conversations" },
|
|
85
|
+
mailbox: { one: "mailbox", many: "mailboxes" },
|
|
86
|
+
sender: { one: "sender account", many: "sender accounts" },
|
|
87
|
+
record: { one: "CRM record", many: "CRM records" },
|
|
88
|
+
domain: { one: "domain", many: "domains" },
|
|
89
|
+
project: { one: "project", many: "projects" },
|
|
90
|
+
engagement_watch: { one: "engagement watch", many: "engagement watches" },
|
|
91
|
+
};
|
|
92
|
+
/** Inline label for a count: `tagKindLabel("sequence", 1)` → "sequence". */
|
|
93
|
+
export function tagKindLabel(kind, count) {
|
|
94
|
+
const entry = isTagKind(kind) ? TAG_KIND_LABELS[kind] : null;
|
|
95
|
+
if (!entry)
|
|
96
|
+
return kind;
|
|
97
|
+
return count === 1 ? entry.one : entry.many;
|
|
98
|
+
}
|
|
99
|
+
/** Section heading for a kind: "Knowledge pages", "CRM records". */
|
|
100
|
+
export function tagKindHeading(kind) {
|
|
101
|
+
const entry = isTagKind(kind) ? TAG_KIND_LABELS[kind] : null;
|
|
102
|
+
const label = entry ? entry.many : kind;
|
|
103
|
+
return label.charAt(0).toUpperCase() + label.slice(1);
|
|
104
|
+
}
|
|
19
105
|
/** Hard cap per tagged item — matches the wiki's long-standing limit. */
|
|
20
106
|
export const MAX_TAGS_PER_ITEM = 50;
|
|
21
107
|
/**
|
|
@@ -32,3 +118,37 @@ export function normalizeTags(value) {
|
|
|
32
118
|
return [];
|
|
33
119
|
return normalizeTagList(value);
|
|
34
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Registry-only caps (ADR 0017). Deliberately NOT enforced by
|
|
123
|
+
* {@link normalizeTagList}: that runs on every writer's hot path, so adding a
|
|
124
|
+
* length filter there would silently drop an existing long tag the next time
|
|
125
|
+
* any row carrying it was rewritten. A longer tag stays legal to carry — it
|
|
126
|
+
* just cannot be declared.
|
|
127
|
+
*/
|
|
128
|
+
export const MAX_TAG_LABEL_LENGTH = 200;
|
|
129
|
+
export const MAX_TAG_DESCRIPTION_LENGTH = 500;
|
|
130
|
+
/**
|
|
131
|
+
* Normalize ONE label the way {@link normalizeTagList} normalizes each element,
|
|
132
|
+
* and reject what the registry cannot store: non-strings, blanks, and anything
|
|
133
|
+
* over {@link MAX_TAG_LABEL_LENGTH}. Returns `null` so callers can raise their
|
|
134
|
+
* own typed error.
|
|
135
|
+
*
|
|
136
|
+
* Note this lowercases and trims but does NOT slugify — "Q3 Outbound" becomes
|
|
137
|
+
* "q3 outbound", space intact. Any character is legal in a tag.
|
|
138
|
+
*/
|
|
139
|
+
export function normalizeTagLabel(value) {
|
|
140
|
+
if (typeof value !== "string")
|
|
141
|
+
return null;
|
|
142
|
+
const normalized = value.trim().toLowerCase();
|
|
143
|
+
if (!normalized || normalized.length > MAX_TAG_LABEL_LENGTH)
|
|
144
|
+
return null;
|
|
145
|
+
return normalized;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Index ordering, shared by the web page, the CLI/MCP payload, and the picker.
|
|
149
|
+
* Pinned first matters beyond the tags page: the picker truncates its suggestion
|
|
150
|
+
* list, and a curated tag with few carriers would otherwise fall off that cliff.
|
|
151
|
+
*/
|
|
152
|
+
export function compareWorkspaceTagEntries(a, b) {
|
|
153
|
+
return Number(b.pinned) - Number(a.pinned) || b.total - a.total || a.tag.localeCompare(b.tag);
|
|
154
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const OXYGEN_VERSION = "1.
|
|
1
|
+
export const OXYGEN_VERSION = "1.575.19";
|
|
2
2
|
// The GLOBAL CLI compatibility floor: the oldest CLI allowed to call any
|
|
3
3
|
// operational route. Raising it hard-rejects every older CLI from the entire
|
|
4
4
|
// product, so it obeys one law, enforced by scripts/ci/cli-min-version-gate.mjs:
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const WORKFLOW_TRIGGER_AUTO_PAUSE_METADATA_KEYS: readonly ["consecutive_failure_count", "last_failure_code", "last_failure_at", "auto_paused_at", "auto_pause_reason"];
|
|
1
|
+
export declare const WORKFLOW_TRIGGER_AUTO_PAUSE_METADATA_KEYS: readonly ["consecutive_failure_count", "consecutive_capacity_failure_count", "last_failure_code", "last_failure_at", "auto_paused_at", "auto_pause_reason"];
|
|
2
2
|
export declare function clearWorkflowTriggerAutoPauseMetadata(metadata: Record<string, unknown>): Record<string, unknown>;
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export const WORKFLOW_TRIGGER_AUTO_PAUSE_METADATA_KEYS = [
|
|
2
2
|
"consecutive_failure_count",
|
|
3
|
+
// The provider-capacity streak, counted independently of the deterministic one
|
|
4
|
+
// so the two classes cannot borrow each other's budget. Listed here so a
|
|
5
|
+
// success — or a re-enable — clears it through the same single path.
|
|
6
|
+
"consecutive_capacity_failure_count",
|
|
3
7
|
"last_failure_code",
|
|
4
8
|
"last_failure_at",
|
|
5
9
|
"auto_paused_at",
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
/** The
|
|
2
|
-
export type WorkspaceAgentSlug = "inbox-reply-drafts" | "meeting-notetaker" | "knowledge-synthesis";
|
|
1
|
+
/** The implemented general Workspace Agent and specialists (incl. the auto-tagger), by stable slug. */
|
|
2
|
+
export type WorkspaceAgentSlug = "workspace" | "inbox-reply-drafts" | "meeting-notetaker" | "knowledge-synthesis" | "inbox-auto-tagger";
|
|
3
3
|
/**
|
|
4
4
|
* How the web layer builds a specialist's deep-link. A pure hint on the
|
|
5
5
|
* descriptor (shared can't import the web URL helpers) that agent-registry
|
|
6
6
|
* switches on to produce the actual `https://oxygen-agent.com/...` URL.
|
|
7
7
|
*/
|
|
8
|
-
export type WorkspaceAgentDeepLinkKind = "inbox_reply_agent" | "notetaker" | "knowledge_agent";
|
|
8
|
+
export type WorkspaceAgentDeepLinkKind = "workspace_agent" | "inbox_reply_agent" | "notetaker" | "knowledge_agent" | "inbox_auto_tagger";
|
|
9
9
|
/** A pure, serializable descriptor for one Workspace Agent specialist. */
|
|
10
10
|
export type WorkspaceAgentDescriptor = {
|
|
11
11
|
/** Stable identifier used across CLI/MCP/API. Never derived from the title. */
|
|
@@ -54,12 +54,13 @@ export type WorkspaceAgentRunItem = {
|
|
|
54
54
|
webUrl: string | null;
|
|
55
55
|
};
|
|
56
56
|
/**
|
|
57
|
-
* The roster.
|
|
58
|
-
* stable (it drives the default CLI/widget ordering).
|
|
59
|
-
* deliberate product decision that requires the
|
|
57
|
+
* The roster. The general Workspace Agent plus the implemented specialists
|
|
58
|
+
* (five entries). Order is stable (it drives the default CLI/widget ordering).
|
|
59
|
+
* Adding an entry is a deliberate product decision that requires the worker to
|
|
60
|
+
* actually ship first.
|
|
60
61
|
*/
|
|
61
62
|
export declare const WORKSPACE_AGENT_ROSTER: readonly WorkspaceAgentDescriptor[];
|
|
62
|
-
/** True when `value` is one of the
|
|
63
|
+
/** True when `value` is one of the implemented specialist slugs. */
|
|
63
64
|
export declare function isWorkspaceAgentSlug(value: unknown): value is WorkspaceAgentSlug;
|
|
64
65
|
/** The descriptor for `slug`, or `undefined` when it is not a known specialist. */
|
|
65
66
|
export declare function getWorkspaceAgentDescriptor(slug: string): WorkspaceAgentDescriptor | undefined;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
// The Agents primitive's roster (ADR 0011): the governed registry of OXYGEN's
|
|
2
|
-
// built-in workspace agents.
|
|
2
|
+
// built-in workspace agents. The general Workspace Agent is backed by the
|
|
3
|
+
// first-class ox_agents runtime; specialists remain computed from their owning
|
|
4
|
+
// primitive stores. Each
|
|
3
5
|
// entry is a product-defined agent that ships as an approval-gated, observable
|
|
4
6
|
// behavior elsewhere in the product, surfaced under /agents and `oxygen agent`.
|
|
5
7
|
// Agents are governed workspace workers: the runtime envelope (approval posture,
|
|
6
8
|
// credit budgets, run observability) is platform-enforced, never agent-optional.
|
|
7
|
-
// User-defined custom agents are ratified (ADR 0011) but arrive
|
|
8
|
-
//
|
|
9
|
+
// User-defined custom agents are ratified (ADR 0011) but arrive after the unified
|
|
10
|
+
// Workspace Agent runtime — until then there is no custom create/delete verb,
|
|
9
11
|
// and there are never placeholder rows: the roster lists only agents that are
|
|
10
12
|
// actually implemented today. CRM-hygiene, deliverability, and any other roadmap
|
|
11
13
|
// agent stay OUT until they ship — the no-placeholder-surface rule.
|
|
@@ -15,11 +17,24 @@
|
|
|
15
17
|
// (config read/write, run history, deep-links) lives in the web layer's
|
|
16
18
|
// agent-registry, which composes these descriptors with the existing helpers.
|
|
17
19
|
/**
|
|
18
|
-
* The roster.
|
|
19
|
-
* stable (it drives the default CLI/widget ordering).
|
|
20
|
-
* deliberate product decision that requires the
|
|
20
|
+
* The roster. The general Workspace Agent plus the implemented specialists
|
|
21
|
+
* (five entries). Order is stable (it drives the default CLI/widget ordering).
|
|
22
|
+
* Adding an entry is a deliberate product decision that requires the worker to
|
|
23
|
+
* actually ship first.
|
|
21
24
|
*/
|
|
22
25
|
export const WORKSPACE_AGENT_ROSTER = [
|
|
26
|
+
{
|
|
27
|
+
slug: "workspace",
|
|
28
|
+
title: "Workspace Agent",
|
|
29
|
+
subtitle: "Runs organization-grounded goals through OXYGEN's governed capability catalog.",
|
|
30
|
+
description: "Takes a workspace goal, plans the work, and operates tables, CRM, context, workflows, sequences, files, and integrations as one durable Agent run.",
|
|
31
|
+
owningPrimitive: "Agents",
|
|
32
|
+
configCommand: "oxygen agent set workspace",
|
|
33
|
+
configApiPath: "/api/cli/agent/workspace",
|
|
34
|
+
mcpConfigTools: ["oxygen_agent_update", "oxygen_agent_run"],
|
|
35
|
+
approvalPosture: "Uses a versioned standing authorization with optional allow, ask, and deny rules; platform previews, caps, deny rules, and run provenance always apply.",
|
|
36
|
+
deepLinkKind: "workspace_agent",
|
|
37
|
+
},
|
|
23
38
|
{
|
|
24
39
|
slug: "inbox-reply-drafts",
|
|
25
40
|
title: "AI Sales Agent",
|
|
@@ -56,8 +71,20 @@ export const WORKSPACE_AGENT_ROSTER = [
|
|
|
56
71
|
approvalPosture: "Writes working knowledge pages as logged revisions; canonical and pinned pages still require proposal approval.",
|
|
57
72
|
deepLinkKind: "knowledge_agent",
|
|
58
73
|
},
|
|
74
|
+
{
|
|
75
|
+
slug: "inbox-auto-tagger",
|
|
76
|
+
title: "AI Inbox Tagger",
|
|
77
|
+
subtitle: "Auto-tags new inbound conversations from your tag allowlist.",
|
|
78
|
+
description: "Classifies every new inbound email, LinkedIn, and WhatsApp conversation against your workspace tag allowlist and applies the matching tags automatically.",
|
|
79
|
+
owningPrimitive: "Messages",
|
|
80
|
+
configCommand: "oxygen inbox auto-tagger",
|
|
81
|
+
configApiPath: "/api/cli/inbox/auto-tagger",
|
|
82
|
+
mcpConfigTools: ["oxygen_inbox_auto_tagger_get", "oxygen_inbox_auto_tagger_set"],
|
|
83
|
+
approvalPosture: "Auto-applies internal workspace tags only — additive, never re-adds a tag you removed, and needs no approval queue because tags never leave the workspace.",
|
|
84
|
+
deepLinkKind: "inbox_auto_tagger",
|
|
85
|
+
},
|
|
59
86
|
];
|
|
60
|
-
/** True when `value` is one of the
|
|
87
|
+
/** True when `value` is one of the implemented specialist slugs. */
|
|
61
88
|
export function isWorkspaceAgentSlug(value) {
|
|
62
89
|
return (typeof value === "string" &&
|
|
63
90
|
WORKSPACE_AGENT_ROSTER.some((descriptor) => descriptor.slug === value));
|