@company-semantics/contracts 85.3.0 → 86.1.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 +1 -1
- package/src/api/generated-spec-hash.ts +2 -2
- package/src/api/generated.ts +8 -8
- package/src/chat/README.md +1 -1
- package/src/chat/__tests__/page-context.test.ts +2 -1
- package/src/chat/schemas.ts +1 -2
- package/src/mcp/__tests__/README.md +29 -0
- package/src/mcp/__tests__/resource-types-fixture.ts +50 -0
- package/src/mcp/__tests__/resources.test.ts +39 -0
- package/src/mcp/resources.ts +16 -1
- package/src/org/__tests__/view-scopes.test.ts +0 -2
- package/src/org/view-scopes.ts +0 -1
- package/src/source-sync/__tests__/upkeep.test.ts +20 -2
- package/src/source-sync/upkeep.ts +7 -7
package/package.json
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
|
|
2
|
-
export const SPEC_HASH = '
|
|
3
|
-
export const SPEC_HASH_FULL = '
|
|
2
|
+
export const SPEC_HASH = '4ec580fdf79a' as const;
|
|
3
|
+
export const SPEC_HASH_FULL = '4ec580fdf79abef6f817c1afee788dc61a640b4f903b3f5181d0c3a7fc934709' as const;
|
package/src/api/generated.ts
CHANGED
|
@@ -5334,7 +5334,7 @@ export interface components {
|
|
|
5334
5334
|
pageContext?: {
|
|
5335
5335
|
route: string;
|
|
5336
5336
|
/** @enum {string} */
|
|
5337
|
-
view: "chat" | "
|
|
5337
|
+
view: "chat" | "company-md" | "execution-detail" | "internal-admin" | "notifications" | "org-settings" | "org-work" | "settings" | "team-md" | "teams" | "teamwork" | "teamwork-member" | "timeline" | "upgrade" | "user-md" | "work";
|
|
5338
5338
|
/** @enum {string} */
|
|
5339
5339
|
scope: "none" | "me" | "team" | "org";
|
|
5340
5340
|
entity?: {
|
|
@@ -5380,7 +5380,7 @@ export interface components {
|
|
|
5380
5380
|
pageContext?: {
|
|
5381
5381
|
route: string;
|
|
5382
5382
|
/** @enum {string} */
|
|
5383
|
-
view: "chat" | "
|
|
5383
|
+
view: "chat" | "company-md" | "execution-detail" | "internal-admin" | "notifications" | "org-settings" | "org-work" | "settings" | "team-md" | "teams" | "teamwork" | "teamwork-member" | "timeline" | "upgrade" | "user-md" | "work";
|
|
5384
5384
|
/** @enum {string} */
|
|
5385
5385
|
scope: "none" | "me" | "team" | "org";
|
|
5386
5386
|
entity?: {
|
|
@@ -5426,7 +5426,7 @@ export interface components {
|
|
|
5426
5426
|
pageContext?: {
|
|
5427
5427
|
route: string;
|
|
5428
5428
|
/** @enum {string} */
|
|
5429
|
-
view: "chat" | "
|
|
5429
|
+
view: "chat" | "company-md" | "execution-detail" | "internal-admin" | "notifications" | "org-settings" | "org-work" | "settings" | "team-md" | "teams" | "teamwork" | "teamwork-member" | "timeline" | "upgrade" | "user-md" | "work";
|
|
5430
5430
|
/** @enum {string} */
|
|
5431
5431
|
scope: "none" | "me" | "team" | "org";
|
|
5432
5432
|
entity?: {
|
|
@@ -8516,7 +8516,7 @@ export interface components {
|
|
|
8516
8516
|
surface: {
|
|
8517
8517
|
route: string;
|
|
8518
8518
|
/** @enum {string} */
|
|
8519
|
-
view: "chat" | "
|
|
8519
|
+
view: "chat" | "company-md" | "execution-detail" | "internal-admin" | "notifications" | "org-settings" | "org-work" | "settings" | "team-md" | "teams" | "teamwork" | "teamwork-member" | "timeline" | "upgrade" | "user-md" | "work";
|
|
8520
8520
|
/** @enum {string} */
|
|
8521
8521
|
scope: "none" | "me" | "team" | "org";
|
|
8522
8522
|
entity?: {
|
|
@@ -8563,7 +8563,7 @@ export interface components {
|
|
|
8563
8563
|
route?: {
|
|
8564
8564
|
route: string;
|
|
8565
8565
|
/** @enum {string} */
|
|
8566
|
-
view: "chat" | "
|
|
8566
|
+
view: "chat" | "company-md" | "execution-detail" | "internal-admin" | "notifications" | "org-settings" | "org-work" | "settings" | "team-md" | "teams" | "teamwork" | "teamwork-member" | "timeline" | "upgrade" | "user-md" | "work";
|
|
8567
8567
|
/** @enum {string} */
|
|
8568
8568
|
scope: "none" | "me" | "team" | "org";
|
|
8569
8569
|
entity?: {
|
|
@@ -9625,7 +9625,7 @@ export interface components {
|
|
|
9625
9625
|
/** Format: date-time */
|
|
9626
9626
|
lastObservedAt: string;
|
|
9627
9627
|
dismissedAt: string | null;
|
|
9628
|
-
fingerprint
|
|
9628
|
+
fingerprint: string;
|
|
9629
9629
|
}[];
|
|
9630
9630
|
};
|
|
9631
9631
|
SourceSyncFinding: {
|
|
@@ -9650,11 +9650,11 @@ export interface components {
|
|
|
9650
9650
|
/** Format: date-time */
|
|
9651
9651
|
lastObservedAt: string;
|
|
9652
9652
|
dismissedAt: string | null;
|
|
9653
|
-
fingerprint
|
|
9653
|
+
fingerprint: string;
|
|
9654
9654
|
};
|
|
9655
9655
|
SourceSyncDismissFindingRequest: {
|
|
9656
9656
|
note?: string;
|
|
9657
|
-
expectedFingerprint
|
|
9657
|
+
expectedFingerprint: string;
|
|
9658
9658
|
};
|
|
9659
9659
|
SourceSyncStandingOrdersResponse: {
|
|
9660
9660
|
orders: {
|
package/src/chat/README.md
CHANGED
|
@@ -65,7 +65,7 @@ A chat can belong to a SUBJECT — the durable case a hand-off to chat is about
|
|
|
65
65
|
- `CHAT_DISCUSSION_REQUEST_TYPES` — The kinds of durable input request a turn can be scoped to.
|
|
66
66
|
- `CHAT_PAGE_ENTITY_TYPES` — What kind of thing the reader has open, when they have one open.
|
|
67
67
|
- `CHAT_PAGE_SCOPES` — The route parser's four URL scopes: unscoped, `/me/*`, `/team/*`, `/@:org/*`.
|
|
68
|
-
- `CHAT_PAGE_VIEWS` — The app route parser's
|
|
68
|
+
- `CHAT_PAGE_VIEWS` — The app route parser's sixteen `type` values, projected onto the wire.
|
|
69
69
|
- `CHAT_RUNTIME_PROFILES` — Ordered list of available profiles for UI rendering.
|
|
70
70
|
- `CONVERSATION_SUBJECT_KINDS` — The kinds of subject a chat can continue.
|
|
71
71
|
- `ChatByInteractionResponse` _(type)_ — Response for GET /api/chats/by-interaction/:interactionId
|
|
@@ -211,9 +211,10 @@ describe("ChatPageContext is the ceiling on both axes", () => {
|
|
|
211
211
|
expect(ChatPageContextSchema.safeParse(VALID).success).toBe(true);
|
|
212
212
|
expect(
|
|
213
213
|
ChatPageContextSchema.safeParse({
|
|
214
|
-
route: "/
|
|
214
|
+
route: "/chat/abc",
|
|
215
215
|
view: "chat",
|
|
216
216
|
scope: "none",
|
|
217
|
+
entity: { type: "chat", id: "abc" },
|
|
217
218
|
}).success,
|
|
218
219
|
).toBe(true);
|
|
219
220
|
});
|
package/src/chat/schemas.ts
CHANGED
|
@@ -509,7 +509,7 @@ export const ChatMentionableResponseSchema = z.object({
|
|
|
509
509
|
// =============================================================================
|
|
510
510
|
|
|
511
511
|
/**
|
|
512
|
-
* The app route parser's
|
|
512
|
+
* The app route parser's sixteen `type` values, projected onto the wire.
|
|
513
513
|
*
|
|
514
514
|
* A PROJECTION, not a parallel vocabulary: keep it in step with
|
|
515
515
|
* `company-semantics-app/src/platform/route-parser.ts`. A view the parser can
|
|
@@ -519,7 +519,6 @@ export const ChatMentionableResponseSchema = z.object({
|
|
|
519
519
|
*/
|
|
520
520
|
export const CHAT_PAGE_VIEWS = [
|
|
521
521
|
"chat",
|
|
522
|
-
"chats",
|
|
523
522
|
"company-md",
|
|
524
523
|
"execution-detail",
|
|
525
524
|
"internal-admin",
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# mcp/\_\_tests\_\_/
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Locks the MCP vocabulary claims the compiler alone does not.
|
|
6
|
+
|
|
7
|
+
- `capability-graph.test.ts` — `buildCapabilityGraph` derives edges from
|
|
8
|
+
`produces`/`consumes` resource flow.
|
|
9
|
+
- `resource-types-fixture.ts` — a COMPILED module, not a `*.test.ts`:
|
|
10
|
+
`src/tsconfig.json` excludes test files and vitest does not typecheck, so a
|
|
11
|
+
`satisfies ResourceType[]` written in a test would never be checked. Here
|
|
12
|
+
`tsc -b` checks that the nine original members and the five agent-reference
|
|
13
|
+
members (`company_md.doc`, `company_md.thread`, `org.member`, `org.unit`,
|
|
14
|
+
`work.item`) are all `ResourceType` members, and that no union member is
|
|
15
|
+
left unlisted.
|
|
16
|
+
- `resources.test.ts` — the runtime half: fourteen distinct members, each
|
|
17
|
+
`<domain>.<kind>`.
|
|
18
|
+
|
|
19
|
+
## Invariants
|
|
20
|
+
|
|
21
|
+
- These assert VOCABULARY and SHAPE, never behavior.
|
|
22
|
+
|
|
23
|
+
## Public API
|
|
24
|
+
|
|
25
|
+
None — test-only.
|
|
26
|
+
|
|
27
|
+
## Dependencies
|
|
28
|
+
|
|
29
|
+
`vitest` and the sibling modules under test.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compile-time fixture for the `ResourceType` union (ADR slug
|
|
3
|
+
* mcp-resource-types-cover-agent-references).
|
|
4
|
+
*
|
|
5
|
+
* WHY A COMPILED MODULE AND NOT `resources.test.ts`. `src/tsconfig.json`
|
|
6
|
+
* excludes every `*.test.ts` file and vitest does not typecheck, so a
|
|
7
|
+
* `satisfies` written inside a test file is never checked. This file is not a
|
|
8
|
+
* `*.test.ts`, so `tsc -b` compiles it: a literal that stops being a
|
|
9
|
+
* `ResourceType` member fails `pnpm typecheck`. `resources.test.ts` imports the
|
|
10
|
+
* arrays to prove the runtime half (count, uniqueness).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { ResourceType } from "../resources";
|
|
14
|
+
|
|
15
|
+
/** The nine members the union carried before agent-reference kinds were added. */
|
|
16
|
+
export const ORIGINAL_RESOURCE_TYPES = [
|
|
17
|
+
"integration.connection",
|
|
18
|
+
"slack.channel",
|
|
19
|
+
"slack.channel_scope",
|
|
20
|
+
"slack.coverage",
|
|
21
|
+
"ingestion.job",
|
|
22
|
+
"knowledge.fingerprint",
|
|
23
|
+
"org.status",
|
|
24
|
+
"system.status",
|
|
25
|
+
"system.runtime",
|
|
26
|
+
] as const satisfies readonly ResourceType[];
|
|
27
|
+
|
|
28
|
+
/** The resource kinds the agent is handed a reference to (control INV-AGENT-GROUND-3). */
|
|
29
|
+
export const AGENT_REFERENCE_RESOURCE_TYPES = [
|
|
30
|
+
"company_md.doc",
|
|
31
|
+
"company_md.thread",
|
|
32
|
+
"org.member",
|
|
33
|
+
"org.unit",
|
|
34
|
+
"work.item",
|
|
35
|
+
] as const satisfies readonly ResourceType[];
|
|
36
|
+
|
|
37
|
+
/** All fourteen members. */
|
|
38
|
+
export const ALL_RESOURCE_TYPES = [
|
|
39
|
+
...ORIGINAL_RESOURCE_TYPES,
|
|
40
|
+
...AGENT_REFERENCE_RESOURCE_TYPES,
|
|
41
|
+
] as const satisfies readonly ResourceType[];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Exhaustiveness: every `ResourceType` member is listed above. If a member is
|
|
45
|
+
* added to the union without being listed here, this assignment fails.
|
|
46
|
+
*/
|
|
47
|
+
type Unlisted = Exclude<ResourceType, (typeof ALL_RESOURCE_TYPES)[number]>;
|
|
48
|
+
export const NO_UNLISTED_RESOURCE_TYPES: [Unlisted] extends [never]
|
|
49
|
+
? true
|
|
50
|
+
: never = true;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import type { ResourceType } from "../resources";
|
|
3
|
+
import {
|
|
4
|
+
AGENT_REFERENCE_RESOURCE_TYPES,
|
|
5
|
+
ALL_RESOURCE_TYPES,
|
|
6
|
+
NO_UNLISTED_RESOURCE_TYPES,
|
|
7
|
+
ORIGINAL_RESOURCE_TYPES,
|
|
8
|
+
} from "./resource-types-fixture";
|
|
9
|
+
|
|
10
|
+
describe("ResourceType", () => {
|
|
11
|
+
it("keeps the nine original members", () => {
|
|
12
|
+
expect(ORIGINAL_RESOURCE_TYPES).toHaveLength(9);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("covers every resource kind the agent is handed a reference to", () => {
|
|
16
|
+
// Checked by the editor only (test files are not typechecked); the
|
|
17
|
+
// compiled twin is AGENT_REFERENCE_RESOURCE_TYPES in the fixture.
|
|
18
|
+
const agentReferences = [
|
|
19
|
+
"company_md.doc",
|
|
20
|
+
"company_md.thread",
|
|
21
|
+
"org.member",
|
|
22
|
+
"org.unit",
|
|
23
|
+
"work.item",
|
|
24
|
+
] satisfies ResourceType[];
|
|
25
|
+
expect([...AGENT_REFERENCE_RESOURCE_TYPES]).toEqual(agentReferences);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("has fourteen distinct members, all listed", () => {
|
|
29
|
+
expect(ALL_RESOURCE_TYPES).toHaveLength(14);
|
|
30
|
+
expect(new Set(ALL_RESOURCE_TYPES).size).toBe(14);
|
|
31
|
+
expect(NO_UNLISTED_RESOURCE_TYPES).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("namespaces every member as <domain>.<kind>", () => {
|
|
35
|
+
for (const resource of ALL_RESOURCE_TYPES) {
|
|
36
|
+
expect(resource).toMatch(/^[a-z_]+\.[a-z_]+$/);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
});
|
package/src/mcp/resources.ts
CHANGED
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
* This is a CLOSED union — new resource types represent architectural
|
|
9
9
|
* additions that require a contracts release and downstream consumer updates.
|
|
10
10
|
* Unlike ToolIntegration, resource types are not extensible via (string & {}).
|
|
11
|
+
*
|
|
12
|
+
* Since the ADR with slug mcp-resource-types-cover-agent-references, a member
|
|
13
|
+
* also exists for every resource kind the agent is handed a reference to (page
|
|
14
|
+
* context entity, citation entityId), so backend tool meta `consumes` can serve
|
|
15
|
+
* as the dereference registry (control INV-AGENT-GROUND-3).
|
|
11
16
|
*/
|
|
12
17
|
export type ResourceType =
|
|
13
18
|
| "integration.connection"
|
|
@@ -18,4 +23,14 @@ export type ResourceType =
|
|
|
18
23
|
| "knowledge.fingerprint"
|
|
19
24
|
| "org.status"
|
|
20
25
|
| "system.status"
|
|
21
|
-
| "system.runtime"
|
|
26
|
+
| "system.runtime"
|
|
27
|
+
/** A company-md document; consumed by id (uuid), or by company-md path for page context. */
|
|
28
|
+
| "company_md.doc"
|
|
29
|
+
/** A company-md comment thread; consumed by id (uuid). */
|
|
30
|
+
| "company_md.thread"
|
|
31
|
+
/** An org member; consumed by users.id (uuid). */
|
|
32
|
+
| "org.member"
|
|
33
|
+
/** An org unit; consumed by org_units.id (uuid). */
|
|
34
|
+
| "org.unit"
|
|
35
|
+
/** A work item; consumed by id (uuid). */
|
|
36
|
+
| "work.item";
|
|
@@ -14,7 +14,6 @@ describe("VIEW_SCOPE_MAP golden snapshot", () => {
|
|
|
14
14
|
teams: null,
|
|
15
15
|
chat: null,
|
|
16
16
|
settings: null,
|
|
17
|
-
chats: null,
|
|
18
17
|
"my-work": null,
|
|
19
18
|
"user-md": null,
|
|
20
19
|
"team-md": null,
|
|
@@ -37,7 +36,6 @@ describe("getViewScope", () => {
|
|
|
37
36
|
it("returns null for public views", () => {
|
|
38
37
|
expect(getViewScope("chat")).toBeNull();
|
|
39
38
|
expect(getViewScope("settings")).toBeNull();
|
|
40
|
-
expect(getViewScope("chats")).toBeNull();
|
|
41
39
|
expect(getViewScope("upgrade")).toBeNull();
|
|
42
40
|
// teams is membership-derived, not scope-gated (ADR-CONTRACTS-062).
|
|
43
41
|
expect(getViewScope("teams")).toBeNull();
|
package/src/org/view-scopes.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
const FINDING_ID = "d3b42536-f7a8-4b3a-9eb5-e3f4a5b6c7d8";
|
|
28
28
|
const SOURCE_ID = "3f1a2b4c-5d6e-4f70-8192-a3b4c5d6e7f8";
|
|
29
29
|
const DOC_ID = "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d";
|
|
30
|
+
const FINGERPRINT = "0123456789abcdef0123456789abcdef";
|
|
30
31
|
|
|
31
32
|
function finding(overrides: Record<string, unknown> = {}) {
|
|
32
33
|
return {
|
|
@@ -41,6 +42,7 @@ function finding(overrides: Record<string, unknown> = {}) {
|
|
|
41
42
|
firstObservedAt: "2026-09-19T10:00:00Z",
|
|
42
43
|
lastObservedAt: "2026-09-20T10:00:00Z",
|
|
43
44
|
dismissedAt: null,
|
|
45
|
+
fingerprint: FINGERPRINT,
|
|
44
46
|
...overrides,
|
|
45
47
|
};
|
|
46
48
|
}
|
|
@@ -100,12 +102,28 @@ describe("staleness findings", () => {
|
|
|
100
102
|
SourceSyncFindingsResponseSchema.safeParse({ findings: [finding()] })
|
|
101
103
|
.success,
|
|
102
104
|
).toBe(true);
|
|
105
|
+
expect(
|
|
106
|
+
SourceSyncDismissFindingRequestSchema.safeParse({
|
|
107
|
+
expectedFingerprint: FINGERPRINT,
|
|
108
|
+
}).success,
|
|
109
|
+
).toBe(true);
|
|
110
|
+
expect(
|
|
111
|
+
SourceSyncDismissFindingRequestSchema.safeParse({
|
|
112
|
+
expectedFingerprint: FINGERPRINT,
|
|
113
|
+
dismissedByLabel: "Dana Reyes",
|
|
114
|
+
}).success,
|
|
115
|
+
).toBe(false);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("a finding carries its fingerprint and a dismissal must send it back", () => {
|
|
119
|
+
const { fingerprint: _f, ...without } = finding();
|
|
120
|
+
expect(SourceSyncFindingSchema.safeParse(without).success).toBe(false);
|
|
103
121
|
expect(SourceSyncDismissFindingRequestSchema.safeParse({}).success).toBe(
|
|
104
|
-
|
|
122
|
+
false,
|
|
105
123
|
);
|
|
106
124
|
expect(
|
|
107
125
|
SourceSyncDismissFindingRequestSchema.safeParse({
|
|
108
|
-
|
|
126
|
+
expectedFingerprint: "0f3c9a",
|
|
109
127
|
}).success,
|
|
110
128
|
).toBe(false);
|
|
111
129
|
});
|
|
@@ -56,9 +56,9 @@ export const SourceSyncFindingSchema = z.object({
|
|
|
56
56
|
dismissedAt: IsoDateTime.nullable(),
|
|
57
57
|
/**
|
|
58
58
|
* What the finding says right now, as the reader saw it. A dismissal sends it
|
|
59
|
-
* back as `expectedFingerprint`.
|
|
59
|
+
* back as `expectedFingerprint`. The backend sends it on every finding.
|
|
60
60
|
*/
|
|
61
|
-
fingerprint: z.string().length(32)
|
|
61
|
+
fingerprint: z.string().length(32),
|
|
62
62
|
});
|
|
63
63
|
|
|
64
64
|
export const SourceSyncFindingsResponseSchema = z.object({
|
|
@@ -68,15 +68,15 @@ export const SourceSyncFindingsResponseSchema = z.object({
|
|
|
68
68
|
/**
|
|
69
69
|
* Dismiss one finding. Who dismissed it is the authenticated caller.
|
|
70
70
|
* `expectedFingerprint` is the finding's fingerprint as the caller rendered it:
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
71
|
+
* a finding that moved since is refused as changed. Required in the contract
|
|
72
|
+
* ahead of the backend: every typed sender must send it before the backend's
|
|
73
|
+
* own route schema requires it and dismissal becomes compare-and-set (backend
|
|
74
|
+
* decision `findings-lifecycle-is-domain-agnostic`; control PRD-01064).
|
|
75
75
|
*/
|
|
76
76
|
export const SourceSyncDismissFindingRequestSchema = z
|
|
77
77
|
.object({
|
|
78
78
|
note: z.string().max(500).optional(),
|
|
79
|
-
expectedFingerprint: z.string().length(32)
|
|
79
|
+
expectedFingerprint: z.string().length(32),
|
|
80
80
|
})
|
|
81
81
|
.strict();
|
|
82
82
|
|