@oxygen-agent/cli 1.1014.1138 → 1.1014.1221
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 +2 -0
- package/dist/command-support.d.ts +8 -0
- package/dist/command-support.js +11 -0
- package/dist/commands/api-keys.d.ts +9 -0
- package/dist/commands/api-keys.js +4 -1
- package/dist/commands/capabilities.js +4 -2
- package/dist/commands/crm.js +28 -2
- package/dist/commands/dashboards.js +2 -2
- package/dist/commands/db.js +34 -0
- package/dist/commands/find.js +1 -1
- package/dist/commands/knowledge.js +2 -0
- package/dist/commands/mailboxes.js +3 -2
- package/dist/commands/messages.js +6 -1
- package/dist/commands/senders.js +2 -0
- package/dist/commands/sequences.js +1 -1
- package/dist/commands/signals.js +11 -2
- package/dist/deliverability-evidence-commands.d.ts +4 -0
- package/dist/deliverability-evidence-commands.js +104 -0
- package/dist/diagnostic-context.d.ts +21 -0
- package/dist/diagnostic-context.js +25 -0
- package/dist/diagnostics.d.ts +1 -3
- package/dist/diagnostics.js +12 -30
- package/dist/http-client.js +1 -1
- package/dist/network-mining-host.d.ts +6 -0
- package/dist/network-mining-host.js +10 -0
- package/node_modules/@oxygen/formula/dist/value-normalizers.d.ts +1 -1
- package/node_modules/@oxygen/formula/dist/value-normalizers.js +5 -0
- package/node_modules/@oxygen/shared/dist/agent-sessions.d.ts +169 -0
- package/node_modules/@oxygen/shared/dist/agent-sessions.js +17 -0
- package/node_modules/@oxygen/shared/dist/capability-discovery.d.ts +8 -0
- package/node_modules/@oxygen/shared/dist/capability-discovery.js +114 -5
- package/node_modules/@oxygen/shared/dist/contracts/customer-operations.js +1 -1
- package/node_modules/@oxygen/shared/dist/contracts/operation-request.d.ts +92 -0
- package/node_modules/@oxygen/shared/dist/contracts/operation-request.js +36 -0
- package/node_modules/@oxygen/shared/dist/contracts/operations.d.ts +3 -90
- package/node_modules/@oxygen/shared/dist/contracts/operations.js +5 -39
- package/node_modules/@oxygen/shared/dist/contracts/reply-oxygent.d.ts +2 -0
- package/node_modules/@oxygen/shared/dist/contracts/reply-oxygent.js +123 -0
- package/node_modules/@oxygen/shared/dist/contracts/teammates.js +1 -0
- package/node_modules/@oxygen/shared/dist/crm-activity-events.d.ts +4 -0
- package/node_modules/@oxygen/shared/dist/crm-activity-events.js +1 -0
- package/node_modules/@oxygen/shared/dist/crm-record-brief.d.ts +10 -0
- package/node_modules/@oxygen/shared/dist/crm-record-brief.js +22 -0
- package/node_modules/@oxygen/shared/dist/deliverability-blacklist.d.ts +74 -0
- package/node_modules/@oxygen/shared/dist/deliverability-blacklist.js +118 -0
- package/node_modules/@oxygen/shared/dist/deliverability-checks.d.ts +138 -0
- package/node_modules/@oxygen/shared/dist/deliverability-checks.js +124 -0
- package/node_modules/@oxygen/shared/dist/deliverability-evidence.d.ts +366 -0
- package/node_modules/@oxygen/shared/dist/deliverability-evidence.js +169 -0
- package/node_modules/@oxygen/shared/dist/deliverability-loop.d.ts +334 -0
- package/node_modules/@oxygen/shared/dist/deliverability-loop.js +312 -0
- package/node_modules/@oxygen/shared/dist/deliverability-surface.d.ts +42 -0
- package/node_modules/@oxygen/shared/dist/deliverability-surface.js +66 -0
- package/node_modules/@oxygen/shared/dist/diagnostic-spool.js +34 -4
- package/node_modules/@oxygen/shared/dist/domain-authentication.d.ts +24 -0
- package/node_modules/@oxygen/shared/dist/domain-authentication.js +20 -0
- package/node_modules/@oxygen/shared/dist/file-import.js +11 -3
- package/node_modules/@oxygen/shared/dist/linkedin-connection-identity.d.ts +16 -0
- package/node_modules/@oxygen/shared/dist/linkedin-connection-identity.js +30 -2
- package/node_modules/@oxygen/shared/dist/network-mining.d.ts +5 -0
- package/node_modules/@oxygen/shared/dist/network-mining.js +9 -0
- package/node_modules/@oxygen/shared/dist/received-authentication.d.ts +74 -0
- package/node_modules/@oxygen/shared/dist/received-authentication.js +452 -0
- package/node_modules/@oxygen/shared/dist/recipient-provider.d.ts +21 -0
- package/node_modules/@oxygen/shared/dist/recipient-provider.js +33 -0
- package/node_modules/@oxygen/shared/dist/reply-oxygent.d.ts +73 -0
- package/node_modules/@oxygen/shared/dist/reply-oxygent.js +31 -0
- package/node_modules/@oxygen/shared/dist/repricing.d.ts +10 -1
- package/node_modules/@oxygen/shared/dist/repricing.js +16 -4
- package/node_modules/@oxygen/shared/dist/research-findings.d.ts +80 -0
- package/node_modules/@oxygen/shared/dist/research-findings.js +199 -0
- package/node_modules/@oxygen/shared/dist/sequence-internals/esp-policy.d.ts +2 -0
- package/node_modules/@oxygen/shared/dist/sequence-internals/esp-policy.js +4 -0
- package/node_modules/@oxygen/shared/dist/sequences.d.ts +6 -0
- package/node_modules/@oxygen/shared/dist/sequences.js +8 -0
- package/node_modules/@oxygen/shared/dist/user-capability-routing.js +20 -3
- 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-event-catalog.d.ts +8 -2
- package/node_modules/@oxygen/shared/dist/workspace-event-catalog.js +63 -0
- package/package.json +1 -1
package/dist/diagnostics.js
CHANGED
|
@@ -7,10 +7,11 @@ import { fileURLToPath } from "node:url";
|
|
|
7
7
|
import { acquireDiagnosticLock } from "@oxygen/shared/diagnostic-lock";
|
|
8
8
|
import { StringDecoder } from "node:string_decoder";
|
|
9
9
|
import { gzipSync } from "node:zlib";
|
|
10
|
-
import { createDiagnosticRecord, credentialSafeDiagnostic, diagnosticId,
|
|
10
|
+
import { createDiagnosticRecord, credentialSafeDiagnostic, diagnosticId, DIAGNOSTIC_MAX_BYTES, DIAGNOSTIC_RECORD_MAX_BYTES, diagnosticOutcome, sanitizeDiagnosticArgv, sanitizeDiagnosticContent } from "@oxygen/shared/diagnostics";
|
|
11
11
|
import { OXYGEN_VERSION } from "@oxygen/shared/version";
|
|
12
12
|
import { loadCredentialsWithEndpoint } from "./credentials.js";
|
|
13
|
-
|
|
13
|
+
import { currentCliDiagnosticContext, recordCliDiagnosticRequest, setCliDiagnosticContext } from "./diagnostic-context.js";
|
|
14
|
+
export { cliDiagnosticHeaders, cliDiagnosticInvocation, recordCliDiagnosticRequest } from "./diagnostic-context.js";
|
|
14
15
|
export function cliDiagnosticDirectory() {
|
|
15
16
|
return join(process.env.OXYGEN_CONFIG_DIR?.trim() || (process.platform === "win32"
|
|
16
17
|
? join(process.env.APPDATA || join(homedir(), "AppData", "Roaming"), "oxygen")
|
|
@@ -20,7 +21,7 @@ export function credentialFingerprint(credentials) {
|
|
|
20
21
|
return createHash("sha256").update(credentials.token).digest("hex");
|
|
21
22
|
}
|
|
22
23
|
export function beginCliDiagnostics(argv = process.argv.slice(2)) {
|
|
23
|
-
if (
|
|
24
|
+
if (currentCliDiagnosticContext() || process.env.OXYGEN_DIAGNOSTICS_ENABLED !== "1")
|
|
24
25
|
return;
|
|
25
26
|
const invocation = createDiagnosticRecord({ surface: "cli", operation: "cli parse",
|
|
26
27
|
client_version: OXYGEN_VERSION, input: { argv: sanitizeDiagnosticArgv(argv) },
|
|
@@ -32,7 +33,7 @@ export function beginCliDiagnostics(argv = process.argv.slice(2)) {
|
|
|
32
33
|
const context = { record: invocation, credentialEnv, scope: loadCredentialsWithEndpoint(credentialEnv).catch(() => null),
|
|
33
34
|
organizationRef: credentialEnv.OXYGEN_ORG?.trim() || null, signalHandlers: [],
|
|
34
35
|
requests: [], bytes: 0, droppedBytes: 0, stdout: [], stderr: [], restore: () => undefined };
|
|
35
|
-
|
|
36
|
+
setCliDiagnosticContext(context);
|
|
36
37
|
const restorers = [];
|
|
37
38
|
for (const [stream, output] of [[process.stdout, context.stdout], [process.stderr, context.stderr]]) {
|
|
38
39
|
const original = stream.write;
|
|
@@ -68,46 +69,27 @@ export function beginCliDiagnostics(argv = process.argv.slice(2)) {
|
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
export function setCliDiagnosticOperation(command) {
|
|
72
|
+
const active = currentCliDiagnosticContext();
|
|
71
73
|
if (active)
|
|
72
74
|
active.record.operation = command.slice(0, 512);
|
|
73
75
|
}
|
|
74
|
-
export function cliDiagnosticInvocation() { return active?.record.invocation_id; }
|
|
75
|
-
export function cliDiagnosticHeaders() {
|
|
76
|
-
return active ? { [DIAGNOSTIC_INVOCATION_HEADER]: active.record.invocation_id, traceparent: active.record.traceparent } : {};
|
|
77
|
-
}
|
|
78
|
-
export function recordCliDiagnosticRequest(input) {
|
|
79
|
-
if (!active)
|
|
80
|
-
return;
|
|
81
|
-
try {
|
|
82
|
-
const sanitized = sanitizeDiagnosticContent(input);
|
|
83
|
-
const size = Buffer.byteLength(JSON.stringify(sanitized));
|
|
84
|
-
if (active.bytes + size <= DIAGNOSTIC_RECORD_MAX_BYTES) {
|
|
85
|
-
active.requests.push(sanitized);
|
|
86
|
-
active.bytes += size;
|
|
87
|
-
}
|
|
88
|
-
else
|
|
89
|
-
active.droppedBytes += size;
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
active.droppedBytes++;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
76
|
export function recordCliDiagnosticFailure(error) {
|
|
96
|
-
if (!
|
|
77
|
+
if (!currentCliDiagnosticContext())
|
|
97
78
|
return;
|
|
79
|
+
// An error getter can finish or replace the invocation before these writes.
|
|
98
80
|
const code = error && typeof error === "object" && "code" in error ? String(error.code) : "unexpected_error";
|
|
99
|
-
|
|
100
|
-
|
|
81
|
+
currentCliDiagnosticContext().record.error_code = code;
|
|
82
|
+
currentCliDiagnosticContext().record.outcome = diagnosticOutcome(false, null, code);
|
|
101
83
|
recordCliDiagnosticRequest({ error });
|
|
102
84
|
}
|
|
103
85
|
export async function finishCliDiagnostics(exitCode) {
|
|
104
|
-
const context =
|
|
86
|
+
const context = currentCliDiagnosticContext();
|
|
105
87
|
if (!context)
|
|
106
88
|
return null;
|
|
107
89
|
context.restore();
|
|
108
90
|
for (const remove of context.signalHandlers)
|
|
109
91
|
remove();
|
|
110
|
-
|
|
92
|
+
setCliDiagnosticContext(undefined);
|
|
111
93
|
try {
|
|
112
94
|
const loaded = await context.scope;
|
|
113
95
|
const credentials = loaded?.credentials;
|
package/dist/http-client.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readGeoExportPagination } from "@oxygen/shared/geo";
|
|
2
2
|
import { OXYGEN_VERSION, OxygenError, isCliResult, isVersionGreater, parseSemver, readEnvelopeCompatibility, vercelProtectionBypassHeaders, withRetryAfterDetails, } from "@oxygen/shared";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
|
-
import { cliDiagnosticHeaders, cliDiagnosticInvocation, recordCliDiagnosticRequest } from "./
|
|
4
|
+
import { cliDiagnosticHeaders, cliDiagnosticInvocation, recordCliDiagnosticRequest } from "./diagnostic-context.js";
|
|
5
5
|
import { readBoundedBinaryResponse, streamBinaryResponse } from "./refactor-internals/binary-download.js";
|
|
6
6
|
import { recordMutatingRequestAnswered, recoverFromCliFloorRejection } from "./auto-update.js";
|
|
7
7
|
import { defaultApiUrl, loadCredentialsWithEndpoint } from "./credentials.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Network Mining is DEV-only and its server answers feature_disabled elsewhere.
|
|
3
|
+
* The CLI cannot read the server's gate, so it shows Network Mining commands
|
|
4
|
+
* (and routes discovery to them) only when its API host is not production.
|
|
5
|
+
*/
|
|
6
|
+
export declare function networkMiningHostServed(env?: NodeJS.ProcessEnv): boolean;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { defaultApiUrl } from "./credentials.js";
|
|
2
|
+
import { isProdApiUrl } from "./runtime.js";
|
|
3
|
+
/**
|
|
4
|
+
* Network Mining is DEV-only and its server answers feature_disabled elsewhere.
|
|
5
|
+
* The CLI cannot read the server's gate, so it shows Network Mining commands
|
|
6
|
+
* (and routes discovery to them) only when its API host is not production.
|
|
7
|
+
*/
|
|
8
|
+
export function networkMiningHostServed(env = process.env) {
|
|
9
|
+
return !isProdApiUrl(defaultApiUrl(env));
|
|
10
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* "does foo@Bar.com match FOO@bar.com" answers the same everywhere — including
|
|
6
6
|
* in the browser. Pure string functions — no I/O, no tenant context.
|
|
7
7
|
*/
|
|
8
|
-
export declare const VALUE_NORMALIZATIONS: readonly ["exact_text_v1", "lower_trim_v1", "email_v1", "domain_v1", "linkedin_url_v1", "uuid_v1"];
|
|
8
|
+
export declare const VALUE_NORMALIZATIONS: readonly ["exact_text_v1", "lower_trim_v1", "email_v1", "domain_v1", "linkedin_url_v1", "linkedin_member_id_v1", "uuid_v1"];
|
|
9
9
|
export type ValueNormalization = (typeof VALUE_NORMALIZATIONS)[number];
|
|
10
10
|
export declare function isValueNormalization(value: unknown): value is ValueNormalization;
|
|
11
11
|
/** `email_v1`: case-insensitive mailbox match. */
|
|
@@ -5,12 +5,14 @@
|
|
|
5
5
|
* "does foo@Bar.com match FOO@bar.com" answers the same everywhere — including
|
|
6
6
|
* in the browser. Pure string functions — no I/O, no tenant context.
|
|
7
7
|
*/
|
|
8
|
+
import { normalizeLinkedinMemberId } from "@oxygen/shared/linkedin-connection-identity";
|
|
8
9
|
export const VALUE_NORMALIZATIONS = [
|
|
9
10
|
"exact_text_v1",
|
|
10
11
|
"lower_trim_v1",
|
|
11
12
|
"email_v1",
|
|
12
13
|
"domain_v1",
|
|
13
14
|
"linkedin_url_v1",
|
|
15
|
+
"linkedin_member_id_v1",
|
|
14
16
|
"uuid_v1",
|
|
15
17
|
];
|
|
16
18
|
export function isValueNormalization(value) {
|
|
@@ -69,6 +71,9 @@ export function normalizeValue(normalization, value) {
|
|
|
69
71
|
return normalizeDomain(value);
|
|
70
72
|
case "linkedin_url_v1":
|
|
71
73
|
return normalizeLinkedinUrl(value);
|
|
74
|
+
case "linkedin_member_id_v1":
|
|
75
|
+
// Case-preserving and provider-scoped: see normalizeLinkedinMemberId.
|
|
76
|
+
return normalizeLinkedinMemberId(value);
|
|
72
77
|
case "uuid_v1":
|
|
73
78
|
return normalizeUuid(value);
|
|
74
79
|
case "lower_trim_v1":
|
|
@@ -133,4 +133,173 @@ export declare const AGENT_SESSION_TEMPLATES: readonly [{
|
|
|
133
133
|
readonly actions: readonly ["oxygen_crm_notes_add"];
|
|
134
134
|
readonly events: readonly [];
|
|
135
135
|
}];
|
|
136
|
+
export declare function agentSessionTemplates(env?: Record<string, string | undefined>): ({
|
|
137
|
+
readonly id: "campaign-monitor";
|
|
138
|
+
readonly title: "Campaign monitor";
|
|
139
|
+
readonly goal: "Watch this native campaign. Compare sends, replies and meetings with earlier reviews; explain changes, propose improvements and schedule the next review.";
|
|
140
|
+
readonly resources: readonly ["sequences"];
|
|
141
|
+
readonly actions: readonly ["oxygen_sequences_set_status"];
|
|
142
|
+
readonly events: readonly ["sequencer.contact_activity.any"];
|
|
143
|
+
} | {
|
|
144
|
+
readonly id: "lead-replenishment";
|
|
145
|
+
readonly title: "Lead replenishment";
|
|
146
|
+
readonly goal: "Maintain a qualified lead pool in the selected Table for this native campaign. Research gaps, deduplicate and enroll only qualified leads within the approved scope.";
|
|
147
|
+
readonly resources: readonly ["tables", "sequences"];
|
|
148
|
+
readonly actions: readonly ["oxygen_rows_upsert", "oxygen_sequences_enroll"];
|
|
149
|
+
readonly events: readonly [];
|
|
150
|
+
} | {
|
|
151
|
+
readonly id: "reply-handling";
|
|
152
|
+
readonly title: "Reply handling";
|
|
153
|
+
readonly goal: "Review the selected conversations, remember prior responses, qualify intent and draft replies. Send only when explicitly authorized; hand off uncertain or sensitive replies.";
|
|
154
|
+
readonly resources: readonly ["conversations"];
|
|
155
|
+
readonly actions: readonly ["oxygen_inbox_set_status"];
|
|
156
|
+
readonly events: readonly ["crm.email_reply_classified"];
|
|
157
|
+
} | {
|
|
158
|
+
readonly id: "deliverability";
|
|
159
|
+
readonly title: "Deliverability";
|
|
160
|
+
readonly goal: "Watch the selected mailboxes for sending health changes. Explain evidence and recommend or apply only approved cap and status adjustments.";
|
|
161
|
+
readonly resources: readonly ["mailboxes"];
|
|
162
|
+
readonly actions: readonly ["oxygen_mailboxes_set_cap", "oxygen_mailboxes_set_status"];
|
|
163
|
+
readonly events: readonly [];
|
|
164
|
+
} | {
|
|
165
|
+
readonly id: "account-research";
|
|
166
|
+
readonly title: "Account research";
|
|
167
|
+
readonly goal: "Maintain cited account research for the selected records and knowledge pages. Track changes and recommend next actions; keep facts separate from hypotheses.";
|
|
168
|
+
readonly resources: readonly ["records", "knowledge"];
|
|
169
|
+
readonly actions: readonly ["oxygen_crm_notes_add", "oxygen_knowledge_page_upsert"];
|
|
170
|
+
readonly events: readonly [];
|
|
171
|
+
} | {
|
|
172
|
+
readonly id: "inbound-qualification";
|
|
173
|
+
readonly title: "Inbound qualification";
|
|
174
|
+
readonly goal: "Qualify the selected inbound conversations and records against workspace ICP and product knowledge. Record evidence and recommendations; hand off high intent to a person.";
|
|
175
|
+
readonly resources: readonly ["conversations", "records"];
|
|
176
|
+
readonly actions: readonly ["oxygen_inbox_set_status", "oxygen_crm_notes_add"];
|
|
177
|
+
readonly events: readonly ["crm.email_reply_classified"];
|
|
178
|
+
} | {
|
|
179
|
+
readonly id: "crm-hygiene";
|
|
180
|
+
readonly title: "CRM hygiene";
|
|
181
|
+
readonly goal: "Review the selected CRM objects for duplicates and missing or inconsistent fields. Propose repairs with evidence, and apply only explicitly authorized changes.";
|
|
182
|
+
readonly resources: readonly ["objects"];
|
|
183
|
+
readonly actions: readonly ["oxygen_crm_merge"];
|
|
184
|
+
readonly events: readonly [];
|
|
185
|
+
} | {
|
|
186
|
+
readonly id: "pipeline-review";
|
|
187
|
+
readonly title: "Pipeline review";
|
|
188
|
+
readonly goal: "Review the selected pipeline and lifecycle stages. Remember earlier reviews, identify stalled opportunities and recommend the next action with evidence.";
|
|
189
|
+
readonly resources: readonly ["objects", "records"];
|
|
190
|
+
readonly actions: readonly ["oxygen_crm_notes_add"];
|
|
191
|
+
readonly events: readonly [];
|
|
192
|
+
} | {
|
|
193
|
+
id: "campaign-monitor";
|
|
194
|
+
title: "Campaign monitor";
|
|
195
|
+
resources: readonly ["sequences"];
|
|
196
|
+
actions: readonly ["oxygen_sequences_set_status"];
|
|
197
|
+
events: readonly ["sequencer.contact_activity.any"];
|
|
198
|
+
goal: string;
|
|
199
|
+
} | {
|
|
200
|
+
id: "lead-replenishment";
|
|
201
|
+
title: "Lead replenishment";
|
|
202
|
+
resources: readonly ["tables", "sequences"];
|
|
203
|
+
actions: readonly ["oxygen_rows_upsert", "oxygen_sequences_enroll"];
|
|
204
|
+
events: readonly [];
|
|
205
|
+
goal: string;
|
|
206
|
+
} | {
|
|
207
|
+
id: "reply-handling";
|
|
208
|
+
title: "Reply handling";
|
|
209
|
+
resources: readonly ["conversations"];
|
|
210
|
+
actions: readonly ["oxygen_inbox_set_status"];
|
|
211
|
+
events: readonly ["crm.email_reply_classified"];
|
|
212
|
+
goal: string;
|
|
213
|
+
} | {
|
|
214
|
+
id: "deliverability";
|
|
215
|
+
title: "Deliverability";
|
|
216
|
+
resources: readonly ["mailboxes"];
|
|
217
|
+
actions: readonly ["oxygen_mailboxes_set_cap", "oxygen_mailboxes_set_status"];
|
|
218
|
+
events: readonly [];
|
|
219
|
+
goal: string;
|
|
220
|
+
} | {
|
|
221
|
+
id: "account-research";
|
|
222
|
+
title: "Account research";
|
|
223
|
+
resources: readonly ["records", "knowledge"];
|
|
224
|
+
actions: readonly ["oxygen_crm_notes_add", "oxygen_knowledge_page_upsert"];
|
|
225
|
+
events: readonly [];
|
|
226
|
+
goal: string;
|
|
227
|
+
} | {
|
|
228
|
+
id: "inbound-qualification";
|
|
229
|
+
title: "Inbound qualification";
|
|
230
|
+
resources: readonly ["conversations", "records"];
|
|
231
|
+
actions: readonly ["oxygen_inbox_set_status", "oxygen_crm_notes_add"];
|
|
232
|
+
events: readonly ["crm.email_reply_classified"];
|
|
233
|
+
goal: string;
|
|
234
|
+
} | {
|
|
235
|
+
id: "crm-hygiene";
|
|
236
|
+
title: "CRM hygiene";
|
|
237
|
+
resources: readonly ["objects"];
|
|
238
|
+
actions: readonly ["oxygen_crm_merge"];
|
|
239
|
+
events: readonly [];
|
|
240
|
+
goal: string;
|
|
241
|
+
} | {
|
|
242
|
+
id: "pipeline-review";
|
|
243
|
+
title: "Pipeline review";
|
|
244
|
+
resources: readonly ["objects", "records"];
|
|
245
|
+
actions: readonly ["oxygen_crm_notes_add"];
|
|
246
|
+
events: readonly [];
|
|
247
|
+
goal: string;
|
|
248
|
+
})[] | readonly [{
|
|
249
|
+
readonly id: "campaign-monitor";
|
|
250
|
+
readonly title: "Campaign monitor";
|
|
251
|
+
readonly goal: "Watch this native campaign. Compare sends, replies and meetings with earlier reviews; explain changes, propose improvements and schedule the next review.";
|
|
252
|
+
readonly resources: readonly ["sequences"];
|
|
253
|
+
readonly actions: readonly ["oxygen_sequences_set_status"];
|
|
254
|
+
readonly events: readonly ["sequencer.contact_activity.any"];
|
|
255
|
+
}, {
|
|
256
|
+
readonly id: "lead-replenishment";
|
|
257
|
+
readonly title: "Lead replenishment";
|
|
258
|
+
readonly goal: "Maintain a qualified lead pool in the selected Table for this native campaign. Research gaps, deduplicate and enroll only qualified leads within the approved scope.";
|
|
259
|
+
readonly resources: readonly ["tables", "sequences"];
|
|
260
|
+
readonly actions: readonly ["oxygen_rows_upsert", "oxygen_sequences_enroll"];
|
|
261
|
+
readonly events: readonly [];
|
|
262
|
+
}, {
|
|
263
|
+
readonly id: "reply-handling";
|
|
264
|
+
readonly title: "Reply handling";
|
|
265
|
+
readonly goal: "Review the selected conversations, remember prior responses, qualify intent and draft replies. Send only when explicitly authorized; hand off uncertain or sensitive replies.";
|
|
266
|
+
readonly resources: readonly ["conversations"];
|
|
267
|
+
readonly actions: readonly ["oxygen_inbox_set_status"];
|
|
268
|
+
readonly events: readonly ["crm.email_reply_classified"];
|
|
269
|
+
}, {
|
|
270
|
+
readonly id: "deliverability";
|
|
271
|
+
readonly title: "Deliverability";
|
|
272
|
+
readonly goal: "Watch the selected mailboxes for sending health changes. Explain evidence and recommend or apply only approved cap and status adjustments.";
|
|
273
|
+
readonly resources: readonly ["mailboxes"];
|
|
274
|
+
readonly actions: readonly ["oxygen_mailboxes_set_cap", "oxygen_mailboxes_set_status"];
|
|
275
|
+
readonly events: readonly [];
|
|
276
|
+
}, {
|
|
277
|
+
readonly id: "account-research";
|
|
278
|
+
readonly title: "Account research";
|
|
279
|
+
readonly goal: "Maintain cited account research for the selected records and knowledge pages. Track changes and recommend next actions; keep facts separate from hypotheses.";
|
|
280
|
+
readonly resources: readonly ["records", "knowledge"];
|
|
281
|
+
readonly actions: readonly ["oxygen_crm_notes_add", "oxygen_knowledge_page_upsert"];
|
|
282
|
+
readonly events: readonly [];
|
|
283
|
+
}, {
|
|
284
|
+
readonly id: "inbound-qualification";
|
|
285
|
+
readonly title: "Inbound qualification";
|
|
286
|
+
readonly goal: "Qualify the selected inbound conversations and records against workspace ICP and product knowledge. Record evidence and recommendations; hand off high intent to a person.";
|
|
287
|
+
readonly resources: readonly ["conversations", "records"];
|
|
288
|
+
readonly actions: readonly ["oxygen_inbox_set_status", "oxygen_crm_notes_add"];
|
|
289
|
+
readonly events: readonly ["crm.email_reply_classified"];
|
|
290
|
+
}, {
|
|
291
|
+
readonly id: "crm-hygiene";
|
|
292
|
+
readonly title: "CRM hygiene";
|
|
293
|
+
readonly goal: "Review the selected CRM objects for duplicates and missing or inconsistent fields. Propose repairs with evidence, and apply only explicitly authorized changes.";
|
|
294
|
+
readonly resources: readonly ["objects"];
|
|
295
|
+
readonly actions: readonly ["oxygen_crm_merge"];
|
|
296
|
+
readonly events: readonly [];
|
|
297
|
+
}, {
|
|
298
|
+
readonly id: "pipeline-review";
|
|
299
|
+
readonly title: "Pipeline review";
|
|
300
|
+
readonly goal: "Review the selected pipeline and lifecycle stages. Remember earlier reviews, identify stalled opportunities and recommend the next action with evidence.";
|
|
301
|
+
readonly resources: readonly ["objects", "records"];
|
|
302
|
+
readonly actions: readonly ["oxygen_crm_notes_add"];
|
|
303
|
+
readonly events: readonly [];
|
|
304
|
+
}];
|
|
136
305
|
export declare function validateAgentSessionConfig(config: AgentSessionConfig, requireResources?: boolean): void;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { agentsEnabled, agentsEnabledIn } from "./agents-enabled.js";
|
|
2
2
|
import { guidedActivationEnabled } from "./guided-activation.js";
|
|
3
|
+
import { networkMiningEnabled } from "./network-mining.js";
|
|
3
4
|
import { validateContract } from "./contracts/schema.js";
|
|
4
5
|
import { GUIDED_ACTIVATION_SCAN_POLICY_CONTRACT } from "./contracts/guided-activation.js";
|
|
5
6
|
import { isStaffEmail } from "./staff.js";
|
|
@@ -50,6 +51,9 @@ export const AGENT_SESSION_ADAPTERS = {
|
|
|
50
51
|
oxygen_rows_upsert: { resource: "tables", field: "table", read: false },
|
|
51
52
|
oxygen_rows_update: { resource: "tables", field: "table", read: false },
|
|
52
53
|
oxygen_tables_dedupe: { resource: "tables", field: "table", read: false },
|
|
54
|
+
oxygen_inbox_reply_oxygent_queue: { resource: "knowledge", field: "voice_slug", read: true },
|
|
55
|
+
oxygen_inbox_reply_oxygent_prepare: { resource: "knowledge", field: "voice_slug", read: false },
|
|
56
|
+
oxygen_inbox_reply_oxygent_voice: { resource: "knowledge", field: "voice_slug", read: false },
|
|
53
57
|
oxygen_inbox_get: { resource: "conversations", field: "conversation", read: true },
|
|
54
58
|
oxygen_inbox_set_status: { resource: "conversations", field: "conversation", read: false },
|
|
55
59
|
oxygen_inbox_send: { resource: "conversations", field: "conversation", read: false },
|
|
@@ -127,6 +131,19 @@ export const AGENT_SESSION_TEMPLATES = [
|
|
|
127
131
|
{ id: "crm-hygiene", title: "CRM hygiene", goal: "Review the selected CRM objects for duplicates and missing or inconsistent fields. Propose repairs with evidence, and apply only explicitly authorized changes.", resources: ["objects"], actions: ["oxygen_crm_merge"], events: [] },
|
|
128
132
|
{ id: "pipeline-review", title: "Pipeline review", goal: "Review the selected pipeline and lifecycle stages. Remember earlier reviews, identify stalled opportunities and recommend the next action with evidence.", resources: ["objects", "records"], actions: ["oxygen_crm_notes_add"], events: [] },
|
|
129
133
|
];
|
|
134
|
+
/**
|
|
135
|
+
* Where Network Mining is served (DEV), record work starts from the one read
|
|
136
|
+
* of everything stored about a record. Elsewhere the templates are unchanged.
|
|
137
|
+
*/
|
|
138
|
+
const BRIEF_FIRST_TEMPLATES = new Set(["account-research", "inbound-qualification"]);
|
|
139
|
+
const BRIEF_FIRST_SENTENCE = "Start each record with its brief (oxygen_crm_record_brief): one free read of its fields, signals, conversations, team connections, notes, tasks and company.";
|
|
140
|
+
export function agentSessionTemplates(env = typeof process === "undefined" ? {} : process.env) {
|
|
141
|
+
if (!networkMiningEnabled(env))
|
|
142
|
+
return AGENT_SESSION_TEMPLATES;
|
|
143
|
+
return AGENT_SESSION_TEMPLATES.map((template) => BRIEF_FIRST_TEMPLATES.has(template.id)
|
|
144
|
+
? { ...template, goal: `${BRIEF_FIRST_SENTENCE} ${template.goal}` }
|
|
145
|
+
: template);
|
|
146
|
+
}
|
|
130
147
|
export function validateAgentSessionConfig(config, requireResources = true) {
|
|
131
148
|
const fail = (message) => { throw new OxygenError("invalid_session_config", message); };
|
|
132
149
|
if (!config.title?.trim() || !config.goal?.trim())
|
|
@@ -107,6 +107,14 @@ export declare function serializeCapabilityRoute(route: CapabilityRouteMatch | n
|
|
|
107
107
|
}, options?: {
|
|
108
108
|
visibleSkills?: readonly string[];
|
|
109
109
|
}): Record<string, unknown> | null;
|
|
110
|
+
/**
|
|
111
|
+
* Put the Network Mining record brief first on a record-dossier route where the
|
|
112
|
+
* host serves it, and take it off where it does not. The shared router decides
|
|
113
|
+
* by `networkMiningEnabled` in its own process; the CLI decides again by its
|
|
114
|
+
* API host, because its local environment says nothing about the server's.
|
|
115
|
+
* Idempotent; any other route is returned unchanged.
|
|
116
|
+
*/
|
|
117
|
+
export declare function withRecordBrief<T extends Pick<CapabilityRouteMatch, "card" | "recommendedTools" | "recommendedCommands">>(route: T, served: boolean): T;
|
|
110
118
|
/** One schedule for every channel of a Sequence, the same as Launch's Sending schedule. */
|
|
111
119
|
export declare const SEQUENCE_SENDING_SCHEDULE_COMMAND = "oxygen sequences update <sequence> --schedule-timezone <IANA zone> --schedule-days mon-fri --schedule-hours-start 09:00 --schedule-hours-end 17:00 --json";
|
|
112
120
|
/** Launch's unsubscribe switch: the visible link and the one-click headers together. */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { findEnrichmentIntentByAlias } from "./enrichment-intents.js";
|
|
2
|
+
import { networkMiningEnabled } from "./network-mining.js";
|
|
2
3
|
import { RECIPE_PRIMITIVES } from "./recipes.js";
|
|
3
4
|
// One compact semantic index covers every top-level /api/cli endpoint section.
|
|
4
5
|
// It is navigation, never execution: live schemas, prices, readiness, and
|
|
@@ -567,6 +568,10 @@ const NORMALIZED_ROUTE_PRIMITIVES = OXYGEN_PRIMITIVE_ROUTES.map((card) => card.p
|
|
|
567
568
|
if (NORMALIZED_PRIMITIVES.join("|") !== NORMALIZED_ROUTE_PRIMITIVES.join("|")) {
|
|
568
569
|
throw new Error("OXYGEN capability routes must cover the shared fifteen-primitive catalog exactly.");
|
|
569
570
|
}
|
|
571
|
+
/** Server env where one exists; a browser bundle has no process and serves no gated tool. */
|
|
572
|
+
function runtimeEnv() {
|
|
573
|
+
return typeof process === "undefined" ? {} : process.env;
|
|
574
|
+
}
|
|
570
575
|
export function getCapabilityRoute(id) {
|
|
571
576
|
return ROUTE_BY_ID.get(id.trim().toLowerCase()) ?? null;
|
|
572
577
|
}
|
|
@@ -618,7 +623,7 @@ export function inferCapabilityRoute(query) {
|
|
|
618
623
|
// Sourcing filters belong to the planner, not per-company enrichment.
|
|
619
624
|
const catalogue = catalogRow && CATALOGUE_CARD_IDS.has(card.id) && !isCompanySourcingIntent(normalized)
|
|
620
625
|
? catalogueRecommendations(catalogRow, card) : null;
|
|
621
|
-
return {
|
|
626
|
+
return withRecordBrief({
|
|
622
627
|
card: routeCardFor(card, normalized),
|
|
623
628
|
recommendedTools: recommendations.tools,
|
|
624
629
|
recommendedCommands: recommendations.commands,
|
|
@@ -627,7 +632,7 @@ export function inferCapabilityRoute(query) {
|
|
|
627
632
|
...(catalogue && catalogRow
|
|
628
633
|
? { catalogIntent: { id: catalogRow.id, label: catalogRow.label, status: catalogRow.status, group: catalogRow.group, tools: catalogue.tools, commands: catalogue.commands, exactCommand: catalogue.exactCommand, exactMcpCall: catalogue.exactMcpCall } }
|
|
629
634
|
: {}),
|
|
630
|
-
};
|
|
635
|
+
}, networkMiningEnabled(runtimeEnv()));
|
|
631
636
|
}
|
|
632
637
|
/**
|
|
633
638
|
* A data-type ask ("tech stack", "decision makers at each account", "funding
|
|
@@ -698,6 +703,7 @@ export function inferPrimitiveRoute(query) {
|
|
|
698
703
|
export const RETIRED_PRODUCT_SKILLS = Object.freeze(["oxygen-playbooks", "oxygen-visual-design", "oxygen-linkedin-marketing"]);
|
|
699
704
|
/** Current hosted instruction audiences, shared by discovery and Agent snapshots. */
|
|
700
705
|
export const OXYGEN_PRODUCT_SKILL_AUDIENCES = Object.freeze({
|
|
706
|
+
"oxygen-reply-oxygent": "internal_dev",
|
|
701
707
|
"oxygen-geo": "internal_dev",
|
|
702
708
|
"oxygen-onboarding": "workspace",
|
|
703
709
|
"oxygen-gtm-consultant": "workspace",
|
|
@@ -1000,6 +1006,84 @@ function crmConnectionsReadRecommendation(query) {
|
|
|
1000
1006
|
execution: "Read stored conversations and Sequence links for one CRM record with oxygen_crm_record_connections. Request projection=compact and a max_chars window; conversation_limit and sequence_limit bound the recent links. hasMore includes older or omitted links; this read has no cursor for older links and does not sync Messages, contact anyone or spend credits.",
|
|
1001
1007
|
};
|
|
1002
1008
|
}
|
|
1009
|
+
/**
|
|
1010
|
+
* "Tell me everything about this person" is a stored-Records read. Before this
|
|
1011
|
+
* rule it matched no card, and lexical search ranked a write tool
|
|
1012
|
+
* (oxygen_profile_viewers_enrichment) first. Every recommended tool is free.
|
|
1013
|
+
*/
|
|
1014
|
+
function recordDossierReadRecommendation(query) {
|
|
1015
|
+
const recordNoun = "(?:person|people|lead|leads|contact|contacts|prospect|prospects|account|accounts|customer|buyer|record|company|someone)";
|
|
1016
|
+
const subject = new RegExp(`\\b(?:this|that|the|my|our|a|one)\\s+(?:${recordNoun})\\b`).test(query)
|
|
1017
|
+
|| /\b(?:them|him|her|they)\b/.test(query)
|
|
1018
|
+
|| /\b(?:know|knows|have|has|got)\s+(?:about|on)\s+(?!our\b|my\b|the\b|us\b)[a-z]+(?:\s+[a-z]+)?\s*\??$/.test(query)
|
|
1019
|
+
|| /\b(?:call|meeting|chat)\s+with\s+[a-z]+(?:\s+[a-z]+)?\s*\??$/.test(query);
|
|
1020
|
+
// "profile, timeline, notes, conversations, meetings": several facets of one record at once.
|
|
1021
|
+
const facets = query.match(/\b(?:profile|timeline|activity|notes?|signals?|conversations?|messages|sequences?|meetings?|calls?|tasks?|related|connections?)\b/g) ?? [];
|
|
1022
|
+
const enumerated = new Set(facets.map((facet) => facet.replace(/s$/, ""))).size >= 3
|
|
1023
|
+
&& new RegExp(`\\b(?:${recordNoun})\\b`).test(query);
|
|
1024
|
+
const dossier = enumerated
|
|
1025
|
+
|| /\beverything\s+(?:about|on|we\s+(?:know|have))\b/.test(query)
|
|
1026
|
+
|| /\beverything\b.{0,40}\b(?:knows?|has|have|stores?|stored)\b(?:.{0,12}\b(?:about|on)\b)?/.test(query)
|
|
1027
|
+
|| /\bwhat\s+(?:do|does|did)\s+(?:we|i|you|oxygen|the\s+workspace)\s+(?:already\s+)?know\b/.test(query)
|
|
1028
|
+
|| /\ball\s+(?:the\s+)?(?:info|information|data|details|context)\s+(?:on|about|for)\b/.test(query)
|
|
1029
|
+
|| /\bfull\s+(?:picture|story|history|context)\b/.test(query)
|
|
1030
|
+
|| /\b(?:prep|prepare)\s+(?:me\s+)?for\s+(?:a\s+|my\s+|the\s+)?(?:call|meeting|chat)\s+with\b/.test(query)
|
|
1031
|
+
|| /\b(?:full|complete|quick|short)?\s*(?:brief|briefing|dossier|background|profile|summary|overview)\s+(?:on|of|for|about)\b/.test(query)
|
|
1032
|
+
|| /\b(?:summari[sz]e|brief me on|tell me about|catch me up on|background on)\b/.test(query);
|
|
1033
|
+
if (!subject && !enumerated)
|
|
1034
|
+
return null;
|
|
1035
|
+
if (!dossier)
|
|
1036
|
+
return null;
|
|
1037
|
+
if (isHostedWorkflowIntent(query)
|
|
1038
|
+
|| /\b(?:send\w*|repl(?:y|ies)|draft\w*|writ\w*|enroll\w*|enrich\w*|find (?:their |the |a )?(?:email|phone|mobile)|scrape\w*|creat\w*|updat\w*|edit\w*|merg\w*|delet\w*|remov\w*|import\w*|sync\w*|tag\w*)\b/.test(query))
|
|
1039
|
+
return null;
|
|
1040
|
+
// Naming another primitive's object is a different owner, unless the ask
|
|
1041
|
+
// enumerates one record's facets ("its notes, conversations, sequences").
|
|
1042
|
+
if (!enumerated && /\b(?:icp|ideal customer|positioning|offers?|brand|voice|meetings?|calls? (?:transcript|recording)s?|tables?|rows?|columns?|spreadsheets?|workflows?|agents?|sequences?|campaigns?|dashboards?|reports?|billing|credits?)\b/.test(query))
|
|
1043
|
+
return null;
|
|
1044
|
+
return {
|
|
1045
|
+
tools: [
|
|
1046
|
+
"oxygen_crm_record_search", "oxygen_crm_record_get", "oxygen_crm_record_timeline",
|
|
1047
|
+
"oxygen_crm_record_connections", "oxygen_crm_notes_list", "oxygen_crm_record_related",
|
|
1048
|
+
],
|
|
1049
|
+
commands: ["crm search", "crm get", "crm activity timeline", "crm connections", "crm notes list", "crm related"],
|
|
1050
|
+
execution: "Read what the workspace already stores about one record: resolve it with search (a name, email, domain or LinkedIn URL), then get its fields and identities, its timeline (signals, calls, meetings, notes), connected conversations and Sequences, notes and related records. Every read is free and contacts no one. Finding missing contact data or enriching is a separate paid action. External MCP uses oxygen_capabilities_read for unlisted reads; built-in agents invoke the native tools.",
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
const RECORD_DOSSIER_FIRST_COMMANDS = ["crm search", "crm get", "crm activity timeline"];
|
|
1054
|
+
const RECORD_BRIEF_TOOL = "oxygen_crm_record_brief";
|
|
1055
|
+
const RECORD_BRIEF_COMMAND = "crm brief";
|
|
1056
|
+
const RECORD_BRIEF_EXECUTION_PREFIX = `Start with ${RECORD_BRIEF_COMMAND} (${RECORD_BRIEF_TOOL}): one free read of everything stored about the record, with a cursor to each section's rest. `;
|
|
1057
|
+
/**
|
|
1058
|
+
* Put the Network Mining record brief first on a record-dossier route where the
|
|
1059
|
+
* host serves it, and take it off where it does not. The shared router decides
|
|
1060
|
+
* by `networkMiningEnabled` in its own process; the CLI decides again by its
|
|
1061
|
+
* API host, because its local environment says nothing about the server's.
|
|
1062
|
+
* Idempotent; any other route is returned unchanged.
|
|
1063
|
+
*/
|
|
1064
|
+
export function withRecordBrief(route, served) {
|
|
1065
|
+
if (route.card.id !== "records")
|
|
1066
|
+
return route;
|
|
1067
|
+
const briefed = route.recommendedTools[0] === RECORD_BRIEF_TOOL;
|
|
1068
|
+
if (!served) {
|
|
1069
|
+
if (!briefed)
|
|
1070
|
+
return route;
|
|
1071
|
+
return {
|
|
1072
|
+
...route,
|
|
1073
|
+
card: { ...route.card, execution: route.card.execution.replace(RECORD_BRIEF_EXECUTION_PREFIX, "") },
|
|
1074
|
+
recommendedTools: route.recommendedTools.slice(1),
|
|
1075
|
+
recommendedCommands: route.recommendedCommands.filter((command) => command !== RECORD_BRIEF_COMMAND),
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1078
|
+
if (briefed || !RECORD_DOSSIER_FIRST_COMMANDS.every((command, index) => route.recommendedCommands[index] === command))
|
|
1079
|
+
return route;
|
|
1080
|
+
return {
|
|
1081
|
+
...route,
|
|
1082
|
+
card: { ...route.card, execution: `${RECORD_BRIEF_EXECUTION_PREFIX}${route.card.execution}` },
|
|
1083
|
+
recommendedTools: [RECORD_BRIEF_TOOL, ...route.recommendedTools],
|
|
1084
|
+
recommendedCommands: [RECORD_BRIEF_COMMAND, ...route.recommendedCommands],
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1003
1087
|
/** Record-file lifecycle asks outrank the generic workspace label vocabulary. */
|
|
1004
1088
|
function crmFileUploadRecommendation(query) {
|
|
1005
1089
|
// Literal filenames and record identities are values, not competing owners:
|
|
@@ -1167,6 +1251,9 @@ function routeCardFor(card, query) {
|
|
|
1167
1251
|
const crmConnections = card.id === "records" ? crmConnectionsReadRecommendation(query) : null;
|
|
1168
1252
|
if (crmConnections)
|
|
1169
1253
|
return { ...card, posture: "read_only", execution: crmConnections.execution };
|
|
1254
|
+
const dossier = card.id === "records" ? recordDossierReadRecommendation(query) : null;
|
|
1255
|
+
if (dossier)
|
|
1256
|
+
return { ...card, posture: "read_only", execution: dossier.execution };
|
|
1170
1257
|
const crmFileUpload = card.id === "records" ? crmFileUploadRecommendation(query) : null;
|
|
1171
1258
|
if (crmFileUpload)
|
|
1172
1259
|
return { ...card, execution: crmFileUpload.execution };
|
|
@@ -1553,9 +1640,26 @@ function isOxygentChatIntent(query) {
|
|
|
1553
1640
|
return true;
|
|
1554
1641
|
if (/\b(chatgpt|claude|codex|oauth|mcp|connectors?|connected apps?)\b/.test(query))
|
|
1555
1642
|
return false;
|
|
1556
|
-
const
|
|
1557
|
-
|
|
1558
|
-
|
|
1643
|
+
const conversation = /\b(chats?|conversations?|histor(?:y|ies))\b/.test(query);
|
|
1644
|
+
const assistantAudience = /\b(?:agents?|assistants?|teammates?)(?:'s)? visibility\b/.test(query)
|
|
1645
|
+
|| /\bvisibility (?:of|for) (?:(?:my|our|the|an?) )?(?:ai )?(?:agents?|assistants?|teammates?)\b/.test(query)
|
|
1646
|
+
// An audience phrase ends here or introduces a relative clause.
|
|
1647
|
+
// Trailing inputs such as "workspace email enrichment" keep their owner.
|
|
1648
|
+
|| /\b(?:assistants?|teammates?) for (?:(?:my|our|the|a) )?(?:(?:personal|private) use|(?:shared|team) workspace(?=$|[,.!?;:)]| (?:that|which|where|with)\b))\b/.test(query)
|
|
1649
|
+
|| /\b(?:private|personal|shared) (?:ai )?(?:agents?|assistants?|teammates?|chats?|conversations?)\b/.test(query);
|
|
1650
|
+
// "Run my private agent only once" is execution, not creator-only access.
|
|
1651
|
+
// An execution request changes owner only when it also names a visibility edit.
|
|
1652
|
+
if (/\bagents?\b/.test(query) && /\b(?:run|execut\w*)\b/.test(query)
|
|
1653
|
+
&& !(assistantAudience && /\bvisibility\b/.test(query) && /\b(?:chang\w*|updat\w*|configur\w*|set)\b/.test(query)))
|
|
1654
|
+
return false;
|
|
1655
|
+
if (/\bai (?:agents? )?(?:assistants?|teammates?)\b/.test(query)) {
|
|
1656
|
+
return (conversation || assistantAudience) && (!/\b(?:run|execut\w*)\b/.test(query) || assistantAudience);
|
|
1657
|
+
}
|
|
1658
|
+
// Privacy of an Agent itself belongs to Oxygents, not custom Agent runs.
|
|
1659
|
+
// Keep ordinary run requests and private input data on the Agent gateway.
|
|
1660
|
+
if (/\bagents?\b/.test(query) && assistantAudience
|
|
1661
|
+
&& /\b(?:visibility|only|creat\w*|make|new|privacy|configur\w*|settings?)\b/.test(query))
|
|
1662
|
+
return true;
|
|
1559
1663
|
// Human membership asks must not re-enter Oxygent mutations through the
|
|
1560
1664
|
// generic fallback after the user-facing routine router excludes them.
|
|
1561
1665
|
// Explicit Oxygent/AI teammate access above keeps its native owner.
|
|
@@ -2087,6 +2191,8 @@ function explicitCapabilityIntent(query) {
|
|
|
2087
2191
|
return ROUTE_BY_ID.get("records") ?? null;
|
|
2088
2192
|
if (crmConnectionsReadRecommendation(query))
|
|
2089
2193
|
return ROUTE_BY_ID.get("records") ?? null;
|
|
2194
|
+
if (recordDossierReadRecommendation(query))
|
|
2195
|
+
return ROUTE_BY_ID.get("records") ?? null;
|
|
2090
2196
|
if (crmFileUploadRecommendation(query))
|
|
2091
2197
|
return ROUTE_BY_ID.get("records") ?? null;
|
|
2092
2198
|
if (crmMaintenanceRecommendation(query))
|
|
@@ -2461,6 +2567,9 @@ function recommendationsFor(card, query) {
|
|
|
2461
2567
|
const crmConnections = card.id === "records" ? crmConnectionsReadRecommendation(query) : null;
|
|
2462
2568
|
if (crmConnections)
|
|
2463
2569
|
return crmConnections;
|
|
2570
|
+
const dossier = card.id === "records" ? recordDossierReadRecommendation(query) : null;
|
|
2571
|
+
if (dossier)
|
|
2572
|
+
return dossier;
|
|
2464
2573
|
const crmSample = card.id === "records" ? crmSampleRecordRecommendation(query) : null;
|
|
2465
2574
|
const crmFileUpload = card.id === "records" ? crmFileUploadRecommendation(query) : null;
|
|
2466
2575
|
if (crmFileUpload)
|
|
@@ -31,7 +31,7 @@ export const CUSTOMER_OPERATIONS = [
|
|
|
31
31
|
operation("activation.state", "/api/cli/activation/state", "GET", "activation state", "oxygen_activation_state", "workspace", "Read this workspace's onboarding activation state and next available actions.", {}, [], mcpOnly),
|
|
32
32
|
operation("activation.checklist", "/api/cli/activation/checklist", "GET", "activation checklist", "oxygen_activation_checklist", "workspace", "Read this workspace's getting-started checklist: leads in a table, a first sequence, a connected sending account and a launched sequence, each with whether it is done and a link to do it; eligible says whether the sidebar shows it. Read-only, 0 credits."),
|
|
33
33
|
operation("api_keys.list", "/api/cli/api-keys", "GET", "api-keys list", "oxygen_api_keys_list", "workspace", "List workspace API keys without revealing their tokens.", {}, [], mcpOnly),
|
|
34
|
-
operation("api_keys.create", "/api/cli/api-keys", "POST", "api-keys create", "oxygen_api_keys_create", "workspace", "Create a workspace API key; requires the caller's personal user session. The token is returned once.", { name: text(), expires_at: text("ISO timestamp; omit for no expiry.") }, [], mcpOnly),
|
|
34
|
+
operation("api_keys.create", "/api/cli/api-keys", "POST", "api-keys create", "oxygen_api_keys_create", "workspace", "Create a workspace API key; requires the caller's personal user session. The token is returned once.", { name: text(), expires_at: text("ISO timestamp; omit for no expiry."), access: enumOf(["full", "read_only"]) }, [], mcpOnly),
|
|
35
35
|
operation("api_keys.revoke", "/api/cli/api-keys/{key_id}", "DELETE", "api-keys revoke", "oxygen_api_keys_revoke", "workspace", "Revoke a workspace API key; requires the caller's personal user session.", {}, [], mcpOnly),
|
|
36
36
|
operation("comments.edit", "/api/cli/collab/comments/{comment_id}", "PATCH", "comments edit", "oxygen_comments_edit", "collaboration", "Edit your own internal workspace comment; records the edit time.", { body: text() }, ["body"], mcpOnly),
|
|
37
37
|
operation("comments.delete", "/api/cli/collab/comments/{comment_id}", "DELETE", "comments delete", "oxygen_comments_delete", "collaboration", "Retract your own internal workspace comment while retaining its audit trail.", {}, [], mcpOnly),
|