@synoi/gap 0.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/LICENSE +195 -0
- package/README.md +223 -0
- package/dist/canonicalize.d.ts +19 -0
- package/dist/canonicalize.d.ts.map +1 -0
- package/dist/canonicalize.js +36 -0
- package/dist/canonicalize.js.map +1 -0
- package/dist/capabilities.d.ts +605 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +53 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/cdro.d.ts +63 -0
- package/dist/cdro.d.ts.map +1 -0
- package/dist/cdro.js +16 -0
- package/dist/cdro.js.map +1 -0
- package/dist/channels.d.ts +107 -0
- package/dist/channels.d.ts.map +1 -0
- package/dist/channels.js +29 -0
- package/dist/channels.js.map +1 -0
- package/dist/constants.d.ts +32 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +36 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/oid.d.ts +28 -0
- package/dist/oid.d.ts.map +1 -0
- package/dist/oid.js +68 -0
- package/dist/oid.js.map +1 -0
- package/dist/receipts.d.ts +128 -0
- package/dist/receipts.d.ts.map +1 -0
- package/dist/receipts.js +14 -0
- package/dist/receipts.js.map +1 -0
- package/dist/revocations.d.ts +65 -0
- package/dist/revocations.d.ts.map +1 -0
- package/dist/revocations.js +22 -0
- package/dist/revocations.js.map +1 -0
- package/dist/validate.d.ts +59 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +835 -0
- package/dist/validate.js.map +1 -0
- package/dist/workflows.d.ts +186 -0
- package/dist/workflows.d.ts.map +1 -0
- package/dist/workflows.js +14 -0
- package/dist/workflows.js.map +1 -0
- package/package.json +55 -0
- package/src/canonicalize.ts +38 -0
- package/src/capabilities.ts +711 -0
- package/src/cdro.ts +92 -0
- package/src/channels.ts +183 -0
- package/src/constants.ts +46 -0
- package/src/index.ts +180 -0
- package/src/oid.ts +71 -0
- package/src/receipts.ts +169 -0
- package/src/revocations.ts +90 -0
- package/src/validate.ts +1008 -0
- package/src/workflows.ts +241 -0
package/src/cdro.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cdro.ts -- GAP CDRO envelope.
|
|
3
|
+
*
|
|
4
|
+
* CDRO = Content-addressed, Deterministic, Replayable Object. Every GAP
|
|
5
|
+
* top-level record sits inside a `GapCdroEnvelope<TBody>`. The shape mirrors
|
|
6
|
+
* the GAP gateway reference implementation wire types.
|
|
7
|
+
*
|
|
8
|
+
* NOTE: this envelope shape is locally redeclared in @synoi/gap rather
|
|
9
|
+
* than imported from @synoi/sraid (which is being built in parallel). The two
|
|
10
|
+
* packages will be wired together in a follow-up. The wire format is
|
|
11
|
+
* identical, so cross-package compatibility is by-shape.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export type GapObjectType =
|
|
15
|
+
// -- v1 REFERENCE IMPLEMENTATION TYPES (conformance-backed) --
|
|
16
|
+
// These 10 types are emitted by the v1 reference implementation and have
|
|
17
|
+
// conformance vectors in synoi-conformance. They are the only types that
|
|
18
|
+
// are considered SHIPPED for interoperability purposes.
|
|
19
|
+
| 'gap:capability_declaration'
|
|
20
|
+
| 'gap:capability_grant'
|
|
21
|
+
| 'gap:capability_invocation'
|
|
22
|
+
| 'gap:workflow_definition'
|
|
23
|
+
| 'gap:workflow_instance'
|
|
24
|
+
| 'gap:stage_transition'
|
|
25
|
+
| 'gap:channel_event'
|
|
26
|
+
| 'gap:decision_receipt'
|
|
27
|
+
| 'gap:revocation_event'
|
|
28
|
+
| 'gap:federation_handshake' // reserved for GAP 1.1 - not part of the active 1.0 conformance surface
|
|
29
|
+
// -- DRAFT TYPES (defined in spec; not yet emitted by the v1 reference
|
|
30
|
+
// implementation; no conformance vector yet) --
|
|
31
|
+
// Do NOT rely on these for interoperability. They will graduate to the
|
|
32
|
+
// conformance-backed set when the reference implementation ships them and
|
|
33
|
+
// vectors are added to synoi-conformance.
|
|
34
|
+
| 'gap:break_glass_token' // DRAFT: break-glass elevated access token
|
|
35
|
+
| 'gap:local_override_credential' // DRAFT: local policy override credential
|
|
36
|
+
| 'gap:lca_root' // DRAFT: lowest-common-ancestor trust root
|
|
37
|
+
| 'gap:erasure_event' // DRAFT: GDPR-style data erasure audit record
|
|
38
|
+
| 'gap:orchestration_chain' // DRAFT: agent delegation chain (Item 1)
|
|
39
|
+
| 'gap:consent_record' // DRAFT: consent version chain (Item 4)
|
|
40
|
+
| 'gap:pip_response' // DRAFT: signed PIP response (Item 7)
|
|
41
|
+
|
|
42
|
+
/** Current GAP wire version. CDROs that don't match this version are
|
|
43
|
+
* rejected by validators. */
|
|
44
|
+
export const GAP_VERSION = '1.0' as const
|
|
45
|
+
export type GapVersion = typeof GAP_VERSION
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Fields excluded from the OID hash input (they are NOT part of the canonical
|
|
49
|
+
* body): `oid`, `gap_version`, `signature`, `signature_key_id`, `supersedes`.
|
|
50
|
+
*
|
|
51
|
+
* Any future addition to this exclusion set constitutes a protocol version
|
|
52
|
+
* bump. Implementors must strip all five fields before passing the object to
|
|
53
|
+
* `computeGapOid` / `canonicalize`.
|
|
54
|
+
*/
|
|
55
|
+
export interface GapCdroEnvelope<TBody> {
|
|
56
|
+
/** Content-addressed identifier: `sha256:<hex>` over canonical body. */
|
|
57
|
+
oid: string
|
|
58
|
+
/** Object type discriminator, e.g. `gap:capability_grant`. */
|
|
59
|
+
type: GapObjectType
|
|
60
|
+
/** Wire version of the GAP protocol. */
|
|
61
|
+
gap_version: GapVersion
|
|
62
|
+
/** Tenant scope. CDROs never cross tenant boundaries implicitly. */
|
|
63
|
+
tenant_id: string
|
|
64
|
+
/** Server-clock millisecond timestamp at envelope construction. */
|
|
65
|
+
created_at_ms: number
|
|
66
|
+
/** Actor OID that created this CDRO. */
|
|
67
|
+
created_by: string
|
|
68
|
+
/** Type-specific payload. */
|
|
69
|
+
body: TBody
|
|
70
|
+
/** Optional Ed25519 signature, base64-encoded. */
|
|
71
|
+
signature?: string
|
|
72
|
+
/** Identifier of the public key that produced `signature`. */
|
|
73
|
+
signature_key_id?: string
|
|
74
|
+
/** OID of a prior CDRO that this one replaces. */
|
|
75
|
+
supersedes?: string
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Payload shape passed to `computeGapOid` -- the envelope minus oid +
|
|
80
|
+
* gap_version + signature fields. Useful when builders are constructing a
|
|
81
|
+
* CDRO step-by-step.
|
|
82
|
+
*
|
|
83
|
+
* This is the canonical input shape for `computeGapOid` -- it already excludes
|
|
84
|
+
* `oid`, `gap_version`, `signature`, `signature_key_id`, and `supersedes`.
|
|
85
|
+
*/
|
|
86
|
+
export interface GapOidPayload<TBody> {
|
|
87
|
+
type: GapObjectType
|
|
88
|
+
tenant_id: string
|
|
89
|
+
created_at_ms: number
|
|
90
|
+
created_by: string
|
|
91
|
+
body: TBody
|
|
92
|
+
}
|
package/src/channels.ts
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* channels.ts -- channel taxonomy + adapter interface.
|
|
3
|
+
*
|
|
4
|
+
* Channels bridge GAP's abstract `actions`/`listen` model to concrete
|
|
5
|
+
* delivery surfaces (SMS, mobile push, home assistant, etc.).
|
|
6
|
+
*
|
|
7
|
+
* This file declares only the TYPES. Adapter implementations live in
|
|
8
|
+
* downstream packages (the gateway, vendor SDKs). The interface here is the
|
|
9
|
+
* contract those implementations satisfy.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { GapCdroEnvelope } from './cdro.js'
|
|
13
|
+
|
|
14
|
+
/** Built-in channel kinds. The `(string & {})` branch keeps the union open
|
|
15
|
+
* for vendor-specific extensions (e.g. 'com.example.pager') while
|
|
16
|
+
* preserving autocomplete on the canonical entries.
|
|
17
|
+
*
|
|
18
|
+
* Connectivity categories:
|
|
19
|
+
* Internet: sms, voice, email, slack, mobile_push
|
|
20
|
+
* LAN/Internet: sse, webhook
|
|
21
|
+
* Local: in_app, game_engine, home_assistant, desktop_overlay
|
|
22
|
+
* Air-gapped: local_terminal, hmi_panel, opc_ua_ack, local_signed_token
|
|
23
|
+
*/
|
|
24
|
+
export type ChannelKind =
|
|
25
|
+
// Internet channels
|
|
26
|
+
| 'sms'
|
|
27
|
+
| 'voice'
|
|
28
|
+
| 'email'
|
|
29
|
+
| 'slack'
|
|
30
|
+
| 'mobile_push'
|
|
31
|
+
// LAN/Internet channels
|
|
32
|
+
| 'sse'
|
|
33
|
+
| 'webhook'
|
|
34
|
+
// Local channels
|
|
35
|
+
| 'in_app'
|
|
36
|
+
| 'game_engine'
|
|
37
|
+
| 'home_assistant'
|
|
38
|
+
| 'desktop_overlay'
|
|
39
|
+
// Air-gapped / local enforcement point channels
|
|
40
|
+
| 'local_terminal'
|
|
41
|
+
| 'hmi_panel'
|
|
42
|
+
| 'opc_ua_ack'
|
|
43
|
+
| 'local_signed_token'
|
|
44
|
+
// Extensible for custom adapters; use reverse-domain prefix e.g. 'com.example.pager'
|
|
45
|
+
| (string & {})
|
|
46
|
+
|
|
47
|
+
/** Subset of ChannelKind containing only the canonical (literal) values.
|
|
48
|
+
* Useful for exhaustive switches in code that only handles built-ins. */
|
|
49
|
+
export type CanonicalChannelKind =
|
|
50
|
+
| 'sms'
|
|
51
|
+
| 'voice'
|
|
52
|
+
| 'email'
|
|
53
|
+
| 'slack'
|
|
54
|
+
| 'mobile_push'
|
|
55
|
+
| 'sse'
|
|
56
|
+
| 'webhook'
|
|
57
|
+
| 'in_app'
|
|
58
|
+
| 'game_engine'
|
|
59
|
+
| 'home_assistant'
|
|
60
|
+
| 'desktop_overlay'
|
|
61
|
+
| 'local_terminal'
|
|
62
|
+
| 'hmi_panel'
|
|
63
|
+
| 'opc_ua_ack'
|
|
64
|
+
| 'local_signed_token'
|
|
65
|
+
|
|
66
|
+
/** Ordered list of canonical channels, useful for menu UIs + tests. */
|
|
67
|
+
export const CANONICAL_CHANNEL_KINDS: readonly CanonicalChannelKind[] = [
|
|
68
|
+
'sms',
|
|
69
|
+
'voice',
|
|
70
|
+
'email',
|
|
71
|
+
'slack',
|
|
72
|
+
'mobile_push',
|
|
73
|
+
'sse',
|
|
74
|
+
'webhook',
|
|
75
|
+
'in_app',
|
|
76
|
+
'game_engine',
|
|
77
|
+
'home_assistant',
|
|
78
|
+
'desktop_overlay',
|
|
79
|
+
'local_terminal',
|
|
80
|
+
'hmi_panel',
|
|
81
|
+
'opc_ua_ack',
|
|
82
|
+
'local_signed_token',
|
|
83
|
+
] as const
|
|
84
|
+
|
|
85
|
+
// -- Stage primitives that reference channels --------------------------------
|
|
86
|
+
|
|
87
|
+
export interface StageAction {
|
|
88
|
+
channel: ChannelKind
|
|
89
|
+
method: string
|
|
90
|
+
params: Record<string, unknown>
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface StageTransitionTarget {
|
|
94
|
+
next_stage_id?: string
|
|
95
|
+
bind?: Record<string, string>
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface StageListen {
|
|
99
|
+
channel: ChannelKind
|
|
100
|
+
intent?: string
|
|
101
|
+
pattern?: string
|
|
102
|
+
event_kind?: string
|
|
103
|
+
next: StageTransitionTarget
|
|
104
|
+
/**
|
|
105
|
+
* When set, the gateway MUST verify that the channel event's `from` field
|
|
106
|
+
* matches this value before accepting it as a valid stage signal. For SMS
|
|
107
|
+
* channels this is the operator's registered phone number (E.164 format).
|
|
108
|
+
* For webhook channels this is the expected sender identity string.
|
|
109
|
+
*
|
|
110
|
+
* Required for stages that govern physical_safety=true or safety_class C
|
|
111
|
+
* capabilities. Absent means no sender-identity check is performed.
|
|
112
|
+
*/
|
|
113
|
+
required_from_binding?: string
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// -- ChannelEvent CDRO -------------------------------------------------------
|
|
117
|
+
|
|
118
|
+
export interface ChannelEventBody {
|
|
119
|
+
channel: ChannelKind
|
|
120
|
+
event_kind: string
|
|
121
|
+
payload: Record<string, unknown>
|
|
122
|
+
observed_at_ms: number
|
|
123
|
+
/** Workflow context if this event originated from / is routed to a workflow. */
|
|
124
|
+
workflow_instance_oid?: string
|
|
125
|
+
stage_id?: string
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export type ChannelEvent = GapCdroEnvelope<ChannelEventBody>
|
|
129
|
+
|
|
130
|
+
// -- ChannelAdapter interface ------------------------------------------------
|
|
131
|
+
|
|
132
|
+
export interface AdapterContext {
|
|
133
|
+
tenant_id: string
|
|
134
|
+
workflow_instance_oid: string
|
|
135
|
+
stage_id: string
|
|
136
|
+
scope_variables: Record<string, unknown>
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface ActionResult {
|
|
140
|
+
ok: boolean
|
|
141
|
+
detail?: string
|
|
142
|
+
/** OID of a channel event spawned by the action, if any. */
|
|
143
|
+
spawned_event_oid?: string
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export interface ListenHandle {
|
|
147
|
+
cancel(): void
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Interface that every channel adapter must implement. Adapter
|
|
152
|
+
* implementations are out of scope for this types package -- they live in
|
|
153
|
+
* synoi-gateway and downstream packages.
|
|
154
|
+
*/
|
|
155
|
+
export interface ChannelAdapter {
|
|
156
|
+
/** Channel kind this adapter handles. */
|
|
157
|
+
kind: ChannelKind
|
|
158
|
+
|
|
159
|
+
/** Adapter capabilities -- which GAP listen/action shapes it supports. */
|
|
160
|
+
supports: {
|
|
161
|
+
actions: string[]
|
|
162
|
+
listens: Array<'intent' | 'pattern' | 'event_kind'>
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Execute an action. Returns when complete or errors. */
|
|
166
|
+
performAction(spec: StageAction, context: AdapterContext): Promise<ActionResult>
|
|
167
|
+
|
|
168
|
+
/** Arm a listener. Returns a handle that can be cancelled. */
|
|
169
|
+
armListen(
|
|
170
|
+
spec: StageListen,
|
|
171
|
+
context: AdapterContext,
|
|
172
|
+
onMatch: (event: ChannelEvent) => void,
|
|
173
|
+
): ListenHandle
|
|
174
|
+
|
|
175
|
+
/** Health check. */
|
|
176
|
+
health(): Promise<{ ok: boolean; detail?: string }>
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export interface ChannelRegistry {
|
|
180
|
+
register(adapter: ChannelAdapter): void
|
|
181
|
+
get(kind: ChannelKind): ChannelAdapter | null
|
|
182
|
+
list(): ChannelAdapter[]
|
|
183
|
+
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* constants.ts -- well-known capability OIDs + channel kinds.
|
|
3
|
+
*
|
|
4
|
+
* Reserved OID strings for the GAP platform. These identifiers are stable
|
|
5
|
+
* across all conforming gateway implementations. Third-party implementations
|
|
6
|
+
* MUST NOT redefine them.
|
|
7
|
+
*
|
|
8
|
+
* - DISCOVERY_QUERY_CAPABILITY reserved for /by-grant discovery queries
|
|
9
|
+
* - SKILL_CREATE_CAPABILITY reserved for skill manifest upload
|
|
10
|
+
* - VOICE_JOIN_CAPABILITY reserved for voice bridge authorization
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { CanonicalChannelKind } from './channels.js'
|
|
14
|
+
|
|
15
|
+
// -- Well-known capability names (dotted taxonomy) ---------------------------
|
|
16
|
+
|
|
17
|
+
/** Capability that authorizes /by-grant discovery queries. */
|
|
18
|
+
export const DISCOVERY_QUERY_CAPABILITY = 'gap.discovery.query' as const
|
|
19
|
+
|
|
20
|
+
/** Capability that authorizes skill creation (skill manifest upload). */
|
|
21
|
+
export const SKILL_CREATE_CAPABILITY = 'skill.create' as const
|
|
22
|
+
|
|
23
|
+
/** Capability that authorizes joining a voice bridge call. */
|
|
24
|
+
export const VOICE_JOIN_CAPABILITY = 'gap.voice.join' as const
|
|
25
|
+
|
|
26
|
+
/** All well-known capability names, useful for tests + audit dashboards. */
|
|
27
|
+
export const WELL_KNOWN_CAPABILITIES = [
|
|
28
|
+
DISCOVERY_QUERY_CAPABILITY,
|
|
29
|
+
SKILL_CREATE_CAPABILITY,
|
|
30
|
+
VOICE_JOIN_CAPABILITY,
|
|
31
|
+
] as const
|
|
32
|
+
|
|
33
|
+
export type WellKnownCapability = typeof WELL_KNOWN_CAPABILITIES[number]
|
|
34
|
+
|
|
35
|
+
// -- Channel kind constants (mirror canonical list) --------------------------
|
|
36
|
+
|
|
37
|
+
export const CHANNEL_VOICE: CanonicalChannelKind = 'voice'
|
|
38
|
+
export const CHANNEL_SMS: CanonicalChannelKind = 'sms'
|
|
39
|
+
export const CHANNEL_SLACK: CanonicalChannelKind = 'slack'
|
|
40
|
+
export const CHANNEL_MOBILE_PUSH: CanonicalChannelKind = 'mobile_push'
|
|
41
|
+
export const CHANNEL_HOME_ASSISTANT: CanonicalChannelKind = 'home_assistant'
|
|
42
|
+
export const CHANNEL_DESKTOP_OVERLAY: CanonicalChannelKind = 'desktop_overlay'
|
|
43
|
+
export const CHANNEL_EMAIL: CanonicalChannelKind = 'email'
|
|
44
|
+
export const CHANNEL_IN_APP: CanonicalChannelKind = 'in_app'
|
|
45
|
+
export const CHANNEL_GAME_ENGINE: CanonicalChannelKind = 'game_engine'
|
|
46
|
+
export const CHANNEL_WEBHOOK: CanonicalChannelKind = 'webhook'
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @synoi/gap -- public surface.
|
|
3
|
+
*
|
|
4
|
+
* Apache-2.0 TypeScript types + runtime validators for SynOI's
|
|
5
|
+
* GAP (Governed Action Protocol).
|
|
6
|
+
*
|
|
7
|
+
* The protocol itself is open under CC0; this package ships the wire-format
|
|
8
|
+
* types so any GAP implementation (third-party gateway, audit tool, vendor
|
|
9
|
+
* SDK) can speak the same wire format from a single source of truth.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// -- CDRO envelope -----------------------------------------------------------
|
|
13
|
+
export {
|
|
14
|
+
GAP_VERSION,
|
|
15
|
+
} from './cdro.js'
|
|
16
|
+
export type {
|
|
17
|
+
GapCdroEnvelope,
|
|
18
|
+
GapObjectType,
|
|
19
|
+
GapOidPayload,
|
|
20
|
+
GapVersion,
|
|
21
|
+
} from './cdro.js'
|
|
22
|
+
|
|
23
|
+
// -- Capabilities (declarations, grants, invocations) ------------------------
|
|
24
|
+
export {
|
|
25
|
+
capabilityMatches,
|
|
26
|
+
} from './capabilities.js'
|
|
27
|
+
export type {
|
|
28
|
+
GapActorType,
|
|
29
|
+
Capability,
|
|
30
|
+
CapabilityDeclaration,
|
|
31
|
+
CapabilityDeclarationBody,
|
|
32
|
+
CapabilityGrant,
|
|
33
|
+
CapabilityGrantBody,
|
|
34
|
+
CapabilityInvocation,
|
|
35
|
+
CapabilityInvocationBody,
|
|
36
|
+
CapabilityPredicate,
|
|
37
|
+
GrantedCapabilityScope,
|
|
38
|
+
// Item 1: Agent Delegation Chain
|
|
39
|
+
DelegationStep,
|
|
40
|
+
OrchestrationChainBody,
|
|
41
|
+
// Item 2: MCP Tool-Call Governance
|
|
42
|
+
McpToolCallContext,
|
|
43
|
+
// Item 3: Token Budget Governance
|
|
44
|
+
TokenBudgetArgs,
|
|
45
|
+
// Item 4: Consent Version Chain
|
|
46
|
+
ConsentRecordBody,
|
|
47
|
+
// Item 5: Identity Binding
|
|
48
|
+
CredentialKind,
|
|
49
|
+
IdentityBinding,
|
|
50
|
+
// Item 6: Compartment-Based Access Scoping (fields added to existing types)
|
|
51
|
+
// Item 7: Signed PIP Response
|
|
52
|
+
ExternalPipArgs,
|
|
53
|
+
PipResponseBody,
|
|
54
|
+
} from './capabilities.js'
|
|
55
|
+
|
|
56
|
+
// -- Channels ----------------------------------------------------------------
|
|
57
|
+
export {
|
|
58
|
+
CANONICAL_CHANNEL_KINDS,
|
|
59
|
+
} from './channels.js'
|
|
60
|
+
export type {
|
|
61
|
+
ActionResult,
|
|
62
|
+
AdapterContext,
|
|
63
|
+
CanonicalChannelKind,
|
|
64
|
+
ChannelAdapter,
|
|
65
|
+
ChannelEvent,
|
|
66
|
+
ChannelEventBody,
|
|
67
|
+
ChannelKind,
|
|
68
|
+
ChannelRegistry,
|
|
69
|
+
ListenHandle,
|
|
70
|
+
StageAction,
|
|
71
|
+
StageListen,
|
|
72
|
+
StageTransitionTarget,
|
|
73
|
+
} from './channels.js'
|
|
74
|
+
|
|
75
|
+
// -- Workflows ---------------------------------------------------------------
|
|
76
|
+
export type {
|
|
77
|
+
OptionalEffect,
|
|
78
|
+
StageSafety,
|
|
79
|
+
StageTransition,
|
|
80
|
+
StageTransitionBody,
|
|
81
|
+
StageTransitionReason,
|
|
82
|
+
WorkflowDefinition,
|
|
83
|
+
WorkflowDefinitionBody,
|
|
84
|
+
WorkflowInstance,
|
|
85
|
+
WorkflowInstanceBody,
|
|
86
|
+
WorkflowStage,
|
|
87
|
+
WorkflowStageDefinition,
|
|
88
|
+
WorkflowTrigger,
|
|
89
|
+
WorkflowTriggerKind,
|
|
90
|
+
} from './workflows.js'
|
|
91
|
+
|
|
92
|
+
// -- Receipts + failures -----------------------------------------------------
|
|
93
|
+
export {
|
|
94
|
+
isGapFailure,
|
|
95
|
+
} from './receipts.js'
|
|
96
|
+
export type {
|
|
97
|
+
GapDecisionReceipt,
|
|
98
|
+
GapDecisionReceiptBody,
|
|
99
|
+
GapFailure,
|
|
100
|
+
GapFailureReason,
|
|
101
|
+
DecisionStatus,
|
|
102
|
+
DecisionSubjectKind,
|
|
103
|
+
// Item 3: Token Budget Governance
|
|
104
|
+
TokenConsumption,
|
|
105
|
+
} from './receipts.js'
|
|
106
|
+
|
|
107
|
+
// -- Revocations -------------------------------------------------------------
|
|
108
|
+
export {
|
|
109
|
+
revokeGapObject,
|
|
110
|
+
} from './revocations.js'
|
|
111
|
+
export type {
|
|
112
|
+
RevocationEvent,
|
|
113
|
+
RevocationEventBody,
|
|
114
|
+
RevocationTargetKind,
|
|
115
|
+
} from './revocations.js'
|
|
116
|
+
|
|
117
|
+
// -- Constants ---------------------------------------------------------------
|
|
118
|
+
export {
|
|
119
|
+
CHANNEL_DESKTOP_OVERLAY,
|
|
120
|
+
CHANNEL_EMAIL,
|
|
121
|
+
CHANNEL_GAME_ENGINE,
|
|
122
|
+
CHANNEL_HOME_ASSISTANT,
|
|
123
|
+
CHANNEL_IN_APP,
|
|
124
|
+
CHANNEL_MOBILE_PUSH,
|
|
125
|
+
CHANNEL_SLACK,
|
|
126
|
+
CHANNEL_SMS,
|
|
127
|
+
CHANNEL_VOICE,
|
|
128
|
+
CHANNEL_WEBHOOK,
|
|
129
|
+
DISCOVERY_QUERY_CAPABILITY,
|
|
130
|
+
SKILL_CREATE_CAPABILITY,
|
|
131
|
+
VOICE_JOIN_CAPABILITY,
|
|
132
|
+
WELL_KNOWN_CAPABILITIES,
|
|
133
|
+
} from './constants.js'
|
|
134
|
+
export type {
|
|
135
|
+
WellKnownCapability,
|
|
136
|
+
} from './constants.js'
|
|
137
|
+
|
|
138
|
+
// -- OID + canonicalize ------------------------------------------------------
|
|
139
|
+
export {
|
|
140
|
+
computeGapOid,
|
|
141
|
+
} from './oid.js'
|
|
142
|
+
export {
|
|
143
|
+
canonicalize,
|
|
144
|
+
} from './canonicalize.js'
|
|
145
|
+
|
|
146
|
+
// -- Validators --------------------------------------------------------------
|
|
147
|
+
export type {
|
|
148
|
+
ValidationResult,
|
|
149
|
+
} from './validate.js'
|
|
150
|
+
export {
|
|
151
|
+
validateGapDecisionReceipt,
|
|
152
|
+
validateGapDecisionReceiptBody,
|
|
153
|
+
validateCapabilityDeclaration,
|
|
154
|
+
validateCapabilityDeclarationBody,
|
|
155
|
+
validateCapabilityGrant,
|
|
156
|
+
validateCapabilityGrantBody,
|
|
157
|
+
validateCapabilityInvocation,
|
|
158
|
+
validateCapabilityInvocationBody,
|
|
159
|
+
validateChannelEvent,
|
|
160
|
+
validateChannelEventBody,
|
|
161
|
+
validateRevocationEvent,
|
|
162
|
+
validateRevocationEventBody,
|
|
163
|
+
validateStageTransition,
|
|
164
|
+
validateStageTransitionBody,
|
|
165
|
+
validateWorkflowDefinition,
|
|
166
|
+
validateWorkflowDefinitionBody,
|
|
167
|
+
validateWorkflowInstance,
|
|
168
|
+
validateWorkflowInstanceBody,
|
|
169
|
+
// Item 1: Agent Delegation Chain
|
|
170
|
+
validateOrchestrationChainBody,
|
|
171
|
+
validateOrchestrationChain,
|
|
172
|
+
// Item 3: Token Budget Governance
|
|
173
|
+
validateTokenConsumption,
|
|
174
|
+
// Item 4: Consent Version Chain
|
|
175
|
+
validateConsentRecordBody,
|
|
176
|
+
validateConsentRecord,
|
|
177
|
+
// Item 7: Signed PIP Response
|
|
178
|
+
validatePipResponseBody,
|
|
179
|
+
validatePipResponse,
|
|
180
|
+
} from './validate.js'
|
package/src/oid.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* oid.ts -- OID computation for GAP CDROs.
|
|
3
|
+
*
|
|
4
|
+
* Implements RFC 8785 JCS canonical JSON. See IMPLEMENTING.md §2.2 for the
|
|
5
|
+
* normative rules.
|
|
6
|
+
*
|
|
7
|
+
* sha256(canonicalize(envelope_minus_excluded_fields))
|
|
8
|
+
*
|
|
9
|
+
* Excluded fields (stripped before hashing): oid, gap_version, signature,
|
|
10
|
+
* signature_key_id, supersedes. Signatures are added after OID computation.
|
|
11
|
+
*
|
|
12
|
+
* The shape passed in is the OID payload: `{ type, tenant_id, created_at_ms,
|
|
13
|
+
* created_by, body }`. The full envelope (with oid + gap_version) is built
|
|
14
|
+
* around it.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { sha256 } from '@noble/hashes/sha256'
|
|
18
|
+
import { canonicalize } from './canonicalize.js'
|
|
19
|
+
|
|
20
|
+
/** Convert a Uint8Array to a lowercase hex string. */
|
|
21
|
+
function bytesToHex(bytes: Uint8Array): string {
|
|
22
|
+
const hex: string[] = []
|
|
23
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
24
|
+
const b = bytes[i] as number
|
|
25
|
+
hex.push((b >>> 4).toString(16))
|
|
26
|
+
hex.push((b & 0x0f).toString(16))
|
|
27
|
+
}
|
|
28
|
+
return hex.join('')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Fields excluded from the OID hash. These are present in the full envelope
|
|
33
|
+
* but MUST NOT contribute to the content hash. Strip them before hashing so
|
|
34
|
+
* that TypeScript and Python produce byte-identical OIDs regardless of whether
|
|
35
|
+
* the caller passes a pre-stripped payload or a full envelope.
|
|
36
|
+
*/
|
|
37
|
+
const EXCLUDED_FIELDS = new Set([
|
|
38
|
+
'oid',
|
|
39
|
+
'gap_version',
|
|
40
|
+
'signature',
|
|
41
|
+
'signature_key_id',
|
|
42
|
+
'supersedes',
|
|
43
|
+
])
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Compute the OID of a GAP CDRO payload.
|
|
47
|
+
*
|
|
48
|
+
* Accepts either a pre-stripped payload or a full envelope (with oid,
|
|
49
|
+
* gap_version, signature, signature_key_id, supersedes present). The 5
|
|
50
|
+
* excluded fields are stripped before canonicalization so both forms produce
|
|
51
|
+
* the same OID.
|
|
52
|
+
*
|
|
53
|
+
* @param body - the OID payload or full envelope (see CDRO §2.1 in GAP_SPEC).
|
|
54
|
+
* @returns the canonical OID string `"sha256:<hex>"`.
|
|
55
|
+
*/
|
|
56
|
+
export function computeGapOid(body: unknown): string {
|
|
57
|
+
let stripped: unknown = body
|
|
58
|
+
if (body !== null && typeof body === 'object' && !Array.isArray(body)) {
|
|
59
|
+
const obj = body as Record<string, unknown>
|
|
60
|
+
const result: Record<string, unknown> = {}
|
|
61
|
+
for (const key of Object.keys(obj)) {
|
|
62
|
+
if (!EXCLUDED_FIELDS.has(key)) {
|
|
63
|
+
result[key] = obj[key]
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
stripped = result
|
|
67
|
+
}
|
|
68
|
+
const canonical = canonicalize(stripped)
|
|
69
|
+
const digest = sha256(new TextEncoder().encode(canonical))
|
|
70
|
+
return 'sha256:' + bytesToHex(digest)
|
|
71
|
+
}
|