@guuey/hooks 0.26.0 → 0.28.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/README.md CHANGED
@@ -40,6 +40,26 @@ Prebuilt hooks (`use: 'email-reporter'`) and dev-defined ones share this spec.
40
40
  `@guuey/config`.
41
41
  - `toWireToolName('server.tool')` → `mcp__server__tool` — the one function
42
42
  both config names and the runtime's tool extractor call.
43
+ - An agent definition's `tools` is required and exact: `[]` mounts nothing
44
+ (a summariser that only writes its output); there is no "absent = every
45
+ declared server" default.
46
+ - An agent definition's optional `output` is a JSON Schema for the run's
47
+ structured output (the runtime hands it to the model's structured-output
48
+ mode); a `required` tool the model skipped is called with that output.
49
+ - `PREBUILT_DEFINITIONS` / `prebuiltBinding(name, event)` — the prebuilt
50
+ catalog, ONE copy for the runtime and the dispatcher, keyed per event.
51
+ `email-reporter`: on `session.ended` an agent run with `tools: []` writes
52
+ the report as its `output` and the platform makes the call — the pod never
53
+ mounts a first-party server for a hook run. On `handoff.requested` it binds
54
+ nothing (`{ kind: 'skip' }`): the platform's notifier already mails every
55
+ hand-off whenever the reporter is on, so a declaration there is legal and
56
+ simply recorded `skipped`.
57
+ - `hookInvokeRequestSchema` / `hookInvokeResultSchema` / `HOOK_DOOR_PATH` —
58
+ the wire between the dispatcher and the runtime's hook door
59
+ (`POST <pod origin>/agent/hook`): `{ runId, name, event, timeoutMs }` in,
60
+ `{ runId, status: ok|failed, output?, effects, error? }` out. The pod
61
+ resolves the hook by `name` from its own snapshot or the prebuilt catalog,
62
+ never from the wire.
43
63
 
44
64
  Everything is optional both ways across a rolling release: a `guuey.json`
45
65
  without `hooks` behaves exactly as before.
package/dist/index.d.ts CHANGED
@@ -61,6 +61,11 @@ export type JsonValue = JsonPrimitive | JsonValue[] | {
61
61
  };
62
62
  /** A JSON-shaped value — the hook's `output` and a reserved event's `data`. */
63
63
  export declare const jsonValueSchema: z.ZodType<JsonValue>;
64
+ /** A JSON object — a tool call's arguments, a hook's structured output when a tool must be called with it. */
65
+ export type JsonObject = {
66
+ [key: string]: JsonValue;
67
+ };
68
+ export declare const jsonObjectSchema: z.ZodType<JsonObject>;
64
69
  /** `"<server>.<tool>"` — the name a config uses for one tool. */
65
70
  export declare const toolNameSchema: z.ZodString;
66
71
  /** `"<server>.<tool>"` or `"<server>.*"` — an allowlist entry. */
@@ -225,6 +230,29 @@ export declare const hookHandlerRefSchema: z.ZodUnion<readonly [z.ZodObject<{
225
230
  definition: z.ZodString;
226
231
  }, z.core.$strict>]>;
227
232
  export type HookHandlerRef = z.infer<typeof hookHandlerRefSchema>;
233
+ declare const agentDefinitionSchema: z.ZodObject<{
234
+ kind: z.ZodLiteral<"agent">;
235
+ on: z.ZodArray<z.ZodEnum<{
236
+ "session.ended": "session.ended";
237
+ "handoff.requested": "handoff.requested";
238
+ "session.idled": "session.idled";
239
+ "turn.completed": "turn.completed";
240
+ "session.started": "session.started";
241
+ "turn.start": "turn.start";
242
+ "schedule.tick": "schedule.tick";
243
+ }>>;
244
+ instruction: z.ZodString;
245
+ tools: z.ZodArray<z.ZodString>;
246
+ required: z.ZodOptional<z.ZodArray<z.ZodString>>;
247
+ model: z.ZodOptional<z.ZodEnum<{
248
+ default: "default";
249
+ small: "small";
250
+ }>>;
251
+ maxTurns: z.ZodOptional<z.ZodNumber>;
252
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
253
+ output: z.ZodOptional<z.ZodType<JsonValue, unknown, z.core.$ZodTypeInternals<JsonValue, unknown>>>;
254
+ actAs: z.ZodOptional<z.ZodLiteral<"hook">>;
255
+ }, z.core.$strict>;
228
256
  export declare const hookDefinitionSchema: z.ZodUnion<readonly [z.ZodObject<{
229
257
  kind: z.ZodLiteral<"agent">;
230
258
  on: z.ZodArray<z.ZodEnum<{
@@ -237,7 +265,7 @@ export declare const hookDefinitionSchema: z.ZodUnion<readonly [z.ZodObject<{
237
265
  "schedule.tick": "schedule.tick";
238
266
  }>>;
239
267
  instruction: z.ZodString;
240
- tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
268
+ tools: z.ZodArray<z.ZodString>;
241
269
  required: z.ZodOptional<z.ZodArray<z.ZodString>>;
242
270
  model: z.ZodOptional<z.ZodEnum<{
243
271
  default: "default";
@@ -245,6 +273,7 @@ export declare const hookDefinitionSchema: z.ZodUnion<readonly [z.ZodObject<{
245
273
  }>>;
246
274
  maxTurns: z.ZodOptional<z.ZodNumber>;
247
275
  timeoutMs: z.ZodOptional<z.ZodNumber>;
276
+ output: z.ZodOptional<z.ZodType<JsonValue, unknown, z.core.$ZodTypeInternals<JsonValue, unknown>>>;
248
277
  actAs: z.ZodOptional<z.ZodLiteral<"hook">>;
249
278
  }, z.core.$strict>, z.ZodObject<{
250
279
  kind: z.ZodLiteral<"tool">;
@@ -260,6 +289,7 @@ export declare const hookDefinitionSchema: z.ZodUnion<readonly [z.ZodObject<{
260
289
  tool: z.ZodString;
261
290
  }, z.core.$strict>]>;
262
291
  export type HookDefinition = z.infer<typeof hookDefinitionSchema>;
292
+ export type AgentHookDefinition = z.infer<typeof agentDefinitionSchema>;
263
293
  export declare const hooksSectionSchema: z.ZodObject<{
264
294
  'session.ended': z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
265
295
  use: z.ZodString;
@@ -343,7 +373,7 @@ export declare const hooksSectionSchema: z.ZodObject<{
343
373
  "schedule.tick": "schedule.tick";
344
374
  }>>;
345
375
  instruction: z.ZodString;
346
- tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
376
+ tools: z.ZodArray<z.ZodString>;
347
377
  required: z.ZodOptional<z.ZodArray<z.ZodString>>;
348
378
  model: z.ZodOptional<z.ZodEnum<{
349
379
  default: "default";
@@ -351,6 +381,7 @@ export declare const hooksSectionSchema: z.ZodObject<{
351
381
  }>>;
352
382
  maxTurns: z.ZodOptional<z.ZodNumber>;
353
383
  timeoutMs: z.ZodOptional<z.ZodNumber>;
384
+ output: z.ZodOptional<z.ZodType<JsonValue, unknown, z.core.$ZodTypeInternals<JsonValue, unknown>>>;
354
385
  actAs: z.ZodOptional<z.ZodLiteral<"hook">>;
355
386
  }, z.core.$strict>, z.ZodObject<{
356
387
  kind: z.ZodLiteral<"tool">;
@@ -381,4 +412,200 @@ export declare const hooksSectionSchema: z.ZodObject<{
381
412
  }, z.core.$strict>>>;
382
413
  }, z.core.$strict>;
383
414
  export type HooksSection = z.infer<typeof hooksSectionSchema>;
415
+ /** Where a live pod answers hook runs: `POST <pod origin>/agent/hook`. */
416
+ export declare const HOOK_DOOR_PATH = "/agent/hook";
417
+ /**
418
+ * What the dispatcher POSTs. Minimal by design: the pod resolves the hook's
419
+ * definition (instruction, tools, required, output, timeout) BY NAME — from
420
+ * its own snapshot for a dev-defined hook, from {@link PREBUILT_DEFINITIONS}
421
+ * for a prebuilt — never from the wire, so nothing on the wire can widen a
422
+ * hook's reach. `timeoutMs` is the dispatcher's wait; the pod caps it by the
423
+ * definition's own.
424
+ */
425
+ export declare const hookInvokeRequestSchema: z.ZodObject<{
426
+ runId: z.ZodString;
427
+ name: z.ZodString;
428
+ event: z.ZodDiscriminatedUnion<[z.ZodObject<{
429
+ type: z.ZodLiteral<"session.ended">;
430
+ data: z.ZodObject<{
431
+ reason: z.ZodLiteral<"idle">;
432
+ idleMinutes: z.ZodNumber;
433
+ lastActivityAt: z.ZodISODateTime;
434
+ turns: z.ZodNumber;
435
+ handoff: z.ZodOptional<z.ZodObject<{
436
+ question: z.ZodString;
437
+ contactEmail: z.ZodOptional<z.ZodString>;
438
+ contactName: z.ZodOptional<z.ZodString>;
439
+ summary: z.ZodOptional<z.ZodString>;
440
+ }, z.core.$strict>>;
441
+ }, z.core.$strict>;
442
+ threadId: z.ZodString;
443
+ id: z.ZodString;
444
+ at: z.ZodISODateTime;
445
+ appId: z.ZodString;
446
+ hook: z.ZodObject<{
447
+ name: z.ZodString;
448
+ runId: z.ZodString;
449
+ }, z.core.$strict>;
450
+ }, z.core.$strict>, z.ZodObject<{
451
+ type: z.ZodLiteral<"handoff.requested">;
452
+ data: z.ZodObject<{
453
+ question: z.ZodString;
454
+ contactEmail: z.ZodOptional<z.ZodString>;
455
+ contactName: z.ZodOptional<z.ZodString>;
456
+ summary: z.ZodOptional<z.ZodString>;
457
+ }, z.core.$strict>;
458
+ threadId: z.ZodString;
459
+ id: z.ZodString;
460
+ at: z.ZodISODateTime;
461
+ appId: z.ZodString;
462
+ hook: z.ZodObject<{
463
+ name: z.ZodString;
464
+ runId: z.ZodString;
465
+ }, z.core.$strict>;
466
+ }, z.core.$strict>, z.ZodObject<{
467
+ type: z.ZodLiteral<"schedule.tick">;
468
+ data: z.ZodObject<{
469
+ schedule: z.ZodString;
470
+ scheduledFor: z.ZodISODateTime;
471
+ }, z.core.$strict>;
472
+ id: z.ZodString;
473
+ at: z.ZodISODateTime;
474
+ appId: z.ZodString;
475
+ hook: z.ZodObject<{
476
+ name: z.ZodString;
477
+ runId: z.ZodString;
478
+ }, z.core.$strict>;
479
+ }, z.core.$strict>, z.ZodObject<{
480
+ type: z.ZodEnum<{
481
+ "session.idled": "session.idled";
482
+ "turn.completed": "turn.completed";
483
+ "session.started": "session.started";
484
+ "turn.start": "turn.start";
485
+ }>;
486
+ data: z.ZodType<JsonValue, unknown, z.core.$ZodTypeInternals<JsonValue, unknown>>;
487
+ threadId: z.ZodString;
488
+ id: z.ZodString;
489
+ at: z.ZodISODateTime;
490
+ appId: z.ZodString;
491
+ hook: z.ZodObject<{
492
+ name: z.ZodString;
493
+ runId: z.ZodString;
494
+ }, z.core.$strict>;
495
+ }, z.core.$strict>], "type">;
496
+ timeoutMs: z.ZodNumber;
497
+ }, z.core.$strict>;
498
+ export type HookInvokeRequest = z.infer<typeof hookInvokeRequestSchema>;
499
+ /** One effect as the POD reports it — `missing` = a `required` tool the model never called (the dispatcher enforces it). */
500
+ export declare const hookDoorEffectSchema: z.ZodObject<{
501
+ tool: z.ZodString;
502
+ status: z.ZodEnum<{
503
+ called: "called";
504
+ failed: "failed";
505
+ missing: "missing";
506
+ }>;
507
+ }, z.core.$strict>;
508
+ export type HookDoorEffect = z.infer<typeof hookDoorEffectSchema>;
509
+ /**
510
+ * What the pod answers, synchronously, with a 200. `ok` / `failed` only — the
511
+ * run's terminal state is the dispatcher's to write (one writer); `deferred`,
512
+ * `parked` and `skipped` are decided on its side. Other faces are HTTP: 503 +
513
+ * `Retry-After` at capacity, 422 `UNKNOWN_HOOK` for a name the pod cannot
514
+ * resolve, 401/403 for a principal that is not this app's hook.
515
+ *
516
+ * NOT strict on purpose (the rolling-release rule): the pod may add fields
517
+ * (`stopReason`, `timedOut`, …) the dispatcher does not read yet; a reader
518
+ * never refuses a key the other side started sending. The REQUEST stays
519
+ * strict — nothing on it may widen a hook's reach.
520
+ */
521
+ export declare const hookInvokeResultSchema: z.ZodObject<{
522
+ runId: z.ZodString;
523
+ status: z.ZodEnum<{
524
+ failed: "failed";
525
+ ok: "ok";
526
+ }>;
527
+ output: z.ZodOptional<z.ZodType<JsonValue, unknown, z.core.$ZodTypeInternals<JsonValue, unknown>>>;
528
+ effects: z.ZodDefault<z.ZodArray<z.ZodObject<{
529
+ tool: z.ZodString;
530
+ status: z.ZodEnum<{
531
+ called: "called";
532
+ failed: "failed";
533
+ missing: "missing";
534
+ }>;
535
+ }, z.core.$strict>>>;
536
+ error: z.ZodOptional<z.ZodString>;
537
+ }, z.core.$strip>;
538
+ export type HookInvokeResult = z.infer<typeof hookInvokeResultSchema>;
539
+ /**
540
+ * A prebuilt bound to an event as ONE tool call, no model: the dispatcher
541
+ * calls `tool` on the FIRST-PARTY hosted server `serverId` (a registry id —
542
+ * never the app's `mcpServers` map; the gateway still requires the app's
543
+ * grant) with `args(event)`. `undefined` from `args` = this event carries
544
+ * nothing for the tool → the run is `skipped`, not failed.
545
+ */
546
+ export interface PrebuiltToolBinding {
547
+ readonly kind: 'tool';
548
+ readonly serverId: string;
549
+ readonly tool: string;
550
+ args(event: HookEvent): JsonObject | undefined;
551
+ }
552
+ /** An effect the DISPATCHER makes after a prebuilt agent run answers `ok`: `tool` on `serverId` with the run's `output` as the arguments. */
553
+ export interface PrebuiltEffect {
554
+ readonly serverId: string;
555
+ readonly tool: string;
556
+ }
557
+ /**
558
+ * A prebuilt bound to an event as an agent run. "The model writes, the
559
+ * platform calls": the definition mounts no tools (`tools: []`) — its structured `output`
560
+ * (shaped by `definition.output`) is what the dispatcher hands to each
561
+ * {@link PrebuiltEffect}. So the pod never mounts a first-party server for a
562
+ * hook run, and the app's `mcpServers` need not declare guuey's own tools.
563
+ */
564
+ export interface PrebuiltAgentBinding {
565
+ readonly kind: 'agent';
566
+ readonly definition: AgentHookDefinition;
567
+ readonly effects: readonly PrebuiltEffect[];
568
+ }
569
+ /**
570
+ * A prebuilt deliberately bound to NOTHING on an event, with the reason: the
571
+ * declaration stays legal in the block, the dispatcher records the run
572
+ * `skipped` (no claim, no call), the door never sees it. The email reporter
573
+ * on `handoff.requested` is the first: the platform's notifier already mails
574
+ * every hand-off whenever the reporter is on (guuey#1525), so a hook there
575
+ * would mail the owner twice (guuey#1561).
576
+ */
577
+ export interface PrebuiltSkipBinding {
578
+ readonly kind: 'skip';
579
+ readonly reason: PrebuiltSkipReason;
580
+ }
581
+ export type PrebuiltSkipReason = 'notifier-mails-handoffs';
582
+ export type PrebuiltBinding = PrebuiltToolBinding | PrebuiltAgentBinding | PrebuiltSkipBinding;
583
+ export declare function isPrebuiltHookName(name: string): name is PrebuiltHookName;
584
+ /** The report the email reporter's tool takes (`report_conversation`'s input): the shape both prebuilt bindings produce. */
585
+ export declare const conversationReportSchema: z.ZodObject<{
586
+ summary: z.ZodString;
587
+ wantedHuman: z.ZodOptional<z.ZodBoolean>;
588
+ contactEmail: z.ZodOptional<z.ZodString>;
589
+ contactName: z.ZodOptional<z.ZodString>;
590
+ }, z.core.$strict>;
591
+ export type ConversationReport = z.infer<typeof conversationReportSchema>;
592
+ /** `report_conversation`'s input as a JSON Schema — the reporter's `output` contract for the session-end run. */
593
+ export declare const CONVERSATION_REPORT_JSON_SCHEMA: JsonObject;
594
+ /**
595
+ * The prebuilt catalog, keyed per event (guuey#1537). A prebuilt with no
596
+ * binding for an event the app declared it on is `prebuilt-not-served` on
597
+ * the dispatcher's side and `UNKNOWN_HOOK` at the door — never a guess.
598
+ *
599
+ * `email-reporter` (guuey#1511): on `session.ended` nothing has summarised
600
+ * the conversation yet — a tool-less agent run writes the report as its
601
+ * structured output and the dispatcher makes the call. On
602
+ * `handoff.requested` it binds NOTHING, on purpose: the platform's notifier
603
+ * mails every hand-off (with the rep's summary, guuey#1510) whenever the
604
+ * reporter is on, no hook needed — a binding there mailed twice
605
+ * (guuey#1561); the declaration stays legal and is recorded `skipped`.
606
+ */
607
+ export declare const PREBUILT_DEFINITIONS: Readonly<Record<PrebuiltHookName, Partial<Readonly<Record<HookEventName, PrebuiltBinding>>>>>;
608
+ /** The binding a prebuilt has for an event, if any. */
609
+ export declare function prebuiltBinding(name: PrebuiltHookName, event: HookEventName): PrebuiltBinding | undefined;
610
+ export {};
384
611
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -58,6 +58,7 @@ export const jsonValueSchema = z.lazy(() => z.union([
58
58
  z.array(jsonValueSchema),
59
59
  z.record(z.string(), jsonValueSchema),
60
60
  ]));
61
+ export const jsonObjectSchema = z.record(z.string(), jsonValueSchema);
61
62
  // ── Tool names ────────────────────────────────────────────────────────
62
63
  /** `guuey.json` mcpServers keys: the same grammar `@guuey/config` accepts (non-empty, no dots — the dot separates server from tool). */
63
64
  const SERVER_KEY = /^[A-Za-z0-9_-]+$/;
@@ -113,15 +114,21 @@ function hookNameSchemaRef() {
113
114
  return hookNameSchema;
114
115
  }
115
116
  // ── The envelope in (§8.2) ────────────────────────────────────────────
117
+ /**
118
+ * Id caps: an event id is `<threadId>#<event>#<seq | ISO instant>` and a run
119
+ * id adds `#<hookName>` — with a 128-char threadId, a 24-char instant and a
120
+ * 64-char name that is ~231 chars, so 320 leaves room; 128 refused valid ids.
121
+ */
122
+ const ID_MAX = 320;
116
123
  const hookRefSchema = z.strictObject({
117
124
  /** The hook the platform is dispatching (a prebuilt name or a `definitions` key). */
118
125
  name: z.string().min(1).max(64),
119
126
  /** The HookRun id — the idempotency key of this delivery. */
120
- runId: z.string().min(1).max(128),
127
+ runId: z.string().min(1).max(ID_MAX),
121
128
  });
122
129
  const baseEvent = {
123
130
  /** The event id — dedupe on it; deliveries are at-least-once and unordered. */
124
- id: z.string().min(1).max(128),
131
+ id: z.string().min(1).max(ID_MAX),
125
132
  at: z.iso.datetime({ offset: true }),
126
133
  appId: z.string().min(1).max(128),
127
134
  hook: hookRefSchema,
@@ -199,18 +206,33 @@ const agentDefinitionSchema = z
199
206
  on: z.array(hookEventNameSchema).min(1),
200
207
  /** The hook's system instruction — what to do with the conversation and the event. */
201
208
  instruction: z.string().min(1).max(8000),
202
- /** Tools the run may call (`server.tool` | `server.*`); absent = the app's declared servers. */
203
- tools: z.array(toolPatternSchema).max(64).optional(),
209
+ /**
210
+ * Tools the run may call (`server.tool` | `server.*`) — REQUIRED, and
211
+ * exactly what is declared: `[]` = the run mounts nothing (a summariser
212
+ * that only writes its `output`). There is no "absent = every declared
213
+ * server" default: that would hand a hook the colocated children too
214
+ * (`guuey-handoff`'s `request_human`, `guuey-memory`) without anyone
215
+ * meaning it (infra's #1529 seam review).
216
+ */
217
+ tools: z.array(toolPatternSchema).max(64),
204
218
  /** Effects that MUST happen; the Router calls them with the structured output if the model did not. */
205
219
  required: z.array(toolNameSchema).max(16).optional(),
206
220
  model: z.enum(['small', 'default']).optional(),
207
221
  maxTurns: z.number().int().min(1).max(16).optional(),
208
222
  timeoutMs: z.number().int().min(1_000).max(300_000).optional(),
223
+ /**
224
+ * A JSON Schema the run's structured `output` must satisfy — the Router
225
+ * hands it to the adapter's structured-output mode (Claude
226
+ * `outputFormat: json_schema`, OpenAI `output_type`, ADK's parsed text).
227
+ * Absent = free-form output. What a `required` effect is called with when
228
+ * the model skipped it, and what a prebuilt's platform-made effect takes.
229
+ */
230
+ output: jsonValueSchema.optional(),
209
231
  /** v1: the hook principal only. The field exists so the enum can widen without a shape change. */
210
232
  actAs: z.literal('hook').optional(),
211
233
  })
212
234
  .superRefine((d, ctx) => {
213
- if (!d.tools || !d.required)
235
+ if (!d.required)
214
236
  return;
215
237
  for (const [i, req] of d.required.entries()) {
216
238
  if (!d.tools.some((p) => toolPatternCovers(p, req))) {
@@ -294,3 +316,113 @@ export const hooksSectionSchema = z
294
316
  }
295
317
  }
296
318
  });
319
+ // ── The runtime's hook door (§8.4 — the wire between the dispatcher and the pod) ──
320
+ /** Where a live pod answers hook runs: `POST <pod origin>/agent/hook`. */
321
+ export const HOOK_DOOR_PATH = '/agent/hook';
322
+ /**
323
+ * What the dispatcher POSTs. Minimal by design: the pod resolves the hook's
324
+ * definition (instruction, tools, required, output, timeout) BY NAME — from
325
+ * its own snapshot for a dev-defined hook, from {@link PREBUILT_DEFINITIONS}
326
+ * for a prebuilt — never from the wire, so nothing on the wire can widen a
327
+ * hook's reach. `timeoutMs` is the dispatcher's wait; the pod caps it by the
328
+ * definition's own.
329
+ */
330
+ export const hookInvokeRequestSchema = z.strictObject({
331
+ runId: z.string().min(1).max(ID_MAX),
332
+ name: hookNameSchema,
333
+ event: hookEventSchema,
334
+ timeoutMs: z.number().int().min(1_000).max(300_000),
335
+ });
336
+ /** One effect as the POD reports it — `missing` = a `required` tool the model never called (the dispatcher enforces it). */
337
+ export const hookDoorEffectSchema = z.strictObject({
338
+ tool: toolNameSchema,
339
+ status: z.enum(['called', 'failed', 'missing']),
340
+ });
341
+ /**
342
+ * What the pod answers, synchronously, with a 200. `ok` / `failed` only — the
343
+ * run's terminal state is the dispatcher's to write (one writer); `deferred`,
344
+ * `parked` and `skipped` are decided on its side. Other faces are HTTP: 503 +
345
+ * `Retry-After` at capacity, 422 `UNKNOWN_HOOK` for a name the pod cannot
346
+ * resolve, 401/403 for a principal that is not this app's hook.
347
+ *
348
+ * NOT strict on purpose (the rolling-release rule): the pod may add fields
349
+ * (`stopReason`, `timedOut`, …) the dispatcher does not read yet; a reader
350
+ * never refuses a key the other side started sending. The REQUEST stays
351
+ * strict — nothing on it may widen a hook's reach.
352
+ */
353
+ export const hookInvokeResultSchema = z.object({
354
+ runId: z.string().min(1).max(ID_MAX),
355
+ status: z.enum(['ok', 'failed']),
356
+ output: jsonValueSchema.optional(),
357
+ effects: z.array(hookDoorEffectSchema).default([]),
358
+ error: z.string().max(2000).optional(),
359
+ });
360
+ export function isPrebuiltHookName(name) {
361
+ return PREBUILT_HOOKS.includes(name);
362
+ }
363
+ /** The report the email reporter's tool takes (`report_conversation`'s input): the shape both prebuilt bindings produce. */
364
+ export const conversationReportSchema = z.strictObject({
365
+ summary: z.string().min(1).max(1200),
366
+ wantedHuman: z.boolean().optional(),
367
+ contactEmail: z.string().max(320).optional(),
368
+ contactName: z.string().max(200).optional(),
369
+ });
370
+ /** `report_conversation`'s input as a JSON Schema — the reporter's `output` contract for the session-end run. */
371
+ export const CONVERSATION_REPORT_JSON_SCHEMA = {
372
+ type: 'object',
373
+ additionalProperties: false,
374
+ required: ['summary', 'wantedHuman'],
375
+ properties: {
376
+ summary: {
377
+ type: 'string',
378
+ minLength: 1,
379
+ maxLength: 1200,
380
+ description: "Two to four sentences for the app's owner: what the visitor wanted, what was answered, and what is still open.",
381
+ },
382
+ wantedHuman: { type: 'boolean', description: 'Whether the visitor asked to talk to a person.' },
383
+ contactEmail: { type: 'string', maxLength: 320, description: "The visitor's email address for a follow-up, if they shared one." },
384
+ contactName: { type: 'string', maxLength: 200, description: "The visitor's name, if they shared one." },
385
+ },
386
+ };
387
+ const EMAIL_REPORTER_SERVER_ID = 'email-reporter';
388
+ const EMAIL_REPORTER_TOOL = 'report_conversation';
389
+ /**
390
+ * The prebuilt catalog, keyed per event (guuey#1537). A prebuilt with no
391
+ * binding for an event the app declared it on is `prebuilt-not-served` on
392
+ * the dispatcher's side and `UNKNOWN_HOOK` at the door — never a guess.
393
+ *
394
+ * `email-reporter` (guuey#1511): on `session.ended` nothing has summarised
395
+ * the conversation yet — a tool-less agent run writes the report as its
396
+ * structured output and the dispatcher makes the call. On
397
+ * `handoff.requested` it binds NOTHING, on purpose: the platform's notifier
398
+ * mails every hand-off (with the rep's summary, guuey#1510) whenever the
399
+ * reporter is on, no hook needed — a binding there mailed twice
400
+ * (guuey#1561); the declaration stays legal and is recorded `skipped`.
401
+ */
402
+ export const PREBUILT_DEFINITIONS = {
403
+ 'email-reporter': {
404
+ 'handoff.requested': { kind: 'skip', reason: 'notifier-mails-handoffs' },
405
+ 'session.ended': {
406
+ kind: 'agent',
407
+ definition: {
408
+ kind: 'agent',
409
+ on: ['session.ended'],
410
+ instruction: "The conversation has ended. Write the app owner's report of it as the structured output: `summary` — two to four " +
411
+ 'sentences on what the visitor wanted, what was answered, and what is still open, written from the whole ' +
412
+ 'conversation; `wantedHuman` — whether the visitor asked to talk to a person; `contactEmail` and `contactName` ' +
413
+ 'only if the visitor shared them. Report facts from the transcript; never invent contact details.',
414
+ // Mounts NOTHING: the report is the structured output; the dispatcher makes the one call below.
415
+ tools: [],
416
+ output: CONVERSATION_REPORT_JSON_SCHEMA,
417
+ model: 'small',
418
+ maxTurns: 2,
419
+ timeoutMs: 90_000,
420
+ },
421
+ effects: [{ serverId: EMAIL_REPORTER_SERVER_ID, tool: EMAIL_REPORTER_TOOL }],
422
+ },
423
+ },
424
+ };
425
+ /** The binding a prebuilt has for an event, if any. */
426
+ export function prebuiltBinding(name, event) {
427
+ return PREBUILT_DEFINITIONS[name][event];
428
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guuey/hooks",
3
- "version": "0.26.0",
3
+ "version": "0.28.0",
4
4
  "description": "The guuey hook spec — lifecycle events a hosted agent's platform fires (session.ended, handoff.requested), the JSON envelope in / result out, the two handler kinds (agent | tool), and the hooks block of guuey.json. Types + zod schemas + the per-event class table; no runtime of its own.",
5
5
  "license": "MIT",
6
6
  "type": "module",