@company-semantics/contracts 14.0.0 → 15.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@company-semantics/contracts",
3
- "version": "14.0.0",
3
+ "version": "15.1.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -119,7 +119,7 @@
119
119
  "zod": "^4.4.3"
120
120
  },
121
121
  "devDependencies": {
122
- "@types/node": "^25.9.3",
122
+ "@types/node": "^22.10.0",
123
123
  "husky": "^9.1.7",
124
124
  "lint-staged": "^17.0.7",
125
125
  "markdownlint-cli2": "^0.22.1",
@@ -1,3 +1,3 @@
1
1
  // AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
2
- export const SPEC_HASH = '16c3b86cc35a' as const;
3
- export const SPEC_HASH_FULL = '16c3b86cc35a3f329766b702414438de4950430991a603372f1335206c96b600' as const;
2
+ export const SPEC_HASH = '575c0e0c7f6c' as const;
3
+ export const SPEC_HASH_FULL = '575c0e0c7f6c7fdec000c8914101c527ed9e783b38bbfbe5aab536200d1b1da1' as const;
@@ -736,6 +736,23 @@ export interface paths {
736
736
  patch: operations["setMemberManager"];
737
737
  trace?: never;
738
738
  };
739
+ "/api/chat/interactive-tasks/submit": {
740
+ parameters: {
741
+ query?: never;
742
+ header?: never;
743
+ path?: never;
744
+ cookie?: never;
745
+ };
746
+ get?: never;
747
+ put?: never;
748
+ /** Submit a filled-in interactive chat task (creates + runs a governed execution) */
749
+ post: operations["submitInteractiveTask"];
750
+ delete?: never;
751
+ options?: never;
752
+ head?: never;
753
+ patch?: never;
754
+ trace?: never;
755
+ };
739
756
  "/api/rbac/roles": {
740
757
  parameters: {
741
758
  query?: never;
@@ -3650,6 +3667,21 @@ export interface components {
3650
3667
  /** Format: uuid */
3651
3668
  managerUserId: string;
3652
3669
  };
3670
+ SubmitInteractiveTaskResponse: {
3671
+ executionId: string;
3672
+ /** @enum {string} */
3673
+ status: "executing" | "completed" | "failed" | "pending_confirmation";
3674
+ };
3675
+ SubmitInteractiveTaskRequest: {
3676
+ /** Format: uuid */
3677
+ taskId: string;
3678
+ /** @constant */
3679
+ taskKind: "member.changeManager";
3680
+ /** Format: uuid */
3681
+ memberId: string;
3682
+ newManagerUserId: string | null;
3683
+ chatId: string;
3684
+ };
3653
3685
  RoleCatalogResponse: {
3654
3686
  roles: {
3655
3687
  name: string;
@@ -3920,7 +3952,7 @@ export interface components {
3920
3952
  summary: {
3921
3953
  executionId: string;
3922
3954
  /** @enum {string} */
3923
- kind: "integration.connect" | "integration.disconnect" | "profile.update" | "slack.send" | "data.ingest" | "data.scope" | "system.cleanup";
3955
+ kind: "integration.connect" | "integration.disconnect" | "profile.update" | "slack.send" | "data.ingest" | "data.scope" | "system.cleanup" | "member.changeManager";
3924
3956
  target: {
3925
3957
  /** @constant */
3926
3958
  type: "slack";
@@ -6536,6 +6568,30 @@ export interface operations {
6536
6568
  };
6537
6569
  };
6538
6570
  };
6571
+ submitInteractiveTask: {
6572
+ parameters: {
6573
+ query?: never;
6574
+ header?: never;
6575
+ path?: never;
6576
+ cookie?: never;
6577
+ };
6578
+ requestBody: {
6579
+ content: {
6580
+ "application/json": components["schemas"]["SubmitInteractiveTaskRequest"];
6581
+ };
6582
+ };
6583
+ responses: {
6584
+ /** @description Execution created and run through the governance engine */
6585
+ 200: {
6586
+ headers: {
6587
+ [name: string]: unknown;
6588
+ };
6589
+ content: {
6590
+ "application/json": components["schemas"]["SubmitInteractiveTaskResponse"];
6591
+ };
6592
+ };
6593
+ };
6594
+ };
6539
6595
  getRbacRoles: {
6540
6596
  parameters: {
6541
6597
  query?: never;
@@ -7073,7 +7129,7 @@ export interface operations {
7073
7129
  query?: {
7074
7130
  cursor?: string;
7075
7131
  limit?: number;
7076
- kind?: "integration.connect" | "integration.disconnect" | "profile.update" | "slack.send" | "data.ingest" | "data.scope" | "system.cleanup";
7132
+ kind?: "integration.connect" | "integration.disconnect" | "profile.update" | "slack.send" | "data.ingest" | "data.scope" | "system.cleanup" | "member.changeManager";
7077
7133
  targetType?: string;
7078
7134
  periodStart?: string;
7079
7135
  periodEnd?: string;
@@ -7208,7 +7264,7 @@ export interface operations {
7208
7264
  query?: {
7209
7265
  limit?: number;
7210
7266
  offset?: number;
7211
- kind?: "integration.connect" | "integration.disconnect" | "profile.update" | "slack.send" | "data.ingest" | "data.scope" | "system.cleanup";
7267
+ kind?: "integration.connect" | "integration.disconnect" | "profile.update" | "slack.send" | "data.ingest" | "data.scope" | "system.cleanup" | "member.changeManager";
7212
7268
  kindPrefix?: string;
7213
7269
  };
7214
7270
  header?: never;
@@ -70,7 +70,7 @@ export interface EmailPayloads {
70
70
  /** Name of the org unit / team the access applies to */
71
71
  unitName: string;
72
72
  /** Human-readable role label the recipient was given */
73
- roleLabel: "Leader" | "Delegate";
73
+ roleLabel: "Unit owner" | "Delegate";
74
74
  /** Full URL to view the org unit in the app */
75
75
  ctaUrl: string;
76
76
  /** Optional message from the granter, shown in the email and recorded with the grant */
@@ -156,6 +156,28 @@ describe("EXECUTION_KINDS golden snapshot", () => {
156
156
  templateId: "system-cleanup",
157
157
  },
158
158
  },
159
+ "member.changeManager": {
160
+ kind: "member.changeManager",
161
+ domain: "organization",
162
+ display: {
163
+ label: "Change Reporting Manager",
164
+ pastTenseLabel: "Reporting manager changed",
165
+ icon: "pencil",
166
+ },
167
+ governance: {
168
+ visibility: "user",
169
+ requiresAdmin: false,
170
+ reversibleBy: "member.changeManager",
171
+ },
172
+ ui: {
173
+ showInAdmin: false,
174
+ showInTimeline: true,
175
+ confirmBeforeRun: true,
176
+ },
177
+ explanation: {
178
+ templateId: "member-change-manager",
179
+ },
180
+ },
159
181
  });
160
182
  });
161
183
  });
@@ -29,4 +29,5 @@ export type ExecutionKind =
29
29
  | "slack.send"
30
30
  | "data.ingest"
31
31
  | "data.scope"
32
- | "system.cleanup";
32
+ | "system.cleanup"
33
+ | "member.changeManager";
@@ -181,6 +181,29 @@ export const EXECUTION_KINDS = {
181
181
  templateId: "system-cleanup",
182
182
  },
183
183
  },
184
+ "member.changeManager": {
185
+ kind: "member.changeManager",
186
+ domain: "organization",
187
+ display: {
188
+ label: "Change Reporting Manager",
189
+ pastTenseLabel: "Reporting manager changed",
190
+ icon: "pencil",
191
+ },
192
+ governance: {
193
+ visibility: "user",
194
+ requiresAdmin: false,
195
+ // Reversible: re-applying the previous solid-line edge undoes the change.
196
+ reversibleBy: "member.changeManager",
197
+ },
198
+ ui: {
199
+ showInAdmin: false,
200
+ showInTimeline: true,
201
+ confirmBeforeRun: true,
202
+ },
203
+ explanation: {
204
+ templateId: "member-change-manager",
205
+ },
206
+ },
184
207
  } as const satisfies Record<ExecutionKind, ExecutionKindDefinition>;
185
208
 
186
209
  // =============================================================================
@@ -38,7 +38,8 @@ export type ExecutionDomain =
38
38
  | "data"
39
39
  | "system"
40
40
  | "profile"
41
- | "communication";
41
+ | "communication"
42
+ | "organization";
42
43
 
43
44
  // =============================================================================
44
45
  // Execution Kind Definition
@@ -11,6 +11,7 @@ export const openApiRoutes = {
11
11
  '/api/account/sessions/{sessionId}': ['DELETE'],
12
12
  '/api/auth/consent/grant': ['POST'],
13
13
  '/api/capabilities': ['GET'],
14
+ '/api/chat/interactive-tasks/submit': ['POST'],
14
15
  '/api/chats': ['GET', 'POST'],
15
16
  '/api/chats/by-interaction/{interactionId}': ['GET'],
16
17
  '/api/chats/events': ['GET'],
package/src/index.ts CHANGED
@@ -454,6 +454,15 @@ export type {
454
454
  ConfirmationData,
455
455
  ConfirmationResponseData,
456
456
  ConfirmationDataPart,
457
+ // Interactive task surface types (editable surfaces)
458
+ InteractiveTaskKind,
459
+ CandidateSource,
460
+ InteractiveFieldType,
461
+ InteractiveFieldDescriptor,
462
+ InteractiveTaskSubject,
463
+ InteractiveTaskData,
464
+ InteractiveTaskPart,
465
+ InteractiveTaskDataPart,
457
466
  // Execution result types (Phase 5)
458
467
  ExecutionArtifactStatus,
459
468
  ExecutionResultSummary,
@@ -119,6 +119,7 @@ describe("CONFIRMATION_LABELS", () => {
119
119
  "data.ingest": "Import Channel",
120
120
  "data.scope": "Update Scope",
121
121
  "system.cleanup": "Cleanup Connections",
122
+ "member.changeManager": "Change Reporting Manager",
122
123
  });
123
124
  });
124
125
  });
@@ -47,6 +47,7 @@ export const CONFIRMATION_LABELS: Record<ExecutionKind, string> = {
47
47
  "data.ingest": "Import Channel",
48
48
  "data.scope": "Update Scope",
49
49
  "system.cleanup": "Cleanup Connections",
50
+ "member.changeManager": "Change Reporting Manager",
50
51
  };
51
52
 
52
53
  /**
@@ -43,6 +43,18 @@ export type {
43
43
  // Confirmation runtime values
44
44
  export { CONFIRMATION_LABELS, getConfirmationLabel } from "./confirmation";
45
45
 
46
+ // Interactive task surface types
47
+ export type {
48
+ InteractiveTaskKind,
49
+ CandidateSource,
50
+ InteractiveFieldType,
51
+ InteractiveFieldDescriptor,
52
+ InteractiveTaskSubject,
53
+ InteractiveTaskData,
54
+ InteractiveTaskPart,
55
+ InteractiveTaskDataPart,
56
+ } from "./interactive";
57
+
46
58
  // Execution result types
47
59
  export type {
48
60
  ExecutionArtifactStatus,
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Interactive Task Surface Types
3
+ *
4
+ * Interactive surfaces are EDITABLE: the assistant emits a form the user fills
5
+ * out inline in chat (e.g. picking a new reporting manager), then submits. They
6
+ * are the third surface class alongside `preview` (read-only diff) and
7
+ * `confirmation` (Y/N). Unlike those, an interactive task is PRE-EXECUTION:
8
+ *
9
+ * INTERACTIVE TASK INVARIANTS:
10
+ * - An interactive task carries NO executionId — no execution exists yet. That
11
+ * absence is the structural marker distinguishing it from ConfirmationPart
12
+ * (which requires an executionId). The execution is born when the user submits.
13
+ * - The assistant emits an interactive task only when it lacks the parameters to
14
+ * produce an exactly-executable preview. Once parameters are known, a normal
15
+ * `preview` + `confirmation` flow is used instead.
16
+ * - The surface collects user input and POSTs it to `submitEndpoint`; the backend
17
+ * then runs the governance engine (create execution → execute), so the change
18
+ * is audited and reversible like every other chat action.
19
+ * - At most one GOVERNED surface (preview ∪ interactive) is active per turn.
20
+ *
21
+ * @see decisions/ADR-CONTRACTS-068.md for design rationale
22
+ */
23
+
24
+ import type { ConfirmationRiskLevel } from "./confirmation";
25
+
26
+ /**
27
+ * The set of editable tasks the assistant can present inline.
28
+ * Mirrors the `{domain}.{verb}` naming of ExecutionKind. Extensible union.
29
+ */
30
+ export type InteractiveTaskKind = "member.changeManager";
31
+
32
+ /**
33
+ * How the frontend sources candidate values for an editable field.
34
+ * - `inline`: the options are shipped with the part (small, bounded sets).
35
+ * - `search`: the frontend queries `endpoint` as the user types (large sets).
36
+ */
37
+ export type CandidateSource =
38
+ | {
39
+ kind: "inline";
40
+ options: Array<{ value: string; label: string; sublabel?: string }>;
41
+ }
42
+ | { kind: "search"; endpoint: string; minChars?: number };
43
+
44
+ /**
45
+ * The input shape of an editable field.
46
+ * - `member-ref`: a workspace member id (backed by a member picker).
47
+ * - `text`: free text.
48
+ * - `enum`: one of the field's inline candidate options.
49
+ */
50
+ export type InteractiveFieldType = "member-ref" | "text" | "enum";
51
+
52
+ /**
53
+ * One editable field within an interactive task.
54
+ */
55
+ export interface InteractiveFieldDescriptor {
56
+ /** Submit key, e.g. "newManagerUserId". */
57
+ name: string;
58
+ /** Display label, e.g. "Reports to". */
59
+ label: string;
60
+ /** Input shape. */
61
+ type: InteractiveFieldType;
62
+ /** Whether the field must be filled before submit is allowed. */
63
+ required: boolean;
64
+ /** Whether the field supports an explicit "clear"/empty submission (e.g. "No manager"). */
65
+ clearable?: boolean;
66
+ /** Current value (for the diff's "old" half); null when unset. */
67
+ currentValue?: string | null;
68
+ /** Human-readable current value (e.g. "Jordan Rivera") for display. */
69
+ currentLabel?: string | null;
70
+ /** How to source candidate values; omitted for free `text` fields. */
71
+ candidates?: CandidateSource;
72
+ }
73
+
74
+ /**
75
+ * Read-only subject the task acts upon (e.g. the member being edited).
76
+ */
77
+ export interface InteractiveTaskSubject {
78
+ /** Workspace member id (users.id) the task edits. */
79
+ memberId: string;
80
+ /** Display name for the surface header. */
81
+ displayName: string;
82
+ /** Arbitrary read-only context the surface may render. */
83
+ context?: Record<string, string>;
84
+ }
85
+
86
+ /**
87
+ * Interactive task surface data payload.
88
+ */
89
+ export interface InteractiveTaskData {
90
+ /** Stable id for this task; becomes the governance correlation key on submit. */
91
+ taskId: string;
92
+ /** Which editable task this is — selects the frontend renderer. */
93
+ taskKind: InteractiveTaskKind;
94
+ /** Human-readable summary of what the user is about to change. */
95
+ title: string;
96
+ /** Optional detail. */
97
+ description?: string;
98
+ /** What the task acts upon. */
99
+ subject: InteractiveTaskSubject;
100
+ /** The editable fields. */
101
+ fields: InteractiveFieldDescriptor[];
102
+ /** Endpoint the frontend POSTs the filled form to. */
103
+ submitEndpoint: string;
104
+ /** Risk level for UI styling; mirrors ConfirmationData.risk. */
105
+ risk: ConfirmationRiskLevel;
106
+ }
107
+
108
+ /**
109
+ * Interactive task message part (semantic type).
110
+ */
111
+ export interface InteractiveTaskPart {
112
+ type: "interactive";
113
+ data: InteractiveTaskData;
114
+ }
115
+
116
+ /**
117
+ * Interactive task data part (wire format).
118
+ * Uses AI SDK's data-{name} convention.
119
+ */
120
+ export interface InteractiveTaskDataPart {
121
+ type: "data-interactive-task";
122
+ data: InteractiveTaskData;
123
+ }
@@ -16,6 +16,7 @@
16
16
  import type { ToolListMessagePart } from "../mcp/index";
17
17
  import type { PreviewPart } from "./preview";
18
18
  import type { ConfirmationPart } from "./confirmation";
19
+ import type { InteractiveTaskPart } from "./interactive";
19
20
 
20
21
  // =============================================================================
21
22
  // Narrative Parts (Streamable)
@@ -118,7 +119,8 @@ export type SurfacePart =
118
119
  | ChartPart
119
120
  | TablePart
120
121
  | ConfirmationPart
121
- | PreviewPart;
122
+ | PreviewPart
123
+ | InteractiveTaskPart;
122
124
 
123
125
  /**
124
126
  * All assistant message part types.
@@ -13,6 +13,10 @@
13
13
  import type { MCPToolDescriptor, ToolListDataPart } from "../mcp/index";
14
14
  import type { PreviewDataPart, PreviewData } from "./preview";
15
15
  import type { ConfirmationData, ConfirmationDataPart } from "./confirmation";
16
+ import type {
17
+ InteractiveTaskData,
18
+ InteractiveTaskDataPart,
19
+ } from "./interactive";
16
20
  import type {
17
21
  ExecutionResultData,
18
22
  ExecutionResultDataPart,
@@ -87,6 +91,27 @@ export const WireSurfaceBuilder = {
87
91
  };
88
92
  },
89
93
 
94
+ /**
95
+ * Build an interactive task data part for streaming.
96
+ * Interactive tasks are EDITABLE forms the user fills in inline; unlike a
97
+ * preview they carry no executionId (no execution exists until the user
98
+ * submits the filled form).
99
+ *
100
+ * INVARIANTS:
101
+ * - taskId must be stable for governance correlation on submit
102
+ * - At most one governed surface (preview ∪ interactive) per turn
103
+ * - No execution is created when this part is emitted
104
+ *
105
+ * @param data - Interactive task data (subject, fields, submitEndpoint)
106
+ * @returns Wire-format interactive task part ready for stream
107
+ */
108
+ interactiveTask(data: InteractiveTaskData): InteractiveTaskDataPart {
109
+ return {
110
+ type: "data-interactive-task",
111
+ data,
112
+ };
113
+ },
114
+
90
115
  /**
91
116
  * Build an execution result data part for streaming.
92
117
  * Reports the outcome of an executed action.
package/src/org/index.ts CHANGED
@@ -157,6 +157,7 @@ export {
157
157
  RemoveMemberResponseSchema,
158
158
  ChangeMemberRoleResponseSchema,
159
159
  SetMemberManagerResponseSchema,
160
+ SubmitInteractiveTaskResponseSchema,
160
161
  UserOrgsResponseSchema,
161
162
  SetActiveOrgResponseSchema,
162
163
  LeaveOrgResponseSchema,
@@ -180,6 +181,7 @@ export type {
180
181
  RemoveMemberResponse,
181
182
  ChangeMemberRoleResponse,
182
183
  SetMemberManagerResponse,
184
+ SubmitInteractiveTaskResponse,
183
185
  UserOrgsResponse,
184
186
  SetActiveOrgResponse,
185
187
  LeaveOrgResponse,
@@ -501,6 +501,31 @@ export type SetMemberManagerResponse = z.infer<
501
501
  typeof SetMemberManagerResponseSchema
502
502
  >;
503
503
 
504
+ // ---------------------------------------------------------------------------
505
+ // POST /api/chat/interactive-tasks/submit
506
+ // ---------------------------------------------------------------------------
507
+
508
+ /**
509
+ * Response for submitting a filled-in interactive chat task surface (e.g. the
510
+ * editable "change reporting manager" form). The submit endpoint runs the
511
+ * Deterministic Execution & Governance Engine: it creates an execution and,
512
+ * when no further confirmation is required, drives it to execution in the same
513
+ * request. The frontend then drives the surface's lifecycle card off
514
+ * `executionId` (the execution-lifecycle stream), exactly like a confirmation.
515
+ *
516
+ * See ADR-CONTRACTS-068.
517
+ */
518
+ export const SubmitInteractiveTaskResponseSchema = z.object({
519
+ /** Lifecycle record id created for this submission. */
520
+ executionId: z.string(),
521
+ /** Coarse lifecycle status at the time the request returns. */
522
+ status: z.enum(["executing", "completed", "failed", "pending_confirmation"]),
523
+ });
524
+
525
+ export type SubmitInteractiveTaskResponse = z.infer<
526
+ typeof SubmitInteractiveTaskResponseSchema
527
+ >;
528
+
504
529
  // ---------------------------------------------------------------------------
505
530
  // GET /api/user/orgs
506
531
  // ---------------------------------------------------------------------------
@@ -44,7 +44,7 @@ export const OrgChartRoleSchema = z.enum(ORG_CHART_ROLES);
44
44
  */
45
45
  export const ORG_CHART_ROLE_LABELS: Record<OrgChartRole, string> = {
46
46
  owner: "CEO",
47
- leader: "Leader",
47
+ leader: "Unit owner",
48
48
  delegate: "Delegate",
49
49
  admin: "Admin",
50
50
  };