@sequenceholdings/managed-functions 0.4.1 → 0.4.3

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 (87) hide show
  1. package/README.md +374 -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 +150 -0
  17. package/dist/capabilities.js +58 -5
  18. package/dist/config.d.ts +17 -2
  19. package/dist/config.js +55 -1
  20. package/dist/deployed-policy.js +90 -2
  21. package/dist/directory-config.js +1 -1
  22. package/dist/directory-contract.d.ts +68 -0
  23. package/dist/directory-contract.js +57 -0
  24. package/dist/directory-manifest.d.ts +5 -0
  25. package/dist/directory-manifest.js +10 -0
  26. package/dist/http-tracing.js +3 -0
  27. package/dist/immutable-release-contract.d.ts +3 -0
  28. package/dist/immutable-release-contract.js +12 -0
  29. package/dist/index.d.ts +37 -1
  30. package/dist/index.js +72 -1
  31. package/dist/invocation-client.d.ts +8 -1
  32. package/dist/invocation-client.js +8 -3
  33. package/dist/invocation-contract.d.ts +36 -0
  34. package/dist/invocation-contract.js +48 -0
  35. package/dist/invocation.d.ts +8 -0
  36. package/dist/invocation.js +134 -0
  37. package/dist/lattice-capability.d.ts +5 -0
  38. package/dist/lattice-capability.js +16 -0
  39. package/dist/lattice-provenance-contract.d.ts +519 -0
  40. package/dist/lattice-provenance-contract.js +148 -0
  41. package/dist/lattice-provenance.d.ts +16 -0
  42. package/dist/lattice-provenance.js +167 -0
  43. package/dist/monitoring-annotations.d.ts +53 -0
  44. package/dist/monitoring-annotations.js +54 -0
  45. package/dist/monitoring-contract.d.ts +463 -0
  46. package/dist/monitoring-contract.js +89 -0
  47. package/dist/monitoring-events.d.ts +257 -0
  48. package/dist/monitoring-events.js +136 -0
  49. package/dist/monitoring.d.ts +14 -0
  50. package/dist/monitoring.js +78 -0
  51. package/dist/orm-contract.d.ts +1 -0
  52. package/dist/orm-contract.js +4 -0
  53. package/dist/orm.d.ts +19 -3
  54. package/dist/orm.js +93 -13
  55. package/dist/preflight-abrigo.d.ts +10 -0
  56. package/dist/preflight-abrigo.js +26 -0
  57. package/dist/preflight-agents.d.ts +10 -0
  58. package/dist/preflight-agents.js +51 -0
  59. package/dist/preflight-immutable-release.d.ts +10 -0
  60. package/dist/preflight-immutable-release.js +34 -0
  61. package/dist/preflight-invocation.d.ts +10 -0
  62. package/dist/preflight-invocation.js +40 -0
  63. package/dist/preflight-lattice.d.ts +10 -0
  64. package/dist/preflight-lattice.js +40 -0
  65. package/dist/preflight-monitoring.d.ts +10 -0
  66. package/dist/preflight-monitoring.js +46 -0
  67. package/dist/preflight-service-directory.d.ts +10 -0
  68. package/dist/preflight-service-directory.js +26 -0
  69. package/dist/preflight-workflows.d.ts +10 -0
  70. package/dist/preflight-workflows.js +38 -0
  71. package/dist/preflight.js +49 -5
  72. package/dist/processes-contract.d.ts +20 -0
  73. package/dist/processes-contract.js +8 -1
  74. package/dist/processes.d.ts +2 -1
  75. package/dist/processes.js +34 -0
  76. package/dist/runtime.cjs +3115 -215
  77. package/dist/sdk-range.d.ts +1 -0
  78. package/dist/sdk-range.js +25 -0
  79. package/dist/service-directory.d.ts +12 -0
  80. package/dist/service-directory.js +72 -0
  81. package/dist/workflow-scopes-contract.d.ts +8 -0
  82. package/dist/workflow-scopes-contract.js +29 -0
  83. package/dist/workflow-source-contract.d.ts +524 -0
  84. package/dist/workflow-source-contract.js +59 -0
  85. package/dist/workflow-source.d.ts +12 -0
  86. package/dist/workflow-source.js +93 -0
  87. package/package.json +3 -3
package/README.md CHANGED
@@ -81,9 +81,32 @@ generic invocation, isolation, secrets, egress, caching, and observability;
81
81
  deployed functions own business logic. Primitive code must not identify a
82
82
  function consumer or branch on a function ID.
83
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
+
84
107
  ## ORM as the invoking user
85
108
 
86
- ### Default SQL (planned SDK 0.4.0)
109
+ ### Default SQL (introduced in SDK 0.4.0)
87
110
 
88
111
  With the matching Atlas runtime, use the same caller-scoped executor without
89
112
  an operation manifest:
@@ -182,9 +205,38 @@ fragment or embedded credentials. Loopback HTTP is accepted for a local platform
182
205
  There is no automatic environment URL: the helper never trusts a request header
183
206
  or body to choose where it sends a delegated credential.
184
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
+
185
224
  ### Caller identity and trust boundary
186
225
 
187
- 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.
188
240
  Scheduled/system-runner and service-account invocations fail closed. They do not
189
241
  silently become an attached service account or a recorded human initiator.
190
242
 
@@ -272,6 +324,18 @@ const page = await processes.listRunsPage({
272
324
  })
273
325
  ```
274
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
+
275
339
  `getRun` returns only `id`, `processId`, `status`, `entityType`, `entityId`,
276
340
  `startedAt` and `completedAt`. Dates are ISO strings; entity fields and
277
341
  `completedAt` may be null. Runtime error text, metadata, state, output and steps
@@ -294,6 +358,171 @@ empty activity list. Reads do not atomically reserve work: applications must
294
358
  still coordinate concurrent starts through a durable claim or idempotency
295
359
  mechanism.
296
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
+
297
526
  ## Opt-in cache
298
527
 
299
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.
@@ -341,6 +570,72 @@ Explicit CRUD throws `CacheError` with `DISABLED`, `UNAVAILABLE`, `UNKNOWN_OUTCO
341
570
 
342
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.
343
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
+
344
639
  ## Local development
345
640
 
346
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.
@@ -355,6 +650,83 @@ Cloud Run emits aggregate `managed_function.cache` logs every 30 seconds of acti
355
650
 
356
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.
357
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
+
358
730
  ## Runtime tracing
359
731
 
360
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;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateAbrigoReviewResponse = validateAbrigoReviewResponse;
4
+ const zod_1 = require("zod");
5
+ const abrigo_contract_1 = require("./abrigo-contract");
6
+ const positiveId = zod_1.z.number().int().positive().max(Number.MAX_SAFE_INTEGER);
7
+ const reviewWorkflowSchema = zod_1.z.strictObject({ id: positiveId, customerId: positiveId });
8
+ const reviewDocumentsSchema = zod_1.z.object({ items: zod_1.z.array(zod_1.z.unknown()).max(500) }).passthrough();
9
+ const reviewCustomerDocumentsSchema = reviewDocumentsSchema.extend({ workflowId: positiveId, customerId: positiveId });
10
+ function validateAbrigoReviewResponse({ query, body }) {
11
+ const invalid = () => new abrigo_contract_1.FunctionAbrigoReadError(502, 'malformed_response', 'Atlas returned an invalid workflow document review response');
12
+ if (query.operation === 'reviewWorkflow') {
13
+ const parsed = reviewWorkflowSchema.safeParse(body);
14
+ if (!parsed.success || parsed.data.id !== query.workflowId)
15
+ throw invalid();
16
+ }
17
+ else if (query.operation === 'reviewWorkflowDocuments') {
18
+ const parsed = reviewDocumentsSchema.safeParse(body);
19
+ if (!parsed.success || parsed.data.items.length > query.perPage)
20
+ throw invalid();
21
+ }
22
+ else if (query.operation === 'reviewWorkflowCustomerDocuments') {
23
+ const parsed = reviewCustomerDocumentsSchema.safeParse(body);
24
+ if (!parsed.success || parsed.data.workflowId !== query.workflowId || parsed.data.items.length > query.perPage)
25
+ throw invalid();
26
+ }
27
+ }
@@ -0,0 +1,14 @@
1
+ import type { JsonValue } from './cache';
2
+ import { type FunctionAbrigoReadRequest } from './abrigo-contract';
3
+ export * from './abrigo-contract';
4
+ export interface FunctionCallerAbrigoOptions {
5
+ request: {
6
+ headers: Pick<Headers, 'get'> | Readonly<Record<string, string | readonly string[] | undefined>>;
7
+ };
8
+ baseUrl: string;
9
+ fetchImpl?: typeof globalThis.fetch;
10
+ }
11
+ export interface FunctionCallerAbrigoClient {
12
+ read(query: FunctionAbrigoReadRequest): Promise<JsonValue>;
13
+ }
14
+ export declare function createFunctionCallerAbrigoClient({ request, baseUrl, fetchImpl }: FunctionCallerAbrigoOptions): FunctionCallerAbrigoClient;