@sequenceholdings/managed-functions 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.
Files changed (92) hide show
  1. package/README.md +444 -2
  2. package/dist/abrigo-contract.d.ts +92 -0
  3. package/dist/abrigo-contract.js +55 -0
  4. package/dist/abrigo-response.d.ts +6 -0
  5. package/dist/abrigo-response.js +27 -0
  6. package/dist/abrigo.d.ts +14 -0
  7. package/dist/abrigo.js +150 -0
  8. package/dist/agent-evidence.d.ts +9 -0
  9. package/dist/agent-evidence.js +84 -0
  10. package/dist/agent-source-evidence-contract.d.ts +1197 -0
  11. package/dist/agent-source-evidence-contract.js +96 -0
  12. package/dist/agents-contract.d.ts +99 -0
  13. package/dist/agents-contract.js +69 -0
  14. package/dist/agents.d.ts +15 -0
  15. package/dist/agents.js +121 -0
  16. package/dist/capabilities.d.ts +165 -0
  17. package/dist/capabilities.js +61 -1
  18. package/dist/config.d.ts +18 -2
  19. package/dist/config.js +62 -1
  20. package/dist/deployed-policy.js +102 -0
  21. package/dist/directory-config.d.ts +23 -0
  22. package/dist/directory-config.js +22 -0
  23. package/dist/directory-contract.d.ts +68 -0
  24. package/dist/directory-contract.js +57 -0
  25. package/dist/directory-manifest.d.ts +5 -0
  26. package/dist/directory-manifest.js +10 -0
  27. package/dist/directory.d.ts +15 -0
  28. package/dist/directory.js +65 -0
  29. package/dist/http-tracing.js +3 -0
  30. package/dist/immutable-release-contract.d.ts +3 -0
  31. package/dist/immutable-release-contract.js +12 -0
  32. package/dist/index.d.ts +40 -1
  33. package/dist/index.js +75 -1
  34. package/dist/invocation-client.d.ts +45 -0
  35. package/dist/invocation-client.js +90 -0
  36. package/dist/invocation-contract.d.ts +36 -0
  37. package/dist/invocation-contract.js +48 -0
  38. package/dist/invocation.d.ts +8 -0
  39. package/dist/invocation.js +134 -0
  40. package/dist/lattice-capability.d.ts +5 -0
  41. package/dist/lattice-capability.js +16 -0
  42. package/dist/lattice-provenance-contract.d.ts +519 -0
  43. package/dist/lattice-provenance-contract.js +148 -0
  44. package/dist/lattice-provenance.d.ts +16 -0
  45. package/dist/lattice-provenance.js +167 -0
  46. package/dist/monitoring-annotations.d.ts +53 -0
  47. package/dist/monitoring-annotations.js +54 -0
  48. package/dist/monitoring-contract.d.ts +463 -0
  49. package/dist/monitoring-contract.js +89 -0
  50. package/dist/monitoring-events.d.ts +257 -0
  51. package/dist/monitoring-events.js +136 -0
  52. package/dist/monitoring.d.ts +14 -0
  53. package/dist/monitoring.js +78 -0
  54. package/dist/orm-contract.d.ts +1 -0
  55. package/dist/orm-contract.js +4 -0
  56. package/dist/orm.d.ts +19 -3
  57. package/dist/orm.js +93 -13
  58. package/dist/preflight-abrigo.d.ts +10 -0
  59. package/dist/preflight-abrigo.js +26 -0
  60. package/dist/preflight-agents.d.ts +10 -0
  61. package/dist/preflight-agents.js +51 -0
  62. package/dist/preflight-directory.d.ts +10 -0
  63. package/dist/preflight-directory.js +36 -0
  64. package/dist/preflight-immutable-release.d.ts +10 -0
  65. package/dist/preflight-immutable-release.js +34 -0
  66. package/dist/preflight-invocation.d.ts +10 -0
  67. package/dist/preflight-invocation.js +40 -0
  68. package/dist/preflight-lattice.d.ts +10 -0
  69. package/dist/preflight-lattice.js +40 -0
  70. package/dist/preflight-monitoring.d.ts +10 -0
  71. package/dist/preflight-monitoring.js +46 -0
  72. package/dist/preflight-service-directory.d.ts +10 -0
  73. package/dist/preflight-service-directory.js +26 -0
  74. package/dist/preflight-workflows.d.ts +10 -0
  75. package/dist/preflight-workflows.js +38 -0
  76. package/dist/preflight.js +50 -2
  77. package/dist/processes-contract.d.ts +20 -0
  78. package/dist/processes-contract.js +8 -1
  79. package/dist/processes.d.ts +4 -8
  80. package/dist/processes.js +52 -83
  81. package/dist/runtime.cjs +3387 -259
  82. package/dist/sdk-range.d.ts +1 -0
  83. package/dist/sdk-range.js +25 -0
  84. package/dist/service-directory.d.ts +12 -0
  85. package/dist/service-directory.js +72 -0
  86. package/dist/workflow-scopes-contract.d.ts +8 -0
  87. package/dist/workflow-scopes-contract.js +29 -0
  88. package/dist/workflow-source-contract.d.ts +524 -0
  89. package/dist/workflow-source-contract.js +59 -0
  90. package/dist/workflow-source.d.ts +12 -0
  91. package/dist/workflow-source.js +93 -0
  92. package/package.json +2 -2
package/README.md CHANGED
@@ -1,5 +1,75 @@
1
1
  # `@sequenceholdings/managed-functions`
2
2
 
3
+ ## Group-member directory lookup (internal rollout)
4
+
5
+ Functions can resolve selected members of existing Atlas groups to active,
6
+ canonical user identities. This is an opt-in read capability, not a staff
7
+ registry, permission grant, arbitrary directory search, or authentication API.
8
+
9
+ ```yaml
10
+ capabilities:
11
+ directory:
12
+ groups: ["<existing-group-id>"]
13
+ ```
14
+
15
+ ```ts
16
+ import { createFunctionDirectoryClient } from '@sequenceholdings/managed-functions'
17
+
18
+ const directory = createFunctionDirectoryClient({
19
+ baseUrl: process.env.ATLAS_ORIGIN!,
20
+ request: req,
21
+ })
22
+ const { members } = await directory.resolveMembers({
23
+ groupId,
24
+ subs: [selectedCanonicalSubject],
25
+ })
26
+ ```
27
+
28
+ Use `userIds` instead of `subs` to resolve row IDs selected from the existing
29
+ group picker; never submit both. Each call accepts 1–50 selectors. Missing,
30
+ disabled, tombstoned, wrong-tenant, or nonmember selections are omitted. Exact
31
+ subject lookup does not accept an email alias. For a save, require the selected
32
+ subject to appear exactly in the response; a failed request is not an empty
33
+ successful lookup. Historical display labels may retain inactive identities,
34
+ but cannot validate a new assignment.
35
+
36
+ Atlas supplies a dedicated five-minute `x-sequence-directory-token` only for
37
+ declared groups on direct human invocations. System runners and service accounts
38
+ are not supported. Each read rechecks current function invocation access,
39
+ active version, declared group scope, active caller, caller group visibility,
40
+ and active target identities inside the function's tenant. No service identity
41
+ widens the caller's directory visibility. The function still enforces its own
42
+ application action permissions. Validation is point-in-time, not atomic with
43
+ an application's later transaction.
44
+
45
+ Directory and process-read clients share the SDK's invocation transport and
46
+ `{ request, baseUrl, fetchImpl? }` options. Both accept Fetch `Headers` or Node
47
+ request headers (case-insensitively), reject duplicate credentials, check expiry
48
+ before each read, and validate responses. `FunctionDirectoryReadError` exposes
49
+ `status` and `code`, like `FunctionProcessReadError`; failures are never an empty
50
+ member list. Directory replies must contain only distinct requested identities.
51
+ Sharing transport does not share authority: process reads retain service-account
52
+ credentials, while directory reads retain their separate human-caller credential.
53
+
54
+ The internal SDK bridge is `POST /api/managed-functions/directory/members`;
55
+ ordinary Auth0 bearers, storage tokens and ORM tokens do not authenticate it.
56
+ Results are not cached; successful reads emit an audit event. The client never
57
+ follows redirects or forwards an unrelated credential. Allow the Atlas origin
58
+ in the function's existing egress policy. Reviewed Atlas/CLI and SDK releases
59
+ are required before consumers opt in (SDK 0.4.1+, CLI 0.5.2+, and Atlas
60
+ advertising `directory_members`). Preflight rejects absent/disabled support
61
+ and incompatible tooling before upload; older deployments do not supply the
62
+ header. This addition does not activate or redeploy any consumer. The existing
63
+ package root remains Preview/blocked on publication; the raw HTTP route is
64
+ internal, not a new public API contract.
65
+
66
+ Tenant deployments reuse `ATLAS_ARTIFACT_STUDIO_SIGNING_KEY`, already provided
67
+ by the OpCo secrets catalog for Managed Functions. Delegation uses a separate
68
+ HMAC domain, audience, environment, function version and invocation binding.
69
+ There are no new secrets, migrations, groups or grants. Missing signing
70
+ configuration fails closed, including local development.
71
+
72
+
3
73
  `@sequenceholdings/managed-functions` is the TypeScript SDK for Managed
4
74
  Functions. It provides caller-scoped ORM access, explicit service-account ORM
5
75
  access, and opt-in caching. The
@@ -11,9 +81,32 @@ generic invocation, isolation, secrets, egress, caching, and observability;
11
81
  deployed functions own business logic. Primitive code must not identify a
12
82
  function consumer or branch on a function ID.
13
83
 
84
+ ## Immutable function releases
85
+
86
+ With CLI **0.5.5**, SDK **0.4.2**, and a platform deployment advertising the
87
+ `immutable_release` capability, opt in before a new function's first deployment:
88
+
89
+ ```yaml
90
+ function:
91
+ id: lending-capture-release-20261013
92
+ title: Lending capture release
93
+ immutable_release: true
94
+ ```
95
+
96
+ The first successful activation records a permanent platform flag. That function
97
+ identity cannot subsequently deploy, promote, roll back, change its runtime
98
+ configuration, or redeploy for rotated secret versions. Removing the manifest
99
+ field cannot undo the flag. Create a new function ID for the next release, and
100
+ retain old identities while workflows still reference them. A mutable function
101
+ that already activated cannot be converted into an immutable release.
102
+
103
+ An exact retry of an already successful deployment returns its stored result
104
+ without replacing the runtime. Failed initial deployments remain retryable until
105
+ activation succeeds. Omission or `false` retains existing mutable behavior.
106
+
14
107
  ## ORM as the invoking user
15
108
 
16
- ### Default SQL (planned SDK 0.4.0)
109
+ ### Default SQL (introduced in SDK 0.4.0)
17
110
 
18
111
  With the matching Atlas runtime, use the same caller-scoped executor without
19
112
  an operation manifest:
@@ -112,9 +205,38 @@ fragment or embedded credentials. Loopback HTTP is accepted for a local platform
112
205
  There is no automatic environment URL: the helper never trusts a request header
113
206
  or body to choose where it sends a delegated credential.
114
207
 
208
+ ### Gateway caller metadata
209
+
210
+ The invoke gateway supplies `x-sequence-function-caller`, a base64url JSON object
211
+ with `kind` (`human` or `service`), `sub`, `email`, `functionSlug`, `bodyHash`
212
+ (SHA-256 of the dispatched JSON body), and `exp` (Unix milliseconds). Email is
213
+ nullable. System runners and service accounts receive no human email; the person
214
+ who initiated a workflow is not silently treated as its acting user.
215
+
216
+ This metadata is gateway-owned and requires platform-only authenticated ingress
217
+ (Cloud Run IAM or the authenticated local runtime). It is not a bearer token or
218
+ signature. Do not accept it on a public handler, forward caller-supplied headers,
219
+ or use the hash as authentication. Consumers must check function/body binding
220
+ and expiry. Identity comes from the effective authenticated principal, including
221
+ platform-authorized impersonation, rather than tool arguments. An external
222
+ integration must still verify that this identity owns the vendor account it uses.
223
+
115
224
  ### Caller identity and trust boundary
116
225
 
117
- This helper supports direct human invocations through the platform gateway.
226
+ `createFunctionCallerOrmExecutor` takes the same options and acts as the actual
227
+ invoking human **or** platform service account. It requires the distinct
228
+ gateway-delivered caller credential, explicitly signed as caller mode. Agents
229
+ preserve their authenticated run account; a Lattice node with an explicit
230
+ `run_as.service_account` can use that account's namespace floor, capabilities
231
+ and row policies. A system/M2M run has no caller credential, and a recorded run
232
+ starter is never used as the actor. Missing or denied caller access never falls
233
+ back to the function's attached account. This helper requires a matching
234
+ platform runtime; older deployments fail closed with `MISSING_CALLER_CONTEXT`.
235
+ Caller service accounts are checked for current existence and disabled state
236
+ on every ORM operation. The delegated grant has its own invocation expiry; it
237
+ does not promise to track revocation of the API credential that started it.
238
+
239
+ The legacy `createFunctionOrmExecutor` helper supports direct human invocations through the platform gateway.
118
240
  Scheduled/system-runner and service-account invocations fail closed. They do not
119
241
  silently become an attached service account or a recorded human initiator.
120
242
 
@@ -202,6 +324,18 @@ const page = await processes.listRunsPage({
202
324
  })
203
325
  ```
204
326
 
327
+ With SDK 0.4.2 and both exact process and agent read declarations,
328
+ `processes.getCompletedAgentReference({ process, runId, nodeId })` recovers only
329
+ `{ stepId, attempt, agentId, agentRunId, threadId }` for a unique completed
330
+ top-level agent step in a terminal process run. The parent may have failed,
331
+ timed out or been cancelled after that agent finished. Both live credentials
332
+ must belong to the same receiving invocation. Current Process, Run, Agent, Job
333
+ and Thread visibility, actual source ownership, frozen bundle pins and immutable
334
+ private execution source remain enforced. Active or ambiguous attempts are
335
+ refused. The reference grants no history access and does not assert successful
336
+ business completion; the application still checks its current generation,
337
+ release and lease before using it.
338
+
205
339
  `getRun` returns only `id`, `processId`, `status`, `entityType`, `entityId`,
206
340
  `startedAt` and `completedAt`. Dates are ISO strings; entity fields and
207
341
  `completedAt` may be null. Runtime error text, metadata, state, output and steps
@@ -224,6 +358,171 @@ empty activity list. Reads do not atomically reserve work: applications must
224
358
  still coordinate concurrent starts through a durable claim or idempotency
225
359
  mechanism.
226
360
 
361
+ ## Resolve exact directory principals
362
+
363
+ This addition targets SDK **0.4.2**, CLI **0.5.5**, and a deployment advertising
364
+ `directory_resolve`. Confirm public package availability before installing the
365
+ new versions. Declare exact registered tenant scopes and attach an account:
366
+
367
+ ```yaml
368
+ service_account: app-directory-reader
369
+ capabilities:
370
+ directory:
371
+ resolve: [example_tenant]
372
+ ```
373
+
374
+ ```ts
375
+ import { createFunctionServiceDirectoryClient } from '@sequenceholdings/managed-functions'
376
+
377
+ const directory = createFunctionServiceDirectoryClient({ request, baseUrl: platformOrigin })
378
+ const result = await directory.resolve({
379
+ tenant: 'example_tenant',
380
+ lookups: [{ type: 'email', value: 'reviewer@example.com' }],
381
+ })
382
+ ```
383
+
384
+ Use at most 50 distinct exact email or subject lookups per call and at most 16
385
+ tenant scopes in the declaration. The account needs the separate current
386
+ Directory Reader grant for the requested tenant; directory administration is
387
+ not a substitute. Each call rechecks the current function version, declaration,
388
+ live invocation and account. The client uses only its invocation-scoped
389
+ directory credential and refuses redirects.
390
+
391
+ `resolved` entries contain the original lookup plus `sub`, `email` and `name`;
392
+ `unresolved` contains the remaining lookups. Inactive, deleted, unlinked,
393
+ ambiguous and machine identities are unresolved. Unknown or inaccessible
394
+ tenants return the same authorization error. This is not a searchable user
395
+ listing. `DirectoryPrincipalResolveError` exposes `status` and `code`; a denied,
396
+ unavailable or malformed response must not become an empty resolution result.
397
+
398
+ ## Abrigo reads as the actual caller
399
+
400
+ The upcoming SDK **0.4.2** and CLI **0.5.5** support a deployment-advertised
401
+ `abrigo_read` capability. Declare only the needed finite read operations in
402
+ `capabilities.abrigo.read` and attach a `service_account` for the receiving
403
+ function. `createFunctionCallerAbrigoClient({ request, baseUrl: platformOrigin })`
404
+ provides `read(query)` for `workflow`, `entity`, `entityGroupSummary`,
405
+ `loanRoles`, `relatedLoanRoles`, `workflowLoanMappings`, `workflowDocuments`
406
+ and `customerDocuments`, plus the narrow `reviewWorkflow`,
407
+ `reviewWorkflowDocuments` and `reviewWorkflowCustomerDocuments`. A declaration
408
+ contains 1 to 11 distinct operations.
409
+
410
+ All reads use the invocation's actual human caller, live receiver lease and
411
+ current function Invoker grant. The original eight operations also require that
412
+ person's current Abrigo integration access. The three review operations use the
413
+ native document-read gate, which also admits Underwriting Approvers.
414
+ Service-account and system callers are refused; the attached function account
415
+ never supplies replacement authority.
416
+ The client has no arbitrary path, write, actor or credential parameter.
417
+
418
+ Review queries take only a `workflowId`, plus pagination for documents.
419
+ `reviewWorkflow` returns exactly `{ id, customerId }`. The customer-document
420
+ operation derives the actual workflow customer at the broker; callers cannot
421
+ select a `customerId`. Each response retains authoritative `workflowId` and
422
+ `customerId`, including empty pages. Application code must compare each page's
423
+ customer with the initial workflow customer to detect changes while paging.
424
+ Both document review operations require an object envelope with an `items`
425
+ array bounded by the requested `perPage`; vendor pagination metadata is retained.
426
+
427
+ Collection queries require an explicit `page` from 1 to 100 and `perPage` from
428
+ 1 to 500. Responses are bounded to 1 MiB and refuse malformed JSON, nonfinite
429
+ numbers and unsafe integers, so vendor IDs are never silently rounded.
430
+ Oversized or unavailable reads fail explicitly. Application code still
431
+ validates the response, pagination completeness and business membership.
432
+ `FunctionAbrigoReadError` retains a safe status/code without exposing vendor
433
+ error bodies.
434
+
435
+ ## Verified invocation identity
436
+
437
+ With SDK 0.4.2, CLI 0.5.5 and a host advertising `invocation_identity`, a
438
+ function with an attached `service_account` can opt into its verified receiver
439
+ and actual caller:
440
+
441
+ ```yaml
442
+ capabilities:
443
+ invocation:
444
+ identity: true
445
+ ```
446
+
447
+ ```ts
448
+ import { createFunctionInvocationClient } from '@sequenceholdings/managed-functions'
449
+
450
+ const invocation = createFunctionInvocationClient({ request, baseUrl: platformOrigin })
451
+ const { receiver, caller } = await invocation.getInvocationContext()
452
+ ```
453
+
454
+ The receiver identifies this function, version, invocation, organization and
455
+ attached service account. The caller is the authenticated human, service
456
+ account or system principal that invoked it; the attached account and a
457
+ workflow's recorded starter are not substitutes for that caller. The request
458
+ has no caller-supplied identity selector. Each read verifies the signed
459
+ credential, current invocation, attached account and actual caller's current
460
+ function Invoker access; expired or revoked context fails closed. Identity
461
+ metadata grants no ORM access or domain authority: the handler must check its
462
+ own expected release and business policy, and use a
463
+ separately declared caller or service executor for data access.
464
+
465
+ ## Trusted Lattice execution and human actions
466
+
467
+ Functions invoked by a first-class Lattice node can opt into invocation-scoped
468
+ context reads. Attach a `service_account` and declare:
469
+
470
+ ```yaml
471
+ capabilities:
472
+ lattice:
473
+ context: true
474
+ ```
475
+
476
+ These reads also require the actual caller's current process Viewer and run
477
+ Viewer access. Process Invoker already inherits those Viewer permissions;
478
+ the attached account does not replace the actual caller's authorization.
479
+
480
+ ```ts
481
+ import { createFunctionLatticeClient } from '@sequenceholdings/managed-functions'
482
+
483
+ const lattice = createFunctionLatticeClient({ request, baseUrl: 'https://your-environment.example.com' })
484
+ const execution = await lattice.getExecutionContext()
485
+ const review = await lattice.getHumanCompletion({ nodeId: 'review' })
486
+ const votes = await lattice.getHumanCompletions({ nodeId: 'vote', ancestorNodeId: 'review' })
487
+ const committee = await lattice.getParallelSnapshot({ nodeId: 'committee', ancestorNodeId: 'review' })
488
+ ```
489
+
490
+ `execution.input` is the persisted node mapper output. `execution.ctxSnapshot`
491
+ includes platform-owned run/entity identity and only data explicitly mapped as
492
+ `input.context`; omitted upstream, state and metadata remain hidden. A reviewed
493
+ node can explicitly map `{ context: ctx, input: domainInput }` when it needs the
494
+ full context. Direct function invocations have no Lattice credential.
495
+
496
+ `execution.receiver.invoker` identifies the authenticated caller, including its
497
+ kind and authentication method. `attachedServiceAccountSub` identifies the
498
+ function's separate attached account. Neither is inferred from the run starter;
499
+ the SDK checks both against the invocation credential.
500
+
501
+ `execution.run.startProvenance` describes the verified authorization that
502
+ admitted this pinned run: its actor, start mode and basis, authorization time,
503
+ and scheduler admission when applicable. The platform verifies the persisted
504
+ stamp against the actual run pins and recorded starter; authored trigger reasons
505
+ or context metadata are never substitutes. Older runs may omit this field or
506
+ return `null`. A start stamp records the original admission decision, rather
507
+ than granting current permissions or identifying a later human action.
508
+
509
+ Human reads verify a platform-signed receipt against the actual completed step,
510
+ current receiving invocation, frozen graph and causal subprocess lineage. The
511
+ caller must still check the expected action, task, approval generation and
512
+ business authority. Run starters, `advanced_by`, email and `assertedBy` alone are
513
+ not approval evidence. Forked/copied history, timeouts, stale attempts and revoked
514
+ invocations fail closed; legacy completions without receipts require a fresh
515
+ human action. `getHumanCompletions` returns at most 64 selected causal receipts.
516
+
517
+ For parallel nodes declared with `captureProvenance: true`,
518
+ `getParallelSnapshot` returns the completed join's canonical proof and frozen
519
+ `observedAt`, its causal ancestor receipt, and at most 64 actual branches. Branch
520
+ IDs, indexes, items, step attempts, statuses and human receipt IDs are preserved.
521
+ An early join may include pending branches; later cancellation does not rewrite
522
+ that observation. The SDK preserves the server's proof and timestamp, while the
523
+ platform verifies the signed source rows. The handler still decides whether the
524
+ observed votes satisfy its business policy.
525
+
227
526
  ## Opt-in cache
228
527
 
229
528
  Opt-in caching inside a Managed Function. When the manifest declares a `cache` block, the platform passes a process-reused `context` as the HTTP handler's third argument. Existing two-argument handlers keep working.
@@ -271,6 +570,72 @@ Explicit CRUD throws `CacheError` with `DISABLED`, `UNAVAILABLE`, `UNKNOWN_OUTCO
271
570
 
272
571
  Authors own authorization, read/write choices, and invalidation. A concurrent loader can repopulate a deleted key with old data; use authoritative versioned keys or bypass the cache when freshness is required. Every activation gets a fresh key namespace; old entries expire naturally.
273
572
 
573
+ ## Deliver app-owned monitoring observations
574
+
575
+ Monitoring delivery requires SDK **0.4.2**, CLI **0.5.5**, and a deployment
576
+ advertising `monitoring_deliveries`. Declare the exact producer namespaces:
577
+
578
+ ```yaml
579
+ service_account: app-effects
580
+ capabilities:
581
+ monitoring:
582
+ producers: [inbox, documents]
583
+ ```
584
+
585
+ Declarations grant no monitoring authority. The attached account needs the
586
+ producer namespace's runtime floor and pinned `emit_monitoring` capability.
587
+ Continuous Improvement must also register that exact namespace and authorize
588
+ publishing or catalog reads. Registrations default to inactive with both
589
+ operations disabled. Only a human with current Monitoring Admin authority can
590
+ review and change this consent; owning or creating a namespace cannot approve
591
+ its use of CI. Deployment never creates registrations or grants that consent.
592
+ The namespace and function must belong to the same registered tenant. Every
593
+ call rechecks the current manifest, active version, live invocation reservation,
594
+ account availability, and namespace claims. Bearer, ORM, Storage, and process
595
+ credentials are never accepted as monitoring credentials.
596
+
597
+ Invocation credentials bind the registration ID and epoch. Publish, receipt
598
+ lookup, and catalog reads lock and recheck current consent on their transaction
599
+ connection. Revocation blocks old credentials, including after re-enabling a
600
+ producer. A new invocation can recover an older immutable receipt after renewed
601
+ consent; the receipt retains its original accepted epoch and business payload hash.
602
+
603
+ ```ts
604
+ import {
605
+ createFunctionServiceMonitoringClient,
606
+ monitoringPayloadHash,
607
+ } from '@sequenceholdings/managed-functions'
608
+
609
+ const monitoring = createFunctionServiceMonitoringClient({ request, baseUrl: platformOrigin })
610
+ const payloadHash = monitoringPayloadHash({ events, observation })
611
+ const receipt = await monitoring.publish({ producer: 'inbox', businessKey, payloadHash, events, observation })
612
+ // After an ambiguous response, look up the immutable key before retrying.
613
+ const recovered = await monitoring.get({ producer: 'inbox', businessKey })
614
+ ```
615
+
616
+ The app freezes accepted inputs, timestamps, event keys, and optional interpreted
617
+ CI annotations before computing the hash. Object keys are sorted, array order
618
+ is retained, and contract defaults are applied. `idempotencyKey` on each input
619
+ event identifies one claim within the delivery; Atlas derives a storage key
620
+ from the trusted tenant, namespace, business key, and that claim key. It cannot
621
+ overwrite another producer's or a legacy native event.
622
+
623
+ An identical retry returns the same receipt; changed content under the same
624
+ business key returns **409**. Up to 100 events and 512 KiB per delivery are
625
+ accepted. A legitimate zero-event result still receives a hash-bound receipt.
626
+ `get` throws `FunctionMonitoringError` with status **404** if the key is absent.
627
+ Both publishing and lookup require producer authorization.
628
+
629
+ `getBucketCatalog({ producer })` reads only the complete active taxonomy
630
+ metadata under the separate namespace `read_monitoring_catalog` capability.
631
+ Freeze that catalog with the analysis inputs. More than 2000 buckets or 512 KiB
632
+ returns an explicit size error; the client never treats a partial catalog as complete.
633
+
634
+ `confirmed` means the submitted CI events and supplied product observations
635
+ committed atomically. It does not mean later triage, clustering, or agent analysis
636
+ completed. The source app performs analysis and constructs neutral events;
637
+ intake never reads native Inbox data to interpret a completion or correction.
638
+
274
639
  ## Local development
275
640
 
276
641
  Run `seq-studio functions dev --dir . --port 8080` after installing dependencies. Enabled caching without a Valkey connection uses bounded process-local TTL/LRU storage (16 MiB encoded payloads, 10,000 entries); it is reset on restart and not shared across processes. The harness compiles TypeScript with the project's installed compiler. A deployed enabled cache without connection configuration fails startup; it never falls back silently.
@@ -285,6 +650,83 @@ Cloud Run emits aggregate `managed_function.cache` logs every 30 seconds of acti
285
650
 
286
651
  Functions without a `cache` manifest block retain their existing two-argument handler. An explicit `cache: { enabled: false }` supplies a disabled `context.cache`, allowing `getOrCompute` to run its loader without caching.
287
652
 
653
+ ## Completed agent responses
654
+
655
+ With SDK 0.4.2, CLI 0.5.5 and a deployment advertising `agents_read`, declare
656
+ exact agent IDs in `capabilities.agents.read` and attach a `service_account`.
657
+ Inside each invocation, call
658
+ `createFunctionServiceAgentsClient({ request, baseUrl: platformOrigin }).getCompletedRun({ agent, runId })`.
659
+ The account needs current agent Viewer access. The declaration grants no access
660
+ to another caller's conversations: both the run and private thread must belong
661
+ to the attached account, or be a verified completed upstream agent step in the
662
+ same immutable Lattice execution context.
663
+
664
+ The response contains persisted completed output, assistant text, citation
665
+ resources and a neutral conversation projection for counts and tool-state metrics.
666
+ It excludes reasoning, reasoning-derived compaction, arbitrary tool results and inputs, and private
667
+ thread configuration. Reused conversations and experimental ORM history are
668
+ denied. Verified same-step structured-output re-asks remain readable; messages
669
+ changed after completion are refused. Results
670
+ over 1,000 messages or 8 MiB fail explicitly; a partial response never substitutes
671
+ for a full generated answer. No thread listing or arbitrary thread-ID read exists.
672
+
673
+ For protected application source records, declare the same exact IDs in
674
+ `capabilities.agents.evidence` on a deployment advertising `agents_evidence_verify`.
675
+ `createFunctionServiceAgentsClient(...).captureCompletedRun({ agent, runId })`
676
+ returns `{ response, responseHash, sourceReceipt }` for a completed source owned
677
+ by the attached account. This neutral capture also requires the live read
678
+ credential; it cannot capture another user's source through upstream lineage.
679
+ The signed receipt identifies the actual service account and the invocation that
680
+ captured evidence, separately from the original agent-run creator. Human captures
681
+ identify the actual signed-in user instead. Neither kind grants read access.
682
+
683
+ `createFunctionAgentEvidenceClient({ request, baseUrl: platformOrigin }).verifySourceEvidence({ agent, runId, response, responseHash, sourceReceipt })`
684
+ verifies supplied historical bytes without fetching conversation history. Its
685
+ separate invocation credential rechecks the current function version, attached
686
+ account, exact evidence scopes, positive invocation lease and Agent Viewer floor.
687
+ The result separates the historical source from the current receiving invocation.
688
+ The application must also enforce its protected release, source and receiver
689
+ associations. Capture responses over 512 KiB and verification requests over
690
+ 768 KiB fail before writing; neither operation truncates evidence.
691
+
692
+ For an explicitly captured user sidebar, call the same evidence client's
693
+ `verifyConversationEvidence({ agent, threadId, response, responseHash, sourceReceipt })`.
694
+ This separate source projection binds a durable settled conversation revision
695
+ and cutoff rather than inventing an agent run. It verifies only previously
696
+ authorized captured bytes; the function gains no arbitrary thread-read access.
697
+
698
+ ## Completed private workflow sources
699
+
700
+ The upcoming SDK **0.4.2** and CLI **0.5.5** add separate workflow capture and
701
+ evidence clients. Confirm publication and the deployment's `workflows_read`
702
+ and `workflows_evidence_verify` features before depending on them. Declare
703
+ exact workflow IDs in `capabilities.workflows.read` and
704
+ `capabilities.workflows.evidence`, and attach an approved `service_account`.
705
+
706
+ `createFunctionServiceWorkflowsClient({ request, baseUrl: platformOrigin })`
707
+ provides `captureCompletedRun({ workflow, runId })`. Capture requires both
708
+ declarations, the source-owning account's current Workflow Invoker and root
709
+ and child Job Viewer access, and the exact verified completed upstream
710
+ workflow execution. It returns `{ response, responseHash, sourceReceipt }`
711
+ for the root's persisted merged JSON. The signed evidence retains failed or
712
+ skipped branch outcomes; capture does not imply every branch succeeded. It
713
+ does not read conversations or reuse an Agent response type.
714
+
715
+ `createFunctionWorkflowEvidenceClient({ request, baseUrl: platformOrigin })`
716
+ provides `verifySourceEvidence({ workflow, runId, response, responseHash,
717
+ sourceReceipt })`. Verification uses its separate evidence declaration and
718
+ current invocation credential. It verifies supplied historical bytes without
719
+ fetching output. The signed historical source, any genuine job-admission
720
+ reference, and the current receiver remain separate: the application must
721
+ bind them to its protected current task and intent. Historical evidence grants
722
+ no current source-read authority or application write authority.
723
+
724
+ Capture is bounded to 512 KiB, verification requests to 768 KiB, and opaque
725
+ source receipts to 16 KiB. Incomplete or changed source records are refused;
726
+ oversized results fail explicitly without truncation. Clients preserve typed
727
+ `FunctionWorkflowReadError` status/code failures rather than treating them as
728
+ empty results.
729
+
288
730
  ## Runtime tracing
289
731
 
290
732
  Newly deployed function entrypoints install request-scoped tracing automatically,
@@ -0,0 +1,92 @@
1
+ import { z } from 'zod';
2
+ export declare const FUNCTION_ABRIGO_READ_TOKEN_HEADER = "x-sequence-abrigo-read-token";
3
+ export declare const FUNCTION_ABRIGO_READ_PATH = "/api/managed-functions/abrigo/read";
4
+ export declare const FUNCTION_ABRIGO_READ_AUDIENCE = "atlas-managed-function-abrigo-read";
5
+ export declare const ABRIGO_READ_MAX_RESPONSE_BYTES: number;
6
+ export declare const ABRIGO_READ_TIMEOUT_MS = 30000;
7
+ export declare const abrigoReadOperationSchema: z.ZodEnum<{
8
+ workflow: "workflow";
9
+ entity: "entity";
10
+ entityGroupSummary: "entityGroupSummary";
11
+ loanRoles: "loanRoles";
12
+ relatedLoanRoles: "relatedLoanRoles";
13
+ workflowLoanMappings: "workflowLoanMappings";
14
+ workflowDocuments: "workflowDocuments";
15
+ customerDocuments: "customerDocuments";
16
+ reviewWorkflow: "reviewWorkflow";
17
+ reviewWorkflowDocuments: "reviewWorkflowDocuments";
18
+ reviewWorkflowCustomerDocuments: "reviewWorkflowCustomerDocuments";
19
+ }>;
20
+ export type FunctionAbrigoReadOperation = z.infer<typeof abrigoReadOperationSchema>;
21
+ export type AbrigoReadOperation = FunctionAbrigoReadOperation;
22
+ export declare const functionAbrigoReadRequestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
23
+ operation: z.ZodLiteral<"workflow">;
24
+ workflowId: z.ZodNumber;
25
+ }, z.core.$strict>, z.ZodObject<{
26
+ operation: z.ZodLiteral<"entity">;
27
+ entityId: z.ZodNumber;
28
+ }, z.core.$strict>, z.ZodObject<{
29
+ operation: z.ZodLiteral<"entityGroupSummary">;
30
+ entityId: z.ZodNumber;
31
+ }, z.core.$strict>, z.ZodObject<{
32
+ operation: z.ZodLiteral<"loanRoles">;
33
+ proposedLoanId: z.ZodNumber;
34
+ page: z.ZodNumber;
35
+ perPage: z.ZodNumber;
36
+ }, z.core.$strict>, z.ZodObject<{
37
+ operation: z.ZodLiteral<"relatedLoanRoles">;
38
+ proposedLoanId: z.ZodNumber;
39
+ page: z.ZodNumber;
40
+ perPage: z.ZodNumber;
41
+ }, z.core.$strict>, z.ZodObject<{
42
+ operation: z.ZodLiteral<"workflowLoanMappings">;
43
+ workflowId: z.ZodNumber;
44
+ page: z.ZodNumber;
45
+ perPage: z.ZodNumber;
46
+ }, z.core.$strict>, z.ZodObject<{
47
+ operation: z.ZodLiteral<"workflowDocuments">;
48
+ workflowId: z.ZodNumber;
49
+ page: z.ZodNumber;
50
+ perPage: z.ZodNumber;
51
+ }, z.core.$strict>, z.ZodObject<{
52
+ operation: z.ZodLiteral<"customerDocuments">;
53
+ customerId: z.ZodNumber;
54
+ page: z.ZodNumber;
55
+ perPage: z.ZodNumber;
56
+ }, z.core.$strict>, z.ZodObject<{
57
+ operation: z.ZodLiteral<"reviewWorkflow">;
58
+ workflowId: z.ZodNumber;
59
+ }, z.core.$strict>, z.ZodObject<{
60
+ operation: z.ZodLiteral<"reviewWorkflowDocuments">;
61
+ workflowId: z.ZodNumber;
62
+ page: z.ZodNumber;
63
+ perPage: z.ZodNumber;
64
+ }, z.core.$strict>, z.ZodObject<{
65
+ operation: z.ZodLiteral<"reviewWorkflowCustomerDocuments">;
66
+ workflowId: z.ZodNumber;
67
+ page: z.ZodNumber;
68
+ perPage: z.ZodNumber;
69
+ }, z.core.$strict>], "operation">;
70
+ export type FunctionAbrigoReadRequest = z.infer<typeof functionAbrigoReadRequestSchema>;
71
+ export declare const functionAbrigoReadScopesSchema: z.ZodObject<{
72
+ read: z.ZodArray<z.ZodEnum<{
73
+ workflow: "workflow";
74
+ entity: "entity";
75
+ entityGroupSummary: "entityGroupSummary";
76
+ loanRoles: "loanRoles";
77
+ relatedLoanRoles: "relatedLoanRoles";
78
+ workflowLoanMappings: "workflowLoanMappings";
79
+ workflowDocuments: "workflowDocuments";
80
+ customerDocuments: "customerDocuments";
81
+ reviewWorkflow: "reviewWorkflow";
82
+ reviewWorkflowDocuments: "reviewWorkflowDocuments";
83
+ reviewWorkflowCustomerDocuments: "reviewWorkflowCustomerDocuments";
84
+ }>>;
85
+ }, z.core.$strict>;
86
+ export declare function readFunctionAbrigoReadScopes(manifest: unknown): FunctionAbrigoReadOperation[];
87
+ export declare class FunctionAbrigoReadError extends Error {
88
+ readonly status: number;
89
+ readonly code: string;
90
+ readonly name = "FunctionAbrigoReadError";
91
+ constructor(status: number, code: string, message: string);
92
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FunctionAbrigoReadError = exports.functionAbrigoReadScopesSchema = exports.functionAbrigoReadRequestSchema = exports.abrigoReadOperationSchema = exports.ABRIGO_READ_TIMEOUT_MS = exports.ABRIGO_READ_MAX_RESPONSE_BYTES = exports.FUNCTION_ABRIGO_READ_AUDIENCE = exports.FUNCTION_ABRIGO_READ_PATH = exports.FUNCTION_ABRIGO_READ_TOKEN_HEADER = void 0;
4
+ exports.readFunctionAbrigoReadScopes = readFunctionAbrigoReadScopes;
5
+ const zod_1 = require("zod");
6
+ exports.FUNCTION_ABRIGO_READ_TOKEN_HEADER = 'x-sequence-abrigo-read-token';
7
+ exports.FUNCTION_ABRIGO_READ_PATH = '/api/managed-functions/abrigo/read';
8
+ exports.FUNCTION_ABRIGO_READ_AUDIENCE = 'atlas-managed-function-abrigo-read';
9
+ exports.ABRIGO_READ_MAX_RESPONSE_BYTES = 1024 * 1024;
10
+ exports.ABRIGO_READ_TIMEOUT_MS = 30_000;
11
+ exports.abrigoReadOperationSchema = zod_1.z.enum([
12
+ 'workflow', 'entity', 'entityGroupSummary', 'loanRoles', 'relatedLoanRoles',
13
+ 'workflowLoanMappings', 'workflowDocuments', 'customerDocuments',
14
+ 'reviewWorkflow', 'reviewWorkflowDocuments', 'reviewWorkflowCustomerDocuments',
15
+ ]);
16
+ const id = zod_1.z.number().int().positive().max(Number.MAX_SAFE_INTEGER);
17
+ const page = zod_1.z.number().int().min(1).max(100);
18
+ const perPage = zod_1.z.number().int().min(1).max(500);
19
+ exports.functionAbrigoReadRequestSchema = zod_1.z.discriminatedUnion('operation', [
20
+ zod_1.z.strictObject({ operation: zod_1.z.literal('workflow'), workflowId: id }),
21
+ zod_1.z.strictObject({ operation: zod_1.z.literal('entity'), entityId: id }),
22
+ zod_1.z.strictObject({ operation: zod_1.z.literal('entityGroupSummary'), entityId: id }),
23
+ zod_1.z.strictObject({ operation: zod_1.z.literal('loanRoles'), proposedLoanId: id, page, perPage }),
24
+ zod_1.z.strictObject({ operation: zod_1.z.literal('relatedLoanRoles'), proposedLoanId: id, page, perPage }),
25
+ zod_1.z.strictObject({ operation: zod_1.z.literal('workflowLoanMappings'), workflowId: id, page, perPage }),
26
+ zod_1.z.strictObject({ operation: zod_1.z.literal('workflowDocuments'), workflowId: id, page, perPage }),
27
+ zod_1.z.strictObject({ operation: zod_1.z.literal('customerDocuments'), customerId: id, page, perPage }),
28
+ zod_1.z.strictObject({ operation: zod_1.z.literal('reviewWorkflow'), workflowId: id }),
29
+ zod_1.z.strictObject({ operation: zod_1.z.literal('reviewWorkflowDocuments'), workflowId: id, page, perPage }),
30
+ zod_1.z.strictObject({ operation: zod_1.z.literal('reviewWorkflowCustomerDocuments'), workflowId: id, page, perPage }),
31
+ ]);
32
+ exports.functionAbrigoReadScopesSchema = zod_1.z.strictObject({
33
+ read: zod_1.z.array(exports.abrigoReadOperationSchema).min(1).max(11)
34
+ .refine((operations) => new Set(operations).size === operations.length, 'Abrigo read operations must be distinct'),
35
+ });
36
+ function readFunctionAbrigoReadScopes(manifest) {
37
+ if (typeof manifest !== 'object' || manifest === null)
38
+ return [];
39
+ const capabilities = Reflect.get(manifest, 'capabilities');
40
+ if (typeof capabilities !== 'object' || capabilities === null)
41
+ return [];
42
+ const abrigo = Reflect.get(capabilities, 'abrigo');
43
+ return abrigo === undefined ? [] : exports.functionAbrigoReadScopesSchema.parse(abrigo).read;
44
+ }
45
+ class FunctionAbrigoReadError extends Error {
46
+ status;
47
+ code;
48
+ name = 'FunctionAbrigoReadError';
49
+ constructor(status, code, message) {
50
+ super(message);
51
+ this.status = status;
52
+ this.code = code;
53
+ }
54
+ }
55
+ exports.FunctionAbrigoReadError = FunctionAbrigoReadError;
@@ -0,0 +1,6 @@
1
+ import type { JsonValue } from './cache';
2
+ import { type FunctionAbrigoReadRequest } from './abrigo-contract';
3
+ export declare function validateAbrigoReviewResponse({ query, body }: {
4
+ query: FunctionAbrigoReadRequest;
5
+ body: JsonValue;
6
+ }): void;