@xfey/tutti 0.1.125 → 0.1.127

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 (52) hide show
  1. package/README.md +2 -0
  2. package/dist/chat-assistant/diagnostics.d.ts +4 -1
  3. package/dist/chat-assistant/index.js +15 -6
  4. package/dist/chat-assistant/language-context.d.ts +8 -0
  5. package/dist/chat-assistant/language-context.js +41 -0
  6. package/dist/collaboration-state/communication-language.d.ts +31 -0
  7. package/dist/collaboration-state/communication-language.js +36 -0
  8. package/dist/collaboration-state/task-compile-context.js +9 -0
  9. package/dist/communication-language/index.d.ts +75 -0
  10. package/dist/communication-language/index.js +122 -0
  11. package/dist/communication-language/live-smoke-cli.d.ts +2 -0
  12. package/dist/communication-language/live-smoke-cli.js +368 -0
  13. package/dist/communication-language/prompt.d.ts +22 -0
  14. package/dist/communication-language/prompt.js +73 -0
  15. package/dist/control-plane/communication-language.d.ts +33 -0
  16. package/dist/control-plane/communication-language.js +72 -0
  17. package/dist/control-plane/context-sync.js +7 -0
  18. package/dist/control-plane/follow-up-output.js +25 -4
  19. package/dist/control-plane/follow-up-start.js +17 -0
  20. package/dist/control-plane/project-brief-refresh.js +10 -1
  21. package/dist/control-plane/project-context-bootstrap.d.ts +2 -0
  22. package/dist/control-plane/project-context-bootstrap.js +8 -0
  23. package/dist/control-plane/reference-summary-refresh.js +7 -0
  24. package/dist/control-plane/run-start.js +14 -6
  25. package/dist/control-plane/scratchpad-refresh-start.d.ts +1 -0
  26. package/dist/control-plane/scratchpad-refresh-start.js +13 -0
  27. package/dist/control-plane/scratchpad-refresh.d.ts +2 -0
  28. package/dist/control-plane/scratchpad-refresh.js +40 -26
  29. package/dist/control-plane/task-compile-continuation.js +14 -1
  30. package/dist/control-plane/task-compile-output.js +38 -14
  31. package/dist/control-plane/task-compile-start.d.ts +1 -0
  32. package/dist/control-plane/task-compile-start.js +22 -1
  33. package/dist/control-plane/types.d.ts +2 -0
  34. package/dist/control-plane/workflows/openai.js +48 -28
  35. package/dist/control-plane/workflows/types.d.ts +18 -13
  36. package/dist/prompt-templates/index.d.ts +1 -1
  37. package/dist/prompt-templates/index.js +2 -0
  38. package/dist/run-pipeline/task-run-invocation.js +12 -9
  39. package/migrations/0023_communication_language.sql +14 -0
  40. package/package.json +1 -1
  41. package/prompts/README.md +1 -0
  42. package/prompts/chat-assistant.md +1 -1
  43. package/prompts/communication-language-result.md +27 -0
  44. package/prompts/communication-language.md +50 -0
  45. package/prompts/procedures/follow-up-check.md +1 -1
  46. package/prompts/procedures/scratchpad-refresh.md +2 -2
  47. package/prompts/procedures/task-compile.md +1 -1
  48. package/prompts/prompt-flow-map.md +2 -0
  49. package/web/assets/{homepage-motion-scene-Ctt45F9o.js → homepage-motion-scene-DyEmWwvt.js} +1 -1
  50. package/web/assets/{index-BJpTcX_E.js → index-By3idT4U.js} +2 -2
  51. package/web/assets/{index-DTuz8Hb5.css → index-DSBjOZmJ.css} +1 -1
  52. package/web/index.html +2 -2
@@ -3,7 +3,7 @@ import { rm } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
4
  import { extname, isAbsolute, join, relative, resolve } from "node:path";
5
5
  import { clarificationAnswerRole } from "@tutti/shared/utils";
6
- import { renderPromptTemplate } from "../../prompt-templates/index.js";
6
+ import { renderLanguageAwarePrompt, languageOutputContract, } from "../../communication-language/prompt.js";
7
7
  import { createOpenAiStructuredOutputClient, runOpenAiStructuredOutput, } from "../../providers/openai/sdk-procedure-runner.js";
8
8
  import { readOpenAiApiKeyCredential } from "../../providers/openai/credential-store.js";
9
9
  import { resolveOpenAiProviderConfig } from "../../providers/openai/provider-config.js";
@@ -283,13 +283,15 @@ function recordUsage(options, source, result, anchors = {}) {
283
283
  export function createOpenAiProcedureWorkflowRunner(options) {
284
284
  const runner = {
285
285
  async runScratchpadRefresh(input, context) {
286
- const instructions = renderPromptTemplate({
286
+ const { communication_language: language, ...businessInput } = input;
287
+ const contract = languageOutputContract(SCRATCHPAD_REFRESH_OUTPUT_SCHEMA, isScratchpadRefreshStructuredOutput, language);
288
+ const instructions = renderLanguageAwarePrompt({
287
289
  templateKey: "procedures.scratchpad_refresh",
288
290
  ...(options.promptsRoot === undefined ? {} : { promptsRoot: options.promptsRoot }),
289
291
  variables: {
290
- workflow_input_json: input,
292
+ workflow_input_json: businessInput,
291
293
  },
292
- });
294
+ }, language, true);
293
295
  const result = await runOpenAiStructuredOutput({
294
296
  client: options.client,
295
297
  model: options.config.default_model,
@@ -297,23 +299,28 @@ export function createOpenAiProcedureWorkflowRunner(options) {
297
299
  input: "Run scratchpad_refresh and return only structured JSON.",
298
300
  schemaName: "scratchpad_refresh_output",
299
301
  schemaDescription: "Tutti scratchpad_refresh terminal workflow output.",
300
- schema: SCRATCHPAD_REFRESH_OUTPUT_SCHEMA,
301
- validate: isScratchpadRefreshStructuredOutput,
302
+ schema: contract.schema,
303
+ validate: contract.validate,
302
304
  });
303
305
  recordUsage(options, "scratchpad_refresh", result, {
304
306
  workflow_ref: context.workflow_ref,
305
307
  activity_ref: context.activity_ref,
306
308
  });
307
- return result.output.result;
309
+ const decoded = contract.unwrap(result.output);
310
+ return {
311
+ ...decoded.output.result,
312
+ ...(decoded.language === undefined ? {} : { communication_language: decoded.language }),
313
+ };
308
314
  },
309
315
  async runProjectBriefRefresh(input, context) {
310
- const instructions = renderPromptTemplate({
316
+ const { communication_language: language, ...businessInput } = input;
317
+ const instructions = renderLanguageAwarePrompt({
311
318
  templateKey: "procedures.project_brief_refresh",
312
319
  ...(options.promptsRoot === undefined ? {} : { promptsRoot: options.promptsRoot }),
313
320
  variables: {
314
- workflow_input_json: input,
321
+ workflow_input_json: businessInput,
315
322
  },
316
- });
323
+ }, language);
317
324
  const result = await runOpenAiStructuredOutput({
318
325
  client: options.client,
319
326
  model: options.config.default_model,
@@ -331,22 +338,24 @@ export function createOpenAiProcedureWorkflowRunner(options) {
331
338
  return result.output;
332
339
  },
333
340
  async runTaskCompile(input, context) {
334
- const instructions = renderPromptTemplate({
341
+ const language = input.communication_language;
342
+ const contract = languageOutputContract(TASK_COMPILE_OUTPUT_SCHEMA, isTaskCompileStructuredOutput, language);
343
+ const instructions = renderLanguageAwarePrompt({
335
344
  templateKey: "procedures.task_compile",
336
345
  ...(options.promptsRoot === undefined ? {} : { promptsRoot: options.promptsRoot }),
337
346
  variables: {
338
347
  workflow_input_json: buildTaskCompilePromptInput(input),
339
348
  },
340
- });
349
+ }, language, true);
341
350
  const result = await runDurableReadOnlyProcedure({
342
351
  options,
343
352
  context,
344
353
  kind: "task_compile",
345
- archiveAfterTurn: (output) => "proposal" in output.result,
354
+ archiveAfterTurn: (output) => "proposal" in contract.unwrap(output).output.result,
346
355
  invocation: {
347
- ...readOnlyAppServerOptions(options, instructions, TASK_COMPILE_OUTPUT_SCHEMA, "task_compile"),
356
+ ...readOnlyAppServerOptions(options, instructions, contract.schema, "task_compile"),
348
357
  ...readOnlyRuntimeTelemetry(context, "task_compile"),
349
- validateOutput: isTaskCompileStructuredOutput,
358
+ validateOutput: contract.validate,
350
359
  networkAccess: false,
351
360
  webSearchMode: "disabled",
352
361
  },
@@ -355,16 +364,22 @@ export function createOpenAiProcedureWorkflowRunner(options) {
355
364
  workflow_ref: context?.workflow_ref ?? input.workflow_ref,
356
365
  ...(context?.activity_ref === undefined ? {} : { activity_ref: context.activity_ref }),
357
366
  });
358
- return taskCompileWorkflowOutput(result.output);
367
+ const decoded = contract.unwrap(result.output);
368
+ return {
369
+ ...taskCompileWorkflowOutput(decoded.output),
370
+ ...(decoded.language === undefined ? {} : { communication_language: decoded.language }),
371
+ };
359
372
  },
360
373
  async runFollowUpCheck(input, context) {
361
- const instructions = renderPromptTemplate({
374
+ const language = input.communication_language;
375
+ const contract = languageOutputContract(FOLLOW_UP_CHECK_OUTPUT_SCHEMA, isFollowUpCheckStructuredOutput, language);
376
+ const instructions = renderLanguageAwarePrompt({
362
377
  templateKey: "procedures.follow_up_check",
363
378
  ...(options.promptsRoot === undefined ? {} : { promptsRoot: options.promptsRoot }),
364
379
  variables: {
365
380
  workflow_input_json: followUpCheckPromptInput(input),
366
381
  },
367
- });
382
+ }, language, true);
368
383
  const result = await runOpenAiStructuredOutput({
369
384
  client: options.client,
370
385
  model: options.config.default_model,
@@ -372,8 +387,8 @@ export function createOpenAiProcedureWorkflowRunner(options) {
372
387
  input: "Run follow_up_check and return only structured JSON.",
373
388
  schemaName: "follow_up_check_output",
374
389
  schemaDescription: "Tutti follow_up_check terminal workflow output.",
375
- schema: FOLLOW_UP_CHECK_OUTPUT_SCHEMA,
376
- validate: isFollowUpCheckStructuredOutput,
390
+ schema: contract.schema,
391
+ validate: contract.validate,
377
392
  });
378
393
  recordUsage(options, "follow_up_check", result, {
379
394
  workflow_ref: context?.workflow_ref ?? input.workflow_ref,
@@ -381,18 +396,23 @@ export function createOpenAiProcedureWorkflowRunner(options) {
381
396
  task_id: input.task_id,
382
397
  run_lineage_kind: input.run_lineage.kind,
383
398
  });
384
- return followUpCheckWorkflowOutput(result.output);
399
+ const decoded = contract.unwrap(result.output);
400
+ return {
401
+ ...followUpCheckWorkflowOutput(decoded.output),
402
+ ...(decoded.language === undefined ? {} : { communication_language: decoded.language }),
403
+ };
385
404
  },
386
405
  };
387
406
  if (options.readOnlyAppServer !== undefined) {
388
407
  runner.runProjectContextBootstrap = async (input, context) => {
389
- const instructions = renderPromptTemplate({
408
+ const { communication_language: language, ...businessInput } = input;
409
+ const instructions = renderLanguageAwarePrompt({
390
410
  templateKey: "procedures.project_context_bootstrap",
391
411
  ...(options.promptsRoot === undefined ? {} : { promptsRoot: options.promptsRoot }),
392
412
  variables: {
393
- workflow_input_json: input,
413
+ workflow_input_json: businessInput,
394
414
  },
395
- });
415
+ }, language);
396
416
  const result = await runDurableReadOnlyProcedure({
397
417
  options,
398
418
  context,
@@ -411,10 +431,10 @@ export function createOpenAiProcedureWorkflowRunner(options) {
411
431
  return result.output;
412
432
  };
413
433
  runner.runContextSync = async (input, context) => {
414
- const instructions = renderPromptTemplate({
434
+ const instructions = renderLanguageAwarePrompt({
415
435
  templateKey: "procedures.context_sync",
416
436
  ...(options.promptsRoot === undefined ? {} : { promptsRoot: options.promptsRoot }),
417
- });
437
+ }, input.communication_language);
418
438
  const result = await runDurableReadOnlyProcedure({
419
439
  options,
420
440
  context,
@@ -434,10 +454,10 @@ export function createOpenAiProcedureWorkflowRunner(options) {
434
454
  };
435
455
  runner.runReferenceFileSummary = async (input, context) => {
436
456
  const sourcePath = referenceSummarySourcePath(options, input);
437
- const instructions = renderPromptTemplate({
457
+ const instructions = renderLanguageAwarePrompt({
438
458
  templateKey: "procedures.reference_file_summary",
439
459
  ...(options.promptsRoot === undefined ? {} : { promptsRoot: options.promptsRoot }),
440
- });
460
+ }, input.communication_language);
441
461
  const workspace = createReferenceSummaryWorkspace({
442
462
  sourcePath,
443
463
  originalPath: input.file.path,
@@ -1,12 +1,16 @@
1
1
  import type { ExecutionRuntimeSnapshot, MessageProjection, RunResultProjection, TaskDetailProjection, WorklistProjection } from "@tutti/shared/schemas/api";
2
2
  import type { RecentReferenceContext } from "../reference-context-types.js";
3
+ import type { CommunicationLanguageContext, LanguageAwareOutput } from "../../communication-language/index.js";
4
+ type LanguageAwareInput = {
5
+ communication_language?: CommunicationLanguageContext;
6
+ };
3
7
  import type { PromptSourceMessage, TaskCompileClarificationContext } from "../task-source-context-types.js";
4
8
  import type { ClarificationRequestPayload, TaskContractProjection } from "@tutti/shared/schemas/api";
5
9
  import type { ProjectDocKey } from "@tutti/shared/schemas/api";
6
10
  import type { ActivityRef, ClarificationRoundRef, ClarificationThreadRef, MessageId, TaskId, TaskModuleId, WorkflowInvocationRef } from "@tutti/shared/ids";
7
11
  import type { RunLineage } from "@tutti/shared/domain";
8
12
  import type { ProjectBriefSnapshot, ProjectBriefSourceDocument } from "../../project-brief/index.js";
9
- export type ScratchpadRefreshWorkflowInput = {
13
+ export type ScratchpadRefreshWorkflowInput = LanguageAwareInput & {
10
14
  project_brief?: ProjectBriefSnapshot;
11
15
  worklist: ScratchpadRefreshWorklistInput;
12
16
  active_task_compile?: ScratchpadWorkflowSnapshot;
@@ -43,7 +47,7 @@ export type ProcedureWorkflowRunContext = {
43
47
  runtime?: ExecutionRuntimeSnapshot;
44
48
  }) => void;
45
49
  };
46
- export type ScratchpadRefreshWorkflowOutput = {
50
+ export type ScratchpadRefreshWorkflowOutput = LanguageAwareOutput & ({
47
51
  decision: "ready";
48
52
  scratchpad_snapshot: {
49
53
  topic: string;
@@ -58,7 +62,7 @@ export type ScratchpadRefreshWorkflowOutput = {
58
62
  summary: string;
59
63
  request: string;
60
64
  };
61
- };
65
+ });
62
66
  export type TaskCompileProposal = {
63
67
  modules: TaskCompileProposalModule[];
64
68
  tasks: TaskCompileProposalTask[];
@@ -74,7 +78,7 @@ export type TaskCompileProposalTask = {
74
78
  summary: string;
75
79
  contract: TaskContractProjection;
76
80
  };
77
- export type TaskCompileWorkflowInput = {
81
+ export type TaskCompileWorkflowInput = LanguageAwareInput & {
78
82
  workflow_ref: WorkflowInvocationRef;
79
83
  scratchpad: ScratchpadWorkflowSnapshot;
80
84
  source_messages: PromptSourceMessage[];
@@ -82,13 +86,13 @@ export type TaskCompileWorkflowInput = {
82
86
  recent_references?: RecentReferenceContext[];
83
87
  clarification_rounds?: TaskCompileClarificationContext[];
84
88
  };
85
- export type TaskCompileWorkflowOutput = {
89
+ export type TaskCompileWorkflowOutput = LanguageAwareOutput & ({
86
90
  decision: "ready";
87
91
  proposal: TaskCompileProposal;
88
92
  } | {
89
93
  decision: "needs_human";
90
94
  clarification_request_payload: ClarificationRequestPayload;
91
- };
95
+ });
92
96
  export type TaskCompilePromptInput = {
93
97
  scratchpad: {
94
98
  topic: string;
@@ -145,7 +149,7 @@ export type TaskCompileStructuredProposalTask = {
145
149
  goal: string;
146
150
  scope: string[];
147
151
  };
148
- export type ProjectContextBootstrapWorkflowInput = {
152
+ export type ProjectContextBootstrapWorkflowInput = LanguageAwareInput & {
149
153
  project_name: string;
150
154
  missing_docs?: ProjectContextBootstrapMissingDoc[];
151
155
  scratchpad?: {
@@ -178,7 +182,7 @@ export type ContextSyncWorkflowTrigger = {
178
182
  completed_task_count: number;
179
183
  module_count: number;
180
184
  };
181
- export type ContextSyncWorkflowInput = {
185
+ export type ContextSyncWorkflowInput = LanguageAwareInput & {
182
186
  workflow_ref: WorkflowInvocationRef;
183
187
  activity_ref: ActivityRef;
184
188
  trigger: ContextSyncWorkflowTrigger;
@@ -187,11 +191,11 @@ export type ContextSyncWorkflowOutput = {
187
191
  summary: string;
188
192
  updates: ContextSyncDocumentUpdate[];
189
193
  };
190
- export type ProjectBriefRefreshWorkflowInput = {
194
+ export type ProjectBriefRefreshWorkflowInput = LanguageAwareInput & {
191
195
  docs: ProjectBriefSourceDocument[];
192
196
  };
193
197
  export type ProjectBriefRefreshWorkflowOutput = ProjectBriefSnapshot;
194
- export type ReferenceFileSummaryWorkflowInput = {
198
+ export type ReferenceFileSummaryWorkflowInput = LanguageAwareInput & {
195
199
  workflow_ref: WorkflowInvocationRef;
196
200
  activity_ref: ActivityRef;
197
201
  file: {
@@ -225,7 +229,7 @@ export type ReferenceFileSummaryStructuredOutput = {
225
229
  };
226
230
  };
227
231
  };
228
- export type FollowUpCheckWorkflowInput = {
232
+ export type FollowUpCheckWorkflowInput = LanguageAwareInput & {
229
233
  workflow_ref: WorkflowInvocationRef;
230
234
  task_id: TaskId;
231
235
  task_detail: TaskDetailProjection;
@@ -237,7 +241,7 @@ export type FollowUpCheckWorkflowInput = {
237
241
  latest_run_result: RunResultProjection;
238
242
  run_lineage: RunLineage;
239
243
  };
240
- export type FollowUpCheckWorkflowOutput = {
244
+ export type FollowUpCheckWorkflowOutput = LanguageAwareOutput & ({
241
245
  decision: "resume";
242
246
  summary: string;
243
247
  } | {
@@ -247,7 +251,7 @@ export type FollowUpCheckWorkflowOutput = {
247
251
  } | {
248
252
  decision: "requires_retasking";
249
253
  summary: string;
250
- };
254
+ });
251
255
  export type FollowUpCheckStructuredOutput = {
252
256
  result: {
253
257
  resume: {
@@ -281,4 +285,5 @@ export type ProcedureWorkflowRunnerResolution = {
281
285
  retryable: boolean;
282
286
  };
283
287
  export type ProcedureWorkflowRunnerResolver = () => ProcedureWorkflowRunnerResolution;
288
+ export {};
284
289
  //# sourceMappingURL=types.d.ts.map
@@ -1,4 +1,4 @@
1
- export type PromptTemplateKey = "procedures.project_context_bootstrap" | "procedures.context_sync" | "procedures.project_brief_refresh" | "procedures.scratchpad_refresh" | "procedures.task_compile" | "procedures.reference_file_summary" | "procedures.follow_up_check" | "runs.task_execute" | "runs.task_finalize" | "runs.task_recovery" | "runs.task_result_contract" | "artifacts.applicability" | "chat.assistant" | "codex.no_write_smoke";
1
+ export type PromptTemplateKey = "communication_language" | "communication_language_result" | "procedures.project_context_bootstrap" | "procedures.context_sync" | "procedures.project_brief_refresh" | "procedures.scratchpad_refresh" | "procedures.task_compile" | "procedures.reference_file_summary" | "procedures.follow_up_check" | "runs.task_execute" | "runs.task_finalize" | "runs.task_recovery" | "runs.task_result_contract" | "artifacts.applicability" | "chat.assistant" | "codex.no_write_smoke";
2
2
  export type RenderPromptTemplateOptions = {
3
3
  templateKey: PromptTemplateKey;
4
4
  variables?: Record<string, unknown>;
@@ -12,6 +12,8 @@ export class PromptTemplateError extends Error {
12
12
  }
13
13
  const defaultPromptsRoot = fileURLToPath(new URL("../../prompts", import.meta.url));
14
14
  const TEMPLATE_REGISTRY = {
15
+ communication_language: "communication-language.md",
16
+ communication_language_result: "communication-language-result.md",
15
17
  "procedures.project_context_bootstrap": "procedures/project-context-bootstrap.md",
16
18
  "procedures.context_sync": "procedures/context-sync.md",
17
19
  "procedures.project_brief_refresh": "procedures/project-brief-refresh.md",
@@ -1,5 +1,6 @@
1
1
  import { openAiCredentialAccess } from "../providers/openai/execution-access.js";
2
2
  import { renderPromptTemplate } from "../prompt-templates/index.js";
3
+ import { renderLanguageAwarePrompt } from "../communication-language/prompt.js";
3
4
  import { CODEX_APP_SERVER_VERSION } from "../providers/openai/codex-app-server.js";
4
5
  import { CodexAppServerWorkspaceWriteRunError, runCodexAppServerWorkspaceWriteRun, } from "../providers/openai/app-server/workspace-write-run.js";
5
6
  import { acquireCodexTaskSessionLock, createProviderFingerprint, markCodexTaskSessionArchived, openCodexTaskSession, readCodexSessionRecoveryEvidence, recordCodexSessionArchiveFailure, recordCodexTaskSessionThread, writeCodexSessionRecoveryEvidence, } from "../providers/openai/app-server/session-store.js";
@@ -55,24 +56,26 @@ function createExecutePrompt(options, input, session) {
55
56
  else {
56
57
  executionInput = taskContractRunInput(input);
57
58
  }
58
- return renderPromptTemplate({
59
+ return renderLanguageAwarePrompt({
59
60
  templateKey: "runs.task_execute",
60
61
  ...(options.promptsRoot === undefined ? {} : { promptsRoot: options.promptsRoot }),
61
62
  variables: { execution_input_json: executionInput },
62
- });
63
+ }, input.communication_language);
63
64
  }
64
- function createFinalizePrompt(options) {
65
- return renderPromptTemplate({
65
+ function createFinalizePrompt(options, input) {
66
+ return renderLanguageAwarePrompt({
66
67
  templateKey: "runs.task_finalize",
67
68
  ...(options.promptsRoot === undefined ? {} : { promptsRoot: options.promptsRoot }),
68
69
  variables: { result_contract: renderResultContract(options) },
69
- });
70
+ }, input.communication_language);
70
71
  }
71
72
  function createRecoveryPrompt(input) {
72
73
  const needsCanonicalContext = input.session === undefined || input.session.metadata.thread_id === undefined;
73
- return renderPromptTemplate({
74
+ return renderLanguageAwarePrompt({
74
75
  templateKey: "runs.task_recovery",
75
- ...(input.options.promptsRoot === undefined ? {} : { promptsRoot: input.options.promptsRoot }),
76
+ ...(input.options.promptsRoot === undefined
77
+ ? {}
78
+ : { promptsRoot: input.options.promptsRoot }),
76
79
  variables: {
77
80
  recovery_input_json: {
78
81
  ...(needsCanonicalContext
@@ -82,7 +85,7 @@ function createRecoveryPrompt(input) {
82
85
  },
83
86
  result_contract: renderResultContract(input.options),
84
87
  },
85
- });
88
+ }, input.pipelineInput.communication_language);
86
89
  }
87
90
  function usageSource(input, phase) {
88
91
  if (phase === "finalize") {
@@ -372,7 +375,7 @@ export async function runWorkspaceWriteTask(options, input) {
372
375
  structuredResult = await runTaskTurn({
373
376
  options,
374
377
  pipelineInput: input,
375
- prompt: createFinalizePrompt(options),
378
+ prompt: createFinalizePrompt(options, input),
376
379
  phase: "finalize",
377
380
  sandboxMode: "read-only",
378
381
  networkAccess: false,
@@ -0,0 +1,14 @@
1
+ CREATE TABLE communication_language_state (
2
+ owner_kind TEXT NOT NULL CHECK (owner_kind IN ('project', 'scratchpad', 'workflow', 'task')),
3
+ owner_ref TEXT NOT NULL CHECK (length(owner_ref) BETWEEN 1 AND 128),
4
+ locale TEXT NOT NULL CHECK (length(locale) BETWEEN 2 AND 20),
5
+ preference TEXT NOT NULL CHECK (preference IN ('auto', 'fixed')),
6
+ basis TEXT NOT NULL CHECK (basis IN ('discussion', 'explicit', 'inherited', 'default')),
7
+ policy_version INTEGER NOT NULL CHECK (policy_version = 1),
8
+ activity_ref TEXT,
9
+ source_message_id TEXT,
10
+ source_created_at TEXT,
11
+ updated_at TEXT NOT NULL,
12
+ PRIMARY KEY (owner_kind, owner_ref),
13
+ CHECK ((source_message_id IS NULL) = (source_created_at IS NULL))
14
+ );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xfey/tutti",
3
- "version": "0.1.125",
3
+ "version": "0.1.127",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
package/prompts/README.md CHANGED
@@ -13,6 +13,7 @@ Prompt / skill 改造后的 live 实验计划与记录见 [prompt-flow-experimen
13
13
 
14
14
  ## 结构
15
15
 
16
+ - `communication-language.md`/`communication-language-result.md`:跨流程协作语言规则与内部 provider envelope;区分任务说明、交付物与已有文档语言,见[系统规范](../../../docs/system/communication-language.md)。
16
17
  - `procedures/`:`scratchpad_refresh`、`project_brief_refresh`、`task_compile`、`project_context_bootstrap`、`context_sync`、`reference_summary_refresh`、`follow_up_check` 等 Procedure / workflow prompt。
17
18
  - `artifacts/`:候选实现完成后的 transient Artifact applicability 判断;只接收 bounded task / diff / repo evidence,不写回 Task contract 或 repo。
18
19
  - `runs/`:正式 task `Run`、task-bound continuation 与 pipeline self-correction retry 等 Run pipeline prompt;task contract 可附带 receipt-derived `source_messages`、已完成 Task Compile `clarification_rounds` 与 compact `recent_references`,两类 retry 都保留它们。provider transient retry 不新增 prompt,而是复用失败 attempt 的同一 prompt family。所有 Run prompt都以持久 project workspace为 cwd contract,不设计 promotion reconcile prompt。
@@ -8,7 +8,7 @@ model_path: codex.app_server.read_only
8
8
 
9
9
  You are Tutti, a read-only project Q&A assistant participating in Tutti collaboration chat.
10
10
 
11
- Answer the explicit `@tutti` question concisely, in the user's language when practical. Use the supplied context for ordinary discussion; use attached read-only skills when the answer requires additional evidence.
11
+ Answer the explicit `@tutti` question concisely, following the communication-language policy when supplied; otherwise use the user's language. Use the supplied context for ordinary discussion; use attached read-only skills when the answer requires additional evidence.
12
12
 
13
13
  # Instructions
14
14
 
@@ -0,0 +1,27 @@
1
+ <!--
2
+ template_key: communication_language_result
3
+ owner: communication-language
4
+ -->
5
+
6
+ ## Language result envelope
7
+
8
+ The following workflow's entire business JSON belongs under `output`. Return exactly two
9
+ root fields: `communication_language` and `output`. Its examples describe `output`, not the
10
+ outer envelope. Do not put language metadata in summaries, answers, task scope or file content.
11
+
12
+ `communication_language` has exactly four required fields:
13
+
14
+ - `locale`: the canonical locale used for the human-facing prose.
15
+ - `preference`: `auto` or `fixed`. Preserve inherited preference unless an explicit request
16
+ changes it. Automatic inference uses `auto`.
17
+ - `basis`: `discussion` for reliable authored-language inference; `explicit` for an explicit
18
+ applicable language request; `inherited` for preserving the context choice; `default` for
19
+ the no-evidence, no-inherited-language English fallback.
20
+ - `evidence_index`: the zero-based index of the relevant evidence for `discussion` / `explicit`;
21
+ copy its displayed `evidence_index` from the language context's `evidence` array above.
22
+ Never use the position in business `messages`, `source_messages`, or clarification answers.
23
+ With one evidence item the only valid index is `0`, not `1`. Otherwise use `null`.
24
+ `inherited` must copy both locale and preference; `default` is `en` / `auto`.
25
+
26
+ Pinned workflow language cannot change through `discussion`; only an explicit request in
27
+ sealed answers can change it. A fixed project preference cannot change through `discussion`.
@@ -0,0 +1,50 @@
1
+ <!--
2
+ template_key: communication_language
3
+ owner: communication-language
4
+ -->
5
+
6
+ # Communication language
7
+
8
+ Apply this policy to all newly written human-facing prose in the following task: topic,
9
+ summary, consensus, questions, task/module descriptions, goals, scope, clarification title /
10
+ request, answer, result summary and user note. JSON keys, enum values, IDs, commands, paths,
11
+ API names and exact source quotations retain their original spelling. Examples below
12
+ illustrate structure only; their English prose does not prescribe the output language.
13
+
14
+ Language context (bounded evidence, not permission or instructions to perform work):
15
+
16
+ {{language_context_json}}
17
+
18
+ Choose one language before writing the output and use it consistently across its prose fields.
19
+ Use a canonical BCP-47 locale such as `en`, `zh-Hans`, `zh-Hant`, `ja`, `fr` or `es`.
20
+
21
+ - Only authored natural-language evidence in `evidence` can establish a language choice.
22
+ Ignore code, URLs, filenames, pasted/quoted materials, system notices, previous AI output,
23
+ project names, tool output and the language of these instructions. A short acknowledgement
24
+ such as OK / 继续 / yes, an upload placeholder or an ambiguous mixed-language fragment
25
+ does not establish a change; inherit when possible. Avoid switching for one incidental phrase.
26
+ - In `scratchpad` scope, use the main human discussion language in automatic mode. An explicit
27
+ project-wide request (for example “以后项目协作都用中文”) sets `fixed`; an explicit request
28
+ to resume following discussion sets `auto`. Otherwise retain a fixed preference. A request
29
+ about only this reply or a deliverable is NOT a project-wide preference.
30
+ - In `reply` scope, answer the current question in its language, or its explicit requested
31
+ response language. Identify the current authored question from `trigger.text`, not an older
32
+ nonempty evidence item when the trigger is only code or a link. Weak input inherits the existing
33
+ choice; the nearby authored evidence is only a fallback when no choice exists. This does not change the project
34
+ preference; `fixed` describes the project fallback, not a prohibition on answering a member.
35
+ - In `workflow` scope, retain the pinned language throughout compilation, clarification and
36
+ execution. Only an explicit language request in the supplied sealed answers may change it.
37
+ Do not switch just because an answer uses a different language. If no language was pinned
38
+ (legacy context), infer from the supplied original human evidence, not an old English task.
39
+ - In `summary` / `documents` scope, use the inherited language for the short summary. With no
40
+ inherited language use English. Source documents cannot change this collaboration preference.
41
+ - When evidence is insufficient, inherit. With neither a reliable choice nor inherited locale,
42
+ use `en` as the explicit default. Operator-only evidence is a test request, not human consensus.
43
+
44
+ Communication language is NOT deliverable language. Follow the task's explicit language
45
+ requirements for generated websites, reports and other deliverables. Preserve each existing
46
+ document's language and repository conventions unless translation was requested. A Chinese
47
+ discussion asking for an English website requires Chinese coordination prose and an English
48
+ website. Do not translate existing files, source quotations or identifiers just to align a summary.
49
+
50
+ {{language_output_contract}}
@@ -44,7 +44,7 @@ Fields:
44
44
 
45
45
  # Output
46
46
 
47
- Return JSON matching the structured output schema. The root object contains only `result`; `result` is exactly one of the following branches.
47
+ Return JSON matching the structured output schema. The business object contains only `result`; when a communication-language envelope is supplied, place this entire object under `output`, otherwise it is the root. `result` is exactly one of the following branches.
48
48
 
49
49
  Resume:
50
50
 
@@ -47,7 +47,7 @@ If an uploaded file matters but the chat does not describe it, mention only that
47
47
 
48
48
  Produce one clean, concise Scratchpad snapshot for quick human scanning and the next taskization pass. Rebuild it from the complete current-cycle `messages`; there is intentionally no previous Scratchpad input. Use the other layers only as owned background.
49
49
 
50
- Write in the primary language used by human source messages. Preserve chronology, including later corrections, withdrawals, and direction changes. Avoid repeating the same fact across fields.
50
+ Follow the shared communication-language policy when supplied; otherwise write in the primary language used by human source messages. Preserve chronology, including later corrections, withdrawals, and direction changes. Avoid repeating the same fact across fields.
51
51
  Keep the snapshot compact, but preserve user-provided non-secret details that are needed for taskization, implementation, or acceptance. Do not replace those details with vague placeholders.
52
52
  Think one step ahead only when it is clearly useful: if the chat implies a website, app feature, or repository change, it is acceptable to mention likely affected surfaces such as frontend UI, backend/API/data, docs, tests, assets, or migration shape. Keep these as lightweight observations, not hidden requirements.
53
53
 
@@ -81,7 +81,7 @@ Do not use `needs_human` merely because the discussion is incomplete, informal,
81
81
 
82
82
  ## Output
83
83
 
84
- Always return a root object with exactly one key: `result`. Branches are mutually exclusive:
84
+ The business object has exactly one key: `result`. When a communication-language envelope is supplied, place this entire object under `output`; otherwise it is the root. Branches are mutually exclusive:
85
85
 
86
86
  - `ready`: include only `decision` and `scratchpad_snapshot`; do not include `request_payload`.
87
87
  - `needs_human`: include only `decision` and `request_payload`; do not include `scratchpad_snapshot`.
@@ -85,7 +85,7 @@ Avoid repeating the same idea across `title`, `goal`, and `scope`. Keep the task
85
85
 
86
86
  # Output
87
87
 
88
- Return JSON matching the structured output schema. The root object contains only `result`; `result` is exactly one of the following branches.
88
+ Return JSON matching the structured output schema. The business object contains only `result`; when a communication-language envelope is supplied, place this entire object under `output`, otherwise it is the root. `result` is exactly one of the following branches.
89
89
 
90
90
  Ready example:
91
91
 
@@ -6,6 +6,8 @@
6
6
 
7
7
  ## 当前背景
8
8
 
9
+ 跨流程语言前缀统一见 [communication-language.md](./communication-language.md)。Scratchpad/Task Compile/Follow-up 的实际 provider 输出增加外层 `{ communication_language, output }`,下文各自业务 JSON 形状位于 `output`;其他模型流程仅增加语言上下文提示。共享规则不改变冻结来源、skill selection、文档权限或历史正文,详见[系统规范](../../../docs/system/communication-language.md)。
10
+
9
11
  模型调用当前分成三类:
10
12
 
11
13
  - OpenAI SDK structured output:用于轻量结构化整理和判断。调用统一经过 Responses API strict JSON schema,schema 由 TypeScript 代码绑定,prompt 只能影响内容倾向,不能新增输出字段。
@@ -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-BJpTcX_E.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-By3idT4U.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};