@company-semantics/contracts 92.0.0 → 93.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.
Files changed (68) hide show
  1. package/package.json +13 -12
  2. package/src/__tests__/resource-keys.test.ts +1 -0
  3. package/src/action-items/README.md +12 -4
  4. package/src/action-items/schemas.ts +10 -0
  5. package/src/affordances/README.md +52 -1
  6. package/src/affordances/__tests__/README.md +10 -1
  7. package/src/affordances/__tests__/affordances.test.ts +13 -1
  8. package/src/affordances/__tests__/catalog-wave.test.ts +237 -0
  9. package/src/affordances/affordance.ts +56 -4
  10. package/src/affordances/args.ts +44 -1
  11. package/src/affordances/available.ts +59 -0
  12. package/src/affordances/coverage.ts +51 -0
  13. package/src/affordances/ids.ts +28 -0
  14. package/src/affordances/index.ts +17 -0
  15. package/src/api/README.md +1 -0
  16. package/src/api/generated-spec-hash.ts +2 -2
  17. package/src/api/generated.ts +506 -1
  18. package/src/api/primitives.ts +13 -0
  19. package/src/billing/README.md +1 -0
  20. package/src/billing/refusals.ts +12 -0
  21. package/src/chat/__tests__/page-context.test.ts +22 -43
  22. package/src/chat/__tests__/runtime-profile.test.ts +20 -44
  23. package/src/generated/openapi-routes.ts +1 -0
  24. package/src/message-parts/README.md +5 -0
  25. package/src/message-parts/affordance-offer.ts +44 -0
  26. package/src/message-parts/index.ts +7 -0
  27. package/src/message-parts/types.ts +7 -1
  28. package/src/message-parts/wire.ts +18 -0
  29. package/src/notifications/README.md +17 -3
  30. package/src/notifications/__tests__/README.md +4 -0
  31. package/src/notifications/__tests__/__snapshots__/render-snapshot.test.ts.snap +27 -25
  32. package/src/notifications/__tests__/fixtures.ts +7 -1
  33. package/src/notifications/__tests__/grounded-ctas.test.ts +103 -0
  34. package/src/notifications/__tests__/render-snapshot.test.ts +1 -1
  35. package/src/notifications/content.ts +9 -0
  36. package/src/notifications/kinds/billing-auto-recharge-failed.ts +4 -2
  37. package/src/notifications/kinds/billing-payment-past-due.ts +2 -0
  38. package/src/notifications/kinds/billing-suspended.ts +2 -0
  39. package/src/notifications/kinds/chat-shared.ts +10 -3
  40. package/src/notifications/kinds/company-md-access-approved.ts +13 -6
  41. package/src/notifications/kinds/share-granted.ts +1 -1
  42. package/src/notifications/kinds/unit-owner-granted.ts +9 -2
  43. package/src/notifications/payloads.ts +6 -0
  44. package/src/proactive/README.md +18 -2
  45. package/src/proactive/__tests__/README.md +11 -1
  46. package/src/proactive/__tests__/chat-templates.test.ts +7 -6
  47. package/src/proactive/__tests__/proactive-offers.test.ts +124 -0
  48. package/src/proactive/__tests__/vocabulary.test.ts +1 -1
  49. package/src/proactive/composer.ts +11 -0
  50. package/src/proactive/templates/README.md +11 -3
  51. package/src/proactive/templates/billing-auto-recharge-failed.ts +1 -0
  52. package/src/proactive/templates/billing-balance-exhausted.ts +2 -6
  53. package/src/proactive/templates/billing-payment-past-due.ts +1 -0
  54. package/src/proactive/templates/billing-subscription-canceled.ts +1 -5
  55. package/src/proactive/templates/billing-suspended.ts +1 -0
  56. package/src/proactive/templates/morning-brief.ts +1 -0
  57. package/src/proactive/templates/org-became-shared.ts +4 -3
  58. package/src/proactive/templates/source-sync-review-ready.ts +3 -8
  59. package/src/proactive/templates/workflow-needs-attention.ts +1 -0
  60. package/src/rate-limit/README.md +27 -1
  61. package/src/rate-limit/__tests__/README.md +23 -0
  62. package/src/rate-limit/__tests__/rate-limited-error.test.ts +53 -0
  63. package/src/rate-limit/index.ts +33 -0
  64. package/src/resource-key-tables.ts +1 -0
  65. package/src/resource-key-types.ts +3 -0
  66. package/src/resource-keys.ts +1 -0
  67. package/src/root-exports/agent-runtime.ts +14 -2
  68. package/src/root-exports/delivery.ts +15 -0
@@ -53,5 +53,6 @@ export const billingPaymentPastDueComposer: ProactiveChatComposer<"billingPaymen
53
53
  "What happens to my workspace if this payment stays unresolved?",
54
54
  },
55
55
  ],
56
+ affordances: [{ id: "billing.open" }],
56
57
  }),
57
58
  };
@@ -47,11 +47,7 @@ export const billingSubscriptionCanceledComposer: ProactiveChatComposer<"billing
47
47
  prompt:
48
48
  "What happens to the AI funds already in my wallet after cancellation?",
49
49
  },
50
- {
51
- id: "resubscribe",
52
- label: "How do I resubscribe?",
53
- prompt: "How do I start the seat subscription again?",
54
- },
55
50
  ],
51
+ affordances: [{ id: "billing.open" }],
56
52
  }),
57
53
  };
@@ -49,5 +49,6 @@ export const billingSuspendedComposer: ProactiveChatComposer<"billingSuspended">
49
49
  "Is any of my workspace data at risk while this is suspended?",
50
50
  },
51
51
  ],
52
+ affordances: [{ id: "billing.open" }],
52
53
  }),
53
54
  };
@@ -73,5 +73,6 @@ export const morningBriefComposer: ProactiveChatComposer<"morningBrief"> = {
73
73
  prompt: "What needs a decision from me today?",
74
74
  },
75
75
  ],
76
+ affordances: [],
76
77
  }),
77
78
  };
@@ -74,9 +74,9 @@ export const orgBecameSharedComposer: ProactiveChatComposer<"orgBecameShared"> =
74
74
  text:
75
75
  `${openingFor(facts)}\n\n` +
76
76
  `This is a relocation, not an addition: Integrations, Invite team ` +
77
- `members, AI usage, Spending controls and Billing have moved out of My ` +
78
- `settings and into Org settings. Nothing was removed — the five sections now ` +
79
- `belong to the workspace rather than to you.`,
77
+ `members, AI usage, Spending controls and Billing have moved from your ` +
78
+ `own area to the workspace's. Nothing was removed — the five sections ` +
79
+ `now belong to the workspace rather than to you.`,
80
80
  replies: [
81
81
  {
82
82
  id: "plain-english",
@@ -96,5 +96,6 @@ export const orgBecameSharedComposer: ProactiveChatComposer<"orgBecameShared"> =
96
96
  prompt: "Who can see what in my workspace now that it is shared?",
97
97
  },
98
98
  ],
99
+ affordances: [{ id: "workspace.openOrgSettings" }],
99
100
  }),
100
101
  };
@@ -36,17 +36,11 @@ export const sourceSyncReviewReadyComposer: ProactiveChatComposer<"sourceSyncRev
36
36
  `strategy documents and have proposed changes for you to ` +
37
37
  `review.\n\n` +
38
38
  `Nothing has been changed. Each document is updated only when ` +
39
- `you say so, as an ordinary version you can restore, and ` +
39
+ `you say so, as an ordinary version that can be restored, and ` +
40
40
  `anything a person has edited is left alone unless you choose ` +
41
41
  `otherwise.`) +
42
- `\n\nOpen the review when you have a few minutes. If you leave it, ` +
43
- `nothing happens.`,
42
+ `\n\nThe review waits for you. If you leave it, nothing happens.`,
44
43
  replies: [
45
- {
46
- id: "open-review",
47
- label: "Open the review",
48
- prompt: "Open the source review.",
49
- },
50
44
  {
51
45
  id: "what-was-found",
52
46
  label: "What did you find?",
@@ -60,5 +54,6 @@ export const sourceSyncReviewReadyComposer: ProactiveChatComposer<"sourceSyncRev
60
54
  "If I attach a source to a document, who will be able to see it?",
61
55
  },
62
56
  ],
57
+ affordances: [],
63
58
  }),
64
59
  };
@@ -30,6 +30,7 @@ export const workflowNeedsAttentionComposer: ProactiveChatComposer<"workflowNeed
30
30
  title: TITLE_OF_WORKFLOW_TYPE[facts.outcome.outcome.workflowType],
31
31
  text: facts.narration,
32
32
  replies: [],
33
+ affordances: [],
33
34
  outcome: facts.outcome,
34
35
  }),
35
36
  };
@@ -35,6 +35,29 @@ interface RateLimitResult {
35
35
  }
36
36
  ```
37
37
 
38
+ ### RateLimitedErrorResponseSchema
39
+
40
+ The 429 body every rate-limited response carries — exactly what backend's
41
+ shared limiter sends, plus the reset fact:
42
+
43
+ ```typescript
44
+ {
45
+ error: "RATE_LIMIT_EXCEEDED"; // RATE_LIMITED_ERROR_CODE
46
+ scope?: "email" | "ip" | "global";
47
+ message: string;
48
+ retryAfterSeconds: number | null; // whole seconds until the window resets
49
+ }
50
+ ```
51
+
52
+ The object is strict and `retryAfterSeconds` is REQUIRED: it mirrors the
53
+ `Retry-After` header, or is null when the limiter cannot say.
54
+
55
+ **Facts only.** `retryAfterSeconds` is a fact, so the message states it
56
+ ("Too many requests. Available in 30s.") and never tells the reader to retry.
57
+ A retry control, if a surface has one, is that surface's own control label
58
+ (control ADR affordance-system). Distinct from `ShedResponse` in
59
+ `src/pressure.ts`, which is the load-shed body only.
60
+
38
61
  ## Semantic Convention
39
62
 
40
63
  This domain uses `allowed: boolean` where `true` means the request is permitted.
@@ -54,9 +77,12 @@ This is the canonical semantic across all Company Semantics repos.
54
77
 
55
78
  ## Public API
56
79
 
80
+ - `RATE_LIMITED_ERROR_CODE`
57
81
  - `RateLimitConfig` _(type)_
58
82
  - `RateLimitResult` _(type)_
59
83
  - `RateLimitTier`
84
+ - `RateLimitedErrorResponse` _(type)_
85
+ - `RateLimitedErrorResponseSchema`
60
86
 
61
87
  <!-- END GENERATED: readme-public-api -->
62
88
 
@@ -70,6 +96,6 @@ _None._
70
96
 
71
97
  **External packages:**
72
98
 
73
- _None._
99
+ - `zod`
74
100
 
75
101
  <!-- END GENERATED: readme-dependencies -->
@@ -0,0 +1,23 @@
1
+ # rate-limit/\_\_tests\_\_/
2
+
3
+ ## Purpose
4
+
5
+ Locks the shape of the rate-limited error envelope.
6
+
7
+ - `rate-limited-error.test.ts` — proves a rate-limited body carries
8
+ `retryAfterSeconds` as whole non-negative seconds or null and is refused
9
+ without it, refuses any code other than `RATE_LIMIT_EXCEEDED`, and that the
10
+ schema and code are reachable from the package root as the same binding.
11
+
12
+ ## Invariants
13
+
14
+ - These assert VOCABULARY and SHAPE, never limiter behavior. Window sizes and
15
+ which routes are limited belong to backend.
16
+
17
+ ## Public API
18
+
19
+ None — test-only.
20
+
21
+ ## Dependencies
22
+
23
+ `vitest`, `../index` and the package root.
@@ -0,0 +1,53 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import * as root from "../../index";
4
+ import {
5
+ RATE_LIMITED_ERROR_CODE,
6
+ RateLimitedErrorResponseSchema,
7
+ } from "../index";
8
+
9
+ const body = (fields: Record<string, unknown>) => ({
10
+ error: "RATE_LIMIT_EXCEEDED",
11
+ message: "Too many requests.",
12
+ ...fields,
13
+ });
14
+
15
+ describe("the rate-limited error envelope", () => {
16
+ it("a rate-limited body states when the window resets, or null", () => {
17
+ expect(
18
+ RateLimitedErrorResponseSchema.safeParse(body({ retryAfterSeconds: 30 }))
19
+ .success,
20
+ ).toBe(true);
21
+ expect(
22
+ RateLimitedErrorResponseSchema.safeParse(
23
+ body({ retryAfterSeconds: null }),
24
+ ).success,
25
+ ).toBe(true);
26
+ });
27
+
28
+ it("refuses a rate-limited body without retryAfterSeconds", () => {
29
+ expect(RateLimitedErrorResponseSchema.safeParse(body({})).success).toBe(
30
+ false,
31
+ );
32
+ expect(
33
+ RateLimitedErrorResponseSchema.safeParse(body({ retryAfterSeconds: -1 }))
34
+ .success,
35
+ ).toBe(false);
36
+ });
37
+
38
+ it("refuses any other code", () => {
39
+ expect(
40
+ RateLimitedErrorResponseSchema.safeParse(
41
+ body({ error: "FORBIDDEN", retryAfterSeconds: 3 }),
42
+ ).success,
43
+ ).toBe(false);
44
+ expect(RATE_LIMITED_ERROR_CODE).toBe("RATE_LIMIT_EXCEEDED");
45
+ });
46
+
47
+ it("is reachable from the package root", () => {
48
+ expect(root.RateLimitedErrorResponseSchema).toBe(
49
+ RateLimitedErrorResponseSchema,
50
+ );
51
+ expect(root.RATE_LIMITED_ERROR_CODE).toBe(RATE_LIMITED_ERROR_CODE);
52
+ });
53
+ });
@@ -1,3 +1,5 @@
1
+ import { z } from "zod";
2
+
1
3
  /**
2
4
  * Rate limiting vocabulary types.
3
5
  * @see PRD-00012 for foundational implementation
@@ -21,3 +23,34 @@ export interface RateLimitResult {
21
23
  remaining: number;
22
24
  resetAt: number;
23
25
  }
26
+
27
+ // =============================================================================
28
+ // The rate-limited error envelope (owner ruling 2026-10-06: retry prose is
29
+ // FACTS ONLY; control ADR affordance-system)
30
+ // =============================================================================
31
+
32
+ /** The error code every rate-limited response carries. */
33
+ export const RATE_LIMITED_ERROR_CODE = "RATE_LIMIT_EXCEEDED" as const;
34
+
35
+ /**
36
+ * A 429 body, exactly as backend's shared limiter sends it
37
+ * (`{ error: 'RATE_LIMIT_EXCEEDED', scope?, message }`), plus the one fact a
38
+ * client needs to say when the window resets.
39
+ *
40
+ * `retryAfterSeconds` mirrors the `Retry-After` header: whole seconds until the
41
+ * window resets, or null when the limiter cannot say. It is a FACT, so the
42
+ * message states it ("Too many requests. Available in 30s.") and never
43
+ * tells the reader to retry; a retry control, if a surface has one, is that
44
+ * surface's own control label.
45
+ */
46
+ export const RateLimitedErrorResponseSchema = z
47
+ .object({
48
+ error: z.literal(RATE_LIMITED_ERROR_CODE),
49
+ scope: z.enum(["email", "ip", "global"]).optional(),
50
+ message: z.string(),
51
+ retryAfterSeconds: z.number().int().nonnegative().nullable(),
52
+ })
53
+ .strict();
54
+ export type RateLimitedErrorResponse = z.infer<
55
+ typeof RateLimitedErrorResponseSchema
56
+ >;
@@ -161,6 +161,7 @@ export const SYSTEM_SCOPED_TYPES = [
161
161
  "factoryFloor",
162
162
  "factorySnapshot",
163
163
  "factoryKpis",
164
+ "factoryLineage",
164
165
  ] as const satisfies readonly SystemScopedType[];
165
166
 
166
167
  /** Every literal `fromQueryKey` can route, across all five tables. */
@@ -157,6 +157,9 @@ export type ResourceKey =
157
157
  // `useResource` disables a query whose `toQueryKey` throws, the panel it backs
158
158
  // never fetched at all. An unregistered key is not a stale read, it is no read.
159
159
  | { type: "factoryKpis"; scope: "system" }
160
+ // Change lineage over the release ledger (ADR-CONTRACTS-222, backend
161
+ // ADR-BE-926): each release with its commits, PRDs, workers, run and health.
162
+ | { type: "factoryLineage"; scope: "system" }
160
163
  // Everything awaiting the viewer's decision, across every domain
161
164
  // (ADR-CONT-104). ORG-scoped, not user-scoped, even though the content is
162
165
  // per-viewer: the bucket differs per org, so a user-keyed entry would serve
@@ -137,6 +137,7 @@ export function toQueryKey(key: ResourceKey): readonly string[] {
137
137
  case "factoryFloor":
138
138
  case "factorySnapshot":
139
139
  case "factoryKpis":
140
+ case "factoryLineage":
140
141
  return [key.type, key.scope] as const;
141
142
 
142
143
  default: {
@@ -97,6 +97,10 @@ export type {
97
97
  WorkflowOutcomeData,
98
98
  WorkflowOutcomePart,
99
99
  WorkflowOutcomeDataPart,
100
+ // Affordance offer types (non-governed, the frozen offer on a pushed chat, control ADR affordance-system)
101
+ AffordanceOfferData,
102
+ AffordanceOfferPart,
103
+ AffordanceOfferDataPart,
100
104
  // Execution result types (Phase 5)
101
105
  ExecutionArtifactStatus,
102
106
  ExecutionResultSummary,
@@ -213,5 +217,13 @@ export type {
213
217
 
214
218
  // Rate limit domain types
215
219
  // @see PRD-00013 for contracts integration
216
- export type { RateLimitConfig, RateLimitResult } from "../rate-limit/index";
217
- export { RateLimitTier } from "../rate-limit/index";
220
+ export type {
221
+ RateLimitConfig,
222
+ RateLimitResult,
223
+ RateLimitedErrorResponse,
224
+ } from "../rate-limit/index";
225
+ export {
226
+ RATE_LIMITED_ERROR_CODE,
227
+ RateLimitTier,
228
+ RateLimitedErrorResponseSchema,
229
+ } from "../rate-limit/index";
@@ -299,13 +299,24 @@ export {
299
299
  AFFORDANCE_UNAVAILABLE_REASONS,
300
300
  AffordanceAvailabilityIdSchema,
301
301
  AffordanceAvailabilitySchema,
302
+ AffordanceCoverageSchema,
302
303
  AffordanceIdSchema,
303
304
  AffordanceSchema,
305
+ AvailableAffordanceSchema,
306
+ AvailableAffordancesSchema,
307
+ ChatOpenArgsSchema,
308
+ CompanyMdDocArgsSchema,
309
+ ExecutionOpenArgsSchema,
304
310
  MAX_OFFERED_AFFORDANCES,
311
+ NO_AFFORDANCE_REASONS,
305
312
  NO_ARG_AFFORDANCE_IDS,
313
+ NoAffordanceReasonSchema,
306
314
  OfferedAffordanceSchema,
315
+ OrgStructureOpenReviewArgsSchema,
307
316
  PROJECTED_AFFORDANCE_IDS,
308
317
  ReviewOpenArgsSchema,
318
+ SourceSyncOpenUpdateArgsSchema,
319
+ TeamOpenArgsSchema,
309
320
  sameAffordances,
310
321
  } from "../affordances/index";
311
322
 
@@ -315,8 +326,12 @@ export type {
315
326
  AffordanceAvailability,
316
327
  AffordanceAvailabilityId,
317
328
  AffordanceBindingKind,
329
+ AffordanceCoverage,
318
330
  AffordanceId,
319
331
  AffordanceUnavailableReason,
332
+ AvailableAffordance,
333
+ AvailableAffordances,
334
+ NoAffordanceReason,
320
335
  OfferedAffordance,
321
336
  ProjectedAffordanceId,
322
337
  } from "../affordances/index";