@xfey/tutti 0.1.78 → 0.1.79

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.
Files changed (54) hide show
  1. package/dist/chat-assistant/index.d.ts +11 -1
  2. package/dist/chat-assistant/index.js +61 -7
  3. package/dist/checks/index.d.ts +22 -0
  4. package/dist/checks/index.js +110 -4
  5. package/dist/control-plane/follow-up-start.d.ts +1 -0
  6. package/dist/control-plane/follow-up-start.js +3 -1
  7. package/dist/control-plane/index.d.ts +6 -1
  8. package/dist/control-plane/index.js +72 -11
  9. package/dist/control-plane/procedure-logging.js +24 -67
  10. package/dist/control-plane/project-context-bootstrap.js +7 -1
  11. package/dist/control-plane/reference-summary-refresh.js +1 -1
  12. package/dist/control-plane/run-result-recording.js +2 -1
  13. package/dist/control-plane/task-compile-start.d.ts +1 -0
  14. package/dist/control-plane/task-compile-start.js +11 -1
  15. package/dist/control-plane/workflows/openai.d.ts +6 -0
  16. package/dist/control-plane/workflows/openai.js +16 -3
  17. package/dist/procedure-engine/index.d.ts +1 -0
  18. package/dist/procedure-engine/index.js +7 -1
  19. package/dist/process-tree.d.ts +25 -0
  20. package/dist/process-tree.js +121 -0
  21. package/dist/providers/openai/app-server/invocation-coordinator.d.ts +28 -0
  22. package/dist/providers/openai/app-server/invocation-coordinator.js +104 -0
  23. package/dist/providers/openai/app-server/invocation-lifecycle.d.ts +45 -0
  24. package/dist/providers/openai/app-server/invocation-lifecycle.js +104 -0
  25. package/dist/providers/openai/app-server/json-rpc.d.ts +1 -1
  26. package/dist/providers/openai/app-server/json-rpc.js +9 -16
  27. package/dist/providers/openai/app-server/read-only-procedure.d.ts +8 -2
  28. package/dist/providers/openai/app-server/read-only-procedure.js +63 -11
  29. package/dist/providers/openai/app-server/smoke-types.d.ts +4 -2
  30. package/dist/providers/openai/app-server/smoke.js +41 -12
  31. package/dist/providers/openai/app-server/stale-temp-cleanup.d.ts +17 -0
  32. package/dist/providers/openai/app-server/stale-temp-cleanup.js +130 -0
  33. package/dist/providers/openai/app-server/workspace-write-run.d.ts +8 -2
  34. package/dist/providers/openai/app-server/workspace-write-run.js +63 -11
  35. package/dist/providers/openai/chat-assistant.d.ts +6 -0
  36. package/dist/providers/openai/chat-assistant.js +12 -0
  37. package/dist/run-engine/index.js +2 -1
  38. package/dist/run-pipeline/artifact-applicability.js +2 -2
  39. package/dist/run-pipeline/openai.d.ts +9 -0
  40. package/dist/run-pipeline/openai.js +26 -26
  41. package/dist/run-pipeline/task-run-invocation.js +6 -0
  42. package/dist/runtime-failures.d.ts +3 -0
  43. package/dist/runtime-failures.js +22 -0
  44. package/dist/server-shell/cli/host-lifecycle.js +1 -1
  45. package/dist/server-shell/cli/host-project-runtime-lifecycle.d.ts +43 -0
  46. package/dist/server-shell/cli/host-project-runtime-lifecycle.js +54 -0
  47. package/dist/server-shell/cli/host-server-runtime.js +77 -14
  48. package/dist/server-shell/http/create-server.d.ts +3 -0
  49. package/dist/server-shell/http/create-server.js +10 -4
  50. package/package.json +1 -1
  51. package/web/assets/{homepage-motion-scene-sNI8fBH3.js → homepage-motion-scene-BFQnHtRi.js} +1 -1
  52. package/web/assets/index-C4cdSaSb.js +69 -0
  53. package/web/index.html +1 -1
  54. package/web/assets/index-d-q9yZ09.js +0 -69
@@ -251,13 +251,13 @@ export async function runArtifactCandidateGate(options) {
251
251
  try {
252
252
  applicability = await judgeApplicability(options.judge ?? createDefaultJudge(options), input);
253
253
  }
254
- catch (error) {
254
+ catch {
255
255
  return gateFailure({
256
256
  reasonCode: "artifact_applicability_failed",
257
257
  summary: "Tutti could not determine whether this candidate requires an Artifact.",
258
258
  guidance: "Retry when the Artifact applicability provider is available.",
259
259
  source: "applicability_judge",
260
- detail: error instanceof Error ? error.message : "Artifact applicability judge failed",
260
+ detail: "Artifact applicability could not be determined by the provider.",
261
261
  correctable: false,
262
262
  });
263
263
  }
@@ -1,5 +1,6 @@
1
1
  import type { ProjectId } from "@tutti/shared/ids";
2
2
  import type { ApprovalRequestManager } from "../approvals/index.js";
3
+ import { type CheckProcessCoordinator } from "../checks/index.js";
3
4
  import type { ControlPlaneLogger, RunPipelineResolution, RunPipelineRunner } from "../control-plane/index.js";
4
5
  import type { ProviderUsageRecorder } from "../provider-usage/index.js";
5
6
  import type { OpenAiStructuredOutputClient } from "../providers/openai/sdk-procedure-runner.js";
@@ -8,6 +9,8 @@ import { type ArtifactApplicabilityJudge } from "./artifact-applicability.js";
8
9
  import { type OpenAiProviderConfig } from "../providers/openai/provider-config.js";
9
10
  import { type CodexLinuxSandboxReadiness } from "../providers/openai/app-server/linux-sandbox-readiness.js";
10
11
  import type { CodexAppServerAgentContextRuntime } from "../providers/openai/app-server/skills.js";
12
+ import type { CodexAppServerInvocationCoordinator } from "../providers/openai/app-server/invocation-coordinator.js";
13
+ import type { CodexAppServerLifecycleIncident } from "../providers/openai/app-server/invocation-lifecycle.js";
11
14
  export { TASK_RUN_OUTPUT_SCHEMA } from "./task-run-output.js";
12
15
  export type OpenAiRunPipelineRunnerOptions = {
13
16
  config: OpenAiProviderConfig;
@@ -23,9 +26,12 @@ export type OpenAiRunPipelineRunnerOptions = {
23
26
  requestTimeoutMs?: number;
24
27
  turnTimeoutMs?: number;
25
28
  checkTimeoutMs?: number;
29
+ checkProcessCoordinator?: CheckProcessCoordinator;
26
30
  baseEnv?: NodeJS.ProcessEnv;
27
31
  logger?: ControlPlaneLogger | undefined;
28
32
  recordUsage?: ProviderUsageRecorder;
33
+ invocationCoordinator?: CodexAppServerInvocationCoordinator;
34
+ onLifecycleIncident?: (incident: CodexAppServerLifecycleIncident) => void;
29
35
  artifactApplicabilityClient?: OpenAiStructuredOutputClient;
30
36
  artifactApplicabilityJudge?: ArtifactApplicabilityJudge;
31
37
  validateArtifactCandidate?: (options: ValidateCandidateArtifactOptions) => Promise<CandidateArtifactValidationResult>;
@@ -40,6 +46,9 @@ export type ProjectOpenAiRunPipelineRunnerOptions = {
40
46
  approvalManager?: ApprovalRequestManager;
41
47
  logger?: ControlPlaneLogger | undefined;
42
48
  recordUsage?: ProviderUsageRecorder;
49
+ invocationCoordinator?: CodexAppServerInvocationCoordinator;
50
+ checkProcessCoordinator?: CheckProcessCoordinator;
51
+ onLifecycleIncident?: (incident: CodexAppServerLifecycleIncident) => void;
43
52
  };
44
53
  export declare function createOpenAiRunPipelineRunner(options: OpenAiRunPipelineRunnerOptions): RunPipelineRunner;
45
54
  export declare function resolveProjectOpenAiRunPipelineRunner(options: ProjectOpenAiRunPipelineRunnerOptions): RunPipelineResolution;
@@ -1,4 +1,4 @@
1
- import { createNodeNpmCheckPlan, executeCheckPlan } from "../checks/index.js";
1
+ import { createNodeNpmCheckPlan, executeCheckPlan, } from "../checks/index.js";
2
2
  import { runArtifactCandidateGate, } from "./artifact-applicability.js";
3
3
  import { checkSkipFields, checkStatus, checksSummary, completedWithoutRepoChanges, docsStatusForChangedPaths, failedBeforeChecks, failedFinishedResult, promotionNotAttempted, promotionNotPromoted, successfulRunSummary, } from "./result-projections.js";
4
4
  import { runWorkspaceWriteTask } from "./task-run-invocation.js";
@@ -13,28 +13,16 @@ function isRecord(value) {
13
13
  return typeof value === "object" && value !== null && !Array.isArray(value);
14
14
  }
15
15
  function summarizeProviderError(error) {
16
- const diagnosticParts = [
17
- error.details.turn_error_code === undefined
18
- ? ""
19
- : `provider_code=${error.details.turn_error_code}`,
20
- error.details.turn_error_http_status === undefined
21
- ? ""
22
- : `http_status=${error.details.turn_error_http_status}`,
23
- error.details.stderr_excerpt === undefined || error.details.stderr_excerpt.trim() === ""
24
- ? ""
25
- : `stderr=${error.details.stderr_excerpt}`,
26
- ].filter((part) => part !== "");
27
- const summary = diagnosticParts.length === 0
28
- ? error.message
29
- : `${error.message} (${diagnosticParts.join("; ")})`;
30
- const looksLikeSchemaError = summary.includes("invalid_json_schema") ||
31
- summary.includes("Invalid schema") ||
32
- summary.includes("response_format");
16
+ const looksLikeSchemaError = error.details.turn_error_code === "invalid_json_schema" ||
17
+ error.message.includes("invalid_json_schema") ||
18
+ error.message.includes("Invalid schema") ||
19
+ error.message.includes("response_format");
20
+ const errorCode = looksLikeSchemaError
21
+ ? "codex_app_server_output_schema_invalid"
22
+ : `codex_app_server_${error.code}`;
33
23
  return failedBeforeChecks({
34
- summary,
35
- errorCode: looksLikeSchemaError
36
- ? "codex_app_server_output_schema_invalid"
37
- : `codex_app_server_${error.code}`,
24
+ summary: `Provider execution failed before checks. (${errorCode})`,
25
+ errorCode,
38
26
  retryable: !looksLikeSchemaError &&
39
27
  (error.code === "app_server_protocol_error" ||
40
28
  error.code === "turn_timeout" ||
@@ -44,13 +32,13 @@ function summarizeProviderError(error) {
44
32
  }
45
33
  function summarizeWorkspaceOpsError(error, changedPaths) {
46
34
  return failedBeforeChecks({
47
- summary: error.message,
35
+ summary: `Workspace operation failed before checks. (workspace_ops_${error.code})`,
48
36
  errorCode: `workspace_ops_${error.code}`,
49
37
  retryable: error.code === "conflict",
50
38
  ...(changedPaths === undefined ? {} : { changedPaths }),
51
39
  promotion: error.code === "conflict"
52
- ? promotionNotPromoted("mainline_not_promotable", error.message)
53
- : promotionNotAttempted("workspace_ops_failed", error.message),
40
+ ? promotionNotPromoted("mainline_not_promotable", "Workspace state could not be accepted safely.")
41
+ : promotionNotAttempted("workspace_ops_failed", "Workspace operation failed before promotion."),
54
42
  });
55
43
  }
56
44
  function runLineageFields(input) {
@@ -359,6 +347,9 @@ async function runTaskAttempt(options, input) {
359
347
  workspaceRoot: options.workspaceRoot,
360
348
  plan: checkPlan,
361
349
  ...(options.checkTimeoutMs === undefined ? {} : { timeoutMs: options.checkTimeoutMs }),
350
+ ...(options.checkProcessCoordinator === undefined
351
+ ? {}
352
+ : { processCoordinator: options.checkProcessCoordinator }),
362
353
  }), (result) => ({ checks_status: result.status, command_count: result.commands.length }));
363
354
  if (checks.status === "failed") {
364
355
  const result = failedFinishedResult({
@@ -441,7 +432,7 @@ async function runTaskAttempt(options, input) {
441
432
  return summarizeWorkspaceOpsError(error, changedPaths);
442
433
  }
443
434
  return failedBeforeChecks({
444
- summary: error instanceof Error ? error.message : "Run pipeline failed",
435
+ summary: "Run pipeline failed before checks. (run_pipeline_failed)",
445
436
  errorCode: "run_pipeline_failed",
446
437
  retryable: false,
447
438
  ...(changedPaths === undefined ? {} : { changedPaths }),
@@ -529,6 +520,15 @@ export function resolveProjectOpenAiRunPipelineRunner(options) {
529
520
  ...(options.promptsRoot === undefined ? {} : { promptsRoot: options.promptsRoot }),
530
521
  ...(options.logger === undefined ? {} : { logger: options.logger }),
531
522
  ...(options.recordUsage === undefined ? {} : { recordUsage: options.recordUsage }),
523
+ ...(options.invocationCoordinator === undefined
524
+ ? {}
525
+ : { invocationCoordinator: options.invocationCoordinator }),
526
+ ...(options.checkProcessCoordinator === undefined
527
+ ? {}
528
+ : { checkProcessCoordinator: options.checkProcessCoordinator }),
529
+ ...(options.onLifecycleIncident === undefined
530
+ ? {}
531
+ : { onLifecycleIncident: options.onLifecycleIncident }),
532
532
  ...(process.platform === "linux"
533
533
  ? { codexSandboxReadiness: () => inspectCodexLinuxSandboxReadiness() }
534
534
  : {}),
@@ -111,6 +111,12 @@ export async function runWorkspaceWriteTask(options, input) {
111
111
  : { requestTimeoutMs: options.requestTimeoutMs }),
112
112
  ...(options.turnTimeoutMs === undefined ? {} : { turnTimeoutMs: options.turnTimeoutMs }),
113
113
  ...(options.baseEnv === undefined ? {} : { baseEnv: options.baseEnv }),
114
+ ...(options.invocationCoordinator === undefined
115
+ ? {}
116
+ : { invocationCoordinator: options.invocationCoordinator }),
117
+ ...(options.onLifecycleIncident === undefined
118
+ ? {}
119
+ : { onLifecycleIncident: options.onLifecycleIncident }),
114
120
  runtimeTelemetry: {
115
121
  activityRef: input.activity_ref,
116
122
  stageId: "provider_task_run",
@@ -0,0 +1,3 @@
1
+ export declare function stableRuntimeFailureCode(error: unknown, fallback: string): string;
2
+ export declare function safeRuntimeFailureSummary(subject: "Procedure" | "Run", error: unknown, fallbackCode: "procedure_failed" | "run_failed"): string;
3
+ //# sourceMappingURL=runtime-failures.d.ts.map
@@ -0,0 +1,22 @@
1
+ const STABLE_FAILURE_CODE = /^[a-z][a-z0-9_]{0,63}$/u;
2
+ export function stableRuntimeFailureCode(error, fallback) {
3
+ if (typeof error === "object" &&
4
+ error !== null &&
5
+ "code" in error &&
6
+ typeof error.code === "string" &&
7
+ STABLE_FAILURE_CODE.test(error.code)) {
8
+ return error.code;
9
+ }
10
+ return fallback;
11
+ }
12
+ export function safeRuntimeFailureSummary(subject, error, fallbackCode) {
13
+ const code = stableRuntimeFailureCode(error, fallbackCode);
14
+ if (code === "process_lifecycle_failed" || code === "process_tree_not_terminated") {
15
+ return `${subject} stopped because the provider process could not close safely. Restart the Host before retrying. (${code})`;
16
+ }
17
+ if (code === "admission_unavailable" || code === "admission_closed") {
18
+ return `${subject} stopped because provider execution is unavailable while the Host is closing. Retry after the Host is ready. (${code})`;
19
+ }
20
+ return `${subject} failed because the runtime returned an internal error. Retry the action. (${code})`;
21
+ }
22
+ //# sourceMappingURL=runtime-failures.js.map
@@ -393,7 +393,7 @@ export function formatLaunchLifecycleResult(result, options = {}) {
393
393
  }
394
394
  export async function waitForForegroundHostShutdown(handle, processLike = process) {
395
395
  const stop = () => {
396
- void handle.close();
396
+ void handle.close().catch(() => undefined);
397
397
  };
398
398
  processLike.once("SIGINT", stop);
399
399
  processLike.once("SIGTERM", stop);
@@ -0,0 +1,43 @@
1
+ type ControlPlaneLifecycleOwner = {
2
+ beginShutdown: () => void;
3
+ waitForIdle: () => Promise<void>;
4
+ };
5
+ type ChatAssistantLifecycleOwner = {
6
+ stopAccepting: () => void;
7
+ waitForIdle: () => Promise<void>;
8
+ };
9
+ type InvocationLifecycleOwner = {
10
+ stopAccepting: () => void;
11
+ closeActive: () => Promise<void>;
12
+ waitForIdle: () => Promise<void>;
13
+ };
14
+ type CheckLifecycleOwner = {
15
+ stopAccepting: () => void;
16
+ terminateActive: () => Promise<void>;
17
+ waitForIdle: () => Promise<void>;
18
+ };
19
+ type ApprovalLifecycleOwner = {
20
+ clearPending: () => void;
21
+ dispose: () => void;
22
+ };
23
+ export declare class HostProjectRuntimeShutdownError extends Error {
24
+ readonly code: "drain_timeout";
25
+ constructor(code: "drain_timeout", message: string);
26
+ }
27
+ export declare class HostProjectRuntimeLifecycle {
28
+ private readonly options;
29
+ private closePromise;
30
+ constructor(options: {
31
+ controlPlane: ControlPlaneLifecycleOwner;
32
+ chatAssistant: ChatAssistantLifecycleOwner;
33
+ invocationCoordinator: InvocationLifecycleOwner;
34
+ checkProcessCoordinator: CheckLifecycleOwner;
35
+ approvalManager: ApprovalLifecycleOwner;
36
+ gracefulDrainMs?: number;
37
+ forcedDrainMs?: number;
38
+ });
39
+ close(): Promise<void>;
40
+ private closeOnce;
41
+ }
42
+ export {};
43
+ //# sourceMappingURL=host-project-runtime-lifecycle.d.ts.map
@@ -0,0 +1,54 @@
1
+ export class HostProjectRuntimeShutdownError extends Error {
2
+ code;
3
+ constructor(code, message) {
4
+ super(message);
5
+ this.code = code;
6
+ this.name = "HostProjectRuntimeShutdownError";
7
+ }
8
+ }
9
+ const DEFAULT_GRACEFUL_DRAIN_MS = 1_000;
10
+ const DEFAULT_FORCED_DRAIN_MS = 10_000;
11
+ function wait(delayMs) {
12
+ return new Promise((resolve) => setTimeout(resolve, delayMs));
13
+ }
14
+ async function settlesWithin(promise, timeoutMs) {
15
+ return await Promise.race([
16
+ promise.then(() => true),
17
+ wait(timeoutMs).then(() => false),
18
+ ]);
19
+ }
20
+ export class HostProjectRuntimeLifecycle {
21
+ options;
22
+ closePromise;
23
+ constructor(options) {
24
+ this.options = options;
25
+ }
26
+ close() {
27
+ this.closePromise ??= this.closeOnce();
28
+ return this.closePromise;
29
+ }
30
+ async closeOnce() {
31
+ this.options.controlPlane.beginShutdown();
32
+ this.options.chatAssistant.stopAccepting();
33
+ this.options.invocationCoordinator.stopAccepting();
34
+ this.options.checkProcessCoordinator.stopAccepting();
35
+ this.options.approvalManager.clearPending();
36
+ const waitForOwners = () => Promise.all([
37
+ this.options.chatAssistant.waitForIdle(),
38
+ this.options.controlPlane.waitForIdle(),
39
+ this.options.invocationCoordinator.waitForIdle(),
40
+ this.options.checkProcessCoordinator.waitForIdle(),
41
+ ]).then(() => undefined);
42
+ if (!(await settlesWithin(waitForOwners(), this.options.gracefulDrainMs ?? DEFAULT_GRACEFUL_DRAIN_MS))) {
43
+ await Promise.all([
44
+ this.options.invocationCoordinator.closeActive(),
45
+ this.options.checkProcessCoordinator.terminateActive(),
46
+ ]);
47
+ if (!(await settlesWithin(waitForOwners(), this.options.forcedDrainMs ?? DEFAULT_FORCED_DRAIN_MS))) {
48
+ throw new HostProjectRuntimeShutdownError("drain_timeout", "Host project runtime did not settle within the bounded shutdown window.");
49
+ }
50
+ }
51
+ this.options.approvalManager.dispose();
52
+ }
53
+ }
54
+ //# sourceMappingURL=host-project-runtime-lifecycle.js.map
@@ -4,9 +4,12 @@ import { completeRelayUpload, resolveRelayUpload } from "@tutti/relay-client";
4
4
  import { ApprovalRequestManager } from "../../approvals/index.js";
5
5
  import { ArtifactPreviewManager } from "../../artifacts/index.js";
6
6
  import { ReadOnlyChatAssistant } from "../../chat-assistant/index.js";
7
+ import { CheckProcessCoordinator } from "../../checks/index.js";
7
8
  import { ensureInitialProjectWelcomeMessage } from "../../collaboration-state/index.js";
8
9
  import { Phase5ControlPlane } from "../../control-plane/index.js";
9
10
  import { resolveProjectOpenAiProcedureWorkflowRunner } from "../../control-plane/workflows/index.js";
11
+ import { cleanupStaleCodexAppServerTempRoots } from "../../providers/openai/app-server/stale-temp-cleanup.js";
12
+ import { CodexAppServerInvocationCoordinator } from "../../providers/openai/app-server/invocation-coordinator.js";
10
13
  import { resolveProjectOpenAiReadOnlyChatAssistantModel } from "../../providers/openai/chat-assistant.js";
11
14
  import { configureProjectOpenAiProvider, readOpenAiProviderConfigProjection, } from "../../providers/openai/index.js";
12
15
  import { readProviderUsageProjection, recordProviderUsageEvent, } from "../../provider-usage/index.js";
@@ -18,6 +21,7 @@ import { readHostRepoProjection } from "../http/routes/project-api.js";
18
21
  import { WorkspaceEventBus } from "../http/workspace-events.js";
19
22
  import { createTrustedRelaySessionMetadataStore, } from "../session/relay-session-context.js";
20
23
  import { LaunchError } from "./errors.js";
24
+ import { HostProjectRuntimeLifecycle } from "./host-project-runtime-lifecycle.js";
21
25
  import { readCliVersion } from "./version.js";
22
26
  import { relayStateFromLaunchStatus } from "./host-relay-status.js";
23
27
  import { appendHostLogLine, readHostRegistrationSecret, createMachineRuntimeEndpointToken, deleteMachineRuntimeEndpoint, ensureHostLogFile, getHostLogFilePath, updateMachineProjectDisplayNameSnapshot, writeMachineRuntimeEndpoint, } from "./machine-local.js";
@@ -47,6 +51,15 @@ function createHostRuntimeLogger(options) {
47
51
  },
48
52
  };
49
53
  }
54
+ function createLifecycleIncidentReporter(logger, consumer) {
55
+ return (incident) => {
56
+ logger.info({
57
+ consumer,
58
+ incident_code: incident.code,
59
+ error_code: incident.error_code,
60
+ }, "Codex app-server lifecycle incident");
61
+ };
62
+ }
50
63
  function createAgentContextRuntime(options) {
51
64
  return {
52
65
  baseUrl: new URL(AGENT_CONTEXT_BASE_PATH, options.baseUrl).toString(),
@@ -214,10 +227,25 @@ export async function startForegroundHostServer(options) {
214
227
  logFilePath,
215
228
  now,
216
229
  });
230
+ const procedureLifecycleIncident = createLifecycleIncidentReporter(controlPlaneLogger, "procedure");
231
+ const runLifecycleIncident = createLifecycleIncidentReporter(runPipelineLogger, "run");
232
+ const chatLifecycleIncident = createLifecycleIncidentReporter(controlPlaneLogger, "chat_assistant");
233
+ const staleTempCleanup = await cleanupStaleCodexAppServerTempRoots({
234
+ onIncident: (incident) => {
235
+ controlPlaneLogger.info({
236
+ consumer: "maintenance",
237
+ incident_code: incident.code,
238
+ error_code: incident.error_code,
239
+ }, "Codex app-server lifecycle incident");
240
+ },
241
+ });
242
+ controlPlaneLogger.info(staleTempCleanup, "Codex app-server stale temp cleanup completed");
217
243
  const approvalManager = new ApprovalRequestManager({
218
244
  events: workspaceEvents,
219
245
  now,
220
246
  });
247
+ const codexInvocationCoordinator = new CodexAppServerInvocationCoordinator();
248
+ const checkProcessCoordinator = new CheckProcessCoordinator();
221
249
  const artifactPreviewManager = new ArtifactPreviewManager({
222
250
  onStatusChange: () => {
223
251
  workspaceEvents.publish({
@@ -254,6 +282,9 @@ export async function startForegroundHostServer(options) {
254
282
  approvalManager,
255
283
  logger: runPipelineLogger,
256
284
  recordUsage,
285
+ invocationCoordinator: codexInvocationCoordinator,
286
+ checkProcessCoordinator,
287
+ onLifecycleIncident: runLifecycleIncident,
257
288
  }),
258
289
  resolveWorkflowRunner: () => resolveProjectOpenAiProcedureWorkflowRunner({
259
290
  tuttiHome: options.preparation.tutti_home,
@@ -261,6 +292,8 @@ export async function startForegroundHostServer(options) {
261
292
  workspaceRoot: options.preparation.workspace_root,
262
293
  agentContext: agentContextRuntime,
263
294
  recordUsage,
295
+ invocationCoordinator: codexInvocationCoordinator,
296
+ onLifecycleIncident: procedureLifecycleIncident,
264
297
  }),
265
298
  });
266
299
  const chatAssistant = new ReadOnlyChatAssistant({
@@ -276,6 +309,8 @@ export async function startForegroundHostServer(options) {
276
309
  workspaceRoot: options.preparation.workspace_root,
277
310
  agentContext: agentContextRuntime,
278
311
  recordUsage,
312
+ invocationCoordinator: codexInvocationCoordinator,
313
+ onLifecycleIncident: chatLifecycleIncident,
279
314
  });
280
315
  if (resolution.kind !== "ready") {
281
316
  throw new Error("Provider is not configured.");
@@ -283,9 +318,17 @@ export async function startForegroundHostServer(options) {
283
318
  return await resolution.model.answer(input);
284
319
  },
285
320
  },
321
+ waitForInspectionAvailability: () => controlPlane.waitForRepoInspectionIdle(),
286
322
  onScratchpadSourceChanged: (message) => controlPlane.notifyScratchpadSourceChanged(message),
287
323
  now,
288
324
  });
325
+ const projectRuntimeLifecycle = new HostProjectRuntimeLifecycle({
326
+ controlPlane,
327
+ chatAssistant,
328
+ invocationCoordinator: codexInvocationCoordinator,
329
+ checkProcessCoordinator,
330
+ approvalManager,
331
+ });
289
332
  let close = async () => { };
290
333
  let relayConnectionManager;
291
334
  const packageUpdateReadiness = () => packageUpdateReadinessFromExecutionStatus(controlPlane.getExecutionStatus());
@@ -372,6 +415,9 @@ export async function startForegroundHostServer(options) {
372
415
  },
373
416
  },
374
417
  logger: { file: logFilePath },
418
+ lifecycle: {
419
+ close: () => projectRuntimeLifecycle.close(),
420
+ },
375
421
  localControl,
376
422
  now,
377
423
  projectApi: {
@@ -456,30 +502,47 @@ export async function startForegroundHostServer(options) {
456
502
  recoverInProgressCommands: true,
457
503
  });
458
504
  let resolveClosed;
459
- const closed = new Promise((resolve) => {
505
+ let rejectClosed;
506
+ const closed = new Promise((resolve, reject) => {
460
507
  resolveClosed = resolve;
508
+ rejectClosed = reject;
461
509
  });
462
510
  let closePromise;
463
511
  close = () => {
464
512
  closePromise ??= (async () => {
513
+ let closeError;
514
+ let appClosed = false;
465
515
  try {
466
- await relayConnectionManager?.stop();
467
- await app.close();
468
- }
469
- finally {
470
516
  try {
471
- approvalManager.dispose();
472
- store.close();
517
+ await relayConnectionManager?.stop();
518
+ }
519
+ catch (error) {
520
+ closeError = error;
473
521
  }
474
- finally {
475
- deleteMachineRuntimeEndpoint({
476
- tuttiHome: options.preparation.tutti_home,
477
- projectId: options.preparation.project_id,
478
- expectedToken: token,
479
- });
480
- resolveClosed();
522
+ try {
523
+ await app.close();
524
+ appClosed = true;
525
+ }
526
+ catch (error) {
527
+ closeError ??= error;
481
528
  }
529
+ if (appClosed) {
530
+ store.close();
531
+ }
532
+ }
533
+ finally {
534
+ deleteMachineRuntimeEndpoint({
535
+ tuttiHome: options.preparation.tutti_home,
536
+ projectId: options.preparation.project_id,
537
+ expectedToken: token,
538
+ });
539
+ }
540
+ if (closeError !== undefined) {
541
+ const normalizedCloseError = closeError instanceof Error ? closeError : new Error("Host server close failed.");
542
+ rejectClosed(normalizedCloseError);
543
+ throw normalizedCloseError;
482
544
  }
545
+ resolveClosed();
483
546
  })();
484
547
  return closePromise;
485
548
  };
@@ -16,6 +16,9 @@ export type HostServerOptions = {
16
16
  logger?: false | {
17
17
  file?: string;
18
18
  };
19
+ lifecycle?: {
20
+ close: () => void | Promise<void>;
21
+ };
19
22
  localControl?: HostLocalControlOptions;
20
23
  now?: () => Date;
21
24
  projectApi?: false | HostProjectApiRouteOptions;
@@ -24,10 +24,11 @@ export function createHostServer(options = {}) {
24
24
  if (options.projectApi !== undefined && options.projectApi !== false) {
25
25
  const projectApiOptions = options.projectApi;
26
26
  registerHostProjectApiRoutes(app, projectApiOptions);
27
- app.addHook("onClose", (_instance, done) => {
28
- projectApiOptions.controlPlane?.dispose();
29
- done();
30
- });
27
+ if (options.lifecycle === undefined) {
28
+ app.addHook("onClose", async () => {
29
+ await projectApiOptions.controlPlane?.dispose();
30
+ });
31
+ }
31
32
  if (options.agentContext !== false) {
32
33
  registerAgentContextRoutes(app, {
33
34
  project: projectApiOptions.project,
@@ -46,6 +47,11 @@ export function createHostServer(options = {}) {
46
47
  });
47
48
  }
48
49
  }
50
+ if (options.lifecycle !== undefined) {
51
+ app.addHook("onClose", async () => {
52
+ await options.lifecycle?.close();
53
+ });
54
+ }
49
55
  if (options.webStatic !== undefined && options.webStatic !== false) {
50
56
  registerHostWebStaticRoutes(app, options.webStatic);
51
57
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xfey/tutti",
3
- "version": "0.1.78",
3
+ "version": "0.1.79",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -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-d-q9yZ09.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)}}if(e>=33.45&&e<35.95){let t={x:87.27,y:66.35},n={x:88.95,y:66.35},r={x:90.64,y:66.35},i;return i=e<34.15?V(e,33.45,34.15,{x:4,y:22.85},t):e<34.78?V(e,34.5,34.78,t,n):e<35.45?V(e,35.15,35.45,n,r):r,{visible:!0,x:i.x,y:i.y,clickPulse:Math.max(H(e,34.15,34.4),H(e,34.78,35.03),H(e,35.45,35.7))}}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-C4cdSaSb.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)}}if(e>=33.45&&e<35.95){let t={x:87.27,y:66.35},n={x:88.95,y:66.35},r={x:90.64,y:66.35},i;return i=e<34.15?V(e,33.45,34.15,{x:4,y:22.85},t):e<34.78?V(e,34.5,34.78,t,n):e<35.45?V(e,35.15,35.45,n,r):r,{visible:!0,x:i.x,y:i.y,clickPulse:Math.max(H(e,34.15,34.4),H(e,34.78,35.03),H(e,35.45,35.7))}}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};