@scp-dev/core 0.1.0 → 0.1.2

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/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # @scp-dev/core
2
+
3
+ The SCP protocol core: wire types, message framing, scopes, policy, the judge and the relay core that brokers and hubs run on.
4
+
5
+ ```sh
6
+ npm install @scp-dev/core
7
+ ```
8
+
9
+ ## Usage
10
+
11
+ ```ts
12
+ import { Database } from "bun:sqlite"
13
+ import { RelayCore, sha256Hex, type Sql, type SqlValue, wrapMessage } from "@scp-dev/core"
14
+
15
+ const db = new Database(":memory:")
16
+ const sql: Sql = { all: <T>(query: string, ...params: SqlValue[]) => db.query(query).all(...params) as T[] }
17
+ const core = new RelayCore(sql, { hash: sha256Hex, transaction: (fn) => db.transaction(fn)() })
18
+ core.migrate()
19
+ const orch = core.claim({ handle: "orch", harness: "claude-code" })
20
+ const parser = core.claim({ handle: "parser", harness: "codex" })
21
+ core.send(orch.endpoint.id, { to: "parser", body: "Also cover the empty-input case." })
22
+ for (const message of core.inbox(parser.endpoint.id)) console.log(wrapMessage(message))
23
+ ```
24
+
25
+ `RelayCore` runs on any synchronous `Sql` store: SQLite locally, a Durable Object in the cloud.
26
+
27
+ ## Provides
28
+
29
+ - `RelayCore`: endpoints, claims, inboxes, threads, replies, asks, grants, loop guards and rate limits.
30
+ - Wire types, `LIMITS` and `RelayError` codes for `scp/1`.
31
+ - Untrusted-content framing: `wrapMessage`, `wrapPeek` and the delivery preambles.
32
+ - Scopes and policy: `parsePolicy`, `scopeDecision` and the tree selectors.
33
+ - Credential and PII scanning: `findSecret`, `redactSecrets`, `redactPii`.
34
+ - The judge: `prefilter`, `runJudge`, `parseJudgePolicy`, `HttpJudgeClient`, with no default model.
35
+
36
+ Implements [Messages](https://scp.dev/spec#7-messages), [Authorization](https://scp.dev/spec#10-authorization) and [Judging](https://scp.dev/spec#15-judging). See the [root README](https://github.com/scphq/scp#readme).
37
+
38
+ ## License
39
+
40
+ [Apache-2.0](LICENSE).
package/dist/index.d.ts CHANGED
@@ -352,10 +352,18 @@ declare const LIMITS: {
352
352
  readonly pushes: 20;
353
353
  /** how long a live worker's next message waits after a push showed one of its own */
354
354
  readonly pushGapS: 30;
355
+ /** with other workers still running, how long a stop's push waits for one more worker result before it goes */
356
+ readonly pushQuietS: 3;
357
+ /** the longest a stop's push gathers worker results, from the first it has to show */
358
+ readonly pushGraceS: 6;
355
359
  readonly stopBlocks: 3;
356
360
  /** the stops a session's late parent directives may hold */
357
361
  readonly directiveHolds: 5;
358
362
  readonly injectHoldS: 10;
363
+ /** how long inbox still lists a message a background hook showed, whose text the harness may hand the agent only after its next tool call */
364
+ readonly deferredShowS: 600;
365
+ /** a deferred showing older than this settles at the session's next tool event or prompt: the harness has handed it over by then */
366
+ readonly deferredSettleMs: 2000;
359
367
  readonly listOfflineWithinS: 86400;
360
368
  readonly handleBoundS: number;
361
369
  readonly idempotencyS: 86400;
@@ -377,8 +385,10 @@ declare const LIMITS: {
377
385
  readonly grantTtlS: number;
378
386
  readonly groupIdleS: 86400;
379
387
  readonly policyLoadsKept: 8;
388
+ /** the prompts between two refreshes of a title a hook inferred */
389
+ readonly titleRefreshTurns: 5;
380
390
  };
381
- type ErrorCode = "not_found" | "inbox_full" | "rate_limited" | "loop_guard" | "data_class_refused" | "secret_refused" | "policy_refused" | "too_large" | "invalid_params" | "unauthorized" | "method_not_found" | "scope_refused" | "unavailable" | "judge_refused";
391
+ type ErrorCode = "not_found" | "inbox_full" | "rate_limited" | "loop_guard" | "data_class_refused" | "secret_refused" | "policy_refused" | "too_large" | "invalid_params" | "unauthorized" | "method_not_found" | "scope_refused" | "unavailable" | "judge_refused" | "not_live";
382
392
  declare class RelayError extends Error {
383
393
  readonly code: ErrorCode;
384
394
  readonly retryAfterS?: number | undefined;
@@ -636,6 +646,8 @@ interface Envelope {
636
646
  expires_at: string;
637
647
  /** a request whose sender holds the recipient's stop until it replies */
638
648
  require_reply?: true;
649
+ /** set by inbox on a message a background hook already showed (and marked read), whose text may reach the agent only after this call */
650
+ shown_at?: string;
639
651
  /**
640
652
  * the sender as the local broker sees it from the recipient, never from anything the sender claims: `parent` when it issued the grant the
641
653
  * recipient's current incarnation spent or is its ancestor, `child` when it is the recipient's descendant; unset for humans and other machines.
@@ -663,6 +675,8 @@ interface StopWaitResult {
663
675
  more: number;
664
676
  workers: string[];
665
677
  starting: number;
678
+ /** the live --no-push workers it spawned, which no stop waits on, by address */
679
+ background?: string[];
666
680
  others: number;
667
681
  others_shown?: number;
668
682
  pushes_left: number;
@@ -683,6 +697,8 @@ interface SendParams {
683
697
  mime?: string;
684
698
  /** on a request only: the recipient's stop is held until it replies */
685
699
  require_reply?: boolean;
700
+ /** refuse `not_live`, queueing nothing, when an agent recipient has no live session */
701
+ live?: boolean;
686
702
  }
687
703
  /** what ask and ask_wait answer: the reply; or pending, with `ended` once every recipient present at the send went offline without replying; or expired */
688
704
  interface AskResult {
@@ -732,10 +748,26 @@ interface SendResult {
732
748
  last_seen: string | null;
733
749
  state?: Reach;
734
750
  }[];
751
+ /** one line per agent recipient no live session holds: `queued: no live session holds <address>` */
752
+ warnings?: string[];
735
753
  duplicate?: boolean;
736
754
  held?: JudgeHeld;
737
755
  redacted?: JudgeRedacted;
738
756
  }
757
+ /**
758
+ * a message the caller sent and, per recipient, when it read the message (acked), or when a background hook delivered it (acked by that
759
+ * showing; the agent gets it with its next request), and when a hook or channel showed it: ISO times, or null
760
+ */
761
+ interface MessageReceipts {
762
+ id: string;
763
+ thread: string;
764
+ recipients: {
765
+ address: string;
766
+ read_at: string | null;
767
+ delivered_at?: string | null;
768
+ shown_at: string | null;
769
+ }[];
770
+ }
739
771
  interface AuditRow {
740
772
  id: number;
741
773
  at: string;
@@ -777,6 +809,8 @@ interface PolicyDefaults {
777
809
  /** `inherit`: a child of a capped parent reaches only what the parent reaches; a preset caps every placed child without grant caps */
778
810
  child_default?: ScopePreset | "inherit";
779
811
  verify_lineage?: "spike" | boolean;
812
+ /** who may rename an agent besides the human at a terminal: `root` (default) only a root session itself, `self` any agent itself, spawned ones too */
813
+ rename?: "root" | "self";
780
814
  }
781
815
  interface Policy {
782
816
  rules: PolicyRule[];
@@ -982,7 +1016,7 @@ declare function relation(sender: PolicySubject, recipient: PolicySubject): stri
982
1016
 
983
1017
  type SqlValue = string | number | null;
984
1018
  /**
985
- * The one SQLite method the core needs: bun:sqlite locally, `ctx.storage.sql` in a Durable Object.
1019
+ * The one SQLite method the core needs: bun:sqlite or node:sqlite locally, `ctx.storage.sql` in a Durable Object.
986
1020
  * Calls stay synchronous, so every read-modify-write in the core is atomic between awaits.
987
1021
  */
988
1022
  interface Sql {
@@ -991,6 +1025,8 @@ interface Sql {
991
1025
  declare const one: <T>(sql: Sql, query: string, ...params: SqlValue[]) => T | undefined;
992
1026
 
993
1027
  declare const HUMAN_HANDLE = "_human";
1028
+ /** an agent title's length cap */
1029
+ declare const TITLE_CHARS = 64;
994
1030
  declare const SYSTEM_HANDLE = "_relay";
995
1031
  declare const GONE_PREFIX = "_gone_";
996
1032
  interface EndpointRow {
@@ -1038,12 +1074,20 @@ interface EndpointRow {
1038
1074
  /** JSON {@link ScopeCaps} */
1039
1075
  caps: string | null;
1040
1076
  title: string | null;
1077
+ /** the title a hook last inferred: the title is inferred while it equals this */
1078
+ title_auto: string | null;
1079
+ /** the prompt count when a summary last set the inferred title; null when the first prompt set it */
1080
+ title_turn: number | null;
1081
+ /** the prompt count when the status note last changed or a hook last reminded the agent to refresh it */
1082
+ tldr_turn: number | null;
1041
1083
  /** when a new spawn last took the endpoint over: mail and messages from before it were the earlier holder's */
1042
1084
  reset_at: number | null;
1043
1085
  /** when a new spawn last took the endpoint over: with its id, names the holder another machine sees */
1044
1086
  spawned_at: number | null;
1045
1087
  /** who may peek: null is off */
1046
1088
  peek: PeekLevel | null;
1089
+ /** when the session last set its handle itself (join or rename): a later claim with another handle does not rename it back */
1090
+ named: number | null;
1047
1091
  }
1048
1092
  interface MessageRow {
1049
1093
  id: string;
@@ -1068,7 +1112,7 @@ interface CoreOptions {
1068
1112
  now?: () => number;
1069
1113
  /** a synchronous hash for resume tokens (sha256 hex) */
1070
1114
  hash: (value: string) => string;
1071
- /** runs `fn` atomically: bun:sqlite's db.transaction, a Durable Object's transactionSync */
1115
+ /** runs `fn` atomically: a local SQLite transaction, a Durable Object's transactionSync */
1072
1116
  transaction?: <T>(fn: () => T) => T;
1073
1117
  /** count every sender but the relay itself, humans included, against LIMITS.userPerDay, and trust no client's terminal claim for a hop reset (default: agents and non-terminal humans count) */
1074
1118
  capEverySender?: boolean;
@@ -1092,6 +1136,8 @@ declare class RelayCore {
1092
1136
  private readonly capEverySender;
1093
1137
  private readonly alive;
1094
1138
  private readonly hostMachine;
1139
+ private readonly namedMachine;
1140
+ private readonly hostname;
1095
1141
  private policy;
1096
1142
  /** the identity each policy `group:<name>` naming no policy group bound to when the policy's text first loaded: that group's then, or `none` */
1097
1143
  private policyBinds;
@@ -1100,6 +1146,8 @@ declare class RelayCore {
1100
1146
  private scopeMemo;
1101
1147
  private readonly deferred;
1102
1148
  constructor(sql: Sql, options: CoreOptions);
1149
+ /** a `machine` label naming this host by its hostname, as clients default it, names it by its machine name when one is set */
1150
+ private machineLabel;
1103
1151
  migrate(): void;
1104
1152
  /** every stored list that is exactly `tree:self`, the tree preset's old list, narrows once to the preset's list */
1105
1153
  private migrateTreePreset;
@@ -1253,6 +1301,49 @@ declare class RelayCore {
1253
1301
  status(endpoint: string, state: unknown, note: unknown): void;
1254
1302
  /** an agent's own title, shown in list and whoami; empty clears it, and no claim overwrites it */
1255
1303
  title(endpoint: string, value: unknown): EndpointView;
1304
+ /**
1305
+ * a hook's guess at the session's title, from the harness's own summary or the first prompt's first line; whether it changed the title.
1306
+ * Never over a title set explicitly; the first prompt fills only an empty title, and a summary replaces an inferred one at most every
1307
+ * ${LIMITS.titleRefreshTurns} prompts, or at once over the first prompt's
1308
+ */
1309
+ inferTitle(endpoint: string, params: {
1310
+ text?: unknown;
1311
+ from?: unknown;
1312
+ }, options?: {
1313
+ transient?: boolean;
1314
+ process?: string;
1315
+ }): boolean;
1316
+ /**
1317
+ * a session joins on demand: its hook claim registers or takes back its endpoint, marked seen now even right after a leave, and `handle`,
1318
+ * when given, renames it in place ({@link rename}'s rules). Nothing is kept when the rename is refused
1319
+ */
1320
+ joinSession(claim: Claim, handle?: unknown): EndpointView;
1321
+ /**
1322
+ * renames an agent in place: its id, mail, threads, status, title and relations stay. An agent renames only itself, and only a root unless
1323
+ * the policy's `defaults.rename` is `self`; the human at a terminal renames any local agent. The new handle is valid and held by no other endpoint
1324
+ */
1325
+ rename(actor: ScopeActor, params: {
1326
+ endpoint?: unknown;
1327
+ handle: unknown;
1328
+ }): {
1329
+ endpoint: EndpointView;
1330
+ from: string;
1331
+ session: string | null;
1332
+ };
1333
+ /** the endpoint's handle set to `value`, with the stored `ep:` and `fleet:` selectors that named it */
1334
+ private renameRow;
1335
+ /**
1336
+ * a session leaves: it lets go of its session and token and shows offline, its mail kept; a later join takes it back. An agent leaves only
1337
+ * itself, the human at a terminal any local root. A spawned worker stays joined while it runs
1338
+ */
1339
+ leaveSession(actor: ScopeActor, params?: {
1340
+ endpoint?: unknown;
1341
+ }): {
1342
+ endpoint: EndpointView;
1343
+ session: string | null;
1344
+ };
1345
+ /** the harness session key an endpoint holds, if any */
1346
+ sessionOf(endpoint: string): string | null;
1256
1347
  /**
1257
1348
  * `origin.tty` is recorded on messages the human inbox sends: whether its client said a terminal was attached. `origin.unverified`: the human
1258
1349
  * sends without a terminal, so the scopes check it like any unplaced sender
@@ -1278,7 +1369,11 @@ declare class RelayCore {
1278
1369
  * that the endpoint has a subject: one it refuses is dropped, audited for the spawner
1279
1370
  */
1280
1371
  private takePending;
1281
- /** the oldest unacked deliveries after the cursor; reading counts as receiving for the causal hop */
1372
+ /**
1373
+ * the oldest unacked deliveries after the cursor, and those a background hook showed in the last ${LIMITS.deferredShowS} s whose showing has
1374
+ * not settled ({@link injected}), with `shown_at`: the harness may hand the agent that hook's text only after this call; reading counts as
1375
+ * receiving for the causal hop
1376
+ */
1282
1377
  inbox(endpoint: string, options?: {
1283
1378
  after?: number | undefined;
1284
1379
  limit?: number | undefined;
@@ -1309,6 +1404,7 @@ declare class RelayCore {
1309
1404
  * `directive_seqs`. Any call says `brief` once the session was shown the full preamble ({@link briefed}), and one that is no stop names in
1310
1405
  * `intro` the spawner of an endpoint a `swarm spawn` grant placed, with the grant's steer key in `intro_key`, until a hook showed it
1311
1406
  * ({@link introduced}). A hook of another process than the one holding the endpoint (a child that inherited the session key) gets nothing.
1407
+ * A wake also offers live workers' messages.
1312
1408
  */
1313
1409
  inject(endpoint: string, options?: {
1314
1410
  stop?: boolean;
@@ -1317,6 +1413,7 @@ declare class RelayCore {
1317
1413
  directives?: boolean;
1318
1414
  transient?: boolean;
1319
1415
  process?: string;
1416
+ wake?: boolean;
1320
1417
  }): {
1321
1418
  messages: Envelope[];
1322
1419
  offered: number[];
@@ -1358,12 +1455,13 @@ declare class RelayCore {
1358
1455
  * messages no hook has shown or holds follow theirs ({@link pushOthers}), `others_shown` of them, held with them; `others` counts the other
1359
1456
  * unread ones left. A live worker's messages wait ${LIMITS.pushGapS} s after a push showed one of its own ({@link pushed}), and with no
1360
1457
  * push left since the last prompt nothing is shown. `prompted` says a prompt came since inject handed out the `prompts` mark. A hook of
1361
- * another process than the one holding the endpoint gets nothing.
1458
+ * another process than the one holding the endpoint gets nothing. `peek`: the same answer, nothing held (`hold` null) or marked received.
1362
1459
  */
1363
1460
  stopWait(endpoint: string, options?: {
1364
1461
  transient?: boolean;
1365
1462
  process?: string;
1366
1463
  prompts?: number;
1464
+ peek?: boolean;
1367
1465
  }): StopWaitResult;
1368
1466
  /**
1369
1467
  * the unread messages no hook has shown or holds that a push shows after its workers' ({@link pushable}): newest first, up to
@@ -1384,6 +1482,10 @@ declare class RelayCore {
1384
1482
  transient?: boolean;
1385
1483
  process?: string;
1386
1484
  }): boolean;
1485
+ /** whether a prompt should remind the agent to refresh its status note: `every` prompts passed since the note or the last reminder; counts the reminder */
1486
+ tldrDue(endpoint: string, every: number): boolean;
1487
+ /** the background hooks' showings ({@link injected}) older than ${LIMITS.deferredSettleMs} ms reached the agent: inbox no longer lists them */
1488
+ private settle;
1387
1489
  /**
1388
1490
  * The endpoint's live spawned workers: its unrevoked spawn grants (minted by `swarm spawn` without --no-push, never by spawn-env) from its
1389
1491
  * current spawn that are either unspent and unexpired (`starting`: the harness has not joined yet) or spent by an endpoint whose status is not
@@ -1431,11 +1533,14 @@ declare class RelayCore {
1431
1533
  }): number;
1432
1534
  /**
1433
1535
  * a hook or channel showed these deliveries, offered to it under `hold`: no hook offers them again; those of them it marked that are `read`
1434
- * (printed in full) and delivered to `endpoint` are acked; how many it marked
1536
+ * (printed in full) and delivered to `endpoint` are acked, and with `deferred` (a background hook, whose text the harness hands the agent
1537
+ * later) still listed by {@link inbox} until that settles ({@link settle}) and marked delivered rather than read ({@link receipts}) until
1538
+ * the agent acks it itself; how many it marked
1435
1539
  */
1436
1540
  injected(seqs: unknown, hold: unknown, options?: {
1437
1541
  endpoint?: string;
1438
1542
  read?: unknown;
1543
+ deferred?: boolean;
1439
1544
  }): number;
1440
1545
  /**
1441
1546
  * whether a caller acts for the harness process holding the endpoint: any non-hook caller, a hook of that process (equal keys, or a bare and a
@@ -1451,6 +1556,14 @@ declare class RelayCore {
1451
1556
  * ${MESSAGE_SUFFIX_MIN} characters, such as its {@link shortId}. Null when there is none; invalid_params when the reference is ambiguous
1452
1557
  */
1453
1558
  delivered(endpoint: string, id: unknown): Envelope | null;
1559
+ /**
1560
+ * a message `endpoint` sent, by its id, a unique suffix of at least ${MESSAGE_SUFFIX_MIN} characters such as its {@link shortId}, or `last`
1561
+ * (the newest it sent), with when each recipient read it (acked), or when a background hook delivered it (acked by that showing, the agent
1562
+ * gets it with its next request, `delivered_at` and no `read_at`), and when a hook or channel showed it; a recipient still waiting for a
1563
+ * `swarm spawn` to join is named by the spawn's address. not_found when it sent no such message; invalid_params when a suffix names more
1564
+ * than one
1565
+ */
1566
+ receipts(endpoint: string, id: unknown): MessageReceipts;
1454
1567
  /**
1455
1568
  * another endpoint's message in a thread `endpoint` sent or got a message in, by its id or a unique suffix of at least ${MESSAGE_SUFFIX_MIN}
1456
1569
  * characters: enough to reply to it, never its body. Null when there is none; invalid_params when the suffix names more than one
@@ -1472,6 +1585,7 @@ declare class RelayCore {
1472
1585
  /** reading messages counts as receiving them for the causal hop */
1473
1586
  private received;
1474
1587
  hasUnread(endpoint: string, after?: number): boolean;
1588
+ /** marks these deliveries to `endpoint` read, one a background hook's showing acked ({@link injected}) read now too; how many were unread */
1475
1589
  ack(endpoint: string, seqs: unknown): number;
1476
1590
  /**
1477
1591
  * the first reply to a request this endpoint sent, delivered to it from an endpoint the request was delivered to (any, for one the hub
@@ -1499,6 +1613,8 @@ declare class RelayCore {
1499
1613
  }): AuditRow[];
1500
1614
  /** drops what expired: deliveries, message bodies past their grace, old guard state and old audit rows */
1501
1615
  sweep(): void;
1616
+ /** ends the hook presence of local agent endpoints whose harness process exited without a session_end, as one would */
1617
+ endExited(now?: number): void;
1502
1618
  /** an agent endpoint left offline drops a working or waiting status, which no one is left to keep current */
1503
1619
  private settleOffline;
1504
1620
  /**
@@ -1608,6 +1724,8 @@ declare class RelayCore {
1608
1724
  in?: string[];
1609
1725
  out?: string[];
1610
1726
  }): ScopeChange;
1727
+ /** the grant issuer's messages to the endpoint that spent it, sent since the grant, that it read, all but those among `unapplied`, oldest first */
1728
+ private readSteers;
1611
1729
  /**
1612
1730
  * a message needs both ends: for each `ep:` selector `widen` added on `target`, the other end's side is widened to admit `target` too when
1613
1731
  * `me` has authority over it, audited as its own widening; when that side still refuses, the change names it and the command that would fix it.
@@ -1695,8 +1813,9 @@ declare class RelayCore {
1695
1813
  /**
1696
1814
  * a spawn's handback to its spawner `me`, the grant's issuer, from the worker (the relay when it never joined): past scopes, pair limits and
1697
1815
  * the judge, since the spawner minted the grant and is the recipient. One per grant (null once sent), in the thread the two last wrote in,
1698
- * else one derived from the grant, naming by short id the spawner's messages the worker left unread; its hop follows the worker's causal
1699
- * hop, capped, and a full thread drops it (null). A worker that never joined or exited non-zero gets its `stderr` last, labelled, its
1816
+ * else one derived from the grant, naming by short id the spawner's messages the worker read, with when (one a background hook showed only
1817
+ * delivered, unless the worker then exited 0: its model had the next request), and those it left unread; its hop follows the worker's
1818
+ * causal hop, capped, and a full thread drops it (null). A worker that never joined or exited non-zero gets its `stderr` last, labelled, its
1700
1819
  * output cut from the front so both stay within ${LIMITS.handbackBytes} bytes
1701
1820
  */
1702
1821
  private handback;
@@ -1946,6 +2065,13 @@ type Delivery = Pick<SendResult["deliveries"][number], "address" | "offline" | "
1946
2065
  * when the broker named no state. With `late`, only those that will not see it at their next turn boundary
1947
2066
  */
1948
2067
  declare function reachLines(deliveries: Delivery[], late?: boolean): string[];
2068
+ /** a time, as ms or ISO, as `HH:MM:SS` in local time */
2069
+ declare function clock(at: number | string): string;
2070
+ /**
2071
+ * a sent message's recipients as lines, one each: when it read the message, else when a background hook delivered it, else when a hook or
2072
+ * channel showed it, else unread
2073
+ */
2074
+ declare function receiptLines(receipts: Pick<MessageReceipts, "recipients">): string[];
1949
2075
  /** a widening's result: what it added and clipped, the other ends it widened too, and those that still refuse with the fix */
1950
2076
  declare function widenText(change: ScopeChange, endpoint: string): string[];
1951
2077
  /** tool names collapsed in first-seen order, `Edit x3` for repeats */
@@ -2027,8 +2153,9 @@ declare function redactPii(text: string): string;
2027
2153
  /** what stands where a handback lost earlier output, on its own line */
2028
2154
  declare const HANDBACK_CUT = "[earlier output cut]";
2029
2155
  /**
2030
- * clean text's last `bytes` bytes: all of it when it fits and nothing was `dropped`, else from the first line that starts in reach (a
2031
- * character boundary when one line is longer than that) behind {@link HANDBACK_CUT} on its own line, when the cap has room for it
2156
+ * clean text's last `bytes` bytes: all of it when it fits and nothing was `dropped`, else from the first line that starts in reach behind
2157
+ * {@link HANDBACK_CUT} on its own line, when the cap has room for it; from a character boundary within the line cut instead when that line
2158
+ * alone is longer than the reach and starting at the next one would keep less than half of it
2032
2159
  */
2033
2160
  declare function keepLast(text: string, bytes: number, dropped?: boolean): string;
2034
2161
  /**
@@ -2295,4 +2422,4 @@ declare function sha256Hex(text: string): string;
2295
2422
  /** the UTF-8 byte length of text, the unit every byte limit counts in */
2296
2423
  declare function utf8Bytes(text: string): number;
2297
2424
 
2298
- export { ACCOUNT_HANDLE, ACTIVITY_KINDS, type ActivityKind, type ActivityRow, type Admission, type AskResult, type Attachment, type AuditRow, BROKER_CAPABILITIES, type BreakerAdmission, CAPABILITIES, CLI_NAME, type Claim, type CoreOptions, type CrossAccountGate, type CrossAccountLink, DEFAULT_IMPLEMENTATION_PREFIXES, DEFAULT_JUDGE_BUDGET, DEFAULT_JUDGE_EFFORT, type DataClass, ERROR_CODES, type EndpointKind, type EndpointRow, type EndpointView, type Envelope, type ErrorCode, FakeJudgeClient, GONE_PREFIX, GROUP_NAME, type GateContext, type GateVerdict, HANDBACK_CUT, HUMAN_HANDLE, HttpJudgeClient, type HttpJudgeOptions, JUDGE_BREAKER_DEFAULTS, JUDGE_CATEGORIES, JUDGE_EFFORTS, JUDGE_FALLBACK_NOTE, JUDGE_MAX_OUTPUT_TOKENS, JUDGE_PROMPT_VERSION, JUDGE_PROVIDERS, JUDGE_VERDICTS, type JudgeAuditDetail, type JudgeAuditEvent, type JudgeBoundary, JudgeBreaker, type JudgeBreakerOptions, type JudgeBudget, type JudgeCategory, type JudgeClient, type JudgeContext, type JudgeEffort, type JudgeHeld, type JudgeModelConfig, type JudgePolicy, type JudgeProvider, JudgeProviderError, type JudgeRedacted, type JudgeSide, type JudgeStats, type JudgeVerdict, LIMITS, MESSAGE_KINDS, type MessageKind, type MessageRow, OMITTED, PASS_GATE, PEEK, PREAMBLE, PREAMBLE_BRIEF, PRESET_LISTS, PROTOCOL, type PeekLevel, type PeekResult, type PeekSeen, type PeekState, type PeekTurn, type PeerView, type PiiHit, type Policy, type PolicyDefaults, type PolicyGroup, type PolicyRule, type PolicySubject, type Prefiltered, type Presence, REDACTION_KINDS, type Reach, type Redaction, RelayCore, RelayError, type RunJudgeInput, SCOPE_PRESETS, SERVER_FIELDS, SHORT_ID, STATUS_STATES, SYSTEM_HANDLE, type ScopeActor, type ScopeCap, type ScopeCaps, type ScopeChange, type ScopeDecision, type ScopeDirection, type ScopeEntry, type ScopeGroupIdentity, type ScopeGroupRules, type ScopeList, type ScopeNode, type ScopePolicyCap, type ScopePreset, type ScopeRules, type ScopeSubject, type ScopeView, type ScopeWorld, type SecretScanner, type Selector, type SendParams, type SendResult, type Sql, type SqlValue, type StatusState, type StopNudge, type StopWaitResult, type ThreadMessage, type Verdict, VerdictCache, type WhyResult, type XAllow, type XContact, XCounters, type XDelivered, type XEnvelopeFrom, XLIMITS, type XLimitConfig, type XLimits, type XListed, type XMessage, type XRelease, admits, allowlisted, applyRedactions, canonicalPolicy, cleanLabels, cleanText, clip, clipPasses, clipped, collapseTools, createSecretScanner, crossFleet, dayStart, declaredLimits, deliveryText, digestText, dropQuoted, effectiveSelector, expandRules, findPii, findSecret, gateRefusal, groupCapKey, handbackOutput, hasScope, honestSender, isLeaving, joinRedacted, judgeBoundary, judgeDetailForAgents, judgeFellBack, judgeModel, judgePrompt, keepLast, leavesFleet, matches, mergeVerdicts, narrower, newId, newJudgeStats, one, parseJudgePolicy, parsePolicy, parseVerdict, parseXMessage, peekLevel, policyRefusal, prefilter, presetOf, randomHex, reachLines, readText, redactPii, redactSecrets, redactionKind, relation, relationText, replyGrant, runJudge, sameSecret, scopeDecision, scopeLists, seqList, setImplementationPrefixes, sha256Hex, shortId, steerNote, stripControl, stripRelaySpans, stripSteerNote, unreachable, untilTomorrow, utf8Bytes, validHandle, validHarness, validSelector, widenText, wrapMessage, wrapPeek, xlimits };
2425
+ export { ACCOUNT_HANDLE, ACTIVITY_KINDS, type ActivityKind, type ActivityRow, type Admission, type AskResult, type Attachment, type AuditRow, BROKER_CAPABILITIES, type BreakerAdmission, CAPABILITIES, CLI_NAME, type Claim, type CoreOptions, type CrossAccountGate, type CrossAccountLink, DEFAULT_IMPLEMENTATION_PREFIXES, DEFAULT_JUDGE_BUDGET, DEFAULT_JUDGE_EFFORT, type DataClass, ERROR_CODES, type EndpointKind, type EndpointRow, type EndpointView, type Envelope, type ErrorCode, FakeJudgeClient, GONE_PREFIX, GROUP_NAME, type GateContext, type GateVerdict, HANDBACK_CUT, HUMAN_HANDLE, HttpJudgeClient, type HttpJudgeOptions, JUDGE_BREAKER_DEFAULTS, JUDGE_CATEGORIES, JUDGE_EFFORTS, JUDGE_FALLBACK_NOTE, JUDGE_MAX_OUTPUT_TOKENS, JUDGE_PROMPT_VERSION, JUDGE_PROVIDERS, JUDGE_VERDICTS, type JudgeAuditDetail, type JudgeAuditEvent, type JudgeBoundary, JudgeBreaker, type JudgeBreakerOptions, type JudgeBudget, type JudgeCategory, type JudgeClient, type JudgeContext, type JudgeEffort, type JudgeHeld, type JudgeModelConfig, type JudgePolicy, type JudgeProvider, JudgeProviderError, type JudgeRedacted, type JudgeSide, type JudgeStats, type JudgeVerdict, LIMITS, MESSAGE_KINDS, type MessageKind, type MessageReceipts, type MessageRow, OMITTED, PASS_GATE, PEEK, PREAMBLE, PREAMBLE_BRIEF, PRESET_LISTS, PROTOCOL, type PeekLevel, type PeekResult, type PeekSeen, type PeekState, type PeekTurn, type PeerView, type PiiHit, type Policy, type PolicyDefaults, type PolicyGroup, type PolicyRule, type PolicySubject, type Prefiltered, type Presence, REDACTION_KINDS, type Reach, type Redaction, RelayCore, RelayError, type RunJudgeInput, SCOPE_PRESETS, SERVER_FIELDS, SHORT_ID, STATUS_STATES, SYSTEM_HANDLE, type ScopeActor, type ScopeCap, type ScopeCaps, type ScopeChange, type ScopeDecision, type ScopeDirection, type ScopeEntry, type ScopeGroupIdentity, type ScopeGroupRules, type ScopeList, type ScopeNode, type ScopePolicyCap, type ScopePreset, type ScopeRules, type ScopeSubject, type ScopeView, type ScopeWorld, type SecretScanner, type Selector, type SendParams, type SendResult, type Sql, type SqlValue, type StatusState, type StopNudge, type StopWaitResult, TITLE_CHARS, type ThreadMessage, type Verdict, VerdictCache, type WhyResult, type XAllow, type XContact, XCounters, type XDelivered, type XEnvelopeFrom, XLIMITS, type XLimitConfig, type XLimits, type XListed, type XMessage, type XRelease, admits, allowlisted, applyRedactions, canonicalPolicy, cleanLabels, cleanText, clip, clipPasses, clipped, clock, collapseTools, createSecretScanner, crossFleet, dayStart, declaredLimits, deliveryText, digestText, dropQuoted, effectiveSelector, expandRules, findPii, findSecret, gateRefusal, groupCapKey, handbackOutput, hasScope, honestSender, isLeaving, joinRedacted, judgeBoundary, judgeDetailForAgents, judgeFellBack, judgeModel, judgePrompt, keepLast, leavesFleet, matches, mergeVerdicts, narrower, newId, newJudgeStats, one, parseJudgePolicy, parsePolicy, parseVerdict, parseXMessage, peekLevel, policyRefusal, prefilter, presetOf, randomHex, reachLines, readText, receiptLines, redactPii, redactSecrets, redactionKind, relation, relationText, replyGrant, runJudge, sameSecret, scopeDecision, scopeLists, seqList, setImplementationPrefixes, sha256Hex, shortId, steerNote, stripControl, stripRelaySpans, stripSteerNote, unreachable, untilTomorrow, utf8Bytes, validHandle, validHarness, validSelector, widenText, wrapMessage, wrapPeek, xlimits };