@arnilo/prism-server 0.0.24 → 0.0.25
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 +10 -0
- package/README.md +1 -1
- package/dist/handler.js +55 -6
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.0.25] - 2026-08-06
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Agent resume accepts batch `decisions: RunDecision[]` (exclusive with legacy binary `decision`) with boundary validation.
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
- Released with exact 0.0.25 graph.
|
|
10
|
+
|
|
11
|
+
See [migration guide](../../docs/migration.md) for the 0.0.24 → 0.0.25 notes.
|
|
12
|
+
|
|
3
13
|
## [0.0.24] - 2026-08-04
|
|
4
14
|
|
|
5
15
|
### Added
|
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ const response = await handler(new Request("https://api.example.test/prism/agent
|
|
|
24
24
|
}));
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
Routes: direct/SSE agent run, cross-replica durable event reconnect through object agent exposures with `events` + `resolveRun`, explicitly selected durable agent status/resume through `agentRuns`, direct/SSE workflow run, durable workflow enqueue/status/cancel/resume/replay, and optional ownership-scoped schedule create/list/pause/resume/trigger/delete. `agentRuns` uses core `createAgentRunLifecycle()`; no lifecycle route exists by default. All bodies, responses, events, queues, concurrency, and timeouts are bounded.
|
|
27
|
+
Routes: direct/SSE agent run, cross-replica durable event reconnect through object agent exposures with `events` + `resolveRun`, explicitly selected durable agent status/resume through `agentRuns`, direct/SSE workflow run, durable workflow enqueue/status/cancel/resume/replay, and optional ownership-scoped schedule create/list/pause/resume/trigger/delete. `agentRuns` uses core `createAgentRunLifecycle()`; no lifecycle route exists by default. Agent resume accepts legacy binary `decision` or batch `decisions: RunDecision[]` (exactly one). All bodies, responses, events, queues, concurrency, and timeouts are bounded.
|
|
28
28
|
|
|
29
29
|
Optional 0.0.14 co-work handlers (mount beside the API handler): `createConversationHandler` (durable user-scoped conversation threads with reconnectable redacted replay — `createConversationService`) and `createArtifactHandler` (artifact revisions, approve/reject review, expiring authorized delivery links — `createArtifactService` over the existing checkpoint store). Both are ownership-scoped and fail closed without authorization.
|
|
30
30
|
|
package/dist/handler.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AgentRunStateError, assertIdentityActive, assertIdentityMatchesOwnership, } from "@arnilo/prism";
|
|
1
|
+
import { AgentRunStateError, assertIdentityActive, assertIdentityMatchesOwnership, HARD_MAX_DECISION_REASON_BYTES, HARD_MAX_ELICITATION_BYTES, HARD_MAX_PENDING_DECISIONS, } from "@arnilo/prism";
|
|
2
2
|
import { cancelWorkflowRun, createWorkflowEventBus, enqueueWorkflow, getWorkflowRun, replayWorkflow, resumeWorkflow, runWorkflow, } from "@arnilo/prism-workflows";
|
|
3
3
|
import { isAdmitOperation } from "./drain.js";
|
|
4
4
|
import { resolvePrismServerLimits } from "./limits.js";
|
|
@@ -558,17 +558,66 @@ function isMessage(value) {
|
|
|
558
558
|
const item = value;
|
|
559
559
|
return ["system", "user", "assistant", "tool"].includes(String(item.role)) && Array.isArray(item.content);
|
|
560
560
|
}
|
|
561
|
+
const RUN_DECISION_OUTCOMES = new Set(["allow_once", "allow_for_run", "reject_once", "reject_for_run"]);
|
|
562
|
+
const RUN_DECISION_KEYS = new Set(["approvalId", "outcome", "reason", "modifiedArguments", "elicitation"]);
|
|
563
|
+
/** Boundary validation for a client-supplied decision batch; core re-validates under CAS. */
|
|
564
|
+
function readAgentDecisions(value) {
|
|
565
|
+
if (!Array.isArray(value) || value.length === 0 || value.length > HARD_MAX_PENDING_DECISIONS) {
|
|
566
|
+
throw new PrismServerError("decisions must be a non-empty bounded array", 400, "ERR_PRISM_SERVER_RESUME");
|
|
567
|
+
}
|
|
568
|
+
return value.map((entry) => {
|
|
569
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
570
|
+
throw new PrismServerError("decision entry must be an object", 400, "ERR_PRISM_SERVER_RESUME");
|
|
571
|
+
}
|
|
572
|
+
const row = entry;
|
|
573
|
+
if (Object.keys(row).some((key) => !RUN_DECISION_KEYS.has(key))) {
|
|
574
|
+
throw new PrismServerError("decision entry has unknown keys", 400, "ERR_PRISM_SERVER_RESUME");
|
|
575
|
+
}
|
|
576
|
+
if (typeof row.approvalId !== "string" || row.approvalId.length === 0 || row.approvalId.length > 128) {
|
|
577
|
+
throw new PrismServerError("decision approvalId is invalid", 400, "ERR_PRISM_SERVER_RESUME");
|
|
578
|
+
}
|
|
579
|
+
if (typeof row.outcome !== "string" || !RUN_DECISION_OUTCOMES.has(row.outcome)) {
|
|
580
|
+
throw new PrismServerError("decision outcome is invalid", 400, "ERR_PRISM_SERVER_RESUME");
|
|
581
|
+
}
|
|
582
|
+
if (row.reason !== undefined &&
|
|
583
|
+
(typeof row.reason !== "string" || Buffer.byteLength(row.reason, "utf8") > HARD_MAX_DECISION_REASON_BYTES)) {
|
|
584
|
+
throw new PrismServerError("decision reason exceeds limits", 400, "ERR_PRISM_SERVER_RESUME");
|
|
585
|
+
}
|
|
586
|
+
for (const key of ["modifiedArguments", "elicitation"]) {
|
|
587
|
+
const field = row[key];
|
|
588
|
+
if (field === undefined)
|
|
589
|
+
continue;
|
|
590
|
+
if (!field || typeof field !== "object" || Array.isArray(field)) {
|
|
591
|
+
throw new PrismServerError(`decision ${key} must be an object`, 400, "ERR_PRISM_SERVER_RESUME");
|
|
592
|
+
}
|
|
593
|
+
const text = JSON.stringify(field);
|
|
594
|
+
if (text === undefined || Buffer.byteLength(text, "utf8") > HARD_MAX_ELICITATION_BYTES) {
|
|
595
|
+
throw new PrismServerError(`decision ${key} exceeds limits`, 400, "ERR_PRISM_SERVER_RESUME");
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
return entry;
|
|
599
|
+
});
|
|
600
|
+
}
|
|
561
601
|
function readAgentResume(body) {
|
|
562
|
-
if (Object.keys(body).some((key) => key !== "decision" && key !== "expectedVersion")) {
|
|
602
|
+
if (Object.keys(body).some((key) => key !== "decision" && key !== "decisions" && key !== "expectedVersion")) {
|
|
563
603
|
throw new PrismServerError("Invalid agent resume body", 400, "ERR_PRISM_SERVER_RESUME");
|
|
564
604
|
}
|
|
565
|
-
if (body.decision !== "approve" && body.decision !== "deny") {
|
|
566
|
-
throw new PrismServerError("decision must be approve or deny", 400, "ERR_PRISM_SERVER_RESUME");
|
|
567
|
-
}
|
|
568
605
|
if (!Number.isSafeInteger(body.expectedVersion) || Number(body.expectedVersion) < 1) {
|
|
569
606
|
throw new PrismServerError("expectedVersion must be a positive safe integer", 400, "ERR_PRISM_SERVER_RESUME");
|
|
570
607
|
}
|
|
571
|
-
|
|
608
|
+
if (body.decision !== undefined && body.decisions !== undefined) {
|
|
609
|
+
throw new PrismServerError("provide exactly one of decision or decisions", 400, "ERR_PRISM_SERVER_RESUME");
|
|
610
|
+
}
|
|
611
|
+
if (body.decision !== undefined) {
|
|
612
|
+
if (body.decision !== "approve" && body.decision !== "deny") {
|
|
613
|
+
throw new PrismServerError("decision must be approve or deny", 400, "ERR_PRISM_SERVER_RESUME");
|
|
614
|
+
}
|
|
615
|
+
return { decision: body.decision, expectedVersion: Number(body.expectedVersion) };
|
|
616
|
+
}
|
|
617
|
+
if (body.decisions === undefined) {
|
|
618
|
+
throw new PrismServerError("provide decision or decisions", 400, "ERR_PRISM_SERVER_RESUME");
|
|
619
|
+
}
|
|
620
|
+
return { decisions: readAgentDecisions(body.decisions), expectedVersion: Number(body.expectedVersion) };
|
|
572
621
|
}
|
|
573
622
|
function readResume(body) {
|
|
574
623
|
if (body.decision !== "approve" && body.decision !== "deny") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arnilo/prism-server",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.25",
|
|
4
4
|
"description": "Optional framework-free Web Request-to-Response handler for explicitly selected Prism agents and workflows.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"pack:dry-run": "npm pack --dry-run"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
|
-
"@arnilo/prism": "0.0.
|
|
29
|
-
"@arnilo/prism-workflows": "0.0.
|
|
28
|
+
"@arnilo/prism": "0.0.25",
|
|
29
|
+
"@arnilo/prism-workflows": "0.0.25"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@arnilo/prism": "file:../..",
|