@company-semantics/contracts 0.81.0 → 0.83.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": "0.81.0",
3
+ "version": "0.83.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -27,7 +27,7 @@ export interface ChatRuntimeProfileInfo {
27
27
  export const CHAT_RUNTIME_PROFILES: readonly ChatRuntimeProfileInfo[] = [
28
28
  { id: 'fast', label: 'Fast', description: 'Single-step, no tools', model: 'gpt-3.5-turbo' },
29
29
  { id: 'balanced', label: 'Balanced', description: 'Multi-step with tools', model: 'gpt-4o' },
30
- { id: 'agentic', label: 'Agentic', description: 'Agent loop, full reasoning', model: 'claude-sonnet-4' },
30
+ { id: 'agentic', label: 'Agentic', description: 'Agent loop, full reasoning', model: 'claude-sonnet-4-20250514' },
31
31
  ] as const
32
32
 
33
33
  /**
@@ -0,0 +1,67 @@
1
+ import type { ExecutionEventType } from './events';
2
+ import type { ExecutionIntent } from './types';
3
+ import type { HashChainVerificationResult } from './hash-chain';
4
+
5
+ /**
6
+ * Immutable governance snapshot captured at execution creation time.
7
+ * Stored in execution_audit.frozen_intent JSONB column.
8
+ *
9
+ * Semantically distinct from ExecutionIntent: this represents what was true
10
+ * at creation time, not what the registry currently says.
11
+ * frozenIntentHash enables tamper detection if JSONB is modified by privileged DBA.
12
+ */
13
+ export type FrozenExecutionIntent = Readonly<ExecutionIntent> & {
14
+ /** ISO 8601 timestamp of when this snapshot was captured */
15
+ frozenAt: string;
16
+ /** SHA-256 of canonicalized frozen intent — for tamper detection */
17
+ frozenIntentHash: string;
18
+ };
19
+
20
+ /**
21
+ * Per-event shape within an audit export bundle.
22
+ * Events MUST be ordered by eventSequence ascending.
23
+ */
24
+ export interface AuditExportEvent {
25
+ eventSequence: number;
26
+ eventType: ExecutionEventType;
27
+ stateAfter: string;
28
+ actorId: string;
29
+ actorType: string;
30
+ metadata: Record<string, unknown>;
31
+ createdAt: string;
32
+ eventHash: string;
33
+ previousHash: string | null;
34
+ }
35
+
36
+ /**
37
+ * Top-level audit export bundle for a single execution.
38
+ *
39
+ * Threat model:
40
+ * - hashChainValid: Detects internal DB tampering (single-row mutation)
41
+ * - frozenIntentHashValid: Detects frozen intent JSONB mutation
42
+ * - exportSignature (HMAC-SHA256): Proves this export was generated by this server.
43
+ * Does NOT prove DB was not tampered prior to export.
44
+ * Hash chaining + frozen intent hashing detect pre-export tampering.
45
+ */
46
+ export interface AuditExportBundle {
47
+ /** Schema version for forward compatibility */
48
+ version: '1.0';
49
+ /** ISO 8601 timestamp of export generation */
50
+ exportedAt: string;
51
+ /** Execution ID */
52
+ executionId: string;
53
+ /** Organization ID */
54
+ orgId: string;
55
+ /** Frozen governance snapshot from execution_audit */
56
+ frozenIntent: FrozenExecutionIntent;
57
+ /** Whether frozen_intent_hash matches recomputed SHA-256(canonicalize(frozen_intent)) */
58
+ frozenIntentHashValid: boolean;
59
+ /** Event chain ordered by eventSequence ascending, with hash fields */
60
+ events: AuditExportEvent[];
61
+ /** Whether all event hashes recompute correctly */
62
+ hashChainValid: boolean;
63
+ /** Detailed chain verification result */
64
+ hashChainVerification: HashChainVerificationResult;
65
+ /** HMAC-SHA256 of bundle content. Present only if AUDIT_EXPORT_SIGNING_KEY is configured. */
66
+ exportSignature?: string;
67
+ }
@@ -0,0 +1,103 @@
1
+ import type { ExecutionEventType } from './events';
2
+ import type { ExecutionIntent } from './types';
3
+
4
+ /** Metadata for execution_created event. Frozen governance snapshot. */
5
+ export interface ExecutionCreatedMetadata {
6
+ /** Full ExecutionIntent frozen at creation time — authoritative governance snapshot (INV-18) */
7
+ executionIntent: ExecutionIntent;
8
+ /** Number of artifacts in the execution */
9
+ artifactCount: number;
10
+ /** ISO 8601 confirmation deadline. Present when requiresConfirmation=true. */
11
+ expiresAt?: string;
12
+ }
13
+
14
+ /** Metadata for execution_started event. Intentionally empty. */
15
+ export type ExecutionStartedMetadata = Record<string, never>;
16
+
17
+ /** Metadata for confirmation_approved event. */
18
+ export interface ConfirmationApprovedMetadata {
19
+ /** userId of the user who confirmed */
20
+ confirmedBy: string;
21
+ }
22
+
23
+ /** Metadata for confirmation_rejected event. */
24
+ export interface ConfirmationRejectedMetadata {
25
+ /** userId of the user who rejected */
26
+ rejectedBy: string;
27
+ /** Optional reason for rejection */
28
+ reason?: string;
29
+ }
30
+
31
+ /** Metadata for confirmation_expired event. */
32
+ export interface ConfirmationExpiredMetadata {
33
+ /** What triggered the expiration */
34
+ trigger: 'job' | 'confirm_attempt';
35
+ /** ISO 8601 timestamp when expiration was recorded */
36
+ expiredAt: string;
37
+ /** ISO 8601 original confirmation deadline */
38
+ originalExpiresAt: string;
39
+ }
40
+
41
+ /** Metadata for approval_requested event. Governance-metadata event. */
42
+ export interface ApprovalRequestedMetadata {
43
+ /** Reason approval is required */
44
+ reason: string;
45
+ /** userId who triggered the approval request */
46
+ requestedBy: string;
47
+ }
48
+
49
+ /** Metadata for execution_completed event. Forward-compatible. */
50
+ export interface ExecutionCompletedMetadata {
51
+ /** Human-readable result summary */
52
+ resultSummary?: string;
53
+ /** Number of artifacts executed */
54
+ artifactCount?: number;
55
+ }
56
+
57
+ /** Metadata for execution_failed event. Forward-compatible. */
58
+ export interface ExecutionFailedMetadata {
59
+ /** Machine-readable error code */
60
+ errorCode?: string;
61
+ /** Error classification for retry decisions */
62
+ errorClass?: 'retryable' | 'terminal';
63
+ /** Human-readable error message */
64
+ message?: string;
65
+ }
66
+
67
+ /** Metadata for undo_completed event. Forward-compatible. */
68
+ export interface UndoCompletedMetadata {
69
+ /** userId who initiated the undo */
70
+ undoneBy: string;
71
+ }
72
+
73
+ /** Metadata for cancelled event. Forward-compatible. */
74
+ export interface CancelledMetadata {
75
+ /** userId who cancelled */
76
+ cancelledBy: string;
77
+ /** Optional cancellation reason */
78
+ reason?: string;
79
+ }
80
+
81
+ /**
82
+ * Exhaustive mapping from ExecutionEventType to its metadata interface.
83
+ * TypeScript enforces completeness — adding a new event type without
84
+ * a metadata definition here is a compile error.
85
+ */
86
+ export interface ExecutionEventMetadataMap {
87
+ execution_created: ExecutionCreatedMetadata;
88
+ execution_started: ExecutionStartedMetadata;
89
+ confirmation_approved: ConfirmationApprovedMetadata;
90
+ confirmation_rejected: ConfirmationRejectedMetadata;
91
+ confirmation_expired: ConfirmationExpiredMetadata;
92
+ approval_requested: ApprovalRequestedMetadata;
93
+ execution_completed: ExecutionCompletedMetadata;
94
+ execution_failed: ExecutionFailedMetadata;
95
+ undo_completed: UndoCompletedMetadata;
96
+ cancelled: CancelledMetadata;
97
+ }
98
+
99
+ // Compile-time exhaustiveness check: this line errors if
100
+ // ExecutionEventMetadataMap keys don't match ExecutionEventType
101
+ type _ExhaustivenessCheck = {
102
+ [K in ExecutionEventType]: ExecutionEventMetadataMap[K];
103
+ };
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Enumerates all valid event_type values for the execution_events table.
3
+ *
4
+ * Currently emitted: execution_created, execution_started, confirmation_approved,
5
+ * confirmation_rejected, confirmation_expired, approval_requested.
6
+ *
7
+ * Forward-compatible: execution_completed, execution_failed, undo_completed, cancelled.
8
+ * These are defined here for type exhaustiveness but not yet emitted by backend.
9
+ */
10
+ export type ExecutionEventType =
11
+ | 'execution_created'
12
+ | 'execution_started'
13
+ | 'confirmation_approved'
14
+ | 'confirmation_rejected'
15
+ | 'confirmation_expired'
16
+ | 'approval_requested'
17
+ | 'execution_completed'
18
+ | 'execution_failed'
19
+ | 'undo_completed'
20
+ | 'cancelled';
21
+
22
+ export const EXECUTION_EVENT_TYPES: readonly ExecutionEventType[] = [
23
+ 'execution_created',
24
+ 'execution_started',
25
+ 'confirmation_approved',
26
+ 'confirmation_rejected',
27
+ 'confirmation_expired',
28
+ 'approval_requested',
29
+ 'execution_completed',
30
+ 'execution_failed',
31
+ 'undo_completed',
32
+ 'cancelled',
33
+ ] as const;
34
+
35
+ export function isExecutionEventType(value: string): value is ExecutionEventType {
36
+ return (EXECUTION_EVENT_TYPES as readonly string[]).includes(value);
37
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Hash chain fields for tamper-evident execution events.
3
+ *
4
+ * Algorithm: SHA-256 over RFC 8785 JCS (JSON Canonicalization Scheme) input.
5
+ * Input: executionId + eventSequence + eventType + stateAfter + actorId + canonicalize(metadata) + (previousHash ?? 'NULL')
6
+ * created_at intentionally excluded — clock drift across replicas could break verification.
7
+ *
8
+ * Threat model: Detects single-row mutation where attacker does not recompute downstream hashes.
9
+ * Does NOT defend against fully malicious DBA who recomputes entire chain.
10
+ * External anchoring (periodic anchor hash stored outside DB) is a future enhancement.
11
+ */
12
+ export interface EventHashFields {
13
+ /** SHA-256 hex digest (64 characters) of this event's canonicalized content */
14
+ eventHash: string;
15
+ /** SHA-256 hex digest of the previous event in this execution's chain, or null for the first event */
16
+ previousHash: string | null;
17
+ }
18
+
19
+ /**
20
+ * Result of verifying a hash chain for a single execution.
21
+ * If valid=false, brokenAtSequence indicates where the chain first breaks.
22
+ */
23
+ export interface HashChainVerificationResult {
24
+ /** True if all event hashes recompute correctly and chain links are valid */
25
+ valid: boolean;
26
+ /** Event sequence number where chain integrity first fails (present only if valid=false) */
27
+ brokenAtSequence?: number;
28
+ /** What the recomputed hash should be (present only if valid=false) */
29
+ expectedHash?: string;
30
+ /** What was stored in the database (present only if valid=false) */
31
+ actualHash?: string;
32
+ /** True if frozen_intent_hash verification failed (present only if checked) */
33
+ frozenIntentMismatch?: boolean;
34
+ }
35
+
36
+ /** Hash algorithm used for event chain integrity */
37
+ export const HASH_ALGORITHM = 'sha256' as const;
38
+
39
+ /** Encoding for hash output */
40
+ export const HASH_ENCODING = 'hex' as const;
@@ -7,12 +7,71 @@
7
7
  * @see ADR-CONT-029 for design rationale
8
8
  */
9
9
 
10
+ // =============================================================================
11
+ // Event Types
12
+ // =============================================================================
13
+
14
+ export type { ExecutionEventType } from './events'
15
+
16
+ export {
17
+ EXECUTION_EVENT_TYPES,
18
+ isExecutionEventType,
19
+ } from './events'
20
+
21
+ // =============================================================================
22
+ // Event Metadata Schemas
23
+ // =============================================================================
24
+
25
+ export type {
26
+ ExecutionCreatedMetadata,
27
+ ExecutionStartedMetadata,
28
+ ConfirmationApprovedMetadata,
29
+ ConfirmationRejectedMetadata,
30
+ ConfirmationExpiredMetadata,
31
+ ApprovalRequestedMetadata,
32
+ ExecutionCompletedMetadata,
33
+ ExecutionFailedMetadata,
34
+ UndoCompletedMetadata,
35
+ CancelledMetadata,
36
+ ExecutionEventMetadataMap,
37
+ } from './event-metadata'
38
+
39
+ // =============================================================================
40
+ // Hash Chain Types
41
+ // =============================================================================
42
+
43
+ export type {
44
+ EventHashFields,
45
+ HashChainVerificationResult,
46
+ } from './hash-chain'
47
+
48
+ export {
49
+ HASH_ALGORITHM,
50
+ HASH_ENCODING,
51
+ } from './hash-chain'
52
+
53
+ // =============================================================================
54
+ // Audit Export Types
55
+ // =============================================================================
56
+
57
+ export type {
58
+ FrozenExecutionIntent,
59
+ AuditExportEvent,
60
+ AuditExportBundle,
61
+ } from './audit-export'
62
+
10
63
  // =============================================================================
11
64
  // Kind Types
12
65
  // =============================================================================
13
66
 
14
67
  export type { ExecutionKind } from './kinds'
15
68
 
69
+ // =============================================================================
70
+ // Lifecycle State
71
+ // =============================================================================
72
+
73
+ export type { ExecutionState } from './status'
74
+
16
75
  // =============================================================================
17
76
  // Definition Types
18
77
  // =============================================================================
@@ -22,6 +81,7 @@ export type {
22
81
  IconName,
23
82
  ExecutionDomain,
24
83
  ExecutionKindDefinition,
84
+ ExecutionIntent,
25
85
  } from './types'
26
86
 
27
87
  // =============================================================================
@@ -25,3 +25,5 @@
25
25
  export type ExecutionKind =
26
26
  | 'integration.connect'
27
27
  | 'integration.disconnect'
28
+ | 'profile.update'
29
+ | 'slack.send'
@@ -76,6 +76,48 @@ export const EXECUTION_KINDS = {
76
76
  templateId: 'integration.disconnect',
77
77
  },
78
78
  },
79
+ 'profile.update': {
80
+ kind: 'profile.update',
81
+ domain: 'profile',
82
+ display: {
83
+ label: 'Update Profile',
84
+ pastTenseLabel: 'Profile updated',
85
+ icon: 'pencil',
86
+ },
87
+ governance: {
88
+ visibility: 'user',
89
+ requiresAdmin: false,
90
+ },
91
+ ui: {
92
+ showInAdmin: false,
93
+ showInTimeline: true,
94
+ confirmBeforeRun: true,
95
+ },
96
+ explanation: {
97
+ templateId: 'profile.update',
98
+ },
99
+ },
100
+ 'slack.send': {
101
+ kind: 'slack.send',
102
+ domain: 'communication',
103
+ display: {
104
+ label: 'Send Slack Message',
105
+ pastTenseLabel: 'Slack message sent',
106
+ icon: 'send',
107
+ },
108
+ governance: {
109
+ visibility: 'user',
110
+ requiresAdmin: false,
111
+ },
112
+ ui: {
113
+ showInAdmin: false,
114
+ showInTimeline: true,
115
+ confirmBeforeRun: true,
116
+ },
117
+ explanation: {
118
+ templateId: 'slack.send',
119
+ },
120
+ },
79
121
  } as const satisfies Record<ExecutionKind, ExecutionKindDefinition>
80
122
 
81
123
  // =============================================================================
@@ -0,0 +1,57 @@
1
+ /**
2
+ * ExecutionState — First-class contract for execution lifecycle state.
3
+ *
4
+ * This type is the sole authority (INV-1) for lifecycle state across all repos.
5
+ * Backend MUST import from contracts, not define locally.
6
+ *
7
+ * ## State Machine
8
+ *
9
+ * ```
10
+ * pending_confirmation ──→ blocked_pending_approval ──→ ready ──→ executing
11
+ * │ │ ↑ │
12
+ * │ │ │ ├──→ completed
13
+ * │ │ │ ├──→ completed_with_rollbacks
14
+ * │ │ │ ├──→ failed_retryable ──→ ready (retry)
15
+ * │ │ │ ├──→ failed_terminal
16
+ * │ │ │ ├──→ failed_with_partial_execution
17
+ * │ │ │ └──→ cancelled
18
+ * └──→ cancelled └──→ cancelled │
19
+ * └──→ expired └──→ expired └──→ expired
20
+ * ```
21
+ *
22
+ * ## Key Semantics
23
+ *
24
+ * - **pending_confirmation** and **blocked_pending_approval** are orthogonal
25
+ * stackable gates that compose. pending_confirmation is the self-attestation
26
+ * gate; blocked_pending_approval is the cross-principal authorization gate.
27
+ *
28
+ * - **ready** is a distinct state separating authorization from execution.
29
+ * All governance gates are cleared. Enables future queueing, backpressure,
30
+ * rate limiting, and scheduling.
31
+ *
32
+ * - **execution_started** is the sole event transitioning to **executing**.
33
+ * Mandatory for all execution paths including retries.
34
+ *
35
+ * - Retry is same-execution: failed_retryable → ready → executing reuses
36
+ * the same executionId.
37
+ *
38
+ * ## Ordering Invariant
39
+ *
40
+ * State transitions are ordered by event_sequence (monotonic per-execution
41
+ * bigint), not timestamps. UNIQUE(execution_id, event_sequence) enforced
42
+ * at database level. event_sequence for ordering, created_at for duration —
43
+ * distinct concerns.
44
+ */
45
+ export type ExecutionState =
46
+ | 'pending_confirmation'
47
+ | 'blocked_pending_approval'
48
+ | 'ready'
49
+ | 'executing'
50
+ | 'completed'
51
+ | 'completed_with_rollbacks'
52
+ | 'failed_retryable'
53
+ | 'failed_terminal'
54
+ | 'failed_with_partial_execution'
55
+ | 'cancelled'
56
+ | 'expired'
57
+ | 'undone';
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  import type { ExecutionKind } from './kinds'
10
+ import type { ConfirmationRiskLevel } from '../message-parts/confirmation'
10
11
 
11
12
  // =============================================================================
12
13
  // Utility Types
@@ -22,7 +23,7 @@ export type ISO8601Timestamp = string
22
23
  * Icon names for execution kinds.
23
24
  * Must be supported by the frontend icon system.
24
25
  */
25
- export type IconName = 'plug' | 'unlink'
26
+ export type IconName = 'plug' | 'unlink' | 'pencil' | 'send'
26
27
 
27
28
  // =============================================================================
28
29
  // Execution Domain
@@ -31,7 +32,7 @@ export type IconName = 'plug' | 'unlink'
31
32
  /**
32
33
  * Domain categories for execution kinds.
33
34
  */
34
- export type ExecutionDomain = 'integration' | 'policy' | 'data' | 'system'
35
+ export type ExecutionDomain = 'integration' | 'policy' | 'data' | 'system' | 'profile' | 'communication'
35
36
 
36
37
  // =============================================================================
37
38
  // Execution Kind Definition
@@ -90,3 +91,33 @@ export interface ExecutionKindDefinition {
90
91
  templateId: string
91
92
  }
92
93
  }
94
+
95
+ // =============================================================================
96
+ // Execution Intent
97
+ // =============================================================================
98
+
99
+ /**
100
+ * Governance requirements for an execution.
101
+ *
102
+ * Executors declare this explicitly when proposing an execution.
103
+ * The bridge transports ExecutionIntent as-is — it never infers governance
104
+ * requirements. The system fails loudly if ExecutionIntent is missing.
105
+ *
106
+ * Invariants:
107
+ * - executionKind MUST exist in EXECUTION_KINDS registry
108
+ * - If requiresConfirmation is false, no confirmation part is emitted
109
+ * and execution starts at 'ready'
110
+ * - If requiresApproval is true, a 'blocked_pending_approval' gate is
111
+ * inserted after confirmation (cross-principal authorization)
112
+ * - risk is used for UI styling and future behavioral hooks
113
+ *
114
+ * NAMING NOTE: This is DIFFERENT from the CI Envelope ExecutionIntent
115
+ * (a string union in ci-envelope/types.ts). This interface is exported
116
+ * from the execution domain barrel only, NOT from root index.ts.
117
+ */
118
+ export interface ExecutionIntent {
119
+ executionKind: ExecutionKind
120
+ requiresConfirmation: boolean
121
+ requiresApproval: boolean
122
+ risk: ConfirmationRiskLevel
123
+ }
package/src/index.ts CHANGED
@@ -303,12 +303,10 @@ export type {
303
303
  PreviewDataPart,
304
304
  // Confirmation types (Phase 4)
305
305
  ConfirmationChoice,
306
- ConfirmationRisk,
306
+ ConfirmationRiskLevel,
307
307
  ConfirmationData,
308
- ConfirmationDataPart,
309
308
  ConfirmationResponseData,
310
- ConfirmationResponsePart,
311
- ConfirmationResponseDataPart,
309
+ ConfirmationDataPart,
312
310
  // Execution result types (Phase 5)
313
311
  ExecutionArtifactStatus,
314
312
  ExecutionResultSummary,
@@ -344,12 +342,16 @@ export {
344
342
  buildParts,
345
343
  getDroppedCount,
346
344
  WireSurfaceBuilder,
345
+ CONFIRMATION_LABELS,
346
+ getConfirmationLabel,
347
347
  } from './message-parts/index'
348
348
 
349
349
  // Execution kind types and registry
350
350
  // @see ADR-CONT-029 for design rationale
351
351
  export type {
352
352
  ExecutionKind,
353
+ ExecutionState,
354
+ ExecutionEventType,
353
355
  ISO8601Timestamp,
354
356
  IconName,
355
357
  ExecutionDomain,
@@ -368,35 +370,10 @@ export {
368
370
  EXECUTION_KINDS,
369
371
  getExecutionKindDefinition,
370
372
  isValidExecutionKind,
373
+ EXECUTION_EVENT_TYPES,
374
+ isExecutionEventType,
371
375
  } from './execution/index'
372
376
 
373
- // CI Execution Envelope types
374
- // @see ADR-CONT-033 for contracts boundary rationale
375
- export type {
376
- // Actor & Session
377
- CIActorInfo,
378
- CISessionInfo,
379
- // Intent
380
- ExecutionIntent,
381
- CIIntentDeclaration,
382
- // Scope
383
- CIChangeKind,
384
- CIExecutionScope,
385
- // Authority
386
- CIAuthorityGrant,
387
- // Guard Plan
388
- CIGuardPlan,
389
- // Status
390
- CIEnvelopeStatus,
391
- // Outcome
392
- CIGuardViolation,
393
- CIExecutionOutcome,
394
- // Core Envelope
395
- CIExecutionEnvelope,
396
- } from './ci-envelope/index'
397
-
398
- export { ENVELOPE_TRANSITIONS } from './ci-envelope/index'
399
-
400
377
  // Ralph autonomous coding loop types
401
378
  // @see company-semantics-control/docs/ralph.md for usage
402
379
  export type {
@@ -493,3 +470,30 @@ export type {
493
470
  BudgetCheck,
494
471
  OrgBudgetSettings,
495
472
  } from './usage/execution-types'
473
+
474
+ // CI Execution Envelope types
475
+ // @see ADR-CONT-033 for contracts boundary rationale
476
+ export type {
477
+ // Actor & Session
478
+ CIActorInfo,
479
+ CISessionInfo,
480
+ // Intent
481
+ ExecutionIntent,
482
+ CIIntentDeclaration,
483
+ // Scope
484
+ CIChangeKind,
485
+ CIExecutionScope,
486
+ // Authority
487
+ CIAuthorityGrant,
488
+ // Guard Plan
489
+ CIGuardPlan,
490
+ // Status
491
+ CIEnvelopeStatus,
492
+ // Outcome
493
+ CIGuardViolation,
494
+ CIExecutionOutcome,
495
+ // Core Envelope
496
+ CIExecutionEnvelope,
497
+ } from './ci-envelope/index'
498
+
499
+ export { ENVELOPE_TRANSITIONS } from './ci-envelope/index'
@@ -1,14 +1,17 @@
1
1
  import { describe, it, expect } from 'vitest'
2
2
  import { WireSurfaceBuilder } from '../wire.js'
3
- import type { ConfirmationData, ConfirmationResponseData } from '../confirmation.js'
3
+ import type { ConfirmationData, ConfirmationRiskLevel } from '../confirmation.js'
4
+ import { CONFIRMATION_LABELS, getConfirmationLabel } from '../confirmation.js'
5
+ import type { ExecutionKind } from '../../execution/kinds.js'
6
+ import { EXECUTION_KINDS } from '../../execution/registry.js'
4
7
 
5
8
  describe('WireSurfaceBuilder.confirmation', () => {
6
- it('creates a confirmation data part', () => {
9
+ it('creates a confirmation data part with new shape', () => {
7
10
  const data: ConfirmationData = {
8
11
  actionId: 'msg-123',
9
- title: 'Send Slack message to #sales',
10
- prompt: 'Confirm sending this message?',
12
+ executorKind: 'slack.send',
11
13
  risk: 'low',
14
+ executionId: 'exec-abc-001',
12
15
  }
13
16
 
14
17
  const result = WireSurfaceBuilder.confirmation(data)
@@ -20,8 +23,9 @@ describe('WireSurfaceBuilder.confirmation', () => {
20
23
  it('preserves actionId exactly', () => {
21
24
  const data: ConfirmationData = {
22
25
  actionId: 'complex-action-id-12345',
23
- title: 'Test',
24
- prompt: 'Confirm?',
26
+ executorKind: 'integration.connect',
27
+ risk: 'moderate',
28
+ executionId: 'exec-xyz-789',
25
29
  }
26
30
 
27
31
  const result = WireSurfaceBuilder.confirmation(data)
@@ -29,28 +33,15 @@ describe('WireSurfaceBuilder.confirmation', () => {
29
33
  expect(result.data.actionId).toBe('complex-action-id-12345')
30
34
  })
31
35
 
32
- it('handles confirmation without optional risk level', () => {
33
- const data: ConfirmationData = {
34
- actionId: 'no-risk-specified',
35
- title: 'Simple action',
36
- prompt: 'Do you want to proceed?',
37
- }
38
-
39
- const result = WireSurfaceBuilder.confirmation(data)
40
-
41
- expect(result.type).toBe('data-confirmation')
42
- expect(result.data.risk).toBeUndefined()
43
- })
44
-
45
- it('preserves all risk levels', () => {
46
- const risks: Array<'low' | 'medium' | 'high'> = ['low', 'medium', 'high']
36
+ it('preserves all ConfirmationRiskLevel values', () => {
37
+ const risks: ConfirmationRiskLevel[] = ['low', 'moderate', 'high', 'irreversible']
47
38
 
48
39
  for (const risk of risks) {
49
40
  const data: ConfirmationData = {
50
41
  actionId: `risk-${risk}`,
51
- title: `${risk} risk action`,
52
- prompt: 'Confirm?',
42
+ executorKind: 'profile.update',
53
43
  risk,
44
+ executionId: `exec-risk-${risk}`,
54
45
  }
55
46
 
56
47
  const result = WireSurfaceBuilder.confirmation(data)
@@ -58,42 +49,47 @@ describe('WireSurfaceBuilder.confirmation', () => {
58
49
  expect(result.data.risk).toBe(risk)
59
50
  }
60
51
  })
61
- })
62
52
 
63
- describe('WireSurfaceBuilder.confirmationResponse', () => {
64
- it('creates a confirmation response for confirm choice', () => {
65
- const data: ConfirmationResponseData = {
66
- actionId: 'msg-123',
67
- choice: 'confirm',
53
+ it('preserves executionId and executorKind', () => {
54
+ const data: ConfirmationData = {
55
+ actionId: 'action-001',
56
+ executorKind: 'integration.disconnect',
57
+ risk: 'high',
58
+ executionId: 'exec-lifecycle-001',
68
59
  }
69
60
 
70
- const result = WireSurfaceBuilder.confirmationResponse(data)
61
+ const result = WireSurfaceBuilder.confirmation(data)
71
62
 
72
- expect(result.type).toBe('data-confirmation-response')
73
- expect(result.data.actionId).toBe('msg-123')
74
- expect(result.data.choice).toBe('confirm')
63
+ expect(result.data.executionId).toBe('exec-lifecycle-001')
64
+ expect(result.data.executorKind).toBe('integration.disconnect')
75
65
  })
66
+ })
76
67
 
77
- it('creates a confirmation response for cancel choice', () => {
78
- const data: ConfirmationResponseData = {
79
- actionId: 'msg-123',
80
- choice: 'cancel',
81
- }
82
-
83
- const result = WireSurfaceBuilder.confirmationResponse(data)
68
+ describe('getConfirmationLabel', () => {
69
+ it('returns correct label for known ExecutionKind', () => {
70
+ expect(getConfirmationLabel('integration.connect')).toBe('Connect Integration')
71
+ expect(getConfirmationLabel('integration.disconnect')).toBe('Disconnect Integration')
72
+ expect(getConfirmationLabel('profile.update')).toBe('Update Profile')
73
+ expect(getConfirmationLabel('slack.send')).toBe('Send Slack Message')
74
+ })
84
75
 
85
- expect(result.type).toBe('data-confirmation-response')
86
- expect(result.data.choice).toBe('cancel')
76
+ it('returns default fallback for undefined', () => {
77
+ expect(getConfirmationLabel(undefined)).toBe('Confirm Action')
87
78
  })
88
79
 
89
- it('preserves actionId exactly in response', () => {
90
- const data: ConfirmationResponseData = {
91
- actionId: 'complex-action-id-12345',
92
- choice: 'confirm',
93
- }
80
+ it('returns custom fallback title when provided', () => {
81
+ expect(getConfirmationLabel(undefined, 'Custom Fallback')).toBe('Custom Fallback')
82
+ })
83
+ })
94
84
 
95
- const result = WireSurfaceBuilder.confirmationResponse(data)
85
+ describe('CONFIRMATION_LABELS', () => {
86
+ it('has an entry for every ExecutionKind', () => {
87
+ const executionKinds = Object.keys(EXECUTION_KINDS) as ExecutionKind[]
96
88
 
97
- expect(result.data.actionId).toBe('complex-action-id-12345')
89
+ for (const kind of executionKinds) {
90
+ expect(CONFIRMATION_LABELS[kind]).toBeDefined()
91
+ expect(typeof CONFIRMATION_LABELS[kind]).toBe('string')
92
+ expect(CONFIRMATION_LABELS[kind].length).toBeGreaterThan(0)
93
+ }
98
94
  })
99
95
  })
@@ -17,6 +17,8 @@
17
17
  * @see DECISIONS.md for design rationale
18
18
  */
19
19
 
20
+ import type { ExecutionKind } from '../execution/kinds'
21
+
20
22
  /**
21
23
  * Choice options for confirmation.
22
24
  */
@@ -24,26 +26,60 @@ export type ConfirmationChoice = 'confirm' | 'cancel';
24
26
 
25
27
  /**
26
28
  * Risk level for confirmation prompts.
29
+ * Uses 'moderate' (not 'medium') for semantic precision.
30
+ * 'irreversible' indicates destructive operations that cannot be undone.
31
+ */
32
+ export type ConfirmationRiskLevel = 'low' | 'moderate' | 'high' | 'irreversible';
33
+
34
+ /**
35
+ * Human-readable confirmation dialog titles for each execution kind.
36
+ * Exhaustive — TypeScript enforces completeness via Record type.
27
37
  */
28
- export type ConfirmationRisk = 'low' | 'medium' | 'high';
38
+ export const CONFIRMATION_LABELS: Record<ExecutionKind, string> = {
39
+ 'integration.connect': 'Connect Integration',
40
+ 'integration.disconnect': 'Disconnect Integration',
41
+ 'profile.update': 'Update Profile',
42
+ 'slack.send': 'Send Slack Message',
43
+ };
29
44
 
30
45
  /**
31
- * Confirmation request data payload.
46
+ * Look up the human-readable confirmation label for an execution kind.
32
47
  */
48
+ export function getConfirmationLabel(
49
+ executorKind: ExecutionKind | undefined,
50
+ fallbackTitle?: string,
51
+ ): string {
52
+ if (executorKind && executorKind in CONFIRMATION_LABELS) {
53
+ return CONFIRMATION_LABELS[executorKind];
54
+ }
55
+ return fallbackTitle ?? 'Confirm Action';
56
+ }
57
+
33
58
  export interface ConfirmationData {
34
59
  /** Must match the active preview's actionId */
35
60
  actionId: string;
36
- /** Summary of the action being confirmed */
37
- title: string;
38
- /** The confirmation prompt, e.g. "Confirm sending this Slack message?" */
39
- prompt: string;
40
- /** Optional risk level for UI styling */
41
- risk?: ConfirmationRisk;
61
+ /** Execution kind for label derivation via getConfirmationLabel() */
62
+ executorKind: ExecutionKind;
63
+ /** Risk level required for UI styling and future governance hooks */
64
+ risk: ConfirmationRiskLevel;
65
+ /** Lifecycle record ID must exist before confirmation part is emitted */
66
+ executionId: string;
42
67
  }
43
68
 
44
69
  /**
45
70
  * Confirmation request message part (semantic type).
46
71
  */
72
+ /**
73
+ * Data sent by the client to confirm or cancel a proposed action.
74
+ * Structured response only — no natural language inference.
75
+ */
76
+ export interface ConfirmationResponseData {
77
+ /** Must match the active preview's actionId */
78
+ actionId: string;
79
+ /** User's explicit decision */
80
+ choice: ConfirmationChoice;
81
+ }
82
+
47
83
  export interface ConfirmationPart {
48
84
  type: 'confirmation';
49
85
  data: ConfirmationData;
@@ -57,31 +93,3 @@ export interface ConfirmationDataPart {
57
93
  data: ConfirmationData;
58
94
  }
59
95
 
60
- // === Confirmation Response Types (New in Phase 4) ===
61
-
62
- /**
63
- * Confirmation response data payload.
64
- * Sent by the user (via UI button or structured message) to confirm/cancel.
65
- */
66
- export interface ConfirmationResponseData {
67
- /** Must match the active confirmation's actionId */
68
- actionId: string;
69
- /** The user's decision */
70
- choice: ConfirmationChoice;
71
- }
72
-
73
- /**
74
- * Confirmation response message part (semantic type).
75
- */
76
- export interface ConfirmationResponsePart {
77
- type: 'confirmation-response';
78
- data: ConfirmationResponseData;
79
- }
80
-
81
- /**
82
- * Confirmation response data part (wire format).
83
- */
84
- export interface ConfirmationResponseDataPart {
85
- type: 'data-confirmation-response';
86
- data: ConfirmationResponseData;
87
- }
@@ -33,15 +33,16 @@ export type {
33
33
  // Confirmation types
34
34
  export type {
35
35
  ConfirmationChoice,
36
- ConfirmationRisk,
36
+ ConfirmationRiskLevel,
37
37
  ConfirmationData,
38
+ ConfirmationResponseData,
38
39
  ConfirmationPart,
39
40
  ConfirmationDataPart,
40
- ConfirmationResponseData,
41
- ConfirmationResponsePart,
42
- ConfirmationResponseDataPart,
43
41
  } from './confirmation'
44
42
 
43
+ // Confirmation runtime values
44
+ export { CONFIRMATION_LABELS, getConfirmationLabel } from './confirmation'
45
+
45
46
  // Execution result types
46
47
  export type {
47
48
  ExecutionArtifactStatus,
@@ -15,7 +15,7 @@
15
15
 
16
16
  import type { ToolListMessagePart } from '../mcp/index'
17
17
  import type { PreviewPart } from './preview'
18
- import type { ConfirmationPart, ConfirmationResponsePart } from './confirmation'
18
+ import type { ConfirmationPart } from './confirmation'
19
19
 
20
20
  // =============================================================================
21
21
  // Narrative Parts (Streamable)
@@ -111,9 +111,6 @@ export interface TablePart {
111
111
  /**
112
112
  * Surface parts are rendered atomically (never streamed).
113
113
  * Extensible: add new surface types to this union.
114
- *
115
- * Note: ConfirmationResponsePart is user-originated but included
116
- * here for uniform parsing. Do not render as UI surface.
117
114
  */
118
115
  export type SurfacePart =
119
116
  | ToolListPart
@@ -121,7 +118,6 @@ export type SurfacePart =
121
118
  | ChartPart
122
119
  | TablePart
123
120
  | ConfirmationPart
124
- | ConfirmationResponsePart
125
121
  | PreviewPart
126
122
 
127
123
  /**
@@ -15,8 +15,6 @@ import type { PreviewDataPart, PreviewData } from './preview'
15
15
  import type {
16
16
  ConfirmationData,
17
17
  ConfirmationDataPart,
18
- ConfirmationResponseData,
19
- ConfirmationResponseDataPart,
20
18
  } from './confirmation'
21
19
  import type {
22
20
  ExecutionResultData,
@@ -92,24 +90,6 @@ export const WireSurfaceBuilder = {
92
90
  }
93
91
  },
94
92
 
95
- /**
96
- * Build a confirmation response data part.
97
- * Used when processing user's confirm/cancel decision.
98
- *
99
- * INVARIANTS:
100
- * - actionId must match the active confirmation
101
- * - choice must be 'confirm' or 'cancel'
102
- *
103
- * @param data - Response data containing actionId and choice
104
- * @returns Wire-format confirmation response part
105
- */
106
- confirmationResponse(data: ConfirmationResponseData): ConfirmationResponseDataPart {
107
- return {
108
- type: 'data-confirmation-response',
109
- data,
110
- }
111
- },
112
-
113
93
  /**
114
94
  * Build an execution result data part for streaming.
115
95
  * Reports the outcome of an executed action.