@xfey/tutti 0.1.115 → 0.1.117
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/dist/approvals/decision-command.d.ts +1 -1
- package/dist/collaboration-state/message-context.js +6 -1
- package/dist/reference-access/message-links.d.ts +4 -0
- package/dist/reference-access/message-links.js +13 -0
- package/dist/reference-access/service.d.ts +8 -1
- package/dist/reference-access/service.js +49 -2
- package/dist/server-shell/cli/external-reference-runtime.js +14 -4
- package/dist/server-shell/http/routes/agent-context-external-reference-routes.js +76 -0
- package/node_modules/@tutti/relay-client/dist/external-references.js +6 -2
- package/node_modules/@tutti/shared/dist/schemas/api/external-references.d.ts +21 -0
- package/node_modules/@tutti/shared/dist/schemas/api/external-references.js +21 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/external-references.d.ts +28 -2
- package/node_modules/@tutti/shared/dist/schemas/internal/external-references.js +19 -2
- package/package.json +1 -1
- package/prompts/chat-assistant.md +1 -0
- package/prompts/skills/read-references/SKILL.md +8 -5
- package/web/assets/{homepage-motion-scene-BkZq6XoY.js → homepage-motion-scene-C_EDqDif.js} +1 -1
- package/web/assets/index-CKKpwsqr.js +70 -0
- package/web/assets/{index-C9grZQCJ.css → index-Ckm1Jy68.css} +1 -1
- package/web/index.html +2 -2
- package/web/assets/index-DZS21yn3.js +0 -69
|
@@ -115,7 +115,7 @@ export declare function executeApprovalDecisionCommand(input: {
|
|
|
115
115
|
account_ref: string;
|
|
116
116
|
};
|
|
117
117
|
label: string;
|
|
118
|
-
outcome: "
|
|
118
|
+
outcome: "expired" | "accepted" | "declined" | "cancelled";
|
|
119
119
|
decision_id: string;
|
|
120
120
|
decided_at: string;
|
|
121
121
|
};
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { formatArtifactFeedbackForModel } from "../artifact-feedback/index.js";
|
|
2
|
+
import { referenceMessageLinks } from "../reference-access/message-links.js";
|
|
2
3
|
/** Presentation data only: the quoted parent is not a second source or a new mention. */
|
|
3
4
|
export function messageTextForModel(message) {
|
|
4
5
|
const feedback = message.refs?.artifact_feedback;
|
|
5
|
-
const
|
|
6
|
+
const authored = feedback === undefined ? message.body : formatArtifactFeedbackForModel(feedback);
|
|
7
|
+
const links = referenceMessageLinks(message);
|
|
8
|
+
const body = links.length === 0
|
|
9
|
+
? authored
|
|
10
|
+
: `${authored}\n\n[Material link handles; resolve only when needed, not evidence of a read]\n${JSON.stringify(links.map((_link, index) => ({ message_id: message.id, link_index: index })))}`;
|
|
6
11
|
const reply = message.refs?.reply_context;
|
|
7
12
|
if (reply === undefined)
|
|
8
13
|
return body;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type MessageProjection } from "@tutti/shared/schemas/api";
|
|
2
|
+
/** Only authored chat links. File/document contents cannot silently grant another locator. */
|
|
3
|
+
export declare function referenceMessageLinks(message: MessageProjection): string[];
|
|
4
|
+
//# sourceMappingURL=message-links.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { parseFeishuExternalReferenceInput, } from "@tutti/shared/schemas/api";
|
|
2
|
+
/** Only authored chat links. File/document contents cannot silently grant another locator. */
|
|
3
|
+
export function referenceMessageLinks(message) {
|
|
4
|
+
if (message.author.kind !== "human" && message.refs?.development_operator !== true)
|
|
5
|
+
return [];
|
|
6
|
+
const links = message.body.match(/https:\/\/[^\s<>"'\u3000]+/giu) ?? [];
|
|
7
|
+
return [
|
|
8
|
+
...new Set(links
|
|
9
|
+
.map((link) => link.replace(/[),.;!?\]}>,。;!?)]+$/u, ""))
|
|
10
|
+
.filter((link) => parseFeishuExternalReferenceInput(link) !== null)),
|
|
11
|
+
].slice(0, 16);
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=message-links.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ExternalReferenceDescriptor } from "@tutti/shared/schemas/api";
|
|
1
|
+
import type { ExternalReferenceDescriptor, ReferenceAuthorizationRequired } from "@tutti/shared/schemas/api";
|
|
2
2
|
import type { ReferenceCopy, ReferenceFetchResult } from "./cache.js";
|
|
3
3
|
import { type ExternalReferenceCache } from "./cache.js";
|
|
4
4
|
export type ReferenceInvocation = {
|
|
@@ -6,6 +6,10 @@ export type ReferenceInvocation = {
|
|
|
6
6
|
activity_ref?: string;
|
|
7
7
|
expires_at: string;
|
|
8
8
|
};
|
|
9
|
+
export declare class ReferenceAuthorizationRequiredError extends Error {
|
|
10
|
+
readonly result: ReferenceAuthorizationRequired;
|
|
11
|
+
constructor(result: ReferenceAuthorizationRequired);
|
|
12
|
+
}
|
|
9
13
|
export declare class ExternalReferenceAccess {
|
|
10
14
|
private readonly options;
|
|
11
15
|
private cache;
|
|
@@ -14,9 +18,12 @@ export declare class ExternalReferenceAccess {
|
|
|
14
18
|
createCache: () => ExternalReferenceCache;
|
|
15
19
|
list: (invocation: ReferenceInvocation) => Promise<ExternalReferenceDescriptor[]>;
|
|
16
20
|
read: (sourceRef: string, invocation: ReferenceInvocation) => Promise<ReferenceFetchResult>;
|
|
21
|
+
resolve?: (url: string, invocation: ReferenceInvocation) => Promise<ExternalReferenceDescriptor>;
|
|
17
22
|
recordRead: (invocation: ReferenceInvocation, copy: ReferenceCopy) => void;
|
|
18
23
|
});
|
|
19
24
|
list(invocation: ReferenceInvocation): Promise<ExternalReferenceDescriptor[]>;
|
|
25
|
+
/** Explicit, authenticated admission does not replace any previously observed version. */
|
|
26
|
+
resolve(invocation: ReferenceInvocation, url: string): Promise<ExternalReferenceDescriptor>;
|
|
20
27
|
read(invocation: ReferenceInvocation, sourceRef: string): Promise<{
|
|
21
28
|
copy: ReferenceCopy;
|
|
22
29
|
cache_hit: boolean;
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { ReferenceCacheError } from "./cache.js";
|
|
2
|
+
export class ReferenceAuthorizationRequiredError extends Error {
|
|
3
|
+
result;
|
|
4
|
+
constructor(result) {
|
|
5
|
+
super("reference_authorization_required");
|
|
6
|
+
this.result = result;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
2
9
|
export class ExternalReferenceAccess {
|
|
3
10
|
options;
|
|
4
11
|
cache;
|
|
@@ -8,6 +15,9 @@ export class ExternalReferenceAccess {
|
|
|
8
15
|
}
|
|
9
16
|
async list(invocation) {
|
|
10
17
|
const now = Date.now();
|
|
18
|
+
if (!Number.isFinite(Date.parse(invocation.expires_at)) ||
|
|
19
|
+
Date.parse(invocation.expires_at) <= now)
|
|
20
|
+
throw new ReferenceCacheError("reference_invocation_expired");
|
|
11
21
|
for (const [key, selection] of this.selections)
|
|
12
22
|
if (selection.expires <= now)
|
|
13
23
|
this.selections.delete(key);
|
|
@@ -18,11 +28,48 @@ export class ExternalReferenceAccess {
|
|
|
18
28
|
selection = {
|
|
19
29
|
expires: Date.parse(invocation.expires_at),
|
|
20
30
|
sources: this.options.list(invocation),
|
|
31
|
+
admitted: new Map(),
|
|
32
|
+
resolutions: new Map(),
|
|
21
33
|
};
|
|
22
34
|
this.selections.set(invocation.reference_invocation_ref, selection);
|
|
23
|
-
|
|
35
|
+
const created = selection;
|
|
36
|
+
selection.sources.catch(() => {
|
|
37
|
+
if (this.selections.get(invocation.reference_invocation_ref) === created)
|
|
38
|
+
this.selections.delete(invocation.reference_invocation_ref);
|
|
39
|
+
});
|
|
24
40
|
}
|
|
25
|
-
|
|
41
|
+
const sources = await selection.sources;
|
|
42
|
+
return [
|
|
43
|
+
...new Map([...sources, ...selection.admitted.values()].map((source) => [source.source_ref, source])).values(),
|
|
44
|
+
];
|
|
45
|
+
}
|
|
46
|
+
/** Explicit, authenticated admission does not replace any previously observed version. */
|
|
47
|
+
async resolve(invocation, url) {
|
|
48
|
+
if (!this.options.resolve)
|
|
49
|
+
throw new ReferenceCacheError("reference_resolve_unavailable");
|
|
50
|
+
await this.list(invocation);
|
|
51
|
+
const selection = this.selections.get(invocation.reference_invocation_ref);
|
|
52
|
+
if (selection.expires <= Date.now())
|
|
53
|
+
throw new ReferenceCacheError("reference_invocation_expired");
|
|
54
|
+
const existing = selection.resolutions.get(url);
|
|
55
|
+
if (existing)
|
|
56
|
+
return existing;
|
|
57
|
+
if (selection.resolutions.size >= 32)
|
|
58
|
+
throw new ReferenceCacheError("reference_resolution_limit_reached");
|
|
59
|
+
const operation = this.options
|
|
60
|
+
.resolve(url, invocation)
|
|
61
|
+
.then((source) => {
|
|
62
|
+
if (selection.expires <= Date.now())
|
|
63
|
+
throw new ReferenceCacheError("reference_invocation_expired");
|
|
64
|
+
selection.admitted.set(source.source_ref, source);
|
|
65
|
+
return source;
|
|
66
|
+
})
|
|
67
|
+
.catch((error) => {
|
|
68
|
+
selection.resolutions.delete(url);
|
|
69
|
+
throw error;
|
|
70
|
+
});
|
|
71
|
+
selection.resolutions.set(url, operation);
|
|
72
|
+
return operation;
|
|
26
73
|
}
|
|
27
74
|
async read(invocation, sourceRef) {
|
|
28
75
|
const sources = await this.list(invocation);
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { requestExternalReferences } from "@tutti/relay-client";
|
|
4
|
-
import { isExternalReferenceList, isExternalReferenceReadResult, } from "@tutti/shared/schemas/internal";
|
|
4
|
+
import { isExternalReferenceList, isExternalReferenceReadResult, isExternalReferenceDescriptor, isReferenceAuthorizationRequired, } from "@tutti/shared/schemas/internal";
|
|
5
5
|
import { ExternalReferenceCache } from "../../reference-access/cache.js";
|
|
6
|
-
import { ExternalReferenceAccess, } from "../../reference-access/service.js";
|
|
6
|
+
import { ExternalReferenceAccess, ReferenceAuthorizationRequiredError, } from "../../reference-access/service.js";
|
|
7
7
|
import { recordExternalReferenceRead } from "../../reference-access/read-evidence.js";
|
|
8
8
|
import { downloadStagedUploadWithResolver, cleanupDownloadedStagedUpload, } from "../http/routes/project-api/staged-uploads.js";
|
|
9
9
|
import { readHostRegistrationSecret } from "./machine-local.js";
|
|
10
10
|
export function createExternalReferenceRuntime(options) {
|
|
11
11
|
const { preparation } = options;
|
|
12
|
-
async function request(action, invocation, sourceRef) {
|
|
12
|
+
async function request(action, invocation, sourceRef, documentUrl) {
|
|
13
13
|
const relay = options.getLaunchStatus().relay;
|
|
14
14
|
if (!relay)
|
|
15
15
|
throw new Error("reference_host_offline");
|
|
16
16
|
const secret = readHostRegistrationSecret(preparation.tutti_home, preparation.project_id);
|
|
17
|
-
|
|
17
|
+
const result = await requestExternalReferences({
|
|
18
18
|
relayUrl: preparation.relay_url,
|
|
19
19
|
body: {
|
|
20
20
|
supports_drive_files: true,
|
|
@@ -26,8 +26,12 @@ export function createExternalReferenceRuntime(options) {
|
|
|
26
26
|
invocation_ref: invocation.reference_invocation_ref,
|
|
27
27
|
action,
|
|
28
28
|
...(sourceRef === undefined ? {} : { source_ref: sourceRef }),
|
|
29
|
+
...(documentUrl === undefined ? {} : { document_url: documentUrl }),
|
|
29
30
|
},
|
|
30
31
|
});
|
|
32
|
+
if (isReferenceAuthorizationRequired(result))
|
|
33
|
+
throw new ReferenceAuthorizationRequiredError(result);
|
|
34
|
+
return result;
|
|
31
35
|
}
|
|
32
36
|
return new ExternalReferenceAccess({
|
|
33
37
|
createCache: () => new ExternalReferenceCache({
|
|
@@ -40,6 +44,12 @@ export function createExternalReferenceRuntime(options) {
|
|
|
40
44
|
throw new Error("reference_list_invalid");
|
|
41
45
|
return result.items;
|
|
42
46
|
},
|
|
47
|
+
resolve: async (url, invocation) => {
|
|
48
|
+
const result = await request("resolve", invocation, undefined, url);
|
|
49
|
+
if (!isExternalReferenceDescriptor(result))
|
|
50
|
+
throw new Error("reference_resolve_invalid");
|
|
51
|
+
return result;
|
|
52
|
+
},
|
|
43
53
|
read: async (sourceRef, invocation) => {
|
|
44
54
|
const result = await request("read", invocation, sourceRef);
|
|
45
55
|
if (!isExternalReferenceReadResult(result) || result.source.source_ref !== sourceRef)
|
|
@@ -1,11 +1,79 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { Value } from "@sinclair/typebox/value";
|
|
2
3
|
import { redactText } from "@tutti/shared/utils";
|
|
3
4
|
import { ExternalReferenceTransportError } from "@tutti/relay-client";
|
|
4
5
|
import { ReferenceCacheError } from "../../../reference-access/cache.js";
|
|
6
|
+
import { ReferenceAuthorizationRequiredError } from "../../../reference-access/service.js";
|
|
7
|
+
import { referenceMessageLinks } from "../../../reference-access/message-links.js";
|
|
8
|
+
import { readMessageProjectionById } from "../../../collaboration-state/index.js";
|
|
9
|
+
import { MessageIdSchema } from "@tutti/shared/schemas/domain";
|
|
10
|
+
import { requireProjectStore } from "./agent-context-shared.js";
|
|
11
|
+
import { randomUUID } from "node:crypto";
|
|
5
12
|
import { WorkspaceOpsError } from "../../../workspace-ops/errors.js";
|
|
6
13
|
import { authorizeAgentContextRequest } from "./agent-context-auth.js";
|
|
7
14
|
import { AGENT_CONTEXT_BASE_PATH } from "./agent-context-shared.js";
|
|
15
|
+
const ResolveMessageLinkBody = Type.Object({ message_id: MessageIdSchema, link_index: Type.Integer({ minimum: 0, maximum: 15 }) }, { additionalProperties: false });
|
|
8
16
|
export function registerAgentContextExternalReferenceRoutes(app, options) {
|
|
17
|
+
app.post(`${AGENT_CONTEXT_BASE_PATH}/references/external/resolve`, {
|
|
18
|
+
bodyLimit: 4096,
|
|
19
|
+
schema: { body: ResolveMessageLinkBody },
|
|
20
|
+
preValidation: async (request, reply) => {
|
|
21
|
+
reply.header("cache-control", "no-store");
|
|
22
|
+
if (!Value.Check(ResolveMessageLinkBody, request.body))
|
|
23
|
+
return reply
|
|
24
|
+
.code(400)
|
|
25
|
+
.send({ kind: "unavailable", reason: "reference_message_link_invalid" });
|
|
26
|
+
},
|
|
27
|
+
}, async (request, reply) => {
|
|
28
|
+
reply.header("cache-control", "no-store");
|
|
29
|
+
const invocation = authorizeAgentContextRequest(request, options, "references:external");
|
|
30
|
+
const requestRef = `eref_${randomUUID().replaceAll("-", "")}`;
|
|
31
|
+
const fields = {
|
|
32
|
+
request_ref: requestRef,
|
|
33
|
+
invocation_ref: invocation.reference_invocation_ref,
|
|
34
|
+
activity_ref: invocation.activity_ref,
|
|
35
|
+
message_ref: request.body.message_id,
|
|
36
|
+
link_index: request.body.link_index,
|
|
37
|
+
stage: "external_reference_resolve",
|
|
38
|
+
};
|
|
39
|
+
const started = performance.now();
|
|
40
|
+
try {
|
|
41
|
+
if (!options.externalReferences)
|
|
42
|
+
throw new ReferenceCacheError("external_references_not_enabled");
|
|
43
|
+
const message = readMessageProjectionById(requireProjectStore(options).db, request.body.message_id);
|
|
44
|
+
const url = message ? referenceMessageLinks(message)[request.body.link_index] : undefined;
|
|
45
|
+
if (!url)
|
|
46
|
+
throw new ReferenceCacheError("reference_message_link_not_found");
|
|
47
|
+
const source = await options.externalReferences.resolve(invocation, url);
|
|
48
|
+
request.log.info({
|
|
49
|
+
...fields,
|
|
50
|
+
source_ref: source.source_ref,
|
|
51
|
+
elapsed_ms: Math.round(performance.now() - started),
|
|
52
|
+
outcome: "resolved",
|
|
53
|
+
}, "Message material resolved");
|
|
54
|
+
return { source, request_ref: requestRef };
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
const reason = error instanceof ReferenceAuthorizationRequiredError
|
|
58
|
+
? "reference_authorization_required"
|
|
59
|
+
: error instanceof ReferenceCacheError
|
|
60
|
+
? error.code
|
|
61
|
+
: error instanceof ExternalReferenceTransportError
|
|
62
|
+
? error.reasonCode
|
|
63
|
+
: "reference_resolve_unavailable";
|
|
64
|
+
request.log.warn({
|
|
65
|
+
...fields,
|
|
66
|
+
reason_code: reason,
|
|
67
|
+
...(error instanceof ReferenceAuthorizationRequiredError
|
|
68
|
+
? { authorization_ref: error.result.authorization.authorization_ref }
|
|
69
|
+
: {}),
|
|
70
|
+
elapsed_ms: Math.round(performance.now() - started),
|
|
71
|
+
}, "Message material unavailable");
|
|
72
|
+
return error instanceof ReferenceAuthorizationRequiredError
|
|
73
|
+
? { ...error.result, request_ref: requestRef }
|
|
74
|
+
: { kind: "unavailable", reason, request_ref: requestRef };
|
|
75
|
+
}
|
|
76
|
+
});
|
|
9
77
|
for (const action of ["list", "read"])
|
|
10
78
|
app.get(`${AGENT_CONTEXT_BASE_PATH}/references/external${action === "read" ? "/file" : ""}`, {
|
|
11
79
|
schema: {
|
|
@@ -86,6 +154,14 @@ export function registerAgentContextExternalReferenceRoutes(app, options) {
|
|
|
86
154
|
};
|
|
87
155
|
}
|
|
88
156
|
catch (error) {
|
|
157
|
+
if (error instanceof ReferenceAuthorizationRequiredError) {
|
|
158
|
+
request.log.info({
|
|
159
|
+
...diagnosticFields,
|
|
160
|
+
stage: "external_reference_authorization_required",
|
|
161
|
+
authorization_ref: error.result.authorization.authorization_ref,
|
|
162
|
+
}, "Document access card requested");
|
|
163
|
+
return error.result;
|
|
164
|
+
}
|
|
89
165
|
const reason = error instanceof ReferenceCacheError
|
|
90
166
|
? error.code
|
|
91
167
|
: error instanceof ExternalReferenceTransportError
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EXTERNAL_REFERENCE_HOST_PATH, isExternalReferenceList, isExternalReferenceReadResult, } from "@tutti/shared/schemas/internal";
|
|
1
|
+
import { EXTERNAL_REFERENCE_HOST_PATH, isExternalReferenceList, isExternalReferenceReadResult, isReferenceAuthorizationRequired, isExternalReferenceDescriptor, } from "@tutti/shared/schemas/internal";
|
|
2
2
|
export class ExternalReferenceTransportError extends Error {
|
|
3
3
|
reasonCode;
|
|
4
4
|
constructor(reasonCode) {
|
|
@@ -48,9 +48,13 @@ export async function requestExternalReferences(options) {
|
|
|
48
48
|
? reason
|
|
49
49
|
: "reference_transport_unavailable");
|
|
50
50
|
}
|
|
51
|
+
if (options.body.action !== "list" && isReferenceAuthorizationRequired(value))
|
|
52
|
+
return value;
|
|
51
53
|
if (options.body.action === "list"
|
|
52
54
|
? !isExternalReferenceList(value)
|
|
53
|
-
:
|
|
55
|
+
: options.body.action === "resolve"
|
|
56
|
+
? !isExternalReferenceDescriptor(value)
|
|
57
|
+
: !isExternalReferenceReadResult(value))
|
|
54
58
|
throw new ExternalReferenceTransportError("reference_transport_invalid_response");
|
|
55
59
|
return value;
|
|
56
60
|
}
|
|
@@ -6,6 +6,25 @@ export type FeishuExternalReferenceLocator = {
|
|
|
6
6
|
/** Private locator parsing only; callers must never fetch the supplied URL. */
|
|
7
7
|
export declare function parseFeishuExternalReferenceInput(value: string): FeishuExternalReferenceLocator | null;
|
|
8
8
|
export declare const ExternalReferenceRefSchema: import("@sinclair/typebox").TString;
|
|
9
|
+
export declare const ReferenceAuthorizationRefSchema: import("@sinclair/typebox").TString;
|
|
10
|
+
export declare const ReferenceAuthorizationProfileSchema: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"docx">, import("@sinclair/typebox").TLiteral<"wiki_docx">, import("@sinclair/typebox").TLiteral<"drive_file">]>;
|
|
11
|
+
export declare const ReferenceAuthorizationSchema: import("@sinclair/typebox").TObject<{
|
|
12
|
+
authorization_ref: import("@sinclair/typebox").TString;
|
|
13
|
+
profile: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"docx">, import("@sinclair/typebox").TLiteral<"wiki_docx">, import("@sinclair/typebox").TLiteral<"drive_file">]>;
|
|
14
|
+
status: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"pending">, import("@sinclair/typebox").TLiteral<"authorized">, import("@sinclair/typebox").TLiteral<"expired">]>;
|
|
15
|
+
expires_at: import("@sinclair/typebox").TString;
|
|
16
|
+
}>;
|
|
17
|
+
export declare const ReferenceAuthorizationRequiredSchema: import("@sinclair/typebox").TObject<{
|
|
18
|
+
kind: import("@sinclair/typebox").TLiteral<"authorization_required">;
|
|
19
|
+
authorization: import("@sinclair/typebox").TObject<{
|
|
20
|
+
authorization_ref: import("@sinclair/typebox").TString;
|
|
21
|
+
profile: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"docx">, import("@sinclair/typebox").TLiteral<"wiki_docx">, import("@sinclair/typebox").TLiteral<"drive_file">]>;
|
|
22
|
+
status: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"pending">, import("@sinclair/typebox").TLiteral<"authorized">, import("@sinclair/typebox").TLiteral<"expired">]>;
|
|
23
|
+
expires_at: import("@sinclair/typebox").TString;
|
|
24
|
+
}>;
|
|
25
|
+
}>;
|
|
26
|
+
export type ReferenceAuthorization = Static<typeof ReferenceAuthorizationSchema>;
|
|
27
|
+
export type ReferenceAuthorizationRequired = Static<typeof ReferenceAuthorizationRequiredSchema>;
|
|
9
28
|
export declare const ReferenceSubmissionRefSchema: import("@sinclair/typebox").TString;
|
|
10
29
|
export declare const ReferenceSubmissionSchema: import("@sinclair/typebox").TObject<{
|
|
11
30
|
submission_ref: import("@sinclair/typebox").TString;
|
|
@@ -35,6 +54,8 @@ export declare const ExternalReferenceListSchema: import("@sinclair/typebox").TO
|
|
|
35
54
|
}>>>;
|
|
36
55
|
}>;
|
|
37
56
|
export declare const RegisterExternalReferenceBodySchema: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TObject<{
|
|
57
|
+
authorization_ref: import("@sinclair/typebox").TString;
|
|
58
|
+
}>, import("@sinclair/typebox").TObject<{
|
|
38
59
|
document_url: import("@sinclair/typebox").TString;
|
|
39
60
|
}>, import("@sinclair/typebox").TObject<{
|
|
40
61
|
submission_ref: import("@sinclair/typebox").TString;
|
|
@@ -16,6 +16,26 @@ export function parseFeishuExternalReferenceInput(value) {
|
|
|
16
16
|
return file?.[1] ? { kind: "file", token: file[1] } : null;
|
|
17
17
|
}
|
|
18
18
|
export const ExternalReferenceRefSchema = Type.String({ pattern: "^external_ref_[a-f0-9]{32}$" });
|
|
19
|
+
export const ReferenceAuthorizationRefSchema = Type.String({ pattern: "^ref_auth_[a-f0-9]{32}$" });
|
|
20
|
+
export const ReferenceAuthorizationProfileSchema = Type.Union([
|
|
21
|
+
Type.Literal("docx"),
|
|
22
|
+
Type.Literal("wiki_docx"),
|
|
23
|
+
Type.Literal("drive_file"),
|
|
24
|
+
]);
|
|
25
|
+
export const ReferenceAuthorizationSchema = Type.Object({
|
|
26
|
+
authorization_ref: ReferenceAuthorizationRefSchema,
|
|
27
|
+
profile: ReferenceAuthorizationProfileSchema,
|
|
28
|
+
status: Type.Union([
|
|
29
|
+
Type.Literal("pending"),
|
|
30
|
+
Type.Literal("authorized"),
|
|
31
|
+
Type.Literal("expired"),
|
|
32
|
+
]),
|
|
33
|
+
expires_at: IsoDateTimeStringSchema,
|
|
34
|
+
}, { additionalProperties: false });
|
|
35
|
+
export const ReferenceAuthorizationRequiredSchema = Type.Object({
|
|
36
|
+
kind: Type.Literal("authorization_required"),
|
|
37
|
+
authorization: ReferenceAuthorizationSchema,
|
|
38
|
+
}, { additionalProperties: false });
|
|
19
39
|
export const ReferenceSubmissionRefSchema = Type.String({
|
|
20
40
|
pattern: "^ref_submission_[a-f0-9]{32}$",
|
|
21
41
|
});
|
|
@@ -42,6 +62,7 @@ export const ExternalReferenceListSchema = Type.Object({
|
|
|
42
62
|
pending: Type.Optional(Type.Array(ReferenceSubmissionSchema, { maxItems: 20 })),
|
|
43
63
|
}, { additionalProperties: false });
|
|
44
64
|
export const RegisterExternalReferenceBodySchema = Type.Union([
|
|
65
|
+
Type.Object({ authorization_ref: ReferenceAuthorizationRefSchema }, { additionalProperties: false }),
|
|
45
66
|
Type.Object({
|
|
46
67
|
document_url: Type.String({ minLength: 1, maxLength: 2048 }),
|
|
47
68
|
}, { additionalProperties: false }),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Static } from "@sinclair/typebox";
|
|
2
|
-
import { type ExternalReferenceList } from "../api/external-references.js";
|
|
2
|
+
import { type ExternalReferenceList, type ReferenceAuthorizationRequired } from "../api/external-references.js";
|
|
3
|
+
import { ExternalReferenceDescriptorSchema } from "../api/external-references.js";
|
|
3
4
|
export declare const EXTERNAL_REFERENCE_CONTROL_PATH = "/internal/v1/external-references";
|
|
4
5
|
export declare const EXTERNAL_REFERENCE_HOST_PATH = "/host-control/v1/external-references";
|
|
5
6
|
export declare const ExternalReferenceAuthoritySchema: import("@sinclair/typebox").TObject<{
|
|
@@ -53,6 +54,28 @@ export declare const ExternalReferenceControlRequestSchema: import("@sinclair/ty
|
|
|
53
54
|
role: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"owner">, import("@sinclair/typebox").TLiteral<"member">]>;
|
|
54
55
|
}>;
|
|
55
56
|
request_ref: import("@sinclair/typebox").TString;
|
|
57
|
+
}>, import("@sinclair/typebox").TObject<{
|
|
58
|
+
action: import("@sinclair/typebox").TLiteral<"resolve">;
|
|
59
|
+
document_url: import("@sinclair/typebox").TString;
|
|
60
|
+
authority: import("@sinclair/typebox").TObject<{
|
|
61
|
+
project_ref: import("@sinclair/typebox").TString;
|
|
62
|
+
installation_ref: import("@sinclair/typebox").TString;
|
|
63
|
+
binding_ref: import("@sinclair/typebox").TString;
|
|
64
|
+
external_subject_ref: import("@sinclair/typebox").TString;
|
|
65
|
+
role: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"owner">, import("@sinclair/typebox").TLiteral<"member">]>;
|
|
66
|
+
}>;
|
|
67
|
+
request_ref: import("@sinclair/typebox").TString;
|
|
68
|
+
}>, import("@sinclair/typebox").TObject<{
|
|
69
|
+
action: import("@sinclair/typebox").TLiteral<"authorization_status">;
|
|
70
|
+
authorization_ref: import("@sinclair/typebox").TString;
|
|
71
|
+
authority: import("@sinclair/typebox").TObject<{
|
|
72
|
+
project_ref: import("@sinclair/typebox").TString;
|
|
73
|
+
installation_ref: import("@sinclair/typebox").TString;
|
|
74
|
+
binding_ref: import("@sinclair/typebox").TString;
|
|
75
|
+
external_subject_ref: import("@sinclair/typebox").TString;
|
|
76
|
+
role: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"owner">, import("@sinclair/typebox").TLiteral<"member">]>;
|
|
77
|
+
}>;
|
|
78
|
+
request_ref: import("@sinclair/typebox").TString;
|
|
56
79
|
}>, import("@sinclair/typebox").TObject<{
|
|
57
80
|
action: import("@sinclair/typebox").TLiteral<"transfer">;
|
|
58
81
|
transfer_ref: import("@sinclair/typebox").TString;
|
|
@@ -109,14 +132,17 @@ export declare const ExternalReferenceHostRequestSchema: import("@sinclair/typeb
|
|
|
109
132
|
relay_project_ref: import("@sinclair/typebox").TString;
|
|
110
133
|
host_connection_ref: import("@sinclair/typebox").TString;
|
|
111
134
|
request_ref: import("@sinclair/typebox").TString;
|
|
112
|
-
action: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"list">, import("@sinclair/typebox").TLiteral<"read">]>;
|
|
135
|
+
action: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"list">, import("@sinclair/typebox").TLiteral<"read">, import("@sinclair/typebox").TLiteral<"resolve">]>;
|
|
113
136
|
source_ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
137
|
+
document_url: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
114
138
|
}>;
|
|
115
139
|
export type ExternalReferenceAuthority = Static<typeof ExternalReferenceAuthoritySchema>;
|
|
116
140
|
export type ExternalReferenceControlRequest = Static<typeof ExternalReferenceControlRequestSchema>;
|
|
117
141
|
export type ExternalReferencePrepared = Static<typeof ExternalReferencePreparedSchema>;
|
|
118
142
|
export type ExternalReferenceReadResult = Static<typeof ExternalReferenceReadResultSchema>;
|
|
119
143
|
export type ExternalReferenceHostRequest = Static<typeof ExternalReferenceHostRequestSchema>;
|
|
144
|
+
export declare function isReferenceAuthorizationRequired(value: unknown): value is ReferenceAuthorizationRequired;
|
|
145
|
+
export declare function isExternalReferenceDescriptor(value: unknown): value is Static<typeof ExternalReferenceDescriptorSchema>;
|
|
120
146
|
export declare function isExternalReferenceList(value: unknown): value is ExternalReferenceList;
|
|
121
147
|
export declare function isExternalReferenceReadResult(value: unknown): value is ExternalReferenceReadResult;
|
|
122
148
|
//# sourceMappingURL=external-references.d.ts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import { Value } from "@sinclair/typebox/value";
|
|
3
|
-
import { ExternalReferenceListSchema, ReferenceSubmissionRefSchema, } from "../api/external-references.js";
|
|
3
|
+
import { ExternalReferenceListSchema, ReferenceSubmissionRefSchema, ReferenceAuthorizationRefSchema, ReferenceAuthorizationRequiredSchema, } from "../api/external-references.js";
|
|
4
4
|
import { ExternalReferenceDescriptorSchema, ExternalReferenceRefSchema, } from "../api/external-references.js";
|
|
5
5
|
import { HostConnectionRefSchema, EventRefSchema, ProjectIdSchema, RelayProjectRefSchema, RelayUploadRefSchema, } from "../domain/index.js";
|
|
6
6
|
export const EXTERNAL_REFERENCE_CONTROL_PATH = "/internal/v1/external-references";
|
|
@@ -27,6 +27,16 @@ export const ExternalReferenceControlRequestSchema = Type.Union([
|
|
|
27
27
|
}, { additionalProperties: false }),
|
|
28
28
|
Type.Object({ ...common, action: Type.Literal("confirm"), submission_ref: ReferenceSubmissionRefSchema }, { additionalProperties: false }),
|
|
29
29
|
Type.Object({ ...common, ...SourceRead, action: Type.Literal("prepare") }, { additionalProperties: false }),
|
|
30
|
+
Type.Object({
|
|
31
|
+
...common,
|
|
32
|
+
action: Type.Literal("resolve"),
|
|
33
|
+
document_url: Type.String({ minLength: 1, maxLength: 2048 }),
|
|
34
|
+
}, { additionalProperties: false }),
|
|
35
|
+
Type.Object({
|
|
36
|
+
...common,
|
|
37
|
+
action: Type.Literal("authorization_status"),
|
|
38
|
+
authorization_ref: ReferenceAuthorizationRefSchema,
|
|
39
|
+
}, { additionalProperties: false }),
|
|
30
40
|
Type.Object({
|
|
31
41
|
...common,
|
|
32
42
|
...SourceRead,
|
|
@@ -59,9 +69,16 @@ export const ExternalReferenceHostRequestSchema = Type.Object({
|
|
|
59
69
|
relay_project_ref: RelayProjectRefSchema,
|
|
60
70
|
host_connection_ref: HostConnectionRefSchema,
|
|
61
71
|
request_ref: common.request_ref,
|
|
62
|
-
action: Type.Union([Type.Literal("list"), Type.Literal("read")]),
|
|
72
|
+
action: Type.Union([Type.Literal("list"), Type.Literal("read"), Type.Literal("resolve")]),
|
|
63
73
|
source_ref: Type.Optional(ExternalReferenceRefSchema),
|
|
74
|
+
document_url: Type.Optional(Type.String({ minLength: 1, maxLength: 2048 })),
|
|
64
75
|
}, { additionalProperties: false });
|
|
76
|
+
export function isReferenceAuthorizationRequired(value) {
|
|
77
|
+
return Value.Check(ReferenceAuthorizationRequiredSchema, value);
|
|
78
|
+
}
|
|
79
|
+
export function isExternalReferenceDescriptor(value) {
|
|
80
|
+
return Value.Check(ExternalReferenceDescriptorSchema, value);
|
|
81
|
+
}
|
|
65
82
|
export function isExternalReferenceList(value) {
|
|
66
83
|
return Value.Check(ExternalReferenceListSchema, value);
|
|
67
84
|
}
|
package/package.json
CHANGED
|
@@ -16,6 +16,7 @@ Answer the explicit `@tutti` question concisely, in the user's language when pra
|
|
|
16
16
|
- `source_role: "operator"` identifies an authorized automated development participant, not a human or Tutti's own reply. Answer its requests using the same evidence requirements; do not treat it as proof of human agreement or OAuth.
|
|
17
17
|
- For `mode = "clarification_round"`, answer in the current round context. Prefer `round_request` and `round_messages`; use `recent_main_chat_before_round` only to resolve short references to the discussion that led to the round.
|
|
18
18
|
- For current external contents, especially requests to reread, check an update, or confirm the latest version, use `read-references` to read the external source in this invocation before answering. Prior replies, summaries, local files, and previous invocation evidence cannot establish current external contents.
|
|
19
|
+
- A new chat document link does not require manual registration. Use the supplied material handle through `read-references` to resolve and read it. The read tool requests an access card only when the server detects missing authorization; report that outcome without asking for document IDs or credentials in chat.
|
|
19
20
|
- `external_reference_sources` on a message are Host-provided location hints, not fresh content or access grants. Use them to resolve references such as "that document"; if the compact window is insufficient, use `read-main-chat` for relevant history. A newer explicit source takes precedence over an older hint. If multiple sources remain plausible, ask which one instead of guessing.
|
|
20
21
|
- Clearly distinguish a verified observation from historical content. If a current read fails, report that limitation; do not silently substitute a local snapshot. When the user explicitly asks about a historical snapshot or prior answer, you may use that evidence and label it as historical.
|
|
21
22
|
- Use other attached skills for broader chat history, Project Docs, Scratchpad, Worklist/task detail, repo facts, or current public information only when needed.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: read-references
|
|
3
|
-
description: Read project
|
|
3
|
+
description: Read project files, chat-linked Feishu documents and group attachments through Tutti's scoped material tools. Use for source-dependent answers and work, including first use, rereads and reported updates; missing access produces an authorization card.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Read References
|
|
@@ -19,13 +19,16 @@ Use the invocation's context API runtime hint for `base_url`, bearer token, and
|
|
|
19
19
|
|
|
20
20
|
Requires `references:external` in the runtime hint.
|
|
21
21
|
|
|
22
|
-
1.
|
|
23
|
-
2. For the
|
|
24
|
-
3.
|
|
22
|
+
1. For a **new chat-linked document**, use its supplied material handle: `POST {base_url}/references/external/resolve` with JSON `{"message_id":"the supplied message ID","link_index":0}`. Use the actual zero-based index in that message's handle list. The server finds the original link; never invent IDs, pass arbitrary URLs or ask the user to register it first. This resolves a source, not its contents. If the compact context has no handle, use the attached history skill when available; otherwise request the missing link/context.
|
|
23
|
+
2. For an attachment or a previously read source, discover the available sources with `GET {base_url}/references/external` and match the supplied source hints. Do not read every item or crawl unrelated links. A title is a selection aid, not identity or freshness evidence.
|
|
24
|
+
3. For the relevant resolved or listed `source_ref`, call `GET {base_url}/references/external/file?source_ref=...` in this same invocation.
|
|
25
|
+
4. Read the returned bounded text. For binary content, use the returned local path with an appropriate available reader; do not guess unsupported contents. Respect truncation when describing coverage.
|
|
25
26
|
|
|
26
27
|
Successful reads carry `provenance = {"origin":"external_reference","verification":"this_invocation"}`, revision, digest, `checked_at`, and a repo-relative working-copy path. The first read of a source in an invocation checks remote access/content; subsequent reads retain that invocation's immutable version. `cache_hit` does not bypass the first check. This proves an observation at `checked_at`, not that the source will remain unchanged. A later invocation must check again when it needs current contents.
|
|
27
28
|
|
|
28
|
-
If
|
|
29
|
+
If a tool returns `kind = "authorization_required"`, Tutti has durably requested a minimal access card in the bound group. Explain that the project owner should use **Review access**, then retry the question or continue the task's existing pending step. Do not poll, repeatedly request the same access, generate OAuth links, request credentials in chat, or claim the card is delivered merely because it was queued. Authorization does not prove a content read and never restarts a completed task automatically. For essential task evidence, use the calling workflow's existing needs-human result.
|
|
30
|
+
|
|
31
|
+
Other unavailable results are not an instruction to authorize: distinguish missing original permissions, missing/unsupported resources, capacity and temporary failures. Report the actual limitation. Never substitute a legacy snapshot or old cache as current evidence.
|
|
29
32
|
|
|
30
33
|
## Read a local Reference
|
|
31
34
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{C as e,S as t,_ as n,a as r,b as i,c as a,d as o,f as s,g as c,h as l,i as u,l as d,m as f,n as p,o as m,p as h,r as g,s as _,t as v,u as y,v as b,w as x,x as S,y as C}from"./index-DZS21yn3.js";var w=e(`mouse-pointer-2`,[[`path`,{d:`M4.037 4.688a.495.495 0 0 1 .651-.651l16 6.5a.5.5 0 0 1-.063.947l-6.124 1.58a2 2 0 0 0-1.438 1.435l-1.579 6.126a.5.5 0 0 1-.947.063z`,key:`edeuup`}]]),T=e(`send`,[[`path`,{d:`M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z`,key:`1ffxy3`}],[`path`,{d:`m21.854 2.147-10.94 10.939`,key:`12cjpa`}]]),E=x();function D(e){return{"--reveal":e}}function O(e,t,n){let r=(e,t)=>Number.parseInt(e.slice(t,t+2),16),i=i=>Math.round(r(e,i)+(r(t,i)-r(e,i))*n).toString(16).padStart(2,`0`);return`#${i(1)}${i(3)}${i(5)}`}function k(e){let t=r((e-p.runEnd)/(p.executionComplete-p.runEnd));return t+t*t-t*t*t}function A({progress:e,children:t,className:n=``}){return(0,E.jsx)(`div`,{className:`scene-reveal ${n}`,style:D(e),children:t})}function j({progress:e,author:t,avatar:n,tone:r,timestamp:i,online:a=!1,assets:o,children:s}){return(0,E.jsxs)(`article`,{className:`scene-message`,style:D(e),children:[(0,E.jsx)(`span`,{className:`scene-avatar is-${r} ${n===`tutti`?`is-tutti`:``} ${a?`is-online`:``}`,"aria-hidden":`true`,children:n===`tutti`?(0,E.jsx)(`img`,{className:`scene-avatar-image`,src:o.tuttiAvatarSrc,alt:``}):(0,E.jsx)(`img`,{className:`scene-avatar-image`,src:o.humanAvatars[n],alt:``})}),(0,E.jsxs)(`div`,{className:`scene-message-copy`,children:[(0,E.jsxs)(`header`,{children:[(0,E.jsx)(`strong`,{children:t}),(0,E.jsx)(`span`,{children:i})]}),(0,E.jsx)(`p`,{children:s})]})]})}function M({time:e,assets:r}){let i=e>=p.artifactLive,a=e>=p.artifactClick,o=e>=p.runEnd&&e<p.executionComplete,u=d(e,p.runEnd,19.2),f=d(e,p.artifactLive,30.92);return(0,E.jsxs)(`aside`,{className:`scene-sidebar`,children:[(0,E.jsx)(`button`,{className:`scene-brand`,type:`button`,"aria-label":`Tutti`,tabIndex:-1,children:(0,E.jsx)(`img`,{src:r.logoSrc,alt:``})}),(0,E.jsxs)(`div`,{className:`scene-nav-stack`,children:[(0,E.jsxs)(`nav`,{className:`scene-nav`,"aria-label":`Workspace pages`,children:[(0,E.jsx)(`button`,{className:a?``:`is-active`,type:`button`,"aria-label":`Chat`,tabIndex:-1,children:(0,E.jsx)(c,{"aria-hidden":`true`})}),(0,E.jsx)(`button`,{type:`button`,"aria-label":`Worklist`,tabIndex:-1,children:(0,E.jsx)(b,{"aria-hidden":`true`})}),(0,E.jsxs)(`button`,{className:a?`is-active`:``,type:`button`,"aria-label":`Artifacts`,tabIndex:-1,children:[(0,E.jsx)(l,{"aria-hidden":`true`}),i?(0,E.jsx)(`span`,{className:`scene-live-pill`,style:D(f),children:`Live`}):null]}),(0,E.jsx)(`button`,{type:`button`,"aria-label":`References`,tabIndex:-1,children:(0,E.jsx)(C,{"aria-hidden":`true`})}),(0,E.jsx)(`button`,{type:`button`,"aria-label":`Skills`,tabIndex:-1,children:(0,E.jsx)(s,{"aria-hidden":`true`})}),(0,E.jsx)(`button`,{type:`button`,"aria-label":`Timeline`,tabIndex:-1,children:(0,E.jsx)(t,{"aria-hidden":`true`})})]}),o?(0,E.jsx)(`button`,{className:`scene-status-entry is-running`,style:D(u),type:`button`,"aria-label":`Task running`,tabIndex:-1,children:(0,E.jsx)(n,{"aria-hidden":`true`})}):null]}),(0,E.jsx)(`button`,{className:`scene-settings`,type:`button`,"aria-label":`Settings`,tabIndex:-1,children:(0,E.jsx)(h,{"aria-hidden":`true`})})]})}function N({time:e,assets:t}){let n=d(e,30.35,30.92);return(0,E.jsxs)(`section`,{className:`scene-panel scene-chat-panel`,children:[(0,E.jsxs)(`header`,{className:`scene-panel-header`,children:[(0,E.jsx)(`span`,{className:`scene-icon-tile`,children:(0,E.jsx)(c,{"aria-hidden":`true`})}),(0,E.jsxs)(`div`,{children:[(0,E.jsx)(`h2`,{children:`Morrow Studio`}),(0,E.jsx)(`p`,{children:`Ceramics storefront`})]}),(0,E.jsxs)(`span`,{className:`scene-members`,"aria-hidden":`true`,children:[(0,E.jsx)(`i`,{className:`is-green`,children:(0,E.jsx)(`img`,{className:`scene-avatar-image`,src:t.humanAvatars.fey,alt:``})}),(0,E.jsx)(`i`,{className:`is-blue`,children:(0,E.jsx)(`img`,{className:`scene-avatar-image`,src:t.humanAvatars.avery,alt:``})}),(0,E.jsx)(`i`,{className:`is-yellow`,children:(0,E.jsx)(`img`,{className:`scene-avatar-image`,src:t.humanAvatars.jun,alt:``})})]})]}),(0,E.jsxs)(`div`,{className:`scene-chat-stream`,children:[(0,E.jsx)(j,{progress:d(e,1.35,2.05),author:`Fey`,avatar:`fey`,tone:`green`,timestamp:`10:12`,online:!0,assets:t,children:`🏺 Let's build an online shop for our ceramics studio.`}),(0,E.jsx)(j,{progress:d(e,2.65,3.35),author:`Avery`,avatar:`avery`,tone:`blue`,timestamp:`10:13`,online:!0,assets:t,children:`✨ Keep it warm, minimal, and editorial.`}),(0,E.jsx)(j,{progress:d(e,3.95,4.65),author:`Jun`,avatar:`jun`,tone:`yellow`,timestamp:`10:14`,online:!0,assets:t,children:`🎨 Let people preview every piece in different glazes.`}),(0,E.jsx)(j,{progress:d(e,5.25,5.95),author:`Tutti`,avatar:`tutti`,tone:`green`,timestamp:`10:15`,assets:t,children:`Got it — I'll put it together. ✨`}),(0,E.jsxs)(`article`,{className:`scene-task-result`,style:D(n),children:[(0,E.jsx)(`span`,{className:`scene-task-result-rail`,"aria-hidden":`true`}),(0,E.jsx)(`span`,{className:`scene-task-result-icon`,"aria-hidden":`true`,children:(0,E.jsx)(S,{})}),(0,E.jsxs)(`div`,{children:[(0,E.jsx)(`span`,{children:`Task completed`}),(0,E.jsx)(`strong`,{children:`Build ceramics storefront`}),(0,E.jsx)(`small`,{children:`Storefront`}),(0,E.jsx)(`p`,{children:`Warm editorial shopping and glaze previews are ready in Artifacts.`})]}),(0,E.jsx)(i,{className:`scene-task-result-chevron`,"aria-hidden":`true`})]})]}),(0,E.jsxs)(`div`,{className:`scene-composer`,"aria-hidden":`true`,children:[(0,E.jsx)(`span`,{children:`Write a message`}),(0,E.jsx)(T,{})]})]})}function P({progress:e,icon:t,children:n}){return(0,E.jsxs)(`div`,{className:`scene-scratchpad-row`,style:D(e),children:[(0,E.jsx)(`span`,{"aria-hidden":`true`,children:t}),(0,E.jsx)(`p`,{children:n})]})}function F({time:e}){return(0,E.jsxs)(`section`,{className:`scene-panel scene-scratchpad-panel`,style:{"--scratchpad-collapse":d(e,p.runEnd,19.18)},children:[(0,E.jsxs)(`header`,{className:`scene-panel-header`,children:[(0,E.jsx)(`span`,{className:`scene-icon-tile`,children:(0,E.jsx)(v,{"aria-hidden":`true`})}),(0,E.jsx)(`h2`,{children:`Scratchpad`})]}),(0,E.jsxs)(`div`,{className:`scene-scratchpad-body`,children:[(0,E.jsxs)(A,{progress:d(e,8.95,10),className:`scene-scratchpad-intro`,children:[(0,E.jsx)(`h3`,{children:`Ceramics storefront`}),(0,E.jsx)(`p`,{children:`A warm, editorial storefront for a small-batch studio, centered on tactile product discovery.`})]}),(0,E.jsx)(A,{progress:d(e,9.85,10.65),className:`scene-scratchpad-label`,children:`Confirmed`}),(0,E.jsx)(P,{progress:d(e,10.4,11.25),icon:(0,E.jsx)(S,{}),children:`Product-first editorial layout with generous space`}),(0,E.jsx)(P,{progress:d(e,11.2,12.05),icon:(0,E.jsx)(S,{}),children:`Warm neutrals with quiet serif headlines`}),(0,E.jsx)(P,{progress:d(e,12,12.85),icon:(0,E.jsx)(S,{}),children:`Keep the collection small, curated, and story-led`}),(0,E.jsx)(A,{progress:d(e,12.75,13.55),className:`scene-scratchpad-label`,children:`Requested feature`}),(0,E.jsx)(P,{progress:d(e,13.3,14.2),icon:(0,E.jsx)(o,{}),children:`Preview every piece in clay, sage, and ink glazes`})]}),(0,E.jsxs)(`footer`,{className:`scene-scratchpad-footer`,style:D(d(e,15.7,16.8)),children:[(0,E.jsxs)(`span`,{className:`scene-writing-mark`,children:[(0,E.jsx)(`strong`,{children:`Updated`}),(0,E.jsx)(`span`,{children:`just now`})]}),(0,E.jsxs)(`button`,{className:`scene-run-button`,type:`button`,tabIndex:-1,children:[(0,E.jsx)(f,{"aria-hidden":`true`}),(0,E.jsx)(`span`,{children:`Run`})]})]})]})}function I(e,t){let n=Math.max(0,Math.floor((e-t)*50/5)*5);return`${Math.floor(n/60)}m${(n%60).toString().padStart(2,`0`)}s`}function L({time:e,scoreSrc:t}){let r=d(e,18.92,19.2),i=_(e),a=i.id===`complete`,o={prepare:p.runEnd,implement:p.executionPrepareEnd,validate:p.executionImplementEnd,update:p.executionValidateEnd,complete:p.executionComplete}[i.id],s=d(e,o,o+.32),c=k(e);return(0,E.jsx)(`div`,{className:`scene-execution`,style:{"--reveal":r,"--stage-reveal":s},children:(0,E.jsxs)(`article`,{className:`homepage-motion-panel homepage-motion-execution-panel homepage-motion-execution-card is-score-${a?`complete`:`running`}`,children:[(0,E.jsx)(`header`,{className:`homepage-motion-panel-header`,children:(0,E.jsxs)(`div`,{className:`homepage-motion-panel-title`,children:[(0,E.jsx)(`span`,{className:`scene-icon-tile`,"aria-hidden":`true`,children:(0,E.jsx)(y,{})}),(0,E.jsx)(`h3`,{children:`Tutti is working on`})]})}),(0,E.jsxs)(`div`,{className:`homepage-motion-execution-body`,children:[(0,E.jsxs)(`div`,{className:`homepage-motion-execution-step homepage-motion-execution-stage is-${a?`complete`:`running`} has-marker`,children:[(0,E.jsx)(`span`,{className:`homepage-motion-execution-marker ${a?`is-done`:`is-running`}`,"aria-hidden":`true`,children:a?(0,E.jsx)(S,{}):(0,E.jsx)(n,{})}),(0,E.jsx)(`span`,{className:`homepage-motion-execution-step-content homepage-motion-execution-stage-copy`,children:(0,E.jsxs)(`span`,{className:`homepage-motion-execution-step-title`,children:[(0,E.jsx)(`span`,{className:`homepage-motion-execution-step-label`,children:i.label}),a?null:(0,E.jsx)(`span`,{className:`homepage-motion-execution-step-elapsed`,children:` (${I(e,o)})`})]})},i.id)]}),(0,E.jsx)(`div`,{className:`homepage-motion-running-score ${a?`is-complete`:``}`,role:`img`,"aria-label":`Ode to Joy score phrase`,children:(0,E.jsx)(`div`,{className:`homepage-motion-score-passage`,style:{"--score-translate-x":`${-395.3*c}px`},"aria-hidden":`true`,children:(0,E.jsx)(`img`,{src:t,alt:``,draggable:!1})})})]})]})})}function R({color:e,variant:t=`vase`,className:n=``}){let r={"--ceramic-color":e};return t===`cup`?(0,E.jsxs)(`svg`,{className:`ceramic-object is-cup ${n}`,viewBox:`0 0 260 260`,style:r,"aria-hidden":`true`,children:[(0,E.jsx)(`path`,{className:`ceramic-main`,d:`M54 64h132l-9 126c-2 26-20 42-46 42h-22c-26 0-44-16-46-42Z`}),(0,E.jsx)(`path`,{className:`ceramic-outline`,d:`M186 92h18c34 0 34 72 1 76h-27`}),(0,E.jsx)(`ellipse`,{className:`ceramic-rim`,cx:`120`,cy:`64`,rx:`66`,ry:`13`}),(0,E.jsx)(`path`,{className:`ceramic-highlight`,d:`M78 90c5 62 4 91 20 114`})]}):t===`bowl`?(0,E.jsxs)(`svg`,{className:`ceramic-object is-bowl ${n}`,viewBox:`0 0 300 220`,style:r,"aria-hidden":`true`,children:[(0,E.jsx)(`ellipse`,{className:`ceramic-rim`,cx:`150`,cy:`55`,rx:`116`,ry:`24`}),(0,E.jsx)(`path`,{className:`ceramic-main`,d:`M34 55c8 82 45 132 116 132S258 137 266 55c-42 25-190 25-232 0Z`}),(0,E.jsx)(`path`,{className:`ceramic-highlight`,d:`M72 82c18 45 40 70 70 82`}),(0,E.jsx)(`path`,{className:`ceramic-base`,d:`M112 185h76`})]}):(0,E.jsxs)(`svg`,{className:`ceramic-object is-vase ${n}`,viewBox:`0 0 320 420`,style:r,"aria-hidden":`true`,children:[(0,E.jsx)(`path`,{className:`ceramic-main`,d:`M116 56c7 38-17 61-36 96-31 56-28 157 5 199 33 42 117 42 150 0 33-42 36-143 5-199-19-35-43-58-36-96Z`}),(0,E.jsx)(`ellipse`,{className:`ceramic-rim`,cx:`160`,cy:`56`,rx:`44`,ry:`12`}),(0,E.jsx)(`ellipse`,{className:`ceramic-base`,cx:`160`,cy:`365`,rx:`66`,ry:`13`}),(0,E.jsx)(`path`,{className:`ceramic-highlight`,d:`M108 153c-25 64-23 145 1 185`})]})}function z({time:e}){let t=d(e,p.artifactClick,33.25),n=d(e,34.25,34.52),r=d(e,34.88,35.15),i=d(e,35.55,35.82),a=g(e),o=O(`#d9cbb4`,`#c56f4f`,n);e>=34.88&&(o=O(`#c56f4f`,`#6f9275`,r)),e>=35.55&&(o=O(`#6f9275`,`#243a46`,i));let s=e>=35.55?`ink`:e>=34.88?`sage`:e>=34.25?`clay`:null,c={"--artifact-scroll":a};return(0,E.jsxs)(`section`,{className:`scene-panel scene-artifact-panel`,style:D(t),children:[(0,E.jsxs)(`header`,{className:`scene-panel-header`,children:[(0,E.jsx)(`span`,{className:`scene-icon-tile`,children:(0,E.jsx)(l,{"aria-hidden":`true`})}),(0,E.jsx)(`h2`,{children:`Artifacts`}),(0,E.jsx)(`span`,{className:`scene-ready-tag`,children:`Ready`})]}),(0,E.jsx)(`div`,{className:`scene-artifact-canvas`,children:(0,E.jsxs)(`div`,{className:`artifact-page-frame`,style:c,children:[(0,E.jsxs)(`div`,{className:`artifact-page-nav`,children:[(0,E.jsx)(`strong`,{children:`Morrow`}),(0,E.jsx)(`span`,{children:`Objects · Journal · Studio`})]}),(0,E.jsxs)(`section`,{className:`artifact-hero`,children:[(0,E.jsxs)(`div`,{className:`artifact-hero-copy`,children:[(0,E.jsx)(`span`,{className:`artifact-eyebrow`,children:`Hand-finished in small batches`}),(0,E.jsx)(`h3`,{children:`Objects for slower days.`}),(0,E.jsx)(`p`,{children:`Quiet forms, warm glazes, and useful pieces made to live with.`}),(0,E.jsx)(`button`,{type:`button`,tabIndex:-1,children:`Explore the collection`})]}),(0,E.jsxs)(`div`,{className:`artifact-hero-object`,children:[(0,E.jsx)(R,{color:o}),(0,E.jsxs)(`div`,{className:`artifact-glaze-picker`,"aria-label":`Glaze preview`,children:[(0,E.jsx)(`span`,{children:`Glaze`}),(0,E.jsx)(`i`,{className:s===`clay`?`is-active is-clay`:`is-clay`}),(0,E.jsx)(`i`,{className:s===`sage`?`is-active is-sage`:`is-sage`}),(0,E.jsx)(`i`,{className:s===`ink`?`is-active is-ink`:`is-ink`})]})]})]}),(0,E.jsxs)(`section`,{className:`artifact-collection`,children:[(0,E.jsxs)(`header`,{children:[(0,E.jsx)(`span`,{children:`Selected pieces`}),(0,E.jsx)(`p`,{children:`Everyday forms shaped for the rituals around them.`})]}),(0,E.jsxs)(`div`,{className:`artifact-product-grid`,children:[(0,E.jsxs)(`article`,{children:[(0,E.jsx)(R,{color:`#b9785f`,variant:`cup`}),(0,E.jsxs)(`div`,{children:[(0,E.jsx)(`strong`,{children:`Low cup`}),(0,E.jsx)(`span`,{children:`Rust glaze`})]})]}),(0,E.jsxs)(`article`,{children:[(0,E.jsx)(R,{color:`#819283`,variant:`vase`}),(0,E.jsxs)(`div`,{children:[(0,E.jsx)(`strong`,{children:`Field vase`}),(0,E.jsx)(`span`,{children:`Sage glaze`})]})]}),(0,E.jsxs)(`article`,{children:[(0,E.jsx)(R,{color:`#d5c7af`,variant:`bowl`}),(0,E.jsxs)(`div`,{children:[(0,E.jsx)(`strong`,{children:`Gather bowl`}),(0,E.jsx)(`span`,{children:`Flax glaze`})]})]})]})]}),(0,E.jsxs)(`section`,{className:`artifact-studio`,children:[(0,E.jsx)(`span`,{children:`Made by hand · Meant for every day`}),(0,E.jsx)(`h3`,{children:`Useful things can still feel special.`}),(0,E.jsx)(`p`,{children:`We make a small number of considered objects, slowly and close to home.`}),(0,E.jsx)(`button`,{type:`button`,tabIndex:-1,children:`Visit the studio`})]}),(0,E.jsxs)(`footer`,{className:`artifact-page-footer`,children:[(0,E.jsx)(`strong`,{children:`Morrow Ceramics`}),(0,E.jsx)(`span`,{className:`artifact-built-with`,children:`Built with Tutti.`}),(0,E.jsx)(`span`,{children:`Small batch · Est. 2026`})]})]})})]})}function B(e,t,n){return e+(t-e)*n}function V(e,t,n,r,i){let a=d(e,t,n,m);return{x:B(r.x,i.x,a),y:B(r.y,i.y,a)}}function H(e,t,n){return e<t||e>n?0:Math.sin(Math.PI*((e-t)/(n-t)))}function U(e){if(e>=p.runCursorStart&&e<18.92){let t=V(e,p.runCursorStart,p.runCursorArrive,{x:86,y:76},{x:94.25,y:94.2});return{visible:!0,x:t.x,y:t.y,clickPulse:H(e,p.runClickStart,p.runEnd)}}if(e>=31&&e<33.45){let t=V(e,31,32.22,{x:50,y:58},{x:4,y:22.85});return{visible:!0,x:t.x,y:t.y,clickPulse:H(e,32.28,32.6)}}return{visible:!1,x:50,y:50,clickPulse:0}}function W({time:e,assets:t}){let n=u(e),r=U(e),i=a(e),o=d(e,.08,1.05,m);return(0,E.jsx)(`div`,{className:`motion-scene`,role:`img`,"aria-label":`Tutti workflow animation from team discussion to a generated ceramics storefront`,style:{opacity:i,visibility:i<=.001?`hidden`:`visible`},children:(0,E.jsxs)(`div`,{className:`motion-stage`,style:{opacity:o,transform:`translate(50%, 50%) scale(${n.scale}) translate(${-n.centerX*100}%, ${-n.centerY*100}%)`},children:[(0,E.jsx)(M,{time:e,assets:t}),(0,E.jsxs)(`div`,{className:`scene-workspace-layer`,children:[(0,E.jsx)(N,{time:e,assets:t}),(0,E.jsx)(F,{time:e}),(0,E.jsx)(L,{time:e,scoreSrc:t.scoreSrc})]}),(0,E.jsx)(z,{time:e}),(0,E.jsxs)(`span`,{className:`scene-cursor ${r.visible?`is-visible`:``}`,style:{left:`${r.x}%`,top:`${r.y}%`,"--click-pulse":r.clickPulse},"aria-hidden":`true`,children:[(0,E.jsx)(w,{}),(0,E.jsx)(`i`,{})]})]})})}export{W as HomepageMotionScene};
|
|
1
|
+
import{C as e,S as t,_ as n,a as r,b as i,c as a,d as o,f as s,g as c,h as l,i as u,l as d,m as f,n as p,o as m,p as h,r as g,s as _,t as v,u as y,v as b,w as x,x as S,y as C}from"./index-CKKpwsqr.js";var w=e(`mouse-pointer-2`,[[`path`,{d:`M4.037 4.688a.495.495 0 0 1 .651-.651l16 6.5a.5.5 0 0 1-.063.947l-6.124 1.58a2 2 0 0 0-1.438 1.435l-1.579 6.126a.5.5 0 0 1-.947.063z`,key:`edeuup`}]]),T=e(`send`,[[`path`,{d:`M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z`,key:`1ffxy3`}],[`path`,{d:`m21.854 2.147-10.94 10.939`,key:`12cjpa`}]]),E=x();function D(e){return{"--reveal":e}}function O(e,t,n){let r=(e,t)=>Number.parseInt(e.slice(t,t+2),16),i=i=>Math.round(r(e,i)+(r(t,i)-r(e,i))*n).toString(16).padStart(2,`0`);return`#${i(1)}${i(3)}${i(5)}`}function k(e){let t=r((e-p.runEnd)/(p.executionComplete-p.runEnd));return t+t*t-t*t*t}function A({progress:e,children:t,className:n=``}){return(0,E.jsx)(`div`,{className:`scene-reveal ${n}`,style:D(e),children:t})}function j({progress:e,author:t,avatar:n,tone:r,timestamp:i,online:a=!1,assets:o,children:s}){return(0,E.jsxs)(`article`,{className:`scene-message`,style:D(e),children:[(0,E.jsx)(`span`,{className:`scene-avatar is-${r} ${n===`tutti`?`is-tutti`:``} ${a?`is-online`:``}`,"aria-hidden":`true`,children:n===`tutti`?(0,E.jsx)(`img`,{className:`scene-avatar-image`,src:o.tuttiAvatarSrc,alt:``}):(0,E.jsx)(`img`,{className:`scene-avatar-image`,src:o.humanAvatars[n],alt:``})}),(0,E.jsxs)(`div`,{className:`scene-message-copy`,children:[(0,E.jsxs)(`header`,{children:[(0,E.jsx)(`strong`,{children:t}),(0,E.jsx)(`span`,{children:i})]}),(0,E.jsx)(`p`,{children:s})]})]})}function M({time:e,assets:r}){let i=e>=p.artifactLive,a=e>=p.artifactClick,o=e>=p.runEnd&&e<p.executionComplete,u=d(e,p.runEnd,19.2),f=d(e,p.artifactLive,30.92);return(0,E.jsxs)(`aside`,{className:`scene-sidebar`,children:[(0,E.jsx)(`button`,{className:`scene-brand`,type:`button`,"aria-label":`Tutti`,tabIndex:-1,children:(0,E.jsx)(`img`,{src:r.logoSrc,alt:``})}),(0,E.jsxs)(`div`,{className:`scene-nav-stack`,children:[(0,E.jsxs)(`nav`,{className:`scene-nav`,"aria-label":`Workspace pages`,children:[(0,E.jsx)(`button`,{className:a?``:`is-active`,type:`button`,"aria-label":`Chat`,tabIndex:-1,children:(0,E.jsx)(c,{"aria-hidden":`true`})}),(0,E.jsx)(`button`,{type:`button`,"aria-label":`Worklist`,tabIndex:-1,children:(0,E.jsx)(b,{"aria-hidden":`true`})}),(0,E.jsxs)(`button`,{className:a?`is-active`:``,type:`button`,"aria-label":`Artifacts`,tabIndex:-1,children:[(0,E.jsx)(l,{"aria-hidden":`true`}),i?(0,E.jsx)(`span`,{className:`scene-live-pill`,style:D(f),children:`Live`}):null]}),(0,E.jsx)(`button`,{type:`button`,"aria-label":`References`,tabIndex:-1,children:(0,E.jsx)(C,{"aria-hidden":`true`})}),(0,E.jsx)(`button`,{type:`button`,"aria-label":`Skills`,tabIndex:-1,children:(0,E.jsx)(s,{"aria-hidden":`true`})}),(0,E.jsx)(`button`,{type:`button`,"aria-label":`Timeline`,tabIndex:-1,children:(0,E.jsx)(t,{"aria-hidden":`true`})})]}),o?(0,E.jsx)(`button`,{className:`scene-status-entry is-running`,style:D(u),type:`button`,"aria-label":`Task running`,tabIndex:-1,children:(0,E.jsx)(n,{"aria-hidden":`true`})}):null]}),(0,E.jsx)(`button`,{className:`scene-settings`,type:`button`,"aria-label":`Settings`,tabIndex:-1,children:(0,E.jsx)(h,{"aria-hidden":`true`})})]})}function N({time:e,assets:t}){let n=d(e,30.35,30.92);return(0,E.jsxs)(`section`,{className:`scene-panel scene-chat-panel`,children:[(0,E.jsxs)(`header`,{className:`scene-panel-header`,children:[(0,E.jsx)(`span`,{className:`scene-icon-tile`,children:(0,E.jsx)(c,{"aria-hidden":`true`})}),(0,E.jsxs)(`div`,{children:[(0,E.jsx)(`h2`,{children:`Morrow Studio`}),(0,E.jsx)(`p`,{children:`Ceramics storefront`})]}),(0,E.jsxs)(`span`,{className:`scene-members`,"aria-hidden":`true`,children:[(0,E.jsx)(`i`,{className:`is-green`,children:(0,E.jsx)(`img`,{className:`scene-avatar-image`,src:t.humanAvatars.fey,alt:``})}),(0,E.jsx)(`i`,{className:`is-blue`,children:(0,E.jsx)(`img`,{className:`scene-avatar-image`,src:t.humanAvatars.avery,alt:``})}),(0,E.jsx)(`i`,{className:`is-yellow`,children:(0,E.jsx)(`img`,{className:`scene-avatar-image`,src:t.humanAvatars.jun,alt:``})})]})]}),(0,E.jsxs)(`div`,{className:`scene-chat-stream`,children:[(0,E.jsx)(j,{progress:d(e,1.35,2.05),author:`Fey`,avatar:`fey`,tone:`green`,timestamp:`10:12`,online:!0,assets:t,children:`🏺 Let's build an online shop for our ceramics studio.`}),(0,E.jsx)(j,{progress:d(e,2.65,3.35),author:`Avery`,avatar:`avery`,tone:`blue`,timestamp:`10:13`,online:!0,assets:t,children:`✨ Keep it warm, minimal, and editorial.`}),(0,E.jsx)(j,{progress:d(e,3.95,4.65),author:`Jun`,avatar:`jun`,tone:`yellow`,timestamp:`10:14`,online:!0,assets:t,children:`🎨 Let people preview every piece in different glazes.`}),(0,E.jsx)(j,{progress:d(e,5.25,5.95),author:`Tutti`,avatar:`tutti`,tone:`green`,timestamp:`10:15`,assets:t,children:`Got it — I'll put it together. ✨`}),(0,E.jsxs)(`article`,{className:`scene-task-result`,style:D(n),children:[(0,E.jsx)(`span`,{className:`scene-task-result-rail`,"aria-hidden":`true`}),(0,E.jsx)(`span`,{className:`scene-task-result-icon`,"aria-hidden":`true`,children:(0,E.jsx)(S,{})}),(0,E.jsxs)(`div`,{children:[(0,E.jsx)(`span`,{children:`Task completed`}),(0,E.jsx)(`strong`,{children:`Build ceramics storefront`}),(0,E.jsx)(`small`,{children:`Storefront`}),(0,E.jsx)(`p`,{children:`Warm editorial shopping and glaze previews are ready in Artifacts.`})]}),(0,E.jsx)(i,{className:`scene-task-result-chevron`,"aria-hidden":`true`})]})]}),(0,E.jsxs)(`div`,{className:`scene-composer`,"aria-hidden":`true`,children:[(0,E.jsx)(`span`,{children:`Write a message`}),(0,E.jsx)(T,{})]})]})}function P({progress:e,icon:t,children:n}){return(0,E.jsxs)(`div`,{className:`scene-scratchpad-row`,style:D(e),children:[(0,E.jsx)(`span`,{"aria-hidden":`true`,children:t}),(0,E.jsx)(`p`,{children:n})]})}function F({time:e}){return(0,E.jsxs)(`section`,{className:`scene-panel scene-scratchpad-panel`,style:{"--scratchpad-collapse":d(e,p.runEnd,19.18)},children:[(0,E.jsxs)(`header`,{className:`scene-panel-header`,children:[(0,E.jsx)(`span`,{className:`scene-icon-tile`,children:(0,E.jsx)(v,{"aria-hidden":`true`})}),(0,E.jsx)(`h2`,{children:`Scratchpad`})]}),(0,E.jsxs)(`div`,{className:`scene-scratchpad-body`,children:[(0,E.jsxs)(A,{progress:d(e,8.95,10),className:`scene-scratchpad-intro`,children:[(0,E.jsx)(`h3`,{children:`Ceramics storefront`}),(0,E.jsx)(`p`,{children:`A warm, editorial storefront for a small-batch studio, centered on tactile product discovery.`})]}),(0,E.jsx)(A,{progress:d(e,9.85,10.65),className:`scene-scratchpad-label`,children:`Confirmed`}),(0,E.jsx)(P,{progress:d(e,10.4,11.25),icon:(0,E.jsx)(S,{}),children:`Product-first editorial layout with generous space`}),(0,E.jsx)(P,{progress:d(e,11.2,12.05),icon:(0,E.jsx)(S,{}),children:`Warm neutrals with quiet serif headlines`}),(0,E.jsx)(P,{progress:d(e,12,12.85),icon:(0,E.jsx)(S,{}),children:`Keep the collection small, curated, and story-led`}),(0,E.jsx)(A,{progress:d(e,12.75,13.55),className:`scene-scratchpad-label`,children:`Requested feature`}),(0,E.jsx)(P,{progress:d(e,13.3,14.2),icon:(0,E.jsx)(o,{}),children:`Preview every piece in clay, sage, and ink glazes`})]}),(0,E.jsxs)(`footer`,{className:`scene-scratchpad-footer`,style:D(d(e,15.7,16.8)),children:[(0,E.jsxs)(`span`,{className:`scene-writing-mark`,children:[(0,E.jsx)(`strong`,{children:`Updated`}),(0,E.jsx)(`span`,{children:`just now`})]}),(0,E.jsxs)(`button`,{className:`scene-run-button`,type:`button`,tabIndex:-1,children:[(0,E.jsx)(f,{"aria-hidden":`true`}),(0,E.jsx)(`span`,{children:`Run`})]})]})]})}function I(e,t){let n=Math.max(0,Math.floor((e-t)*50/5)*5);return`${Math.floor(n/60)}m${(n%60).toString().padStart(2,`0`)}s`}function L({time:e,scoreSrc:t}){let r=d(e,18.92,19.2),i=_(e),a=i.id===`complete`,o={prepare:p.runEnd,implement:p.executionPrepareEnd,validate:p.executionImplementEnd,update:p.executionValidateEnd,complete:p.executionComplete}[i.id],s=d(e,o,o+.32),c=k(e);return(0,E.jsx)(`div`,{className:`scene-execution`,style:{"--reveal":r,"--stage-reveal":s},children:(0,E.jsxs)(`article`,{className:`homepage-motion-panel homepage-motion-execution-panel homepage-motion-execution-card is-score-${a?`complete`:`running`}`,children:[(0,E.jsx)(`header`,{className:`homepage-motion-panel-header`,children:(0,E.jsxs)(`div`,{className:`homepage-motion-panel-title`,children:[(0,E.jsx)(`span`,{className:`scene-icon-tile`,"aria-hidden":`true`,children:(0,E.jsx)(y,{})}),(0,E.jsx)(`h3`,{children:`Tutti is working on`})]})}),(0,E.jsxs)(`div`,{className:`homepage-motion-execution-body`,children:[(0,E.jsxs)(`div`,{className:`homepage-motion-execution-step homepage-motion-execution-stage is-${a?`complete`:`running`} has-marker`,children:[(0,E.jsx)(`span`,{className:`homepage-motion-execution-marker ${a?`is-done`:`is-running`}`,"aria-hidden":`true`,children:a?(0,E.jsx)(S,{}):(0,E.jsx)(n,{})}),(0,E.jsx)(`span`,{className:`homepage-motion-execution-step-content homepage-motion-execution-stage-copy`,children:(0,E.jsxs)(`span`,{className:`homepage-motion-execution-step-title`,children:[(0,E.jsx)(`span`,{className:`homepage-motion-execution-step-label`,children:i.label}),a?null:(0,E.jsx)(`span`,{className:`homepage-motion-execution-step-elapsed`,children:` (${I(e,o)})`})]})},i.id)]}),(0,E.jsx)(`div`,{className:`homepage-motion-running-score ${a?`is-complete`:``}`,role:`img`,"aria-label":`Ode to Joy score phrase`,children:(0,E.jsx)(`div`,{className:`homepage-motion-score-passage`,style:{"--score-translate-x":`${-395.3*c}px`},"aria-hidden":`true`,children:(0,E.jsx)(`img`,{src:t,alt:``,draggable:!1})})})]})]})})}function R({color:e,variant:t=`vase`,className:n=``}){let r={"--ceramic-color":e};return t===`cup`?(0,E.jsxs)(`svg`,{className:`ceramic-object is-cup ${n}`,viewBox:`0 0 260 260`,style:r,"aria-hidden":`true`,children:[(0,E.jsx)(`path`,{className:`ceramic-main`,d:`M54 64h132l-9 126c-2 26-20 42-46 42h-22c-26 0-44-16-46-42Z`}),(0,E.jsx)(`path`,{className:`ceramic-outline`,d:`M186 92h18c34 0 34 72 1 76h-27`}),(0,E.jsx)(`ellipse`,{className:`ceramic-rim`,cx:`120`,cy:`64`,rx:`66`,ry:`13`}),(0,E.jsx)(`path`,{className:`ceramic-highlight`,d:`M78 90c5 62 4 91 20 114`})]}):t===`bowl`?(0,E.jsxs)(`svg`,{className:`ceramic-object is-bowl ${n}`,viewBox:`0 0 300 220`,style:r,"aria-hidden":`true`,children:[(0,E.jsx)(`ellipse`,{className:`ceramic-rim`,cx:`150`,cy:`55`,rx:`116`,ry:`24`}),(0,E.jsx)(`path`,{className:`ceramic-main`,d:`M34 55c8 82 45 132 116 132S258 137 266 55c-42 25-190 25-232 0Z`}),(0,E.jsx)(`path`,{className:`ceramic-highlight`,d:`M72 82c18 45 40 70 70 82`}),(0,E.jsx)(`path`,{className:`ceramic-base`,d:`M112 185h76`})]}):(0,E.jsxs)(`svg`,{className:`ceramic-object is-vase ${n}`,viewBox:`0 0 320 420`,style:r,"aria-hidden":`true`,children:[(0,E.jsx)(`path`,{className:`ceramic-main`,d:`M116 56c7 38-17 61-36 96-31 56-28 157 5 199 33 42 117 42 150 0 33-42 36-143 5-199-19-35-43-58-36-96Z`}),(0,E.jsx)(`ellipse`,{className:`ceramic-rim`,cx:`160`,cy:`56`,rx:`44`,ry:`12`}),(0,E.jsx)(`ellipse`,{className:`ceramic-base`,cx:`160`,cy:`365`,rx:`66`,ry:`13`}),(0,E.jsx)(`path`,{className:`ceramic-highlight`,d:`M108 153c-25 64-23 145 1 185`})]})}function z({time:e}){let t=d(e,p.artifactClick,33.25),n=d(e,34.25,34.52),r=d(e,34.88,35.15),i=d(e,35.55,35.82),a=g(e),o=O(`#d9cbb4`,`#c56f4f`,n);e>=34.88&&(o=O(`#c56f4f`,`#6f9275`,r)),e>=35.55&&(o=O(`#6f9275`,`#243a46`,i));let s=e>=35.55?`ink`:e>=34.88?`sage`:e>=34.25?`clay`:null,c={"--artifact-scroll":a};return(0,E.jsxs)(`section`,{className:`scene-panel scene-artifact-panel`,style:D(t),children:[(0,E.jsxs)(`header`,{className:`scene-panel-header`,children:[(0,E.jsx)(`span`,{className:`scene-icon-tile`,children:(0,E.jsx)(l,{"aria-hidden":`true`})}),(0,E.jsx)(`h2`,{children:`Artifacts`}),(0,E.jsx)(`span`,{className:`scene-ready-tag`,children:`Ready`})]}),(0,E.jsx)(`div`,{className:`scene-artifact-canvas`,children:(0,E.jsxs)(`div`,{className:`artifact-page-frame`,style:c,children:[(0,E.jsxs)(`div`,{className:`artifact-page-nav`,children:[(0,E.jsx)(`strong`,{children:`Morrow`}),(0,E.jsx)(`span`,{children:`Objects · Journal · Studio`})]}),(0,E.jsxs)(`section`,{className:`artifact-hero`,children:[(0,E.jsxs)(`div`,{className:`artifact-hero-copy`,children:[(0,E.jsx)(`span`,{className:`artifact-eyebrow`,children:`Hand-finished in small batches`}),(0,E.jsx)(`h3`,{children:`Objects for slower days.`}),(0,E.jsx)(`p`,{children:`Quiet forms, warm glazes, and useful pieces made to live with.`}),(0,E.jsx)(`button`,{type:`button`,tabIndex:-1,children:`Explore the collection`})]}),(0,E.jsxs)(`div`,{className:`artifact-hero-object`,children:[(0,E.jsx)(R,{color:o}),(0,E.jsxs)(`div`,{className:`artifact-glaze-picker`,"aria-label":`Glaze preview`,children:[(0,E.jsx)(`span`,{children:`Glaze`}),(0,E.jsx)(`i`,{className:s===`clay`?`is-active is-clay`:`is-clay`}),(0,E.jsx)(`i`,{className:s===`sage`?`is-active is-sage`:`is-sage`}),(0,E.jsx)(`i`,{className:s===`ink`?`is-active is-ink`:`is-ink`})]})]})]}),(0,E.jsxs)(`section`,{className:`artifact-collection`,children:[(0,E.jsxs)(`header`,{children:[(0,E.jsx)(`span`,{children:`Selected pieces`}),(0,E.jsx)(`p`,{children:`Everyday forms shaped for the rituals around them.`})]}),(0,E.jsxs)(`div`,{className:`artifact-product-grid`,children:[(0,E.jsxs)(`article`,{children:[(0,E.jsx)(R,{color:`#b9785f`,variant:`cup`}),(0,E.jsxs)(`div`,{children:[(0,E.jsx)(`strong`,{children:`Low cup`}),(0,E.jsx)(`span`,{children:`Rust glaze`})]})]}),(0,E.jsxs)(`article`,{children:[(0,E.jsx)(R,{color:`#819283`,variant:`vase`}),(0,E.jsxs)(`div`,{children:[(0,E.jsx)(`strong`,{children:`Field vase`}),(0,E.jsx)(`span`,{children:`Sage glaze`})]})]}),(0,E.jsxs)(`article`,{children:[(0,E.jsx)(R,{color:`#d5c7af`,variant:`bowl`}),(0,E.jsxs)(`div`,{children:[(0,E.jsx)(`strong`,{children:`Gather bowl`}),(0,E.jsx)(`span`,{children:`Flax glaze`})]})]})]})]}),(0,E.jsxs)(`section`,{className:`artifact-studio`,children:[(0,E.jsx)(`span`,{children:`Made by hand · Meant for every day`}),(0,E.jsx)(`h3`,{children:`Useful things can still feel special.`}),(0,E.jsx)(`p`,{children:`We make a small number of considered objects, slowly and close to home.`}),(0,E.jsx)(`button`,{type:`button`,tabIndex:-1,children:`Visit the studio`})]}),(0,E.jsxs)(`footer`,{className:`artifact-page-footer`,children:[(0,E.jsx)(`strong`,{children:`Morrow Ceramics`}),(0,E.jsx)(`span`,{className:`artifact-built-with`,children:`Built with Tutti.`}),(0,E.jsx)(`span`,{children:`Small batch · Est. 2026`})]})]})})]})}function B(e,t,n){return e+(t-e)*n}function V(e,t,n,r,i){let a=d(e,t,n,m);return{x:B(r.x,i.x,a),y:B(r.y,i.y,a)}}function H(e,t,n){return e<t||e>n?0:Math.sin(Math.PI*((e-t)/(n-t)))}function U(e){if(e>=p.runCursorStart&&e<18.92){let t=V(e,p.runCursorStart,p.runCursorArrive,{x:86,y:76},{x:94.25,y:94.2});return{visible:!0,x:t.x,y:t.y,clickPulse:H(e,p.runClickStart,p.runEnd)}}if(e>=31&&e<33.45){let t=V(e,31,32.22,{x:50,y:58},{x:4,y:22.85});return{visible:!0,x:t.x,y:t.y,clickPulse:H(e,32.28,32.6)}}return{visible:!1,x:50,y:50,clickPulse:0}}function W({time:e,assets:t}){let n=u(e),r=U(e),i=a(e),o=d(e,.08,1.05,m);return(0,E.jsx)(`div`,{className:`motion-scene`,role:`img`,"aria-label":`Tutti workflow animation from team discussion to a generated ceramics storefront`,style:{opacity:i,visibility:i<=.001?`hidden`:`visible`},children:(0,E.jsxs)(`div`,{className:`motion-stage`,style:{opacity:o,transform:`translate(50%, 50%) scale(${n.scale}) translate(${-n.centerX*100}%, ${-n.centerY*100}%)`},children:[(0,E.jsx)(M,{time:e,assets:t}),(0,E.jsxs)(`div`,{className:`scene-workspace-layer`,children:[(0,E.jsx)(N,{time:e,assets:t}),(0,E.jsx)(F,{time:e}),(0,E.jsx)(L,{time:e,scoreSrc:t.scoreSrc})]}),(0,E.jsx)(z,{time:e}),(0,E.jsxs)(`span`,{className:`scene-cursor ${r.visible?`is-visible`:``}`,style:{left:`${r.x}%`,top:`${r.y}%`,"--click-pulse":r.clickPulse},"aria-hidden":`true`,children:[(0,E.jsx)(w,{}),(0,E.jsx)(`i`,{})]})]})})}export{W as HomepageMotionScene};
|