@leuduan/lagent 0.0.0-stage → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +16 -0
- package/README.md +144 -2
- package/dist/chunks/2O7ZGEIT.js +1 -0
- package/dist/chunks/2OSESBIL.js +1 -0
- package/dist/chunks/34KW6VNQ.js +1 -0
- package/dist/chunks/37YOGDGY.js +1 -0
- package/dist/chunks/3LQHQRIU.js +6 -0
- package/dist/chunks/3QCW4WDJ.js +1 -0
- package/dist/chunks/4XVRUSCQ.js +1 -0
- package/dist/chunks/6ECI2GGV.js +1 -0
- package/dist/chunks/74HKUGRR.js +1 -0
- package/dist/chunks/ADHERZLW.js +2 -0
- package/dist/chunks/AP6Q4EPT.js +36 -0
- package/dist/chunks/BM3S37EZ.js +1 -0
- package/dist/chunks/C5LYCXJG.js +1 -0
- package/dist/chunks/CDQG7RKA.js +1 -0
- package/dist/chunks/D7E6EWIE.js +3 -0
- package/dist/chunks/DDANMY45.js +3 -0
- package/dist/chunks/DRYAN665.js +1 -0
- package/dist/chunks/DXFXAYDN.js +17 -0
- package/dist/chunks/EGZI4XQX.js +24 -0
- package/dist/chunks/ENWFSU27.js +41 -0
- package/dist/chunks/G6RXC3AL.js +1 -0
- package/dist/chunks/G74AGRAJ.js +15 -0
- package/dist/chunks/GQGNVSKB.js +1 -0
- package/dist/chunks/IJ2WNMD5.js +7 -0
- package/dist/chunks/J5S4URAW.js +1 -0
- package/dist/chunks/JPZFKSWT.js +1 -0
- package/dist/chunks/KESCTXGO.js +5 -0
- package/dist/chunks/L3N4FIEN.js +1 -0
- package/dist/chunks/NAOUTU7A.js +4 -0
- package/dist/chunks/NDIM7MEW.js +1 -0
- package/dist/chunks/O4PPOM5Z.js +2 -0
- package/dist/chunks/OVZFGILJ.js +1 -0
- package/dist/chunks/P5ZHHOOM.js +6 -0
- package/dist/chunks/PUSUPTAT.js +1 -0
- package/dist/chunks/QMMLQJBS.js +1 -0
- package/dist/chunks/RK3VP2Q4.js +3 -0
- package/dist/chunks/SGGMBSBA.js +1 -0
- package/dist/chunks/SL2YLB2F.js +1 -0
- package/dist/chunks/TOOIB6J2.js +1 -0
- package/dist/chunks/TYTQRLTX.js +1 -0
- package/dist/chunks/UC3FVVJ2.js +5 -0
- package/dist/chunks/UCTEU62F.js +1 -0
- package/dist/chunks/UD3VJER6.js +1 -0
- package/dist/chunks/UKXMCHHT.js +1 -0
- package/dist/chunks/WUUKTSRT.js +7 -0
- package/dist/chunks/X65X4KP5.js +7 -0
- package/dist/chunks/YIR5MYGU.js +1 -0
- package/dist/chunks/YLNJXQ3Z.js +1 -0
- package/dist/context.d.ts +101 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +77 -0
- package/dist/journal/catalog.d.ts +112 -0
- package/dist/journal/forkChain.d.ts +16 -0
- package/dist/journal/index.d.ts +15 -0
- package/dist/journal/journal.d.ts +475 -0
- package/dist/journal/projection.d.ts +87 -0
- package/dist/journal/store.d.ts +181 -0
- package/dist/kit/claim.d.ts +13 -0
- package/dist/kit/drain.d.ts +10 -0
- package/dist/kit/extension.d.ts +180 -0
- package/dist/kit/grants.d.ts +9 -0
- package/dist/kit/modes.d.ts +65 -0
- package/dist/kit/retries.d.ts +25 -0
- package/dist/kit/scope.d.ts +32 -0
- package/dist/kit/service.d.ts +7 -0
- package/dist/kit/strategy.d.ts +10 -0
- package/dist/kit/timers.d.ts +43 -0
- package/dist/kit/tool.d.ts +72 -0
- package/dist/llm/client.d.ts +135 -0
- package/dist/llm/content.d.ts +52 -0
- package/dist/llm/door.d.ts +21 -0
- package/dist/llm/door.js +1 -0
- package/dist/llm/failure.d.ts +82 -0
- package/dist/llm/index.d.ts +16 -0
- package/dist/llm/primitives.d.ts +286 -0
- package/dist/llm/prompt.d.ts +21 -0
- package/dist/llm/provider.d.ts +63 -0
- package/dist/llm/readLedger.d.ts +24 -0
- package/dist/llm/request.d.ts +58 -0
- package/dist/loop/agent.d.ts +134 -0
- package/dist/loop/auxiliary.d.ts +54 -0
- package/dist/loop/chain.d.ts +46 -0
- package/dist/loop/chunks.d.ts +27 -0
- package/dist/loop/continuation.d.ts +166 -0
- package/dist/loop/decide.d.ts +64 -0
- package/dist/loop/errors.d.ts +162 -0
- package/dist/loop/forks.d.ts +61 -0
- package/dist/loop/holds.d.ts +104 -0
- package/dist/loop/hooks.d.ts +241 -0
- package/dist/loop/inbox.d.ts +188 -0
- package/dist/loop/index.d.ts +37 -0
- package/dist/loop/limits.d.ts +14 -0
- package/dist/loop/opening.d.ts +42 -0
- package/dist/loop/prepare.d.ts +30 -0
- package/dist/loop/projections.d.ts +307 -0
- package/dist/loop/replace.d.ts +33 -0
- package/dist/loop/requests.d.ts +129 -0
- package/dist/loop/route.d.ts +53 -0
- package/dist/loop/runtime.d.ts +85 -0
- package/dist/loop/state.d.ts +70 -0
- package/dist/loop/strategy.d.ts +71 -0
- package/dist/loop/summary.d.ts +3 -0
- package/dist/loop/surface.d.ts +153 -0
- package/dist/loop/threadState.d.ts +72 -0
- package/dist/loop/tool.d.ts +101 -0
- package/dist/loop/toolCalls.d.ts +42 -0
- package/dist/loop/wake.d.ts +32 -0
- package/dist/models/config/agent.d.ts +18 -0
- package/dist/models/config/prompt.d.ts +18 -0
- package/dist/models/door.d.ts +12 -0
- package/dist/models/door.js +1 -0
- package/dist/models/index.d.ts +7 -0
- package/dist/models/jsonSchema.d.ts +7 -0
- package/dist/models/wire/journal.d.ts +2772 -0
- package/dist/models/wire/primitives.d.ts +65 -0
- package/dist/plugin.d.ts +53 -0
- package/dist/plugin.js +1 -0
- package/dist/plugins/agentsMd.d.ts +76 -0
- package/dist/plugins/agentsMd.js +1 -0
- package/dist/plugins/ask/askQuestion.d.ts +9 -0
- package/dist/plugins/ask/index.d.ts +26 -0
- package/dist/plugins/ask/index.js +1 -0
- package/dist/plugins/ask/service.d.ts +125 -0
- package/dist/plugins/commands/background.d.ts +8 -0
- package/dist/plugins/commands/foreground.d.ts +4 -0
- package/dist/plugins/commands/index.d.ts +40 -0
- package/dist/plugins/commands/index.js +1 -0
- package/dist/plugins/commands/output.d.ts +115 -0
- package/dist/plugins/commands/record.d.ts +549 -0
- package/dist/plugins/commands/service.d.ts +314 -0
- package/dist/plugins/commands/shell.d.ts +25 -0
- package/dist/plugins/compaction/checkpoint.d.ts +91 -0
- package/dist/plugins/compaction/config.d.ts +133 -0
- package/dist/plugins/compaction/estimate.d.ts +29 -0
- package/dist/plugins/compaction/index.d.ts +2 -0
- package/dist/plugins/compaction/index.js +7 -0
- package/dist/plugins/compaction/log.d.ts +61 -0
- package/dist/plugins/compaction/planner.d.ts +207 -0
- package/dist/plugins/compaction/preset.d.ts +86 -0
- package/dist/plugins/compaction/prune.d.ts +9 -0
- package/dist/plugins/compaction/records.d.ts +227 -0
- package/dist/plugins/compaction/segments.d.ts +23 -0
- package/dist/plugins/compaction/shared.d.ts +14 -0
- package/dist/plugins/compaction/summarize.d.ts +13 -0
- package/dist/plugins/dateAndBranch.d.ts +47 -0
- package/dist/plugins/dateAndBranch.js +1 -0
- package/dist/plugins/delegations/answers.d.ts +39 -0
- package/dist/plugins/delegations/assignmentKey.d.ts +22 -0
- package/dist/plugins/delegations/cleanup.d.ts +161 -0
- package/dist/plugins/delegations/constraint.d.ts +76 -0
- package/dist/plugins/delegations/index.d.ts +39 -0
- package/dist/plugins/delegations/index.js +1 -0
- package/dist/plugins/delegations/notices.d.ts +261 -0
- package/dist/plugins/delegations/obligations.d.ts +210 -0
- package/dist/plugins/delegations/record.d.ts +632 -0
- package/dist/plugins/delegations/request.d.ts +381 -0
- package/dist/plugins/delegations/role.d.ts +30 -0
- package/dist/plugins/delegations/service.d.ts +562 -0
- package/dist/plugins/delegations/standing.d.ts +19 -0
- package/dist/plugins/delegations/stopCheck.d.ts +21 -0
- package/dist/plugins/delegations/testing.d.ts +16 -0
- package/dist/plugins/delegations/tools.d.ts +71 -0
- package/dist/plugins/delegations/waitBook.d.ts +116 -0
- package/dist/plugins/delegations/waits.d.ts +118 -0
- package/dist/plugins/diagnostics/digest.d.ts +14 -0
- package/dist/plugins/diagnostics/index.d.ts +68 -0
- package/dist/plugins/diagnostics/index.js +1 -0
- package/dist/plugins/historyRecall/history.d.ts +9 -0
- package/dist/plugins/historyRecall/index.d.ts +31 -0
- package/dist/plugins/historyRecall/index.js +1 -0
- package/dist/plugins/historyRecall/render.d.ts +34 -0
- package/dist/plugins/historyRecall/tools.d.ts +4 -0
- package/dist/plugins/mcp/catalog.d.ts +36 -0
- package/dist/plugins/mcp/config.d.ts +63 -0
- package/dist/plugins/mcp/connection.d.ts +53 -0
- package/dist/plugins/mcp/errors.d.ts +32 -0
- package/dist/plugins/mcp/index.d.ts +33 -0
- package/dist/plugins/mcp/index.js +5 -0
- package/dist/plugins/mcp/mount.d.ts +98 -0
- package/dist/plugins/mcp/names.d.ts +25 -0
- package/dist/plugins/mcp/render.d.ts +29 -0
- package/dist/plugins/mcp/slots.d.ts +15 -0
- package/dist/plugins/mcp/stdio.d.ts +49 -0
- package/dist/plugins/mcp/tool.d.ts +41 -0
- package/dist/plugins/providers/anthropic.d.ts +175 -0
- package/dist/plugins/providers/anthropicMessages.d.ts +65 -0
- package/dist/plugins/providers/codex.d.ts +60 -0
- package/dist/plugins/providers/codexAuth.d.ts +116 -0
- package/dist/plugins/providers/deepseek.d.ts +55 -0
- package/dist/plugins/providers/door.d.ts +61 -0
- package/dist/plugins/providers/door.js +1 -0
- package/dist/plugins/providers/failure.d.ts +23 -0
- package/dist/plugins/providers/gateway.d.ts +23 -0
- package/dist/plugins/providers/index.d.ts +6 -0
- package/dist/plugins/providers/openaiChat.d.ts +62 -0
- package/dist/plugins/providers/openaiResponses.d.ts +100 -0
- package/dist/plugins/providers/opencodeCopilot.d.ts +23 -0
- package/dist/plugins/providers/opencodeGo.d.ts +98 -0
- package/dist/plugins/providers/shared.d.ts +104 -0
- package/dist/plugins/providers/stream.d.ts +154 -0
- package/dist/plugins/routeContext.d.ts +22 -0
- package/dist/plugins/routeContext.js +1 -0
- package/dist/plugins/schedules/index.d.ts +21 -0
- package/dist/plugins/schedules/index.js +5 -0
- package/dist/plugins/schedules/record.d.ts +396 -0
- package/dist/plugins/schedules/service.d.ts +120 -0
- package/dist/plugins/schedules/time.d.ts +26 -0
- package/dist/plugins/schedules/tools.d.ts +6 -0
- package/dist/plugins/skills/discovery.d.ts +54 -0
- package/dist/plugins/skills/index.d.ts +31 -0
- package/dist/plugins/skills/index.js +1 -0
- package/dist/plugins/skills/tool.d.ts +11 -0
- package/dist/plugins/store/durableObject.d.ts +23 -0
- package/dist/plugins/store/durableObject.js +1 -0
- package/dist/plugins/store/durableObjectConnection.d.ts +27 -0
- package/dist/plugins/store/memory.d.ts +29 -0
- package/dist/plugins/store/sql.d.ts +59 -0
- package/dist/plugins/store/sqlite.d.ts +31 -0
- package/dist/plugins/store/sqlite.js +1 -0
- package/dist/plugins/subAgents/availableRoutes.d.ts +53 -0
- package/dist/plugins/subAgents/fork.d.ts +32 -0
- package/dist/plugins/subAgents/index.d.ts +67 -0
- package/dist/plugins/subAgents/index.js +1 -0
- package/dist/plugins/subAgents/tool.d.ts +38 -0
- package/dist/plugins/toolPolicy/index.d.ts +61 -0
- package/dist/plugins/toolPolicy/index.js +1 -0
- package/dist/plugins/tools/editFiles.d.ts +128 -0
- package/dist/plugins/tools/fsRead.d.ts +17 -0
- package/dist/plugins/tools/html.d.ts +23 -0
- package/dist/plugins/tools/index.d.ts +59 -0
- package/dist/plugins/tools/index.js +1 -0
- package/dist/plugins/tools/network.d.ts +23 -0
- package/dist/plugins/tools/ripgrep.d.ts +20 -0
- package/dist/plugins/tools/textEdit.d.ts +67 -0
- package/dist/plugins/tools/web.d.ts +32 -0
- package/dist/plugins/truncateToolOutput.d.ts +26 -0
- package/dist/plugins/truncateToolOutput.js +1 -0
- package/dist/plugins/usage/index.d.ts +24 -0
- package/dist/plugins/usage/index.js +1 -0
- package/dist/plugins/usage/tracker.d.ts +34 -0
- package/dist/plugins/verify/facts.d.ts +356 -0
- package/dist/plugins/verify/index.d.ts +75 -0
- package/dist/plugins/verify/index.js +1 -0
- package/dist/plugins/verify/service.d.ts +90 -0
- package/dist/plugins/workflows/assignment.d.ts +16 -0
- package/dist/plugins/workflows/bindings.d.ts +95 -0
- package/dist/plugins/workflows/cleanup.d.ts +38 -0
- package/dist/plugins/workflows/definition.d.ts +184 -0
- package/dist/plugins/workflows/fold.d.ts +373 -0
- package/dist/plugins/workflows/index.d.ts +28 -0
- package/dist/plugins/workflows/index.js +6 -0
- package/dist/plugins/workflows/observations.d.ts +81 -0
- package/dist/plugins/workflows/participants.d.ts +143 -0
- package/dist/plugins/workflows/policy.d.ts +112 -0
- package/dist/plugins/workflows/providers.d.ts +146 -0
- package/dist/plugins/workflows/record.d.ts +2863 -0
- package/dist/plugins/workflows/routing.d.ts +116 -0
- package/dist/plugins/workflows/schema.d.ts +26 -0
- package/dist/plugins/workflows/service.d.ts +477 -0
- package/dist/plugins/workflows/submit.d.ts +8 -0
- package/dist/plugins/workflows/view.d.ts +103 -0
- package/dist/plugins/workflows/work.d.ts +21 -0
- package/dist/plugins/workspaceContext.d.ts +38 -0
- package/dist/plugins/workspaceContext.js +1 -0
- package/dist/presets/agents.d.ts +7 -0
- package/dist/presets/base.d.ts +10 -0
- package/dist/presets/codex.d.ts +60 -0
- package/dist/presets/index.d.ts +4 -0
- package/dist/presets/prompts.d.ts +56 -0
- package/dist/sandbox/absent.d.ts +32 -0
- package/dist/sandbox/client.d.ts +39 -0
- package/dist/sandbox/files.d.ts +28 -0
- package/dist/sandbox/index.d.ts +49 -0
- package/dist/sandbox/index.js +4 -0
- package/dist/sandbox/mode.d.ts +57 -0
- package/dist/sandbox/policy.d.ts +86 -0
- package/dist/sandbox/protocol.d.ts +107 -0
- package/dist/sandbox/service.d.ts +143 -0
- package/dist/sandbox/worker.d.ts +12 -0
- package/dist/sandbox/worker.js +1 -0
- package/dist/services/admission.d.ts +174 -0
- package/dist/services/agents.d.ts +37 -0
- package/dist/services/birth.d.ts +220 -0
- package/dist/services/contributions.d.ts +73 -0
- package/dist/services/core.d.ts +34 -0
- package/dist/services/journal.d.ts +26 -0
- package/dist/services/launches.d.ts +104 -0
- package/dist/services/llm.d.ts +80 -0
- package/dist/services/owner.d.ts +367 -0
- package/dist/services/preset.d.ts +154 -0
- package/dist/services/presets.d.ts +139 -0
- package/dist/services/prompts.d.ts +67 -0
- package/dist/services/runEvents.d.ts +79 -0
- package/dist/services/thread.d.ts +267 -0
- package/dist/services/threadActivation.d.ts +100 -0
- package/dist/services/threadDirs.d.ts +38 -0
- package/dist/services/threadView.d.ts +92 -0
- package/dist/services/threads.d.ts +449 -0
- package/dist/services/tools.d.ts +41 -0
- package/dist/sys/environment.d.ts +47 -0
- package/dist/sys/git.d.ts +60 -0
- package/dist/sys/paths.d.ts +56 -0
- package/dist/sys/spawn.d.ts +176 -0
- package/dist/testing/clock.d.ts +17 -0
- package/dist/testing/fakeLlm.d.ts +74 -0
- package/dist/testing/fedLlm.d.ts +32 -0
- package/dist/testing/helpers.d.ts +344 -0
- package/dist/testing/index.d.ts +13 -0
- package/dist/testing/index.js +1 -0
- package/dist/testing/processes.d.ts +13 -0
- package/dist/testing/sandbox.d.ts +71 -0
- package/dist/testing/settlement.d.ts +6 -0
- package/dist/testing/tmpDirs.d.ts +17 -0
- package/dist/util/async.d.ts +8 -0
- package/dist/util/diff.d.ts +25 -0
- package/dist/util/dispatch.d.ts +8 -0
- package/dist/util/errors.d.ts +28 -0
- package/dist/util/foldState.d.ts +17 -0
- package/dist/util/freeze.d.ts +2 -0
- package/dist/util/ids.d.ts +2 -0
- package/dist/util/json.d.ts +37 -0
- package/dist/util/log.d.ts +6 -0
- package/dist/util/text.d.ts +21 -0
- package/dist/util/version.d.ts +2 -0
- package/package.json +175 -3
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { SpawnEnvironment } from '../sys/spawn.js';
|
|
2
|
+
import type { CallConfig, PathEntry } from './protocol.js';
|
|
3
|
+
export declare function refuseStartupInjection(environment: SpawnEnvironment): void;
|
|
4
|
+
/** The most command text one launch carries, which travels in the environment of the process that starts it. */
|
|
5
|
+
export declare const MAX_COMMAND_BYTES: number;
|
|
6
|
+
/** `path` with every link in its existing part resolved, so containment is judged on real locations. */
|
|
7
|
+
export declare function canonical(path: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* The root a command's protected names are rebuilt for, canonical. sandbox-runtime substitutes the
|
|
10
|
+
* real prefix before it compiles a pattern, and cannot anchor one this module hands it, so a root
|
|
11
|
+
* whose real path holds glob characters or its compiler's placeholders would turn the patterns
|
|
12
|
+
* below into something else: it is refused, not escaped.
|
|
13
|
+
*/
|
|
14
|
+
export declare function canonicalRoot(root: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* sandbox-runtime's mandatory write denies, rebuilt for `root`: its own set anchors at the host's
|
|
17
|
+
* working directory and only ever adds denies there. Each name once as the path at the root and
|
|
18
|
+
* once as a pattern for the same name anywhere beneath it. `root` is canonical and holds no glob
|
|
19
|
+
* characters (`canonicalRoot`).
|
|
20
|
+
*/
|
|
21
|
+
export declare function mandatoryDenies(root: string): PathEntry[];
|
|
22
|
+
/**
|
|
23
|
+
* The programs a launch runs before confinement, as the search path finds them: the directories
|
|
24
|
+
* searched, since a program dropped into an earlier one would win, and the real file of every
|
|
25
|
+
* candidate for each program, wherever its link leads.
|
|
26
|
+
*/
|
|
27
|
+
export declare function helperLocations(searchPath: string): string[];
|
|
28
|
+
/**
|
|
29
|
+
* Refuses roots a command could use against the sandbox itself: one overlapping, either way, what
|
|
30
|
+
* a launch runs before confinement, or the temporary root, whose directories the host creates
|
|
31
|
+
* unconfined. `roots` are canonical.
|
|
32
|
+
*/
|
|
33
|
+
export declare function refuseUnsafeRoots(roots: readonly string[], trusted: readonly string[], tmpRoot: string): void;
|
|
34
|
+
/** What a command's filesystem policy is made of, besides its root and scratch directory. */
|
|
35
|
+
export type HostFacts = {
|
|
36
|
+
readonly allowWrite: readonly string[];
|
|
37
|
+
readonly denyWrite: readonly string[];
|
|
38
|
+
readonly denyRead: readonly string[];
|
|
39
|
+
readonly dataDir: string;
|
|
40
|
+
/** The paths under the home directory sandbox-runtime keeps writable for itself, as its worker reported them. */
|
|
41
|
+
readonly conveniences: readonly string[];
|
|
42
|
+
readonly privateDirectory: string;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* A command's complete filesystem lists, which replace the initialized ones for the call: writes
|
|
46
|
+
* only in the root, the host's roots and the scratch directory, and never in what the host or
|
|
47
|
+
* sandbox-runtime keeps for itself or in the root's protected names. Every path is literal.
|
|
48
|
+
*/
|
|
49
|
+
export declare function callPolicy(facts: HostFacts, root: string, scratchDirectory: string): CallConfig['filesystem'];
|
|
50
|
+
/**
|
|
51
|
+
* The script the sandbox runs: restores the command's `PATH`, sets its temporary directory and runs
|
|
52
|
+
* it with `/bin/sh -c`. Everything it needs arrives in variables, never spliced into the script.
|
|
53
|
+
*/
|
|
54
|
+
export declare const BOOTSTRAP: string;
|
|
55
|
+
/**
|
|
56
|
+
* The environment of the process that starts the sandbox: the command's own, but with the trusted
|
|
57
|
+
* `PATH` until the sandbox holds, since the wrapped line runs `env` and `sandbox-exec` before
|
|
58
|
+
* confinement. The bootstrap restores the rest.
|
|
59
|
+
*/
|
|
60
|
+
export declare function launchEnvironment(environment: SpawnEnvironment, searchPath: string, scratchDirectory: string, command: string): SpawnEnvironment;
|
|
61
|
+
/** What the name and version say in a command's recorded launch. */
|
|
62
|
+
export declare const LAUNCHER = "lagent-srt";
|
|
63
|
+
export declare const versionOf: (runtime: string) => string;
|
|
64
|
+
/** Everything enforcement depends on, digested: recorded with a launch as its policy. */
|
|
65
|
+
export declare function policyDigest(facts: {
|
|
66
|
+
readonly filesystem: CallConfig['filesystem'];
|
|
67
|
+
readonly network: unknown;
|
|
68
|
+
readonly searchPath: string;
|
|
69
|
+
readonly cwd: string;
|
|
70
|
+
readonly home: string;
|
|
71
|
+
readonly conveniences: readonly string[];
|
|
72
|
+
readonly javaAgent: string;
|
|
73
|
+
readonly version: string;
|
|
74
|
+
}): string;
|
|
75
|
+
/** What the model is told about sandboxed commands. */
|
|
76
|
+
export declare function instructionsFor(config: {
|
|
77
|
+
readonly filesystem: {
|
|
78
|
+
readonly allowWrite: readonly string[];
|
|
79
|
+
};
|
|
80
|
+
readonly network: {
|
|
81
|
+
readonly mode: 'allowlist';
|
|
82
|
+
readonly allowedDomains: readonly string[];
|
|
83
|
+
} | {
|
|
84
|
+
readonly mode: 'unrestricted';
|
|
85
|
+
};
|
|
86
|
+
}): string;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The messages between the sandbox service on the host's main thread and its worker thread
|
|
3
|
+
* (`worker.ts`), which holds sandbox-runtime. Types only, so the worker, which runs from the sources
|
|
4
|
+
* under Node's type stripping as from `dist`, imports nothing of lagent.
|
|
5
|
+
*/
|
|
6
|
+
/** A path as sandbox-runtime takes it: a pattern when it reads as one, or a name marked literal. */
|
|
7
|
+
export type PathEntry = string | {
|
|
8
|
+
readonly path: string;
|
|
9
|
+
readonly literal: true;
|
|
10
|
+
};
|
|
11
|
+
export type ParentProxy = {
|
|
12
|
+
readonly http?: string;
|
|
13
|
+
readonly https?: string;
|
|
14
|
+
readonly noProxy?: string;
|
|
15
|
+
};
|
|
16
|
+
/** The configuration sandbox-runtime is initialized with, once, for the worker's life. */
|
|
17
|
+
export type SrtConfig = {
|
|
18
|
+
readonly network: {
|
|
19
|
+
readonly allowedDomains: readonly string[];
|
|
20
|
+
readonly deniedDomains: readonly string[];
|
|
21
|
+
/** Absent without a configured proxy: sandbox-runtime then reads its environment, which carries none. */
|
|
22
|
+
readonly parentProxy?: ParentProxy;
|
|
23
|
+
}
|
|
24
|
+
/** No `allowedDomains`: sandbox-runtime restricts the network only when it is defined. */
|
|
25
|
+
| {
|
|
26
|
+
readonly allowedDomains?: never;
|
|
27
|
+
readonly deniedDomains: readonly [];
|
|
28
|
+
readonly parentProxy?: never;
|
|
29
|
+
};
|
|
30
|
+
readonly filesystem: {
|
|
31
|
+
readonly denyRead: readonly PathEntry[];
|
|
32
|
+
readonly allowWrite: readonly PathEntry[];
|
|
33
|
+
readonly denyWrite: readonly PathEntry[];
|
|
34
|
+
};
|
|
35
|
+
/** The private copy of its JVM proxy agent: an existing explicit path ends sandbox-runtime's own search. */
|
|
36
|
+
readonly javaAgentJarPath: string;
|
|
37
|
+
};
|
|
38
|
+
/** A command's filesystem policy: complete lists, which replace the initialized ones for the call. */
|
|
39
|
+
export type CallConfig = {
|
|
40
|
+
readonly filesystem: {
|
|
41
|
+
readonly allowWrite: readonly PathEntry[];
|
|
42
|
+
readonly denyWrite: readonly PathEntry[];
|
|
43
|
+
readonly denyRead: readonly PathEntry[];
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
/** What the worker found when it started sandbox-runtime. */
|
|
47
|
+
export type InitReport = {
|
|
48
|
+
readonly version: string;
|
|
49
|
+
readonly supported: boolean;
|
|
50
|
+
readonly errors: readonly string[];
|
|
51
|
+
readonly warnings: readonly string[];
|
|
52
|
+
/** The paths under the home directory that sandbox-runtime keeps writable for its own uses. */
|
|
53
|
+
readonly conveniences: readonly string[];
|
|
54
|
+
/** How many JVM agent flags a wrapped command carries, and whether the one is the private copy. */
|
|
55
|
+
readonly javaAgent: {
|
|
56
|
+
readonly flags: number;
|
|
57
|
+
readonly isCopy: boolean;
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
export type WorkerRequest = {
|
|
61
|
+
readonly type: 'init';
|
|
62
|
+
readonly id: number;
|
|
63
|
+
readonly config: SrtConfig;
|
|
64
|
+
/** What the worker's own environment must say: its private temporary directory and its home. */
|
|
65
|
+
readonly expect: {
|
|
66
|
+
readonly tmpdir: string;
|
|
67
|
+
readonly home: string;
|
|
68
|
+
};
|
|
69
|
+
} | {
|
|
70
|
+
readonly type: 'wrap';
|
|
71
|
+
readonly id: number;
|
|
72
|
+
readonly command: string;
|
|
73
|
+
readonly commandId: string;
|
|
74
|
+
/** The command the wrap stands for, bounded, for sandbox-runtime's reports. */
|
|
75
|
+
readonly commandText: string;
|
|
76
|
+
readonly config: CallConfig;
|
|
77
|
+
}
|
|
78
|
+
/** Abandons the wrap sent with the same id; handled on arrival, not behind queued wraps. */
|
|
79
|
+
| {
|
|
80
|
+
readonly type: 'cancel';
|
|
81
|
+
readonly id: number;
|
|
82
|
+
} | {
|
|
83
|
+
readonly type: 'violations';
|
|
84
|
+
readonly id: number;
|
|
85
|
+
readonly commandId: string;
|
|
86
|
+
} | {
|
|
87
|
+
readonly type: 'reset';
|
|
88
|
+
readonly id: number;
|
|
89
|
+
};
|
|
90
|
+
/** A request as a caller writes it: the client assigns the id. */
|
|
91
|
+
export type WorkerMessage = WorkerRequest extends infer R ? (R extends WorkerRequest ? Omit<R, 'id'> : never) : never;
|
|
92
|
+
/** What each request's reply carries. */
|
|
93
|
+
export type WorkerValues = {
|
|
94
|
+
init: InitReport;
|
|
95
|
+
wrap: string;
|
|
96
|
+
violations: readonly string[];
|
|
97
|
+
reset: null;
|
|
98
|
+
};
|
|
99
|
+
export type WorkerReply = {
|
|
100
|
+
readonly id: number;
|
|
101
|
+
} & ({
|
|
102
|
+
readonly ok: true;
|
|
103
|
+
readonly value: unknown;
|
|
104
|
+
} | {
|
|
105
|
+
readonly ok: false;
|
|
106
|
+
readonly error: string;
|
|
107
|
+
});
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { Service, type Context } from '@leuduan/cordis';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { WriteRows } from '../loop/index.js';
|
|
4
|
+
import { type LaunchHandler, type LaunchRequest, type PreparedLaunch } from '../services/launches.js';
|
|
5
|
+
import { type SandboxMode } from './mode.js';
|
|
6
|
+
export declare const SandboxConfigSchema: z.ZodObject<{
|
|
7
|
+
worker: z.ZodUnion<readonly [z.ZodCustom<import("url").URL, import("url").URL>, z.ZodString]>;
|
|
8
|
+
searchPath: z.ZodString;
|
|
9
|
+
tmpRoot: z.ZodString;
|
|
10
|
+
javaAgentJar: z.ZodString;
|
|
11
|
+
unconfined: z.ZodEnum<{
|
|
12
|
+
refused: "refused";
|
|
13
|
+
allowed: "allowed";
|
|
14
|
+
}>;
|
|
15
|
+
filesystem: z.ZodObject<{
|
|
16
|
+
allowWrite: z.ZodArray<z.ZodString>;
|
|
17
|
+
denyRead: z.ZodArray<z.ZodString>;
|
|
18
|
+
denyWrite: z.ZodArray<z.ZodString>;
|
|
19
|
+
}, z.core.$strict>;
|
|
20
|
+
network: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
21
|
+
mode: z.ZodLiteral<"allowlist">;
|
|
22
|
+
allowedDomains: z.ZodArray<z.ZodString>;
|
|
23
|
+
deniedDomains: z.ZodArray<z.ZodString>;
|
|
24
|
+
parentProxy: z.ZodUnion<readonly [z.ZodLiteral<"none">, z.ZodObject<{
|
|
25
|
+
http: z.ZodOptional<z.ZodURL>;
|
|
26
|
+
https: z.ZodOptional<z.ZodURL>;
|
|
27
|
+
noProxy: z.ZodOptional<z.ZodString>;
|
|
28
|
+
}, z.core.$strict>]>;
|
|
29
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
30
|
+
mode: z.ZodLiteral<"unrestricted">;
|
|
31
|
+
}, z.core.$strict>], "mode">;
|
|
32
|
+
drainTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
33
|
+
}, z.core.$strict>;
|
|
34
|
+
export type SandboxConfigInput = z.input<typeof SandboxConfigSchema>;
|
|
35
|
+
/** Whether the sandbox can confine a launch now: `failed` once its worker died or its start failed, for good. */
|
|
36
|
+
export type SandboxHealth = 'ready' | 'failed';
|
|
37
|
+
declare module '@leuduan/cordis' {
|
|
38
|
+
interface Context {
|
|
39
|
+
/** The OS sandbox for shell commands, the launch handler of `ctx.launches` (optional plugin, `@leuduan/lagent/sandbox`). */
|
|
40
|
+
sandbox: SandboxService;
|
|
41
|
+
}
|
|
42
|
+
interface Events {
|
|
43
|
+
/**
|
|
44
|
+
* The sandbox can no longer confine a launch, so it refuses every one, confined or not: its
|
|
45
|
+
* worker died or it failed to start. For a host that shows conversations' sandbox state.
|
|
46
|
+
*/
|
|
47
|
+
'lagent/sandbox/health'(health: SandboxHealth): void;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The OS sandbox for shell commands, as a service and as the one launch handler of `ctx.launches`.
|
|
52
|
+
* sandbox-runtime runs on a worker thread of this process (`./worker.ts`), started once, and this
|
|
53
|
+
* service owns it: the policy built for each command, the worker's messages and deadlines, and the
|
|
54
|
+
* order of teardown.
|
|
55
|
+
*
|
|
56
|
+
* Each conversation is confined or unconfined, as its durable mode says (`mode`, `setMode`, `opening`),
|
|
57
|
+
* unless the host's `unconfined: 'refused'` confines them all. The mode is read once when a launch
|
|
58
|
+
* starts preparing: a switch applies to launches that start preparing after it, and a launch already
|
|
59
|
+
* preparing or running keeps the policy it selected. An unconfined launch is still the sandbox's
|
|
60
|
+
* handler's, so it stops with it and a failed sandbox refuses it too. The model is told the mode as a
|
|
61
|
+
* notice, never in the prompt.
|
|
62
|
+
*
|
|
63
|
+
* The handler is registered before the worker starts and refuses every launch until the self-check
|
|
64
|
+
* passed, so a sandbox that fails to start never leaves a window in which launches run plain.
|
|
65
|
+
* Teardown releases the handle first, so what it confined ends before its proxy does. sandbox-runtime
|
|
66
|
+
* starts at most once per process, so a failed or disposed service is never restarted. The teardown
|
|
67
|
+
* order and its failure cases are in `docs/sandbox.md`.
|
|
68
|
+
*/
|
|
69
|
+
export declare class SandboxService extends Service<SandboxConfigInput> implements LaunchHandler {
|
|
70
|
+
static inject: string[];
|
|
71
|
+
/** What the model is told about confined commands: part of the notice's fact, so a changed one is told again. */
|
|
72
|
+
private readonly guidance;
|
|
73
|
+
private readonly config;
|
|
74
|
+
private readonly dataDir;
|
|
75
|
+
private state;
|
|
76
|
+
private failure;
|
|
77
|
+
private admitting;
|
|
78
|
+
private client;
|
|
79
|
+
private host;
|
|
80
|
+
private readonly handle;
|
|
81
|
+
private releasing;
|
|
82
|
+
constructor(ctx: Context, config: SandboxConfigInput);
|
|
83
|
+
[Service.init](): Promise<() => Promise<void>>;
|
|
84
|
+
/** The host's gate: whether any conversation may run its commands unconfined. */
|
|
85
|
+
get unconfined(): 'allowed' | 'refused';
|
|
86
|
+
/** Whether the sandbox can confine a launch now. */
|
|
87
|
+
get health(): SandboxHealth;
|
|
88
|
+
/**
|
|
89
|
+
* The mode `threadId` runs its commands in: its conversation's recorded mode, `confined` before
|
|
90
|
+
* one is recorded, and `confined` whatever was recorded when the host refuses unconfined. The one
|
|
91
|
+
* value preparation, the notice and a host's display read.
|
|
92
|
+
*/
|
|
93
|
+
mode(threadId: string): SandboxMode;
|
|
94
|
+
/**
|
|
95
|
+
* Records `mode` for `threadId`'s conversation, when it changes it. Refuses a mode the host's gate
|
|
96
|
+
* refuses, a thread the journal does not hold, and a thread with an origin, which is in its
|
|
97
|
+
* conversation's mode: switch the conversation.
|
|
98
|
+
*/
|
|
99
|
+
setMode(threadId: string, mode: SandboxMode): Promise<void>;
|
|
100
|
+
/**
|
|
101
|
+
* The rows that record `mode` in the write that creates or forks a conversation (`Opening.rows`),
|
|
102
|
+
* always one, so an opening leaves evidence of the choice. The mode and the host's gate are judged
|
|
103
|
+
* here, before anything is written; a thread with an origin is refused inside the write.
|
|
104
|
+
*/
|
|
105
|
+
opening(mode: SandboxMode): WriteRows;
|
|
106
|
+
private refuseGated;
|
|
107
|
+
prepare(request: LaunchRequest): Promise<PreparedLaunch>;
|
|
108
|
+
/**
|
|
109
|
+
* An unconfined launch: what core would have run with no handler, with the provenance that says so.
|
|
110
|
+
* None of the confined machinery runs: no scratch directory, policy, digest, worker request or
|
|
111
|
+
* violation lookup.
|
|
112
|
+
*/
|
|
113
|
+
private plain;
|
|
114
|
+
private start;
|
|
115
|
+
/** Runs a fixed command through the path every command takes, so a sandbox that cannot confine never serves. */
|
|
116
|
+
private selfCheck;
|
|
117
|
+
private requireOpen;
|
|
118
|
+
/**
|
|
119
|
+
* A confined launch from its geometry alone, never a thread, so the self-check builds one with none.
|
|
120
|
+
* `trusted` skips the root checks, for the one launch whose root lies in the temporary root.
|
|
121
|
+
*/
|
|
122
|
+
private build;
|
|
123
|
+
/** What sandbox-runtime recorded against one command: best-effort, from its global tail of recent events. */
|
|
124
|
+
private violationsOf;
|
|
125
|
+
private onWorkerDeath;
|
|
126
|
+
/** Tells the host's listeners the sandbox failed; a listener that throws is reported and never interrupts the caller. */
|
|
127
|
+
private announceFailure;
|
|
128
|
+
/** The unload guard: closes admission at once, then releases what the handler confined. */
|
|
129
|
+
private closeAndRelease;
|
|
130
|
+
/**
|
|
131
|
+
* Releases the handle once, whoever asks: core stops admitting through the handler, aborts what it
|
|
132
|
+
* prepared and waits for the groups. `false`, with the error reported, when a group could not be
|
|
133
|
+
* confirmed stopped, or one still ran at the bound: a confined process may still run, so the worker
|
|
134
|
+
* stays.
|
|
135
|
+
*/
|
|
136
|
+
private releaseHandle;
|
|
137
|
+
/**
|
|
138
|
+
* Resets sandbox-runtime and ends the worker, once the handle was released: freeing its proxy port
|
|
139
|
+
* while a confined process may still run would hand the port to a listener without the
|
|
140
|
+
* allowlist, which that process's profile still permits.
|
|
141
|
+
*/
|
|
142
|
+
private release;
|
|
143
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sandbox worker: a message interface over Anthropic's sandbox-runtime (`SandboxManager`) on a
|
|
3
|
+
* worker thread of the host, and nothing else. It is the one module that loads sandbox-runtime, so
|
|
4
|
+
* the library's process-wide state, its proxies and its signal handlers live here: signals are not
|
|
5
|
+
* delivered to worker threads, and the proxy runs on an event loop that does nothing else.
|
|
6
|
+
*
|
|
7
|
+
* The service (`./service.ts`) starts it with an explicit environment, sends `init` once, then
|
|
8
|
+
* `wrap` for each command, and `reset` last. Self-contained: it imports sandbox-runtime and Node
|
|
9
|
+
* only (types aside), so it runs from the sources under Node's type stripping as from `dist`, and
|
|
10
|
+
* bundles into a single-file executable as an extra entry point.
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{homedir as c,tmpdir as y}from"node:os";import{sep as u}from"node:path";import{parentPort as P}from"node:worker_threads";import{getDefaultWritePaths as S,NetworkConfigSchema as _,SandboxManager as n}from"@anthropic-ai/sandbox-runtime";import b from"@anthropic-ai/sandbox-runtime/package.json"with{type:"json"};var A=20,E=500,T=2e3,j=["HTTP_PROXY","HTTPS_PROXY","NO_PROXY","http_proxy","https_proxy","no_proxy"],a=P;if(a===null)throw new Error("lagent sandbox worker: started outside a worker thread");var d=new Map;a.on("message",e=>{I(e)});async function I(e){try{switch(e.type){case"init":return i(e.id,await O(e));case"wrap":{let o=new AbortController;d.set(e.id,o);try{let t=await n.wrapWithSandbox(e.command,"/bin/sh",e.config,o.signal,{commandId:e.commandId,commandText:e.commandText});return i(e.id,t)}finally{d.delete(e.id)}}case"cancel":d.get(e.id)?.abort();return;case"violations":{let o=n.getSandboxViolationStore().getViolationsForCommand(e.commandId);return i(e.id,o.slice(-A).map(t=>t.line.slice(0,E)))}case"reset":{let o;return await Promise.race([n.reset().catch(()=>{}),new Promise(t=>{o=setTimeout(t,T)})]),clearTimeout(o),i(e.id,null)}}}catch(o){let t={id:e.id,ok:!1,error:o instanceof Error?o.message:String(o)};a.postMessage(t)}}function i(e,o){let t={id:e,ok:!0,value:o};a.postMessage(t)}async function O(e){let{config:o,expect:t}=e;if(o.network.allowedDomains!==void 0){let r=_.safeParse(o.network);if(!r.success){let v=r.error.issues.map(g=>`${g.path.join(".")}: ${g.message}`);throw new Error(`sandbox-runtime rejects the network configuration: ${v.join("; ")}`)}}if(y()!==t.tmpdir)throw new Error(`the worker's temporary directory is ${y()}, not the private directory ${t.tmpdir}`);if(c()!==t.home)throw new Error(`the worker's home directory is ${c()}, not ${t.home}`);let m=o.network.parentProxy===void 0?j.filter(r=>r in process.env):[];if(m.length>0)throw new Error(`the worker's environment sets ${m.join(", ")} but no parent proxy is configured`);let p=n.isSupportedPlatform(),{errors:l,warnings:x}=n.checkDependencies(),h={version:b.version,supported:p,errors:l,warnings:x,conveniences:[],javaAgent:{flags:0,isCopy:!1}};if(!p||l.length>0)return h;await n.initialize(o);let s=c(),k=S({denyRead:o.filesystem.denyRead}).filter(r=>r.startsWith(s.endsWith(u)?s:s+u)),w=await n.wrapWithSandbox("true","/bin/sh"),f=w.split("-javaagent:").length-1;return{...h,conveniences:k,javaAgent:{flags:f,isCopy:f===1&&w.includes(`-javaagent:${o.javaAgentJarPath}`)}}}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import type { Lease } from '../loop/index.js';
|
|
2
|
+
import type { CancelIntent, InboxTarget } from '../models/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* An exclusive operation on one thread; see `Admission.onOperation`. A `cancel` holds the thread from a
|
|
5
|
+
* `cancel()` until its write has landed and what it cancelled has released the thread.
|
|
6
|
+
*/
|
|
7
|
+
export type OperationKind = 'run' | 'compact' | 'rebuild' | 'configure' | 'release' | 'cancel';
|
|
8
|
+
/** How an operation ended: as asked, by throwing, or because it was cancelled or lagent closed. */
|
|
9
|
+
export type OperationEnding = 'completed' | 'failed' | 'cancelled';
|
|
10
|
+
/** What holds a thread: the operation's kind, and the run request a run serves (`null` for any other). */
|
|
11
|
+
export type OperationHolder = {
|
|
12
|
+
readonly kind: OperationKind;
|
|
13
|
+
readonly runId: string | null;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* What `onOperation` listeners are told: an operation was admitted on a thread, or it settled and
|
|
17
|
+
* how. Process-local and never a journal row; `settled` is published once admission is released,
|
|
18
|
+
* so a listener may start the thread's next operation from it.
|
|
19
|
+
*/
|
|
20
|
+
export type OperationEvent = {
|
|
21
|
+
readonly thread_id: string;
|
|
22
|
+
readonly kind: OperationKind;
|
|
23
|
+
readonly phase: 'admitted';
|
|
24
|
+
} | {
|
|
25
|
+
readonly thread_id: string;
|
|
26
|
+
readonly kind: OperationKind;
|
|
27
|
+
readonly phase: 'settled';
|
|
28
|
+
readonly ended: OperationEnding;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* An admitted operation as the code running it sees it: what it is, and the signal and lease it
|
|
32
|
+
* hands to the work. Everything else about it is the owner's.
|
|
33
|
+
*/
|
|
34
|
+
export type AdmittedOperation = {
|
|
35
|
+
readonly threadId: string;
|
|
36
|
+
readonly kind: OperationKind;
|
|
37
|
+
/** The run request a run serves; `null` for any other operation. */
|
|
38
|
+
readonly runId: string | null;
|
|
39
|
+
readonly signal: AbortSignal;
|
|
40
|
+
readonly lease: Lease;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* What a thread's handle and its activation ask the owner: whether it runs, where input waits, the
|
|
44
|
+
* cancel, and the unload guard's wait. Nothing that admits or settles an operation.
|
|
45
|
+
*/
|
|
46
|
+
export type AdmissionView = Pick<Admission, 'isRunning' | 'inputTarget' | 'cancel' | 'whenSettled' | 'isAdmitted'>;
|
|
47
|
+
/**
|
|
48
|
+
* What one `cancel()` gets back: the run of the execution the thread's cancel captured, if it
|
|
49
|
+
* captured a run, and the one settlement of that cancel. `wrote` reports the call's own write,
|
|
50
|
+
* once it is queued, and only then aborts what the call captured, so a cancel an abort listener
|
|
51
|
+
* makes is written after this one. `capturedSettles` says, read inside that write, whether the
|
|
52
|
+
* captured execution's finalization is still to build its run's settlement: until then the
|
|
53
|
+
* finalization settles it by the cancel's intent; after, what it built stands, and a run it left
|
|
54
|
+
* pending is the cancel's to settle.
|
|
55
|
+
*/
|
|
56
|
+
export type CancelHandle = {
|
|
57
|
+
readonly capturedRunId: string | null;
|
|
58
|
+
readonly settled: Promise<void>;
|
|
59
|
+
capturedSettles(): boolean;
|
|
60
|
+
wrote(write: Promise<unknown>): void;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* The one owner of which operation holds each thread: admission or refusal, the releasing phase,
|
|
64
|
+
* cancellation, and the publication of admission and settlement in the order
|
|
65
|
+
* docs/lifecycle.md#ordering states. It knows nothing of activations; the threads service runs
|
|
66
|
+
* each operation's work, and disposes activations under it, through `hold`.
|
|
67
|
+
*/
|
|
68
|
+
export declare class Admission {
|
|
69
|
+
/**
|
|
70
|
+
* The one operation admitted on each thread, from before its first `await` until its work and
|
|
71
|
+
* cleanup settled: what `cancel()` aborts and awaits, what refuses a second operation as busy,
|
|
72
|
+
* and what says whether input joins a run's next step.
|
|
73
|
+
*/
|
|
74
|
+
private readonly admitted;
|
|
75
|
+
/** The cancel in progress on each thread, from its first call until it settles. */
|
|
76
|
+
private readonly cancels;
|
|
77
|
+
private readonly listeners;
|
|
78
|
+
private readonly closing;
|
|
79
|
+
private readonly report;
|
|
80
|
+
/**
|
|
81
|
+
* @param closing aborts once lagent is closing: nothing is admitted after, and it is composed
|
|
82
|
+
* into every operation's signal.
|
|
83
|
+
*/
|
|
84
|
+
constructor(closing: AbortSignal, report: (error: unknown) => void);
|
|
85
|
+
/** Refuses as admission would, without admitting: once lagent is closing, or busy while an operation holds the thread. */
|
|
86
|
+
requireFree(threadId: string): void;
|
|
87
|
+
/**
|
|
88
|
+
* Admits `kind` on the thread, or refuses it busy, synchronously, and publishes `admitted` before
|
|
89
|
+
* returning. The operation's signal composes shutdown's, `cancel()`'s and the caller's; its lease
|
|
90
|
+
* says whether it still holds the thread, for a callback that escaped it. A configuration change
|
|
91
|
+
* is admitted releasing: it starts by releasing the live activation, before anything can see it.
|
|
92
|
+
*/
|
|
93
|
+
admit(threadId: string, kind: OperationKind, options?: {
|
|
94
|
+
readonly signal?: AbortSignal | undefined;
|
|
95
|
+
readonly runId?: string | undefined;
|
|
96
|
+
}): AdmittedOperation;
|
|
97
|
+
private operation;
|
|
98
|
+
/**
|
|
99
|
+
* Runs `work` as the admitted operation, then the disposal `cleanup` returns, if any, then its
|
|
100
|
+
* `finalize`, if any, both still under it, then releases admission and publishes `settled` in the
|
|
101
|
+
* same synchronous step, however `work` ended. `finalize` runs in the releasing phase: the
|
|
102
|
+
* operation still holds the thread, so nothing else is admitted and nothing mounts, but it executes
|
|
103
|
+
* nothing more, and input sent meanwhile waits for the next turn. It runs again when a cancel
|
|
104
|
+
* captured the operation while it ran, so it must be safe to repeat. Without either step nothing is
|
|
105
|
+
* awaited between the work's end and the release. The value `work` returns is its own, taken
|
|
106
|
+
* before admission is released. A failing finalization or disposal is reported; the admission is
|
|
107
|
+
* released all the same.
|
|
108
|
+
*/
|
|
109
|
+
hold<T>(admitted: AdmittedOperation, work: () => Promise<T>, steps?: {
|
|
110
|
+
readonly finalize?: () => Promise<void>;
|
|
111
|
+
readonly cleanup?: () => Promise<void> | undefined;
|
|
112
|
+
}): Promise<T>;
|
|
113
|
+
/**
|
|
114
|
+
* Enters or leaves the operation's releasing phase; entering wakes whatever waits for it to stop
|
|
115
|
+
* executing. Set by whoever disposes an activation under the operation, before the disposal starts.
|
|
116
|
+
*/
|
|
117
|
+
setReleasing(admitted: AdmittedOperation, releasing: boolean): void;
|
|
118
|
+
/**
|
|
119
|
+
* Starts the thread's cancel, or joins the one in progress, synchronously. The first call captures
|
|
120
|
+
* the operation admitted now, if any, and marks it with its intent; `wrote` aborts it (a release is
|
|
121
|
+
* waited for, never aborted). The run that operation serves settles by the intent of the first
|
|
122
|
+
* cancel that reached it. With nothing
|
|
123
|
+
* admitted, a `cancel` operation holds the thread at once, closing or not, since a shutdown still
|
|
124
|
+
* cancels. Either way the cancel holds the thread, refusing a run as busy, until every joined
|
|
125
|
+
* call's write has finished, landed or failed (`wrote`), and the captured operation has released;
|
|
126
|
+
* its `settled` then resolves, once for every call.
|
|
127
|
+
*/
|
|
128
|
+
cancel(threadId: string, reason: string, intent: CancelIntent): CancelHandle;
|
|
129
|
+
/** The intent of the first cancel that captured `admitted`, or `null`: what the run it serves settles by. */
|
|
130
|
+
cancelIntent(admitted: AdmittedOperation): CancelIntent | null;
|
|
131
|
+
/**
|
|
132
|
+
* Records, inside the finalization's write, that `admitted` built its run's settlement: a cancel
|
|
133
|
+
* written after it settles a run that build left pending.
|
|
134
|
+
*/
|
|
135
|
+
settlementBuilt(admitted: AdmittedOperation): void;
|
|
136
|
+
/** The thread's cancel holds the thread as a `cancel` operation of its own; published by the caller. */
|
|
137
|
+
private holdForCancel;
|
|
138
|
+
private wroteCancel;
|
|
139
|
+
/** Settles the thread's cancel once every joined write finished and what it captured released. */
|
|
140
|
+
private finishCancel;
|
|
141
|
+
/** What holds the thread now, or `null`. */
|
|
142
|
+
holder(threadId: string): OperationHolder | null;
|
|
143
|
+
/** Whether any operation holds the thread. */
|
|
144
|
+
isAdmitted(threadId: string): boolean;
|
|
145
|
+
/** Whether a run or a compaction holds the thread: what `Thread.status()` calls running. */
|
|
146
|
+
isRunning(threadId: string): boolean;
|
|
147
|
+
/**
|
|
148
|
+
* Whether the operation holding the thread is disposing its activation, a release or another in
|
|
149
|
+
* its releasing phase: nothing of the thread may be mounted meanwhile, and it executes nothing.
|
|
150
|
+
*/
|
|
151
|
+
isReleasing(threadId: string): boolean;
|
|
152
|
+
/** Where input sent now waits: a run's next step while a run holds the thread and is not cancelled, else the next turn. */
|
|
153
|
+
inputTarget(threadId: string): InboxTarget;
|
|
154
|
+
/** Resolves once the operation holding the thread now, if any, has settled. */
|
|
155
|
+
settlement(threadId: string): Promise<void>;
|
|
156
|
+
/**
|
|
157
|
+
* Resolves once no operation that executes holds the thread: what an activation waits for before
|
|
158
|
+
* it disposes, so a scope never goes under a running loop. An operation disposing the activation
|
|
159
|
+
* is not waited for, since that would be the disposal waiting on itself.
|
|
160
|
+
*/
|
|
161
|
+
whenSettled(threadId: string): Promise<void>;
|
|
162
|
+
/**
|
|
163
|
+
* Resolves once no operation holds any thread and no cancel is in progress: what shutdown waits
|
|
164
|
+
* for before releasing scopes. Read again after each wait, since a cancel takes over a thread its
|
|
165
|
+
* captured operation releases, and a shutdown still cancels.
|
|
166
|
+
*/
|
|
167
|
+
drain(): Promise<void>;
|
|
168
|
+
/** Tells `listener` when an operation is admitted on any thread and when it settled; see {@link OperationEvent}. */
|
|
169
|
+
onOperation(listener: (event: OperationEvent) => void): () => void;
|
|
170
|
+
/** The owner's record of `admitted`, which must hold its thread: anything else is a caller bug. */
|
|
171
|
+
private holding;
|
|
172
|
+
/** One frozen copy for every listener, so no listener can change what the next one sees. */
|
|
173
|
+
private publish;
|
|
174
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Context, Service, type ScopeKey } from '@leuduan/cordis';
|
|
2
|
+
import type { AgentDefinition, AgentDefinitionInput } from '../models/index.js';
|
|
3
|
+
/** A definition and the layer that registered it: `undefined` for the global one. */
|
|
4
|
+
export type AgentResolution = {
|
|
5
|
+
readonly agent: AgentDefinition;
|
|
6
|
+
readonly layer: ScopeKey | undefined;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* The agents this process can run. Live: a registration lasts exactly as long as the plugin
|
|
10
|
+
* that made it, and a thread resolves its agent from the scope it runs in, not when it was created.
|
|
11
|
+
*
|
|
12
|
+
* Layered by Cordis scope like tools: an untagged registration is global, a scoped one is visible
|
|
13
|
+
* from that scope and its descendants, and the closest definition of a name wins. Global and scoped
|
|
14
|
+
* names are disjoint (`isScopedAgentName`): a scoped name contains a dot and a global one does not,
|
|
15
|
+
* so a scoped agent can never answer to a name a global one has, or will have.
|
|
16
|
+
*/
|
|
17
|
+
export declare class AgentsService extends Service {
|
|
18
|
+
private readonly layers;
|
|
19
|
+
constructor(ctx: Context);
|
|
20
|
+
/** Agent names visible from `scope`, sorted and deduplicated. */
|
|
21
|
+
names(scope: ScopeKey | undefined): string[];
|
|
22
|
+
/**
|
|
23
|
+
* Every agent visible from `scope`, each name once with its closest definition: the global
|
|
24
|
+
* layer's first, then each closer layer's, each in registration order.
|
|
25
|
+
*/
|
|
26
|
+
all(scope: ScopeKey | undefined): AgentDefinition[];
|
|
27
|
+
has(scope: ScopeKey | undefined, name: string): boolean;
|
|
28
|
+
get(scope: ScopeKey | undefined, name: string): AgentDefinition | undefined;
|
|
29
|
+
/** The definition, or a thrown `UnknownAgentError`. */
|
|
30
|
+
require(scope: ScopeKey | undefined, name: string): AgentDefinition;
|
|
31
|
+
/** The closest definition of `name` visible from `scope`, with the layer that registered it. */
|
|
32
|
+
resolve(scope: ScopeKey | undefined, name: string): AgentResolution | undefined;
|
|
33
|
+
/** Adds an agent to one layer; returns the unregister function. Duplicates fail within that layer. */
|
|
34
|
+
register(scope: ScopeKey | undefined, agent: AgentDefinitionInput): () => void;
|
|
35
|
+
}
|
|
36
|
+
/** Registers an agent in the calling plugin's layer for the lifetime of its fiber. */
|
|
37
|
+
export declare const registerAgent: (ctx: Context, agent: AgentDefinitionInput) => (() => void | Promise<void>);
|