@oxygen-agent/cli 1.720.7 → 1.739.0
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 +27 -12
- package/dist/index.js +227 -63
- package/dist/skills.js +2 -2
- package/node_modules/@oxygen/shared/dist/cli-result.js +2 -0
- package/node_modules/@oxygen/shared/dist/crm-activity-events.d.ts +20 -0
- package/node_modules/@oxygen/shared/dist/crm-activity-events.js +5 -0
- package/node_modules/@oxygen/shared/dist/future-signup-events.d.ts +27 -0
- package/node_modules/@oxygen/shared/dist/future-signup-events.js +90 -0
- package/node_modules/@oxygen/shared/dist/future-signup-lifecycle-projection.d.ts +320 -0
- package/node_modules/@oxygen/shared/dist/future-signup-lifecycle-projection.js +1233 -0
- package/node_modules/@oxygen/shared/dist/index.d.ts +6 -0
- package/node_modules/@oxygen/shared/dist/index.js +6 -0
- package/node_modules/@oxygen/shared/dist/notetaker-events.d.ts +4 -0
- package/node_modules/@oxygen/shared/dist/notetaker-events.js +23 -0
- package/node_modules/@oxygen/shared/dist/plain-support-events.d.ts +40 -0
- package/node_modules/@oxygen/shared/dist/plain-support-events.js +43 -0
- package/node_modules/@oxygen/shared/dist/redaction.js +6 -1
- package/node_modules/@oxygen/shared/dist/user-capability-routing.d.ts +2 -0
- package/node_modules/@oxygen/shared/dist/user-capability-routing.js +23 -0
- 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/webhook-headers.d.ts +10 -0
- package/node_modules/@oxygen/shared/dist/webhook-headers.js +48 -0
- package/node_modules/@oxygen/shared/dist/workspace-event-catalog.d.ts +31 -1
- package/node_modules/@oxygen/shared/dist/workspace-event-catalog.js +112 -1
- package/node_modules/@oxygen/workflows/dist/graph/expression.js +7 -5
- package/node_modules/@oxygen/workflows/dist/graph/lint.d.ts +9 -0
- package/node_modules/@oxygen/workflows/dist/graph/lint.js +409 -7
- package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.d.ts +513 -0
- package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.js +104 -1
- package/node_modules/@oxygen/workflows/dist/graph/params.d.ts +2 -0
- package/node_modules/@oxygen/workflows/dist/graph/params.js +15 -0
- package/node_modules/@oxygen/workflows/dist/graph/remap.js +12 -3
- package/node_modules/@oxygen/workflows/dist/graph/topology.d.ts +9 -0
- package/node_modules/@oxygen/workflows/dist/graph/topology.js +33 -1
- package/node_modules/@oxygen/workflows/dist/graph/types.d.ts +123 -16
- package/node_modules/@oxygen/workflows/dist/graph/types.js +45 -0
- package/node_modules/@oxygen/workflows/dist/index.d.ts +94 -2
- package/node_modules/@oxygen/workflows/dist/index.js +169 -26
- package/node_modules/@oxygen/workflows/dist/portable.js +3 -1
- package/package.json +1 -1
|
@@ -5,7 +5,9 @@ export * from "./billing.js";
|
|
|
5
5
|
export * from "./billing-anchors.js";
|
|
6
6
|
export * from "./budget-scopes.js";
|
|
7
7
|
export * from "./capability-discovery.js";
|
|
8
|
+
export * from "./user-capability-routing.js";
|
|
8
9
|
export * from "./plan-limits.js";
|
|
10
|
+
export * from "./plain-support-events.js";
|
|
9
11
|
export * from "./spend-safety.js";
|
|
10
12
|
export * from "./cell-format.js";
|
|
11
13
|
export * from "./cli-envelope.js";
|
|
@@ -22,6 +24,8 @@ export * from "./directory.js";
|
|
|
22
24
|
export * from "./email-tracking-token.js";
|
|
23
25
|
export * from "./email-unsubscribe-token.js";
|
|
24
26
|
export * from "./error-redaction.js";
|
|
27
|
+
export * from "./future-signup-events.js";
|
|
28
|
+
export * from "./future-signup-lifecycle-projection.js";
|
|
25
29
|
export * from "./feature-gates.js";
|
|
26
30
|
export * from "./hosted-ai.js";
|
|
27
31
|
export * from "./identifiers.js";
|
|
@@ -64,8 +68,10 @@ export * from "./timing.js";
|
|
|
64
68
|
export * from "./type-guards.js";
|
|
65
69
|
export * from "./worker-failures-queue.js";
|
|
66
70
|
export * from "./workflow-mcp-tools.js";
|
|
71
|
+
export * from "./webhook-headers.js";
|
|
67
72
|
export * from "./workflow-trigger-label.js";
|
|
68
73
|
export * from "./workspace-event-catalog.js";
|
|
74
|
+
export * from "./notetaker-events.js";
|
|
69
75
|
export * from "./workspace-agents.js";
|
|
70
76
|
export declare const MAX_ROW_LOOP_WRITE_ROWS = 500;
|
|
71
77
|
export type SemanticVersion = {
|
|
@@ -5,7 +5,9 @@ export * from "./billing.js";
|
|
|
5
5
|
export * from "./billing-anchors.js";
|
|
6
6
|
export * from "./budget-scopes.js";
|
|
7
7
|
export * from "./capability-discovery.js";
|
|
8
|
+
export * from "./user-capability-routing.js";
|
|
8
9
|
export * from "./plan-limits.js";
|
|
10
|
+
export * from "./plain-support-events.js";
|
|
9
11
|
export * from "./spend-safety.js";
|
|
10
12
|
export * from "./cell-format.js";
|
|
11
13
|
export * from "./cli-envelope.js";
|
|
@@ -22,6 +24,8 @@ export * from "./directory.js";
|
|
|
22
24
|
export * from "./email-tracking-token.js";
|
|
23
25
|
export * from "./email-unsubscribe-token.js";
|
|
24
26
|
export * from "./error-redaction.js";
|
|
27
|
+
export * from "./future-signup-events.js";
|
|
28
|
+
export * from "./future-signup-lifecycle-projection.js";
|
|
25
29
|
export * from "./feature-gates.js";
|
|
26
30
|
export * from "./hosted-ai.js";
|
|
27
31
|
export * from "./identifiers.js";
|
|
@@ -75,8 +79,10 @@ export * from "./timing.js";
|
|
|
75
79
|
export * from "./type-guards.js";
|
|
76
80
|
export * from "./worker-failures-queue.js";
|
|
77
81
|
export * from "./workflow-mcp-tools.js";
|
|
82
|
+
export * from "./webhook-headers.js";
|
|
78
83
|
export * from "./workflow-trigger-label.js";
|
|
79
84
|
export * from "./workspace-event-catalog.js";
|
|
85
|
+
export * from "./notetaker-events.js";
|
|
80
86
|
export * from "./workspace-agents.js";
|
|
81
87
|
// Maximum rows a single row-loop write (insert/upsert/preview) may process. The
|
|
82
88
|
// row-loop engine issues one DB round-trip per row, so a 500-row write already
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Canonical workflow-event contract emitted after a Meeting Notetaker run finishes. */
|
|
2
|
+
export declare const NOTETAKER_WORKFLOW_EVENT_SOURCE = "oxygen.notetaker";
|
|
3
|
+
export declare const NOTETAKER_MEETING_COMPLETED_EVENT = "meeting.completed";
|
|
4
|
+
export declare const NOTETAKER_MEETING_COMPLETED_PAYLOAD_FIELDS: readonly ["provider", "source", "event", "session.id", "session.title", "session.status", "session.capture_mode", "session.scheduled_start_at", "session.started_at", "session.ended_at", "session.completed_at", "session.credits_used", "session.web_url", "meeting_note.markdown", "meeting_note.web_url", "crm.activity_id", "crm.links", "artifacts"];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Canonical workflow-event contract emitted after a Meeting Notetaker run finishes. */
|
|
2
|
+
export const NOTETAKER_WORKFLOW_EVENT_SOURCE = "oxygen.notetaker";
|
|
3
|
+
export const NOTETAKER_MEETING_COMPLETED_EVENT = "meeting.completed";
|
|
4
|
+
export const NOTETAKER_MEETING_COMPLETED_PAYLOAD_FIELDS = [
|
|
5
|
+
"provider",
|
|
6
|
+
"source",
|
|
7
|
+
"event",
|
|
8
|
+
"session.id",
|
|
9
|
+
"session.title",
|
|
10
|
+
"session.status",
|
|
11
|
+
"session.capture_mode",
|
|
12
|
+
"session.scheduled_start_at",
|
|
13
|
+
"session.started_at",
|
|
14
|
+
"session.ended_at",
|
|
15
|
+
"session.completed_at",
|
|
16
|
+
"session.credits_used",
|
|
17
|
+
"session.web_url",
|
|
18
|
+
"meeting_note.markdown",
|
|
19
|
+
"meeting_note.web_url",
|
|
20
|
+
"crm.activity_id",
|
|
21
|
+
"crm.links",
|
|
22
|
+
"artifacts",
|
|
23
|
+
];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export declare const PLAIN_SUPPORT_WORKFLOW_EVENT_SOURCE = "plain_support";
|
|
2
|
+
export declare const PLAIN_SUPPORT_MESSAGE_RECEIVED_EVENT = "message_received";
|
|
3
|
+
export declare const PLAIN_SUPPORT_WEBHOOK_VERSION = "2026-02-11";
|
|
4
|
+
export declare const PLAIN_SUPPORT_MESSAGE_RECEIVED_PAYLOAD_FIELDS: readonly ["event_id", "event_type", "occurred_at", "workspace_id", "webhook_target_id", "webhook_target_version", "thread_id", "thread_ref", "timeline_entry_id", "channel", "status", "priority", "tenant_external_id", "has_assignee", "assignee_kind", "label_external_ids", "support_fields", "thread_created_at", "thread_updated_at", "automation_mode", "customer_content_retained"];
|
|
5
|
+
export declare const PLAIN_SUPPORT_INBOUND_EVENT_TYPES: readonly ["thread.chat_received", "thread.email_received", "thread.slack_message_received", "thread.ms_teams_message_received", "thread.discord_message_received"];
|
|
6
|
+
export type PlainSupportInboundEventType = (typeof PLAIN_SUPPORT_INBOUND_EVENT_TYPES)[number];
|
|
7
|
+
export declare const PLAIN_SUPPORT_CHANNELS: readonly ["chat", "email", "slack", "ms_teams", "discord"];
|
|
8
|
+
export type PlainSupportChannel = (typeof PLAIN_SUPPORT_CHANNELS)[number];
|
|
9
|
+
/**
|
|
10
|
+
* Metadata-only contract persisted in OXYGEN's Workflow event/run ledger.
|
|
11
|
+
*
|
|
12
|
+
* Plain remains the canonical and only customer-content store. Never add message
|
|
13
|
+
* text, Thread title/preview/description, customer identity, email, or attachment
|
|
14
|
+
* contents to this payload. A human or a separately governed support Agent reads
|
|
15
|
+
* that content from the canonical Plain Thread when it actually needs it.
|
|
16
|
+
*/
|
|
17
|
+
export type PlainSupportMessageReceivedPayload = {
|
|
18
|
+
event_id: string;
|
|
19
|
+
event_type: PlainSupportInboundEventType;
|
|
20
|
+
occurred_at: string;
|
|
21
|
+
workspace_id: string;
|
|
22
|
+
webhook_target_id: string;
|
|
23
|
+
webhook_target_version: typeof PLAIN_SUPPORT_WEBHOOK_VERSION;
|
|
24
|
+
thread_id: string;
|
|
25
|
+
thread_ref: string;
|
|
26
|
+
timeline_entry_id: string;
|
|
27
|
+
channel: PlainSupportChannel;
|
|
28
|
+
status: "TODO" | "SNOOZED" | "DONE";
|
|
29
|
+
priority: number;
|
|
30
|
+
tenant_external_id: string | null;
|
|
31
|
+
has_assignee: boolean;
|
|
32
|
+
assignee_kind: "user" | "machine_user" | "system" | null;
|
|
33
|
+
label_external_ids: string[];
|
|
34
|
+
support_fields: Record<string, string>;
|
|
35
|
+
thread_created_at: string;
|
|
36
|
+
thread_updated_at: string;
|
|
37
|
+
automation_mode: "observe";
|
|
38
|
+
customer_content_retained: false;
|
|
39
|
+
};
|
|
40
|
+
export declare function isPlainSupportInboundEventType(value: string): value is PlainSupportInboundEventType;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export const PLAIN_SUPPORT_WORKFLOW_EVENT_SOURCE = "plain_support";
|
|
2
|
+
export const PLAIN_SUPPORT_MESSAGE_RECEIVED_EVENT = "message_received";
|
|
3
|
+
export const PLAIN_SUPPORT_WEBHOOK_VERSION = "2026-02-11";
|
|
4
|
+
export const PLAIN_SUPPORT_MESSAGE_RECEIVED_PAYLOAD_FIELDS = [
|
|
5
|
+
"event_id",
|
|
6
|
+
"event_type",
|
|
7
|
+
"occurred_at",
|
|
8
|
+
"workspace_id",
|
|
9
|
+
"webhook_target_id",
|
|
10
|
+
"webhook_target_version",
|
|
11
|
+
"thread_id",
|
|
12
|
+
"thread_ref",
|
|
13
|
+
"timeline_entry_id",
|
|
14
|
+
"channel",
|
|
15
|
+
"status",
|
|
16
|
+
"priority",
|
|
17
|
+
"tenant_external_id",
|
|
18
|
+
"has_assignee",
|
|
19
|
+
"assignee_kind",
|
|
20
|
+
"label_external_ids",
|
|
21
|
+
"support_fields",
|
|
22
|
+
"thread_created_at",
|
|
23
|
+
"thread_updated_at",
|
|
24
|
+
"automation_mode",
|
|
25
|
+
"customer_content_retained",
|
|
26
|
+
];
|
|
27
|
+
export const PLAIN_SUPPORT_INBOUND_EVENT_TYPES = [
|
|
28
|
+
"thread.chat_received",
|
|
29
|
+
"thread.email_received",
|
|
30
|
+
"thread.slack_message_received",
|
|
31
|
+
"thread.ms_teams_message_received",
|
|
32
|
+
"thread.discord_message_received",
|
|
33
|
+
];
|
|
34
|
+
export const PLAIN_SUPPORT_CHANNELS = [
|
|
35
|
+
"chat",
|
|
36
|
+
"email",
|
|
37
|
+
"slack",
|
|
38
|
+
"ms_teams",
|
|
39
|
+
"discord",
|
|
40
|
+
];
|
|
41
|
+
export function isPlainSupportInboundEventType(value) {
|
|
42
|
+
return PLAIN_SUPPORT_INBOUND_EVENT_TYPES.includes(value);
|
|
43
|
+
}
|
|
@@ -36,6 +36,10 @@ const BEARER_TOKEN_PATTERN = /\bBearer\s+(?!tokens?(?![\w.~+/=-]))[\w.~+/=-]+/gi
|
|
|
36
36
|
// missing" diagnostics (its actual value is an `sk-…` string, already covered).
|
|
37
37
|
const DB_URL_PATTERN = /(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|rediss?|amqps?):\/\/[^\s"'<>()]+/gi;
|
|
38
38
|
const SK_TOKEN_PATTERN = /sk-[A-Za-z0-9_-]{20,}/g;
|
|
39
|
+
// PostHog personal (`phx_`) and project (`phc_`) keys can be echoed inside a
|
|
40
|
+
// provider hostname-validation error. They are secrets regardless of which
|
|
41
|
+
// ordinary string field carried them, so scrub both credential families.
|
|
42
|
+
const POSTHOG_TOKEN_PATTERN = /\bph[xc]_[A-Za-z0-9_-]{6,}/g;
|
|
39
43
|
const MAX_LOG_STRING_LENGTH = 8_000;
|
|
40
44
|
const MAX_TELEMETRY_STRING_LENGTH = 500;
|
|
41
45
|
const MAX_ARRAY_LENGTH = 20;
|
|
@@ -151,7 +155,8 @@ export function redactSecretsInString(value) {
|
|
|
151
155
|
return value
|
|
152
156
|
.replace(BEARER_TOKEN_PATTERN, "Bearer[REDACTED]")
|
|
153
157
|
.replace(DB_URL_PATTERN, "[REDACTED_DB_URL]")
|
|
154
|
-
.replace(SK_TOKEN_PATTERN, "[REDACTED_SK]")
|
|
158
|
+
.replace(SK_TOKEN_PATTERN, "[REDACTED_SK]")
|
|
159
|
+
.replace(POSTHOG_TOKEN_PATTERN, "[REDACTED_POSTHOG_KEY]");
|
|
155
160
|
}
|
|
156
161
|
function truncateString(value, maxLength) {
|
|
157
162
|
return value.length > maxLength ? `${value.slice(0, maxLength)}...` : value;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { getCapabilityRouteMatch, inferCapabilityRoute, } from "./capability-discovery.js";
|
|
2
|
+
// Provider brands are intentionally not duplicated into the static capability
|
|
3
|
+
// catalog. A short "connect <provider>" query would otherwise match Tables via
|
|
4
|
+
// its relationship-oriented "connect" term. Correct that narrow ambiguity at
|
|
5
|
+
// the user-facing boundary while preserving table joins and channel-specific
|
|
6
|
+
// connection routes.
|
|
7
|
+
export function inferUserCapabilityRoute(query) {
|
|
8
|
+
const route = inferCapabilityRoute(query);
|
|
9
|
+
if (route?.card.id !== "tables" || !isSimpleProviderConnectionIntent(query)) {
|
|
10
|
+
return route;
|
|
11
|
+
}
|
|
12
|
+
return getCapabilityRouteMatch("connected-integrations") ?? route;
|
|
13
|
+
}
|
|
14
|
+
function isSimpleProviderConnectionIntent(query) {
|
|
15
|
+
const normalized = query.toLowerCase().replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim();
|
|
16
|
+
if (/\b(table|tables|row|rows|column|columns|dataset|join|relationship|relate)\b/.test(normalized)) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
if (/\b(integration|provider|oauth|byok|api key|connected account)\b/.test(normalized)) {
|
|
20
|
+
return /\b(connect|reconnect|disconnect)\b/.test(normalized);
|
|
21
|
+
}
|
|
22
|
+
return /\b(?:connect|reconnect|disconnect)\s+(?:(?:my|our|the)\s+)?(?!(?:this|that|them|us|me)\b)[a-z0-9][a-z0-9.]*\s*$/.test(normalized);
|
|
23
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const OXYGEN_VERSION = "1.
|
|
1
|
+
export const OXYGEN_VERSION = "1.739.0";
|
|
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:
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Prevent one attacker-controlled audit value from bloating every delivery. */
|
|
2
|
+
export declare const WEBHOOK_AUDIT_HEADER_VALUE_MAX_CHARS = 4096;
|
|
3
|
+
/**
|
|
4
|
+
* Return only non-secret webhook audit headers, normalized to lower-case keys.
|
|
5
|
+
*
|
|
6
|
+
* `blockedHeaderNames` remains useful to callers documenting a provider's
|
|
7
|
+
* authentication headers, even though an auth header is omitted by default
|
|
8
|
+
* unless it is explicitly part of the allowlist above.
|
|
9
|
+
*/
|
|
10
|
+
export declare function sanitizeWebhookHeaderRecord(headers: Record<string, unknown> | null | undefined, blockedHeaderNames?: readonly string[]): Record<string, string>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webhook headers that are useful for delivery correlation and payload parsing.
|
|
3
|
+
*
|
|
4
|
+
* Inbound requests also carry platform and authentication material (for example
|
|
5
|
+
* Vercel OIDC assertions, proxy signatures, cookies, and provider signatures).
|
|
6
|
+
* A denylist will always lag those providers, so persisted webhook audit data is
|
|
7
|
+
* intentionally an allowlist. Signature verification still receives the raw
|
|
8
|
+
* Headers object before this sanitizer runs.
|
|
9
|
+
*/
|
|
10
|
+
const WEBHOOK_AUDIT_HEADER_NAMES = new Set([
|
|
11
|
+
"accept",
|
|
12
|
+
"content-length",
|
|
13
|
+
"content-type",
|
|
14
|
+
"idempotency-key",
|
|
15
|
+
"user-agent",
|
|
16
|
+
"webhook-id",
|
|
17
|
+
"webhook-timestamp",
|
|
18
|
+
"x-correlation-id",
|
|
19
|
+
"x-event-id",
|
|
20
|
+
"x-event-type",
|
|
21
|
+
"x-idempotency-key",
|
|
22
|
+
"x-oxygen-delivery-id",
|
|
23
|
+
"x-request-id",
|
|
24
|
+
"x-webhook-event",
|
|
25
|
+
"x-webhook-id",
|
|
26
|
+
]);
|
|
27
|
+
/** Prevent one attacker-controlled audit value from bloating every delivery. */
|
|
28
|
+
export const WEBHOOK_AUDIT_HEADER_VALUE_MAX_CHARS = 4_096;
|
|
29
|
+
/**
|
|
30
|
+
* Return only non-secret webhook audit headers, normalized to lower-case keys.
|
|
31
|
+
*
|
|
32
|
+
* `blockedHeaderNames` remains useful to callers documenting a provider's
|
|
33
|
+
* authentication headers, even though an auth header is omitted by default
|
|
34
|
+
* unless it is explicitly part of the allowlist above.
|
|
35
|
+
*/
|
|
36
|
+
export function sanitizeWebhookHeaderRecord(headers, blockedHeaderNames = []) {
|
|
37
|
+
const blocked = new Set(blockedHeaderNames.map((name) => name.trim().toLowerCase()));
|
|
38
|
+
const sanitized = {};
|
|
39
|
+
for (const [rawName, rawValue] of Object.entries(headers ?? {})) {
|
|
40
|
+
const name = rawName.trim().toLowerCase();
|
|
41
|
+
if (!WEBHOOK_AUDIT_HEADER_NAMES.has(name) || blocked.has(name))
|
|
42
|
+
continue;
|
|
43
|
+
if (typeof rawValue !== "string")
|
|
44
|
+
continue;
|
|
45
|
+
sanitized[name] = rawValue.slice(0, WEBHOOK_AUDIT_HEADER_VALUE_MAX_CHARS);
|
|
46
|
+
}
|
|
47
|
+
return sanitized;
|
|
48
|
+
}
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
import { CRM_SIGNAL_WORKFLOW_EVENT_DEFINITIONS } from "./crm-reply-events.js";
|
|
2
|
+
export declare const WORKSPACE_EVENT_PRODUCER_READINESS_REQUIREMENTS: readonly ["always", "notetaker_enabled"];
|
|
3
|
+
export type WorkspaceEventProducerReadinessRequirement = (typeof WORKSPACE_EVENT_PRODUCER_READINESS_REQUIREMENTS)[number];
|
|
4
|
+
export type CrmSignalWorkflowEvent = (typeof CRM_SIGNAL_WORKFLOW_EVENT_DEFINITIONS)[number]["event"];
|
|
5
|
+
export type WorkspaceEventProducerId = "sequencer.contact_activity" | "sequencer.enrollment_terminal" | "crm.activity_created" | `crm.${CrmSignalWorkflowEvent}` | "notetaker.meeting_completed" | "plain_support.message_received";
|
|
6
|
+
export type WorkspaceEventProducerDefinition = {
|
|
7
|
+
/** Stable producer identity used by typed internal dispatchers. */
|
|
8
|
+
id: WorkspaceEventProducerId;
|
|
9
|
+
source: string;
|
|
10
|
+
event: string;
|
|
11
|
+
/** Where the durable payload is recovered or deduplicated. */
|
|
12
|
+
delivery: "durable_ledger" | "transactional_outbox" | "direct_durable_write";
|
|
13
|
+
/** Human-readable idempotency contract surfaced to authors and operators. */
|
|
14
|
+
idempotency: string;
|
|
15
|
+
readiness: WorkspaceEventProducerReadinessRequirement;
|
|
16
|
+
payloadFields: readonly string[];
|
|
17
|
+
};
|
|
1
18
|
export type WorkspaceEventCatalogFilter = {
|
|
2
19
|
path: string;
|
|
3
20
|
op: "eq";
|
|
@@ -6,6 +23,7 @@ export type WorkspaceEventCatalogFilter = {
|
|
|
6
23
|
export type WorkspaceEventCatalogEntry = {
|
|
7
24
|
/** Stable catalog identity; independent of the underlying source/event pair. */
|
|
8
25
|
id: string;
|
|
26
|
+
producerId: string;
|
|
9
27
|
source: string;
|
|
10
28
|
event: string;
|
|
11
29
|
group: string;
|
|
@@ -14,7 +32,19 @@ export type WorkspaceEventCatalogEntry = {
|
|
|
14
32
|
filters?: readonly WorkspaceEventCatalogFilter[];
|
|
15
33
|
payloadFields: readonly string[];
|
|
16
34
|
};
|
|
17
|
-
export declare const WORKSPACE_EVENT_CATALOG_VERSION =
|
|
35
|
+
export declare const WORKSPACE_EVENT_CATALOG_VERSION = 2;
|
|
36
|
+
/**
|
|
37
|
+
* Every app-native event producer that is safe to bind to a durable Workflow.
|
|
38
|
+
* Catalog presets below may fan one producer into many filtered choices, but a
|
|
39
|
+
* producer exists exactly once and is the authority for source/event identity.
|
|
40
|
+
*/
|
|
41
|
+
export declare const WORKSPACE_EVENT_PRODUCERS: readonly WorkspaceEventProducerDefinition[];
|
|
42
|
+
export declare function getWorkspaceEventProducer(producerId: string): WorkspaceEventProducerDefinition | undefined;
|
|
43
|
+
export declare function crmSignalWorkflowEventProducerId(event: CrmSignalWorkflowEvent): `crm.${CrmSignalWorkflowEvent}`;
|
|
44
|
+
export declare function findWorkspaceEventProducer(input: {
|
|
45
|
+
source: string;
|
|
46
|
+
event: string;
|
|
47
|
+
}): WorkspaceEventProducerDefinition | undefined;
|
|
18
48
|
/**
|
|
19
49
|
* Every app-native workspace event that has a durable or explicitly callable
|
|
20
50
|
* producer. Integration-specific subscriptions are merged in at request time.
|
|
@@ -2,7 +2,13 @@ import { CRM_ACTIVITY_CREATED_EVENT, CRM_ACTIVITY_EVENT_DEFINITIONS, } from "./c
|
|
|
2
2
|
import { CRM_SIGNAL_WORKFLOW_EVENT_DEFINITIONS, CRM_WORKFLOW_EVENT_SOURCE, } from "./crm-reply-events.js";
|
|
3
3
|
import { SEQUENCE_CONTACT_ACTIVITY_EVENT, SEQUENCE_CRM_EVENT_DEFINITIONS, SEQUENCE_WORKFLOW_EVENT_SOURCE, } from "./sequence-crm-events.js";
|
|
4
4
|
import { SEQUENCE_ENROLLMENT_TERMINAL_EVENT, SEQUENCE_TERMINAL_EVENT_SOURCE, SEQUENCE_TERMINAL_STATUSES, } from "./sequence-terminal-events.js";
|
|
5
|
-
|
|
5
|
+
import { NOTETAKER_MEETING_COMPLETED_EVENT, NOTETAKER_MEETING_COMPLETED_PAYLOAD_FIELDS, NOTETAKER_WORKFLOW_EVENT_SOURCE, } from "./notetaker-events.js";
|
|
6
|
+
import { PLAIN_SUPPORT_MESSAGE_RECEIVED_EVENT, PLAIN_SUPPORT_MESSAGE_RECEIVED_PAYLOAD_FIELDS, PLAIN_SUPPORT_WORKFLOW_EVENT_SOURCE, } from "./plain-support-events.js";
|
|
7
|
+
export const WORKSPACE_EVENT_PRODUCER_READINESS_REQUIREMENTS = [
|
|
8
|
+
"always",
|
|
9
|
+
"notetaker_enabled",
|
|
10
|
+
];
|
|
11
|
+
export const WORKSPACE_EVENT_CATALOG_VERSION = 2;
|
|
6
12
|
const sequenceActivityFields = [
|
|
7
13
|
"event",
|
|
8
14
|
"occurred_at",
|
|
@@ -49,6 +55,78 @@ const crmActivityFields = [
|
|
|
49
55
|
"metadata",
|
|
50
56
|
"links",
|
|
51
57
|
];
|
|
58
|
+
const crmSignalProducerDefinitions = CRM_SIGNAL_WORKFLOW_EVENT_DEFINITIONS.map((definition) => ({
|
|
59
|
+
id: `crm.${definition.event}`,
|
|
60
|
+
source: CRM_WORKFLOW_EVENT_SOURCE,
|
|
61
|
+
event: definition.event,
|
|
62
|
+
delivery: "direct_durable_write",
|
|
63
|
+
idempotency: "Provider or conversation event identity is persisted before workflow fan-out.",
|
|
64
|
+
readiness: "always",
|
|
65
|
+
payloadFields: definition.payloadFields,
|
|
66
|
+
}));
|
|
67
|
+
/**
|
|
68
|
+
* Every app-native event producer that is safe to bind to a durable Workflow.
|
|
69
|
+
* Catalog presets below may fan one producer into many filtered choices, but a
|
|
70
|
+
* producer exists exactly once and is the authority for source/event identity.
|
|
71
|
+
*/
|
|
72
|
+
export const WORKSPACE_EVENT_PRODUCERS = [
|
|
73
|
+
{
|
|
74
|
+
id: "sequencer.contact_activity",
|
|
75
|
+
source: SEQUENCE_WORKFLOW_EVENT_SOURCE,
|
|
76
|
+
event: SEQUENCE_CONTACT_ACTIVITY_EVENT,
|
|
77
|
+
delivery: "durable_ledger",
|
|
78
|
+
idempotency: "The Sequencer activity ledger identity deduplicates repeated bridge scans.",
|
|
79
|
+
readiness: "always",
|
|
80
|
+
payloadFields: sequenceActivityFields,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: "sequencer.enrollment_terminal",
|
|
84
|
+
source: SEQUENCE_TERMINAL_EVENT_SOURCE,
|
|
85
|
+
event: SEQUENCE_ENROLLMENT_TERMINAL_EVENT,
|
|
86
|
+
delivery: "transactional_outbox",
|
|
87
|
+
idempotency: "The append-only terminal outbox and enrollment outcome form one stable event identity.",
|
|
88
|
+
readiness: "always",
|
|
89
|
+
payloadFields: sequenceTerminalFields,
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: "crm.activity_created",
|
|
93
|
+
source: CRM_WORKFLOW_EVENT_SOURCE,
|
|
94
|
+
event: CRM_ACTIVITY_CREATED_EVENT,
|
|
95
|
+
delivery: "durable_ledger",
|
|
96
|
+
idempotency: "The CRM activity id is the durable workflow-event identity.",
|
|
97
|
+
readiness: "always",
|
|
98
|
+
payloadFields: crmActivityFields,
|
|
99
|
+
},
|
|
100
|
+
...crmSignalProducerDefinitions,
|
|
101
|
+
{
|
|
102
|
+
id: "notetaker.meeting_completed",
|
|
103
|
+
source: NOTETAKER_WORKFLOW_EVENT_SOURCE,
|
|
104
|
+
event: NOTETAKER_MEETING_COMPLETED_EVENT,
|
|
105
|
+
delivery: "direct_durable_write",
|
|
106
|
+
idempotency: "The Notetaker session and provider completion identities deduplicate completion delivery.",
|
|
107
|
+
readiness: "notetaker_enabled",
|
|
108
|
+
payloadFields: NOTETAKER_MEETING_COMPLETED_PAYLOAD_FIELDS,
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
id: "plain_support.message_received",
|
|
112
|
+
source: PLAIN_SUPPORT_WORKFLOW_EVENT_SOURCE,
|
|
113
|
+
event: PLAIN_SUPPORT_MESSAGE_RECEIVED_EVENT,
|
|
114
|
+
delivery: "direct_durable_write",
|
|
115
|
+
idempotency: "The verified Plain event id deduplicates metadata-only support delivery.",
|
|
116
|
+
readiness: "always",
|
|
117
|
+
payloadFields: PLAIN_SUPPORT_MESSAGE_RECEIVED_PAYLOAD_FIELDS,
|
|
118
|
+
},
|
|
119
|
+
];
|
|
120
|
+
const workspaceEventProducersById = new Map(WORKSPACE_EVENT_PRODUCERS.map((producer) => [producer.id, producer]));
|
|
121
|
+
export function getWorkspaceEventProducer(producerId) {
|
|
122
|
+
return workspaceEventProducersById.get(producerId);
|
|
123
|
+
}
|
|
124
|
+
export function crmSignalWorkflowEventProducerId(event) {
|
|
125
|
+
return `crm.${event}`;
|
|
126
|
+
}
|
|
127
|
+
export function findWorkspaceEventProducer(input) {
|
|
128
|
+
return WORKSPACE_EVENT_PRODUCERS.find((producer) => producer.source === input.source && producer.event === input.event);
|
|
129
|
+
}
|
|
52
130
|
function sequenceActivityDescription(key, label) {
|
|
53
131
|
if (key === "email_bounced") {
|
|
54
132
|
return "An outreach sequence enrollment reported an email bounce. Use the CRM timeline preset instead for any bounce activity logged on a CRM record.";
|
|
@@ -70,6 +148,7 @@ function crmActivityDescription(key, label) {
|
|
|
70
148
|
const sequenceActivityEntries = [
|
|
71
149
|
{
|
|
72
150
|
id: "sequencer.contact_activity.any",
|
|
151
|
+
producerId: "sequencer.contact_activity",
|
|
73
152
|
source: SEQUENCE_WORKFLOW_EVENT_SOURCE,
|
|
74
153
|
event: SEQUENCE_CONTACT_ACTIVITY_EVENT,
|
|
75
154
|
group: "Sequencer · All activity",
|
|
@@ -79,6 +158,7 @@ const sequenceActivityEntries = [
|
|
|
79
158
|
},
|
|
80
159
|
...SEQUENCE_CRM_EVENT_DEFINITIONS.map((definition) => ({
|
|
81
160
|
id: `sequencer.contact_activity.${definition.key}`,
|
|
161
|
+
producerId: "sequencer.contact_activity",
|
|
82
162
|
source: SEQUENCE_WORKFLOW_EVENT_SOURCE,
|
|
83
163
|
event: SEQUENCE_CONTACT_ACTIVITY_EVENT,
|
|
84
164
|
group: `Sequencer · ${definition.group}`,
|
|
@@ -91,6 +171,7 @@ const sequenceActivityEntries = [
|
|
|
91
171
|
const sequenceTerminalEntries = [
|
|
92
172
|
{
|
|
93
173
|
id: "sequencer.enrollment_terminal.any",
|
|
174
|
+
producerId: "sequencer.enrollment_terminal",
|
|
94
175
|
source: SEQUENCE_TERMINAL_EVENT_SOURCE,
|
|
95
176
|
event: SEQUENCE_ENROLLMENT_TERMINAL_EVENT,
|
|
96
177
|
group: "Sequencer · Journey status",
|
|
@@ -100,6 +181,7 @@ const sequenceTerminalEntries = [
|
|
|
100
181
|
},
|
|
101
182
|
...SEQUENCE_TERMINAL_STATUSES.map((status) => ({
|
|
102
183
|
id: `sequencer.enrollment_terminal.${status}`,
|
|
184
|
+
producerId: "sequencer.enrollment_terminal",
|
|
103
185
|
source: SEQUENCE_TERMINAL_EVENT_SOURCE,
|
|
104
186
|
event: SEQUENCE_ENROLLMENT_TERMINAL_EVENT,
|
|
105
187
|
group: "Sequencer · Journey status",
|
|
@@ -112,6 +194,7 @@ const sequenceTerminalEntries = [
|
|
|
112
194
|
const crmActivityEntries = [
|
|
113
195
|
{
|
|
114
196
|
id: "crm.activity_created.any",
|
|
197
|
+
producerId: "crm.activity_created",
|
|
115
198
|
source: CRM_WORKFLOW_EVENT_SOURCE,
|
|
116
199
|
event: CRM_ACTIVITY_CREATED_EVENT,
|
|
117
200
|
group: "CRM · All timeline activity",
|
|
@@ -121,6 +204,7 @@ const crmActivityEntries = [
|
|
|
121
204
|
},
|
|
122
205
|
...CRM_ACTIVITY_EVENT_DEFINITIONS.map((definition) => ({
|
|
123
206
|
id: `crm.activity_created.${definition.key}`,
|
|
207
|
+
producerId: "crm.activity_created",
|
|
124
208
|
source: CRM_WORKFLOW_EVENT_SOURCE,
|
|
125
209
|
event: CRM_ACTIVITY_CREATED_EVENT,
|
|
126
210
|
group: `CRM · ${definition.group}`,
|
|
@@ -132,6 +216,7 @@ const crmActivityEntries = [
|
|
|
132
216
|
];
|
|
133
217
|
const crmSignalEntries = CRM_SIGNAL_WORKFLOW_EVENT_DEFINITIONS.map((definition) => ({
|
|
134
218
|
id: `crm.${definition.event}`,
|
|
219
|
+
producerId: `crm.${definition.event}`,
|
|
135
220
|
source: CRM_WORKFLOW_EVENT_SOURCE,
|
|
136
221
|
event: definition.event,
|
|
137
222
|
group: `CRM · ${definition.group}`,
|
|
@@ -139,6 +224,30 @@ const crmSignalEntries = CRM_SIGNAL_WORKFLOW_EVENT_DEFINITIONS.map((definition)
|
|
|
139
224
|
description: definition.description,
|
|
140
225
|
payloadFields: definition.payloadFields,
|
|
141
226
|
}));
|
|
227
|
+
const plainSupportEntries = [
|
|
228
|
+
{
|
|
229
|
+
id: "plain_support.message_received",
|
|
230
|
+
producerId: "plain_support.message_received",
|
|
231
|
+
source: PLAIN_SUPPORT_WORKFLOW_EVENT_SOURCE,
|
|
232
|
+
event: PLAIN_SUPPORT_MESSAGE_RECEIVED_EVENT,
|
|
233
|
+
group: "Support · Plain",
|
|
234
|
+
label: "Customer message received",
|
|
235
|
+
description: "A customer sent a Chat, email, Slack, Microsoft Teams, or Discord message into a canonical Plain Thread. The event contains routing metadata only; customer content remains in Plain.",
|
|
236
|
+
payloadFields: PLAIN_SUPPORT_MESSAGE_RECEIVED_PAYLOAD_FIELDS,
|
|
237
|
+
},
|
|
238
|
+
];
|
|
239
|
+
const notetakerEntries = [
|
|
240
|
+
{
|
|
241
|
+
id: "notetaker.meeting_completed",
|
|
242
|
+
producerId: "notetaker.meeting_completed",
|
|
243
|
+
source: NOTETAKER_WORKFLOW_EVENT_SOURCE,
|
|
244
|
+
event: NOTETAKER_MEETING_COMPLETED_EVENT,
|
|
245
|
+
group: "Meeting Notetaker",
|
|
246
|
+
label: "Meeting completed",
|
|
247
|
+
description: "A Meeting Notetaker session finished processing and its transcript and meeting note are ready.",
|
|
248
|
+
payloadFields: NOTETAKER_MEETING_COMPLETED_PAYLOAD_FIELDS,
|
|
249
|
+
},
|
|
250
|
+
];
|
|
142
251
|
/**
|
|
143
252
|
* Every app-native workspace event that has a durable or explicitly callable
|
|
144
253
|
* producer. Integration-specific subscriptions are merged in at request time.
|
|
@@ -148,4 +257,6 @@ export const WORKSPACE_EVENT_CATALOG = [
|
|
|
148
257
|
...sequenceTerminalEntries,
|
|
149
258
|
...crmActivityEntries,
|
|
150
259
|
...crmSignalEntries,
|
|
260
|
+
...plainSupportEntries,
|
|
261
|
+
...notetakerEntries,
|
|
151
262
|
];
|
|
@@ -279,11 +279,13 @@ function resolveTokensDeep(value, scope, depth = 0) {
|
|
|
279
279
|
return value.map((entry) => resolveTokensDeep(entry, scope, depth + 1));
|
|
280
280
|
}
|
|
281
281
|
if (isStructured(value)) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
282
|
+
// Object.fromEntries defines own data properties even for `__proto__`.
|
|
283
|
+
// Assigning onto `{}` would mutate the clone's prototype and silently lose
|
|
284
|
+
// a valid JSON key before strict Code-input serialization can inspect it.
|
|
285
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
|
|
286
|
+
key,
|
|
287
|
+
resolveTokensDeep(entry, scope, depth + 1),
|
|
288
|
+
]));
|
|
287
289
|
}
|
|
288
290
|
return value;
|
|
289
291
|
}
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import { type WorkflowLintResult, type WorkflowManifestValidationOptions } from "../index.js";
|
|
2
|
+
import type { WorkflowCodeV1 } from "./types.js";
|
|
2
3
|
import { type WorkflowGraphManifest } from "./types.js";
|
|
3
4
|
export declare function lintWorkflowGraphManifest(value: unknown, options?: WorkflowManifestValidationOptions): WorkflowLintResult;
|
|
4
5
|
export declare function assertWorkflowGraphManifest(value: unknown, options?: WorkflowManifestValidationOptions): asserts value is WorkflowGraphManifest;
|
|
6
|
+
/**
|
|
7
|
+
* Validate one nested oxygen-js-v1 envelope without requiring its surrounding
|
|
8
|
+
* graph. Runtime entry points call this again immediately before sandbox
|
|
9
|
+
* creation; ref syntax is checked here, while graph node reachability remains a
|
|
10
|
+
* manifest-lint concern.
|
|
11
|
+
*/
|
|
12
|
+
export declare function lintWorkflowCodeV1Contract(value: unknown): WorkflowLintResult;
|
|
13
|
+
export declare function assertWorkflowCodeV1Contract(value: unknown): asserts value is WorkflowCodeV1;
|