@sema-agent/core 7.56.0 → 7.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +541 -0
- package/LICENSE +1 -1
- package/dist/agents/org-delivery-gate.js +3 -4
- package/dist/agents/peer-held-queue.d.ts +69 -49
- package/dist/agents/peer-held-queue.js +255 -106
- package/dist/agents/peer-session-drain.js +63 -43
- package/dist/agents/send-message-tool.js +3 -2
- package/dist/agents/subagent.js +8 -8
- package/dist/brain/anthropic.js +16 -8
- package/dist/brain/open-responses.js +10 -8
- package/dist/brain/openai.js +11 -9
- package/dist/brain/provider-dialect.d.ts +3 -2
- package/dist/brain/provider-dialect.js +2 -2
- package/dist/brain/request-params.d.ts +6 -0
- package/dist/brain/request-params.js +14 -1
- package/dist/brain/stream-engine.d.ts +9 -0
- package/dist/brain/stream-engine.js +24 -10
- package/dist/brain/stream-shared.d.ts +5 -4
- package/dist/brain/stream-shared.js +4 -4
- package/dist/core/allow-entry.d.ts +3 -10
- package/dist/core/allow-entry.js +17 -25
- package/dist/core/approval-object.d.ts +21 -5
- package/dist/core/approval-object.js +44 -17
- package/dist/core/ask-origin.d.ts +2 -2
- package/dist/core/auto-compaction.d.ts +71 -6
- package/dist/core/auto-compaction.js +42 -26
- package/dist/core/background-agent-store.d.ts +6 -2
- package/dist/core/background-agent-store.js +1 -1
- package/dist/core/checkpoint-requirements.d.ts +7 -1
- package/dist/core/checkpoint-requirements.js +2 -0
- package/dist/core/checkpoint-store.d.ts +17 -29
- package/dist/core/checkpoint-store.js +19 -11
- package/dist/core/cross-boundary-taint.js +4 -2
- package/dist/core/engine-notice.d.ts +3 -2
- package/dist/core/engine-notice.js +6 -0
- package/dist/core/file-history-store.d.ts +14 -8
- package/dist/core/file-history-store.js +14 -4
- package/dist/core/gate-lanes.js +2 -3
- package/dist/core/hooks.d.ts +2 -2
- package/dist/core/mailbox-store.d.ts +62 -1
- package/dist/core/mailbox-store.js +59 -0
- package/dist/core/mcp-name-judge.d.ts +46 -0
- package/dist/core/mcp-name-judge.js +61 -0
- package/dist/core/mcp-probe.d.ts +1 -1
- package/dist/core/mcp-probe.js +2 -5
- package/dist/core/mcp.js +10 -30
- package/dist/core/memory-engine/consolidation-driver.js +4 -3
- package/dist/core/memory-engine/distiller.js +26 -17
- package/dist/core/memory-engine/layout.js +5 -2
- package/dist/core/park-selfcheck.js +2 -0
- package/dist/core/permission-rule-consent.d.ts +5 -3
- package/dist/core/permission-rule-consent.js +20 -15
- package/dist/core/permission-rule-model.d.ts +43 -2
- package/dist/core/permission-rule-model.js +52 -8
- package/dist/core/permission-rule-org.js +1 -1
- package/dist/core/protocol-http-auth.d.ts +16 -2
- package/dist/core/protocol-http-auth.js +42 -11
- package/dist/core/protocol-naming.d.ts +20 -0
- package/dist/core/protocol-naming.js +20 -3
- package/dist/core/read-only-shell.d.ts +20 -5
- package/dist/core/read-only-shell.js +29 -14
- package/dist/core/remote-env.d.ts +37 -1
- package/dist/core/remote-env.js +11 -0
- package/dist/core/resume-startup-cause.d.ts +24 -2
- package/dist/core/resume-startup-cause.js +20 -1
- package/dist/core/roles.js +50 -7
- package/dist/core/runner/compact-session.d.ts +10 -0
- package/dist/core/runner/compact-session.js +202 -0
- package/dist/core/runner/compaction-call-options.d.ts +2 -2
- package/dist/core/runner/compaction-seams.d.ts +21 -1
- package/dist/core/runner/compaction-seams.js +11 -8
- package/dist/core/runner/contracts.d.ts +23 -16
- package/dist/core/runner/derived-route-fallback.d.ts +13 -0
- package/dist/core/runner/derived-route-fallback.js +6 -0
- package/dist/core/runner/env-capability-census.d.ts +3 -2
- package/dist/core/runner/env-capability-census.js +9 -3
- package/dist/core/runner/permission-rule-lanes.d.ts +7 -7
- package/dist/core/runner/permission-rule-lanes.js +17 -11
- package/dist/core/runner/prepare-acquire-reconcile.d.ts +3 -0
- package/dist/core/runner/prepare-acquire-reconcile.js +2 -1
- package/dist/core/runner/prepare-config-doors.d.ts +6 -0
- package/dist/core/runner/prepare-config-doors.js +4 -3
- package/dist/core/runner/prepare-hands-readface.d.ts +3 -1
- package/dist/core/runner/prepare-hands-readface.js +2 -0
- package/dist/core/runner/prepare-park-ask.d.ts +2 -0
- package/dist/core/runner/prepare-park-ask.js +2 -2
- package/dist/core/runner/prepare-policy-chain.js +2 -4
- package/dist/core/runner/prepare-task.d.ts +4 -1
- package/dist/core/runner/prepare-task.js +23 -27
- package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +13 -0
- package/dist/core/runner/prepare-tool-disclosure-mount.js +13 -7
- package/dist/core/runner/rewind-files.d.ts +2 -5
- package/dist/core/runner/rewind-files.js +9 -51
- package/dist/core/runner/run-leg.js +10 -21
- package/dist/core/runner/run-notification-lane.d.ts +2 -0
- package/dist/core/runner/run-notification-lane.js +2 -2
- package/dist/core/runner/run-recovery-lanes.js +3 -4
- package/dist/core/runner/run-settle-and-teardown.js +5 -11
- package/dist/core/runner/run-terminal-adoption.js +3 -4
- package/dist/core/runner/run-turn-boundary.js +3 -5
- package/dist/core/runner/runtask.d.ts +8 -1
- package/dist/core/runner/runtask.js +28 -34
- package/dist/core/runner/send-message-continuation.js +1 -1
- package/dist/core/runner/session-verb-doors.d.ts +28 -0
- package/dist/core/runner/session-verb-doors.js +48 -0
- package/dist/core/runner/session-write-ledger.d.ts +30 -0
- package/dist/core/runner/session-write-ledger.js +101 -0
- package/dist/core/runner/stream-settle-backstop.js +11 -14
- package/dist/core/runner/sub-agent-deny-lists.d.ts +25 -3
- package/dist/core/runner/sub-agent-deny-lists.js +21 -10
- package/dist/core/runner/teardown-bounded.d.ts +1 -0
- package/dist/core/runner/teardown-bounded.js +1 -0
- package/dist/core/runner-deps.d.ts +2 -2
- package/dist/core/secret-env.d.ts +4 -0
- package/dist/core/secret-env.js +3 -0
- package/dist/core/session-policy-store.d.ts +7 -2
- package/dist/core/session-policy-store.js +1 -1
- package/dist/core/shell-wrapper-projection.d.ts +1 -1
- package/dist/core/store-contracts/background-agent-store-contract.js +1 -0
- package/dist/core/store-contracts/checkpoint-store-contract.js +2 -1
- package/dist/core/store-contracts/file-history-store-contract.js +9 -0
- package/dist/core/store-contracts/mailbox-store-contract.js +49 -1
- package/dist/core/task-registry-agent.d.ts +8 -4
- package/dist/core/task-registry-agent.js +14 -8
- package/dist/core/task-registry-shared.d.ts +35 -6
- package/dist/core/task-registry-shared.js +11 -0
- package/dist/core/task-registry.d.ts +1 -1
- package/dist/core/task-registry.js +1 -1
- package/dist/core/tool-catalog-entries.d.ts +6 -0
- package/dist/core/tool-catalog-entries.js +1 -0
- package/dist/core/tool-name-aliases.d.ts +32 -2
- package/dist/core/tool-name-aliases.js +23 -0
- package/dist/core/tool-name-readings.d.ts +4 -0
- package/dist/core/tool-name-readings.js +1 -0
- package/dist/core/tool-policy.d.ts +10 -6
- package/dist/core/tool-policy.js +4 -7
- package/dist/core/trace.d.ts +4 -1
- package/dist/core/trace.js +2 -1
- package/dist/core/untrusted-egress.d.ts +20 -0
- package/dist/core/untrusted-egress.js +77 -3
- package/dist/engine/harness/agent-harness.d.ts +7 -5
- package/dist/engine/harness/agent-harness.js +11 -2
- package/dist/engine/loop/agent-loop.d.ts +1 -4
- package/dist/engine/loop/agent-loop.js +26 -4
- package/dist/index.d.ts +12 -7
- package/dist/index.js +7 -4
- package/dist/internal/harness.d.ts +2 -1
- package/dist/internal/harness.js +1 -0
- package/dist/primitives/credential-ref.d.ts +89 -0
- package/dist/primitives/credential-ref.js +125 -0
- package/dist/primitives/index.d.ts +2 -0
- package/dist/primitives/index.js +1 -0
- package/dist/stores/file/checkpoint-store.d.ts +0 -4
- package/dist/stores/file/checkpoint-store.js +0 -1
- package/dist/stores/file/file-history-store.js +12 -4
- package/dist/stores/file/mailbox-store.d.ts +2 -1
- package/dist/stores/file/mailbox-store.js +26 -1
- package/dist/tools/fs/bash-readonly-classifier.d.ts +15 -19
- package/dist/tools/fs/bash-readonly-classifier.js +5 -10
- package/dist/tools/fs/fs-bash.js +10 -3
- package/dist/tools/fs/pattern-kill-guard.d.ts +3 -2
- package/dist/tools/fs/pattern-kill-guard.js +68 -36
- package/dist/tools/fs/shell-read-check.d.ts +10 -0
- package/dist/tools/fs/shell-read-check.js +7 -1
- package/dist/tools/fs/shell-read-identity.d.ts +12 -9
- package/dist/tools/fs/shell-read-identity.js +9 -4
- package/dist/tools/web.js +9 -7
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +79 -3
- package/wire-changes/core-stores-sql@7.57.0.json +29 -0
- package/wire-changes/core@7.57.0.json +1487 -0
- package/wire-changes/mods-kernel@7.57.0.json +29 -0
- package/wire-changes/primitives@7.57.0.json +245 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isCredentialHeaderName } from "../core/secret-env.js";
|
|
1
|
+
import { isCredentialHeaderName, namesASecret } from "../core/secret-env.js";
|
|
2
2
|
import { isPlainRecord } from "../core/mods/kernel/receive.js";
|
|
3
3
|
import { BrainError } from "./errors.js";
|
|
4
4
|
export const OPENAI_RESERVED = new Set([
|
|
@@ -129,6 +129,19 @@ export function mergeHeaders(...bags) {
|
|
|
129
129
|
}
|
|
130
130
|
return out;
|
|
131
131
|
}
|
|
132
|
+
export function declaredRequestValues(bags, stated, key) {
|
|
133
|
+
const out = new Set();
|
|
134
|
+
for (const bag of bags)
|
|
135
|
+
for (const value of Object.values(bag ?? {}))
|
|
136
|
+
if (typeof value === "string" && value !== "")
|
|
137
|
+
out.add(value);
|
|
138
|
+
for (const value of stated)
|
|
139
|
+
if (value !== undefined && value !== "")
|
|
140
|
+
out.add(value);
|
|
141
|
+
if (key !== undefined && namesASecret(key))
|
|
142
|
+
out.add(key);
|
|
143
|
+
return [...out];
|
|
144
|
+
}
|
|
132
145
|
export const OUTPUT_CAP_KEYS = {
|
|
133
146
|
openai: ["max_tokens", "max_completion_tokens"],
|
|
134
147
|
anthropic: ["max_tokens"],
|
|
@@ -109,6 +109,12 @@ export declare const WAITING_FIRST_TOKEN_EVERY_MS = 30000;
|
|
|
109
109
|
export interface SSERequest {
|
|
110
110
|
url: string;
|
|
111
111
|
headers: Record<string, string>;
|
|
112
|
+
/**
|
|
113
|
+
* Every header value this request was DECLARED with — the model entry's, the deployment's and the call's header bags as the
|
|
114
|
+
* route kept them, and the key — as the adapter built them. An endpoint's words lose these before the engine carries them
|
|
115
|
+
* ({@link remoteWords}); the engine's own protocol words (`content-type`, the version and the beta list it chose) are not declared.
|
|
116
|
+
*/
|
|
117
|
+
declared: readonly string[];
|
|
112
118
|
body: string;
|
|
113
119
|
/**
|
|
114
120
|
* The per-request output cap this request ACTUALLY carries on the wire, when the adapter sets one
|
|
@@ -194,6 +200,9 @@ export interface StreamControls {
|
|
|
194
200
|
* raising the provider's own in-band failure from `finalize`: the terminal message the engine mints for that
|
|
195
201
|
* failure keeps it (the text the provider streamed, the usage it reported), rather than an empty shell. */
|
|
196
202
|
keepOutput(output: KeptOutput): void;
|
|
203
|
+
/** The provider's own words in an in-band failure, as the engine carries them: this attempt's request ({@link SSERequest.declared}) taken
|
|
204
|
+
* out, then the credential vocabulary. A judge that reads the frame (the condition, the input-too-long anchor) reads the raw words. */
|
|
205
|
+
providerWords(words: string): string;
|
|
197
206
|
}
|
|
198
207
|
/** The output a parser hands over with {@link StreamControls.keepOutput}. */
|
|
199
208
|
export interface KeptOutput {
|
|
@@ -9,7 +9,7 @@ import { FAST_MAX_BACKOFF_MS, providerWaitHint, retryBackoffMs } from "./retry.j
|
|
|
9
9
|
import { bindBrainStatusEmitter, emitBrainStatus, emitBrainTelemetry, reportExtraBodyDropped } from "./status-sink.js";
|
|
10
10
|
import { createAttemptController, firstByteWindow, refuseRetiredTimeoutKnobs, resolveFirstByteRetries, resolveWatchdogMs, STALL_FIRST_BYTE_MS, STALL_FIRST_TOKEN_MS, STALL_IDLE_MS, } from "./timeout.js";
|
|
11
11
|
import { readMember, thrownText } from "../core/thrown-value.js";
|
|
12
|
-
import {
|
|
12
|
+
import { remoteWords } from "../core/protocol-http-auth.js";
|
|
13
13
|
const DEFAULT_MAX_RETRIES = 10;
|
|
14
14
|
const MAX_RETRIES_ENV_CEILING = 15;
|
|
15
15
|
const MAX_RETRIES_ENV = "SEMA_MAX_RETRIES";
|
|
@@ -294,6 +294,13 @@ export function runStreamingBrain(args) {
|
|
|
294
294
|
let attemptPartial;
|
|
295
295
|
let attemptOutput;
|
|
296
296
|
let routeSeen;
|
|
297
|
+
let declaredSeen = [];
|
|
298
|
+
const echoCarried = (value, field) => {
|
|
299
|
+
if (remoteWords(value, declaredSeen) === value)
|
|
300
|
+
return value;
|
|
301
|
+
emitBrainTelemetry({ kind: "echo_dropped", field });
|
|
302
|
+
return undefined;
|
|
303
|
+
};
|
|
297
304
|
void run()
|
|
298
305
|
.catch((err) => {
|
|
299
306
|
const aborted = signal?.aborted === true || isAbortError(err);
|
|
@@ -313,7 +320,8 @@ export function runStreamingBrain(args) {
|
|
|
313
320
|
errorMsg.usage = kept.usage;
|
|
314
321
|
}
|
|
315
322
|
errorMsg.stopReason = aborted ? "aborted" : "error";
|
|
316
|
-
|
|
323
|
+
const said = thrownText(err);
|
|
324
|
+
errorMsg.errorMessage = brainErrorCodeOf(err) !== undefined ? said : remoteWords(said, declaredSeen);
|
|
317
325
|
const failure = aborted ? {} : thrownBrainFailure(err);
|
|
318
326
|
if (failure.code !== undefined) {
|
|
319
327
|
if (failure.errorKind !== undefined)
|
|
@@ -328,13 +336,17 @@ export function runStreamingBrain(args) {
|
|
|
328
336
|
const statusNum = typeof status === "number" ? status : undefined;
|
|
329
337
|
if (namesTheFailure(statusNum))
|
|
330
338
|
errorMsg.apiErrorStatus = statusNum;
|
|
331
|
-
|
|
332
|
-
|
|
339
|
+
const requestId = requestIdSeen === undefined ? undefined : echoCarried(requestIdSeen, "requestId");
|
|
340
|
+
if (requestId !== undefined)
|
|
341
|
+
errorMsg.requestId = requestId;
|
|
333
342
|
if (routeSeen !== undefined)
|
|
334
343
|
errorMsg.apiErrorRoute = routeSeen;
|
|
335
344
|
const providerCode = readMember(err, "providerCode");
|
|
336
|
-
if ((statusNum === undefined || refusedTheRequest(statusNum)) && typeof providerCode === "string" && isProviderEchoShape(providerCode))
|
|
337
|
-
|
|
345
|
+
if ((statusNum === undefined || refusedTheRequest(statusNum)) && typeof providerCode === "string" && isProviderEchoShape(providerCode)) {
|
|
346
|
+
const apiErrorCode = echoCarried(providerCode, "apiErrorCode");
|
|
347
|
+
if (apiErrorCode !== undefined)
|
|
348
|
+
errorMsg.apiErrorCode = apiErrorCode;
|
|
349
|
+
}
|
|
338
350
|
const condition = readMember(err, "providerCondition");
|
|
339
351
|
const source = readMember(err, "providerConditionSource");
|
|
340
352
|
if (isProviderCondition(condition) && isProviderConditionSource(source)) {
|
|
@@ -370,6 +382,7 @@ export function runStreamingBrain(args) {
|
|
|
370
382
|
r.onCommitted?.();
|
|
371
383
|
reportRequestStrip(model, r);
|
|
372
384
|
routeSeen = admittedRequestRoute(r.route);
|
|
385
|
+
declaredSeen = r.declared;
|
|
373
386
|
};
|
|
374
387
|
commit(req);
|
|
375
388
|
const dialect = resolveProviderDialect(req.url, model.api);
|
|
@@ -475,7 +488,7 @@ export function runStreamingBrain(args) {
|
|
|
475
488
|
}
|
|
476
489
|
else if (classifyFetchThrow(e) === "request_unbuildable") {
|
|
477
490
|
cc.dispose();
|
|
478
|
-
throw new BrainError("invalid_request", `${httpLabel} request could not be built: ${
|
|
491
|
+
throw new BrainError("invalid_request", `${httpLabel} request could not be built: ${remoteWords(stripUrlCredentials(thrownText(e)), req.declared)}`);
|
|
479
492
|
}
|
|
480
493
|
else {
|
|
481
494
|
netErr = e;
|
|
@@ -634,14 +647,14 @@ export function runStreamingBrain(args) {
|
|
|
634
647
|
continue;
|
|
635
648
|
}
|
|
636
649
|
if (netErr) {
|
|
637
|
-
const chain = describeNetworkError(netErr);
|
|
650
|
+
const chain = remoteWords(describeNetworkError(netErr), req.declared);
|
|
638
651
|
const proxyRefusal = connectReading?.proxyRefusal;
|
|
639
652
|
throw new BrainError("network", proxyRefusal === undefined ? chain : `${proxyTunnelRefusedSentence(proxyRefusal.status)} — ${chain}`, undefined, { apiError: true });
|
|
640
653
|
}
|
|
641
654
|
if (r !== undefined && errorBody === undefined)
|
|
642
655
|
errorBody = await readErrorBody(r, signal);
|
|
643
656
|
const terminalVerdict = judge();
|
|
644
|
-
const detail = errorBody
|
|
657
|
+
const detail = errorBody === undefined ? "" : remoteWords(errorBody.endedNormally ? errorBody.text : errorBody.text.replace(/\S+$/u, ""), req.declared);
|
|
645
658
|
const status = r?.status ?? 0;
|
|
646
659
|
const itl = terminalVerdict?.inputTooLong ?? "no";
|
|
647
660
|
const providerCode = refusedTheRequest(status) ? readProviderErrorCode(errorBody) : undefined;
|
|
@@ -737,6 +750,7 @@ export function runStreamingBrain(args) {
|
|
|
737
750
|
keepOutput: (output) => {
|
|
738
751
|
attemptOutput = output;
|
|
739
752
|
},
|
|
753
|
+
providerWords: (words) => remoteWords(words, req.declared),
|
|
740
754
|
});
|
|
741
755
|
const INBAND_OVERLOAD_WARNING = "API overloaded mid-response. The response above may be incomplete.";
|
|
742
756
|
const onLineOverloadTierA = (line) => {
|
|
@@ -761,7 +775,7 @@ export function runStreamingBrain(args) {
|
|
|
761
775
|
throw e;
|
|
762
776
|
failure = {
|
|
763
777
|
kind: "connection",
|
|
764
|
-
err: new BrainError("network", `mid-stream read failed: ${describeNetworkError(e)}`, undefined, { apiError: true }),
|
|
778
|
+
err: new BrainError("network", `mid-stream read failed: ${remoteWords(describeNetworkError(e), req.declared)}`, undefined, { apiError: true }),
|
|
765
779
|
};
|
|
766
780
|
break readLoop;
|
|
767
781
|
}
|
|
@@ -17,9 +17,10 @@ import { type AssistantMessage, type ModelOutputInvalid, type StreamDropCause }
|
|
|
17
17
|
/** The warning (the reference's former words; later replaced by a resume request) stamped beside `partialFinalized` when a stream ended before its close — by the
|
|
18
18
|
* mid-stream promotion (the body threw) and by the parsers' own finalize (content arrived, no finish frame). */
|
|
19
19
|
export declare const STREAM_TORN_WARNING = "Connection closed mid-response. The response above may be incomplete.";
|
|
20
|
-
/** A provider word as a stream-end seat carries it: the word in the provider echo shape
|
|
21
|
-
*
|
|
22
|
-
|
|
20
|
+
/** A provider word as a stream-end seat carries it: the word in the provider echo shape that the stream's mint
|
|
21
|
+
* (`providerWords`) leaves as it is, else `?` — the fact that a word arrived survives, bytes outside the shape or holding
|
|
22
|
+
* what the request was declared with do not (the echo discipline: dropped, never rewritten). */
|
|
23
|
+
export declare function echoFoldedWord(raw: string, providerWords: (words: string) => string): string;
|
|
23
24
|
/**
|
|
24
25
|
* The record of what arrived after a stream's first terminal statement — the one ordering rule both wire parsers
|
|
25
26
|
* apply: the first ending (a finish / stop reason, a message stop, an in-band error) decides the turn; after it the
|
|
@@ -27,7 +28,7 @@ export declare function echoFoldedWord(raw: string): string;
|
|
|
27
28
|
* here. Rides `AssistantMessage.streamEndIgnored` (the first refused frame's kind and word, how many came); the
|
|
28
29
|
* Runner announces it (`route.stream_end_ignored`).
|
|
29
30
|
*/
|
|
30
|
-
export declare function createStreamEndLedger(): {
|
|
31
|
+
export declare function createStreamEndLedger(providerWords: (words: string) => string): {
|
|
31
32
|
note(statement: StreamEndStatement, word: string): void;
|
|
32
33
|
readonly ignored: AssistantMessage["streamEndIgnored"];
|
|
33
34
|
};
|
|
@@ -3,14 +3,14 @@ import { inspectDegenerate } from "./repetition.js";
|
|
|
3
3
|
import { emitBrainTelemetry } from "./status-sink.js";
|
|
4
4
|
import { stampStreamDrops } from "../internal/llm.js";
|
|
5
5
|
export const STREAM_TORN_WARNING = "Connection closed mid-response. The response above may be incomplete.";
|
|
6
|
-
export function echoFoldedWord(raw) {
|
|
7
|
-
return isProviderEchoShape(raw) ? raw : "?";
|
|
6
|
+
export function echoFoldedWord(raw, providerWords) {
|
|
7
|
+
return isProviderEchoShape(raw) && providerWords(raw) === raw ? raw : "?";
|
|
8
8
|
}
|
|
9
|
-
export function createStreamEndLedger() {
|
|
9
|
+
export function createStreamEndLedger(providerWords) {
|
|
10
10
|
let ignored;
|
|
11
11
|
return {
|
|
12
12
|
note(statement, word) {
|
|
13
|
-
ignored = ignored === undefined ? { statement, word: echoFoldedWord(word), frames: 1 } : { ...ignored, frames: ignored.frames + 1 };
|
|
13
|
+
ignored = ignored === undefined ? { statement, word: echoFoldedWord(word, providerWords), frames: 1 } : { ...ignored, frames: ignored.frames + 1 };
|
|
14
14
|
},
|
|
15
15
|
get ignored() {
|
|
16
16
|
return ignored;
|
|
@@ -73,15 +73,6 @@ export type CompiledAllowEntry = {
|
|
|
73
73
|
readonly kind: "domain";
|
|
74
74
|
readonly rule: PersistedRule;
|
|
75
75
|
};
|
|
76
|
-
/**
|
|
77
|
-
* THE near-miss reading of a tool name the judgment calls current: the spelling the run mounts when the run mounts no tool of this
|
|
78
|
-
* exact name but one that differs from it only by case (`BASH` beside `Bash`), else `undefined`. Two kinds of name are never a near
|
|
79
|
-
* miss, because the roster they would be read against is not the final one: an engine tool's (the engine mounts some of its tools
|
|
80
|
-
* after a list is read) and a protocol tool's (a refresh mounts and unmounts them while the run goes on) — in both the exact
|
|
81
|
-
* spelling can be the right one by the time it matters. Every station that asks "is this name a case twin of a mounted tool" reads
|
|
82
|
-
* this answer: an entry list ({@link readRuleEntry}), a tool policy's name lists (the prepare-time policy-name audit).
|
|
83
|
-
*/
|
|
84
|
-
export declare function nearMissSpellingOf(name: string, roster: ReadonlySet<string>): string | undefined;
|
|
85
76
|
/**
|
|
86
77
|
* THE universe a ROOT-facing tool-name list is read against: every name the engine's catalog carries, and the names the reader knows
|
|
87
78
|
* are carried (`carried`). A list is root-facing when it bounds the tools of the run that holds it — the run's own `allowTools` /
|
|
@@ -135,7 +126,9 @@ export declare const AUTO_STRIPPED_PROGRAMS: readonly ["python", "python3", "pyt
|
|
|
135
126
|
* disclosed.
|
|
136
127
|
*/
|
|
137
128
|
export declare function autoStripped(compiled: CompiledAllowEntry, call: ToolCallRequest | undefined): boolean;
|
|
138
|
-
/** {@link autoStripped} over a person's stored ALLOW row
|
|
129
|
+
/** {@link autoStripped} over a person's stored ALLOW row, read as its list twin ({@link entryOfRow}): a row is stripped exactly when
|
|
130
|
+
* the entry spelling the same rule is (a deny / ask row never is; a row no grammar reads has no twin and is not stripped here — the
|
|
131
|
+
* lane reads no allow it cannot parse). */
|
|
139
132
|
export declare function autoStrippedRule(rule: PersistedRule, call: ToolCallRequest): boolean;
|
|
140
133
|
/** One row a list matcher reads. */
|
|
141
134
|
export interface AllowEntryRow {
|
package/dist/core/allow-entry.js
CHANGED
|
@@ -2,11 +2,11 @@ import { heldBy } from "./tool-name-readings.js";
|
|
|
2
2
|
import { ruleIssue } from "./permission-rules.js";
|
|
3
3
|
import { isCoveringRuleName, ruleNameCovers } from "./namespaced-rule-name.js";
|
|
4
4
|
import { ruleSpellingOf } from "./permission-rule-syntax.js";
|
|
5
|
-
import {
|
|
5
|
+
import { splitNamespacedName, toolNameFaultOf } from "./tool-name-aliases.js";
|
|
6
6
|
import { catalogToolNames, isShellFaceToolName } from "./tool-registry.js";
|
|
7
7
|
import { ALLOW_RULE_MEMBERS } from "./allow-rule-shadow.js";
|
|
8
8
|
import { callHostTargetOf } from "./host-target.js";
|
|
9
|
-
import {
|
|
9
|
+
import { adjudicatePersistedDomainRules, wholeToolRuleReaches, adjudicatePersistedNameRules, commandCoverageOf, laneToolNamesOf, ruleContentOf, noticeRuleText, parseRuleText, programRunReachOf, ruleToolGrammarOf, commandPatternMayRun } from "./permission-rule-model.js";
|
|
10
10
|
import { pathRuleLaneAnswer } from "./runner/permission-rule-lanes.js";
|
|
11
11
|
import { callTargetReadingOf } from "./effective-path-target.js";
|
|
12
12
|
import { splitShellCompoundSegments } from "../tools/fs/bash-readonly-classifier.js";
|
|
@@ -25,30 +25,12 @@ export function nonListValueLabel(read) {
|
|
|
25
25
|
function ruleRowOf(parsed) {
|
|
26
26
|
return { ...parsed, scope: { kind: "global" }, adds: [{ dot: { actor: "list", counter: 1 }, origin: "user", createdAt: "" }] };
|
|
27
27
|
}
|
|
28
|
-
function mountedSpellingOf(name, roster) {
|
|
29
|
-
const folded = name.toLowerCase();
|
|
30
|
-
for (const mounted of roster)
|
|
31
|
-
if (mounted.toLowerCase() === folded)
|
|
32
|
-
return mounted;
|
|
33
|
-
return undefined;
|
|
34
|
-
}
|
|
35
|
-
export function nearMissSpellingOf(name, roster) {
|
|
36
|
-
if (roster.has(name) || catalogToolNames().has(name) || splitNamespacedName(name) !== undefined)
|
|
37
|
-
return undefined;
|
|
38
|
-
return mountedSpellingOf(name, roster);
|
|
39
|
-
}
|
|
40
28
|
export function rootFacingToolNames(carried) {
|
|
41
29
|
return new Set([...catalogToolNames(), ...carried]);
|
|
42
30
|
}
|
|
43
31
|
function toolNameReading(name, roster, behavior) {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
return { kind: "inert", reason: "tool_name", verdict: verdict.kind };
|
|
47
|
-
if (verdict.kind !== "current") {
|
|
48
|
-
return { kind: "inert", reason: "tool_name", verdict: verdict.kind, guidance: verdict.guidance, ...(verdict.currentName !== undefined ? { currentName: verdict.currentName } : {}) };
|
|
49
|
-
}
|
|
50
|
-
const mounted = nearMissSpellingOf(name, roster);
|
|
51
|
-
return mounted === undefined ? undefined : { kind: "inert", reason: "near_miss", currentName: mounted };
|
|
32
|
+
const fault = toolNameFaultOf(name, roster, behavior);
|
|
33
|
+
return fault === undefined ? undefined : { kind: "inert", ...fault };
|
|
52
34
|
}
|
|
53
35
|
export function readRuleEntry(raw, behavior, roster) {
|
|
54
36
|
const inert = (reason) => ({ kind: "inert", reason });
|
|
@@ -165,12 +147,22 @@ export function autoStripped(compiled, call) {
|
|
|
165
147
|
}
|
|
166
148
|
}
|
|
167
149
|
export function autoStrippedRule(rule, call) {
|
|
168
|
-
|
|
150
|
+
if (rule.behavior !== "allow")
|
|
151
|
+
return false;
|
|
152
|
+
const entry = entryOfRow(rule);
|
|
153
|
+
return entry !== undefined && autoStripped(entry, call);
|
|
154
|
+
}
|
|
155
|
+
function entryOfRow(rule) {
|
|
156
|
+
if (rule.match === "tool")
|
|
157
|
+
return isShellFaceToolName(rule.tool) ? { kind: "shell", name: rule.tool } : { kind: "tool", name: rule.tool };
|
|
158
|
+
const grammar = ruleToolGrammarOf(rule.tool);
|
|
159
|
+
return grammar === undefined ? undefined : { kind: grammar, rule };
|
|
169
160
|
}
|
|
170
161
|
const NO_LEADS = new Map();
|
|
171
162
|
function nameRowReaches(compiled, req) {
|
|
172
|
-
|
|
173
|
-
|
|
163
|
+
if (compiled.kind === "tool" || compiled.kind === "shell")
|
|
164
|
+
return wholeToolRuleReaches(compiled.name, req);
|
|
165
|
+
return compiled.kind === "covering" && answersTo(req, (n) => ruleNameCovers(compiled.pattern, n));
|
|
174
166
|
}
|
|
175
167
|
function commandOf(req) {
|
|
176
168
|
const tool = laneToolNamesOf(req).find((n) => ruleToolGrammarOf(n) === "command");
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { PendingAction } from "./checkpoint-store.js";
|
|
2
2
|
import type { CallTargetReading } from "./effective-path-target.js";
|
|
3
3
|
import { type SkillGrantSource } from "./skill-grant.js";
|
|
4
|
+
import type { ReadLanding } from "../tools/fs/shell-read-identity.js";
|
|
4
5
|
import type { ToolCallPathTarget, ToolFamily, ToolHostTarget } from "./tool-face.js";
|
|
5
6
|
import { type ToolRosterEntry } from "./tool-roster.js";
|
|
6
7
|
type ParkedApproval = Extract<PendingAction, {
|
|
@@ -16,6 +17,15 @@ export type ApprovalTargetRole = (typeof APPROVAL_TARGET_ROLES)[number];
|
|
|
16
17
|
export type ApprovalTargets = {
|
|
17
18
|
readonly [R in ApprovalTargetRole]?: string | null;
|
|
18
19
|
};
|
|
20
|
+
/** The row's `targetReads` member: per read candidate the shell read station judged for the parked bytes (its key's
|
|
21
|
+
* digest), where it landed (its settled targets' digest — a pattern lands on a set). A sibling of `targets`, not a key
|
|
22
|
+
* inside it: a reader that knows only the path roles reads an unknown key there as damage and spends the approval, where
|
|
23
|
+
* a member of its own carries its own word and that reader refuses the row unread (`version_newer`). Present exactly when
|
|
24
|
+
* the station judged the parked bytes — `{}` when it found no read candidate: a candidate appearing by the resume is a
|
|
25
|
+
* read the card did not cover. */
|
|
26
|
+
export type ApprovalTargetReads = {
|
|
27
|
+
readonly [candidate: string]: string;
|
|
28
|
+
};
|
|
19
29
|
/** The tool member as the row carries it: every member optional (a row is read, never trusted to be whole). */
|
|
20
30
|
export interface ApprovalTool {
|
|
21
31
|
readonly capabilityId?: string;
|
|
@@ -29,17 +39,21 @@ export interface ApprovalTool {
|
|
|
29
39
|
export interface ApprovalObject {
|
|
30
40
|
/** Absent on a row minted before the identity members (all four absent): such a row binds the tool's name alone. */
|
|
31
41
|
readonly tool?: ApprovalTool;
|
|
32
|
-
/** The row's
|
|
42
|
+
/** The row's two target members as stored — read, validated, by the judge. */
|
|
33
43
|
readonly targets?: unknown;
|
|
44
|
+
readonly targetReads?: unknown;
|
|
34
45
|
readonly grants?: string;
|
|
35
46
|
}
|
|
36
47
|
/** The view of one parked approval's object. */
|
|
37
48
|
export declare function approvalObjectOf(row: ParkedApproval): ApprovalObject;
|
|
38
|
-
/** The
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
|
|
49
|
+
/** The TARGETS of a reading — the one mint of both members, for the row and for the live call alike: `targets` per role,
|
|
50
|
+
* the canonical view, or `null` for a view the environment was asked for and did not answer (nothing when no role was read
|
|
51
|
+
* either way, or no reading was taken); `targetReads` per read candidate the shell read station judged, where it landed
|
|
52
|
+
* (nothing when the station judged no candidate of these bytes). The park mints them from the gate's readings of the parked
|
|
53
|
+
* bytes; the execution station reads the bytes that will run the same way. */
|
|
54
|
+
export declare function approvalTargetsOf(reading: CallTargetReading | undefined, reads?: readonly ReadLanding[]): {
|
|
42
55
|
targets?: ApprovalTargets;
|
|
56
|
+
targetReads?: ApprovalTargetReads;
|
|
43
57
|
};
|
|
44
58
|
/** Where a resume judges the object: before the claim (the decided bytes), before the session is touched (the live
|
|
45
59
|
* tool), right before the execution (the tool as mounted then — a host's re-dial can replace it between the two — and the
|
|
@@ -71,6 +85,8 @@ export interface LiveApprovalObject {
|
|
|
71
85
|
readonly skillToolName?: string;
|
|
72
86
|
/** execution: the reading of the bytes that will run, taken at this station (`undefined`: none could be taken). */
|
|
73
87
|
readonly targetReading?: CallTargetReading | undefined;
|
|
88
|
+
/** execution: where this leg's shell read station found those bytes' reads land (`undefined`: it holds no judgment of them). */
|
|
89
|
+
readonly reads?: readonly ReadLanding[] | undefined;
|
|
74
90
|
/** execution: the bytes the decision rewrote the call to, as the tool reads them; absent when it rewrote nothing. When
|
|
75
91
|
* the row's binding does not cover them ({@link licenseCovers}, the one judge of "are these the approved bytes") the
|
|
76
92
|
* person approved bytes whose target the row never recorded, and the targets member is not judged. */
|
|
@@ -5,22 +5,23 @@ import { toolIdentityOf } from "./tool-roster.js";
|
|
|
5
5
|
import { inlineUntrusted } from "./untrusted-text.js";
|
|
6
6
|
export const APPROVAL_TARGET_ROLES = Object.freeze(["protective", "declared"]);
|
|
7
7
|
export function approvalObjectOf(row) {
|
|
8
|
-
const { capabilityId, contractId, shapeDigest, implementationRevision, family, pathTarget, hostTarget, targets, boundGrantDigest } = row;
|
|
8
|
+
const { capabilityId, contractId, shapeDigest, implementationRevision, family, pathTarget, hostTarget, targets, targetReads, boundGrantDigest } = row;
|
|
9
9
|
const carried = [capabilityId, contractId, shapeDigest, implementationRevision].some((m) => m !== undefined);
|
|
10
10
|
return {
|
|
11
11
|
...(carried ? { tool: { capabilityId, contractId, shapeDigest, implementationRevision, family, pathTarget, hostTarget } } : {}),
|
|
12
12
|
...(targets !== undefined ? { targets } : {}),
|
|
13
|
+
...(targetReads !== undefined ? { targetReads } : {}),
|
|
13
14
|
...(boundGrantDigest !== undefined ? { grants: boundGrantDigest } : {}),
|
|
14
15
|
};
|
|
15
16
|
}
|
|
16
|
-
export function approvalTargetsOf(reading) {
|
|
17
|
+
export function approvalTargetsOf(reading, reads) {
|
|
17
18
|
const targets = {};
|
|
18
19
|
for (const role of APPROVAL_TARGET_ROLES) {
|
|
19
20
|
const view = reading?.[role];
|
|
20
21
|
if (view?.kind === "path" && (view.canonical !== undefined || view.unread === true))
|
|
21
22
|
targets[role] = view.canonical ?? null;
|
|
22
23
|
}
|
|
23
|
-
return Object.keys(targets).length > 0 ? { targets } : {};
|
|
24
|
+
return { ...(Object.keys(targets).length > 0 ? { targets } : {}), ...(reads !== undefined ? { targetReads: Object.fromEntries(reads.map((r) => [r.key, r.landing])) } : {}) };
|
|
24
25
|
}
|
|
25
26
|
export const APPROVAL_STATION_MEMBERS = Object.freeze({
|
|
26
27
|
claim: Object.freeze(["grants"]),
|
|
@@ -52,22 +53,48 @@ function judgeTool(row, live) {
|
|
|
52
53
|
return { member: "tool", disposition: "veto", code: "resume.target_moved", message: vetoed(row.toolName, "the tool's face (its display family, or the path target or URL slot it declares) changed after the approval") };
|
|
53
54
|
}
|
|
54
55
|
function judgeTargets(row, live) {
|
|
55
|
-
|
|
56
|
-
if (bound === undefined || ("decided" in live && !licenseCovers(row.boundInputHash, live.decided)))
|
|
56
|
+
if ((row.targets === undefined && row.targetReads === undefined) || ("decided" in live && !licenseCovers(row.boundInputHash, live.decided)))
|
|
57
57
|
return undefined;
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
|
|
58
|
+
const why = landedElsewhere(row.targets, row.targetReads, live);
|
|
59
|
+
return why === undefined ? undefined : { member: "targets", disposition: "veto", code: "resume.target_moved", message: vetoed(row.toolName, why) };
|
|
60
|
+
}
|
|
61
|
+
const DIGEST = /^[0-9a-f]{64}$/;
|
|
62
|
+
function landedElsewhere(bound, boundReads, live) {
|
|
63
|
+
if (bound !== undefined) {
|
|
64
|
+
const roles = typeof bound === "object" && bound !== null && !Array.isArray(bound) ? Object.entries(bound) : [];
|
|
65
|
+
const whole = roles.length > 0 && roles.every(([k, v]) => APPROVAL_TARGET_ROLES.includes(k) && (v === null || (typeof v === "string" && v.length > 0)));
|
|
66
|
+
if (!whole)
|
|
67
|
+
return "the target member of the approval on its checkpoint row is damaged (not a reading → resolved location record)";
|
|
68
|
+
const now = approvalTargetsOf(live.targetReading).targets ?? {};
|
|
69
|
+
const where = (p) => (p === null ? "did not resolve" : `resolved to ${shown(p)}`);
|
|
70
|
+
for (const [role, was] of roles) {
|
|
71
|
+
const is = now[role];
|
|
72
|
+
if (is === was)
|
|
73
|
+
continue;
|
|
74
|
+
return is === undefined ? `where its target resolves (it ${where(was)} when the card was asked) could not be established on this leg` : `its target ${is === null ? "does not resolve" : `resolves to ${shown(is)}`} now and ${where(was)} when the card was asked`;
|
|
75
|
+
}
|
|
69
76
|
}
|
|
70
|
-
|
|
77
|
+
if (boundReads === undefined)
|
|
78
|
+
return undefined;
|
|
79
|
+
const record = typeof boundReads === "object" && boundReads !== null && !Array.isArray(boundReads);
|
|
80
|
+
const reads = record ? Object.entries(boundReads) : [];
|
|
81
|
+
if (!record || !reads.every(([k, v]) => DIGEST.test(k) && typeof v === "string" && DIGEST.test(v)))
|
|
82
|
+
return "the read member of the approval on its checkpoint row is damaged (not a read → landing record)";
|
|
83
|
+
if (live.reads === undefined)
|
|
84
|
+
return `where its reads land (the read station judged ${reads.length} when the card was asked) could not be established on this leg`;
|
|
85
|
+
const was = new Map(reads);
|
|
86
|
+
const now = new Map(Object.entries(approvalTargetsOf(undefined, live.reads).targetReads ?? {}));
|
|
87
|
+
const named = (key) => live.reads?.find((r) => r.key === key);
|
|
88
|
+
const added = [...now.keys()].find((k) => !was.has(k));
|
|
89
|
+
if (added !== undefined || [...was.keys()].some((k) => !now.has(k)))
|
|
90
|
+
return `this leg reads the command differently than when the card was asked (${added !== undefined ? `${shown(named(added)?.raw ?? "?")} is read now at a place or in a form the card did not cover` : "a read the card covered is not read now"})`;
|
|
91
|
+
const movedKey = [...now.keys()].find((k) => now.get(k) !== was.get(k));
|
|
92
|
+
const moved = movedKey === undefined ? undefined : named(movedKey);
|
|
93
|
+
if (movedKey === undefined)
|
|
94
|
+
return undefined;
|
|
95
|
+
const targets = moved?.targets ?? [];
|
|
96
|
+
const at = targets.length === 0 ? "cannot be placed" : `lands on ${shown(targets[0])}${targets.length > 1 ? ` (and ${targets.length - 1} more)` : ""}`;
|
|
97
|
+
return `its read ${shown(moved?.raw ?? "?")} ${at} now, not where it landed when the card was asked`;
|
|
71
98
|
}
|
|
72
99
|
function judgeGrants(row, live, station) {
|
|
73
100
|
const refusal = parkedSkillApprovalRefusal(row, live.args, live.skills ?? [], live.skillToolName ?? "", station);
|
|
@@ -203,8 +203,8 @@ export type RuleOffersAbsence = (typeof RULE_OFFERS_ABSENCES)[number];
|
|
|
203
203
|
* class row's `ruleMayClear` ({@link classifyAsk} — every door below is a class there), read once by the gate's allow layer.
|
|
204
204
|
* · `word` — the DISCLOSED reason ({@link RuleOffersAbsence}), first door wins, in the display order the card has always
|
|
205
205
|
* used (governance, the outranked allow, the hook, the ask rule, the two ancestor doors, then the marks). Display
|
|
206
|
-
* only.
|
|
207
|
-
* task that cannot hold a rule) is decided BEFORE this and is deliberately silent;
|
|
206
|
+
* only. The structural door the offer factory owns (no rule speaks for the call: no lane wired, a
|
|
207
|
+
* task that cannot hold a rule, a call the lane does not judge) is decided BEFORE this and is deliberately silent;
|
|
208
208
|
* · `mandated` — the FACT the ask carries as its own member. Read off the whole mandate set, so which
|
|
209
209
|
* door is DISPLAYED cannot change it: an ask that is both outranked and mandated is mandated;
|
|
210
210
|
* · `mandate` — WHICH table word the bit stands on, only when the MARKS family is its whole reason (no
|
|
@@ -3,8 +3,8 @@ import { type Message, type Model, type Tool } from "../internal/llm.js";
|
|
|
3
3
|
import type { ContextWindowOverlay } from "./context-settings.js";
|
|
4
4
|
import type { PostCompactContext, PreCompactContext, PreCompactResult } from "./hooks.js";
|
|
5
5
|
import type { TraceEvent } from "./trace.js";
|
|
6
|
-
import type { Brain } from "./types.js";
|
|
7
|
-
import type { TaskEvent, TaskEventIdentity } from "./task-event.js";
|
|
6
|
+
import type { Brain, ModelRef, TaskSpec } from "./types.js";
|
|
7
|
+
import type { CompactOutcome, TaskEvent, TaskEventIdentity } from "./task-event.js";
|
|
8
8
|
import type { CompactionEffect } from "./frame-settlement.js";
|
|
9
9
|
import type { MintedAttachmentFrame } from "./user-attachment.js";
|
|
10
10
|
/**
|
|
@@ -39,13 +39,18 @@ export type CompactionPhaseDurations = Omit<Extract<TraceEvent, {
|
|
|
39
39
|
kind: "compaction.phase_timings";
|
|
40
40
|
}>, "kind" | "version" | "taskId" | "ts" | "durationMs">;
|
|
41
41
|
/** True iff `err` is a compaction failure caused by a manual compact() caller WITHDRAWING its
|
|
42
|
-
* request — see {@link MaybeCompactOptions.manualCancelSignal}.
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
42
|
+
* request — see {@link MaybeCompactOptions.manualCancelSignal}. Minted at every cut point of the
|
|
43
|
+
* pass: before the pass commits, while the summary provider or the credential callback is
|
|
44
|
+
* answering ({@link untilPassCut}), the summary call's own abort outcome, and the pre-persist
|
|
45
|
+
* "cancelled before commit" throw (design/374 slice-3, C16: a brain that ignored the abort finished
|
|
46
|
+
* the summary, but the withdrawal still fired before anything landed). Absence of the marker means
|
|
46
47
|
* the failure was NOT the withdrawal: a real summarizer error racing a late cancel stays
|
|
47
48
|
* unmarked so the caller's breaker/onError accounting still sees it. */
|
|
48
49
|
export declare function isCompactionManualCancel(err: unknown): boolean;
|
|
50
|
+
/** The ONE reason a failed compaction pass reports, read by every lane that accounts for one (the turn boundary, the end-of-task pass,
|
|
51
|
+
* the prompt-too-long recovery) for its trace frame and its `compaction_outcome{failed}` event: the thrown value's words through the
|
|
52
|
+
* same-principal tier, THEN bounded — minted before the cut, so the cut can leave no part of a value. */
|
|
53
|
+
export declare function compactionFailureReason(err: unknown): string;
|
|
49
54
|
/**
|
|
50
55
|
* design/145 §1/§3 — the window-safety decision surfaced to the caller BEFORE the summary call
|
|
51
56
|
* (same pre-call posture as `onInputTruncated`: the disclosure must survive a later summary
|
|
@@ -734,6 +739,66 @@ export interface CompactedFrameSource {
|
|
|
734
739
|
export declare function mintCompactedEvent(comp: CompactedFrameSource, trigger: "auto" | "manual" | "forced", identity: TaskEventIdentity): Extract<TaskEvent, {
|
|
735
740
|
type: "compacted";
|
|
736
741
|
}>;
|
|
742
|
+
/** `Runner.compactSession`'s request — the out-of-run manual compaction of a session no leg of the Runner holds. A plain
|
|
743
|
+
* data object; the keys are a closed set. */
|
|
744
|
+
export interface CompactSessionRequest {
|
|
745
|
+
/** The session to compact. It must exist. */
|
|
746
|
+
sessionId: string;
|
|
747
|
+
/** The model the session's next turn runs on — its window bounds the pass (resolved like `TaskSpec.model`). */
|
|
748
|
+
model: ModelRef;
|
|
749
|
+
/** This pass's summarization instructions: they REPLACE `compaction.instructions` and the default for this pass (the
|
|
750
|
+
* `TaskStream.compact` reading); sanitized and capped like every instructions channel. */
|
|
751
|
+
instructions?: string;
|
|
752
|
+
/** The members of `TaskSpec.compaction` a pass reads, as a run would carry them. */
|
|
753
|
+
compaction?: Pick<NonNullable<TaskSpec["compaction"]>, "enabled" | "reserveTokens" | "keepRecentTokens" | "instructions" | "clampTolerance">;
|
|
754
|
+
/** The independent summary model (`TaskSpec.compactionModel`); absent ⇒ the deployment's `summarize` role (or its tier
|
|
755
|
+
* default), else the main model. A role-derived model whose key↔URL pairing is broken falls back to the main model,
|
|
756
|
+
* announced, as on a run. */
|
|
757
|
+
compactionModel?: ModelRef;
|
|
758
|
+
/** Per-model credentials (`TaskSpec.getApiKeyAndHeaders`). */
|
|
759
|
+
getApiKeyAndHeaders?: TaskSpec["getApiKeyAndHeaders"];
|
|
760
|
+
/** Withdraws the pass: aborted before the session's lock is taken, the answer is `"mooted"` with nothing done; while the pass
|
|
761
|
+
* runs the summary call and the credential wait read it (store reads and appends, and the pre-flight's own 5 s timer, do not); a pass that already persisted is not undone. */
|
|
762
|
+
signal?: AbortSignal;
|
|
763
|
+
}
|
|
764
|
+
/** `Runner.compactSession`'s answer. */
|
|
765
|
+
export interface CompactSessionResult {
|
|
766
|
+
/** What the pass did — the closed set `TaskStream.compact` resolves with. */
|
|
767
|
+
outcome: CompactOutcome;
|
|
768
|
+
/**
|
|
769
|
+
* The pass's frames, in order, each with its own `eventId` and no task attribution: `compaction_started` once the
|
|
770
|
+
* pass committed, then the frame that ends it — `compacted` (`trigger: "manual"`) on a landing, else
|
|
771
|
+
* `compaction_outcome` (`trigger: "manual"`). Empty when the pass never ran (a withdrawn request). Returned, not
|
|
772
|
+
* streamed: a `compaction_started` here is a record of the pass, not a live progress signal.
|
|
773
|
+
*/
|
|
774
|
+
events: Array<Extract<TaskEvent, {
|
|
775
|
+
type: "compaction_started" | "compacted" | "compaction_outcome";
|
|
776
|
+
}>>;
|
|
777
|
+
/** The model calls the pass made (the summary, a hook's `$.model.complete`), priced as a run's side calls are; absent
|
|
778
|
+
* when none was made. `usageMissing` ⇒ a reply reported no usage (a lower bound); `unpriced` ⇒ a call ran on a model
|
|
779
|
+
* the deployment could not price. */
|
|
780
|
+
usage?: {
|
|
781
|
+
tokens: number;
|
|
782
|
+
totalInputTokens: number;
|
|
783
|
+
promptTokens: number;
|
|
784
|
+
cachedTokens: number;
|
|
785
|
+
cacheWriteTokens: number;
|
|
786
|
+
outputTokens: number;
|
|
787
|
+
costMicroUsd: number;
|
|
788
|
+
usageMissing?: true;
|
|
789
|
+
unpriced?: true;
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* The MANUAL outcome of a pass that returned (did not throw) — the one reading for every manual pass: the turn
|
|
794
|
+
* boundary serving a `TaskStream.compact` request and the out-of-run `Runner.compactSession`. A manual pass forces
|
|
795
|
+
* (`force` + `minTokens: 0`), so the floor's `suppressedByFloor` cannot occur and is not a case here.
|
|
796
|
+
*/
|
|
797
|
+
export declare function manualCompactOutcomeOf(comp: {
|
|
798
|
+
compacted: boolean;
|
|
799
|
+
blocked?: boolean;
|
|
800
|
+
disabled?: boolean;
|
|
801
|
+
}): CompactOutcome;
|
|
737
802
|
/**
|
|
738
803
|
* THE mint of the `compaction_started` wire frame — the opening half of the pair whose closing half is the
|
|
739
804
|
* `compacted` frame above (or a `compaction_outcome` that carries the same `pass`). One function, three
|