@opengeni/contracts 1.0.2 → 1.2.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/dist/agent-config.d.ts +29 -8
- package/dist/artifact-catalog.d.ts +19 -0
- package/dist/atlassian.js +2 -1
- package/dist/bundled-skills.d.ts +9 -0
- package/dist/{chunk-X7ETVO4P.js → chunk-L73LKLKP.js} +59 -12
- package/dist/chunk-L73LKLKP.js.map +1 -0
- package/dist/{chunk-46KLD2UH.js → chunk-QZPIUG3I.js} +2 -2
- package/dist/chunk-XSBEB2HQ.js +146 -0
- package/dist/chunk-XSBEB2HQ.js.map +1 -0
- package/dist/connect.js +2 -1
- package/dist/connect.js.map +1 -1
- package/dist/connection-authority.js +3 -2
- package/dist/google-drive.js +2 -1
- package/dist/google-drive.js.map +1 -1
- package/dist/host-mcp-bindings.js +3 -2
- package/dist/host-mcp-bindings.js.map +1 -1
- package/dist/index.d.ts +52 -1
- package/dist/index.js +16 -1
- package/dist/model-display.d.ts +3 -0
- package/dist/model-display.js +9 -126
- package/dist/model-display.js.map +1 -1
- package/dist/personal-github.js +2 -1
- package/dist/personal-github.js.map +1 -1
- package/package.json +1 -1
- package/src/agent-config.ts +51 -17
- package/src/artifact-catalog.ts +14 -0
- package/src/bundled-skills.ts +13 -0
- package/src/index.ts +36 -1
- package/src/model-display.ts +13 -0
- package/dist/chunk-X7ETVO4P.js.map +0 -1
- /package/dist/{chunk-46KLD2UH.js.map → chunk-QZPIUG3I.js.map} +0 -0
package/src/agent-config.ts
CHANGED
|
@@ -200,9 +200,22 @@ export type ClientAgentConfig = z.infer<typeof ClientAgentConfig>;
|
|
|
200
200
|
// Capability registry
|
|
201
201
|
// ---------------------------------------------------------------------------
|
|
202
202
|
|
|
203
|
-
/** A
|
|
203
|
+
/** A built-in MCP server's owner: a capability, or pure runtime mechanics. */
|
|
204
204
|
export type AgentToolCapability = AgentCapabilityId | "runtime";
|
|
205
205
|
|
|
206
|
+
/**
|
|
207
|
+
* A first-party tool's owner: a capability, pure runtime mechanics, or the
|
|
208
|
+
* attached compute (a managed sandbox or Connected Machine).
|
|
209
|
+
*/
|
|
210
|
+
export type AgentFirstPartyToolOwner = AgentToolCapability | "sandbox";
|
|
211
|
+
|
|
212
|
+
/** Owners no capability toggles: the runtime or the attached compute derives them. */
|
|
213
|
+
export function isDerivedAgentToolOwner(
|
|
214
|
+
owner: AgentFirstPartyToolOwner,
|
|
215
|
+
): owner is "runtime" | "sandbox" {
|
|
216
|
+
return owner === "runtime" || owner === "sandbox";
|
|
217
|
+
}
|
|
218
|
+
|
|
206
219
|
/**
|
|
207
220
|
* Exhaustive map from every `FIRST_PARTY_MCP_TOOL_NAMES` entry to exactly one
|
|
208
221
|
* owner. The `Record` type makes an unmapped new name a compile error, and
|
|
@@ -210,15 +223,18 @@ export type AgentToolCapability = AgentCapabilityId | "runtime";
|
|
|
210
223
|
*
|
|
211
224
|
* `runtime` tools are mechanics every agent needs regardless of what it may do:
|
|
212
225
|
* - `wait_for_input`: ends the turn and waits for the next input.
|
|
213
|
-
* - `command_read` / `command_wait`: read/await the session's own background
|
|
214
|
-
* sandbox commands (the shell already exists whenever a sandbox is attached).
|
|
215
226
|
* - `set_session_title`: session titling.
|
|
227
|
+
*
|
|
228
|
+
* `sandbox` tools are derived from attached compute and never toggled:
|
|
229
|
+
* - `command_read` / `command_wait`: read/await the session's own background
|
|
230
|
+
* commands. A command can only exist on a managed sandbox or Connected
|
|
231
|
+
* Machine, so a turn with neither attached never receives them.
|
|
216
232
|
*/
|
|
217
233
|
export const FIRST_PARTY_MCP_TOOL_CAPABILITIES = {
|
|
218
234
|
set_session_title: "runtime",
|
|
219
235
|
wait_for_input: "runtime",
|
|
220
|
-
command_wait: "
|
|
221
|
-
command_read: "
|
|
236
|
+
command_wait: "sandbox",
|
|
237
|
+
command_read: "sandbox",
|
|
222
238
|
|
|
223
239
|
goal_set: "goals",
|
|
224
240
|
goal_update: "goals",
|
|
@@ -400,7 +416,7 @@ export const FIRST_PARTY_MCP_TOOL_CAPABILITIES = {
|
|
|
400
416
|
editable_artifact_apply: "artifacts",
|
|
401
417
|
editable_artifact_export: "artifacts",
|
|
402
418
|
editable_artifact_export_status: "artifacts",
|
|
403
|
-
} as const satisfies Record<FirstPartyMcpToolName,
|
|
419
|
+
} as const satisfies Record<FirstPartyMcpToolName, AgentFirstPartyToolOwner>;
|
|
404
420
|
|
|
405
421
|
/** Owner of a non-MCP tool: a capability, runtime mechanics, or the sandbox. */
|
|
406
422
|
export type AgentFunctionToolClass = AgentCapabilityId | "runtime" | "sandbox";
|
|
@@ -493,7 +509,7 @@ export const AGENT_BUILTIN_MCP_SERVER_CAPABILITIES = {
|
|
|
493
509
|
"google-drive-publishing": "workspaceConnectors",
|
|
494
510
|
} as const satisfies Record<string, AgentToolCapability>;
|
|
495
511
|
|
|
496
|
-
export function firstPartyMcpToolCapability(tool: FirstPartyMcpToolName):
|
|
512
|
+
export function firstPartyMcpToolCapability(tool: FirstPartyMcpToolName): AgentFirstPartyToolOwner {
|
|
497
513
|
return FIRST_PARTY_MCP_TOOL_CAPABILITIES[tool];
|
|
498
514
|
}
|
|
499
515
|
|
|
@@ -508,10 +524,21 @@ export type AgentToolEnvironment = {
|
|
|
508
524
|
media?: boolean | undefined;
|
|
509
525
|
hasDeferredTools?: boolean;
|
|
510
526
|
routerInHistory?: boolean;
|
|
527
|
+
/**
|
|
528
|
+
* Whether the turn has a managed sandbox or Connected Machine attached.
|
|
529
|
+
* `false` withholds the `sandbox`-owned first-party tools, which could never
|
|
530
|
+
* find a command; undefined means not yet known and keeps them.
|
|
531
|
+
*/
|
|
532
|
+
sandboxAttached?: boolean;
|
|
511
533
|
};
|
|
512
534
|
|
|
535
|
+
/** Runtime mechanics every configured agent receives. */
|
|
513
536
|
export const AGENT_RUNTIME_MECHANIC_TOOL_NAMES = [
|
|
514
537
|
"wait_for_input",
|
|
538
|
+
] as const satisfies readonly FirstPartyMcpToolName[];
|
|
539
|
+
|
|
540
|
+
/** First-party tools a configured agent receives whenever compute is attached. */
|
|
541
|
+
export const AGENT_SANDBOX_MECHANIC_TOOL_NAMES = [
|
|
515
542
|
"command_read",
|
|
516
543
|
"command_wait",
|
|
517
544
|
] as const satisfies readonly FirstPartyMcpToolName[];
|
|
@@ -519,7 +546,9 @@ export const AGENT_RUNTIME_MECHANIC_TOOL_NAMES = [
|
|
|
519
546
|
/**
|
|
520
547
|
* The one turn-time capability gate. Null means the historical attachment
|
|
521
548
|
* rules, including unconditional Skill tools and router. Resource-derived
|
|
522
|
-
* sandbox tools, product MCPs and runtime mechanics are never toggled
|
|
549
|
+
* sandbox tools, product MCPs and runtime mechanics are never toggled; the
|
|
550
|
+
* `sandbox`-owned first-party tools follow `environment.sandboxAttached` for
|
|
551
|
+
* every session, configured or not.
|
|
523
552
|
*/
|
|
524
553
|
export function resolveAgentToolFamilies(
|
|
525
554
|
config: ResolvedAgentConfig | null | undefined,
|
|
@@ -534,8 +563,10 @@ export function resolveAgentToolFamilies(
|
|
|
534
563
|
const humanInput = enabled("humanInput") && environment.humanInput !== false;
|
|
535
564
|
const media = enabled("media") && environment.media !== false;
|
|
536
565
|
const skills = !config ? "manage" : enabled("skills") ? config.capabilities.skills : false;
|
|
566
|
+
const sandboxTools = environment.sandboxAttached !== false;
|
|
537
567
|
const allowsFirstPartyTool = (name: FirstPartyMcpToolName): boolean => {
|
|
538
|
-
const owner = FIRST_PARTY_MCP_TOOL_CAPABILITIES[name];
|
|
568
|
+
const owner: AgentFirstPartyToolOwner = FIRST_PARTY_MCP_TOOL_CAPABILITIES[name];
|
|
569
|
+
if (owner === "sandbox") return sandboxTools;
|
|
539
570
|
return owner === "runtime" || enabled(owner);
|
|
540
571
|
};
|
|
541
572
|
return {
|
|
@@ -549,11 +580,14 @@ export function resolveAgentToolFamilies(
|
|
|
549
580
|
allowsFirstPartyTool,
|
|
550
581
|
firstPartyTools(names: readonly FirstPartyMcpToolName[]): FirstPartyMcpToolName[] {
|
|
551
582
|
return !config
|
|
552
|
-
?
|
|
583
|
+
? names.filter(
|
|
584
|
+
(name) => sandboxTools || FIRST_PARTY_MCP_TOOL_CAPABILITIES[name] !== "sandbox",
|
|
585
|
+
)
|
|
553
586
|
: [
|
|
554
587
|
...new Set([
|
|
555
588
|
...names.filter(allowsFirstPartyTool),
|
|
556
589
|
...AGENT_RUNTIME_MECHANIC_TOOL_NAMES,
|
|
590
|
+
...(sandboxTools ? AGENT_SANDBOX_MECHANIC_TOOL_NAMES : []),
|
|
557
591
|
]),
|
|
558
592
|
];
|
|
559
593
|
},
|
|
@@ -1203,8 +1237,8 @@ export function agentConfigFirstPartyMcpTools(
|
|
|
1203
1237
|
explicit?: readonly FirstPartyMcpToolName[],
|
|
1204
1238
|
): FirstPartyMcpToolName[] {
|
|
1205
1239
|
for (const tool of explicit ?? []) {
|
|
1206
|
-
const owner = FIRST_PARTY_MCP_TOOL_CAPABILITIES[tool];
|
|
1207
|
-
if (owner
|
|
1240
|
+
const owner: AgentFirstPartyToolOwner = FIRST_PARTY_MCP_TOOL_CAPABILITIES[tool];
|
|
1241
|
+
if (!isDerivedAgentToolOwner(owner) && capabilityFilteredOut(config, owner)) {
|
|
1208
1242
|
throw new AgentConfigError(
|
|
1209
1243
|
"agent_config_conflict",
|
|
1210
1244
|
`firstPartyMcpTools lists ${tool}, but agent capability ${owner} is off`,
|
|
@@ -1213,8 +1247,8 @@ export function agentConfigFirstPartyMcpTools(
|
|
|
1213
1247
|
}
|
|
1214
1248
|
}
|
|
1215
1249
|
return baseline.filter((tool) => {
|
|
1216
|
-
const owner = FIRST_PARTY_MCP_TOOL_CAPABILITIES[tool];
|
|
1217
|
-
return owner
|
|
1250
|
+
const owner: AgentFirstPartyToolOwner = FIRST_PARTY_MCP_TOOL_CAPABILITIES[tool];
|
|
1251
|
+
return isDerivedAgentToolOwner(owner) || !capabilityFilteredOut(config, owner);
|
|
1218
1252
|
});
|
|
1219
1253
|
}
|
|
1220
1254
|
|
|
@@ -1229,9 +1263,9 @@ export function agentConfigAddedFirstPartyMcpTools(
|
|
|
1229
1263
|
defaults: readonly FirstPartyMcpToolName[],
|
|
1230
1264
|
): FirstPartyMcpToolName[] {
|
|
1231
1265
|
return defaults.filter((tool) => {
|
|
1232
|
-
const owner = FIRST_PARTY_MCP_TOOL_CAPABILITIES[tool];
|
|
1266
|
+
const owner: AgentFirstPartyToolOwner = FIRST_PARTY_MCP_TOOL_CAPABILITIES[tool];
|
|
1233
1267
|
return (
|
|
1234
|
-
owner
|
|
1268
|
+
!isDerivedAgentToolOwner(owner) &&
|
|
1235
1269
|
!agentCapabilityEnabled(previous, owner) &&
|
|
1236
1270
|
agentCapabilityEnabled(next.capabilities, owner)
|
|
1237
1271
|
);
|
|
@@ -1325,7 +1359,7 @@ export function legacyEffectiveAgentCapabilities(input: {
|
|
|
1325
1359
|
*/
|
|
1326
1360
|
defaultServerIds?: Iterable<string> | undefined;
|
|
1327
1361
|
}): ResolvedAgentCapabilities {
|
|
1328
|
-
const selected = new Set<
|
|
1362
|
+
const selected = new Set<AgentFirstPartyToolOwner>(
|
|
1329
1363
|
input.firstPartyMcpTools.map((tool) => FIRST_PARTY_MCP_TOOL_CAPABILITIES[tool]),
|
|
1330
1364
|
);
|
|
1331
1365
|
const ids = new Set(input.tools.map((tool) => tool.id));
|
package/src/artifact-catalog.ts
CHANGED
|
@@ -20,6 +20,8 @@ export const ArtifactCatalogItem = z
|
|
|
20
20
|
createdAt: z.string().datetime({ offset: true }),
|
|
21
21
|
updatedAt: z.string().datetime({ offset: true }),
|
|
22
22
|
status: z.enum(["active", "archived"]),
|
|
23
|
+
/** Shared workspace metadata, not content-access authority. */
|
|
24
|
+
pinned: z.boolean().optional(),
|
|
23
25
|
sourceSessionId: z.string().uuid().optional(),
|
|
24
26
|
versionId: z.string().min(1).max(128).optional(),
|
|
25
27
|
file: RetainedArtifactReferenceSchema.optional(),
|
|
@@ -58,3 +60,15 @@ export const ArtifactCatalogListResponse = z
|
|
|
58
60
|
})
|
|
59
61
|
.strict();
|
|
60
62
|
export type ArtifactCatalogListResponse = z.infer<typeof ArtifactCatalogListResponse>;
|
|
63
|
+
|
|
64
|
+
export const UpdateArtifactPinRequest = z.object({ pinned: z.boolean() }).strict();
|
|
65
|
+
export type UpdateArtifactPinRequest = z.infer<typeof UpdateArtifactPinRequest>;
|
|
66
|
+
|
|
67
|
+
export const ArtifactPinResponse = z
|
|
68
|
+
.object({
|
|
69
|
+
kind: ArtifactCatalogKind,
|
|
70
|
+
artifactId: z.string().min(1).max(128),
|
|
71
|
+
pinned: z.boolean(),
|
|
72
|
+
})
|
|
73
|
+
.strict();
|
|
74
|
+
export type ArtifactPinResponse = z.infer<typeof ArtifactPinResponse>;
|
package/src/bundled-skills.ts
CHANGED
|
@@ -32,6 +32,19 @@ export function resolveBundledSkillSelection(
|
|
|
32
32
|
return selected === undefined ? undefined : BundledSkillSelection.parse(selected);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Freeze the defaults a session's agent configuration implies. An agent that
|
|
37
|
+
* starts from `capabilities: "none"` carries no bundled Opengeni guides unless
|
|
38
|
+
* the request lists them; an explicit list (including `[]`) is kept exactly,
|
|
39
|
+
* and `"all"` or legacy (null) configurations keep the bundled defaults.
|
|
40
|
+
*/
|
|
41
|
+
export function bundledSkillSelectionForAgentConfig(
|
|
42
|
+
selection: BundledSkillId[] | undefined,
|
|
43
|
+
agentConfig: { from: "all" | "none" } | null | undefined,
|
|
44
|
+
): BundledSkillId[] | undefined {
|
|
45
|
+
return selection === undefined && agentConfig?.from === "none" ? [] : selection;
|
|
46
|
+
}
|
|
47
|
+
|
|
35
48
|
// Immutable session configuration, following the existing create-identity
|
|
36
49
|
// metadata convention. Callers cannot set this through arbitrary metadata.
|
|
37
50
|
const BUNDLED_SKILL_SELECTION_KEY = "_opengeni_bundled_skill_ids_v1";
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { modelLogoUrl } from "./model-display";
|
|
1
2
|
import {
|
|
2
3
|
ClaudeProviderAccountAuthoritySnapshotV1,
|
|
3
4
|
WORKSPACE_CLAUDE_PROVIDER_ACCOUNT_AUTHORITY_SNAPSHOT_V1,
|
|
@@ -5003,11 +5004,26 @@ export type GitHubAppApiPort = {
|
|
|
5003
5004
|
}) => Promise<GitHubAppRepositoryBranchPage>;
|
|
5004
5005
|
};
|
|
5005
5006
|
|
|
5007
|
+
export const PromotionalCreditScope = z.object({
|
|
5008
|
+
label: z.string().trim().min(1).max(120),
|
|
5009
|
+
eligibleModelIds: z.array(z.string().trim().min(1).max(200)).min(1).max(40),
|
|
5010
|
+
});
|
|
5011
|
+
export type PromotionalCreditScope = z.infer<typeof PromotionalCreditScope>;
|
|
5012
|
+
|
|
5013
|
+
export const PromotionalCreditBalance = PromotionalCreditScope.extend({
|
|
5014
|
+
grantId: z.string().uuid(),
|
|
5015
|
+
remainingMicros: z.number().int().nonnegative(),
|
|
5016
|
+
});
|
|
5017
|
+
export type PromotionalCreditBalance = z.infer<typeof PromotionalCreditBalance>;
|
|
5018
|
+
|
|
5006
5019
|
export const BillingBalance = z.object({
|
|
5007
5020
|
accountId: z.string().uuid(),
|
|
5008
5021
|
balanceMicros: z.number().int(),
|
|
5009
5022
|
currency: z.literal("usd"),
|
|
5010
5023
|
updatedAt: z.string(),
|
|
5024
|
+
/** General credits, including unrestricted legacy grants. May be negative after use. */
|
|
5025
|
+
generalBalanceMicros: z.number().int().optional(),
|
|
5026
|
+
promotionalCredits: z.array(PromotionalCreditBalance).optional(),
|
|
5011
5027
|
});
|
|
5012
5028
|
export type BillingBalance = z.infer<typeof BillingBalance>;
|
|
5013
5029
|
|
|
@@ -5038,6 +5054,7 @@ export const CreateCheckoutResponse = z.object({
|
|
|
5038
5054
|
url: z.string().url(),
|
|
5039
5055
|
/** The credits this checkout grants once it completes. */
|
|
5040
5056
|
amountUsd: z.number().optional(),
|
|
5057
|
+
promotionalScope: PromotionalCreditScope.optional(),
|
|
5041
5058
|
});
|
|
5042
5059
|
export type CreateCheckoutResponse = z.infer<typeof CreateCheckoutResponse>;
|
|
5043
5060
|
|
|
@@ -5055,6 +5072,7 @@ export const BillingCheckoutStatus = z.object({
|
|
|
5055
5072
|
currency: z.literal("usd"),
|
|
5056
5073
|
/** True when a coupon covered the whole checkout, so nothing was charged. */
|
|
5057
5074
|
free: z.boolean(),
|
|
5075
|
+
promotionalScope: PromotionalCreditScope.optional(),
|
|
5058
5076
|
}),
|
|
5059
5077
|
balance: BillingBalance.nullable(),
|
|
5060
5078
|
});
|
|
@@ -6709,6 +6727,16 @@ export const SessionGoalContinuation = z.object({
|
|
|
6709
6727
|
});
|
|
6710
6728
|
export type SessionGoalContinuation = z.infer<typeof SessionGoalContinuation>;
|
|
6711
6729
|
|
|
6730
|
+
/** New admission pauses are specific; the wire field remains open for older peers. */
|
|
6731
|
+
export type GoalAdmissionPausedReason =
|
|
6732
|
+
| "model_unavailable"
|
|
6733
|
+
| "model_policy"
|
|
6734
|
+
| "credits"
|
|
6735
|
+
| "budget"
|
|
6736
|
+
| "usage_limit"
|
|
6737
|
+
| "usage_policy"
|
|
6738
|
+
| "allowance";
|
|
6739
|
+
|
|
6712
6740
|
export const SessionGoal = z.object({
|
|
6713
6741
|
id: z.string().uuid(),
|
|
6714
6742
|
accountId: z.string().uuid(),
|
|
@@ -10430,7 +10458,8 @@ export type KnowledgeSourceSyncRunSummary = z.infer<typeof KnowledgeSourceSyncRu
|
|
|
10430
10458
|
* means every occurrence is refused until the task or a resource it names
|
|
10431
10459
|
* changes. Known reasons: `scheduled_authority_unavailable`,
|
|
10432
10460
|
* `machine_target_unavailable`, `machine_enrollment_inactive`,
|
|
10433
|
-
* `variable_set_unavailable`, `rig_version_unavailable`
|
|
10461
|
+
* `variable_set_unavailable`, `rig_version_unavailable`, `scheduled_model_unavailable`
|
|
10462
|
+
* (terminal) and
|
|
10434
10463
|
* `insufficient_credits`, `allowance_exhausted`, `monthly_model_cost_limit`, `monthly_agent_run_limit`
|
|
10435
10464
|
* (transient). Readers must tolerate new reasons.
|
|
10436
10465
|
*/
|
|
@@ -18098,6 +18127,10 @@ export const ClientModel = /* @__PURE__ */ defineModelContractSchema(() =>
|
|
|
18098
18127
|
label: z.string(),
|
|
18099
18128
|
/** Optional curated compact label for dense UI (e.g. mobile composer). */
|
|
18100
18129
|
shortLabel: z.string().min(1).max(64).optional(),
|
|
18130
|
+
logoUrl: z
|
|
18131
|
+
.string()
|
|
18132
|
+
.refine((value) => modelLogoUrl({ id: "", logoUrl: value }) !== null)
|
|
18133
|
+
.optional(),
|
|
18101
18134
|
provider: z.string(), // provider id
|
|
18102
18135
|
providerLabel: z.string(),
|
|
18103
18136
|
api: z.enum(["responses", "chat", "anthropic-messages"]),
|
|
@@ -18218,6 +18251,8 @@ export const WorkspaceModelCatalogModel =
|
|
|
18218
18251
|
/* @__PURE__ */ defineModelContractSchema(() =>
|
|
18219
18252
|
ClientModel.extend({
|
|
18220
18253
|
credentialReadiness: ModelCredentialReadinessV1,
|
|
18254
|
+
/** Current funding status, without exposing the organization's balance. */
|
|
18255
|
+
creditFunding: z.enum(["promotional", "general", "unavailable"]).optional(),
|
|
18221
18256
|
/** Exact workspace-policy verdict without exposing provider identity. */
|
|
18222
18257
|
policyAllowed: z.boolean().optional(),
|
|
18223
18258
|
availability: ModelAvailabilityV1,
|
package/src/model-display.ts
CHANGED
|
@@ -28,10 +28,23 @@ export type ModelDisplayInput =
|
|
|
28
28
|
| {
|
|
29
29
|
id: string;
|
|
30
30
|
label?: string | null | undefined;
|
|
31
|
+
logoUrl?: string | null | undefined;
|
|
31
32
|
upstreamModelId?: string | null | undefined;
|
|
32
33
|
deployment?: { upstreamModelId?: string | null | undefined } | null | undefined;
|
|
33
34
|
};
|
|
34
35
|
|
|
36
|
+
/** Catalog maker logo; remote images must use HTTPS without embedded credentials. */
|
|
37
|
+
export function modelLogoUrl(input: ModelDisplayInput): string | null {
|
|
38
|
+
const value = typeof input === "string" ? undefined : input.logoUrl;
|
|
39
|
+
if (!value || value.length > 2048 || value !== value.trim()) return null;
|
|
40
|
+
try {
|
|
41
|
+
const url = new URL(value);
|
|
42
|
+
return url.protocol === "https:" && !url.username && !url.password ? value : null;
|
|
43
|
+
} catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
35
48
|
const WORDS: Readonly<Record<string, string>> = {
|
|
36
49
|
gpt: "GPT",
|
|
37
50
|
chatgpt: "ChatGPT",
|