@xfey/tutti 0.1.107 → 0.1.108
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 +1 -0
- package/dist/collaboration-ingestion/external-source.js +10 -1
- package/dist/control-plane/task-source-context-types.d.ts +2 -1
- package/dist/control-plane/task-source-context.js +7 -0
- package/dist/providers/openai/app-server/read-only-procedure.js +3 -0
- package/dist/providers/openai/app-server/skills.d.ts +4 -1
- package/dist/providers/openai/app-server/skills.js +12 -2
- package/dist/providers/openai/app-server/workspace-write-run.js +14 -10
- package/dist/reference-access/cache.d.ts +50 -0
- package/dist/reference-access/cache.js +268 -0
- package/dist/reference-access/read-evidence.d.ts +6 -0
- package/dist/reference-access/read-evidence.js +15 -0
- package/dist/reference-access/service.d.ts +26 -0
- package/dist/reference-access/service.js +42 -0
- package/dist/server-shell/cli/external-reference-runtime.d.ts +12 -0
- package/dist/server-shell/cli/external-reference-runtime.js +75 -0
- package/dist/server-shell/cli/host-server-runtime.js +17 -1
- package/dist/server-shell/cli/relay-registration.js +1 -0
- package/dist/server-shell/http/create-server.d.ts +2 -0
- package/dist/server-shell/http/create-server.js +5 -4
- package/dist/server-shell/http/routes/agent-context-auth.d.ts +2 -1
- package/dist/server-shell/http/routes/agent-context-auth.js +3 -1
- package/dist/server-shell/http/routes/agent-context-external-reference-routes.d.ts +4 -0
- package/dist/server-shell/http/routes/agent-context-external-reference-routes.js +83 -0
- package/dist/server-shell/http/routes/agent-context-shared.d.ts +2 -0
- package/dist/server-shell/http/routes/agent-context.js +3 -1
- package/dist/workspace-ops/project-workspace.js +6 -6
- package/dist/workspace-ops/reference-cache-policy.d.ts +6 -0
- package/dist/workspace-ops/reference-cache-policy.js +79 -0
- package/migrations/0021_external_reference_reads.sql +11 -0
- package/node_modules/@tutti/relay-client/dist/external-references.d.ts +11 -0
- package/node_modules/@tutti/relay-client/dist/external-references.js +63 -0
- package/node_modules/@tutti/relay-client/dist/host-control.d.ts +1 -0
- package/node_modules/@tutti/relay-client/dist/host-control.js +1 -0
- package/node_modules/@tutti/relay-client/dist/index.d.ts +1 -0
- package/node_modules/@tutti/relay-client/dist/index.js +1 -0
- package/node_modules/@tutti/shared/dist/schemas/api/clarifications.d.ts +6 -0
- package/node_modules/@tutti/shared/dist/schemas/api/external-references.d.ts +23 -0
- package/node_modules/@tutti/shared/dist/schemas/api/external-references.js +21 -0
- package/node_modules/@tutti/shared/dist/schemas/api/feishu-documents.d.ts +6 -1
- package/node_modules/@tutti/shared/dist/schemas/api/feishu-documents.js +16 -3
- package/node_modules/@tutti/shared/dist/schemas/api/index.d.ts +3 -2
- package/node_modules/@tutti/shared/dist/schemas/api/index.js +2 -1
- package/node_modules/@tutti/shared/dist/schemas/api/messages.d.ts +24 -0
- package/node_modules/@tutti/shared/dist/schemas/api/messages.js +2 -0
- package/node_modules/@tutti/shared/dist/schemas/api/platform-sessions.d.ts +7 -1
- package/node_modules/@tutti/shared/dist/schemas/api/platform-sessions.js +17 -3
- package/node_modules/@tutti/shared/dist/schemas/api/viewer-reference.d.ts +6 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/external-references.d.ts +109 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/external-references.js +70 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/feishu-documents.d.ts +11 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/feishu-documents.js +5 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/index.d.ts +1 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/index.js +1 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/platform-integration.d.ts +36 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/platform-integration.js +3 -0
- package/package.json +1 -1
- package/prompts/skills/read-references/SKILL.md +32 -3
- package/web/assets/{homepage-motion-scene-CFs5rMWq.js → homepage-motion-scene-B6yPJnY8.js} +1 -1
- package/web/assets/{index-BmiLZmaV.js → index-aQnJ5JoI.js} +3 -3
- package/web/index.html +1 -1
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { requestExternalReferences } from "@tutti/relay-client";
|
|
4
|
+
import { isExternalReferenceList, isExternalReferenceReadResult, } from "@tutti/shared/schemas/internal";
|
|
5
|
+
import { ExternalReferenceCache } from "../../reference-access/cache.js";
|
|
6
|
+
import { ExternalReferenceAccess, } from "../../reference-access/service.js";
|
|
7
|
+
import { recordExternalReferenceRead } from "../../reference-access/read-evidence.js";
|
|
8
|
+
import { downloadStagedUploadWithResolver, cleanupDownloadedStagedUpload, } from "../http/routes/project-api/staged-uploads.js";
|
|
9
|
+
import { readHostRegistrationSecret } from "./machine-local.js";
|
|
10
|
+
export function createExternalReferenceRuntime(options) {
|
|
11
|
+
const { preparation } = options;
|
|
12
|
+
async function request(action, invocation, sourceRef) {
|
|
13
|
+
const relay = options.getLaunchStatus().relay;
|
|
14
|
+
if (!relay)
|
|
15
|
+
throw new Error("reference_host_offline");
|
|
16
|
+
const secret = readHostRegistrationSecret(preparation.tutti_home, preparation.project_id);
|
|
17
|
+
return requestExternalReferences({
|
|
18
|
+
relayUrl: preparation.relay_url,
|
|
19
|
+
body: {
|
|
20
|
+
project_identity: { source: "git_config", project_id: preparation.project_id },
|
|
21
|
+
host_auth: { kind: "host_registration_secret_v1", secret: secret.secret },
|
|
22
|
+
relay_project_ref: relay.relay_project_ref,
|
|
23
|
+
host_connection_ref: relay.host_connection_ref,
|
|
24
|
+
request_ref: `eref_${randomUUID().replaceAll("-", "")}`,
|
|
25
|
+
invocation_ref: invocation.reference_invocation_ref,
|
|
26
|
+
action,
|
|
27
|
+
...(sourceRef === undefined ? {} : { source_ref: sourceRef }),
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return new ExternalReferenceAccess({
|
|
32
|
+
createCache: () => new ExternalReferenceCache({
|
|
33
|
+
workspaceRoot: preparation.workspace_root,
|
|
34
|
+
ledgerRoot: join(preparation.tutti_home, "projects", preparation.project_id, "reference-cache"),
|
|
35
|
+
}),
|
|
36
|
+
list: async (invocation) => {
|
|
37
|
+
const result = await request("list", invocation);
|
|
38
|
+
if (!isExternalReferenceList(result))
|
|
39
|
+
throw new Error("reference_list_invalid");
|
|
40
|
+
return result.items;
|
|
41
|
+
},
|
|
42
|
+
read: async (sourceRef, invocation) => {
|
|
43
|
+
const result = await request("read", invocation, sourceRef);
|
|
44
|
+
if (!isExternalReferenceReadResult(result) || result.source.source_ref !== sourceRef)
|
|
45
|
+
throw new Error("reference_read_invalid");
|
|
46
|
+
if (result.size_bytes === 0)
|
|
47
|
+
return {
|
|
48
|
+
content: Buffer.alloc(0),
|
|
49
|
+
revision: result.revision,
|
|
50
|
+
media_type: result.media_type,
|
|
51
|
+
};
|
|
52
|
+
if (!result.upload_id)
|
|
53
|
+
throw new Error("reference_read_invalid");
|
|
54
|
+
const downloaded = await downloadStagedUploadWithResolver({
|
|
55
|
+
resolveStagedUpload: options.resolveStagedUpload,
|
|
56
|
+
purpose: "reference_file",
|
|
57
|
+
expected: {
|
|
58
|
+
upload_id: result.upload_id,
|
|
59
|
+
file_name: `${sourceRef}.bin`,
|
|
60
|
+
size_bytes: result.size_bytes,
|
|
61
|
+
sha256: `sha256:${result.sha256}`,
|
|
62
|
+
media_type: result.media_type,
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
await cleanupDownloadedStagedUpload(downloaded);
|
|
66
|
+
return {
|
|
67
|
+
content: downloaded.content,
|
|
68
|
+
revision: result.revision,
|
|
69
|
+
media_type: result.media_type,
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
recordRead: (invocation, copy) => recordExternalReferenceRead(options.store.db, invocation, copy),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=external-reference-runtime.js.map
|
|
@@ -27,6 +27,7 @@ import { createTrustedRelaySessionMetadataStore, } from "../session/relay-sessio
|
|
|
27
27
|
import { LaunchError } from "./errors.js";
|
|
28
28
|
import { HostProjectRuntimeLifecycle } from "./host-project-runtime-lifecycle.js";
|
|
29
29
|
import { readCliVersion } from "./version.js";
|
|
30
|
+
import { createExternalReferenceRuntime } from "./external-reference-runtime.js";
|
|
30
31
|
import { relayStateFromLaunchStatus } from "./host-relay-status.js";
|
|
31
32
|
import { appendHostLogLine, readHostRegistrationSecret, createMachineRuntimeEndpointToken, deleteMachineRuntimeEndpoint, ensureHostLogFile, getHostLogFilePath, updateMachineProjectDisplayNameSnapshot, writeMachineRuntimeEndpoint, } from "./machine-local.js";
|
|
32
33
|
import { readProjectDescription, readProjectDisplayName, writeProjectDescription, writeProjectDisplayName, } from "./project-identity.js";
|
|
@@ -128,6 +129,7 @@ function createAgentContextRuntime(options) {
|
|
|
128
129
|
baseUrl: new URL(AGENT_CONTEXT_BASE_PATH, options.baseUrl).toString(),
|
|
129
130
|
issueToken: (input) => {
|
|
130
131
|
const token = options.registry.issueToken({
|
|
132
|
+
...(input.activityRef === undefined ? {} : { activityRef: input.activityRef }),
|
|
131
133
|
projectId: options.projectId,
|
|
132
134
|
selection: input.selectionName,
|
|
133
135
|
scopes: input.scopes,
|
|
@@ -533,7 +535,21 @@ export async function startForegroundHostServer(options) {
|
|
|
533
535
|
: { getLaunchStatus: options.getLaunchStatus }),
|
|
534
536
|
});
|
|
535
537
|
app = options.createServer({
|
|
536
|
-
agentContext: {
|
|
538
|
+
agentContext: {
|
|
539
|
+
tokenRegistry: agentContextTokenRegistry,
|
|
540
|
+
...(options.preparation.machine_local.binding.surface_profile.kind === "feishu" &&
|
|
541
|
+
options.getLaunchStatus !== undefined &&
|
|
542
|
+
resolveStagedUpload !== undefined
|
|
543
|
+
? {
|
|
544
|
+
externalReferences: createExternalReferenceRuntime({
|
|
545
|
+
preparation: options.preparation,
|
|
546
|
+
store,
|
|
547
|
+
getLaunchStatus: options.getLaunchStatus,
|
|
548
|
+
resolveStagedUpload,
|
|
549
|
+
}),
|
|
550
|
+
}
|
|
551
|
+
: {}),
|
|
552
|
+
},
|
|
537
553
|
serviceVersion: HOST_SERVER_VERSION,
|
|
538
554
|
health: {
|
|
539
555
|
readiness: {
|
|
@@ -37,6 +37,7 @@ export async function registerHostWithRelay(options) {
|
|
|
37
37
|
joinToken: { mode: options.joinTokenMode ?? "rotate" },
|
|
38
38
|
capabilities: {
|
|
39
39
|
integration_source_ingestion_v1: true,
|
|
40
|
+
integration_external_references_v1: true,
|
|
40
41
|
integration_attachment_ingestion_v1: true,
|
|
41
42
|
integration_managed_reference_ingestion_v1: true,
|
|
42
43
|
integration_outbound_v1: true,
|
|
@@ -3,9 +3,11 @@ import { type HostLocalControlOptions } from "./routes/local-control.js";
|
|
|
3
3
|
import { type HostProjectApiRouteOptions } from "./routes/project-api.js";
|
|
4
4
|
import { AgentContextTokenRegistry } from "./routes/agent-context.js";
|
|
5
5
|
import { type HostWebStaticOptions } from "./static-web.js";
|
|
6
|
+
import type { ExternalReferenceAccess } from "../../reference-access/service.js";
|
|
6
7
|
export type HostServerOptions = {
|
|
7
8
|
agentContext?: false | {
|
|
8
9
|
tokenRegistry?: AgentContextTokenRegistry;
|
|
10
|
+
externalReferences?: ExternalReferenceAccess;
|
|
9
11
|
};
|
|
10
12
|
health?: {
|
|
11
13
|
readiness?: {
|
|
@@ -3,7 +3,7 @@ import { createRedactedLoggerOptions } from "./logger.js";
|
|
|
3
3
|
import { registerHostHealthRoute } from "./routes/health.js";
|
|
4
4
|
import { registerHostLocalControlRoutes, } from "./routes/local-control.js";
|
|
5
5
|
import { registerHostProjectApiRoutes, } from "./routes/project-api.js";
|
|
6
|
-
import { AgentContextTokenRegistry, registerAgentContextRoutes
|
|
6
|
+
import { AgentContextTokenRegistry, registerAgentContextRoutes } from "./routes/agent-context.js";
|
|
7
7
|
import { registerHostWebStaticRoutes } from "./static-web.js";
|
|
8
8
|
import { registerApiErrorHandler } from "./validation.js";
|
|
9
9
|
export function createHostServer(options = {}) {
|
|
@@ -12,9 +12,7 @@ export function createHostServer(options = {}) {
|
|
|
12
12
|
: fastify({ logger: createRedactedLoggerOptions("tutti-host", options.logger) });
|
|
13
13
|
registerApiErrorHandler(app);
|
|
14
14
|
registerHostHealthRoute(app, {
|
|
15
|
-
...(options.health?.readiness === undefined
|
|
16
|
-
? {}
|
|
17
|
-
: { readiness: options.health.readiness }),
|
|
15
|
+
...(options.health?.readiness === undefined ? {} : { readiness: options.health.readiness }),
|
|
18
16
|
now: options.now ?? (() => new Date()),
|
|
19
17
|
serviceVersion: options.serviceVersion ?? "0.0.0",
|
|
20
18
|
});
|
|
@@ -31,6 +29,9 @@ export function createHostServer(options = {}) {
|
|
|
31
29
|
}
|
|
32
30
|
if (options.agentContext !== false) {
|
|
33
31
|
registerAgentContextRoutes(app, {
|
|
32
|
+
...(options.agentContext?.externalReferences === undefined
|
|
33
|
+
? {}
|
|
34
|
+
: { externalReferences: options.agentContext.externalReferences }),
|
|
34
35
|
project: projectApiOptions.project,
|
|
35
36
|
...(projectApiOptions.store === undefined ? {} : { store: projectApiOptions.store }),
|
|
36
37
|
...(projectApiOptions.workspaceOps === undefined
|
|
@@ -3,6 +3,7 @@ import type { ActivityRef, ProjectId } from "@tutti/shared/ids";
|
|
|
3
3
|
import type { CodexAppServerContextApiScope, CodexAppServerSkillSelectionName } from "../../../providers/openai/app-server/skills.js";
|
|
4
4
|
export declare const DEFAULT_AGENT_CONTEXT_TOKEN_TTL_MS: number;
|
|
5
5
|
export type AgentContextTokenRecord = {
|
|
6
|
+
reference_invocation_ref: string;
|
|
6
7
|
token: string;
|
|
7
8
|
project_id: ProjectId;
|
|
8
9
|
activity_ref?: ActivityRef;
|
|
@@ -37,5 +38,5 @@ export declare function authorizeAgentContextRequest(request: FastifyRequest, op
|
|
|
37
38
|
project_id: ProjectId;
|
|
38
39
|
};
|
|
39
40
|
tokenRegistry: AgentContextTokenRegistry;
|
|
40
|
-
}, requiredScope: CodexAppServerContextApiScope):
|
|
41
|
+
}, requiredScope: CodexAppServerContextApiScope): AgentContextTokenRecord;
|
|
41
42
|
//# sourceMappingURL=agent-context-auth.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { createEventRef } from "@tutti/shared/ids";
|
|
2
3
|
export const DEFAULT_AGENT_CONTEXT_TOKEN_TTL_MS = 30 * 60 * 1000;
|
|
3
4
|
export class AgentContextTokenRegistry {
|
|
4
5
|
options;
|
|
@@ -11,6 +12,7 @@ export class AgentContextTokenRegistry {
|
|
|
11
12
|
const ttlMs = input.ttlMs ?? this.options.ttlMs ?? DEFAULT_AGENT_CONTEXT_TOKEN_TTL_MS;
|
|
12
13
|
const token = this.options.tokenFactory?.() ?? `agctx_${randomBytes(32).toString("base64url")}`;
|
|
13
14
|
const record = {
|
|
15
|
+
reference_invocation_ref: createEventRef(),
|
|
14
16
|
token,
|
|
15
17
|
project_id: input.projectId,
|
|
16
18
|
...(input.activityRef === undefined ? {} : { activity_ref: input.activityRef }),
|
|
@@ -64,7 +66,7 @@ function bearerToken(request) {
|
|
|
64
66
|
return match[1].trim();
|
|
65
67
|
}
|
|
66
68
|
export function authorizeAgentContextRequest(request, options, requiredScope) {
|
|
67
|
-
options.tokenRegistry.validate({
|
|
69
|
+
return options.tokenRegistry.validate({
|
|
68
70
|
token: bearerToken(request),
|
|
69
71
|
projectId: options.project.project_id,
|
|
70
72
|
requiredScope,
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { FastifyInstance } from "fastify";
|
|
2
|
+
import type { AgentContextRouteOptions } from "./agent-context-shared.js";
|
|
3
|
+
export declare function registerAgentContextExternalReferenceRoutes(app: FastifyInstance, options: AgentContextRouteOptions): void;
|
|
4
|
+
//# sourceMappingURL=agent-context-external-reference-routes.d.ts.map
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { redactText } from "@tutti/shared/utils";
|
|
3
|
+
import { ExternalReferenceTransportError } from "@tutti/relay-client";
|
|
4
|
+
import { ReferenceCacheError } from "../../../reference-access/cache.js";
|
|
5
|
+
import { WorkspaceOpsError } from "../../../workspace-ops/errors.js";
|
|
6
|
+
import { authorizeAgentContextRequest } from "./agent-context-auth.js";
|
|
7
|
+
import { AGENT_CONTEXT_BASE_PATH } from "./agent-context-shared.js";
|
|
8
|
+
export function registerAgentContextExternalReferenceRoutes(app, options) {
|
|
9
|
+
for (const action of ["list", "read"])
|
|
10
|
+
app.get(`${AGENT_CONTEXT_BASE_PATH}/references/external${action === "read" ? "/file" : ""}`, {
|
|
11
|
+
schema: {
|
|
12
|
+
querystring: action === "read"
|
|
13
|
+
? Type.Object({ source_ref: Type.String({ pattern: "^external_ref_[a-f0-9]{32}$" }) }, { additionalProperties: false })
|
|
14
|
+
: Type.Object({}, { additionalProperties: false }),
|
|
15
|
+
},
|
|
16
|
+
}, async (request, reply) => {
|
|
17
|
+
reply.header("cache-control", "no-store");
|
|
18
|
+
const invocation = authorizeAgentContextRequest(request, options, "references:external");
|
|
19
|
+
const service = options.externalReferences;
|
|
20
|
+
if (!service)
|
|
21
|
+
return { kind: "unavailable", reason: "external_references_not_enabled" };
|
|
22
|
+
const sourceRef = request.query.source_ref;
|
|
23
|
+
if (action === "read" &&
|
|
24
|
+
(sourceRef === undefined || !/^external_ref_[a-f0-9]{32}$/u.test(sourceRef)))
|
|
25
|
+
return { kind: "unavailable", reason: "reference_source_invalid" };
|
|
26
|
+
try {
|
|
27
|
+
if (action === "list")
|
|
28
|
+
return { items: await service.list(invocation) };
|
|
29
|
+
const result = await service.read(invocation, sourceRef);
|
|
30
|
+
const { copy } = result;
|
|
31
|
+
request.log.info({
|
|
32
|
+
stage: "external_reference_read",
|
|
33
|
+
invocation_ref: invocation.reference_invocation_ref,
|
|
34
|
+
activity_ref: invocation.activity_ref,
|
|
35
|
+
source_ref: copy.source_ref,
|
|
36
|
+
revision: copy.revision,
|
|
37
|
+
sha256: copy.sha256,
|
|
38
|
+
cache_hit: result.cache_hit,
|
|
39
|
+
size_bytes: copy.size_bytes,
|
|
40
|
+
}, "External Reference working copy ready");
|
|
41
|
+
let text;
|
|
42
|
+
if (!result.content.includes(0)) {
|
|
43
|
+
try {
|
|
44
|
+
text = new TextDecoder("utf-8", { fatal: true }).decode(result.content);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
/* Binary resources are available as a controlled local working copy. */
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const limit = 128 * 1024;
|
|
51
|
+
const safeText = text === undefined ? undefined : Buffer.from(redactText(text), "utf8");
|
|
52
|
+
return {
|
|
53
|
+
kind: text === undefined ? "local_file" : "text",
|
|
54
|
+
...copy,
|
|
55
|
+
cache_hit: result.cache_hit,
|
|
56
|
+
...(safeText === undefined
|
|
57
|
+
? {}
|
|
58
|
+
: {
|
|
59
|
+
text: safeText.subarray(0, limit).toString("utf8"),
|
|
60
|
+
truncated: safeText.byteLength > limit,
|
|
61
|
+
}),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
const reason = error instanceof ReferenceCacheError
|
|
66
|
+
? error.code
|
|
67
|
+
: error instanceof ExternalReferenceTransportError
|
|
68
|
+
? error.reasonCode
|
|
69
|
+
: error instanceof WorkspaceOpsError
|
|
70
|
+
? "reference_cache_unsafe"
|
|
71
|
+
: "reference_read_unavailable";
|
|
72
|
+
request.log.warn({
|
|
73
|
+
stage: "external_reference_read",
|
|
74
|
+
invocation_ref: invocation.reference_invocation_ref,
|
|
75
|
+
activity_ref: invocation.activity_ref,
|
|
76
|
+
...(sourceRef === undefined ? {} : { source_ref: sourceRef }),
|
|
77
|
+
reason_code: reason,
|
|
78
|
+
}, "External Reference read unavailable");
|
|
79
|
+
return { kind: "unavailable", reason };
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=agent-context-external-reference-routes.js.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { HostProjectStore } from "../../../store/index.js";
|
|
2
2
|
import type { HostProjectApiRouteOptions } from "./project-api.js";
|
|
3
3
|
import type { AgentContextTokenRegistry } from "./agent-context-auth.js";
|
|
4
|
+
import type { ExternalReferenceAccess } from "../../../reference-access/service.js";
|
|
4
5
|
export declare const AGENT_CONTEXT_BASE_PATH: "/internal/agent-context";
|
|
5
6
|
export declare const MESSAGE_LIMIT_DEFAULT = 50;
|
|
6
7
|
export declare const MESSAGE_LIMIT_MAX = 100;
|
|
@@ -27,6 +28,7 @@ export declare const USER_SKILL_MD_DEFAULT_MAX_CHARS = 20000;
|
|
|
27
28
|
export declare const USER_SKILL_MD_MAX_CHARS = 100000;
|
|
28
29
|
export type AgentContextRouteOptions = Pick<HostProjectApiRouteOptions, "project" | "store" | "workspaceOps" | "tuttiHome" | "now"> & {
|
|
29
30
|
tokenRegistry: AgentContextTokenRegistry;
|
|
31
|
+
externalReferences?: ExternalReferenceAccess;
|
|
30
32
|
};
|
|
31
33
|
export declare function requireProjectStore(options: AgentContextRouteOptions): HostProjectStore;
|
|
32
34
|
export declare function requireWorkspaceRoot(options: AgentContextRouteOptions): string;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { registerAgentContextMessageRoutes } from "./agent-context-messages-routes.js";
|
|
2
2
|
import { registerAgentContextProjectDocRoutes } from "./agent-context-project-doc-routes.js";
|
|
3
3
|
import { registerAgentContextReferenceRoutes } from "./agent-context-reference-routes.js";
|
|
4
|
+
import { registerAgentContextExternalReferenceRoutes } from "./agent-context-external-reference-routes.js";
|
|
4
5
|
import { registerAgentContextSkillRoutes } from "./agent-context-skills-routes.js";
|
|
5
|
-
import { AGENT_CONTEXT_BASE_PATH
|
|
6
|
+
import { AGENT_CONTEXT_BASE_PATH } from "./agent-context-shared.js";
|
|
6
7
|
import { registerAgentContextWorkspaceRoutes } from "./agent-context-workspace-routes.js";
|
|
7
8
|
export { DEFAULT_AGENT_CONTEXT_TOKEN_TTL_MS, AgentContextTokenRegistry, } from "./agent-context-auth.js";
|
|
8
9
|
export { AGENT_CONTEXT_BASE_PATH };
|
|
@@ -11,6 +12,7 @@ export function registerAgentContextRoutes(app, options) {
|
|
|
11
12
|
registerAgentContextProjectDocRoutes(app, options);
|
|
12
13
|
registerAgentContextWorkspaceRoutes(app, options);
|
|
13
14
|
registerAgentContextReferenceRoutes(app, options);
|
|
15
|
+
registerAgentContextExternalReferenceRoutes(app, options);
|
|
14
16
|
registerAgentContextSkillRoutes(app, options);
|
|
15
17
|
}
|
|
16
18
|
//# sourceMappingURL=agent-context.js.map
|
|
@@ -2,6 +2,7 @@ import { classifyRepositoryPathMetadata } from "@tutti/shared/utils";
|
|
|
2
2
|
import { WorkspaceOpsError } from "./errors.js";
|
|
3
3
|
import { runGit, runGitRaw, runGitText, stagedChangesExist, summarizeGitFailure, tryGitText, } from "./git.js";
|
|
4
4
|
import { requireMainlineBranch } from "./mainline.js";
|
|
5
|
+
import { assertReferenceCacheIsolation } from "./reference-cache-policy.js";
|
|
5
6
|
const MAX_RUN_DIFF_SUMMARY_BYTES = 32 * 1024;
|
|
6
7
|
const ACCEPTED_TAG_PREFIX = "tutti/accepted/";
|
|
7
8
|
function truncateText(text, maxBytes) {
|
|
@@ -48,6 +49,7 @@ function requireGitCommandSuccess(args, workspaceRoot) {
|
|
|
48
49
|
return result;
|
|
49
50
|
}
|
|
50
51
|
export function collectProjectSourceDiff(workspaceRoot) {
|
|
52
|
+
assertReferenceCacheIsolation(workspaceRoot);
|
|
51
53
|
const status = requireGitCommandSuccess(["status", "--porcelain=v1", "-z"], workspaceRoot);
|
|
52
54
|
const changedPaths = parsePorcelainChangedPaths(status.stdout);
|
|
53
55
|
const summaryParts = [];
|
|
@@ -80,12 +82,8 @@ function acceptedTagName(workspaceRoot, activityRef) {
|
|
|
80
82
|
return tagName;
|
|
81
83
|
}
|
|
82
84
|
function nearestAcceptedCommit(workspaceRoot) {
|
|
83
|
-
const refs = requireGitCommandSuccess([
|
|
84
|
-
"
|
|
85
|
-
"--format=%(objectname) %(refname:short)",
|
|
86
|
-
`refs/tags/${ACCEPTED_TAG_PREFIX}`,
|
|
87
|
-
], workspaceRoot).stdout
|
|
88
|
-
.toString("utf8")
|
|
85
|
+
const refs = requireGitCommandSuccess(["for-each-ref", "--format=%(objectname) %(refname:short)", `refs/tags/${ACCEPTED_TAG_PREFIX}`], workspaceRoot)
|
|
86
|
+
.stdout.toString("utf8")
|
|
89
87
|
.trim();
|
|
90
88
|
if (refs.length === 0) {
|
|
91
89
|
return null;
|
|
@@ -209,7 +207,9 @@ export function createAcceptedCheckpoint(options) {
|
|
|
209
207
|
};
|
|
210
208
|
}
|
|
211
209
|
const inspection = inspectProjectWorkspace(options.workspaceRoot);
|
|
210
|
+
assertReferenceCacheIsolation(options.workspaceRoot);
|
|
212
211
|
runGit(["add", "-A"], options.workspaceRoot);
|
|
212
|
+
assertReferenceCacheIsolation(options.workspaceRoot);
|
|
213
213
|
if (!stagedChangesExist(options.workspaceRoot)) {
|
|
214
214
|
throw new WorkspaceOpsError("validation_failed", "Accepted checkpoint has no staged source changes", { reason_code: "accepted_checkpoint_candidate_missing" });
|
|
215
215
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const REFERENCE_CACHE_PATH = "docs/reference/.tutti-cache";
|
|
2
|
+
/** Only the exact managed namespace is eligible. Never adopt an existing directory. */
|
|
3
|
+
export declare function initializeReferenceCacheDirectory(workspaceRoot: string): string;
|
|
4
|
+
/** Fail closed on force-added cache or ignore drift; never unstage or delete user changes. */
|
|
5
|
+
export declare function assertReferenceCacheIsolation(workspaceRoot: string, required?: boolean): void;
|
|
6
|
+
//# sourceMappingURL=reference-cache-policy.d.ts.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { lstatSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
3
|
+
import { WorkspaceOpsError } from "./errors.js";
|
|
4
|
+
import { runGit, runGitRaw, runGitText, tryGitText } from "./git.js";
|
|
5
|
+
export const REFERENCE_CACHE_PATH = "docs/reference/.tutti-cache";
|
|
6
|
+
const CONFIG_KEY = "tutti.reference-cache-path";
|
|
7
|
+
function reject() {
|
|
8
|
+
throw new WorkspaceOpsError("conflict", "Reference cache isolation is unavailable", {
|
|
9
|
+
reason_code: "reference_cache_unsafe",
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
/** Only the exact managed namespace is eligible. Never adopt an existing directory. */
|
|
13
|
+
export function initializeReferenceCacheDirectory(workspaceRoot) {
|
|
14
|
+
const root = realpathSync(workspaceRoot);
|
|
15
|
+
const configured = tryGitText(["config", "--local", "--get", CONFIG_KEY], root);
|
|
16
|
+
if (configured !== null && configured !== REFERENCE_CACHE_PATH)
|
|
17
|
+
reject();
|
|
18
|
+
let current = root;
|
|
19
|
+
for (const part of REFERENCE_CACHE_PATH.split("/")) {
|
|
20
|
+
current = join(current, part);
|
|
21
|
+
const stat = lstatSync(current, { throwIfNoEntry: false });
|
|
22
|
+
if (stat !== undefined) {
|
|
23
|
+
if (!stat.isDirectory() || stat.isSymbolicLink())
|
|
24
|
+
reject();
|
|
25
|
+
if (current === join(root, REFERENCE_CACHE_PATH) && configured === null)
|
|
26
|
+
reject();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (runGitText(["ls-files", "--", REFERENCE_CACHE_PATH], root) !== "")
|
|
30
|
+
reject();
|
|
31
|
+
// Git-local exclusion preserves the project's own .gitignore and unrelated index state.
|
|
32
|
+
const gitPath = runGitText(["rev-parse", "--git-path", "info/exclude"], root);
|
|
33
|
+
const exclude = isAbsolute(gitPath) ? gitPath : resolve(root, gitPath);
|
|
34
|
+
const gitDirectory = realpathSync(dirname(dirname(exclude)));
|
|
35
|
+
const info = join(gitDirectory, "info");
|
|
36
|
+
const infoStat = lstatSync(info, { throwIfNoEntry: false });
|
|
37
|
+
if (infoStat !== undefined && (!infoStat.isDirectory() || infoStat.isSymbolicLink()))
|
|
38
|
+
reject();
|
|
39
|
+
mkdirSync(info, { recursive: true });
|
|
40
|
+
const excludeStat = lstatSync(exclude, { throwIfNoEntry: false });
|
|
41
|
+
if (excludeStat !== undefined && (!excludeStat.isFile() || excludeStat.isSymbolicLink()))
|
|
42
|
+
reject();
|
|
43
|
+
const previous = excludeStat === undefined ? "" : readFileSync(exclude, "utf8");
|
|
44
|
+
const rule = `/${REFERENCE_CACHE_PATH}/`;
|
|
45
|
+
if (!previous.split(/\r?\n/u).includes(rule)) {
|
|
46
|
+
writeFileSync(exclude, `${previous}${previous.endsWith("\n") ? "" : "\n"}${rule}\n`, {
|
|
47
|
+
flag: excludeStat === undefined ? "wx" : "w",
|
|
48
|
+
mode: 0o600,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
runGit(["config", "--local", CONFIG_KEY, REFERENCE_CACHE_PATH], root);
|
|
52
|
+
assertReferenceCacheIsolation(root);
|
|
53
|
+
mkdirSync(join(root, REFERENCE_CACHE_PATH), { recursive: true, mode: 0o700 });
|
|
54
|
+
return join(root, REFERENCE_CACHE_PATH);
|
|
55
|
+
}
|
|
56
|
+
/** Fail closed on force-added cache or ignore drift; never unstage or delete user changes. */
|
|
57
|
+
export function assertReferenceCacheIsolation(workspaceRoot, required = false) {
|
|
58
|
+
const configured = tryGitText(["config", "--local", "--get", CONFIG_KEY], workspaceRoot);
|
|
59
|
+
if (configured === null) {
|
|
60
|
+
if (required || lstatSync(join(workspaceRoot, REFERENCE_CACHE_PATH), { throwIfNoEntry: false }))
|
|
61
|
+
reject();
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (configured !== REFERENCE_CACHE_PATH)
|
|
65
|
+
reject();
|
|
66
|
+
if (runGitText(["ls-files", "--", REFERENCE_CACHE_PATH], workspaceRoot) !== "")
|
|
67
|
+
reject();
|
|
68
|
+
const ignored = runGitRaw(["check-ignore", "--no-index", "-q", `${REFERENCE_CACHE_PATH}/isolation-probe`], workspaceRoot);
|
|
69
|
+
if (ignored.status !== 0)
|
|
70
|
+
reject();
|
|
71
|
+
let current = realpathSync(workspaceRoot);
|
|
72
|
+
for (const part of REFERENCE_CACHE_PATH.split("/")) {
|
|
73
|
+
current = join(current, part);
|
|
74
|
+
const stat = lstatSync(current, { throwIfNoEntry: false });
|
|
75
|
+
if (stat !== undefined && (!stat.isDirectory() || stat.isSymbolicLink()))
|
|
76
|
+
reject();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=reference-cache-policy.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
CREATE TABLE external_reference_reads (
|
|
2
|
+
invocation_ref TEXT NOT NULL,
|
|
3
|
+
activity_ref TEXT,
|
|
4
|
+
source_ref TEXT NOT NULL,
|
|
5
|
+
revision TEXT NOT NULL,
|
|
6
|
+
content_sha256 TEXT NOT NULL,
|
|
7
|
+
size_bytes INTEGER NOT NULL CHECK (size_bytes >= 0),
|
|
8
|
+
checked_at TEXT NOT NULL,
|
|
9
|
+
PRIMARY KEY (invocation_ref, source_ref)
|
|
10
|
+
);
|
|
11
|
+
CREATE INDEX external_reference_reads_activity ON external_reference_reads(activity_ref);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ExternalReferenceHostRequest } from "@tutti/shared/schemas/internal";
|
|
2
|
+
export declare class ExternalReferenceTransportError extends Error {
|
|
3
|
+
readonly reasonCode: string;
|
|
4
|
+
constructor(reasonCode: string);
|
|
5
|
+
}
|
|
6
|
+
export declare function requestExternalReferences(options: {
|
|
7
|
+
relayUrl: string;
|
|
8
|
+
body: ExternalReferenceHostRequest;
|
|
9
|
+
fetchImpl?: typeof fetch;
|
|
10
|
+
}): Promise<unknown>;
|
|
11
|
+
//# sourceMappingURL=external-references.d.ts.map
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { EXTERNAL_REFERENCE_HOST_PATH, isExternalReferenceList, isExternalReferenceReadResult, } from "@tutti/shared/schemas/internal";
|
|
2
|
+
export class ExternalReferenceTransportError extends Error {
|
|
3
|
+
reasonCode;
|
|
4
|
+
constructor(reasonCode) {
|
|
5
|
+
super(reasonCode);
|
|
6
|
+
this.reasonCode = reasonCode;
|
|
7
|
+
this.name = "ExternalReferenceTransportError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export async function requestExternalReferences(options) {
|
|
11
|
+
try {
|
|
12
|
+
const response = await (options.fetchImpl ?? fetch)(new URL(EXTERNAL_REFERENCE_HOST_PATH, options.relayUrl), {
|
|
13
|
+
method: "POST",
|
|
14
|
+
redirect: "error",
|
|
15
|
+
headers: { "content-type": "application/json" },
|
|
16
|
+
body: JSON.stringify(options.body),
|
|
17
|
+
signal: AbortSignal.timeout(90_000),
|
|
18
|
+
});
|
|
19
|
+
const reader = response.body?.getReader();
|
|
20
|
+
if (!reader)
|
|
21
|
+
throw new ExternalReferenceTransportError("reference_transport_invalid_response");
|
|
22
|
+
const chunks = [];
|
|
23
|
+
let size = 0;
|
|
24
|
+
try {
|
|
25
|
+
for (;;) {
|
|
26
|
+
const next = await reader.read();
|
|
27
|
+
if (next.done)
|
|
28
|
+
break;
|
|
29
|
+
if (!(next.value instanceof Uint8Array))
|
|
30
|
+
throw new ExternalReferenceTransportError("reference_transport_invalid_response");
|
|
31
|
+
size += next.value.byteLength;
|
|
32
|
+
if (size > 512 * 1024)
|
|
33
|
+
throw new ExternalReferenceTransportError("reference_transport_invalid_response");
|
|
34
|
+
chunks.push(next.value);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
await reader.cancel().catch(() => undefined);
|
|
39
|
+
reader.releaseLock();
|
|
40
|
+
}
|
|
41
|
+
const value = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
42
|
+
if (!response.ok) {
|
|
43
|
+
const reason = typeof value === "object" && value !== null && "reason_code" in value
|
|
44
|
+
? value.reason_code
|
|
45
|
+
: null;
|
|
46
|
+
throw new ExternalReferenceTransportError(typeof reason === "string" &&
|
|
47
|
+
/^(reference|document_wiki|feishu|attachment)_[a-z_]{1,70}$/u.test(reason)
|
|
48
|
+
? reason
|
|
49
|
+
: "reference_transport_unavailable");
|
|
50
|
+
}
|
|
51
|
+
if (options.body.action === "list"
|
|
52
|
+
? !isExternalReferenceList(value)
|
|
53
|
+
: !isExternalReferenceReadResult(value))
|
|
54
|
+
throw new ExternalReferenceTransportError("reference_transport_invalid_response");
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
throw error instanceof ExternalReferenceTransportError
|
|
59
|
+
? error
|
|
60
|
+
: new ExternalReferenceTransportError("reference_transport_unavailable");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=external-references.js.map
|
|
@@ -44,6 +44,7 @@ export type RegisterRelayHostConnectionPayload = {
|
|
|
44
44
|
browser_secret_write: false;
|
|
45
45
|
repo_viewer: boolean;
|
|
46
46
|
integration_source_ingestion_v1?: boolean;
|
|
47
|
+
integration_external_references_v1?: boolean;
|
|
47
48
|
integration_attachment_ingestion_v1?: boolean;
|
|
48
49
|
integration_managed_reference_ingestion_v1?: boolean;
|
|
49
50
|
integration_outbound_v1?: boolean;
|
|
@@ -57,6 +57,7 @@ function createRegisterPayload(options) {
|
|
|
57
57
|
browser_secret_write: false,
|
|
58
58
|
repo_viewer: capabilities.repo_viewer ?? false,
|
|
59
59
|
integration_source_ingestion_v1: capabilities.integration_source_ingestion_v1 ?? false,
|
|
60
|
+
integration_external_references_v1: capabilities.integration_external_references_v1 ?? false,
|
|
60
61
|
integration_attachment_ingestion_v1: capabilities.integration_attachment_ingestion_v1 ?? false,
|
|
61
62
|
integration_managed_reference_ingestion_v1: capabilities.integration_managed_reference_ingestion_v1 ?? false,
|
|
62
63
|
integration_outbound_v1: capabilities.integration_outbound_v1 ?? false,
|
|
@@ -507,6 +507,12 @@ export declare const SendClarificationRoundMessageResultSchema: import("@sinclai
|
|
|
507
507
|
message_kind: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"user_text">, import("@sinclair/typebox").TLiteral<"agent_text">, import("@sinclair/typebox").TLiteral<"system_notice">, import("@sinclair/typebox").TLiteral<"clarification_request">, import("@sinclair/typebox").TLiteral<"clarification_card">]>;
|
|
508
508
|
body: import("@sinclair/typebox").TString;
|
|
509
509
|
refs: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
510
|
+
external_references: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
511
|
+
source_ref: import("@sinclair/typebox").TString;
|
|
512
|
+
kind: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"feishu_docx">, import("@sinclair/typebox").TLiteral<"feishu_file">, import("@sinclair/typebox").TLiteral<"feishu_image">]>;
|
|
513
|
+
name: import("@sinclair/typebox").TString;
|
|
514
|
+
created_at: import("@sinclair/typebox").TString;
|
|
515
|
+
}>>>;
|
|
510
516
|
task_id: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
511
517
|
activity_ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
512
518
|
workflow_ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type Static } from "@sinclair/typebox";
|
|
2
|
+
export declare const ExternalReferenceRefSchema: import("@sinclair/typebox").TString;
|
|
3
|
+
export declare const ExternalReferenceDescriptorSchema: import("@sinclair/typebox").TObject<{
|
|
4
|
+
source_ref: import("@sinclair/typebox").TString;
|
|
5
|
+
kind: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"feishu_docx">, import("@sinclair/typebox").TLiteral<"feishu_file">, import("@sinclair/typebox").TLiteral<"feishu_image">]>;
|
|
6
|
+
name: import("@sinclair/typebox").TString;
|
|
7
|
+
created_at: import("@sinclair/typebox").TString;
|
|
8
|
+
}>;
|
|
9
|
+
export declare const ExternalReferenceListSchema: import("@sinclair/typebox").TObject<{
|
|
10
|
+
can_register: import("@sinclair/typebox").TBoolean;
|
|
11
|
+
items: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
12
|
+
source_ref: import("@sinclair/typebox").TString;
|
|
13
|
+
kind: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"feishu_docx">, import("@sinclair/typebox").TLiteral<"feishu_file">, import("@sinclair/typebox").TLiteral<"feishu_image">]>;
|
|
14
|
+
name: import("@sinclair/typebox").TString;
|
|
15
|
+
created_at: import("@sinclair/typebox").TString;
|
|
16
|
+
}>>;
|
|
17
|
+
}>;
|
|
18
|
+
export declare const RegisterExternalReferenceBodySchema: import("@sinclair/typebox").TObject<{
|
|
19
|
+
document_url: import("@sinclair/typebox").TString;
|
|
20
|
+
}>;
|
|
21
|
+
export type ExternalReferenceDescriptor = Static<typeof ExternalReferenceDescriptorSchema>;
|
|
22
|
+
export type ExternalReferenceList = Static<typeof ExternalReferenceListSchema>;
|
|
23
|
+
//# sourceMappingURL=external-references.d.ts.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { IsoDateTimeStringSchema } from "../domain/index.js";
|
|
3
|
+
export const ExternalReferenceRefSchema = Type.String({ pattern: "^external_ref_[a-f0-9]{32}$" });
|
|
4
|
+
export const ExternalReferenceDescriptorSchema = Type.Object({
|
|
5
|
+
source_ref: ExternalReferenceRefSchema,
|
|
6
|
+
kind: Type.Union([
|
|
7
|
+
Type.Literal("feishu_docx"),
|
|
8
|
+
Type.Literal("feishu_file"),
|
|
9
|
+
Type.Literal("feishu_image"),
|
|
10
|
+
]),
|
|
11
|
+
name: Type.String({ minLength: 1, maxLength: 240 }),
|
|
12
|
+
created_at: IsoDateTimeStringSchema,
|
|
13
|
+
}, { additionalProperties: false });
|
|
14
|
+
export const ExternalReferenceListSchema = Type.Object({
|
|
15
|
+
can_register: Type.Boolean(),
|
|
16
|
+
items: Type.Array(ExternalReferenceDescriptorSchema, { maxItems: 500 }),
|
|
17
|
+
}, { additionalProperties: false });
|
|
18
|
+
export const RegisterExternalReferenceBodySchema = Type.Object({
|
|
19
|
+
document_url: Type.String({ minLength: 1, maxLength: 2048 }),
|
|
20
|
+
}, { additionalProperties: false });
|
|
21
|
+
//# sourceMappingURL=external-references.js.map
|