@cueframe/kernel 0.1.0 → 0.1.1
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/LICENSE +661 -0
- package/dist/_members/composition/ops/compositionOps.d.ts +1 -0
- package/dist/_members/composition/projection/behind-readiness.d.ts +127 -0
- package/dist/_members/composition/projection/index.d.ts +2 -0
- package/dist/_members/composition/projection/reframe-readiness.d.ts +156 -0
- package/dist/_members/composition/validators/composition.d.ts +42 -25
- package/dist/_members/composition/validators/render.d.ts +32 -0
- package/dist/_members/core/render/compositionRender.d.ts +17 -1
- package/dist/_members/core/render/types.d.ts +31 -0
- package/dist/authoring/build-tools.js +6 -6
- package/dist/authoring/context.js +4 -4
- package/dist/authoring/tools.js +5 -5
- package/dist/authoring.js +11 -9
- package/dist/{chunk-ZB3C3HM7.js → chunk-7FO3ZJ43.js} +3 -0
- package/dist/{chunk-ODL7GQJG.js → chunk-AP6ZIVAV.js} +2 -2
- package/dist/{chunk-IBYQG46O.js → chunk-AQBREB5O.js} +1 -1
- package/dist/{chunk-YIOY2XEO.js → chunk-DK2ODBNT.js} +15 -5
- package/dist/{chunk-JRQVPFLF.js → chunk-HY3RIEZW.js} +1 -1
- package/dist/{chunk-GEA7E2FL.js → chunk-JJ6XD7NG.js} +151 -1
- package/dist/{chunk-COYMZ76S.js → chunk-MBCXCJOE.js} +1 -1
- package/dist/{chunk-37NJ27UU.js → chunk-OCFDUCF4.js} +3 -0
- package/dist/{chunk-VOO5FQSS.js → chunk-PDYO3DOJ.js} +5 -5
- package/dist/{chunk-MXFAADCQ.js → chunk-PODIYN2K.js} +9 -2
- package/dist/{chunk-NFEGPWGS.js → chunk-TOJEXFXL.js} +1 -1
- package/dist/{chunk-LM4WDUQJ.js → chunk-UHGSZZDI.js} +1 -1
- package/dist/{chunk-XWKZB5U5.js → chunk-ZWMV6DD2.js} +24 -1
- package/dist/compose/renderer.js +3 -3
- package/dist/compose.js +6 -6
- package/dist/composition/graphics-plan.js +1 -1
- package/dist/composition/ops.js +3 -3
- package/dist/composition/primitive-catalog.js +1 -1
- package/dist/composition/projection.js +19 -3
- package/dist/composition/validators.js +6 -4
- package/dist/composition-ops.js +8 -6
- package/dist/composition.js +24 -6
- package/dist/{context-OKX3HHFS.js → context-YWUZDG5X.js} +4 -4
- package/dist/core/render.js +3 -3
- package/dist/core/select.js +2 -2
- package/dist/core.js +3 -3
- package/dist/errors.js +1 -1
- package/dist/mcp/adapter.js +1 -0
- package/dist/media-model/testing.js +1071 -718
- package/dist/nle-export/server.js +328 -130
- package/dist/nle-export.js +7 -6
- package/package.json +13 -14
- package/dist/_members/account-model/index.js +0 -4
- package/dist/_members/account-model/testing/conversationStoreContract.js +0 -168
- package/dist/_members/account-model/testing/index.js +0 -7
- package/dist/_members/account-model/testing/restorePointStoreContract.js +0 -69
- package/dist/_members/account-model/types/index.js +0 -1
- package/dist/_members/account-model/validators/contextFile.js +0 -18
- package/dist/_members/account-model/validators/conversation.js +0 -95
- package/dist/_members/account-model/validators/dashboardInsight.js +0 -16
- package/dist/_members/account-model/validators/index.js +0 -7
- package/dist/_members/account-model/validators/sessionToken.js +0 -49
- package/dist/_members/account-model/validators/uploadToken.js +0 -16
- package/dist/_members/account-model/validators/userSettings.js +0 -12
- package/dist/_members/account-model/validators/workingComposition.js +0 -20
- package/dist/_members/brand-derivation/constants.js +0 -20
- package/dist/_members/brand-derivation/contrastSafe.js +0 -114
- package/dist/_members/brand-derivation/index.js +0 -1
- package/dist/_members/cloud-import/chunk-XKO6ZSWI.js +0 -162
- package/dist/_members/cloud-import/chunk-XKO6ZSWI.js.map +0 -1
- package/dist/_members/cloud-import/index.js +0 -241
- package/dist/_members/cloud-import/index.js.map +0 -1
- package/dist/_members/cloud-import/multipartUpload.js +0 -7
- package/dist/_members/cloud-import/multipartUpload.js.map +0 -1
- package/dist/_members/composition/audio/audioMix.js +0 -61
- package/dist/_members/composition/audio/index.js +0 -5
- package/dist/_members/composition/audio/mixStaging.js +0 -195
- package/dist/_members/composition/audio/musicPack.generated.js +0 -136
- package/dist/_members/composition/audio/musicPack.js +0 -36
- package/dist/_members/composition/audio/pack.generated.js +0 -603
- package/dist/_members/composition/audio/pack.js +0 -48
- package/dist/_members/composition/audio/palette.js +0 -134
- package/dist/_members/composition/autozoom/index.js +0 -83
- package/dist/_members/composition/brand-kit.js +0 -45
- package/dist/_members/composition/brief/contract.js +0 -209
- package/dist/_members/composition/color-grade.js +0 -80
- package/dist/_members/composition/depth.js +0 -8
- package/dist/_members/composition/fixtures/index.js +0 -143
- package/dist/_members/composition/fixtures/vaayaErrored.d.ts +0 -25
- package/dist/_members/composition/fixtures/vaayaErrored.js +0 -203
- package/dist/_members/composition/fonts.js +0 -162
- package/dist/_members/composition/graphics-plan/compile.js +0 -221
- package/dist/_members/composition/graphics-plan/index.js +0 -10
- package/dist/_members/composition/graphics-plan/instantiateMotif.js +0 -44
- package/dist/_members/composition/graphics-plan/motion.js +0 -66
- package/dist/_members/composition/graphics-plan/schemas.js +0 -261
- package/dist/_members/composition/graphics-plan/solve.js +0 -145
- package/dist/_members/composition/index.js +0 -5
- package/dist/_members/composition/invariants/clipPlacement.js +0 -69
- package/dist/_members/composition/invariants/composition.js +0 -28
- package/dist/_members/composition/invariants/errors.js +0 -10
- package/dist/_members/composition/invariants/etag.js +0 -38
- package/dist/_members/composition/invariants/ids.js +0 -33
- package/dist/_members/composition/invariants/index.js +0 -9
- package/dist/_members/composition/invariants/keyframes.js +0 -45
- package/dist/_members/composition/invariants/markers.js +0 -29
- package/dist/_members/composition/invariants/time.js +0 -20
- package/dist/_members/composition/invariants/tracks.js +0 -304
- package/dist/_members/composition/ops/compositionOps.js +0 -1056
- package/dist/_members/composition/ops/headlessStore.js +0 -94
- package/dist/_members/composition/ops/index.js +0 -4
- package/dist/_members/composition/ops/selectors.js +0 -118
- package/dist/_members/composition/ops/timeline-word.js +0 -1
- package/dist/_members/composition/ops/transcript.js +0 -77
- package/dist/_members/composition/ops/types.js +0 -16
- package/dist/_members/composition/primitive-catalog/catalogQuery.js +0 -150
- package/dist/_members/composition/primitive-catalog/disabled.js +0 -32
- package/dist/_members/composition/primitive-catalog/generated/meta.js +0 -15161
- package/dist/_members/composition/primitive-catalog/index.js +0 -46
- package/dist/_members/composition/primitive-catalog/paramValidation.js +0 -231
- package/dist/_members/composition/primitive-catalog/projection.js +0 -164
- package/dist/_members/composition/primitive-catalog/types.js +0 -14
- package/dist/_members/composition/projection/behind-placement.js +0 -62
- package/dist/_members/composition/projection/brandKit.js +0 -482
- package/dist/_members/composition/projection/cardTokens.js +0 -209
- package/dist/_members/composition/projection/composition-floor.js +0 -582
- package/dist/_members/composition/projection/cropProjection.js +0 -257
- package/dist/_members/composition/projection/index.js +0 -7
- package/dist/_members/composition/projection/occlusion-budget.js +0 -104
- package/dist/_members/composition/projection/safe-zones.js +0 -99
- package/dist/_members/composition/reframe/aspectLoss.js +0 -115
- package/dist/_members/composition/reframe/coverage.js +0 -62
- package/dist/_members/composition/reframe/derive.js +0 -73
- package/dist/_members/composition/reframe/index.js +0 -109
- package/dist/_members/composition/reframe/kenBurns.js +0 -64
- package/dist/_members/composition/reframe/shotScale.js +0 -176
- package/dist/_members/composition/reframe/trajectory.js +0 -124
- package/dist/_members/composition/timeline/compound.js +0 -134
- package/dist/_members/composition/timeline/index.js +0 -9
- package/dist/_members/composition/timeline/intent.js +0 -228
- package/dist/_members/composition/timeline/keyframes.js +0 -121
- package/dist/_members/composition/timeline/link.js +0 -59
- package/dist/_members/composition/timeline/occupancy.js +0 -36
- package/dist/_members/composition/timeline/placement.js +0 -74
- package/dist/_members/composition/timeline/plan.js +0 -31
- package/dist/_members/composition/timeline/resolve.js +0 -170
- package/dist/_members/composition/timeline/trims.js +0 -278
- package/dist/_members/composition/types/index.js +0 -1
- package/dist/_members/composition/validators/brandKit.js +0 -295
- package/dist/_members/composition/validators/composePlan.js +0 -113
- package/dist/_members/composition/validators/composition.js +0 -703
- package/dist/_members/composition/validators/font.js +0 -34
- package/dist/_members/composition/validators/graphicKinds.js +0 -30
- package/dist/_members/composition/validators/index.js +0 -9
- package/dist/_members/composition/validators/project.js +0 -29
- package/dist/_members/composition/validators/registry.js +0 -41
- package/dist/_members/composition/validators/render.js +0 -139
- package/dist/_members/composition/validators/session.js +0 -37
- package/dist/_members/composition/validators/webhook.js +0 -72
- package/dist/_members/composition-ops/alphaLayers.js +0 -141
- package/dist/_members/composition-ops/index.js +0 -637
- package/dist/_members/composition-ops/materializeGrid.js +0 -99
- package/dist/_members/composition-ops/opRecorder.js +0 -196
- package/dist/_members/composition-ops/recordingStore.js +0 -14
- package/dist/_members/composition-ops/replaceRegion.js +0 -102
- package/dist/_members/composition-ops/testFixtures.js +0 -19
- package/dist/_members/core/index.js +0 -5
- package/dist/_members/core/render/bookends.js +0 -53
- package/dist/_members/core/render/captions.js +0 -127
- package/dist/_members/core/render/compositionRender.js +0 -1249
- package/dist/_members/core/render/deriveTrajectory.js +0 -889
- package/dist/_members/core/render/emphasis.js +0 -16
- package/dist/_members/core/render/eval/cropBox.js +0 -139
- package/dist/_members/core/render/eval/inclusion.js +0 -96
- package/dist/_members/core/render/eval/index.js +0 -146
- package/dist/_members/core/render/eval/metrics.js +0 -94
- package/dist/_members/core/render/exportSettings.js +0 -65
- package/dist/_members/core/render/index.js +0 -20
- package/dist/_members/core/render/mapFaceToPerson.js +0 -73
- package/dist/_members/core/render/types.js +0 -8
- package/dist/_members/core/render/vcam/cameraPath.js +0 -248
- package/dist/_members/core/render/vcam/constants.js +0 -115
- package/dist/_members/core/render/vcam/errorCodes.js +0 -39
- package/dist/_members/core/render/vcam/framing.js +0 -136
- package/dist/_members/core/render/vcam/requiredRegion.js +0 -137
- package/dist/_members/core/render/vcam/shots.js +0 -53
- package/dist/_members/core/render/vcam/subjectSet.js +0 -388
- package/dist/_members/core/select/errorCodes.js +0 -38
- package/dist/_members/core/select/index.js +0 -26
- package/dist/_members/core/select/model.js +0 -18
- package/dist/_members/core/select/select.js +0 -244
- package/dist/_members/core/select/stabilize.js +0 -242
- package/dist/_members/core/types.js +0 -7
- package/dist/_members/errors/index.js +0 -1
- package/dist/_members/errors/registry.js +0 -560
- package/dist/_members/foundation/codes/index.js +0 -31
- package/dist/_members/foundation/index.js +0 -2
- package/dist/_members/foundation/primitives/index.js +0 -5
- package/dist/_members/mcp/adapter.js +0 -771
- package/dist/_members/mcp/burnedNames.js +0 -73
- package/dist/_members/mcp/core.js +0 -390
- package/dist/_members/mcp/curation.js +0 -792
- package/dist/_members/mcp/executor.js +0 -142
- package/dist/_members/mcp/format.js +0 -9
- package/dist/_members/mcp/generated/catalogPreviews.js +0 -95
- package/dist/_members/mcp/generated/harness.js +0 -81
- package/dist/_members/mcp/generated/operations.js +0 -131
- package/dist/_members/mcp/index.js +0 -50
- package/dist/_members/mcp/internalLoopTools.js +0 -109
- package/dist/_members/mcp/prompts.js +0 -45
- package/dist/_members/mcp/resources.js +0 -166
- package/dist/_members/mcp/server.js +0 -340
- package/dist/_members/mcp/wait.js +0 -378
- package/dist/_members/mcp/x402Meta.js +0 -66
- package/dist/_members/media-model/api.js +0 -175
- package/dist/_members/media-model/index.js +0 -1
- package/dist/_members/media-model/media-kind.js +0 -116
- package/dist/_members/media-model/testing/index.js +0 -1
- package/dist/_members/media-model/testing/mediaStoreContract.js +0 -82
- package/dist/_members/media-model/transcriptSearch.js +0 -70
- package/dist/_members/media-model/transcriptSearchCases.json +0 -79
- package/dist/_members/nle-export/compact-timeline.js +0 -73
- package/dist/_members/nle-export/compact-timeline.js.map +0 -1
- package/dist/_members/nle-export/composition-to-input.js +0 -147
- package/dist/_members/nle-export/composition-to-input.js.map +0 -1
- package/dist/_members/nle-export/fcpxml.js +0 -159
- package/dist/_members/nle-export/fcpxml.js.map +0 -1
- package/dist/_members/nle-export/index.js +0 -12
- package/dist/_members/nle-export/index.js.map +0 -1
- package/dist/_members/nle-export/premiere.js +0 -161
- package/dist/_members/nle-export/premiere.js.map +0 -1
- package/dist/_members/nle-export/server/index.js +0 -2
- package/dist/_members/nle-export/server/index.js.map +0 -1
- package/dist/_members/nle-export/server/pack-zip.js +0 -38
- package/dist/_members/nle-export/server/pack-zip.js.map +0 -1
- package/dist/_members/nle-export/srt.js +0 -50
- package/dist/_members/nle-export/srt.js.map +0 -1
- package/dist/_members/nle-export/types.js +0 -9
- package/dist/_members/nle-export/types.js.map +0 -1
- package/dist/_members/nle-export/xml/index.js +0 -11
- package/dist/_members/nle-export/xml/index.js.map +0 -1
- package/dist/_members/nle-export/xml-utils.js +0 -48
- package/dist/_members/nle-export/xml-utils.js.map +0 -1
- package/dist/_members/perception/activeSpeaker.js +0 -103
- package/dist/_members/perception/audioQuality.js +0 -76
- package/dist/_members/perception/energyBeat.js +0 -99
- package/dist/_members/perception/identityMatch.js +0 -91
- package/dist/_members/perception/index.js +0 -48
- package/dist/_members/perception/visualQuality.js +0 -95
- package/dist/_members/plane/conformance.js +0 -90
- package/dist/_members/plane/contract.js +0 -49
- package/dist/_members/plane/index.js +0 -16
- package/dist/_members/primitives/backgrounds/index.js +0 -32
- package/dist/_members/primitives/backgrounds/index.js.map +0 -1
- package/dist/_members/primitives/captions/index.js +0 -398
- package/dist/_members/primitives/captions/index.js.map +0 -1
- package/dist/_members/primitives/chunk-2LVE4UIT.js +0 -139
- package/dist/_members/primitives/chunk-2LVE4UIT.js.map +0 -1
- package/dist/_members/primitives/chunk-3IQUH6KK.js +0 -20
- package/dist/_members/primitives/chunk-3IQUH6KK.js.map +0 -1
- package/dist/_members/primitives/chunk-3OMURV6Q.js +0 -112
- package/dist/_members/primitives/chunk-3OMURV6Q.js.map +0 -1
- package/dist/_members/primitives/chunk-4IS6TMEG.js +0 -21
- package/dist/_members/primitives/chunk-4IS6TMEG.js.map +0 -1
- package/dist/_members/primitives/chunk-5MWGAEGQ.js +0 -1644
- package/dist/_members/primitives/chunk-5MWGAEGQ.js.map +0 -1
- package/dist/_members/primitives/chunk-7RRPFPQK.js +0 -15
- package/dist/_members/primitives/chunk-7RRPFPQK.js.map +0 -1
- package/dist/_members/primitives/chunk-DH5TRZK2.js +0 -125
- package/dist/_members/primitives/chunk-DH5TRZK2.js.map +0 -1
- package/dist/_members/primitives/chunk-E3KNUSFG.js +0 -2337
- package/dist/_members/primitives/chunk-E3KNUSFG.js.map +0 -1
- package/dist/_members/primitives/chunk-G2B3G4HK.js +0 -47
- package/dist/_members/primitives/chunk-G2B3G4HK.js.map +0 -1
- package/dist/_members/primitives/chunk-HFPFGZXT.js +0 -1494
- package/dist/_members/primitives/chunk-HFPFGZXT.js.map +0 -1
- package/dist/_members/primitives/chunk-HIWVFZVH.js +0 -19
- package/dist/_members/primitives/chunk-HIWVFZVH.js.map +0 -1
- package/dist/_members/primitives/chunk-IDAL6G3Y.js +0 -134
- package/dist/_members/primitives/chunk-IDAL6G3Y.js.map +0 -1
- package/dist/_members/primitives/chunk-KNC6JP5F.js +0 -34
- package/dist/_members/primitives/chunk-KNC6JP5F.js.map +0 -1
- package/dist/_members/primitives/chunk-KYRDVZST.js +0 -459
- package/dist/_members/primitives/chunk-KYRDVZST.js.map +0 -1
- package/dist/_members/primitives/chunk-LWLHS2NS.js +0 -3410
- package/dist/_members/primitives/chunk-LWLHS2NS.js.map +0 -1
- package/dist/_members/primitives/chunk-QAQ3P7DR.js +0 -8855
- package/dist/_members/primitives/chunk-QAQ3P7DR.js.map +0 -1
- package/dist/_members/primitives/chunk-ROV2ODWC.js +0 -81
- package/dist/_members/primitives/chunk-ROV2ODWC.js.map +0 -1
- package/dist/_members/primitives/chunk-TCRC2FH3.js +0 -183
- package/dist/_members/primitives/chunk-TCRC2FH3.js.map +0 -1
- package/dist/_members/primitives/chunk-US4YEDAA.js +0 -96
- package/dist/_members/primitives/chunk-US4YEDAA.js.map +0 -1
- package/dist/_members/primitives/chunk-UVKRO5ER.js +0 -13
- package/dist/_members/primitives/chunk-UVKRO5ER.js.map +0 -1
- package/dist/_members/primitives/chunk-W4EIIF2I.js +0 -10
- package/dist/_members/primitives/chunk-W4EIIF2I.js.map +0 -1
- package/dist/_members/primitives/chunk-WPGKIPCO.js +0 -3563
- package/dist/_members/primitives/chunk-WPGKIPCO.js.map +0 -1
- package/dist/_members/primitives/chunk-WZFLEGMD.js +0 -1435
- package/dist/_members/primitives/chunk-WZFLEGMD.js.map +0 -1
- package/dist/_members/primitives/chunk-XHU3WBEI.js +0 -33
- package/dist/_members/primitives/chunk-XHU3WBEI.js.map +0 -1
- package/dist/_members/primitives/chunk-YN7WTZZ7.js +0 -46
- package/dist/_members/primitives/chunk-YN7WTZZ7.js.map +0 -1
- package/dist/_members/primitives/effects/index.js +0 -30
- package/dist/_members/primitives/effects/index.js.map +0 -1
- package/dist/_members/primitives/hero-text/index.js +0 -14
- package/dist/_members/primitives/hero-text/index.js.map +0 -1
- package/dist/_members/primitives/index.js +0 -64
- package/dist/_members/primitives/index.js.map +0 -1
- package/dist/_members/primitives/layouts/index.js +0 -24
- package/dist/_members/primitives/layouts/index.js.map +0 -1
- package/dist/_members/primitives/lib/easings.js +0 -26
- package/dist/_members/primitives/lib/easings.js.map +0 -1
- package/dist/_members/primitives/lib/index.js +0 -74
- package/dist/_members/primitives/lib/index.js.map +0 -1
- package/dist/_members/primitives/lib/palette.js +0 -72
- package/dist/_members/primitives/lib/palette.js.map +0 -1
- package/dist/_members/primitives/lib/preview.js +0 -8
- package/dist/_members/primitives/lib/preview.js.map +0 -1
- package/dist/_members/primitives/lib/schema.js +0 -12
- package/dist/_members/primitives/lib/schema.js.map +0 -1
- package/dist/_members/primitives/lib/seeded-random.js +0 -14
- package/dist/_members/primitives/lib/seeded-random.js.map +0 -1
- package/dist/_members/primitives/lib/text/contrast.js +0 -12
- package/dist/_members/primitives/lib/text/contrast.js.map +0 -1
- package/dist/_members/primitives/lib/text/fill.js +0 -8
- package/dist/_members/primitives/lib/text/fill.js.map +0 -1
- package/dist/_members/primitives/lib/text/fitText.js +0 -10
- package/dist/_members/primitives/lib/text/fitText.js.map +0 -1
- package/dist/_members/primitives/lib/text/marker.js +0 -8
- package/dist/_members/primitives/lib/text/marker.js.map +0 -1
- package/dist/_members/primitives/lib/text/motion.js +0 -16
- package/dist/_members/primitives/lib/text/motion.js.map +0 -1
- package/dist/_members/primitives/lib/text/recolor.js +0 -8
- package/dist/_members/primitives/lib/text/recolor.js.map +0 -1
- package/dist/_members/primitives/lib/text/spec.js +0 -1
- package/dist/_members/primitives/lib/text/spec.js.map +0 -1
- package/dist/_members/primitives/lib/text/stage.js +0 -8
- package/dist/_members/primitives/lib/text/stage.js.map +0 -1
- package/dist/_members/primitives/lib/text/typeface.js +0 -8
- package/dist/_members/primitives/lib/text/typeface.js.map +0 -1
- package/dist/_members/primitives/lib/tokens.js +0 -24
- package/dist/_members/primitives/lib/tokens.js.map +0 -1
- package/dist/_members/primitives/lib/types.js +0 -1
- package/dist/_members/primitives/lib/types.js.map +0 -1
- package/dist/_members/primitives/params-required.json +0 -153
- package/dist/_members/primitives/scenes/index.js +0 -25
- package/dist/_members/primitives/scenes/index.js.map +0 -1
- package/dist/_members/primitives/subject-foreground/index.js +0 -30
- package/dist/_members/primitives/subject-foreground/index.js.map +0 -1
- package/dist/_members/primitives/text/index.js +0 -57
- package/dist/_members/primitives/text/index.js.map +0 -1
- package/dist/_members/primitives/transitions/index.js +0 -67
- package/dist/_members/primitives/transitions/index.js.map +0 -1
- package/dist/_members/primitives/ui-blocks/index.js +0 -76
- package/dist/_members/primitives/ui-blocks/index.js.map +0 -1
- package/dist/_members/primitives/version-map.json +0 -105
- package/dist/_members/primitives/workspace.js +0 -60
- package/dist/_members/primitives/workspace.js.map +0 -1
|
@@ -1,771 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Director-onto-MCP adapter — turns the curated SSOT (curation.ts) into an
|
|
3
|
-
* `ai`-SDK `ToolSet` the Director's author LLM can drive over the real `/v1`
|
|
4
|
-
* network surface (the dogfood), instead of the in-process `buildDirectorTools`
|
|
5
|
-
* store. One `ai` `tool()` per phase-1 curated name; each `execute()` runs the
|
|
6
|
-
* op through the injected `Executor` (server.ts:registerCuratedTools is the
|
|
7
|
-
* parallel SSOT consumer for the MCP-server transport).
|
|
8
|
-
*
|
|
9
|
-
* Correctness contracts preserved from buildTools.ts:107-200:
|
|
10
|
-
* (a) execute() returns errors as a JSON-STRING tool output, never throws —
|
|
11
|
-
* the LLM must SEE the error as tool output.
|
|
12
|
-
* (b) images never leave execute() as content-shaped objects (T4).
|
|
13
|
-
* (c/d) role filtering + dedup/progress/heartbeat stay external to tool-building.
|
|
14
|
-
*/
|
|
15
|
-
import { z } from 'zod';
|
|
16
|
-
import { tool } from 'ai';
|
|
17
|
-
import { invokeOp, opById, bodySchemaFor, isOk, apiErrorFrom, fmtErr, safeParse, sniffImageMediaType, fetchPreviews, } from './core.js';
|
|
18
|
-
import { CURATED_TOOLS } from './curation.js';
|
|
19
|
-
import { INTERNAL_LOOP_TOOLS } from './internalLoopTools.js';
|
|
20
|
-
import { waitForJob } from './wait.js';
|
|
21
|
-
// Re-exported for surface MIRRORS (e.g. @cueframe/local-harness's
|
|
22
|
-
// buildLocalToolSet): a mirror must serve the SAME names, descriptions and
|
|
23
|
-
// operationIds as this adapter — importing the specs is the only non-drifting
|
|
24
|
-
// way to do that.
|
|
25
|
-
export { CURATED_TOOLS } from './curation.js';
|
|
26
|
-
export { INTERNAL_LOOP_TOOLS } from './internalLoopTools.js';
|
|
27
|
-
/**
|
|
28
|
-
* B6 (spec 2026-07-11-preview-capture-unification §B6): INFRA-class error codes
|
|
29
|
-
* the author/designer LLM cannot fix — there is no tool it could call to create
|
|
30
|
-
* a missing scratch or revive a dead session. Returning these as Contract-3(a)
|
|
31
|
-
* tool output makes the model flail (prod dogfood: ×49 scratch_not_found renders
|
|
32
|
-
* ending in design-lock, 49 calls from the cause). A CLOSED set: adaptable
|
|
33
|
-
* errors (bad params, over budget, timestamp_out_of_bounds, component compile
|
|
34
|
-
* errors) keep the tool-output contract.
|
|
35
|
-
*/
|
|
36
|
-
const INFRA_TOOL_ERROR_CODES = new Set([
|
|
37
|
-
'scratch_not_found',
|
|
38
|
-
'session_not_found',
|
|
39
|
-
'session_not_ready',
|
|
40
|
-
// Version-gate timeout: the box's daemon/subscription is dead — no tool call
|
|
41
|
-
// can revive it (2026-07-12: 234 dead renders, two 24-min blind designer
|
|
42
|
-
// attempts before design-lock).
|
|
43
|
-
'session_version_timeout',
|
|
44
|
-
]);
|
|
45
|
-
/**
|
|
46
|
-
* The typed sentinel for infra-class tool failures. Thrown from a curated
|
|
47
|
-
* tool's execute(); the AI SDK catches it into a `tool-error` step part, so the
|
|
48
|
-
* throw ALONE changes nothing — the agent loop (authoring/agent.ts) detects the
|
|
49
|
-
* sentinel STRUCTURALLY (name+infra, no package import — mcp↔authoring stay
|
|
50
|
-
* decoupled per Constraint 3(d)), halts the run, and rethrows; the director
|
|
51
|
-
* (mcpSurface) maps it to a nonRetryable ApplicationFailure so the candidate
|
|
52
|
-
* fails loud with the REAL code instead of a design-lock 49 renders later.
|
|
53
|
-
*/
|
|
54
|
-
export class InfraToolError extends Error {
|
|
55
|
-
code;
|
|
56
|
-
infra = true;
|
|
57
|
-
constructor(code, message) {
|
|
58
|
-
super(message);
|
|
59
|
-
this.code = code;
|
|
60
|
-
this.name = 'InfraToolError';
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
/** Structural guard (mirror of the authoring-side local guard). */
|
|
64
|
-
export function isInfraToolError(v) {
|
|
65
|
-
return (v instanceof Error &&
|
|
66
|
-
v.name === 'InfraToolError' &&
|
|
67
|
-
v.infra === true &&
|
|
68
|
-
typeof v.code === 'string');
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Classify a non-2xx op response: an infra-class code returns the sentinel to
|
|
72
|
-
* THROW; anything else returns null (caller keeps the Contract-3(a) JSON-string
|
|
73
|
-
* return). Pure + exported for tests. Parses the error envelope itself so it
|
|
74
|
-
* has no dependency on ApiError's shape.
|
|
75
|
-
*/
|
|
76
|
-
export function infraToolErrorFromResponse(res) {
|
|
77
|
-
if (isOk(res.status))
|
|
78
|
-
return null;
|
|
79
|
-
let code;
|
|
80
|
-
try {
|
|
81
|
-
code = JSON.parse(res.text)?.error?.code;
|
|
82
|
-
}
|
|
83
|
-
catch {
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
if (typeof code !== 'string' || !INFRA_TOOL_ERROR_CODES.has(code))
|
|
87
|
-
return null;
|
|
88
|
-
return new InfraToolError(code, fmtErr(apiErrorFrom(res)));
|
|
89
|
-
}
|
|
90
|
-
/** Default per-run in-loop budget — mirrors director.ts:282/290. */
|
|
91
|
-
const DEFAULT_BUDGETS = { captures: 10, verifies: 3, styleViews: 4 };
|
|
92
|
-
/** Async-job poll defaults: a verify renders eval beats + runs the vision judge
|
|
93
|
-
* (~1-3 min); a preview is one still (~10-60s). One generous ceiling for both. */
|
|
94
|
-
const JOB_POLL_MS = 4_000;
|
|
95
|
-
const JOB_TIMEOUT_MS = 240_000;
|
|
96
|
-
/**
|
|
97
|
-
* Poll the generic jobs resource (GET /v1/jobs/{id}) until terminal or the
|
|
98
|
-
* deadline. Mirrors wait.ts's loop, but returns the RAW job JSON so each
|
|
99
|
-
* caller formats its own kind-specific result (verify → scores text,
|
|
100
|
-
* preview → result.url still). 4xx while polling → failed (fail loud);
|
|
101
|
-
* transient 5xx → keep polling.
|
|
102
|
-
*/
|
|
103
|
-
async function pollJobToTerminal(ex, jobId, jobPoll) {
|
|
104
|
-
const op = opById('getJob');
|
|
105
|
-
const sleep = jobPoll?.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
106
|
-
const pollMs = jobPoll?.pollMs ?? JOB_POLL_MS;
|
|
107
|
-
const timeoutMs = jobPoll?.timeoutMs ?? JOB_TIMEOUT_MS;
|
|
108
|
-
const deadline = Date.now() + timeoutMs;
|
|
109
|
-
for (;;) {
|
|
110
|
-
const res = await invokeOp(ex, op, () => jobId, undefined);
|
|
111
|
-
if (!isOk(res.status)) {
|
|
112
|
-
if (res.status >= 400 && res.status < 500) {
|
|
113
|
-
return { outcome: 'failed', pollError: fmtErr(apiErrorFrom(res)) };
|
|
114
|
-
}
|
|
115
|
-
// transient 5xx: fall through to sleep + retry
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
const job = safeParse(res.text);
|
|
119
|
-
const s = String(job?.status ?? '');
|
|
120
|
-
if (s === 'succeeded')
|
|
121
|
-
return { outcome: 'succeeded', job };
|
|
122
|
-
if (s === 'failed')
|
|
123
|
-
return { outcome: 'failed', job };
|
|
124
|
-
}
|
|
125
|
-
if (Date.now() + pollMs >= deadline)
|
|
126
|
-
return { outcome: 'timeout' };
|
|
127
|
-
await sleep(pollMs);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
/** Format a terminal verify job as the scores text the author acts on —
|
|
131
|
-
* mirrors wait.ts's verify terminal formatting. */
|
|
132
|
-
function formatVerifyResult(job) {
|
|
133
|
-
const r = (job?.result ?? {});
|
|
134
|
-
const lines = ['Verify complete.'];
|
|
135
|
-
if (typeof r.composite === 'number')
|
|
136
|
-
lines.push(`Composite score: ${r.composite}`);
|
|
137
|
-
if (r.perCriterion && typeof r.perCriterion === 'object') {
|
|
138
|
-
for (const [criterion, v] of Object.entries(r.perCriterion)) {
|
|
139
|
-
if (typeof v?.score === 'number') {
|
|
140
|
-
lines.push(`- ${criterion}: ${v.score}${v?.critique ? ` — ${v.critique}` : ''}`);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
if (typeof r.critique === 'string' && r.critique.length > 0) {
|
|
145
|
-
lines.push(`\nWorst-first critique: ${r.critique}`);
|
|
146
|
-
}
|
|
147
|
-
lines.push('\nFix the lowest criterion via apply_composition, then re-verify.');
|
|
148
|
-
return lines.join('\n');
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Stream a presigned still to base64 — mirrors inLoopCapture.ts:173-174.
|
|
152
|
-
*
|
|
153
|
-
* `btoa` + chunking rather than `Buffer.from(buf).toString('base64')`: this
|
|
154
|
-
* adapter now also builds tool sets inside the DESKTOP WEBVIEW (the local /v1
|
|
155
|
-
* lane), where `Buffer` does not exist. `btoa` is present in Node ≥16, the
|
|
156
|
-
* Convex isolate and every browser; the 8k chunk keeps `String.fromCharCode`
|
|
157
|
-
* under its argument limit on multi-MB stills. Byte-identical output.
|
|
158
|
-
*/
|
|
159
|
-
async function defaultFetchImage(url) {
|
|
160
|
-
const buf = await (await fetch(url)).arrayBuffer();
|
|
161
|
-
const bytes = new Uint8Array(buf);
|
|
162
|
-
const chunks = [];
|
|
163
|
-
for (let i = 0; i < bytes.length; i += 8192) {
|
|
164
|
-
chunks.push(String.fromCharCode(...bytes.subarray(i, i + 8192)));
|
|
165
|
-
}
|
|
166
|
-
return btoa(chunks.join(''));
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* Friendly path-arg names the adapter injects from `opts.session` and strips from
|
|
170
|
-
* the tool schema. Any other path arg (e.g. `mediaItemId`) stays LLM-supplied.
|
|
171
|
-
*/
|
|
172
|
-
const SESSION_PATH_ARGS = new Set(['projectId', 'sessionId']);
|
|
173
|
-
/** Resolve a session-managed path arg from opts.session, else undefined. */
|
|
174
|
-
function sessionPathValue(friendly, opts) {
|
|
175
|
-
if (!opts?.session)
|
|
176
|
-
return undefined;
|
|
177
|
-
if (friendly === 'projectId')
|
|
178
|
-
return opts.session.projectId;
|
|
179
|
-
if (friendly === 'sessionId')
|
|
180
|
-
return opts.session.sessionId;
|
|
181
|
-
return undefined;
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Compute the effective request body for a tool. Under a session, every tool
|
|
185
|
-
* that reads or writes "the composition being worked on" stamps
|
|
186
|
-
* `body.session_id` so the op targets the session SCRATCH:
|
|
187
|
-
* - `apply_composition` writes the scratch;
|
|
188
|
-
* - `verify_composition` judges it. Before this, verifies resolved the
|
|
189
|
-
* project's ACTIVE row — the session author's edits were invisible to its
|
|
190
|
-
* own in-loop judge (the scratch-blind class, prod 2026-07-14; render_frame
|
|
191
|
-
* had the same hole and stamps in buildRenderFrameTool).
|
|
192
|
-
* Every other tool passes its body through untouched (bodyless stay bodyless).
|
|
193
|
-
*/
|
|
194
|
-
const SESSION_STAMPED_TOOLS = new Set(['apply_composition', 'verify_composition']);
|
|
195
|
-
function injectSession(spec, args, opts) {
|
|
196
|
-
if (opts?.session && SESSION_STAMPED_TOOLS.has(spec.name)) {
|
|
197
|
-
return { ...(args.body ?? {}), session_id: opts.session.sessionId };
|
|
198
|
-
}
|
|
199
|
-
return args.body;
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Vision inlining, shared by `render_frame` (whole-composition stills) and
|
|
203
|
-
* `preview_component` (one graphic still). Fetch each presigned PNG url → base64
|
|
204
|
-
* and return them RAW as `string[]`. Finding 3: a 2xx that yields ZERO usable
|
|
205
|
-
* urls must NOT return an empty `string[]` — that becomes an empty
|
|
206
|
-
* `{type:'content', value:[]}` block the model API can reject — so return a
|
|
207
|
-
* JSON-string `{note}` (a TEXT tool result) instead. The base64 bytes never leave
|
|
208
|
-
* execute() content-shaped (Constraint 3(b)); they become a vision block only in
|
|
209
|
-
* `imageVisionModelOutput`.
|
|
210
|
-
*/
|
|
211
|
-
async function inlineStillsOrNote(urls, fetchImage, emptyNote) {
|
|
212
|
-
const out = [];
|
|
213
|
-
for (const u of urls)
|
|
214
|
-
if (u)
|
|
215
|
-
out.push(await fetchImage(u));
|
|
216
|
-
if (out.length === 0)
|
|
217
|
-
return JSON.stringify({ note: emptyNote });
|
|
218
|
-
return out;
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* The `toModelOutput` shared by the vision tools: a base64 `string[]` becomes
|
|
222
|
-
* image content blocks (the pixels the model SEES); anything else (an
|
|
223
|
-
* `{error}` / `{note}` JSON string) renders as TEXT. The media type is SNIFFED
|
|
224
|
-
* per image from its magic bytes (`sniffImageMediaType`), not hardcoded — PNG
|
|
225
|
-
* stills stay png, JPEG exemplars label jpeg, so the adapter never mislabels the
|
|
226
|
-
* bytes it inlines (and does not inherit buildTools.ts:37's hardcoded image/jpeg).
|
|
227
|
-
*/
|
|
228
|
-
function imageVisionModelOutput({ output }) {
|
|
229
|
-
return Array.isArray(output)
|
|
230
|
-
? {
|
|
231
|
-
type: 'content',
|
|
232
|
-
value: output.map((data) => ({
|
|
233
|
-
type: 'image-data',
|
|
234
|
-
mediaType: sniffImageMediaType(data),
|
|
235
|
-
data,
|
|
236
|
-
})),
|
|
237
|
-
}
|
|
238
|
-
: {
|
|
239
|
-
type: 'text',
|
|
240
|
-
value: typeof output === 'string' ? output : JSON.stringify(output),
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
function isPreviewAugmented(v) {
|
|
244
|
-
return !!v && typeof v === 'object' && v.__preview === true;
|
|
245
|
-
}
|
|
246
|
-
function previewVisionModelOutput({ output }) {
|
|
247
|
-
if (isPreviewAugmented(output)) {
|
|
248
|
-
return {
|
|
249
|
-
type: 'content',
|
|
250
|
-
value: [
|
|
251
|
-
{ type: 'text', text: output.text },
|
|
252
|
-
...output.images.map((data) => ({
|
|
253
|
-
type: 'image-data',
|
|
254
|
-
mediaType: sniffImageMediaType(data),
|
|
255
|
-
data,
|
|
256
|
-
})),
|
|
257
|
-
],
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
return {
|
|
261
|
-
type: 'text',
|
|
262
|
-
value: typeof output === 'string' ? output : JSON.stringify(output),
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
/**
|
|
266
|
-
* `render_frame` vision special-case (T4). Renders whole-composition still(s) in
|
|
267
|
-
* the warm box, fetches each presigned PNG, and inlines it as base64. The bytes
|
|
268
|
-
* are returned RAW as `string[]` from execute() and only become a vision block in
|
|
269
|
-
* `toModelOutput` — a content-shaped object leaving execute() would be JSON-
|
|
270
|
-
* stringified by the AI SDK (~50k text tokens/image → window overflow) AND the
|
|
271
|
-
* model still couldn't see the pixels (Constraint 3(b)).
|
|
272
|
-
*/
|
|
273
|
-
function buildRenderFrameTool(spec, ex, budget, opts) {
|
|
274
|
-
const op = opById(spec.operationId);
|
|
275
|
-
const fetchImage = opts?.fetchImage ?? defaultFetchImage;
|
|
276
|
-
// Path args (projectId/sessionId) come from the scratch session — never LLM-managed.
|
|
277
|
-
const reverse = {};
|
|
278
|
-
for (const [friendly, opParam] of Object.entries(spec.pathArgs ?? {})) {
|
|
279
|
-
reverse[opParam] = friendly;
|
|
280
|
-
}
|
|
281
|
-
const mapArg = (p) => sessionPathValue(reverse[p], opts) ?? undefined;
|
|
282
|
-
return tool({
|
|
283
|
-
description: spec.description,
|
|
284
|
-
// composition is omitted; session_id (stamped below) makes the server render
|
|
285
|
-
// the session SCRATCH — the composition this agent is actually editing. The
|
|
286
|
-
// box cutover left this body bare and previewFrame fell back to the project's
|
|
287
|
-
// ACTIVE row, so the designer refined against stills WITHOUT its own edits
|
|
288
|
-
// until the capture budget refused (prod 2026-07-14, rs7fanev…).
|
|
289
|
-
inputSchema: z.object({ body: z.object({ timestamps: z.array(z.number()).min(1) }) }),
|
|
290
|
-
// K4: teach BATCHED inspection — several timestamps bracketing the edited
|
|
291
|
-
// window in ONE call (the measured anti-pattern: repeated single-frame
|
|
292
|
-
// renders, each costing a capture from the same budget).
|
|
293
|
-
inputExamples: [{ input: { body: { timestamps: [14.2, 14.8, 15.6] } } }],
|
|
294
|
-
execute: async (args) => {
|
|
295
|
-
// Budget (T5): a capture costs COGS — over budget returns a tool-output error, never throws.
|
|
296
|
-
if (++budget.used.captures > budget.caps.captures) {
|
|
297
|
-
return JSON.stringify({
|
|
298
|
-
error: `capture budget exhausted; you have used all ${budget.caps.captures} in-loop renders — finalize from your current composition.`,
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
const res = await invokeOp(ex, op, mapArg, {
|
|
302
|
-
timestamps: args.body.timestamps,
|
|
303
|
-
// Vision-bound shrink (2026-07-17 trace audit): this tool is the AGENT's
|
|
304
|
-
// eyes — full-res PNG stills base64'd into context cost +10k tokens per
|
|
305
|
-
// look and slower renders. The judge grades jpeg/0.5/70; the agent sees
|
|
306
|
-
// the same. The PUBLIC preview_frame (human preview) stays full-res —
|
|
307
|
-
// this body rides the dedicated internal-loop builder only.
|
|
308
|
-
imageFormat: 'jpeg',
|
|
309
|
-
scale: 0.5,
|
|
310
|
-
jpegQuality: 70,
|
|
311
|
-
...(opts?.session ? { session_id: opts.session.sessionId } : {}),
|
|
312
|
-
});
|
|
313
|
-
// B6: an INFRA-class code (dead scratch/session) is unfixable by the model —
|
|
314
|
-
// throw the sentinel (loop-halt + loud candidate failure), never tool output.
|
|
315
|
-
const infra = infraToolErrorFromResponse(res);
|
|
316
|
-
if (infra)
|
|
317
|
-
throw infra;
|
|
318
|
-
// Contract 3(a): any OTHER non-2xx is RETURNED as a JSON-string, never thrown / never an image.
|
|
319
|
-
if (!isOk(res.status))
|
|
320
|
-
return JSON.stringify({ error: fmtErr(apiErrorFrom(res)) });
|
|
321
|
-
const json = safeParse(res.text);
|
|
322
|
-
// The capture budget was already charged above; a zero-still 2xx returns a
|
|
323
|
-
// {note} (charge stands) rather than an empty content block (Finding 3).
|
|
324
|
-
return inlineStillsOrNote((json?.stills ?? []).map((s) => s.url), fetchImage, 'no stills returned for the requested timestamps');
|
|
325
|
-
},
|
|
326
|
-
toModelOutput: imageVisionModelOutput,
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
/**
|
|
330
|
-
* `preview_component` vision special-case. Renders ONE org-installed component as
|
|
331
|
-
* a single still in the warm box and returns a presigned PNG `{ url }` — the
|
|
332
|
-
* designer's author→preview→fix loop for a graphic. WITHOUT this the generic tool
|
|
333
|
-
* path returns the `{url}` JSON as TEXT and the designer LLM never SEES the
|
|
334
|
-
* pixels; here we inline it exactly like render_frame (base64 `string[]` from
|
|
335
|
-
* execute → a vision block only in `toModelOutput`). Un-budgeted (a preview is not
|
|
336
|
-
* charged against the in-loop capture cap; the agent's step cap is the backstop).
|
|
337
|
-
* A non-2xx — the 422 `component_does_not_compile` / `component_runtime_error` the
|
|
338
|
-
* designer self-fixes from — is RETURNED as a JSON `{error}` string, never thrown, never an image
|
|
339
|
-
* (Constraint 3(a)); a 2xx with no url returns a `{note}` (Finding 3), never an
|
|
340
|
-
* empty content block. The body schema (componentId + params/width/height) comes
|
|
341
|
-
* from the manifest, so a param rename fails loud at codegen, not at runtime.
|
|
342
|
-
*/
|
|
343
|
-
function buildPreviewComponentTool(spec, ex, opts) {
|
|
344
|
-
const op = opById(spec.operationId);
|
|
345
|
-
const fetchImage = opts?.fetchImage ?? defaultFetchImage;
|
|
346
|
-
// Path args (projectId/sessionId) come from the scratch session — never LLM-managed.
|
|
347
|
-
const reverse = {};
|
|
348
|
-
for (const [friendly, opParam] of Object.entries(spec.pathArgs ?? {})) {
|
|
349
|
-
reverse[opParam] = friendly;
|
|
350
|
-
}
|
|
351
|
-
const shape = {};
|
|
352
|
-
for (const friendly of Object.keys(spec.pathArgs ?? {})) {
|
|
353
|
-
// Session-managed path args are injected from opts.session — never exposed.
|
|
354
|
-
if (opts?.session && SESSION_PATH_ARGS.has(friendly))
|
|
355
|
-
continue;
|
|
356
|
-
shape[friendly] = z.string().describe(`${friendly} (path).`);
|
|
357
|
-
}
|
|
358
|
-
const bodySchema = bodySchemaFor(op);
|
|
359
|
-
if (bodySchema)
|
|
360
|
-
shape.body = bodySchema;
|
|
361
|
-
return tool({
|
|
362
|
-
description: spec.description,
|
|
363
|
-
inputSchema: z.object(shape),
|
|
364
|
-
execute: async (args) => {
|
|
365
|
-
const res = await invokeOp(ex, op, (p) => sessionPathValue(reverse[p], opts) ?? args[reverse[p]], args.body);
|
|
366
|
-
// Contract 3(a): a non-2xx (422 component_does_not_compile /
|
|
367
|
-
// component_runtime_error / 5xx) is RETURNED as a JSON-string {error}, never
|
|
368
|
-
// thrown / never an image — the designer reads the compile-or-runtime error
|
|
369
|
-
// message and fixes the source via create_component. EXCEPT infra-class
|
|
370
|
-
// codes (B6): a dead session is not author-fixable — sentinel throw.
|
|
371
|
-
{
|
|
372
|
-
const infra = infraToolErrorFromResponse(res);
|
|
373
|
-
if (infra)
|
|
374
|
-
throw infra;
|
|
375
|
-
}
|
|
376
|
-
if (!isOk(res.status))
|
|
377
|
-
return JSON.stringify({ error: fmtErr(apiErrorFrom(res)) });
|
|
378
|
-
const json = safeParse(res.text);
|
|
379
|
-
// Async-jobs migration: the op ENQUEUES a preview job and returns a
|
|
380
|
-
// receipt. The designer has no wait_job, so BLOCK here until terminal and
|
|
381
|
-
// inline the resulting still (a sync {url} response — older API — still
|
|
382
|
-
// takes the fast path).
|
|
383
|
-
let url = json?.url;
|
|
384
|
-
if (!url && json?.jobId) {
|
|
385
|
-
const done = await pollJobToTerminal(ex, json.jobId, opts?.jobPoll);
|
|
386
|
-
if (done.outcome === 'failed') {
|
|
387
|
-
const err = done.pollError ??
|
|
388
|
-
(done.job?.error
|
|
389
|
-
? `${done.job.error.code ?? 'preview_failed'}: ${done.job.error.message ?? ''}`
|
|
390
|
-
: 'preview job failed');
|
|
391
|
-
// The author-fixable compile/runtime message rides job.error — return
|
|
392
|
-
// it as {error} text so the designer fixes the source via create_component.
|
|
393
|
-
return JSON.stringify({ error: err });
|
|
394
|
-
}
|
|
395
|
-
if (done.outcome === 'timeout') {
|
|
396
|
-
return JSON.stringify({
|
|
397
|
-
note: `component preview still rendering after ${Math.round((opts?.jobPoll?.timeoutMs ?? JOB_TIMEOUT_MS) / 1000)}s — proceed, or re-preview later`,
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
|
-
url = done.job?.result?.url;
|
|
401
|
-
}
|
|
402
|
-
return inlineStillsOrNote([url], fetchImage, 'component preview returned no still url');
|
|
403
|
-
},
|
|
404
|
-
toModelOutput: imageVisionModelOutput,
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
/**
|
|
408
|
-
* `verify_composition` blocking special-case. The API op ENQUEUES the judge and
|
|
409
|
-
* returns a receipt ("call wait_job…") — but wait_job is NOT on the curated
|
|
410
|
-
* author/designer surface, so via the generic tool path the agent can never
|
|
411
|
-
* read its own score (the 2026-07-07 prod LBO run called verify once and the
|
|
412
|
-
* verdict evaporated). Here the tool blocks until the verify job is terminal
|
|
413
|
-
* and returns the per-criterion scores + worst-first critique as TEXT — one
|
|
414
|
-
* call, one verdict, same contract as the in-process score_composition.
|
|
415
|
-
* Budgeted (a verify = eval-beat renders + the vision judge).
|
|
416
|
-
*/
|
|
417
|
-
function buildVerifyCompositionTool(spec, ex, budget, opts) {
|
|
418
|
-
const op = opById(spec.operationId);
|
|
419
|
-
const onProgress = opts?.onProgress;
|
|
420
|
-
const reverse = {};
|
|
421
|
-
for (const [friendly, opParam] of Object.entries(spec.pathArgs ?? {})) {
|
|
422
|
-
reverse[opParam] = friendly;
|
|
423
|
-
}
|
|
424
|
-
const shape = {};
|
|
425
|
-
for (const friendly of Object.keys(spec.pathArgs ?? {})) {
|
|
426
|
-
if (opts?.session && SESSION_PATH_ARGS.has(friendly))
|
|
427
|
-
continue;
|
|
428
|
-
shape[friendly] = z.string().describe(`${friendly} (path).`);
|
|
429
|
-
}
|
|
430
|
-
const bodySchema = bodySchemaFor(op);
|
|
431
|
-
if (bodySchema)
|
|
432
|
-
shape.body = bodySchema;
|
|
433
|
-
return tool({
|
|
434
|
-
description: 'Score the current composition on the judge\'s own rubric — BLOCKS until the scores are ready (1-3 min) and returns per-criterion scores 0-10 (editorial / spatial / brand / caption), the weighted composite, and a worst-first critique. Fix the lowest criterion via apply_composition, then re-verify. Do NOT apply/render while it runs.',
|
|
435
|
-
inputSchema: z.object(shape),
|
|
436
|
-
execute: async (args) => {
|
|
437
|
-
if (++budget.used.verifies > budget.caps.verifies) {
|
|
438
|
-
return JSON.stringify({
|
|
439
|
-
error: `verify budget exhausted; you have used all ${budget.caps.verifies} in-loop verifies — commit your best composition.`,
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
const toolCallId = `tc_${crypto.randomUUID().slice(0, 8)}`;
|
|
443
|
-
onProgress?.({
|
|
444
|
-
type: 'tool-call',
|
|
445
|
-
agent: 'router',
|
|
446
|
-
toolName: spec.name,
|
|
447
|
-
args,
|
|
448
|
-
toolCallId,
|
|
449
|
-
timestamp: Date.now(),
|
|
450
|
-
});
|
|
451
|
-
const finish = (output, ok) => {
|
|
452
|
-
onProgress?.({
|
|
453
|
-
type: 'tool-result',
|
|
454
|
-
agent: 'router',
|
|
455
|
-
toolName: spec.name,
|
|
456
|
-
success: ok,
|
|
457
|
-
message: output.slice(0, 200),
|
|
458
|
-
toolCallId,
|
|
459
|
-
timestamp: Date.now(),
|
|
460
|
-
});
|
|
461
|
-
return output;
|
|
462
|
-
};
|
|
463
|
-
const res = await invokeOp(ex, op, (p) => sessionPathValue(reverse[p], opts) ?? args[reverse[p]], injectSession(spec, args, opts));
|
|
464
|
-
{
|
|
465
|
-
// B6: infra-class enqueue failure (dead session) — sentinel throw.
|
|
466
|
-
const infra = infraToolErrorFromResponse(res);
|
|
467
|
-
if (infra)
|
|
468
|
-
throw infra;
|
|
469
|
-
}
|
|
470
|
-
if (!isOk(res.status)) {
|
|
471
|
-
return finish(JSON.stringify({ error: fmtErr(apiErrorFrom(res)) }), false);
|
|
472
|
-
}
|
|
473
|
-
const receipt = safeParse(res.text);
|
|
474
|
-
if (!receipt?.jobId) {
|
|
475
|
-
// Older sync API shape (scores inline) — pass it through unchanged.
|
|
476
|
-
return finish(res.text || 'verify returned no result', true);
|
|
477
|
-
}
|
|
478
|
-
const done = await pollJobToTerminal(ex, receipt.jobId, opts?.jobPoll);
|
|
479
|
-
if (done.outcome === 'succeeded')
|
|
480
|
-
return finish(formatVerifyResult(done.job), true);
|
|
481
|
-
if (done.outcome === 'timeout') {
|
|
482
|
-
return finish(JSON.stringify({
|
|
483
|
-
error: `verify still running after ${Math.round((opts?.jobPoll?.timeoutMs ?? JOB_TIMEOUT_MS) / 1000)}s — proceed with your best judgment; the final judge scores the cut regardless.`,
|
|
484
|
-
}), false);
|
|
485
|
-
}
|
|
486
|
-
const err = done.pollError ??
|
|
487
|
-
(done.job?.error
|
|
488
|
-
? `${done.job.error.code ?? 'verify_failed'}: ${done.job.error.message ?? ''}`
|
|
489
|
-
: 'verify job failed');
|
|
490
|
-
return finish(JSON.stringify({ error: err }), false);
|
|
491
|
-
},
|
|
492
|
-
});
|
|
493
|
-
}
|
|
494
|
-
/**
|
|
495
|
-
* `view_style_reference` vision special-case (T3, brand-kit-from-inspiration).
|
|
496
|
-
* Presigns the BOUND brand kit's inspiration exemplar frames (optionally scoped to
|
|
497
|
-
* one motif) and inlines them as base64 `string[]` — the reference pixels the maker
|
|
498
|
-
* conditions on and the judge grades against. The kit id comes from `opts.brandKitId`
|
|
499
|
-
* (the project's bound kit, threaded by the director), NEVER the LLM — so the tool
|
|
500
|
-
* cannot peek at another project's kit. No kit bound ⇒ a `{note}` (not an error: the
|
|
501
|
-
* kit is optional). Budgeted (`styleViews`): the vision window is finite, so past the
|
|
502
|
-
* cap it returns a tool-output error (never throws — Constraint 3(a)) so the maker
|
|
503
|
-
* designs from what it has already seen. Same base64→vision contract as render_frame.
|
|
504
|
-
*/
|
|
505
|
-
function buildViewStyleReferenceTool(spec, ex, budget, opts) {
|
|
506
|
-
const op = opById(spec.operationId);
|
|
507
|
-
const fetchImage = opts?.fetchImage ?? defaultFetchImage;
|
|
508
|
-
return tool({
|
|
509
|
-
description: spec.description,
|
|
510
|
-
inputSchema: z.object({
|
|
511
|
-
motifId: z.string().optional().describe('Filter to the frames this motif was observed in.'),
|
|
512
|
-
}),
|
|
513
|
-
execute: async (args) => {
|
|
514
|
-
if (!opts?.brandKitId) {
|
|
515
|
-
return JSON.stringify({ note: 'no brand kit bound to this project — no style reference available' });
|
|
516
|
-
}
|
|
517
|
-
if (++budget.used.styleViews > budget.caps.styleViews) {
|
|
518
|
-
return JSON.stringify({ error: `style-reference budget exhausted (${budget.caps.styleViews} views) — design from what you have seen` });
|
|
519
|
-
}
|
|
520
|
-
const res = await invokeOp(ex, op, () => opts.brandKitId, { ...(args.motifId ? { motifId: args.motifId } : {}), limit: 6 });
|
|
521
|
-
{
|
|
522
|
-
const infra = infraToolErrorFromResponse(res);
|
|
523
|
-
if (infra)
|
|
524
|
-
throw infra;
|
|
525
|
-
}
|
|
526
|
-
if (!isOk(res.status))
|
|
527
|
-
return JSON.stringify({ error: fmtErr(apiErrorFrom(res)) });
|
|
528
|
-
const json = safeParse(res.text);
|
|
529
|
-
return inlineStillsOrNote((json?.exemplars ?? []).map((e) => e.url), fetchImage, 'kit has no exemplar frames');
|
|
530
|
-
},
|
|
531
|
-
toModelOutput: imageVisionModelOutput,
|
|
532
|
-
});
|
|
533
|
-
}
|
|
534
|
-
const WAIT_KINDS = [
|
|
535
|
-
'render',
|
|
536
|
-
'compose',
|
|
537
|
-
'suggestions',
|
|
538
|
-
'verify',
|
|
539
|
-
'preview',
|
|
540
|
-
'brand_kit_extract',
|
|
541
|
-
'consult',
|
|
542
|
-
];
|
|
543
|
-
/**
|
|
544
|
-
* `wait_job` — the adapter twin of server.ts:registerWaitJob, over the SAME
|
|
545
|
-
* `waitForJob` poll loop (one implementation, two transports). It is not a
|
|
546
|
-
* generic single-op tool: `waitForJob` picks the status op PER KIND (wait.ts
|
|
547
|
-
* `KINDS`), so `spec.operationId` is representative only and is never invoked
|
|
548
|
-
* here.
|
|
549
|
-
*
|
|
550
|
-
* BOUNDED on purpose. The MCP transport caps this with `waitCapMs` because a
|
|
551
|
-
* 30-minute synchronous poll outlives the request; the adapter's callers are
|
|
552
|
-
* Convex actions and Temporal activities with the same problem, so the ceiling
|
|
553
|
-
* is `opts.jobPoll.timeoutMs` (default `JOB_TIMEOUT_MS`) and the caller's own
|
|
554
|
-
* `timeoutMs` may only lower it. Hitting the window is a CONTINUATION, not a
|
|
555
|
-
* failure — the returned text says to call again with the same arguments.
|
|
556
|
-
*/
|
|
557
|
-
function buildWaitJobTool(spec, ex, opts) {
|
|
558
|
-
const onProgress = opts?.onProgress;
|
|
559
|
-
const capMs = opts?.jobPoll?.timeoutMs ?? JOB_TIMEOUT_MS;
|
|
560
|
-
const fetchImage = opts?.fetchImage;
|
|
561
|
-
return tool({
|
|
562
|
-
description: spec.description,
|
|
563
|
-
inputSchema: z.object({
|
|
564
|
-
kind: z
|
|
565
|
-
.enum(WAIT_KINDS)
|
|
566
|
-
.describe('What to wait on: a render job, a compose job, a consult advisory, AI clip-suggestion analysis, or a generic async job (verify / preview / brand_kit_extract — the kind is named in the enqueue receipt).'),
|
|
567
|
-
id: z.string().describe('The job id from the enqueue call (renderId / composeJobId / jobId / mediaItemId for suggestions).'),
|
|
568
|
-
projectId: z.string().optional().describe('Required for render / compose / consult (their status routes are project-scoped).'),
|
|
569
|
-
timeoutMs: z
|
|
570
|
-
.number()
|
|
571
|
-
.optional()
|
|
572
|
-
.describe(`Maximum wait in ms. Capped at ${capMs}; a longer value is clamped, not honoured.`),
|
|
573
|
-
}),
|
|
574
|
-
execute: async (args) => {
|
|
575
|
-
const toolCallId = `tc_${crypto.randomUUID().slice(0, 8)}`;
|
|
576
|
-
onProgress?.({
|
|
577
|
-
type: 'tool-call',
|
|
578
|
-
agent: 'router',
|
|
579
|
-
toolName: spec.name,
|
|
580
|
-
args,
|
|
581
|
-
toolCallId,
|
|
582
|
-
timestamp: Date.now(),
|
|
583
|
-
});
|
|
584
|
-
const finish = (output, ok) => {
|
|
585
|
-
onProgress?.({
|
|
586
|
-
type: 'tool-result',
|
|
587
|
-
agent: 'router',
|
|
588
|
-
toolName: spec.name,
|
|
589
|
-
success: ok,
|
|
590
|
-
message: output.slice(0, 200),
|
|
591
|
-
toolCallId,
|
|
592
|
-
timestamp: Date.now(),
|
|
593
|
-
});
|
|
594
|
-
return output;
|
|
595
|
-
};
|
|
596
|
-
// The project-scoped kinds cannot be polled without a projectId — say so
|
|
597
|
-
// instead of issuing a request to `/projects/undefined/...`.
|
|
598
|
-
if (!args.projectId && (args.kind === 'render' || args.kind === 'compose' || args.kind === 'consult')) {
|
|
599
|
-
return finish(JSON.stringify({ error: `wait_job(kind="${args.kind}") requires a projectId.` }), false);
|
|
600
|
-
}
|
|
601
|
-
const deadline = Date.now() + Math.min(args.timeoutMs ?? capMs, capMs);
|
|
602
|
-
const res = await waitForJob(ex, args.kind, args.id, args.projectId, deadline, opts?.jobPoll?.sleep || opts?.jobPoll?.pollMs
|
|
603
|
-
? { ...(opts.jobPoll?.sleep ? { sleep: opts.jobPoll.sleep } : {}), ...(opts.jobPoll?.pollMs ? { pollMs: opts.jobPoll.pollMs } : {}) }
|
|
604
|
-
: undefined);
|
|
605
|
-
// An async tool's PIXELS arrive here, not at the enqueue call (a 202 carries
|
|
606
|
-
// only a jobId). Inline them the same way spec.preview does on the generic
|
|
607
|
-
// path, or the agent judges a preview it was only told about.
|
|
608
|
-
if (res.mediaUrls?.length) {
|
|
609
|
-
const images = await fetchPreviews(res.mediaUrls, 12, fetchImage ? { fetchOne: fetchImage } : undefined);
|
|
610
|
-
if (images.length) {
|
|
611
|
-
finish(res.text, res.ok);
|
|
612
|
-
return { __preview: true, text: res.text, images };
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
return finish(res.text, res.ok);
|
|
616
|
-
},
|
|
617
|
-
toModelOutput: previewVisionModelOutput,
|
|
618
|
-
});
|
|
619
|
-
}
|
|
620
|
-
/** Build one `ai` tool from a curated spec, executing through `ex`. */
|
|
621
|
-
function buildOneTool(spec, ex, budget, opts) {
|
|
622
|
-
if (spec.name === 'render_frame')
|
|
623
|
-
return buildRenderFrameTool(spec, ex, budget, opts);
|
|
624
|
-
// wait_job polls a DIFFERENT status op per kind — it has no single op to
|
|
625
|
-
// dispatch, so the generic path cannot build it.
|
|
626
|
-
if (spec.name === 'wait_job')
|
|
627
|
-
return buildWaitJobTool(spec, ex, opts);
|
|
628
|
-
// preview_component returns a presigned PNG `{ url }`; inline it so the designer
|
|
629
|
-
// LLM SEES the graphic (the generic path below would return the url as TEXT).
|
|
630
|
-
if (spec.name === 'preview_component')
|
|
631
|
-
return buildPreviewComponentTool(spec, ex, opts);
|
|
632
|
-
// verify_composition enqueues the judge; block until terminal and return the
|
|
633
|
-
// SCORES (the surface has no wait_job — a receipt is a verdict the LLM can
|
|
634
|
-
// never read).
|
|
635
|
-
if (spec.name === 'verify_composition')
|
|
636
|
-
return buildVerifyCompositionTool(spec, ex, budget, opts);
|
|
637
|
-
// view_style_reference presigns the BOUND kit's exemplar frames and inlines
|
|
638
|
-
// them as vision blocks (the reference pixels) — the generic path would return
|
|
639
|
-
// the {exemplars:[{url}]} JSON as TEXT and the maker would never SEE the frames.
|
|
640
|
-
if (spec.name === 'view_style_reference')
|
|
641
|
-
return buildViewStyleReferenceTool(spec, ex, budget, opts);
|
|
642
|
-
const op = opById(spec.operationId);
|
|
643
|
-
const onProgress = opts?.onProgress;
|
|
644
|
-
// friendly input arg name → op path-param name, reversed for mapArg lookup.
|
|
645
|
-
const reverse = {};
|
|
646
|
-
for (const [friendly, opParam] of Object.entries(spec.pathArgs ?? {})) {
|
|
647
|
-
reverse[opParam] = friendly;
|
|
648
|
-
}
|
|
649
|
-
const shape = {};
|
|
650
|
-
for (const friendly of Object.keys(spec.pathArgs ?? {})) {
|
|
651
|
-
// Session-managed path args are injected from opts.session — never exposed.
|
|
652
|
-
if (opts?.session && SESSION_PATH_ARGS.has(friendly))
|
|
653
|
-
continue;
|
|
654
|
-
shape[friendly] = z.string().describe(`${friendly} (path).`);
|
|
655
|
-
}
|
|
656
|
-
// Query filters (curation queryArgs): optional strings threaded onto the
|
|
657
|
-
// request URL — the arg name IS the query-param name. Without this a
|
|
658
|
-
// description teaching a filter returned the UNFILTERED page silently.
|
|
659
|
-
for (const [qArg, qDesc] of Object.entries(spec.queryArgs ?? {})) {
|
|
660
|
-
shape[qArg] = z.string().optional().describe(qDesc);
|
|
661
|
-
}
|
|
662
|
-
const bodySchema = bodySchemaFor(op);
|
|
663
|
-
if (bodySchema)
|
|
664
|
-
shape.body = bodySchema;
|
|
665
|
-
// K4 examples are authored for the SESSION-injected surface (path args
|
|
666
|
-
// hidden). On a sessionless build the path args are part of the schema, and
|
|
667
|
-
// Anthropic VALIDATES every example against input_schema — one example
|
|
668
|
-
// missing a required `projectId` 400s the entire request ("tools.N.custom:
|
|
669
|
-
// Example at index 0 is invalid"). Keep only examples that carry every
|
|
670
|
-
// exposed path arg; with a session none are exposed, so all examples pass
|
|
671
|
-
// through unchanged (the Director path is unaffected).
|
|
672
|
-
const exposedPathArgs = Object.keys(spec.pathArgs ?? {}).filter((friendly) => !(opts?.session && SESSION_PATH_ARGS.has(friendly)));
|
|
673
|
-
const schemaSafeExamples = spec.inputExamples?.filter((exm) => exposedPathArgs.every((a) => exm.input[a] !== undefined));
|
|
674
|
-
return tool({
|
|
675
|
-
description: spec.description,
|
|
676
|
-
inputSchema: z.object(shape),
|
|
677
|
-
// K4: worked examples from the curated spec (apply_composition teaches the
|
|
678
|
-
// targeted patch). Serialized as Anthropic input_examples; the provider
|
|
679
|
-
// adds the advanced-tool-use beta itself.
|
|
680
|
-
...(schemaSafeExamples?.length ? { inputExamples: schemaSafeExamples } : {}),
|
|
681
|
-
execute: async (args) => {
|
|
682
|
-
// Budgets live in the special-cased tools (render_frame / verify_composition);
|
|
683
|
-
// every generic read/mutate tool (apply_composition, get_media_context, …) is
|
|
684
|
-
// un-budgeted.
|
|
685
|
-
const toolCallId = `tc_${crypto.randomUUID().slice(0, 8)}`;
|
|
686
|
-
// Progress: tool-call (before the network op — buildTools.ts:131-134).
|
|
687
|
-
onProgress?.({
|
|
688
|
-
type: 'tool-call',
|
|
689
|
-
agent: 'router',
|
|
690
|
-
toolName: spec.name,
|
|
691
|
-
args,
|
|
692
|
-
toolCallId,
|
|
693
|
-
timestamp: Date.now(),
|
|
694
|
-
});
|
|
695
|
-
const queryPairs = Object.keys(spec.queryArgs ?? {})
|
|
696
|
-
.filter((qArg) => args[qArg] !== undefined && args[qArg] !== null && args[qArg] !== '')
|
|
697
|
-
.map((qArg) => `${encodeURIComponent(qArg)}=${encodeURIComponent(String(args[qArg]))}`);
|
|
698
|
-
const res = await invokeOp(ex, op, (p) => sessionPathValue(reverse[p], opts) ?? args[reverse[p]], injectSession(spec, args, opts), undefined, queryPairs.length > 0 ? queryPairs.join('&') : undefined);
|
|
699
|
-
const ok = isOk(res.status);
|
|
700
|
-
const json = ok && res.text ? safeParse(res.text) : undefined;
|
|
701
|
-
// Contract 3(a): a non-2xx is RETURNED as a JSON-string tool output, NOT thrown.
|
|
702
|
-
const output = ok
|
|
703
|
-
? spec.okText
|
|
704
|
-
? spec.okText(args)
|
|
705
|
-
: spec.okFormat
|
|
706
|
-
? spec.okFormat(json ?? {}, args)
|
|
707
|
-
: res.text || `${op.method} → ${res.status}`
|
|
708
|
-
: JSON.stringify({ error: fmtErr(apiErrorFrom(res)) });
|
|
709
|
-
// Progress: tool-result (message capped at 200 chars — buildTools.ts:160).
|
|
710
|
-
onProgress?.({
|
|
711
|
-
type: 'tool-result',
|
|
712
|
-
agent: 'router',
|
|
713
|
-
toolName: spec.name,
|
|
714
|
-
success: ok,
|
|
715
|
-
message: output.slice(0, 200),
|
|
716
|
-
toolCallId,
|
|
717
|
-
timestamp: Date.now(),
|
|
718
|
-
});
|
|
719
|
-
// spec.preview — the generic-path twin of server.ts:202: attach the
|
|
720
|
-
// pixels the JSON already links (preview_frame stills, checkpoint
|
|
721
|
-
// storyboards) so the model judges what it can SEE. ADDITIVE (text
|
|
722
|
-
// unchanged), gated by `attach` BEFORE any bytes move, and a failed
|
|
723
|
-
// fetch drops silently — a dead thumbnail never fails the call it
|
|
724
|
-
// decorates. Audio previews stay URL-as-text (tool results carry
|
|
725
|
-
// image blocks only).
|
|
726
|
-
if (ok && spec.preview && json && (spec.preview.attach?.(json) ?? true)) {
|
|
727
|
-
const kind = typeof spec.preview.kind === 'function'
|
|
728
|
-
? spec.preview.kind(json)
|
|
729
|
-
: spec.preview.kind ?? 'image';
|
|
730
|
-
if (kind === 'image') {
|
|
731
|
-
// fetchPreviews (not defaultFetchImage): isolate-safe base64 — the
|
|
732
|
-
// Convex V8 runtime has no Buffer — plus the byte budget and cache.
|
|
733
|
-
const images = await fetchPreviews(spec.preview.urls(json), spec.preview.cap ?? 12, opts?.fetchImage ? { fetchOne: opts.fetchImage } : undefined);
|
|
734
|
-
if (images.length)
|
|
735
|
-
return { __preview: true, text: output, images };
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
return output;
|
|
739
|
-
},
|
|
740
|
-
...(spec.preview ? { toModelOutput: previewVisionModelOutput } : {}),
|
|
741
|
-
});
|
|
742
|
-
}
|
|
743
|
-
/**
|
|
744
|
-
* Build an `ai` `ToolSet` from the curated SSOT, filtered to the caller's
|
|
745
|
-
* `allowedNames`. Uses the REAL generated manifest (via opById) so a
|
|
746
|
-
* missing/renamed operationId fails loud — that is the drift signal.
|
|
747
|
-
*/
|
|
748
|
-
export function buildCuratedToolSet(ex, opts) {
|
|
749
|
-
const allowed = new Set(opts.allowedNames);
|
|
750
|
-
// One budget per build → one budget spans the whole author run (T5). A fresh
|
|
751
|
-
// ToolSet is built per author invocation, so counters reset per run — matching
|
|
752
|
-
// the in-process per-run caps (director.ts:282/290).
|
|
753
|
-
const budget = {
|
|
754
|
-
caps: opts?.budgets ?? DEFAULT_BUDGETS,
|
|
755
|
-
used: { captures: 0, verifies: 0, styleViews: 0 },
|
|
756
|
-
};
|
|
757
|
-
const tools = {};
|
|
758
|
-
// Internal loop entries FIRST, then the public curated set — the in-loop
|
|
759
|
-
// session-scoped names (A11/A12-internal) resolve even though they left the
|
|
760
|
-
// client surface at the 2026-07 verb cutover. `preview_component` exists in
|
|
761
|
-
// BOTH (public = sessionless managed box; internal = the run's own box) —
|
|
762
|
-
// internal wins here because this builder only serves the in-loop surface.
|
|
763
|
-
for (const spec of [...INTERNAL_LOOP_TOOLS, ...CURATED_TOOLS]) {
|
|
764
|
-
if (!allowed.has(spec.name))
|
|
765
|
-
continue;
|
|
766
|
-
if (tools[spec.name])
|
|
767
|
-
continue;
|
|
768
|
-
tools[spec.name] = buildOneTool(spec, ex, budget, opts);
|
|
769
|
-
}
|
|
770
|
-
return tools;
|
|
771
|
-
}
|