@company-semantics/contracts 15.0.0 → 16.0.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 +4 -2
- package/src/api/generated-spec-hash.ts +2 -2
- package/src/api/generated.ts +70 -14
- package/src/execution/__tests__/registry.test.ts +22 -0
- package/src/execution/kinds.ts +2 -1
- package/src/execution/registry.ts +23 -0
- package/src/execution/types.ts +2 -1
- package/src/generated/openapi-routes.ts +1 -0
- package/src/index.ts +9 -0
- package/src/message-parts/__tests__/confirmation.test.ts +1 -0
- package/src/message-parts/confirmation.ts +1 -0
- package/src/message-parts/index.ts +12 -0
- package/src/message-parts/interactive.ts +123 -0
- package/src/message-parts/types.ts +3 -1
- package/src/message-parts/wire.ts +25 -0
- package/src/org/index.ts +6 -2
- package/src/org/schemas.ts +49 -12
- package/src/org/types.ts +5 -5
- package/src/permissions/orgchart-roles.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@company-semantics/contracts",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "16.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -109,7 +109,9 @@
|
|
|
109
109
|
"generate:api-types": "openapi-typescript openapi/backend.yaml -o src/api/generated.ts",
|
|
110
110
|
"generate:openapi-routes": "tsx scripts/generate-openapi-routes.ts",
|
|
111
111
|
"generate:api-types:check": "openapi-typescript openapi/backend.yaml -o /tmp/cs-api-types-check.ts && diff -q src/api/generated.ts /tmp/cs-api-types-check.ts",
|
|
112
|
-
"generate:current": "tsx ../company-semantics-ci/scripts/generate-current.ts"
|
|
112
|
+
"generate:current": "tsx ../company-semantics-ci/scripts/generate-current.ts",
|
|
113
|
+
"repo-map": "tsx ../company-semantics-ci/scripts/generate-repo-map.ts --roots src --name company-semantics-contracts --write",
|
|
114
|
+
"repo-map:check": "tsx ../company-semantics-ci/scripts/generate-repo-map.ts --roots src --name company-semantics-contracts --check"
|
|
113
115
|
},
|
|
114
116
|
"packageManager": "pnpm@10.25.0",
|
|
115
117
|
"engines": {
|
|
@@ -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 = 'bd74b6f7478d' as const;
|
|
3
|
+
export const SPEC_HASH_FULL = 'bd74b6f7478dcfb445d1e848ab3b65309f9b602b40c3fe2f0ce29b484e94943f' as const;
|
package/src/api/generated.ts
CHANGED
|
@@ -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;
|
|
@@ -3398,7 +3415,7 @@ export interface components {
|
|
|
3398
3415
|
email: string;
|
|
3399
3416
|
avatarUrl?: string | null;
|
|
3400
3417
|
jobTitle: string | null;
|
|
3401
|
-
role: ("owner" | "
|
|
3418
|
+
role: ("owner" | "unit_owner" | "delegate" | "admin") | null;
|
|
3402
3419
|
roleNames: string[];
|
|
3403
3420
|
joinedAt: string;
|
|
3404
3421
|
lastActiveAt: string | null;
|
|
@@ -3410,11 +3427,11 @@ export interface components {
|
|
|
3410
3427
|
/** @enum {string} */
|
|
3411
3428
|
role: "owner" | "manager" | "member";
|
|
3412
3429
|
}[];
|
|
3413
|
-
|
|
3430
|
+
unitDesignations: {
|
|
3414
3431
|
unitId: string;
|
|
3415
3432
|
unitName: string;
|
|
3416
3433
|
/** @enum {string} */
|
|
3417
|
-
designation: "whole_org" | "admin" | "
|
|
3434
|
+
designation: "whole_org" | "admin" | "unit_owner" | "delegate" | "member";
|
|
3418
3435
|
}[];
|
|
3419
3436
|
unitMembershipsTruncated: boolean;
|
|
3420
3437
|
inviteStatus: ("active" | "pending" | "expired") | null;
|
|
@@ -3593,7 +3610,7 @@ export interface components {
|
|
|
3593
3610
|
email: string;
|
|
3594
3611
|
avatarUrl?: string | null;
|
|
3595
3612
|
jobTitle: string | null;
|
|
3596
|
-
role: ("owner" | "
|
|
3613
|
+
role: ("owner" | "unit_owner" | "delegate" | "admin") | null;
|
|
3597
3614
|
roleNames: string[];
|
|
3598
3615
|
joinedAt: string;
|
|
3599
3616
|
lastActiveAt: string | null;
|
|
@@ -3605,11 +3622,11 @@ export interface components {
|
|
|
3605
3622
|
/** @enum {string} */
|
|
3606
3623
|
role: "owner" | "manager" | "member";
|
|
3607
3624
|
}[];
|
|
3608
|
-
|
|
3625
|
+
unitDesignations: {
|
|
3609
3626
|
unitId: string;
|
|
3610
3627
|
unitName: string;
|
|
3611
3628
|
/** @enum {string} */
|
|
3612
|
-
designation: "whole_org" | "admin" | "
|
|
3629
|
+
designation: "whole_org" | "admin" | "unit_owner" | "delegate" | "member";
|
|
3613
3630
|
}[];
|
|
3614
3631
|
unitMembershipsTruncated: boolean;
|
|
3615
3632
|
inviteStatus: ("active" | "pending" | "expired") | null;
|
|
@@ -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;
|
|
@@ -3666,7 +3698,7 @@ export interface components {
|
|
|
3666
3698
|
orgId: string;
|
|
3667
3699
|
orgName: string;
|
|
3668
3700
|
orgSlug: string;
|
|
3669
|
-
role: ("owner" | "
|
|
3701
|
+
role: ("owner" | "unit_owner" | "delegate" | "admin") | null;
|
|
3670
3702
|
joinedAt: string;
|
|
3671
3703
|
isActive: boolean;
|
|
3672
3704
|
/** @enum {string} */
|
|
@@ -3711,7 +3743,7 @@ export interface components {
|
|
|
3711
3743
|
/** Format: uuid */
|
|
3712
3744
|
homeUnitId?: string;
|
|
3713
3745
|
/** @enum {string} */
|
|
3714
|
-
homeUnitRole?: "member" | "
|
|
3746
|
+
homeUnitRole?: "member" | "unit_owner";
|
|
3715
3747
|
createdAt: string;
|
|
3716
3748
|
expiresAt: string;
|
|
3717
3749
|
acceptedAt?: string;
|
|
@@ -3725,7 +3757,7 @@ export interface components {
|
|
|
3725
3757
|
/** Format: uuid */
|
|
3726
3758
|
homeUnitId?: string;
|
|
3727
3759
|
/** @enum {string} */
|
|
3728
|
-
homeUnitRole?: "member" | "
|
|
3760
|
+
homeUnitRole?: "member" | "unit_owner";
|
|
3729
3761
|
};
|
|
3730
3762
|
InviteListResponse: {
|
|
3731
3763
|
id: string;
|
|
@@ -3742,7 +3774,7 @@ export interface components {
|
|
|
3742
3774
|
/** Format: uuid */
|
|
3743
3775
|
homeUnitId?: string;
|
|
3744
3776
|
/** @enum {string} */
|
|
3745
|
-
homeUnitRole?: "member" | "
|
|
3777
|
+
homeUnitRole?: "member" | "unit_owner";
|
|
3746
3778
|
createdAt: string;
|
|
3747
3779
|
expiresAt: string;
|
|
3748
3780
|
acceptedAt?: 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";
|
|
@@ -4616,7 +4648,7 @@ export interface components {
|
|
|
4616
4648
|
OrgUnitMyAuthorityResponse: {
|
|
4617
4649
|
/** Format: uuid */
|
|
4618
4650
|
unitId: string;
|
|
4619
|
-
role: ("owner" | "
|
|
4651
|
+
role: ("owner" | "unit_owner" | "delegate" | "admin") | null;
|
|
4620
4652
|
canManageMembers: boolean;
|
|
4621
4653
|
canAssignDelegate: boolean;
|
|
4622
4654
|
canAssignAdmin: boolean;
|
|
@@ -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;
|
|
@@ -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
|
});
|
package/src/execution/kinds.ts
CHANGED
|
@@ -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
|
// =============================================================================
|
package/src/execution/types.ts
CHANGED
|
@@ -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,
|
|
@@ -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
|
@@ -141,8 +141,9 @@ export {
|
|
|
141
141
|
WorkspaceOverviewSchema,
|
|
142
142
|
WorkspaceMembersResponseSchema,
|
|
143
143
|
WorkspaceMemberDetailSchema,
|
|
144
|
-
|
|
144
|
+
WorkspaceMemberUnitDesignationEntrySchema,
|
|
145
145
|
OrgUnitDesignationSchema,
|
|
146
|
+
HomeUnitRoleSchema,
|
|
146
147
|
RoleCatalogEntrySchema,
|
|
147
148
|
RoleCatalogResponseSchema,
|
|
148
149
|
WorkspaceAuthConfigSchema,
|
|
@@ -157,6 +158,7 @@ export {
|
|
|
157
158
|
RemoveMemberResponseSchema,
|
|
158
159
|
ChangeMemberRoleResponseSchema,
|
|
159
160
|
SetMemberManagerResponseSchema,
|
|
161
|
+
SubmitInteractiveTaskResponseSchema,
|
|
160
162
|
UserOrgsResponseSchema,
|
|
161
163
|
SetActiveOrgResponseSchema,
|
|
162
164
|
LeaveOrgResponseSchema,
|
|
@@ -167,8 +169,9 @@ export type {
|
|
|
167
169
|
WorkspaceAccessResponse,
|
|
168
170
|
WorkspaceOverview as WorkspaceOverviewDto,
|
|
169
171
|
WorkspaceMembersResponse,
|
|
170
|
-
|
|
172
|
+
WorkspaceMemberUnitDesignationEntry,
|
|
171
173
|
OrgUnitDesignation,
|
|
174
|
+
HomeUnitRole,
|
|
172
175
|
WorkspaceAuthConfig as WorkspaceAuthConfigDto,
|
|
173
176
|
WorkspaceAuditEvent as WorkspaceAuditEventDto,
|
|
174
177
|
WorkspaceResolvePathResponse,
|
|
@@ -180,6 +183,7 @@ export type {
|
|
|
180
183
|
RemoveMemberResponse,
|
|
181
184
|
ChangeMemberRoleResponse,
|
|
182
185
|
SetMemberManagerResponse,
|
|
186
|
+
SubmitInteractiveTaskResponse,
|
|
183
187
|
UserOrgsResponse,
|
|
184
188
|
SetActiveOrgResponse,
|
|
185
189
|
LeaveOrgResponse,
|
package/src/org/schemas.ts
CHANGED
|
@@ -28,7 +28,7 @@ const WorkspaceMemberUnitSummarySchema = z.object({
|
|
|
28
28
|
/**
|
|
29
29
|
* Designation a member holds over a unit they manage (ADR-CTRL-112).
|
|
30
30
|
*
|
|
31
|
-
* Precedence, highest-first: whole_org > admin >
|
|
31
|
+
* Precedence, highest-first: whole_org > admin > unit_owner > delegate > member.
|
|
32
32
|
* The org-wide designations (`whole_org`, `admin`) anchor to the org root
|
|
33
33
|
* unit. This vocabulary is DISPLAY-ONLY — it summarizes management reach for
|
|
34
34
|
* UI surfaces and is NEVER an authorization input (authority resolution uses
|
|
@@ -37,19 +37,19 @@ const WorkspaceMemberUnitSummarySchema = z.object({
|
|
|
37
37
|
export const OrgUnitDesignationSchema = z.enum([
|
|
38
38
|
"whole_org",
|
|
39
39
|
"admin",
|
|
40
|
-
"
|
|
40
|
+
"unit_owner",
|
|
41
41
|
"delegate",
|
|
42
42
|
"member",
|
|
43
43
|
]);
|
|
44
44
|
export type OrgUnitDesignation = z.infer<typeof OrgUnitDesignationSchema>;
|
|
45
45
|
|
|
46
|
-
export const
|
|
46
|
+
export const WorkspaceMemberUnitDesignationEntrySchema = z.object({
|
|
47
47
|
unitId: z.string(),
|
|
48
48
|
unitName: z.string(),
|
|
49
49
|
designation: OrgUnitDesignationSchema,
|
|
50
50
|
});
|
|
51
|
-
export type
|
|
52
|
-
typeof
|
|
51
|
+
export type WorkspaceMemberUnitDesignationEntry = z.infer<
|
|
52
|
+
typeof WorkspaceMemberUnitDesignationEntrySchema
|
|
53
53
|
>;
|
|
54
54
|
|
|
55
55
|
const WorkspaceMemberSchema = z.object({
|
|
@@ -87,11 +87,13 @@ const WorkspaceMemberSchema = z.object({
|
|
|
87
87
|
/** OrgUnit memberships for this member, capped at a small number server-side. */
|
|
88
88
|
unitMemberships: z.array(WorkspaceMemberUnitSummarySchema),
|
|
89
89
|
/**
|
|
90
|
-
* Per-unit org-chart designations the member holds
|
|
91
|
-
*
|
|
92
|
-
*
|
|
90
|
+
* Per-unit org-chart designations the member holds (ADR-CTRL-112). Carries
|
|
91
|
+
* BOTH management-reach entries (whole_org/admin/unit_owner/delegate) and
|
|
92
|
+
* plain contributor entries (`member`); consumers split by designation.
|
|
93
|
+
* Display-only summary; never an authorization input. Empty array when the
|
|
94
|
+
* member holds no per-unit designation.
|
|
93
95
|
*/
|
|
94
|
-
|
|
96
|
+
unitDesignations: z.array(WorkspaceMemberUnitDesignationEntrySchema),
|
|
95
97
|
/** True when server truncated `unitMemberships`; detail endpoint returns the full list. */
|
|
96
98
|
unitMembershipsTruncated: z.boolean(),
|
|
97
99
|
/** Invite lifecycle status. `null` for members without an invite record. */
|
|
@@ -501,6 +503,31 @@ export type SetMemberManagerResponse = z.infer<
|
|
|
501
503
|
typeof SetMemberManagerResponseSchema
|
|
502
504
|
>;
|
|
503
505
|
|
|
506
|
+
// ---------------------------------------------------------------------------
|
|
507
|
+
// POST /api/chat/interactive-tasks/submit
|
|
508
|
+
// ---------------------------------------------------------------------------
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Response for submitting a filled-in interactive chat task surface (e.g. the
|
|
512
|
+
* editable "change reporting manager" form). The submit endpoint runs the
|
|
513
|
+
* Deterministic Execution & Governance Engine: it creates an execution and,
|
|
514
|
+
* when no further confirmation is required, drives it to execution in the same
|
|
515
|
+
* request. The frontend then drives the surface's lifecycle card off
|
|
516
|
+
* `executionId` (the execution-lifecycle stream), exactly like a confirmation.
|
|
517
|
+
*
|
|
518
|
+
* See ADR-CONTRACTS-068.
|
|
519
|
+
*/
|
|
520
|
+
export const SubmitInteractiveTaskResponseSchema = z.object({
|
|
521
|
+
/** Lifecycle record id created for this submission. */
|
|
522
|
+
executionId: z.string(),
|
|
523
|
+
/** Coarse lifecycle status at the time the request returns. */
|
|
524
|
+
status: z.enum(["executing", "completed", "failed", "pending_confirmation"]),
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
export type SubmitInteractiveTaskResponse = z.infer<
|
|
528
|
+
typeof SubmitInteractiveTaskResponseSchema
|
|
529
|
+
>;
|
|
530
|
+
|
|
504
531
|
// ---------------------------------------------------------------------------
|
|
505
532
|
// GET /api/user/orgs
|
|
506
533
|
// ---------------------------------------------------------------------------
|
|
@@ -570,6 +597,15 @@ export type ScopeCheckBatchResponse = z.infer<
|
|
|
570
597
|
// Invite sub-schema
|
|
571
598
|
// ---------------------------------------------------------------------------
|
|
572
599
|
|
|
600
|
+
/**
|
|
601
|
+
* Role the invitee takes in their home unit on acceptance. `unit_owner` is a
|
|
602
|
+
* deferred structural-leadership grant applied at accept time. Source of truth
|
|
603
|
+
* for the hand-mirrored `OrgInvite`/`CreateInviteRequest` interface unions in
|
|
604
|
+
* `org/types.ts` (enforced by the `schema-type-mirror` guard).
|
|
605
|
+
*/
|
|
606
|
+
export const HomeUnitRoleSchema = z.enum(["member", "unit_owner"]);
|
|
607
|
+
export type HomeUnitRole = z.infer<typeof HomeUnitRoleSchema>;
|
|
608
|
+
|
|
573
609
|
const OrgInviteSchema = z.object({
|
|
574
610
|
id: z.string(),
|
|
575
611
|
orgId: z.string(),
|
|
@@ -580,9 +616,10 @@ const OrgInviteSchema = z.object({
|
|
|
580
616
|
// Home unit the invitee is placed in on acceptance (users.primary_unit_id).
|
|
581
617
|
// Optional/nullable: legacy invites predate this field.
|
|
582
618
|
homeUnitId: z.string().uuid().optional(),
|
|
583
|
-
// Role the invitee takes in homeUnitId on acceptance ('
|
|
584
|
-
// structural-leadership grant). Optional/'member' for legacy +
|
|
585
|
-
|
|
619
|
+
// Role the invitee takes in homeUnitId on acceptance ('unit_owner' is a
|
|
620
|
+
// deferred structural-leadership grant). Optional/'member' for legacy +
|
|
621
|
+
// unit-less invites.
|
|
622
|
+
homeUnitRole: HomeUnitRoleSchema.optional(),
|
|
586
623
|
createdAt: z.string(),
|
|
587
624
|
expiresAt: z.string(),
|
|
588
625
|
acceptedAt: z.string().optional(),
|
package/src/org/types.ts
CHANGED
|
@@ -420,11 +420,11 @@ export interface OrgInvite {
|
|
|
420
420
|
homeUnitId?: string;
|
|
421
421
|
/**
|
|
422
422
|
* Role the invitee takes in `homeUnitId` on acceptance — a plain team member,
|
|
423
|
-
* or a
|
|
423
|
+
* or a unit owner of that unit (a *deferred* leadership grant applied
|
|
424
424
|
* at acceptance time). Distinct from the org-level {@link role}. Optional:
|
|
425
425
|
* legacy invites and unit-less invites have none (treated as `member`).
|
|
426
426
|
*/
|
|
427
|
-
homeUnitRole?: "member" | "
|
|
427
|
+
homeUnitRole?: "member" | "unit_owner";
|
|
428
428
|
createdAt: string;
|
|
429
429
|
expiresAt: string;
|
|
430
430
|
acceptedAt?: string;
|
|
@@ -444,12 +444,12 @@ export interface CreateInviteRequest {
|
|
|
444
444
|
homeUnitId?: string;
|
|
445
445
|
/**
|
|
446
446
|
* Role the invitee takes in `homeUnitId` on acceptance: a plain team member
|
|
447
|
-
* (default), or a
|
|
447
|
+
* (default), or a unit owner of that unit. `unit_owner` is a *deferred*
|
|
448
448
|
* grant — the invite is sent now and the leadership is assigned when the
|
|
449
449
|
* invitee accepts. Only meaningful when `homeUnitId` is set; the backend
|
|
450
|
-
* rejects `
|
|
450
|
+
* rejects `unit_owner` without a home unit. Optional/`member` for older callers.
|
|
451
451
|
*/
|
|
452
|
-
homeUnitRole?: "member" | "
|
|
452
|
+
homeUnitRole?: "member" | "unit_owner";
|
|
453
453
|
}
|
|
454
454
|
|
|
455
455
|
/**
|
|
@@ -24,7 +24,7 @@ import { z } from "zod";
|
|
|
24
24
|
|
|
25
25
|
export const ORG_CHART_ROLES = [
|
|
26
26
|
"owner",
|
|
27
|
-
"
|
|
27
|
+
"unit_owner",
|
|
28
28
|
"delegate",
|
|
29
29
|
"admin",
|
|
30
30
|
] as const;
|
|
@@ -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
|
-
|
|
47
|
+
unit_owner: "Unit owner",
|
|
48
48
|
delegate: "Delegate",
|
|
49
49
|
admin: "Admin",
|
|
50
50
|
};
|