@opengeni/contracts 0.50.0 → 1.0.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-authored-durable-text.d.ts +70 -0
- package/dist/agent-config.d.ts +869 -0
- package/dist/agent-instruction-changes.d.ts +110 -0
- package/dist/agent-learning.d.ts +243 -0
- package/dist/agent-topology.d.ts +292 -0
- package/dist/allowance-refusal.d.ts +9 -0
- package/dist/allowance-refusal.js +50 -0
- package/dist/allowance-refusal.js.map +1 -0
- package/dist/analytics-consent-report.d.ts +19 -0
- package/dist/analytics-consent-report.js +10 -0
- package/dist/analytics-consent-report.js.map +1 -0
- package/dist/api-key-presets.d.ts +19 -0
- package/dist/artifact-catalog.d.ts +161 -0
- package/dist/artifacts.d.ts +135 -11
- package/dist/atlassian-native-retirement.d.ts +16 -0
- package/dist/atlassian-native-retirement.js +30 -0
- package/dist/atlassian-native-retirement.js.map +1 -0
- package/dist/atlassian.d.ts +2 -0
- package/dist/atlassian.js +26 -12
- package/dist/browser-storage.d.ts +5 -0
- package/dist/browser-storage.js +9 -0
- package/dist/browser-storage.js.map +1 -0
- package/dist/bundled-skills.d.ts +40 -0
- package/dist/chunk-2ACYHZFB.js +292 -0
- package/dist/chunk-2ACYHZFB.js.map +1 -0
- package/dist/chunk-2VORLN2R.js +353 -0
- package/dist/chunk-2VORLN2R.js.map +1 -0
- package/dist/{chunk-Y3MLSLB4.js → chunk-3SIZGL3O.js} +42 -30
- package/dist/chunk-3SIZGL3O.js.map +1 -0
- package/dist/chunk-46KLD2UH.js +186 -0
- package/dist/chunk-46KLD2UH.js.map +1 -0
- package/dist/chunk-4JGHSSB7.js +11 -0
- package/dist/chunk-4JGHSSB7.js.map +1 -0
- package/dist/{chunk-M5ZGRVIQ.js → chunk-5IXYF6XN.js} +14 -5
- package/dist/chunk-5IXYF6XN.js.map +1 -0
- package/dist/chunk-BCS7WHCK.js +139 -0
- package/dist/chunk-BCS7WHCK.js.map +1 -0
- package/dist/chunk-EK7NDGFR.js +128 -0
- package/dist/chunk-EK7NDGFR.js.map +1 -0
- package/dist/{chunk-5UDNP7AO.js → chunk-FZ6BXQHL.js} +78 -38
- package/dist/chunk-FZ6BXQHL.js.map +1 -0
- package/dist/{chunk-CM6BMECR.js → chunk-GRG7PHWQ.js} +2 -1
- package/dist/chunk-GRG7PHWQ.js.map +1 -0
- package/dist/chunk-KHHZTBP7.js +11 -0
- package/dist/chunk-KHHZTBP7.js.map +1 -0
- package/dist/chunk-KR2V42UZ.js +187 -0
- package/dist/chunk-KR2V42UZ.js.map +1 -0
- package/dist/chunk-MZLBPKFE.js +168 -0
- package/dist/chunk-MZLBPKFE.js.map +1 -0
- package/dist/{chunk-LW7OH6CS.js → chunk-NRMCVHAU.js} +6 -5
- package/dist/chunk-NRMCVHAU.js.map +1 -0
- package/dist/{chunk-TZ6YEYN4.js → chunk-NVJMJWLL.js} +3 -1
- package/dist/chunk-NVJMJWLL.js.map +1 -0
- package/dist/chunk-O77VSFYZ.js +70 -0
- package/dist/chunk-O77VSFYZ.js.map +1 -0
- package/dist/chunk-PX3VCKAU.js +29 -0
- package/dist/chunk-PX3VCKAU.js.map +1 -0
- package/dist/chunk-QR5D2IQC.js +61 -0
- package/dist/chunk-QR5D2IQC.js.map +1 -0
- package/dist/chunk-RJ76DRPO.js +117 -0
- package/dist/chunk-RJ76DRPO.js.map +1 -0
- package/dist/chunk-RKX3NQYS.js +28 -0
- package/dist/chunk-RKX3NQYS.js.map +1 -0
- package/dist/{chunk-JBCIY7QD.js → chunk-SIOU2K4U.js} +45 -22
- package/dist/chunk-SIOU2K4U.js.map +1 -0
- package/dist/chunk-TSCDAGET.js +75 -0
- package/dist/chunk-TSCDAGET.js.map +1 -0
- package/dist/{chunk-LSB7GRLG.js → chunk-TXTQIYCV.js} +15 -11
- package/dist/chunk-TXTQIYCV.js.map +1 -0
- package/dist/chunk-X7ETVO4P.js +26544 -0
- package/dist/chunk-X7ETVO4P.js.map +1 -0
- package/dist/{chunk-CADSX6UN.js → chunk-Y3KNXVTD.js} +2 -2
- package/dist/chunk-YBW5JSA3.js +251 -0
- package/dist/chunk-YBW5JSA3.js.map +1 -0
- package/dist/chunk-YRXAWWCU.js +45 -0
- package/dist/chunk-YRXAWWCU.js.map +1 -0
- package/dist/claude-provider-account-authority.d.ts +18 -0
- package/dist/claude-subscription-oauth.d.ts +42 -0
- package/dist/claude-subscription-usage.d.ts +159 -0
- package/dist/client-error-report.d.ts +109 -0
- package/dist/client-error-report.js +87 -0
- package/dist/client-error-report.js.map +1 -0
- package/dist/code-search.d.ts +52 -0
- package/dist/code-search.js +40 -0
- package/dist/code-search.js.map +1 -0
- package/dist/company-brain-governed-writes.d.ts +475 -0
- package/dist/company-brain.d.ts +336 -0
- package/dist/company-profile.d.ts +566 -0
- package/dist/connect.d.ts +325 -0
- package/dist/connect.js +191 -0
- package/dist/connect.js.map +1 -0
- package/dist/connection-account-label.d.ts +2 -0
- package/dist/connection-account-label.js +35 -0
- package/dist/connection-account-label.js.map +1 -0
- package/dist/connection-authority.d.ts +193 -0
- package/dist/connection-authority.js +50 -0
- package/dist/connection-authority.js.map +1 -0
- package/dist/connector-attachments.d.ts +95 -0
- package/dist/connector-attachments.js +51 -0
- package/dist/connector-attachments.js.map +1 -0
- package/dist/connector-tool-permissions.d.ts +73 -0
- package/dist/direct-model-provider.d.ts +36 -0
- package/dist/editable-artifact-codec-registry.d.ts +16 -7
- package/dist/editable-artifact-codec-registry.js +18 -8
- package/dist/editable-artifact-committed-transaction.d.ts +2 -2
- package/dist/editable-artifact-committed-transaction.js +2 -2
- package/dist/editable-artifact-live.d.ts +11 -12
- package/dist/editable-artifact-live.js +110 -45
- package/dist/editable-artifact-live.js.map +1 -1
- package/dist/editable-artifact-serialized-commit.d.ts +2 -1
- package/dist/editable-artifact-serialized-commit.js +8 -7
- package/dist/editable-artifact-versions.d.ts +20 -5
- package/dist/editable-artifact-versions.js +23 -5
- package/dist/editable-artifacts.d.ts +2 -0
- package/dist/editable-artifacts.js +47 -35
- package/dist/external-identities.d.ts +784 -0
- package/dist/external-identities.js +44 -0
- package/dist/external-identities.js.map +1 -0
- package/dist/feedback.d.ts +51 -0
- package/dist/github-repository-contracts.d.ts +36 -0
- package/dist/github-repository-contracts.js +59 -0
- package/dist/github-repository-contracts.js.map +1 -0
- package/dist/github-repository.d.ts +14 -0
- package/dist/github-repository.js +36 -0
- package/dist/github-repository.js.map +1 -0
- package/dist/google-drive.d.ts +90 -1
- package/dist/google-drive.js +90 -16
- package/dist/google-drive.js.map +1 -1
- package/dist/governed-learning-activation.d.ts +154 -0
- package/dist/governed-learning-evaluator.d.ts +152 -0
- package/dist/host-mcp-bindings.d.ts +326 -0
- package/dist/host-mcp-bindings.js +252 -0
- package/dist/host-mcp-bindings.js.map +1 -0
- package/dist/image-generation.d.ts +1 -0
- package/dist/index.d.ts +32396 -11386
- package/dist/index.js +2198 -164
- package/dist/insights-usage.d.ts +877 -0
- package/dist/insights-usage.js +509 -0
- package/dist/insights-usage.js.map +1 -0
- package/dist/interaction.d.ts +344 -0
- package/dist/knowledge-entries.d.ts +982 -0
- package/dist/knowledge-preparation.d.ts +237 -0
- package/dist/knowledge.d.ts +197 -0
- package/dist/managed-auth-session-sets.d.ts +241 -0
- package/dist/managed-auth-session-sets.js +191 -0
- package/dist/managed-auth-session-sets.js.map +1 -0
- package/dist/managed-sign-in-methods.d.ts +56 -0
- package/dist/managed-sign-in-methods.js +46 -0
- package/dist/managed-sign-in-methods.js.map +1 -0
- package/dist/mcp-catalog-limits.d.ts +2 -0
- package/dist/mcp-endpoint.d.ts +7 -0
- package/dist/mcp-endpoint.js +9 -0
- package/dist/mcp-endpoint.js.map +1 -0
- package/dist/mcp-oauth.d.ts +71 -0
- package/dist/modal-native-proof-v2.d.ts +587 -0
- package/dist/modal-native-proof-v2.js +37 -0
- package/dist/modal-native-proof-v2.js.map +1 -0
- package/dist/model-availability.d.ts +15 -0
- package/dist/model-connection-access.d.ts +24 -0
- package/dist/model-context-inspector.d.ts +329 -0
- package/dist/model-display.d.ts +38 -0
- package/dist/model-display.js +134 -0
- package/dist/model-display.js.map +1 -0
- package/dist/model-picker-order.d.ts +25 -0
- package/dist/model-picker-order.js +42 -0
- package/dist/model-picker-order.js.map +1 -0
- package/dist/organization-access.d.ts +231 -0
- package/dist/organization-access.js +34 -0
- package/dist/organization-access.js.map +1 -0
- package/dist/organization-integration-policy.d.ts +27 -0
- package/dist/organization-membership-lifecycle.d.ts +1022 -0
- package/dist/organization-model-usage.d.ts +146 -0
- package/dist/organization-model-usage.js +67 -0
- package/dist/organization-model-usage.js.map +1 -0
- package/dist/organization-recovery.d.ts +337 -0
- package/dist/organization-recovery.js +37 -0
- package/dist/organization-recovery.js.map +1 -0
- package/dist/organization-usage.d.ts +138 -0
- package/dist/permissions.d.ts +54 -0
- package/dist/personal-github.d.ts +320 -0
- package/dist/personal-github.js +222 -0
- package/dist/personal-github.js.map +1 -0
- package/dist/plugin-discovery.d.ts +35 -0
- package/dist/plugin-discovery.js +7 -0
- package/dist/plugin-discovery.js.map +1 -0
- package/dist/preference-registry.d.ts +44 -1
- package/dist/product-analytics.d.ts +113 -0
- package/dist/product-lifecycle-facts.d.ts +73 -0
- package/dist/remember.d.ts +350 -0
- package/dist/retained-output.d.ts +8 -0
- package/dist/sandbox-file-artifacts.d.ts +57 -0
- package/dist/sandbox-provider-command.d.ts +113 -0
- package/dist/sandbox-recovery.d.ts +146 -0
- package/dist/sandbox-snapshots.d.ts +39 -0
- package/dist/session-final-reply.d.ts +6 -0
- package/dist/session-final-reply.js +9 -0
- package/dist/session-final-reply.js.map +1 -0
- package/dist/session-goal-reports.d.ts +21 -0
- package/dist/session-history-import.d.ts +947 -0
- package/dist/session-list-entries.d.ts +4 -0
- package/dist/session-list-entries.js +51 -0
- package/dist/session-list-entries.js.map +1 -0
- package/dist/session-mcp-projections.d.ts +215 -0
- package/dist/session-message-search.d.ts +68 -0
- package/dist/session-titles.d.ts +68 -0
- package/dist/session-titles.js +25 -0
- package/dist/session-titles.js.map +1 -0
- package/dist/session-topology-primitives.d.ts +22 -0
- package/dist/signup-attribution.d.ts +39 -0
- package/dist/site-request-headers.test.d.ts +1 -0
- package/dist/site-session-http.d.ts +11 -0
- package/dist/site-session-http.js +11 -0
- package/dist/site-session-http.js.map +1 -0
- package/dist/skill-catalog-context.d.ts +7 -0
- package/dist/skill-files.d.ts +12 -0
- package/dist/skill-metadata.d.ts +9 -0
- package/dist/skill-use.d.ts +61 -0
- package/dist/skills.d.ts +185 -0
- package/dist/slack-bot-scopes.d.ts +46 -21
- package/dist/slack-bot-scopes.js +14 -3
- package/dist/slack-rest-mcp.d.ts +31 -0
- package/dist/slack-rest-mcp.js +19 -0
- package/dist/slack-rest-mcp.js.map +1 -0
- package/dist/spreadsheet-artifact-commands.d.ts +2 -3
- package/dist/spreadsheet-artifact-commands.js +4 -2
- package/dist/spreadsheet-artifact-query.js +3 -2
- package/dist/subscription-account-pools.d.ts +292 -0
- package/dist/task-notes.d.ts +42 -1
- package/dist/tool-catalog.d.ts +286 -0
- package/dist/tool-result-spill.d.ts +16 -0
- package/dist/usage-allowances.d.ts +285 -0
- package/dist/usage-allowances.js +49 -0
- package/dist/usage-allowances.js.map +1 -0
- package/dist/video-generation.d.ts +3 -0
- package/dist/video-generation.js +1 -1
- package/dist/work-claims.d.ts +411 -0
- package/dist/workspace-instruction-policies.d.ts +94 -0
- package/dist/workspace-integration-wire.d.ts +36 -0
- package/dist/workspace-integration-wire.js +23 -0
- package/dist/workspace-integration-wire.js.map +1 -0
- package/dist/workspace-integrations.d.ts +948 -0
- package/dist/workspace-learning-administration.d.ts +289 -0
- package/dist/workspace-learning-policy.d.ts +1 -1
- package/dist/workspace-state.d.ts +85 -416
- package/dist/xai-provider-account-authority.d.ts +3 -0
- package/dist/xai-provider-account-authority.js +1 -1
- package/package.json +134 -1
- package/src/agent-authored-durable-text.ts +116 -0
- package/src/agent-config.ts +1527 -0
- package/src/agent-instruction-changes.ts +98 -0
- package/src/agent-learning.ts +161 -0
- package/src/agent-topology.ts +69 -0
- package/src/allowance-refusal.ts +81 -0
- package/src/analytics-consent-report.ts +20 -0
- package/src/api-key-presets.ts +25 -0
- package/src/artifact-catalog.ts +60 -0
- package/src/artifacts.ts +146 -18
- package/src/atlassian-native-retirement.ts +33 -0
- package/src/browser-storage.ts +10 -0
- package/src/bundled-skills.ts +73 -0
- package/src/claude-provider-account-authority.ts +12 -0
- package/src/claude-subscription-oauth.ts +62 -0
- package/src/claude-subscription-usage.ts +59 -0
- package/src/client-error-report.ts +184 -0
- package/src/code-search.ts +105 -0
- package/src/company-brain-governed-writes.ts +356 -0
- package/src/company-brain.ts +201 -0
- package/src/company-profile.ts +167 -1
- package/src/connect.ts +202 -0
- package/src/connection-account-label.ts +46 -0
- package/src/connection-authority.ts +213 -0
- package/src/connector-attachments.ts +327 -0
- package/src/connector-tool-permissions.ts +60 -0
- package/src/direct-model-provider.ts +98 -0
- package/src/editable-artifact-codec-registry.ts +55 -17
- package/src/editable-artifact-committed-transaction.ts +44 -31
- package/src/editable-artifact-live.ts +123 -57
- package/src/editable-artifact-serialized-commit.ts +2 -1
- package/src/editable-artifact-versions.ts +26 -5
- package/src/editable-artifacts.ts +2 -0
- package/src/external-identities.ts +285 -0
- package/src/feedback.ts +39 -0
- package/src/github-repository-contracts.ts +85 -0
- package/src/github-repository.ts +59 -0
- package/src/google-drive.ts +76 -4
- package/src/governed-learning-activation.ts +191 -0
- package/src/governed-learning-evaluator.ts +176 -0
- package/src/host-mcp-bindings.ts +298 -0
- package/src/image-generation.ts +8 -2
- package/src/index.ts +7093 -1466
- package/src/insights-usage.ts +679 -0
- package/src/interaction.ts +220 -5
- package/src/knowledge-entries.ts +375 -0
- package/src/knowledge-preparation.ts +50 -0
- package/src/knowledge.ts +134 -6
- package/src/managed-auth-session-sets.ts +253 -0
- package/src/managed-sign-in-methods.ts +37 -0
- package/src/mcp-catalog-limits.ts +2 -0
- package/src/mcp-endpoint.ts +30 -0
- package/src/mcp-oauth.ts +66 -0
- package/src/modal-native-proof-v2.ts +398 -0
- package/src/model-availability.ts +22 -0
- package/src/model-connection-access.ts +17 -0
- package/src/model-context-inspector.ts +139 -0
- package/src/model-display.ts +195 -0
- package/src/model-picker-order.ts +97 -0
- package/src/organization-access.ts +176 -0
- package/src/organization-integration-policy.ts +54 -0
- package/src/organization-membership-lifecycle.ts +559 -0
- package/src/organization-model-usage.ts +72 -0
- package/src/organization-recovery.ts +186 -0
- package/src/organization-usage.ts +79 -0
- package/src/permissions.ts +58 -0
- package/src/personal-github.ts +267 -0
- package/src/plugin-discovery.ts +33 -0
- package/src/preference-registry.ts +20 -1
- package/src/product-analytics.ts +253 -0
- package/src/product-lifecycle-facts.ts +121 -0
- package/src/remember.ts +243 -0
- package/src/retained-output.ts +20 -3
- package/src/sandbox-file-artifacts.ts +73 -0
- package/src/sandbox-provider-command.ts +82 -0
- package/src/sandbox-recovery.ts +116 -0
- package/src/sandbox-snapshots.ts +142 -0
- package/src/session-final-reply.ts +16 -0
- package/src/session-goal-reports.ts +55 -0
- package/src/session-history-import.ts +159 -0
- package/src/session-list-entries.ts +91 -0
- package/src/session-mcp-projections.ts +273 -0
- package/src/session-message-search.ts +54 -0
- package/src/session-titles.ts +467 -0
- package/src/session-topology-primitives.ts +21 -0
- package/src/signup-attribution.ts +81 -0
- package/src/site-request-headers.test.ts +28 -0
- package/src/site-session-http.ts +63 -0
- package/src/skill-catalog-context.ts +50 -0
- package/src/skill-files.ts +52 -0
- package/src/skill-metadata.ts +40 -0
- package/src/skill-use.ts +77 -0
- package/src/skills.ts +136 -0
- package/src/slack-bot-scopes.ts +106 -42
- package/src/slack-rest-mcp.ts +194 -0
- package/src/spreadsheet-artifact-commands.ts +12 -13
- package/src/subscription-account-pools.ts +44 -0
- package/src/task-notes.ts +14 -1
- package/src/tool-catalog.ts +155 -0
- package/src/tool-result-spill.ts +35 -0
- package/src/usage-allowances.ts +187 -0
- package/src/work-claims.ts +266 -0
- package/src/workspace-instruction-policies.ts +31 -0
- package/src/workspace-integration-wire.ts +99 -0
- package/src/workspace-integrations.ts +640 -0
- package/src/workspace-learning-administration.ts +88 -0
- package/src/workspace-learning-policy.ts +4 -3
- package/src/workspace-state.ts +16 -126
- package/src/xai-provider-account-authority.ts +1 -0
- package/dist/chunk-5UDNP7AO.js.map +0 -1
- package/dist/chunk-BVGIYW7D.js +0 -11
- package/dist/chunk-BVGIYW7D.js.map +0 -1
- package/dist/chunk-CM6BMECR.js.map +0 -1
- package/dist/chunk-JBCIY7QD.js.map +0 -1
- package/dist/chunk-LSB7GRLG.js.map +0 -1
- package/dist/chunk-LW7OH6CS.js.map +0 -1
- package/dist/chunk-M5ZGRVIQ.js.map +0 -1
- package/dist/chunk-Q6A7XT2N.js +0 -16734
- package/dist/chunk-Q6A7XT2N.js.map +0 -1
- package/dist/chunk-TZ6YEYN4.js.map +0 -1
- package/dist/chunk-Y3MLSLB4.js.map +0 -1
- /package/dist/{chunk-CADSX6UN.js.map → chunk-Y3KNXVTD.js.map} +0 -0
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
/** Human-authored session titles keep the existing public API ceiling. */
|
|
2
|
+
export const SESSION_TITLE_MAX_CHARACTERS = 200;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Automatic titles are intentionally much shorter than the manual rename
|
|
6
|
+
* ceiling. This is measured in grapheme clusters so emoji and combining text
|
|
7
|
+
* are never split into malformed display strings.
|
|
8
|
+
*/
|
|
9
|
+
export const AUTOMATIC_SESSION_TITLE_MAX_GRAPHEMES = 80;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Safe durable title used until semantic generation succeeds. It contains no
|
|
13
|
+
* user prompt bytes, so provider failure/offline operation cannot leak a
|
|
14
|
+
* credential or leave a raw prompt prefix in navigation surfaces.
|
|
15
|
+
*/
|
|
16
|
+
export const AUTOMATIC_SESSION_TITLE_FALLBACK = "New conversation";
|
|
17
|
+
|
|
18
|
+
// Session creation accepts a body far larger than a navigation label. Bound
|
|
19
|
+
// the source before any replace/split/normalization so a persisted large prompt
|
|
20
|
+
// cannot amplify memory or CPU on every client render.
|
|
21
|
+
export const PROMPT_PREVIEW_SCAN_MAX_CODE_UNITS = 4_096;
|
|
22
|
+
|
|
23
|
+
const SESSION_ID_PATTERN =
|
|
24
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
|
|
25
|
+
|
|
26
|
+
let titleSegmenter: Intl.Segmenter | null | undefined;
|
|
27
|
+
|
|
28
|
+
const KNOWN_SENSITIVE_VALUE_PATTERNS = [
|
|
29
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----/iu,
|
|
30
|
+
/\bBearer\s+[A-Za-z0-9._~+/=-]{8,}/iu,
|
|
31
|
+
/\b(?:sk-(?:proj-)?|gh[oprsu]_|github_pat_|glpat-|xox[baprs]-)[A-Za-z0-9_-]{8,}/iu,
|
|
32
|
+
/\bAKIA[0-9A-Z]{16}\b/u,
|
|
33
|
+
/\bAIza[0-9A-Za-z_-]{20,}\b/u,
|
|
34
|
+
/\beyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/u,
|
|
35
|
+
/[?&](?:access_token|api_key|apikey|password|secret|token)=[^\s&#]+/iu,
|
|
36
|
+
] as const;
|
|
37
|
+
|
|
38
|
+
const URI_SCHEME_CANDIDATE_PATTERN = /\b[a-z][a-z0-9+.-]*:\S+/giu;
|
|
39
|
+
const WINDOWS_DRIVE_PATH_PATTERN = /^[a-z]:[\\/](?![\\/])[^:]*$/iu;
|
|
40
|
+
|
|
41
|
+
const SCHEMELESS_HOST_CANDIDATE_PATTERN =
|
|
42
|
+
/\b(?:www\.)?(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?(?::\d{1,5})?(?:[/?#][^\s]*)?/giu;
|
|
43
|
+
|
|
44
|
+
const SCHEMELESS_LOCAL_NETWORK_PATTERNS = [
|
|
45
|
+
/\blocalhost(?:(?::\d{1,5})(?:[/?#][^\s]*)?|[/?#][^\s]*)/iu,
|
|
46
|
+
/\b(?:(?:25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|1?\d?\d)(?:(?::\d{1,5})(?:[/?#][^\s]*)?|[/?#][^\s]*)/u,
|
|
47
|
+
/\[(?=[0-9a-f:.]*:[0-9a-f:.]*\])[0-9a-f:.]+\](?:(?::\d{1,5})(?:[/?#][^\s]*)?|[/?#][^\s]*)/iu,
|
|
48
|
+
] as const;
|
|
49
|
+
|
|
50
|
+
const FILE_LIKE_HOST_SUFFIXES = new Set([
|
|
51
|
+
"css",
|
|
52
|
+
"html",
|
|
53
|
+
"js",
|
|
54
|
+
"json",
|
|
55
|
+
"jsx",
|
|
56
|
+
"lock",
|
|
57
|
+
"md",
|
|
58
|
+
"sql",
|
|
59
|
+
"toml",
|
|
60
|
+
"ts",
|
|
61
|
+
"tsx",
|
|
62
|
+
"xml",
|
|
63
|
+
"yaml",
|
|
64
|
+
"yml",
|
|
65
|
+
]);
|
|
66
|
+
|
|
67
|
+
// These authorities are established framework/namespace notation whose exact
|
|
68
|
+
// casing is meaningful. Keep this exception deliberately narrow: generic
|
|
69
|
+
// PascalCase or uppercase authorities remain host-like because an uppercase
|
|
70
|
+
// real domain (for example MICROSOFT.COM/Admin) must not bypass the URL gate.
|
|
71
|
+
const DOTTED_TECHNOLOGY_PATH_AUTHORITIES = new Set([
|
|
72
|
+
"ASP.NET",
|
|
73
|
+
"AWS.SDK",
|
|
74
|
+
"Microsoft.Extensions",
|
|
75
|
+
"System.IO",
|
|
76
|
+
]);
|
|
77
|
+
|
|
78
|
+
const SECRET_ASSIGNMENT_CANDIDATE_PATTERN =
|
|
79
|
+
/(?:^|[^A-Za-z0-9])(?:(['"])([A-Za-z][A-Za-z0-9_. -]*)\1|([A-Za-z][A-Za-z0-9_.-]*))\s*[=:]\s*[^\s,;]+/gu;
|
|
80
|
+
|
|
81
|
+
const SECRET_LABEL_ASSIGNMENT_PATTERN =
|
|
82
|
+
/\b(?:api[ _-]?key|access[ _-]?token|auth[ _-]?token|credential|credentials|password|passwd|private[ _-]?key|secret|token)\b\s*[=:]\s*[^\s,;]+/iu;
|
|
83
|
+
|
|
84
|
+
const SENSITIVE_ASSIGNMENT_KEY_SUFFIXES = new Set([
|
|
85
|
+
"credential",
|
|
86
|
+
"credentials",
|
|
87
|
+
"password",
|
|
88
|
+
"passwd",
|
|
89
|
+
"secret",
|
|
90
|
+
"token",
|
|
91
|
+
]);
|
|
92
|
+
|
|
93
|
+
const COMPACT_SENSITIVE_ASSIGNMENT_KEY_SUFFIXES = [
|
|
94
|
+
"apikey",
|
|
95
|
+
"accesskey",
|
|
96
|
+
"accesskeyid",
|
|
97
|
+
"accesstoken",
|
|
98
|
+
"authtoken",
|
|
99
|
+
"credential",
|
|
100
|
+
"credentials",
|
|
101
|
+
"password",
|
|
102
|
+
"passwd",
|
|
103
|
+
"privatekey",
|
|
104
|
+
"secret",
|
|
105
|
+
"secretkey",
|
|
106
|
+
"token",
|
|
107
|
+
] as const;
|
|
108
|
+
|
|
109
|
+
const SENSITIVE_ASSIGNMENT_KEY_WORD_SUFFIXES = [
|
|
110
|
+
["api", "key"],
|
|
111
|
+
["access", "key"],
|
|
112
|
+
["access", "key", "id"],
|
|
113
|
+
["auth", "key"],
|
|
114
|
+
["private", "key"],
|
|
115
|
+
["secret", "key"],
|
|
116
|
+
] as const;
|
|
117
|
+
|
|
118
|
+
function hasWordSuffix(words: readonly string[], suffix: readonly string[]): boolean {
|
|
119
|
+
if (words.length < suffix.length) return false;
|
|
120
|
+
const offset = words.length - suffix.length;
|
|
121
|
+
return suffix.every((word, index) => words[offset + index] === word);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function containsSensitiveAssignment(value: string): boolean {
|
|
125
|
+
for (const match of value.matchAll(SECRET_ASSIGNMENT_CANDIDATE_PATTERN)) {
|
|
126
|
+
const key = match[2] ?? match[3];
|
|
127
|
+
if (!key) continue;
|
|
128
|
+
const words = key
|
|
129
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
130
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2")
|
|
131
|
+
.toLowerCase()
|
|
132
|
+
.split(/[^a-z0-9]+/)
|
|
133
|
+
.filter(Boolean);
|
|
134
|
+
const last = words.at(-1);
|
|
135
|
+
if (last && SENSITIVE_ASSIGNMENT_KEY_SUFFIXES.has(last)) return true;
|
|
136
|
+
if (last && COMPACT_SENSITIVE_ASSIGNMENT_KEY_SUFFIXES.some((suffix) => last.endsWith(suffix))) {
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
if (SENSITIVE_ASSIGNMENT_KEY_WORD_SUFFIXES.some((suffix) => hasWordSuffix(words, suffix))) {
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function containsUriScheme(value: string): boolean {
|
|
147
|
+
for (const match of value.matchAll(URI_SCHEME_CANDIDATE_PATTERN)) {
|
|
148
|
+
// A Windows drive path is the one scheme-shaped token accepted here. Keep
|
|
149
|
+
// the exception exact: one separator after the drive letter and no later
|
|
150
|
+
// colon, so x://host and a nested scheme remain rejected.
|
|
151
|
+
if (!WINDOWS_DRIVE_PATH_PATTERN.test(match[0])) return true;
|
|
152
|
+
}
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function isDottedTechnologyPath(candidate: string): boolean {
|
|
157
|
+
if (/[?:#]/u.test(candidate)) return false;
|
|
158
|
+
const [authority, ...pathSegments] = candidate.split("/");
|
|
159
|
+
if (!authority || pathSegments.length === 0 || pathSegments.some((segment) => !segment)) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
if (
|
|
163
|
+
!DOTTED_TECHNOLOGY_PATH_AUTHORITIES.has(authority) ||
|
|
164
|
+
pathSegments.some((segment) => !/^[A-Z][A-Za-z0-9_-]*$/u.test(segment))
|
|
165
|
+
) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function containsSchemelessUrl(value: string): boolean {
|
|
172
|
+
if (SCHEMELESS_LOCAL_NETWORK_PATTERNS.some((pattern) => pattern.test(value))) return true;
|
|
173
|
+
|
|
174
|
+
for (const match of value.matchAll(SCHEMELESS_HOST_CANDIDATE_PATTERN)) {
|
|
175
|
+
const candidate = match[0];
|
|
176
|
+
if (candidate.toLowerCase().startsWith("www.")) return true;
|
|
177
|
+
if (!/[/?#]/u.test(candidate)) continue;
|
|
178
|
+
|
|
179
|
+
const authority = candidate.split(/[/?#]/u, 1)[0] ?? "";
|
|
180
|
+
const hostname = authority.replace(/:\d{1,5}$/u, "");
|
|
181
|
+
const suffix = hostname.split(".").at(-1)?.toLowerCase();
|
|
182
|
+
if (suffix && FILE_LIKE_HOST_SUFFIXES.has(suffix)) continue;
|
|
183
|
+
if (isDottedTechnologyPath(candidate)) continue;
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const OPAQUE_IDENTIFIER_PATTERN =
|
|
190
|
+
/\b(?=[A-Za-z0-9_-]{32,}\b)(?=[A-Za-z0-9_-]*[A-Za-z])(?=[A-Za-z0-9_-]*\d)[A-Za-z0-9_-]+\b/u;
|
|
191
|
+
|
|
192
|
+
const DEFAULT_IGNORABLE_CODE_POINTS = /\p{Default_Ignorable_Code_Point}+/gu;
|
|
193
|
+
const ESCAPED_QUOTE_DELIMITERS = /\\+(?=["'])/gu;
|
|
194
|
+
|
|
195
|
+
const TITLE_LABEL_PATTERN =
|
|
196
|
+
/^(?:(?:suggested|generated|concise)\s+)?(?:(?:session|chat|conversation|task)\s+)?title\s*[:\-–—]\s*/iu;
|
|
197
|
+
|
|
198
|
+
const LEADING_BOILERPLATE_PATTERNS = [
|
|
199
|
+
/^(?:hi|hello|hey)(?:\s+there)?\s*[,!:.\-–—]*\s*/iu,
|
|
200
|
+
/^(?:i\s+(?:would\s+like|want|need)\s+you\s+to|i['’]d\s+like\s+you\s+to)\s+/iu,
|
|
201
|
+
/^(?:please\s+)?(?:can|could|would|will)\s+you\s+/iu,
|
|
202
|
+
/^(?:your|the)\s+(?:task|job)\s+is\s+to\s+/iu,
|
|
203
|
+
/^(?:please\s+)?help\s+me\s+(?:to\s+)?/iu,
|
|
204
|
+
/^please(?:\s*[,!:.\-–—]+\s*|\s+|$)/iu,
|
|
205
|
+
] as const;
|
|
206
|
+
|
|
207
|
+
function automaticTitleDetectionValue(value: string): string {
|
|
208
|
+
// Detection uses a compatibility-normalized shadow value so fullwidth
|
|
209
|
+
// punctuation/letters, invisible token splits, and serialized quote
|
|
210
|
+
// delimiters cannot evade the policy.
|
|
211
|
+
// The accepted title itself stays byte-for-byte in the user's language and
|
|
212
|
+
// emoji form; this shadow is never returned or persisted.
|
|
213
|
+
return value
|
|
214
|
+
.normalize("NFKC")
|
|
215
|
+
.replace(DEFAULT_IGNORABLE_CODE_POINTS, "")
|
|
216
|
+
.replace(ESCAPED_QUOTE_DELIMITERS, "");
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function hasVisibleAutomaticTitleContent(value: string): boolean {
|
|
220
|
+
return automaticTitleDetectionValue(value).trim().length > 0;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Whether a bounded automatic-title candidate contains a credential, URL, or
|
|
225
|
+
* opaque identifier that must stay out of navigation and other display-title
|
|
226
|
+
* surfaces. Callers handling an unbounded source must bound it before invoking
|
|
227
|
+
* this helper.
|
|
228
|
+
*/
|
|
229
|
+
export function containsSensitiveAutomaticSessionTitleValue(value: string): boolean {
|
|
230
|
+
const detectionValue = automaticTitleDetectionValue(value);
|
|
231
|
+
|
|
232
|
+
if (KNOWN_SENSITIVE_VALUE_PATTERNS.some((pattern) => pattern.test(detectionValue))) return true;
|
|
233
|
+
if (containsUriScheme(detectionValue)) return true;
|
|
234
|
+
if (containsSchemelessUrl(detectionValue)) return true;
|
|
235
|
+
if (SECRET_LABEL_ASSIGNMENT_PATTERN.test(detectionValue)) return true;
|
|
236
|
+
if (containsSensitiveAssignment(detectionValue)) return true;
|
|
237
|
+
if (OPAQUE_IDENTIFIER_PATTERN.test(detectionValue)) return true;
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function stripAutomaticTitleBoilerplate(value: string): string {
|
|
242
|
+
let title = value;
|
|
243
|
+
for (let pass = 0; pass < 4; pass += 1) {
|
|
244
|
+
const before = title;
|
|
245
|
+
title = title
|
|
246
|
+
.replace(/^(?:```[^\n]*|[\s#>*_`"'“”‘’\-–—]+)+/u, "")
|
|
247
|
+
.replace(TITLE_LABEL_PATTERN, "");
|
|
248
|
+
for (const pattern of LEADING_BOILERPLATE_PATTERNS) {
|
|
249
|
+
title = title.replace(pattern, "");
|
|
250
|
+
}
|
|
251
|
+
title = title.trim();
|
|
252
|
+
if (title === before) break;
|
|
253
|
+
}
|
|
254
|
+
return title;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const AUTOMATIC_TITLE_TRAILING_PUNCTUATION = /[\s.!?,;:\-–—]+$/u;
|
|
258
|
+
|
|
259
|
+
// Closing quote and markdown marks, mapped to the distinct opening mark that
|
|
260
|
+
// would pair with them inside the title.
|
|
261
|
+
const TRAILING_WRAPPER_MARKS = new Map<string, string | null>([
|
|
262
|
+
['"', null],
|
|
263
|
+
["'", null],
|
|
264
|
+
["`", null],
|
|
265
|
+
["*", null],
|
|
266
|
+
["”", "“"],
|
|
267
|
+
["’", "‘"],
|
|
268
|
+
["»", "«"],
|
|
269
|
+
]);
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Remove trailing punctuation and a closing quote or markdown mark whose
|
|
273
|
+
* opening counterpart was already stripped as a leading wrapper, so
|
|
274
|
+
* `"Pod Crash Debugging"` and `**Pod Crash Debugging**` do not keep a dangling
|
|
275
|
+
* closing mark. A mark that still pairs with one inside the title stays.
|
|
276
|
+
*/
|
|
277
|
+
function trimAutomaticTitleEnd(value: string): string {
|
|
278
|
+
let title = value;
|
|
279
|
+
for (let pass = 0; pass < 4; pass += 1) {
|
|
280
|
+
const before = title;
|
|
281
|
+
title = title.replace(AUTOMATIC_TITLE_TRAILING_PUNCTUATION, "");
|
|
282
|
+
const last = title.at(-1);
|
|
283
|
+
const opening = last === undefined ? undefined : TRAILING_WRAPPER_MARKS.get(last);
|
|
284
|
+
if (last !== undefined && opening !== undefined) {
|
|
285
|
+
let rest = title;
|
|
286
|
+
while (rest.endsWith(last)) rest = rest.slice(0, -last.length);
|
|
287
|
+
if (!rest.includes(last) && (opening === null || !rest.includes(opening))) {
|
|
288
|
+
title = rest;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (title === before) break;
|
|
292
|
+
}
|
|
293
|
+
return title.trim();
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function automaticTitleGraphemes(value: string): string[] {
|
|
297
|
+
if (titleSegmenter === undefined) {
|
|
298
|
+
titleSegmenter =
|
|
299
|
+
typeof Intl.Segmenter === "function"
|
|
300
|
+
? new Intl.Segmenter(undefined, { granularity: "grapheme" })
|
|
301
|
+
: null;
|
|
302
|
+
}
|
|
303
|
+
if (titleSegmenter) {
|
|
304
|
+
return Array.from(titleSegmenter.segment(value), (part) => part.segment);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Older embedded runtimes may not ship Intl.Segmenter. Preserve surrogate
|
|
308
|
+
// pairs plus common combining/emoji sequences instead of failing module load
|
|
309
|
+
// or slicing UTF-16 code units.
|
|
310
|
+
const graphemes: string[] = [];
|
|
311
|
+
for (const point of value) {
|
|
312
|
+
const prior = graphemes.at(-1);
|
|
313
|
+
if (
|
|
314
|
+
prior &&
|
|
315
|
+
(/^[\p{Mark}\u{FE0E}\u{FE0F}\p{Emoji_Modifier}]$/u.test(point) ||
|
|
316
|
+
point === "\u200d" ||
|
|
317
|
+
prior.endsWith("\u200d"))
|
|
318
|
+
) {
|
|
319
|
+
graphemes[graphemes.length - 1] = `${prior}${point}`;
|
|
320
|
+
} else {
|
|
321
|
+
graphemes.push(point);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return graphemes;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** Bound an already-normalized automatic-title candidate without splitting graphemes. */
|
|
328
|
+
export function boundAutomaticSessionTitle(value: string): string {
|
|
329
|
+
const words = value.split(/\s+/u);
|
|
330
|
+
const wordBounded = words.length > 10 ? words.slice(0, 10).join(" ") : value;
|
|
331
|
+
const graphemes = automaticTitleGraphemes(wordBounded);
|
|
332
|
+
if (graphemes.length <= AUTOMATIC_SESSION_TITLE_MAX_GRAPHEMES) return wordBounded;
|
|
333
|
+
|
|
334
|
+
const prefix = graphemes.slice(0, AUTOMATIC_SESSION_TITLE_MAX_GRAPHEMES).join("");
|
|
335
|
+
const lastWhitespace = prefix.search(/\s+\S*$/u);
|
|
336
|
+
return (lastWhitespace >= 16 ? prefix.slice(0, lastWhitespace) : prefix).trimEnd();
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Normalize a model/system-authored title before it reaches durable session
|
|
341
|
+
* metadata. Human renames intentionally do not use this path.
|
|
342
|
+
*
|
|
343
|
+
* Returns null when the candidate is empty, only boilerplate, or appears to
|
|
344
|
+
* contain a credential/opaque prompt value. Callers should retain the current
|
|
345
|
+
* title (normally {@link AUTOMATIC_SESSION_TITLE_FALLBACK}) in that case.
|
|
346
|
+
*/
|
|
347
|
+
export function normalizeAutomaticSessionTitle(value: string): string | null {
|
|
348
|
+
const firstLine = value
|
|
349
|
+
.replace(/[\u0000-\u001f\u007f-\u009f]+/gu, "\n")
|
|
350
|
+
.split(/\n+/u)
|
|
351
|
+
.map((line) => line.trim())
|
|
352
|
+
.find(Boolean);
|
|
353
|
+
if (
|
|
354
|
+
!firstLine ||
|
|
355
|
+
!hasVisibleAutomaticTitleContent(firstLine) ||
|
|
356
|
+
containsSensitiveAutomaticSessionTitleValue(firstLine)
|
|
357
|
+
) {
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
let title = trimAutomaticTitleEnd(
|
|
362
|
+
stripAutomaticTitleBoilerplate(firstLine).replace(/\s+/gu, " "),
|
|
363
|
+
);
|
|
364
|
+
if (
|
|
365
|
+
!title ||
|
|
366
|
+
!hasVisibleAutomaticTitleContent(title) ||
|
|
367
|
+
containsSensitiveAutomaticSessionTitleValue(title)
|
|
368
|
+
) {
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
title = trimAutomaticTitleEnd(boundAutomaticSessionTitle(title));
|
|
373
|
+
if (!title || !hasVisibleAutomaticTitleContent(title)) return null;
|
|
374
|
+
return title;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
export type SessionDisplayTitleInput = {
|
|
378
|
+
id?: string | null | undefined;
|
|
379
|
+
title?: string | null | undefined;
|
|
380
|
+
titleSource?: "user" | "agent" | null | undefined;
|
|
381
|
+
initialMessage?: string | null | undefined;
|
|
382
|
+
metadata?: Readonly<Record<string, unknown>> | undefined;
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
export type SessionDisplayTitleOptions = {
|
|
386
|
+
/** Optional metadata fields to try before the opening-prompt preview. */
|
|
387
|
+
metadataKeys?: readonly string[] | undefined;
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Derive a bounded, sensitive-safe preview from the opening prompt.
|
|
392
|
+
*
|
|
393
|
+
* Unsafe leading lines are skipped instead of forcing the whole session back
|
|
394
|
+
* to a generic label. This covers prompts that begin with a pasted URL or
|
|
395
|
+
* identifier followed by an ordinary natural-language request on the next
|
|
396
|
+
* line, without putting the rejected value into navigation surfaces.
|
|
397
|
+
*/
|
|
398
|
+
export function deriveAutomaticSessionTitlePreview(value: unknown): string | null {
|
|
399
|
+
if (typeof value !== "string") return null;
|
|
400
|
+
|
|
401
|
+
const lines = value
|
|
402
|
+
.slice(0, PROMPT_PREVIEW_SCAN_MAX_CODE_UNITS)
|
|
403
|
+
.replace(/[\u0000-\u001f\u007f-\u009f]+/gu, "\n")
|
|
404
|
+
.split(/\n+/u);
|
|
405
|
+
|
|
406
|
+
for (const candidate of lines) {
|
|
407
|
+
const line = candidate.trim();
|
|
408
|
+
if (!line || containsSensitiveAutomaticSessionTitleValue(line)) continue;
|
|
409
|
+
|
|
410
|
+
const preview = boundAutomaticSessionTitle(line.replace(/\s+/gu, " "))
|
|
411
|
+
.replace(/[\s.!?,;:\-–—]+$/u, "")
|
|
412
|
+
.trim();
|
|
413
|
+
if (preview) return preview;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return null;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function automaticSessionReferenceTitle(id: unknown): string {
|
|
420
|
+
const sessionId = typeof id === "string" ? id.trim() : "";
|
|
421
|
+
return SESSION_ID_PATTERN.test(sessionId)
|
|
422
|
+
? `Conversation ${sessionId.slice(0, 13)}`
|
|
423
|
+
: AUTOMATIC_SESSION_TITLE_FALLBACK;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Whether the durable title still represents the automatic-title pending state.
|
|
428
|
+
* A user-authored title always wins, even when its literal value is the marker.
|
|
429
|
+
*/
|
|
430
|
+
export function sessionTitleIsPending(input: SessionDisplayTitleInput): boolean {
|
|
431
|
+
const title = input.title?.trim() ?? "";
|
|
432
|
+
return input.titleSource !== "user" && (!title || title === AUTOMATIC_SESSION_TITLE_FALLBACK);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Derive the title a human-facing client should display for a session.
|
|
437
|
+
*
|
|
438
|
+
* A semantic agent title or human rename wins. While automatic naming is still
|
|
439
|
+
* pending, clients show a short, sensitive-safe preview of the opening prompt.
|
|
440
|
+
* If no safe prompt text exists, a UUID-derived reference keeps real sessions
|
|
441
|
+
* distinguishable without exposing prompt bytes. The durable pending marker is
|
|
442
|
+
* therefore an internal lifecycle value rather than the ordinary visible name.
|
|
443
|
+
*/
|
|
444
|
+
export function deriveSessionDisplayTitle(
|
|
445
|
+
input: SessionDisplayTitleInput,
|
|
446
|
+
options: SessionDisplayTitleOptions = {},
|
|
447
|
+
): string {
|
|
448
|
+
const title = input.title?.trim() ?? "";
|
|
449
|
+
if (input.titleSource === "user") {
|
|
450
|
+
return title || automaticSessionReferenceTitle(input.id);
|
|
451
|
+
}
|
|
452
|
+
if (title && !sessionTitleIsPending(input)) {
|
|
453
|
+
return title;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
for (const key of options.metadataKeys ?? []) {
|
|
457
|
+
const value = input.metadata?.[key];
|
|
458
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
459
|
+
return value.trim();
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return (
|
|
464
|
+
deriveAutomaticSessionTitlePreview(input.initialMessage) ??
|
|
465
|
+
automaticSessionReferenceTitle(input.id)
|
|
466
|
+
);
|
|
467
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const SessionStatus = z.enum([
|
|
4
|
+
"queued",
|
|
5
|
+
"running",
|
|
6
|
+
"idle",
|
|
7
|
+
"requires_action",
|
|
8
|
+
"recovering",
|
|
9
|
+
"waiting_capacity",
|
|
10
|
+
"failed",
|
|
11
|
+
"cancelled",
|
|
12
|
+
]);
|
|
13
|
+
export type SessionStatus = z.infer<typeof SessionStatus>;
|
|
14
|
+
|
|
15
|
+
export const SessionGoalStatus = z.enum(["active", "paused", "completed"]);
|
|
16
|
+
export type SessionGoalStatus = z.infer<typeof SessionGoalStatus>;
|
|
17
|
+
|
|
18
|
+
/** Physical ceiling of the PostgreSQL integer columns that persist depth policy. */
|
|
19
|
+
export const MAX_NESTED_AGENT_DEPTH = 2_147_483_647;
|
|
20
|
+
export const NestedAgentDepthValue = z.number().int().nonnegative().max(MAX_NESTED_AGENT_DEPTH);
|
|
21
|
+
export type NestedAgentDepthValue = z.infer<typeof NestedAgentDepthValue>;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* First-touch marketing parameters accepted on the web app landing URL. The
|
|
5
|
+
* browser keeps them in memory only and forwards them with a sign-up request,
|
|
6
|
+
* so nothing is written to the device before analytics consent.
|
|
7
|
+
*/
|
|
8
|
+
export const SIGNUP_ATTRIBUTION_URL_PARAMETERS = {
|
|
9
|
+
utmSource: "utm_source",
|
|
10
|
+
utmMedium: "utm_medium",
|
|
11
|
+
utmCampaign: "utm_campaign",
|
|
12
|
+
utmContent: "utm_content",
|
|
13
|
+
ref: "ref",
|
|
14
|
+
} as const;
|
|
15
|
+
|
|
16
|
+
/** Maximum retained length of one attribution value. */
|
|
17
|
+
export const SIGNUP_ATTRIBUTION_VALUE_MAX_LENGTH = 100;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Campaign labels are marketer-chosen slug tokens such as `hero-cta` or
|
|
21
|
+
* `opengeni.ai`. The closed character set has no space, `@`, `:`, `/`, `?`,
|
|
22
|
+
* `=`, or `%`, so free text, email addresses, and URL payloads are rejected
|
|
23
|
+
* before they reach any sink. The web boot capture and the session-set social
|
|
24
|
+
* start request mirror this rule; tests pin each copy to it.
|
|
25
|
+
*/
|
|
26
|
+
export const SIGNUP_ATTRIBUTION_VALUE_PATTERN = /^[A-Za-z0-9._~+-]+$/;
|
|
27
|
+
|
|
28
|
+
const SignupAttributionValue = z
|
|
29
|
+
.string()
|
|
30
|
+
.trim()
|
|
31
|
+
.min(1)
|
|
32
|
+
.max(SIGNUP_ATTRIBUTION_VALUE_MAX_LENGTH)
|
|
33
|
+
.regex(SIGNUP_ATTRIBUTION_VALUE_PATTERN);
|
|
34
|
+
|
|
35
|
+
export const SignupAttribution = z
|
|
36
|
+
.object({
|
|
37
|
+
utmSource: SignupAttributionValue.optional(),
|
|
38
|
+
utmMedium: SignupAttributionValue.optional(),
|
|
39
|
+
utmCampaign: SignupAttributionValue.optional(),
|
|
40
|
+
utmContent: SignupAttributionValue.optional(),
|
|
41
|
+
ref: SignupAttributionValue.optional(),
|
|
42
|
+
})
|
|
43
|
+
.strict();
|
|
44
|
+
export type SignupAttribution = z.infer<typeof SignupAttribution>;
|
|
45
|
+
|
|
46
|
+
/** Closed acquisition channels reported by server-side sign-up metrics. */
|
|
47
|
+
export const SIGNUP_ACQUISITION_SOURCES = ["producthunt", "website", "direct", "other"] as const;
|
|
48
|
+
export type SignupAcquisitionSource = (typeof SIGNUP_ACQUISITION_SOURCES)[number];
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Normalize untrusted first-touch attribution to a closed channel. Missing
|
|
52
|
+
* attribution is `direct`; malformed or unrecognized attribution is `other`.
|
|
53
|
+
*/
|
|
54
|
+
export function signupAcquisitionSource(input: unknown): SignupAcquisitionSource {
|
|
55
|
+
if (input === undefined || input === null) return "direct";
|
|
56
|
+
const parsed = SignupAttribution.safeParse(input);
|
|
57
|
+
if (!parsed.success) return "other";
|
|
58
|
+
const attribution = parsed.data;
|
|
59
|
+
const values = Object.values(attribution).filter((value) => value !== undefined);
|
|
60
|
+
if (values.length === 0) return "direct";
|
|
61
|
+
const source = attribution.utmSource?.toLowerCase();
|
|
62
|
+
const medium = attribution.utmMedium?.toLowerCase();
|
|
63
|
+
const ref = attribution.ref?.toLowerCase();
|
|
64
|
+
if ([source, ref].some((value) => value !== undefined && isProductHunt(value))) {
|
|
65
|
+
return "producthunt";
|
|
66
|
+
}
|
|
67
|
+
if (
|
|
68
|
+
source === "opengeni.ai" ||
|
|
69
|
+
source?.endsWith(".opengeni.ai") ||
|
|
70
|
+
source === "opengeni" ||
|
|
71
|
+
medium === "website"
|
|
72
|
+
) {
|
|
73
|
+
return "website";
|
|
74
|
+
}
|
|
75
|
+
return "other";
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function isProductHunt(value: string): boolean {
|
|
79
|
+
const compact = value.replace(/[\s._-]/g, "");
|
|
80
|
+
return compact === "ph" || compact.includes("producthunt");
|
|
81
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import { siteRequestHeaders } from "./site-session-http";
|
|
3
|
+
|
|
4
|
+
test("Site forwarding preserves application headers and removes host authority and transport", () => {
|
|
5
|
+
const input = {
|
|
6
|
+
range: "bytes=0-1023",
|
|
7
|
+
"if-match": '"version"',
|
|
8
|
+
"idempotency-key": "operation",
|
|
9
|
+
"x-opengeni-chunk-sha256": "digest",
|
|
10
|
+
"x-custom-option": "value",
|
|
11
|
+
authorization: "forged",
|
|
12
|
+
cookie: "session=forged",
|
|
13
|
+
"x-opengeni-access-key": "forged",
|
|
14
|
+
"x-opengeni-external-actor": "forged",
|
|
15
|
+
"x-opengeni-site-id": "forged",
|
|
16
|
+
connection: "x-private-hop",
|
|
17
|
+
"x-private-hop": "transport",
|
|
18
|
+
"content-length": "999",
|
|
19
|
+
};
|
|
20
|
+
const headers = siteRequestHeaders(input);
|
|
21
|
+
expect(Object.fromEntries(headers)).toEqual({
|
|
22
|
+
range: "bytes=0-1023",
|
|
23
|
+
"if-match": '"version"',
|
|
24
|
+
"idempotency-key": "operation",
|
|
25
|
+
"x-opengeni-chunk-sha256": "digest",
|
|
26
|
+
"x-custom-option": "value",
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const SITE_WORKSPACE_PREFIX = "/v1/workspaces/site-host";
|
|
2
|
+
|
|
3
|
+
/** A Site/Codemode SDK request named a route outside the proxied surface. */
|
|
4
|
+
export class SiteSessionPathError extends Error {
|
|
5
|
+
readonly code = "site_session_path_unsupported";
|
|
6
|
+
|
|
7
|
+
constructor() {
|
|
8
|
+
super("Unsupported Site session API path");
|
|
9
|
+
this.name = "SiteSessionPathError";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Route browser/preview SDK requests to the host workspace. This is a URL
|
|
14
|
+
* boundary, not an endpoint or HTTP-method allowlist. Ordinary API handlers
|
|
15
|
+
* authorize every operation with the viewer or agent's existing authority. */
|
|
16
|
+
export function siteSessionPath(
|
|
17
|
+
path: string,
|
|
18
|
+
workspaceId: string,
|
|
19
|
+
_method = "GET",
|
|
20
|
+
siteId?: string,
|
|
21
|
+
): string {
|
|
22
|
+
const pathname = path.split("?")[0]!;
|
|
23
|
+
const workspace =
|
|
24
|
+
pathname === SITE_WORKSPACE_PREFIX || pathname.startsWith(`${SITE_WORKSPACE_PREFIX}/`);
|
|
25
|
+
const context = pathname === "/v1/config/client";
|
|
26
|
+
if (
|
|
27
|
+
(!workspace && !context) ||
|
|
28
|
+
/[%\\#\u0000-\u0020\u007f]/u.test(pathname) ||
|
|
29
|
+
pathname.split("/").some((p) => p === "." || p === "..")
|
|
30
|
+
) {
|
|
31
|
+
throw new SiteSessionPathError();
|
|
32
|
+
}
|
|
33
|
+
const rewritten = path.replace(
|
|
34
|
+
"/workspaces/site-host",
|
|
35
|
+
`/workspaces/${encodeURIComponent(workspaceId)}`,
|
|
36
|
+
);
|
|
37
|
+
// Preview has no published Site identity. Never widen a Site-only list to the workspace.
|
|
38
|
+
const url = new URL(rewritten, "http://site.invalid");
|
|
39
|
+
if (url.searchParams.get("originSiteId") === "current" && workspaceId !== "site-host") {
|
|
40
|
+
url.searchParams.set("originSiteId", siteId ?? "00000000-0000-0000-0000-000000000000");
|
|
41
|
+
return url.pathname + url.search;
|
|
42
|
+
}
|
|
43
|
+
return rewritten;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Preserve SDK request semantics while the host supplies identity and transport headers. */
|
|
47
|
+
export function siteRequestHeaders(input: HeadersInit): Headers {
|
|
48
|
+
const headers = new Headers(input);
|
|
49
|
+
const connectionHeaders = (headers.get("connection") ?? "")
|
|
50
|
+
.split(",")
|
|
51
|
+
.map((value) => value.trim().toLowerCase());
|
|
52
|
+
for (const name of [...headers.keys()]) {
|
|
53
|
+
if (
|
|
54
|
+
connectionHeaders.includes(name) ||
|
|
55
|
+
/^(authorization|proxy-authorization|cookie|cookie2|host|origin|referer|connection|keep-alive|proxy-connection|te|trailer|transfer-encoding|upgrade|content-length|accept-encoding|forwarded|x-api-key)$/i.test(
|
|
56
|
+
name,
|
|
57
|
+
) ||
|
|
58
|
+
/^(sec-|x-forwarded-|x-opengeni-(access-key|external-actor|site-|managed-actor))/i.test(name)
|
|
59
|
+
)
|
|
60
|
+
headers.delete(name);
|
|
61
|
+
}
|
|
62
|
+
return headers;
|
|
63
|
+
}
|