@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
@@ -0,0 +1,31 @@
1
+ import type { SqliteDatabase } from "../store/index.js";
2
+ import { type LanguageDecision, type LanguagePreference } from "../communication-language/index.js";
3
+ export type LanguageOwner = {
4
+ kind: "project" | "scratchpad" | "workflow" | "task";
5
+ ref: string;
6
+ };
7
+ export type StoredCommunicationLanguage = LanguagePreference & {
8
+ basis: LanguageDecision["basis"];
9
+ activity_ref: string | null;
10
+ source_message_id: string | null;
11
+ source_created_at: string | null;
12
+ };
13
+ export declare function readCommunicationLanguage(db: SqliteDatabase, owner?: LanguageOwner): StoredCommunicationLanguage | null;
14
+ export declare function writeCommunicationLanguage(db: SqliteDatabase, input: {
15
+ owner: LanguageOwner;
16
+ language: LanguagePreference & {
17
+ basis: LanguageDecision["basis"];
18
+ };
19
+ activityRef?: string;
20
+ sourceCursor?: {
21
+ message_id: string;
22
+ created_at: string;
23
+ };
24
+ now: () => Date;
25
+ }): void;
26
+ export declare function copyCommunicationLanguage(db: SqliteDatabase, input: {
27
+ from: LanguageOwner;
28
+ to: LanguageOwner;
29
+ now: () => Date;
30
+ }): void;
31
+ //# sourceMappingURL=communication-language.d.ts.map
@@ -0,0 +1,36 @@
1
+ import { COMMUNICATION_LANGUAGE_POLICY_VERSION, isLanguagePreference, } from "../communication-language/index.js";
2
+ export function readCommunicationLanguage(db, owner = { kind: "project", ref: "current" }) {
3
+ const row = db
4
+ .prepare(`SELECT locale, preference, basis, activity_ref, source_message_id, source_created_at
5
+ FROM communication_language_state WHERE owner_kind = ? AND owner_ref = ?`)
6
+ .get(owner.kind, owner.ref);
7
+ if (row === undefined)
8
+ return null;
9
+ if (!isLanguagePreference(row))
10
+ throw new Error("communication_language_state_invalid");
11
+ return row;
12
+ }
13
+ export function writeCommunicationLanguage(db, input) {
14
+ if (!isLanguagePreference(input.language))
15
+ throw new Error("communication_language_state_invalid");
16
+ db.prepare(`INSERT INTO communication_language_state
17
+ (owner_kind, owner_ref, locale, preference, basis, policy_version, activity_ref,
18
+ source_message_id, source_created_at, updated_at)
19
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
20
+ ON CONFLICT(owner_kind, owner_ref) DO UPDATE SET
21
+ locale = excluded.locale, preference = excluded.preference, basis = excluded.basis,
22
+ policy_version = excluded.policy_version, activity_ref = excluded.activity_ref,
23
+ source_message_id = excluded.source_message_id, source_created_at = excluded.source_created_at,
24
+ updated_at = excluded.updated_at
25
+ WHERE excluded.owner_kind != 'project' OR
26
+ communication_language_state.source_created_at IS NULL OR
27
+ (excluded.source_created_at, excluded.source_message_id) >
28
+ (communication_language_state.source_created_at, communication_language_state.source_message_id)`).run(input.owner.kind, input.owner.ref, input.language.locale, input.language.preference, input.language.basis, COMMUNICATION_LANGUAGE_POLICY_VERSION, input.activityRef ?? null, input.sourceCursor?.message_id ?? null, input.sourceCursor?.created_at ?? null, input.now().toISOString());
29
+ }
30
+ export function copyCommunicationLanguage(db, input) {
31
+ const language = readCommunicationLanguage(db, input.from);
32
+ if (language === null)
33
+ return;
34
+ writeCommunicationLanguage(db, { owner: input.to, language, now: input.now });
35
+ }
36
+ //# sourceMappingURL=communication-language.js.map
@@ -24,7 +24,7 @@ export declare function completeTaskCompileContext(db: SqliteDatabase, input: Co
24
24
  task_changes: string[];
25
25
  receipts: {
26
26
  compiled_tasks?: {
27
- status: "failed" | "pending" | "done" | "todo" | "running";
27
+ status: "failed" | "pending" | "running" | "done" | "todo";
28
28
  task_id: string;
29
29
  title: string;
30
30
  summary: string;
@@ -1,7 +1,9 @@
1
1
  import { withHostStoreTransaction } from "../store/index.js";
2
2
  import { readScratchpadProjection } from "./scratchpad.js";
3
+ import { readCommunicationLanguage, copyCommunicationLanguage } from "./communication-language.js";
3
4
  import { readScratchpadSourceState } from "./scratchpad-source-state.js";
4
5
  import { nowIso, stringifyJson } from "./serialization.js";
6
+ import { registerTaskCompileRequest, closeFailedTaskCompileRequest, } from "./task-compile-requests.js";
5
7
  function cursorIsAfter(left, right) {
6
8
  if (left === undefined) {
7
9
  return false;
@@ -144,6 +146,7 @@ export function recordTaskCompileSubmission(db, input) {
144
146
  if (readActiveTaskCompileContext(db) !== null) {
145
147
  throw new Error("An active Task Compile Context already exists");
146
148
  }
149
+ registerTaskCompileRequest(db, input.workflow_ref, input.accepted_activity_ref, nowIso(input.now));
147
150
  db.prepare(`
148
151
  INSERT INTO pending_task_compile_submissions (
149
152
  id,
@@ -206,6 +209,14 @@ export function captureTaskCompileContext(db, input) {
206
209
  if (scratchpad.state !== "ready" || scratchpad.updated_at === undefined) {
207
210
  throw new Error("A ready Scratchpad is required to capture Task Compile Context");
208
211
  }
212
+ const language = readCommunicationLanguage(tx, { kind: "scratchpad", ref: "current" });
213
+ if (language !== null && language.activity_ref === scratchpad.updated_by_activity_ref) {
214
+ copyCommunicationLanguage(tx, {
215
+ from: { kind: "scratchpad", ref: "current" },
216
+ to: { kind: "workflow", ref: input.workflow_ref },
217
+ now: input.now ?? (() => new Date()),
218
+ });
219
+ }
209
220
  const sourceState = readScratchpadSourceState(tx);
210
221
  const submission = readPendingTaskCompileSubmission(tx, input.workflow_ref);
211
222
  const sources = readCurrentScratchpadSourceRefs(tx, submission?.source_window.through);
@@ -217,6 +228,7 @@ export function captureTaskCompileContext(db, input) {
217
228
  : { created_at: lastSource.created_at, message_id: lastSource.message_id });
218
229
  const after = submission?.source_window.after ?? sourceState.cycle_start_cursor;
219
230
  const submittedAt = nowIso(input.now);
231
+ registerTaskCompileRequest(tx, input.workflow_ref, input.captured_by_activity_ref, submittedAt);
220
232
  tx.prepare(`
221
233
  INSERT INTO active_task_compile_context (
222
234
  id,
@@ -328,9 +340,16 @@ export function rollbackTaskCompileContext(db, input) {
328
340
  return withHostStoreTransaction(db, (tx) => {
329
341
  const context = readActiveTaskCompileContext(tx, input.workflow_ref);
330
342
  if (context === null) {
331
- return false;
343
+ const pending = readPendingTaskCompileSubmission(tx, input.workflow_ref);
344
+ if (pending === null)
345
+ return false;
346
+ // Before capture the source cursor has not moved: release only this admission.
347
+ closeFailedTaskCompileRequest(tx, input.workflow_ref, nowIso(input.now));
348
+ tx.prepare("DELETE FROM pending_task_compile_submissions WHERE workflow_ref = ?").run(input.workflow_ref);
349
+ return true;
332
350
  }
333
351
  const rolledBackAt = nowIso(input.now);
352
+ closeFailedTaskCompileRequest(tx, input.workflow_ref, rolledBackAt);
334
353
  const sourceState = readScratchpadSourceState(tx);
335
354
  const after = context.source_window.after;
336
355
  const through = context.source_window.through;
@@ -0,0 +1,6 @@
1
+ import type { SqliteDatabase } from "../store/index.js";
2
+ /** Persistence only: callers own acceptance, stage selection and rollback. */
3
+ export declare function registerTaskCompileRequest(db: SqliteDatabase, workflowRef: string, activityRef: string, createdAt: string): void;
4
+ export declare function linkTaskCompileActivity(db: SqliteDatabase, workflowRef: string, activityRef: string): void;
5
+ export declare function closeFailedTaskCompileRequest(db: SqliteDatabase, workflowRef: string, now: string, code?: "task_compile_failed" | "scratchpad_empty_after_refresh"): void;
6
+ //# sourceMappingURL=task-compile-requests.d.ts.map
@@ -0,0 +1,16 @@
1
+ /** Persistence only: callers own acceptance, stage selection and rollback. */
2
+ export function registerTaskCompileRequest(db, workflowRef, activityRef, createdAt) {
3
+ db.prepare(`INSERT OR IGNORE INTO task_compile_requests
4
+ (workflow_ref, accepted_activity_ref, current_activity_ref, created_at)
5
+ VALUES (?, ?, ?, ?)`).run(workflowRef, activityRef, activityRef, createdAt);
6
+ }
7
+ export function linkTaskCompileActivity(db, workflowRef, activityRef) {
8
+ db.prepare(`UPDATE task_compile_requests SET current_activity_ref = ?
9
+ WHERE workflow_ref = ? AND closed_at IS NULL`).run(activityRef, workflowRef);
10
+ }
11
+ export function closeFailedTaskCompileRequest(db, workflowRef, now, code = "task_compile_failed") {
12
+ db.prepare(`UPDATE task_compile_requests SET failure_code = ?, closed_at = ?
13
+ WHERE workflow_ref = ? AND closed_at IS NULL
14
+ AND NOT EXISTS (SELECT 1 FROM scratchpad_receipts WHERE workflow_ref = ?)`).run(code, now, workflowRef, workflowRef);
15
+ }
16
+ //# sourceMappingURL=task-compile-requests.js.map
@@ -0,0 +1,75 @@
1
+ import type { MessageProjection } from "@tutti/shared/schemas/api";
2
+ export declare const COMMUNICATION_LANGUAGE_POLICY_VERSION = 1;
3
+ export type LanguagePreference = {
4
+ locale: string;
5
+ preference: "auto" | "fixed";
6
+ };
7
+ export type LanguageDecision = LanguagePreference & {
8
+ basis: "discussion" | "explicit" | "inherited" | "default";
9
+ evidence_index: number | null;
10
+ };
11
+ export type LanguageEvidenceMessage = {
12
+ role: "human" | "tutti" | "operator" | "system";
13
+ text: string;
14
+ };
15
+ export declare function messageLanguageEvidence(message: MessageProjection): LanguageEvidenceMessage;
16
+ export type CommunicationLanguageContext = {
17
+ policy_version: 1;
18
+ scope: "scratchpad" | "reply" | "workflow" | "summary" | "documents";
19
+ inherited: LanguagePreference | null;
20
+ pinned: boolean;
21
+ evidence: Array<{
22
+ role: "human" | "operator";
23
+ text: string;
24
+ }>;
25
+ };
26
+ export declare function isLanguagePreference(value: unknown): value is LanguagePreference;
27
+ export declare function isLanguageDecision(value: unknown): value is LanguageDecision;
28
+ /** A language sample, not task context. The original message is never rewritten. */
29
+ export declare function authoredLanguageSample(text: string): string;
30
+ export declare function createLanguageContext(input: {
31
+ scope: CommunicationLanguageContext["scope"];
32
+ inherited?: LanguagePreference | null;
33
+ pinned?: boolean;
34
+ messages?: readonly LanguageEvidenceMessage[];
35
+ }): CommunicationLanguageContext;
36
+ export declare function languageDecisionMatchesContext(decision: unknown, context: CommunicationLanguageContext): decision is LanguageDecision;
37
+ export declare function languageDiagnosticFields(context: CommunicationLanguageContext): {
38
+ language_policy_version: 1;
39
+ language_scope: "scratchpad" | "summary" | "workflow" | "reply" | "documents";
40
+ language_locale: string;
41
+ language_pinned: boolean;
42
+ language_evidence_count: number;
43
+ };
44
+ export type LanguageAwareOutput = {
45
+ communication_language?: LanguageDecision;
46
+ };
47
+ export declare const LANGUAGE_DECISION_SCHEMA: {
48
+ readonly type: "object";
49
+ readonly additionalProperties: false;
50
+ readonly required: readonly ["locale", "preference", "basis", "evidence_index"];
51
+ readonly properties: {
52
+ readonly locale: {
53
+ readonly type: "string";
54
+ readonly pattern: string;
55
+ };
56
+ readonly preference: {
57
+ readonly type: "string";
58
+ readonly enum: readonly ["auto", "fixed"];
59
+ };
60
+ readonly basis: {
61
+ readonly type: "string";
62
+ readonly enum: readonly ["discussion", "explicit", "inherited", "default"];
63
+ };
64
+ readonly evidence_index: {
65
+ readonly anyOf: readonly [{
66
+ readonly type: "integer";
67
+ readonly minimum: 0;
68
+ readonly maximum: 11;
69
+ }, {
70
+ readonly type: "null";
71
+ }];
72
+ };
73
+ };
74
+ };
75
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,122 @@
1
+ export const COMMUNICATION_LANGUAGE_POLICY_VERSION = 1;
2
+ export function messageLanguageEvidence(message) {
3
+ const feedback = message.refs?.artifact_feedback;
4
+ return {
5
+ role: message.author.kind === "human"
6
+ ? "human"
7
+ : message.refs?.development_operator === true
8
+ ? "operator"
9
+ : "system",
10
+ text: message.refs?.reference_file !== undefined
11
+ ? ""
12
+ : feedback === undefined
13
+ ? message.body
14
+ : [feedback.summary, ...feedback.annotations.map((entry) => entry.comment)]
15
+ .filter(Boolean)
16
+ .join("\n"),
17
+ };
18
+ }
19
+ const BASES = new Set(["discussion", "explicit", "inherited", "default"]);
20
+ const LOCALE_PATTERN = /^[a-z]{2,3}(?:-[A-Z][a-z]{3})?(?:-[A-Z]{2}|-\d{3})?$/u;
21
+ export function isLanguagePreference(value) {
22
+ if (typeof value !== "object" || value === null)
23
+ return false;
24
+ const entry = value;
25
+ if (entry.preference !== "auto" && entry.preference !== "fixed")
26
+ return false;
27
+ if (typeof entry.locale !== "string" || !LOCALE_PATTERN.test(entry.locale))
28
+ return false;
29
+ try {
30
+ return Intl.getCanonicalLocales(entry.locale)[0] === entry.locale && entry.locale !== "und";
31
+ }
32
+ catch {
33
+ return false;
34
+ }
35
+ }
36
+ export function isLanguageDecision(value) {
37
+ if (!isLanguagePreference(value))
38
+ return false;
39
+ const entry = value;
40
+ return (BASES.has(entry.basis ?? "") &&
41
+ (entry.evidence_index === null ||
42
+ (Number.isSafeInteger(entry.evidence_index) && (entry.evidence_index ?? -1) >= 0)) &&
43
+ Object.keys(value).every((key) => ["locale", "preference", "basis", "evidence_index"].includes(key)));
44
+ }
45
+ /** A language sample, not task context. The original message is never rewritten. */
46
+ export function authoredLanguageSample(text) {
47
+ const sample = text
48
+ .replace(/```[^]*?```|~~~[^]*?~~~/gu, " ")
49
+ .replace(/^(?: {0,3}>| {4}|\t).*$/gmu, " ")
50
+ .replace(/`[^`]*`/gu, " ")
51
+ .replace(/!\[[^\]]*\]\([^)]*\)/gu, " ")
52
+ .replace(/\[([^\]]+)\]\([^)]*\)/gu, "$1")
53
+ .replace(/https?:\/\/\S+|@tutti\b/giu, " ")
54
+ .replace(/\s+/gu, " ")
55
+ .trim();
56
+ return sample.length <= 800 ? sample : `${sample.slice(0, 398)} … ${sample.slice(-399)}`;
57
+ }
58
+ export function createLanguageContext(input) {
59
+ const evidence = (input.messages ?? []).flatMap((message) => {
60
+ if (message.role !== "human" && message.role !== "operator")
61
+ return [];
62
+ const text = authoredLanguageSample(message.text);
63
+ return /\p{L}/u.test(text) ? [{ role: message.role, text }] : [];
64
+ });
65
+ // Prefer humans. Operator-only tests can infer locally but cannot update project memory.
66
+ const selected = input.scope !== "reply" && evidence.some((entry) => entry.role === "human")
67
+ ? evidence.filter((entry) => entry.role === "human")
68
+ : evidence;
69
+ return {
70
+ policy_version: COMMUNICATION_LANGUAGE_POLICY_VERSION,
71
+ scope: input.scope,
72
+ inherited: input.inherited == null
73
+ ? null
74
+ : { locale: input.inherited.locale, preference: input.inherited.preference },
75
+ pinned: input.pinned ?? false,
76
+ evidence: selected.slice(-12),
77
+ };
78
+ }
79
+ export function languageDecisionMatchesContext(decision, context) {
80
+ if (!isLanguageDecision(decision))
81
+ return false;
82
+ if (decision.basis === "inherited") {
83
+ return (decision.evidence_index === null &&
84
+ decision.locale === context.inherited?.locale &&
85
+ decision.preference === context.inherited.preference);
86
+ }
87
+ if (decision.basis === "default") {
88
+ return (decision.evidence_index === null &&
89
+ context.inherited === null &&
90
+ decision.locale === "en" &&
91
+ decision.preference === "auto");
92
+ }
93
+ if (decision.evidence_index === null || !context.evidence[decision.evidence_index])
94
+ return false;
95
+ if (context.scope === "summary" || context.scope === "documents")
96
+ return false;
97
+ if (decision.basis === "discussion") {
98
+ return (!context.pinned && context.inherited?.preference !== "fixed" && decision.preference === "auto");
99
+ }
100
+ return true;
101
+ }
102
+ export function languageDiagnosticFields(context) {
103
+ return {
104
+ language_policy_version: context.policy_version,
105
+ language_scope: context.scope,
106
+ language_locale: context.inherited?.locale ?? "undetermined",
107
+ language_pinned: context.pinned,
108
+ language_evidence_count: context.evidence.length,
109
+ };
110
+ }
111
+ export const LANGUAGE_DECISION_SCHEMA = {
112
+ type: "object",
113
+ additionalProperties: false,
114
+ required: ["locale", "preference", "basis", "evidence_index"],
115
+ properties: {
116
+ locale: { type: "string", pattern: LOCALE_PATTERN.source },
117
+ preference: { type: "string", enum: ["auto", "fixed"] },
118
+ basis: { type: "string", enum: ["discussion", "explicit", "inherited", "default"] },
119
+ evidence_index: { anyOf: [{ type: "integer", minimum: 0, maximum: 11 }, { type: "null" }] },
120
+ },
121
+ };
122
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=live-smoke-cli.d.ts.map