@sema-agent/server 7.142.0 → 7.144.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/MIGRATION.md +608 -0
- package/USAGE.md +63 -36
- package/dist/adoption/plan.js +22 -0
- package/dist/approval-ask-audit-store.d.ts +7 -5
- package/dist/approval-ask-audit-store.js +12 -6
- package/dist/approval-ask-machine.d.ts +15 -7
- package/dist/approval-ask-machine.js +10 -4
- package/dist/approval-card.d.ts +155 -39
- package/dist/approval-card.js +111 -31
- package/dist/approval-queue-kind.d.ts +27 -0
- package/dist/approval-queue-kind.js +6 -0
- package/dist/approval-reconciler.d.ts +8 -8
- package/dist/approval-reconciler.js +3 -3
- package/dist/audit.js +4 -2
- package/dist/boot/budget-tracing.d.ts +1 -1
- package/dist/boot/execution-env.js +1 -1
- package/dist/boot/governance-seams.d.ts +26 -41
- package/dist/boot/governance-seams.js +61 -28
- package/dist/boot/late-binds.d.ts +7 -4
- package/dist/boot/late-binds.js +3 -1
- package/dist/boot/leader.js +2 -4
- package/dist/boot/memory-consolidation.d.ts +0 -8
- package/dist/boot/memory-consolidation.js +3 -3
- package/dist/boot/reapers.js +5 -4
- package/dist/boot/resolve-spec.d.ts +1 -1
- package/dist/boot/resolve-spec.js +8 -8
- package/dist/boot/runner-deps.d.ts +14 -6
- package/dist/boot/runner-deps.js +6 -17
- package/dist/boot/shutdown.d.ts +9 -0
- package/dist/boot/shutdown.js +18 -2
- package/dist/boot/stage-01-config.js +2 -0
- package/dist/boot/stage-02-web-admin-pull.js +3 -0
- package/dist/boot/stage-03-store-backend.d.ts +4 -1
- package/dist/boot/stage-03-store-backend.js +1 -2
- package/dist/boot/stage-04-stores.d.ts +5 -2
- package/dist/boot/stage-04-stores.js +3 -5
- package/dist/boot/stage-05-execution-env.d.ts +5 -2
- package/dist/boot/stage-06-runners.d.ts +5 -2
- package/dist/boot/stage-06-runners.js +1 -3
- package/dist/boot/stage-07-capability-layer.d.ts +5 -2
- package/dist/boot/stage-07-capability-layer.js +6 -2
- package/dist/boot/stage-08-reapers.d.ts +5 -2
- package/dist/boot/stage-09-leader.d.ts +5 -2
- package/dist/boot/stage-10-http-server.d.ts +5 -2
- package/dist/boot/stage-10-http-server.js +4 -1
- package/dist/boot/stores.d.ts +5 -2
- package/dist/boot/stores.js +7 -7
- package/dist/boot/web-admin.d.ts +5 -1
- package/dist/boot/web-admin.js +31 -19
- package/dist/capabilities/hands-lane.d.ts +9 -39
- package/dist/capabilities/hands-lane.js +8 -13
- package/dist/capabilities/mcp-probe.d.ts +3 -2
- package/dist/capabilities/scenarios.d.ts +23 -17
- package/dist/capabilities/scenarios.js +2 -2
- package/dist/capabilities/skills.d.ts +1 -17
- package/dist/capabilities/skills.js +0 -8
- package/dist/capability-facts.d.ts +8 -8
- package/dist/capability-facts.js +4 -4
- package/dist/card-void.d.ts +6 -9
- package/dist/config-catalog.js +1 -1
- package/dist/config-d-migration.d.ts +2 -0
- package/dist/config-dir-trust.js +58 -29
- package/dist/config-invariants.d.ts +7 -1
- package/dist/config-invariants.js +7 -0
- package/dist/config-provider.d.ts +10 -0
- package/dist/config-provider.js +15 -4
- package/dist/config-root-migration.d.ts +13 -0
- package/dist/config-root-migration.js +99 -0
- package/dist/config-types.d.ts +5 -5
- package/dist/config.d.ts +4 -3
- package/dist/config.js +1 -1
- package/dist/delegated-session-owner.d.ts +35 -151
- package/dist/delegated-session-owner.js +1 -83
- package/dist/device-ws-protocol.d.ts +1 -0
- package/dist/device-ws-protocol.js +2 -1
- package/dist/entry-mode-clamp-notice.d.ts +1 -1
- package/dist/env-model-table-gate.d.ts +53 -0
- package/dist/env-model-table-gate.js +53 -0
- package/dist/execution-placement.d.ts +7 -4
- package/dist/fleet/fleet-bus.d.ts +28 -52
- package/dist/fleet/fleet-bus.js +35 -100
- package/dist/fleet/fleet-reconciler.d.ts +4 -4
- package/dist/fleet/fleet-reconciler.js +1 -1
- package/dist/fleet/subagent-tail-bus.d.ts +14 -11
- package/dist/fleet/subagent-tail-bus.js +15 -28
- package/dist/hooks/classic-stations.d.ts +3 -1
- package/dist/hooks/classic-stations.js +2 -0
- package/dist/hooks/hook-runner.js +28 -7
- package/dist/host-decision.d.ts +54 -20
- package/dist/host-decision.js +42 -14
- package/dist/http/admission.d.ts +1 -1
- package/dist/http/admission.js +7 -3
- package/dist/http/approval-sweeps.js +11 -8
- package/dist/http/caller-trust.d.ts +1 -1
- package/dist/http/durable-run-leg.js +2 -2
- package/dist/http/engine-refusal.d.ts +16 -0
- package/dist/http/engine-refusal.js +17 -0
- package/dist/http/memory-compliance-failure-reply.d.ts +1 -3
- package/dist/http/memory-compliance-failure-reply.js +4 -6
- package/dist/http/owner-access.d.ts +11 -0
- package/dist/http/owner-access.js +13 -0
- package/dist/http/principal-gate.d.ts +1 -1
- package/dist/http/refusal-body.d.ts +7 -3
- package/dist/http/refusal-body.js +1 -0
- package/dist/http/resume-legs.d.ts +21 -7
- package/dist/http/resume-legs.js +82 -53
- package/dist/http/resume-restatement.d.ts +39 -0
- package/dist/http/resume-restatement.js +25 -0
- package/dist/http/route-ctx.d.ts +35 -10
- package/dist/http/route-ctx.js +1 -1
- package/dist/http/routes/approvals-assistant.d.ts +9 -1
- package/dist/http/routes/approvals-assistant.js +143 -31
- package/dist/http/routes/capabilities.js +11 -2
- package/dist/http/routes/memory-policy.d.ts +2 -2
- package/dist/http/routes/memory-policy.js +1 -1
- package/dist/http/routes/notify-wake.js +23 -25
- package/dist/http/routes/rules.js +48 -13
- package/dist/http/routes/runs.js +9 -8
- package/dist/http/routes/session-memory-sync.d.ts +34 -6
- package/dist/http/routes/session-memory-sync.js +27 -10
- package/dist/http/routes/session-sync.js +13 -20
- package/dist/http/routes/sessions.d.ts +2 -2
- package/dist/http/routes/sessions.js +22 -23
- package/dist/http/routes/tasks.js +27 -49
- package/dist/http/server.d.ts +13 -7
- package/dist/http/server.js +2 -2
- package/dist/http/stored-body-replay.js +15 -4
- package/dist/http/wire-types.d.ts +17 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lane-home-probe.d.ts +15 -0
- package/dist/lane-home-probe.js +4 -0
- package/dist/leader/leader.js +5 -2
- package/dist/leader/wire.d.ts +7 -1
- package/dist/leader/wire.js +6 -5
- package/dist/leg-facts.d.ts +37 -0
- package/dist/leg-facts.js +15 -0
- package/dist/memory-bundle-engine.d.ts +1 -4
- package/dist/memory-bundle-engine.js +2 -2
- package/dist/memory-operator-faces.d.ts +30 -66
- package/dist/memory-operator-faces.js +17 -88
- package/dist/memory-scope-stores.d.ts +38 -40
- package/dist/memory-scope-stores.js +51 -36
- package/dist/memory-scope.d.ts +0 -9
- package/dist/memory-scope.js +0 -9
- package/dist/memory-sync-client.js +14 -3
- package/dist/memory-sync.d.ts +20 -4
- package/dist/memory-sync.js +23 -4
- package/dist/model-plane-face.d.ts +13 -7
- package/dist/model-plane-face.js +2 -1
- package/dist/observability/corrupt-read-seat.d.ts +5 -9
- package/dist/observability/corrupt-read-seat.js +1 -2
- package/dist/observability/fail-open.d.ts +30 -10
- package/dist/observability/fail-open.js +27 -10
- package/dist/observability/run-terminal.d.ts +1 -1
- package/dist/observability/run-terminal.js +6 -2
- package/dist/orchestration/workflow-completion-inbox.d.ts +5 -19
- package/dist/orchestration/workflow-completion-inbox.js +0 -12
- package/dist/orchestration/workflow-notify-journal.d.ts +3 -2
- package/dist/orchestration/workflow-park-decision.d.ts +8 -3
- package/dist/orchestration/workflow-park-decision.js +5 -3
- package/dist/org-delivery-gate.d.ts +11 -0
- package/dist/org-delivery-gate.js +19 -1
- package/dist/parked-decide.d.ts +13 -9
- package/dist/parked-decide.js +7 -7
- package/dist/parked-revive-surface.d.ts +6 -7
- package/dist/parked-revive-surface.js +1 -1
- package/dist/peer-inbound-face.d.ts +122 -0
- package/dist/peer-inbound-face.js +123 -0
- package/dist/permission-rule-list.d.ts +13 -9
- package/dist/permission-rule-list.js +7 -3
- package/dist/plugins/approval-ask-store-file.js +18 -6
- package/dist/plugins/approval-ask-store-memory.js +8 -6
- package/dist/plugins/approval-ask-store-sql.d.ts +28 -13
- package/dist/plugins/approval-ask-store-sql.js +30 -11
- package/dist/plugins/caching-session-store.d.ts +0 -3
- package/dist/plugins/caching-session-store.js +0 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +45 -38
- package/dist/plugins/checkpoint-store-sql.js +108 -45
- package/dist/plugins/file-history-store-sql.d.ts +24 -18
- package/dist/plugins/file-history-store-sql.js +24 -19
- package/dist/plugins/file-run-store.d.ts +29 -0
- package/dist/plugins/file-run-store.js +97 -8
- package/dist/plugins/fork-routing-session-store.d.ts +8 -1
- package/dist/plugins/fork-routing-session-store.js +6 -0
- package/dist/plugins/imported-layer-root-retirement.d.ts +82 -0
- package/dist/plugins/imported-layer-root-retirement.js +96 -0
- package/dist/plugins/local-checkpoint-store.d.ts +6 -8
- package/dist/plugins/local-checkpoint-store.js +5 -4
- package/dist/plugins/local-session-store.d.ts +69 -30
- package/dist/plugins/local-session-store.js +133 -57
- package/dist/plugins/local-session-window.d.ts +4 -2
- package/dist/plugins/mailbox-recipient-lifecycle.d.ts +9 -31
- package/dist/plugins/mailbox-recipient-lifecycle.js +4 -0
- package/dist/plugins/mailbox-store-sql.d.ts +12 -8
- package/dist/plugins/mailbox-store-sql.js +76 -7
- package/dist/plugins/memory-sync-store-pg.d.ts +15 -0
- package/dist/plugins/memory-sync-store-pg.js +21 -0
- package/dist/plugins/memory-sync-store-tidb.d.ts +2 -0
- package/dist/plugins/memory-sync-store-tidb.js +13 -1
- package/dist/plugins/one-time-migrations.d.ts +44 -14
- package/dist/plugins/one-time-migrations.js +118 -15
- package/dist/plugins/org-delivery-quota-store.d.ts +1 -1
- package/dist/plugins/org-delivery-quota-store.js +2 -12
- package/dist/plugins/park-commit-watch.d.ts +1 -1
- package/dist/plugins/permission-rule-store-file.d.ts +10 -1
- package/dist/plugins/permission-rule-store-file.js +15 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +31 -1
- package/dist/plugins/permission-rule-store-sql.js +35 -2
- package/dist/plugins/pg-session-storage.d.ts +1 -9
- package/dist/plugins/pg-session-storage.js +2 -19
- package/dist/plugins/remote-env-device.d.ts +8 -11
- package/dist/plugins/remote-env-device.js +6 -3
- package/dist/plugins/remote-env-host.d.ts +8 -4
- package/dist/plugins/remote-env-host.js +7 -9
- package/dist/plugins/remote-env-k8s.d.ts +37 -24
- package/dist/plugins/remote-env-k8s.js +50 -17
- package/dist/plugins/remote-shell.js +12 -2
- package/dist/plugins/retention-store-sql.d.ts +2 -2
- package/dist/plugins/sandbox-read-floor.d.ts +5 -7
- package/dist/plugins/sandbox-read-floor.js +2 -7
- package/dist/plugins/schema-consistency.d.ts +5 -1
- package/dist/plugins/session-fork.d.ts +1 -1
- package/dist/plugins/session-ownership.d.ts +9 -0
- package/dist/plugins/session-ownership.js +6 -0
- package/dist/plugins/session-store.d.ts +13 -16
- package/dist/plugins/session-store.js +9 -10
- package/dist/plugins/sql-errors.d.ts +10 -1
- package/dist/plugins/sql-errors.js +13 -0
- package/dist/plugins/sql-json-column.d.ts +24 -2
- package/dist/plugins/sql-json-column.js +38 -3
- package/dist/plugins/store-backend.d.ts +23 -28
- package/dist/plugins/store-backend.js +12 -3
- package/dist/plugins/store-contracts.d.ts +29 -5
- package/dist/plugins/store-contracts.js +4 -1
- package/dist/plugins/task-attachment-store.d.ts +2 -2
- package/dist/plugins/task-attachment-store.js +37 -52
- package/dist/plugins/tidb-session-store.d.ts +2 -14
- package/dist/plugins/tidb-session-store.js +2 -20
- package/dist/plugins/tool-result-store-sql.d.ts +9 -8
- package/dist/plugins/tool-result-store-sql.js +23 -2
- package/dist/plugins/wiring-section-codec.d.ts +32 -0
- package/dist/plugins/wiring-section-codec.js +55 -0
- package/dist/plugins/workspace-fill.d.ts +1 -1
- package/dist/rule-entry-list.d.ts +3 -2
- package/dist/rule-entry-list.js +5 -1
- package/dist/rules-consent.d.ts +28 -5
- package/dist/rules-consent.js +40 -28
- package/dist/run-local.js +11 -6
- package/dist/runs.d.ts +12 -8
- package/dist/runs.js +10 -7
- package/dist/runtime-governance.d.ts +1 -1
- package/dist/security.d.ts +0 -4
- package/dist/server-secret-env.d.ts +4 -0
- package/dist/session-error-wire.d.ts +34 -23
- package/dist/session-error-wire.js +8 -3
- package/dist/session-incarnation.d.ts +3 -3
- package/dist/session-notify-door.d.ts +106 -0
- package/dist/session-notify-door.js +97 -0
- package/dist/session-sync.d.ts +9 -14
- package/dist/session-sync.js +5 -5
- package/dist/session-workspace.d.ts +3 -13
- package/dist/session-workspace.js +0 -9
- package/dist/standing-answer-grant.d.ts +5 -0
- package/dist/standing-answer-grant.js +10 -0
- package/dist/stores-sql/session-policy-store-sql.d.ts +4 -2
- package/dist/stores-sql/session-policy-store-sql.js +3 -13
- package/dist/task-mcp.d.ts +1 -1
- package/dist/task-settings.d.ts +11 -8
- package/dist/task-settings.js +31 -11
- package/dist/tool-approval.d.ts +55 -35
- package/dist/tool-approval.js +112 -74
- package/dist/trace/core-keyset-guard.d.ts +12 -9
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +4 -2
- package/dist/trace/exit-seat.d.ts +7 -1
- package/dist/trace/exit-seat.js +1 -0
- package/dist/trace/ledger-sink.d.ts +2 -2
- package/dist/trace/ledger-sink.js +12 -4
- package/dist/trace/model-segment.d.ts +74 -25
- package/dist/trace/model-segment.js +141 -32
- package/dist/trace/project.d.ts +15 -6
- package/dist/trace/project.js +12 -4
- package/dist/trace/redact.d.ts +72 -9
- package/dist/trace/redact.js +1509 -471
- package/dist/trace/self-minted-wire.d.ts +598 -47
- package/dist/trace/self-minted-wire.js +190 -20
- package/dist/trace/wire-projection-faces.d.ts +1 -1
- package/dist/trace/wire-projection-faces.js +1 -1
- package/dist/verifier-pause-card.d.ts +33 -5
- package/dist/verifier-pause-card.js +15 -10
- package/dist/web-admin-client/apply-effective.d.ts +12 -16
- package/dist/web-admin-client/apply-effective.js +90 -10
- package/dist/workspace-space.d.ts +19 -3
- package/dist/workspace-space.js +8 -4
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -1
- package/package.json +3 -2
- package/wire-changes/server@7.143.0.json +1167 -0
- package/wire-changes/server@7.144.0.json +291 -0
- package/dist/session-records-skipped-notice.d.ts +0 -21
- package/dist/session-records-skipped-notice.js +0 -27
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { externalNotificationInboxEntry } from "./orchestration/workflow-completion-inbox.js";
|
|
2
|
+
import { sameOccupant } from "./execution-placement.js";
|
|
3
|
+
import { recordFailOpen } from "./observability/fail-open.js";
|
|
4
|
+
export function createSessionNotifyDoor(io) {
|
|
5
|
+
async function recheck(target) {
|
|
6
|
+
if (io.headOf === undefined)
|
|
7
|
+
throw new Error("session notify door: the session store has no head read (headOf); the occupant cannot be re-checked before delivery");
|
|
8
|
+
const head = await io.headOf(target.sessionId);
|
|
9
|
+
if (head === undefined)
|
|
10
|
+
return { delivery: "withdrawn", reason: "gone" };
|
|
11
|
+
return sameOccupant(target.occupant, head) ? undefined : { delivery: "withdrawn", reason: "replaced" };
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
async deliver(target, event) {
|
|
15
|
+
const liveTaskId = await io.activeTaskIdOf(target.sessionId).catch((e) => {
|
|
16
|
+
recordFailOpen("server.session-notify.active-run-read-failed", `sessionId=${target.sessionId} err=${e instanceof Error ? e.message : String(e)}`);
|
|
17
|
+
return undefined;
|
|
18
|
+
});
|
|
19
|
+
if (liveTaskId !== undefined && liveTaskId !== null && io.liveRuns.has(liveTaskId)) {
|
|
20
|
+
const withdrawn = await recheck(target);
|
|
21
|
+
if (withdrawn !== undefined)
|
|
22
|
+
return withdrawn;
|
|
23
|
+
const live = io.liveRuns.get(liveTaskId);
|
|
24
|
+
if (live !== undefined) {
|
|
25
|
+
try {
|
|
26
|
+
await live.notify(event, { priority: "next" });
|
|
27
|
+
return { delivery: "live" };
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
const code = e.code;
|
|
31
|
+
if (typeof code === "string" && code.startsWith("notify."))
|
|
32
|
+
return { delivery: "refused", code, message: e instanceof Error ? e.message : "invalid notification" };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (io.inbox === undefined)
|
|
37
|
+
return { delivery: "no_inbox" };
|
|
38
|
+
const withdrawn = await recheck(target);
|
|
39
|
+
if (withdrawn !== undefined)
|
|
40
|
+
return withdrawn;
|
|
41
|
+
await io.inbox.enqueue(externalNotificationInboxEntry(target.sessionId, target.occupant.owner ?? target.ownerlessEntryOwner, { task_type: "external", ...event }, Date.now()));
|
|
42
|
+
return { delivery: "parked" };
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function createBackgroundCompletionRelay(io) {
|
|
47
|
+
return (root) => (n) => {
|
|
48
|
+
void relayToRoot(io, root, n).catch((err) => io.logger.warn("background_completion_undelivered", { ...completionFields(root, n), reason: "relay_failed", error: err instanceof Error ? err.message : String(err) }));
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async function relayToRoot(io, root, n) {
|
|
52
|
+
const undelivered = (reason, extra) => io.logger.warn("background_completion_undelivered", { ...completionFields(root, n), reason, ...extra });
|
|
53
|
+
const door = io.door();
|
|
54
|
+
if (door === undefined)
|
|
55
|
+
return undelivered("door_unbound");
|
|
56
|
+
const out = await door.deliver({ sessionId: root.sessionId, occupant: { owner: root.owner, incarnation: root.incarnation ?? undefined }, ownerlessEntryOwner: null }, completionEvent(n));
|
|
57
|
+
switch (out.delivery) {
|
|
58
|
+
case "live":
|
|
59
|
+
case "parked":
|
|
60
|
+
return;
|
|
61
|
+
case "refused":
|
|
62
|
+
return undelivered("refused", { code: out.code });
|
|
63
|
+
case "no_inbox":
|
|
64
|
+
return undelivered("no_inbox");
|
|
65
|
+
case "withdrawn":
|
|
66
|
+
return undelivered(withdrawnReason(out.reason));
|
|
67
|
+
default: {
|
|
68
|
+
const unhandled = out;
|
|
69
|
+
throw new Error(`unhandled session notify outcome: ${JSON.stringify(unhandled)}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function withdrawnReason(reason) {
|
|
74
|
+
switch (reason) {
|
|
75
|
+
case "gone":
|
|
76
|
+
return "root_gone";
|
|
77
|
+
case "replaced":
|
|
78
|
+
return "root_replaced";
|
|
79
|
+
default: {
|
|
80
|
+
const unhandled = reason;
|
|
81
|
+
throw new Error(`unhandled withdrawn reason: ${JSON.stringify(unhandled)}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function completionEvent(n) {
|
|
86
|
+
return {
|
|
87
|
+
task_id: n.task_id,
|
|
88
|
+
status: n.status,
|
|
89
|
+
summary: n.summary,
|
|
90
|
+
...(n.result !== undefined ? { result: n.result } : {}),
|
|
91
|
+
...(n.seq !== undefined ? { seq: n.seq } : {}),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function completionFields(root, n) {
|
|
95
|
+
return { sessionId: root.sessionId, taskId: n.task_id, taskType: n.task_type, status: n.status };
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=session-notify-door.js.map
|
package/dist/session-sync.d.ts
CHANGED
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
import type { StoreBackend } from "./plugins/store-backend.js";
|
|
47
47
|
import { type SyncRelation } from "./session-sync-kernel.js";
|
|
48
48
|
import { type SessionTreeEntry, type SessionRulesRecord, type FileHistoryError, type FileHistoryExport, type FileHistoryResult, type SessionPermissionRules, type StoredSessionRules, type SessionPolicyStore } from "@sema-agent/core";
|
|
49
|
-
import type { ServiceFileHistoryStore, ServiceSessionPolicyStore, ResumeAnchorStore } from "./plugins/store-backend.js";
|
|
49
|
+
import type { ServiceBlobExchange, ServiceFileHistoryStore, ServiceSessionPolicyStore, ResumeAnchorStore } from "./plugins/store-backend.js";
|
|
50
50
|
/**
|
|
51
51
|
* core 7.55.0 [ref] T2([ref]):一只会话的文件历史**读不全**(撕裂 / 读不出的边界、读不出的跟踪集 —— 店的 `exportHistory` 答 `{ ok: false }`,`adopt_failed`
|
|
52
52
|
* 带店判的 `permanence`)。它**不是**「没有历史」:读成 `null` 的同步会把这只会话的全部 rewind 历史丢在路上、还答成功。会话同步的两只拉取口
|
|
@@ -67,20 +67,15 @@ export declare function exportedFileHistory(store: Pick<ServiceFileHistoryStore,
|
|
|
67
67
|
* Returned by {@link exportSessionManifest} (closes over the source store); consumed wherever a caller pulls
|
|
68
68
|
* content-addressed history bytes lazily (the live PUSH route's `importHistory` replay). */
|
|
69
69
|
export type BlobGetter = (hash: string) => Promise<Uint8Array | undefined>;
|
|
70
|
-
/** blob 两面(`putBlob` / `getBlob`)齐备的文件历史店(marker 门,不做名字探测:local 的引擎文件店带同名私有助手)。 */
|
|
71
|
-
export type SyncBlobHistoryStore = ServiceFileHistoryStore & {
|
|
72
|
-
readonly syncBlobFaces: true;
|
|
73
|
-
} & {
|
|
74
|
-
[P in "putBlob" | "getBlob"]-?: NonNullable<ServiceFileHistoryStore[P]>;
|
|
75
|
-
};
|
|
76
70
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
71
|
+
* 「这只文件历史店能不能交换 blob」的**唯一**读法:店的 `blobExchange` 组,经 core 的判官 `fileHistoryBlobExchangeOf` 读 —— 没有组 ⇒ `undefined`
|
|
72
|
+
* (core 的 local 文件店);整组 ⇒ 三只成员(判官已绑好);缺一员 / 组不是对象 / 店还带退役的标记词 ⇒ 判官拒(`config.invalid_argument`,响亮,不是「没有」)。
|
|
73
|
+
* 能力事实 `sessionSyncFileHistory`(capability-facts)与取值口 {@link blobGetterOf} 都读它 —— 事实说有,取值口就通到同一组的 `get`;事实说没有,取值口恒空。
|
|
74
|
+
* 返回型收窄到本仓的 {@link ServiceBlobExchange}:判官交回的是店自己那一组的成员(只绑定、不包装),店型声明的组就是那一型。
|
|
80
75
|
*/
|
|
81
|
-
export declare function
|
|
82
|
-
/** 一只文件历史店自己的 blob 取值口({@link BlobGetter}):从 {@link
|
|
83
|
-
* 拉取面(manifest 的 `getBlob`)、identical
|
|
76
|
+
export declare function blobExchangeOf(store: ServiceFileHistoryStore): ServiceBlobExchange | undefined;
|
|
77
|
+
/** 一只文件历史店自己的 blob 取值口({@link BlobGetter}):从 {@link blobExchangeOf} 派生 —— 有交换组 ⇒ 组的 `get`;没有(core 的 local 文件店)⇒ 恒答 `undefined`(「这里取不到」)。
|
|
78
|
+
* 拉取面(manifest 的 `getBlob`)、identical 自愈臂、提交臂三处读同一只,不各写一份「无组则 undefined」。 */
|
|
84
79
|
export declare function blobGetterOf(store: ServiceFileHistoryStore): BlobGetter;
|
|
85
80
|
/** The history-import capability face of a `FileHistoryStore` (`importHistory` is OPTIONAL on the core
|
|
86
81
|
* contract — a store without it cannot be a 2c history sink). */
|
|
@@ -123,7 +118,7 @@ export type FileHistoryReplayFailure = Extract<FileHistoryReplayResult, {
|
|
|
123
118
|
* The alternative (keeping the old graph) would wedge every retry on the same typed conflict forever.
|
|
124
119
|
*
|
|
125
120
|
* 缺 blob 的事实只在这里读一次:每次尝试给店的 getter 都包一层观察,店取到 `undefined` 就记下那枚 hash(按次计,替换臂的第二次尝试从空白起),
|
|
126
|
-
* 失败时随结果交出({@link FileHistoryReplayResult.missingBlob})。预检({@link sessionSyncFileHistory} 事实 + `
|
|
121
|
+
* 失败时随结果交出({@link FileHistoryReplayResult.missingBlob})。预检({@link sessionSyncFileHistory} 事实 + 交换组的 `has`)管「这台部署根本收不到 / 索引里没有」,
|
|
127
122
|
* 这里管「预检过了而这一枚取不到」(对象存储里已丢 / 校验和不符按缺席读 / 预检与重放之间被孤儿回收清走)—— 同一只不变量的两端,wire 上同一个词。
|
|
128
123
|
*/
|
|
129
124
|
export declare function replayFileHistory(store: ServiceFileHistoryStore, scope: string, envelope: FileHistoryExport, srcGetBlob: BlobGetter, opts: {
|
package/dist/session-sync.js
CHANGED
|
@@ -2,7 +2,7 @@ import { anchorEntryDeclared, classifySyncRelationship } from "./session-sync-ke
|
|
|
2
2
|
import { recordFailOpen } from "./observability/fail-open.js";
|
|
3
3
|
import { loosenReasons, normalizeRules } from "@sema-agent/core";
|
|
4
4
|
import { headReadOf } from "./session-incarnation.js";
|
|
5
|
-
import { judgeSessionRulesWrite } from "@sema-agent/core";
|
|
5
|
+
import { fileHistoryBlobExchangeOf, judgeSessionRulesWrite } from "@sema-agent/core";
|
|
6
6
|
export class FileHistoryUnreadableError extends Error {
|
|
7
7
|
sessionId;
|
|
8
8
|
failure;
|
|
@@ -22,12 +22,12 @@ export async function exportedFileHistory(store, sessionId) {
|
|
|
22
22
|
throw new FileHistoryUnreadableError(sessionId, exported.error);
|
|
23
23
|
return exported.history;
|
|
24
24
|
}
|
|
25
|
-
export function
|
|
26
|
-
return store
|
|
25
|
+
export function blobExchangeOf(store) {
|
|
26
|
+
return fileHistoryBlobExchangeOf(store);
|
|
27
27
|
}
|
|
28
28
|
export function blobGetterOf(store) {
|
|
29
|
-
const
|
|
30
|
-
return
|
|
29
|
+
const exchange = blobExchangeOf(store);
|
|
30
|
+
return exchange ? exchange.get : async () => undefined;
|
|
31
31
|
}
|
|
32
32
|
export function fileHistoryImportFace(fs) {
|
|
33
33
|
const candidate = fs?.importHistory;
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* 门在 `trace/core-keyset-guard.ts` ⑰ 读同一张表)。**唯一写者** = 腿的记录座 {@link createSessionWorkspaceRecorder}
|
|
12
12
|
* (持流的四条腿逐帧喂同一只;委派子代转上来的帧 —— 带 `parentToolCallId` / `sourceTaskId` / `bgAgentId` —— 不是这条腿的位置,不认)。
|
|
13
13
|
* 每一笔写**在腿的流循环里等完**(不排队、没有在飞的写:腿从任何出口离开时都没有它的写还悬着 ⇒ 写恒落在这条腿的 run 认领之内,
|
|
14
|
-
* 同一会话的下一条腿不会被一笔晚到的写盖掉),并且**只写受理时见证到的那一位住客**(
|
|
14
|
+
* 同一会话的下一条腿不会被一笔晚到的写盖掉),并且**只写受理时见证到的那一位住客**(这条腿的部署事实 `sessionWitness`,`leg-facts.ts`:resolveSpec
|
|
15
15
|
* 受理时读会话行的化身那一读,同一只值也是委派树根的见证)—— 会话被删掉又以同一 id 重建之后,上一位住客的腿写不进新住客的行。
|
|
16
16
|
* · **每条腿声明一个值**:`TaskSpec.projectId` = 这次提交自己带的 `projectId`(新提交的请求体;续跑腿重放的是持久化的同一个体)
|
|
17
17
|
* ?? 会话行上最后一帧的 `project.id` —— **只当它是调用方声明过的**(`source: "declared"`:没有记号的检出里声明就是身份,行存它就是为了接着声明);
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
* · **行读在 run 认领之前**(resolveSpec 的每一读都是):同一会话两次提交交叠、后一次的装配读行之后前一次才落定并放认领 ⇒ 后一次读到的是更早
|
|
35
35
|
* 一条腿的项目(有记号的检出里多一次 409,没有记号的检出里续声明旧项目)。修法要把声明的装配挪到认领之后,不在本模块补第二个写者。
|
|
36
36
|
*/
|
|
37
|
-
import type { TaskEvent
|
|
37
|
+
import type { TaskEvent } from "@sema-agent/core";
|
|
38
38
|
import type { DelegationRootWitness } from "./delegated-session-owner.js";
|
|
39
39
|
/**
|
|
40
40
|
* `workspace_changed` 帧在会话行上的样子(**唯一**一张表;表序 = 存储形的键序)。`forward` = 原值存进行;`strip` = 不进行。
|
|
@@ -84,16 +84,6 @@ export interface SessionWorkspaceStore {
|
|
|
84
84
|
}
|
|
85
85
|
/** 店有没有这一面 —— 结构判(包装店 `CachingSessionStore` 逐方法转发,判的是能力不是名字)。没有 ⇒ 不记、不读(core 的内存 dev 店)。 */
|
|
86
86
|
export declare function sessionWorkspaceStoreOf(store: unknown): SessionWorkspaceStore | undefined;
|
|
87
|
-
/**
|
|
88
|
-
* 这条腿的会话**在受理时**是哪一位住客(会话 id + 化身令牌)的 spec 字段键 —— 与 run 名册 / 执行车道字段同一形:symbol 键随 spread / rest 解构走
|
|
89
|
-
* (续跑腿的 `taskConfig` 是 rest 解构出来的),对引擎的键面不可见(JSON / 结构化克隆跳过 symbol 键)。铸点唯一 = resolveSpec 出口
|
|
90
|
-
* ({@link stampLegSessionWitness}),值 = 受理时读会话行的那**一**读(同一只值也交给场景层作委派树根的见证);读点唯一 = {@link legSessionWitnessOf}。
|
|
91
|
-
*/
|
|
92
|
-
export declare const LEG_SESSION_WITNESS_FIELD: unique symbol;
|
|
93
|
-
/** 铸见证(**单一属主**:只许 resolveSpec 的出口调用)。`writable:false` + `configurable:false`:同一只 spec 被第二次铸 ⇒ 当场 TypeError。缺席 ⇒ 不铸。 */
|
|
94
|
-
export declare function stampLegSessionWitness<T extends TaskSpec>(spec: T, witness: DelegationRootWitness | undefined): T;
|
|
95
|
-
/** 读见证。`undefined` = 这只 spec 不是本仓装配出口产的,或这条腿没有会话 ⇒ 记录座不写(没有住客可认)。入参收 `object`:续跑腿手里是 rest 解构出来的 taskConfig。 */
|
|
96
|
-
export declare function legSessionWitnessOf(spec: object): DelegationRootWitness | undefined;
|
|
97
87
|
/**
|
|
98
88
|
* 一帧 → 会话行上那一格的值。只认这条腿**自己的**帧(腿身份三键任一在场 = 委派子代转上来的,不是这条腿站在哪里);
|
|
99
89
|
* 按表投影后过存储形的同一只读者(写得进的就是读得出的)。读不出(引擎不会铸出这种帧)⇒ 不记、计一次。
|
|
@@ -109,7 +99,7 @@ export interface SessionWorkspaceRecorder {
|
|
|
109
99
|
}
|
|
110
100
|
/**
|
|
111
101
|
* 记录座的**唯一**铸点(四条持流腿各在开流时铸一只:后台腿 `runs.ts`、活流腿与同步腿 `http/routes/tasks.ts`、续跑腿 `http/resume-legs.ts`),
|
|
112
|
-
* `witness` =
|
|
102
|
+
* `witness` = 这条腿的受理见证(部署事实 `LegFacts.sessionWitness`,`leg-facts.ts`;嵌入方自带装配器 ⇒ 缺席)。店没有这一面、或见证缺席 / 受理时这只会话还没有住客令牌 ⇒
|
|
113
103
|
* 一只什么都不做的座(诚实缺席:认不出住客就不写 —— 写错住客比少记一次坏)。
|
|
114
104
|
*
|
|
115
105
|
* 失败方向(F 类):写失败(库抖 / 盘满 / 锁等待超时)⇒ 行停在上一帧,计一次;下一帧照写。放行的最坏后果 = 下一条不带 `projectId` 的提交读到旧的
|
|
@@ -19,15 +19,6 @@ export function sessionWorkspaceStoreOf(store) {
|
|
|
19
19
|
return undefined;
|
|
20
20
|
return { readSessionWorkspace: s.readSessionWorkspace.bind(s), recordSessionWorkspace: s.recordSessionWorkspace.bind(s) };
|
|
21
21
|
}
|
|
22
|
-
export const LEG_SESSION_WITNESS_FIELD = Symbol("sema-server.legSessionWitness");
|
|
23
|
-
export function stampLegSessionWitness(spec, witness) {
|
|
24
|
-
if (witness !== undefined)
|
|
25
|
-
Object.defineProperty(spec, LEG_SESSION_WITNESS_FIELD, { value: Object.freeze({ ...witness }), enumerable: true, writable: false, configurable: false });
|
|
26
|
-
return spec;
|
|
27
|
-
}
|
|
28
|
-
export function legSessionWitnessOf(spec) {
|
|
29
|
-
return spec[LEG_SESSION_WITNESS_FIELD];
|
|
30
|
-
}
|
|
31
22
|
export function sessionWorkspaceOfFrame(ev) {
|
|
32
23
|
if (ev.type !== "workspace_changed")
|
|
33
24
|
return undefined;
|
|
@@ -35,6 +35,11 @@ export type GrantBinding = {
|
|
|
35
35
|
/** 载体上的授予绑定 —— 只读引擎的词(读法见文件头注):摘要是串 ⇒ bound;摘要不在且词是 `not_bindable` ⇒ none;其余 ⇒ unreadable。
|
|
36
36
|
* 形参是任意对象(载体不论):读自持久层的停驻行(运维队列行的投影、`/decide` 的原始行)两座都按 `unknown` 读,结构上满足 {@link StandingAnswerCard} 的载体照传。 */
|
|
37
37
|
export declare function grantBindingOf(card: StandingAnswerCard | object): GrantBinding;
|
|
38
|
+
/**
|
|
39
|
+
* 同一只问的两份授予绑定读数取严(7.143.0;读者 = `tool-approval.ts` 幂等重入那一臂:这一腿的请求 × 行上记着的事实座)。
|
|
40
|
+
* 两份逐臂同 ⇒ 那一份;不同(含任一份 `unreadable`)⇒ `unreadable`(本会话放行记不住)。只收窄、永不放宽任一份。
|
|
41
|
+
*/
|
|
42
|
+
export declare function agreedGrantBinding(a: GrantBinding, b: GrantBinding): GrantBinding;
|
|
38
43
|
/** 授予绑定在读面上的形:恰一臂 —— 摘要,或缺席词(闭集 core `BOUND_GRANT_ABSENCES`)。 */
|
|
39
44
|
export type GrantBindingArm = {
|
|
40
45
|
readonly boundGrantDigest: string;
|
|
@@ -7,6 +7,16 @@ export function grantBindingOf(card) {
|
|
|
7
7
|
const word = "boundGrantAbsence" in card ? card.boundGrantAbsence : undefined;
|
|
8
8
|
return digest === undefined && word === NOT_BINDABLE ? { kind: "none" } : { kind: "unreadable" };
|
|
9
9
|
}
|
|
10
|
+
export function agreedGrantBinding(a, b) {
|
|
11
|
+
switch (a.kind) {
|
|
12
|
+
case "none":
|
|
13
|
+
return b.kind === "none" ? a : { kind: "unreadable" };
|
|
14
|
+
case "bound":
|
|
15
|
+
return b.kind === "bound" && b.digest === a.digest ? a : { kind: "unreadable" };
|
|
16
|
+
case "unreadable":
|
|
17
|
+
return a;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
10
20
|
export function grantBindingArm(binding) {
|
|
11
21
|
switch (binding.kind) {
|
|
12
22
|
case "bound":
|
|
@@ -86,8 +86,10 @@ export declare class SqlSessionPolicyStore implements SessionPolicyStore {
|
|
|
86
86
|
* `main`'s statement sequence, zero mark table involved): InnoDB 1 winner / 7 `ER_LOCK_DEADLOCK`, TiDB 0,
|
|
87
87
|
* PG 0 — i.e. this is a PRE-EXISTING MySQL-leg defect that [ref]'s lock-shape site made visible, not a new one.
|
|
88
88
|
* After the first insert commits, the retried writers find the row and take the UPDATE path (record lock,
|
|
89
|
-
* no gap) ⇒ the loop converges in one extra round per missing-row window; the bound is the suite's concurrency
|
|
90
|
-
*
|
|
89
|
+
* no gap) ⇒ the loop converges in one extra round per missing-row window; the bound is the suite's concurrency
|
|
90
|
+
* (a writer is picked at most once per missing-row window, and there are 1 + this round's erases of them) —
|
|
91
|
+
* the repo's one bound, `DEADLOCK_RETRY_LIMIT`, owned with the loop itself by `retryOnDeadlock` in `sql-errors.ts`.
|
|
92
|
+
* Lock-WAIT-TIMEOUT is deliberately NOT retried (`isDeadlockError`'s own note: it is a latency signal,
|
|
91
93
|
* and retrying it multiplies an already-long wait).
|
|
92
94
|
*/
|
|
93
95
|
private retrying;
|
|
@@ -4,10 +4,9 @@ import { SESSION_RULES_COLUMN } from "../plugins/session-rules-column.js";
|
|
|
4
4
|
import { decodeJsonColumn, isJsonColumnBodyProblem, jsonColumnText } from "../plugins/sql-json-column.js";
|
|
5
5
|
import { pgHasUnstorable } from "../plugins/pg-safe-json.js";
|
|
6
6
|
import { mysqlDriver, pgDriver } from "../plugins/sql-driver.js";
|
|
7
|
-
import {
|
|
7
|
+
import { isDupKeyError, retryOnDeadlock } from "../plugins/sql-errors.js";
|
|
8
8
|
export const policyKeyFor = (sessionId, principal) => createHash("sha256").update(JSON.stringify([sessionId, principal ?? null])).digest("hex");
|
|
9
9
|
const policyKey = policyKeyFor;
|
|
10
|
-
const RETRY_LIMIT = 8;
|
|
11
10
|
const RULES = SESSION_RULES_COLUMN.name;
|
|
12
11
|
export function readSessionRulesBody(body) {
|
|
13
12
|
const read = readSessionRulesShape(body);
|
|
@@ -129,17 +128,8 @@ export class SqlSessionPolicyStore {
|
|
|
129
128
|
return structuredClone({ ...clean, rev: nextRev, gen: nextGen });
|
|
130
129
|
}));
|
|
131
130
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
try {
|
|
135
|
-
return await attempt();
|
|
136
|
-
}
|
|
137
|
-
catch (e) {
|
|
138
|
-
if (tries < RETRY_LIMIT && (this.isDupKey(e) || isDeadlockError(this.db.dialect, e)))
|
|
139
|
-
continue;
|
|
140
|
-
throw e;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
131
|
+
retrying(attempt) {
|
|
132
|
+
return retryOnDeadlock(this.db.dialect, attempt, (e) => this.isDupKey(e));
|
|
143
133
|
}
|
|
144
134
|
async deleteBySession(sessionId) {
|
|
145
135
|
const sealed = await this.retrying(() => this.tx(async (conn) => {
|
package/dist/task-mcp.d.ts
CHANGED
|
@@ -71,7 +71,7 @@ export declare const MCP_SERVER_ENTRY_KEYS: readonly ["allowTools", "contentOrig
|
|
|
71
71
|
/** `transport.kind:"stdio"` 的键闭集 = stdio 臂处置表的键(`env` 是件,其余进声明)。 */
|
|
72
72
|
export declare const STDIO_MCP_TRANSPORT_KEYS: readonly ("env" | "kind" | "args" | "command")[];
|
|
73
73
|
/** `transport.kind:"http"` 的键闭集 = http 臂处置表的键(`headers` 是件,`assertionHeader` 承载不发送,其余进声明)。 */
|
|
74
|
-
export declare const HTTP_MCP_TRANSPORT_KEYS: readonly ("headers" | "
|
|
74
|
+
export declare const HTTP_MCP_TRANSPORT_KEYS: readonly ("headers" | "url" | "kind" | "principalHeader" | "assertionHeader")[];
|
|
75
75
|
/**
|
|
76
76
|
* Does THIS deployment honor caller-supplied per-request MCP servers? TRUE for a SINGLE-USER deployment
|
|
77
77
|
* (deployment-shape row `multiTenant` does not hold) — the requester is the super-admin of their own worker, so their `.mcp.json` is
|
package/dist/task-settings.d.ts
CHANGED
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
* - `deny` / `ask` are folded via `tightenTaskSpec` (deny-wins `combinePolicies`); it THROWS on any override that would
|
|
16
16
|
* LOOSEN a stricter base safety field. Through those two buckets the client can only ADD denies / asks on top of the
|
|
17
17
|
* deployment + operator baseline.
|
|
18
|
-
* - `allow` (server 7.117.0, core 7.42.0) is NOT a policy: the list rides WHOLE
|
|
18
|
+
* - `allow` (server 7.117.0, core 7.42.0) is NOT a policy: the list rides WHOLE, one member per entry in the entry's position, into `TaskSpec.allowedTools`
|
|
19
|
+
* (a string verbatim; a settings layer's `{ rule, layer }` entry as its rule text, or — when its `/` reads at the configuration root — as
|
|
20
|
+
* `{ rule, pathBases: { root } }`, the same anchor its deny / ask twin's set declares: {@link allowMemberOf}),
|
|
19
21
|
* the request member of the engine's ONE allow layer (beside a deployment's `RunnerDeps.allowedTools`, a person's stored
|
|
20
22
|
* rows, an active skill's `allowed-tools` and a PreToolUse hook's `allow`), read under one predicate. It IS an accepted
|
|
21
23
|
* loosening for this run: an entry clears the `unstated` / `ordinary` questions the engine raises for the calls it covers
|
|
@@ -198,7 +200,8 @@ export declare function skillListingMaxDescOk(v: unknown): v is number;
|
|
|
198
200
|
* deferred (submit 路径另有 400 fail-loud),valid `hooks` 进 applied shape(hook-runner 阶段一)。 */
|
|
199
201
|
export interface ParsedTaskSettings {
|
|
200
202
|
permissions?: {
|
|
201
|
-
|
|
203
|
+
/** 与 `deny` 同一条目形(带层条目的层只选 `/` 的锚);投 `TaskSpec.allowedTools` 的唯一写点 {@link applyTaskSettings}。 */
|
|
204
|
+
allow?: RuleListEntry[];
|
|
202
205
|
/** 纯串 = 请求自己写的规则;`{ rule, layer }` = 端从某个设置层读来的规则(`permission-rule-list.ts` 的 {@link RuleListEntry};层只选 `/` 的锚)。 */
|
|
203
206
|
deny?: RuleListEntry[];
|
|
204
207
|
/** 同 `deny` 的条目形;编译与 `deny` 同一口({@link deriveSettingsPolicy})。 */
|
|
@@ -297,8 +300,8 @@ export interface SettingsPermissionBucketIssue {
|
|
|
297
300
|
/**
|
|
298
301
|
* 三桶的**唯一**判据(纯函数)。`perms` 不是对象 ⇒ 不判(那一形归「settings.permissions must be an object」那道门);桶缺席 / `null`
|
|
299
302
|
* ⇒ 不判(与 `permissions: null` 的「容器缺席」同一约定)。逐桶按 {@link SETTINGS_PERMISSION_BUCKETS} 的序、按该桶的条目规则:不是数组 ⇒ `bad_entry`
|
|
300
|
-
* (无 `index`);`rule_policy` 桶超帽 ⇒ `over_cap`(先判,拒体有界)、首个不合条目律的项(不是非空串、也不是合形的带层条目)⇒ `bad_entry` + 它的下标;`allow_layer`
|
|
301
|
-
* `bad_entry` +
|
|
303
|
+
* (无 `index`);`rule_policy` 桶超帽 ⇒ `over_cap`(先判,拒体有界)、首个不合条目律的项(不是非空串、也不是合形的带层条目)⇒ `bad_entry` + 它的下标;`allow_layer` 桶首个既不是串、
|
|
304
|
+
* 也不是合形带层条目的项 ⇒ `bad_entry` + 它的下标,超帽同样 `over_cap`(帽的理由不同,见段头)。全合法 ⇒ `null`。
|
|
302
305
|
*/
|
|
303
306
|
export declare function settingsPermissionBucketIssue(perms: unknown): SettingsPermissionBucketIssue | null;
|
|
304
307
|
/** 本 service 真正**消费**的 `settings` 顶层键 —— 闭集。每一个都有消费点:
|
|
@@ -459,7 +462,7 @@ export interface FsWriteGateWiring {
|
|
|
459
462
|
taskHome?: string;
|
|
460
463
|
/**
|
|
461
464
|
* **执行环境的配置根**(唯一属主 `execution-config-root.ts` 的 `executionConfigRootOf`):user 设置层里 `/` 形路径规则的根 —— 带 `layer: "userSettings"`
|
|
462
|
-
* 且要 `root` 的条目编进只声明 `{ root: configRoot }`
|
|
465
|
+
* 且要 `root` 的条目编进只声明 `{ root: configRoot }` 的那一集、allow 桶里的投成带同一声明的那一员(`permission-rule-list.ts` 的 `ruleRootOf`)。缺席(沙箱车道既没转 `SEMA_CONFIG_DIR`、home 也读不出)
|
|
463
466
|
* ⇒ 这样的条目由 {@link rejectUnmatchableSettingsNames} 响亮拒(422 `home_unresolvable`),不猜目录。拒点与存体预判读同一只函数。
|
|
464
467
|
*/
|
|
465
468
|
configRoot?: string;
|
|
@@ -486,7 +489,7 @@ export type RulePolicyBucket = (typeof RULE_POLICY_BUCKETS)[number];
|
|
|
486
489
|
/**
|
|
487
490
|
* deny / ask 两桶的**完整规则判**,唯一一只(拒点 {@link rejectUnmatchableSettingsNames} 与存体预判 `http/stored-body-replay.ts` 同调):
|
|
488
491
|
* ② 按 {@link RULE_POLICY_BUCKETS} 的序逐桶、桶内按 `RULE_ROOTS` 的序逐集(`permission-rule-list.ts` 的 `ruleSetsOf`:判的就是要编的那一集)问 `judgeUnmatchableToolNames`(名字层 + core 规则文法);
|
|
489
|
-
* ③ 再判「这条规则要的基本部署给不出」那一拒:`~/` 形缺 home;配置根那一集缺配置根(
|
|
492
|
+
* ③ 再判「这条规则要的基本部署给不出」那一拒:`~/` 形缺 home(deny / ask);配置根那一集缺配置根(三桶,{@link LAYER_ROOT_BUCKETS};两者都落在执行环境的 home 上,同词 `home_unresolvable`)。答 `{ bucket, message, problems }`:`bucket` 与 `message` 是**第一处**坏处的
|
|
490
493
|
* (句子冻结,先撞上的那一条),`problems` 是**两段、两只桶判下来的全部**坏条目(机读位的原料,形的属主 `entry-problems.ts`;`entry` = 规则原文,带层条目也一样)。
|
|
491
494
|
* 入参:桶、{@link RulePolicyBases}。**home 同源**:拒点的 home 是 `resolve-spec.ts` 的 `laneHomeBase()` 经 `executionLaneHomeDir(config)` 算出的,预判传同一个函数对同一份部署配置算出的;配置根同理(`executionConfigRootOf`)。
|
|
492
495
|
* **任务根那一集声明给文法的基**只有拒点有(根 + home 的并集,执行车道才知道根):core 的规则文法对**没声明**的基不下判词(见 `config.ts` `uncompilableRuleGuidance`),所以预判什么也不声明、两腿逐字同句;
|
|
@@ -502,8 +505,8 @@ export interface RulePolicyBases {
|
|
|
502
505
|
/** 执行环境的配置根(配置根那一集的根;拒点与预判都给);读不出 ⇒ 缺席。 */
|
|
503
506
|
readonly configRoot?: string;
|
|
504
507
|
}
|
|
505
|
-
export declare function rulePolicyBucketsRefusal(permissions: Readonly<Partial<Record<
|
|
506
|
-
readonly bucket:
|
|
508
|
+
export declare function rulePolicyBucketsRefusal(permissions: Readonly<Partial<Record<SettingsPermissionBucket, readonly RuleListEntry[]>>>, bases: RulePolicyBases): {
|
|
509
|
+
readonly bucket: SettingsPermissionBucket;
|
|
507
510
|
readonly message: string;
|
|
508
511
|
readonly problems: readonly EntryProblem[];
|
|
509
512
|
} | undefined;
|
package/dist/task-settings.js
CHANGED
|
@@ -2,8 +2,8 @@ import { combinePolicies, parseRuleText, resolveReasoningProfile, rankOf, isThin
|
|
|
2
2
|
import { judgeUnmatchableToolNames } from "./config.js";
|
|
3
3
|
import { NAME_TABLE_BEHAVIOR } from "./namespaced-rule-name.js";
|
|
4
4
|
import { entryProblem } from "./entry-problems.js";
|
|
5
|
-
import { MAX_PERMISSION_RULES_PER_LIST, RULE_ROOTS, createRuleListPolicy, ruleListEntryOk, ruleSetsOf, ruleSourceOf, ruleTextOf } from "./permission-rule-list.js";
|
|
6
|
-
import { allowLayerEntryCountIssue, describeAllowLayerOverCap
|
|
5
|
+
import { MAX_PERMISSION_RULES_PER_LIST, RULE_ROOTS, allowListEntryOk, createRuleListPolicy, ruleListEntryOk, ruleRootOf, ruleSetsOf, ruleSourceOf, ruleTextOf } from "./permission-rule-list.js";
|
|
6
|
+
import { allowLayerEntryCountIssue, describeAllowLayerOverCap } from "./rule-entry-list.js";
|
|
7
7
|
import { limitRefusal } from "./limit-refusal.js";
|
|
8
8
|
import { tightenGoverned } from "./governance-overrides.js";
|
|
9
9
|
import { MAX_KEY_ECHO, keyClosureClause, scanKeyClosure } from "./request-key-closure.js";
|
|
@@ -113,8 +113,8 @@ function bucketIssueMessage(f, rule) {
|
|
|
113
113
|
: `${f.field}[${f.index}] is neither a non-empty rule string nor a { "rule", "layer" } entry (exactly those two keys; rule = a non-empty rule string; layer = one of ${SETTINGS_SOURCES.join(", ")}) — every entry must be one. Refused rather than dropped, because a dropped entry reads as "in effect" to the caller`;
|
|
114
114
|
case "allow_layer":
|
|
115
115
|
return f.index === undefined
|
|
116
|
-
? `${f.field} must be an array of
|
|
117
|
-
: `${f.field}[${f.index}] is
|
|
116
|
+
? `${f.field} must be an array of rule entries (a rule string, or { "rule": <rule>, "layer": <settings source> }) — refused rather than ignored, because an ignored list reads as "in effect" to the caller`
|
|
117
|
+
: `${f.field}[${f.index}] is neither a rule string nor a { "rule", "layer" } entry (exactly those two keys; rule = a non-empty rule string; layer = one of ${SETTINGS_SOURCES.join(", ")}) — every entry must be one. Refused rather than dropped, because a dropped entry reads as "in effect" to the caller`;
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
}
|
|
@@ -146,8 +146,11 @@ export function settingsPermissionBucketIssue(perms) {
|
|
|
146
146
|
const over = allowLayerEntryCountIssue(field, v);
|
|
147
147
|
if (over !== undefined)
|
|
148
148
|
fields = { field, reason: "over_cap", count: over.facts.count, cap: over.facts.cap };
|
|
149
|
-
else
|
|
150
|
-
|
|
149
|
+
else {
|
|
150
|
+
const index = v.findIndex((x) => !allowListEntryOk(x));
|
|
151
|
+
if (index >= 0)
|
|
152
|
+
fields = { field, reason: "bad_entry", index };
|
|
153
|
+
}
|
|
151
154
|
break;
|
|
152
155
|
}
|
|
153
156
|
}
|
|
@@ -372,6 +375,7 @@ export class SettingsRuleRefusalError extends Error {
|
|
|
372
375
|
}
|
|
373
376
|
export const SETTINGS_RULE_REFUSAL_CODE = "request.unprocessable";
|
|
374
377
|
export const RULE_POLICY_BUCKETS = ["ask", "deny"];
|
|
378
|
+
const LAYER_ROOT_BUCKETS = [...RULE_POLICY_BUCKETS, "allow"];
|
|
375
379
|
export function rulePolicyBucketsRefusal(permissions, bases) {
|
|
376
380
|
let first;
|
|
377
381
|
const problems = [];
|
|
@@ -423,7 +427,7 @@ export function rulePolicyBucketsRefusal(permissions, bases) {
|
|
|
423
427
|
}
|
|
424
428
|
}
|
|
425
429
|
if (bases.configRoot === undefined) {
|
|
426
|
-
for (const bucket of
|
|
430
|
+
for (const bucket of LAYER_ROOT_BUCKETS) {
|
|
427
431
|
const field = `permissions.${bucket}`;
|
|
428
432
|
for (const name of ruleSetsOf(permissions[bucket] ?? [], bucket).configuration.map(ruleTextOf)) {
|
|
429
433
|
first ??= {
|
|
@@ -443,9 +447,25 @@ function ruleSetBases(root, taskBases, configRoot) {
|
|
|
443
447
|
case "task":
|
|
444
448
|
return taskBases;
|
|
445
449
|
case "configuration":
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
450
|
+
return configurationSetBases(configRoot);
|
|
451
|
+
default: {
|
|
452
|
+
const unhandled = root;
|
|
453
|
+
return unhandled;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
function configurationSetBases(configRoot) {
|
|
458
|
+
if (configRoot === undefined)
|
|
459
|
+
throw new Error("a user-settings path rule reached the compiler without a configuration root — the admission judgement refuses it first (home_unresolvable); refusing rather than anchoring it at the task root");
|
|
460
|
+
return { root: configRoot };
|
|
461
|
+
}
|
|
462
|
+
function allowMemberOf(entry, configRoot) {
|
|
463
|
+
const root = ruleRootOf(entry, "allow");
|
|
464
|
+
switch (root) {
|
|
465
|
+
case "task":
|
|
466
|
+
return ruleTextOf(entry);
|
|
467
|
+
case "configuration":
|
|
468
|
+
return { rule: ruleTextOf(entry), pathBases: configurationSetBases(configRoot) };
|
|
449
469
|
default: {
|
|
450
470
|
const unhandled = root;
|
|
451
471
|
return unhandled;
|
|
@@ -503,7 +523,7 @@ export function applyTaskSettings(base, settings, bases) {
|
|
|
503
523
|
next = tightenGoverned(next, { ...(toolPolicy !== undefined ? { toolPolicy } : {}), ...seatSpec });
|
|
504
524
|
const allow = settings.permissions?.allow;
|
|
505
525
|
if (allow !== undefined)
|
|
506
|
-
next = { ...next, allowedTools: allow };
|
|
526
|
+
next = { ...next, allowedTools: allow.map((entry) => allowMemberOf(entry, bases?.configRoot)) };
|
|
507
527
|
if (settings.outputStyle) {
|
|
508
528
|
const appended = next.appendSystemPrompt ? `${next.appendSystemPrompt}\n\n${settings.outputStyle}` : settings.outputStyle;
|
|
509
529
|
next = { ...next, appendSystemPrompt: appended };
|