@danceiny/gotry 0.0.1-rc.21 → 0.0.1-rc.24
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 +102 -194
- package/README.zh-CN.md +115 -210
- package/bin/gotry-backend.js +53 -0
- package/bin/gotry-bootstrap.js +466 -35
- package/bin/gotry-inner.js +255 -36
- package/bin/gotry-process-liveness.d.ts +27 -0
- package/bin/gotry-process-liveness.js +97 -0
- package/bin/gotry-runtime-resolution.d.ts +1 -1
- package/bin/gotry-runtime-resolution.js +2 -2
- package/client/client.js +174 -0
- package/cordis.gotry-patch.yml +70 -93
- package/dist/capabilities/agent-reach-deep.js +45 -122
- package/dist/capabilities/agent-reach.js +63 -148
- package/dist/capabilities/anything.js +89 -75
- package/dist/capabilities/artifacts.js +123 -104
- package/dist/capabilities/channel-health.js +65 -45
- package/dist/capabilities/channel-registry.js +108 -87
- package/dist/capabilities/doctor.js +357 -235
- package/dist/capabilities/effect.js +223 -309
- package/dist/capabilities/fact-log.js +28 -14
- package/dist/capabilities/flyai.js +245 -156
- package/dist/capabilities/ground-transfer.js +509 -0
- package/dist/capabilities/hbcli.js +144 -148
- package/dist/capabilities/hotelbyte-transaction.js +194 -0
- package/dist/capabilities/incident-log.js +131 -61
- package/dist/capabilities/model-override.js +30 -10
- package/dist/capabilities/opensky.js +54 -43
- package/dist/capabilities/resilience.js +50 -32
- package/dist/capabilities/session/action-cache.js +50 -44
- package/dist/capabilities/session/adapters/ctrip-flight.js +119 -62
- package/dist/capabilities/session/adapters/ctrip-hotel.js +125 -110
- package/dist/capabilities/session/adapters/dida-portal.js +206 -0
- package/dist/capabilities/session/adapters/meituan-local.js +35 -54
- package/dist/capabilities/session/adapters/rail-12306.js +169 -112
- package/dist/capabilities/session/benchmark.js +85 -149
- package/dist/capabilities/session/extension-bridge.js +320 -259
- package/dist/capabilities/session/extension-channel.js +57 -76
- package/dist/capabilities/session/extension-distribution.js +115 -119
- package/dist/capabilities/session/extract.js +31 -21
- package/dist/capabilities/session/golden-score.js +55 -33
- package/dist/capabilities/session/health-watch.js +146 -154
- package/dist/capabilities/session/read-guard.js +55 -47
- package/dist/capabilities/session/static-flight-golden.js +49 -36
- package/dist/capabilities/session/transport.js +54 -61
- package/dist/capabilities/session/wizard.js +63 -93
- package/dist/capabilities/session-consent.js +143 -47
- package/dist/capabilities/session-login.js +95 -126
- package/dist/capabilities/session-search.js +391 -253
- package/dist/capabilities/sponsor-plugin.js +341 -0
- package/dist/capabilities/visa-policy.js +87 -73
- package/dist/capabilities/weather.js +163 -235
- package/dist/scripts/action-cache-tests.js +24 -59
- package/dist/scripts/agent-planning-turn-deadline-e2e.js +91 -142
- package/dist/scripts/agent-planning-turn-deadline-tests.js +80 -176
- package/dist/scripts/agent-reach-deep-tests.js +30 -40
- package/dist/scripts/agent-reach-tests.js +26 -18
- package/dist/scripts/agent-reach-wrapper-tests.js +46 -50
- package/dist/scripts/anything-tests.js +58 -62
- package/dist/scripts/artifact-client-contract-tests.js +69 -0
- package/dist/scripts/artifacts-capability-tests.js +235 -0
- package/dist/scripts/async-collect.js +26 -15
- package/dist/scripts/benchmark-environment-bridge-e2e.js +287 -684
- package/dist/scripts/benchmark-environment-bridge-tests.js +818 -3123
- package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +66 -279
- package/dist/scripts/booking-copilot-availability-policy-tests.js +153 -1257
- package/dist/scripts/booking-copilot-bin-proof-tests.js +22 -45
- package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +64 -76
- package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +69 -199
- package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +393 -759
- package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +33 -75
- package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +60 -110
- package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +28 -66
- package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +72 -271
- package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +73 -275
- package/dist/scripts/booking-copilot-runtime-proof-tests.js +784 -3398
- package/dist/scripts/booking-copilot-server-proof-tests.js +63 -168
- package/dist/scripts/booking-copilot-startup-proof-tests.js +38 -124
- package/dist/scripts/booking-executor-tests.js +80 -0
- package/dist/scripts/booking-recovery-chain-tests.js +182 -0
- package/dist/scripts/booking-saga-tests.js +61 -123
- package/dist/scripts/booking-surface-contract-proof-tests.js +152 -1167
- package/dist/scripts/bootstrap-tests.js +1383 -128
- package/dist/scripts/build-changelog.js +89 -130
- package/dist/scripts/build-metrics-report.js +135 -143
- package/dist/scripts/changelog-tests.js +35 -82
- package/dist/scripts/channel-probe-tests.js +44 -116
- package/dist/scripts/channel-probe.js +84 -167
- package/dist/scripts/channel-registry-tests.js +97 -185
- package/dist/scripts/companion-tests.js +25 -70
- package/dist/scripts/dida-cookie-probe.js +87 -0
- package/dist/scripts/dida-sw-probe.js +114 -0
- package/dist/scripts/diff-test.js +17 -12
- package/dist/scripts/doctor-tests.js +428 -120
- package/dist/scripts/dsh-artifact-e2e.js +150 -0
- package/dist/scripts/dsh-artifact-web-e2e.js +443 -0
- package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
- package/dist/scripts/dsh-runtime-closure-tests.js +110 -153
- package/dist/scripts/dsh-runtime-closure.js +75 -63
- package/dist/scripts/dsh-session-v3-migration-proof.js +106 -0
- package/dist/scripts/dsh-subprocess-local-proof.js +149 -0
- package/dist/scripts/dsh-target-closure-proof.js +178 -0
- package/dist/scripts/effect-tests.js +165 -552
- package/dist/scripts/engine-run.js +1 -3
- package/dist/scripts/engine-tests.js +16 -10
- package/dist/scripts/evaluation-cadence-tests.js +69 -242
- package/dist/scripts/evaluation-contract-tests.js +193 -499
- package/dist/scripts/extension-distribution-cli.js +19 -15
- package/dist/scripts/extension-distribution-tests.js +98 -211
- package/dist/scripts/extension-tests.js +366 -389
- package/dist/scripts/fact-gate-tests.js +725 -263
- package/dist/scripts/fixtures/extension-bridge-unref-child.mjs +21 -0
- package/dist/scripts/flight-hotel-anchor-363-registered-e2e.js +169 -0
- package/dist/scripts/flight-malformed-tests.js +159 -0
- package/dist/scripts/flyai-tests.js +325 -68
- package/dist/scripts/g5-guard-tests.js +131 -0
- package/dist/scripts/g5-guard.js +189 -0
- package/dist/scripts/gen-lunar-anchors.js +106 -0
- package/dist/scripts/gotry-backend-kernel-tests.js +79 -0
- package/dist/scripts/ground-transfer-tests.js +956 -0
- package/dist/scripts/hbcli-e2e-tests.js +71 -90
- package/dist/scripts/hbcli-live-optin-tests.js +96 -0
- package/dist/scripts/hbcli-release-contract-tests.js +122 -0
- package/dist/scripts/hbcli-tests.js +41 -115
- package/dist/scripts/health-watch-cli.js +42 -28
- package/dist/scripts/hotel-date-gate-tests.js +496 -0
- package/dist/scripts/hotelbyte-reconcile-tests.js +141 -0
- package/dist/scripts/hotelbyte-spawn-e2e-tests.js +373 -0
- package/dist/scripts/i18n-tests.js +16 -9
- package/dist/scripts/incident-tests.js +281 -87
- package/dist/scripts/issue-194-job-id-guard-tests.js +106 -0
- package/dist/scripts/issue-233-cancel-refund-commission-tests.js +392 -0
- package/dist/scripts/issue-271-liveness-tests.js +547 -0
- package/dist/scripts/issue-289-model-retry-real-tests.js +296 -0
- package/dist/scripts/issue-289-model-retry-tests.js +441 -0
- package/dist/scripts/issue-289-web-retry-e2e.js +630 -0
- package/dist/scripts/issue-338-home-city-e2e.js +577 -0
- package/dist/scripts/issue343-real-entry-e2e.js +160 -0
- package/dist/scripts/journey-tests.js +22 -27
- package/dist/scripts/kernel-manifest-evidence.js +109 -0
- package/dist/scripts/kernel-manifest-gate.js +126 -0
- package/dist/scripts/kernel-manifest-tests.js +212 -0
- package/dist/scripts/kernel-manifest-trace-entry.js +7 -0
- package/dist/scripts/kernel-manifest-trace-hook.mjs +28 -0
- package/dist/scripts/kernel-manifest.js +369 -0
- package/dist/scripts/ledger-repair-apply-tests.js +316 -0
- package/dist/scripts/ledger-repair-plan-tests.js +375 -0
- package/dist/scripts/ledger-tests.js +325 -340
- package/dist/scripts/ledger-workflow-crash.js +10 -20
- package/dist/scripts/map-tools-vendor-package-proof.js +206 -134
- package/dist/scripts/memory-capture-tests.js +242 -61
- package/dist/scripts/memory-decay-tests.js +18 -52
- package/dist/scripts/memory-lifecycle-tests.js +657 -0
- package/dist/scripts/memory-lifecycle.js +196 -0
- package/dist/scripts/memory-metrics.js +24 -12
- package/dist/scripts/memory-value-report.js +474 -222
- package/dist/scripts/memory-value-tests.js +328 -0
- package/dist/scripts/metrics-report-tests.js +62 -199
- package/dist/scripts/model-override-e2e.js +66 -105
- package/dist/scripts/nightly-evidence-tests.js +64 -89
- package/dist/scripts/nightly-evidence.js +108 -111
- package/dist/scripts/nudge-digest.js +39 -28
- package/dist/scripts/onboarding-tests.js +63 -88
- package/dist/scripts/opensky-check.js +18 -13
- package/dist/scripts/opensky-tests.js +28 -28
- package/dist/scripts/persona-surface-guard-tests.js +37 -12
- package/dist/scripts/pnpm-dsh-closure-proof.js +2 -5
- package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
- package/dist/scripts/price-drift-tests.js +126 -227
- package/dist/scripts/price-drift-watch.js +209 -253
- package/dist/scripts/probe-poi-tests.js +52 -45
- package/dist/scripts/product-metrics.js +187 -273
- package/dist/scripts/publish-preverify.js +47 -41
- package/dist/scripts/realtime-pricing-tests.js +37 -76
- package/dist/scripts/replay-async.js +20 -18
- package/dist/scripts/replay-real.js +16 -18
- package/dist/scripts/replay.js +52 -82
- package/dist/scripts/session-attach-diagnose.js +16 -12
- package/dist/scripts/session-attach-poc.js +34 -37
- package/dist/scripts/session-benchmark.js +66 -147
- package/dist/scripts/session-dida-live-e2e.js +151 -0
- package/dist/scripts/session-extract-tests.js +14 -35
- package/dist/scripts/session-login.js +14 -13
- package/dist/scripts/session-tests.js +545 -583
- package/dist/scripts/sf-live-benchmark.js +92 -110
- package/dist/scripts/sf-live-cli-tests.js +2 -9
- package/dist/scripts/sf-soft-score-tests.js +19 -60
- package/dist/scripts/sf-summary-tests.js +149 -0
- package/dist/scripts/sf-summary.js +466 -84
- package/dist/scripts/skeleton-check.js +16 -14
- package/dist/scripts/skeleton-integration-test.js +11 -10
- package/dist/scripts/skills-contract-tests.js +51 -46
- package/dist/scripts/smoke-session-gate-tests.js +12 -20
- package/dist/scripts/smoke.js +541 -422
- package/dist/scripts/sponsor-reuse-tests.js +262 -0
- package/dist/scripts/state-cli-tests.js +226 -101
- package/dist/scripts/state-cli.js +549 -212
- package/dist/scripts/static-golden-tests.js +46 -180
- package/dist/scripts/time-eval-tests.js +210 -192
- package/dist/scripts/travel-timeline-tests.js +29 -93
- package/dist/scripts/turn-handoff-collect-tests.js +46 -93
- package/dist/scripts/turn-handoff-collect.js +71 -99
- package/dist/scripts/turn-policy-tests.js +28 -29
- package/dist/scripts/typed-contract-canary.js +77 -162
- package/dist/scripts/tz-resolver-sanity.js +50 -0
- package/dist/scripts/tz-resolver-tests.js +584 -0
- package/dist/scripts/unified-tests.js +23 -19
- package/dist/scripts/visa-policy-tests.js +31 -44
- package/dist/scripts/weather-tests.js +121 -543
- package/dist/scripts/wish-channel-gate-tests.js +29 -86
- package/dist/scripts/write-gate-tests.js +522 -0
- package/dist/scripts/z3-lifecycle-fault-tests.js +188 -0
- package/dist/scripts/z3-lifecycle-tests.js +170 -0
- package/dist/scripts/z3-race-repeat-tests.js +39 -0
- package/dist/scripts/z3-race-tests.js +18 -12
- package/dist/src/artifact-gate.js +652 -221
- package/dist/src/backend/kernel.js +91 -0
- package/dist/src/backend/modules/booking-copilot.js +55 -0
- package/dist/src/backend/modules/booking-executor.js +124 -0
- package/dist/src/backend/modules/session-search.js +194 -0
- package/dist/src/benchmark-agent-conformance.js +296 -262
- package/dist/src/benchmark-environment-bridge.js +298 -413
- package/dist/src/benchmark-headless-child-diagnostics.js +93 -100
- package/dist/src/benchmark-tool-isolation.js +94 -67
- package/dist/src/bookable-facts.js +299 -174
- package/dist/src/booking-saga.js +64 -120
- package/dist/src/booking-surface/availability-policy.js +443 -576
- package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
- package/dist/src/booking-surface/contracts.js +24 -80
- package/dist/src/booking-surface/dsh-planner.js +510 -281
- package/dist/src/booking-surface/dsh-plugin.js +17 -1
- package/dist/src/booking-surface/error-codes.js +82 -10
- package/dist/src/booking-surface/index.js +0 -3
- package/dist/src/booking-surface/profile.js +10 -54
- package/dist/src/booking-surface/recovery-chain.js +367 -0
- package/dist/src/booking-surface/runtime.js +765 -891
- package/dist/src/booking-surface/server.js +200 -251
- package/dist/src/booking-surface/startup.js +58 -54
- package/dist/src/booking-surface/validation.js +150 -218
- package/dist/src/bridge.js +20 -13
- package/dist/src/companions.js +48 -74
- package/dist/src/contracts.js +21 -43
- package/dist/src/dsh-llm.js +82 -120
- package/dist/src/engine.js +253 -239
- package/dist/src/evaluation-cadence.js +72 -209
- package/dist/src/evaluation-contracts.js +220 -843
- package/dist/src/flight-pack-adapter.js +54 -0
- package/dist/src/flight-pack-contract.js +2 -0
- package/dist/src/gotry-backend.js +70 -0
- package/dist/src/hotel-date-gate.js +118 -0
- package/dist/src/i18n.js +18 -7
- package/dist/src/index.js +1425 -1629
- package/dist/src/journey.js +132 -81
- package/dist/src/ledger-repair-apply.js +538 -0
- package/dist/src/ledger-repair-plan.js +463 -0
- package/dist/src/loop.js +257 -166
- package/dist/src/memory-capture.js +180 -32
- package/dist/src/memory-decay.js +30 -14
- package/dist/src/memory-lifecycle.js +1310 -0
- package/dist/src/memory-utility.js +27 -40
- package/dist/src/mock-llm.js +36 -61
- package/dist/src/model.js +81 -36
- package/dist/src/realtime-pricing.js +63 -72
- package/dist/src/slot-spec.js +84 -106
- package/dist/src/state-ledger.js +310 -471
- package/dist/src/subagent-job-id-guard.js +58 -0
- package/dist/src/time-anchor.js +231 -33
- package/dist/src/tool-packet.js +20 -8
- package/dist/src/travel-slots.js +58 -35
- package/dist/src/travel-timeline.js +44 -63
- package/dist/src/turn-deadline.js +121 -127
- package/dist/src/turn-policy.js +61 -89
- package/dist/src/tz-resolver.js +143 -0
- package/dist/src/unified.js +504 -381
- package/dist/src/wish-pool.js +39 -14
- package/dist/src/write-gate.js +495 -0
- package/dist/src/z3-shared.js +273 -7
- package/extension/README.md +4 -3
- package/extension/background.js +125 -14
- package/extension/content-bridge.js +23 -1
- package/extension/content-main.js +15 -4
- package/extension/manifest.json +16 -8
- package/package.json +251 -220
- package/ts/capabilities/agent-reach.ts +4 -4
- package/ts/capabilities/anything.ts +1 -1
- package/ts/capabilities/artifacts.ts +46 -9
- package/ts/capabilities/flyai.ts +156 -37
- package/ts/capabilities/hbcli.ts +1 -1
- package/ts/capabilities/incident-log.ts +88 -59
- package/ts/capabilities/session/adapters/ctrip-flight.ts +99 -36
- package/ts/capabilities/session/adapters/dida-portal.ts +250 -0
- package/ts/capabilities/session/adapters/rail-12306.ts +123 -24
- package/ts/capabilities/session/extension-bridge.ts +226 -101
- package/ts/capabilities/session/extension-channel.ts +22 -9
- package/ts/capabilities/session/wizard.ts +2 -2
- package/ts/capabilities/session-consent.ts +98 -14
- package/ts/capabilities/session-login.ts +1 -0
- package/ts/capabilities/session-search.ts +315 -31
- package/ts/dsh-runtime/vendor/README.md +8 -4
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +7 -1
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +5 -3
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +1 -1
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +1 -1
- package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +6 -6
- package/ts/package.json +236 -16
- package/ts/scripts/map-tools-vendor-package-proof.ts +152 -31
- package/ts/scripts/state-cli.ts +418 -51
- package/ts/src/artifact-gate.ts +561 -15
- package/ts/src/benchmark-environment-bridge.ts +37 -20
- package/ts/src/bookable-facts.ts +183 -7
- package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
- package/ts/src/booking-surface/dsh-planner.ts +240 -32
- package/ts/src/booking-surface/dsh-plugin.js +17 -1
- package/ts/src/booking-surface/error-codes.ts +75 -0
- package/ts/src/booking-surface/recovery-chain.ts +409 -0
- package/ts/src/booking-surface/server.ts +84 -51
- package/ts/src/booking-surface/validation.ts +2 -0
- package/ts/src/contracts.ts +12 -2
- package/ts/src/dsh-llm.ts +4 -8
- package/ts/src/index.ts +609 -90
- package/ts/src/loop.ts +116 -7
- package/ts/src/memory-capture.ts +227 -3
- package/ts/src/mock-llm.ts +4 -8
- package/ts/src/model.ts +88 -2
- package/ts/src/state-ledger.ts +47 -29
- package/ts/src/subagent-job-id-guard.ts +72 -0
- package/ts/src/time-anchor.ts +217 -13
- package/ts/src/unified.ts +316 -54
|
@@ -2,37 +2,32 @@ import { readFileSync } from 'node:fs';
|
|
|
2
2
|
import { Ajv2020 } from 'ajv/dist/2020.js';
|
|
3
3
|
import { BOOKING_READ_ACTION_KINDS, BOOKING_BLOCKER_CODES, BOOKING_GAP_CODES } from './contracts.js';
|
|
4
4
|
const schema = JSON.parse(readFileSync(new URL('../../../schemas/booking.surface.schema.json', import.meta.url), 'utf8'));
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
});
|
|
5
|
+
/** Canonical schema bytes (single source of truth) for prompt/tool derivations elsewhere. */
|
|
6
|
+
export const bookingSurfaceSchema = schema;
|
|
7
|
+
const ajv = new Ajv2020({ allErrors: true, strict: true });
|
|
9
8
|
ajv.addSchema(schema);
|
|
10
9
|
const validate = ajv.compile(schema);
|
|
11
|
-
const validateActionCanonical = ajv.compile({
|
|
12
|
-
|
|
13
|
-
});
|
|
14
|
-
const
|
|
15
|
-
$ref: `${schema.$id}#/$defs/Blocker`
|
|
16
|
-
});
|
|
17
|
-
const validateApprovalCanonical = ajv.compile({
|
|
18
|
-
$ref: `${schema.$id}#/$defs/Approval`
|
|
19
|
-
});
|
|
20
|
-
const validateEventCanonical = ajv.compile({
|
|
21
|
-
$ref: `${schema.$id}#/$defs/Event`
|
|
22
|
-
});
|
|
10
|
+
const validateActionCanonical = ajv.compile({ $ref: `${schema.$id}#/$defs/Action` });
|
|
11
|
+
const validateBlockerCanonical = ajv.compile({ $ref: `${schema.$id}#/$defs/Blocker` });
|
|
12
|
+
const validateApprovalCanonical = ajv.compile({ $ref: `${schema.$id}#/$defs/Approval` });
|
|
13
|
+
const validateEventCanonical = ajv.compile({ $ref: `${schema.$id}#/$defs/Event` });
|
|
23
14
|
const kinds = new Set(BOOKING_READ_ACTION_KINDS);
|
|
24
15
|
const codes = new Set(BOOKING_BLOCKER_CODES);
|
|
25
16
|
const gapCodes = new Set(BOOKING_GAP_CODES);
|
|
26
|
-
const safeOpaque = (value)=>typeof value === 'string' && /^[A-Za-z0-9][A-Za-z0-9:._-]*$/.test(value);
|
|
27
|
-
const safeRequestKey = (value)=>typeof value === 'string' && /^[A-Za-z0-9][A-Za-z0-9:._-]{7,127}$/.test(value) && !secret.test(value);
|
|
17
|
+
const safeOpaque = (value) => typeof value === 'string' && /^[A-Za-z0-9][A-Za-z0-9:._-]*$/.test(value);
|
|
18
|
+
const safeRequestKey = (value) => typeof value === 'string' && /^[A-Za-z0-9][A-Za-z0-9:._-]{7,127}$/.test(value) && !secret.test(value);
|
|
28
19
|
const secret = /(?:Bearer\s+\S|\bsk-(?!ill(?:s)?\b)[A-Za-z0-9_-]{12,}|\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)/i;
|
|
20
|
+
// `.example` is reserved for documentation, so these identifiers cannot be a
|
|
21
|
+
// real mailbox. Do not allowlist lookalikes on routable domains.
|
|
29
22
|
const technicalEmail = /^(?:schema|version)@[a-z0-9-]+\.example$/i;
|
|
30
23
|
const currencyAmount = /\b(?:[A-Z]{3}\s*\d+(?:\.\d+)?|\d+(?:\.\d+)?\s*[A-Z]{3})\b/;
|
|
31
24
|
const rfc3339DateTime = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,9}))?(Z|([+-])(\d{2}):(\d{2}))$/;
|
|
32
25
|
export function isBookingDateTime(value) {
|
|
33
|
-
if (typeof value !== 'string')
|
|
26
|
+
if (typeof value !== 'string')
|
|
27
|
+
return false;
|
|
34
28
|
const match = rfc3339DateTime.exec(value);
|
|
35
|
-
if (!match)
|
|
29
|
+
if (!match)
|
|
30
|
+
return false;
|
|
36
31
|
const [, yearText, monthText, dayText, hourText, minuteText, secondText] = match;
|
|
37
32
|
const year = Number(yearText);
|
|
38
33
|
const month = Number(monthText);
|
|
@@ -42,9 +37,11 @@ export function isBookingDateTime(value) {
|
|
|
42
37
|
const second = Number(secondText);
|
|
43
38
|
const offsetHour = match[9] === undefined ? 0 : Number(match[9]);
|
|
44
39
|
const offsetMinute = match[10] === undefined ? 0 : Number(match[10]);
|
|
45
|
-
if (month < 1 || month > 12 || hour > 23 || minute > 59 || second > 59 || offsetHour > 23 || offsetMinute > 59)
|
|
40
|
+
if (month < 1 || month > 12 || hour > 23 || minute > 59 || second > 59 || offsetHour > 23 || offsetMinute > 59)
|
|
41
|
+
return false;
|
|
46
42
|
const daysInMonth = new Date(Date.UTC(year, month, 0)).getUTCDate();
|
|
47
|
-
if (day < 1 || day > daysInMonth)
|
|
43
|
+
if (day < 1 || day > daysInMonth)
|
|
44
|
+
return false;
|
|
48
45
|
return !Number.isNaN(Date.parse(value));
|
|
49
46
|
}
|
|
50
47
|
function normalizedWords(value) {
|
|
@@ -53,20 +50,24 @@ function normalizedWords(value) {
|
|
|
53
50
|
function hasSupplierCost(value) {
|
|
54
51
|
const normalized = normalizedWords(value);
|
|
55
52
|
const lower = normalized.toLowerCase();
|
|
56
|
-
return /\b(?:supplier|vendor|internal)\b/.test(lower) &&
|
|
53
|
+
return (/\b(?:supplier|vendor|internal)\b/.test(lower) &&
|
|
54
|
+
/\b(?:cost|rate|price)\b/.test(lower) &&
|
|
55
|
+
currencyAmount.test(normalized.toUpperCase()));
|
|
57
56
|
}
|
|
58
57
|
function hasInternationalPhone(value) {
|
|
59
58
|
const normalized = value.normalize('NFKC');
|
|
60
|
-
for (const plusIndex of normalized.split('').flatMap((char, index)=>char === '+' ? [
|
|
61
|
-
index
|
|
62
|
-
] : [])){
|
|
59
|
+
for (const plusIndex of normalized.split('').flatMap((char, index) => char === '+' ? [index] : [])) {
|
|
63
60
|
let digits = '';
|
|
64
|
-
for (const char of normalized.slice(plusIndex + 1, plusIndex + 48)){
|
|
65
|
-
if (/\d/u.test(char))
|
|
66
|
-
|
|
67
|
-
if (
|
|
61
|
+
for (const char of normalized.slice(plusIndex + 1, plusIndex + 48)) {
|
|
62
|
+
if (/\d/u.test(char))
|
|
63
|
+
digits += char;
|
|
64
|
+
else if (/\p{L}|@/u.test(char))
|
|
65
|
+
break;
|
|
66
|
+
if (digits.length > 15)
|
|
67
|
+
break;
|
|
68
68
|
}
|
|
69
|
-
if (/^[1-9]\d{7,14}$/.test(digits))
|
|
69
|
+
if (/^[1-9]\d{7,14}$/.test(digits))
|
|
70
|
+
return true;
|
|
70
71
|
}
|
|
71
72
|
return false;
|
|
72
73
|
}
|
|
@@ -74,246 +75,177 @@ function hasPaymentCard(value) {
|
|
|
74
75
|
const normalized = value.normalize('NFKC');
|
|
75
76
|
const candidates = normalized.match(/(?:\d[ -]?){12,18}\d/g) ?? [];
|
|
76
77
|
const explicitlyLabeled = /\b(?:card|payment|visa|mastercard)\b/i.test(normalized);
|
|
77
|
-
return candidates.some((candidate)=>(explicitlyLabeled || /[ -]/.test(candidate)) && luhn(candidate));
|
|
78
|
+
return candidates.some((candidate) => (explicitlyLabeled || /[ -]/.test(candidate)) && luhn(candidate));
|
|
78
79
|
}
|
|
79
80
|
function hasSensitiveText(value, opts = {}) {
|
|
80
81
|
const normalized = value.normalize('NFKC');
|
|
81
|
-
if (normalized.includes('@') && !technicalEmail.test(normalized.trim()))
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
let
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (
|
|
91
|
-
n
|
|
92
|
-
if (n > 9) n -= 9;
|
|
93
|
-
}
|
|
94
|
-
sum += n;
|
|
82
|
+
if (normalized.includes('@') && !technicalEmail.test(normalized.trim()))
|
|
83
|
+
return true;
|
|
84
|
+
return secret.test(normalized) || hasInternationalPhone(normalized) || hasSupplierCost(normalized) || (!opts.allowOpaqueNumericId && hasPaymentCard(normalized));
|
|
85
|
+
}
|
|
86
|
+
function luhn(value) { const digits = value.replaceAll(/\D/g, ''); if (digits.length < 13 || digits.length > 19)
|
|
87
|
+
return false; let sum = 0; for (let i = digits.length - 1, parity = 0; i >= 0; i--, parity++) {
|
|
88
|
+
let n = Number(digits[i]);
|
|
89
|
+
if (parity % 2 === 1) {
|
|
90
|
+
n *= 2;
|
|
91
|
+
if (n > 9)
|
|
92
|
+
n -= 9;
|
|
95
93
|
}
|
|
96
|
-
|
|
97
|
-
}
|
|
94
|
+
sum += n;
|
|
95
|
+
} return sum % 10 === 0; }
|
|
98
96
|
const opaqueFieldNames = new Set([
|
|
99
|
-
'eventId',
|
|
100
|
-
'
|
|
101
|
-
'
|
|
102
|
-
'
|
|
103
|
-
'hotelRef',
|
|
104
|
-
'offerRef',
|
|
105
|
-
'offerVersionRef',
|
|
106
|
-
'checkedOfferVersionRef',
|
|
107
|
-
'currentOfferVersionRef',
|
|
108
|
-
'verifiedOfferRef',
|
|
109
|
-
'orderRef',
|
|
110
|
-
'searchSessionRef',
|
|
111
|
-
'handoffRef',
|
|
112
|
-
'questionId',
|
|
113
|
-
'blockerId',
|
|
114
|
-
'approvalId',
|
|
115
|
-
'sourceActionId',
|
|
116
|
-
'targetActionId',
|
|
117
|
-
'sourceTurnId',
|
|
118
|
-
'turnId',
|
|
119
|
-
'requestKey',
|
|
120
|
-
'taskHandle',
|
|
121
|
-
'nonce',
|
|
122
|
-
'deliveryNonce',
|
|
123
|
-
'optionDigest',
|
|
124
|
-
'sourceReceiptDigest',
|
|
125
|
-
'valueDigest'
|
|
97
|
+
'eventId', 'taskId', 'contextRef', 'actionId', 'hotelRef', 'offerRef', 'offerVersionRef', 'checkedOfferVersionRef', 'currentOfferVersionRef', 'verifiedOfferRef',
|
|
98
|
+
'orderRef', 'searchSessionRef', 'handoffRef', 'questionId', 'blockerId', 'approvalId',
|
|
99
|
+
'sourceActionId', 'targetActionId', 'sourceTurnId', 'turnId', 'requestKey', 'taskHandle',
|
|
100
|
+
'nonce', 'deliveryNonce', 'optionDigest', 'sourceReceiptDigest', 'valueDigest',
|
|
126
101
|
]);
|
|
127
102
|
function isOpaqueFieldPath(path) {
|
|
128
103
|
const clean = path.replace(/\[\]$/g, '');
|
|
129
104
|
const field = clean.split('.').at(-1);
|
|
130
105
|
return field ? opaqueFieldNames.has(field) || field.endsWith('Ref') || field.endsWith('Refs') : false;
|
|
131
106
|
}
|
|
132
|
-
const scan = (v, path = '$')=>typeof v === 'string'
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
107
|
+
const scan = (v, path = '$') => typeof v === 'string'
|
|
108
|
+
? hasSensitiveText(v, { allowOpaqueNumericId: isOpaqueFieldPath(path) })
|
|
109
|
+
: Array.isArray(v)
|
|
110
|
+
? v.some((child) => scan(child, `${path}[]`))
|
|
111
|
+
: !!v && typeof v === 'object'
|
|
112
|
+
? Object.entries(v).some(([k, x]) => /cost|price/i.test(k) ? /supplier|internal|wholesale/i.test(String(x)) : scan(x, `${path}.${k}`))
|
|
113
|
+
: false;
|
|
114
|
+
export function isSafeBookingRequestKey(value) { return safeRequestKey(value); }
|
|
115
|
+
function errors(v) { return (v.errors ?? []).map(e => `${e.instancePath || '$'}: ${e.message ?? e.keyword}`); }
|
|
141
116
|
function workspaceErrors(workspace, path) {
|
|
142
117
|
const e = [];
|
|
143
|
-
if (!workspace || typeof workspace !== 'object' || Array.isArray(workspace))
|
|
118
|
+
if (!workspace || typeof workspace !== 'object' || Array.isArray(workspace))
|
|
119
|
+
return e;
|
|
144
120
|
const loadedOffers = Array.isArray(workspace.loadedOffers) ? workspace.loadedOffers : [];
|
|
145
|
-
const offerRefs = loadedOffers.map((offer)=>offer?.offerRef).filter((ref)=>typeof ref === 'string');
|
|
146
|
-
const offerVersionRefs = loadedOffers.map((offer)=>offer?.offerVersionRef).filter((ref)=>typeof ref === 'string');
|
|
147
|
-
if (new Set(offerRefs).size !== offerRefs.length)
|
|
148
|
-
|
|
121
|
+
const offerRefs = loadedOffers.map((offer) => offer?.offerRef).filter((ref) => typeof ref === 'string');
|
|
122
|
+
const offerVersionRefs = loadedOffers.map((offer) => offer?.offerVersionRef).filter((ref) => typeof ref === 'string');
|
|
123
|
+
if (new Set(offerRefs).size !== offerRefs.length)
|
|
124
|
+
e.push(`${path}.loadedOffers: duplicate offerRef`);
|
|
125
|
+
if (new Set(offerVersionRefs).size !== offerVersionRefs.length)
|
|
126
|
+
e.push(`${path}.loadedOffers: duplicate offerVersionRef`);
|
|
149
127
|
const verified = workspace.verifiedOffer;
|
|
150
128
|
if (verified !== undefined) {
|
|
151
|
-
if (!loadedOffers.some((offer)=>offer?.offerRef === verified.offerRef && offer?.offerVersionRef === verified.offerVersionRef))
|
|
152
|
-
|
|
129
|
+
if (!loadedOffers.some((offer) => offer?.offerRef === verified.offerRef && offer?.offerVersionRef === verified.offerVersionRef))
|
|
130
|
+
e.push(`${path}.verifiedOffer: loaded offer version required`);
|
|
131
|
+
if (!safeOpaque(verified.verifiedOfferRef) || !isBookingDateTime(verified.expiresAt))
|
|
132
|
+
e.push(`${path}.verifiedOffer: safe ref and parseable expiry required`);
|
|
153
133
|
}
|
|
154
134
|
return e;
|
|
155
135
|
}
|
|
156
136
|
function cross(value) {
|
|
157
137
|
const e = [];
|
|
158
|
-
if (scan(value))
|
|
159
|
-
|
|
138
|
+
if (scan(value))
|
|
139
|
+
e.push('$: unsafe PII/payment/supplier-cost/credential text');
|
|
140
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
141
|
+
return e;
|
|
160
142
|
const x = value;
|
|
161
143
|
const semantic = x.kind === 'action.receipt.continuation' ? x.receipt : x;
|
|
162
|
-
if (x.workspace && x.kind !== 'user.turn.ingress')
|
|
163
|
-
|
|
144
|
+
if (x.workspace && x.kind !== 'user.turn.ingress')
|
|
145
|
+
e.push(...workspaceErrors(x.workspace, '$.workspace'));
|
|
146
|
+
if (x.schemaVersion === 'booking.surface' && Array.isArray(x.loadedOffers))
|
|
147
|
+
e.push(...workspaceErrors(x, '$'));
|
|
164
148
|
if (x.kind === 'user.turn.ingress') {
|
|
165
|
-
if (!safeRequestKey(x.requestKey))
|
|
149
|
+
if (!safeRequestKey(x.requestKey))
|
|
150
|
+
e.push('$.requestKey: safe opaque request key required');
|
|
166
151
|
const workspace = x.workspace;
|
|
167
152
|
e.push(...workspaceErrors(workspace, '$.workspace'));
|
|
168
153
|
const visibleHotels = Array.isArray(workspace?.visibleHotels) ? workspace.visibleHotels : [];
|
|
169
154
|
const loadedOffers = Array.isArray(workspace?.loadedOffers) ? workspace.loadedOffers : [];
|
|
170
|
-
if (workspace?.focusedHotelRef !== undefined && !visibleHotels.some((hotel)=>hotel?.hotelRef === workspace.focusedHotelRef))
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (
|
|
155
|
+
if (workspace?.focusedHotelRef !== undefined && !visibleHotels.some((hotel) => hotel?.hotelRef === workspace.focusedHotelRef))
|
|
156
|
+
e.push('$.workspace.focusedHotelRef: visible hotel required');
|
|
157
|
+
const selectedOffer = workspace?.selectedOfferRef === undefined ? undefined : loadedOffers.find((offer) => offer?.offerRef === workspace.selectedOfferRef);
|
|
158
|
+
if (workspace?.selectedOfferRef !== undefined && !selectedOffer)
|
|
159
|
+
e.push('$.workspace.selectedOfferRef: loaded offer required');
|
|
160
|
+
if (selectedOffer && !visibleHotels.some((hotel) => hotel?.hotelRef === selectedOffer.hotelRef))
|
|
161
|
+
e.push('$.workspace.selectedOfferRef: offer hotel must be visible');
|
|
174
162
|
}
|
|
175
|
-
if (semantic !== x && semantic?.resultContract?.hardCriteriaMet === true && semantic.resultContract.blockers?.length)
|
|
163
|
+
if (semantic !== x && semantic?.resultContract?.hardCriteriaMet === true && semantic.resultContract.blockers?.length)
|
|
164
|
+
e.push('$.receipt.resultContract: hardCriteriaMet cannot coexist with blockers');
|
|
176
165
|
if (kinds.has(x.kind)) {
|
|
177
166
|
const allowed = {
|
|
178
|
-
'search.patch': [
|
|
179
|
-
|
|
180
|
-
],
|
|
181
|
-
'
|
|
182
|
-
'
|
|
183
|
-
'patch'
|
|
184
|
-
],
|
|
185
|
-
'hotel.focus': [
|
|
186
|
-
'hotelRef'
|
|
187
|
-
],
|
|
188
|
-
'hotel.select': [
|
|
189
|
-
'hotelRef'
|
|
190
|
-
],
|
|
191
|
-
'offers.query': [
|
|
192
|
-
'hotelRefs',
|
|
193
|
-
'criteria'
|
|
194
|
-
],
|
|
195
|
-
'offers.view.patch': [
|
|
196
|
-
'hotelRef',
|
|
197
|
-
'criteria'
|
|
198
|
-
],
|
|
199
|
-
'offers.compare': [
|
|
200
|
-
'offerRefs',
|
|
201
|
-
'requestedCount'
|
|
202
|
-
],
|
|
203
|
-
'offer.select': [
|
|
204
|
-
'offerRef',
|
|
205
|
-
'offerVersionRef'
|
|
206
|
-
],
|
|
207
|
-
'offer.check': [
|
|
208
|
-
'offerRef',
|
|
209
|
-
'offerVersionRef'
|
|
210
|
-
],
|
|
211
|
-
'checkout.prepare': [
|
|
212
|
-
'offerRef',
|
|
213
|
-
'offerVersionRef',
|
|
214
|
-
'verifiedOfferRef'
|
|
215
|
-
],
|
|
216
|
-
'order.observe': [
|
|
217
|
-
'orderRef'
|
|
218
|
-
]
|
|
167
|
+
'search.patch': ['patch'], 'search.run': [], 'results.view.patch': ['patch'],
|
|
168
|
+
'hotel.focus': ['hotelRef'], 'hotel.select': ['hotelRef'],
|
|
169
|
+
'offers.query': ['hotelRefs', 'criteria'], 'offers.view.patch': ['hotelRef', 'criteria'],
|
|
170
|
+
'offers.compare': ['offerRefs', 'requestedCount'], 'offer.select': ['offerRef', 'offerVersionRef'],
|
|
171
|
+
'offer.check': ['offerRef', 'offerVersionRef'], 'checkout.prepare': ['offerRef', 'offerVersionRef', 'verifiedOfferRef'], 'order.observe': ['orderRef'],
|
|
219
172
|
};
|
|
220
173
|
const input = x.input;
|
|
221
|
-
if (!input || typeof input !== 'object' || Array.isArray(input) || Object.keys(input).some(
|
|
174
|
+
if (!input || typeof input !== 'object' || Array.isArray(input) || Object.keys(input).some(k => !allowed[x.kind].includes(k)))
|
|
175
|
+
e.push('$.input: closed kind-specific input required');
|
|
222
176
|
}
|
|
223
|
-
if (x.kind === 'action.receipt' && x.resultContract?.hardCriteriaMet === true && x.resultContract.blockers?.length)
|
|
224
|
-
|
|
177
|
+
if (x.kind === 'action.receipt' && x.resultContract?.hardCriteriaMet === true && x.resultContract.blockers?.length)
|
|
178
|
+
e.push('$.resultContract: hardCriteriaMet cannot coexist with blockers');
|
|
179
|
+
if (x.kind === 'user.turn' && Array.isArray(x.request?.approval))
|
|
180
|
+
e.push('$.request.approval: at most one approval');
|
|
225
181
|
if (x.kind === 'action.receipt' || x.kind === 'action.receipt.continuation') {
|
|
226
182
|
const r = x.kind === 'action.receipt.continuation' ? x.receipt : x;
|
|
227
|
-
if (r?.resultContract?.gapCodes?.some((code)
|
|
183
|
+
if (r?.resultContract?.gapCodes?.some((code) => !gapCodes.has(String(code))))
|
|
184
|
+
e.push('$.resultContract.gapCodes: unregistered gap code');
|
|
228
185
|
const observation = r?.observation;
|
|
229
186
|
if (observation?.kind === 'offer.availability') {
|
|
230
|
-
if (r.status === 'applied' && (observation.available !== true || !observation.verifiedOfferRef || observation.currentOfferVersionRef !== observation.checkedOfferVersionRef || observation.changedFactRefs?.length || observation.gapCodes?.length))
|
|
231
|
-
|
|
232
|
-
if (r.status === '
|
|
233
|
-
|
|
234
|
-
if (r.status
|
|
187
|
+
if (r.status === 'applied' && (observation.available !== true || !observation.verifiedOfferRef || observation.currentOfferVersionRef !== observation.checkedOfferVersionRef || observation.changedFactRefs?.length || observation.gapCodes?.length))
|
|
188
|
+
e.push('$.observation: applied availability requires unchanged checked/current version, verified ref, and no gaps');
|
|
189
|
+
if (r.status === 'applied' && (r.resultContract?.outcome !== 'complete' || r.resultContract?.hardCriteriaMet !== true || r.resultContract?.gapCodes?.length || r.resultContract?.blockers?.length))
|
|
190
|
+
e.push('$.resultContract: applied availability requires complete hard-criteria result without gaps or blockers');
|
|
191
|
+
if (r.status === 'changed' && (observation.available !== true || !observation.currentOfferVersionRef || observation.currentOfferVersionRef === observation.checkedOfferVersionRef || !observation.changedFactRefs?.length || observation.verifiedOfferRef))
|
|
192
|
+
e.push('$.observation: changed availability requires available replacement current version without verified ref');
|
|
193
|
+
if ((r.status === 'unavailable' || r.status === 'no_match') && (observation.available !== false || observation.currentOfferVersionRef || observation.verifiedOfferRef))
|
|
194
|
+
e.push('$.observation: negative availability must not expose current or verified version');
|
|
195
|
+
if (r.status !== 'applied' && observation.verifiedOfferRef)
|
|
196
|
+
e.push('$.observation: only applied availability may expose verified ref');
|
|
235
197
|
}
|
|
236
198
|
}
|
|
237
199
|
const blocker = x.blocker ?? x.question?.blocker;
|
|
238
|
-
if (blocker && !/^(searchDraft|results|offers|checkout)(\.[A-Za-z][A-Za-z0-9_]*){0,3}$/.test(blocker.criterionPath ?? ''))
|
|
239
|
-
|
|
200
|
+
if (blocker && !/^(searchDraft|results|offers|checkout)(\.[A-Za-z][A-Za-z0-9_]*){0,3}$/.test(blocker.criterionPath ?? ''))
|
|
201
|
+
e.push('$.blocker.criterionPath: closed criterion path required');
|
|
202
|
+
if (blocker?.evidence?.factRefs && new Set(blocker.evidence.factRefs).size !== blocker.evidence.factRefs.length)
|
|
203
|
+
e.push('$.blocker.evidence.factRefs: unique refs required');
|
|
240
204
|
if (x.kind === 'question' && blocker && Array.isArray(x.question?.approvalOptions)) {
|
|
241
|
-
for (const [i, option] of x.question.approvalOptions.entries()){
|
|
205
|
+
for (const [i, option] of x.question.approvalOptions.entries()) {
|
|
242
206
|
const result = validateApprovalAgainstBlocker(option?.approval, blocker);
|
|
243
|
-
if (!result.ok)
|
|
244
|
-
|
|
245
|
-
if (option?.approval?.
|
|
207
|
+
if (!result.ok)
|
|
208
|
+
e.push(...result.errors.map(error => `$.question.approvalOptions[${i}].${error.slice(2)}`));
|
|
209
|
+
if (option?.approval?.taskId !== x.taskId)
|
|
210
|
+
e.push(`$.question.approvalOptions[${i}].approval.taskId: must match event`);
|
|
211
|
+
if (option?.approval?.contextRef !== x.contextRef)
|
|
212
|
+
e.push(`$.question.approvalOptions[${i}].approval.contextRef: must match event`);
|
|
246
213
|
}
|
|
247
214
|
}
|
|
248
215
|
if (kinds.has(String(x.kind)) && x.relaxationApprovalRef) {
|
|
249
216
|
const ref = x.relaxationApprovalRef;
|
|
250
|
-
if (ref.contextRef !== x.contextRef)
|
|
251
|
-
|
|
252
|
-
if (ref.
|
|
253
|
-
|
|
254
|
-
if (
|
|
217
|
+
if (ref.contextRef !== x.contextRef)
|
|
218
|
+
e.push('$.relaxationApprovalRef.contextRef: must match action');
|
|
219
|
+
if (ref.sourceRevision !== x.expectedRevision)
|
|
220
|
+
e.push('$.relaxationApprovalRef.sourceRevision: must equal action revision');
|
|
221
|
+
if (ref.targetActionKind !== x.kind)
|
|
222
|
+
e.push('$.relaxationApprovalRef.targetActionKind: must match action kind');
|
|
223
|
+
if (ref.targetActionId !== x.actionId)
|
|
224
|
+
e.push('$.relaxationApprovalRef.targetActionId: must match action id');
|
|
225
|
+
// Expiry and one-time consumption are runtime concerns; validation checks only structure/binding.
|
|
226
|
+
if (!safeOpaque(ref.nonce) || !isBookingDateTime(ref.expiresAt))
|
|
227
|
+
e.push('$.relaxationApprovalRef: invalid expiry or nonce');
|
|
255
228
|
}
|
|
256
|
-
if (x.kind === 'question' && (!Array.isArray(x.question?.approvalOptions) || x.question.approvalOptions.length < 1 || x.question.approvalOptions.length > 2))
|
|
229
|
+
if (x.kind === 'question' && (!Array.isArray(x.question?.approvalOptions) || x.question.approvalOptions.length < 1 || x.question.approvalOptions.length > 2))
|
|
230
|
+
e.push('$.question.approvalOptions: one or two options required');
|
|
257
231
|
return e;
|
|
258
232
|
}
|
|
259
|
-
export function validateBookingSurface(value) {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
errors: e
|
|
266
|
-
} : {
|
|
267
|
-
ok: true
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
function fromCanonical(fn, value) {
|
|
271
|
-
const ok = fn(value);
|
|
272
|
-
const e = ok ? [] : errors(fn);
|
|
273
|
-
e.push(...cross(value));
|
|
274
|
-
return e.length ? {
|
|
275
|
-
ok: false,
|
|
276
|
-
errors: e
|
|
277
|
-
} : {
|
|
278
|
-
ok: true
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
export function validateBookingReadAction(value) {
|
|
282
|
-
return fromCanonical(validateActionCanonical, value);
|
|
283
|
-
}
|
|
284
|
-
export function validateCriterionBlocker(value) {
|
|
285
|
-
return fromCanonical(validateBlockerCanonical, value);
|
|
286
|
-
}
|
|
287
|
-
export function validateRelaxationApproval(value) {
|
|
288
|
-
return fromCanonical(validateApprovalCanonical, value);
|
|
289
|
-
}
|
|
290
|
-
export function validateBookingSurfaceEvent(value) {
|
|
291
|
-
return fromCanonical(validateEventCanonical, value);
|
|
292
|
-
}
|
|
233
|
+
export function validateBookingSurface(value) { const ok = validate(value); const e = ok ? [] : errors(validate); e.push(...cross(value)); return e.length ? { ok: false, errors: e } : { ok: true }; }
|
|
234
|
+
function fromCanonical(fn, value) { const ok = fn(value); const e = ok ? [] : errors(fn); e.push(...cross(value)); return e.length ? { ok: false, errors: e } : { ok: true }; }
|
|
235
|
+
export function validateBookingReadAction(value) { return fromCanonical(validateActionCanonical, value); }
|
|
236
|
+
export function validateCriterionBlocker(value) { return fromCanonical(validateBlockerCanonical, value); }
|
|
237
|
+
export function validateRelaxationApproval(value) { return fromCanonical(validateApprovalCanonical, value); }
|
|
238
|
+
export function validateBookingSurfaceEvent(value) { return fromCanonical(validateEventCanonical, value); }
|
|
293
239
|
export function validateApprovalAgainstBlocker(approval, blocker) {
|
|
294
240
|
const a = approval;
|
|
295
241
|
const b = blocker;
|
|
296
242
|
const blockerResult = validateCriterionBlocker(b);
|
|
297
|
-
if (!blockerResult.ok)
|
|
243
|
+
if (!blockerResult.ok)
|
|
244
|
+
return blockerResult;
|
|
298
245
|
const approvalResult = validateRelaxationApproval(a);
|
|
299
|
-
if (!approvalResult.ok)
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
'scope',
|
|
305
|
-
'code',
|
|
306
|
-
'criterionPath',
|
|
307
|
-
'valueDigest'
|
|
308
|
-
];
|
|
309
|
-
const mismatches = fields.filter((field)=>a[field] !== b?.[field]);
|
|
310
|
-
return mismatches.length ? {
|
|
311
|
-
ok: false,
|
|
312
|
-
errors: mismatches.map((field)=>`$.${field}: approval does not match blocker`)
|
|
313
|
-
} : {
|
|
314
|
-
ok: true
|
|
315
|
-
};
|
|
246
|
+
if (!approvalResult.ok)
|
|
247
|
+
return approvalResult;
|
|
248
|
+
const fields = ['blockerId', 'sourceActionId', 'sourceReceiptDigest', 'scope', 'code', 'criterionPath', 'valueDigest'];
|
|
249
|
+
const mismatches = fields.filter(field => a[field] !== b?.[field]);
|
|
250
|
+
return mismatches.length ? { ok: false, errors: mismatches.map(field => `$.${field}: approval does not match blocker`) } : { ok: true };
|
|
316
251
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
//# sourceURL=ts/src/booking-surface/validation.ts
|
package/dist/src/bridge.js
CHANGED
|
@@ -1,34 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GoTry 桥接工具集(纯 Node 版,无 Python 依赖):
|
|
3
|
+
* - ensureStateDir: 状态目录创建
|
|
4
|
+
* - recordLatency: 延迟日志追加
|
|
5
|
+
* - readJson / writeJson: JSON 文件安全读写
|
|
6
|
+
*
|
|
7
|
+
* 历史:曾有 callFeasibilityEngine 用 spawn(.venv/bin/python) 起 Python CLI,
|
|
8
|
+
* 作为 TS 求解的回退路径。D-7 切轨后 unified solveChoiceSegment 是唯一求解入口,
|
|
9
|
+
* Python oracle 路径于 v0.0.1-rc.2 移除——npm 一键分发不再需要 Python 运行时依赖。
|
|
10
|
+
*/
|
|
1
11
|
import { appendFile, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
12
|
import { join } from 'node:path';
|
|
13
|
+
/** 状态目录:动机画像 / wish pool 的落盘位置(红线 6:用户数据可见、可编辑、可删除) */
|
|
3
14
|
export async function ensureStateDir(root) {
|
|
4
15
|
const dir = join(root, 'gotry-state');
|
|
5
|
-
await mkdir(dir, {
|
|
6
|
-
recursive: true
|
|
7
|
-
});
|
|
16
|
+
await mkdir(dir, { recursive: true });
|
|
8
17
|
return dir;
|
|
9
18
|
}
|
|
19
|
+
/** JSON 安全读取:文件不存在返回 fallback,解析失败抛错 */
|
|
10
20
|
export async function readJson(path, fallback) {
|
|
11
21
|
try {
|
|
12
22
|
const text = await readFile(path, 'utf-8');
|
|
13
23
|
return JSON.parse(text);
|
|
14
|
-
}
|
|
15
|
-
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
if (e.code === 'ENOENT')
|
|
27
|
+
return fallback;
|
|
16
28
|
throw e;
|
|
17
29
|
}
|
|
18
30
|
}
|
|
31
|
+
/** JSON 原子写入 */
|
|
19
32
|
export async function writeJson(path, value) {
|
|
20
33
|
const tmp = `${path}.tmp`;
|
|
21
34
|
await writeFile(tmp, JSON.stringify(value, null, 2), 'utf-8');
|
|
22
35
|
const { rename } = await import('node:fs/promises');
|
|
23
36
|
await rename(tmp, path);
|
|
24
37
|
}
|
|
38
|
+
/** 桥接延迟日志(追加式;T5 成本工程的度量数据源之一) */
|
|
25
39
|
export async function recordLatency(logPath, latencyMs, kind) {
|
|
26
|
-
await appendFile(logPath, JSON.stringify({
|
|
27
|
-
ts: new Date().toISOString(),
|
|
28
|
-
kind,
|
|
29
|
-
latencyMs
|
|
30
|
-
}) + '\n');
|
|
40
|
+
await appendFile(logPath, JSON.stringify({ ts: new Date().toISOString(), kind, latencyMs }) + '\n');
|
|
31
41
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
//# sourceURL=ts/src/bridge.ts
|