@77-zhike/app-sdk 0.2.0-d7b1.2 → 0.2.0-d7b1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +281 -0
- package/package.json +17 -3
- package/src/cli/api-client.js +4 -0
- package/src/cli/browser-auth.js +64 -18
- package/src/cli/credential-store.js +21 -0
- package/src/cli/development-logs.js +5 -2
- package/src/cli/index.js +59 -42
- package/src/cli/local-commands.js +6 -3
- package/src/cli/logs.js +56 -0
- package/src/cli/project-init.js +111 -179
- package/src/cli/windows-credential-bridge.ps1 +131 -0
- package/src/cli/windows-credential-store.js +112 -0
- package/src/client/analysis.d.ts +4 -0
- package/src/client/analysis.js +12 -0
- package/src/client/destinations.d.ts +25 -0
- package/src/client/destinations.js +70 -0
- package/src/client/documents.d.ts +21 -0
- package/src/client/documents.js +39 -0
- package/src/client/external-url.js +9 -0
- package/src/client/index.d.ts +225 -3
- package/src/client/index.js +75 -3
- package/src/client/interactions.d.ts +23 -3
- package/src/client/interactions.js +53 -3
- package/src/client/points.generated.js +56 -0
- package/src/config.js +11 -7
- package/src/forms/array-contract.d.ts +32 -0
- package/src/forms/array-contract.js +138 -0
- package/src/forms/components.generated.js +2 -2
- package/src/forms/factory.js +21 -7
- package/src/forms/field-array.d.ts +16 -0
- package/src/forms/field-array.js +52 -0
- package/src/forms/index.d.ts +74 -3
- package/src/forms/index.js +2 -0
- package/src/forms/options.d.ts +7 -2
- package/src/forms/options.js +8 -1
- package/src/forms/record-options.d.ts +28 -0
- package/src/forms/record-options.js +92 -0
- package/src/forms/schema.d.ts +73 -9
- package/src/forms/schema.js +128 -0
- package/src/forms/state.d.ts +30 -0
- package/src/forms/state.js +82 -0
- package/src/forms/validation.d.ts +9 -0
- package/src/forms/validation.js +81 -0
- package/src/forms/values.d.ts +5 -0
- package/src/forms/values.js +38 -0
- package/src/jsx-dev-runtime.d.ts +2 -0
- package/src/jsx-dev-runtime.js +1 -0
- package/src/jsx-runtime.d.ts +2 -0
- package/src/jsx-runtime.js +1 -0
- package/src/server/agents/index.d.ts +62 -0
- package/src/server/agents/index.js +40 -0
- package/src/server/index.d.ts +60 -5
- package/src/server/index.js +43 -0
- package/src/server/steps/index.d.ts +71 -0
- package/src/server/steps/index.js +15 -0
- package/src/server/triggers/index.d.ts +68 -0
- package/src/server/triggers/index.js +27 -0
- package/src/server/workflow-blocks/index.d.ts +72 -0
- package/src/server/workflow-blocks/index.js +32 -0
- package/src/toolchain/agents/discovery.js +126 -0
- package/src/toolchain/agents/exposure.js +31 -0
- package/src/toolchain/agents/manifest.js +84 -0
- package/src/toolchain/ai-capabilities.js +69 -0
- package/src/toolchain/allowed-packages.json +7 -0
- package/src/toolchain/dependency-policy.js +181 -0
- package/src/toolchain/index.js +0 -88
- package/src/toolchain/real-build.js +135 -27
- package/src/toolchain/source-discovery.js +330 -81
- package/src/toolchain/workflow-blocks/discovery.js +133 -0
- package/src/toolchain/workflow-steps/manifest.js +99 -0
- package/src/toolchain/workflow-triggers/manifest.js +47 -0
- package/src/ui/components.generated.js +4 -1
- package/src/ui/conversation.d.ts +81 -0
- package/src/ui/conversation.js +82 -0
- package/src/ui/data-table-model.d.ts +14 -0
- package/src/ui/data-table-model.js +98 -0
- package/src/ui/data-table.d.ts +70 -0
- package/src/ui/data-table.js +74 -0
- package/src/ui/events.d.ts +10 -0
- package/src/ui/factory.js +87 -3
- package/src/ui/index.d.ts +351 -8
- package/src/ui/index.js +1 -1
- package/src/ui/jsx.generated.d.ts +359 -0
- package/src/ui/native-dom.d.ts +49 -0
- package/src/ui/native-dom.js +44 -0
- package/src/ui/scroll-viewport.d.ts +22 -0
- package/src/ui/scroll-viewport.js +23 -0
- package/src/ui/slider.d.ts +27 -0
- package/src/ui/slider.js +26 -0
- package/src/ui/style.d.ts +5 -0
- package/src/ui/style.generated.js +2 -0
- package/src/ui/style.js +15 -0
- package/src/ui/text-selection.d.ts +22 -0
- package/src/ui/text-selection.js +23 -0
- package/src/ui/validation.js +86 -0
- package/src/cli/validate-bundle.js +0 -36
package/src/server/index.js
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
|
+
export { defineAgent, defineTool, defineDataCapability } from "./agents/index.js";
|
|
1
2
|
const SERVER_FUNCTION_MARKER = Symbol.for("zhike.server-function.v1");
|
|
3
|
+
export { defineWorkflowStep, defineWorkflowStepExecute } from "./steps/index.js";
|
|
4
|
+
export { defineWorkflowTrigger, defineWorkflowTriggerActivate, defineWorkflowTriggerOnEvent, defineWorkflowTriggerDeactivate } from "./triggers/index.js";
|
|
5
|
+
export {
|
|
6
|
+
defineWorkflowBlock,
|
|
7
|
+
defineWorkflowBlockExecute,
|
|
8
|
+
defineWorkflowBlockActivate,
|
|
9
|
+
defineWorkflowBlockTrigger,
|
|
10
|
+
defineWorkflowBlockDeactivate,
|
|
11
|
+
} from "./workflow-blocks/index.js";
|
|
2
12
|
const LIFECYCLE_EVENT_MARKER = Symbol.for("zhike.lifecycle-event.v1");
|
|
3
13
|
const WEBHOOK_HANDLER_MARKER = Symbol.for("zhike.webhook-handler.v1");
|
|
4
14
|
const WORKSPACE_CONNECTION_EVENTS = new Set([
|
|
@@ -9,6 +19,7 @@ const WORKSPACE_CONNECTION_EVENTS = new Set([
|
|
|
9
19
|
]);
|
|
10
20
|
|
|
11
21
|
export const ZHIKE_API_TOKEN = globalThis.ZHIKE_API_TOKEN;
|
|
22
|
+
export const ZHIKE_CURRENT_USER_TOKEN = globalThis.ZHIKE_CURRENT_USER_TOKEN;
|
|
12
23
|
const ZHIKE_OPEN_API_BASE_URL = "https://api-clm.77hub.com/v1";
|
|
13
24
|
export const kv = globalThis.ZHIKE_APP_KV;
|
|
14
25
|
|
|
@@ -145,11 +156,43 @@ export function getWorkspaceConnection() {
|
|
|
145
156
|
error.code = code;
|
|
146
157
|
throw error;
|
|
147
158
|
}
|
|
159
|
+
if (connection.protocol === "oidc" && (typeof connection.idToken !== "string" || !connection.idToken)) {
|
|
160
|
+
const error = new Error("connection_credential_unavailable");
|
|
161
|
+
error.code = "connection_credential_unavailable";
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
const protocol = connection.type === "secret"
|
|
165
|
+
? "secret"
|
|
166
|
+
: (connection.protocol === "oidc" && typeof connection.idToken === "string" && connection.idToken
|
|
167
|
+
? "oidc" : "oauth2");
|
|
148
168
|
return Object.freeze({
|
|
149
169
|
id: connection.id,
|
|
150
170
|
generation: connection.generation,
|
|
151
171
|
type: connection.type,
|
|
172
|
+
protocol,
|
|
152
173
|
value: connection.value,
|
|
174
|
+
...(connection.type === "oauth2" ? { accessToken: connection.value } : {}),
|
|
175
|
+
...(protocol === "oidc" ? { idToken: connection.idToken } : {}),
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function getCurrentUser() {
|
|
180
|
+
const current = globalThis.ZHIKE_CURRENT_USER;
|
|
181
|
+
if (
|
|
182
|
+
!current ||
|
|
183
|
+
typeof current !== "object" ||
|
|
184
|
+
typeof current.workspaceMemberId !== "string" ||
|
|
185
|
+
typeof current.displayName !== "string"
|
|
186
|
+
) {
|
|
187
|
+
const error = new Error("current_user_unavailable");
|
|
188
|
+
error.code = "current_user_unavailable";
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
return Object.freeze({
|
|
192
|
+
workspaceMemberId: current.workspaceMemberId,
|
|
193
|
+
displayName: current.displayName,
|
|
194
|
+
primaryEmailAddress: typeof current.primaryEmailAddress === "string" ? current.primaryEmailAddress : null,
|
|
195
|
+
avatarUrl: typeof current.avatarUrl === "string" ? current.avatarUrl : null,
|
|
153
196
|
});
|
|
154
197
|
}
|
|
155
198
|
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export type WorkflowScalarSchema =
|
|
2
|
+
| { readonly type: "string"; readonly minLength?: number; readonly enum?: readonly string[]; readonly title?: string; readonly description?: string; readonly examples?: readonly string[] }
|
|
3
|
+
| { readonly type: "number"; readonly title?: string; readonly description?: string; readonly examples?: readonly number[] }
|
|
4
|
+
| { readonly type: "boolean"; readonly title?: string; readonly description?: string; readonly examples?: readonly boolean[] };
|
|
5
|
+
|
|
6
|
+
export interface WorkflowObjectSchema {
|
|
7
|
+
readonly type: "object";
|
|
8
|
+
readonly properties: Readonly<Record<string, WorkflowScalarSchema>>;
|
|
9
|
+
readonly required?: readonly string[];
|
|
10
|
+
readonly additionalProperties: false;
|
|
11
|
+
readonly title?: string;
|
|
12
|
+
readonly description?: string;
|
|
13
|
+
readonly examples?: readonly (string | number | boolean)[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface WorkflowStepOutcome {
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly label: string;
|
|
19
|
+
readonly dataSchema: WorkflowObjectSchema | null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface WorkflowStepDefinition<
|
|
23
|
+
Config extends WorkflowObjectSchema = WorkflowObjectSchema,
|
|
24
|
+
Outcomes extends readonly WorkflowStepOutcome[] = readonly WorkflowStepOutcome[],
|
|
25
|
+
> {
|
|
26
|
+
readonly key: string;
|
|
27
|
+
readonly title: string;
|
|
28
|
+
readonly description?: string;
|
|
29
|
+
readonly connection: "workspace" | "none";
|
|
30
|
+
readonly configSchema: Config;
|
|
31
|
+
readonly outcomes: Outcomes;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type ScalarValue<S> = S extends { readonly enum: readonly (infer E)[] } ? E
|
|
35
|
+
: S extends { readonly type: "string" } ? string
|
|
36
|
+
: S extends { readonly type: "number" } ? number
|
|
37
|
+
: S extends { readonly type: "boolean" } ? boolean
|
|
38
|
+
: never;
|
|
39
|
+
|
|
40
|
+
type RequiredNames<S extends WorkflowObjectSchema> = S["required"] extends readonly string[]
|
|
41
|
+
? S["required"][number] : never;
|
|
42
|
+
|
|
43
|
+
export type WorkflowSchemaValue<S extends WorkflowObjectSchema> = {
|
|
44
|
+
[K in keyof S["properties"] as K extends RequiredNames<S> ? K : never]-?: ScalarValue<S["properties"][K]>;
|
|
45
|
+
} & {
|
|
46
|
+
[K in keyof S["properties"] as K extends RequiredNames<S> ? never : K]?: ScalarValue<S["properties"][K]>;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
type OutcomeResult<O extends WorkflowStepOutcome> = O extends WorkflowStepOutcome
|
|
50
|
+
? O["dataSchema"] extends WorkflowObjectSchema
|
|
51
|
+
? { readonly outcome: O["id"]; readonly data: WorkflowSchemaValue<O["dataSchema"]> }
|
|
52
|
+
: { readonly outcome: O["id"]; readonly data: null }
|
|
53
|
+
: never;
|
|
54
|
+
|
|
55
|
+
export type WorkflowStepResult<Outcomes extends readonly WorkflowStepOutcome[]> =
|
|
56
|
+
OutcomeResult<Outcomes[number]>;
|
|
57
|
+
|
|
58
|
+
export function defineWorkflowStep<
|
|
59
|
+
const Config extends WorkflowObjectSchema,
|
|
60
|
+
const Outcomes extends readonly [WorkflowStepOutcome, ...WorkflowStepOutcome[]],
|
|
61
|
+
>(definition: WorkflowStepDefinition<Config, Outcomes>): WorkflowStepDefinition<Config, Outcomes>;
|
|
62
|
+
|
|
63
|
+
export function defineWorkflowStepExecute<
|
|
64
|
+
Config extends WorkflowObjectSchema,
|
|
65
|
+
Outcomes extends readonly WorkflowStepOutcome[],
|
|
66
|
+
>(
|
|
67
|
+
step: WorkflowStepDefinition<Config, Outcomes>,
|
|
68
|
+
execute: (params: Readonly<{ config: WorkflowSchemaValue<Config> }>) =>
|
|
69
|
+
WorkflowStepResult<Outcomes> | Promise<WorkflowStepResult<Outcomes>>,
|
|
70
|
+
): (params: Readonly<{ config: WorkflowSchemaValue<Config> }>) =>
|
|
71
|
+
WorkflowStepResult<Outcomes> | Promise<WorkflowStepResult<Outcomes>>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const STEP_MARKER = Symbol.for("zhike.workflow-step.v1");
|
|
2
|
+
|
|
3
|
+
export function defineWorkflowStep(definition) {
|
|
4
|
+
if (!definition || typeof definition !== "object") {
|
|
5
|
+
throw new TypeError("workflow_step_definition_invalid");
|
|
6
|
+
}
|
|
7
|
+
return Object.freeze({ ...definition, [STEP_MARKER]: true });
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function defineWorkflowStepExecute(step, execute) {
|
|
11
|
+
if (!step?.[STEP_MARKER] || typeof execute !== "function") {
|
|
12
|
+
throw new TypeError("workflow_step_execute_invalid");
|
|
13
|
+
}
|
|
14
|
+
return execute;
|
|
15
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { WorkflowObjectSchema, WorkflowSchemaValue } from "../steps/index.js";
|
|
2
|
+
|
|
3
|
+
export interface WorkflowTriggerOutcome {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly label: string;
|
|
6
|
+
readonly dataSchema: WorkflowObjectSchema;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface WorkflowTriggerDefinition<
|
|
10
|
+
Config extends WorkflowObjectSchema = WorkflowObjectSchema,
|
|
11
|
+
Outcomes extends readonly WorkflowTriggerOutcome[] = readonly WorkflowTriggerOutcome[],
|
|
12
|
+
> {
|
|
13
|
+
readonly key: string;
|
|
14
|
+
readonly title: string;
|
|
15
|
+
readonly description?: string;
|
|
16
|
+
readonly connection: "workspace" | "none";
|
|
17
|
+
readonly requestHeaders?: readonly string[];
|
|
18
|
+
readonly configSchema: Config;
|
|
19
|
+
readonly outcomes: Outcomes;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type OutcomeResult<O extends WorkflowTriggerOutcome> = O extends WorkflowTriggerOutcome
|
|
23
|
+
? {
|
|
24
|
+
readonly type: "outcome";
|
|
25
|
+
readonly id: O["id"];
|
|
26
|
+
readonly eventKey: string;
|
|
27
|
+
readonly data: WorkflowSchemaValue<O["dataSchema"]>;
|
|
28
|
+
}
|
|
29
|
+
: never;
|
|
30
|
+
|
|
31
|
+
export type WorkflowTriggerEventResult<Outcomes extends readonly WorkflowTriggerOutcome[]> =
|
|
32
|
+
| { readonly type: "no-op" }
|
|
33
|
+
| { readonly type: "error"; readonly code: "invalid_signature"; readonly retryable: false }
|
|
34
|
+
| OutcomeResult<Outcomes[number]>;
|
|
35
|
+
|
|
36
|
+
export function defineWorkflowTrigger<
|
|
37
|
+
const Config extends WorkflowObjectSchema,
|
|
38
|
+
const Outcomes extends readonly [WorkflowTriggerOutcome, ...WorkflowTriggerOutcome[]],
|
|
39
|
+
>(definition: WorkflowTriggerDefinition<Config, Outcomes>): WorkflowTriggerDefinition<Config, Outcomes>;
|
|
40
|
+
|
|
41
|
+
export function defineWorkflowTriggerActivate<Config extends WorkflowObjectSchema>(
|
|
42
|
+
trigger: WorkflowTriggerDefinition<Config>,
|
|
43
|
+
handler: (params: Readonly<{
|
|
44
|
+
config: WorkflowSchemaValue<Config>;
|
|
45
|
+
activationId: string;
|
|
46
|
+
triggerCallbackUrl: string;
|
|
47
|
+
}>) => Promise<Readonly<{ subscriptionRef: string | null }>>,
|
|
48
|
+
): typeof handler;
|
|
49
|
+
|
|
50
|
+
export function defineWorkflowTriggerOnEvent<
|
|
51
|
+
Config extends WorkflowObjectSchema,
|
|
52
|
+
Outcomes extends readonly WorkflowTriggerOutcome[],
|
|
53
|
+
>(
|
|
54
|
+
trigger: WorkflowTriggerDefinition<Config, Outcomes>,
|
|
55
|
+
handler: (params: Readonly<{
|
|
56
|
+
config: WorkflowSchemaValue<Config>;
|
|
57
|
+
body: string;
|
|
58
|
+
headers: Readonly<Record<string, string>>;
|
|
59
|
+
}>) => Promise<WorkflowTriggerEventResult<Outcomes>>,
|
|
60
|
+
): typeof handler;
|
|
61
|
+
|
|
62
|
+
export function defineWorkflowTriggerDeactivate(
|
|
63
|
+
trigger: WorkflowTriggerDefinition,
|
|
64
|
+
handler: (params: Readonly<{
|
|
65
|
+
activationId: string;
|
|
66
|
+
subscriptionRef: string | null;
|
|
67
|
+
}>) => Promise<void>,
|
|
68
|
+
): typeof handler;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const TRIGGER_MARKER = Symbol.for("zhike.workflow-trigger.v1");
|
|
2
|
+
|
|
3
|
+
export function defineWorkflowTrigger(definition) {
|
|
4
|
+
if (!definition || typeof definition !== "object" || Array.isArray(definition)) {
|
|
5
|
+
throw new TypeError("workflow_trigger_definition_invalid");
|
|
6
|
+
}
|
|
7
|
+
return Object.freeze({ ...definition, [TRIGGER_MARKER]: true });
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function bindTriggerHandler(trigger, handler, kind) {
|
|
11
|
+
if (!trigger?.[TRIGGER_MARKER] || typeof handler !== "function") {
|
|
12
|
+
throw new TypeError(`workflow_trigger_${kind}_invalid`);
|
|
13
|
+
}
|
|
14
|
+
return handler;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function defineWorkflowTriggerActivate(trigger, handler) {
|
|
18
|
+
return bindTriggerHandler(trigger, handler, "activate");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function defineWorkflowTriggerOnEvent(trigger, handler) {
|
|
22
|
+
return bindTriggerHandler(trigger, handler, "on_event");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function defineWorkflowTriggerDeactivate(trigger, handler) {
|
|
26
|
+
return bindTriggerHandler(trigger, handler, "deactivate");
|
|
27
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
WorkflowObjectSchema,
|
|
3
|
+
WorkflowSchemaValue,
|
|
4
|
+
WorkflowStepDefinition,
|
|
5
|
+
WorkflowStepOutcome,
|
|
6
|
+
WorkflowStepResult,
|
|
7
|
+
} from "../steps/index.js";
|
|
8
|
+
import type {
|
|
9
|
+
WorkflowTriggerDefinition,
|
|
10
|
+
WorkflowTriggerEventResult,
|
|
11
|
+
WorkflowTriggerOutcome,
|
|
12
|
+
} from "../triggers/index.js";
|
|
13
|
+
|
|
14
|
+
export type WorkflowStepBlockDefinition<
|
|
15
|
+
Config extends WorkflowObjectSchema = WorkflowObjectSchema,
|
|
16
|
+
Outcomes extends readonly WorkflowStepOutcome[] = readonly WorkflowStepOutcome[],
|
|
17
|
+
> = WorkflowStepDefinition<Config, Outcomes> & { readonly type: "step" };
|
|
18
|
+
|
|
19
|
+
export type WorkflowTriggerBlockDefinition<
|
|
20
|
+
Config extends WorkflowObjectSchema = WorkflowObjectSchema,
|
|
21
|
+
Outcomes extends readonly WorkflowTriggerOutcome[] = readonly WorkflowTriggerOutcome[],
|
|
22
|
+
> = WorkflowTriggerDefinition<Config, Outcomes> & { readonly type: "trigger" };
|
|
23
|
+
|
|
24
|
+
export type WorkflowBlockDefinition = WorkflowStepBlockDefinition | WorkflowTriggerBlockDefinition;
|
|
25
|
+
|
|
26
|
+
export function defineWorkflowBlock<
|
|
27
|
+
const Config extends WorkflowObjectSchema,
|
|
28
|
+
const Outcomes extends readonly [WorkflowStepOutcome, ...WorkflowStepOutcome[]],
|
|
29
|
+
>(definition: WorkflowStepBlockDefinition<Config, Outcomes>): WorkflowStepBlockDefinition<Config, Outcomes>;
|
|
30
|
+
|
|
31
|
+
export function defineWorkflowBlock<
|
|
32
|
+
const Config extends WorkflowObjectSchema,
|
|
33
|
+
const Outcomes extends readonly [WorkflowTriggerOutcome, ...WorkflowTriggerOutcome[]],
|
|
34
|
+
>(definition: WorkflowTriggerBlockDefinition<Config, Outcomes>): WorkflowTriggerBlockDefinition<Config, Outcomes>;
|
|
35
|
+
|
|
36
|
+
export function defineWorkflowBlockExecute<
|
|
37
|
+
Config extends WorkflowObjectSchema,
|
|
38
|
+
Outcomes extends readonly WorkflowStepOutcome[],
|
|
39
|
+
>(
|
|
40
|
+
block: WorkflowStepBlockDefinition<Config, Outcomes>,
|
|
41
|
+
execute: (params: Readonly<{ config: WorkflowSchemaValue<Config> }>) =>
|
|
42
|
+
WorkflowStepResult<Outcomes> | Promise<WorkflowStepResult<Outcomes>>,
|
|
43
|
+
): typeof execute;
|
|
44
|
+
|
|
45
|
+
export function defineWorkflowBlockActivate<Config extends WorkflowObjectSchema>(
|
|
46
|
+
block: WorkflowTriggerBlockDefinition<Config>,
|
|
47
|
+
handler: (params: Readonly<{
|
|
48
|
+
config: WorkflowSchemaValue<Config>;
|
|
49
|
+
activationId: string;
|
|
50
|
+
triggerCallbackUrl: string;
|
|
51
|
+
}>) => Promise<Readonly<{ subscriptionRef: string | null }>>,
|
|
52
|
+
): typeof handler;
|
|
53
|
+
|
|
54
|
+
export function defineWorkflowBlockTrigger<
|
|
55
|
+
Config extends WorkflowObjectSchema,
|
|
56
|
+
Outcomes extends readonly WorkflowTriggerOutcome[],
|
|
57
|
+
>(
|
|
58
|
+
block: WorkflowTriggerBlockDefinition<Config, Outcomes>,
|
|
59
|
+
handler: (params: Readonly<{
|
|
60
|
+
config: WorkflowSchemaValue<Config>;
|
|
61
|
+
body: string;
|
|
62
|
+
headers: Readonly<Record<string, string>>;
|
|
63
|
+
}>) => Promise<WorkflowTriggerEventResult<Outcomes>>,
|
|
64
|
+
): typeof handler;
|
|
65
|
+
|
|
66
|
+
export function defineWorkflowBlockDeactivate(
|
|
67
|
+
block: WorkflowTriggerBlockDefinition,
|
|
68
|
+
handler: (params: Readonly<{
|
|
69
|
+
activationId: string;
|
|
70
|
+
subscriptionRef: string | null;
|
|
71
|
+
}>) => Promise<void>,
|
|
72
|
+
): typeof handler;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const WORKFLOW_BLOCK_MARKER = Symbol.for("zhike.workflow-block.v1");
|
|
2
|
+
|
|
3
|
+
export function defineWorkflowBlock(definition) {
|
|
4
|
+
if (!definition || typeof definition !== "object" || Array.isArray(definition) ||
|
|
5
|
+
!["step", "trigger"].includes(definition.type)) {
|
|
6
|
+
throw new TypeError("workflow_block_definition_invalid");
|
|
7
|
+
}
|
|
8
|
+
return Object.freeze({ ...definition, [WORKFLOW_BLOCK_MARKER]: true });
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function bindWorkflowBlockHandler(block, handler, type, kind) {
|
|
12
|
+
if (!block?.[WORKFLOW_BLOCK_MARKER] || block.type !== type || typeof handler !== "function") {
|
|
13
|
+
throw new TypeError(`workflow_block_${kind}_invalid`);
|
|
14
|
+
}
|
|
15
|
+
return handler;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function defineWorkflowBlockExecute(block, handler) {
|
|
19
|
+
return bindWorkflowBlockHandler(block, handler, "step", "execute");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function defineWorkflowBlockActivate(block, handler) {
|
|
23
|
+
return bindWorkflowBlockHandler(block, handler, "trigger", "activate");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function defineWorkflowBlockTrigger(block, handler) {
|
|
27
|
+
return bindWorkflowBlockHandler(block, handler, "trigger", "trigger");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function defineWorkflowBlockDeactivate(block, handler) {
|
|
31
|
+
return bindWorkflowBlockHandler(block, handler, "trigger", "deactivate");
|
|
32
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import { posix } from 'node:path';
|
|
3
|
+
import { ZhikeCliError } from '../../errors.js';
|
|
4
|
+
import { assertAiCapabilities } from '../ai-capabilities.js';
|
|
5
|
+
import { assertAgentManifest } from './manifest.js';
|
|
6
|
+
|
|
7
|
+
const AGENT_PATH = /^app\/agents\/([a-z][a-z0-9-]{0,63})\/agent\.ts$/;
|
|
8
|
+
const ACTION_PATH = /^app\/actions\/([a-z][a-z0-9-]{0,63})\.tools\.ts$/;
|
|
9
|
+
const DATA_PATH = /^app\/data-capabilities\/([a-z][a-z0-9-]{0,63})\.ts$/;
|
|
10
|
+
const TOOL_PATH = /^app\/agents\/([a-z][a-z0-9-]{0,63})\/([a-z][a-z0-9-]{0,63})\.tools\.ts$/;
|
|
11
|
+
function invalid(path, message) { throw new ZhikeCliError('zhike_agent_invalid', `${path}: ${message}`); }
|
|
12
|
+
function literal(node, path, depth = 0) {
|
|
13
|
+
if (depth > 24) invalid(path, '静态声明嵌套过深');
|
|
14
|
+
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) return node.text;
|
|
15
|
+
if (ts.isNumericLiteral(node)) return Number(node.text);
|
|
16
|
+
if (ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.MinusToken && ts.isNumericLiteral(node.operand)) return -Number(node.operand.text);
|
|
17
|
+
if (node.kind === ts.SyntaxKind.TrueKeyword) return true;
|
|
18
|
+
if (node.kind === ts.SyntaxKind.FalseKeyword) return false;
|
|
19
|
+
if (node.kind === ts.SyntaxKind.NullKeyword) return null;
|
|
20
|
+
if (ts.isArrayLiteralExpression(node)) return node.elements.map(item => literal(item, path, depth + 1));
|
|
21
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
22
|
+
const result = Object.create(null);
|
|
23
|
+
for (const property of node.properties) {
|
|
24
|
+
if (!ts.isPropertyAssignment(property) || !(ts.isIdentifier(property.name) || ts.isStringLiteral(property.name))) invalid(path, '元数据必须为静态 JSON 字面量');
|
|
25
|
+
const key = property.name.text;
|
|
26
|
+
if (Object.hasOwn(result, key) || ['__proto__', 'prototype', 'constructor'].includes(key)) invalid(path, '重复或保留字段');
|
|
27
|
+
result[key] = literal(property.initializer, path, depth + 1);
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
invalid(path, '元数据不能读取变量、函数或运行时代码');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function definition(file, api) {
|
|
35
|
+
const { sourceFile, relativePath } = file;
|
|
36
|
+
const imported = sourceFile.statements.some(statement => ts.isImportDeclaration(statement) &&
|
|
37
|
+
statement.moduleSpecifier.text === '@77-zhike/app-sdk/server' &&
|
|
38
|
+
statement.importClause?.namedBindings && ts.isNamedImports(statement.importClause.namedBindings) &&
|
|
39
|
+
statement.importClause.namedBindings.elements.some(item => !item.propertyName && item.name.text === api));
|
|
40
|
+
const exports = sourceFile.statements.filter(statement => ts.isExportAssignment(statement) && !statement.isExportEquals);
|
|
41
|
+
const call = exports[0]?.expression;
|
|
42
|
+
if (!imported || exports.length !== 1 || !call || !ts.isCallExpression(call) ||
|
|
43
|
+
!ts.isIdentifier(call.expression) || call.expression.text !== api || call.arguments.length !== 1 ||
|
|
44
|
+
!ts.isObjectLiteralExpression(call.arguments[0])) invalid(relativePath, `必须直接默认导出 ${api}({...})`);
|
|
45
|
+
const value = Object.create(null);
|
|
46
|
+
for (const property of call.arguments[0].properties) {
|
|
47
|
+
if (!property.name || !(ts.isIdentifier(property.name) || ts.isStringLiteral(property.name))) invalid(relativePath, '声明禁止 spread 或动态属性');
|
|
48
|
+
const key = property.name.text;
|
|
49
|
+
if (Object.hasOwn(value, key)) invalid(relativePath, '重复声明字段');
|
|
50
|
+
if (api === 'defineTool' && key === 'execute') {
|
|
51
|
+
const handler = ts.isMethodDeclaration(property) ? property : ts.isPropertyAssignment(property) ? property.initializer : null;
|
|
52
|
+
if (!handler || !(ts.isMethodDeclaration(handler) || ts.isArrowFunction(handler) || ts.isFunctionExpression(handler)) || handler.parameters.length > 1) invalid(relativePath, 'execute 必须是仅接收业务 input 的函数');
|
|
53
|
+
value[key] = true;
|
|
54
|
+
} else {
|
|
55
|
+
if (!ts.isPropertyAssignment(property)) invalid(relativePath, '元数据必须为静态字段');
|
|
56
|
+
value[key] = literal(property.initializer, relativePath);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function discoverAgents(files) {
|
|
63
|
+
const agents = new Map();
|
|
64
|
+
for (const file of files) {
|
|
65
|
+
const match = file.relativePath.match(AGENT_PATH);
|
|
66
|
+
if (!match) continue;
|
|
67
|
+
const value = definition(file, 'defineAgent');
|
|
68
|
+
if (value.key !== match[1] || Object.keys(value).some(key => !['key', 'name', 'systemPrompt', 'actionRefs', 'aiExposure'].includes(key))) invalid(file.relativePath, 'Agent key 必须等于目录且只接受 key/name/systemPrompt');
|
|
69
|
+
agents.set(value.key, { ...value, relativePath: file.relativePath, tools: [] });
|
|
70
|
+
}
|
|
71
|
+
if (agents.size > 16) invalid("app/agents", "一个 App 最多 16 个 Agent");
|
|
72
|
+
for (const file of files) {
|
|
73
|
+
const match = file.relativePath.match(TOOL_PATH);
|
|
74
|
+
if (file.relativePath.endsWith('.tools.ts') && !match && !ACTION_PATH.test(file.relativePath)) invalid(file.relativePath, '工具必须位于 Agent 的直接子目录');
|
|
75
|
+
if (/^app\/agents\/.+\/agent\.(?:ts|tsx)$/.test(file.relativePath) && !AGENT_PATH.test(file.relativePath)) invalid(file.relativePath, 'Agent 入口必须是直接子目录 agent.ts');
|
|
76
|
+
for (const statement of file.sourceFile.statements) {
|
|
77
|
+
if (!(ts.isImportDeclaration(statement) || ts.isExportDeclaration(statement)) || !statement.moduleSpecifier || !ts.isStringLiteral(statement.moduleSpecifier)) continue;
|
|
78
|
+
const specifier = statement.moduleSpecifier.text;
|
|
79
|
+
if (!specifier.startsWith('.')) continue;
|
|
80
|
+
const target = posix.normalize(posix.join(posix.dirname(file.relativePath), specifier)).replace(/\.js$/, '.ts');
|
|
81
|
+
if (ACTION_PATH.test(target.endsWith('.ts') ? target : `${target}.ts`)) invalid(file.relativePath, '独立 Action 只能通过 actionRefs 引用,不能导入执行声明;请抽取普通 helper');
|
|
82
|
+
const tool = (target.endsWith(".ts") ? target : `${target}.ts`).match(TOOL_PATH);
|
|
83
|
+
if (tool && (!match || match[1] !== tool[1])) invalid(file.relativePath, '不能跨 Agent 导入工具声明,请抽取普通 helper');
|
|
84
|
+
}
|
|
85
|
+
if (!match) continue;
|
|
86
|
+
const owner = agents.get(match[1]);
|
|
87
|
+
if (!owner) invalid(file.relativePath, '工具缺少所属 agent.ts');
|
|
88
|
+
const value = definition(file, 'defineTool');
|
|
89
|
+
if (value.key !== match[2] || value.execute !== true) invalid(file.relativePath, 'Tool key 必须等于文件前缀且提供 execute');
|
|
90
|
+
delete value.execute;
|
|
91
|
+
owner.tools.push({ ...value, clientResult: value.clientResult ?? 'none', artifact: 'server',
|
|
92
|
+
entry: `agent-tool/${match[1]}/${match[2]}`, relativePath: file.relativePath });
|
|
93
|
+
}
|
|
94
|
+
return [...agents.values()].sort((a, b) => a.key.localeCompare(b.key)).map(agent => {
|
|
95
|
+
agent.tools.sort((a, b) => a.key.localeCompare(b.key));
|
|
96
|
+
try { assertAgentManifest(agentManifest(agent)); } catch { invalid(agent.relativePath, 'Agent/Tool 元数据、Schema 或归属无效'); }
|
|
97
|
+
return Object.freeze(agent);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function agentManifest({ relativePath, tools, ...agent }) {
|
|
102
|
+
return { ...agent, tools: tools.map(({ relativePath: path, ...tool }) => tool) };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function discoverAiCapabilities(files, agents) {
|
|
106
|
+
const actions = [], dataCapabilities = [];
|
|
107
|
+
for (const file of files) {
|
|
108
|
+
const action = file.relativePath.match(ACTION_PATH);
|
|
109
|
+
const data = file.relativePath.match(DATA_PATH);
|
|
110
|
+
if (file.relativePath.startsWith('app/actions/') && file.relativePath.endsWith('.tools.ts') && !action) invalid(file.relativePath, 'Action 必须位于 actions 直接目录');
|
|
111
|
+
if (!action && !data) continue;
|
|
112
|
+
const value = definition(file, action ? 'defineTool' : 'defineDataCapability');
|
|
113
|
+
if (value.key !== (action ?? data)[1]) invalid(file.relativePath, 'key 必须与文件名一致');
|
|
114
|
+
if (action) {
|
|
115
|
+
if (value.execute !== true) invalid(file.relativePath, 'Action 缺少 execute');
|
|
116
|
+
delete value.execute;
|
|
117
|
+
actions.push({...value, clientResult:value.clientResult ?? 'none', artifact:'server', entry:`action/${value.key}`, relativePath:file.relativePath});
|
|
118
|
+
} else dataCapabilities.push({...value, relativePath:file.relativePath});
|
|
119
|
+
}
|
|
120
|
+
actions.sort((a,b)=>a.key.localeCompare(b.key));
|
|
121
|
+
dataCapabilities.sort((a,b)=>a.key.localeCompare(b.key));
|
|
122
|
+
try { assertAiCapabilities({sdkAbiVersion:'2',aiCapabilitiesVersion:'1',actions:actions.map(stripPath),dataCapabilities:dataCapabilities.map(stripPath),agents:agents.map(agentManifest)}); }
|
|
123
|
+
catch { invalid('app/actions', '独立能力声明或跨引用无效'); }
|
|
124
|
+
return {actions, dataCapabilities};
|
|
125
|
+
}
|
|
126
|
+
export function stripPath({relativePath, ...value}) { return value; }
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const OBJECT = /^[a-z][a-z0-9_]{0,63}$/;
|
|
2
|
+
export function assertAiExposure(value, inputSchema, expert = false) {
|
|
3
|
+
const invalid = () => { throw new TypeError('app_manifest_ai_exposure_invalid'); };
|
|
4
|
+
const allowed = expert
|
|
5
|
+
? ['subjectTypes', 'usage', 'limitations', 'outputSchema', 'toolKeys']
|
|
6
|
+
: ['subjectTypes', 'usage', 'limitations', 'effect', 'subjectInput'];
|
|
7
|
+
if (!value || typeof value !== 'object' || Array.isArray(value) ||
|
|
8
|
+
Object.keys(value).some(key => !allowed.includes(key)) ||
|
|
9
|
+
!Array.isArray(value.subjectTypes) || value.subjectTypes.length > 16 ||
|
|
10
|
+
new Set(value.subjectTypes).size !== value.subjectTypes.length ||
|
|
11
|
+
value.subjectTypes.some(item => typeof item !== 'string' || !OBJECT.test(item))) invalid();
|
|
12
|
+
for (const key of ['usage', 'limitations']) {
|
|
13
|
+
if (typeof value[key] !== 'string' || !value[key].trim() || value[key].length > 1200) invalid();
|
|
14
|
+
}
|
|
15
|
+
if (expert) {
|
|
16
|
+
if (!value.outputSchema || (value.toolKeys !== undefined && (!Array.isArray(value.toolKeys) ||
|
|
17
|
+
new Set(value.toolKeys).size !== value.toolKeys.length ||
|
|
18
|
+
value.toolKeys.some(key => typeof key !== 'string' || !/^[a-z][a-z0-9-]{0,63}$/.test(key))))) invalid();
|
|
19
|
+
} else {
|
|
20
|
+
if (value.effect !== 'query') invalid();
|
|
21
|
+
if (value.subjectInput !== undefined) {
|
|
22
|
+
const subject = inputSchema?.properties?.[value.subjectInput];
|
|
23
|
+
if (typeof value.subjectInput !== 'string' || !Object.hasOwn(inputSchema.properties, value.subjectInput) ||
|
|
24
|
+
subject?.type !== 'object' || subject.additionalProperties !== false ||
|
|
25
|
+
Object.keys(subject.properties ?? {}).sort().join(',') !== 'object,recordId' ||
|
|
26
|
+
subject.properties.object?.type !== 'string' || subject.properties.recordId?.type !== 'string' ||
|
|
27
|
+
!subject.required?.includes('object') || !subject.required?.includes('recordId')) invalid();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { assertAiExposure } from './exposure.js';
|
|
2
|
+
const KEY = /^[a-z][a-z0-9-]{0,63}$/;
|
|
3
|
+
const FIELD = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;
|
|
4
|
+
const RESERVED = new Set(['tenant_id', 'tenantId', 'member_id', 'memberId', 'installation_id', 'installationId',
|
|
5
|
+
'app_id', 'appId', 'version_id', 'versionId', 'run_id', 'executionId', 'tool_call_id', 'constructor', 'prototype']);
|
|
6
|
+
const SCHEMA_KEYS = ['type', 'properties', 'required', 'additionalProperties', 'items', 'enum', 'const',
|
|
7
|
+
'minLength', 'maxLength', 'pattern', 'minimum', 'maximum', 'minItems', 'maxItems', 'title', 'description'];
|
|
8
|
+
function invalid() { throw new TypeError('app_manifest_agent_invalid'); }
|
|
9
|
+
function record(value) { if (!value || typeof value !== 'object' || Array.isArray(value)) invalid(); }
|
|
10
|
+
function keys(value, allowed) { if (Object.keys(value).some(key => !allowed.includes(key))) invalid(); }
|
|
11
|
+
function text(value, max) { if (typeof value !== 'string' || !value.trim() || value.length > max) invalid(); }
|
|
12
|
+
|
|
13
|
+
export function assertAgentSchema(value) {
|
|
14
|
+
function visit(schema, depth = 0) {
|
|
15
|
+
record(schema); keys(schema, SCHEMA_KEYS);
|
|
16
|
+
if (depth > 6 || !['object', 'array', 'string', 'integer', 'number', 'boolean', 'null'].includes(schema.type)) invalid();
|
|
17
|
+
if (schema.type === 'object') {
|
|
18
|
+
record(schema.properties);
|
|
19
|
+
if (schema.additionalProperties !== false || Object.keys(schema.properties).length > 32) invalid();
|
|
20
|
+
const required = schema.required ?? [];
|
|
21
|
+
if (!Array.isArray(required) || new Set(required).size !== required.length ||
|
|
22
|
+
required.some(key => typeof key !== 'string' || !Object.hasOwn(schema.properties, key))) invalid();
|
|
23
|
+
for (const [key, child] of Object.entries(schema.properties)) {
|
|
24
|
+
if (key.trim() !== key || !FIELD.test(key) || RESERVED.has(key)) invalid();
|
|
25
|
+
visit(child, depth + 1);
|
|
26
|
+
}
|
|
27
|
+
} else if (schema.type === 'array') visit(schema.items, depth + 1);
|
|
28
|
+
else if (schema.properties !== undefined || schema.items !== undefined || schema.required !== undefined ||
|
|
29
|
+
schema.additionalProperties !== undefined) invalid();
|
|
30
|
+
for (const key of ['minLength', 'maxLength', 'minItems', 'maxItems']) {
|
|
31
|
+
if (schema[key] !== undefined && (!Number.isSafeInteger(schema[key]) || schema[key] < 0 || schema[key] > 8192)) invalid();
|
|
32
|
+
}
|
|
33
|
+
for (const [min, max] of [['minLength', 'maxLength'], ['minItems', 'maxItems'], ['minimum', 'maximum']]) {
|
|
34
|
+
if (schema[min] !== undefined && schema[max] !== undefined && schema[min] > schema[max]) invalid();
|
|
35
|
+
}
|
|
36
|
+
for (const key of ['minimum', 'maximum']) if (schema[key] !== undefined &&
|
|
37
|
+
(typeof schema[key] !== 'number' || !Number.isFinite(schema[key]))) invalid();
|
|
38
|
+
if (schema.enum !== undefined && (!Array.isArray(schema.enum) || !schema.enum.length || schema.enum.length > 64 ||
|
|
39
|
+
schema.enum.some(item => item !== null && (!['string', 'boolean', 'number'].includes(typeof item) || typeof item === 'number' && !Number.isFinite(item))))) invalid();
|
|
40
|
+
if (schema.const !== undefined && schema.const !== null && (!['string', 'boolean', 'number'].includes(typeof schema.const) || typeof schema.const === 'number' && !Number.isFinite(schema.const))) invalid();
|
|
41
|
+
for (const key of ['title', 'description']) if (schema[key] !== undefined) text(schema[key], 1200);
|
|
42
|
+
if (schema.pattern !== undefined) { text(schema.pattern, 256); try { new RegExp(schema.pattern); } catch { invalid(); } }
|
|
43
|
+
}
|
|
44
|
+
visit(value);
|
|
45
|
+
if (value.type !== 'object' || new TextEncoder().encode(JSON.stringify(value)).length > 16384) invalid();
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function assertAgentManifest(value) {
|
|
50
|
+
record(value); keys(value, ['key', 'name', 'systemPrompt', 'tools', 'actionRefs', 'aiExposure']);
|
|
51
|
+
if (typeof value.key !== "string" || value.key.trim() !== value.key || !KEY.test(value.key)) invalid();
|
|
52
|
+
text(value.name, 80); text(value.systemPrompt, 16384);
|
|
53
|
+
if (new TextEncoder().encode(value.systemPrompt).length > 16384 || !Array.isArray(value.tools) || value.tools.length > 32) invalid();
|
|
54
|
+
const seen = new Set();
|
|
55
|
+
for (const tool of value.tools) {
|
|
56
|
+
if (seen.has(tool.key)) invalid();
|
|
57
|
+
seen.add(tool.key);
|
|
58
|
+
assertToolManifest(tool, `agent-tool/${value.key}/${tool.key}`);
|
|
59
|
+
}
|
|
60
|
+
return value;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function assertToolManifest(tool, entry) {
|
|
64
|
+
record(tool); keys(tool, ['artifact', 'key', 'entry', 'name', 'description', 'inputSchema', 'outputSchema', 'clientResult', 'aiExposure', 'toolRenderer']);
|
|
65
|
+
if (typeof tool.key !== 'string' || tool.key.trim() !== tool.key || !KEY.test(tool.key) ||
|
|
66
|
+
tool.artifact !== 'server' || tool.entry !== entry || !['none', 'output'].includes(tool.clientResult)) invalid();
|
|
67
|
+
text(tool.name, 80); text(tool.description, 1200);
|
|
68
|
+
assertAgentSchema(tool.inputSchema); assertAgentSchema(tool.outputSchema);
|
|
69
|
+
if (tool.toolRenderer !== undefined) assertToolRendererReference(tool.toolRenderer);
|
|
70
|
+
if (tool.aiExposure !== undefined) {
|
|
71
|
+
if (Object.hasOwn(tool.inputSchema.properties, 'explanation')) invalid();
|
|
72
|
+
assertAiExposure(tool.aiExposure, tool.inputSchema);
|
|
73
|
+
}
|
|
74
|
+
return tool;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function assertToolRendererReference(value) {
|
|
78
|
+
record(value); keys(value, ['key', 'showByDefault', 'dedupeBy']);
|
|
79
|
+
if (typeof value.key !== 'string' || !KEY.test(value.key) || typeof value.showByDefault !== 'boolean') invalid();
|
|
80
|
+
if (value.dedupeBy !== undefined && (typeof value.dedupeBy !== 'string' || value.dedupeBy.length > 100
|
|
81
|
+
|| !/^[A-Za-z][A-Za-z0-9_]*(?:\.[A-Za-z][A-Za-z0-9_]*){0,3}$/.test(value.dedupeBy)
|
|
82
|
+
|| value.dedupeBy.split('.').some(key => ['__proto__', 'prototype', 'constructor'].includes(key)))) invalid();
|
|
83
|
+
return value;
|
|
84
|
+
}
|