@company-semantics/contracts 63.0.0 → 63.2.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": "63.0.0",
3
+ "version": "63.2.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -118,7 +118,7 @@
118
118
  "anchor-corpus:sync": "tsx scripts/anchor-corpus.ts --sync",
119
119
  "generate:spec-hash": "tsx scripts/generate-spec-hash.ts",
120
120
  "generate:spec-hash:check": "tsx scripts/generate-spec-hash.ts --check",
121
- "generate:api": "pnpm generate:api-types && pnpm generate:spec-hash && pnpm generate:openapi-routes",
121
+ "generate:api": "pnpm generate:api-types && pnpm generate:spec-hash && pnpm generate:openapi-routes && pnpm file-sizes:pin",
122
122
  "generate:api:check": "pnpm generate:api-types:check && pnpm generate:spec-hash:check && pnpm generate:openapi-routes:check",
123
123
  "generate:api-types": "openapi-typescript openapi/backend.yaml -o src/api/generated.ts",
124
124
  "generate:openapi-routes": "tsx scripts/generate-openapi-routes.ts",
@@ -181,5 +181,5 @@
181
181
  "*.md": "markdownlint-cli2",
182
182
  "package.json": "node -e \"JSON.parse(require('fs').readFileSync('package.json'))\""
183
183
  },
184
- "securityRequirementsVersion": "2dcea336317c1136c8d94798b1cadc9c115febf574d041a173974a5f7959de0b"
184
+ "securityRequirementsVersion": "b5085eaa2fcf9fee09a3c8d2a9e15eab119800def73c98dc6ab6937d1c6e07b2"
185
185
  }
@@ -1,3 +1,3 @@
1
1
  // AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
2
- export const SPEC_HASH = 'e90aa233fbf5' as const;
3
- export const SPEC_HASH_FULL = 'e90aa233fbf5c821f7a67d53e8da52152166bf1eefb029e1999fde02e8e66a4f' as const;
2
+ export const SPEC_HASH = '387045558d18' as const;
3
+ export const SPEC_HASH_FULL = '387045558d1867722a744ddb05cf5ebb7260c54df0ff272b868bd4dd2ad2a973' as const;
@@ -4373,6 +4373,11 @@ export interface components {
4373
4373
  /** @enum {string} */
4374
4374
  origin?: "user" | "proactive";
4375
4375
  launch: {
4376
+ /** @constant */
4377
+ kind: "structure-review";
4378
+ /** Format: uuid */
4379
+ proposalId: string;
4380
+ } | {
4376
4381
  /** @constant */
4377
4382
  kind: "workflow";
4378
4383
  /** Format: uuid */
@@ -6961,12 +6966,19 @@ export interface components {
6961
6966
  insertedText?: string;
6962
6967
  };
6963
6968
  body?: string;
6964
- mentions?: {
6969
+ mentions?: ({
6970
+ /** @constant */
6971
+ kind: "user";
6965
6972
  /** Format: uuid */
6966
6973
  userId: string;
6967
6974
  startOffset: number;
6968
6975
  endOffset: number;
6969
- }[];
6976
+ } | {
6977
+ /** @constant */
6978
+ kind: "agent";
6979
+ startOffset: number;
6980
+ endOffset: number;
6981
+ })[];
6970
6982
  };
6971
6983
  /** @description The written reply, and whether it auto-reopened a resolved thread. */
6972
6984
  CommentReplyResponse: {
@@ -6975,12 +6987,19 @@ export interface components {
6975
6987
  };
6976
6988
  CommentReplyRequest: {
6977
6989
  body: string;
6978
- mentions?: {
6990
+ mentions?: ({
6991
+ /** @constant */
6992
+ kind: "user";
6979
6993
  /** Format: uuid */
6980
6994
  userId: string;
6981
6995
  startOffset: number;
6982
6996
  endOffset: number;
6983
- }[];
6997
+ } | {
6998
+ /** @constant */
6999
+ kind: "agent";
7000
+ startOffset: number;
7001
+ endOffset: number;
7002
+ })[];
6984
7003
  };
6985
7004
  /** @description The comment after the mutation (redacted projection after delete). */
6986
7005
  CommentResponse: {
@@ -6988,12 +7007,19 @@ export interface components {
6988
7007
  };
6989
7008
  EditCommentRequest: {
6990
7009
  body: string;
6991
- mentions?: {
7010
+ mentions?: ({
7011
+ /** @constant */
7012
+ kind: "user";
6992
7013
  /** Format: uuid */
6993
7014
  userId: string;
6994
7015
  startOffset: number;
6995
7016
  endOffset: number;
6996
- }[];
7017
+ } | {
7018
+ /** @constant */
7019
+ kind: "agent";
7020
+ startOffset: number;
7021
+ endOffset: number;
7022
+ })[];
6997
7023
  };
6998
7024
  /** @description The thread after a resolve/reopen transition. */
6999
7025
  CommentThreadSummaryResponse: {
@@ -7026,7 +7052,14 @@ export interface components {
7026
7052
  };
7027
7053
  /** @description A principal who can read the document and may therefore be mentioned. */
7028
7054
  CompanyMdMentionableCandidate: {
7029
- userId: string;
7055
+ principal: {
7056
+ /** @constant */
7057
+ kind: "user";
7058
+ userId: string;
7059
+ } | {
7060
+ /** @constant */
7061
+ kind: "agent";
7062
+ };
7030
7063
  displayName: string;
7031
7064
  avatarUrl: string | null;
7032
7065
  };
@@ -7107,6 +7140,29 @@ export interface components {
7107
7140
  /** @enum {string} */
7108
7141
  kind: "comment" | "suggestion";
7109
7142
  suggestion: components["schemas"]["Suggestion"] | null;
7143
+ origin: {
7144
+ /** @constant */
7145
+ kind: "comment";
7146
+ } | {
7147
+ /** @constant */
7148
+ kind: "doc_mention";
7149
+ byUser: ({
7150
+ /** @constant */
7151
+ kind: "user";
7152
+ userId: string;
7153
+ } | {
7154
+ /** @constant */
7155
+ kind: "agent";
7156
+ }) | null;
7157
+ introducedAt: string;
7158
+ };
7159
+ agentOutcomes: {
7160
+ triggerId: string;
7161
+ /** @enum {string} */
7162
+ status: "pending" | "replied" | "unanswered";
7163
+ reason: ("replies_off" | "mentioner_unavailable" | "delegate_unknown" | "subject_unreadable" | "rate_capped" | "turn_failed") | null;
7164
+ replyCommentId: string | null;
7165
+ }[];
7110
7166
  createdByUserId: string | null;
7111
7167
  resolvedByUserId: string | null;
7112
7168
  resolvedAt: string | null;
@@ -7155,16 +7211,30 @@ export interface components {
7155
7211
  Comment: {
7156
7212
  id: string;
7157
7213
  threadId: string;
7158
- authorUserId: string | null;
7214
+ author: ({
7215
+ /** @constant */
7216
+ kind: "user";
7217
+ userId: string;
7218
+ } | {
7219
+ /** @constant */
7220
+ kind: "agent";
7221
+ }) | null;
7159
7222
  body: string | null;
7160
7223
  editedAt: string | null;
7161
7224
  deletedAt: string | null;
7162
7225
  createdAt: string;
7163
7226
  mentions: components["schemas"]["CommentMention"][];
7164
7227
  };
7165
- /** @description One mention on a comment: the mentioned user id, its zero-based exclusive-end UTF-16 range (null until the offset columns exist), and the display name resolved at read time. */
7228
+ /** @description One mention on a comment: the principal mentioned, its zero-based exclusive-end UTF-16 range (null until the offset columns exist), and the display name resolved at read time. */
7166
7229
  CommentMention: {
7167
- userId: string;
7230
+ target: {
7231
+ /** @constant */
7232
+ kind: "user";
7233
+ userId: string;
7234
+ } | {
7235
+ /** @constant */
7236
+ kind: "agent";
7237
+ };
7168
7238
  startOffset: number | null;
7169
7239
  endOffset: number | null;
7170
7240
  displayName: string;
@@ -7191,6 +7261,29 @@ export interface components {
7191
7261
  /** @enum {string} */
7192
7262
  kind: "comment" | "suggestion";
7193
7263
  suggestion: components["schemas"]["Suggestion"] | null;
7264
+ origin: {
7265
+ /** @constant */
7266
+ kind: "comment";
7267
+ } | {
7268
+ /** @constant */
7269
+ kind: "doc_mention";
7270
+ byUser: ({
7271
+ /** @constant */
7272
+ kind: "user";
7273
+ userId: string;
7274
+ } | {
7275
+ /** @constant */
7276
+ kind: "agent";
7277
+ }) | null;
7278
+ introducedAt: string;
7279
+ };
7280
+ agentOutcomes: {
7281
+ triggerId: string;
7282
+ /** @enum {string} */
7283
+ status: "pending" | "replied" | "unanswered";
7284
+ reason: ("replies_off" | "mentioner_unavailable" | "delegate_unknown" | "subject_unreadable" | "rate_capped" | "turn_failed") | null;
7285
+ replyCommentId: string | null;
7286
+ }[];
7194
7287
  createdByUserId: string | null;
7195
7288
  resolvedByUserId: string | null;
7196
7289
  resolvedAt: string | null;
@@ -86,6 +86,8 @@ function makeThread(over: Record<string, unknown> = {}) {
86
86
  canReopen: false,
87
87
  canDecideSuggestion: false,
88
88
  canWithdraw: false,
89
+ canRevertSuggestion: false,
90
+ canDeleteThread: false,
89
91
  },
90
92
  ...over,
91
93
  };
@@ -34,10 +34,36 @@ describe("the vocabulary tuples", () => {
34
34
  expect([...SUGGESTION_OPS]).toEqual(["insert", "delete", "replace"]);
35
35
  });
36
36
 
37
- it("SUGGESTION_STATUSES is exactly open, accepted and rejected", () => {
37
+ it("SUGGESTION_STATUSES is exactly open, accepted, rejected and reverted", () => {
38
38
  // The SECOND status level. The thread-level enum stays open|resolved —
39
39
  // `./schemas.test.ts` pins that separately — and the decision lives here.
40
- expect([...SUGGESTION_STATUSES]).toEqual(["open", "accepted", "rejected"]);
40
+ //
41
+ // PINNED AS A LIST, NOT A LENGTH, because widening it is the one change on
42
+ // this surface that cannot be walked back: a published `z.enum` refuses a
43
+ // member it has not shipped, and this one is nested inside the thread
44
+ // summary, so an unfamiliar status costs a client the whole document's
45
+ // comments. Failing here is the reminder that adding a member is a
46
+ // coordinated release — readers first, emitter second.
47
+ expect([...SUGGESTION_STATUSES]).toEqual([
48
+ "open",
49
+ "accepted",
50
+ "rejected",
51
+ "reverted",
52
+ ]);
53
+ });
54
+
55
+ it("accepts a reverted payload", () => {
56
+ // The state a taken-back decision lands in. Parsed here so the widening is
57
+ // proven at the schema, not just in the tuple above.
58
+ expect(
59
+ SuggestionSchema.safeParse(
60
+ makeSuggestion({
61
+ op: "replace",
62
+ insertedText: "x",
63
+ status: "reverted",
64
+ }),
65
+ ).success,
66
+ ).toBe(true);
41
67
  });
42
68
  });
43
69
 
@@ -355,6 +355,32 @@ export const ThreadCapabilitiesSchema = z.object({
355
355
  * their own suggestion.
356
356
  */
357
357
  canWithdraw: z.boolean(),
358
+ /**
359
+ * Take back a decision on a DECIDED suggestion — the complement of
360
+ * `canDecideSuggestion`, exactly as `canReopen` is the complement of
361
+ * `canResolve` one kind over.
362
+ *
363
+ * The edit band, like deciding: undoing an acceptance puts the replaced
364
+ * passage back into the document, which is the same authority as putting the
365
+ * proposal in. A reverted suggestion offers it no longer — `reverted` is
366
+ * terminal, so this is false there as well as on an open one.
367
+ */
368
+ canRevertSuggestion: z.boolean(),
369
+ /**
370
+ * Delete the whole thread — the conversation, not one comment in it.
371
+ *
372
+ * TRUE FOR BOTH KINDS, and it is the only capability here that does not
373
+ * mention one. A comment thread could previously only be dismantled by
374
+ * deleting its first comment, which cascaded; a suggestion thread could not be
375
+ * deleted at all, because its proposal is not a comment and there was nothing
376
+ * to aim the cascade at. One flag answers both.
377
+ *
378
+ * IT SAYS NOTHING ABOUT THE DOCUMENT. Deleting a thread whose suggestion was
379
+ * accepted leaves the accepted words where they are: they are the document's
380
+ * now, and taking them back is `canRevertSuggestion`'s act, deliberately kept
381
+ * separate so neither one silently performs the other.
382
+ */
383
+ canDeleteThread: z.boolean(),
358
384
  });
359
385
  export type ThreadCapabilities = z.infer<typeof ThreadCapabilitiesSchema>;
360
386
 
@@ -37,14 +37,43 @@ export const SUGGESTION_OPS = ["insert", "delete", "replace"] as const;
37
37
  export type SuggestionOp = (typeof SUGGESTION_OPS)[number];
38
38
 
39
39
  /**
40
- * open -> accepted | rejected. Terminal states are terminal: there is no
41
- * reopen for a decided suggestion (unlike comment resolve/reopen). This is
42
- * the SECOND status level — the thread-level `status` enum stays exactly
43
- * `open | resolved`, and a decided suggestion arrives as thread status
44
- * `resolved` with this field carrying which way it went. Widening the closed
45
- * thread enum instead would be a parse outage for every deployed client.
40
+ * `open -> accepted | rejected -> reverted`. This is the SECOND status level —
41
+ * the thread-level `status` enum stays exactly `open | resolved`, and a decided
42
+ * suggestion arrives as thread status `resolved` with this field carrying which
43
+ * way it went. Widening the closed thread enum instead would be a parse outage
44
+ * for every deployed client.
45
+ *
46
+ * A DECISION IS NO LONGER THE LAST WORD, BUT IT IS STILL ONE-WAY. This tuple
47
+ * read `open | accepted | rejected` with "terminal states are terminal: there is
48
+ * no reopen for a decided suggestion" written beside it, and that is now false:
49
+ * people are allowed to change their minds, and undoing an acceptance restores
50
+ * the passage the edit replaced. What has NOT changed is that the arrow never
51
+ * points back to `open`. `reverted` is its own terminal state rather than a
52
+ * return to the start, for two reasons — a reverted proposal is a decision
53
+ * somebody took and then took back, which is a different fact from one nobody
54
+ * has answered yet; and an `open` suggestion whose document still carries an
55
+ * application receipt is exactly the shape the server's reconciler treats as
56
+ * proof of an unacknowledged accept, so returning one to `open` would have it
57
+ * silently re-accepted by background maintenance.
58
+ *
59
+ * WIDENING THIS TUPLE IS ORDERED, NOT FREE. `SuggestionSchema` below is a
60
+ * `z.strictObject` nested inside `CommentThreadSummarySchema`, so for anyone who
61
+ * PARSES a thread list, one row carrying a status they do not know fails the
62
+ * whole list — every comment on that document, not just the odd suggestion — and
63
+ * it fails the first time somebody reverts rather than at deploy. No consumer in
64
+ * this workspace parses these at runtime today (the app is typed off the
65
+ * generated spec; the backend validates against its own local copies), which was
66
+ * checked rather than assumed, so this package leads the release and the emitter
67
+ * follows. The next person to add a member owes that check again for their own
68
+ * consumers — which is why `__tests__/suggestion.test.ts` pins this list
69
+ * exactly. See the ADR with slug `suggestion-revert-vocabulary`.
46
70
  */
47
- export const SUGGESTION_STATUSES = ["open", "accepted", "rejected"] as const;
71
+ export const SUGGESTION_STATUSES = [
72
+ "open",
73
+ "accepted",
74
+ "rejected",
75
+ "reverted",
76
+ ] as const;
48
77
  export type SuggestionStatus = (typeof SUGGESTION_STATUSES)[number];
49
78
 
50
79
  /**