@sema-agent/server 7.133.0 → 7.134.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 +87 -0
- package/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/USAGE.md +129 -11
- package/deploy/sema-up/README.md +16 -0
- package/deploy/sema-up/chart/Chart.yaml +1 -1
- package/deploy/sema-up/chart/templates/NOTES.txt +15 -0
- package/deploy/sema-up/chart/templates/_helpers.tpl +129 -0
- package/deploy/sema-up/chart/templates/sealed-key.yaml +24 -0
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +14 -0
- package/deploy/sema-up/chart/templates/server.yaml +14 -0
- package/deploy/sema-up/chart/values-full-single.yaml +5 -1
- package/deploy/sema-up/chart/values.yaml +34 -0
- package/dist/approval-ask-audit-store.d.ts +1 -1
- package/dist/approval-card.d.ts +4 -4
- package/dist/approval-card.js +1 -2
- package/dist/approval-content-kind.d.ts +5 -2
- package/dist/approval-content-kind.js +16 -3
- package/dist/approval-gate-currency.d.ts +72 -0
- package/dist/approval-gate-currency.js +94 -0
- package/dist/approval.d.ts +6 -0
- package/dist/approval.js +31 -14
- package/dist/boot/coordinators.d.ts +6 -1
- package/dist/boot/coordinators.js +6 -3
- package/dist/boot/leader.d.ts +2 -0
- package/dist/boot/leader.js +2 -1
- package/dist/boot/read-deny-anchor-audit.js +2 -2
- package/dist/boot/runner-deps.d.ts +18 -5
- package/dist/boot/runner-deps.js +9 -0
- package/dist/boot/stage-06-runners.d.ts +4 -3
- package/dist/boot/stage-06-runners.js +15 -2
- package/dist/boot/stage-07-capability-layer.d.ts +2 -1
- package/dist/boot/stage-08-reapers.d.ts +2 -1
- package/dist/boot/stage-09-leader.d.ts +2 -1
- package/dist/boot/stage-09-leader.js +2 -2
- package/dist/boot/stage-10-http-server.d.ts +2 -1
- package/dist/boot/stage-10-http-server.js +2 -1
- package/dist/boot/stores.d.ts +8 -0
- package/dist/boot/stores.js +8 -12
- package/dist/boot/web-admin.d.ts +6 -0
- package/dist/boot/web-admin.js +11 -2
- package/dist/budget.js +5 -1
- package/dist/caller-text-echo.d.ts +2 -0
- package/dist/caller-text-echo.js +2 -1
- package/dist/canonical-json.d.ts +2 -41
- package/dist/canonical-json.js +2 -13
- package/dist/capabilities/composite-members.d.ts +1 -1
- package/dist/capabilities/composite-members.js +1 -1
- package/dist/capabilities/mcp-probe.js +2 -2
- package/dist/capability-facts.d.ts +2 -0
- package/dist/capability-facts.js +4 -4
- package/dist/checkpoint-ctx-seal-m28.d.ts +42 -0
- package/dist/checkpoint-ctx-seal-m28.js +19 -0
- package/dist/field-conflict.d.ts +2 -1
- package/dist/field-conflict.js +1 -0
- package/dist/file-history-disclosure.d.ts +34 -5
- package/dist/file-history-disclosure.js +2 -2
- package/dist/fleet/fleet-bus.d.ts +4 -0
- package/dist/fleet/fleet-bus.js +1 -0
- package/dist/hooks/hook-runner.js +2 -2
- package/dist/http/admission.js +8 -0
- package/dist/http/approval-sweeps.js +3 -1
- package/dist/http/checkpoint-ctx.d.ts +157 -2
- package/dist/http/checkpoint-ctx.js +158 -2
- package/dist/http/dispatch.js +3 -3
- package/dist/http/durable-run-leg.js +3 -2
- package/dist/http/idempotency.d.ts +2 -2
- package/dist/http/idempotency.js +2 -2
- package/dist/http/principal-gate.d.ts +32 -6
- package/dist/http/principal-gate.js +27 -4
- package/dist/http/refusal-body.d.ts +12 -0
- package/dist/http/refusal-body.js +11 -0
- package/dist/http/request-guards.d.ts +0 -9
- package/dist/http/request-guards.js +2 -16
- package/dist/http/resume-ctx-read.d.ts +48 -1
- package/dist/http/resume-ctx-read.js +26 -3
- package/dist/http/resume-legs.d.ts +2 -1
- package/dist/http/resume-legs.js +37 -22
- package/dist/http/route-ctx.d.ts +0 -9
- package/dist/http/routes/agents-roster.js +2 -3
- package/dist/http/routes/approvals-assistant.js +43 -9
- package/dist/http/routes/capabilities.js +1 -1
- package/dist/http/routes/diagnostics.d.ts +3 -3
- package/dist/http/routes/diagnostics.js +1 -1
- package/dist/http/routes/fleet.js +4 -4
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/rules.js +4 -4
- package/dist/http/routes/runs.js +30 -9
- package/dist/http/routes/session-sync.d.ts +3 -2
- package/dist/http/routes/session-sync.js +38 -20
- package/dist/http/routes/sessions-list.d.ts +1 -1
- package/dist/http/routes/sessions-list.js +3 -4
- package/dist/http/routes/sessions.js +4 -4
- package/dist/http/routes/tasks.js +21 -16
- package/dist/http/routes/trace-usage.js +7 -9
- package/dist/http/run-declared-families.d.ts +16 -0
- package/dist/http/run-declared-families.js +12 -0
- package/dist/http/server.d.ts +6 -0
- package/dist/http/server.js +5 -2
- package/dist/http/submit-ctx.d.ts +4 -3
- package/dist/http/submit-ctx.js +19 -3
- package/dist/http/wire-types.d.ts +7 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/leader/wire.d.ts +4 -0
- package/dist/leader/wire.js +8 -7
- package/dist/main.js +1 -1
- package/dist/memory-origin-fence.js +2 -2
- package/dist/model-compat.d.ts +1 -1
- package/dist/observability/prompt-manifest.d.ts +19 -8
- package/dist/observability/prompt-manifest.js +9 -8
- package/dist/observability/tool-trace.js +8 -9
- package/dist/orchestration/workflow-park-decision.d.ts +1 -1
- package/dist/orchestration/workflow-park-decision.js +1 -0
- package/dist/parked-decide.d.ts +4 -5
- package/dist/parked-decide.js +3 -1
- package/dist/permission-rule-vocab.d.ts +1 -23
- package/dist/permission-rule-vocab.js +0 -13
- package/dist/plugins/approval-payload-fingerprint.d.ts +14 -6
- package/dist/plugins/approval-payload-fingerprint.js +1 -1
- package/dist/plugins/caching-session-store.d.ts +1 -1
- package/dist/plugins/caching-session-store.js +4 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +22 -1
- package/dist/plugins/checkpoint-store-sql.js +45 -1
- package/dist/plugins/local-checkpoint-store.d.ts +13 -2
- package/dist/plugins/local-checkpoint-store.js +26 -0
- package/dist/plugins/local-session-store.d.ts +12 -9
- package/dist/plugins/local-session-store.js +23 -14
- package/dist/plugins/local-session-window.d.ts +12 -6
- package/dist/plugins/local-session-window.js +1 -17
- package/dist/plugins/one-time-migrations.d.ts +11 -0
- package/dist/plugins/one-time-migrations.js +12 -0
- package/dist/plugins/permission-rule-store-file.js +10 -6
- package/dist/plugins/permission-rule-store-sql.js +8 -8
- package/dist/plugins/pg-session-storage.d.ts +1 -1
- package/dist/plugins/pg-session-storage.js +39 -17
- package/dist/plugins/remote-env-device.d.ts +14 -7
- package/dist/plugins/remote-env-device.js +23 -15
- package/dist/plugins/session-ownership.d.ts +11 -0
- package/dist/plugins/session-ownership.js +11 -0
- package/dist/plugins/session-placement.d.ts +12 -14
- package/dist/plugins/session-placement.js +2 -15
- package/dist/plugins/tidb-session-store.d.ts +1 -1
- package/dist/plugins/tidb-session-store.js +39 -17
- package/dist/request-tool-axes.d.ts +12 -0
- package/dist/request-tool-axes.js +20 -0
- package/dist/run-local.js +1 -0
- package/dist/runs.d.ts +2 -1
- package/dist/runs.js +4 -3
- package/dist/sealed-key.d.ts +32 -0
- package/dist/sealed-key.js +55 -1
- package/dist/security.d.ts +9 -7
- package/dist/session-error-wire.d.ts +12 -5
- package/dist/session-error-wire.js +4 -1
- package/dist/session-sync-kernel.d.ts +37 -10
- package/dist/session-sync-kernel.js +10 -0
- package/dist/task-a2a.js +4 -3
- package/dist/task-mcp.d.ts +2 -2
- package/dist/task-mcp.js +20 -8
- package/dist/task-settings.d.ts +5 -1
- package/dist/task-settings.js +1 -1
- package/dist/tool-approval.d.ts +151 -52
- package/dist/tool-approval.js +113 -20
- package/dist/tool-rule-names.d.ts +6 -0
- package/dist/trace/core-keyset-guard.d.ts +4 -3
- package/dist/trace/exit-seat.d.ts +13 -1
- package/dist/trace/exit-seat.js +55 -1
- package/dist/trace/ledger-events.d.ts +3 -1
- package/dist/trace/project.d.ts +18 -1
- package/dist/trace/project.js +16 -4
- package/dist/trace/redact.d.ts +6 -3
- package/dist/trace/redact.js +191 -23
- package/dist/trace/self-minted-wire.d.ts +4 -0
- package/dist/trace/self-minted-wire.js +1 -0
- package/dist/trace/wire-projection-faces.d.ts +6 -0
- package/dist/trace/wire-projection-faces.js +6 -0
- package/dist/web-admin-client/skills-mcp.d.ts +1 -1
- package/dist/web-admin-client/skills-mcp.js +2 -3
- package/dist/web-admin-client/types.d.ts +1 -1
- package/package.json +2 -2
- package/scripts/drain-check-local.mjs +34 -2
- package/scripts/drain-check.mysql.sql +20 -2
- package/scripts/drain-check.pg.sql +20 -2
- package/dist/mcp-tool-faces.d.ts +0 -69
- package/dist/mcp-tool-faces.js +0 -104
package/dist/tool-approval.js
CHANGED
|
@@ -4,6 +4,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
4
4
|
import { uuidv7, classifyAsk, foldArbitrationRow, policyCannotAnswer, screenAskClassMembers, MAX_RULE_TEXT_CHARS, READ_ROOT_CANDIDATE_DIR_MAX, parseRuleText, pathConfinableWriteToolNames } from "@sema-agent/core";
|
|
5
5
|
import { precheckCardRuleText } from "./rules-consent.js";
|
|
6
6
|
import { redactDeep, redactSecrets, redactSecretsWithArms } from "./trace/redact.js";
|
|
7
|
+
import { seatText } from "./trace/exit-seat.js";
|
|
7
8
|
import { hasBidiControls } from "./text-bidi.js";
|
|
8
9
|
import { approvalPayloadFingerprint } from "./plugins/approval-payload-fingerprint.js";
|
|
9
10
|
import { createLogger } from "./observability/logger.js";
|
|
@@ -327,9 +328,14 @@ function parseToolApprovalFields(body, retired) {
|
|
|
327
328
|
export function buildUnattendedPolicyRefusal(reason) {
|
|
328
329
|
return { allow: false, settledBy: POLICY_REFUSAL_DECISION.settledBy, reason };
|
|
329
330
|
}
|
|
330
|
-
function unattendedRefusalReason(
|
|
331
|
+
function unattendedRefusalReason(site) {
|
|
332
|
+
if (site.arm === "frame_family_undeclared") {
|
|
333
|
+
return (`UNATTENDED_APPROVAL_POLICY=deny: this run has no live approval channel — the client that started it did not declare the "${site.family}" ` +
|
|
334
|
+
"approval frame family (settings.approvalFrameFamilies), so no approval card is shown to it, and an approval request no one can answer " +
|
|
335
|
+
`is refused here instead of being parked for a person. Upgrade the client, or declare the family it renders (arm: ${site.arm}).`);
|
|
336
|
+
}
|
|
331
337
|
return ("UNATTENDED_APPROVAL_POLICY=deny: this deployment declares that no one attends its approvals, so an approval " +
|
|
332
|
-
`request no one can answer is refused here instead of being parked for a person (arm: ${arm}).`);
|
|
338
|
+
`request no one can answer is refused here instead of being parked for a person (arm: ${site.arm}).`);
|
|
333
339
|
}
|
|
334
340
|
function isChildAsk(req) {
|
|
335
341
|
return req.fromSubagent === true;
|
|
@@ -361,11 +367,53 @@ export function resolveStreamApprovalGate(input) {
|
|
|
361
367
|
return { active: false, reason: "protocol_disabled" };
|
|
362
368
|
if (!input.backend)
|
|
363
369
|
return { active: false, reason: "no_backend" };
|
|
364
|
-
if (
|
|
370
|
+
if (input.checkpointStore === undefined)
|
|
365
371
|
return { active: false, reason: "no_park_facility" };
|
|
366
|
-
return { active: true, askStore: input.backend.approvalAsk() };
|
|
372
|
+
return { active: true, askStore: input.backend.approvalAsk(), checkpointStore: input.checkpointStore };
|
|
367
373
|
}
|
|
368
374
|
export const APPROVAL_FRAME_FAMILIES = ["tool_approval", "approval_request"];
|
|
375
|
+
export function presentedApprovalFrameFamily(streamApprovalOn) {
|
|
376
|
+
return streamApprovalOn ? "approval_request" : "tool_approval";
|
|
377
|
+
}
|
|
378
|
+
const APPROVAL_FRAME_FAMILIES_FIELD = "settings.approvalFrameFamilies";
|
|
379
|
+
const APPROVAL_FRAME_FAMILY_WORDS = APPROVAL_FRAME_FAMILIES.map((w) => `"${w}"`).join(" | ");
|
|
380
|
+
export function approvalFrameFamiliesIssue(settings) {
|
|
381
|
+
if (settings === null || typeof settings !== "object" || Array.isArray(settings))
|
|
382
|
+
return null;
|
|
383
|
+
const raw = Reflect.get(settings, "approvalFrameFamilies");
|
|
384
|
+
if (raw === undefined)
|
|
385
|
+
return null;
|
|
386
|
+
if (!Array.isArray(raw)) {
|
|
387
|
+
return {
|
|
388
|
+
message: `${APPROVAL_FRAME_FAMILIES_FIELD} must be an array of approval frame family words (${APPROVAL_FRAME_FAMILY_WORDS}) — the families of approval cards this client renders. ` +
|
|
389
|
+
'Refused rather than ignored, because an ignored declaration reads as "in effect" to the caller',
|
|
390
|
+
fields: { field: APPROVAL_FRAME_FAMILIES_FIELD, reason: "bad_entry" },
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
const seen = new Set();
|
|
394
|
+
for (const [index, entry] of raw.entries()) {
|
|
395
|
+
if (!APPROVAL_FRAME_FAMILIES.includes(entry)) {
|
|
396
|
+
return {
|
|
397
|
+
message: `${APPROVAL_FRAME_FAMILIES_FIELD}[${index}] is not one of ${APPROVAL_FRAME_FAMILY_WORDS} — every entry must be an approval frame family word. Refused rather than dropped, because a dropped entry reads as "in effect" to the caller`,
|
|
398
|
+
fields: { field: APPROVAL_FRAME_FAMILIES_FIELD, reason: "bad_entry", index },
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
if (seen.has(entry)) {
|
|
402
|
+
return {
|
|
403
|
+
message: `${APPROVAL_FRAME_FAMILIES_FIELD}[${index}] repeats a family declared earlier in the list — declare each family once`,
|
|
404
|
+
fields: { field: APPROVAL_FRAME_FAMILIES_FIELD, reason: "bad_entry", index },
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
seen.add(entry);
|
|
408
|
+
}
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
export function declaredApprovalFrameFamilies(settings) {
|
|
412
|
+
if (approvalFrameFamiliesIssue(settings) !== null || settings === null || typeof settings !== "object")
|
|
413
|
+
return new Set();
|
|
414
|
+
const raw = Reflect.get(settings, "approvalFrameFamilies");
|
|
415
|
+
return new Set(Array.isArray(raw) ? raw.filter((w) => APPROVAL_FRAME_FAMILIES.includes(w)) : []);
|
|
416
|
+
}
|
|
369
417
|
export function createApprovalCardEmitter(sinks) {
|
|
370
418
|
const present = [sinks.appendDurable, sinks.writeLive].filter((w) => w !== undefined);
|
|
371
419
|
const attempt = (write, frame) => (async () => {
|
|
@@ -410,17 +458,22 @@ export function createApprovalCardEmitter(sinks) {
|
|
|
410
458
|
};
|
|
411
459
|
}
|
|
412
460
|
export function resolveApprovalLeg(input) {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
const tightDeadline = input.legWalltimeMs !== undefined && input.legWalltimeMs - input.windowMarginMs <= 0;
|
|
461
|
+
const active = input.streamApprovalOn;
|
|
462
|
+
const tightDeadline = active && input.legWalltimeMs !== undefined && input.legWalltimeMs - input.windowMarginMs <= 0;
|
|
416
463
|
if (input.windowMs === 0 || tightDeadline)
|
|
417
|
-
return { active:
|
|
464
|
+
return { active, unattended: { arm: "window_zero" } };
|
|
465
|
+
const family = presentedApprovalFrameFamily(active);
|
|
466
|
+
if (!input.declaredFamilies.has(family))
|
|
467
|
+
return { active, unattended: { arm: "frame_family_undeclared", family } };
|
|
418
468
|
return {
|
|
419
|
-
active
|
|
420
|
-
|
|
421
|
-
...(input.legWalltimeMs !== undefined ? { legDeadlineMonotonic: input.nowMonotonicMs + input.legWalltimeMs } : {}),
|
|
469
|
+
active,
|
|
470
|
+
...(active && input.legWalltimeMs !== undefined ? { legDeadlineMonotonic: input.nowMonotonicMs + input.legWalltimeMs } : {}),
|
|
422
471
|
};
|
|
423
472
|
}
|
|
473
|
+
export function approvalPreambleReplays(input) {
|
|
474
|
+
const leg = resolveApprovalLeg({ ...input, nowMonotonicMs: performance.now() });
|
|
475
|
+
return leg.active && leg.unattended === undefined;
|
|
476
|
+
}
|
|
424
477
|
function effectiveAskWindowMs(ttlMs, windowMarginMs, legDeadlineMonotonic, nowMonotonicMs) {
|
|
425
478
|
if (legDeadlineMonotonic === undefined)
|
|
426
479
|
return ttlMs;
|
|
@@ -513,6 +566,7 @@ export class ToolApprovalCoordinator {
|
|
|
513
566
|
parkRedemptionTtlMs;
|
|
514
567
|
parkRedemptionPerPrincipalMax;
|
|
515
568
|
parkedRedeem;
|
|
569
|
+
parkedGate;
|
|
516
570
|
askAudit;
|
|
517
571
|
sessionGrants;
|
|
518
572
|
deploymentListAsks;
|
|
@@ -532,6 +586,7 @@ export class ToolApprovalCoordinator {
|
|
|
532
586
|
this.admitMaxPerOwner = opts?.admitMaxPerOwner ?? DEFAULT_ADMIT_MAX_PER_OWNER;
|
|
533
587
|
this.unattendedPolicy = opts?.unattendedPolicy ?? "park";
|
|
534
588
|
this.parkedRedeem = opts?.parkedRedeem;
|
|
589
|
+
this.parkedGate = opts?.parkedGate;
|
|
535
590
|
this.parkRedemptionMax = opts?.parkRedemptionMax ?? DEFAULT_PARK_REDEMPTION_MAX;
|
|
536
591
|
this.parkRedemptionTtlMs = opts?.parkRedemptionTtlMs ?? DEFAULT_PARK_REDEMPTION_TTL_MS;
|
|
537
592
|
this.parkRedemptionPerPrincipalMax = opts?.parkRedemptionPerPrincipalMax ?? DEFAULT_PARK_REDEMPTION_PER_PRINCIPAL_MAX;
|
|
@@ -553,7 +608,7 @@ export class ToolApprovalCoordinator {
|
|
|
553
608
|
outcomeWord = "unavailable";
|
|
554
609
|
break;
|
|
555
610
|
case "deny":
|
|
556
|
-
outcome = buildUnattendedPolicyRefusal(unattendedRefusalReason(site
|
|
611
|
+
outcome = buildUnattendedPolicyRefusal(unattendedRefusalReason(site));
|
|
557
612
|
outcomeWord = "deny";
|
|
558
613
|
break;
|
|
559
614
|
default: {
|
|
@@ -579,12 +634,12 @@ export class ToolApprovalCoordinator {
|
|
|
579
634
|
return this.unattendedRoutesToPark ? deriveBatchId(sourceTaskId, runId, legKey) : deriveAskScopedBatchId(askId);
|
|
580
635
|
}
|
|
581
636
|
unattendedAskOutcome(site) {
|
|
582
|
-
|
|
583
|
-
arm: "window_zero",
|
|
637
|
+
const facts = {
|
|
584
638
|
...(site.taskId !== undefined ? { taskId: site.taskId } : {}),
|
|
585
639
|
...(site.owner !== undefined && site.owner !== null ? { owner: site.owner } : {}),
|
|
586
640
|
...(site.toolName !== undefined ? { toolName: site.toolName } : {}),
|
|
587
|
-
}
|
|
641
|
+
};
|
|
642
|
+
return this.unattendedOutcome(site.arm === "frame_family_undeclared" ? { arm: site.arm, family: site.family, ...facts } : { arm: site.arm, ...facts });
|
|
588
643
|
}
|
|
589
644
|
async landUnattendedOutcome(outcome, row) {
|
|
590
645
|
const store = this.askStore;
|
|
@@ -933,12 +988,12 @@ export class ToolApprovalCoordinator {
|
|
|
933
988
|
return false;
|
|
934
989
|
}
|
|
935
990
|
}
|
|
936
|
-
boundUnattendedAsk = (identity) => {
|
|
991
|
+
boundUnattendedAsk = (identity, why) => {
|
|
937
992
|
return async (req) => {
|
|
938
993
|
const origin = { owner: identity.owner, taskId: identity.taskId, ...(identity.sessionId !== undefined ? { sessionId: identity.sessionId } : {}), legKey: "" };
|
|
939
994
|
if (await this.standingGrantSettles(origin, req, this.governanceForcedOf(origin, req)))
|
|
940
995
|
return true;
|
|
941
|
-
return this.unattendedAskOutcome({ taskId: identity.taskId, owner: identity.owner, toolName: req.toolName });
|
|
996
|
+
return this.unattendedAskOutcome({ ...why, taskId: identity.taskId, owner: identity.owner, toolName: req.toolName });
|
|
942
997
|
};
|
|
943
998
|
};
|
|
944
999
|
async sessionGrantUncontestedByRow(origin, req) {
|
|
@@ -2142,7 +2197,19 @@ export class ToolApprovalCoordinator {
|
|
|
2142
2197
|
async readAskRowState(store, id, principal) {
|
|
2143
2198
|
return this.readStateOfRow(await this.askRowFor(store, id, "getAsk(read)"), principal);
|
|
2144
2199
|
}
|
|
2145
|
-
|
|
2200
|
+
async readParkedGate(row) {
|
|
2201
|
+
const gate = this.parkedGate;
|
|
2202
|
+
if (gate === undefined || row.gateToken === null)
|
|
2203
|
+
return { kind: "open" };
|
|
2204
|
+
try {
|
|
2205
|
+
return await this.withStoreDeadline(gate({ gateToken: row.gateToken, gateBoundCallId: row.gateBoundCallId }), "readParkedGate", DURABLE_CONVERGE_READ_TIMEOUT_MS);
|
|
2206
|
+
}
|
|
2207
|
+
catch (err) {
|
|
2208
|
+
this.noteStoreError(err, "readParkedGate");
|
|
2209
|
+
return { kind: "open" };
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
async readStateOfRow(row, principal) {
|
|
2146
2213
|
if (!askRowVisibleTo(row, principal))
|
|
2147
2214
|
return this.notPending(NOT_PENDING_UNKNOWN_CAUSE);
|
|
2148
2215
|
const projection = buildAskRowReadProjection(row, Date.now());
|
|
@@ -2157,8 +2224,34 @@ export class ToolApprovalCoordinator {
|
|
|
2157
2224
|
}
|
|
2158
2225
|
case "PARKING":
|
|
2159
2226
|
case "PARKED": {
|
|
2160
|
-
const
|
|
2161
|
-
|
|
2227
|
+
const gate = row.state === "PARKED" ? await this.readParkedGate(row) : { kind: "open" };
|
|
2228
|
+
switch (gate.kind) {
|
|
2229
|
+
case "open": {
|
|
2230
|
+
const parkSession = parkSessionOf(row);
|
|
2231
|
+
body = { state: "parked", ...projection, ...(parkSession !== undefined ? { decidePath: approvalDecidePath(parkSession) } : {}) };
|
|
2232
|
+
break;
|
|
2233
|
+
}
|
|
2234
|
+
case "decided":
|
|
2235
|
+
body = { state: "decided", ...projection, decision: gate.decision, decidedAtMs: gate.decidedAtMs, ...(gate.decisionNote !== undefined ? { decisionNote: gate.decisionNote } : {}) };
|
|
2236
|
+
break;
|
|
2237
|
+
case "void":
|
|
2238
|
+
body = { state: "void", ...projection };
|
|
2239
|
+
break;
|
|
2240
|
+
case "unknown":
|
|
2241
|
+
if (gate.why === "decision_unreadable") {
|
|
2242
|
+
defaultLogger.error("tool approval read saw a PARKED row whose gate is settled but carries no usable decision", {
|
|
2243
|
+
askId: row.askId,
|
|
2244
|
+
defect: seatText("operator-log-text", gate.defects[0]),
|
|
2245
|
+
defectCount: gate.defects.length,
|
|
2246
|
+
});
|
|
2247
|
+
}
|
|
2248
|
+
return this.notPending();
|
|
2249
|
+
default: {
|
|
2250
|
+
const unhandled = gate;
|
|
2251
|
+
defaultLogger.error("tool approval read hit an unknown gate reading", { askId: row.askId, gate: unhandled });
|
|
2252
|
+
return this.notPending();
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2162
2255
|
break;
|
|
2163
2256
|
}
|
|
2164
2257
|
case "DENIED":
|
|
@@ -17,6 +17,12 @@
|
|
|
17
17
|
* 修前(≤7.107.0)本仓自写的这些臂只比 `req.toolName`:模型按别名调嵌入方工具时,运维名单一个都不拦(core 同族策略判 deny、
|
|
18
18
|
* 本仓判 allow)。本仓自身不声明带别名的工具,所以只有经 `resolveSpec` 席挂别名工具的嵌入方走得到。
|
|
19
19
|
*
|
|
20
|
+
* ⚠️ **别名只是名字,不是标记的载体**(core 7.54.0 #2105,[ref] 已定案):上面这条读法只回答「这次调用答得上哪些名字」;嵌入方工具自己的
|
|
21
|
+
* 安全标记(`egress`、不可逆层级 `irreversibility`、可逆探针)**不再**因别名恰是引擎工具名(`MyShell{aliases:["Bash"]}`)而镜像到引擎 shell 上 ——
|
|
22
|
+
* 7.53.x 及以前,`MyShell` 的 `egress: true` / `irreversibility: "always"` 会让引擎 `Bash` 的调用多问一次(收紧),它的恒可逆探针会让引擎 `Bash` 的
|
|
23
|
+
* `touch` 不问就跑(放宽);7.54.0 起两者都不再作用于引擎 shell。要收紧引擎 shell:给**正名**叫 `Bash` 的工具声明标记(同名标记仍作用),或请求
|
|
24
|
+
* `TaskSpec.shellGate: "always"`(本仓的入口 `MANUAL_MODE_SHELL_GATE` / 请求 `shellGate`)。
|
|
25
|
+
*
|
|
20
26
|
* 返回**命中的那个名字**(调用答得上的名字里第一个被规则够到的,拼写在先)—— 规则归因(`matchedAskRule`)写运维名单里的那个名,
|
|
21
27
|
* 不写一个名单里查不到的 wire 名;够不到 ⇒ `undefined`。判定式(精确集 / covering 规则)由调用方给,本函数只管「读哪些名字」。
|
|
22
28
|
*/
|
|
@@ -145,8 +145,9 @@ type _GuardWiringGovernanceReverse = AssertAllKeysHandled<Exclude<WiringGovernan
|
|
|
145
145
|
type PermissionTraceKind = Extract<TraceEvent["kind"], `permission.${string}`>;
|
|
146
146
|
export type PermissionTraceProjected = "permission.persisted_rule_allowed" | "permission.rule_store_unreadable" | "permission.read_only_allowed" | "permission.skill_allowed" | "permission.allowed_tools_allowed";
|
|
147
147
|
type PermissionTraceDropped = "permission.sandbox_admitted" | "permission.rule_sync_resurrected" | "permission.rule_sync_dropped" | "permission.org_snapshot_unavailable";
|
|
148
|
-
type
|
|
149
|
-
type
|
|
148
|
+
type PermissionTraceRunEvent = "permission.cross_boundary_taint";
|
|
149
|
+
type _GuardPermissionTrace = AssertAllKeysHandled<Exclude<PermissionTraceKind, PermissionTraceProjected | PermissionTraceDropped | PermissionTraceRunEvent>>;
|
|
150
|
+
type _GuardPermissionTraceReverse = AssertAllKeysHandled<Exclude<PermissionTraceProjected | PermissionTraceDropped | PermissionTraceRunEvent, PermissionTraceKind>>;
|
|
150
151
|
/** `T` 非 `never` 时收敛到 `never`(空集 ⇒ 残留 `"EMPTY"` ⇒ tsc 红)。 */
|
|
151
152
|
type AssertNonEmpty<T> = [T] extends [never] ? "EMPTY" : never;
|
|
152
153
|
type _GuardPermissionTraceNonEmpty = AssertAllKeysHandled<AssertNonEmpty<PermissionTraceKind>>;
|
|
@@ -215,7 +216,7 @@ type ParkArmKeys = keyof Extract<PendingAction, {
|
|
|
215
216
|
kind: "tool_approval";
|
|
216
217
|
}>;
|
|
217
218
|
type ParkArmQueueProjected = "toolName" | "toolCallId" | "boundInputHash" | "ruleOffers" | "hasBidiControls" | keyof import("../approval-card.js").ParkRowFacts;
|
|
218
|
-
type ParkArmQueueExcluded = "kind" | "args" | "batchToolCallIds" | "completedCallIds" | "capabilityId" | "contractId" | "shapeDigest" | "implementationRevision" | "
|
|
219
|
+
type ParkArmQueueExcluded = "kind" | "args" | "batchToolCallIds" | "completedCallIds" | "capabilityId" | "contractId" | "shapeDigest" | "implementationRevision" | "family" | "pathTarget" | "targets" | "names" | "preview" | "previewWithheld" | "execCwd" | "denialLimitFallback" | "authoredBy" | "boundGrantDigest" | "boundGrants" | "classifierNoRuling";
|
|
219
220
|
type _GuardParkArmQueue = AssertAllKeysHandled<Exclude<ParkArmKeys, ParkArmQueueProjected | ParkArmQueueExcluded>>;
|
|
220
221
|
type _GuardParkArmQueueReverse = AssertAllKeysHandled<Exclude<ParkArmQueueProjected | ParkArmQueueExcluded, ParkArmKeys>>;
|
|
221
222
|
type _NoDoubleBookParkArmQueue = AssertAllKeysHandled<Extract<ParkArmQueueProjected, ParkArmQueueExcluded>>;
|
|
@@ -7,7 +7,9 @@ export type SafeText = string & {
|
|
|
7
7
|
/**
|
|
8
8
|
* **座表(闭集;一行 = 一类出口)**。加一类出口 = 加一行;表外的座名是编译错误。
|
|
9
9
|
* · `trace-frame-text`:trace 帧的自由文本位(子代名 / 动作行 / 通告正文与结果 / 诊断 / 注入摘录 / 压缩说明 / 人类输入的署名与附件标签 …),不设界;
|
|
10
|
-
* · `trace-frame-line`:trace 帧里一行宽的展示位(`workspace_changed.cwd` / `brain_status.detail`),界 300
|
|
10
|
+
* · `trace-frame-line`:trace 帧里一行宽的展示位(`workspace_changed.cwd` / `brain_status.detail`),界 300 码元;
|
|
11
|
+
* · `operator-log-text`:运维日志字段里的一句话 —— 读口坏行的缺陷句(core 的筛按位置点名并回显落盘标量)、`tool_trace` 行里工具的输出 / 失败句 / 拒因;日志字段行,界 200 码元(含截断记号);
|
|
12
|
+
* · `operator-log-arg`:运维日志字段里模型写的工具入参(`tool_trace` 的 `arg.*`:路径 / 命令 / 模式 / 非对象入参的串形);日志字段行,界 120 码元(含截断记号)。
|
|
11
13
|
*/
|
|
12
14
|
declare const EXIT_SEATS: {
|
|
13
15
|
readonly "trace-frame-text": {
|
|
@@ -17,6 +19,16 @@ declare const EXIT_SEATS: {
|
|
|
17
19
|
readonly scope: "free-text";
|
|
18
20
|
readonly maxChars: 300;
|
|
19
21
|
};
|
|
22
|
+
readonly "operator-log-text": {
|
|
23
|
+
readonly scope: "free-text";
|
|
24
|
+
readonly maxChars: 200;
|
|
25
|
+
readonly logField: true;
|
|
26
|
+
};
|
|
27
|
+
readonly "operator-log-arg": {
|
|
28
|
+
readonly scope: "free-text";
|
|
29
|
+
readonly maxChars: 120;
|
|
30
|
+
readonly logField: true;
|
|
31
|
+
};
|
|
20
32
|
};
|
|
21
33
|
export type ExitSeat = keyof typeof EXIT_SEATS;
|
|
22
34
|
/**
|
package/dist/trace/exit-seat.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { CONTROL_AND_BIDI_CHARS, stripControlAndBidi } from "@sema-agent/settings-schema";
|
|
2
|
+
import { CALLER_TEXT_CUT_MARK } from "../caller-text-echo.js";
|
|
1
3
|
import { errorKindOf } from "../observability/err-text.js";
|
|
2
4
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
3
5
|
import { OVERSIZED_REDACTION_PLACEHOLDER } from "../observability/run-terminal.js";
|
|
@@ -5,12 +7,17 @@ import { proposeRedactionSpans, REDACTION_TOKEN_RE_SRC } from "./redact.js";
|
|
|
5
7
|
const EXIT_SEATS = {
|
|
6
8
|
"trace-frame-text": { scope: "free-text" },
|
|
7
9
|
"trace-frame-line": { scope: "free-text", maxChars: 300 },
|
|
10
|
+
"operator-log-text": { scope: "free-text", maxChars: 200, logField: true },
|
|
11
|
+
"operator-log-arg": { scope: "free-text", maxChars: 120, logField: true },
|
|
8
12
|
};
|
|
9
13
|
const SEAT_ROWS = new Map(Object.entries(EXIT_SEATS));
|
|
10
14
|
for (const [name, row] of SEAT_ROWS) {
|
|
11
15
|
if (row.maxChars !== undefined && row.maxChars < OVERSIZED_REDACTION_PLACEHOLDER.length) {
|
|
12
16
|
throw new Error(`exit seat "${name}": maxChars ${row.maxChars} cannot hold the failure placeholder (${OVERSIZED_REDACTION_PLACEHOLDER.length} code units)`);
|
|
13
17
|
}
|
|
18
|
+
if (row.logField === true && row.maxChars === undefined) {
|
|
19
|
+
throw new Error(`exit seat "${name}": a log-field row must declare maxChars (the cut mark counts inside the bound)`);
|
|
20
|
+
}
|
|
14
21
|
}
|
|
15
22
|
function mergeSpans(spans) {
|
|
16
23
|
const order = spans.map((s, i) => ({ s, i })).sort((a, b) => a.s.start - b.s.start || b.s.end - a.s.end || a.i - b.i);
|
|
@@ -59,13 +66,60 @@ function renderOnce(text, groups, maxChars) {
|
|
|
59
66
|
parts.push(maxChars === undefined || len + tail.length <= maxChars ? tail : cutPlain(tail, maxChars - len));
|
|
60
67
|
return parts.join("");
|
|
61
68
|
}
|
|
69
|
+
const STRIPPED_UNIT = (() => {
|
|
70
|
+
const inSet = new RegExp(`[${CONTROL_AND_BIDI_CHARS}]`);
|
|
71
|
+
const table = new Uint8Array(0x10000);
|
|
72
|
+
for (let unit = 0; unit < table.length; unit++)
|
|
73
|
+
if (inSet.test(String.fromCharCode(unit)))
|
|
74
|
+
table[unit] = 1;
|
|
75
|
+
return table;
|
|
76
|
+
})();
|
|
77
|
+
function mapSpansIntoStripped(text, spans) {
|
|
78
|
+
const marks = [...new Set(spans.flatMap((sp) => [sp.start, sp.end]))].sort((a, b) => a - b);
|
|
79
|
+
const strippedBefore = new Map();
|
|
80
|
+
let removed = 0;
|
|
81
|
+
let at = 0;
|
|
82
|
+
for (const mark of marks) {
|
|
83
|
+
for (; at < mark; at++)
|
|
84
|
+
if (STRIPPED_UNIT[text.charCodeAt(at)] === 1)
|
|
85
|
+
removed += 1;
|
|
86
|
+
strippedBefore.set(mark, mark - removed);
|
|
87
|
+
}
|
|
88
|
+
const mapped = [];
|
|
89
|
+
for (const sp of spans) {
|
|
90
|
+
const start = strippedBefore.get(sp.start);
|
|
91
|
+
const end = strippedBefore.get(sp.end);
|
|
92
|
+
if (end > start)
|
|
93
|
+
mapped.push({ start, end, repl: sp.repl });
|
|
94
|
+
}
|
|
95
|
+
return mapped;
|
|
96
|
+
}
|
|
97
|
+
function proposeLogFieldSpans(text, scope) {
|
|
98
|
+
const read = stripControlAndBidi(text);
|
|
99
|
+
if (read === text)
|
|
100
|
+
return { read, spans: proposeRedactionSpans(text, scope) };
|
|
101
|
+
if (read.length === 0)
|
|
102
|
+
return { read, spans: [] };
|
|
103
|
+
const original = proposeRedactionSpans(text, scope);
|
|
104
|
+
const stripped = proposeRedactionSpans(read, scope, { countHits: original.length === 0 });
|
|
105
|
+
return { read, spans: [...stripped, ...mapSpansIntoStripped(text, original)] };
|
|
106
|
+
}
|
|
107
|
+
function renderRow(text, groups, row) {
|
|
108
|
+
if (row.logField !== true || row.maxChars === undefined)
|
|
109
|
+
return renderOnce(text, groups, row.maxChars);
|
|
110
|
+
const whole = renderOnce(text, groups, undefined);
|
|
111
|
+
if (whole.length <= row.maxChars)
|
|
112
|
+
return whole;
|
|
113
|
+
return renderOnce(text, groups, row.maxChars - CALLER_TEXT_CUT_MARK.length) + CALLER_TEXT_CUT_MARK;
|
|
114
|
+
}
|
|
62
115
|
export function seatText(seat, text) {
|
|
63
116
|
const row = SEAT_ROWS.get(seat);
|
|
64
117
|
if (row === undefined)
|
|
65
118
|
throw new Error(`exit seat: ${JSON.stringify(seat)} is not a row of the seat table`);
|
|
66
119
|
let out;
|
|
67
120
|
try {
|
|
68
|
-
|
|
121
|
+
const { read, spans } = row.logField === true ? proposeLogFieldSpans(text, row.scope) : { read: text, spans: proposeRedactionSpans(text, row.scope) };
|
|
122
|
+
out = renderRow(read, mergeSpans(spans), row);
|
|
69
123
|
}
|
|
70
124
|
catch (err) {
|
|
71
125
|
recordFailOpen("server.run-terminal.redact-threw", `seat=${seat} len=${typeof text === "string" ? text.length : -1} err=${errorKindOf(err)}`);
|
|
@@ -52,7 +52,9 @@ export type LedgerEventType = "reasoning" | "text" | "tool_start" | "tool_end" |
|
|
|
52
52
|
| "task_started" | "task_progress" | "task_notification" | "turn_end"
|
|
53
53
|
/** park 行名**两个**(`appendParked(kind)` 逐字取自 `result.status`)—— 运行期闭集见
|
|
54
54
|
* {@link PARK_LEDGER_EVENT_TYPES}(本联合是它的类型面,两者由下方的双向断言锁步)。 */
|
|
55
|
-
| "suspended" | "needs_review" | "done" | "failed" | "suggestions" | "model_usage" | "prompt_assembled" | "config_assembled"
|
|
55
|
+
| "suspended" | "needs_review" | "done" | "failed" | "suggestions" | "model_usage" | "prompt_assembled" | "config_assembled"
|
|
56
|
+
/** core 7.54.0 [ref] O9b S1:`permission.cross_boundary_taint` 帧的耐久行(伴随记录,与 `config_assembled` 同一只队列、同一批 drain 点;只在执行于目标设备的树上有)。 */
|
|
57
|
+
| "cross_boundary_taint" | "file_link"
|
|
56
58
|
/** 人在环三族的 open/close 帧对(投递面 = 本腿的 durable events tail)。 */
|
|
57
59
|
| "elicitation" | "elicitation_complete" | "question" | "question_complete" | "tool_approval" | "tool_approval_complete"
|
|
58
60
|
/** [ref] §4.3 呈卡族的开卡帧与单卡消卡帧(与上面两只 tool_approval 帧同一条投递面;一条腿只发其中一族,
|
package/dist/trace/project.d.ts
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
import type { GateOutcome, McpDelivered, TaskResult, SystemInjectionPriority } from "@sema-agent/core";
|
|
27
27
|
import type { RunEvent, RunRecord } from "../plugins/store-contracts.js";
|
|
28
28
|
import type { ModelUsageDelta, ModelUsageTracker, PromptManifestRecord } from "../budget.js";
|
|
29
|
+
import type { RunCompanionRecord } from "../observability/prompt-manifest.js";
|
|
29
30
|
import { type SafeText } from "./exit-seat.js";
|
|
30
31
|
import { type SemaProvenanceInput } from "./sema-provenance.js";
|
|
31
32
|
import type { LedgerEventType } from "./ledger-events.js";
|
|
@@ -51,7 +52,7 @@ export declare function appendModelUsageDelta(append: AppendFn, modelUsage: Mode
|
|
|
51
52
|
* (one Map.get), so callers fire it liberally. */
|
|
52
53
|
export declare function appendPromptManifest(append: AppendFn, tracker: {
|
|
53
54
|
drain(taskId: string): PromptManifestRecord[] | undefined;
|
|
54
|
-
|
|
55
|
+
drainCompanions?(taskId: string): RunCompanionRecord[] | undefined;
|
|
55
56
|
} | undefined, taskId: string): Promise<void>;
|
|
56
57
|
/** E8: at a TERMINAL done — flush the final delta, then SUM all `model_usage` events (every process leg) into the
|
|
57
58
|
* result's `stats.modelUsage` echo. Returns the result unchanged when no usage was tracked (shell soft-degrades). */
|
|
@@ -1083,6 +1084,22 @@ export declare function contextUsageEventData(ev: {
|
|
|
1083
1084
|
* 与结果面的孪生 `TaskResult.responseModel` 同一只闸 —— 同一个名字在两张脸上不许两种待遇。非串 / 空串同样不铸键(空串在
|
|
1084
1085
|
* 那条谓词的长度下界之外;core 的读律 `providerReportedModel` 对空串也答「没报」)。
|
|
1085
1086
|
*/
|
|
1087
|
+
/**
|
|
1088
|
+
* 🔴 core 7.54.0 [ref] O9b S1 —— 设备车道发射闸的 `permission.cross_boundary_taint` 帧 → 耐久行 `cross_boundary_taint`(`GET /v1/runs/:id/events` 账本回放同名交出;
|
|
1089
|
+
* trace SSE `GET /v1/tasks/:id/stream` 上是 `cross-boundary-taint` 帧,按存下的行原样交出)。帧只在**执行于目标设备的树**上发(武装腿 prepare / 续跑腿 resume 各一帧,离目标读第一次放进树时 observed 一帧),
|
|
1090
|
+
* 宿主车道零帧 ⇒ 本行在宿主车道上永不出现。纯元数据(core 逐字「Metadata only」):树的污染位(`taint`,core `CROSS_BOUNDARY_TAINT_BITS` 的子集)、
|
|
1091
|
+
* 树是否受治理(`governed`)、阶段(`phase`)、腿的 `runId`,observed 时那一只调用(`toolName` / `toolCallId`)。
|
|
1092
|
+
* 信封(`kind` / `version` / `taskId` / `ts`)不上行:行自带 seq / ts,run 就是 taskId(与清单行同一条律;登记表 `cross_boundary_taint` 行)。
|
|
1093
|
+
* 值:`taint` 交一份数组副本(只收串);`toolName` / `toolCallId` 在场才铸。词按开集串过境(属主是引擎的闭集;本仓不判词表)。
|
|
1094
|
+
*/
|
|
1095
|
+
export declare function crossBoundaryTaintEventData(ev: {
|
|
1096
|
+
runId?: unknown;
|
|
1097
|
+
phase?: unknown;
|
|
1098
|
+
taint?: unknown;
|
|
1099
|
+
governed?: unknown;
|
|
1100
|
+
toolName?: unknown;
|
|
1101
|
+
toolCallId?: unknown;
|
|
1102
|
+
}): Record<string, unknown>;
|
|
1086
1103
|
export declare function turnEndEventData(ev: {
|
|
1087
1104
|
usage?: unknown;
|
|
1088
1105
|
usageMissing?: true;
|
package/dist/trace/project.js
CHANGED
|
@@ -24,10 +24,10 @@ export async function appendPromptManifest(append, tracker, taskId) {
|
|
|
24
24
|
if (records)
|
|
25
25
|
for (const m of records)
|
|
26
26
|
await append("prompt_assembled", m);
|
|
27
|
-
const
|
|
28
|
-
if (
|
|
29
|
-
for (const
|
|
30
|
-
await append(
|
|
27
|
+
const companions = tracker?.drainCompanions?.(taskId);
|
|
28
|
+
if (companions)
|
|
29
|
+
for (const c of companions)
|
|
30
|
+
await append(c.type, c.data);
|
|
31
31
|
}
|
|
32
32
|
export async function attachModelUsage(result, ctx) {
|
|
33
33
|
if (!ctx.modelUsage)
|
|
@@ -846,6 +846,16 @@ function contextUsageSectionsField(sections) {
|
|
|
846
846
|
}
|
|
847
847
|
return out.length > 0 ? { sections: out } : {};
|
|
848
848
|
}
|
|
849
|
+
export function crossBoundaryTaintEventData(ev) {
|
|
850
|
+
return {
|
|
851
|
+
...(typeof ev.runId === "string" ? { runId: ev.runId } : {}),
|
|
852
|
+
...(typeof ev.phase === "string" ? { phase: ev.phase } : {}),
|
|
853
|
+
...(Array.isArray(ev.taint) ? { taint: ev.taint.filter((b) => typeof b === "string") } : {}),
|
|
854
|
+
...(typeof ev.governed === "boolean" ? { governed: ev.governed } : {}),
|
|
855
|
+
...(typeof ev.toolName === "string" ? { toolName: ev.toolName } : {}),
|
|
856
|
+
...(typeof ev.toolCallId === "string" ? { toolCallId: ev.toolCallId } : {}),
|
|
857
|
+
};
|
|
858
|
+
}
|
|
849
859
|
export function turnEndEventData(ev) {
|
|
850
860
|
return {
|
|
851
861
|
...(ev.usage ? { usage: ev.usage } : {}),
|
|
@@ -1004,6 +1014,8 @@ function mapTraceEventInner(type, seq, data) {
|
|
|
1004
1014
|
}
|
|
1005
1015
|
case "prompt_assembled":
|
|
1006
1016
|
return { event: "prompt-assembled", data: { seq, ...data } };
|
|
1017
|
+
case "cross_boundary_taint":
|
|
1018
|
+
return { event: "cross-boundary-taint", data: { seq, ...data } };
|
|
1007
1019
|
case "done":
|
|
1008
1020
|
return { event: "done", data: { seq } };
|
|
1009
1021
|
case "suspended":
|
package/dist/trace/redact.d.ts
CHANGED
|
@@ -165,8 +165,9 @@ export declare function redactionSpansForTest(text: string, opts?: {
|
|
|
165
165
|
/**
|
|
166
166
|
* 🔴 **出口座的提案口**(S-485 / RH-1 第一期;座 = `src/trace/exit-seat.ts` 的 `seatText`)。
|
|
167
167
|
*
|
|
168
|
-
* 同一只 {@link runRedaction}、同一遍扫描、同一次观察者计数(
|
|
169
|
-
*
|
|
168
|
+
* 同一只 {@link runRedaction}、同一遍扫描、同一次观察者计数(座上的一次脱敏就是一次主扫),交出**原文坐标**上的跨度与各自的替换串:臂认什么、档里有哪些臂,仍然只由本模块答。
|
|
169
|
+
* `opts.countHits` 缺省 `true`;`false` = 这一遍照常提案、照常留吃尾一类的 fail-open 痕,只是不把命中表喂观察者(`redactions_applied_total`)——
|
|
170
|
+
* 座对同一段文本要扫两种读法时,让其中一遍计数。与 {@link redactSecrets} 的 `opts.observe:false` 不同:那是影子重扫,计数与留痕一并不记。座拿它做**一次区间合并 + 一次渲染**
|
|
170
171
|
* (合并律与渲染律住在座里,本模块不拼第二份产物给座)。
|
|
171
172
|
*
|
|
172
173
|
* 第一期交的是池解完重叠之后的那一份(升序、两两不交);第二期改交未解重叠的原始候选,池内并集律 {@link resolveSpanOverlaps} 与
|
|
@@ -178,7 +179,9 @@ export type RedactionSpan = {
|
|
|
178
179
|
readonly repl: string;
|
|
179
180
|
};
|
|
180
181
|
export type { RedactionScope };
|
|
181
|
-
export declare function proposeRedactionSpans(text: string, scope: RedactionScope
|
|
182
|
+
export declare function proposeRedactionSpans(text: string, scope: RedactionScope, opts?: {
|
|
183
|
+
readonly countHits?: boolean;
|
|
184
|
+
}): readonly RedactionSpan[];
|
|
182
185
|
/**
|
|
183
186
|
* 🔴 环 + 深度预算(2026-07-25 补)。原实现是纯递归、**既不记已访问节点也不限深度**,而它的调用位置是
|
|
184
187
|
* **最不可信的边界**:`toolStart`/`toolEnd` 把工具的 `args`/`output`/`structured` 直接喂进来。后果两条:
|