@arnilo/prism 0.7.0 → 0.9.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 +73 -0
- package/README.md +12 -11
- package/dist/agent-approval.d.ts +15 -2
- package/dist/agent-approval.js +5 -1
- package/dist/agent-event-source.d.ts +9 -1
- package/dist/agent-event-source.js +10 -3
- package/dist/agent-loops.js +7 -4
- package/dist/agent-run-lifecycle.d.ts +15 -1
- package/dist/agent-run-lifecycle.js +91 -10
- package/dist/agent-run-state.d.ts +34 -2
- package/dist/agent-run-state.js +68 -6
- package/dist/agent-session/helpers.js +20 -1
- package/dist/agent-session/session/assemble.js +250 -27
- package/dist/agent-session/session/persist.d.ts +27 -0
- package/dist/agent-session/session/persist.js +94 -12
- package/dist/agent-session/session/provider-round.d.ts +14 -4
- package/dist/agent-session/session/provider-round.js +197 -25
- package/dist/agent-session/session/tool-round.js +24 -2
- package/dist/agent-session/session/types.d.ts +36 -2
- package/dist/agent-session/session.d.ts +40 -4
- package/dist/agent-session/session.js +78 -5
- package/dist/attention-compiler.d.ts +51 -2
- package/dist/attention-compiler.js +282 -21
- package/dist/cache-helpers.d.ts +4 -2
- package/dist/cache-helpers.js +8 -6
- package/dist/checkpoint-restore.d.ts +45 -0
- package/dist/checkpoint-restore.js +54 -0
- package/dist/checkpoints.js +7 -11
- package/dist/context-budget.d.ts +2 -1
- package/dist/context-budget.js +24 -2
- package/dist/contracts-core/agent.d.ts +30 -0
- package/dist/contracts-core/attention.d.ts +95 -0
- package/dist/contracts-core/content.d.ts +15 -0
- package/dist/contracts-core/guardrail-packs.d.ts +41 -0
- package/dist/contracts-core/guardrail-packs.js +2 -0
- package/dist/contracts-core/loop.d.ts +42 -0
- package/dist/contracts-core/provider.d.ts +25 -0
- package/dist/contracts-core/run-limits.d.ts +21 -0
- package/dist/contracts-core/session.d.ts +23 -5
- package/dist/contracts-core/session.js +21 -2
- package/dist/contracts-core/usage.d.ts +40 -0
- package/dist/contracts-core/usage.js +8 -0
- package/dist/contracts-core.d.ts +2 -0
- package/dist/contracts-core.js +2 -0
- package/dist/contracts-protocol.d.ts +90 -4
- package/dist/contracts-run-state.d.ts +82 -6
- package/dist/evidence-grounding.d.ts +29 -0
- package/dist/evidence-grounding.js +162 -0
- package/dist/guardrail-packs/coding-standard.d.ts +3 -0
- package/dist/guardrail-packs/coding-standard.js +63 -0
- package/dist/guardrail-packs/destructive-commands.d.ts +3 -0
- package/dist/guardrail-packs/destructive-commands.js +46 -0
- package/dist/guardrail-packs/errors.d.ts +7 -0
- package/dist/guardrail-packs/errors.js +9 -0
- package/dist/guardrail-packs/index.d.ts +4 -0
- package/dist/guardrail-packs/index.js +15 -0
- package/dist/guardrail-packs/secrets-hygiene.d.ts +3 -0
- package/dist/guardrail-packs/secrets-hygiene.js +23 -0
- package/dist/guardrail-packs/types.d.ts +16 -0
- package/dist/guardrail-packs/types.js +2 -0
- package/dist/guardrail-packs/validation-respect.d.ts +3 -0
- package/dist/guardrail-packs/validation-respect.js +53 -0
- package/dist/guardrails.d.ts +20 -1
- package/dist/guardrails.js +268 -0
- package/dist/host-composition.d.ts +13 -0
- package/dist/host-composition.js +33 -2
- package/dist/index.d.ts +19 -10
- package/dist/index.js +11 -6
- package/dist/input.d.ts +8 -1
- package/dist/input.js +68 -6
- package/dist/middleware.d.ts +37 -2
- package/dist/middleware.js +41 -0
- package/dist/node/session-store-jsonl.js +18 -3
- package/dist/observability.js +6 -0
- package/dist/provider-events.d.ts +11 -3
- package/dist/provider-events.js +62 -4
- package/dist/providers/openai-compatible.js +6 -3
- package/dist/providers/transport.d.ts +3 -1
- package/dist/providers/transport.js +36 -0
- package/dist/redaction.js +18 -2
- package/dist/run-bundle.d.ts +89 -0
- package/dist/run-bundle.js +150 -0
- package/dist/run-limits.d.ts +11 -1
- package/dist/run-limits.js +46 -0
- package/dist/session-stores.d.ts +12 -1
- package/dist/session-stores.js +21 -4
- package/dist/testing/agent-event-source-conformance.js +41 -2
- package/dist/testing/prefix-stability-conformance.d.ts +30 -0
- package/dist/testing/prefix-stability-conformance.js +104 -0
- package/dist/testing/session-store-conformance.d.ts +3 -2
- package/dist/testing/session-store-conformance.js +48 -0
- package/dist/testing/state-concurrency-conformance.js +5 -12
- package/dist/tools.d.ts +5 -0
- package/dist/tools.js +11 -3
- package/dist/usage-estimation.d.ts +29 -0
- package/dist/usage-estimation.js +79 -0
- package/docs/ag-ui.md +5 -0
- package/docs/agent-events.md +68 -1
- package/docs/agent-loops.md +33 -0
- package/docs/agent-session-runtime.md +5 -3
- package/docs/attention-compiler.md +89 -8
- package/docs/coding-agent-tools.md +1 -1
- package/docs/coding-security.md +1 -0
- package/docs/coding-tools.md +0 -1
- package/docs/compaction-and-retry.md +1 -1
- package/docs/compaction-observational-memory.md +34 -7
- package/docs/connected-apps.md +116 -0
- package/docs/context-and-skills.md +13 -0
- package/docs/core.md +1 -1
- package/docs/diagrams.md +6 -6
- package/docs/document-reader.md +9 -9
- package/docs/documents.md +32 -11
- package/docs/durable-runs.md +129 -0
- package/docs/embeddings.md +5 -0
- package/docs/enterprise-postgres-state.md +4 -0
- package/docs/evaluations.md +5 -0
- package/docs/execution-timeline.md +84 -1
- package/docs/guardrails.md +71 -2
- package/docs/history/079-messaging-primitive-review.md +391 -0
- package/docs/history/080-messaging-followon-primitive-review.md +234 -0
- package/docs/history/081-connected-apps-primitive-review.md +74 -0
- package/docs/history/083-prism-work-primitive-review.md +84 -0
- package/docs/history/084-primitive-review.md +96 -0
- package/docs/history/085-honesty-and-cut-primitive-review.md +91 -0
- package/docs/history/README.md +5 -0
- package/docs/history/release-handoffs.md +38 -0
- package/docs/host-compositions.md +8 -6
- package/docs/host-security.md +2 -2
- package/docs/index.md +66 -29
- package/docs/input-and-prompt-assembly.md +3 -3
- package/docs/knowledge-sync.md +4 -0
- package/docs/live-testing.md +5 -3
- package/docs/mcp-tools.md +1 -0
- package/docs/messaging-channel-operations.md +166 -0
- package/docs/messaging-channels.md +150 -0
- package/docs/middleware-hooks.md +38 -2
- package/docs/migrate-to-0.8.md +124 -0
- package/docs/migrate-to-0.9.md +210 -0
- package/docs/migration.md +43 -0
- package/docs/model-registry.md +12 -2
- package/docs/multi-agent-patterns.md +25 -2
- package/docs/node-jsonl-session-store.md +7 -1
- package/docs/observability.md +7 -3
- package/docs/openapi-tools.md +1 -1
- package/docs/operations.md +1 -3
- package/docs/options-index.md +36 -3
- package/docs/peer-dependencies.md +6 -6
- package/docs/policy-and-audit.md +13 -1
- package/docs/postgres-persistence.md +1 -1
- package/docs/prefix-stability-conformance.md +93 -0
- package/docs/provider-caching.md +4 -4
- package/docs/provider-conformance.md +16 -0
- package/docs/provider-layer.md +2 -2
- package/docs/provider-packages.md +20 -20
- package/docs/providers/neuralwatt.md +5 -1
- package/docs/public-contracts.md +2 -2
- package/docs/rag.md +102 -4
- package/docs/release-and-install.md +55 -47
- package/docs/run-bundle.md +92 -0
- package/docs/runs-and-usage.md +57 -6
- package/docs/scoped-agent-memory.md +262 -0
- package/docs/server.md +2 -0
- package/docs/session-store-conformance.md +1 -2
- package/docs/session-stores.md +17 -17
- package/docs/sheets.md +9 -9
- package/docs/signal-channel.md +112 -0
- package/docs/speech.md +5 -1
- package/docs/sqlite-persistence.md +1 -1
- package/docs/supervisors.md +32 -12
- package/docs/telegram-channel.md +157 -0
- package/docs/testing.md +2 -2
- package/docs/tools.md +17 -0
- package/docs/wiki.md +1 -1
- package/docs/work-artifacts-and-review.md +1 -1
- package/docs/work-connectors.md +9 -9
- package/docs/work-sandbox.md +115 -0
- package/docs/work-tools.md +38 -16
- package/docs/workflows.md +5 -0
- package/package.json +9 -3
- package/templates/business-worker/manifest.json +2 -1
- package/templates/business-worker/src/agent.ts.tmpl +1 -1
- package/templates/business-worker/src/tests/agent.test.ts.tmpl +1 -1
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Work sandbox (`@arnilo/prism-work/sandbox`)
|
|
2
|
+
|
|
3
|
+
## What it does
|
|
4
|
+
|
|
5
|
+
Host-built Docker image and in-process composition for document work. `WORK_SANDBOX_IMAGE` is a digest-pinned fixture (`name@sha256:<64-hex>`); hosts replace the zero digest after `docker build`. `createWorkComposition({ sandbox })` takes an injected `DisposableSandbox` (from `createDockerSandbox`, never forked here), copies its capability attestation, wires `createOfficeTools` filesystem mode plus `work_exec`, and optionally host-side `createWorkTools`. Connectors stay on the host. Default env has no `M365_*` / `GOOGLE_*` keys.
|
|
6
|
+
|
|
7
|
+
## When to use it
|
|
8
|
+
|
|
9
|
+
Use when Office parse/generate and Python/LibreOffice/poppler scripts must run inside a network-none container. Do **not** put Graph/Gmail tokens in the sandbox. Do not import `@arnilo/prism-coding-tools` from this package — the host constructs `createDockerSandbox({ image: WORK_SANDBOX_IMAGE, user: "65532:65532", network: { mode: "none" } })` and injects the session.
|
|
10
|
+
|
|
11
|
+
## Inputs / request
|
|
12
|
+
|
|
13
|
+
`createWorkComposition(options)`:
|
|
14
|
+
|
|
15
|
+
| Field | Meaning |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| `sandbox` | Injected adapter with `execFile` and optional `readFile`/`writeFile`/`root`. Required. |
|
|
18
|
+
| `connectors?` | `WorkToolsOptions` for host-side M365/GWS tools. |
|
|
19
|
+
| `office?` | Extra `createOfficeTools` options (caps, artifacts, redactor). |
|
|
20
|
+
| `reader?` | Host-built `DocumentReader`; exposed on the composition, not turned into tools. |
|
|
21
|
+
| `filesystem?` | `WorkSandboxFilesystem` when the sandbox has no `readFile`/`writeFile`. |
|
|
22
|
+
| `env?` | Extra container env. `M365_*` and `GOOGLE_*` names throw. |
|
|
23
|
+
|
|
24
|
+
Image build context: `packages/prism-work` (`sandbox/Dockerfile` + `sandbox/soffice.sh` + `vendor/hermes-agent`). Vendored Hermes scripts land at `/opt/prism-work/skills`.
|
|
25
|
+
|
|
26
|
+
## Outputs / response / events
|
|
27
|
+
|
|
28
|
+
`{ tools, composition }`. `tools` always include `office_*` and `work_exec`. `composition.capabilities` is a frozen copy of the sandbox attestation (malformed metadata → every field `false`). `networkIsolated` is true only when the sandbox attests it — Docker reports that solely for `network: { mode: "none" }`. `composition.execFile` strips token env names and forces LibreOffice `-env:UserInstallation=file:///tmp/lo-profile` without `--accept` / macro flags.
|
|
29
|
+
|
|
30
|
+
## Request/response example
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { createDockerSandbox } from "@arnilo/prism-coding-tools/security";
|
|
34
|
+
import { createWorkComposition, WORK_SANDBOX_IMAGE } from "@arnilo/prism-work/sandbox";
|
|
35
|
+
|
|
36
|
+
const sandbox = await createDockerSandbox({
|
|
37
|
+
docker: "/usr/bin/docker",
|
|
38
|
+
image: WORK_SANDBOX_IMAGE, // replace zeros with the host-built digest
|
|
39
|
+
sourceRoot: workdir,
|
|
40
|
+
user: "65532:65532",
|
|
41
|
+
network: { mode: "none" },
|
|
42
|
+
});
|
|
43
|
+
const { tools, composition } = createWorkComposition({ sandbox, connectors });
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Implementation example
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { createWorkComposition, WORK_SANDBOX_IMAGE } from "@arnilo/prism-work/sandbox";
|
|
50
|
+
|
|
51
|
+
const { tools, composition } = createWorkComposition({
|
|
52
|
+
sandbox: fakeDisposableSandbox, // tests inject this; no Docker
|
|
53
|
+
});
|
|
54
|
+
composition.capabilities.networkIsolated; // copied, never invented
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Build:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
docker build -f packages/prism-work/sandbox/Dockerfile -t prism-work-sandbox packages/prism-work
|
|
61
|
+
docker image inspect --format '{{index .RepoDigests 0}}' prism-work-sandbox
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Extension and configuration notes
|
|
65
|
+
|
|
66
|
+
- Do not fork `createDockerSandbox`. Image pull/build stays outside Prism (`--pull=never`).
|
|
67
|
+
- Connectors optional and host-side. Bytes move via sandbox import/export and office filesystem tools.
|
|
68
|
+
- `work_exec` is argv-only (`file` + `args`); no model-supplied shell string.
|
|
69
|
+
- Protected image check: `PRISM_TEST_WORK_SANDBOX=1` runs `scripts/work-sandbox-image.test.mjs` and sandbox recalc/render/legacy-convert tests. Default `npm test` does not spawn `soffice`.
|
|
70
|
+
|
|
71
|
+
## Recalc and visual QA
|
|
72
|
+
|
|
73
|
+
In-process `SheetModel` does not evaluate formulas. Cached values are filled only by LibreOffice in this image (`network: none`, isolated `/tmp/lo-profile`, deleted with the container). Vendored scripts:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
python3 /opt/prism-work/skills/skills/productivity/xlsx/scripts/xlsx_recalc.py /workspace/out.xlsx --timeout 60
|
|
77
|
+
python3 /opt/prism-work/skills/skills/productivity/powerpoint/scripts/pptx_render.py /workspace/deck.pptx --outdir /workspace/render
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Equivalent argv (wrapper already injects `-env:UserInstallation=file:///tmp/lo-profile --headless`):
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
soffice --headless -env:UserInstallation=file:///tmp/lo-profile --convert-to pdf --outdir /tmp/out /workspace/out.xlsx
|
|
84
|
+
pdftoppm -png -r 100 /tmp/out/out.pdf /tmp/out/page
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
External workbook links cannot be fetched. Recalc then fails closed: formula stays, cached value missing or error — Prism does not invent a number. `soffice` timeout ≤ 60 s.
|
|
88
|
+
|
|
89
|
+
## Legacy convert
|
|
90
|
+
|
|
91
|
+
Prism AST still refuses non-ZIP packages. Convert OLE `.doc` / `.xls` / `.ppt` inside this image (`network: none`, isolated `/tmp/lo-profile`, macros refused), then `office_parse`:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
soffice --headless -env:UserInstallation=file:///tmp/lo-profile --convert-to docx --outdir /workspace /workspace/legacy.doc
|
|
95
|
+
soffice --headless -env:UserInstallation=file:///tmp/lo-profile --convert-to xlsx --outdir /workspace /workspace/legacy.xls
|
|
96
|
+
soffice --headless -env:UserInstallation=file:///tmp/lo-profile --convert-to pptx --outdir /workspace /workspace/legacy.ppt
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Do not enable macros. Encrypted OOXML stays dropped. No in-process OLE parser.
|
|
100
|
+
|
|
101
|
+
## Security and performance notes
|
|
102
|
+
|
|
103
|
+
- Default network none. Composition does not claim isolation the sandbox did not attest.
|
|
104
|
+
- Token env keys denied by name (`M365_*`, `GOOGLE_*`) at composition construct and `execFile`.
|
|
105
|
+
- LibreOffice wrapper: private `/tmp/lo-profile`, `--headless`, no macro enable, `--accept` refused. No listening socket. Legacy convert uses the same wrapper.
|
|
106
|
+
- Zip bombs: existing office parse caps; sandbox export uses existing export caps.
|
|
107
|
+
- Image build is CI/protected, not default unit tests. Composition construct is in-process with a fake sandbox.
|
|
108
|
+
|
|
109
|
+
## Related APIs
|
|
110
|
+
|
|
111
|
+
- [Coding security](coding-security.md) — `createDockerSandbox` digest pin, user, network none
|
|
112
|
+
- [Work tools](work-tools.md) — host-side connectors
|
|
113
|
+
- [Documents](documents.md) — `createOfficeTools`
|
|
114
|
+
- [Document reader](document-reader.md) — optional `reader` injection
|
|
115
|
+
- [Context and skills](context-and-skills.md) — `loadWorkSkills()` (`docx`, `xlsx`, `powerpoint`, `pdf`)
|
package/docs/work-tools.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# Work tools
|
|
2
2
|
|
|
3
|
-
Optional `@arnilo/prism-
|
|
3
|
+
Optional `@arnilo/prism-work/connectors` subpath: identity-scoped Microsoft 365 and Google Workspace connectors. Host-pinned CLI binaries or pinned HTTP adapters; hard-coded operation maps; draft-then-approve mutations; side-effect idempotency; shared mail/calendar/file/task result shapes. HTTP file gets persist untrusted bytes to a host artifact store or contained filesystem, never the transcript.
|
|
4
4
|
|
|
5
5
|
## When to use
|
|
6
6
|
|
|
7
|
-
Use when agents must read or mutate tenant mail/calendar/files/tasks through
|
|
7
|
+
Use when agents must read or mutate tenant mail/calendar/files/tasks through a host-pinned enterprise CLI or a pinned HTTP adapter — not through model-built shell strings, model-supplied URLs, or generic Graph/Discovery free-form calls.
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npm install @arnilo/prism-
|
|
12
|
+
npm install @arnilo/prism @arnilo/prism-work
|
|
13
13
|
# host separately:
|
|
14
14
|
# npm i -g @pnp/cli-microsoft365
|
|
15
15
|
# npm i -g @googleworkspace/cli
|
|
@@ -20,26 +20,28 @@ npm install @arnilo/prism-core
|
|
|
20
20
|
```ts
|
|
21
21
|
import {
|
|
22
22
|
createWorkTools,
|
|
23
|
-
|
|
23
|
+
createMicrosoft365HttpAdapter,
|
|
24
24
|
createGoogleWorkspaceCliAdapter,
|
|
25
|
+
createGoogleWorkspaceHttpAdapter,
|
|
25
26
|
createMemoryIdempotencyStore,
|
|
26
|
-
} from "@arnilo/prism-
|
|
27
|
-
|
|
27
|
+
} from "@arnilo/prism-work/connectors";
|
|
28
|
+
import { createOAuthWorkTokenProvider } from "@arnilo/prism-core/credentials/node";
|
|
29
|
+
// or: import { createGoogleWorkspaceCliAdapter } from "@arnilo/prism-work/connectors/google-workspace";
|
|
28
30
|
|
|
29
|
-
const microsoft365 =
|
|
30
|
-
binary: process.env.M365_BIN!,
|
|
31
|
-
configDir: `/var/prism/m365/${tenant}/${user}`,
|
|
31
|
+
const microsoft365 = createMicrosoft365HttpAdapter({
|
|
32
32
|
identity,
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
tokenProvider: createOAuthWorkTokenProvider({ provider: m365OAuth, store, envVar: "M365_ACCESSTOKEN" }),
|
|
34
|
+
accessEnvVar: "M365_ACCESSTOKEN",
|
|
35
35
|
});
|
|
36
|
+
// Or retain createMicrosoft365CliAdapter({ binary, configDir, identity }) for host-pinned m365.
|
|
36
37
|
|
|
37
|
-
const googleWorkspace =
|
|
38
|
-
binary: process.env.GWS_BIN!,
|
|
39
|
-
configDir: `/var/prism/gws/${tenant}/${user}`,
|
|
38
|
+
const googleWorkspace = createGoogleWorkspaceHttpAdapter({
|
|
40
39
|
identity,
|
|
40
|
+
tokenProvider: createOAuthWorkTokenProvider({ provider: gwsOAuth, store, envVar: "GOOGLE_ACCESS_TOKEN" }),
|
|
41
|
+
accessEnvVar: "GOOGLE_ACCESS_TOKEN",
|
|
41
42
|
// allowedOps: add docs.create / sheets.create / slides.create when gated
|
|
42
43
|
});
|
|
44
|
+
// Or retain createGoogleWorkspaceCliAdapter({ binary, configDir, identity }) for host-pinned gws.
|
|
43
45
|
|
|
44
46
|
const tools = createWorkTools({
|
|
45
47
|
microsoft365,
|
|
@@ -47,6 +49,9 @@ const tools = createWorkTools({
|
|
|
47
49
|
idempotencyStore: createMemoryIdempotencyStore(),
|
|
48
50
|
approval: { isApproved: ({ draftId }) => hostHasApproved(draftId) },
|
|
49
51
|
externalRecipients: { allow: (addr) => addr.endsWith("@contoso.com") },
|
|
52
|
+
scanAttachment: ({ bytes }) => hostScan(bytes), // required before file-get persistence
|
|
53
|
+
artifacts: hostWorkArtifacts, // creates ArtifactBodyRef values and owns body storage
|
|
54
|
+
filesystem: containedFilesystem, // optional destination/source for work-sandbox files
|
|
50
55
|
});
|
|
51
56
|
```
|
|
52
57
|
|
|
@@ -64,7 +69,9 @@ Verified against [CLI for Microsoft 365](https://pnp.github.io/cli-microsoft365/
|
|
|
64
69
|
| `calendar.list` | `m365 outlook event list --output json` |
|
|
65
70
|
| `calendar.add` | `m365 outlook event add --output json --subject … --start … --end …` |
|
|
66
71
|
| `file.list` | `m365 file list --output json --webUrl … --folderUrl …` |
|
|
72
|
+
| `file.get` | HTTP only: `GET /me/drive/items/{id}/content` on `graph.microsoft.com` |
|
|
67
73
|
| `file.add` | `m365 file add --output json --folderUrl … --filePath …` |
|
|
74
|
+
| `file.copy` | `m365 file copy --output json --webUrl … --sourceUrl … --targetUrl …` (draft-then-approve) |
|
|
68
75
|
| `file.share` | `m365 spo file sharinglink add` (`--scope organization` only) |
|
|
69
76
|
| `todo.*` / `planner.*` | capability-gated via `allowedOps` |
|
|
70
77
|
|
|
@@ -80,12 +87,22 @@ Verified against [`@googleworkspace/cli` / `gws`](https://github.com/googleworks
|
|
|
80
87
|
| `calendar.list` | `gws calendar events list --params … --fields …` |
|
|
81
88
|
| `calendar.add` | `gws calendar events insert --params … --json …` |
|
|
82
89
|
| `file.list` | `gws drive files list --params … [--page-all]` (NDJSON when paginated) |
|
|
90
|
+
| `file.get` | HTTP only: `GET /drive/v3/files/{id}?alt=media` on `www.googleapis.com` |
|
|
83
91
|
| `file.add` | `gws drive files create --json … --upload …` |
|
|
84
92
|
| `file.share` | `gws drive permissions create` (`type=domain\|user` only; `anyone` denied) |
|
|
85
93
|
| `task.*` | `gws tasks tasks list\|insert\|patch` |
|
|
86
94
|
| `docs.create` / `sheets.create` / `slides.create` | capability-gated via `allowedOps` |
|
|
95
|
+
| `docs.update` / `sheets.update` / `slides.update` | capability-gated fixed-shape updates; never free-form batch requests |
|
|
87
96
|
|
|
88
|
-
|
|
97
|
+
### Microsoft 365 HTTP adapter
|
|
98
|
+
|
|
99
|
+
`createMicrosoft365HttpAdapter()` uses host-provided OAuth tokens only in the `Authorization` header and pinned fetch against `graph.microsoft.com`. Its fixed map covers Outlook messages/events, draft-then-approve OneDrive copy/sharing, and capability-gated To Do/Planner tasks. `ensureReady()` performs a bounded Graph `/me` request. `m365_file_get` accepts only an item ID and uses fixed `/me/drive/items/{id}/content`; it writes untrusted bytes to an artifact and/or contained filesystem after `scanAttachment`, returning only `{ artifact?, path?, byteLength, contentHash, untrusted: true }`. File list/upload/copy accepts an HTTPS Graph Drive-item URL; arbitrary SharePoint links are rejected rather than resolved with an extra request. The CLI adapter remains available for CLI-specific SharePoint paths.
|
|
100
|
+
|
|
101
|
+
### Google Workspace HTTP adapter
|
|
102
|
+
|
|
103
|
+
`createGoogleWorkspaceHttpAdapter()` uses host-provided OAuth tokens only in the `Authorization` header and pinned fetch against `docs.googleapis.com`, `gmail.googleapis.com`, `sheets.googleapis.com`, `slides.googleapis.com`, `www.googleapis.com`, and `tasks.googleapis.com`. Its operation map is fixed: Gmail messages, Calendar events, Drive files/permissions, Google Tasks, and capability-gated native Docs/Sheets/Slides creates plus draft-then-approve fixed-shape updates. Docs accepts only replace-text and insert-text requests; Sheets PUTs a string matrix with `valueInputOption=RAW`; Slides accepts only shape text insertion. No tool accepts a free-form `requests[]`. `ensureReady()` performs a bounded Gmail profile request; `gws_file_get` accepts only an item ID and uses fixed `Drive files.get?alt=media`, persisting untrusted bytes exactly like `m365_file_get`. `file.add` accepts a host-local path, `ArtifactBodyRef`, or contained sandbox path; its approved draft binds the content SHA-256 and rejects changed bytes. The CLI adapter remains available.
|
|
104
|
+
|
|
105
|
+
Startup: M365 CLI uses `version --output json`; M365 HTTP `ensureReady()` uses Graph `/me`; GWS CLI uses `--version`; GWS HTTP `ensureReady()` uses Gmail profile. Forbidden: `login`, `setup`, `auth`, `schema`, `doctor`, `--debug`, `--verbose`, credentials in argv, anonymous share, model-supplied command strings / URLs / free-form Discovery.
|
|
89
106
|
|
|
90
107
|
### Draft → approve → execute (0.7.0, R02)
|
|
91
108
|
|
|
@@ -99,6 +116,7 @@ In Prism 0.7.0, draft lifecycles are durably managed:
|
|
|
99
116
|
- **Resuming approved drafts**: Mutation tools accept `{ draftId, revision }` without requiring callers to re-supply the full payload. The tool loads the stored draft, validates approval status and digest, reauthorizes immediately before execution, and executes the effect.
|
|
100
117
|
- **Idempotent duplicate approvals**: Re-approving an approved draft with the same approval object is idempotent. Submitting an approval with a mismatched revision or payload digest is rejected with `ERR_PRISM_WORK_DRAFT_STALE` or `ERR_PRISM_WORK_DRAFT_DIGEST`.
|
|
101
118
|
- **Ambiguous failure handling**: If a connector call fails ambiguously after dispatch, both the idempotency record and the draft are marked `unknown`. Re-running with that draft ID or idempotency key fails closed (`ERR_PRISM_WORK_IDEMPOTENCY_UNKNOWN`) and never auto-replays without explicit operator reconciliation.
|
|
119
|
+
- **File-byte binding**: `*_file_draft_upload` accepts a host-local path, `ArtifactBodyRef`, or contained sandbox path. Its payload digest includes `contentHash`; execution re-hashes bytes and rejects a changed source. Artifact reads verify their hash/size through `ArtifactBodyStore`.
|
|
102
120
|
- **Optional body offloading**: Supplying `bodies: ArtifactBodyStore` automatically stores large draft message/file bodies in the object store with an `ArtifactBodyRef` recorded on the draft metadata.
|
|
103
121
|
|
|
104
122
|
### Durable idempotency (0.0.23)
|
|
@@ -116,6 +134,8 @@ In Prism 0.7.0, draft lifecycles are durably managed:
|
|
|
116
134
|
|
|
117
135
|
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.
|
|
118
136
|
|
|
137
|
+
Durable adapters reject with the portable codes `ERR_PRISM_WORK_IDEMPOTENCY` (a claim or payload the adapter refuses) and `ERR_PRISM_WORK_IDEMPOTENCY_CONFLICT` (a lost race, a stale claim token, or a transition out of order). Match on `error.code`: the error *class* is adapter-specific — the in-memory store raises `WorkToolError`, `createPostgresEnterpriseState(...).workIdempotency` raises `EnterprisePostgresError`, because `@arnilo/prism-core` cannot depend on `@arnilo/prism-work` at runtime — so an `instanceof` check that worked against the pre-move import path will silently stop matching. `packages/prism-core/src/enterprise/postgres/__tests__/work-idempotency.integration.test.ts` drives both adapters through the same conflict scenarios and asserts they report the same codes.
|
|
138
|
+
|
|
119
139
|
## Subprocess environment isolation (0.2.0, plan 020 Task 3)
|
|
120
140
|
|
|
121
141
|
`createCliRunner` never inherits the host environment. The child process receives only:
|
|
@@ -145,6 +165,7 @@ Environment maps are validated before spawn: NUL-free, `[A-Za-z_][A-Za-z0-9_]*`
|
|
|
145
165
|
| Pagination pages | 20 / 100 |
|
|
146
166
|
| Items / aggregate | 50/500 ; 200/2000 |
|
|
147
167
|
| Body / stdout | 256 KiB–2 MiB / 2–16 MiB |
|
|
168
|
+
| Download / upload file | 10 MiB / 50 MiB |
|
|
148
169
|
| Process wall time | 60 s / 10 min |
|
|
149
170
|
| Concurrent CLI / identity | 2 / 8 |
|
|
150
171
|
|
|
@@ -155,9 +176,10 @@ Approved mutations require core-derived `context.idempotencyKey` and a configure
|
|
|
155
176
|
## Security
|
|
156
177
|
|
|
157
178
|
- Require host-verified `AgentIdentity`; no cross-identity configDir reuse.
|
|
158
|
-
- Connector tokens
|
|
179
|
+
- Connector tokens: a `tokenProvider` resolves a per-identity access token only at the connector edge — into CLI env for CLI adapters or an `Authorization` header for HTTP adapters, never argv or model context. A missing/expired/revoked/cross-identity/wrong-tenant token fails the call closed before dispatch. Refresh is late-bound and single-flighted per account. Build one with `createOAuthWorkTokenProvider()` from `@arnilo/prism-core/credentials/node`.
|
|
159
180
|
- External mail recipients fail closed unless `externalRecipients.allow` returns true.
|
|
160
181
|
- Anonymous / `anyone` sharing denied.
|
|
182
|
+
- File gets accept IDs, never URLs; their response stream is cancelled at `maxFileBytes`, scanned before persistence, and returned only as artifact/path metadata with `untrusted: true`.
|
|
161
183
|
- CLI stdout/stderr capped (linear chunk capture, killed/rejected before bytes beyond the cap are retained); NDJSON page streams strictly parsed and page-capped; process killed on timeout/abort/overflow.
|
|
162
184
|
- Subprocess environment isolated (0.2.0): fixed allow-listed base + explicit `env` + late-bound token env; `HOME`/telemetry controls forced; reserved/duplicate/NUL/over-cap env and non-absolute binary/configDir fail before spawn. See [Subprocess environment isolation](#subprocess-environment-isolation-020-plan-020-task-3).
|
|
163
185
|
|
package/docs/workflows.md
CHANGED
|
@@ -88,11 +88,16 @@ All workflow limits and runtime `concurrency` reject non-safe integers, zero, ne
|
|
|
88
88
|
| `runId` | Caller-supplied id; otherwise generated (`wfr_…`) |
|
|
89
89
|
| `resume` | For suspended runs: `{ decision: "approve" | "deny", input?, expectedVersion }`; version is mandatory for an exact-once CAS claim |
|
|
90
90
|
| `validateResume` | Host validator for resume input; required when `suspend()` declares `resumeSchema` |
|
|
91
|
+
| `metadata` | Sidecar map (`Record<string, unknown>`) persisted on the checkpoint value; a resume that does not re-state it keeps the recorded map |
|
|
92
|
+
| `restoreHooks` | External-state restore hooks (`CheckpointRestoreHook`) run sequentially on every resume before the scheduler writes; the first failure/timeout throws `CheckpointRestoreError` (`ERR_PRISM_CHECKPOINT_RESTORE`) and leaves the checkpoint untouched |
|
|
93
|
+
| `restoreHookTimeoutMs` | Per-hook restore ceiling in ms; defaults to `DEFAULT_CHECKPOINT_RESTORE_TIMEOUT_MS` (10 s) |
|
|
91
94
|
| `validateState` | Host validator for every initial/restored/updated state; required when workflow declares `state.schema` |
|
|
92
95
|
| `initialState` | Optional host initial state override; nested workflows receive parent state automatically |
|
|
93
96
|
|
|
94
97
|
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.
|
|
95
98
|
|
|
99
|
+
Restore hooks make the resume all-or-nothing across layers: workflow checkpoints carry the host's `metadata` (git commit, document version, workspace fingerprint), `restoreHooks` put each recorded layer back, and only when every hook succeeds does the scheduler claim the checkpoint and continue. Each hook receives `{ workflowId, runId, version, status, metadata, checkpoint }` and an `AbortSignal`; the successful run's `workflow_resumed` event carries `restore: { hooks: [{ hook, durationMs }], durationMs }`. No hooks ⇒ no hook call and no `restore` field.
|
|
100
|
+
|
|
96
101
|
> **Contract — resume-aware nodes.** After an approved resume, the **same** node's `execute` is re-invoked with `ctx.resume`. Returning `suspend(...)` unconditionally re-suspends silently; downstream nodes never run. Branch on `ctx.resume`:
|
|
97
102
|
>
|
|
98
103
|
> ```ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arnilo/prism",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Agent harness for AI providers, agents, sessions, and tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -50,6 +50,10 @@
|
|
|
50
50
|
"types": "./dist/testing/compaction-conformance.d.ts",
|
|
51
51
|
"default": "./dist/testing/compaction-conformance.js"
|
|
52
52
|
},
|
|
53
|
+
"./testing/prefix-stability-conformance": {
|
|
54
|
+
"types": "./dist/testing/prefix-stability-conformance.d.ts",
|
|
55
|
+
"default": "./dist/testing/prefix-stability-conformance.js"
|
|
56
|
+
},
|
|
53
57
|
"./testing/tool-conformance": {
|
|
54
58
|
"types": "./dist/testing/tool-conformance.d.ts",
|
|
55
59
|
"default": "./dist/testing/tool-conformance.js"
|
|
@@ -125,9 +129,10 @@
|
|
|
125
129
|
"packages/mcp",
|
|
126
130
|
"packages/prism-providers",
|
|
127
131
|
"packages/memory",
|
|
132
|
+
"packages/prism-work",
|
|
128
133
|
"packages/prism-core",
|
|
134
|
+
"packages/prism-channels",
|
|
129
135
|
"packages/prism-coding-tools",
|
|
130
|
-
"packages/office",
|
|
131
136
|
"packages/ag-ui",
|
|
132
137
|
"packages/web-tools",
|
|
133
138
|
"packages/acp-agent"
|
|
@@ -146,7 +151,8 @@
|
|
|
146
151
|
"format": "biome format --write .",
|
|
147
152
|
"format:check": "biome format .",
|
|
148
153
|
"pack:dry-run": "npm pack --dry-run && npm run pack:dry-run --workspaces --if-present",
|
|
149
|
-
"test:postgres": "node scripts/
|
|
154
|
+
"test:postgres": "node scripts/postgres-evidence.mjs",
|
|
155
|
+
"test:postgres:run": "node scripts/require-postgres-url.mjs && npm run test:postgres --workspace @arnilo/prism-core --if-present && npm run test:postgres --workspace @arnilo/prism-memory && npm run test:postgres --workspace @arnilo/prism-channels && node --test scripts/phase7-conformance.test.mjs scripts/phase12-restart-recovery.test.mjs scripts/phase22-conformance.test.mjs",
|
|
150
156
|
"test:nats": "node scripts/require-nats-url.mjs && npm run test:nats --workspace @arnilo/prism-core --if-present",
|
|
151
157
|
"release:dry-run": "npm run sdk:ready",
|
|
152
158
|
"release:check": "node scripts/release.mjs check",
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
providerTextDelta,
|
|
11
11
|
} from "@arnilo/prism";
|
|
12
12
|
import { createJsonSchemaArgumentValidator } from "@arnilo/prism-core/validation/json-schema";
|
|
13
|
-
import { createMemoryWorkDraftStore, type SyncWorkDraftStore } from "@arnilo/prism-
|
|
13
|
+
import { createMemoryWorkDraftStore, type SyncWorkDraftStore } from "@arnilo/prism-work/connectors";
|
|
14
14
|
import type { AgentIdentity } from "@arnilo/prism";
|
|
15
15
|
|
|
16
16
|
export interface CreateBusinessWorkerOptions {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { describe, it } from "node:test";
|
|
3
3
|
import { assertHostCompositionReadiness, inspectHostComposition, HostCompositionError } from "@arnilo/prism";
|
|
4
|
-
import { validateApproval } from "@arnilo/prism-
|
|
4
|
+
import { validateApproval } from "@arnilo/prism-work/connectors";
|
|
5
5
|
import type { AgentIdentity } from "@arnilo/prism";
|
|
6
6
|
import { createAppAgent, createWorkerDraftStore } from "../agent.js";
|
|
7
7
|
|