@xfey/tutti 0.1.126 → 0.1.128

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 (107) hide show
  1. package/README.md +2 -0
  2. package/dist/approvals/decision-command.d.ts +2 -2
  3. package/dist/chat-assistant/diagnostics.d.ts +11 -3
  4. package/dist/chat-assistant/diagnostics.js +5 -2
  5. package/dist/chat-assistant/failures.d.ts +27 -0
  6. package/dist/chat-assistant/failures.js +102 -0
  7. package/dist/chat-assistant/index.d.ts +14 -2
  8. package/dist/chat-assistant/index.js +241 -54
  9. package/dist/chat-assistant/language-context.d.ts +8 -0
  10. package/dist/chat-assistant/language-context.js +41 -0
  11. package/dist/chat-assistant/requests.d.ts +30 -0
  12. package/dist/chat-assistant/requests.js +58 -0
  13. package/dist/collaboration-ingestion/clarification.js +3 -1
  14. package/dist/collaboration-ingestion/main-chat.js +1 -0
  15. package/dist/collaboration-ingestion/types.d.ts +2 -0
  16. package/dist/collaboration-state/clarification-snapshot.d.ts +5 -5
  17. package/dist/collaboration-state/communication-language.d.ts +31 -0
  18. package/dist/collaboration-state/communication-language.js +36 -0
  19. package/dist/collaboration-state/task-compile-context.d.ts +1 -1
  20. package/dist/collaboration-state/task-compile-context.js +20 -1
  21. package/dist/collaboration-state/task-compile-requests.d.ts +6 -0
  22. package/dist/collaboration-state/task-compile-requests.js +16 -0
  23. package/dist/communication-language/index.d.ts +75 -0
  24. package/dist/communication-language/index.js +122 -0
  25. package/dist/communication-language/live-smoke-cli.d.ts +2 -0
  26. package/dist/communication-language/live-smoke-cli.js +368 -0
  27. package/dist/communication-language/prompt.d.ts +22 -0
  28. package/dist/communication-language/prompt.js +73 -0
  29. package/dist/control-plane/communication-language.d.ts +33 -0
  30. package/dist/control-plane/communication-language.js +72 -0
  31. package/dist/control-plane/context-sync.js +7 -0
  32. package/dist/control-plane/follow-up-output.js +25 -4
  33. package/dist/control-plane/follow-up-start.js +17 -0
  34. package/dist/control-plane/project-brief-refresh.js +10 -1
  35. package/dist/control-plane/project-context-bootstrap.d.ts +2 -0
  36. package/dist/control-plane/project-context-bootstrap.js +8 -0
  37. package/dist/control-plane/reference-summary-refresh.js +7 -0
  38. package/dist/control-plane/run-start.js +14 -6
  39. package/dist/control-plane/scratchpad-refresh-start.d.ts +1 -0
  40. package/dist/control-plane/scratchpad-refresh-start.js +13 -0
  41. package/dist/control-plane/scratchpad-refresh.d.ts +2 -0
  42. package/dist/control-plane/scratchpad-refresh.js +40 -26
  43. package/dist/control-plane/task-compile-continuation.js +16 -1
  44. package/dist/control-plane/task-compile-output.js +38 -14
  45. package/dist/control-plane/task-compile-request-status.d.ts +6 -0
  46. package/dist/control-plane/task-compile-request-status.js +70 -0
  47. package/dist/control-plane/task-compile-start.d.ts +1 -0
  48. package/dist/control-plane/task-compile-start.js +62 -3
  49. package/dist/control-plane/types.d.ts +2 -0
  50. package/dist/control-plane/workflows/openai.js +48 -28
  51. package/dist/control-plane/workflows/types.d.ts +18 -13
  52. package/dist/prompt-templates/index.d.ts +1 -1
  53. package/dist/prompt-templates/index.js +2 -0
  54. package/dist/providers/failure-classification.d.ts +23 -0
  55. package/dist/providers/failure-classification.js +43 -0
  56. package/dist/reference-access/read-evidence.d.ts +2 -0
  57. package/dist/reference-access/read-evidence.js +5 -0
  58. package/dist/reference-access/service.d.ts +2 -0
  59. package/dist/reference-access/service.js +2 -0
  60. package/dist/run-pipeline/task-run-invocation.js +12 -9
  61. package/dist/runtime-failures.js +15 -1
  62. package/dist/server-shell/cli/external-reference-runtime.js +6 -3
  63. package/dist/server-shell/cli/host-server-runtime.js +4 -1
  64. package/dist/server-shell/cli/relay-registration.js +1 -0
  65. package/dist/server-shell/http/routes/project-api/messages-routes.js +22 -1
  66. package/dist/server-shell/http/routes/project-api/openapi-collaboration-routes.d.ts +140 -5
  67. package/dist/server-shell/http/routes/project-api/openapi-collaboration-routes.js +29 -2
  68. package/dist/server-shell/http/routes/project-api/openapi-skills-routes.d.ts +2 -0
  69. package/dist/server-shell/http/routes/project-api/openapi-workspace-routes.d.ts +6 -0
  70. package/dist/server-shell/http/routes/project-api/openapi.d.ts +148 -5
  71. package/dist/server-shell/http/routes/project-api/schemas.d.ts +6 -0
  72. package/dist/server-shell/http/routes/project-api/schemas.js +1 -0
  73. package/dist/server-shell/http/routes/project-api/types.d.ts +1 -0
  74. package/dist/server-shell/http/routes/project-api/workflow-routes.js +39 -3
  75. package/dist/server-shell/http/routes/project-api/workspace-projections.js +1 -0
  76. package/dist/server-shell/http/validation.js +8 -2
  77. package/dist/server-shell/local-console/managed-project-service.d.ts +1 -1
  78. package/dist/server-shell/managed-access/transport.d.ts +1 -1
  79. package/migrations/0023_communication_language.sql +14 -0
  80. package/migrations/0024_chat_requests.sql +19 -0
  81. package/migrations/0025_task_compile_requests.sql +50 -0
  82. package/migrations/README.md +3 -1
  83. package/node_modules/@tutti/relay-client/dist/host-control.d.ts +1 -0
  84. package/node_modules/@tutti/relay-client/dist/host-control.js +1 -0
  85. package/node_modules/@tutti/shared/dist/schemas/api/index.d.ts +1 -0
  86. package/node_modules/@tutti/shared/dist/schemas/api/index.js +1 -0
  87. package/node_modules/@tutti/shared/dist/schemas/api/operation-status.d.ts +174 -0
  88. package/node_modules/@tutti/shared/dist/schemas/api/operation-status.js +75 -0
  89. package/node_modules/@tutti/shared/dist/schemas/api/primitives.d.ts +1 -0
  90. package/node_modules/@tutti/shared/dist/schemas/api/primitives.js +1 -0
  91. package/node_modules/@tutti/shared/dist/schemas/api/workspace-commands.d.ts +1 -0
  92. package/node_modules/@tutti/shared/dist/schemas/api/workspace-commands.js +2 -1
  93. package/package.json +1 -1
  94. package/prompts/README.md +1 -0
  95. package/prompts/chat-assistant.md +1 -1
  96. package/prompts/communication-language-result.md +27 -0
  97. package/prompts/communication-language.md +50 -0
  98. package/prompts/procedures/follow-up-check.md +1 -1
  99. package/prompts/procedures/scratchpad-refresh.md +2 -2
  100. package/prompts/procedures/task-compile.md +1 -1
  101. package/prompts/prompt-flow-map.md +2 -0
  102. package/web/assets/{homepage-motion-scene-DyEmWwvt.js → homepage-motion-scene-BcMt0AVW.js} +1 -1
  103. package/web/assets/index-2eX37Jx6.js +70 -0
  104. package/web/assets/index-BJvKuUiT.css +1 -0
  105. package/web/index.html +2 -2
  106. package/web/assets/index-By3idT4U.js +0 -70
  107. package/web/assets/index-DSBjOZmJ.css +0 -1
@@ -1,4 +1,5 @@
1
1
  import { createWorkflowInvocationRef, } from "@tutti/shared/ids";
2
+ import { workflowLanguageContext, sourceLanguageEvidence, logLanguageContext, } from "./communication-language.js";
2
3
  import { captureTaskCompileContext, readActiveTaskCompileContext, readCurrentScratchpadSourceRefs, readPendingTaskCompileSubmission, readScratchpadProjection, readScratchpadSourceState, readWorklistProjection, recordTaskCompileSubmission, rollbackTaskCompileContext, } from "../collaboration-state/index.js";
3
4
  import { withHostStoreTransaction } from "../store/index.js";
4
5
  import { initializeProjectDocs, readProjectDocsInitializationStatus, } from "../workspace-ops/index.js";
@@ -11,10 +12,15 @@ import { enqueueControlPlaneIntent } from "./dispatch-intents.js";
11
12
  import { materializePromptSourceMessages } from "./task-source-context.js";
12
13
  import { materializeRecentReferences, readPendingRecentReferenceSummaryPaths, readRecentReferenceSources, } from "./recent-references.js";
13
14
  import { startReferenceSummaryRefreshProcedure } from "./reference-summary-refresh.js";
15
+ import { linkTaskCompileActivity, closeFailedTaskCompileRequest, } from "../collaboration-state/task-compile-requests.js";
14
16
  export function startControlPlaneTaskCompile(options) {
15
17
  const activeContext = readActiveTaskCompileContext(options.store.db);
16
18
  if (activeContext !== null) {
17
19
  return {
20
+ result: {
21
+ submission_ref: activeContext.workflow_ref,
22
+ activity_ref: activeContext.captured_by_activity_ref,
23
+ },
18
24
  disposition: {
19
25
  kind: "already_running",
20
26
  activity_ref: activeContext.captured_by_activity_ref,
@@ -24,6 +30,10 @@ export function startControlPlaneTaskCompile(options) {
24
30
  const pendingSubmission = readPendingTaskCompileSubmission(options.store.db);
25
31
  if (pendingSubmission !== null) {
26
32
  return {
33
+ result: {
34
+ submission_ref: pendingSubmission.workflow_ref,
35
+ activity_ref: pendingSubmission.accepted_activity_ref,
36
+ },
27
37
  disposition: {
28
38
  kind: "already_running",
29
39
  activity_ref: pendingSubmission.accepted_activity_ref,
@@ -71,7 +81,7 @@ export function startControlPlaneTaskCompile(options) {
71
81
  },
72
82
  };
73
83
  }
74
- return startScratchpadRefreshBeforeTaskCompileActivity({
84
+ const started = startScratchpadRefreshBeforeTaskCompileActivity({
75
85
  ...options,
76
86
  runner: runnerResolution.runner,
77
87
  sourceBatch,
@@ -85,6 +95,7 @@ export function startControlPlaneTaskCompile(options) {
85
95
  now: options.now,
86
96
  }),
87
97
  });
98
+ return withSubmissionRef(started, taskCompileWorkflowRef);
88
99
  }
89
100
  if (!scratchpadHasSubmittableContent(scratchpad)) {
90
101
  return { disposition: { kind: "scratchpad_empty" } };
@@ -107,7 +118,12 @@ export function startControlPlaneTaskCompile(options) {
107
118
  now: options.now,
108
119
  });
109
120
  }
110
- return started;
121
+ return withSubmissionRef(started, taskCompileWorkflowRef);
122
+ }
123
+ function withSubmissionRef(result, submissionRef) {
124
+ return result.disposition.kind === "accepted"
125
+ ? { ...result, result: { ...result.result, submission_ref: submissionRef } }
126
+ : result;
111
127
  }
112
128
  function recordTaskCompileSubmissionIntent(options) {
113
129
  return withHostStoreTransaction(options.store.db, (tx) => {
@@ -151,6 +167,7 @@ function startControlPlaneTaskCompileActivity(options) {
151
167
  summary: "Compiling Scratchpad into Worklist tasks.",
152
168
  now: options.now,
153
169
  onTransition: (state) => {
170
+ linkTaskCompileActivity(options.store.db, options.taskCompileWorkflowRef, state.activity.activity_ref);
154
171
  options.publishProcedureTransition(state);
155
172
  if (state.transition_kind === "terminal") {
156
173
  options.onChainTerminal?.(state.activity.status === "failed"
@@ -166,6 +183,7 @@ function startControlPlaneTaskCompileActivity(options) {
166
183
  error,
167
184
  }),
168
185
  execute: async (context) => runControlPlaneTaskCompileProcedure({
186
+ logger: options.logger,
169
187
  runner: options.runner,
170
188
  store: options.store,
171
189
  events: options.events,
@@ -204,11 +222,16 @@ function startScratchpadRefreshBeforeTaskCompileActivity(options) {
204
222
  summary: "Refreshing Scratchpad before submitting to Worklist.",
205
223
  now: options.now,
206
224
  onTransition: (state) => {
225
+ linkTaskCompileActivity(options.store.db, options.taskCompileWorkflowRef, state.activity.activity_ref);
207
226
  options.publishProcedureTransition(state);
208
227
  if (state.transition_kind !== "terminal") {
209
228
  return;
210
229
  }
211
230
  if (state.activity.status !== "finished") {
231
+ rollbackTaskCompileContext(options.store.db, {
232
+ workflow_ref: options.taskCompileWorkflowRef,
233
+ now: options.now,
234
+ });
212
235
  options.onChainTerminal?.({
213
236
  state: "failed",
214
237
  errorCode: "scratchpad_refresh_failed",
@@ -220,6 +243,10 @@ function startScratchpadRefreshBeforeTaskCompileActivity(options) {
220
243
  }
221
244
  const scratchpad = readScratchpadProjection(options.store.db);
222
245
  if (!scratchpadHasSubmittableContent(scratchpad)) {
246
+ withHostStoreTransaction(options.store.db, (tx) => {
247
+ closeFailedTaskCompileRequest(tx, options.taskCompileWorkflowRef, options.now().toISOString(), "scratchpad_empty_after_refresh");
248
+ tx.prepare("DELETE FROM pending_task_compile_submissions WHERE workflow_ref = ?").run(options.taskCompileWorkflowRef);
249
+ });
223
250
  options.onChainTerminal?.({
224
251
  state: "failed",
225
252
  errorCode: "scratchpad_empty_after_refresh",
@@ -228,6 +255,10 @@ function startScratchpadRefreshBeforeTaskCompileActivity(options) {
228
255
  }
229
256
  const next = startReferenceSummaryGateOrNextStage(options);
230
257
  if (next.disposition.kind !== "accepted" && next.disposition.kind !== "already_running") {
258
+ rollbackTaskCompileContext(options.store.db, {
259
+ workflow_ref: options.taskCompileWorkflowRef,
260
+ now: options.now,
261
+ });
231
262
  options.onChainTerminal?.({
232
263
  state: "failed",
233
264
  errorCode: "task_compile_stage_unavailable",
@@ -242,6 +273,7 @@ function startScratchpadRefreshBeforeTaskCompileActivity(options) {
242
273
  error,
243
274
  }),
244
275
  execute: async (context) => runControlPlaneScratchpadRefreshProcedure({
276
+ logger: options.logger,
245
277
  runner: options.runner,
246
278
  store: options.store,
247
279
  events: options.events,
@@ -410,6 +442,10 @@ function startReferenceSummaryGateOrNextStage(options) {
410
442
  });
411
443
  registerReferenceSummaryGateWaiter(options, activeReferenceSummary.activity_ref);
412
444
  return {
445
+ result: {
446
+ submission_ref: options.taskCompileWorkflowRef,
447
+ activity_ref: activeReferenceSummary.activity_ref,
448
+ },
413
449
  disposition: {
414
450
  kind: "already_running",
415
451
  activity_ref: activeReferenceSummary.activity_ref,
@@ -511,6 +547,10 @@ function startProjectContextBootstrapActivity(options) {
511
547
  return;
512
548
  }
513
549
  if (state.activity.status !== "finished") {
550
+ rollbackTaskCompileContext(options.store.db, {
551
+ workflow_ref: options.taskCompileWorkflowRef,
552
+ now: options.now,
553
+ });
514
554
  options.onChainTerminal?.({ state: "failed", errorCode: "project_context_failed" });
515
555
  return;
516
556
  }
@@ -552,6 +592,13 @@ function startProjectContextBootstrapActivity(options) {
552
592
  try {
553
593
  const initializationContext = buildProjectDocsInitializationContext(taskCompileContext.scratchpad_snapshot);
554
594
  const modelDocuments = await tryProjectContextBootstrapDocuments({
595
+ communicationLanguage: {
596
+ ...workflowLanguageContext(options.store.db, {
597
+ workflowRef: options.taskCompileWorkflowRef,
598
+ sources: [],
599
+ }),
600
+ scope: "documents",
601
+ },
555
602
  runner: options.runner,
556
603
  projectContext: options.projectContext,
557
604
  scratchpad: taskCompileContext.scratchpad_snapshot,
@@ -639,10 +686,20 @@ export async function runControlPlaneTaskCompileProcedure(options) {
639
686
  sources: readRecentReferenceSources(options.store.db, context.sources),
640
687
  now: options.now,
641
688
  });
689
+ const sourceMessages = materializePromptSourceMessages(options.store.db, context.sources);
690
+ const language = workflowLanguageContext(options.store.db, {
691
+ workflowRef: options.workflowRef,
692
+ sources: sourceLanguageEvidence(options.store.db, context.sources),
693
+ });
694
+ logLanguageContext(options.logger, language, {
695
+ workflow_ref: options.workflowRef,
696
+ activity_ref: options.activityRef,
697
+ });
642
698
  const output = await options.runner.runTaskCompile({
699
+ communication_language: language,
643
700
  workflow_ref: options.workflowRef,
644
701
  scratchpad: context.scratchpad_snapshot,
645
- source_messages: materializePromptSourceMessages(options.store.db, context.sources),
702
+ source_messages: sourceMessages,
646
703
  worklist: readWorklistProjection(options.store.db),
647
704
  ...(recentReferences.length === 0 ? {} : { recent_references: recentReferences }),
648
705
  }, {
@@ -650,6 +707,7 @@ export async function runControlPlaneTaskCompileProcedure(options) {
650
707
  activity_ref: options.activityRef,
651
708
  ...(options.reportProgress === undefined ? {} : { reportProgress: options.reportProgress }),
652
709
  });
710
+ logLanguageContext(options.logger, language, { workflow_ref: options.workflowRef, activity_ref: options.activityRef }, output.communication_language);
653
711
  return applyTaskCompileProcedureOutput({
654
712
  store: options.store,
655
713
  events: options.events,
@@ -676,6 +734,7 @@ function readOrCaptureTaskCompileContext(input) {
676
734
  captured_by_activity_ref: input.activityRef,
677
735
  now: input.now,
678
736
  });
737
+ linkTaskCompileActivity(tx, input.workflowRef, input.activityRef);
679
738
  enqueueControlPlaneIntent(tx, {
680
739
  kind: "task_compile_start",
681
740
  ownerKind: "task_compile",
@@ -3,6 +3,7 @@ import type { RuntimeLogger } from "@tutti/shared/utils";
3
3
  import type { ActivityRef, ClarificationRoundRef, TaskId, TaskModuleId } from "@tutti/shared/ids";
4
4
  import type { ActiveApprovalProjection, ClarificationRequestPayload, ExecutionRuntimeSnapshot, MessageProjection, RecoverySummaryProjection, TaskDetailProjection, TaskProjection } from "@tutti/shared/schemas/api";
5
5
  import type { HostProjectStore } from "../store/index.js";
6
+ import type { CommunicationLanguageContext } from "../communication-language/index.js";
6
7
  import type { WorkspaceEventBus } from "../server-shell/http/workspace-events.js";
7
8
  import type { RecentReferenceContext } from "./reference-context-types.js";
8
9
  import type { PromptSourceMessage, TaskCompileClarificationContext } from "./task-source-context-types.js";
@@ -114,6 +115,7 @@ export type RunCorrectionContext = {
114
115
  } | null;
115
116
  };
116
117
  export type RunPipelineInput = {
118
+ communication_language?: CommunicationLanguageContext;
117
119
  activity_ref: ActivityRef;
118
120
  task: TaskProjection;
119
121
  task_detail: TaskDetailProjection;
@@ -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",
@@ -0,0 +1,23 @@
1
+ declare const aliases: {
2
+ readonly provider_not_configured: "provider_not_configured";
3
+ readonly provider_auth_invalid: "provider_auth_invalid";
4
+ readonly provider_quota_or_billing_required: "provider_quota_or_billing_required";
5
+ readonly provider_rate_limited: "provider_rate_limited";
6
+ readonly provider_model_unavailable: "provider_model_unavailable";
7
+ readonly provider_network_error: "provider_network_error";
8
+ readonly auth_failed: "provider_auth_invalid";
9
+ readonly host_delegation_revoked: "provider_auth_invalid";
10
+ readonly authority_invalid: "provider_auth_invalid";
11
+ readonly gateway_rate_limited: "provider_rate_limited";
12
+ readonly control_queue_full: "provider_rate_limited";
13
+ readonly turn_timeout: "provider_timeout";
14
+ readonly output_parse_failed: "provider_output_invalid";
15
+ readonly output_validation_failed: "provider_output_invalid";
16
+ readonly process_lifecycle_failed: "provider_lifecycle_failed";
17
+ readonly process_tree_not_terminated: "provider_lifecycle_failed";
18
+ };
19
+ export type ProviderFailureCode = (typeof aliases)[keyof typeof aliases];
20
+ /** Facts only, never a retry policy. Do not parse provider prose or raw stderr. */
21
+ export declare function providerFailureCode(error: unknown): ProviderFailureCode | undefined;
22
+ export {};
23
+ //# sourceMappingURL=failure-classification.d.ts.map
@@ -0,0 +1,43 @@
1
+ const aliases = {
2
+ provider_not_configured: "provider_not_configured",
3
+ provider_auth_invalid: "provider_auth_invalid",
4
+ provider_quota_or_billing_required: "provider_quota_or_billing_required",
5
+ provider_rate_limited: "provider_rate_limited",
6
+ provider_model_unavailable: "provider_model_unavailable",
7
+ provider_network_error: "provider_network_error",
8
+ auth_failed: "provider_auth_invalid",
9
+ host_delegation_revoked: "provider_auth_invalid",
10
+ authority_invalid: "provider_auth_invalid",
11
+ gateway_rate_limited: "provider_rate_limited",
12
+ control_queue_full: "provider_rate_limited",
13
+ turn_timeout: "provider_timeout",
14
+ output_parse_failed: "provider_output_invalid",
15
+ output_validation_failed: "provider_output_invalid",
16
+ process_lifecycle_failed: "provider_lifecycle_failed",
17
+ process_tree_not_terminated: "provider_lifecycle_failed",
18
+ };
19
+ /** Facts only, never a retry policy. Do not parse provider prose or raw stderr. */
20
+ export function providerFailureCode(error) {
21
+ if (typeof error !== "object" || error === null)
22
+ return undefined;
23
+ const value = error;
24
+ const code = typeof value.code === "string" ? value.code : value.reason;
25
+ if (typeof code === "string" && Object.hasOwn(aliases, code))
26
+ return aliases[code];
27
+ if (code !== "turn_failed" || typeof value.details !== "object" || value.details === null)
28
+ return undefined;
29
+ const details = value.details;
30
+ // Quota can also carry HTTP 429; the specific bounded code takes precedence.
31
+ if (details.turn_error_code === "insufficient_quota")
32
+ return "provider_quota_or_billing_required";
33
+ if (details.turn_error_code === "invalid_api_key")
34
+ return "provider_auth_invalid";
35
+ if (details.turn_error_code === "model_not_found")
36
+ return "provider_model_unavailable";
37
+ if (details.turn_error_code === "rate_limit_exceeded" || details.turn_error_http_status === 429)
38
+ return "provider_rate_limited";
39
+ if (details.turn_error_http_status === 401)
40
+ return "provider_auth_invalid";
41
+ return undefined;
42
+ }
43
+ //# sourceMappingURL=failure-classification.js.map
@@ -1,6 +1,8 @@
1
1
  import type { SqliteDatabase } from "../store/index.js";
2
2
  import type { ReferenceCopy } from "./cache.js";
3
3
  import type { ReferenceInvocation } from "./service.js";
4
+ import type { ReferenceAuthorization } from "@tutti/shared/schemas/api";
5
+ export declare function recordExternalReferenceAccessRequirement(db: SqliteDatabase, invocation: ReferenceInvocation, requestRef: string, authorization: ReferenceAuthorization, now?: () => Date): void;
4
6
  /** Prior answer context only: a source reference is not proof of freshness in a later turn. */
5
7
  export declare function readExternalReferenceReadSources(db: SqliteDatabase, invocationRef: string): string[];
6
8
  /** Durable observation, separate from evictable cache metadata; no content or locator. */
@@ -1,3 +1,8 @@
1
+ export function recordExternalReferenceAccessRequirement(db, invocation, requestRef, authorization, now = () => new Date()) {
2
+ db.prepare(`INSERT OR IGNORE INTO external_reference_access_requirements
3
+ (invocation_ref, request_ref, activity_ref, authorization_ref, profile, observed_at)
4
+ VALUES (?, ?, ?, ?, ?, ?)`).run(invocation.reference_invocation_ref, requestRef, invocation.activity_ref ?? null, authorization.authorization_ref, authorization.profile, now().toISOString());
5
+ }
1
6
  /** Prior answer context only: a source reference is not proof of freshness in a later turn. */
2
7
  export function readExternalReferenceReadSources(db, invocationRef) {
3
8
  return db
@@ -8,6 +8,8 @@ export type ReferenceInvocation = {
8
8
  };
9
9
  export declare class ReferenceAuthorizationRequiredError extends Error {
10
10
  readonly result: ReferenceAuthorizationRequired;
11
+ readonly code = "reference_authorization_required";
12
+ readonly continuation = "explicit_request";
11
13
  constructor(result: ReferenceAuthorizationRequired);
12
14
  }
13
15
  export declare class ExternalReferenceAccess {
@@ -1,6 +1,8 @@
1
1
  import { ReferenceCacheError } from "./cache.js";
2
2
  export class ReferenceAuthorizationRequiredError extends Error {
3
3
  result;
4
+ code = "reference_authorization_required";
5
+ continuation = "explicit_request";
4
6
  constructor(result) {
5
7
  super("reference_authorization_required");
6
8
  this.result = result;