@arnilo/prism 0.0.96 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +285 -2
- package/README.md +17 -3
- package/dist/agent-definitions.js +2 -3
- package/dist/agent-event-source.d.ts +11 -0
- package/dist/agent-event-source.js +512 -0
- package/dist/agent-loops.d.ts +5 -0
- package/dist/agent-loops.js +99 -14
- package/dist/agent-run-lifecycle.d.ts +5 -2
- package/dist/agent-run-lifecycle.js +18 -2
- package/dist/agent-run-state.d.ts +27 -1
- package/dist/agent-run-state.js +113 -7
- package/dist/agents.d.ts +3 -1
- package/dist/agents.js +1255 -129
- package/dist/artifacts.d.ts +132 -0
- package/dist/artifacts.js +44 -0
- package/dist/cache-helpers.js +18 -9
- package/dist/checkpoints.d.ts +4 -0
- package/dist/checkpoints.js +17 -9
- package/dist/cli-init.js +3 -7
- package/dist/cli-runner.d.ts +2 -6
- package/dist/cli-runner.js +71 -33
- package/dist/compaction.js +5 -4
- package/dist/config.js +7 -4
- package/dist/content.js +26 -24
- package/dist/context-budget.d.ts +67 -0
- package/dist/context-budget.js +288 -0
- package/dist/contracts.d.ts +590 -8
- package/dist/contracts.js +142 -1
- package/dist/contribution-parsing.js +6 -2
- package/dist/contributions.d.ts +2 -0
- package/dist/contributions.js +3 -0
- package/dist/conversations.d.ts +50 -0
- package/dist/conversations.js +98 -0
- package/dist/credentials.d.ts +22 -2
- package/dist/credentials.js +18 -3
- package/dist/devices.d.ts +94 -0
- package/dist/devices.js +138 -0
- package/dist/event-multiplexer.js +18 -4
- package/dist/extensions.d.ts +18 -1
- package/dist/extensions.js +79 -6
- package/dist/feedback.js +12 -10
- package/dist/guardrails.d.ts +1 -1
- package/dist/guardrails.js +26 -17
- package/dist/identity.d.ts +92 -0
- package/dist/identity.js +265 -0
- package/dist/index.d.ts +94 -72
- package/dist/index.js +48 -36
- package/dist/input.d.ts +10 -1
- package/dist/input.js +152 -52
- package/dist/instruction-injection.d.ts +1 -1
- package/dist/middleware.js +9 -1
- package/dist/models.d.ts +2 -0
- package/dist/models.js +3 -0
- package/dist/node/agent-definitions.js +16 -8
- package/dist/node/contribution-discovery.d.ts +1 -2
- package/dist/node/contribution-discovery.js +3 -3
- package/dist/node/session-store-jsonl.js +13 -7
- package/dist/node/settings.d.ts +1 -1
- package/dist/node/settings.js +1 -1
- package/dist/node/system-project-prompts.js +2 -4
- package/dist/node/trust.js +1 -1
- package/dist/persistence-lifecycle.d.ts +103 -0
- package/dist/persistence-lifecycle.js +202 -0
- package/dist/provider-events.d.ts +1 -0
- package/dist/provider-events.js +6 -1
- package/dist/provider-request-policy.js +3 -4
- package/dist/providers/media.d.ts +1 -1
- package/dist/providers/openai-compatible.d.ts +46 -1
- package/dist/providers/openai-compatible.js +123 -53
- package/dist/providers/openai-primitives.js +10 -7
- package/dist/providers/transport.d.ts +6 -0
- package/dist/providers/transport.js +21 -0
- package/dist/providers.d.ts +2 -0
- package/dist/providers.js +3 -0
- package/dist/redaction.d.ts +1 -0
- package/dist/redaction.js +26 -9
- package/dist/resources.d.ts +2 -2
- package/dist/resources.js +2 -2
- package/dist/retry.d.ts +5 -0
- package/dist/retry.js +8 -1
- package/dist/rpc.js +55 -11
- package/dist/run-ledger.d.ts +6 -0
- package/dist/run-ledger.js +16 -13
- package/dist/run-limits.js +49 -10
- package/dist/secure-agent.js +8 -2
- package/dist/security.js +7 -2
- package/dist/session-stores.d.ts +7 -2
- package/dist/session-stores.js +195 -21
- package/dist/skill-disclosure.d.ts +35 -0
- package/dist/skill-disclosure.js +101 -0
- package/dist/skill-load.d.ts +25 -0
- package/dist/skill-load.js +112 -0
- package/dist/structured-output.d.ts +5 -1
- package/dist/structured-output.js +20 -2
- package/dist/system-prompts.js +7 -2
- package/dist/testing/agent-event-source-conformance.d.ts +4 -0
- package/dist/testing/agent-event-source-conformance.js +54 -0
- package/dist/testing/compaction-conformance.js +5 -1
- package/dist/testing/extension-conformance.js +15 -3
- package/dist/testing/feedback.d.ts +1 -3
- package/dist/testing/feedback.js +1 -1
- package/dist/testing/persistence-schema.d.ts +2 -2
- package/dist/testing/persistence-schema.js +280 -35
- package/dist/testing/provider-conformance.js +3 -3
- package/dist/testing/run-ledger-conformance.js +1 -1
- package/dist/testing/session-store-conformance.d.ts +6 -0
- package/dist/testing/session-store-conformance.js +37 -2
- package/dist/testing/tool-conformance.js +30 -5
- package/dist/testing/tool-effect-store-conformance.d.ts +9 -0
- package/dist/testing/tool-effect-store-conformance.js +85 -0
- package/dist/thinking.js +4 -1
- package/dist/tool-effects.d.ts +15 -0
- package/dist/tool-effects.js +352 -0
- package/dist/tool-result-fold.d.ts +40 -0
- package/dist/tool-result-fold.js +176 -0
- package/dist/tools.d.ts +8 -3
- package/dist/tools.js +248 -13
- package/docs/0.1.0-readiness.md +202 -0
- package/docs/a2a.md +33 -2
- package/docs/acp.md +126 -0
- package/docs/ag-ui-adoption.md +77 -0
- package/docs/ag-ui.md +225 -0
- package/docs/agent-events.md +34 -3
- package/docs/agent-identity.md +144 -0
- package/docs/agent-loops.md +17 -2
- package/docs/agent-session-runtime.md +21 -4
- package/docs/browser-automation.md +5 -0
- package/docs/caveman.md +129 -0
- package/docs/cli-rpc.md +3 -6
- package/docs/coding-agent-tools.md +229 -25
- package/docs/coding-security.md +77 -11
- package/docs/compaction-and-retry.md +5 -2
- package/docs/compaction-llm.md +20 -1
- package/docs/compaction-observational-memory.md +52 -8
- package/docs/context-and-skills.md +94 -7
- package/docs/contribution-registries.md +1 -0
- package/docs/conversations.md +135 -0
- package/docs/credential-storage.md +34 -1
- package/docs/credentials-and-redaction.md +11 -1
- package/docs/database-persistence.md +27 -7
- package/docs/device-adapters.md +97 -0
- package/docs/enterprise-postgres-state.md +178 -0
- package/docs/evaluations.md +14 -1
- package/docs/extensions.md +4 -1
- package/docs/forge-integration.md +113 -0
- package/docs/guardrails.md +16 -2
- package/docs/host-security.md +35 -4
- package/docs/index.md +69 -37
- package/docs/input-and-prompt-assembly.md +8 -7
- package/docs/language-intelligence.md +162 -0
- package/docs/mcp-tools.md +62 -5
- package/docs/middleware-hooks.md +2 -2
- package/docs/migration.md +423 -2
- package/docs/model-routing.md +111 -0
- package/docs/multimodal-content.md +8 -5
- package/docs/node-jsonl-session-store.md +1 -1
- package/docs/observability.md +2 -0
- package/docs/openapi-tools.md +56 -0
- package/docs/performance.md +282 -0
- package/docs/policy-and-audit.md +171 -0
- package/docs/ponytail.md +127 -0
- package/docs/postgres-persistence.md +8 -4
- package/docs/process-sessions.md +147 -0
- package/docs/provider-caching.md +13 -1
- package/docs/provider-conformance.md +29 -5
- package/docs/provider-packages.md +43 -2
- package/docs/provider-request-policies.md +2 -0
- package/docs/providers/ai-sdk.md +24 -7
- package/docs/providers/alibaba.md +179 -0
- package/docs/providers/anthropic.md +93 -0
- package/docs/providers/azure.md +74 -0
- package/docs/providers/bedrock.md +72 -0
- package/docs/providers/google.md +89 -0
- package/docs/providers/ollama.md +166 -0
- package/docs/providers/openai-compatible.md +31 -2
- package/docs/providers/openai.md +24 -5
- package/docs/providers/openrouter.md +2 -0
- package/docs/providers/vertex.md +71 -0
- package/docs/public-contracts.md +61 -4
- package/docs/rag.md +41 -12
- package/docs/release-and-install.md +323 -206
- package/docs/resource-loading.md +3 -0
- package/docs/runs-and-usage.md +3 -0
- package/docs/server.md +44 -6
- package/docs/session-store-conformance.md +2 -0
- package/docs/session-stores.md +41 -2
- package/docs/sqlite-persistence.md +11 -3
- package/docs/structured-output.md +7 -1
- package/docs/supervisors.md +8 -0
- package/docs/tool-effects.md +95 -0
- package/docs/tools.md +5 -0
- package/docs/work-artifacts-and-review.md +102 -0
- package/docs/work-connectors.md +32 -0
- package/docs/work-tools.md +137 -0
- package/docs/workflows.md +6 -0
- package/docs/working-and-semantic-memory.md +40 -7
- package/package.json +30 -8
- package/templates/init/providers.json +22 -0
- package/docs/review-coverage-2026-07-14.md +0 -260
- package/docs/review-coverage-2026-07-15.md +0 -193
- package/docs/review-coverage-2026-07-17-provider-validation.md +0 -192
- package/docs/review-coverage-2026-07-19-phase-3.md +0 -174
- package/docs/review-coverage-2026-07-20-phase-4.md +0 -175
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Work connectors
|
|
2
|
+
|
|
3
|
+
Least-privilege Microsoft 365 and Google Workspace connectors live in `@arnilo/prism-work-tools`.
|
|
4
|
+
|
|
5
|
+
## Principles
|
|
6
|
+
|
|
7
|
+
1. **Host-pinned binary** — Prism never downloads or shells an untrusted CLI path.
|
|
8
|
+
2. **Hard-coded argv templates** — models choose typed tool args; they never supply command strings.
|
|
9
|
+
3. **Draft-then-approve** — mutations create a draft; side effects run only after host approval.
|
|
10
|
+
4. **Idempotent retries** — `IdempotencyStore` keyed by identity + operation key.
|
|
11
|
+
5. **Isolated config** — per-identity `configDir` (CLI `HOME`); no credential argv.
|
|
12
|
+
6. **Shared result shapes** — mail/calendar/file/task list/get tools normalize onto `WorkMailMessage` / `WorkCalendarEvent` / `WorkFileItem` / `WorkTaskItem` without hiding provider-specific ops.
|
|
13
|
+
|
|
14
|
+
## Microsoft 365
|
|
15
|
+
|
|
16
|
+
See [Work tools](work-tools.md). Adapter: `createMicrosoft365CliAdapter` / subpath `@arnilo/prism-work-tools/microsoft365`.
|
|
17
|
+
|
|
18
|
+
Uses [@pnp/cli-microsoft365](https://pnp.github.io/cli-microsoft365/) commands such as `outlook message list|get`, `outlook mail send`, `outlook event list|add`, `file list|add`, `spo file sharinglink add`. To Do / Planner / Teams remain capability-gated.
|
|
19
|
+
|
|
20
|
+
## Google Workspace
|
|
21
|
+
|
|
22
|
+
See [Work tools](work-tools.md). Adapter: `createGoogleWorkspaceCliAdapter` / subpath `@arnilo/prism-work-tools/google-workspace`.
|
|
23
|
+
|
|
24
|
+
Uses [`@googleworkspace/cli` (`gws`)](https://github.com/googleworkspace/cli): `gmail users messages list|get`, `gmail +send`, `calendar events list|insert`, `drive files list|create`, `drive permissions create`, `tasks tasks *`. Docs/Sheets/Slides create remain capability-gated. Discovery `schema` and `auth`/`login`/`setup` are forbidden from Prism argv.
|
|
25
|
+
|
|
26
|
+
## Scoped OAuth establishment (0.0.14)
|
|
27
|
+
|
|
28
|
+
Hosts establish, refresh, and revoke scoped OAuth credentials for these workloads through the existing `OAuthProvider` / credential-store seams (`@arnilo/prism-credentials-node`): `createMicrosoft365OAuthProvider` / `createGoogleWorkspaceOAuthProvider` (PKCE + device code), least-privilege scope bundles per capability (`resolveMicrosoft365Scopes` / `resolveGoogleWorkspaceScopes`, read vs mutation). Connectors consume a per-identity token via a late-bound `tokenProvider` injected as an env var — never argv, never model context; revocation fails closed. See [Credential storage](credential-storage.md) and [Work tools](work-tools.md).
|
|
29
|
+
|
|
30
|
+
## Out of scope
|
|
31
|
+
|
|
32
|
+
Local Office binaries, model-controlled CLI, generic Graph/Discovery free-form calls, tenant-admin/login/debug from Prism. **Slack/Teams chat-channel adapters are not shipped** (demand-gated until web/AG-UI usage is measured); the M365 `teams` capability op is a separate gated workload op, not a channel adapter.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Work tools
|
|
2
|
+
|
|
3
|
+
Optional `@arnilo/prism-work-tools` package: identity-scoped Microsoft 365 and Google Workspace connectors. Host-pinned CLI binaries only; hard-coded `execFile` argv templates; draft-then-approve mutations; side-effect idempotency; shared mail/calendar/file/task result shapes.
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
Use when agents must read or mutate tenant mail/calendar/files/tasks through the enterprise CLI the host already operates — not through model-built shell strings or generic Graph/Discovery free-form calls.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @arnilo/prism-work-tools
|
|
13
|
+
# host separately:
|
|
14
|
+
# npm i -g @pnp/cli-microsoft365
|
|
15
|
+
# npm i -g @googleworkspace/cli
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## API
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import {
|
|
22
|
+
createWorkTools,
|
|
23
|
+
createMicrosoft365CliAdapter,
|
|
24
|
+
createGoogleWorkspaceCliAdapter,
|
|
25
|
+
createMemoryIdempotencyStore,
|
|
26
|
+
} from "@arnilo/prism-work-tools";
|
|
27
|
+
// or: import { createGoogleWorkspaceCliAdapter } from "@arnilo/prism-work-tools/google-workspace";
|
|
28
|
+
|
|
29
|
+
const microsoft365 = createMicrosoft365CliAdapter({
|
|
30
|
+
binary: process.env.M365_BIN!,
|
|
31
|
+
configDir: `/var/prism/m365/${tenant}/${user}`,
|
|
32
|
+
identity,
|
|
33
|
+
// Optional late-bound per-identity token (0.0.14): env var only, never argv/model context.
|
|
34
|
+
// tokenProvider: createOAuthWorkTokenProvider({ provider: m365OAuth, store, envVar: "M365_ACCESSTOKEN" }),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const googleWorkspace = createGoogleWorkspaceCliAdapter({
|
|
38
|
+
binary: process.env.GWS_BIN!,
|
|
39
|
+
configDir: `/var/prism/gws/${tenant}/${user}`,
|
|
40
|
+
identity,
|
|
41
|
+
// allowedOps: add docs.create / sheets.create / slides.create when gated
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const tools = createWorkTools({
|
|
45
|
+
microsoft365,
|
|
46
|
+
googleWorkspace,
|
|
47
|
+
idempotencyStore: createMemoryIdempotencyStore(),
|
|
48
|
+
approval: { isApproved: ({ draftId }) => hostHasApproved(draftId) },
|
|
49
|
+
externalRecipients: { allow: (addr) => addr.endsWith("@contoso.com") },
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
List/get tools return shared `WorkPage` / `WorkMailMessage` / `WorkCalendarEvent` / `WorkFileItem` / `WorkTaskItem` shapes (`untrusted: true`) via package normalizers — provider-specific fields are not hidden; they are mapped onto the common denominator.
|
|
54
|
+
|
|
55
|
+
### Hard-coded Microsoft 365 ops
|
|
56
|
+
|
|
57
|
+
Verified against [CLI for Microsoft 365](https://pnp.github.io/cli-microsoft365/) (2026-07-23):
|
|
58
|
+
|
|
59
|
+
| Prism op | CLI |
|
|
60
|
+
| --- | --- |
|
|
61
|
+
| `mail.list` | `m365 outlook message list --output json` |
|
|
62
|
+
| `mail.get` | `m365 outlook message get --output json --id …` |
|
|
63
|
+
| `mail.send` | `m365 outlook mail send --output json --to … --subject … --bodyContents …` |
|
|
64
|
+
| `calendar.list` | `m365 outlook event list --output json` |
|
|
65
|
+
| `calendar.add` | `m365 outlook event add --output json --subject … --start … --end …` |
|
|
66
|
+
| `file.list` | `m365 file list --output json --webUrl … --folderUrl …` |
|
|
67
|
+
| `file.add` | `m365 file add --output json --folderUrl … --filePath …` |
|
|
68
|
+
| `file.share` | `m365 spo file sharinglink add` (`--scope organization` only) |
|
|
69
|
+
| `todo.*` / `planner.*` | capability-gated via `allowedOps` |
|
|
70
|
+
|
|
71
|
+
### Hard-coded Google Workspace ops
|
|
72
|
+
|
|
73
|
+
Verified against [`@googleworkspace/cli` / `gws`](https://github.com/googleworkspace/cli) (2026-07-24):
|
|
74
|
+
|
|
75
|
+
| Prism op | CLI |
|
|
76
|
+
| --- | --- |
|
|
77
|
+
| `mail.list` | `gws gmail users messages list --params … --fields …` |
|
|
78
|
+
| `mail.get` | `gws gmail users messages get --params …` |
|
|
79
|
+
| `mail.send` | `gws gmail +send --to … --subject … --body …` |
|
|
80
|
+
| `calendar.list` | `gws calendar events list --params … --fields …` |
|
|
81
|
+
| `calendar.add` | `gws calendar events insert --params … --json …` |
|
|
82
|
+
| `file.list` | `gws drive files list --params … [--page-all]` (NDJSON when paginated) |
|
|
83
|
+
| `file.add` | `gws drive files create --json … --upload …` |
|
|
84
|
+
| `file.share` | `gws drive permissions create` (`type=domain\|user` only; `anyone` denied) |
|
|
85
|
+
| `task.*` | `gws tasks tasks list\|insert\|patch` |
|
|
86
|
+
| `docs.create` / `sheets.create` / `slides.create` | capability-gated via `allowedOps` |
|
|
87
|
+
|
|
88
|
+
Startup: M365 `version --output json`; GWS `--version`. Forbidden: `login`, `setup`, `auth`, `schema`, `doctor`, `--debug`, `--verbose`, credentials in argv, anonymous share, model-supplied command strings / free-form Discovery.
|
|
89
|
+
|
|
90
|
+
### Draft → approve → execute
|
|
91
|
+
|
|
92
|
+
Mutation tools (`*_mail_draft_send`, `*_draft_*`) create an in-adapter draft and return `{ status: "pending_approval", draftId }` until `approval.isApproved` is true.
|
|
93
|
+
|
|
94
|
+
### Durable idempotency (0.0.23)
|
|
95
|
+
|
|
96
|
+
`createMemoryIdempotencyStore()` remains for tests and a single process. For production replicas, use `createPostgresEnterpriseState({ pool }).workIdempotency`. It changes the old `get`/`put` replay abstraction to explicit async state transitions:
|
|
97
|
+
|
|
98
|
+
| Observable state | Meaning / host action |
|
|
99
|
+
| --- | --- |
|
|
100
|
+
| absent | `begin()` atomically acquires the first claim. |
|
|
101
|
+
| `in_progress` | Another worker owns the claim; do not dispatch a second connector effect. |
|
|
102
|
+
| `completed` | Return the bounded stored `{ draftId, resourceId? }` duplicate summary. |
|
|
103
|
+
| `failed_retryable` | A later `begin()` may reclaim it within the capped attempt policy. |
|
|
104
|
+
| `failed_terminal` | Do not retry; surface the bounded failure. |
|
|
105
|
+
| `unknown` | External result is ambiguous; reconcile with the connector/operator through `resolveUnknown()`. Never auto-replay. |
|
|
106
|
+
|
|
107
|
+
Call `begin({ identity, key, op })` **before** the external effect. After it succeeds, call `complete`, `fail`, or `markUnknown` with the returned claim token and version. The connector effect stays outside the database transaction, so this is claim-before-effect/deduplication—not exactly-once delivery. Claims default to 15 minutes (hard 60 minutes); expired claims transition to `unknown`; attempts default to 3 (hard 5). Stored rows contain no request body, token, raw provider response, or unrestricted payload.
|
|
108
|
+
|
|
109
|
+
## Limits
|
|
110
|
+
|
|
111
|
+
| Resource | Default / hard |
|
|
112
|
+
| --- | ---: |
|
|
113
|
+
| Pagination pages | 20 / 100 |
|
|
114
|
+
| Items / aggregate | 50/500 ; 200/2000 |
|
|
115
|
+
| Body / stdout | 256 KiB–2 MiB / 2–16 MiB |
|
|
116
|
+
| Process wall time | 60 s / 10 min |
|
|
117
|
+
| Concurrent CLI / identity | 2 / 8 |
|
|
118
|
+
|
|
119
|
+
## Tool effects
|
|
120
|
+
|
|
121
|
+
Approved mutations require core-derived `context.idempotencyKey` and a configured store (`effect: external_mutation/tool_managed`). Model-supplied idempotency keys are ignored. Ambiguous connector outcomes stay `unknown` — never auto-replayed (not exactly-once). See [tool effects](tool-effects.md).
|
|
122
|
+
|
|
123
|
+
## Security
|
|
124
|
+
|
|
125
|
+
- Require host-verified `AgentIdentity`; no cross-identity configDir reuse.
|
|
126
|
+
- Connector tokens (0.0.14): an optional `tokenProvider` resolves a per-identity access token into an env var per call — never argv, never model context. A missing/expired/revoked/cross-identity/wrong-tenant token fails the call closed before any exec. Refresh is late-bound and single-flighted per account (no refresh storm under reconnect). Build one with `createOAuthWorkTokenProvider()` from `@arnilo/prism-credentials-node`.
|
|
127
|
+
- External mail recipients fail closed unless `externalRecipients.allow` returns true.
|
|
128
|
+
- Anonymous / `anyone` sharing denied.
|
|
129
|
+
- CLI stdout/stderr capped; NDJSON page streams strictly parsed and page-capped; process killed on timeout/abort/overflow.
|
|
130
|
+
|
|
131
|
+
## Related
|
|
132
|
+
|
|
133
|
+
- [Enterprise PostgreSQL state](enterprise-postgres-state.md): durable claim/CAS store, cleanup, and operator reconciliation.
|
|
134
|
+
- [Work connectors](work-connectors.md)
|
|
135
|
+
- [Agent identity](agent-identity.md)
|
|
136
|
+
- [Host security](host-security.md)
|
|
137
|
+
- [Credential storage](credential-storage.md)
|
package/docs/workflows.md
CHANGED
|
@@ -18,6 +18,7 @@ Primary exports:
|
|
|
18
18
|
| `createWorkflowCommands` | Optional `CommandDefinition[]` for direct/background/replay/status/list/cancel/resume and, when selected, schedule control |
|
|
19
19
|
| `enqueueWorkflow` / `startWorkflowBackground` / `createWorkflowCoordinator` | Persist queued work and atomically claim/renew/execute it across processes using `LeaseStore` |
|
|
20
20
|
| `createWorkflowSchedules` | Explicit ownership-scoped one-time/interval/host-calculated schedules over existing checkpoint/lease stores |
|
|
21
|
+
| `createProactiveScheduleCapabilities` | Scoped, expiring, revocable capability tokens that enable proactive schedules; revocation stops firing fail-closed |
|
|
21
22
|
|
|
22
23
|
Included through `@arnilo/prism-sdk` and `@arnilo/prism-all`; installing either profile does not start workflows. Interactive TUI is out of scope (C-012 deferred).
|
|
23
24
|
|
|
@@ -72,6 +73,8 @@ All workflow limits and runtime `concurrency` reject non-safe integers, zero, ne
|
|
|
72
73
|
|
|
73
74
|
A function node returns `suspend({ reason, data?, resumeSchema? })` to persist `status: "suspended"`. Its next invocation receives `ctx.resume` only after an approved resume. `resumeWorkflow(workflow, { runId }, options)` validates schema/version/ownership/`definitionHash`, claims the checkpoint before node execution, and continues the suspended node. Denial persists terminal `denied` status without invoking it. Existing failed/aborted checkpoint resume remains available without a human decision.
|
|
74
75
|
|
|
76
|
+
Coding-agent ask-user glue (opt-in, no Goal DB): `suspendAskUserDecision(request)` wraps `suspend` with durable question/options/`selectionMode`/`allowCustom` data + resume schema; resume with `createAskUserDecisionResumeValidator()` or `validateAskUserDecisionResume`. Goal→verify: `runCodingGoalVerify` / `createCodingGoalVerifyWorkflow` compose plan Markdown → named checks → approve suspend → bounded handoff over the same primitives (`examples/coding-goal-verify.ts`). When a workflow node wraps a durable agent run, that run's shared pending-decision batch (Task 2) is the approval authority — workflow `suspend`/`resume` stay workflow-scoped and do not mint a parallel decision store.
|
|
77
|
+
|
|
75
78
|
Every node receives bounded `ctx.state`, `ctx.stateVersion`, and async `ctx.updateState(patch, { mode: "merge" | "replace" })`. Updates serialize, validate, redact, and snapshot before checkpoint save. `workflowNode({ workflow })` runs its child with the same ownership, agent/tool registries, execution policy, redactor, signal, checkpoints, and event bus; child state replaces parent state after success.
|
|
76
79
|
|
|
77
80
|
`replayWorkflow(workflow, { sourceRunId, fromNodeId, runId? }, options)` requires a succeeded source/node, creates a new checkpoint, copies terminal evidence outside the selected node's downstream closure, restores selected-node pre-state, and records `{ sourceRunId, fromNodeId, rootRunId, depth }`. Source evidence is untouched. Copying any prior nested/tool approval is rejected; replay from that approval node or earlier so Phase 8 approval executes again.
|
|
@@ -80,6 +83,8 @@ Every node receives bounded `ctx.state`, `ctx.stateVersion`, and async `ctx.upda
|
|
|
80
83
|
|
|
81
84
|
`createWorkflowSchedules({ store, leases, checkpoints, workflows, ownership, ownerId, calculators? })` is inert until its host calls `pollOnce()` or `run({ signal })`. Ownership requires `tenantId` plus `accountId` or `userId`. Methods are `create`, `get`, `list`, `pause`, `resume`, `trigger`, `delete`, `pollOnce`, and `run`. A record has one required `nextRunAt`, optional fixed `intervalMs` or registered `calculatorId` (never both), bounded input/metadata, status, version, and last-fire attribution. Manual trigger requires an idempotency key. Scheduled run IDs derive from schedule ID plus fire timestamp, so retry after enqueue-before-advance finds the same queued checkpoint instead of duplicating it. Defaults: page 100/hard 500, due claims 16/hard 256, input 256 KiB/hard 1 MiB, poll 1s, fire lease 30s.
|
|
82
85
|
|
|
86
|
+
`createProactiveScheduleCapabilities({ schedules, store, ownership, ownerId, defaultTtlMs?, maxTtlMs?, onCapability? })` wraps a `WorkflowSchedules` facade in explicit user enablement. `enable({ workflowId, scope, actor, nextRunAt, intervalMs?|calculatorId?, input?, ttlMs? })` creates the schedule plus a scoped, expiring `ScheduleCapabilityToken` (default TTL 24h / hard 31d, record ≤ 16 KiB) stamped with redacted actor refs. `revoke(tokenId, actor)` marks the token revoked and pauses the underlying schedule so `pollOnce()` never fires it (fail-closed). `assertActive(tokenId)` is a fail-closed guard for manual trigger paths — it throws on missing/revoked/expired tokens. `onCapability` emits `capability_enabled` / `capability_revoked` / `capability_denied` events (redacted refs only) that hosts bridge to `@arnilo/prism-policy` for an auditable ledger. Tokens are ownership-scoped checkpoint records; no cron expression or secret is persisted.
|
|
87
|
+
|
|
83
88
|
## Outputs / response / events
|
|
84
89
|
|
|
85
90
|
`runWorkflow` / `resumeWorkflow` resolve to `WorkflowRunResult`:
|
|
@@ -278,6 +283,7 @@ runRpcServer({
|
|
|
278
283
|
- Nested workflows inherit host registries/policies and cannot inject broader tools, agents, ownership, or credentials. Nested depth is inherited; child suspension bubbles to the parent review cursor.
|
|
279
284
|
- Replay source ownership/hash/status/node eligibility are checked before a new checkpoint is created. Source records are immutable, lineage is bounded, and copied approval-bearing paths are rejected.
|
|
280
285
|
- Schedule services are ownership-scoped and explicitly started. Per-fire leases plus deterministic run IDs/CAS prevent duplicate enqueue across coordinators and crash retry. Host calculator IDs resolve only from the supplied map; no callback or cron expression is persisted.
|
|
286
|
+
- Proactive schedules require an explicit capability grant. Revocation pauses the schedule (never fired by `pollOnce`) and `assertActive` fails closed on missing/revoked/expired tokens; enable/revoke/deny events carry redacted actor refs for the host policy ledger. Capability TTL is capped (default 24h / hard 31d) and the token record is byte-bounded (≤ 16 KiB); tokens are ownership-scoped, so foreign access fails closed rather than leaking existence.
|
|
281
287
|
- Scheduler stores O(nodes + active outputs + bounded state history); ready-node work uses indegree maps, not repeated full scans.
|
|
282
288
|
- Lease acquisition is atomic; opaque tokens protect renew/release; monotonically increasing fencing tokens plus checkpoint compare-and-swap prevent expired workers from committing after takeover. Node functions must honor `ctx.signal` for prompt cooperative cancellation.
|
|
283
289
|
|
|
@@ -23,19 +23,44 @@ Ordinary Prism sessions do not require this package or any vector backend.
|
|
|
23
23
|
| `vectorStore` / `workingStore` | no | Defaults to in-memory adapters |
|
|
24
24
|
| `schema` / `validateWorkingMemory` | no | Working-memory shape checks (JSON Schema subset or host hook) |
|
|
25
25
|
| `workingMemoryTemplate` | no | `{{path}}` template for context injection |
|
|
26
|
-
| `limits` | no | top-K, adjacent range, batch, payload, injected-token caps |
|
|
26
|
+
| `limits` | no | top-K, adjacent range, batch, payload, injected-token, export, and rebuild caps |
|
|
27
27
|
| `redactor` / `secrets` | no | Redact text/metadata before persist/inject |
|
|
28
|
+
| `requireConsent` | no | Strict mode: recall/injection excludes entries lacking explicit consent |
|
|
28
29
|
|
|
29
|
-
Semantic indexing:
|
|
30
|
+
Semantic indexing (entries carry `MemoryConsent` source/visibility; unset defaults to `{ source: "user", scope: "thread", visible: true }`):
|
|
31
|
+
|
|
32
|
+
| `MemoryConsent` field | Meaning |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| `source` | `"user"`, `"agent"`, or `"system"` provenance. |
|
|
35
|
+
| `scope` | `"thread"`, `"profile"`, or `"user"` control scope. |
|
|
36
|
+
| `visible` | `false` immediately excludes the record from recall, injection, export, and telemetry. |
|
|
37
|
+
| `grantedAt` / `revokedAt` | Optional host/audit timestamps; a revocation excludes the record. |
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
await memory.remember({ entries: [{ id, text, metadata?, consent?, sequence? }] }, { wait?: boolean })
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Semantic recall (honors consent/visibility at assembly time):
|
|
30
44
|
|
|
31
45
|
```ts
|
|
32
|
-
await memory.
|
|
46
|
+
await memory.recall(query, { topK?, messageRange?, requireConsent?, signal? })
|
|
33
47
|
```
|
|
34
48
|
|
|
35
|
-
|
|
49
|
+
Consent + lifecycle (real grant/correct/delete/retention on stored entries):
|
|
36
50
|
|
|
37
51
|
```ts
|
|
38
|
-
await memory.
|
|
52
|
+
await memory.setConsent(entryId, { visible?: boolean, source?, scope? }) // grant/revoke; no re-embed
|
|
53
|
+
await memory.correct(entryId, text) // re-embeds, preserves consent
|
|
54
|
+
await memory.forget({ ids? }) // real delete (whole thread if no ids)
|
|
55
|
+
await memory.applyRetention({ maxAgeDays?, maxEntries?, batchSize? }) // bounded real-delete sweep
|
|
56
|
+
|
|
57
|
+
const page = await memory.exportMemory({
|
|
58
|
+
identity: { tenantId, resourceId, threadId }, // exact host-verified owner
|
|
59
|
+
cursor?, limit?, maxBytes?, maxMs?, signal?,
|
|
60
|
+
}); // visible, explicitly consented, redacted records only
|
|
61
|
+
|
|
62
|
+
const rebuilt = await memory.rebuildIndex({ cursor?, batchSize?, maxMs?, signal? });
|
|
63
|
+
// re-embeds one page; save rebuilt.nextCursor and call again to resume
|
|
39
64
|
```
|
|
40
65
|
|
|
41
66
|
## Outputs / response / events
|
|
@@ -44,7 +69,12 @@ await memory.recall(query, { topK?, messageRange?, signal? })
|
|
|
44
69
|
| --- | --- |
|
|
45
70
|
| `updateWorking` / `getWorking` | Versioned `WorkingMemoryRecord` |
|
|
46
71
|
| `remember` | `{ accepted, pending, done }` — default `wait: false` indexes asynchronously |
|
|
47
|
-
| `recall` | `{ hits, adjacent }` tenant/thread scoped |
|
|
72
|
+
| `recall` | `{ hits, adjacent }` tenant/thread scoped; invisible/revoked entries excluded |
|
|
73
|
+
| `setConsent` / `correct` | Updated `MemoryVectorRecord` with stamped grant/revoke times |
|
|
74
|
+
| `forget` | Removed count (real delete) |
|
|
75
|
+
| `applyRetention` | `{ deleted, scanned }` bounded real-delete sweep |
|
|
76
|
+
| `exportMemory` | `{ entries, bytes, nextCursor? }` redacted, explicitly consented, identity-bound page |
|
|
77
|
+
| `rebuildIndex` | `{ rebuilt, nextCursor? }` re-embedded bounded page; caller owns resume scheduling |
|
|
48
78
|
| `createContextProvider()` | Inert `ContextProvider` blocks for working and/or semantic text |
|
|
49
79
|
| `createWorkingMemoryProcessor({ extract })` | Explicit host-invoked updater; never auto-runs |
|
|
50
80
|
|
|
@@ -131,6 +161,8 @@ const memory = createMemory({
|
|
|
131
161
|
- The working-memory processor is opt-in and host-invoked; middleware is not required.
|
|
132
162
|
- `createHashEmbedder()` is for tests/demos only; production hosts supply a real `Embedder`.
|
|
133
163
|
- Observational memory (`@arnilo/prism-compaction-observational-memory`) remains unchanged and composable.
|
|
164
|
+
- Consent is enforced at the single `recall()` gate, so both direct recall and `createContextProvider()` injection honor it; `visible: false` (or a revoked grant) keeps an entry out of prompts, events, exports, and telemetry. `setConsent`/`correct` re-upsert in place (consent change does not re-embed); `forget`/`applyRetention` are real deletes, not tombstones. Retention uses indexed oldest-first pages plus a scoped count, deleting one default-500/hard-5000 batch without reading a corpus into memory. The PostgreSQL adapter persists consent in a `consent JSONB` column added by `buildMemoryDdl`.
|
|
165
|
+
- `exportMemory()` requires an exact `{ tenantId, resourceId, threadId }` identity equal to its `createMemory()` scope. It excludes legacy consent-less, invisible, and revoked records even when normal recall allows legacy entries. It returns a stable sequence cursor page, redacted before response, with defaults/hard caps of 100/200 entries, 4/32 MiB, and 10/60 seconds. `rebuildIndex()` uses the same stable cursor shape to re-embed one 32/128-record page under a 10/60-second cap; save the cursor durably to resume. Both APIs require a store implementing bounded `listByThread()`; retention also requires `countByThread()`. PostgreSQL/pgvector and the in-memory reference adapter conform; SQLite persistence stores sessions, not semantic vectors.
|
|
134
166
|
- Profile bundles do not include this package yet.
|
|
135
167
|
|
|
136
168
|
Shared conformance:
|
|
@@ -149,10 +181,11 @@ await runMemoryConformance(() => ({
|
|
|
149
181
|
|
|
150
182
|
- Every write/query/delete requires `tenantId` + `resourceId`; semantic paths also require `threadId`.
|
|
151
183
|
- Cross-tenant and cross-thread access is denied.
|
|
184
|
+
- Revoked/invisible/non-consented memories never enter prompts, events, exports, or telemetry; `requireConsent: true` additionally drops consent-less (legacy) entries. Consent checks are O(hits) at recall, within the existing injected-token cap.
|
|
152
185
|
- Configure `secrets` / `redactor` so memory text and metadata cannot persist or inject raw canaries.
|
|
153
186
|
- Injected context is inert text — it cannot grant tools or permissions.
|
|
154
187
|
- Hard caps: top-K ≤ 32, messageRange ≤ 4, embed batch ≤ 128, injected tokens ≤ 8000, payload/working-memory byte limits enforced.
|
|
155
|
-
- Every embedding is a non-empty finite number vector. `embedBatched()`, in-memory `VectorStore` upserts/queries,
|
|
188
|
+
- Every embedding is a non-empty finite number vector. `embedBatched()`, in-memory `VectorStore` upserts/queries, PostgreSQL/pgvector parameters, and export/rebuild page boundaries reject NaN, ±Infinity, non-numbers, and wrong configured dimensions before similarity scoring, SQL, response, or re-indexing. Custom adapters can call `assertFiniteVector(vector, label, expectedLength?)` at their trust boundary.
|
|
156
189
|
- Default `remember()` does not block agent completion; pass `{ wait: true }` when indexing must finish first.
|
|
157
190
|
- PostgreSQL live suite is gated by `PRISM_TEST_POSTGRES_URL` and requires the `vector` extension.
|
|
158
191
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arnilo/prism",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "Agent harness for AI providers, agents, sessions, and tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
"types": "./dist/testing/provider-conformance.d.ts",
|
|
31
31
|
"default": "./dist/testing/provider-conformance.js"
|
|
32
32
|
},
|
|
33
|
+
"./testing/agent-event-source-conformance": {
|
|
34
|
+
"types": "./dist/testing/agent-event-source-conformance.d.ts",
|
|
35
|
+
"default": "./dist/testing/agent-event-source-conformance.js"
|
|
36
|
+
},
|
|
33
37
|
"./testing/session-store-conformance": {
|
|
34
38
|
"types": "./dist/testing/session-store-conformance.d.ts",
|
|
35
39
|
"default": "./dist/testing/session-store-conformance.js"
|
|
@@ -42,6 +46,10 @@
|
|
|
42
46
|
"types": "./dist/testing/tool-conformance.d.ts",
|
|
43
47
|
"default": "./dist/testing/tool-conformance.js"
|
|
44
48
|
},
|
|
49
|
+
"./testing/tool-effect-store-conformance": {
|
|
50
|
+
"types": "./dist/testing/tool-effect-store-conformance.d.ts",
|
|
51
|
+
"default": "./dist/testing/tool-effect-store-conformance.js"
|
|
52
|
+
},
|
|
45
53
|
"./testing/extension-conformance": {
|
|
46
54
|
"types": "./dist/testing/extension-conformance.d.ts",
|
|
47
55
|
"default": "./dist/testing/extension-conformance.js"
|
|
@@ -99,6 +107,7 @@
|
|
|
99
107
|
"!dist/__tests__",
|
|
100
108
|
"!dist/**/*.map",
|
|
101
109
|
"docs",
|
|
110
|
+
"!docs/review-coverage-*",
|
|
102
111
|
"templates",
|
|
103
112
|
"CHANGELOG.md"
|
|
104
113
|
],
|
|
@@ -111,32 +120,45 @@
|
|
|
111
120
|
"packages/credentials-node",
|
|
112
121
|
"packages/mcp",
|
|
113
122
|
"packages/evals",
|
|
123
|
+
"packages/workflows",
|
|
114
124
|
"packages/coding-agent",
|
|
115
125
|
"packages/coding-security",
|
|
116
|
-
"packages/workflows",
|
|
117
126
|
"packages/memory",
|
|
118
127
|
"packages/rag",
|
|
119
128
|
"packages/server",
|
|
120
129
|
"packages/supervisor",
|
|
121
130
|
"packages/web-tools",
|
|
131
|
+
"packages/work-tools",
|
|
132
|
+
"packages/policy",
|
|
133
|
+
"packages/model-router",
|
|
134
|
+
"packages/enterprise-postgres",
|
|
122
135
|
"packages/browser",
|
|
136
|
+
"packages/ag-ui",
|
|
123
137
|
"packages/prism-*"
|
|
124
138
|
],
|
|
125
139
|
"scripts": {
|
|
126
140
|
"build:core": "tsc",
|
|
127
|
-
"
|
|
141
|
+
"clean": "rm -rf dist packages/*/dist",
|
|
142
|
+
"build": "npm run clean && npm run build:core && npm run build --workspaces --if-present",
|
|
128
143
|
"typecheck": "npm run build && npm run typecheck --workspaces --if-present && tsc -p examples --noEmit",
|
|
129
|
-
"test": "npm run build && node --test dist/__tests__/*.test.js && npm run test --workspaces --if-present",
|
|
144
|
+
"test": "npm run build && node --test dist/__tests__/*.test.js && node --test scripts/release-gate.test.mjs scripts/tooling-gate.test.mjs scripts/budget-gate.test.mjs scripts/phase8-conformance.test.mjs scripts/phase9-conformance.test.mjs scripts/phase10-conformance.test.mjs scripts/phase11-conformance.test.mjs scripts/phase11-freeze.test.mjs scripts/phase12-freeze.test.mjs scripts/benchmark-0.1.0.test.mjs scripts/e2e-enterprise-journey.test.mjs scripts/e2e-coding-journey.test.mjs && npm run test --workspaces --if-present",
|
|
145
|
+
"test:coverage": "node --test --experimental-test-coverage --test-coverage-lines=60 --test-coverage-functions=70 --test-coverage-branches=75 --test-coverage-exclude='**/__tests__/**' --test-coverage-exclude='**/node_modules/**' --test-coverage-exclude='**/scripts/**' --test-coverage-exclude='**/packages/**' --test-coverage-exclude='**/examples/**' dist/__tests__/*.test.js",
|
|
146
|
+
"lint": "biome lint .",
|
|
147
|
+
"format": "biome format --write .",
|
|
148
|
+
"format:check": "biome format .",
|
|
130
149
|
"pack:dry-run": "npm pack --dry-run && npm run pack:dry-run --workspaces --if-present",
|
|
131
|
-
"test:postgres": "npm run test:postgres --workspace @arnilo/prism-session-store-postgres && npm run test:postgres --workspace @arnilo/prism-memory",
|
|
150
|
+
"test:postgres": "node scripts/require-postgres-url.mjs && npm run test:postgres --workspace @arnilo/prism-session-store-postgres && npm run test:postgres --workspace @arnilo/prism-memory && npm run test:postgres --workspace @arnilo/prism-enterprise-postgres && node --test scripts/phase7-conformance.test.mjs scripts/phase12-restart-recovery.test.mjs",
|
|
132
151
|
"release:dry-run": "npm run sdk:ready",
|
|
133
152
|
"release:check": "node scripts/release.mjs check",
|
|
134
153
|
"release:publish": "node scripts/release.mjs publish",
|
|
135
|
-
"sdk:ready": "npm run typecheck && npm test && npm run pack:dry-run"
|
|
154
|
+
"sdk:ready": "npm run typecheck && npm run lint && npm run format:check && npm test && npm run test:coverage && npm run pack:dry-run && npm run release:gate",
|
|
155
|
+
"release:gate": "node scripts/release.mjs gate",
|
|
156
|
+
"security:threat-suites": "node --test scripts/phase8-conformance.test.mjs scripts/phase9-conformance.test.mjs scripts/phase10-conformance.test.mjs scripts/phase11-conformance.test.mjs"
|
|
136
157
|
},
|
|
137
158
|
"devDependencies": {
|
|
138
|
-
"
|
|
139
|
-
"@types/node": "^26.1.1"
|
|
159
|
+
"@biomejs/biome": "^2.5.5",
|
|
160
|
+
"@types/node": "^26.1.1",
|
|
161
|
+
"typescript": "^7.0.2"
|
|
140
162
|
},
|
|
141
163
|
"engines": {
|
|
142
164
|
"node": ">=20"
|
|
@@ -72,5 +72,27 @@
|
|
|
72
72
|
"imports": "import { createAgent } from \"@arnilo/prism\";\nimport {\n createNeuralWattProvider,\n defineNeuralWattModel,\n} from \"@arnilo/prism-provider-neuralwatt\";",
|
|
73
73
|
"providerExpression": "createNeuralWattProvider({\n apiKey: () => process.env.NEURALWATT_API_KEY,\n })",
|
|
74
74
|
"modelExpression": "defineNeuralWattModel({\n model: \"glm-5.2\",\n cache: { kind: \"implicit\" },\n })"
|
|
75
|
+
},
|
|
76
|
+
"alibaba": {
|
|
77
|
+
"id": "alibaba",
|
|
78
|
+
"packageName": "@arnilo/prism-provider-alibaba",
|
|
79
|
+
"envKey": "DASHSCOPE_API_KEY",
|
|
80
|
+
"envPlaceholder": "sk-...",
|
|
81
|
+
"modelProvider": "alibaba",
|
|
82
|
+
"modelName": "qwen-plus",
|
|
83
|
+
"imports": "import { createAgent } from \"@arnilo/prism\";\nimport {\n createAlibabaProvider,\n defineAlibabaModel,\n} from \"@arnilo/prism-provider-alibaba\";",
|
|
84
|
+
"providerExpression": "createAlibabaProvider({\n apiKey: () => process.env.DASHSCOPE_API_KEY,\n })",
|
|
85
|
+
"modelExpression": "defineAlibabaModel({ model: \"qwen-plus\" })"
|
|
86
|
+
},
|
|
87
|
+
"ollama": {
|
|
88
|
+
"id": "ollama",
|
|
89
|
+
"packageName": "@arnilo/prism-provider-ollama",
|
|
90
|
+
"envKey": "OLLAMA_API_KEY",
|
|
91
|
+
"envPlaceholder": "your-ollama-api-key",
|
|
92
|
+
"modelProvider": "ollama",
|
|
93
|
+
"modelName": "gpt-oss:20b",
|
|
94
|
+
"imports": "import { createAgent } from \"@arnilo/prism\";\nimport {\n createOllamaProvider,\n defineOllamaModel,\n} from \"@arnilo/prism-provider-ollama\";",
|
|
95
|
+
"providerExpression": "createOllamaProvider({\n apiKey: () => process.env.OLLAMA_API_KEY,\n })",
|
|
96
|
+
"modelExpression": "defineOllamaModel({ model: \"gpt-oss:20b\" })"
|
|
75
97
|
}
|
|
76
98
|
}
|