agl 1.0.17 → 21.0.2
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/.smartconfig.json +55 -0
- package/changelog.md +1026 -0
- package/cli.js +14 -0
- package/dist_serve/bundle.js +44778 -0
- package/dist_serve/index.html +13 -0
- package/dist_ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/00_commitinfo_data.js +9 -0
- package/dist_ts/classes.authmodels.d.ts +126 -0
- package/dist_ts/classes.authmodels.js +1422 -0
- package/dist_ts/classes.authstore.d.ts +160 -0
- package/dist_ts/classes.authstore.js +2483 -0
- package/dist_ts/classes.browserresourcehost.d.ts +85 -0
- package/dist_ts/classes.browserresourcehost.js +912 -0
- package/dist_ts/classes.cli.d.ts +21 -0
- package/dist_ts/classes.cli.js +1082 -0
- package/dist_ts/classes.config.d.ts +23 -0
- package/dist_ts/classes.config.js +267 -0
- package/dist_ts/classes.controller.d.ts +523 -0
- package/dist_ts/classes.controller.js +10611 -0
- package/dist_ts/classes.controllermanagement.d.ts +32 -0
- package/dist_ts/classes.controllermanagement.js +222 -0
- package/dist_ts/classes.draftmanager.d.ts +30 -0
- package/dist_ts/classes.draftmanager.js +179 -0
- package/dist_ts/classes.embeddeddb.d.ts +30 -0
- package/dist_ts/classes.embeddeddb.js +159 -0
- package/dist_ts/classes.flexframedtransport.d.ts +56 -0
- package/dist_ts/classes.flexframedtransport.js +275 -0
- package/dist_ts/classes.flexmodels.private.d.ts +231 -0
- package/dist_ts/classes.flexmodels.private.js +985 -0
- package/dist_ts/classes.flexmodels.public.d.ts +207 -0
- package/dist_ts/classes.flexmodels.public.js +885 -0
- package/dist_ts/classes.flexprojectionreader.d.ts +89 -0
- package/dist_ts/classes.flexprojectionreader.js +580 -0
- package/dist_ts/classes.flexservice.d.ts +196 -0
- package/dist_ts/classes.flexservice.js +3704 -0
- package/dist_ts/classes.flexstore.d.ts +174 -0
- package/dist_ts/classes.flexstore.js +3400 -0
- package/dist_ts/classes.flexsupervisor.d.ts +105 -0
- package/dist_ts/classes.flexsupervisor.js +1191 -0
- package/dist_ts/classes.gitreversion.d.ts +431 -0
- package/dist_ts/classes.gitreversion.js +10592 -0
- package/dist_ts/classes.harnessoperationmanager.d.ts +113 -0
- package/dist_ts/classes.harnessoperationmanager.js +602 -0
- package/dist_ts/classes.opencodeclient.d.ts +146 -0
- package/dist_ts/classes.opencodeclient.js +3003 -0
- package/dist_ts/classes.opencodesupervisor.d.ts +60 -0
- package/dist_ts/classes.opencodesupervisor.js +1003 -0
- package/dist_ts/classes.passkeymanager.d.ts +34 -0
- package/dist_ts/classes.passkeymanager.js +456 -0
- package/dist_ts/classes.processinspection.d.ts +50 -0
- package/dist_ts/classes.processinspection.js +484 -0
- package/dist_ts/classes.resourcecoordinator.d.ts +64 -0
- package/dist_ts/classes.resourcecoordinator.js +294 -0
- package/dist_ts/classes.sessionintelligencereader.d.ts +13 -0
- package/dist_ts/classes.sessionintelligencereader.js +40 -0
- package/dist_ts/classes.systemmetricssampler.d.ts +53 -0
- package/dist_ts/classes.systemmetricssampler.js +168 -0
- package/dist_ts/classes.terminalmanager.d.ts +95 -0
- package/dist_ts/classes.terminalmanager.js +981 -0
- package/dist_ts/classes.terminalresourcehost.d.ts +20 -0
- package/dist_ts/classes.terminalresourcehost.js +84 -0
- package/dist_ts/classes.upgradecoordinator.d.ts +244 -0
- package/dist_ts/classes.upgradecoordinator.js +2155 -0
- package/dist_ts/classes.upgradetransaction.d.ts +92 -0
- package/dist_ts/classes.upgradetransaction.js +2125 -0
- package/dist_ts/classes.uploadmanager.d.ts +24 -0
- package/dist_ts/classes.uploadmanager.js +210 -0
- package/dist_ts/classes.validation.d.ts +91 -0
- package/dist_ts/classes.validation.js +1087 -0
- package/dist_ts/flexharness.child.d.ts +1 -0
- package/dist_ts/flexharness.child.js +28 -0
- package/dist_ts/functions.canonicaldirectory.d.ts +21 -0
- package/dist_ts/functions.canonicaldirectory.js +144 -0
- package/dist_ts/functions.controllerdataroot.d.ts +15 -0
- package/dist_ts/functions.controllerdataroot.js +76 -0
- package/dist_ts/functions.embeddeddb.d.ts +2 -0
- package/dist_ts/functions.embeddeddb.js +21 -0
- package/dist_ts/functions.flexcredentialstore.d.ts +16 -0
- package/dist_ts/functions.flexcredentialstore.js +93 -0
- package/dist_ts/functions.opencodeauth.d.ts +3 -0
- package/dist_ts/functions.opencodeauth.js +31 -0
- package/dist_ts/functions.runtimeenvironment.d.ts +2 -0
- package/dist_ts/functions.runtimeenvironment.js +79 -0
- package/dist_ts/functions.upgradefollower.d.ts +3 -0
- package/dist_ts/functions.upgradefollower.js +95 -0
- package/dist_ts/index.d.ts +24 -0
- package/dist_ts/index.js +25 -0
- package/dist_ts/interfaces.auth.d.ts +181 -0
- package/dist_ts/interfaces.auth.js +82 -0
- package/dist_ts/interfaces.config.d.ts +34 -0
- package/dist_ts/interfaces.config.js +2 -0
- package/dist_ts/interfaces.flexipc.d.ts +939 -0
- package/dist_ts/interfaces.flexipc.js +1492 -0
- package/dist_ts/interfaces.gitreversion.d.ts +246 -0
- package/dist_ts/interfaces.gitreversion.js +2 -0
- package/dist_ts/interfaces.lifecycle.d.ts +37 -0
- package/dist_ts/interfaces.lifecycle.js +2 -0
- package/dist_ts/interfaces.opencode.d.ts +101 -0
- package/dist_ts/interfaces.opencode.js +3 -0
- package/dist_ts/interfaces.projects.d.ts +156 -0
- package/dist_ts/interfaces.projects.js +5 -0
- package/dist_ts/plugins.d.ts +36 -0
- package/dist_ts/plugins.js +40 -0
- package/dist_ts_interfaces/index.d.ts +2 -0
- package/dist_ts_interfaces/index.js +3 -0
- package/dist_ts_interfaces/interfaces.d.ts +747 -0
- package/dist_ts_interfaces/interfaces.js +47 -0
- package/dist_ts_interfaces/plugins.d.ts +4 -0
- package/dist_ts_interfaces/plugins.js +3 -0
- package/dist_ts_interfaces/requests.d.ts +806 -0
- package/dist_ts_interfaces/requests.js +2 -0
- package/dist_ts_migration/classes.documentmigrationrunner.d.ts +8 -0
- package/dist_ts_migration/classes.documentmigrationrunner.js +24 -0
- package/dist_ts_migration/classes.flexmigrationrunner.d.ts +11 -0
- package/dist_ts_migration/classes.flexmigrationrunner.js +25 -0
- package/dist_ts_migration/classes.flexpostharnessmigrationrunner.d.ts +10 -0
- package/dist_ts_migration/classes.flexpostharnessmigrationrunner.js +15 -0
- package/dist_ts_migration/classes.migrationrunner.d.ts +8 -0
- package/dist_ts_migration/classes.migrationrunner.js +34 -0
- package/dist_ts_migration/index.d.ts +12 -0
- package/dist_ts_migration/index.js +13 -0
- package/dist_ts_migration/v10_flexharnessv3migration.d.ts +11 -0
- package/dist_ts_migration/v10_flexharnessv3migration.js +23 -0
- package/dist_ts_migration/v13_flexprojectionorder.d.ts +11 -0
- package/dist_ts_migration/v13_flexprojectionorder.js +23 -0
- package/dist_ts_migration/v16_flexprovidercredentials.d.ts +22 -0
- package/dist_ts_migration/v16_flexprovidercredentials.js +149 -0
- package/dist_ts_migration/v18_resourcelayout.d.ts +22 -0
- package/dist_ts_migration/v18_resourcelayout.js +23 -0
- package/dist_ts_migration/v18_resourcelayoutmigration.d.ts +6 -0
- package/dist_ts_migration/v18_resourcelayoutmigration.js +61 -0
- package/dist_ts_migration/v19_flexprojectmanagement.d.ts +11 -0
- package/dist_ts_migration/v19_flexprojectmanagement.js +21 -0
- package/dist_ts_migration/v1_embeddeddatabaselocation.d.ts +28 -0
- package/dist_ts_migration/v1_embeddeddatabaselocation.js +415 -0
- package/dist_ts_migration/v22_projectfilesystemidentitymigration.d.ts +7 -0
- package/dist_ts_migration/v22_projectfilesystemidentitymigration.js +105 -0
- package/dist_ts_migration/v2_controllerdataroot.d.ts +130 -0
- package/dist_ts_migration/v2_controllerdataroot.js +1887 -0
- package/dist_ts_migration/v6_protocoldocumentmigration.d.ts +7 -0
- package/dist_ts_migration/v6_protocoldocumentmigration.js +100 -0
- package/dist_ts_migration/v6_protocoldocuments.d.ts +7 -0
- package/dist_ts_migration/v6_protocoldocuments.js +138 -0
- package/html/index.html +13 -0
- package/{LICENSE → license.md} +2 -2
- package/package.json +86 -48
- package/readme.md +480 -0
- package/readme.plan.md +188 -0
- package/ts/00_commitinfo_data.ts +8 -0
- package/ts/classes.authmodels.ts +1245 -0
- package/ts/classes.authstore.ts +3274 -0
- package/ts/classes.browserresourcehost.ts +1205 -0
- package/ts/classes.cli.ts +1254 -0
- package/ts/classes.config.ts +351 -0
- package/ts/classes.controller.ts +14426 -0
- package/ts/classes.controllermanagement.ts +330 -0
- package/ts/classes.draftmanager.ts +232 -0
- package/ts/classes.embeddeddb.ts +166 -0
- package/ts/classes.flexframedtransport.ts +283 -0
- package/ts/classes.flexmodels.private.ts +823 -0
- package/ts/classes.flexmodels.public.ts +732 -0
- package/ts/classes.flexprojectionreader.ts +816 -0
- package/ts/classes.flexservice.ts +4302 -0
- package/ts/classes.flexstore.ts +4565 -0
- package/ts/classes.flexsupervisor.ts +1453 -0
- package/ts/classes.gitreversion.ts +13353 -0
- package/ts/classes.harnessoperationmanager.ts +870 -0
- package/ts/classes.opencodeclient.ts +4110 -0
- package/ts/classes.opencodesupervisor.ts +1280 -0
- package/ts/classes.passkeymanager.ts +570 -0
- package/ts/classes.processinspection.ts +623 -0
- package/ts/classes.resourcecoordinator.ts +436 -0
- package/ts/classes.sessionintelligencereader.ts +52 -0
- package/ts/classes.systemmetricssampler.ts +215 -0
- package/ts/classes.terminalmanager.ts +1207 -0
- package/ts/classes.terminalresourcehost.ts +122 -0
- package/ts/classes.upgradecoordinator.ts +2675 -0
- package/ts/classes.upgradetransaction.ts +2900 -0
- package/ts/classes.uploadmanager.ts +240 -0
- package/ts/classes.validation.ts +1409 -0
- package/ts/flexharness.child.ts +35 -0
- package/ts/functions.canonicaldirectory.ts +213 -0
- package/ts/functions.controllerdataroot.ts +107 -0
- package/ts/functions.embeddeddb.ts +22 -0
- package/ts/functions.flexcredentialstore.ts +128 -0
- package/ts/functions.opencodeauth.ts +34 -0
- package/ts/functions.runtimeenvironment.ts +90 -0
- package/ts/functions.upgradefollower.ts +115 -0
- package/ts/index.ts +36 -6
- package/ts/interfaces.auth.ts +316 -0
- package/ts/interfaces.config.ts +37 -0
- package/ts/interfaces.flexipc.ts +2586 -0
- package/ts/interfaces.gitreversion.ts +336 -0
- package/ts/interfaces.lifecycle.ts +46 -0
- package/ts/interfaces.opencode.ts +153 -0
- package/ts/interfaces.projects.ts +182 -0
- package/ts/plugins.ts +64 -0
- package/ts_interfaces/index.ts +2 -0
- package/ts_interfaces/interfaces.ts +827 -0
- package/ts_interfaces/plugins.ts +15 -0
- package/ts_interfaces/requests.ts +1186 -0
- package/ts_migration/classes.documentmigrationrunner.ts +32 -0
- package/ts_migration/classes.flexmigrationrunner.ts +33 -0
- package/ts_migration/classes.flexpostharnessmigrationrunner.ts +22 -0
- package/ts_migration/classes.migrationrunner.ts +40 -0
- package/ts_migration/index.ts +12 -0
- package/ts_migration/v10_flexharnessv3migration.ts +35 -0
- package/ts_migration/v13_flexprojectionorder.ts +33 -0
- package/ts_migration/v16_flexprovidercredentials.ts +196 -0
- package/ts_migration/v18_resourcelayout.ts +55 -0
- package/ts_migration/v18_resourcelayoutmigration.ts +68 -0
- package/ts_migration/v19_flexprojectmanagement.ts +28 -0
- package/ts_migration/v1_embeddeddatabaselocation.ts +560 -0
- package/ts_migration/v22_projectfilesystemidentitymigration.ts +121 -0
- package/ts_migration/v2_controllerdataroot.ts +2349 -0
- package/ts_migration/v6_protocoldocumentmigration.ts +144 -0
- package/ts_migration/v6_protocoldocuments.ts +198 -0
- package/ts_web/00_commitinfo_data.ts +8 -0
- package/ts_web/classes.controllersocketclient.ts +517 -0
- package/ts_web/classes.sessiondraftsync.ts +390 -0
- package/ts_web/elements.harnesscontrollerapp.ts +11494 -0
- package/ts_web/index.ts +2 -0
- package/ts_web/plugins.ts +31 -0
- package/.gitlab-ci.yml +0 -71
- package/.npmignore +0 -5
- package/README.md +0 -40
- package/dist/cli.js +0 -2
- package/dist/index.d.ts +0 -0
- package/dist/index.js +0 -9
- package/dist/mod_bundle/index.d.ts +0 -1
- package/dist/mod_bundle/index.js +0 -18
- package/dist/mod_bundle/mod.plugins.d.ts +0 -4
- package/dist/mod_bundle/mod.plugins.js +0 -11
- package/dist/mod_install/index.d.ts +0 -4
- package/dist/mod_install/index.js +0 -21
- package/dist/mod_install/mod.plugins.d.ts +0 -1
- package/dist/mod_install/mod.plugins.js +0 -7
- package/dist/mod_standard/index.d.ts +0 -4
- package/dist/mod_standard/index.js +0 -21
- package/dist/mod_standard/mod.browser.d.ts +0 -6
- package/dist/mod_standard/mod.browser.js +0 -14
- package/dist/mod_standard/mod.clean.d.ts +0 -4
- package/dist/mod_standard/mod.clean.js +0 -12
- package/dist/mod_standard/mod.compile.d.ts +0 -4
- package/dist/mod_standard/mod.compile.js +0 -21
- package/dist/mod_standard/mod.plugins.d.ts +0 -8
- package/dist/mod_standard/mod.plugins.js +0 -19
- package/dist/mod_standard/mod.test.d.ts +0 -4
- package/dist/mod_standard/mod.test.js +0 -12
- package/dist/plr.cli.d.ts +0 -3
- package/dist/plr.cli.js +0 -57
- package/dist/plr.config.d.ts +0 -11
- package/dist/plr.config.js +0 -26
- package/dist/plr.mods.d.ts +0 -17
- package/dist/plr.mods.js +0 -17
- package/dist/plr.paths.d.ts +0 -19
- package/dist/plr.paths.js +0 -26
- package/dist/plr.plugins.d.ts +0 -8
- package/dist/plr.plugins.js +0 -16
- package/npmextra.json +0 -10
- package/ts/mod_bundle/index.ts +0 -20
- package/ts/mod_bundle/mod.plugins.ts +0 -9
- package/ts/mod_install/index.ts +0 -20
- package/ts/mod_install/mod.plugins.ts +0 -1
- package/ts/mod_standard/index.ts +0 -23
- package/ts/mod_standard/mod.browser.ts +0 -17
- package/ts/mod_standard/mod.clean.ts +0 -13
- package/ts/mod_standard/mod.compile.ts +0 -23
- package/ts/mod_standard/mod.plugins.ts +0 -17
- package/ts/mod_standard/mod.test.ts +0 -13
- package/ts/plr.cli.ts +0 -60
- package/ts/plr.config.ts +0 -36
- package/ts/plr.mods.ts +0 -25
- package/ts/plr.paths.ts +0 -29
- package/ts/plr.plugins.ts +0 -17
- package/tslint.json +0 -3
- package/yarn.lock +0 -3464
|
@@ -0,0 +1,2586 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import {
|
|
3
|
+
controllerFilesystemIdentityMaximumComponents,
|
|
4
|
+
controllerProjectLimit,
|
|
5
|
+
type IControllerProjectFilesystemIdentity,
|
|
6
|
+
} from './interfaces.projects.js';
|
|
7
|
+
|
|
8
|
+
type TFlexCreateSessionOptions = plugins.flexharness.IFlexCreateSessionOptions;
|
|
9
|
+
type TFlexMessage = plugins.flexharness.IFlexMessage;
|
|
10
|
+
type TFlexMessagePage = plugins.flexharness.IFlexMessagePage;
|
|
11
|
+
type TFlexPermissionRequest = plugins.flexharness.IFlexPermissionRequest;
|
|
12
|
+
export type TFlexPromptQueueEntry = plugins.flexharness.IFlexPromptQueueEntry;
|
|
13
|
+
type TFlexPromptOptions = plugins.flexharness.IFlexPromptOptions;
|
|
14
|
+
type TFlexSession = plugins.flexharness.IFlexSession;
|
|
15
|
+
type TFlexToolMessagePart = plugins.flexharness.IFlexToolMessagePart;
|
|
16
|
+
type TFlexReasoningMessagePart = Extract<plugins.flexharness.TFlexMessagePart, { type: 'reasoning' }>;
|
|
17
|
+
type TFlexTextMessagePart = Extract<plugins.flexharness.TFlexMessagePart, { type: 'text' }>;
|
|
18
|
+
type TFlexUpdateSessionOptions = plugins.flexharness.IFlexUpdateSessionOptions;
|
|
19
|
+
type TFlexPermissionDecision = plugins.flexharness.TFlexPermissionDecision;
|
|
20
|
+
type TFlexPrompt = plugins.flexharness.TFlexPrompt;
|
|
21
|
+
type TFlexProjectManagementRecord = plugins.flexharness.TFlexProjectManagementRecord;
|
|
22
|
+
type TFlexProjectManagementSnapshot = plugins.flexharness.IFlexProjectManagementSnapshot;
|
|
23
|
+
type TFlexProjectManagementTombstone = plugins.flexharness.IFlexProjectManagementTombstone;
|
|
24
|
+
type TFlexProjectManagementWriteContext = plugins.flexharness.IFlexProjectManagementWriteContext;
|
|
25
|
+
type TFlexJsonValue = plugins.flexharness.TJsonValue;
|
|
26
|
+
export type TFlexSlashCommandDescriptor = plugins.flexharness.IFlexSlashCommandDescriptor;
|
|
27
|
+
export type TFlexSessionReversionInfo = plugins.flexharness.IFlexSessionReversionInfo;
|
|
28
|
+
export type TFlexTurnReversionFinalizedOutcome =
|
|
29
|
+
plugins.flexharness.TFlexTurnReversionFinalizedOutcome;
|
|
30
|
+
export type TFlexTurnReversionCaptureInspectionV2 =
|
|
31
|
+
plugins.flexharness.TFlexTurnReversionCaptureInspectionV2;
|
|
32
|
+
export type TFlexTurnReversionApplyInspection =
|
|
33
|
+
plugins.flexharness.IFlexTurnReversionApplyInspection;
|
|
34
|
+
|
|
35
|
+
export const flexIpcProtocolVersion = 14 as const;
|
|
36
|
+
export const flexIpcMaximumFrameBytes = 2 * 1024 * 1024;
|
|
37
|
+
export const flexIpcMaximumPendingRequests = 64;
|
|
38
|
+
export const flexIpcMaximumBrowserChannels = 128;
|
|
39
|
+
export const flexIpcMaximumBrowserFrameBytes = 256 * 1024 + 4;
|
|
40
|
+
export const flexIpcMaximumPendingBrowserFrames = 128;
|
|
41
|
+
export const flexIpcMaximumPendingBrowserFrameBytes = 8 * 1024 * 1024;
|
|
42
|
+
export const flexIpcMaximumChildJobs = 64;
|
|
43
|
+
export const flexIpcMaximumPromptQueueEntries = 128;
|
|
44
|
+
export const flexIpcMaximumQueuedSessionChanges = 256;
|
|
45
|
+
export const flexIpcMaximumQueuedSessionChangeBytes = 8 * 1024 * 1024;
|
|
46
|
+
export const flexIpcControlTimeoutMs = 35_000;
|
|
47
|
+
export const flexIpcStartupTimeoutMs = 180_000;
|
|
48
|
+
export const flexIpcDisposeTimeoutMs = 10_000;
|
|
49
|
+
export const flexIpcSlashTimeoutMs = 30 * 60 * 1000;
|
|
50
|
+
export const flexIpcMaximumHostTimeoutMs = 5 * 60 * 1000;
|
|
51
|
+
export const flexIpcCancellationGraceTimeoutMs = 2_000;
|
|
52
|
+
|
|
53
|
+
export interface IFlexProjectRecord {
|
|
54
|
+
projectId: string;
|
|
55
|
+
directory: string;
|
|
56
|
+
directoryIdentity?: IControllerProjectFilesystemIdentity;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface IFlexIdentityBoundProjectRecord extends IFlexProjectRecord {
|
|
60
|
+
directoryIdentity: IControllerProjectFilesystemIdentity;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface IFlexDatabaseDescriptor {
|
|
64
|
+
mongoDbUrl: string;
|
|
65
|
+
mongoDbName: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface IFlexServiceInit {
|
|
69
|
+
controllerId: string;
|
|
70
|
+
database: IFlexDatabaseDescriptor;
|
|
71
|
+
flexCredentialDirectory: string;
|
|
72
|
+
projects: IFlexProjectRecord[];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface IFlexBrowserResourceDescriptor {
|
|
76
|
+
resourceId: string;
|
|
77
|
+
attachmentRevision: number;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface IFlexBrowserChannelBinding {
|
|
81
|
+
projectId: string;
|
|
82
|
+
browserResourceId: string;
|
|
83
|
+
attachmentAuthorityId: string;
|
|
84
|
+
attachmentRevision: number;
|
|
85
|
+
actorId: string;
|
|
86
|
+
peerId: string;
|
|
87
|
+
role: 'agent';
|
|
88
|
+
source: 'flex';
|
|
89
|
+
scopeId: string;
|
|
90
|
+
channelId: string;
|
|
91
|
+
runId: string;
|
|
92
|
+
sessionId: { harnessId: 'flex'; nativeId: string };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface IFlexIntelligenceSessionId {
|
|
96
|
+
harnessId: 'opencode' | 'flex';
|
|
97
|
+
nativeId: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface IFlexIntelligenceChatSummary {
|
|
101
|
+
sessionId: IFlexIntelligenceSessionId;
|
|
102
|
+
title: string;
|
|
103
|
+
updatedAt: string;
|
|
104
|
+
isActive?: boolean;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface IFlexIntelligenceChatMessage {
|
|
108
|
+
role: string;
|
|
109
|
+
text: string;
|
|
110
|
+
timestamp?: string;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface IFlexIntelligenceChatTranscript {
|
|
114
|
+
sessionId: IFlexIntelligenceSessionId;
|
|
115
|
+
title: string;
|
|
116
|
+
messages: IFlexIntelligenceChatMessage[];
|
|
117
|
+
totalMessages?: number;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface IFlexIntelligenceResult {
|
|
121
|
+
answer: string;
|
|
122
|
+
scratchpad: string;
|
|
123
|
+
model: string;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface IFlexTurnReversionBaseRequest {
|
|
127
|
+
scopeId: string;
|
|
128
|
+
storageKey: string;
|
|
129
|
+
sessionId: string;
|
|
130
|
+
runId: string;
|
|
131
|
+
captureId: string;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface IFlexTurnReversionApplyRequest extends IFlexTurnReversionBaseRequest {
|
|
135
|
+
reference: TFlexJsonValue;
|
|
136
|
+
operationId: string;
|
|
137
|
+
direction: 'undo' | 'redo';
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export interface IFlexTurnReversionReleaseRequest extends IFlexTurnReversionBaseRequest {
|
|
141
|
+
reference: TFlexJsonValue;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface IFlexGitWorktreeDescriptor {
|
|
145
|
+
worktreeId: string;
|
|
146
|
+
projectId: string;
|
|
147
|
+
sessionId: string;
|
|
148
|
+
path: string;
|
|
149
|
+
repositoryId: string;
|
|
150
|
+
createdAt: string;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export type TFlexIntelligenceJobStatus = 'running' | 'completed' | 'failed' | 'cancelled';
|
|
154
|
+
|
|
155
|
+
export interface IFlexHostRequestMap {
|
|
156
|
+
'reversion.prepare': {
|
|
157
|
+
request: IFlexTurnReversionBaseRequest;
|
|
158
|
+
response: { prepared: true };
|
|
159
|
+
};
|
|
160
|
+
'reversion.inspect-capture': {
|
|
161
|
+
request: IFlexTurnReversionBaseRequest;
|
|
162
|
+
response: TFlexTurnReversionCaptureInspectionV2;
|
|
163
|
+
};
|
|
164
|
+
'reversion.finalize': {
|
|
165
|
+
request: IFlexTurnReversionBaseRequest;
|
|
166
|
+
response: TFlexTurnReversionFinalizedOutcome;
|
|
167
|
+
};
|
|
168
|
+
'reversion.inspect-apply': {
|
|
169
|
+
request: IFlexTurnReversionApplyRequest;
|
|
170
|
+
response: TFlexTurnReversionApplyInspection;
|
|
171
|
+
};
|
|
172
|
+
'reversion.apply': {
|
|
173
|
+
request: IFlexTurnReversionApplyRequest;
|
|
174
|
+
response: { applied: true };
|
|
175
|
+
};
|
|
176
|
+
'reversion.release': {
|
|
177
|
+
request: IFlexTurnReversionReleaseRequest;
|
|
178
|
+
response: { released: true };
|
|
179
|
+
};
|
|
180
|
+
'git.worktree.create': {
|
|
181
|
+
request: { scopeId: string; sessionId: string };
|
|
182
|
+
response: { worktree: IFlexGitWorktreeDescriptor };
|
|
183
|
+
};
|
|
184
|
+
'git.worktree.list': {
|
|
185
|
+
request: { scopeId: string; sessionId: string };
|
|
186
|
+
response: { worktrees: IFlexGitWorktreeDescriptor[] };
|
|
187
|
+
};
|
|
188
|
+
'git.worktree.remove': {
|
|
189
|
+
request: {
|
|
190
|
+
scopeId: string;
|
|
191
|
+
sessionId: string;
|
|
192
|
+
worktreeId: string;
|
|
193
|
+
compensate?: true;
|
|
194
|
+
};
|
|
195
|
+
response: { removed: true };
|
|
196
|
+
};
|
|
197
|
+
'crossharness.chats.list': {
|
|
198
|
+
request: { capabilityToken: string; limit: number };
|
|
199
|
+
response: { chats: IFlexIntelligenceChatSummary[] };
|
|
200
|
+
};
|
|
201
|
+
'crossharness.chat.read': {
|
|
202
|
+
request: {
|
|
203
|
+
capabilityToken: string;
|
|
204
|
+
sessionId: IFlexIntelligenceSessionId;
|
|
205
|
+
limit: number;
|
|
206
|
+
maxChars: number;
|
|
207
|
+
};
|
|
208
|
+
response: { transcript: IFlexIntelligenceChatTranscript };
|
|
209
|
+
};
|
|
210
|
+
'project-management.load': {
|
|
211
|
+
request: {
|
|
212
|
+
storageKey: string;
|
|
213
|
+
sessionId: string;
|
|
214
|
+
sessionGenerationId: string;
|
|
215
|
+
sessionGenerationSequence: number;
|
|
216
|
+
};
|
|
217
|
+
response: { record: TFlexProjectManagementRecord | null };
|
|
218
|
+
};
|
|
219
|
+
'project-management.save': {
|
|
220
|
+
request: {
|
|
221
|
+
storageKey: string;
|
|
222
|
+
sessionId: string;
|
|
223
|
+
snapshot: TFlexProjectManagementSnapshot;
|
|
224
|
+
expectedRevision: number;
|
|
225
|
+
writeContext: TFlexProjectManagementWriteContext;
|
|
226
|
+
};
|
|
227
|
+
response: TFlexProjectManagementCommitResult;
|
|
228
|
+
};
|
|
229
|
+
'project-management.tombstone': {
|
|
230
|
+
request: {
|
|
231
|
+
storageKey: string;
|
|
232
|
+
sessionId: string;
|
|
233
|
+
tombstone: TFlexProjectManagementTombstone;
|
|
234
|
+
expectedRevision: number;
|
|
235
|
+
};
|
|
236
|
+
response: TFlexProjectManagementCommitResult;
|
|
237
|
+
};
|
|
238
|
+
'project-management.purge': {
|
|
239
|
+
request: { storageKey: string };
|
|
240
|
+
response: { purged: true };
|
|
241
|
+
};
|
|
242
|
+
'browser.resources.resolve': {
|
|
243
|
+
request: { scopeId: string; sessionId: string; runId: string };
|
|
244
|
+
response: { resources: IFlexBrowserResourceDescriptor[] };
|
|
245
|
+
};
|
|
246
|
+
'browser.channel.open': {
|
|
247
|
+
request: {
|
|
248
|
+
scopeId: string;
|
|
249
|
+
sessionId: string;
|
|
250
|
+
runId: string;
|
|
251
|
+
resourceId: string;
|
|
252
|
+
attachmentRevision: number;
|
|
253
|
+
channelId: string;
|
|
254
|
+
};
|
|
255
|
+
response: { binding: IFlexBrowserChannelBinding; capabilityToken: string };
|
|
256
|
+
};
|
|
257
|
+
'browser.channel.close': {
|
|
258
|
+
request: { channelId: string };
|
|
259
|
+
response: { closed: true };
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export type TFlexProjectManagementCommitResult =
|
|
264
|
+
| { committed: true }
|
|
265
|
+
| { committed: false; actualRevision: number };
|
|
266
|
+
|
|
267
|
+
export type TFlexHostRequestMethod = keyof IFlexHostRequestMap;
|
|
268
|
+
export type TFlexHostRequest<TMethod extends TFlexHostRequestMethod> =
|
|
269
|
+
IFlexHostRequestMap[TMethod]['request'];
|
|
270
|
+
export type TFlexHostResponse<TMethod extends TFlexHostRequestMethod> =
|
|
271
|
+
IFlexHostRequestMap[TMethod]['response'];
|
|
272
|
+
|
|
273
|
+
export type TFlexServiceState =
|
|
274
|
+
| 'starting'
|
|
275
|
+
| 'ready'
|
|
276
|
+
| 'unsupported'
|
|
277
|
+
| 'failed'
|
|
278
|
+
| 'stopping'
|
|
279
|
+
| 'stopped';
|
|
280
|
+
|
|
281
|
+
export type TFlexServiceStatusCode =
|
|
282
|
+
| 'UNSUPPORTED_RUNTIME'
|
|
283
|
+
| 'INITIALIZATION_FAILED'
|
|
284
|
+
| 'SERVICE_STOPPED';
|
|
285
|
+
|
|
286
|
+
export interface IFlexServiceStatus {
|
|
287
|
+
state: TFlexServiceState;
|
|
288
|
+
ready: boolean;
|
|
289
|
+
code?: TFlexServiceStatusCode;
|
|
290
|
+
startedAt?: string;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export type TFlexPublicConnectionState =
|
|
294
|
+
| 'pending'
|
|
295
|
+
| 'active'
|
|
296
|
+
| 'deleting'
|
|
297
|
+
| 'reauthRequired';
|
|
298
|
+
|
|
299
|
+
export interface IFlexPublicAccountSummary {
|
|
300
|
+
providerId: string;
|
|
301
|
+
authKind: 'chatgptOAuth';
|
|
302
|
+
accountId?: string;
|
|
303
|
+
email?: string;
|
|
304
|
+
plan?: string;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export interface IFlexPublicProviderConnection {
|
|
308
|
+
loginId: string;
|
|
309
|
+
providerId: 'openai';
|
|
310
|
+
status: TFlexPublicConnectionState;
|
|
311
|
+
account?: IFlexPublicAccountSummary;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** Private child-to-controller projection. It is never exposed by controller RPC. */
|
|
315
|
+
export interface IFlexOpenCodeOAuthAuth {
|
|
316
|
+
type: 'oauth';
|
|
317
|
+
access: string;
|
|
318
|
+
refresh: string;
|
|
319
|
+
expires: number;
|
|
320
|
+
accountId?: string;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export interface IFlexPublicProviderModel {
|
|
324
|
+
providerId: 'openai';
|
|
325
|
+
modelId: string;
|
|
326
|
+
displayName: string;
|
|
327
|
+
description: string;
|
|
328
|
+
hidden: boolean;
|
|
329
|
+
reasoningEfforts: Array<{ effort: string; description: string }>;
|
|
330
|
+
defaultReasoningEffort: string;
|
|
331
|
+
inputModalities: Array<'text' | 'image' | 'audio'>;
|
|
332
|
+
supportsPersonality: boolean;
|
|
333
|
+
serviceTiers: Array<{ id: string; name: string; description: string }>;
|
|
334
|
+
defaultServiceTier?: string;
|
|
335
|
+
isDefault: boolean;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export interface IFlexPublicProviderRateLimitWindow {
|
|
339
|
+
usedPercent: number;
|
|
340
|
+
limitWindowSeconds: number;
|
|
341
|
+
resetAfterSeconds: number;
|
|
342
|
+
resetsAt: number;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export interface IFlexPublicProviderRateLimitDetails {
|
|
346
|
+
allowed: boolean;
|
|
347
|
+
limitReached: boolean;
|
|
348
|
+
primaryWindow?: IFlexPublicProviderRateLimitWindow;
|
|
349
|
+
secondaryWindow?: IFlexPublicProviderRateLimitWindow;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export interface IFlexPublicProviderAdditionalRateLimit {
|
|
353
|
+
limitName: string;
|
|
354
|
+
meteredFeature: string;
|
|
355
|
+
rateLimit?: IFlexPublicProviderRateLimitDetails;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export interface IFlexPublicProviderAccountRateLimits {
|
|
359
|
+
providerId: 'openai';
|
|
360
|
+
plan: string;
|
|
361
|
+
observedAt: string;
|
|
362
|
+
rateLimit?: IFlexPublicProviderRateLimitDetails;
|
|
363
|
+
additionalRateLimits: IFlexPublicProviderAdditionalRateLimit[];
|
|
364
|
+
rateLimitReachedType?: string;
|
|
365
|
+
rateLimitResetCreditsAvailableCount?: number;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export interface IFlexModelChoice {
|
|
369
|
+
providerConnectionId: string;
|
|
370
|
+
modelId: string;
|
|
371
|
+
variant?: string;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export interface IFlexJobAdmission {
|
|
375
|
+
jobId: string;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export interface IFlexSessionListResult {
|
|
379
|
+
sessions: TFlexSession[];
|
|
380
|
+
total: number;
|
|
381
|
+
truncated: boolean;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
export type TFlexSlashExecutionResult =
|
|
385
|
+
| plugins.flexharness.IFlexSlashCommandNotCommandResult
|
|
386
|
+
| plugins.flexharness.IFlexSlashCommandMalformedResult
|
|
387
|
+
| plugins.flexharness.IFlexSlashCommandUnknownResult
|
|
388
|
+
| plugins.flexharness.IFlexSlashCommandOperationResult
|
|
389
|
+
| plugins.flexharness.IFlexSlashCommandHandlerResult
|
|
390
|
+
| {
|
|
391
|
+
type: 'prompt-admission';
|
|
392
|
+
name: string;
|
|
393
|
+
admission: { queueId: string; runId: string };
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
export interface IFlexRequestMap {
|
|
397
|
+
'service.status': {
|
|
398
|
+
request: Record<string, never>;
|
|
399
|
+
response: IFlexServiceStatus;
|
|
400
|
+
};
|
|
401
|
+
'project.replaceAll': {
|
|
402
|
+
request: { projects: IFlexProjectRecord[] };
|
|
403
|
+
response: { replaced: true; retired: number };
|
|
404
|
+
};
|
|
405
|
+
'project.register': {
|
|
406
|
+
request: { project: IFlexIdentityBoundProjectRecord };
|
|
407
|
+
response: { registered: true };
|
|
408
|
+
};
|
|
409
|
+
'project.remove': {
|
|
410
|
+
request: { project: IFlexIdentityBoundProjectRecord };
|
|
411
|
+
response: { removed: true };
|
|
412
|
+
};
|
|
413
|
+
'intelligence.start': {
|
|
414
|
+
request: {
|
|
415
|
+
scopeId: string;
|
|
416
|
+
sourceSessionId: IFlexIntelligenceSessionId;
|
|
417
|
+
question: string;
|
|
418
|
+
scratchpad: string;
|
|
419
|
+
model: IFlexModelChoice;
|
|
420
|
+
capabilityToken: string;
|
|
421
|
+
};
|
|
422
|
+
response: { jobId: string; workerSessionId: string };
|
|
423
|
+
};
|
|
424
|
+
'intelligence.get': {
|
|
425
|
+
request: { scopeId: string; jobId: string; capabilityToken: string };
|
|
426
|
+
response: {
|
|
427
|
+
status: TFlexIntelligenceJobStatus;
|
|
428
|
+
workerSessionId: string;
|
|
429
|
+
result?: IFlexIntelligenceResult;
|
|
430
|
+
error?: string;
|
|
431
|
+
};
|
|
432
|
+
};
|
|
433
|
+
'intelligence.cancel': {
|
|
434
|
+
request: { scopeId: string; capabilityToken: string };
|
|
435
|
+
response: { cancelled: boolean };
|
|
436
|
+
};
|
|
437
|
+
'session.list': {
|
|
438
|
+
request: { scopeId: string; offset?: number; limit?: number };
|
|
439
|
+
response: IFlexSessionListResult;
|
|
440
|
+
};
|
|
441
|
+
'session.create': {
|
|
442
|
+
request: { scopeId: string; options?: TFlexCreateSessionOptions };
|
|
443
|
+
response: TFlexSession;
|
|
444
|
+
};
|
|
445
|
+
'session.get': {
|
|
446
|
+
request: { scopeId: string; sessionId: string };
|
|
447
|
+
response: TFlexSession;
|
|
448
|
+
};
|
|
449
|
+
'session.update': {
|
|
450
|
+
request: { scopeId: string; sessionId: string; options: TFlexUpdateSessionOptions };
|
|
451
|
+
response: TFlexSession;
|
|
452
|
+
};
|
|
453
|
+
'session.delete': {
|
|
454
|
+
request: { scopeId: string; sessionId: string };
|
|
455
|
+
response: { deleted: true };
|
|
456
|
+
};
|
|
457
|
+
'session.compact': {
|
|
458
|
+
request: { scopeId: string; sessionId: string; model?: IFlexModelChoice };
|
|
459
|
+
response: { compacted: true };
|
|
460
|
+
};
|
|
461
|
+
'session.reversion.info': {
|
|
462
|
+
request: { scopeId: string; sessionId: string };
|
|
463
|
+
response: TFlexSessionReversionInfo;
|
|
464
|
+
};
|
|
465
|
+
'slash.list': {
|
|
466
|
+
request: { scopeId: string; sessionId: string };
|
|
467
|
+
response: { commands: TFlexSlashCommandDescriptor[] };
|
|
468
|
+
};
|
|
469
|
+
'slash.execute': {
|
|
470
|
+
request: {
|
|
471
|
+
scopeId: string;
|
|
472
|
+
sessionId: string;
|
|
473
|
+
input: string;
|
|
474
|
+
model?: IFlexModelChoice;
|
|
475
|
+
};
|
|
476
|
+
response: TFlexSlashExecutionResult;
|
|
477
|
+
};
|
|
478
|
+
'message.page': {
|
|
479
|
+
request: { scopeId: string; sessionId: string; limit?: number; before?: string };
|
|
480
|
+
response: TFlexMessagePage;
|
|
481
|
+
};
|
|
482
|
+
'message.get': {
|
|
483
|
+
request: { scopeId: string; sessionId: string; messageId: string };
|
|
484
|
+
response: TFlexMessage;
|
|
485
|
+
};
|
|
486
|
+
'prompt.start': {
|
|
487
|
+
request: {
|
|
488
|
+
scopeId: string;
|
|
489
|
+
sessionId: string;
|
|
490
|
+
prompt: TFlexPrompt;
|
|
491
|
+
options?: TFlexPromptOptions;
|
|
492
|
+
model?: IFlexModelChoice;
|
|
493
|
+
upload?: {
|
|
494
|
+
operationId: string;
|
|
495
|
+
directory: string;
|
|
496
|
+
};
|
|
497
|
+
};
|
|
498
|
+
response: { queueId: string; runId: string };
|
|
499
|
+
};
|
|
500
|
+
'prompt.acknowledge': {
|
|
501
|
+
request: { scopeId: string; sessionId: string; queueId: string; runId: string };
|
|
502
|
+
response: { acknowledged: true };
|
|
503
|
+
};
|
|
504
|
+
'prompt.get': {
|
|
505
|
+
request: { scopeId: string; sessionId: string; queueId: string };
|
|
506
|
+
response: { entry: TFlexPromptQueueEntry };
|
|
507
|
+
};
|
|
508
|
+
'prompt.list': {
|
|
509
|
+
request: { scopeId: string; sessionId: string };
|
|
510
|
+
response: { entries: TFlexPromptQueueEntry[] };
|
|
511
|
+
};
|
|
512
|
+
'prompt.cancel': {
|
|
513
|
+
request: { scopeId: string; sessionId: string; queueId: string };
|
|
514
|
+
response: { accepted: boolean };
|
|
515
|
+
};
|
|
516
|
+
'permission.list': {
|
|
517
|
+
request: { scopeId: string; sessionId?: string; familyRootSessionId?: string };
|
|
518
|
+
response: { permissions: TFlexPermissionRequest[] };
|
|
519
|
+
};
|
|
520
|
+
'permission.respond': {
|
|
521
|
+
request: {
|
|
522
|
+
scopeId: string;
|
|
523
|
+
sessionId: string;
|
|
524
|
+
permissionId: string;
|
|
525
|
+
decision: TFlexPermissionDecision;
|
|
526
|
+
};
|
|
527
|
+
response: { accepted: true };
|
|
528
|
+
};
|
|
529
|
+
'provider.list': {
|
|
530
|
+
request: Record<string, never>;
|
|
531
|
+
response: {
|
|
532
|
+
providers: Array<{ providerId: 'openai'; displayName: string; loginFlow: 'device' }>;
|
|
533
|
+
};
|
|
534
|
+
};
|
|
535
|
+
'provider.login.begin': {
|
|
536
|
+
request: { providerId: 'openai' };
|
|
537
|
+
response: {
|
|
538
|
+
loginId: string;
|
|
539
|
+
verificationUrl: string;
|
|
540
|
+
userCode: string;
|
|
541
|
+
status: 'pending';
|
|
542
|
+
};
|
|
543
|
+
};
|
|
544
|
+
'provider.login.status': {
|
|
545
|
+
request: { loginId: string };
|
|
546
|
+
response: IFlexPublicProviderConnection;
|
|
547
|
+
};
|
|
548
|
+
'provider.login.cancel': {
|
|
549
|
+
request: { loginId: string };
|
|
550
|
+
response: { canceled: boolean };
|
|
551
|
+
};
|
|
552
|
+
'provider.connection.list': {
|
|
553
|
+
request: Record<string, never>;
|
|
554
|
+
response: { connections: IFlexPublicProviderConnection[] };
|
|
555
|
+
};
|
|
556
|
+
'provider.connection.opencode-auth.get': {
|
|
557
|
+
request: { providerConnectionId: string };
|
|
558
|
+
response: { auth: IFlexOpenCodeOAuthAuth };
|
|
559
|
+
};
|
|
560
|
+
'provider.connection.logout.prepare': {
|
|
561
|
+
request: { providerConnectionId: string };
|
|
562
|
+
response: { accepted: true };
|
|
563
|
+
};
|
|
564
|
+
'provider.connection.logout': {
|
|
565
|
+
request: { providerConnectionId: string };
|
|
566
|
+
response: { accepted: true };
|
|
567
|
+
};
|
|
568
|
+
'provider.connection.ratelimits.get': {
|
|
569
|
+
request: { providerConnectionId: string };
|
|
570
|
+
response: { rateLimits: IFlexPublicProviderAccountRateLimits };
|
|
571
|
+
};
|
|
572
|
+
'provider.models.refresh': {
|
|
573
|
+
request: { providerConnectionId: string };
|
|
574
|
+
response: IFlexJobAdmission;
|
|
575
|
+
};
|
|
576
|
+
'model.choice.get': {
|
|
577
|
+
request: { scopeId: string };
|
|
578
|
+
response: { choice: IFlexModelChoice | null };
|
|
579
|
+
};
|
|
580
|
+
'model.choice.set': {
|
|
581
|
+
request: { scopeId: string; choice: IFlexModelChoice };
|
|
582
|
+
response: { choice: IFlexModelChoice };
|
|
583
|
+
};
|
|
584
|
+
'model.choice.validate': {
|
|
585
|
+
request: { choice: IFlexModelChoice };
|
|
586
|
+
response: { valid: true };
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
export type TFlexRequestMethod = keyof IFlexRequestMap;
|
|
591
|
+
export type TFlexRequest<TMethod extends TFlexRequestMethod> = IFlexRequestMap[TMethod]['request'];
|
|
592
|
+
export type TFlexResponse<TMethod extends TFlexRequestMethod> = IFlexRequestMap[TMethod]['response'];
|
|
593
|
+
|
|
594
|
+
export interface IFlexIpcInitMessage {
|
|
595
|
+
version: typeof flexIpcProtocolVersion;
|
|
596
|
+
type: 'init';
|
|
597
|
+
init: IFlexServiceInit;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
export interface IFlexIpcRequestMessage {
|
|
601
|
+
version: typeof flexIpcProtocolVersion;
|
|
602
|
+
type: 'request';
|
|
603
|
+
requestId: string;
|
|
604
|
+
method: TFlexRequestMethod;
|
|
605
|
+
payload: unknown;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
export interface IFlexIpcRequestCancelMessage {
|
|
609
|
+
version: typeof flexIpcProtocolVersion;
|
|
610
|
+
type: 'request.cancel';
|
|
611
|
+
requestId: string;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
export interface IFlexIpcDisposeMessage {
|
|
615
|
+
version: typeof flexIpcProtocolVersion;
|
|
616
|
+
type: 'dispose';
|
|
617
|
+
requestId: string;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
export type TFlexIpcHostResponseMessage = {
|
|
621
|
+
version: typeof flexIpcProtocolVersion;
|
|
622
|
+
type: 'host.response';
|
|
623
|
+
requestId: string;
|
|
624
|
+
} & (
|
|
625
|
+
| { ok: true; result: unknown }
|
|
626
|
+
| { ok: false; error: IFlexServiceError }
|
|
627
|
+
);
|
|
628
|
+
|
|
629
|
+
export interface IFlexIpcParentBrowserFrameMessage {
|
|
630
|
+
version: typeof flexIpcProtocolVersion;
|
|
631
|
+
type: 'browser.channel.frame';
|
|
632
|
+
channelId: string;
|
|
633
|
+
dataBase64: string;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
export interface IFlexIpcBrowserChannelClosedMessage {
|
|
637
|
+
version: typeof flexIpcProtocolVersion;
|
|
638
|
+
type: 'browser.channel.closed';
|
|
639
|
+
channelId: string;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
export type TFlexParentMessage =
|
|
643
|
+
| IFlexIpcInitMessage
|
|
644
|
+
| IFlexIpcRequestMessage
|
|
645
|
+
| IFlexIpcRequestCancelMessage
|
|
646
|
+
| IFlexIpcDisposeMessage
|
|
647
|
+
| TFlexIpcHostResponseMessage
|
|
648
|
+
| IFlexIpcParentBrowserFrameMessage
|
|
649
|
+
| IFlexIpcBrowserChannelClosedMessage;
|
|
650
|
+
|
|
651
|
+
export type TFlexServiceErrorCode =
|
|
652
|
+
| 'INVALID_REQUEST'
|
|
653
|
+
| 'NOT_READY'
|
|
654
|
+
| 'NOT_FOUND'
|
|
655
|
+
| 'CONFLICT'
|
|
656
|
+
| 'BUSY'
|
|
657
|
+
| 'TIMEOUT'
|
|
658
|
+
| 'ABORTED'
|
|
659
|
+
| 'OUTCOME_UNKNOWN'
|
|
660
|
+
| 'SLASH_UNAVAILABLE'
|
|
661
|
+
| 'LIMIT_EXCEEDED'
|
|
662
|
+
| 'STALE_RUN'
|
|
663
|
+
| 'PROVIDER_LOGIN_FAILED'
|
|
664
|
+
| 'PROVIDER_REAUTH_REQUIRED'
|
|
665
|
+
| 'MODEL_CATALOG_REQUIRED'
|
|
666
|
+
| 'UNSUPPORTED_RUNTIME'
|
|
667
|
+
| 'INTERNAL';
|
|
668
|
+
|
|
669
|
+
export const flexServiceErrorMessages: Readonly<Record<TFlexServiceErrorCode, string>> = {
|
|
670
|
+
INVALID_REQUEST: 'The Flex request is invalid.',
|
|
671
|
+
NOT_READY: 'The Flex service is not ready.',
|
|
672
|
+
NOT_FOUND: 'The requested Flex resource was not found.',
|
|
673
|
+
CONFLICT: 'The Flex operation conflicted with a concurrent change.',
|
|
674
|
+
BUSY: 'The Flex resource is busy.',
|
|
675
|
+
TIMEOUT: 'The Flex operation timed out.',
|
|
676
|
+
ABORTED: 'The Flex operation was aborted.',
|
|
677
|
+
OUTCOME_UNKNOWN: 'The Flex operation outcome is unknown.',
|
|
678
|
+
SLASH_UNAVAILABLE: 'The Flex slash command is unavailable.',
|
|
679
|
+
LIMIT_EXCEEDED: 'The Flex service limit was reached.',
|
|
680
|
+
STALE_RUN: 'The requested Flex run is no longer current.',
|
|
681
|
+
PROVIDER_LOGIN_FAILED: 'The provider login did not complete.',
|
|
682
|
+
PROVIDER_REAUTH_REQUIRED: 'The provider connection requires authentication.',
|
|
683
|
+
MODEL_CATALOG_REQUIRED: 'The provider model catalog must be refreshed.',
|
|
684
|
+
UNSUPPORTED_RUNTIME: 'The Flex child runtime is unsupported.',
|
|
685
|
+
INTERNAL: 'The Flex operation failed.',
|
|
686
|
+
};
|
|
687
|
+
|
|
688
|
+
export interface IFlexServiceError {
|
|
689
|
+
name: 'FlexServiceError';
|
|
690
|
+
code: TFlexServiceErrorCode;
|
|
691
|
+
message: string;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
export interface IFlexIpcStatusMessage {
|
|
695
|
+
version: typeof flexIpcProtocolVersion;
|
|
696
|
+
type: 'status';
|
|
697
|
+
status: IFlexServiceStatus;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
export type TFlexIpcResponseMessage = {
|
|
701
|
+
version: typeof flexIpcProtocolVersion;
|
|
702
|
+
type: 'response';
|
|
703
|
+
requestId: string;
|
|
704
|
+
} & (
|
|
705
|
+
| { ok: true; result: unknown }
|
|
706
|
+
| { ok: false; error: IFlexServiceError }
|
|
707
|
+
);
|
|
708
|
+
|
|
709
|
+
export type TFlexJobKind = 'provider.models';
|
|
710
|
+
|
|
711
|
+
export type TFlexChildEvent =
|
|
712
|
+
| {
|
|
713
|
+
type: 'session.history.changed';
|
|
714
|
+
scopeId: string;
|
|
715
|
+
sessionId: string;
|
|
716
|
+
direction: 'undo' | 'redo' | 'branch';
|
|
717
|
+
runId?: string;
|
|
718
|
+
}
|
|
719
|
+
| {
|
|
720
|
+
type: 'sessions.changed';
|
|
721
|
+
scopeId?: string;
|
|
722
|
+
sessionId?: string;
|
|
723
|
+
coalesced: boolean;
|
|
724
|
+
}
|
|
725
|
+
| {
|
|
726
|
+
type: 'permissions.changed';
|
|
727
|
+
scopeId: string;
|
|
728
|
+
sessionId: string;
|
|
729
|
+
}
|
|
730
|
+
| {
|
|
731
|
+
type: 'tool.updated';
|
|
732
|
+
scopeId: string;
|
|
733
|
+
sessionId: string;
|
|
734
|
+
runId: string;
|
|
735
|
+
messageId: string;
|
|
736
|
+
messageIndex: number;
|
|
737
|
+
partIndex: number;
|
|
738
|
+
sequence: number;
|
|
739
|
+
timestamp: string;
|
|
740
|
+
part: TFlexToolMessagePart;
|
|
741
|
+
}
|
|
742
|
+
| {
|
|
743
|
+
type: 'reasoning.updated';
|
|
744
|
+
scopeId: string;
|
|
745
|
+
sessionId: string;
|
|
746
|
+
runId: string;
|
|
747
|
+
messageId: string;
|
|
748
|
+
messageIndex: number;
|
|
749
|
+
partIndex: number;
|
|
750
|
+
sequence: number;
|
|
751
|
+
timestamp: string;
|
|
752
|
+
part: TFlexReasoningMessagePart;
|
|
753
|
+
}
|
|
754
|
+
| {
|
|
755
|
+
type: 'text.updated';
|
|
756
|
+
scopeId: string;
|
|
757
|
+
sessionId: string;
|
|
758
|
+
runId: string;
|
|
759
|
+
messageId: string;
|
|
760
|
+
messageIndex: number;
|
|
761
|
+
partIndex: number;
|
|
762
|
+
sequence: number;
|
|
763
|
+
timestamp: string;
|
|
764
|
+
status: 'running' | 'completed';
|
|
765
|
+
part: TFlexTextMessagePart;
|
|
766
|
+
}
|
|
767
|
+
| {
|
|
768
|
+
type: 'prompt.queued';
|
|
769
|
+
scopeId: string;
|
|
770
|
+
sessionId: string;
|
|
771
|
+
queueId: string;
|
|
772
|
+
runId?: never;
|
|
773
|
+
entry: TFlexPromptQueueEntry;
|
|
774
|
+
}
|
|
775
|
+
| {
|
|
776
|
+
type: 'prompt.started' | 'prompt.running' | 'prompt.finished';
|
|
777
|
+
scopeId: string;
|
|
778
|
+
sessionId: string;
|
|
779
|
+
queueId: string;
|
|
780
|
+
runId: string;
|
|
781
|
+
entry: TFlexPromptQueueEntry;
|
|
782
|
+
}
|
|
783
|
+
| {
|
|
784
|
+
type: 'prompt.message-bound';
|
|
785
|
+
scopeId: string;
|
|
786
|
+
sessionId: string;
|
|
787
|
+
queueId: string;
|
|
788
|
+
runId: string;
|
|
789
|
+
messageId: string;
|
|
790
|
+
}
|
|
791
|
+
| {
|
|
792
|
+
type: 'job.finished';
|
|
793
|
+
jobId: string;
|
|
794
|
+
kind: TFlexJobKind;
|
|
795
|
+
status: 'completed' | 'failed' | 'cancelled';
|
|
796
|
+
models?: IFlexPublicProviderModel[];
|
|
797
|
+
}
|
|
798
|
+
| {
|
|
799
|
+
type: 'provider.connection.changed';
|
|
800
|
+
connection: IFlexPublicProviderConnection;
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
export interface IFlexIpcEventMessage {
|
|
804
|
+
version: typeof flexIpcProtocolVersion;
|
|
805
|
+
type: 'event';
|
|
806
|
+
event: TFlexChildEvent;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
export interface IFlexIpcHostRequestMessage {
|
|
810
|
+
version: typeof flexIpcProtocolVersion;
|
|
811
|
+
type: 'host.request';
|
|
812
|
+
requestId: string;
|
|
813
|
+
method: TFlexHostRequestMethod;
|
|
814
|
+
payload: unknown;
|
|
815
|
+
timeoutMs: number;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
export interface IFlexIpcHostCancelMessage {
|
|
819
|
+
version: typeof flexIpcProtocolVersion;
|
|
820
|
+
type: 'host.cancel';
|
|
821
|
+
requestId: string;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
export interface IFlexIpcHostResponseAcknowledgementMessage {
|
|
825
|
+
version: typeof flexIpcProtocolVersion;
|
|
826
|
+
type: 'host.response.acknowledge';
|
|
827
|
+
requestId: string;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
export interface IFlexIpcChildBrowserFrameMessage {
|
|
831
|
+
version: typeof flexIpcProtocolVersion;
|
|
832
|
+
type: 'browser.channel.frame';
|
|
833
|
+
channelId: string;
|
|
834
|
+
dataBase64: string;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
export type TFlexChildMessage =
|
|
838
|
+
| IFlexIpcStatusMessage
|
|
839
|
+
| TFlexIpcResponseMessage
|
|
840
|
+
| IFlexIpcEventMessage
|
|
841
|
+
| IFlexIpcHostRequestMessage
|
|
842
|
+
| IFlexIpcHostCancelMessage
|
|
843
|
+
| IFlexIpcHostResponseAcknowledgementMessage
|
|
844
|
+
| IFlexIpcChildBrowserFrameMessage;
|
|
845
|
+
|
|
846
|
+
const requestMethods: ReadonlySet<string> = new Set<TFlexRequestMethod>([
|
|
847
|
+
'service.status',
|
|
848
|
+
'project.replaceAll',
|
|
849
|
+
'project.register',
|
|
850
|
+
'project.remove',
|
|
851
|
+
'intelligence.start',
|
|
852
|
+
'intelligence.get',
|
|
853
|
+
'intelligence.cancel',
|
|
854
|
+
'session.list',
|
|
855
|
+
'session.create',
|
|
856
|
+
'session.get',
|
|
857
|
+
'session.update',
|
|
858
|
+
'session.delete',
|
|
859
|
+
'session.compact',
|
|
860
|
+
'session.reversion.info',
|
|
861
|
+
'slash.list',
|
|
862
|
+
'slash.execute',
|
|
863
|
+
'message.page',
|
|
864
|
+
'message.get',
|
|
865
|
+
'prompt.start',
|
|
866
|
+
'prompt.acknowledge',
|
|
867
|
+
'prompt.get',
|
|
868
|
+
'prompt.list',
|
|
869
|
+
'prompt.cancel',
|
|
870
|
+
'permission.list',
|
|
871
|
+
'permission.respond',
|
|
872
|
+
'provider.list',
|
|
873
|
+
'provider.login.begin',
|
|
874
|
+
'provider.login.status',
|
|
875
|
+
'provider.login.cancel',
|
|
876
|
+
'provider.connection.list',
|
|
877
|
+
'provider.connection.opencode-auth.get',
|
|
878
|
+
'provider.connection.logout.prepare',
|
|
879
|
+
'provider.connection.logout',
|
|
880
|
+
'provider.connection.ratelimits.get',
|
|
881
|
+
'provider.models.refresh',
|
|
882
|
+
'model.choice.get',
|
|
883
|
+
'model.choice.set',
|
|
884
|
+
'model.choice.validate',
|
|
885
|
+
]);
|
|
886
|
+
const hostRequestMethods: ReadonlySet<string> = new Set<TFlexHostRequestMethod>([
|
|
887
|
+
'reversion.prepare',
|
|
888
|
+
'reversion.inspect-capture',
|
|
889
|
+
'reversion.finalize',
|
|
890
|
+
'reversion.inspect-apply',
|
|
891
|
+
'reversion.apply',
|
|
892
|
+
'reversion.release',
|
|
893
|
+
'git.worktree.create',
|
|
894
|
+
'git.worktree.list',
|
|
895
|
+
'git.worktree.remove',
|
|
896
|
+
'crossharness.chats.list',
|
|
897
|
+
'crossharness.chat.read',
|
|
898
|
+
'project-management.load',
|
|
899
|
+
'project-management.save',
|
|
900
|
+
'project-management.tombstone',
|
|
901
|
+
'project-management.purge',
|
|
902
|
+
'browser.resources.resolve',
|
|
903
|
+
'browser.channel.open',
|
|
904
|
+
'browser.channel.close',
|
|
905
|
+
]);
|
|
906
|
+
const serviceStates: ReadonlySet<string> = new Set<TFlexServiceState>([
|
|
907
|
+
'starting', 'ready', 'unsupported', 'failed', 'stopping', 'stopped',
|
|
908
|
+
]);
|
|
909
|
+
const serviceStatusCodes: ReadonlySet<string> = new Set<TFlexServiceStatusCode>([
|
|
910
|
+
'UNSUPPORTED_RUNTIME', 'INITIALIZATION_FAILED', 'SERVICE_STOPPED',
|
|
911
|
+
]);
|
|
912
|
+
const serviceErrorCodes: ReadonlySet<string> = new Set<TFlexServiceErrorCode>(
|
|
913
|
+
Object.keys(flexServiceErrorMessages) as TFlexServiceErrorCode[],
|
|
914
|
+
);
|
|
915
|
+
const identifierPattern = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,511}$/u;
|
|
916
|
+
const projectIdPattern = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/u;
|
|
917
|
+
const requestIdPattern = /^[A-Za-z0-9_-]{22}$/u;
|
|
918
|
+
const qualifiedIdPattern = /^flex:(?:job|provider:openai):[A-Za-z0-9_-]{22}$/u;
|
|
919
|
+
const intelligenceCapabilityTokenPattern = /^[A-Za-z0-9_-]{43}$/u;
|
|
920
|
+
|
|
921
|
+
export const isFlexPlainObject = (valueArg: unknown): valueArg is Record<string, unknown> =>
|
|
922
|
+
typeof valueArg === 'object'
|
|
923
|
+
&& valueArg !== null
|
|
924
|
+
&& !Array.isArray(valueArg)
|
|
925
|
+
&& (
|
|
926
|
+
Object.getPrototypeOf(valueArg) === Object.prototype
|
|
927
|
+
|| Object.getPrototypeOf(valueArg) === null
|
|
928
|
+
);
|
|
929
|
+
|
|
930
|
+
export const hasFlexExactKeys = (
|
|
931
|
+
valueArg: Record<string, unknown>,
|
|
932
|
+
requiredKeysArg: readonly string[],
|
|
933
|
+
optionalKeysArg: readonly string[] = [],
|
|
934
|
+
): boolean => {
|
|
935
|
+
const keys = Object.keys(valueArg);
|
|
936
|
+
return requiredKeysArg.every((key) => Object.hasOwn(valueArg, key))
|
|
937
|
+
&& keys.every((key) => requiredKeysArg.includes(key) || optionalKeysArg.includes(key));
|
|
938
|
+
};
|
|
939
|
+
|
|
940
|
+
const isBoundedString = (
|
|
941
|
+
valueArg: unknown,
|
|
942
|
+
maximumBytesArg: number,
|
|
943
|
+
allowEmptyArg = false,
|
|
944
|
+
): valueArg is string => typeof valueArg === 'string'
|
|
945
|
+
&& (allowEmptyArg || valueArg.length > 0)
|
|
946
|
+
&& Buffer.byteLength(valueArg, 'utf8') <= maximumBytesArg;
|
|
947
|
+
|
|
948
|
+
const isIdentifier = (valueArg: unknown): valueArg is string =>
|
|
949
|
+
isBoundedString(valueArg, 512) && identifierPattern.test(valueArg);
|
|
950
|
+
|
|
951
|
+
const isRequestId = (valueArg: unknown): valueArg is string =>
|
|
952
|
+
typeof valueArg === 'string' && requestIdPattern.test(valueArg);
|
|
953
|
+
|
|
954
|
+
const isChannelId = (valueArg: unknown): valueArg is string =>
|
|
955
|
+
typeof valueArg === 'string' && identifierPattern.test(valueArg);
|
|
956
|
+
|
|
957
|
+
const isCanonicalBrowserFrame = (valueArg: unknown): valueArg is string => {
|
|
958
|
+
if (typeof valueArg !== 'string' || valueArg.length === 0) return false;
|
|
959
|
+
const bytes = Buffer.from(valueArg, 'base64');
|
|
960
|
+
return bytes.byteLength >= 1
|
|
961
|
+
&& bytes.byteLength <= flexIpcMaximumBrowserFrameBytes
|
|
962
|
+
&& bytes.toString('base64') === valueArg;
|
|
963
|
+
};
|
|
964
|
+
|
|
965
|
+
const isQualifiedId = (valueArg: unknown): valueArg is string =>
|
|
966
|
+
typeof valueArg === 'string' && qualifiedIdPattern.test(valueArg);
|
|
967
|
+
|
|
968
|
+
const isIntelligenceCapabilityToken = (valueArg: unknown): valueArg is string =>
|
|
969
|
+
typeof valueArg === 'string' && intelligenceCapabilityTokenPattern.test(valueArg);
|
|
970
|
+
|
|
971
|
+
const isIsoDate = (valueArg: unknown): valueArg is string =>
|
|
972
|
+
isBoundedString(valueArg, 64)
|
|
973
|
+
&& Number.isFinite(Date.parse(valueArg))
|
|
974
|
+
&& new Date(valueArg).toISOString() === valueArg;
|
|
975
|
+
|
|
976
|
+
const isNonNegativeInteger = (valueArg: unknown): valueArg is number =>
|
|
977
|
+
Number.isSafeInteger(valueArg) && (valueArg as number) >= 0;
|
|
978
|
+
|
|
979
|
+
const isFlexIntelligenceSessionId = (
|
|
980
|
+
valueArg: unknown,
|
|
981
|
+
): valueArg is IFlexIntelligenceSessionId => isFlexPlainObject(valueArg)
|
|
982
|
+
&& hasFlexExactKeys(valueArg, ['harnessId', 'nativeId'])
|
|
983
|
+
&& (valueArg.harnessId === 'opencode' || valueArg.harnessId === 'flex')
|
|
984
|
+
&& isIdentifier(valueArg.nativeId);
|
|
985
|
+
|
|
986
|
+
const isFlexIntelligenceChatSummary = (
|
|
987
|
+
valueArg: unknown,
|
|
988
|
+
): valueArg is IFlexIntelligenceChatSummary => isFlexPlainObject(valueArg)
|
|
989
|
+
&& hasFlexExactKeys(valueArg, ['sessionId', 'title', 'updatedAt'], ['isActive'])
|
|
990
|
+
&& isFlexIntelligenceSessionId(valueArg.sessionId)
|
|
991
|
+
&& isBoundedString(valueArg.title, 4096, true)
|
|
992
|
+
&& isIsoDate(valueArg.updatedAt)
|
|
993
|
+
&& (valueArg.isActive === undefined || typeof valueArg.isActive === 'boolean');
|
|
994
|
+
|
|
995
|
+
const isFlexIntelligenceChatMessage = (
|
|
996
|
+
valueArg: unknown,
|
|
997
|
+
): valueArg is IFlexIntelligenceChatMessage => isFlexPlainObject(valueArg)
|
|
998
|
+
&& hasFlexExactKeys(valueArg, ['role', 'text'], ['timestamp'])
|
|
999
|
+
&& isBoundedString(valueArg.role, 128)
|
|
1000
|
+
&& isBoundedString(valueArg.text, 16 * 1024, true)
|
|
1001
|
+
&& (valueArg.timestamp === undefined || isIsoDate(valueArg.timestamp));
|
|
1002
|
+
|
|
1003
|
+
const isFlexIntelligenceChatTranscript = (
|
|
1004
|
+
valueArg: unknown,
|
|
1005
|
+
): valueArg is IFlexIntelligenceChatTranscript => isFlexPlainObject(valueArg)
|
|
1006
|
+
&& hasFlexExactKeys(valueArg, ['sessionId', 'title', 'messages'], ['totalMessages'])
|
|
1007
|
+
&& isFlexIntelligenceSessionId(valueArg.sessionId)
|
|
1008
|
+
&& isBoundedString(valueArg.title, 4096, true)
|
|
1009
|
+
&& Array.isArray(valueArg.messages)
|
|
1010
|
+
&& valueArg.messages.length <= 50
|
|
1011
|
+
&& valueArg.messages.every(isFlexIntelligenceChatMessage)
|
|
1012
|
+
&& (valueArg.totalMessages === undefined || isNonNegativeInteger(valueArg.totalMessages))
|
|
1013
|
+
&& Buffer.byteLength(JSON.stringify(valueArg), 'utf8') <= 256 * 1024;
|
|
1014
|
+
|
|
1015
|
+
const isFlexIntelligenceResult = (valueArg: unknown): valueArg is IFlexIntelligenceResult => (
|
|
1016
|
+
isFlexPlainObject(valueArg)
|
|
1017
|
+
&& hasFlexExactKeys(valueArg, ['answer', 'scratchpad', 'model'])
|
|
1018
|
+
&& isBoundedString(valueArg.answer, 128 * 1024, true)
|
|
1019
|
+
&& isBoundedString(valueArg.scratchpad, 128 * 1024, true)
|
|
1020
|
+
&& valueArg.answer.length <= 32_768
|
|
1021
|
+
&& valueArg.scratchpad.length <= 32_768
|
|
1022
|
+
&& valueArg.model === 'openai/gpt-5.6-luna'
|
|
1023
|
+
);
|
|
1024
|
+
|
|
1025
|
+
const isJsonValue = (valueArg: unknown, depthArg = 0): boolean => {
|
|
1026
|
+
if (depthArg > 20) return false;
|
|
1027
|
+
if (
|
|
1028
|
+
valueArg === null
|
|
1029
|
+
|| typeof valueArg === 'string'
|
|
1030
|
+
|| typeof valueArg === 'boolean'
|
|
1031
|
+
|| (typeof valueArg === 'number' && Number.isFinite(valueArg))
|
|
1032
|
+
) return true;
|
|
1033
|
+
if (Array.isArray(valueArg)) {
|
|
1034
|
+
return valueArg.length <= 4096 && valueArg.every((entry) => isJsonValue(entry, depthArg + 1));
|
|
1035
|
+
}
|
|
1036
|
+
if (!isFlexPlainObject(valueArg) || Object.keys(valueArg).length > 4096) return false;
|
|
1037
|
+
return Object.values(valueArg).every((entry) => isJsonValue(entry, depthArg + 1));
|
|
1038
|
+
};
|
|
1039
|
+
|
|
1040
|
+
const isFlexSessionGeneration = (valueArg: Record<string, unknown>): boolean =>
|
|
1041
|
+
isBoundedString(
|
|
1042
|
+
valueArg.sessionGenerationId,
|
|
1043
|
+
plugins.flexharness.FLEX_SESSION_GENERATION_ID_MAX_BYTES,
|
|
1044
|
+
)
|
|
1045
|
+
&& isNonNegativeInteger(valueArg.sessionGenerationSequence)
|
|
1046
|
+
&& Number(valueArg.sessionGenerationSequence) >= 1;
|
|
1047
|
+
|
|
1048
|
+
const isFlexProjectManagementRecord = (
|
|
1049
|
+
valueArg: unknown,
|
|
1050
|
+
): valueArg is TFlexProjectManagementRecord => {
|
|
1051
|
+
try {
|
|
1052
|
+
plugins.flexharness.assertFlexProjectManagementRecord(valueArg);
|
|
1053
|
+
return true;
|
|
1054
|
+
} catch {
|
|
1055
|
+
return false;
|
|
1056
|
+
}
|
|
1057
|
+
};
|
|
1058
|
+
|
|
1059
|
+
const isFlexProjectManagementSnapshot = (
|
|
1060
|
+
valueArg: unknown,
|
|
1061
|
+
): valueArg is TFlexProjectManagementSnapshot => {
|
|
1062
|
+
try {
|
|
1063
|
+
plugins.flexharness.assertFlexProjectManagementSnapshot(valueArg);
|
|
1064
|
+
return true;
|
|
1065
|
+
} catch {
|
|
1066
|
+
return false;
|
|
1067
|
+
}
|
|
1068
|
+
};
|
|
1069
|
+
|
|
1070
|
+
const isFlexProjectManagementTombstone = (
|
|
1071
|
+
valueArg: unknown,
|
|
1072
|
+
): valueArg is TFlexProjectManagementTombstone => {
|
|
1073
|
+
try {
|
|
1074
|
+
plugins.flexharness.assertFlexProjectManagementTombstone(valueArg);
|
|
1075
|
+
return true;
|
|
1076
|
+
} catch {
|
|
1077
|
+
return false;
|
|
1078
|
+
}
|
|
1079
|
+
};
|
|
1080
|
+
|
|
1081
|
+
const isFlexProjectManagementWriteContext = (
|
|
1082
|
+
valueArg: unknown,
|
|
1083
|
+
): valueArg is TFlexProjectManagementWriteContext => isFlexPlainObject(valueArg)
|
|
1084
|
+
&& hasFlexExactKeys(valueArg, ['actor'], ['runId', 'agent', 'toolCallId'])
|
|
1085
|
+
&& (valueArg.actor === 'agent' || valueArg.actor === 'application')
|
|
1086
|
+
&& ['runId', 'agent', 'toolCallId'].every((key) => (
|
|
1087
|
+
valueArg[key] === undefined || isBoundedString(valueArg[key], 512)
|
|
1088
|
+
));
|
|
1089
|
+
|
|
1090
|
+
const isFlexProjectManagementCommitResult = (
|
|
1091
|
+
valueArg: unknown,
|
|
1092
|
+
): valueArg is TFlexProjectManagementCommitResult => isFlexPlainObject(valueArg)
|
|
1093
|
+
&& (
|
|
1094
|
+
(hasFlexExactKeys(valueArg, ['committed']) && valueArg.committed === true)
|
|
1095
|
+
|| (
|
|
1096
|
+
hasFlexExactKeys(valueArg, ['committed', 'actualRevision'])
|
|
1097
|
+
&& valueArg.committed === false
|
|
1098
|
+
&& isNonNegativeInteger(valueArg.actualRevision)
|
|
1099
|
+
)
|
|
1100
|
+
);
|
|
1101
|
+
|
|
1102
|
+
export const isFlexProjectRecord = (valueArg: unknown): valueArg is IFlexProjectRecord => {
|
|
1103
|
+
if (!isFlexPlainObject(valueArg) || !hasFlexExactKeys(
|
|
1104
|
+
valueArg,
|
|
1105
|
+
['projectId', 'directory'],
|
|
1106
|
+
['directoryIdentity'],
|
|
1107
|
+
)) {
|
|
1108
|
+
return false;
|
|
1109
|
+
}
|
|
1110
|
+
if (
|
|
1111
|
+
typeof valueArg.projectId !== 'string'
|
|
1112
|
+
|| !projectIdPattern.test(valueArg.projectId)
|
|
1113
|
+
|| !isBoundedString(valueArg.directory, 4096)
|
|
1114
|
+
|| valueArg.directory.includes('\0')
|
|
1115
|
+
|| !plugins.path.isAbsolute(valueArg.directory)
|
|
1116
|
+
|| plugins.path.normalize(valueArg.directory) !== valueArg.directory
|
|
1117
|
+
) return false;
|
|
1118
|
+
if (valueArg.directoryIdentity === undefined) return true;
|
|
1119
|
+
const directoryIdentity = valueArg.directoryIdentity;
|
|
1120
|
+
if (!isFlexPlainObject(directoryIdentity) || !Array.isArray(directoryIdentity.ancestry)) {
|
|
1121
|
+
return false;
|
|
1122
|
+
}
|
|
1123
|
+
const ancestry = directoryIdentity.ancestry;
|
|
1124
|
+
return hasFlexExactKeys(directoryIdentity, ['ancestry'])
|
|
1125
|
+
&& ancestry.length === 1
|
|
1126
|
+
+ valueArg.directory.slice(1).split('/').filter(Boolean).length
|
|
1127
|
+
&& ancestry.length <= controllerFilesystemIdentityMaximumComponents
|
|
1128
|
+
&& ancestry.every((component) => (
|
|
1129
|
+
isFlexPlainObject(component)
|
|
1130
|
+
&& hasFlexExactKeys(component, ['device', 'inode', 'type'])
|
|
1131
|
+
&& typeof component.device === 'string'
|
|
1132
|
+
&& /^(?:0|[1-9][0-9]{0,31})$/u.test(component.device)
|
|
1133
|
+
&& typeof component.inode === 'string'
|
|
1134
|
+
&& /^(?:0|[1-9][0-9]{0,31})$/u.test(component.inode)
|
|
1135
|
+
&& component.type === 'directory'
|
|
1136
|
+
));
|
|
1137
|
+
};
|
|
1138
|
+
|
|
1139
|
+
export const isFlexIdentityBoundProjectRecord = (
|
|
1140
|
+
valueArg: unknown,
|
|
1141
|
+
): valueArg is IFlexIdentityBoundProjectRecord => isFlexProjectRecord(valueArg)
|
|
1142
|
+
&& valueArg.directoryIdentity !== undefined;
|
|
1143
|
+
|
|
1144
|
+
export const isFlexDatabaseDescriptor = (
|
|
1145
|
+
valueArg: unknown,
|
|
1146
|
+
): valueArg is IFlexDatabaseDescriptor => isFlexPlainObject(valueArg)
|
|
1147
|
+
&& hasFlexExactKeys(valueArg, ['mongoDbUrl', 'mongoDbName'])
|
|
1148
|
+
&& isBoundedString(valueArg.mongoDbUrl, 4096)
|
|
1149
|
+
&& /^(?:mongodb|mongodb\+srv):\/\//u.test(valueArg.mongoDbUrl)
|
|
1150
|
+
&& typeof valueArg.mongoDbName === 'string'
|
|
1151
|
+
&& /^[A-Za-z0-9_-]{1,63}$/u.test(valueArg.mongoDbName);
|
|
1152
|
+
|
|
1153
|
+
const isFlexCredentialDirectory = (valueArg: unknown): valueArg is string => {
|
|
1154
|
+
if (
|
|
1155
|
+
!isBoundedString(valueArg, 4096)
|
|
1156
|
+
|| !valueArg.startsWith('/')
|
|
1157
|
+
|| valueArg.includes('\0')
|
|
1158
|
+
|| valueArg === '/'
|
|
1159
|
+
) return false;
|
|
1160
|
+
const segments = valueArg.split('/');
|
|
1161
|
+
return !segments.some((segment, index) => (
|
|
1162
|
+
index > 0 && (segment === '' || segment === '.' || segment === '..')
|
|
1163
|
+
));
|
|
1164
|
+
};
|
|
1165
|
+
|
|
1166
|
+
export const isFlexServiceInit = (valueArg: unknown): valueArg is IFlexServiceInit => {
|
|
1167
|
+
if (
|
|
1168
|
+
!isFlexPlainObject(valueArg)
|
|
1169
|
+
|| !hasFlexExactKeys(valueArg, [
|
|
1170
|
+
'controllerId', 'database', 'flexCredentialDirectory', 'projects',
|
|
1171
|
+
])
|
|
1172
|
+
|| !isIdentifier(valueArg.controllerId)
|
|
1173
|
+
|| !isFlexDatabaseDescriptor(valueArg.database)
|
|
1174
|
+
|| !isFlexCredentialDirectory(valueArg.flexCredentialDirectory)
|
|
1175
|
+
|| !Array.isArray(valueArg.projects)
|
|
1176
|
+
|| valueArg.projects.length > controllerProjectLimit
|
|
1177
|
+
|| !valueArg.projects.every(isFlexProjectRecord)
|
|
1178
|
+
) return false;
|
|
1179
|
+
return new Set(valueArg.projects.map((project) => project.projectId)).size
|
|
1180
|
+
=== valueArg.projects.length;
|
|
1181
|
+
};
|
|
1182
|
+
|
|
1183
|
+
export const isFlexServiceStatus = (valueArg: unknown): valueArg is IFlexServiceStatus => {
|
|
1184
|
+
if (
|
|
1185
|
+
!isFlexPlainObject(valueArg)
|
|
1186
|
+
|| !hasFlexExactKeys(valueArg, ['state', 'ready'], ['code', 'startedAt'])
|
|
1187
|
+
|| typeof valueArg.state !== 'string'
|
|
1188
|
+
|| !serviceStates.has(valueArg.state)
|
|
1189
|
+
|| typeof valueArg.ready !== 'boolean'
|
|
1190
|
+
|| (valueArg.code !== undefined
|
|
1191
|
+
&& (typeof valueArg.code !== 'string' || !serviceStatusCodes.has(valueArg.code)))
|
|
1192
|
+
|| (valueArg.startedAt !== undefined && !isIsoDate(valueArg.startedAt))
|
|
1193
|
+
) return false;
|
|
1194
|
+
if (valueArg.state === 'ready') return valueArg.ready && valueArg.code === undefined;
|
|
1195
|
+
return !valueArg.ready;
|
|
1196
|
+
};
|
|
1197
|
+
|
|
1198
|
+
const isAccountSummary = (valueArg: unknown): valueArg is IFlexPublicAccountSummary =>
|
|
1199
|
+
isFlexPlainObject(valueArg)
|
|
1200
|
+
&& hasFlexExactKeys(valueArg, ['providerId', 'authKind'], ['accountId', 'email', 'plan'])
|
|
1201
|
+
&& valueArg.providerId === 'openai'
|
|
1202
|
+
&& valueArg.authKind === 'chatgptOAuth'
|
|
1203
|
+
&& ['accountId', 'email', 'plan'].every((key) => (
|
|
1204
|
+
valueArg[key] === undefined || isBoundedString(valueArg[key], 512)
|
|
1205
|
+
));
|
|
1206
|
+
|
|
1207
|
+
export const isFlexPublicProviderConnection = (
|
|
1208
|
+
valueArg: unknown,
|
|
1209
|
+
): valueArg is IFlexPublicProviderConnection => isFlexPlainObject(valueArg)
|
|
1210
|
+
&& hasFlexExactKeys(valueArg, ['loginId', 'providerId', 'status'], ['account'])
|
|
1211
|
+
&& isQualifiedId(valueArg.loginId)
|
|
1212
|
+
&& valueArg.providerId === 'openai'
|
|
1213
|
+
&& ['pending', 'active', 'deleting', 'reauthRequired'].includes(String(valueArg.status))
|
|
1214
|
+
&& (valueArg.account === undefined || isAccountSummary(valueArg.account));
|
|
1215
|
+
|
|
1216
|
+
const isOpenCodeOAuthAuth = (valueArg: unknown): valueArg is IFlexOpenCodeOAuthAuth => (
|
|
1217
|
+
isFlexPlainObject(valueArg)
|
|
1218
|
+
&& hasFlexExactKeys(valueArg, ['type', 'access', 'refresh', 'expires'], ['accountId'])
|
|
1219
|
+
&& valueArg.type === 'oauth'
|
|
1220
|
+
&& isBoundedString(valueArg.access, 16 * 1024)
|
|
1221
|
+
&& isBoundedString(valueArg.refresh, 16 * 1024)
|
|
1222
|
+
&& isNonNegativeInteger(valueArg.expires)
|
|
1223
|
+
&& (valueArg.accountId === undefined || isBoundedString(valueArg.accountId, 512))
|
|
1224
|
+
&& Buffer.byteLength(JSON.stringify(valueArg), 'utf8') <= 48 * 1024
|
|
1225
|
+
);
|
|
1226
|
+
|
|
1227
|
+
const isModelChoice = (valueArg: unknown): valueArg is IFlexModelChoice =>
|
|
1228
|
+
isFlexPlainObject(valueArg)
|
|
1229
|
+
&& hasFlexExactKeys(valueArg, ['providerConnectionId', 'modelId'], ['variant'])
|
|
1230
|
+
&& isQualifiedId(valueArg.providerConnectionId)
|
|
1231
|
+
&& isBoundedString(valueArg.modelId, 256)
|
|
1232
|
+
&& (valueArg.variant === undefined || isBoundedString(valueArg.variant, 128));
|
|
1233
|
+
|
|
1234
|
+
const isModelIdentity = (valueArg: unknown): boolean => isFlexPlainObject(valueArg)
|
|
1235
|
+
&& hasFlexExactKeys(valueArg, ['provider', 'model'], ['displayName', 'variant'])
|
|
1236
|
+
&& isBoundedString(valueArg.provider, 256)
|
|
1237
|
+
&& isBoundedString(valueArg.model, 256)
|
|
1238
|
+
&& (valueArg.displayName === undefined || isBoundedString(valueArg.displayName, 512))
|
|
1239
|
+
&& (valueArg.variant === undefined || isBoundedString(valueArg.variant, 128));
|
|
1240
|
+
|
|
1241
|
+
const isUsage = (valueArg: unknown): boolean => isFlexPlainObject(valueArg)
|
|
1242
|
+
&& hasFlexExactKeys(valueArg, [
|
|
1243
|
+
'inputTokens', 'outputTokens', 'totalTokens', 'cacheReadTokens', 'cacheWriteTokens',
|
|
1244
|
+
])
|
|
1245
|
+
&& Object.values(valueArg).every(isNonNegativeInteger);
|
|
1246
|
+
|
|
1247
|
+
const isMessagePart = (valueArg: unknown): boolean => {
|
|
1248
|
+
if (!isFlexPlainObject(valueArg) || !isIdentifier(valueArg.partId)) return false;
|
|
1249
|
+
switch (valueArg.type) {
|
|
1250
|
+
case 'text':
|
|
1251
|
+
return hasFlexExactKeys(valueArg, ['partId', 'type', 'text'])
|
|
1252
|
+
&& isBoundedString(valueArg.text, 96 * 1024, true);
|
|
1253
|
+
case 'reasoning':
|
|
1254
|
+
return hasFlexExactKeys(valueArg, ['partId', 'type', 'text', 'status'])
|
|
1255
|
+
&& isBoundedString(valueArg.text, 96 * 1024, true)
|
|
1256
|
+
&& ['running', 'completed', 'cancelled'].includes(String(valueArg.status));
|
|
1257
|
+
case 'tool':
|
|
1258
|
+
return hasFlexExactKeys(
|
|
1259
|
+
valueArg,
|
|
1260
|
+
['partId', 'type', 'toolCallId', 'toolName', 'status', 'input'],
|
|
1261
|
+
['output', 'error', 'childSessionId', 'model'],
|
|
1262
|
+
)
|
|
1263
|
+
&& isIdentifier(valueArg.toolCallId)
|
|
1264
|
+
&& isBoundedString(valueArg.toolName, 512)
|
|
1265
|
+
&& ['running', 'completed', 'failed', 'cancelled'].includes(String(valueArg.status))
|
|
1266
|
+
&& isJsonValue(valueArg.input)
|
|
1267
|
+
&& (valueArg.output === undefined || isJsonValue(valueArg.output))
|
|
1268
|
+
&& (valueArg.error === undefined || isBoundedString(valueArg.error, 2048))
|
|
1269
|
+
&& (valueArg.childSessionId === undefined || isIdentifier(valueArg.childSessionId))
|
|
1270
|
+
&& (valueArg.model === undefined || (
|
|
1271
|
+
valueArg.childSessionId !== undefined && isModelIdentity(valueArg.model)
|
|
1272
|
+
));
|
|
1273
|
+
case 'attachment':
|
|
1274
|
+
return hasFlexExactKeys(
|
|
1275
|
+
valueArg,
|
|
1276
|
+
['partId', 'type', 'attachmentType', 'source'],
|
|
1277
|
+
['sizeBytes', 'mediaType', 'name'],
|
|
1278
|
+
)
|
|
1279
|
+
&& ['image', 'file'].includes(String(valueArg.attachmentType))
|
|
1280
|
+
&& ['inline-base64', 'data-url', 'remote-url'].includes(String(valueArg.source))
|
|
1281
|
+
&& (valueArg.sizeBytes === undefined || isNonNegativeInteger(valueArg.sizeBytes))
|
|
1282
|
+
&& (valueArg.mediaType === undefined || isBoundedString(valueArg.mediaType, 512))
|
|
1283
|
+
&& (valueArg.name === undefined || isBoundedString(valueArg.name, 1024));
|
|
1284
|
+
default:
|
|
1285
|
+
return false;
|
|
1286
|
+
}
|
|
1287
|
+
};
|
|
1288
|
+
|
|
1289
|
+
export const isFlexPublicMessage = (valueArg: unknown): valueArg is TFlexMessage =>
|
|
1290
|
+
isFlexPlainObject(valueArg)
|
|
1291
|
+
&& hasFlexExactKeys(
|
|
1292
|
+
valueArg,
|
|
1293
|
+
['messageId', 'sessionId', 'runId', 'role', 'status', 'createdAt', 'parts'],
|
|
1294
|
+
['completedAt', 'model', 'usage', 'error'],
|
|
1295
|
+
)
|
|
1296
|
+
&& isIdentifier(valueArg.messageId)
|
|
1297
|
+
&& isIdentifier(valueArg.sessionId)
|
|
1298
|
+
&& isIdentifier(valueArg.runId)
|
|
1299
|
+
&& ['user', 'assistant'].includes(String(valueArg.role))
|
|
1300
|
+
&& ['streaming', 'completed', 'failed', 'cancelled'].includes(String(valueArg.status))
|
|
1301
|
+
&& isIsoDate(valueArg.createdAt)
|
|
1302
|
+
&& (valueArg.completedAt === undefined || isIsoDate(valueArg.completedAt))
|
|
1303
|
+
&& Array.isArray(valueArg.parts)
|
|
1304
|
+
&& valueArg.parts.length <= 2000
|
|
1305
|
+
&& valueArg.parts.every(isMessagePart)
|
|
1306
|
+
&& (valueArg.model === undefined || isModelIdentity(valueArg.model))
|
|
1307
|
+
&& (valueArg.usage === undefined || isUsage(valueArg.usage))
|
|
1308
|
+
&& (valueArg.error === undefined || isBoundedString(valueArg.error, 2048));
|
|
1309
|
+
|
|
1310
|
+
export const isFlexPublicSession = (valueArg: unknown): valueArg is TFlexSession => {
|
|
1311
|
+
if (
|
|
1312
|
+
!isFlexPlainObject(valueArg)
|
|
1313
|
+
|| !hasFlexExactKeys(
|
|
1314
|
+
valueArg,
|
|
1315
|
+
['scopeId', 'sessionId', 'createdAt', 'updatedAt', 'status', 'activity'],
|
|
1316
|
+
[
|
|
1317
|
+
'sessionGenerationId', 'sessionGenerationSequence',
|
|
1318
|
+
'title', 'archivedAt', 'parentSessionId', 'parentRunId',
|
|
1319
|
+
'parentToolCallId', 'agent', 'depth',
|
|
1320
|
+
],
|
|
1321
|
+
)
|
|
1322
|
+
|| !isIdentifier(valueArg.scopeId)
|
|
1323
|
+
|| !isIdentifier(valueArg.sessionId)
|
|
1324
|
+
|| !isIsoDate(valueArg.createdAt)
|
|
1325
|
+
|| !isIsoDate(valueArg.updatedAt)
|
|
1326
|
+
|| (valueArg.title !== undefined || Object.hasOwn(valueArg, 'title'))
|
|
1327
|
+
&& !isBoundedString(valueArg.title, 4096, true)
|
|
1328
|
+
|| (valueArg.archivedAt !== undefined && !isIsoDate(valueArg.archivedAt))
|
|
1329
|
+
|| !['idle', 'scheduled', 'running', 'waiting_permission', 'failed', 'cancelled']
|
|
1330
|
+
.includes(String(valueArg.status))
|
|
1331
|
+
|| !isFlexPlainObject(valueArg.activity)
|
|
1332
|
+
|| !hasFlexExactKeys(valueArg.activity, ['status'], [
|
|
1333
|
+
'runId', 'startedAt', 'completedAt', 'error',
|
|
1334
|
+
])
|
|
1335
|
+
|| !['idle', 'scheduled', 'running', 'waiting_permission', 'failed', 'cancelled']
|
|
1336
|
+
.includes(String(valueArg.activity.status))
|
|
1337
|
+
) return false;
|
|
1338
|
+
const hasGenerationId = valueArg.sessionGenerationId !== undefined;
|
|
1339
|
+
const hasGenerationSequence = valueArg.sessionGenerationSequence !== undefined;
|
|
1340
|
+
if (
|
|
1341
|
+
hasGenerationId !== hasGenerationSequence
|
|
1342
|
+
|| (hasGenerationId && !isFlexSessionGeneration(valueArg))
|
|
1343
|
+
) return false;
|
|
1344
|
+
const relationship = [
|
|
1345
|
+
valueArg.parentSessionId,
|
|
1346
|
+
valueArg.parentRunId,
|
|
1347
|
+
valueArg.parentToolCallId,
|
|
1348
|
+
valueArg.agent,
|
|
1349
|
+
];
|
|
1350
|
+
const relationshipCount = relationship.filter((entry) => entry !== undefined).length;
|
|
1351
|
+
return (valueArg.activity.runId === undefined || isIdentifier(valueArg.activity.runId))
|
|
1352
|
+
&& (valueArg.activity.startedAt === undefined || isIsoDate(valueArg.activity.startedAt))
|
|
1353
|
+
&& (valueArg.activity.completedAt === undefined || isIsoDate(valueArg.activity.completedAt))
|
|
1354
|
+
&& (valueArg.activity.error === undefined || isBoundedString(valueArg.activity.error, 2048))
|
|
1355
|
+
&& (relationshipCount === 0 || relationshipCount === relationship.length)
|
|
1356
|
+
&& (valueArg.parentSessionId === undefined || isIdentifier(valueArg.parentSessionId))
|
|
1357
|
+
&& (valueArg.parentRunId === undefined || isIdentifier(valueArg.parentRunId))
|
|
1358
|
+
&& (valueArg.parentToolCallId === undefined || isIdentifier(valueArg.parentToolCallId))
|
|
1359
|
+
&& (valueArg.agent === undefined || isBoundedString(valueArg.agent, 128))
|
|
1360
|
+
&& (
|
|
1361
|
+
relationshipCount === 0
|
|
1362
|
+
? valueArg.depth === undefined || valueArg.depth === 0
|
|
1363
|
+
: Number.isSafeInteger(valueArg.depth)
|
|
1364
|
+
&& Number(valueArg.depth) >= 1
|
|
1365
|
+
&& Number(valueArg.depth) <= 8
|
|
1366
|
+
);
|
|
1367
|
+
};
|
|
1368
|
+
|
|
1369
|
+
const isPromptQueueEntry = (valueArg: unknown): valueArg is TFlexPromptQueueEntry => (
|
|
1370
|
+
isFlexPlainObject(valueArg)
|
|
1371
|
+
&& hasFlexExactKeys(
|
|
1372
|
+
valueArg,
|
|
1373
|
+
['queueId', 'queueSequence', 'scopeId', 'sessionId', 'status', 'queuedAt'],
|
|
1374
|
+
['runId', 'scheduleKey', 'startedAt', 'finishedAt', 'error'],
|
|
1375
|
+
)
|
|
1376
|
+
&& isIdentifier(valueArg.queueId)
|
|
1377
|
+
&& isNonNegativeInteger(valueArg.queueSequence)
|
|
1378
|
+
&& valueArg.queueSequence >= 1
|
|
1379
|
+
&& isIdentifier(valueArg.scopeId)
|
|
1380
|
+
&& isIdentifier(valueArg.sessionId)
|
|
1381
|
+
&& ['queued', 'starting', 'scheduled', 'running', 'completed', 'failed', 'cancelled']
|
|
1382
|
+
.includes(String(valueArg.status))
|
|
1383
|
+
&& isIsoDate(valueArg.queuedAt)
|
|
1384
|
+
&& (valueArg.runId === undefined || isIdentifier(valueArg.runId))
|
|
1385
|
+
&& (valueArg.scheduleKey === undefined || isIdentifier(valueArg.scheduleKey))
|
|
1386
|
+
&& (valueArg.startedAt === undefined || isIsoDate(valueArg.startedAt))
|
|
1387
|
+
&& (valueArg.finishedAt === undefined || isIsoDate(valueArg.finishedAt))
|
|
1388
|
+
&& (valueArg.error === undefined || (
|
|
1389
|
+
isFlexPlainObject(valueArg.error)
|
|
1390
|
+
&& hasFlexExactKeys(valueArg.error, ['name', 'message'], ['code'])
|
|
1391
|
+
&& isBoundedString(valueArg.error.name, 128)
|
|
1392
|
+
&& isBoundedString(valueArg.error.message, 2048)
|
|
1393
|
+
&& (valueArg.error.code === undefined || isBoundedString(valueArg.error.code, 128))
|
|
1394
|
+
))
|
|
1395
|
+
);
|
|
1396
|
+
|
|
1397
|
+
const isPermissionRequest = (valueArg: unknown): valueArg is TFlexPermissionRequest =>
|
|
1398
|
+
isFlexPlainObject(valueArg)
|
|
1399
|
+
&& hasFlexExactKeys(
|
|
1400
|
+
valueArg,
|
|
1401
|
+
['permissionId', 'scopeId', 'sessionId', 'runId', 'createdAt', 'kind', 'description'],
|
|
1402
|
+
['toolCallId', 'rememberKey', 'metadata'],
|
|
1403
|
+
)
|
|
1404
|
+
&& isIdentifier(valueArg.permissionId)
|
|
1405
|
+
&& isIdentifier(valueArg.scopeId)
|
|
1406
|
+
&& isIdentifier(valueArg.sessionId)
|
|
1407
|
+
&& isIdentifier(valueArg.runId)
|
|
1408
|
+
&& isIsoDate(valueArg.createdAt)
|
|
1409
|
+
&& isBoundedString(valueArg.kind, 128)
|
|
1410
|
+
&& isBoundedString(valueArg.description, 2048)
|
|
1411
|
+
&& (valueArg.toolCallId === undefined || isIdentifier(valueArg.toolCallId))
|
|
1412
|
+
&& (valueArg.rememberKey === undefined || isBoundedString(valueArg.rememberKey, 512))
|
|
1413
|
+
&& (valueArg.metadata === undefined || isJsonValue(valueArg.metadata));
|
|
1414
|
+
|
|
1415
|
+
const isPrompt = (valueArg: unknown): valueArg is TFlexPrompt => {
|
|
1416
|
+
if (isBoundedString(valueArg, 768 * 1024, true)) return true;
|
|
1417
|
+
if (!Array.isArray(valueArg) || valueArg.length < 1 || valueArg.length > 128) return false;
|
|
1418
|
+
return valueArg.every((part) => {
|
|
1419
|
+
if (!isFlexPlainObject(part)) return false;
|
|
1420
|
+
if (part.type === 'text') {
|
|
1421
|
+
return hasFlexExactKeys(part, ['type', 'text'])
|
|
1422
|
+
&& isBoundedString(part.text, 768 * 1024, true);
|
|
1423
|
+
}
|
|
1424
|
+
if (part.type === 'image') {
|
|
1425
|
+
return hasFlexExactKeys(part, ['type', 'data'], ['mediaType', 'name'])
|
|
1426
|
+
&& isBoundedString(part.data, 768 * 1024)
|
|
1427
|
+
&& (part.mediaType === undefined || isBoundedString(part.mediaType, 512))
|
|
1428
|
+
&& (part.name === undefined || isBoundedString(part.name, 1024));
|
|
1429
|
+
}
|
|
1430
|
+
if (part.type === 'file') {
|
|
1431
|
+
return hasFlexExactKeys(part, ['type', 'data', 'mediaType'], ['name'])
|
|
1432
|
+
&& isBoundedString(part.data, 768 * 1024)
|
|
1433
|
+
&& isBoundedString(part.mediaType, 512)
|
|
1434
|
+
&& (part.name === undefined || isBoundedString(part.name, 1024));
|
|
1435
|
+
}
|
|
1436
|
+
return false;
|
|
1437
|
+
});
|
|
1438
|
+
};
|
|
1439
|
+
|
|
1440
|
+
const isPromptOptions = (valueArg: unknown): valueArg is TFlexPromptOptions =>
|
|
1441
|
+
isFlexPlainObject(valueArg)
|
|
1442
|
+
&& hasFlexExactKeys(valueArg, [], ['modelHint', 'system', 'maxSteps'])
|
|
1443
|
+
&& (valueArg.modelHint === undefined || isBoundedString(valueArg.modelHint, 512))
|
|
1444
|
+
&& (valueArg.system === undefined || isBoundedString(valueArg.system, 128 * 1024, true))
|
|
1445
|
+
&& (valueArg.maxSteps === undefined
|
|
1446
|
+
|| (Number.isSafeInteger(valueArg.maxSteps) && Number(valueArg.maxSteps) >= 1
|
|
1447
|
+
&& Number(valueArg.maxSteps) <= 1000));
|
|
1448
|
+
|
|
1449
|
+
const isCreateSessionOptions = (valueArg: unknown): valueArg is TFlexCreateSessionOptions =>
|
|
1450
|
+
isFlexPlainObject(valueArg)
|
|
1451
|
+
&& hasFlexExactKeys(valueArg, [], ['sessionId', 'title'])
|
|
1452
|
+
&& (valueArg.sessionId === undefined || isIdentifier(valueArg.sessionId))
|
|
1453
|
+
&& (valueArg.title === undefined || isBoundedString(valueArg.title, 4096, true));
|
|
1454
|
+
|
|
1455
|
+
const isUpdateSessionOptions = (valueArg: unknown): valueArg is TFlexUpdateSessionOptions =>
|
|
1456
|
+
isFlexPlainObject(valueArg)
|
|
1457
|
+
&& hasFlexExactKeys(valueArg, [], ['title', 'archived'])
|
|
1458
|
+
&& (valueArg.title === undefined || valueArg.title === null
|
|
1459
|
+
|| isBoundedString(valueArg.title, 4096, true))
|
|
1460
|
+
&& (valueArg.archived === undefined || typeof valueArg.archived === 'boolean');
|
|
1461
|
+
|
|
1462
|
+
const isScopeAndSession = (valueArg: Record<string, unknown>): boolean =>
|
|
1463
|
+
isIdentifier(valueArg.scopeId) && isIdentifier(valueArg.sessionId);
|
|
1464
|
+
|
|
1465
|
+
const isAffectedWorkspace = (valueArg: unknown): boolean => isFlexPlainObject(valueArg)
|
|
1466
|
+
&& hasFlexExactKeys(valueArg, ['id', 'label'])
|
|
1467
|
+
&& isBoundedString(valueArg.id, 512)
|
|
1468
|
+
&& valueArg.id.trim().length > 0
|
|
1469
|
+
&& isBoundedString(valueArg.label, 2048)
|
|
1470
|
+
&& valueArg.label.trim().length > 0;
|
|
1471
|
+
|
|
1472
|
+
const isReversionOutcome = (
|
|
1473
|
+
valueArg: unknown,
|
|
1474
|
+
): valueArg is TFlexTurnReversionFinalizedOutcome => {
|
|
1475
|
+
if (!isFlexPlainObject(valueArg) || !isJsonValue(valueArg.reference)) return false;
|
|
1476
|
+
if (valueArg.disposition === 'no-change') {
|
|
1477
|
+
return hasFlexExactKeys(valueArg, ['disposition', 'reference'], ['affectedWorkspaces'])
|
|
1478
|
+
&& (valueArg.affectedWorkspaces === undefined
|
|
1479
|
+
|| (Array.isArray(valueArg.affectedWorkspaces) && valueArg.affectedWorkspaces.length === 0));
|
|
1480
|
+
}
|
|
1481
|
+
if (valueArg.disposition === 'revertible') {
|
|
1482
|
+
return hasFlexExactKeys(valueArg, ['disposition', 'reference', 'affectedWorkspaces'])
|
|
1483
|
+
&& Array.isArray(valueArg.affectedWorkspaces)
|
|
1484
|
+
&& valueArg.affectedWorkspaces.length >= 1
|
|
1485
|
+
&& valueArg.affectedWorkspaces.length <= 64
|
|
1486
|
+
&& valueArg.affectedWorkspaces.every(isAffectedWorkspace)
|
|
1487
|
+
&& new Set(valueArg.affectedWorkspaces.map((entry) => (
|
|
1488
|
+
entry as { id: string }
|
|
1489
|
+
).id)).size === valueArg.affectedWorkspaces.length;
|
|
1490
|
+
}
|
|
1491
|
+
return valueArg.disposition === 'nonrevertible'
|
|
1492
|
+
&& hasFlexExactKeys(
|
|
1493
|
+
valueArg,
|
|
1494
|
+
['disposition', 'reference', 'reasonCode'],
|
|
1495
|
+
['affectedWorkspaces'],
|
|
1496
|
+
)
|
|
1497
|
+
&& isBoundedString(valueArg.reasonCode, 128)
|
|
1498
|
+
&& /^[A-Za-z0-9][A-Za-z0-9._-]*$/u.test(valueArg.reasonCode)
|
|
1499
|
+
&& (valueArg.affectedWorkspaces === undefined || (
|
|
1500
|
+
Array.isArray(valueArg.affectedWorkspaces)
|
|
1501
|
+
&& valueArg.affectedWorkspaces.length <= 64
|
|
1502
|
+
&& valueArg.affectedWorkspaces.every(isAffectedWorkspace)
|
|
1503
|
+
));
|
|
1504
|
+
};
|
|
1505
|
+
|
|
1506
|
+
const isSlashDescriptor = (valueArg: unknown): valueArg is TFlexSlashCommandDescriptor => (
|
|
1507
|
+
isFlexPlainObject(valueArg)
|
|
1508
|
+
&& hasFlexExactKeys(
|
|
1509
|
+
valueArg,
|
|
1510
|
+
['name', 'description', 'kind', 'hints', 'available', 'workspaceReversion'],
|
|
1511
|
+
['unavailableReason'],
|
|
1512
|
+
)
|
|
1513
|
+
&& typeof valueArg.name === 'string'
|
|
1514
|
+
&& /^[a-z][a-z0-9-]{0,63}$/u.test(valueArg.name)
|
|
1515
|
+
&& isBoundedString(valueArg.description, 2048, true)
|
|
1516
|
+
&& ['builtin', 'template', 'handler'].includes(String(valueArg.kind))
|
|
1517
|
+
&& Array.isArray(valueArg.hints)
|
|
1518
|
+
&& valueArg.hints.length <= 64
|
|
1519
|
+
&& valueArg.hints.every((hint) => isBoundedString(hint, 128))
|
|
1520
|
+
&& typeof valueArg.available === 'boolean'
|
|
1521
|
+
&& (valueArg.unavailableReason === undefined
|
|
1522
|
+
|| isBoundedString(valueArg.unavailableReason, 2048))
|
|
1523
|
+
&& ['supported', 'unsupported', 'not-applicable'].includes(
|
|
1524
|
+
String(valueArg.workspaceReversion),
|
|
1525
|
+
)
|
|
1526
|
+
);
|
|
1527
|
+
|
|
1528
|
+
const isSlashInvocation = (valueArg: Record<string, unknown>): boolean => (
|
|
1529
|
+
isBoundedString(valueArg.input, 768 * 1024)
|
|
1530
|
+
&& typeof valueArg.name === 'string'
|
|
1531
|
+
&& /^[a-z][a-z0-9-]{0,63}$/u.test(valueArg.name)
|
|
1532
|
+
&& isBoundedString(valueArg.rawArguments, 768 * 1024, true)
|
|
1533
|
+
&& Array.isArray(valueArg.arguments)
|
|
1534
|
+
&& valueArg.arguments.length <= 4096
|
|
1535
|
+
&& valueArg.arguments.every((entry) => isBoundedString(entry, 768 * 1024, true))
|
|
1536
|
+
);
|
|
1537
|
+
|
|
1538
|
+
const isSlashExecutionResult = (valueArg: unknown): valueArg is TFlexSlashExecutionResult => {
|
|
1539
|
+
if (!isFlexPlainObject(valueArg) || typeof valueArg.type !== 'string') return false;
|
|
1540
|
+
if (valueArg.type === 'not-command') return hasFlexExactKeys(valueArg, ['type']);
|
|
1541
|
+
if (valueArg.type === 'malformed') {
|
|
1542
|
+
return hasFlexExactKeys(valueArg, ['type', 'reason'])
|
|
1543
|
+
&& isBoundedString(valueArg.reason, 2048);
|
|
1544
|
+
}
|
|
1545
|
+
if (valueArg.type === 'unknown') {
|
|
1546
|
+
return hasFlexExactKeys(valueArg, ['type', 'input', 'name', 'rawArguments', 'arguments'])
|
|
1547
|
+
&& isSlashInvocation(valueArg);
|
|
1548
|
+
}
|
|
1549
|
+
if (valueArg.type === 'operation') {
|
|
1550
|
+
return hasFlexExactKeys(valueArg, ['type', 'name'])
|
|
1551
|
+
&& typeof valueArg.name === 'string'
|
|
1552
|
+
&& /^[a-z][a-z0-9-]{0,63}$/u.test(valueArg.name);
|
|
1553
|
+
}
|
|
1554
|
+
if (valueArg.type === 'handler-result') {
|
|
1555
|
+
return hasFlexExactKeys(valueArg, ['type', 'name', 'result'])
|
|
1556
|
+
&& typeof valueArg.name === 'string'
|
|
1557
|
+
&& /^[a-z][a-z0-9-]{0,63}$/u.test(valueArg.name)
|
|
1558
|
+
&& isJsonValue(valueArg.result);
|
|
1559
|
+
}
|
|
1560
|
+
return valueArg.type === 'prompt-admission'
|
|
1561
|
+
&& hasFlexExactKeys(valueArg, ['type', 'name', 'admission'])
|
|
1562
|
+
&& typeof valueArg.name === 'string'
|
|
1563
|
+
&& /^[a-z][a-z0-9-]{0,63}$/u.test(valueArg.name)
|
|
1564
|
+
&& isFlexPlainObject(valueArg.admission)
|
|
1565
|
+
&& hasFlexExactKeys(valueArg.admission, ['queueId', 'runId'])
|
|
1566
|
+
&& isIdentifier(valueArg.admission.queueId)
|
|
1567
|
+
&& isIdentifier(valueArg.admission.runId);
|
|
1568
|
+
};
|
|
1569
|
+
|
|
1570
|
+
const isSessionReversionInfo = (valueArg: unknown): valueArg is TFlexSessionReversionInfo => (
|
|
1571
|
+
isFlexPlainObject(valueArg)
|
|
1572
|
+
&& hasFlexExactKeys(valueArg, ['undoAvailable', 'redoAvailable', 'groups'])
|
|
1573
|
+
&& typeof valueArg.undoAvailable === 'boolean'
|
|
1574
|
+
&& typeof valueArg.redoAvailable === 'boolean'
|
|
1575
|
+
&& Array.isArray(valueArg.groups)
|
|
1576
|
+
&& valueArg.groups.length <= 3000
|
|
1577
|
+
&& valueArg.groups.every((entry) => isFlexPlainObject(entry)
|
|
1578
|
+
&& hasFlexExactKeys(entry, [
|
|
1579
|
+
'runId', 'kind', 'visibility', 'affectedWorkspaces', 'affectedWorkspacesTruncated',
|
|
1580
|
+
])
|
|
1581
|
+
&& isIdentifier(entry.runId)
|
|
1582
|
+
&& ['candidate', 'barrier', 'no-change'].includes(String(entry.kind))
|
|
1583
|
+
&& ['visible', 'hidden'].includes(String(entry.visibility))
|
|
1584
|
+
&& Array.isArray(entry.affectedWorkspaces)
|
|
1585
|
+
&& entry.affectedWorkspaces.length <= 64
|
|
1586
|
+
&& entry.affectedWorkspaces.every(isAffectedWorkspace)
|
|
1587
|
+
&& typeof entry.affectedWorkspacesTruncated === 'boolean')
|
|
1588
|
+
);
|
|
1589
|
+
|
|
1590
|
+
export const isFlexRequestPayload = <TMethod extends TFlexRequestMethod>(
|
|
1591
|
+
methodArg: TMethod,
|
|
1592
|
+
valueArg: unknown,
|
|
1593
|
+
): valueArg is TFlexRequest<TMethod> => {
|
|
1594
|
+
if (!isFlexPlainObject(valueArg)) return false;
|
|
1595
|
+
switch (methodArg) {
|
|
1596
|
+
case 'service.status':
|
|
1597
|
+
case 'provider.list':
|
|
1598
|
+
case 'provider.connection.list':
|
|
1599
|
+
return hasFlexExactKeys(valueArg, []);
|
|
1600
|
+
case 'project.replaceAll':
|
|
1601
|
+
return hasFlexExactKeys(valueArg, ['projects'])
|
|
1602
|
+
&& Array.isArray(valueArg.projects)
|
|
1603
|
+
&& valueArg.projects.length <= controllerProjectLimit
|
|
1604
|
+
&& valueArg.projects.every(isFlexProjectRecord)
|
|
1605
|
+
&& new Set(valueArg.projects.map((project) => project.projectId)).size
|
|
1606
|
+
=== valueArg.projects.length;
|
|
1607
|
+
case 'project.register':
|
|
1608
|
+
case 'project.remove':
|
|
1609
|
+
return hasFlexExactKeys(valueArg, ['project'])
|
|
1610
|
+
&& isFlexIdentityBoundProjectRecord(valueArg.project);
|
|
1611
|
+
case 'intelligence.start':
|
|
1612
|
+
return hasFlexExactKeys(valueArg, [
|
|
1613
|
+
'scopeId', 'sourceSessionId', 'question', 'scratchpad', 'model', 'capabilityToken',
|
|
1614
|
+
])
|
|
1615
|
+
&& isIdentifier(valueArg.scopeId)
|
|
1616
|
+
&& isFlexIntelligenceSessionId(valueArg.sourceSessionId)
|
|
1617
|
+
&& isBoundedString(valueArg.question, 32 * 1024)
|
|
1618
|
+
&& valueArg.question.length <= 8_192
|
|
1619
|
+
&& String(valueArg.question).trim().length > 0
|
|
1620
|
+
&& isBoundedString(valueArg.scratchpad, 128 * 1024, true)
|
|
1621
|
+
&& valueArg.scratchpad.length <= 32_768
|
|
1622
|
+
&& isModelChoice(valueArg.model)
|
|
1623
|
+
&& valueArg.model.modelId === 'gpt-5.6-luna'
|
|
1624
|
+
&& valueArg.model.variant === undefined
|
|
1625
|
+
&& isIntelligenceCapabilityToken(valueArg.capabilityToken);
|
|
1626
|
+
case 'intelligence.get':
|
|
1627
|
+
return hasFlexExactKeys(valueArg, ['scopeId', 'jobId', 'capabilityToken'])
|
|
1628
|
+
&& isIdentifier(valueArg.scopeId)
|
|
1629
|
+
&& isQualifiedId(valueArg.jobId)
|
|
1630
|
+
&& isIntelligenceCapabilityToken(valueArg.capabilityToken);
|
|
1631
|
+
case 'intelligence.cancel':
|
|
1632
|
+
return hasFlexExactKeys(valueArg, ['scopeId', 'capabilityToken'])
|
|
1633
|
+
&& isIdentifier(valueArg.scopeId)
|
|
1634
|
+
&& isIntelligenceCapabilityToken(valueArg.capabilityToken);
|
|
1635
|
+
case 'session.list':
|
|
1636
|
+
return hasFlexExactKeys(valueArg, ['scopeId'], ['offset', 'limit'])
|
|
1637
|
+
&& isIdentifier(valueArg.scopeId)
|
|
1638
|
+
&& (valueArg.offset === undefined || isNonNegativeInteger(valueArg.offset))
|
|
1639
|
+
&& (valueArg.limit === undefined
|
|
1640
|
+
|| (Number.isSafeInteger(valueArg.limit) && Number(valueArg.limit) >= 1
|
|
1641
|
+
&& Number(valueArg.limit) <= 50));
|
|
1642
|
+
case 'session.create':
|
|
1643
|
+
return hasFlexExactKeys(valueArg, ['scopeId'], ['options'])
|
|
1644
|
+
&& isIdentifier(valueArg.scopeId)
|
|
1645
|
+
&& (valueArg.options === undefined || isCreateSessionOptions(valueArg.options));
|
|
1646
|
+
case 'session.get':
|
|
1647
|
+
case 'session.delete':
|
|
1648
|
+
case 'session.reversion.info':
|
|
1649
|
+
case 'slash.list':
|
|
1650
|
+
case 'prompt.list':
|
|
1651
|
+
return hasFlexExactKeys(valueArg, ['scopeId', 'sessionId'])
|
|
1652
|
+
&& isScopeAndSession(valueArg);
|
|
1653
|
+
case 'session.compact':
|
|
1654
|
+
return hasFlexExactKeys(valueArg, ['scopeId', 'sessionId'], ['model'])
|
|
1655
|
+
&& isScopeAndSession(valueArg)
|
|
1656
|
+
&& (valueArg.model === undefined || isModelChoice(valueArg.model));
|
|
1657
|
+
case 'slash.execute':
|
|
1658
|
+
return hasFlexExactKeys(valueArg, ['scopeId', 'sessionId', 'input'], ['model'])
|
|
1659
|
+
&& isScopeAndSession(valueArg)
|
|
1660
|
+
&& isBoundedString(valueArg.input, 768 * 1024)
|
|
1661
|
+
&& (valueArg.model === undefined || isModelChoice(valueArg.model));
|
|
1662
|
+
case 'session.update':
|
|
1663
|
+
return hasFlexExactKeys(valueArg, ['scopeId', 'sessionId', 'options'])
|
|
1664
|
+
&& isScopeAndSession(valueArg)
|
|
1665
|
+
&& isUpdateSessionOptions(valueArg.options);
|
|
1666
|
+
case 'message.page':
|
|
1667
|
+
return hasFlexExactKeys(valueArg, ['scopeId', 'sessionId'], ['limit', 'before'])
|
|
1668
|
+
&& isScopeAndSession(valueArg)
|
|
1669
|
+
&& (valueArg.limit === undefined
|
|
1670
|
+
|| (Number.isSafeInteger(valueArg.limit) && Number(valueArg.limit) >= 1
|
|
1671
|
+
&& Number(valueArg.limit) <= 50))
|
|
1672
|
+
&& (valueArg.before === undefined || isBoundedString(valueArg.before, 4096));
|
|
1673
|
+
case 'message.get':
|
|
1674
|
+
return hasFlexExactKeys(valueArg, ['scopeId', 'sessionId', 'messageId'])
|
|
1675
|
+
&& isScopeAndSession(valueArg) && isIdentifier(valueArg.messageId);
|
|
1676
|
+
case 'prompt.start':
|
|
1677
|
+
return hasFlexExactKeys(
|
|
1678
|
+
valueArg,
|
|
1679
|
+
['scopeId', 'sessionId', 'prompt'],
|
|
1680
|
+
['options', 'model', 'upload'],
|
|
1681
|
+
)
|
|
1682
|
+
&& isScopeAndSession(valueArg)
|
|
1683
|
+
&& isPrompt(valueArg.prompt)
|
|
1684
|
+
&& (valueArg.options === undefined || isPromptOptions(valueArg.options))
|
|
1685
|
+
&& (valueArg.model === undefined || isModelChoice(valueArg.model))
|
|
1686
|
+
&& (valueArg.upload === undefined || (
|
|
1687
|
+
isFlexPlainObject(valueArg.upload)
|
|
1688
|
+
&& hasFlexExactKeys(valueArg.upload, ['operationId', 'directory'])
|
|
1689
|
+
&& isIdentifier(valueArg.upload.operationId)
|
|
1690
|
+
&& isBoundedString(valueArg.upload.directory, 4096)
|
|
1691
|
+
&& plugins.path.isAbsolute(valueArg.upload.directory)
|
|
1692
|
+
));
|
|
1693
|
+
case 'prompt.get':
|
|
1694
|
+
case 'prompt.cancel':
|
|
1695
|
+
return hasFlexExactKeys(valueArg, ['scopeId', 'sessionId', 'queueId'])
|
|
1696
|
+
&& isScopeAndSession(valueArg) && isIdentifier(valueArg.queueId);
|
|
1697
|
+
case 'prompt.acknowledge':
|
|
1698
|
+
return hasFlexExactKeys(valueArg, ['scopeId', 'sessionId', 'queueId', 'runId'])
|
|
1699
|
+
&& isScopeAndSession(valueArg)
|
|
1700
|
+
&& isIdentifier(valueArg.queueId)
|
|
1701
|
+
&& isIdentifier(valueArg.runId);
|
|
1702
|
+
case 'permission.list':
|
|
1703
|
+
return hasFlexExactKeys(valueArg, ['scopeId'], ['sessionId', 'familyRootSessionId'])
|
|
1704
|
+
&& isIdentifier(valueArg.scopeId)
|
|
1705
|
+
&& (valueArg.sessionId === undefined || isIdentifier(valueArg.sessionId))
|
|
1706
|
+
&& (valueArg.familyRootSessionId === undefined
|
|
1707
|
+
|| isIdentifier(valueArg.familyRootSessionId))
|
|
1708
|
+
&& !(valueArg.sessionId !== undefined && valueArg.familyRootSessionId !== undefined);
|
|
1709
|
+
case 'permission.respond':
|
|
1710
|
+
return hasFlexExactKeys(
|
|
1711
|
+
valueArg,
|
|
1712
|
+
['scopeId', 'sessionId', 'permissionId', 'decision'],
|
|
1713
|
+
)
|
|
1714
|
+
&& isScopeAndSession(valueArg)
|
|
1715
|
+
&& isIdentifier(valueArg.permissionId)
|
|
1716
|
+
&& ['once', 'always', 'reject'].includes(String(valueArg.decision));
|
|
1717
|
+
case 'provider.login.begin':
|
|
1718
|
+
return hasFlexExactKeys(valueArg, ['providerId']) && valueArg.providerId === 'openai';
|
|
1719
|
+
case 'provider.login.status':
|
|
1720
|
+
case 'provider.login.cancel':
|
|
1721
|
+
return hasFlexExactKeys(valueArg, ['loginId']) && isQualifiedId(valueArg.loginId);
|
|
1722
|
+
case 'provider.connection.logout.prepare':
|
|
1723
|
+
case 'provider.connection.logout':
|
|
1724
|
+
case 'provider.connection.opencode-auth.get':
|
|
1725
|
+
case 'provider.connection.ratelimits.get':
|
|
1726
|
+
case 'provider.models.refresh':
|
|
1727
|
+
return hasFlexExactKeys(valueArg, ['providerConnectionId'])
|
|
1728
|
+
&& isQualifiedId(valueArg.providerConnectionId);
|
|
1729
|
+
case 'model.choice.get':
|
|
1730
|
+
return hasFlexExactKeys(valueArg, ['scopeId']) && isIdentifier(valueArg.scopeId);
|
|
1731
|
+
case 'model.choice.set':
|
|
1732
|
+
return hasFlexExactKeys(valueArg, ['scopeId', 'choice'])
|
|
1733
|
+
&& isIdentifier(valueArg.scopeId) && isModelChoice(valueArg.choice);
|
|
1734
|
+
case 'model.choice.validate':
|
|
1735
|
+
return hasFlexExactKeys(valueArg, ['choice']) && isModelChoice(valueArg.choice);
|
|
1736
|
+
default:
|
|
1737
|
+
return false;
|
|
1738
|
+
}
|
|
1739
|
+
};
|
|
1740
|
+
|
|
1741
|
+
export const isFlexHostRequestPayload = <TMethod extends TFlexHostRequestMethod>(
|
|
1742
|
+
methodArg: TMethod,
|
|
1743
|
+
valueArg: unknown,
|
|
1744
|
+
): valueArg is TFlexHostRequest<TMethod> => {
|
|
1745
|
+
if (!isFlexPlainObject(valueArg)) return false;
|
|
1746
|
+
const reversionBase = (optionalKeysArg: readonly string[] = []) => (
|
|
1747
|
+
hasFlexExactKeys(
|
|
1748
|
+
valueArg,
|
|
1749
|
+
['scopeId', 'storageKey', 'sessionId', 'runId', 'captureId'],
|
|
1750
|
+
optionalKeysArg,
|
|
1751
|
+
)
|
|
1752
|
+
&& isIdentifier(valueArg.scopeId)
|
|
1753
|
+
&& isIdentifier(valueArg.storageKey)
|
|
1754
|
+
&& isIdentifier(valueArg.sessionId)
|
|
1755
|
+
&& isIdentifier(valueArg.runId)
|
|
1756
|
+
&& isIdentifier(valueArg.captureId)
|
|
1757
|
+
);
|
|
1758
|
+
if (
|
|
1759
|
+
methodArg === 'reversion.prepare'
|
|
1760
|
+
|| methodArg === 'reversion.inspect-capture'
|
|
1761
|
+
|| methodArg === 'reversion.finalize'
|
|
1762
|
+
) return reversionBase();
|
|
1763
|
+
if (methodArg === 'reversion.inspect-apply' || methodArg === 'reversion.apply') {
|
|
1764
|
+
return reversionBase(['reference', 'operationId', 'direction'])
|
|
1765
|
+
&& Object.hasOwn(valueArg, 'reference')
|
|
1766
|
+
&& isJsonValue(valueArg.reference)
|
|
1767
|
+
&& Buffer.byteLength(JSON.stringify(valueArg.reference), 'utf8') <= 256 * 1024
|
|
1768
|
+
&& isIdentifier(valueArg.operationId)
|
|
1769
|
+
&& (valueArg.direction === 'undo' || valueArg.direction === 'redo');
|
|
1770
|
+
}
|
|
1771
|
+
if (methodArg === 'reversion.release') {
|
|
1772
|
+
return reversionBase(['reference'])
|
|
1773
|
+
&& Object.hasOwn(valueArg, 'reference')
|
|
1774
|
+
&& isJsonValue(valueArg.reference)
|
|
1775
|
+
&& Buffer.byteLength(JSON.stringify(valueArg.reference), 'utf8') <= 256 * 1024;
|
|
1776
|
+
}
|
|
1777
|
+
if (methodArg === 'git.worktree.create' || methodArg === 'git.worktree.list') {
|
|
1778
|
+
return hasFlexExactKeys(valueArg, ['scopeId', 'sessionId'])
|
|
1779
|
+
&& isIdentifier(valueArg.scopeId)
|
|
1780
|
+
&& isIdentifier(valueArg.sessionId);
|
|
1781
|
+
}
|
|
1782
|
+
if (methodArg === 'git.worktree.remove') {
|
|
1783
|
+
const compensating = Object.hasOwn(valueArg, 'compensate');
|
|
1784
|
+
return hasFlexExactKeys(
|
|
1785
|
+
valueArg,
|
|
1786
|
+
['scopeId', 'sessionId', 'worktreeId', ...(compensating ? ['compensate'] : [])],
|
|
1787
|
+
)
|
|
1788
|
+
&& isIdentifier(valueArg.scopeId)
|
|
1789
|
+
&& isIdentifier(valueArg.sessionId)
|
|
1790
|
+
&& isIdentifier(valueArg.worktreeId)
|
|
1791
|
+
&& (!compensating || valueArg.compensate === true);
|
|
1792
|
+
}
|
|
1793
|
+
if (methodArg === 'crossharness.chats.list') {
|
|
1794
|
+
return hasFlexExactKeys(valueArg, ['capabilityToken', 'limit'])
|
|
1795
|
+
&& isIntelligenceCapabilityToken(valueArg.capabilityToken)
|
|
1796
|
+
&& Number.isSafeInteger(valueArg.limit)
|
|
1797
|
+
&& Number(valueArg.limit) >= 1
|
|
1798
|
+
&& Number(valueArg.limit) <= 50;
|
|
1799
|
+
}
|
|
1800
|
+
if (methodArg === 'crossharness.chat.read') {
|
|
1801
|
+
return hasFlexExactKeys(
|
|
1802
|
+
valueArg,
|
|
1803
|
+
['capabilityToken', 'sessionId', 'limit', 'maxChars'],
|
|
1804
|
+
)
|
|
1805
|
+
&& isIntelligenceCapabilityToken(valueArg.capabilityToken)
|
|
1806
|
+
&& isFlexIntelligenceSessionId(valueArg.sessionId)
|
|
1807
|
+
&& Number.isSafeInteger(valueArg.limit)
|
|
1808
|
+
&& Number(valueArg.limit) >= 1
|
|
1809
|
+
&& Number(valueArg.limit) <= 50
|
|
1810
|
+
&& Number.isSafeInteger(valueArg.maxChars)
|
|
1811
|
+
&& Number(valueArg.maxChars) >= 128
|
|
1812
|
+
&& Number(valueArg.maxChars) <= 4096;
|
|
1813
|
+
}
|
|
1814
|
+
if (methodArg === 'project-management.load') {
|
|
1815
|
+
return hasFlexExactKeys(valueArg, [
|
|
1816
|
+
'storageKey',
|
|
1817
|
+
'sessionId',
|
|
1818
|
+
'sessionGenerationId',
|
|
1819
|
+
'sessionGenerationSequence',
|
|
1820
|
+
])
|
|
1821
|
+
&& isIdentifier(valueArg.storageKey)
|
|
1822
|
+
&& isIdentifier(valueArg.sessionId)
|
|
1823
|
+
&& isFlexSessionGeneration(valueArg);
|
|
1824
|
+
}
|
|
1825
|
+
if (methodArg === 'project-management.save') {
|
|
1826
|
+
return hasFlexExactKeys(valueArg, [
|
|
1827
|
+
'storageKey',
|
|
1828
|
+
'sessionId',
|
|
1829
|
+
'snapshot',
|
|
1830
|
+
'expectedRevision',
|
|
1831
|
+
'writeContext',
|
|
1832
|
+
])
|
|
1833
|
+
&& isIdentifier(valueArg.storageKey)
|
|
1834
|
+
&& isIdentifier(valueArg.sessionId)
|
|
1835
|
+
&& isFlexProjectManagementSnapshot(valueArg.snapshot)
|
|
1836
|
+
&& isNonNegativeInteger(valueArg.expectedRevision)
|
|
1837
|
+
&& valueArg.snapshot.revision === Number(valueArg.expectedRevision) + 1
|
|
1838
|
+
&& isFlexProjectManagementWriteContext(valueArg.writeContext);
|
|
1839
|
+
}
|
|
1840
|
+
if (methodArg === 'project-management.tombstone') {
|
|
1841
|
+
return hasFlexExactKeys(valueArg, [
|
|
1842
|
+
'storageKey',
|
|
1843
|
+
'sessionId',
|
|
1844
|
+
'tombstone',
|
|
1845
|
+
'expectedRevision',
|
|
1846
|
+
])
|
|
1847
|
+
&& isIdentifier(valueArg.storageKey)
|
|
1848
|
+
&& isIdentifier(valueArg.sessionId)
|
|
1849
|
+
&& isFlexProjectManagementTombstone(valueArg.tombstone)
|
|
1850
|
+
&& isNonNegativeInteger(valueArg.expectedRevision)
|
|
1851
|
+
&& valueArg.tombstone.revision === Number(valueArg.expectedRevision) + 1;
|
|
1852
|
+
}
|
|
1853
|
+
if (methodArg === 'project-management.purge') {
|
|
1854
|
+
return hasFlexExactKeys(valueArg, ['storageKey']) && isIdentifier(valueArg.storageKey);
|
|
1855
|
+
}
|
|
1856
|
+
if (methodArg === 'browser.channel.close') {
|
|
1857
|
+
return hasFlexExactKeys(valueArg, ['channelId']) && isChannelId(valueArg.channelId);
|
|
1858
|
+
}
|
|
1859
|
+
const common = hasFlexExactKeys(
|
|
1860
|
+
valueArg,
|
|
1861
|
+
methodArg === 'browser.resources.resolve'
|
|
1862
|
+
? ['scopeId', 'sessionId', 'runId']
|
|
1863
|
+
: ['scopeId', 'sessionId', 'runId', 'resourceId', 'attachmentRevision', 'channelId'],
|
|
1864
|
+
)
|
|
1865
|
+
&& isBoundedString(valueArg.scopeId, 128)
|
|
1866
|
+
&& identifierPattern.test(String(valueArg.scopeId))
|
|
1867
|
+
&& isBoundedString(valueArg.sessionId, 128)
|
|
1868
|
+
&& identifierPattern.test(String(valueArg.sessionId))
|
|
1869
|
+
&& isBoundedString(valueArg.runId, 512)
|
|
1870
|
+
&& identifierPattern.test(String(valueArg.runId));
|
|
1871
|
+
if (!common || methodArg === 'browser.resources.resolve') return common;
|
|
1872
|
+
return isBoundedString(valueArg.resourceId, 256)
|
|
1873
|
+
&& Buffer.byteLength(String(valueArg.resourceId), 'utf8') >= 16
|
|
1874
|
+
&& identifierPattern.test(String(valueArg.resourceId))
|
|
1875
|
+
&& isNonNegativeInteger(valueArg.attachmentRevision)
|
|
1876
|
+
&& isChannelId(valueArg.channelId);
|
|
1877
|
+
};
|
|
1878
|
+
|
|
1879
|
+
const isGitWorktreeDescriptor = (
|
|
1880
|
+
valueArg: unknown,
|
|
1881
|
+
): valueArg is IFlexGitWorktreeDescriptor => isFlexPlainObject(valueArg)
|
|
1882
|
+
&& hasFlexExactKeys(valueArg, [
|
|
1883
|
+
'worktreeId', 'projectId', 'sessionId', 'path', 'repositoryId', 'createdAt',
|
|
1884
|
+
])
|
|
1885
|
+
&& isIdentifier(valueArg.worktreeId)
|
|
1886
|
+
&& typeof valueArg.projectId === 'string'
|
|
1887
|
+
&& projectIdPattern.test(valueArg.projectId)
|
|
1888
|
+
&& isIdentifier(valueArg.sessionId)
|
|
1889
|
+
&& isBoundedString(valueArg.path, 4096)
|
|
1890
|
+
&& !valueArg.path.includes('\0')
|
|
1891
|
+
&& plugins.path.isAbsolute(valueArg.path)
|
|
1892
|
+
&& plugins.path.normalize(valueArg.path) === valueArg.path
|
|
1893
|
+
&& isIdentifier(valueArg.repositoryId)
|
|
1894
|
+
&& isIsoDate(valueArg.createdAt);
|
|
1895
|
+
|
|
1896
|
+
const isFlexBrowserResourceDescriptor = (
|
|
1897
|
+
valueArg: unknown,
|
|
1898
|
+
): valueArg is IFlexBrowserResourceDescriptor => isFlexPlainObject(valueArg)
|
|
1899
|
+
&& hasFlexExactKeys(valueArg, ['resourceId', 'attachmentRevision'])
|
|
1900
|
+
&& isBoundedString(valueArg.resourceId, 256)
|
|
1901
|
+
&& Buffer.byteLength(valueArg.resourceId, 'utf8') >= 16
|
|
1902
|
+
&& identifierPattern.test(valueArg.resourceId)
|
|
1903
|
+
&& isNonNegativeInteger(valueArg.attachmentRevision);
|
|
1904
|
+
|
|
1905
|
+
const isFlexBrowserChannelBinding = (
|
|
1906
|
+
valueArg: unknown,
|
|
1907
|
+
): valueArg is IFlexBrowserChannelBinding => isFlexPlainObject(valueArg)
|
|
1908
|
+
&& hasFlexExactKeys(valueArg, [
|
|
1909
|
+
'projectId',
|
|
1910
|
+
'browserResourceId',
|
|
1911
|
+
'attachmentAuthorityId',
|
|
1912
|
+
'attachmentRevision',
|
|
1913
|
+
'actorId',
|
|
1914
|
+
'peerId',
|
|
1915
|
+
'role',
|
|
1916
|
+
'source',
|
|
1917
|
+
'scopeId',
|
|
1918
|
+
'channelId',
|
|
1919
|
+
'runId',
|
|
1920
|
+
'sessionId',
|
|
1921
|
+
])
|
|
1922
|
+
&& isBoundedString(valueArg.projectId, 128)
|
|
1923
|
+
&& isBoundedString(valueArg.browserResourceId, 256)
|
|
1924
|
+
&& Buffer.byteLength(valueArg.browserResourceId, 'utf8') >= 16
|
|
1925
|
+
&& isBoundedString(valueArg.attachmentAuthorityId, 256)
|
|
1926
|
+
&& isNonNegativeInteger(valueArg.attachmentRevision)
|
|
1927
|
+
&& isBoundedString(valueArg.actorId, 256)
|
|
1928
|
+
&& isBoundedString(valueArg.peerId, 256)
|
|
1929
|
+
&& valueArg.role === 'agent'
|
|
1930
|
+
&& valueArg.source === 'flex'
|
|
1931
|
+
&& isBoundedString(valueArg.scopeId, 128)
|
|
1932
|
+
&& isChannelId(valueArg.channelId)
|
|
1933
|
+
&& isBoundedString(valueArg.runId, 512)
|
|
1934
|
+
&& identifierPattern.test(valueArg.runId)
|
|
1935
|
+
&& isFlexPlainObject(valueArg.sessionId)
|
|
1936
|
+
&& hasFlexExactKeys(valueArg.sessionId, ['harnessId', 'nativeId'])
|
|
1937
|
+
&& valueArg.sessionId.harnessId === 'flex'
|
|
1938
|
+
&& isBoundedString(valueArg.sessionId.nativeId, 128);
|
|
1939
|
+
|
|
1940
|
+
export const isFlexHostResponseResult = <TMethod extends TFlexHostRequestMethod>(
|
|
1941
|
+
methodArg: TMethod,
|
|
1942
|
+
valueArg: unknown,
|
|
1943
|
+
): valueArg is TFlexHostResponse<TMethod> => {
|
|
1944
|
+
if (!isFlexPlainObject(valueArg)) return false;
|
|
1945
|
+
if (methodArg === 'reversion.prepare') {
|
|
1946
|
+
return hasFlexExactKeys(valueArg, ['prepared']) && valueArg.prepared === true;
|
|
1947
|
+
}
|
|
1948
|
+
if (methodArg === 'reversion.inspect-capture') {
|
|
1949
|
+
if (valueArg.status === 'finalized') {
|
|
1950
|
+
return hasFlexExactKeys(valueArg, ['status', 'outcome'])
|
|
1951
|
+
&& isReversionOutcome(valueArg.outcome);
|
|
1952
|
+
}
|
|
1953
|
+
return hasFlexExactKeys(valueArg, ['status'])
|
|
1954
|
+
&& ['missing', 'prepared', 'unknown'].includes(String(valueArg.status));
|
|
1955
|
+
}
|
|
1956
|
+
if (methodArg === 'reversion.finalize') return isReversionOutcome(valueArg);
|
|
1957
|
+
if (methodArg === 'reversion.inspect-apply') {
|
|
1958
|
+
return hasFlexExactKeys(valueArg, ['status'])
|
|
1959
|
+
&& ['not-applied', 'applied', 'unknown'].includes(String(valueArg.status));
|
|
1960
|
+
}
|
|
1961
|
+
if (methodArg === 'reversion.apply') {
|
|
1962
|
+
return hasFlexExactKeys(valueArg, ['applied']) && valueArg.applied === true;
|
|
1963
|
+
}
|
|
1964
|
+
if (methodArg === 'reversion.release') {
|
|
1965
|
+
return hasFlexExactKeys(valueArg, ['released']) && valueArg.released === true;
|
|
1966
|
+
}
|
|
1967
|
+
if (methodArg === 'git.worktree.create') {
|
|
1968
|
+
return hasFlexExactKeys(valueArg, ['worktree'])
|
|
1969
|
+
&& isGitWorktreeDescriptor(valueArg.worktree);
|
|
1970
|
+
}
|
|
1971
|
+
if (methodArg === 'git.worktree.list') {
|
|
1972
|
+
return hasFlexExactKeys(valueArg, ['worktrees'])
|
|
1973
|
+
&& Array.isArray(valueArg.worktrees)
|
|
1974
|
+
&& valueArg.worktrees.length <= 128
|
|
1975
|
+
&& valueArg.worktrees.every(isGitWorktreeDescriptor);
|
|
1976
|
+
}
|
|
1977
|
+
if (methodArg === 'git.worktree.remove') {
|
|
1978
|
+
return hasFlexExactKeys(valueArg, ['removed']) && valueArg.removed === true;
|
|
1979
|
+
}
|
|
1980
|
+
if (methodArg === 'crossharness.chats.list') {
|
|
1981
|
+
return hasFlexExactKeys(valueArg, ['chats'])
|
|
1982
|
+
&& Array.isArray(valueArg.chats)
|
|
1983
|
+
&& valueArg.chats.length <= 50
|
|
1984
|
+
&& valueArg.chats.every(isFlexIntelligenceChatSummary)
|
|
1985
|
+
&& Buffer.byteLength(JSON.stringify(valueArg), 'utf8') <= 256 * 1024;
|
|
1986
|
+
}
|
|
1987
|
+
if (methodArg === 'crossharness.chat.read') {
|
|
1988
|
+
return hasFlexExactKeys(valueArg, ['transcript'])
|
|
1989
|
+
&& isFlexIntelligenceChatTranscript(valueArg.transcript);
|
|
1990
|
+
}
|
|
1991
|
+
if (methodArg === 'project-management.load') {
|
|
1992
|
+
return hasFlexExactKeys(valueArg, ['record'])
|
|
1993
|
+
&& (valueArg.record === null || isFlexProjectManagementRecord(valueArg.record));
|
|
1994
|
+
}
|
|
1995
|
+
if (
|
|
1996
|
+
methodArg === 'project-management.save'
|
|
1997
|
+
|| methodArg === 'project-management.tombstone'
|
|
1998
|
+
) return isFlexProjectManagementCommitResult(valueArg);
|
|
1999
|
+
if (methodArg === 'project-management.purge') {
|
|
2000
|
+
return hasFlexExactKeys(valueArg, ['purged']) && valueArg.purged === true;
|
|
2001
|
+
}
|
|
2002
|
+
if (methodArg === 'browser.resources.resolve') {
|
|
2003
|
+
return hasFlexExactKeys(valueArg, ['resources'])
|
|
2004
|
+
&& Array.isArray(valueArg.resources)
|
|
2005
|
+
&& valueArg.resources.length <= flexIpcMaximumBrowserChannels
|
|
2006
|
+
&& valueArg.resources.every(isFlexBrowserResourceDescriptor)
|
|
2007
|
+
&& new Set(valueArg.resources.map((entry) => entry.resourceId)).size
|
|
2008
|
+
=== valueArg.resources.length;
|
|
2009
|
+
}
|
|
2010
|
+
if (methodArg === 'browser.channel.open') {
|
|
2011
|
+
return hasFlexExactKeys(valueArg, ['binding', 'capabilityToken'])
|
|
2012
|
+
&& isFlexBrowserChannelBinding(valueArg.binding)
|
|
2013
|
+
&& isBoundedString(valueArg.capabilityToken, 512)
|
|
2014
|
+
&& Buffer.byteLength(valueArg.capabilityToken, 'utf8') >= 16;
|
|
2015
|
+
}
|
|
2016
|
+
return hasFlexExactKeys(valueArg, ['closed']) && valueArg.closed === true;
|
|
2017
|
+
};
|
|
2018
|
+
|
|
2019
|
+
const isPublicProviderModel = (valueArg: unknown): valueArg is IFlexPublicProviderModel => {
|
|
2020
|
+
if (
|
|
2021
|
+
!isFlexPlainObject(valueArg)
|
|
2022
|
+
|| !hasFlexExactKeys(
|
|
2023
|
+
valueArg,
|
|
2024
|
+
[
|
|
2025
|
+
'providerId', 'modelId', 'displayName', 'description', 'hidden',
|
|
2026
|
+
'reasoningEfforts', 'defaultReasoningEffort', 'inputModalities',
|
|
2027
|
+
'supportsPersonality', 'serviceTiers', 'isDefault',
|
|
2028
|
+
],
|
|
2029
|
+
['defaultServiceTier'],
|
|
2030
|
+
)
|
|
2031
|
+
|| valueArg.providerId !== 'openai'
|
|
2032
|
+
|| !isBoundedString(valueArg.modelId, 256)
|
|
2033
|
+
|| !isBoundedString(valueArg.displayName, 512)
|
|
2034
|
+
|| !isBoundedString(valueArg.description, 2048, true)
|
|
2035
|
+
|| typeof valueArg.hidden !== 'boolean'
|
|
2036
|
+
|| !Array.isArray(valueArg.reasoningEfforts)
|
|
2037
|
+
|| valueArg.reasoningEfforts.length > 16
|
|
2038
|
+
|| !valueArg.reasoningEfforts.every((entry) => isFlexPlainObject(entry)
|
|
2039
|
+
&& hasFlexExactKeys(entry, ['effort', 'description'])
|
|
2040
|
+
&& isBoundedString(entry.effort, 128)
|
|
2041
|
+
&& isBoundedString(entry.description, 1024, true))
|
|
2042
|
+
|| !isBoundedString(valueArg.defaultReasoningEffort, 128)
|
|
2043
|
+
|| !Array.isArray(valueArg.inputModalities)
|
|
2044
|
+
|| valueArg.inputModalities.length > 3
|
|
2045
|
+
|| !valueArg.inputModalities.every((entry) => ['text', 'image', 'audio'].includes(String(entry)))
|
|
2046
|
+
|| typeof valueArg.supportsPersonality !== 'boolean'
|
|
2047
|
+
|| !Array.isArray(valueArg.serviceTiers)
|
|
2048
|
+
|| valueArg.serviceTiers.length > 16
|
|
2049
|
+
|| !valueArg.serviceTiers.every((entry) => isFlexPlainObject(entry)
|
|
2050
|
+
&& hasFlexExactKeys(entry, ['id', 'name', 'description'])
|
|
2051
|
+
&& isBoundedString(entry.id, 128)
|
|
2052
|
+
&& isBoundedString(entry.name, 256)
|
|
2053
|
+
&& isBoundedString(entry.description, 1024, true))
|
|
2054
|
+
|| (valueArg.defaultServiceTier !== undefined
|
|
2055
|
+
&& !isBoundedString(valueArg.defaultServiceTier, 128))
|
|
2056
|
+
|| typeof valueArg.isDefault !== 'boolean'
|
|
2057
|
+
) return false;
|
|
2058
|
+
return true;
|
|
2059
|
+
};
|
|
2060
|
+
|
|
2061
|
+
const isRateLimitWindow = (valueArg: unknown): valueArg is IFlexPublicProviderRateLimitWindow => (
|
|
2062
|
+
isFlexPlainObject(valueArg)
|
|
2063
|
+
&& hasFlexExactKeys(valueArg, [
|
|
2064
|
+
'usedPercent', 'limitWindowSeconds', 'resetAfterSeconds', 'resetsAt',
|
|
2065
|
+
])
|
|
2066
|
+
&& isNonNegativeInteger(valueArg.usedPercent)
|
|
2067
|
+
&& valueArg.usedPercent <= 100
|
|
2068
|
+
&& isNonNegativeInteger(valueArg.limitWindowSeconds)
|
|
2069
|
+
&& valueArg.limitWindowSeconds >= 1
|
|
2070
|
+
&& isNonNegativeInteger(valueArg.resetAfterSeconds)
|
|
2071
|
+
&& isNonNegativeInteger(valueArg.resetsAt)
|
|
2072
|
+
);
|
|
2073
|
+
|
|
2074
|
+
const isRateLimitDetails = (valueArg: unknown): valueArg is IFlexPublicProviderRateLimitDetails => (
|
|
2075
|
+
isFlexPlainObject(valueArg)
|
|
2076
|
+
&& hasFlexExactKeys(valueArg, ['allowed', 'limitReached'], ['primaryWindow', 'secondaryWindow'])
|
|
2077
|
+
&& typeof valueArg.allowed === 'boolean'
|
|
2078
|
+
&& typeof valueArg.limitReached === 'boolean'
|
|
2079
|
+
&& (valueArg.primaryWindow === undefined || isRateLimitWindow(valueArg.primaryWindow))
|
|
2080
|
+
&& (valueArg.secondaryWindow === undefined || isRateLimitWindow(valueArg.secondaryWindow))
|
|
2081
|
+
);
|
|
2082
|
+
|
|
2083
|
+
const isAccountRateLimits = (
|
|
2084
|
+
valueArg: unknown,
|
|
2085
|
+
): valueArg is IFlexPublicProviderAccountRateLimits => isFlexPlainObject(valueArg)
|
|
2086
|
+
&& hasFlexExactKeys(
|
|
2087
|
+
valueArg,
|
|
2088
|
+
['providerId', 'plan', 'observedAt', 'additionalRateLimits'],
|
|
2089
|
+
['rateLimit', 'rateLimitReachedType', 'rateLimitResetCreditsAvailableCount'],
|
|
2090
|
+
)
|
|
2091
|
+
&& valueArg.providerId === 'openai'
|
|
2092
|
+
&& isBoundedString(valueArg.plan, 256)
|
|
2093
|
+
&& isIsoDate(valueArg.observedAt)
|
|
2094
|
+
&& (valueArg.rateLimit === undefined || isRateLimitDetails(valueArg.rateLimit))
|
|
2095
|
+
&& Array.isArray(valueArg.additionalRateLimits)
|
|
2096
|
+
&& valueArg.additionalRateLimits.length <= 64
|
|
2097
|
+
&& valueArg.additionalRateLimits.every((entry) => isFlexPlainObject(entry)
|
|
2098
|
+
&& hasFlexExactKeys(entry, ['limitName', 'meteredFeature'], ['rateLimit'])
|
|
2099
|
+
&& isBoundedString(entry.limitName, 256)
|
|
2100
|
+
&& isBoundedString(entry.meteredFeature, 256)
|
|
2101
|
+
&& (entry.rateLimit === undefined || isRateLimitDetails(entry.rateLimit)))
|
|
2102
|
+
&& (valueArg.rateLimitReachedType === undefined
|
|
2103
|
+
|| isBoundedString(valueArg.rateLimitReachedType, 256))
|
|
2104
|
+
&& (valueArg.rateLimitResetCreditsAvailableCount === undefined
|
|
2105
|
+
|| isNonNegativeInteger(valueArg.rateLimitResetCreditsAvailableCount));
|
|
2106
|
+
|
|
2107
|
+
export const isFlexResponseResult = <TMethod extends TFlexRequestMethod>(
|
|
2108
|
+
methodArg: TMethod,
|
|
2109
|
+
valueArg: unknown,
|
|
2110
|
+
): valueArg is TFlexResponse<TMethod> => {
|
|
2111
|
+
switch (methodArg) {
|
|
2112
|
+
case 'service.status':
|
|
2113
|
+
return isFlexServiceStatus(valueArg);
|
|
2114
|
+
case 'project.replaceAll':
|
|
2115
|
+
return isFlexPlainObject(valueArg)
|
|
2116
|
+
&& hasFlexExactKeys(valueArg, ['replaced', 'retired'])
|
|
2117
|
+
&& valueArg.replaced === true
|
|
2118
|
+
&& isNonNegativeInteger(valueArg.retired)
|
|
2119
|
+
&& valueArg.retired <= controllerProjectLimit;
|
|
2120
|
+
case 'project.register':
|
|
2121
|
+
return isFlexPlainObject(valueArg)
|
|
2122
|
+
&& hasFlexExactKeys(valueArg, ['registered'])
|
|
2123
|
+
&& valueArg.registered === true;
|
|
2124
|
+
case 'project.remove':
|
|
2125
|
+
return isFlexPlainObject(valueArg)
|
|
2126
|
+
&& hasFlexExactKeys(valueArg, ['removed'])
|
|
2127
|
+
&& valueArg.removed === true;
|
|
2128
|
+
case 'intelligence.start':
|
|
2129
|
+
return isFlexPlainObject(valueArg)
|
|
2130
|
+
&& hasFlexExactKeys(valueArg, ['jobId', 'workerSessionId'])
|
|
2131
|
+
&& isQualifiedId(valueArg.jobId)
|
|
2132
|
+
&& isIdentifier(valueArg.workerSessionId);
|
|
2133
|
+
case 'intelligence.get':
|
|
2134
|
+
if (
|
|
2135
|
+
!isFlexPlainObject(valueArg)
|
|
2136
|
+
|| !hasFlexExactKeys(
|
|
2137
|
+
valueArg,
|
|
2138
|
+
['status', 'workerSessionId'],
|
|
2139
|
+
['result', 'error'],
|
|
2140
|
+
)
|
|
2141
|
+
|| !['running', 'completed', 'failed', 'cancelled'].includes(String(valueArg.status))
|
|
2142
|
+
|| !isIdentifier(valueArg.workerSessionId)
|
|
2143
|
+
|| (valueArg.result !== undefined && !isFlexIntelligenceResult(valueArg.result))
|
|
2144
|
+
|| (valueArg.error !== undefined && valueArg.error !== 'Session Intelligence failed.')
|
|
2145
|
+
) return false;
|
|
2146
|
+
return (valueArg.status === 'completed' && valueArg.result !== undefined
|
|
2147
|
+
&& valueArg.error === undefined)
|
|
2148
|
+
|| (valueArg.status === 'failed' && valueArg.result === undefined
|
|
2149
|
+
&& valueArg.error === 'Session Intelligence failed.')
|
|
2150
|
+
|| (valueArg.status === 'cancelled' && valueArg.result === undefined
|
|
2151
|
+
&& valueArg.error === undefined)
|
|
2152
|
+
|| (valueArg.status === 'running' && valueArg.result === undefined
|
|
2153
|
+
&& valueArg.error === undefined);
|
|
2154
|
+
case 'intelligence.cancel':
|
|
2155
|
+
return isFlexPlainObject(valueArg)
|
|
2156
|
+
&& hasFlexExactKeys(valueArg, ['cancelled'])
|
|
2157
|
+
&& typeof valueArg.cancelled === 'boolean';
|
|
2158
|
+
case 'session.list':
|
|
2159
|
+
return isFlexPlainObject(valueArg)
|
|
2160
|
+
&& hasFlexExactKeys(valueArg, ['sessions', 'total', 'truncated'])
|
|
2161
|
+
&& Array.isArray(valueArg.sessions) && valueArg.sessions.length <= 50
|
|
2162
|
+
&& valueArg.sessions.every(isFlexPublicSession)
|
|
2163
|
+
&& isNonNegativeInteger(valueArg.total)
|
|
2164
|
+
&& typeof valueArg.truncated === 'boolean';
|
|
2165
|
+
case 'session.create':
|
|
2166
|
+
case 'session.get':
|
|
2167
|
+
case 'session.update':
|
|
2168
|
+
return isFlexPublicSession(valueArg);
|
|
2169
|
+
case 'session.delete':
|
|
2170
|
+
return isFlexPlainObject(valueArg) && hasFlexExactKeys(valueArg, ['deleted'])
|
|
2171
|
+
&& valueArg.deleted === true;
|
|
2172
|
+
case 'session.compact':
|
|
2173
|
+
return isFlexPlainObject(valueArg) && hasFlexExactKeys(valueArg, ['compacted'])
|
|
2174
|
+
&& valueArg.compacted === true;
|
|
2175
|
+
case 'session.reversion.info':
|
|
2176
|
+
return isSessionReversionInfo(valueArg);
|
|
2177
|
+
case 'slash.list':
|
|
2178
|
+
return isFlexPlainObject(valueArg)
|
|
2179
|
+
&& hasFlexExactKeys(valueArg, ['commands'])
|
|
2180
|
+
&& Array.isArray(valueArg.commands)
|
|
2181
|
+
&& valueArg.commands.length <= 132
|
|
2182
|
+
&& valueArg.commands.every(isSlashDescriptor);
|
|
2183
|
+
case 'slash.execute':
|
|
2184
|
+
return isSlashExecutionResult(valueArg);
|
|
2185
|
+
case 'message.page':
|
|
2186
|
+
return isFlexPlainObject(valueArg)
|
|
2187
|
+
&& hasFlexExactKeys(valueArg, ['messages'], ['nextCursor'])
|
|
2188
|
+
&& Array.isArray(valueArg.messages) && valueArg.messages.length <= 50
|
|
2189
|
+
&& valueArg.messages.every(isFlexPublicMessage)
|
|
2190
|
+
&& (valueArg.nextCursor === undefined || isBoundedString(valueArg.nextCursor, 4096));
|
|
2191
|
+
case 'message.get':
|
|
2192
|
+
return isFlexPublicMessage(valueArg);
|
|
2193
|
+
case 'prompt.start':
|
|
2194
|
+
return isFlexPlainObject(valueArg)
|
|
2195
|
+
&& hasFlexExactKeys(valueArg, ['queueId', 'runId'])
|
|
2196
|
+
&& isIdentifier(valueArg.queueId)
|
|
2197
|
+
&& isIdentifier(valueArg.runId);
|
|
2198
|
+
case 'prompt.acknowledge':
|
|
2199
|
+
return isFlexPlainObject(valueArg)
|
|
2200
|
+
&& hasFlexExactKeys(valueArg, ['acknowledged'])
|
|
2201
|
+
&& valueArg.acknowledged === true;
|
|
2202
|
+
case 'prompt.get':
|
|
2203
|
+
return isFlexPlainObject(valueArg)
|
|
2204
|
+
&& hasFlexExactKeys(valueArg, ['entry'])
|
|
2205
|
+
&& isPromptQueueEntry(valueArg.entry);
|
|
2206
|
+
case 'prompt.list':
|
|
2207
|
+
return isFlexPlainObject(valueArg)
|
|
2208
|
+
&& hasFlexExactKeys(valueArg, ['entries'])
|
|
2209
|
+
&& Array.isArray(valueArg.entries)
|
|
2210
|
+
&& valueArg.entries.length <= flexIpcMaximumPromptQueueEntries
|
|
2211
|
+
&& valueArg.entries.every(isPromptQueueEntry);
|
|
2212
|
+
case 'prompt.cancel':
|
|
2213
|
+
return isFlexPlainObject(valueArg) && hasFlexExactKeys(valueArg, ['accepted'])
|
|
2214
|
+
&& typeof valueArg.accepted === 'boolean';
|
|
2215
|
+
case 'permission.list':
|
|
2216
|
+
return isFlexPlainObject(valueArg) && hasFlexExactKeys(valueArg, ['permissions'])
|
|
2217
|
+
&& Array.isArray(valueArg.permissions) && valueArg.permissions.length <= 64
|
|
2218
|
+
&& valueArg.permissions.every(isPermissionRequest);
|
|
2219
|
+
case 'permission.respond':
|
|
2220
|
+
case 'provider.connection.logout.prepare':
|
|
2221
|
+
case 'provider.connection.logout':
|
|
2222
|
+
return isFlexPlainObject(valueArg) && hasFlexExactKeys(valueArg, ['accepted'])
|
|
2223
|
+
&& valueArg.accepted === true;
|
|
2224
|
+
case 'provider.connection.ratelimits.get':
|
|
2225
|
+
return isFlexPlainObject(valueArg)
|
|
2226
|
+
&& hasFlexExactKeys(valueArg, ['rateLimits'])
|
|
2227
|
+
&& isAccountRateLimits(valueArg.rateLimits);
|
|
2228
|
+
case 'provider.list':
|
|
2229
|
+
return isFlexPlainObject(valueArg) && hasFlexExactKeys(valueArg, ['providers'])
|
|
2230
|
+
&& Array.isArray(valueArg.providers) && valueArg.providers.length === 1
|
|
2231
|
+
&& isFlexPlainObject(valueArg.providers[0])
|
|
2232
|
+
&& hasFlexExactKeys(valueArg.providers[0], ['providerId', 'displayName', 'loginFlow'])
|
|
2233
|
+
&& valueArg.providers[0].providerId === 'openai'
|
|
2234
|
+
&& isBoundedString(valueArg.providers[0].displayName, 256)
|
|
2235
|
+
&& valueArg.providers[0].loginFlow === 'device';
|
|
2236
|
+
case 'provider.login.begin':
|
|
2237
|
+
return isFlexPlainObject(valueArg)
|
|
2238
|
+
&& hasFlexExactKeys(
|
|
2239
|
+
valueArg,
|
|
2240
|
+
['loginId', 'verificationUrl', 'userCode', 'status'],
|
|
2241
|
+
)
|
|
2242
|
+
&& isQualifiedId(valueArg.loginId)
|
|
2243
|
+
&& isBoundedString(valueArg.verificationUrl, 4096)
|
|
2244
|
+
&& isBoundedString(valueArg.userCode, 256)
|
|
2245
|
+
&& valueArg.status === 'pending';
|
|
2246
|
+
case 'provider.login.status':
|
|
2247
|
+
return isFlexPublicProviderConnection(valueArg);
|
|
2248
|
+
case 'provider.login.cancel':
|
|
2249
|
+
return isFlexPlainObject(valueArg) && hasFlexExactKeys(valueArg, ['canceled'])
|
|
2250
|
+
&& typeof valueArg.canceled === 'boolean';
|
|
2251
|
+
case 'provider.connection.list':
|
|
2252
|
+
return isFlexPlainObject(valueArg) && hasFlexExactKeys(valueArg, ['connections'])
|
|
2253
|
+
&& Array.isArray(valueArg.connections) && valueArg.connections.length <= 512
|
|
2254
|
+
&& valueArg.connections.every(isFlexPublicProviderConnection);
|
|
2255
|
+
case 'provider.connection.opencode-auth.get':
|
|
2256
|
+
return isFlexPlainObject(valueArg) && hasFlexExactKeys(valueArg, ['auth'])
|
|
2257
|
+
&& isOpenCodeOAuthAuth(valueArg.auth);
|
|
2258
|
+
case 'provider.models.refresh':
|
|
2259
|
+
return isFlexPlainObject(valueArg) && hasFlexExactKeys(valueArg, ['jobId'])
|
|
2260
|
+
&& isQualifiedId(valueArg.jobId);
|
|
2261
|
+
case 'model.choice.get':
|
|
2262
|
+
return isFlexPlainObject(valueArg) && hasFlexExactKeys(valueArg, ['choice'])
|
|
2263
|
+
&& (valueArg.choice === null || isModelChoice(valueArg.choice));
|
|
2264
|
+
case 'model.choice.set':
|
|
2265
|
+
return isFlexPlainObject(valueArg) && hasFlexExactKeys(valueArg, ['choice'])
|
|
2266
|
+
&& isModelChoice(valueArg.choice);
|
|
2267
|
+
case 'model.choice.validate':
|
|
2268
|
+
return isFlexPlainObject(valueArg)
|
|
2269
|
+
&& hasFlexExactKeys(valueArg, ['valid'])
|
|
2270
|
+
&& valueArg.valid === true;
|
|
2271
|
+
default:
|
|
2272
|
+
return false;
|
|
2273
|
+
}
|
|
2274
|
+
};
|
|
2275
|
+
|
|
2276
|
+
export const isFlexServiceError = (valueArg: unknown): valueArg is IFlexServiceError =>
|
|
2277
|
+
isFlexPlainObject(valueArg)
|
|
2278
|
+
&& hasFlexExactKeys(valueArg, ['name', 'code', 'message'])
|
|
2279
|
+
&& valueArg.name === 'FlexServiceError'
|
|
2280
|
+
&& typeof valueArg.code === 'string'
|
|
2281
|
+
&& serviceErrorCodes.has(valueArg.code)
|
|
2282
|
+
&& valueArg.message === flexServiceErrorMessages[valueArg.code as TFlexServiceErrorCode];
|
|
2283
|
+
|
|
2284
|
+
const isFlexChildEvent = (valueArg: unknown): valueArg is TFlexChildEvent => {
|
|
2285
|
+
if (!isFlexPlainObject(valueArg)) return false;
|
|
2286
|
+
switch (valueArg.type) {
|
|
2287
|
+
case 'session.history.changed':
|
|
2288
|
+
return hasFlexExactKeys(
|
|
2289
|
+
valueArg,
|
|
2290
|
+
['type', 'scopeId', 'sessionId', 'direction'],
|
|
2291
|
+
['runId'],
|
|
2292
|
+
)
|
|
2293
|
+
&& isIdentifier(valueArg.scopeId)
|
|
2294
|
+
&& isIdentifier(valueArg.sessionId)
|
|
2295
|
+
&& ['undo', 'redo', 'branch'].includes(String(valueArg.direction))
|
|
2296
|
+
&& (valueArg.runId === undefined || isIdentifier(valueArg.runId))
|
|
2297
|
+
&& (valueArg.direction === 'branch'
|
|
2298
|
+
? valueArg.runId === undefined
|
|
2299
|
+
: valueArg.runId !== undefined);
|
|
2300
|
+
case 'sessions.changed':
|
|
2301
|
+
return hasFlexExactKeys(valueArg, ['type', 'coalesced'], ['scopeId', 'sessionId'])
|
|
2302
|
+
&& typeof valueArg.coalesced === 'boolean'
|
|
2303
|
+
&& (valueArg.scopeId === undefined || isIdentifier(valueArg.scopeId))
|
|
2304
|
+
&& (valueArg.sessionId === undefined || isIdentifier(valueArg.sessionId))
|
|
2305
|
+
&& (valueArg.sessionId === undefined || valueArg.scopeId !== undefined);
|
|
2306
|
+
case 'permissions.changed':
|
|
2307
|
+
return hasFlexExactKeys(valueArg, ['type', 'scopeId', 'sessionId'])
|
|
2308
|
+
&& isIdentifier(valueArg.scopeId) && isIdentifier(valueArg.sessionId);
|
|
2309
|
+
case 'tool.updated':
|
|
2310
|
+
return hasFlexExactKeys(valueArg, [
|
|
2311
|
+
'type', 'scopeId', 'sessionId', 'runId', 'messageId',
|
|
2312
|
+
'messageIndex', 'partIndex', 'sequence', 'timestamp', 'part',
|
|
2313
|
+
])
|
|
2314
|
+
&& isIdentifier(valueArg.scopeId)
|
|
2315
|
+
&& isIdentifier(valueArg.sessionId)
|
|
2316
|
+
&& isIdentifier(valueArg.runId)
|
|
2317
|
+
&& isIdentifier(valueArg.messageId)
|
|
2318
|
+
&& isNonNegativeInteger(valueArg.messageIndex)
|
|
2319
|
+
&& isNonNegativeInteger(valueArg.partIndex)
|
|
2320
|
+
&& isNonNegativeInteger(valueArg.sequence)
|
|
2321
|
+
&& valueArg.sequence >= 1
|
|
2322
|
+
&& isIsoDate(valueArg.timestamp)
|
|
2323
|
+
&& isFlexPlainObject(valueArg.part)
|
|
2324
|
+
&& valueArg.part.type === 'tool'
|
|
2325
|
+
&& isMessagePart(valueArg.part);
|
|
2326
|
+
case 'reasoning.updated':
|
|
2327
|
+
return hasFlexExactKeys(valueArg, [
|
|
2328
|
+
'type', 'scopeId', 'sessionId', 'runId', 'messageId',
|
|
2329
|
+
'messageIndex', 'partIndex', 'sequence', 'timestamp', 'part',
|
|
2330
|
+
])
|
|
2331
|
+
&& isIdentifier(valueArg.scopeId)
|
|
2332
|
+
&& isIdentifier(valueArg.sessionId)
|
|
2333
|
+
&& isIdentifier(valueArg.runId)
|
|
2334
|
+
&& isIdentifier(valueArg.messageId)
|
|
2335
|
+
&& isNonNegativeInteger(valueArg.messageIndex)
|
|
2336
|
+
&& isNonNegativeInteger(valueArg.partIndex)
|
|
2337
|
+
&& isNonNegativeInteger(valueArg.sequence)
|
|
2338
|
+
&& valueArg.sequence >= 1
|
|
2339
|
+
&& isIsoDate(valueArg.timestamp)
|
|
2340
|
+
&& isFlexPlainObject(valueArg.part)
|
|
2341
|
+
&& valueArg.part.type === 'reasoning'
|
|
2342
|
+
&& isMessagePart(valueArg.part);
|
|
2343
|
+
case 'text.updated':
|
|
2344
|
+
return hasFlexExactKeys(valueArg, [
|
|
2345
|
+
'type', 'scopeId', 'sessionId', 'runId', 'messageId',
|
|
2346
|
+
'messageIndex', 'partIndex', 'sequence', 'timestamp', 'status', 'part',
|
|
2347
|
+
])
|
|
2348
|
+
&& isIdentifier(valueArg.scopeId)
|
|
2349
|
+
&& isIdentifier(valueArg.sessionId)
|
|
2350
|
+
&& isIdentifier(valueArg.runId)
|
|
2351
|
+
&& isIdentifier(valueArg.messageId)
|
|
2352
|
+
&& isNonNegativeInteger(valueArg.messageIndex)
|
|
2353
|
+
&& isNonNegativeInteger(valueArg.partIndex)
|
|
2354
|
+
&& isNonNegativeInteger(valueArg.sequence)
|
|
2355
|
+
&& valueArg.sequence >= 1
|
|
2356
|
+
&& isIsoDate(valueArg.timestamp)
|
|
2357
|
+
&& ['running', 'completed'].includes(String(valueArg.status))
|
|
2358
|
+
&& isFlexPlainObject(valueArg.part)
|
|
2359
|
+
&& valueArg.part.type === 'text'
|
|
2360
|
+
&& isMessagePart(valueArg.part);
|
|
2361
|
+
case 'prompt.queued':
|
|
2362
|
+
case 'prompt.started':
|
|
2363
|
+
case 'prompt.running':
|
|
2364
|
+
case 'prompt.finished':
|
|
2365
|
+
return hasFlexExactKeys(
|
|
2366
|
+
valueArg,
|
|
2367
|
+
['type', 'scopeId', 'sessionId', 'queueId', 'entry'],
|
|
2368
|
+
['runId'],
|
|
2369
|
+
)
|
|
2370
|
+
&& isIdentifier(valueArg.scopeId)
|
|
2371
|
+
&& isIdentifier(valueArg.sessionId)
|
|
2372
|
+
&& isIdentifier(valueArg.queueId)
|
|
2373
|
+
&& (valueArg.runId === undefined || isIdentifier(valueArg.runId))
|
|
2374
|
+
&& isPromptQueueEntry(valueArg.entry)
|
|
2375
|
+
&& valueArg.entry.scopeId === valueArg.scopeId
|
|
2376
|
+
&& valueArg.entry.sessionId === valueArg.sessionId
|
|
2377
|
+
&& valueArg.entry.queueId === valueArg.queueId
|
|
2378
|
+
&& valueArg.entry.runId === valueArg.runId
|
|
2379
|
+
&& (
|
|
2380
|
+
(valueArg.type === 'prompt.queued'
|
|
2381
|
+
&& valueArg.runId === undefined
|
|
2382
|
+
&& valueArg.entry.status === 'queued')
|
|
2383
|
+
|| (valueArg.type === 'prompt.started'
|
|
2384
|
+
&& valueArg.runId !== undefined
|
|
2385
|
+
&& ['starting', 'scheduled'].includes(valueArg.entry.status))
|
|
2386
|
+
|| (valueArg.type === 'prompt.running'
|
|
2387
|
+
&& valueArg.runId !== undefined
|
|
2388
|
+
&& valueArg.entry.status === 'running')
|
|
2389
|
+
|| (valueArg.type === 'prompt.finished'
|
|
2390
|
+
&& valueArg.runId !== undefined
|
|
2391
|
+
&& ['completed', 'failed', 'cancelled'].includes(valueArg.entry.status))
|
|
2392
|
+
);
|
|
2393
|
+
case 'prompt.message-bound':
|
|
2394
|
+
return hasFlexExactKeys(valueArg, [
|
|
2395
|
+
'type', 'scopeId', 'sessionId', 'queueId', 'runId', 'messageId',
|
|
2396
|
+
])
|
|
2397
|
+
&& isIdentifier(valueArg.scopeId)
|
|
2398
|
+
&& isIdentifier(valueArg.sessionId)
|
|
2399
|
+
&& isIdentifier(valueArg.queueId)
|
|
2400
|
+
&& isIdentifier(valueArg.runId)
|
|
2401
|
+
&& isIdentifier(valueArg.messageId);
|
|
2402
|
+
case 'job.finished':
|
|
2403
|
+
return hasFlexExactKeys(
|
|
2404
|
+
valueArg,
|
|
2405
|
+
['type', 'jobId', 'kind', 'status'],
|
|
2406
|
+
['models'],
|
|
2407
|
+
)
|
|
2408
|
+
&& isQualifiedId(valueArg.jobId)
|
|
2409
|
+
&& valueArg.kind === 'provider.models'
|
|
2410
|
+
&& ['completed', 'failed', 'cancelled'].includes(String(valueArg.status))
|
|
2411
|
+
&& (valueArg.models === undefined || (
|
|
2412
|
+
valueArg.status === 'completed'
|
|
2413
|
+
&& Array.isArray(valueArg.models)
|
|
2414
|
+
&& valueArg.models.length <= 256
|
|
2415
|
+
&& valueArg.models.every(isPublicProviderModel)
|
|
2416
|
+
));
|
|
2417
|
+
case 'provider.connection.changed':
|
|
2418
|
+
return hasFlexExactKeys(valueArg, ['type', 'connection'])
|
|
2419
|
+
&& isFlexPublicProviderConnection(valueArg.connection);
|
|
2420
|
+
default:
|
|
2421
|
+
return false;
|
|
2422
|
+
}
|
|
2423
|
+
};
|
|
2424
|
+
|
|
2425
|
+
export const parseFlexParentMessage = (valueArg: unknown): TFlexParentMessage => {
|
|
2426
|
+
if (
|
|
2427
|
+
!isFlexPlainObject(valueArg)
|
|
2428
|
+
|| valueArg.version !== flexIpcProtocolVersion
|
|
2429
|
+
|| typeof valueArg.type !== 'string'
|
|
2430
|
+
) throw new Error('Invalid Flex parent message.');
|
|
2431
|
+
if (valueArg.type === 'init') {
|
|
2432
|
+
if (!hasFlexExactKeys(valueArg, ['version', 'type', 'init']) || !isFlexServiceInit(valueArg.init)) {
|
|
2433
|
+
throw new Error('Invalid Flex init message.');
|
|
2434
|
+
}
|
|
2435
|
+
return valueArg as unknown as IFlexIpcInitMessage;
|
|
2436
|
+
}
|
|
2437
|
+
if (valueArg.type === 'request') {
|
|
2438
|
+
if (
|
|
2439
|
+
!hasFlexExactKeys(valueArg, ['version', 'type', 'requestId', 'method', 'payload'])
|
|
2440
|
+
|| !isRequestId(valueArg.requestId)
|
|
2441
|
+
|| typeof valueArg.method !== 'string'
|
|
2442
|
+
|| !requestMethods.has(valueArg.method)
|
|
2443
|
+
|| !isFlexRequestPayload(valueArg.method as TFlexRequestMethod, valueArg.payload)
|
|
2444
|
+
) throw new Error('Invalid Flex request message.');
|
|
2445
|
+
return valueArg as unknown as IFlexIpcRequestMessage;
|
|
2446
|
+
}
|
|
2447
|
+
if (valueArg.type === 'request.cancel') {
|
|
2448
|
+
if (
|
|
2449
|
+
!hasFlexExactKeys(valueArg, ['version', 'type', 'requestId'])
|
|
2450
|
+
|| !isRequestId(valueArg.requestId)
|
|
2451
|
+
) throw new Error('Invalid Flex request cancellation message.');
|
|
2452
|
+
return valueArg as unknown as IFlexIpcRequestCancelMessage;
|
|
2453
|
+
}
|
|
2454
|
+
if (valueArg.type === 'dispose') {
|
|
2455
|
+
if (!hasFlexExactKeys(valueArg, ['version', 'type', 'requestId']) || !isRequestId(valueArg.requestId)) {
|
|
2456
|
+
throw new Error('Invalid Flex dispose message.');
|
|
2457
|
+
}
|
|
2458
|
+
return valueArg as unknown as IFlexIpcDisposeMessage;
|
|
2459
|
+
}
|
|
2460
|
+
if (valueArg.type === 'host.response') {
|
|
2461
|
+
if (!isRequestId(valueArg.requestId) || typeof valueArg.ok !== 'boolean') {
|
|
2462
|
+
throw new Error('Invalid Flex host response message.');
|
|
2463
|
+
}
|
|
2464
|
+
if (valueArg.ok) {
|
|
2465
|
+
if (!hasFlexExactKeys(valueArg, ['version', 'type', 'requestId', 'ok', 'result'])) {
|
|
2466
|
+
throw new Error('Invalid successful Flex host response message.');
|
|
2467
|
+
}
|
|
2468
|
+
} else if (
|
|
2469
|
+
!hasFlexExactKeys(valueArg, ['version', 'type', 'requestId', 'ok', 'error'])
|
|
2470
|
+
|| !isFlexServiceError(valueArg.error)
|
|
2471
|
+
) {
|
|
2472
|
+
throw new Error('Invalid failed Flex host response message.');
|
|
2473
|
+
}
|
|
2474
|
+
return valueArg as unknown as TFlexIpcHostResponseMessage;
|
|
2475
|
+
}
|
|
2476
|
+
if (valueArg.type === 'browser.channel.frame') {
|
|
2477
|
+
if (
|
|
2478
|
+
!hasFlexExactKeys(valueArg, ['version', 'type', 'channelId', 'dataBase64'])
|
|
2479
|
+
|| !isChannelId(valueArg.channelId)
|
|
2480
|
+
|| !isCanonicalBrowserFrame(valueArg.dataBase64)
|
|
2481
|
+
) throw new Error('Invalid Flex browser channel frame.');
|
|
2482
|
+
return valueArg as unknown as IFlexIpcParentBrowserFrameMessage;
|
|
2483
|
+
}
|
|
2484
|
+
if (valueArg.type === 'browser.channel.closed') {
|
|
2485
|
+
if (
|
|
2486
|
+
!hasFlexExactKeys(valueArg, ['version', 'type', 'channelId'])
|
|
2487
|
+
|| !isChannelId(valueArg.channelId)
|
|
2488
|
+
) throw new Error('Invalid Flex browser channel close message.');
|
|
2489
|
+
return valueArg as unknown as IFlexIpcBrowserChannelClosedMessage;
|
|
2490
|
+
}
|
|
2491
|
+
throw new Error('Invalid Flex parent message type.');
|
|
2492
|
+
};
|
|
2493
|
+
|
|
2494
|
+
export const parseFlexChildMessage = (valueArg: unknown): TFlexChildMessage => {
|
|
2495
|
+
if (
|
|
2496
|
+
!isFlexPlainObject(valueArg)
|
|
2497
|
+
|| valueArg.version !== flexIpcProtocolVersion
|
|
2498
|
+
|| typeof valueArg.type !== 'string'
|
|
2499
|
+
) throw new Error('Invalid Flex child message.');
|
|
2500
|
+
if (valueArg.type === 'status') {
|
|
2501
|
+
if (!hasFlexExactKeys(valueArg, ['version', 'type', 'status']) || !isFlexServiceStatus(valueArg.status)) {
|
|
2502
|
+
throw new Error('Invalid Flex status message.');
|
|
2503
|
+
}
|
|
2504
|
+
return valueArg as unknown as IFlexIpcStatusMessage;
|
|
2505
|
+
}
|
|
2506
|
+
if (valueArg.type === 'response') {
|
|
2507
|
+
if (!isRequestId(valueArg.requestId) || typeof valueArg.ok !== 'boolean') {
|
|
2508
|
+
throw new Error('Invalid Flex response message.');
|
|
2509
|
+
}
|
|
2510
|
+
if (valueArg.ok) {
|
|
2511
|
+
if (!hasFlexExactKeys(valueArg, ['version', 'type', 'requestId', 'ok', 'result'])) {
|
|
2512
|
+
throw new Error('Invalid successful Flex response message.');
|
|
2513
|
+
}
|
|
2514
|
+
} else if (
|
|
2515
|
+
!hasFlexExactKeys(valueArg, ['version', 'type', 'requestId', 'ok', 'error'])
|
|
2516
|
+
|| !isFlexServiceError(valueArg.error)
|
|
2517
|
+
) {
|
|
2518
|
+
throw new Error('Invalid failed Flex response message.');
|
|
2519
|
+
}
|
|
2520
|
+
return valueArg as unknown as TFlexIpcResponseMessage;
|
|
2521
|
+
}
|
|
2522
|
+
if (valueArg.type === 'event') {
|
|
2523
|
+
if (!hasFlexExactKeys(valueArg, ['version', 'type', 'event']) || !isFlexChildEvent(valueArg.event)) {
|
|
2524
|
+
throw new Error('Invalid Flex event message.');
|
|
2525
|
+
}
|
|
2526
|
+
return valueArg as unknown as IFlexIpcEventMessage;
|
|
2527
|
+
}
|
|
2528
|
+
if (valueArg.type === 'host.request') {
|
|
2529
|
+
if (
|
|
2530
|
+
!hasFlexExactKeys(
|
|
2531
|
+
valueArg,
|
|
2532
|
+
['version', 'type', 'requestId', 'method', 'payload', 'timeoutMs'],
|
|
2533
|
+
)
|
|
2534
|
+
|| !isRequestId(valueArg.requestId)
|
|
2535
|
+
|| typeof valueArg.method !== 'string'
|
|
2536
|
+
|| !hostRequestMethods.has(valueArg.method)
|
|
2537
|
+
|| !Number.isSafeInteger(valueArg.timeoutMs)
|
|
2538
|
+
|| Number(valueArg.timeoutMs) < 1
|
|
2539
|
+
|| Number(valueArg.timeoutMs) > flexIpcMaximumHostTimeoutMs
|
|
2540
|
+
|| !isFlexHostRequestPayload(
|
|
2541
|
+
valueArg.method as TFlexHostRequestMethod,
|
|
2542
|
+
valueArg.payload,
|
|
2543
|
+
)
|
|
2544
|
+
) throw new Error('Invalid Flex host request message.');
|
|
2545
|
+
return valueArg as unknown as IFlexIpcHostRequestMessage;
|
|
2546
|
+
}
|
|
2547
|
+
if (valueArg.type === 'host.cancel') {
|
|
2548
|
+
if (
|
|
2549
|
+
!hasFlexExactKeys(valueArg, ['version', 'type', 'requestId'])
|
|
2550
|
+
|| !isRequestId(valueArg.requestId)
|
|
2551
|
+
) throw new Error('Invalid Flex host cancellation message.');
|
|
2552
|
+
return valueArg as unknown as IFlexIpcHostCancelMessage;
|
|
2553
|
+
}
|
|
2554
|
+
if (valueArg.type === 'host.response.acknowledge') {
|
|
2555
|
+
if (
|
|
2556
|
+
!hasFlexExactKeys(valueArg, ['version', 'type', 'requestId'])
|
|
2557
|
+
|| !isRequestId(valueArg.requestId)
|
|
2558
|
+
) throw new Error('Invalid Flex host response acknowledgement message.');
|
|
2559
|
+
return valueArg as unknown as IFlexIpcHostResponseAcknowledgementMessage;
|
|
2560
|
+
}
|
|
2561
|
+
if (valueArg.type === 'browser.channel.frame') {
|
|
2562
|
+
if (
|
|
2563
|
+
!hasFlexExactKeys(valueArg, ['version', 'type', 'channelId', 'dataBase64'])
|
|
2564
|
+
|| !isChannelId(valueArg.channelId)
|
|
2565
|
+
|| !isCanonicalBrowserFrame(valueArg.dataBase64)
|
|
2566
|
+
) throw new Error('Invalid Flex browser channel frame.');
|
|
2567
|
+
return valueArg as unknown as IFlexIpcChildBrowserFrameMessage;
|
|
2568
|
+
}
|
|
2569
|
+
throw new Error('Invalid Flex child message type.');
|
|
2570
|
+
};
|
|
2571
|
+
|
|
2572
|
+
export const createFlexServiceError = (
|
|
2573
|
+
codeArg: TFlexServiceErrorCode,
|
|
2574
|
+
): IFlexServiceError => ({
|
|
2575
|
+
name: 'FlexServiceError',
|
|
2576
|
+
code: codeArg,
|
|
2577
|
+
message: flexServiceErrorMessages[codeArg],
|
|
2578
|
+
});
|
|
2579
|
+
|
|
2580
|
+
export class FlexServiceError extends Error {
|
|
2581
|
+
public readonly name = 'FlexServiceError';
|
|
2582
|
+
|
|
2583
|
+
constructor(public readonly code: TFlexServiceErrorCode) {
|
|
2584
|
+
super(flexServiceErrorMessages[code]);
|
|
2585
|
+
}
|
|
2586
|
+
}
|