@dynamicagents/core 0.9.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 +201 -0
- package/README.md +522 -0
- package/dist/a2a/agent-stub.d.ts +60 -0
- package/dist/a2a/agent-stub.js +1 -0
- package/dist/a2a/caller-token.d.ts +44 -0
- package/dist/a2a/caller-token.js +61 -0
- package/dist/a2a/caller.d.ts +23 -0
- package/dist/a2a/caller.js +33 -0
- package/dist/a2a/card.d.ts +158 -0
- package/dist/a2a/card.js +163 -0
- package/dist/a2a/context.d.ts +19 -0
- package/dist/a2a/context.js +61 -0
- package/dist/a2a/deliver.d.ts +159 -0
- package/dist/a2a/deliver.js +185 -0
- package/dist/a2a/executor.d.ts +84 -0
- package/dist/a2a/executor.js +106 -0
- package/dist/a2a/index.d.ts +35 -0
- package/dist/a2a/index.js +34 -0
- package/dist/a2a/notify.d.ts +106 -0
- package/dist/a2a/notify.js +180 -0
- package/dist/a2a/parts.d.ts +45 -0
- package/dist/a2a/parts.js +94 -0
- package/dist/a2a/push.d.ts +70 -0
- package/dist/a2a/push.js +53 -0
- package/dist/a2a/self-origin.d.ts +91 -0
- package/dist/a2a/self-origin.js +114 -0
- package/dist/a2a/task-store.d.ts +44 -0
- package/dist/a2a/task-store.js +99 -0
- package/dist/a2a/task.d.ts +85 -0
- package/dist/a2a/task.js +15 -0
- package/dist/a2a/verify.d.ts +80 -0
- package/dist/a2a/verify.js +143 -0
- package/dist/agent/budget.d.ts +46 -0
- package/dist/agent/budget.js +42 -0
- package/dist/agent/control.d.ts +109 -0
- package/dist/agent/control.js +115 -0
- package/dist/agent/errors.d.ts +85 -0
- package/dist/agent/errors.js +64 -0
- package/dist/agent/final-reply.d.ts +49 -0
- package/dist/agent/final-reply.js +68 -0
- package/dist/agent/history.d.ts +97 -0
- package/dist/agent/history.js +133 -0
- package/dist/agent/index.d.ts +29 -0
- package/dist/agent/index.js +29 -0
- package/dist/agent/inference.d.ts +110 -0
- package/dist/agent/inference.js +120 -0
- package/dist/agent/model.d.ts +90 -0
- package/dist/agent/model.js +1 -0
- package/dist/agent/session.d.ts +100 -0
- package/dist/agent/session.js +82 -0
- package/dist/agent/workers-ai/index.d.ts +23 -0
- package/dist/agent/workers-ai/index.js +23 -0
- package/dist/agent/workers-ai/runtime.d.ts +42 -0
- package/dist/agent/workers-ai/runtime.js +63 -0
- package/dist/alarm/index.d.ts +77 -0
- package/dist/alarm/index.js +116 -0
- package/dist/config.d.ts +202 -0
- package/dist/config.js +135 -0
- package/dist/contract/index.d.ts +9 -0
- package/dist/contract/index.js +8 -0
- package/dist/contract/plugin.d.ts +324 -0
- package/dist/contract/plugin.js +114 -0
- package/dist/contract/recipe.d.ts +180 -0
- package/dist/contract/recipe.js +1 -0
- package/dist/contract/validation.d.ts +91 -0
- package/dist/contract/validation.js +84 -0
- package/dist/db/db.d.ts +147 -0
- package/dist/db/db.js +90 -0
- package/dist/db/index.d.ts +8 -0
- package/dist/db/index.js +8 -0
- package/dist/db/migrations/index.d.ts +20 -0
- package/dist/db/migrations/index.js +60 -0
- package/dist/db/models/subtasks.d.ts +100 -0
- package/dist/db/models/subtasks.js +241 -0
- package/dist/db/models/tasks.d.ts +118 -0
- package/dist/db/models/tasks.js +274 -0
- package/dist/db/schema.d.ts +468 -0
- package/dist/db/schema.js +88 -0
- package/dist/env.d.ts +53 -0
- package/dist/env.js +47 -0
- package/dist/host/agent.d.ts +305 -0
- package/dist/host/agent.js +400 -0
- package/dist/host/index.d.ts +20 -0
- package/dist/host/index.js +19 -0
- package/dist/host/plugin-host.d.ts +42 -0
- package/dist/host/plugin-host.js +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +17 -0
- package/dist/job/index.d.ts +21 -0
- package/dist/job/index.js +21 -0
- package/dist/job/lifecycle.d.ts +176 -0
- package/dist/job/lifecycle.js +230 -0
- package/dist/job/state.d.ts +92 -0
- package/dist/job/state.js +40 -0
- package/dist/platform.d.ts +138 -0
- package/dist/platform.js +140 -0
- package/dist/round/agent.d.ts +271 -0
- package/dist/round/agent.js +678 -0
- package/dist/round/index.d.ts +25 -0
- package/dist/round/index.js +23 -0
- package/dist/round/policy.d.ts +98 -0
- package/dist/round/policy.js +1 -0
- package/dist/round/subagent.d.ts +87 -0
- package/dist/round/subagent.js +108 -0
- package/dist/round/turn.d.ts +249 -0
- package/dist/round/turn.js +564 -0
- package/dist/round/workflow.d.ts +147 -0
- package/dist/round/workflow.js +387 -0
- package/dist/runtime/index.d.ts +127 -0
- package/dist/runtime/index.js +186 -0
- package/dist/runtime/tool-families.d.ts +32 -0
- package/dist/runtime/tool-families.js +61 -0
- package/dist/subagent/fingerprint.d.ts +37 -0
- package/dist/subagent/fingerprint.js +92 -0
- package/dist/subagent/index.d.ts +169 -0
- package/dist/subagent/index.js +330 -0
- package/dist/subagent/prompt.d.ts +32 -0
- package/dist/subagent/prompt.js +37 -0
- package/dist/subagent/run.d.ts +157 -0
- package/dist/subagent/run.js +540 -0
- package/dist/subagent/workspace.d.ts +85 -0
- package/dist/subagent/workspace.js +127 -0
- package/dist/subtasks/catalog.d.ts +40 -0
- package/dist/subtasks/catalog.js +36 -0
- package/dist/subtasks/decomposition.d.ts +85 -0
- package/dist/subtasks/decomposition.js +156 -0
- package/dist/subtasks/delegate.d.ts +120 -0
- package/dist/subtasks/delegate.js +131 -0
- package/dist/subtasks/index.d.ts +9 -0
- package/dist/subtasks/index.js +9 -0
- package/dist/subtasks/subtask-types.d.ts +91 -0
- package/dist/subtasks/subtask-types.js +103 -0
- package/dist/subtasks/types.d.ts +295 -0
- package/dist/subtasks/types.js +15 -0
- package/dist/testing/auth.d.ts +34 -0
- package/dist/testing/auth.js +35 -0
- package/dist/testing/do.d.ts +29 -0
- package/dist/testing/do.js +25 -0
- package/dist/testing/fake-session.d.ts +26 -0
- package/dist/testing/fake-session.js +37 -0
- package/dist/testing/fixtures.d.ts +64 -0
- package/dist/testing/fixtures.js +104 -0
- package/dist/testing/harness.d.ts +97 -0
- package/dist/testing/harness.js +138 -0
- package/dist/testing/index.d.ts +31 -0
- package/dist/testing/index.js +35 -0
- package/dist/testing/mock-model.d.ts +77 -0
- package/dist/testing/mock-model.js +136 -0
- package/dist/testing/node.d.ts +56 -0
- package/dist/testing/node.js +56 -0
- package/dist/testing/vcr-global-setup.d.ts +12 -0
- package/dist/testing/vcr-global-setup.js +15 -0
- package/dist/testing/vcr-shared.d.ts +38 -0
- package/dist/testing/vcr-shared.js +33 -0
- package/dist/testing/vcr-spec.d.ts +25 -0
- package/dist/testing/vcr-spec.js +124 -0
- package/dist/testing/vcr-store.d.ts +86 -0
- package/dist/testing/vcr-store.js +191 -0
- package/dist/testing/vcr.d.ts +117 -0
- package/dist/testing/vcr.js +275 -0
- package/dist/worker/define-agent.d.ts +123 -0
- package/dist/worker/define-agent.js +20 -0
- package/dist/worker/index.d.ts +218 -0
- package/dist/worker/index.js +369 -0
- package/eslint-rules/index.js +31 -0
- package/eslint-rules/no-deprecated-object-properties.js +81 -0
- package/package.json +178 -0
- package/scripts/generate-keys.mjs +48 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import type { AgentLimits } from "../config.js";
|
|
3
|
+
/**
|
|
4
|
+
* What a recipe domain declares.
|
|
5
|
+
*
|
|
6
|
+
* These types flow **outward**: the runtime consumes them, and nothing in this
|
|
7
|
+
* file imports from the runtime. That direction is the whole reason a plugin can
|
|
8
|
+
* live in a separate package — a new domain is a new module plus one line in the
|
|
9
|
+
* consuming agent's plugin list, with no edit inside core.
|
|
10
|
+
*
|
|
11
|
+
* Two shapes, and the distinction between them carries real weight:
|
|
12
|
+
*
|
|
13
|
+
* - A **type** ({@link SubtaskTypeSpec}) is the semantic contract of a unit of
|
|
14
|
+
* work: what it means, and what it must be given to be doable at all.
|
|
15
|
+
* - A **Recipe** ({@link ResolvedRecipe}) is the execution *configuration* —
|
|
16
|
+
* models, soul, tool families, budgets. It declares no params, and one Recipe
|
|
17
|
+
* may serve several types.
|
|
18
|
+
*/
|
|
19
|
+
/** Params carried by a Subtask: model-chosen, string-valued, shape-checked. */
|
|
20
|
+
export type SubtaskParams = Record<string, string>;
|
|
21
|
+
/**
|
|
22
|
+
* A type's param declaration, as a shape the agent can read back — not an opaque
|
|
23
|
+
* validator. The keys have to be *enumerable* because the delegate tool's schema
|
|
24
|
+
* is built from them: a param the model cannot see declared is a param it will
|
|
25
|
+
* not send. See {@link SubtaskTypeSpec.params}.
|
|
26
|
+
*/
|
|
27
|
+
export type SubtaskParamsShape = Record<string, z.ZodType<string>>;
|
|
28
|
+
export type SubtaskParamsSchema = z.ZodObject<SubtaskParamsShape>;
|
|
29
|
+
/**
|
|
30
|
+
* The control-tool names the agent injects when it renders a type's
|
|
31
|
+
* {@link SubtaskTypeSpec.delegationGuidance}.
|
|
32
|
+
*
|
|
33
|
+
* Guidance legitimately names those tools ("ask the user with `final_reply`
|
|
34
|
+
* rather than guessing"), and their names are the agent's to own. Injecting them
|
|
35
|
+
* is what lets a domain write that sentence without importing from the runtime —
|
|
36
|
+
* the direction this module depends on.
|
|
37
|
+
*/
|
|
38
|
+
export interface DelegationNames {
|
|
39
|
+
delegateTool: string;
|
|
40
|
+
finalReplyTool: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The execution budget for one Recipe, enforced by the resumable runner (not the
|
|
44
|
+
* Workflow). Exactly two fields, because there are exactly two things worth
|
|
45
|
+
* bounding: what an execution **costs** and how long it can **run away for**. The
|
|
46
|
+
* run ends on whichever it reaches first, and either way through the graceful
|
|
47
|
+
* budget summary.
|
|
48
|
+
*
|
|
49
|
+
* Deliberately *not* here: how a run is sliced into durable chunks. That is a
|
|
50
|
+
* Workers step-timeout constraint, it is identical for every Recipe, and it lives
|
|
51
|
+
* in {@link file://../platform.ts}. A turn count cannot do that job — nothing
|
|
52
|
+
* predicts how long a turn takes — so no per-chunk turn budget belongs on a
|
|
53
|
+
* Recipe.
|
|
54
|
+
*
|
|
55
|
+
* Structurally identical to {@link AgentLimits} — a budget is a budget at both
|
|
56
|
+
* levels, and keeping one shape means `resolveLimits` merges a recipe's
|
|
57
|
+
* declaration straight over the host's baseline with no translation. Aliased
|
|
58
|
+
* rather than redeclared so the two can never drift apart.
|
|
59
|
+
*/
|
|
60
|
+
export type RecipeLimits = AgentLimits;
|
|
61
|
+
/**
|
|
62
|
+
* A Recipe configuration as a domain **declares** it, one per folder in
|
|
63
|
+
* `recipes/<domain>/recipe.ts`; caller-local DB rows mapping into this shape are
|
|
64
|
+
* deferred until a Recipe admin surface exists. Model ids, tool families, and
|
|
65
|
+
* limits are code-validated downstream
|
|
66
|
+
* ({@link file://./validation.ts validateRecipe}), which is also what turns this
|
|
67
|
+
* into a {@link ValidatedRecipe}.
|
|
68
|
+
*
|
|
69
|
+
* One rule governs every field here, and it is worth stating once: **if
|
|
70
|
+
* `config.ts` declares a baseline, a Recipe overrides it; if it does not, the
|
|
71
|
+
* Recipe must supply it.** So `limits` is partial and merges, while `soul` and
|
|
72
|
+
* `historyWindow` are required and a missing one is refused rather than filled in.
|
|
73
|
+
*/
|
|
74
|
+
export interface ResolvedRecipe {
|
|
75
|
+
key: string;
|
|
76
|
+
version: number;
|
|
77
|
+
/** Required, never defaulted — see `validateRecipe`. */
|
|
78
|
+
soul: string;
|
|
79
|
+
toolFamilies: string[];
|
|
80
|
+
enabled: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Only the budget fields this Recipe overrides; the rest come from the host's
|
|
83
|
+
* `CoreConfig.subagentLimits`, which reaches validation as
|
|
84
|
+
* `RecipePolicy.baselineLimits`. `{}` means "the baseline", which is what most
|
|
85
|
+
* Recipes want.
|
|
86
|
+
*/
|
|
87
|
+
limits: Partial<RecipeLimits>;
|
|
88
|
+
/**
|
|
89
|
+
* Most-recent turns kept verbatim in the rolling model context; older turns are
|
|
90
|
+
* pruned. Required, and required *of the Recipe*: how much context a domain
|
|
91
|
+
* needs is a property of the domain, so there is no house default to fall back
|
|
92
|
+
* to and a missing one is a refused Recipe.
|
|
93
|
+
*/
|
|
94
|
+
historyWindow: number;
|
|
95
|
+
/** Append a runtime metrics footer (turns, model calls, wall-clock) to the final result. */
|
|
96
|
+
reportMetrics: boolean;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* A Recipe that has been through {@link file://./validation.ts validateRecipe} —
|
|
100
|
+
* limits merged over the baseline, models and tool families checked. The only
|
|
101
|
+
* shape the runner ever consumes: a `Partial<RecipeLimits>` can never reach it.
|
|
102
|
+
*/
|
|
103
|
+
export interface ValidatedRecipe extends ResolvedRecipe {
|
|
104
|
+
limits: RecipeLimits;
|
|
105
|
+
/**
|
|
106
|
+
* The pair this recipe will actually run on — **the host's, always**.
|
|
107
|
+
*
|
|
108
|
+
* These exist only here, never on {@link ResolvedRecipe}, and that asymmetry
|
|
109
|
+
* is the design: a recipe cannot state a model, so the only way to hold one is
|
|
110
|
+
* to have been through `validateRecipe`, which copies the host's. Recipe data
|
|
111
|
+
* has no path to influence them.
|
|
112
|
+
*
|
|
113
|
+
* Guaranteed non-empty and guaranteed distinct, because `resolveConfig`
|
|
114
|
+
* refuses a config that is either — so the fallback is always a genuinely
|
|
115
|
+
* different model from the primary, which is the entire point of having one.
|
|
116
|
+
*/
|
|
117
|
+
primaryModelId: string;
|
|
118
|
+
fallbackModelId: string;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* One entry in the closed set of Subtask types the main agent may delegate,
|
|
122
|
+
* declared by the domain that owns it and collected in
|
|
123
|
+
* {@link file://./index.ts}.
|
|
124
|
+
*
|
|
125
|
+
* Two things follow from the set being closed rather than free prose:
|
|
126
|
+
*
|
|
127
|
+
* - The delegating model picks from an enum, so an invented type is rejected by
|
|
128
|
+
* the tool schema itself instead of silently falling back to a general recipe.
|
|
129
|
+
* - A type can *require params*. `arc-game` cannot be attempted without a
|
|
130
|
+
* scorecard and a game, so a subtask that names neither is refused up front
|
|
131
|
+
* rather than discovering it has nothing to play several turns later.
|
|
132
|
+
*
|
|
133
|
+
* Params are the model's declared inputs — ids it chose, validated for shape and
|
|
134
|
+
* resolved against durable rows at execution start. They are never the place for
|
|
135
|
+
* anything the model cannot know: an API session pinned to one of those ids is
|
|
136
|
+
* resolved by the parent from the id, never carried in the params.
|
|
137
|
+
*/
|
|
138
|
+
export interface SubtaskTypeSpec {
|
|
139
|
+
key: string;
|
|
140
|
+
/** One line shown to the delegating model so it picks the right type. */
|
|
141
|
+
description: string;
|
|
142
|
+
/**
|
|
143
|
+
* Required params for this type, or null when it takes none. Kept to flat
|
|
144
|
+
* strings: these are ids the model quotes from a tool result, not structures.
|
|
145
|
+
*
|
|
146
|
+
* A `z.object`, not an opaque `z.ZodType`, and that is load-bearing: the agent
|
|
147
|
+
* reads `.shape` back to build the `params` field of the delegate tool's schema
|
|
148
|
+
* (see `SubtaskTypeRegistry.paramProperties`). Declaring a param the model is
|
|
149
|
+
* never shown is the failure this shape exists to prevent — describe each key
|
|
150
|
+
* with `.describe()`, because that text is what the model reads.
|
|
151
|
+
*/
|
|
152
|
+
params: SubtaskParamsSchema | null;
|
|
153
|
+
/** How the model is told to obtain each param, appended to the description. */
|
|
154
|
+
paramsHelp?: string;
|
|
155
|
+
/**
|
|
156
|
+
* What the main agent is told it can *do* with this domain, rendered into its
|
|
157
|
+
* soul alongside the other capability blocks. Omit when the type needs no
|
|
158
|
+
* introduction beyond {@link description}.
|
|
159
|
+
*
|
|
160
|
+
* These two prompt fields exist to hold one rule: **everything the main agent
|
|
161
|
+
* is told about a domain is declared here, never written inside the runtime.**
|
|
162
|
+
* Advice written in the runtime instead has to be repeated per call site, and
|
|
163
|
+
* the copies drift — into telling the main agent two contradictory things
|
|
164
|
+
* about the same domain, twice per round.
|
|
165
|
+
*/
|
|
166
|
+
capability?: string;
|
|
167
|
+
/**
|
|
168
|
+
* How to construct a `delegate` payload for this type, rendered into the round
|
|
169
|
+
* contract the main agent reads every round.
|
|
170
|
+
*
|
|
171
|
+
* A function because the text names the control tools, whose names belong to
|
|
172
|
+
* the agent (see {@link DelegationNames}). Three rules, none of them enforced
|
|
173
|
+
* beyond a test: it must open with its own `## ` heading, it must not restate
|
|
174
|
+
* the params schema — the `delegate` tool description already renders that from
|
|
175
|
+
* {@link params} — and it must stay short, because every round pays for it.
|
|
176
|
+
*/
|
|
177
|
+
delegationGuidance?: (names: DelegationNames) => string;
|
|
178
|
+
/** The execution configuration this type runs under. */
|
|
179
|
+
recipe: ResolvedRecipe;
|
|
180
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { AgentLimits } from "../config.js";
|
|
2
|
+
import type { ResolvedRecipe, ValidatedRecipe } from "./recipe.js";
|
|
3
|
+
/**
|
|
4
|
+
* The capability boundary every recipe passes through, whatever declared it.
|
|
5
|
+
*
|
|
6
|
+
* This module imports no domain. It owns only what code must be able to say
|
|
7
|
+
* about *any* recipe: which tool families it may select, which models it runs
|
|
8
|
+
* on, and how a malformed one is made safe or refused. A domain cannot widen
|
|
9
|
+
* its *capabilities* by declaring them — the legal tool families come from
|
|
10
|
+
* {@link RecipePolicy}, which the host builds from its installed plugins, and
|
|
11
|
+
* the model pair comes from the host's config with no declaration involved at
|
|
12
|
+
* all. Its budget is the deliberate exception; see {@link resolveLimits}.
|
|
13
|
+
*
|
|
14
|
+
* In the predecessor repo the two allowlists were module constants, and one of
|
|
15
|
+
* them hardcoded a domain key (`"arc-game"`) inside otherwise generic code. That
|
|
16
|
+
* is the coupling this indirection removes: the set of legal families is now
|
|
17
|
+
* exactly the set of families the installed plugins registered.
|
|
18
|
+
*/
|
|
19
|
+
export interface RecipePolicy {
|
|
20
|
+
/**
|
|
21
|
+
* The pair every recipe runs on: the host agent's own, copied verbatim onto
|
|
22
|
+
* each {@link ValidatedRecipe}.
|
|
23
|
+
*
|
|
24
|
+
* There is no allowlist any more, because there is nothing to check against
|
|
25
|
+
* it — a recipe cannot state a model. `modelAllowlist` existed to police
|
|
26
|
+
* recipe-stated preferences, and it could only ever contain these same two
|
|
27
|
+
* ids, so the "preference" it policed could never reach a third model. It
|
|
28
|
+
* could only swap the primary for the fallback, which is not a capability
|
|
29
|
+
* anyone wants and which broke the pair's distinctness.
|
|
30
|
+
*/
|
|
31
|
+
primaryModelId: string;
|
|
32
|
+
/** See {@link primaryModelId}. Guaranteed distinct from it by `resolveConfig`. */
|
|
33
|
+
fallbackModelId: string;
|
|
34
|
+
/**
|
|
35
|
+
* Tool-family keys the runtime recognizes — derived from the installed
|
|
36
|
+
* plugins, never hardcoded.
|
|
37
|
+
*
|
|
38
|
+
* A main-agent-only tool is never a valid family — the Session's
|
|
39
|
+
* `set_context`, or anything a plugin offers only through `mainAgentTools`
|
|
40
|
+
* (an episodic-memory search, say). A subagent has no Session or durable
|
|
41
|
+
* memory to reach, and such a tool's absence from every plugin's
|
|
42
|
+
* `toolFamilies` makes it structurally impossible to enable through recipe
|
|
43
|
+
* data.
|
|
44
|
+
*/
|
|
45
|
+
knownToolFamilies: ReadonlySet<string>;
|
|
46
|
+
/** The baseline a recipe's declared `limits` merge over. */
|
|
47
|
+
baselineLimits: AgentLimits;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Merge a recipe's declared budget over the baseline, per field. A positive
|
|
51
|
+
* integer wins; anything else — missing, null, zero, fractional — falls back to
|
|
52
|
+
* the baseline rather than reaching the runner.
|
|
53
|
+
*
|
|
54
|
+
* The baseline is a default, not a ceiling: a recipe may declare a budget larger
|
|
55
|
+
* than the baseline and it is honored — sizing its own branch is what declaring
|
|
56
|
+
* `limits` is for, so nothing here clamps. What the merge buys, as
|
|
57
|
+
* defense-in-depth for a recipe that ever comes from data rather than code, is
|
|
58
|
+
* that a corrupt or absent value cannot reach the runner as a zero, fractional,
|
|
59
|
+
* or missing budget.
|
|
60
|
+
*
|
|
61
|
+
* Exported separately from {@link validateRecipe} because it must never throw.
|
|
62
|
+
*/
|
|
63
|
+
export declare function resolveLimits(limits: Partial<AgentLimits>, baseline: AgentLimits): AgentLimits;
|
|
64
|
+
/**
|
|
65
|
+
* Thrown by {@link validateRecipe} for a recipe that is unusable as given — it
|
|
66
|
+
* is disabled, carries no soul, or states no history window. All are
|
|
67
|
+
* deterministic caller bugs (the parent must only hand enabled, complete recipes
|
|
68
|
+
* to a subagent), so the child maps this to a terminal failed result rather than
|
|
69
|
+
* retrying.
|
|
70
|
+
*/
|
|
71
|
+
export declare class RecipeValidationError extends Error {
|
|
72
|
+
constructor(message: string);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Code-owned defensive validation of an already-resolved recipe. Returns a
|
|
76
|
+
* normalized copy (never mutates the input): the host's model pair is stamped
|
|
77
|
+
* on, and unknown tool families are dropped (deduped, order-preserving).
|
|
78
|
+
* Applied by the parent when it resolves a recipe and re-applied by the subagent
|
|
79
|
+
* on its inbound request, so recipe data can never select arbitrary models or
|
|
80
|
+
* tools — models because there is no field to select one with, tools because
|
|
81
|
+
* the legal set comes from the installed plugins rather than the declaration.
|
|
82
|
+
*
|
|
83
|
+
* The split between what is normalized and what is refused follows one rule:
|
|
84
|
+
* **the host declares a baseline ⇒ merge; it does not ⇒ require.** `limits`
|
|
85
|
+
* merge. A soul and a `historyWindow` do not: substituting a generic soul would
|
|
86
|
+
* run the work under an identity nobody declared — the model would answer,
|
|
87
|
+
* plausibly, as something other than what the recipe is for — and how much
|
|
88
|
+
* context a domain needs is likewise a property of the domain, not something a
|
|
89
|
+
* house default can guess. Both fail the recipe outright.
|
|
90
|
+
*/
|
|
91
|
+
export declare function validateRecipe(recipe: ResolvedRecipe, policy: RecipePolicy): ValidatedRecipe;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge a recipe's declared budget over the baseline, per field. A positive
|
|
3
|
+
* integer wins; anything else — missing, null, zero, fractional — falls back to
|
|
4
|
+
* the baseline rather than reaching the runner.
|
|
5
|
+
*
|
|
6
|
+
* The baseline is a default, not a ceiling: a recipe may declare a budget larger
|
|
7
|
+
* than the baseline and it is honored — sizing its own branch is what declaring
|
|
8
|
+
* `limits` is for, so nothing here clamps. What the merge buys, as
|
|
9
|
+
* defense-in-depth for a recipe that ever comes from data rather than code, is
|
|
10
|
+
* that a corrupt or absent value cannot reach the runner as a zero, fractional,
|
|
11
|
+
* or missing budget.
|
|
12
|
+
*
|
|
13
|
+
* Exported separately from {@link validateRecipe} because it must never throw.
|
|
14
|
+
*/
|
|
15
|
+
export function resolveLimits(limits, baseline) {
|
|
16
|
+
const positiveInt = (n, fallback) => typeof n === "number" && Number.isInteger(n) && n > 0 ? n : fallback;
|
|
17
|
+
return {
|
|
18
|
+
maxTurns: positiveInt(limits?.maxTurns, baseline.maxTurns),
|
|
19
|
+
maxWallMs: positiveInt(limits?.maxWallMs, baseline.maxWallMs)
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Thrown by {@link validateRecipe} for a recipe that is unusable as given — it
|
|
24
|
+
* is disabled, carries no soul, or states no history window. All are
|
|
25
|
+
* deterministic caller bugs (the parent must only hand enabled, complete recipes
|
|
26
|
+
* to a subagent), so the child maps this to a terminal failed result rather than
|
|
27
|
+
* retrying.
|
|
28
|
+
*/
|
|
29
|
+
export class RecipeValidationError extends Error {
|
|
30
|
+
constructor(message) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = "RecipeValidationError";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Code-owned defensive validation of an already-resolved recipe. Returns a
|
|
37
|
+
* normalized copy (never mutates the input): the host's model pair is stamped
|
|
38
|
+
* on, and unknown tool families are dropped (deduped, order-preserving).
|
|
39
|
+
* Applied by the parent when it resolves a recipe and re-applied by the subagent
|
|
40
|
+
* on its inbound request, so recipe data can never select arbitrary models or
|
|
41
|
+
* tools — models because there is no field to select one with, tools because
|
|
42
|
+
* the legal set comes from the installed plugins rather than the declaration.
|
|
43
|
+
*
|
|
44
|
+
* The split between what is normalized and what is refused follows one rule:
|
|
45
|
+
* **the host declares a baseline ⇒ merge; it does not ⇒ require.** `limits`
|
|
46
|
+
* merge. A soul and a `historyWindow` do not: substituting a generic soul would
|
|
47
|
+
* run the work under an identity nobody declared — the model would answer,
|
|
48
|
+
* plausibly, as something other than what the recipe is for — and how much
|
|
49
|
+
* context a domain needs is likewise a property of the domain, not something a
|
|
50
|
+
* house default can guess. Both fail the recipe outright.
|
|
51
|
+
*/
|
|
52
|
+
export function validateRecipe(recipe, policy) {
|
|
53
|
+
if (!recipe.enabled) {
|
|
54
|
+
throw new RecipeValidationError(`recipe "${recipe.key}" (v${recipe.version}) is disabled`);
|
|
55
|
+
}
|
|
56
|
+
if (recipe.soul.trim() === "") {
|
|
57
|
+
throw new RecipeValidationError(`recipe "${recipe.key}" (v${recipe.version}) has no soul`);
|
|
58
|
+
}
|
|
59
|
+
if (!Number.isInteger(recipe.historyWindow) || recipe.historyWindow <= 0) {
|
|
60
|
+
throw new RecipeValidationError(`recipe "${recipe.key}" (v${recipe.version}) has no usable historyWindow ` +
|
|
61
|
+
`(got ${recipe.historyWindow}); every recipe states its own`);
|
|
62
|
+
}
|
|
63
|
+
const toolFamilies = [...new Set(recipe.toolFamilies)].filter((family) => policy.knownToolFamilies.has(family));
|
|
64
|
+
return {
|
|
65
|
+
...recipe,
|
|
66
|
+
// The host's pair, copied. Not selected, not substituted, not merged —
|
|
67
|
+
// there is nothing on a `ResolvedRecipe` to select *from*.
|
|
68
|
+
//
|
|
69
|
+
// This used to substitute a recipe's stated preference against
|
|
70
|
+
// an allowlist, per slot and independently. That produced a real defect:
|
|
71
|
+
// a recipe preferring the host's *fallback* as its primary, and stating no
|
|
72
|
+
// fallback of its own, resolved to the same id in both slots — so the
|
|
73
|
+
// fallback retried the model that had just failed, defeating the distinct-
|
|
74
|
+
// pair invariant `resolveConfig` enforces one layer up.
|
|
75
|
+
//
|
|
76
|
+
// Copying wholesale makes that unrepresentable: the pair here is exactly the
|
|
77
|
+
// pair the agent configured, which `resolveConfig` has already guaranteed is
|
|
78
|
+
// non-empty and distinct.
|
|
79
|
+
primaryModelId: policy.primaryModelId,
|
|
80
|
+
fallbackModelId: policy.fallbackModelId,
|
|
81
|
+
toolFamilies,
|
|
82
|
+
limits: resolveLimits(recipe.limits, policy.baselineLimits)
|
|
83
|
+
};
|
|
84
|
+
}
|
package/dist/db/db.d.ts
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { type DrizzleSqliteDODatabase } from "drizzle-orm/durable-sqlite";
|
|
2
|
+
import * as schema from "./schema.js";
|
|
3
|
+
export type DB = DrizzleSqliteDODatabase<typeof schema>;
|
|
4
|
+
/**
|
|
5
|
+
* A table owner outside core's migration journal.
|
|
6
|
+
*
|
|
7
|
+
* What a plugin cannot share is the **migrator**. `drizzle-orm/durable-sqlite/
|
|
8
|
+
* migrator` keeps one flat integer journal and one global
|
|
9
|
+
* `__drizzle_migrations` table, and two independently-versioned npm packages
|
|
10
|
+
* cannot share that index space — not hypothetically: the two predecessor
|
|
11
|
+
* agents, both consuming the same `notify_tasks` module, had already forked the
|
|
12
|
+
* journal at index 1 (`0001_unusual_nova` vs `0001_great_goliath`). Worse,
|
|
13
|
+
* `drizzle-kit generate` diffs against a snapshot in one output directory, so a
|
|
14
|
+
* plugin shipping from its own repo cannot produce a correct diff at all.
|
|
15
|
+
*
|
|
16
|
+
* **The query builder is a different thing entirely, and a plugin should use
|
|
17
|
+
* it.** `drizzle(storage, { schema })` is a typed wrapper over the same
|
|
18
|
+
* `DurableObjectStorage`; it holds no journal, no connection, and no state that
|
|
19
|
+
* a second handle could disturb. So the rule is narrow — *never import the
|
|
20
|
+
* migrator* — rather than "no drizzle".
|
|
21
|
+
*
|
|
22
|
+
* A plugin therefore does three things: declares its tables with `sqliteTable`
|
|
23
|
+
* as usual, emits idempotent DDL here, and queries through its own drizzle
|
|
24
|
+
* handle. Version bookkeeping lives in the {@link PLUGIN_MIGRATIONS_TABLE} row
|
|
25
|
+
* this class manages for it. Only the DDL is hand-written, and that pattern is
|
|
26
|
+
* not novel here — it is what the subagent facet already does for its own two
|
|
27
|
+
* tables.
|
|
28
|
+
*
|
|
29
|
+
* ```ts
|
|
30
|
+
* // schema.ts — an ordinary drizzle table, under the plugin's own prefix.
|
|
31
|
+
* export const arcScorecards = sqliteTable("arc_scorecards", {
|
|
32
|
+
* cardId: text("card_id").primaryKey(),
|
|
33
|
+
* lastUsedAt: integer("last_used_at").notNull()
|
|
34
|
+
* });
|
|
35
|
+
*
|
|
36
|
+
* // The DDL half: idempotent, re-run on every hibernation wake-up.
|
|
37
|
+
* const store: PluginStore = {
|
|
38
|
+
* plugin: "arc-agi",
|
|
39
|
+
* version: 2,
|
|
40
|
+
* ensureTables(sql, from) {
|
|
41
|
+
* sql.exec(`CREATE TABLE IF NOT EXISTS arc_scorecards (…)`);
|
|
42
|
+
* if (from < 2) sql.exec(`ALTER TABLE arc_scorecards ADD COLUMN …`);
|
|
43
|
+
* }
|
|
44
|
+
* };
|
|
45
|
+
*
|
|
46
|
+
* // The query half: drizzle, over the plugin's own handle.
|
|
47
|
+
* const db = drizzle(storage, { schema: { arcScorecards } });
|
|
48
|
+
* db.select().from(arcScorecards).where(gte(arcScorecards.lastUsedAt, since));
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export interface PluginStore {
|
|
52
|
+
/** Stable identifier. Also the bookkeeping row's key — never rename it. */
|
|
53
|
+
plugin: string;
|
|
54
|
+
/**
|
|
55
|
+
* Monotonically increasing schema version, starting at 1. Bump it whenever
|
|
56
|
+
* {@link ensureTables} needs to do something it did not do before.
|
|
57
|
+
*/
|
|
58
|
+
version: number;
|
|
59
|
+
/**
|
|
60
|
+
* Bring this plugin's tables up to {@link version}.
|
|
61
|
+
*
|
|
62
|
+
* Called once per DO instance, and re-called on every hibernation wake-up, so
|
|
63
|
+
* it **must be idempotent**. `from` is the version last recorded for this
|
|
64
|
+
* plugin (0 on first ever run), which is what lets an upgrade path branch
|
|
65
|
+
* without re-running earlier DDL.
|
|
66
|
+
*/
|
|
67
|
+
ensureTables(sql: SqlStorage, from: number): void;
|
|
68
|
+
}
|
|
69
|
+
/** Bookkeeping for {@link PluginStore} versions. Core owns the table; plugins own their rows. */
|
|
70
|
+
export declare const PLUGIN_MIGRATIONS_TABLE = "plugin_migrations";
|
|
71
|
+
export interface AgentDBOptions {
|
|
72
|
+
/**
|
|
73
|
+
* Plugin-owned stores to bring up alongside core's own tables. Order is
|
|
74
|
+
* preserved but must not matter: a plugin that depends on another plugin's
|
|
75
|
+
* table is a plugin that should have been one plugin.
|
|
76
|
+
*/
|
|
77
|
+
stores?: readonly PluginStore[];
|
|
78
|
+
/**
|
|
79
|
+
* `CoreConfig.maxSubtasks` — the durable guard `createDecomposition` enforces.
|
|
80
|
+
* Passed rather than imported so one resolved config governs both the schema
|
|
81
|
+
* offered to the model and the write that has to hold.
|
|
82
|
+
*/
|
|
83
|
+
maxSubtasks: number;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The agent's database: one drizzle handle over the DO's SQLite, with a memoized
|
|
87
|
+
* namespace per table domain (`db.tasks`, `db.subtasks`).
|
|
88
|
+
*
|
|
89
|
+
* Constructed once per DO instance. Core's migrations run in the constructor —
|
|
90
|
+
* the durable-sqlite migrator is idempotent, so a fresh `AgentDB` on every
|
|
91
|
+
* hibernation wake-up re-validates the schema safely. Call `ensureReady()` (and
|
|
92
|
+
* await it) before issuing any queries.
|
|
93
|
+
*
|
|
94
|
+
* Plugin stores are brought up in the same place but through a different
|
|
95
|
+
* mechanism, deliberately — see {@link PluginStore}.
|
|
96
|
+
*/
|
|
97
|
+
export declare class AgentDB {
|
|
98
|
+
private readonly storage;
|
|
99
|
+
private readonly options;
|
|
100
|
+
private readonly db;
|
|
101
|
+
private readonly _ready;
|
|
102
|
+
private _tasks?;
|
|
103
|
+
private _subtasks?;
|
|
104
|
+
constructor(storage: DurableObjectStorage, options: AgentDBOptions);
|
|
105
|
+
ensureReady(): Promise<void>;
|
|
106
|
+
get tasks(): {
|
|
107
|
+
begin(input: {
|
|
108
|
+
messageId: string;
|
|
109
|
+
taskId: string;
|
|
110
|
+
contextId: string;
|
|
111
|
+
}): import("../a2a/task.js").PlainTask;
|
|
112
|
+
get(taskId: string): import("../a2a/task.js").PlainTask | null;
|
|
113
|
+
list(query: import("./index.js").TaskListQuery): {
|
|
114
|
+
tasks: import("../a2a/task.js").PlainTask[];
|
|
115
|
+
totalSize: number;
|
|
116
|
+
};
|
|
117
|
+
save(task: import("@a2a-js/sdk").Task): boolean;
|
|
118
|
+
markWorking(taskId: string): "ok" | "canceled";
|
|
119
|
+
cancel(taskId: string): import("../a2a/task.js").PlainTask | null;
|
|
120
|
+
cleanup(): void;
|
|
121
|
+
};
|
|
122
|
+
get subtasks(): {
|
|
123
|
+
createDecomposition(taskId: string, round: number, drafts: import("../subtasks/types.js").SubtaskDraft[]): import("../subtasks/types.js").Subtask[];
|
|
124
|
+
get(id: import("../subtasks/types.js").SubtaskId): import("../subtasks/types.js").Subtask | null;
|
|
125
|
+
list(taskId: string): import("../subtasks/types.js").Subtask[];
|
|
126
|
+
listRound(taskId: string, round: number): import("../subtasks/types.js").Subtask[];
|
|
127
|
+
start(id: import("../subtasks/types.js").SubtaskId, recipe: {
|
|
128
|
+
recipeId: string;
|
|
129
|
+
recipeVersion: number;
|
|
130
|
+
}): boolean;
|
|
131
|
+
complete(id: import("../subtasks/types.js").SubtaskId, resultParts: import("../subtasks/types.js").SubtaskResultPart[]): boolean;
|
|
132
|
+
fail(id: import("../subtasks/types.js").SubtaskId, error: string): boolean;
|
|
133
|
+
cancelRunning(id: import("../subtasks/types.js").SubtaskId): boolean;
|
|
134
|
+
cancelPending(taskId: string): number;
|
|
135
|
+
cleanup(): void;
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* Run each plugin store's DDL and record its version, after core's own
|
|
139
|
+
* migrations so a plugin may safely reference core tables (nothing should, but
|
|
140
|
+
* the ordering is free and the alternative is a surprising failure).
|
|
141
|
+
*
|
|
142
|
+
* A store that throws fails DO start rather than being skipped: a plugin whose
|
|
143
|
+
* tables are missing would otherwise fail later, at its first tool call, in a
|
|
144
|
+
* request the user is waiting on.
|
|
145
|
+
*/
|
|
146
|
+
private applyStores;
|
|
147
|
+
}
|
package/dist/db/db.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { drizzle } from "drizzle-orm/durable-sqlite";
|
|
2
|
+
import { migrate } from "drizzle-orm/durable-sqlite/migrator";
|
|
3
|
+
import * as schema from "./schema.js";
|
|
4
|
+
import dbMigrations from "./migrations/index.js";
|
|
5
|
+
import { makeTasks } from "./models/tasks.js";
|
|
6
|
+
import { makeSubtasks } from "./models/subtasks.js";
|
|
7
|
+
/** Bookkeeping for {@link PluginStore} versions. Core owns the table; plugins own their rows. */
|
|
8
|
+
export const PLUGIN_MIGRATIONS_TABLE = "plugin_migrations";
|
|
9
|
+
/**
|
|
10
|
+
* The agent's database: one drizzle handle over the DO's SQLite, with a memoized
|
|
11
|
+
* namespace per table domain (`db.tasks`, `db.subtasks`).
|
|
12
|
+
*
|
|
13
|
+
* Constructed once per DO instance. Core's migrations run in the constructor —
|
|
14
|
+
* the durable-sqlite migrator is idempotent, so a fresh `AgentDB` on every
|
|
15
|
+
* hibernation wake-up re-validates the schema safely. Call `ensureReady()` (and
|
|
16
|
+
* await it) before issuing any queries.
|
|
17
|
+
*
|
|
18
|
+
* Plugin stores are brought up in the same place but through a different
|
|
19
|
+
* mechanism, deliberately — see {@link PluginStore}.
|
|
20
|
+
*/
|
|
21
|
+
export class AgentDB {
|
|
22
|
+
storage;
|
|
23
|
+
options;
|
|
24
|
+
db;
|
|
25
|
+
_ready;
|
|
26
|
+
_tasks;
|
|
27
|
+
_subtasks;
|
|
28
|
+
constructor(storage, options) {
|
|
29
|
+
this.storage = storage;
|
|
30
|
+
this.options = options;
|
|
31
|
+
this.db = drizzle(storage, { schema });
|
|
32
|
+
this._ready = migrate(this.db, dbMigrations).then(() => {
|
|
33
|
+
this.applyStores(options.stores ?? []);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
ensureReady() {
|
|
37
|
+
return this._ready;
|
|
38
|
+
}
|
|
39
|
+
get tasks() {
|
|
40
|
+
return (this._tasks ??= makeTasks(this.db));
|
|
41
|
+
}
|
|
42
|
+
get subtasks() {
|
|
43
|
+
return (this._subtasks ??= makeSubtasks(this.db, {
|
|
44
|
+
maxSubtasks: this.options.maxSubtasks
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Run each plugin store's DDL and record its version, after core's own
|
|
49
|
+
* migrations so a plugin may safely reference core tables (nothing should, but
|
|
50
|
+
* the ordering is free and the alternative is a surprising failure).
|
|
51
|
+
*
|
|
52
|
+
* A store that throws fails DO start rather than being skipped: a plugin whose
|
|
53
|
+
* tables are missing would otherwise fail later, at its first tool call, in a
|
|
54
|
+
* request the user is waiting on.
|
|
55
|
+
*/
|
|
56
|
+
applyStores(stores) {
|
|
57
|
+
if (stores.length === 0)
|
|
58
|
+
return;
|
|
59
|
+
const sql = this.storage.sql;
|
|
60
|
+
sql.exec(`CREATE TABLE IF NOT EXISTS ${PLUGIN_MIGRATIONS_TABLE} (
|
|
61
|
+
plugin TEXT PRIMARY KEY,
|
|
62
|
+
version INTEGER NOT NULL,
|
|
63
|
+
updated_at INTEGER NOT NULL
|
|
64
|
+
)`);
|
|
65
|
+
const seen = new Set();
|
|
66
|
+
for (const store of stores) {
|
|
67
|
+
if (seen.has(store.plugin)) {
|
|
68
|
+
throw new Error(`duplicate PluginStore '${store.plugin}' — two plugins claim the same storage namespace`);
|
|
69
|
+
}
|
|
70
|
+
seen.add(store.plugin);
|
|
71
|
+
if (!Number.isInteger(store.version) || store.version < 1) {
|
|
72
|
+
throw new Error(`PluginStore '${store.plugin}' version must be an integer >= 1, got ${store.version}`);
|
|
73
|
+
}
|
|
74
|
+
const row = sql
|
|
75
|
+
.exec(`SELECT version FROM ${PLUGIN_MIGRATIONS_TABLE} WHERE plugin = ?`, store.plugin)
|
|
76
|
+
.toArray()[0];
|
|
77
|
+
const from = row?.version ?? 0;
|
|
78
|
+
if (from > store.version) {
|
|
79
|
+
throw new Error(`PluginStore '${store.plugin}' is at version ${from} on disk but the ` +
|
|
80
|
+
`installed plugin declares ${store.version} — downgrade is not supported`);
|
|
81
|
+
}
|
|
82
|
+
store.ensureTables(sql, from);
|
|
83
|
+
if (from !== store.version) {
|
|
84
|
+
sql.exec(`INSERT INTO ${PLUGIN_MIGRATIONS_TABLE} (plugin, version, updated_at)
|
|
85
|
+
VALUES (?, ?, ?)
|
|
86
|
+
ON CONFLICT(plugin) DO UPDATE SET version = excluded.version, updated_at = excluded.updated_at`, store.plugin, store.version, Date.now());
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@dynamicagents/core/db` — the two tables core owns, and the seam a plugin uses to
|
|
3
|
+
* own its own without touching core's migration journal.
|
|
4
|
+
*/
|
|
5
|
+
export { AgentDB, PLUGIN_MIGRATIONS_TABLE, type AgentDBOptions, type DB, type PluginStore } from "./db.js";
|
|
6
|
+
export { notifyTasks, subtasks } from "./schema.js";
|
|
7
|
+
export { makeTasks, stateOf, type TaskListQuery } from "./models/tasks.js";
|
|
8
|
+
export { makeSubtasks, type SubtaskModelOptions } from "./models/subtasks.js";
|
package/dist/db/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@dynamicagents/core/db` — the two tables core owns, and the seam a plugin uses to
|
|
3
|
+
* own its own without touching core's migration journal.
|
|
4
|
+
*/
|
|
5
|
+
export { AgentDB, PLUGIN_MIGRATIONS_TABLE } from "./db.js";
|
|
6
|
+
export { notifyTasks, subtasks } from "./schema.js";
|
|
7
|
+
export { makeTasks, stateOf } from "./models/tasks.js";
|
|
8
|
+
export { makeSubtasks } from "./models/subtasks.js";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drizzle migrations, inlined for Cloudflare Workers.
|
|
3
|
+
*
|
|
4
|
+
* GENERATED FILE — do not edit. Run `npm run db:generate` after changing
|
|
5
|
+
* `src/db/schema.ts`; that runs `drizzle-kit generate` and then
|
|
6
|
+
* `scripts/build-migrations.mjs`, which rebuilds this from
|
|
7
|
+
* `meta/_journal.json` and the `.sql` files beside it.
|
|
8
|
+
*
|
|
9
|
+
* Key format: "m{zero-padded idx}" — what drizzle's durable-sqlite migrator
|
|
10
|
+
* reads from `config.migrations[key]`.
|
|
11
|
+
*
|
|
12
|
+
* This journal is CORE's alone. A plugin must never add an entry: the journal is
|
|
13
|
+
* a flat integer sequence over one shared `__drizzle_migrations` table, and two
|
|
14
|
+
* independently-versioned packages writing to it will collide. Plugins own their
|
|
15
|
+
* tables through `PluginStore` instead — see `src/db/db.ts`.
|
|
16
|
+
*/
|
|
17
|
+
import type { migrate } from "drizzle-orm/durable-sqlite/migrator";
|
|
18
|
+
type MigrationConfig = Parameters<typeof migrate>[1];
|
|
19
|
+
declare const dbMigrations: MigrationConfig;
|
|
20
|
+
export default dbMigrations;
|