@humanagencyp/hap-core 0.4.0 → 0.4.2

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/dist/index.d.mts CHANGED
@@ -113,13 +113,14 @@ interface CumulativeFieldDef {
113
113
  type ExecutionContextFieldDef = DeclaredFieldDef | CumulativeFieldDef;
114
114
  /**
115
115
  * Gate question definition.
116
+ * @deprecated v0.4 uses a single intent gate with no profile-specific questions.
116
117
  */
117
118
  interface GateQuestion {
118
119
  question: string;
119
120
  required: boolean;
120
121
  }
121
122
  /**
122
- * Execution path definition within a profile.
123
+ * @deprecated Execution paths removed in v0.4. Kept for backward compatibility.
123
124
  */
124
125
  interface ExecutionPath {
125
126
  description: string;
@@ -166,12 +167,17 @@ interface AgentProfile {
166
167
  executionContextSchema: {
167
168
  fields: Record<string, ExecutionContextFieldDef>;
168
169
  };
169
- executionPaths: Record<string, ExecutionPath>;
170
+ /** @deprecated Execution paths removed in v0.4. Kept for backward compatibility. */
171
+ executionPaths?: Record<string, ExecutionPath>;
170
172
  requiredGates: string[];
171
- gateQuestions: {
172
- problem: GateQuestion;
173
- objective: GateQuestion;
174
- tradeoffs: GateQuestion;
173
+ /**
174
+ * v0.4: no gateQuestions — intent prompt is universal, defined in the gateway UI.
175
+ * v0.3: profile-specific gate questions (deprecated).
176
+ */
177
+ gateQuestions?: {
178
+ problem?: GateQuestion;
179
+ objective?: GateQuestion;
180
+ tradeoffs?: GateQuestion;
175
181
  };
176
182
  ttl: {
177
183
  default: number;
package/dist/index.d.ts CHANGED
@@ -113,13 +113,14 @@ interface CumulativeFieldDef {
113
113
  type ExecutionContextFieldDef = DeclaredFieldDef | CumulativeFieldDef;
114
114
  /**
115
115
  * Gate question definition.
116
+ * @deprecated v0.4 uses a single intent gate with no profile-specific questions.
116
117
  */
117
118
  interface GateQuestion {
118
119
  question: string;
119
120
  required: boolean;
120
121
  }
121
122
  /**
122
- * Execution path definition within a profile.
123
+ * @deprecated Execution paths removed in v0.4. Kept for backward compatibility.
123
124
  */
124
125
  interface ExecutionPath {
125
126
  description: string;
@@ -166,12 +167,17 @@ interface AgentProfile {
166
167
  executionContextSchema: {
167
168
  fields: Record<string, ExecutionContextFieldDef>;
168
169
  };
169
- executionPaths: Record<string, ExecutionPath>;
170
+ /** @deprecated Execution paths removed in v0.4. Kept for backward compatibility. */
171
+ executionPaths?: Record<string, ExecutionPath>;
170
172
  requiredGates: string[];
171
- gateQuestions: {
172
- problem: GateQuestion;
173
- objective: GateQuestion;
174
- tradeoffs: GateQuestion;
173
+ /**
174
+ * v0.4: no gateQuestions — intent prompt is universal, defined in the gateway UI.
175
+ * v0.3: profile-specific gate questions (deprecated).
176
+ */
177
+ gateQuestions?: {
178
+ problem?: GateQuestion;
179
+ objective?: GateQuestion;
180
+ tradeoffs?: GateQuestion;
175
181
  };
176
182
  ttl: {
177
183
  default: number;
package/dist/index.js CHANGED
@@ -309,21 +309,13 @@ async function verify(request, publicKeyHex, now = Math.floor(Date.now() / 1e3),
309
309
  }
310
310
  }
311
311
  async function verifyV3(request, profile, publicKeyHex, now, executionLog, errors) {
312
- const pathId = request.frame.path;
313
- if (typeof pathId !== "string") {
314
- return { approved: false, errors: [{ code: "INVALID_PROFILE", message: "Missing path in frame" }] };
315
- }
316
- const executionPath = profile.executionPaths[pathId];
317
- if (!executionPath) {
318
- return { approved: false, errors: [{ code: "INVALID_PROFILE", message: `Unknown execution path: ${pathId}` }] };
319
- }
320
312
  let expectedFrameHash;
321
313
  try {
322
314
  expectedFrameHash = computeFrameHash(request.frame, profile);
323
315
  } catch (err) {
324
316
  return { approved: false, errors: [{ code: "FRAME_MISMATCH", message: `Frame hash computation failed: ${err}` }] };
325
317
  }
326
- const requiredDomains = executionPath.requiredDomains ?? [];
318
+ const requiredDomains = [];
327
319
  const coveredDomains = /* @__PURE__ */ new Set();
328
320
  for (const blob of request.attestations) {
329
321
  let attestation;
@@ -379,15 +371,7 @@ async function verifyV3(request, profile, publicKeyHex, now, executionLog, error
379
371
  async function verifyV4(request, profile, publicKeyHex, now, executionLog) {
380
372
  const errors = [];
381
373
  const bounds = request.frame;
382
- const context = request.context ?? {};
383
- const pathId = bounds.path;
384
- if (typeof pathId !== "string") {
385
- return { approved: false, errors: [{ code: "INVALID_PROFILE", message: "Missing path in bounds" }] };
386
- }
387
- const executionPath = profile.executionPaths[pathId];
388
- if (!executionPath) {
389
- return { approved: false, errors: [{ code: "INVALID_PROFILE", message: `Unknown execution path: ${pathId}` }] };
390
- }
374
+ const context = request.context;
391
375
  let expectedBoundsHash;
392
376
  let expectedContextHash;
393
377
  try {
@@ -395,12 +379,14 @@ async function verifyV4(request, profile, publicKeyHex, now, executionLog) {
395
379
  } catch (err) {
396
380
  return { approved: false, errors: [{ code: "BOUNDS_MISMATCH", message: `Bounds hash computation failed: ${err}` }] };
397
381
  }
398
- try {
399
- expectedContextHash = computeContextHash(context, profile);
400
- } catch (err) {
401
- return { approved: false, errors: [{ code: "CONTEXT_MISMATCH", message: `Context hash computation failed: ${err}` }] };
382
+ if (context && Object.keys(context).length > 0) {
383
+ try {
384
+ expectedContextHash = computeContextHash(context, profile);
385
+ } catch (err) {
386
+ return { approved: false, errors: [{ code: "CONTEXT_MISMATCH", message: `Context hash computation failed: ${err}` }] };
387
+ }
402
388
  }
403
- const requiredDomains = executionPath.requiredDomains ?? [];
389
+ const requiredDomains = [];
404
390
  const coveredDomains = /* @__PURE__ */ new Set();
405
391
  for (const blob of request.attestations) {
406
392
  let attestation;
@@ -422,7 +408,7 @@ async function verifyV4(request, profile, publicKeyHex, now, executionLog) {
422
408
  errors.push({ code: "BOUNDS_MISMATCH", message: "Attestation bounds_hash does not match computed bounds_hash" });
423
409
  continue;
424
410
  }
425
- if (isV4Attestation(attestation)) {
411
+ if (isV4Attestation(attestation) && expectedContextHash) {
426
412
  try {
427
413
  verifyContextHash(attestation, expectedContextHash);
428
414
  } catch {
@@ -459,7 +445,7 @@ async function verifyV4(request, profile, publicKeyHex, now, executionLog) {
459
445
  if (boundsErrors.length > 0) {
460
446
  return { approved: false, errors: boundsErrors };
461
447
  }
462
- if (profile.contextSchema && Object.keys(profile.contextSchema.fields).length > 0) {
448
+ if (context && profile.contextSchema && Object.keys(profile.contextSchema.fields).length > 0) {
463
449
  const contextErrors = checkContextConstraints(context, request.execution, profile);
464
450
  if (contextErrors.length > 0) {
465
451
  return { approved: false, errors: contextErrors };
@@ -603,12 +589,12 @@ function checkContextConstraints(context, execution, profile) {
603
589
  function resolveCumulativeFields(request, profile, executionLog, now) {
604
590
  const errors = [];
605
591
  const profileId = String(request.frame.profile);
606
- const path = String(request.frame.path);
607
592
  const boundsOrFrame = request.frame;
608
593
  for (const [fieldName, fieldDef] of Object.entries(profile.executionContextSchema.fields)) {
609
594
  if (fieldDef.source !== "cumulative") continue;
610
595
  const cumDef = fieldDef;
611
596
  const { cumulativeField, window: windowType } = cumDef;
597
+ const path = request.frame.path ? String(request.frame.path) : "";
612
598
  const runningTotal = executionLog.sumByWindow(profileId, path, cumulativeField, windowType, now);
613
599
  let currentContribution;
614
600
  if (cumulativeField === "_count") {
package/dist/index.mjs CHANGED
@@ -247,21 +247,13 @@ async function verify(request, publicKeyHex, now = Math.floor(Date.now() / 1e3),
247
247
  }
248
248
  }
249
249
  async function verifyV3(request, profile, publicKeyHex, now, executionLog, errors) {
250
- const pathId = request.frame.path;
251
- if (typeof pathId !== "string") {
252
- return { approved: false, errors: [{ code: "INVALID_PROFILE", message: "Missing path in frame" }] };
253
- }
254
- const executionPath = profile.executionPaths[pathId];
255
- if (!executionPath) {
256
- return { approved: false, errors: [{ code: "INVALID_PROFILE", message: `Unknown execution path: ${pathId}` }] };
257
- }
258
250
  let expectedFrameHash;
259
251
  try {
260
252
  expectedFrameHash = computeFrameHash(request.frame, profile);
261
253
  } catch (err) {
262
254
  return { approved: false, errors: [{ code: "FRAME_MISMATCH", message: `Frame hash computation failed: ${err}` }] };
263
255
  }
264
- const requiredDomains = executionPath.requiredDomains ?? [];
256
+ const requiredDomains = [];
265
257
  const coveredDomains = /* @__PURE__ */ new Set();
266
258
  for (const blob of request.attestations) {
267
259
  let attestation;
@@ -317,15 +309,7 @@ async function verifyV3(request, profile, publicKeyHex, now, executionLog, error
317
309
  async function verifyV4(request, profile, publicKeyHex, now, executionLog) {
318
310
  const errors = [];
319
311
  const bounds = request.frame;
320
- const context = request.context ?? {};
321
- const pathId = bounds.path;
322
- if (typeof pathId !== "string") {
323
- return { approved: false, errors: [{ code: "INVALID_PROFILE", message: "Missing path in bounds" }] };
324
- }
325
- const executionPath = profile.executionPaths[pathId];
326
- if (!executionPath) {
327
- return { approved: false, errors: [{ code: "INVALID_PROFILE", message: `Unknown execution path: ${pathId}` }] };
328
- }
312
+ const context = request.context;
329
313
  let expectedBoundsHash;
330
314
  let expectedContextHash;
331
315
  try {
@@ -333,12 +317,14 @@ async function verifyV4(request, profile, publicKeyHex, now, executionLog) {
333
317
  } catch (err) {
334
318
  return { approved: false, errors: [{ code: "BOUNDS_MISMATCH", message: `Bounds hash computation failed: ${err}` }] };
335
319
  }
336
- try {
337
- expectedContextHash = computeContextHash(context, profile);
338
- } catch (err) {
339
- return { approved: false, errors: [{ code: "CONTEXT_MISMATCH", message: `Context hash computation failed: ${err}` }] };
320
+ if (context && Object.keys(context).length > 0) {
321
+ try {
322
+ expectedContextHash = computeContextHash(context, profile);
323
+ } catch (err) {
324
+ return { approved: false, errors: [{ code: "CONTEXT_MISMATCH", message: `Context hash computation failed: ${err}` }] };
325
+ }
340
326
  }
341
- const requiredDomains = executionPath.requiredDomains ?? [];
327
+ const requiredDomains = [];
342
328
  const coveredDomains = /* @__PURE__ */ new Set();
343
329
  for (const blob of request.attestations) {
344
330
  let attestation;
@@ -360,7 +346,7 @@ async function verifyV4(request, profile, publicKeyHex, now, executionLog) {
360
346
  errors.push({ code: "BOUNDS_MISMATCH", message: "Attestation bounds_hash does not match computed bounds_hash" });
361
347
  continue;
362
348
  }
363
- if (isV4Attestation(attestation)) {
349
+ if (isV4Attestation(attestation) && expectedContextHash) {
364
350
  try {
365
351
  verifyContextHash(attestation, expectedContextHash);
366
352
  } catch {
@@ -397,7 +383,7 @@ async function verifyV4(request, profile, publicKeyHex, now, executionLog) {
397
383
  if (boundsErrors.length > 0) {
398
384
  return { approved: false, errors: boundsErrors };
399
385
  }
400
- if (profile.contextSchema && Object.keys(profile.contextSchema.fields).length > 0) {
386
+ if (context && profile.contextSchema && Object.keys(profile.contextSchema.fields).length > 0) {
401
387
  const contextErrors = checkContextConstraints(context, request.execution, profile);
402
388
  if (contextErrors.length > 0) {
403
389
  return { approved: false, errors: contextErrors };
@@ -541,12 +527,12 @@ function checkContextConstraints(context, execution, profile) {
541
527
  function resolveCumulativeFields(request, profile, executionLog, now) {
542
528
  const errors = [];
543
529
  const profileId = String(request.frame.profile);
544
- const path = String(request.frame.path);
545
530
  const boundsOrFrame = request.frame;
546
531
  for (const [fieldName, fieldDef] of Object.entries(profile.executionContextSchema.fields)) {
547
532
  if (fieldDef.source !== "cumulative") continue;
548
533
  const cumDef = fieldDef;
549
534
  const { cumulativeField, window: windowType } = cumDef;
535
+ const path = request.frame.path ? String(request.frame.path) : "";
550
536
  const runningTotal = executionLog.sumByWindow(profileId, path, cumulativeField, windowType, now);
551
537
  let currentContribution;
552
538
  if (cumulativeField === "_count") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@humanagencyp/hap-core",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Core types, cryptographic primitives, and verification logic for the Human Agency Protocol",
5
5
  "license": "MIT",
6
6
  "repository": {
package/src/gatekeeper.ts CHANGED
@@ -94,16 +94,6 @@ async function verifyV3(
94
94
  executionLog: ExecutionLogQuery | undefined,
95
95
  errors: GatekeeperError[],
96
96
  ): Promise<GatekeeperResult> {
97
- const pathId = request.frame.path;
98
- if (typeof pathId !== 'string') {
99
- return { approved: false, errors: [{ code: 'INVALID_PROFILE', message: 'Missing path in frame' }] };
100
- }
101
-
102
- const executionPath = profile.executionPaths[pathId];
103
- if (!executionPath) {
104
- return { approved: false, errors: [{ code: 'INVALID_PROFILE', message: `Unknown execution path: ${pathId}` }] };
105
- }
106
-
107
97
  let expectedFrameHash: string;
108
98
  try {
109
99
  expectedFrameHash = computeFrameHash(request.frame, profile);
@@ -111,8 +101,8 @@ async function verifyV3(
111
101
  return { approved: false, errors: [{ code: 'FRAME_MISMATCH', message: `Frame hash computation failed: ${err}` }] };
112
102
  }
113
103
 
114
- // Verify attestations
115
- const requiredDomains = executionPath.requiredDomains ?? [];
104
+ // Verify attestations (domains come from SP group config in v0.4, not from profile)
105
+ const requiredDomains: string[] = [];
116
106
  const coveredDomains = new Set<string>();
117
107
 
118
108
  for (const blob of request.attestations) {
@@ -191,21 +181,11 @@ async function verifyV4(
191
181
 
192
182
  // In v0.4 the `frame` param carries bounds; `context` carries context params
193
183
  const bounds = request.frame as AgentBoundsParams;
194
- const context: AgentContextParams = request.context ?? {};
195
-
196
- const pathId = bounds.path;
197
- if (typeof pathId !== 'string') {
198
- return { approved: false, errors: [{ code: 'INVALID_PROFILE', message: 'Missing path in bounds' }] };
199
- }
200
-
201
- const executionPath = profile.executionPaths[pathId];
202
- if (!executionPath) {
203
- return { approved: false, errors: [{ code: 'INVALID_PROFILE', message: `Unknown execution path: ${pathId}` }] };
204
- }
184
+ const context: AgentContextParams | undefined = request.context;
205
185
 
206
186
  // Compute expected hashes
207
187
  let expectedBoundsHash: string;
208
- let expectedContextHash: string;
188
+ let expectedContextHash: string | undefined;
209
189
 
210
190
  try {
211
191
  expectedBoundsHash = computeBoundsHash(bounds, profile);
@@ -213,14 +193,18 @@ async function verifyV4(
213
193
  return { approved: false, errors: [{ code: 'BOUNDS_MISMATCH', message: `Bounds hash computation failed: ${err}` }] };
214
194
  }
215
195
 
216
- try {
217
- expectedContextHash = computeContextHash(context, profile);
218
- } catch (err) {
219
- return { approved: false, errors: [{ code: 'CONTEXT_MISMATCH', message: `Context hash computation failed: ${err}` }] };
196
+ // Context hash is only computed when context is explicitly provided.
197
+ // At execution time, context is not re-verified — it was checked at authorization time.
198
+ if (context && Object.keys(context).length > 0) {
199
+ try {
200
+ expectedContextHash = computeContextHash(context, profile);
201
+ } catch (err) {
202
+ return { approved: false, errors: [{ code: 'CONTEXT_MISMATCH', message: `Context hash computation failed: ${err}` }] };
203
+ }
220
204
  }
221
205
 
222
- // Verify attestations (requiredDomains may be undefined in v0.4 — domains come from SP group config)
223
- const requiredDomains = executionPath.requiredDomains ?? [];
206
+ // Verify attestations (domains come from SP group config, not profile)
207
+ const requiredDomains: string[] = [];
224
208
  const coveredDomains = new Set<string>();
225
209
 
226
210
  for (const blob of request.attestations) {
@@ -247,8 +231,8 @@ async function verifyV4(
247
231
  continue;
248
232
  }
249
233
 
250
- // Verify context hash (only for v0.4 attestations that have context_hash)
251
- if (isV4Attestation(attestation)) {
234
+ // Verify context hash (only when context was provided and hash was computed)
235
+ if (isV4Attestation(attestation) && expectedContextHash) {
252
236
  try {
253
237
  verifyContextHash(attestation, expectedContextHash);
254
238
  } catch {
@@ -295,7 +279,7 @@ async function verifyV4(
295
279
  }
296
280
 
297
281
  // Check context constraints using contextSchema
298
- if (profile.contextSchema && Object.keys(profile.contextSchema.fields).length > 0) {
282
+ if (context && profile.contextSchema && Object.keys(profile.contextSchema.fields).length > 0) {
299
283
  const contextErrors = checkContextConstraints(context, request.execution, profile);
300
284
  if (contextErrors.length > 0) {
301
285
  return { approved: false, errors: contextErrors };
@@ -517,9 +501,8 @@ function resolveCumulativeFields(
517
501
  ): GatekeeperError[] {
518
502
  const errors: GatekeeperError[] = [];
519
503
 
520
- // Profile ID and path come from bounds (v0.4) or frame (v0.3)
504
+ // Profile ID comes from bounds (v0.4) or frame (v0.3)
521
505
  const profileId = String(request.frame.profile);
522
- const path = String(request.frame.path);
523
506
 
524
507
  // For v0.4, the bounds source (request.frame) holds the cumulative max fields
525
508
  const boundsOrFrame = request.frame;
@@ -530,6 +513,7 @@ function resolveCumulativeFields(
530
513
  const cumDef = fieldDef as CumulativeFieldDef;
531
514
  const { cumulativeField, window: windowType } = cumDef;
532
515
 
516
+ const path = request.frame.path ? String(request.frame.path) : '';
533
517
  const runningTotal = executionLog.sumByWindow(profileId, path, cumulativeField, windowType, now);
534
518
 
535
519
  let currentContribution: number;
package/src/types.ts CHANGED
@@ -130,6 +130,7 @@ export type ExecutionContextFieldDef = DeclaredFieldDef | CumulativeFieldDef;
130
130
 
131
131
  /**
132
132
  * Gate question definition.
133
+ * @deprecated v0.4 uses a single intent gate with no profile-specific questions.
133
134
  */
134
135
  export interface GateQuestion {
135
136
  question: string;
@@ -137,11 +138,11 @@ export interface GateQuestion {
137
138
  }
138
139
 
139
140
  /**
140
- * Execution path definition within a profile.
141
+ * @deprecated Execution paths removed in v0.4. Kept for backward compatibility.
141
142
  */
142
143
  export interface ExecutionPath {
143
144
  description: string;
144
- requiredDomains?: string[]; // v0.3: defined in profile. v0.4: moved to SP group config.
145
+ requiredDomains?: string[];
145
146
  ttl?: { default: number; max: number };
146
147
  }
147
148
 
@@ -187,14 +188,19 @@ export interface AgentProfile {
187
188
  fields: Record<string, ExecutionContextFieldDef>;
188
189
  };
189
190
 
190
- executionPaths: Record<string, ExecutionPath>;
191
+ /** @deprecated Execution paths removed in v0.4. Kept for backward compatibility. */
192
+ executionPaths?: Record<string, ExecutionPath>;
191
193
 
192
194
  requiredGates: string[];
193
195
 
194
- gateQuestions: {
195
- problem: GateQuestion;
196
- objective: GateQuestion;
197
- tradeoffs: GateQuestion;
196
+ /**
197
+ * v0.4: no gateQuestions — intent prompt is universal, defined in the gateway UI.
198
+ * v0.3: profile-specific gate questions (deprecated).
199
+ */
200
+ gateQuestions?: {
201
+ problem?: GateQuestion;
202
+ objective?: GateQuestion;
203
+ tradeoffs?: GateQuestion;
198
204
  };
199
205
 
200
206
  ttl: { default: number; max: number };