@company-semantics/contracts 62.9.0 → 62.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@company-semantics/contracts",
3
- "version": "62.9.0",
3
+ "version": "62.11.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,3 +1,3 @@
1
1
  // AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
2
- export const SPEC_HASH = '71dd78eccef8' as const;
3
- export const SPEC_HASH_FULL = '71dd78eccef89c2b48c1ac446802164817d850a8c3a5680a3096956f17e59f08' as const;
2
+ export const SPEC_HASH = '9829bca605e4' as const;
3
+ export const SPEC_HASH_FULL = '9829bca605e4a464d73b067d1d063d2a68c5871b9fae570470a43987e481bb3a' as const;
@@ -2714,8 +2714,8 @@ export interface paths {
2714
2714
  get?: never;
2715
2715
  put?: never;
2716
2716
  /**
2717
- * Upload an org-chart artifact (PDF/DOCX/TXT/MD/CSV/XLSX/PNG/JPG/HEIC) for ingestion
2718
- * @description Accepts a multipart file upload up to 10 MB. With `?dryRun=true` the operation only reaches the validated state and writes no live rows. Returns 202 with the operationId so the caller can poll.
2717
+ * Upload an org-chart artifact (text, office documents, PDF, images, diagrams) for ingestion
2718
+ * @description Accepts one multipart file admitted by the org-chart ingestion surface (contracts INGESTION_SURFACES: text, document, image and diagram families; 10 MiB ceiling). With `?dryRun=true` the operation only reaches the validated state and writes no live rows. Returns 202 with the operationId so the caller can poll.
2719
2719
  */
2720
2720
  post: operations["importOrgChart"];
2721
2721
  delete?: never;
@@ -2826,7 +2826,7 @@ export interface paths {
2826
2826
  path?: never;
2827
2827
  cookie?: never;
2828
2828
  };
2829
- /** Get extracted text content from a Google Drive file */
2829
+ /** Get extracted text content from a Google Drive file (fetched in a registry format and parsed like an upload) */
2830
2830
  get: operations["getDriveFileContent"];
2831
2831
  put?: never;
2832
2832
  post?: never;
@@ -4762,6 +4762,8 @@ export interface components {
4762
4762
  destinationDocId: string;
4763
4763
  title: string;
4764
4764
  rawText: string;
4765
+ /** Format: uuid */
4766
+ sourceOperationId?: string;
4765
4767
  chatId: string;
4766
4768
  };
4767
4769
  RoleCatalogResponse: {
@@ -11686,14 +11688,14 @@ export interface operations {
11686
11688
  };
11687
11689
  content?: never;
11688
11690
  };
11689
- /** @description Upload exceeds the 10 MB size limit */
11691
+ /** @description Upload exceeds the surface ceiling (10 MiB) or the format cap */
11690
11692
  413: {
11691
11693
  headers: {
11692
11694
  [name: string]: unknown;
11693
11695
  };
11694
11696
  content?: never;
11695
11697
  };
11696
- /** @description Unsupported MIME type or declared MIME contradicts server-side sniff */
11698
+ /** @description Bytes match no registered format, or a format the org-chart surface does not admit */
11697
11699
  415: {
11698
11700
  headers: {
11699
11701
  [name: string]: unknown;
@@ -19,18 +19,31 @@ attribute and supported-formats copy from the same data.
19
19
  hand-maintained — adding a format updates both automatically.
20
20
  - No normalizer-routing fields here (`canonicalKind`, `normalizerKey` are
21
21
  backend-only). This package stays pure vocabulary.
22
+ - Every entry carries a `family`; `INGESTION_SURFACES` declares which families
23
+ each upload surface admits, and `formatsFor` / `acceptFor` /
24
+ `supportedLabelFor` / `maxBytesFor` are **derived** from that declaration
25
+ (ADR-CONTRACTS-159). A surface never re-spells a MIME.
22
26
 
23
27
  <!-- BEGIN GENERATED: readme-public-api — derived from code by `pnpm readme-api`. Do not edit. -->
24
28
 
25
29
  ## Public API
26
30
 
27
31
  - `INGESTION_FORMATS` — The canonical public format registry.
32
+ - `INGESTION_SURFACES` — The canonical surface declarations. - `company-md`: context-bank and goals extraction.
28
33
  - `INGEST_ACCEPT` — Comma-joined extension string for a file input's `accept` attribute.
34
+ - `IngestionFamily` _(type)_ — The semantic family a format belongs to.
29
35
  - `IngestionFormat` _(type)_ — One accepted upload format, keyed by its canonical MIME type.
36
+ - `IngestionSurface` _(type)_ — What one upload surface consumes: the families it admits and, optionally, a byte ceiling tighter than the…
37
+ - `IngestionSurfaceKey` _(type)_ — The upload surfaces that consume the registry.
30
38
  - `SUPPORTED_LABEL` — Human-readable supported-formats summary, derived from {@link humanLabels}.
31
39
  - `acceptExtensions` — All accepted filename extensions, in registry order.
40
+ - `acceptFor` — Comma-joined extension string for the surface's file input `accept`.
32
41
  - `byMime` — Look up the entry for a canonical MIME; undefined means unsupported.
42
+ - `formatsFor` — The registry formats a surface admits, in registry order.
33
43
  - `humanLabels` — Unique group labels for human display, in registry order.
44
+ - `maxBytesFor` — The transport byte ceiling for a surface: the largest registry cap among its admitted formats, tightened to…
45
+ - `supportedLabelFor` — "Supported: …" copy naming every label group the surface admits.
46
+ - `surfaceAdmits` — True when the surface admits this registry format.
34
47
 
35
48
  <!-- END GENERATED: readme-public-api -->
36
49
 
@@ -0,0 +1,65 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { INGESTION_FORMATS, byMime } from "../registry";
3
+ import {
4
+ INGESTION_SURFACES,
5
+ formatsFor,
6
+ surfaceAdmits,
7
+ acceptFor,
8
+ supportedLabelFor,
9
+ maxBytesFor,
10
+ } from "../surfaces";
11
+
12
+ describe("ingestion surfaces", () => {
13
+ it("every registry entry carries a family", () => {
14
+ for (const entry of INGESTION_FORMATS) {
15
+ expect(entry.family).toMatch(
16
+ /^(text|document|image|diagram|audio|video)$/,
17
+ );
18
+ }
19
+ });
20
+
21
+ it("every surface admits at least one format, in registry order", () => {
22
+ for (const key of Object.keys(INGESTION_SURFACES) as Array<
23
+ keyof typeof INGESTION_SURFACES
24
+ >) {
25
+ const formats = formatsFor(key);
26
+ expect(formats.length).toBeGreaterThan(0);
27
+ const order = formats.map((f) => INGESTION_FORMATS.indexOf(f));
28
+ expect(order).toEqual([...order].sort((a, b) => a - b));
29
+ }
30
+ });
31
+
32
+ it("company-md admits text and documents only", () => {
33
+ expect(surfaceAdmits("company-md", byMime("application/pdf")!)).toBe(true);
34
+ expect(surfaceAdmits("company-md", byMime("text/csv")!)).toBe(true);
35
+ expect(surfaceAdmits("company-md", byMime("image/png")!)).toBe(false);
36
+ expect(surfaceAdmits("company-md", byMime("audio/mpeg")!)).toBe(false);
37
+ expect(acceptFor("company-md")).toBe(
38
+ ".txt,.md,.html,.htm,.csv,.rtf,.docx,.doc,.pptx,.ppt,.xlsx,.xls,.odt,.ods,.odp,.epub,.pdf",
39
+ );
40
+ expect(supportedLabelFor("company-md")).toBe(
41
+ "Supported: Text, Markdown, HTML, CSV, RTF, Word, PowerPoint, Excel, OpenDocument, EPUB, PDF",
42
+ );
43
+ });
44
+
45
+ it("org-chart adds images and diagrams but never media", () => {
46
+ expect(surfaceAdmits("org-chart", byMime("image/heic")!)).toBe(true);
47
+ expect(surfaceAdmits("org-chart", byMime("image/svg+xml")!)).toBe(true);
48
+ expect(surfaceAdmits("org-chart", byMime("video/mp4")!)).toBe(false);
49
+ expect(acceptFor("org-chart")).toBe(
50
+ ".txt,.md,.html,.htm,.csv,.rtf,.docx,.doc,.pptx,.ppt,.xlsx,.xls,.odt,.ods,.odp,.epub,.pdf,.png,.jpg,.jpeg,.webp,.gif,.heic,.svg,.drawio",
51
+ );
52
+ });
53
+
54
+ it("derives the byte ceiling from the admitted formats and the surface cap", () => {
55
+ expect(maxBytesFor("company-md")).toBe(50_000_000);
56
+ expect(maxBytesFor("org-chart")).toBe(10 * 1024 * 1024);
57
+ for (const key of ["company-md", "org-chart"] as const) {
58
+ const ceiling = INGESTION_SURFACES[key].maxBytes ?? Infinity;
59
+ expect(maxBytesFor(key)).toBeLessThanOrEqual(ceiling);
60
+ expect(maxBytesFor(key)).toBeLessThanOrEqual(
61
+ Math.max(...formatsFor(key).map((f) => f.maxBytes)),
62
+ );
63
+ }
64
+ });
65
+ });
@@ -1,13 +1,19 @@
1
1
  /**
2
2
  * Ingestion Domain Barrel
3
3
  *
4
- * Re-exports the public document-ingestion format vocabulary.
4
+ * Re-exports the public document-ingestion format vocabulary and the
5
+ * per-surface views derived from it.
5
6
  * Import from '@company-semantics/contracts/ingestion'.
6
7
  *
7
- * @see ADR-CONT-075 for design rationale
8
+ * @see ADR-CONTRACTS-075 (registry), ADR-CONTRACTS-159 (surfaces)
8
9
  */
9
10
 
10
- export type { IngestionFormat } from "./types";
11
+ export type {
12
+ IngestionFamily,
13
+ IngestionFormat,
14
+ IngestionSurface,
15
+ IngestionSurfaceKey,
16
+ } from "./types";
11
17
 
12
18
  export {
13
19
  INGESTION_FORMATS,
@@ -17,3 +23,12 @@ export {
17
23
  INGEST_ACCEPT,
18
24
  SUPPORTED_LABEL,
19
25
  } from "./registry";
26
+
27
+ export {
28
+ INGESTION_SURFACES,
29
+ formatsFor,
30
+ surfaceAdmits,
31
+ acceptFor,
32
+ supportedLabelFor,
33
+ maxBytesFor,
34
+ } from "./surfaces";
@@ -21,174 +21,203 @@ import type { IngestionFormat } from "./types";
21
21
  export const INGESTION_FORMATS: readonly IngestionFormat[] = [
22
22
  {
23
23
  mime: "text/plain",
24
+ family: "text",
24
25
  extensions: [".txt"],
25
26
  label: "Text",
26
27
  maxBytes: 10_000_000,
27
28
  },
28
29
  {
29
30
  mime: "text/markdown",
31
+ family: "text",
30
32
  extensions: [".md"],
31
33
  label: "Markdown",
32
34
  maxBytes: 10_000_000,
33
35
  },
34
36
  {
35
37
  mime: "text/html",
38
+ family: "text",
36
39
  extensions: [".html", ".htm"],
37
40
  label: "HTML",
38
41
  maxBytes: 10_000_000,
39
42
  },
40
43
  {
41
44
  mime: "text/csv",
45
+ family: "text",
42
46
  extensions: [".csv"],
43
47
  label: "CSV",
44
48
  maxBytes: 25_000_000,
45
49
  },
46
50
  {
47
51
  mime: "application/rtf",
52
+ family: "document",
48
53
  extensions: [".rtf"],
49
54
  label: "RTF",
50
55
  maxBytes: 25_000_000,
51
56
  },
52
57
  {
53
58
  mime: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
59
+ family: "document",
54
60
  extensions: [".docx"],
55
61
  label: "Word",
56
62
  maxBytes: 25_000_000,
57
63
  },
58
64
  {
59
65
  mime: "application/msword",
66
+ family: "document",
60
67
  extensions: [".doc"],
61
68
  label: "Word",
62
69
  maxBytes: 25_000_000,
63
70
  },
64
71
  {
65
72
  mime: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
73
+ family: "document",
66
74
  extensions: [".pptx"],
67
75
  label: "PowerPoint",
68
76
  maxBytes: 50_000_000,
69
77
  },
70
78
  {
71
79
  mime: "application/vnd.ms-powerpoint",
80
+ family: "document",
72
81
  extensions: [".ppt"],
73
82
  label: "PowerPoint",
74
83
  maxBytes: 50_000_000,
75
84
  },
76
85
  {
77
86
  mime: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
87
+ family: "document",
78
88
  extensions: [".xlsx"],
79
89
  label: "Excel",
80
90
  maxBytes: 50_000_000,
81
91
  },
82
92
  {
83
93
  mime: "application/vnd.ms-excel",
94
+ family: "document",
84
95
  extensions: [".xls"],
85
96
  label: "Excel",
86
97
  maxBytes: 50_000_000,
87
98
  },
88
99
  {
89
100
  mime: "application/vnd.oasis.opendocument.text",
101
+ family: "document",
90
102
  extensions: [".odt"],
91
103
  label: "OpenDocument",
92
104
  maxBytes: 25_000_000,
93
105
  },
94
106
  {
95
107
  mime: "application/vnd.oasis.opendocument.spreadsheet",
108
+ family: "document",
96
109
  extensions: [".ods"],
97
110
  label: "OpenDocument",
98
111
  maxBytes: 50_000_000,
99
112
  },
100
113
  {
101
114
  mime: "application/vnd.oasis.opendocument.presentation",
115
+ family: "document",
102
116
  extensions: [".odp"],
103
117
  label: "OpenDocument",
104
118
  maxBytes: 50_000_000,
105
119
  },
106
120
  {
107
121
  mime: "application/epub+zip",
122
+ family: "document",
108
123
  extensions: [".epub"],
109
124
  label: "EPUB",
110
125
  maxBytes: 25_000_000,
111
126
  },
112
127
  {
113
128
  mime: "application/pdf",
129
+ family: "document",
114
130
  extensions: [".pdf"],
115
131
  label: "PDF",
116
132
  maxBytes: 50_000_000,
117
133
  },
118
134
  {
119
135
  mime: "image/png",
136
+ family: "image",
120
137
  extensions: [".png"],
121
138
  label: "Image",
122
139
  maxBytes: 25_000_000,
123
140
  },
124
141
  {
125
142
  mime: "image/jpeg",
143
+ family: "image",
126
144
  extensions: [".jpg", ".jpeg"],
127
145
  label: "Image",
128
146
  maxBytes: 25_000_000,
129
147
  },
130
148
  {
131
149
  mime: "image/webp",
150
+ family: "image",
132
151
  extensions: [".webp"],
133
152
  label: "Image",
134
153
  maxBytes: 25_000_000,
135
154
  },
136
155
  {
137
156
  mime: "image/gif",
157
+ family: "image",
138
158
  extensions: [".gif"],
139
159
  label: "Image",
140
160
  maxBytes: 25_000_000,
141
161
  },
142
162
  {
143
163
  mime: "image/heic",
164
+ family: "image",
144
165
  extensions: [".heic"],
145
166
  label: "Image",
146
167
  maxBytes: 25_000_000,
147
168
  },
148
169
  {
149
170
  mime: "image/svg+xml",
171
+ family: "diagram",
150
172
  extensions: [".svg"],
151
173
  label: "Diagram",
152
174
  maxBytes: 10_000_000,
153
175
  },
154
176
  {
155
177
  mime: "application/vnd.jgraph.mxfile",
178
+ family: "diagram",
156
179
  extensions: [".drawio"],
157
180
  label: "Diagram",
158
181
  maxBytes: 10_000_000,
159
182
  },
160
183
  {
161
184
  mime: "audio/mpeg",
185
+ family: "audio",
162
186
  extensions: [".mp3"],
163
187
  label: "Audio",
164
188
  maxBytes: 500_000_000,
165
189
  },
166
190
  {
167
191
  mime: "audio/x-m4a",
192
+ family: "audio",
168
193
  extensions: [".m4a"],
169
194
  label: "Audio",
170
195
  maxBytes: 500_000_000,
171
196
  },
172
197
  {
173
198
  mime: "audio/wav",
199
+ family: "audio",
174
200
  extensions: [".wav"],
175
201
  label: "Audio",
176
202
  maxBytes: 500_000_000,
177
203
  },
178
204
  {
179
205
  mime: "audio/aac",
206
+ family: "audio",
180
207
  extensions: [".aac"],
181
208
  label: "Audio",
182
209
  maxBytes: 500_000_000,
183
210
  },
184
211
  {
185
212
  mime: "video/mp4",
213
+ family: "video",
186
214
  extensions: [".mp4", ".m4v"],
187
215
  label: "Video",
188
216
  maxBytes: 500_000_000,
189
217
  },
190
218
  {
191
219
  mime: "video/quicktime",
220
+ family: "video",
192
221
  extensions: [".mov"],
193
222
  label: "Video",
194
223
  maxBytes: 500_000_000,
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Ingestion Surfaces
3
+ *
4
+ * Per-surface views of the single format registry. A surface names the
5
+ * families it consumes; everything else — the formats, the file-input `accept`
6
+ * string, the "Supported: …" copy, the transport byte ceiling — is DERIVED from
7
+ * `INGESTION_FORMATS` through that declaration. No surface re-spells a MIME.
8
+ *
9
+ * Invariants:
10
+ * - Every surface admits at least one registry format.
11
+ * - `maxBytesFor` never exceeds the registry cap of any admitted format, and
12
+ * never exceeds the surface ceiling when one is declared.
13
+ *
14
+ * @see ADR-CONTRACTS-159 for design rationale
15
+ */
16
+
17
+ import { INGESTION_FORMATS } from "./registry";
18
+ import type {
19
+ IngestionFormat,
20
+ IngestionSurface,
21
+ IngestionSurfaceKey,
22
+ } from "./types";
23
+
24
+ /** 10 MiB — the org-chart ceiling (see the surface JSDoc below). */
25
+ const ORG_CHART_MAX_BYTES = 10 * 1024 * 1024;
26
+
27
+ /**
28
+ * The canonical surface declarations.
29
+ *
30
+ * - `company-md`: context-bank and goals extraction. Consumes text and
31
+ * documents; images, diagrams and media are not extracted on this surface
32
+ * yet, so the picker must not advertise them.
33
+ * - `org-chart`: the HRIS / org-chart import. Consumes text and documents plus
34
+ * images and diagrams (a photographed or drawn chart goes to a vision-capable
35
+ * model natively). The 10 MiB ceiling bounds the bytes handed to that model
36
+ * per upload; the registry caps for its families would otherwise reach 50 MB.
37
+ */
38
+ export const INGESTION_SURFACES: Readonly<
39
+ Record<IngestionSurfaceKey, IngestionSurface>
40
+ > = {
41
+ "company-md": {
42
+ key: "company-md",
43
+ families: ["text", "document"],
44
+ },
45
+ "org-chart": {
46
+ key: "org-chart",
47
+ families: ["text", "document", "image", "diagram"],
48
+ maxBytes: ORG_CHART_MAX_BYTES,
49
+ },
50
+ };
51
+
52
+ /** The registry formats a surface admits, in registry order. */
53
+ export function formatsFor(surface: IngestionSurfaceKey): IngestionFormat[] {
54
+ const { families } = INGESTION_SURFACES[surface];
55
+ return INGESTION_FORMATS.filter((entry) => families.includes(entry.family));
56
+ }
57
+
58
+ /** True when the surface admits this registry format. */
59
+ export function surfaceAdmits(
60
+ surface: IngestionSurfaceKey,
61
+ format: IngestionFormat,
62
+ ): boolean {
63
+ return INGESTION_SURFACES[surface].families.includes(format.family);
64
+ }
65
+
66
+ /** Comma-joined extension string for the surface's file input `accept`. */
67
+ export function acceptFor(surface: IngestionSurfaceKey): string {
68
+ return formatsFor(surface)
69
+ .flatMap((entry) => [...entry.extensions])
70
+ .join(",");
71
+ }
72
+
73
+ /** "Supported: …" copy naming every label group the surface admits. */
74
+ export function supportedLabelFor(surface: IngestionSurfaceKey): string {
75
+ const labels = [...new Set(formatsFor(surface).map((entry) => entry.label))];
76
+ return `Supported: ${labels.join(", ")}`;
77
+ }
78
+
79
+ /**
80
+ * The transport byte ceiling for a surface: the largest registry cap among
81
+ * its admitted formats, tightened to the surface ceiling when one is declared.
82
+ * The per-format registry cap still applies underneath it.
83
+ */
84
+ export function maxBytesFor(surface: IngestionSurfaceKey): number {
85
+ const registryMax = formatsFor(surface).reduce(
86
+ (max, entry) => Math.max(max, entry.maxBytes),
87
+ 0,
88
+ );
89
+ const ceiling = INGESTION_SURFACES[surface].maxBytes;
90
+ return ceiling === undefined ? registryMax : Math.min(registryMax, ceiling);
91
+ }
@@ -15,6 +15,18 @@
15
15
  * @see ADR-CONT-075 for design rationale
16
16
  */
17
17
 
18
+ /**
19
+ * The semantic family a format belongs to. A SURFACE (an upload picker and the
20
+ * route behind it) declares which families it consumes; the accept-set and the
21
+ * byte ceiling for that surface derive from the registry through this field.
22
+ * Public and semantic — NOT the backend normalizer dispatch (`canonicalKind` /
23
+ * `normalizerKey` stay backend-local, ADR-CONTRACTS-075).
24
+ *
25
+ * @see ADR-CONTRACTS-159
26
+ */
27
+ export type IngestionFamily =
28
+ "text" | "document" | "image" | "diagram" | "audio" | "video";
29
+
18
30
  /**
19
31
  * One accepted upload format, keyed by its canonical MIME type.
20
32
  *
@@ -24,10 +36,13 @@
24
36
  * - `label` is the human display GROUP and is intentionally shared across
25
37
  * related formats (`.docx` + `.doc` are both `Word`; the rasters are all
26
38
  * `Image`; `.svg` + `.drawio` are `Diagram`).
39
+ * - `family` is the semantic family a surface selects on; every entry has one.
27
40
  */
28
41
  export interface IngestionFormat {
29
42
  /** Canonical MIME type (the registry key). */
30
43
  mime: string;
44
+ /** Semantic family the format belongs to (what a surface selects on). */
45
+ family: IngestionFamily;
31
46
  /** Lowercase, dot-prefixed filename extensions for this format. */
32
47
  extensions: readonly string[];
33
48
  /** Human display group label (e.g. `Word`, `Image`, `Audio`). */
@@ -35,3 +50,24 @@ export interface IngestionFormat {
35
50
  /** Per-format upload byte cap. */
36
51
  maxBytes: number;
37
52
  }
53
+
54
+ /** The upload surfaces that consume the registry. */
55
+ export type IngestionSurfaceKey = "company-md" | "org-chart";
56
+
57
+ /**
58
+ * What one upload surface consumes: the families it admits and, optionally, a
59
+ * byte ceiling tighter than the registry caps of those families.
60
+ *
61
+ * @see ADR-CONTRACTS-159
62
+ */
63
+ export interface IngestionSurface {
64
+ /** The surface this declaration describes. */
65
+ key: IngestionSurfaceKey;
66
+ /** Families the surface admits; a format outside them is refused (415). */
67
+ families: readonly IngestionFamily[];
68
+ /**
69
+ * Surface-level byte ceiling. When set it caps every admitted format below
70
+ * its registry `maxBytes`; absent means the registry caps apply as-is.
71
+ */
72
+ maxBytes?: number;
73
+ }