@inhuman.tools/turn 0.0.0-stage → 0.2.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/LICENSE.md +7 -0
- package/dist/agent-calls/acceptance.d.ts +12 -0
- package/dist/agent-calls/acceptance.d.ts.map +1 -0
- package/dist/agent-calls/acceptance.js +64 -0
- package/dist/agent-calls/acceptance.js.map +1 -0
- package/dist/agent-calls/callbacks.d.ts +14 -0
- package/dist/agent-calls/callbacks.d.ts.map +1 -0
- package/dist/agent-calls/callbacks.js +59 -0
- package/dist/agent-calls/callbacks.js.map +1 -0
- package/dist/agent-calls/delivery.d.ts +7 -0
- package/dist/agent-calls/delivery.d.ts.map +1 -0
- package/dist/agent-calls/delivery.js +16 -0
- package/dist/agent-calls/delivery.js.map +1 -0
- package/dist/agent-calls/index.d.ts +7 -0
- package/dist/agent-calls/index.d.ts.map +1 -0
- package/dist/agent-calls/index.js +6 -0
- package/dist/agent-calls/index.js.map +1 -0
- package/dist/agent-calls/operator.d.ts +21 -0
- package/dist/agent-calls/operator.d.ts.map +1 -0
- package/dist/agent-calls/operator.js +78 -0
- package/dist/agent-calls/operator.js.map +1 -0
- package/dist/agent-calls/runtime.d.ts +99 -0
- package/dist/agent-calls/runtime.d.ts.map +1 -0
- package/dist/agent-calls/runtime.js +245 -0
- package/dist/agent-calls/runtime.js.map +1 -0
- package/dist/agent-calls/types.d.ts +31 -0
- package/dist/agent-calls/types.d.ts.map +1 -0
- package/dist/agent-calls/types.js +2 -0
- package/dist/agent-calls/types.js.map +1 -0
- package/dist/context/compact-floors.d.ts +43 -0
- package/dist/context/compact-floors.d.ts.map +1 -0
- package/dist/context/compact-floors.js +52 -0
- package/dist/context/compact-floors.js.map +1 -0
- package/dist/context/compact.d.ts +42 -0
- package/dist/context/compact.d.ts.map +1 -0
- package/dist/context/compact.js +155 -0
- package/dist/context/compact.js.map +1 -0
- package/dist/context/delivery.d.ts +13 -0
- package/dist/context/delivery.d.ts.map +1 -0
- package/dist/context/delivery.js +6 -0
- package/dist/context/delivery.js.map +1 -0
- package/dist/context/history.d.ts +66 -0
- package/dist/context/history.d.ts.map +1 -0
- package/dist/context/history.js +573 -0
- package/dist/context/history.js.map +1 -0
- package/dist/context/index.d.ts +10 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +10 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/observations.d.ts +7 -0
- package/dist/context/observations.d.ts.map +1 -0
- package/dist/context/observations.js +11 -0
- package/dist/context/observations.js.map +1 -0
- package/dist/context/retention/defaults.d.ts +10 -0
- package/dist/context/retention/defaults.d.ts.map +1 -0
- package/dist/context/retention/defaults.js +47 -0
- package/dist/context/retention/defaults.js.map +1 -0
- package/dist/context/retention/index.d.ts +14 -0
- package/dist/context/retention/index.d.ts.map +1 -0
- package/dist/context/retention/index.js +21 -0
- package/dist/context/retention/index.js.map +1 -0
- package/dist/context/retention/narrow.d.ts +22 -0
- package/dist/context/retention/narrow.d.ts.map +1 -0
- package/dist/context/retention/narrow.js +140 -0
- package/dist/context/retention/narrow.js.map +1 -0
- package/dist/context/retention/render.d.ts +37 -0
- package/dist/context/retention/render.d.ts.map +1 -0
- package/dist/context/retention/render.js +149 -0
- package/dist/context/retention/render.js.map +1 -0
- package/dist/context/retention/resolve.d.ts +51 -0
- package/dist/context/retention/resolve.d.ts.map +1 -0
- package/dist/context/retention/resolve.js +254 -0
- package/dist/context/retention/resolve.js.map +1 -0
- package/dist/context/retention/schema.d.ts +345 -0
- package/dist/context/retention/schema.d.ts.map +1 -0
- package/dist/context/retention/schema.js +149 -0
- package/dist/context/retention/schema.js.map +1 -0
- package/dist/context/retention/stamp.d.ts +25 -0
- package/dist/context/retention/stamp.d.ts.map +1 -0
- package/dist/context/retention/stamp.js +103 -0
- package/dist/context/retention/stamp.js.map +1 -0
- package/dist/context/retention/step.d.ts +33 -0
- package/dist/context/retention/step.d.ts.map +1 -0
- package/dist/context/retention/step.js +74 -0
- package/dist/context/retention/step.js.map +1 -0
- package/dist/context/retention/view.d.ts +57 -0
- package/dist/context/retention/view.d.ts.map +1 -0
- package/dist/context/retention/view.js +121 -0
- package/dist/context/retention/view.js.map +1 -0
- package/dist/context/squeeze.d.ts +45 -0
- package/dist/context/squeeze.d.ts.map +1 -0
- package/dist/context/squeeze.js +182 -0
- package/dist/context/squeeze.js.map +1 -0
- package/dist/context/tools.d.ts +16 -0
- package/dist/context/tools.d.ts.map +1 -0
- package/dist/context/tools.js +73 -0
- package/dist/context/tools.js.map +1 -0
- package/dist/context/transcript.d.ts +151 -0
- package/dist/context/transcript.d.ts.map +1 -0
- package/dist/context/transcript.js +659 -0
- package/dist/context/transcript.js.map +1 -0
- package/dist/policy-processor/bypass.d.ts +95 -0
- package/dist/policy-processor/bypass.d.ts.map +1 -0
- package/dist/policy-processor/bypass.js +188 -0
- package/dist/policy-processor/bypass.js.map +1 -0
- package/dist/policy-processor/classifier.d.ts +47 -0
- package/dist/policy-processor/classifier.d.ts.map +1 -0
- package/dist/policy-processor/classifier.js +671 -0
- package/dist/policy-processor/classifier.js.map +1 -0
- package/dist/policy-processor/cluster.d.ts +7 -0
- package/dist/policy-processor/cluster.d.ts.map +1 -0
- package/dist/policy-processor/cluster.js +32 -0
- package/dist/policy-processor/cluster.js.map +1 -0
- package/dist/policy-processor/evidence.d.ts +26 -0
- package/dist/policy-processor/evidence.d.ts.map +1 -0
- package/dist/policy-processor/evidence.js +204 -0
- package/dist/policy-processor/evidence.js.map +1 -0
- package/dist/policy-processor/explain.d.ts +59 -0
- package/dist/policy-processor/explain.d.ts.map +1 -0
- package/dist/policy-processor/explain.js +224 -0
- package/dist/policy-processor/explain.js.map +1 -0
- package/dist/policy-processor/index.d.ts +6 -0
- package/dist/policy-processor/index.d.ts.map +1 -0
- package/dist/policy-processor/index.js +6 -0
- package/dist/policy-processor/index.js.map +1 -0
- package/dist/policy-processor/license.d.ts +23 -0
- package/dist/policy-processor/license.d.ts.map +1 -0
- package/dist/policy-processor/license.js +47 -0
- package/dist/policy-processor/license.js.map +1 -0
- package/dist/policy-processor/peel-cwd.d.ts +2 -0
- package/dist/policy-processor/peel-cwd.d.ts.map +1 -0
- package/dist/policy-processor/peel-cwd.js +2 -0
- package/dist/policy-processor/peel-cwd.js.map +1 -0
- package/dist/policy-processor/real-path.d.ts +7 -0
- package/dist/policy-processor/real-path.d.ts.map +1 -0
- package/dist/policy-processor/real-path.js +42 -0
- package/dist/policy-processor/real-path.js.map +1 -0
- package/dist/policy-processor/scope.d.ts +111 -0
- package/dist/policy-processor/scope.d.ts.map +1 -0
- package/dist/policy-processor/scope.js +629 -0
- package/dist/policy-processor/scope.js.map +1 -0
- package/dist/policy-processor/self-protect.d.ts +10 -0
- package/dist/policy-processor/self-protect.d.ts.map +1 -0
- package/dist/policy-processor/self-protect.js +48 -0
- package/dist/policy-processor/self-protect.js.map +1 -0
- package/dist/policy-processor/soft-deny.d.ts +75 -0
- package/dist/policy-processor/soft-deny.d.ts.map +1 -0
- package/dist/policy-processor/soft-deny.js +185 -0
- package/dist/policy-processor/soft-deny.js.map +1 -0
- package/dist/policy-processor/unattended-guard.d.ts +28 -0
- package/dist/policy-processor/unattended-guard.d.ts.map +1 -0
- package/dist/policy-processor/unattended-guard.js +145 -0
- package/dist/policy-processor/unattended-guard.js.map +1 -0
- package/dist/session/cancel.d.ts +14 -0
- package/dist/session/cancel.d.ts.map +1 -0
- package/dist/session/cancel.js +32 -0
- package/dist/session/cancel.js.map +1 -0
- package/dist/session/commands.d.ts +41 -0
- package/dist/session/commands.d.ts.map +1 -0
- package/dist/session/commands.js +241 -0
- package/dist/session/commands.js.map +1 -0
- package/dist/session/compact-actor.d.ts +23 -0
- package/dist/session/compact-actor.d.ts.map +1 -0
- package/dist/session/compact-actor.js +91 -0
- package/dist/session/compact-actor.js.map +1 -0
- package/dist/session/compact-env.d.ts +54 -0
- package/dist/session/compact-env.d.ts.map +1 -0
- package/dist/session/compact-env.js +345 -0
- package/dist/session/compact-env.js.map +1 -0
- package/dist/session/compact.d.ts +42 -0
- package/dist/session/compact.d.ts.map +1 -0
- package/dist/session/compact.js +68 -0
- package/dist/session/compact.js.map +1 -0
- package/dist/session/index.d.ts +9 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +8 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/park.d.ts +16 -0
- package/dist/session/park.d.ts.map +1 -0
- package/dist/session/park.js +73 -0
- package/dist/session/park.js.map +1 -0
- package/dist/session/side-channel-reminder.d.ts +41 -0
- package/dist/session/side-channel-reminder.d.ts.map +1 -0
- package/dist/session/side-channel-reminder.js +132 -0
- package/dist/session/side-channel-reminder.js.map +1 -0
- package/dist/session/types.d.ts +44 -0
- package/dist/session/types.d.ts.map +1 -0
- package/dist/session/types.js +2 -0
- package/dist/session/types.js.map +1 -0
- package/dist/tool-catalog/catalog.d.ts +77 -0
- package/dist/tool-catalog/catalog.d.ts.map +1 -0
- package/dist/tool-catalog/catalog.js +380 -0
- package/dist/tool-catalog/catalog.js.map +1 -0
- package/dist/tool-catalog/index.d.ts +4 -0
- package/dist/tool-catalog/index.d.ts.map +1 -0
- package/dist/tool-catalog/index.js +3 -0
- package/dist/tool-catalog/index.js.map +1 -0
- package/dist/tool-catalog/recipes.d.ts +36 -0
- package/dist/tool-catalog/recipes.d.ts.map +1 -0
- package/dist/tool-catalog/recipes.js +121 -0
- package/dist/tool-catalog/recipes.js.map +1 -0
- package/dist/tool-catalog/types.d.ts +103 -0
- package/dist/tool-catalog/types.d.ts.map +1 -0
- package/dist/tool-catalog/types.js +5 -0
- package/dist/tool-catalog/types.js.map +1 -0
- package/dist/tool-runtime/approval/approved.d.ts +13 -0
- package/dist/tool-runtime/approval/approved.d.ts.map +1 -0
- package/dist/tool-runtime/approval/approved.js +20 -0
- package/dist/tool-runtime/approval/approved.js.map +1 -0
- package/dist/tool-runtime/approval/dist.d.ts +6 -0
- package/dist/tool-runtime/approval/dist.d.ts.map +1 -0
- package/dist/tool-runtime/approval/dist.js +28 -0
- package/dist/tool-runtime/approval/dist.js.map +1 -0
- package/dist/tool-runtime/approval/inbound.d.ts +7 -0
- package/dist/tool-runtime/approval/inbound.d.ts.map +1 -0
- package/dist/tool-runtime/approval/inbound.js +48 -0
- package/dist/tool-runtime/approval/inbound.js.map +1 -0
- package/dist/tool-runtime/approval/unattended.d.ts +8 -0
- package/dist/tool-runtime/approval/unattended.d.ts.map +1 -0
- package/dist/tool-runtime/approval/unattended.js +23 -0
- package/dist/tool-runtime/approval/unattended.js.map +1 -0
- package/dist/tool-runtime/blob-port.d.ts +46 -0
- package/dist/tool-runtime/blob-port.d.ts.map +1 -0
- package/dist/tool-runtime/blob-port.js +47 -0
- package/dist/tool-runtime/blob-port.js.map +1 -0
- package/dist/tool-runtime/extra-root.d.ts +3 -0
- package/dist/tool-runtime/extra-root.d.ts.map +1 -0
- package/dist/tool-runtime/extra-root.js +14 -0
- package/dist/tool-runtime/extra-root.js.map +1 -0
- package/dist/tool-runtime/handler.d.ts +29 -0
- package/dist/tool-runtime/handler.d.ts.map +1 -0
- package/dist/tool-runtime/handler.js +460 -0
- package/dist/tool-runtime/handler.js.map +1 -0
- package/dist/tool-runtime/index.d.ts +14 -0
- package/dist/tool-runtime/index.d.ts.map +1 -0
- package/dist/tool-runtime/index.js +14 -0
- package/dist/tool-runtime/index.js.map +1 -0
- package/dist/tool-runtime/peel.d.ts +19 -0
- package/dist/tool-runtime/peel.d.ts.map +1 -0
- package/dist/tool-runtime/peel.js +74 -0
- package/dist/tool-runtime/peel.js.map +1 -0
- package/dist/tool-runtime/plugin-context.d.ts +13 -0
- package/dist/tool-runtime/plugin-context.d.ts.map +1 -0
- package/dist/tool-runtime/plugin-context.js +55 -0
- package/dist/tool-runtime/plugin-context.js.map +1 -0
- package/dist/tool-runtime/recall.d.ts +18 -0
- package/dist/tool-runtime/recall.d.ts.map +1 -0
- package/dist/tool-runtime/recall.js +100 -0
- package/dist/tool-runtime/recall.js.map +1 -0
- package/dist/tool-runtime/run-system-tool.d.ts +13 -0
- package/dist/tool-runtime/run-system-tool.d.ts.map +1 -0
- package/dist/tool-runtime/run-system-tool.js +230 -0
- package/dist/tool-runtime/run-system-tool.js.map +1 -0
- package/dist/tool-runtime/tool-error.d.ts +6 -0
- package/dist/tool-runtime/tool-error.d.ts.map +1 -0
- package/dist/tool-runtime/tool-error.js +6 -0
- package/dist/tool-runtime/tool-error.js.map +1 -0
- package/dist/tool-runtime/worker-tools.d.ts +8 -0
- package/dist/tool-runtime/worker-tools.d.ts.map +1 -0
- package/dist/tool-runtime/worker-tools.js +65 -0
- package/dist/tool-runtime/worker-tools.js.map +1 -0
- package/dist/tool-runtime/write-admit.d.ts +21 -0
- package/dist/tool-runtime/write-admit.d.ts.map +1 -0
- package/dist/tool-runtime/write-admit.js +89 -0
- package/dist/tool-runtime/write-admit.js.map +1 -0
- package/package.json +114 -3
- package/policy/auto-mode-ask.scm +201 -0
- package/policy/bypass-detect.prompt +55 -0
- package/policy/classifier-bash-incomplete.prompt +46 -0
- package/policy/classifier-bash.prompt +45 -0
- package/policy/classifier-mcp.prompt +45 -0
- package/policy/classifier-read.prompt +44 -0
- package/policy/classifier-write-outside.prompt +46 -0
- package/policy/classifier-write-risky.prompt +45 -0
- package/policy/classifier-write-workspace.prompt +46 -0
- package/policy/classifier-write.prompt +45 -0
- package/policy/soft-deny-round1.prompt +51 -0
- package/policy/soft-deny-round2.prompt +60 -0
- package/src/agent-calls/acceptance.ts +71 -0
- package/src/agent-calls/callbacks.ts +75 -0
- package/src/agent-calls/delivery.ts +15 -0
- package/src/agent-calls/index.ts +16 -0
- package/src/agent-calls/operator.ts +99 -0
- package/src/agent-calls/runtime.ts +310 -0
- package/src/agent-calls/types.ts +33 -0
- package/src/context/compact-floors.ts +76 -0
- package/src/context/compact.ts +176 -0
- package/src/context/delivery.ts +15 -0
- package/src/context/history.ts +725 -0
- package/src/context/index.ts +35 -0
- package/src/context/observations.ts +13 -0
- package/src/context/retention/defaults.ts +58 -0
- package/src/context/retention/index.ts +45 -0
- package/src/context/retention/narrow.ts +123 -0
- package/src/context/retention/render.ts +177 -0
- package/src/context/retention/resolve.ts +306 -0
- package/src/context/retention/schema.ts +173 -0
- package/src/context/retention/stamp.ts +121 -0
- package/src/context/retention/step.ts +85 -0
- package/src/context/retention/view.ts +161 -0
- package/src/context/squeeze.ts +218 -0
- package/src/context/tools.ts +92 -0
- package/src/context/transcript.ts +705 -0
- package/src/policy-processor/README.md +43 -0
- package/src/policy-processor/bypass.ts +246 -0
- package/src/policy-processor/classifier.ts +711 -0
- package/src/policy-processor/cluster.ts +31 -0
- package/src/policy-processor/evidence.ts +214 -0
- package/src/policy-processor/explain.ts +310 -0
- package/src/policy-processor/index.ts +6 -0
- package/src/policy-processor/license.ts +45 -0
- package/src/policy-processor/peel-cwd.ts +1 -0
- package/src/policy-processor/real-path.ts +41 -0
- package/src/policy-processor/scope.ts +655 -0
- package/src/policy-processor/self-protect.ts +46 -0
- package/src/policy-processor/soft-deny.ts +225 -0
- package/src/policy-processor/unattended-guard.ts +158 -0
- package/src/session/cancel.ts +34 -0
- package/src/session/commands.ts +292 -0
- package/src/session/compact-actor.ts +132 -0
- package/src/session/compact-env.ts +539 -0
- package/src/session/compact.ts +95 -0
- package/src/session/index.ts +18 -0
- package/src/session/park.ts +73 -0
- package/src/session/side-channel-reminder.ts +148 -0
- package/src/session/types.ts +50 -0
- package/src/tool-catalog/catalog.ts +282 -0
- package/src/tool-catalog/index.ts +26 -0
- package/src/tool-catalog/recipes.ts +154 -0
- package/src/tool-catalog/types.ts +110 -0
- package/src/tool-runtime/approval/approved.ts +20 -0
- package/src/tool-runtime/approval/dist.ts +29 -0
- package/src/tool-runtime/approval/inbound.ts +50 -0
- package/src/tool-runtime/approval/unattended.ts +24 -0
- package/src/tool-runtime/blob-port.ts +64 -0
- package/src/tool-runtime/extra-root.ts +16 -0
- package/src/tool-runtime/handler.ts +576 -0
- package/src/tool-runtime/index.ts +21 -0
- package/src/tool-runtime/peel.ts +91 -0
- package/src/tool-runtime/plugin-context.ts +68 -0
- package/src/tool-runtime/recall.ts +104 -0
- package/src/tool-runtime/run-system-tool.ts +273 -0
- package/src/tool-runtime/tool-error.ts +6 -0
- package/src/tool-runtime/worker-tools.ts +76 -0
- package/src/tool-runtime/write-admit.ts +90 -0
- package/README.md +0 -3
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compact env is an EnvCapability, not a bag of names.
|
|
3
|
+
*
|
|
4
|
+
* Five keys (`writing-capabilities.md`, `environments.md` §CAPABILITY):
|
|
5
|
+
* configuration, resources, prelude, symbols, deps. Symbols are the membrane
|
|
6
|
+
* crossing (honest codecs). Prelude is Scheme bootstrap that PERSISTS into the
|
|
7
|
+
* strategy eval — derived names are `(define …)` over primitives, not a second
|
|
8
|
+
* JS verb. User code may call prelude defines; it may not name a `preludeOnly`
|
|
9
|
+
* symbol (those exist only while the prelude runs).
|
|
10
|
+
*
|
|
11
|
+
* Compact is not a classify walk. `applyCompact` loads the strategy with
|
|
12
|
+
* `execState` and calls `compact` / `forced-compact` as host functions,
|
|
13
|
+
* passing `Transcript` instances as arguments. Occupancy primitives are
|
|
14
|
+
* `tokens/used` and `tokens/total`; remaining and relative live in the prelude.
|
|
15
|
+
* `user-rounds/count` is a primitive (prompt nonempty). `message/ack` is a
|
|
16
|
+
* primitive so the cell is marked synthetic.
|
|
17
|
+
*
|
|
18
|
+
* Membrane: `Transcript` is `@arrival.private` — opaque `z.instance(Transcript)`.
|
|
19
|
+
* Eat cells are a union of host classes, so they cross as `z.box` (non-opaque)
|
|
20
|
+
* — `z.instance` unions throw on the first arm instead of falling through.
|
|
21
|
+
*
|
|
22
|
+
* Provenance: config-readers (`chunks`, `reason`, `kv/get`, …) are `source` —
|
|
23
|
+
* external data crossing in. Meters and slicers over a transcript ARGUMENT
|
|
24
|
+
* are `pipe` — pure transforms that mint nothing. `transcript/warm?` is the
|
|
25
|
+
* one argument-reader that stays `source`: `isWarm` reads the live clock.
|
|
26
|
+
*/
|
|
27
|
+
import { dirname } from "node:path";
|
|
28
|
+
|
|
29
|
+
import {
|
|
30
|
+
EnvCapability,
|
|
31
|
+
execState,
|
|
32
|
+
jsToScheme,
|
|
33
|
+
makeCallCtx,
|
|
34
|
+
toJS,
|
|
35
|
+
type RunContext,
|
|
36
|
+
type SchemeValue,
|
|
37
|
+
} from "@here.build/arrival";
|
|
38
|
+
import { applyCallback, is_callable_value } from "@here.build/arrival/host-internals";
|
|
39
|
+
import { arrivalLoaderCapability } from "@here.build/arrival-modules";
|
|
40
|
+
import { promptIsolatedCapability, type PromptComplete } from "@inhuman.tools/env-capability-module-prompt";
|
|
41
|
+
import {
|
|
42
|
+
CompactJob,
|
|
43
|
+
CompletionExpectation,
|
|
44
|
+
CompletionJob,
|
|
45
|
+
ProgrammaticJob,
|
|
46
|
+
tapeCompletion,
|
|
47
|
+
ToolCallJob,
|
|
48
|
+
TurnApprovalNoteChunk,
|
|
49
|
+
TurnNotificationChunk,
|
|
50
|
+
TurnSteerChunk,
|
|
51
|
+
} from "@inhuman.tools/harness-model";
|
|
52
|
+
import { Job } from "@inhuman.tools/plexus-expectations";
|
|
53
|
+
import { programSourceLoader } from "@inhuman.tools/scheme-program/fs";
|
|
54
|
+
import invariant from "tiny-invariant";
|
|
55
|
+
import { z } from "zod";
|
|
56
|
+
|
|
57
|
+
import {
|
|
58
|
+
contentTokensOf,
|
|
59
|
+
MessageChunk,
|
|
60
|
+
payloadSuffix,
|
|
61
|
+
textOfCell,
|
|
62
|
+
toolCallRepresentation,
|
|
63
|
+
Transcript,
|
|
64
|
+
type EatCell,
|
|
65
|
+
} from "../context/index.js";
|
|
66
|
+
import { charsOf } from "../context/transcript.js";
|
|
67
|
+
|
|
68
|
+
/** Same door as scheme_repl `load`: last-write-wins over `require`. */
|
|
69
|
+
const schemeLoadAliasCapability = EnvCapability.define("harness/scheme-repl-load", {
|
|
70
|
+
deps: [arrivalLoaderCapability],
|
|
71
|
+
symbols: (symbol) => ({
|
|
72
|
+
load: symbol.defineSyntax`load: alias of require; spills .scm defines into this run`(
|
|
73
|
+
"(lambda (specifier) `(require ,specifier))",
|
|
74
|
+
),
|
|
75
|
+
}),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const hostChunk = z.union([
|
|
79
|
+
z.instanceof(CompactJob),
|
|
80
|
+
z.instanceof(CompletionExpectation),
|
|
81
|
+
z.instanceof(CompletionJob),
|
|
82
|
+
z.instanceof(ToolCallJob),
|
|
83
|
+
z.instanceof(ProgrammaticJob),
|
|
84
|
+
z.instanceof(TurnNotificationChunk),
|
|
85
|
+
z.instanceof(TurnApprovalNoteChunk),
|
|
86
|
+
z.instanceof(TurnSteerChunk),
|
|
87
|
+
z.instanceof(MessageChunk),
|
|
88
|
+
z.string(),
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
export type CompactStrategyOpts = {
|
|
92
|
+
readonly reason?: string;
|
|
93
|
+
readonly focus?: string;
|
|
94
|
+
readonly kv?: Readonly<Record<string, string>>;
|
|
95
|
+
readonly plan?: string | null;
|
|
96
|
+
readonly forced?: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Absolute path the program source was read from. Enables `(require "…")` —
|
|
99
|
+
* `.prompt` pretreats ship next to the strategy — jailed under its dirname.
|
|
100
|
+
* Null for the inline fallback: no `require` surface.
|
|
101
|
+
*/
|
|
102
|
+
readonly sourcePath?: string | null;
|
|
103
|
+
/**
|
|
104
|
+
* Live completion wiring for the summarizer call a `.prompt` performs. The
|
|
105
|
+
* prompt capability admits an agent/ask on this job; absent → the strategy
|
|
106
|
+
* renders but cannot run a model call (bench/stub path).
|
|
107
|
+
*/
|
|
108
|
+
readonly job?: Job;
|
|
109
|
+
readonly complete?: PromptComplete;
|
|
110
|
+
readonly connectionId?: string;
|
|
111
|
+
readonly completionModel?: string;
|
|
112
|
+
/** Abort rejects at once and hands the abort to each summarizer round through `complete`. */
|
|
113
|
+
readonly signal?: AbortSignal;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
export type CompactFailure = {
|
|
117
|
+
readonly ok: false;
|
|
118
|
+
readonly reason: "empty-check-in" | "did-not-shrink";
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export type CompactSuccess = {
|
|
122
|
+
readonly ok: true;
|
|
123
|
+
readonly checkIn: EatCell[];
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export type CompactApplyResult = CompactSuccess | CompactFailure;
|
|
127
|
+
|
|
128
|
+
type CompactConfig = {
|
|
129
|
+
chunks: Array<z.infer<typeof hostChunk>>;
|
|
130
|
+
reason: string;
|
|
131
|
+
focus: string;
|
|
132
|
+
kv: Record<string, string>;
|
|
133
|
+
plan: string | null;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export const compactReducerCapability = EnvCapability.define("harness/compact", {
|
|
137
|
+
configuration: {
|
|
138
|
+
chunks: z.array(hostChunk).default([]),
|
|
139
|
+
reason: z.string().default("overflow"),
|
|
140
|
+
focus: z.string().default(""),
|
|
141
|
+
kv: z.record(z.string(), z.string()).default({}),
|
|
142
|
+
plan: z.string().nullable().optional(),
|
|
143
|
+
},
|
|
144
|
+
symbols: (symbol, sz) => {
|
|
145
|
+
const chunk = sz.union([sz.box, sz.string]);
|
|
146
|
+
const transcript = sz.instance(Transcript);
|
|
147
|
+
const cells = sz.list(chunk);
|
|
148
|
+
// "value or #f" — the R7RS search-sentinel union (r7rs/lists.ts), never z.dynamic.
|
|
149
|
+
const stringOrFalse = sz.union([sz.string, sz.booleanFalse]);
|
|
150
|
+
return {
|
|
151
|
+
chunks: symbol.rosetta`chunks: current eat; completions and tool calls`(
|
|
152
|
+
{ input: [], output: [cells], provenance: "source" },
|
|
153
|
+
function () {
|
|
154
|
+
return this.configuration.chunks;
|
|
155
|
+
},
|
|
156
|
+
),
|
|
157
|
+
|
|
158
|
+
"tokens/used": symbol.rosetta`tokens/used: occupancy of a transcript`(
|
|
159
|
+
// Gauge diff at the slice boundary — never a per-cell sum of the
|
|
160
|
+
// window gauge (transcript.ts preamble).
|
|
161
|
+
{ input: [transcript], output: [sz.number], provenance: "pipe" },
|
|
162
|
+
(t: Transcript) => t.tokensUsed(),
|
|
163
|
+
),
|
|
164
|
+
"tokens/total": symbol.rosetta`tokens/total: cap attached to a transcript`(
|
|
165
|
+
{ input: [transcript], output: [sz.number], provenance: "pipe" },
|
|
166
|
+
(t: Transcript) => t.cap,
|
|
167
|
+
),
|
|
168
|
+
"tokens/count": symbol.rosetta`tokens/count: raw content meter of a cell or list`(
|
|
169
|
+
{ input: [transcript], output: [sz.number], provenance: "pipe" },
|
|
170
|
+
(t: Transcript) => contentTokensOf(t.cells),
|
|
171
|
+
),
|
|
172
|
+
"chars/count": symbol.rosetta`chars/count: JSON-char meter (Gemini)`(
|
|
173
|
+
{ input: [transcript], output: [sz.number], provenance: "pipe" },
|
|
174
|
+
(t: Transcript) => charsOf(t.cells),
|
|
175
|
+
),
|
|
176
|
+
"rounds/count": symbol.rosetta`rounds/count: ReACT rounds in a transcript`(
|
|
177
|
+
{ input: [transcript], output: [sz.number], provenance: "pipe" },
|
|
178
|
+
(t: Transcript) => t.rounds().length,
|
|
179
|
+
),
|
|
180
|
+
"user-rounds/count": symbol.rosetta`user-rounds/count: rounds whose prompt is nonempty`(
|
|
181
|
+
{ input: [transcript], output: [sz.number], provenance: "pipe" },
|
|
182
|
+
(t: Transcript) => t.rounds().filter((round) => round.prompt !== "").length,
|
|
183
|
+
),
|
|
184
|
+
|
|
185
|
+
"transcript/warm?": symbol.rosetta`transcript/warm?: any completion still in the prompt cache`(
|
|
186
|
+
{ input: [transcript], output: [sz.boolean], provenance: "source" },
|
|
187
|
+
(t: Transcript) =>
|
|
188
|
+
t.cells.some((cell) => {
|
|
189
|
+
const pew = tapeCompletion(cell) ?? (cell instanceof CompletionExpectation ? cell : null);
|
|
190
|
+
return pew != null && pew.isWarm;
|
|
191
|
+
}),
|
|
192
|
+
),
|
|
193
|
+
// Frontier direction: preserve runs frontier → newest, compact runs
|
|
194
|
+
// oldest → frontier. The frontier is preserve's OLDEST round and
|
|
195
|
+
// compact's NEWEST — newer drops the oldest, older drops the newest.
|
|
196
|
+
"transcript/round": symbol.rosetta`transcript/round: frontier as a one-round transcript`(
|
|
197
|
+
{ input: [transcript], output: [transcript], provenance: "pipe" },
|
|
198
|
+
(t: Transcript) => t.frontierRound(),
|
|
199
|
+
),
|
|
200
|
+
"transcript/newer": symbol.rosetta`transcript/newer: preserve without the frontier`(
|
|
201
|
+
{ input: [transcript], output: [transcript], provenance: "pipe" },
|
|
202
|
+
(t: Transcript) => t.withoutFrontier(),
|
|
203
|
+
),
|
|
204
|
+
"transcript/older": symbol.rosetta`transcript/older: compact without the frontier`(
|
|
205
|
+
{ input: [transcript], output: [transcript], provenance: "pipe" },
|
|
206
|
+
(t: Transcript) => t.withoutNewest(),
|
|
207
|
+
),
|
|
208
|
+
"transcript/tools": symbol.rosetta`transcript/tools: flattened tool cells`(
|
|
209
|
+
{ input: [transcript], output: [cells], provenance: "pipe" },
|
|
210
|
+
(t: Transcript) => t.cells.filter((cell) => cell instanceof ToolCallJob || cell instanceof ProgrammaticJob),
|
|
211
|
+
),
|
|
212
|
+
"transcript/cells": symbol.rosetta`transcript/cells: flattened eat cells`(
|
|
213
|
+
{ input: [transcript], output: [cells], provenance: "pipe" },
|
|
214
|
+
(t: Transcript) => [...t.cells],
|
|
215
|
+
),
|
|
216
|
+
"transcript/compaction-results":
|
|
217
|
+
symbol.rosetta`transcript/compaction-results: prior compact check-ins, oldest to newest`(
|
|
218
|
+
{ input: [transcript], output: [cells], provenance: "pipe" },
|
|
219
|
+
(t: Transcript) => t.checkInCells(),
|
|
220
|
+
),
|
|
221
|
+
"transcript/payload": symbol.rosetta`transcript/payload: t without its prior compact cards`(
|
|
222
|
+
{ input: [transcript], output: [transcript], provenance: "pipe" },
|
|
223
|
+
(t: Transcript) => t.withoutCheckIns(),
|
|
224
|
+
),
|
|
225
|
+
"transcript/complete-rounds": symbol.rosetta`transcript/complete-rounds: rounds in t that ended`(
|
|
226
|
+
{ input: [transcript], output: [transcript], provenance: "pipe" },
|
|
227
|
+
(t: Transcript) => t.completeRounds(),
|
|
228
|
+
),
|
|
229
|
+
"transcript/partial-round": symbol.rosetta`transcript/partial-round: unfinished last round, or #f`(
|
|
230
|
+
{
|
|
231
|
+
input: [transcript],
|
|
232
|
+
output: [sz.union([transcript, sz.nil])],
|
|
233
|
+
provenance: "pipe",
|
|
234
|
+
},
|
|
235
|
+
(t: Transcript) => t.partialRound(),
|
|
236
|
+
),
|
|
237
|
+
"transcript/concat": symbol.rosetta`transcript/concat: join two transcripts`(
|
|
238
|
+
{
|
|
239
|
+
input: [transcript, transcript],
|
|
240
|
+
output: [transcript],
|
|
241
|
+
provenance: "pipe",
|
|
242
|
+
},
|
|
243
|
+
(a: Transcript, b: Transcript) => a.concat(b),
|
|
244
|
+
),
|
|
245
|
+
"transcript/empty": symbol.rosetta`transcript/empty: empty transcript`(
|
|
246
|
+
{ input: [], output: [transcript], provenance: "pipe" },
|
|
247
|
+
() => Transcript.empty(),
|
|
248
|
+
),
|
|
249
|
+
|
|
250
|
+
"payload/suffix": symbol.rosetta`payload/suffix: pair-safe last n cells that are not compact check-ins`(
|
|
251
|
+
{
|
|
252
|
+
input: [transcript, sz.number],
|
|
253
|
+
output: [cells],
|
|
254
|
+
provenance: "pipe",
|
|
255
|
+
},
|
|
256
|
+
(t: Transcript, n: number) => payloadSuffix(t, n),
|
|
257
|
+
),
|
|
258
|
+
|
|
259
|
+
"message/user": symbol.rosetta`message/user: user cell or user-shaped check-in`(
|
|
260
|
+
{ input: [sz.string], output: [chunk], provenance: "pipe" },
|
|
261
|
+
(text: string) => new MessageChunk("user", text),
|
|
262
|
+
),
|
|
263
|
+
"message/assistant": symbol.rosetta`message/assistant: assistant cell or assistant-shaped check-in`(
|
|
264
|
+
{ input: [sz.string], output: [chunk], provenance: "pipe" },
|
|
265
|
+
(text: string) => new MessageChunk("assistant", text),
|
|
266
|
+
),
|
|
267
|
+
"message/ack": symbol.rosetta`message/ack: synthetic turn-taking cell`(
|
|
268
|
+
{
|
|
269
|
+
input: [sz.foldName.pipe(sz.enum(["user", "assistant"])), sz.string],
|
|
270
|
+
output: [chunk],
|
|
271
|
+
provenance: "pipe",
|
|
272
|
+
},
|
|
273
|
+
(role, text: string) => new MessageChunk(role, text, { ack: true }),
|
|
274
|
+
),
|
|
275
|
+
"message/user?": symbol.rosetta`message/user?: eat cell is user-shaped`(
|
|
276
|
+
{ input: [chunk], output: [sz.boolean], provenance: "pipe" },
|
|
277
|
+
(cell: unknown) => typeof cell === "string" || (cell instanceof MessageChunk && cell.role === "user"),
|
|
278
|
+
),
|
|
279
|
+
"message/assistant?": symbol.rosetta`message/assistant?: eat cell is assistant-shaped`(
|
|
280
|
+
{ input: [chunk], output: [sz.boolean], provenance: "pipe" },
|
|
281
|
+
(cell: unknown) => {
|
|
282
|
+
const pew = tapeCompletion(cell) ?? (cell instanceof CompletionExpectation ? cell : null);
|
|
283
|
+
return pew != null || (cell instanceof MessageChunk && cell.role === "assistant");
|
|
284
|
+
},
|
|
285
|
+
),
|
|
286
|
+
"message/tool?": symbol.rosetta`message/tool?: eat cell is a tool call`(
|
|
287
|
+
{ input: [chunk], output: [sz.boolean], provenance: "pipe" },
|
|
288
|
+
(cell: unknown) => cell instanceof ToolCallJob || cell instanceof ProgrammaticJob,
|
|
289
|
+
),
|
|
290
|
+
"pair-id": symbol.rosetta`pair-id: toolCallId, or #f`(
|
|
291
|
+
{ input: [chunk], output: [stringOrFalse], provenance: "pipe" },
|
|
292
|
+
(cell: unknown) => {
|
|
293
|
+
if (cell instanceof ToolCallJob && cell.toolCallId !== "") return cell.toolCallId;
|
|
294
|
+
if (cell instanceof ProgrammaticJob && cell.toolCallId !== "") return cell.toolCallId;
|
|
295
|
+
return false;
|
|
296
|
+
},
|
|
297
|
+
),
|
|
298
|
+
"tool-name": symbol.rosetta`tool-name: Read / Bash / Shell / …`(
|
|
299
|
+
{ input: [chunk], output: [sz.string], provenance: "pipe" },
|
|
300
|
+
(cell: unknown) =>
|
|
301
|
+
cell instanceof ToolCallJob ? cell.name : cell instanceof ProgrammaticJob ? cell.toolName : "",
|
|
302
|
+
),
|
|
303
|
+
"chunk-text": symbol.rosetta`chunk-text: cell text, or #f`(
|
|
304
|
+
{ input: [chunk], output: [stringOrFalse], provenance: "pipe" },
|
|
305
|
+
(cell: unknown) => textOfCell(cell) || false,
|
|
306
|
+
),
|
|
307
|
+
"message/transcript-as-single-prompt":
|
|
308
|
+
symbol.rosetta`message/transcript-as-single-prompt: paint a transcript as one summarizer blob`(
|
|
309
|
+
{ input: [transcript], output: [sz.string], provenance: "pipe" },
|
|
310
|
+
(t: Transcript) =>
|
|
311
|
+
t.cells
|
|
312
|
+
.map(function (cell: EatCell): string {
|
|
313
|
+
if (typeof cell === "string") return cell;
|
|
314
|
+
if (cell instanceof CompactJob) return `user: ${textOfCell(cell)}`;
|
|
315
|
+
if (cell instanceof MessageChunk) return `${cell.role}: ${cell.text}`;
|
|
316
|
+
if (cell instanceof TurnNotificationChunk) return textOfCell(cell);
|
|
317
|
+
if (cell instanceof TurnApprovalNoteChunk) return `user: ${cell.approvalNoteText()}`;
|
|
318
|
+
if (cell instanceof TurnSteerChunk) return `user: ${cell.completionMessage}`;
|
|
319
|
+
if (cell instanceof ToolCallJob) {
|
|
320
|
+
const scheme = cell.children.find(
|
|
321
|
+
(child): child is ProgrammaticJob => child instanceof ProgrammaticJob,
|
|
322
|
+
);
|
|
323
|
+
if (scheme !== undefined) {
|
|
324
|
+
return `[scheme ${scheme.toolCallId}]\n${scheme.expr}\n${scheme.resultText()}`.trim();
|
|
325
|
+
}
|
|
326
|
+
const painted = toolCallRepresentation(cell);
|
|
327
|
+
return `[${painted.name} ${painted.id}]\n${painted.args}\n${painted.result}`.trim();
|
|
328
|
+
}
|
|
329
|
+
if (cell instanceof ProgrammaticJob) {
|
|
330
|
+
return `[scheme ${cell.toolCallId}]\n${cell.expr}\n${cell.resultText()}`.trim();
|
|
331
|
+
}
|
|
332
|
+
const pew = tapeCompletion(cell) ?? (cell instanceof CompletionExpectation ? cell : null);
|
|
333
|
+
if (pew == null) return "";
|
|
334
|
+
const parts: string[] = [];
|
|
335
|
+
if (pew.userPrompt !== "") parts.push(`user: ${pew.userPrompt}`);
|
|
336
|
+
if (pew.responseText !== "") parts.push(`assistant: ${pew.responseText}`);
|
|
337
|
+
return parts.join("\n");
|
|
338
|
+
})
|
|
339
|
+
.filter((line) => line !== "")
|
|
340
|
+
.join("\n\n"),
|
|
341
|
+
),
|
|
342
|
+
|
|
343
|
+
reason: symbol.rosetta`reason: why classify was invoked; overflow, reactive, context-exceeded`(
|
|
344
|
+
{ input: [], output: [sz.foldName], provenance: "source" },
|
|
345
|
+
function () {
|
|
346
|
+
return this.configuration.reason;
|
|
347
|
+
},
|
|
348
|
+
),
|
|
349
|
+
focus: symbol.rosetta`focus: /compact instructions; empty on auto`(
|
|
350
|
+
{ input: [], output: [sz.string], provenance: "source" },
|
|
351
|
+
function () {
|
|
352
|
+
return this.configuration.focus;
|
|
353
|
+
},
|
|
354
|
+
),
|
|
355
|
+
"kv/get": symbol.rosetta`kv/get: session kv value, or #f`(
|
|
356
|
+
{
|
|
357
|
+
input: [sz.foldName],
|
|
358
|
+
output: [sz.union([sz.string, sz.number, sz.booleanFalse])],
|
|
359
|
+
provenance: "source",
|
|
360
|
+
},
|
|
361
|
+
function (key: string) {
|
|
362
|
+
return kvGet(this.configuration.kv, key);
|
|
363
|
+
},
|
|
364
|
+
),
|
|
365
|
+
"plan/get": symbol.rosetta`plan/get: working plan text, or #f`(
|
|
366
|
+
{ input: [], output: [stringOrFalse], provenance: "source" },
|
|
367
|
+
function () {
|
|
368
|
+
return this.configuration.plan ?? false;
|
|
369
|
+
},
|
|
370
|
+
),
|
|
371
|
+
};
|
|
372
|
+
},
|
|
373
|
+
prelude: `
|
|
374
|
+
(define (tokens/remaining t)
|
|
375
|
+
(- (tokens/total t) (tokens/used t)))
|
|
376
|
+
|
|
377
|
+
(define (tokens/relative/used t)
|
|
378
|
+
(let ((total (tokens/total t)))
|
|
379
|
+
(if (zero? total) 0 (/ (tokens/used t) total))))
|
|
380
|
+
|
|
381
|
+
(define (tokens/relative/total t)
|
|
382
|
+
1)
|
|
383
|
+
|
|
384
|
+
(define (tokens/relative/remaining t)
|
|
385
|
+
(- (tokens/relative/total t) (tokens/relative/used t)))
|
|
386
|
+
|
|
387
|
+
(define (cells/count t)
|
|
388
|
+
(length (transcript/cells t)))
|
|
389
|
+
|
|
390
|
+
(define (starts-with s prefix)
|
|
391
|
+
(string-prefix? prefix s))
|
|
392
|
+
`,
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Load the strategy with `execState`, then call `compact` / `forced-compact`
|
|
397
|
+
* as host functions with the prefix and full transcripts as arguments. Empty or
|
|
398
|
+
* non-shrinking cards are typed failures — the kernel's "failed to compact"
|
|
399
|
+
* path is the caller's.
|
|
400
|
+
*/
|
|
401
|
+
export async function applyCompact(
|
|
402
|
+
program: string,
|
|
403
|
+
compactTranscript: Transcript,
|
|
404
|
+
fullTranscript: Transcript,
|
|
405
|
+
opts: CompactStrategyOpts = {},
|
|
406
|
+
): Promise<CompactApplyResult> {
|
|
407
|
+
invariant(program.trim() !== "", "compact strategy program is empty");
|
|
408
|
+
const names = opts.forced === true ? (["forced-compact", "compact"] as const) : (["compact"] as const);
|
|
409
|
+
// `(require "compact.prompt")` — the threaded-snapshot strategy is a
|
|
410
|
+
// `.scm` + `.prompt` pair. `require` is a loader symbol, and the loader is
|
|
411
|
+
// jailed under the strategy file's directory (same rule as scheme_repl:
|
|
412
|
+
// `programSourceLoader`). Without these the strategy throws
|
|
413
|
+
// `Unbound variable require` and the compact silently degrades to the fallback.
|
|
414
|
+
const moduleRoot = opts.sourcePath != null && opts.sourcePath !== "" ? dirname(opts.sourcePath) : undefined;
|
|
415
|
+
// `require` only needs the loader. The isolated host is assembled when a
|
|
416
|
+
// parent job and a connection exist; without them a stub strategy still
|
|
417
|
+
// loads, and `dotprompt/run` is not a symbol.
|
|
418
|
+
const connectionId = opts.connectionId ?? "";
|
|
419
|
+
const isolated = moduleRoot != null && opts.job != null && connectionId !== "";
|
|
420
|
+
const state = await execState(program, {
|
|
421
|
+
capabilities:
|
|
422
|
+
moduleRoot == null
|
|
423
|
+
? [compactReducerCapability]
|
|
424
|
+
: isolated
|
|
425
|
+
? [compactReducerCapability, promptIsolatedCapability, schemeLoadAliasCapability]
|
|
426
|
+
: [compactReducerCapability, schemeLoadAliasCapability],
|
|
427
|
+
config: {
|
|
428
|
+
...compactConfig(opts, fullTranscript.cells),
|
|
429
|
+
...(moduleRoot == null
|
|
430
|
+
? {}
|
|
431
|
+
: {
|
|
432
|
+
loader: programSourceLoader(moduleRoot),
|
|
433
|
+
dirname: "",
|
|
434
|
+
...(isolated
|
|
435
|
+
? {
|
|
436
|
+
job: opts.job,
|
|
437
|
+
connectionId,
|
|
438
|
+
completionModel: opts.completionModel ?? "",
|
|
439
|
+
}
|
|
440
|
+
: {}),
|
|
441
|
+
}),
|
|
442
|
+
},
|
|
443
|
+
});
|
|
444
|
+
const fn = names.map((name) => state.scope.lookup(name)).find(is_callable_value);
|
|
445
|
+
invariant(fn !== undefined, `compact strategy: program does not define ${names.join(" or ")}`);
|
|
446
|
+
const raw = await abortable(() => callLoaded(state.runCtx, fn, compactTranscript, fullTranscript), opts);
|
|
447
|
+
if (!Array.isArray(raw) || raw.length === 0) {
|
|
448
|
+
return { ok: false, reason: "empty-check-in" };
|
|
449
|
+
}
|
|
450
|
+
const checkIn = raw
|
|
451
|
+
.filter((cell): cell is EatCell => isEatCellValue(cell))
|
|
452
|
+
.map((cell) => (cell instanceof MessageChunk ? cell.asCheckIn() : cell));
|
|
453
|
+
if (checkIn.length === 0) return { ok: false, reason: "empty-check-in" };
|
|
454
|
+
// Prefix occupancy is the gauge; the check-in is fresh content with no gauge.
|
|
455
|
+
const prefixTokens = compactTranscript.tokensUsed();
|
|
456
|
+
const checkInTokens = contentTokensOf(checkIn);
|
|
457
|
+
if (checkInTokens >= prefixTokens) return { ok: false, reason: "did-not-shrink" };
|
|
458
|
+
return { ok: true, checkIn };
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* The strategy cannot be interrupted mid-eval, so abort stops waiting for it.
|
|
463
|
+
* A summarizer round it opened joins the abort; the driver owns that round.
|
|
464
|
+
*/
|
|
465
|
+
async function abortable(run: () => Promise<unknown>, opts: CompactStrategyOpts): Promise<unknown> {
|
|
466
|
+
const signal = opts.signal;
|
|
467
|
+
if (signal == null) return run();
|
|
468
|
+
const before = new Set(opts.job?.children);
|
|
469
|
+
const pending = run();
|
|
470
|
+
let onAbort = (): void => {};
|
|
471
|
+
const aborted = new Promise<never>((_resolve, reject) => {
|
|
472
|
+
onAbort = () => {
|
|
473
|
+
for (const child of opts.job?.children ?? []) {
|
|
474
|
+
if (before.has(child) || !(child instanceof CompletionJob)) continue;
|
|
475
|
+
const round = child.expectation;
|
|
476
|
+
if (round == null || round.isDone) continue;
|
|
477
|
+
void opts.complete?.({ connectionId: round.connectionId, model: round.model, round, signal }).catch(() => {});
|
|
478
|
+
}
|
|
479
|
+
reject(signal.reason);
|
|
480
|
+
};
|
|
481
|
+
if (signal.aborted) onAbort();
|
|
482
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
483
|
+
});
|
|
484
|
+
try {
|
|
485
|
+
return await Promise.race([pending, aborted]);
|
|
486
|
+
} finally {
|
|
487
|
+
signal.removeEventListener("abort", onAbort);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function isEatCellValue(value: unknown): value is EatCell {
|
|
492
|
+
return (
|
|
493
|
+
typeof value === "string" ||
|
|
494
|
+
value instanceof CompactJob ||
|
|
495
|
+
value instanceof CompletionExpectation ||
|
|
496
|
+
value instanceof CompletionJob ||
|
|
497
|
+
value instanceof ToolCallJob ||
|
|
498
|
+
value instanceof ProgrammaticJob ||
|
|
499
|
+
value instanceof TurnNotificationChunk ||
|
|
500
|
+
value instanceof TurnApprovalNoteChunk ||
|
|
501
|
+
value instanceof TurnSteerChunk ||
|
|
502
|
+
value instanceof MessageChunk
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function kvGet(kv: Record<string, string>, key: string): string | number | false {
|
|
507
|
+
const folded = key.startsWith(":") ? key.slice(1) : key;
|
|
508
|
+
const raw = kv[folded] ?? kv[key] ?? kv[`:${folded}`];
|
|
509
|
+
if (raw === undefined) return false;
|
|
510
|
+
if (folded === "compact.keep-tokens" || folded === "compact.keep-turns") {
|
|
511
|
+
const n = Number(raw);
|
|
512
|
+
return Number.isFinite(n) ? n : raw;
|
|
513
|
+
}
|
|
514
|
+
return raw;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function compactConfig(opts: CompactStrategyOpts, cells: readonly EatCell[]): CompactConfig {
|
|
518
|
+
return {
|
|
519
|
+
chunks: [...cells],
|
|
520
|
+
reason: opts.reason ?? "overflow",
|
|
521
|
+
focus: opts.focus ?? "",
|
|
522
|
+
kv: { ...(opts.kv ?? {}) },
|
|
523
|
+
plan: opts.plan ?? null,
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
async function callLoaded(
|
|
528
|
+
runCtx: RunContext,
|
|
529
|
+
fn: Parameters<typeof applyCallback>[0],
|
|
530
|
+
compactTranscript: Transcript,
|
|
531
|
+
fullTranscript: Transcript,
|
|
532
|
+
): Promise<unknown> {
|
|
533
|
+
const boxed = await applyCallback(
|
|
534
|
+
fn,
|
|
535
|
+
[jsToScheme(runCtx, compactTranscript) as SchemeValue, jsToScheme(runCtx, fullTranscript) as SchemeValue],
|
|
536
|
+
makeCallCtx(runCtx),
|
|
537
|
+
);
|
|
538
|
+
return toJS(boxed);
|
|
539
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compact is the unique well-known session action. Obligatory.
|
|
3
|
+
* Calculations (floors, occupancy, reject, zones) live in harness-context.
|
|
4
|
+
* `applyCompact` is the executing actor in this package.
|
|
5
|
+
*
|
|
6
|
+
* `loadCompactProgram` reads a strategy path through an optional resolve
|
|
7
|
+
* host. Composition supplies package defaults, and the shipped default is a
|
|
8
|
+
* real strategy (a model-written state snapshot, `compact.scm` +
|
|
9
|
+
* `compact.prompt` in the harness package). The inline keep-first program is
|
|
10
|
+
* a FAILSAFE, nothing more: it runs only when the configured program and the
|
|
11
|
+
* shipped default are both missing or unreadable, so that a compact never
|
|
12
|
+
* has nothing to run. It is not a strategy. A program that is returned with
|
|
13
|
+
* `failsafe` set tells the caller to log a diagnostic.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const INLINE_KEEP_FIRST =
|
|
17
|
+
"(define (compact compact-transcript full-transcript) (let ((xs (transcript/cells compact-transcript))) (if (null? xs) xs (list (car xs)))))";
|
|
18
|
+
|
|
19
|
+
export type CompactProgram = {
|
|
20
|
+
readonly source: string;
|
|
21
|
+
/**
|
|
22
|
+
* Absolute path the source was read from. `require` (`.prompt` pretreats) is
|
|
23
|
+
* jailed under its dirname; absent for the inline fallback and a dist stub
|
|
24
|
+
* that has no `compact.prompt` next to it.
|
|
25
|
+
*/
|
|
26
|
+
readonly sourcePath: string | null;
|
|
27
|
+
/**
|
|
28
|
+
* Set only on the inline keep-first failsafe: why no real program could be
|
|
29
|
+
* loaded. The caller must surface it; a silent failsafe would hide a broken
|
|
30
|
+
* install behind a compaction that drops almost everything.
|
|
31
|
+
*/
|
|
32
|
+
readonly failsafe?: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type CompactStrategyResolve = {
|
|
36
|
+
resolvePath(spec: string, kernelFsRoot: string): string;
|
|
37
|
+
defaultSource(): string;
|
|
38
|
+
packageStrategyPath(): string | null;
|
|
39
|
+
readFile(path: string): string;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The loaded strategy. `source` is what `applyCompact` executes; `sourcePath`
|
|
44
|
+
* is the module root for `(require …)` — a strategy ships its `compact.prompt`
|
|
45
|
+
* next to its `.scm`, and `require` is jailed to that directory.
|
|
46
|
+
*/
|
|
47
|
+
export function loadCompactProgram(
|
|
48
|
+
spec?: string,
|
|
49
|
+
kernelFsRoot?: string,
|
|
50
|
+
resolve?: CompactStrategyResolve,
|
|
51
|
+
): CompactProgram {
|
|
52
|
+
const failsafe = (reason: string): CompactProgram => ({
|
|
53
|
+
source: INLINE_KEEP_FIRST,
|
|
54
|
+
sourcePath: null,
|
|
55
|
+
failsafe: reason,
|
|
56
|
+
});
|
|
57
|
+
if (resolve == null) return failsafe("no strategy resolver");
|
|
58
|
+
let configured: string | null = null;
|
|
59
|
+
try {
|
|
60
|
+
if (spec != null && spec.trim() !== "" && kernelFsRoot != null && kernelFsRoot !== "") {
|
|
61
|
+
const path = resolve.resolvePath(spec, kernelFsRoot);
|
|
62
|
+
const loaded = identityOrSource(resolve.readFile(path));
|
|
63
|
+
if (loaded !== "") return { source: loaded, sourcePath: path };
|
|
64
|
+
configured = `configured strategy ${path} is empty`;
|
|
65
|
+
}
|
|
66
|
+
} catch (error) {
|
|
67
|
+
configured = `configured strategy ${spec ?? ""} could not be read: ${errorText(error)}`;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
const source = identityOrSource(resolve.defaultSource());
|
|
71
|
+
if (source !== "") return { source, sourcePath: resolve.packageStrategyPath() };
|
|
72
|
+
return failsafe(`${configured ?? "no strategy configured"}; the shipped default program is empty`);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
return failsafe(
|
|
75
|
+
`${configured ?? "no strategy configured"}; the shipped default program could not be read: ${errorText(error)}`,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function errorText(error: unknown): string {
|
|
81
|
+
return error instanceof Error ? error.message : String(error);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function identityOrSource(source: string): string {
|
|
85
|
+
const code = source
|
|
86
|
+
.split("\n")
|
|
87
|
+
.filter((line) => {
|
|
88
|
+
const trimmed = line.trim();
|
|
89
|
+
return trimmed !== "" && !trimmed.startsWith(";;");
|
|
90
|
+
})
|
|
91
|
+
.join("\n")
|
|
92
|
+
.trim();
|
|
93
|
+
if (code === "" || code === "()") return "";
|
|
94
|
+
return source;
|
|
95
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { type SessionCommandHost } from "./types.js";
|
|
2
|
+
export { failLeftoverApprovables, turnLoopStopped } from "./cancel.js";
|
|
3
|
+
export { abortParkableWork, dispatchedMcpSettled, liveEvalWork, mcpJobOnWire } from "./park.js";
|
|
4
|
+
export { admitCompact, admitTurn, cancelTurnCommand, drainPromptQueue, retryTurn, rewindTurn } from "./commands.js";
|
|
5
|
+
export { type CompactProgram, type CompactStrategyResolve, loadCompactProgram } from "./compact.js";
|
|
6
|
+
export { applyCompact, compactReducerCapability } from "./compact-env.js";
|
|
7
|
+
export { type CompactHost, runCompactJob } from "./compact-actor.js";
|
|
8
|
+
export {
|
|
9
|
+
CONTEXT_NEED_REMINDER,
|
|
10
|
+
joinSystemNotes,
|
|
11
|
+
quietRoundsSince,
|
|
12
|
+
sealedActorRounds,
|
|
13
|
+
SIDE_CHANNEL_REMINDER_EVERY,
|
|
14
|
+
sideChannelReminderDue,
|
|
15
|
+
sideChannelReminderNote,
|
|
16
|
+
TITLE_UPDATE_REMINDER,
|
|
17
|
+
type SideChannelRound,
|
|
18
|
+
} from "./side-channel-reminder.js";
|