agentfootprint 9.46.3 → 9.48.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/CLAUDE.md +49 -2
- package/dist/adapters/code/agentcore.js +35 -7
- package/dist/adapters/code/agentcore.js.map +1 -1
- package/dist/adapters/code/local.js +94 -10
- package/dist/adapters/code/local.js.map +1 -1
- package/dist/core/Agent.js +18 -1
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/agent/AgentBuilder.js +147 -3
- package/dist/core/agent/AgentBuilder.js.map +1 -1
- package/dist/core/agent/runManifest.js +7 -0
- package/dist/core/agent/runManifest.js.map +1 -1
- package/dist/core/checkin.js +16 -2
- package/dist/core/checkin.js.map +1 -1
- package/dist/core/pause.js +64 -1
- package/dist/core/pause.js.map +1 -1
- package/dist/doors/recipes.js +55 -0
- package/dist/doors/recipes.js.map +1 -0
- package/dist/esm/adapters/code/agentcore.js +35 -7
- package/dist/esm/adapters/code/agentcore.js.map +1 -1
- package/dist/esm/adapters/code/local.js +94 -10
- package/dist/esm/adapters/code/local.js.map +1 -1
- package/dist/esm/core/Agent.d.ts +9 -1
- package/dist/esm/core/Agent.js +19 -2
- package/dist/esm/core/Agent.js.map +1 -1
- package/dist/esm/core/agent/AgentBuilder.d.ts +68 -0
- package/dist/esm/core/agent/AgentBuilder.js +147 -3
- package/dist/esm/core/agent/AgentBuilder.js.map +1 -1
- package/dist/esm/core/agent/runManifest.d.ts +18 -0
- package/dist/esm/core/agent/runManifest.js +7 -0
- package/dist/esm/core/agent/runManifest.js.map +1 -1
- package/dist/esm/core/checkin.d.ts +58 -0
- package/dist/esm/core/checkin.js +16 -2
- package/dist/esm/core/checkin.js.map +1 -1
- package/dist/esm/core/pause.d.ts +44 -0
- package/dist/esm/core/pause.js +61 -0
- package/dist/esm/core/pause.js.map +1 -1
- package/dist/esm/doors/recipes.d.ts +38 -0
- package/dist/esm/doors/recipes.js +39 -0
- package/dist/esm/doors/recipes.js.map +1 -0
- package/dist/esm/events/payloads.d.ts +29 -0
- package/dist/esm/hosting/conformance/cases.js +23 -4
- package/dist/esm/hosting/conformance/cases.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/observe.d.ts +2 -0
- package/dist/esm/observe.js +12 -0
- package/dist/esm/observe.js.map +1 -1
- package/dist/esm/recipes/apply.d.ts +67 -0
- package/dist/esm/recipes/apply.js +117 -0
- package/dist/esm/recipes/apply.js.map +1 -0
- package/dist/esm/recipes/defineAgentRecipe.d.ts +68 -0
- package/dist/esm/recipes/defineAgentRecipe.js +112 -0
- package/dist/esm/recipes/defineAgentRecipe.js.map +1 -0
- package/dist/esm/recipes/identifier.d.ts +40 -0
- package/dist/esm/recipes/identifier.js +95 -0
- package/dist/esm/recipes/identifier.js.map +1 -0
- package/dist/esm/recipes/index.d.ts +19 -0
- package/dist/esm/recipes/index.js +19 -0
- package/dist/esm/recipes/index.js.map +1 -0
- package/dist/esm/recipes/provenance.d.ts +57 -0
- package/dist/esm/recipes/provenance.js +53 -0
- package/dist/esm/recipes/provenance.js.map +1 -0
- package/dist/esm/recipes/types.d.ts +134 -0
- package/dist/esm/recipes/types.js +63 -0
- package/dist/esm/recipes/types.js.map +1 -0
- package/dist/esm/recipes/version.d.ts +38 -0
- package/dist/esm/recipes/version.js +84 -0
- package/dist/esm/recipes/version.js.map +1 -0
- package/dist/esm/recorders/observability/fileRecordingSink.d.ts +104 -0
- package/dist/esm/recorders/observability/fileRecordingSink.js +195 -0
- package/dist/esm/recorders/observability/fileRecordingSink.js.map +1 -0
- package/dist/esm/recorders/observability/recordingEnvelope.d.ts +292 -0
- package/dist/esm/recorders/observability/recordingEnvelope.js +375 -0
- package/dist/esm/recorders/observability/recordingEnvelope.js.map +1 -0
- package/dist/hosting/conformance/cases.js +23 -4
- package/dist/hosting/conformance/cases.js.map +1 -1
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/observe.js +23 -1
- package/dist/observe.js.map +1 -1
- package/dist/recipes/apply.js +125 -0
- package/dist/recipes/apply.js.map +1 -0
- package/dist/recipes/defineAgentRecipe.js +118 -0
- package/dist/recipes/defineAgentRecipe.js.map +1 -0
- package/dist/recipes/identifier.js +100 -0
- package/dist/recipes/identifier.js.map +1 -0
- package/dist/recipes/index.js +24 -0
- package/dist/recipes/index.js.map +1 -0
- package/dist/recipes/provenance.js +57 -0
- package/dist/recipes/provenance.js.map +1 -0
- package/dist/recipes/types.js +64 -0
- package/dist/recipes/types.js.map +1 -0
- package/dist/recipes/version.js +89 -0
- package/dist/recipes/version.js.map +1 -0
- package/dist/recorders/observability/fileRecordingSink.js +201 -0
- package/dist/recorders/observability/fileRecordingSink.js.map +1 -0
- package/dist/recorders/observability/recordingEnvelope.js +382 -0
- package/dist/recorders/observability/recordingEnvelope.js.map +1 -0
- package/dist/types/adapters/code/agentcore.d.ts.map +1 -1
- package/dist/types/adapters/code/local.d.ts.map +1 -1
- package/dist/types/core/Agent.d.ts +9 -1
- package/dist/types/core/Agent.d.ts.map +1 -1
- package/dist/types/core/agent/AgentBuilder.d.ts +68 -0
- package/dist/types/core/agent/AgentBuilder.d.ts.map +1 -1
- package/dist/types/core/agent/runManifest.d.ts +18 -0
- package/dist/types/core/agent/runManifest.d.ts.map +1 -1
- package/dist/types/core/checkin.d.ts +58 -0
- package/dist/types/core/checkin.d.ts.map +1 -1
- package/dist/types/core/pause.d.ts +44 -0
- package/dist/types/core/pause.d.ts.map +1 -1
- package/dist/types/doors/recipes.d.ts +39 -0
- package/dist/types/doors/recipes.d.ts.map +1 -0
- package/dist/types/events/payloads.d.ts +29 -0
- package/dist/types/events/payloads.d.ts.map +1 -1
- package/dist/types/hosting/conformance/cases.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/observe.d.ts +2 -0
- package/dist/types/observe.d.ts.map +1 -1
- package/dist/types/recipes/apply.d.ts +68 -0
- package/dist/types/recipes/apply.d.ts.map +1 -0
- package/dist/types/recipes/defineAgentRecipe.d.ts +69 -0
- package/dist/types/recipes/defineAgentRecipe.d.ts.map +1 -0
- package/dist/types/recipes/identifier.d.ts +41 -0
- package/dist/types/recipes/identifier.d.ts.map +1 -0
- package/dist/types/recipes/index.d.ts +20 -0
- package/dist/types/recipes/index.d.ts.map +1 -0
- package/dist/types/recipes/provenance.d.ts +58 -0
- package/dist/types/recipes/provenance.d.ts.map +1 -0
- package/dist/types/recipes/types.d.ts +135 -0
- package/dist/types/recipes/types.d.ts.map +1 -0
- package/dist/types/recipes/version.d.ts +39 -0
- package/dist/types/recipes/version.d.ts.map +1 -0
- package/dist/types/recorders/observability/fileRecordingSink.d.ts +105 -0
- package/dist/types/recorders/observability/fileRecordingSink.d.ts.map +1 -0
- package/dist/types/recorders/observability/recordingEnvelope.d.ts +293 -0
- package/dist/types/recorders/observability/recordingEnvelope.d.ts.map +1 -0
- package/package.json +15 -2
package/dist/esm/observe.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observe.js","sourceRoot":"","sources":["../../src/observe.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"observe.js","sourceRoot":"","sources":["../../src/observe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* apply — the pure half of applying a recipe.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: policy resolution + the sentences the applier raises. Pure, so
|
|
5
|
+
* every refusal can be read and tested without building an agent.
|
|
6
|
+
* Role: recipes/ layer. `AgentBuilder.recipe()` is the only caller; it owns
|
|
7
|
+
* the mutation, this file owns the words.
|
|
8
|
+
* Emits: N/A.
|
|
9
|
+
*/
|
|
10
|
+
import { type RecipeSource } from './provenance.js';
|
|
11
|
+
import type { AppliedRecipe, RecipeConflictPolicy } from './types.js';
|
|
12
|
+
/**
|
|
13
|
+
* Resolve the conflict policy, or refuse the requested one BY NAME.
|
|
14
|
+
*
|
|
15
|
+
* The three a reader reaches for are named in the refusal because each is a
|
|
16
|
+
* real design and none is implemented: every one of them has to answer where
|
|
17
|
+
* the dropped registration is RECORDED, and until it does, running it as
|
|
18
|
+
* `'error'`'s quiet cousin would be the accepted-and-silently-wrong shape this
|
|
19
|
+
* library refuses. So the unimplemented policy is refused rather than
|
|
20
|
+
* approximated by the one that ships.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveRecipeConflictPolicy(value: unknown, callSite: string): RecipeConflictPolicy;
|
|
23
|
+
/**
|
|
24
|
+
* The refusal for a name two sources both registered.
|
|
25
|
+
*
|
|
26
|
+
* Raised only when at least one side came from a recipe. A collision between
|
|
27
|
+
* two direct builder calls keeps the sentence it has always had — the message
|
|
28
|
+
* an app already reads in its tests should not change because a feature it does
|
|
29
|
+
* not use shipped.
|
|
30
|
+
*
|
|
31
|
+
* `what` is the word the reader uses (`'tool name'` / `'injection id'`);
|
|
32
|
+
* `existing` is who registered it first and `incoming` who is registering it
|
|
33
|
+
* now, either of which may be `undefined` for the unattributed case;
|
|
34
|
+
* `callSite` is the API being called, e.g. `Agent.tool()`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function duplicateRegistrationRefusal(params: {
|
|
37
|
+
readonly what: 'tool name' | 'injection id';
|
|
38
|
+
readonly name: string;
|
|
39
|
+
readonly existing: RecipeSource | undefined;
|
|
40
|
+
readonly incoming: RecipeSource | undefined;
|
|
41
|
+
readonly callSite: string;
|
|
42
|
+
}): string;
|
|
43
|
+
/** The refusal for one composition applied twice to one agent. */
|
|
44
|
+
export declare function duplicateRecipeRefusal(params: {
|
|
45
|
+
readonly existing: AppliedRecipe;
|
|
46
|
+
readonly incoming: AppliedRecipe;
|
|
47
|
+
readonly callSite: string;
|
|
48
|
+
}): string;
|
|
49
|
+
/**
|
|
50
|
+
* The refusal for a recipe that applies ITSELF, directly or through another.
|
|
51
|
+
*
|
|
52
|
+
* A SEPARATE sentence from {@link duplicateRecipeRefusal}, because these are two
|
|
53
|
+
* different facts and the fix is different for each: "already applied" means the
|
|
54
|
+
* chain names one composition twice, and "currently applying" means the
|
|
55
|
+
* composition is its own ancestor and would never terminate. Telling the author
|
|
56
|
+
* their recursion is a duplicate would send them to look at the wrong line.
|
|
57
|
+
*
|
|
58
|
+
* `stack` is the application chain, outermost first, so the message can show the
|
|
59
|
+
* cycle rather than assert one.
|
|
60
|
+
*/
|
|
61
|
+
export declare function recursiveRecipeRefusal(params: {
|
|
62
|
+
readonly stack: readonly AppliedRecipe[];
|
|
63
|
+
readonly incoming: AppliedRecipe;
|
|
64
|
+
readonly callSite: string;
|
|
65
|
+
}): string;
|
|
66
|
+
/** The refusal for `configure` returning a promise. */
|
|
67
|
+
export declare function asyncConfigureRefusal(recipe: AppliedRecipe, callSite: string): string;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* apply — the pure half of applying a recipe.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: policy resolution + the sentences the applier raises. Pure, so
|
|
5
|
+
* every refusal can be read and tested without building an agent.
|
|
6
|
+
* Role: recipes/ layer. `AgentBuilder.recipe()` is the only caller; it owns
|
|
7
|
+
* the mutation, this file owns the words.
|
|
8
|
+
* Emits: N/A.
|
|
9
|
+
*/
|
|
10
|
+
import { describeRecipeSource } from './provenance.js';
|
|
11
|
+
/** The policies that exist. One, today — see {@link RecipeConflictPolicy}. */
|
|
12
|
+
const CONFLICT_POLICIES = ['error'];
|
|
13
|
+
/**
|
|
14
|
+
* Resolve the conflict policy, or refuse the requested one BY NAME.
|
|
15
|
+
*
|
|
16
|
+
* The three a reader reaches for are named in the refusal because each is a
|
|
17
|
+
* real design and none is implemented: every one of them has to answer where
|
|
18
|
+
* the dropped registration is RECORDED, and until it does, running it as
|
|
19
|
+
* `'error'`'s quiet cousin would be the accepted-and-silently-wrong shape this
|
|
20
|
+
* library refuses. So the unimplemented policy is refused rather than
|
|
21
|
+
* approximated by the one that ships.
|
|
22
|
+
*/
|
|
23
|
+
export function resolveRecipeConflictPolicy(value, callSite) {
|
|
24
|
+
if (value === undefined)
|
|
25
|
+
return 'error';
|
|
26
|
+
if (CONFLICT_POLICIES.includes(value)) {
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
throw new Error(`${callSite}: conflict policy ${typeof value === 'string' ? `'${value}'` : String(value)} is not implemented. The only policy this library has is 'error' (the default): a tool ` +
|
|
30
|
+
`name or injection id a recipe introduces that is already taken refuses at build, naming ` +
|
|
31
|
+
`both sources.\n\n` +
|
|
32
|
+
`'skip', 'replace' and automatic renaming are each a real design, and each one has to ` +
|
|
33
|
+
`answer the same question first — where the dropped or overridden registration is ` +
|
|
34
|
+
`RECORDED. A composition that silently loses a tool answers a turn without it and says ` +
|
|
35
|
+
`nothing, which is the failure this option exists to prevent, so an unimplemented policy ` +
|
|
36
|
+
`is refused here rather than quietly run as 'error'.\n\n` +
|
|
37
|
+
`To compose recipes that overlap today: rename one of the colliding registrations, or ` +
|
|
38
|
+
`have the recipe export the piece so the app registers it once itself.`);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The refusal for a name two sources both registered.
|
|
42
|
+
*
|
|
43
|
+
* Raised only when at least one side came from a recipe. A collision between
|
|
44
|
+
* two direct builder calls keeps the sentence it has always had — the message
|
|
45
|
+
* an app already reads in its tests should not change because a feature it does
|
|
46
|
+
* not use shipped.
|
|
47
|
+
*
|
|
48
|
+
* `what` is the word the reader uses (`'tool name'` / `'injection id'`);
|
|
49
|
+
* `existing` is who registered it first and `incoming` who is registering it
|
|
50
|
+
* now, either of which may be `undefined` for the unattributed case;
|
|
51
|
+
* `callSite` is the API being called, e.g. `Agent.tool()`.
|
|
52
|
+
*/
|
|
53
|
+
export function duplicateRegistrationRefusal(params) {
|
|
54
|
+
const { what, name, existing, incoming, callSite } = params;
|
|
55
|
+
const consequence = what === 'tool name'
|
|
56
|
+
? `The model dispatches tools BY NAME, so two tools under one name is a coin flip whose ` +
|
|
57
|
+
`loser is never called and never mentioned.`
|
|
58
|
+
: `An injection id is how the engine addresses one piece of context — activation, ` +
|
|
59
|
+
`caching, the trace and every recorder key on it — so two under one name is one of ` +
|
|
60
|
+
`them silently never reaching the model.`;
|
|
61
|
+
return (`${callSite}: duplicate ${what} '${name}' — already registered by ` +
|
|
62
|
+
`${describeRecipeSource(existing)}, and now by ${describeRecipeSource(incoming)}.\n\n` +
|
|
63
|
+
`${consequence}\n\n` +
|
|
64
|
+
`Pick one: rename one of them, or have the recipe export the piece so the app registers it ` +
|
|
65
|
+
`once itself. There is no conflict policy that picks a winner for you — see ` +
|
|
66
|
+
`.recipe(recipe, { conflict }).`);
|
|
67
|
+
}
|
|
68
|
+
/** The refusal for one composition applied twice to one agent. */
|
|
69
|
+
export function duplicateRecipeRefusal(params) {
|
|
70
|
+
const { existing, incoming, callSite } = params;
|
|
71
|
+
const sameVersion = existing.version === incoming.version;
|
|
72
|
+
return (`${callSite}: recipe '${incoming.id}' is already applied to this agent ` +
|
|
73
|
+
`${sameVersion
|
|
74
|
+
? `(both at ${existing.version})`
|
|
75
|
+
: `at ${existing.version}, and this one is ${incoming.version}`}.\n\n` +
|
|
76
|
+
`${sameVersion
|
|
77
|
+
? `Applying it twice would run its builder calls twice — which for anything that ` +
|
|
78
|
+
`refuses a duplicate (a tool, an injection) fails on the second pass, and for ` +
|
|
79
|
+
`anything that does not would silently double it.`
|
|
80
|
+
: `One agent runs ONE version of a composition. Two would each apply their builder ` +
|
|
81
|
+
`calls over the other, and the manifest would carry two rows for a composition that ` +
|
|
82
|
+
`cannot be two things at once — nothing downstream could say which one shaped the ` +
|
|
83
|
+
`answer.`}\n\n` +
|
|
84
|
+
`Apply it once${sameVersion ? '' : `, at the version you mean`}.`);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The refusal for a recipe that applies ITSELF, directly or through another.
|
|
88
|
+
*
|
|
89
|
+
* A SEPARATE sentence from {@link duplicateRecipeRefusal}, because these are two
|
|
90
|
+
* different facts and the fix is different for each: "already applied" means the
|
|
91
|
+
* chain names one composition twice, and "currently applying" means the
|
|
92
|
+
* composition is its own ancestor and would never terminate. Telling the author
|
|
93
|
+
* their recursion is a duplicate would send them to look at the wrong line.
|
|
94
|
+
*
|
|
95
|
+
* `stack` is the application chain, outermost first, so the message can show the
|
|
96
|
+
* cycle rather than assert one.
|
|
97
|
+
*/
|
|
98
|
+
export function recursiveRecipeRefusal(params) {
|
|
99
|
+
const { stack, incoming, callSite } = params;
|
|
100
|
+
const cycle = [...stack, incoming].map((r) => `'${r.id}' ${r.version}`).join(' → ');
|
|
101
|
+
return (`${callSite}: recipe '${incoming.id}' ${incoming.version} is applying itself — ${cycle}.\n\n` +
|
|
102
|
+
`\`configure\` runs immediately, so this would recurse until the stack ran out rather than ` +
|
|
103
|
+
`converge on a configured agent.\n\n` +
|
|
104
|
+
`Pull the shared part into a THIRD recipe and have both apply that one, or drop the ` +
|
|
105
|
+
`self-application.`);
|
|
106
|
+
}
|
|
107
|
+
/** The refusal for `configure` returning a promise. */
|
|
108
|
+
export function asyncConfigureRefusal(recipe, callSite) {
|
|
109
|
+
return (`${callSite}: recipe '${recipe.id}' ${recipe.version} returned a promise from \`configure\`. ` +
|
|
110
|
+
`A recipe composes CONFIGURATION only, and \`build()\` is synchronous — there is no phase ` +
|
|
111
|
+
`in which this library would await it, so the work would run after the agent was already ` +
|
|
112
|
+
`built and land on nothing.\n\n` +
|
|
113
|
+
`Do the async part before you build (\`const tools = await mcpClient(…).tools()\`), and ` +
|
|
114
|
+
`have the recipe take what it needs: a recipe can be a plain function of its inputs that ` +
|
|
115
|
+
`RETURNS a recipe (\`export const crm = (tools) => defineAgentRecipe({ … }))\`.`);
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=apply.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply.js","sourceRoot":"","sources":["../../../src/recipes/apply.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,oBAAoB,EAAqB,MAAM,iBAAiB,CAAC;AAG1E,8EAA8E;AAC9E,MAAM,iBAAiB,GAAoC,CAAC,OAAO,CAAC,CAAC;AAErE;;;;;;;;;GASG;AACH,MAAM,UAAU,2BAA2B,CACzC,KAAc,EACd,QAAgB;IAEhB,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,OAAO,CAAC;IACxC,IAAK,iBAAwC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9D,OAAO,KAA6B,CAAC;IACvC,CAAC;IACD,MAAM,IAAI,KAAK,CACb,GAAG,QAAQ,qBACT,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CACzD,yFAAyF;QACvF,0FAA0F;QAC1F,mBAAmB;QACnB,uFAAuF;QACvF,mFAAmF;QACnF,wFAAwF;QACxF,0FAA0F;QAC1F,yDAAyD;QACzD,uFAAuF;QACvF,uEAAuE,CAC1E,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,4BAA4B,CAAC,MAM5C;IACC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IAC5D,MAAM,WAAW,GACf,IAAI,KAAK,WAAW;QAClB,CAAC,CAAC,uFAAuF;YACvF,4CAA4C;QAC9C,CAAC,CAAC,iFAAiF;YACjF,oFAAoF;YACpF,yCAAyC,CAAC;IAChD,OAAO,CACL,GAAG,QAAQ,eAAe,IAAI,KAAK,IAAI,4BAA4B;QACnE,GAAG,oBAAoB,CAAC,QAAQ,CAAC,gBAAgB,oBAAoB,CAAC,QAAQ,CAAC,OAAO;QACtF,GAAG,WAAW,MAAM;QACpB,4FAA4F;QAC5F,6EAA6E;QAC7E,gCAAgC,CACjC,CAAC;AACJ,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,sBAAsB,CAAC,MAItC;IACC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IAChD,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,KAAK,QAAQ,CAAC,OAAO,CAAC;IAC1D,OAAO,CACL,GAAG,QAAQ,aAAa,QAAQ,CAAC,EAAE,qCAAqC;QACxE,GACE,WAAW;YACT,CAAC,CAAC,YAAY,QAAQ,CAAC,OAAO,GAAG;YACjC,CAAC,CAAC,MAAM,QAAQ,CAAC,OAAO,qBAAqB,QAAQ,CAAC,OAAO,EACjE,OAAO;QACP,GACE,WAAW;YACT,CAAC,CAAC,gFAAgF;gBAChF,+EAA+E;gBAC/E,kDAAkD;YACpD,CAAC,CAAC,kFAAkF;gBAClF,qFAAqF;gBACrF,mFAAmF;gBACnF,SACN,MAAM;QACN,gBAAgB,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,2BAA2B,GAAG,CAClE,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAItC;IACC,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IAC7C,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,EAAE,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpF,OAAO,CACL,GAAG,QAAQ,aAAa,QAAQ,CAAC,EAAE,KAAK,QAAQ,CAAC,OAAO,yBAAyB,KAAK,OAAO;QAC7F,4FAA4F;QAC5F,qCAAqC;QACrC,qFAAqF;QACrF,mBAAmB,CACpB,CAAC;AACJ,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,qBAAqB,CAAC,MAAqB,EAAE,QAAgB;IAC3E,OAAO,CACL,GAAG,QAAQ,aAAa,MAAM,CAAC,EAAE,KAAK,MAAM,CAAC,OAAO,0CAA0C;QAC9F,2FAA2F;QAC3F,0FAA0F;QAC1F,gCAAgC;QAChC,yFAAyF;QACzF,0FAA0F;QAC1F,gFAAgF,CACjF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* defineAgentRecipe — declare a named, versioned composition.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: a validating factory that freezes. No class, no registry, no
|
|
5
|
+
* instance state — the "recipes over primitives" instruction taken
|
|
6
|
+
* literally.
|
|
7
|
+
* Role: recipes/ layer, pure. The validation it runs is the SAME function
|
|
8
|
+
* `AgentBuilder.recipe()` runs, so a hand-written literal cannot get
|
|
9
|
+
* past the checks the factory makes; the factory only moves the
|
|
10
|
+
* refusal to the declaration, which is where the fix is.
|
|
11
|
+
* Emits: N/A.
|
|
12
|
+
*
|
|
13
|
+
* ## Why it freezes
|
|
14
|
+
*
|
|
15
|
+
* A recipe is handed to `.recipe()` on one agent and, typically, to `.recipe()`
|
|
16
|
+
* on several more. A mutable one is a shared object that a single consumer can
|
|
17
|
+
* edit for everybody — and the edit would be invisible on the record, because
|
|
18
|
+
* the manifest reports the id and the version, both of which would still say
|
|
19
|
+
* what they always said. `Object.freeze` is shallow, which is exactly the
|
|
20
|
+
* depth that matters here: the four fields are three strings and a function.
|
|
21
|
+
*/
|
|
22
|
+
import type { AgentRecipe } from './types.js';
|
|
23
|
+
/**
|
|
24
|
+
* A recipe declaration that cannot be honoured. Thrown by
|
|
25
|
+
* {@link defineAgentRecipe} and by `AgentBuilder.recipe()` — the same class
|
|
26
|
+
* from both doors, because it is the same mistake wherever it is caught.
|
|
27
|
+
*/
|
|
28
|
+
export declare class InvalidAgentRecipeError extends Error {
|
|
29
|
+
readonly code: "ERR_INVALID_AGENT_RECIPE";
|
|
30
|
+
/** Which field was refused (`'id'`, `'version'`, `'configure'`, `'shape'`). */
|
|
31
|
+
readonly field: string;
|
|
32
|
+
constructor(field: string, message: string);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Validate a recipe declaration, or refuse it by name.
|
|
36
|
+
*
|
|
37
|
+
* Total over `unknown`: this is the one gate, and it is called from the
|
|
38
|
+
* factory AND from `.recipe()`, so no recipe reaches an agent unvalidated.
|
|
39
|
+
*
|
|
40
|
+
* @param value - the candidate declaration.
|
|
41
|
+
* @param callSite - the API the author called, named in every refusal.
|
|
42
|
+
*/
|
|
43
|
+
export declare function assertAgentRecipe(value: unknown, callSite: string): asserts value is AgentRecipe;
|
|
44
|
+
/**
|
|
45
|
+
* Declare a recipe: a name, a version, and the builder calls it stands for.
|
|
46
|
+
*
|
|
47
|
+
* Validates every field and returns a frozen object. Refusals name the field
|
|
48
|
+
* and the fix — `defineAgentRecipe` is where a bad id or version costs one
|
|
49
|
+
* line, and `.recipe()` is where the same mistake costs a stack trace through
|
|
50
|
+
* somebody else's app.
|
|
51
|
+
*
|
|
52
|
+
* @example the composition an app imports and applies
|
|
53
|
+
* ```ts
|
|
54
|
+
* import { defineAgentRecipe } from 'agentfootprint/recipes';
|
|
55
|
+
*
|
|
56
|
+
* export const supportDesk = defineAgentRecipe({
|
|
57
|
+
* id: 'support-desk',
|
|
58
|
+
* version: '1.2.0',
|
|
59
|
+
* description: 'Order lookup + refund policy, the way support runs it.',
|
|
60
|
+
* configure: (agent) => {
|
|
61
|
+
* agent.system('You answer support questions.').tool(lookupOrder);
|
|
62
|
+
* },
|
|
63
|
+
* });
|
|
64
|
+
*
|
|
65
|
+
* const agent = Agent.create({ provider, model }).recipe(supportDesk).build();
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export declare function defineAgentRecipe(recipe: AgentRecipe): AgentRecipe;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* defineAgentRecipe — declare a named, versioned composition.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: a validating factory that freezes. No class, no registry, no
|
|
5
|
+
* instance state — the "recipes over primitives" instruction taken
|
|
6
|
+
* literally.
|
|
7
|
+
* Role: recipes/ layer, pure. The validation it runs is the SAME function
|
|
8
|
+
* `AgentBuilder.recipe()` runs, so a hand-written literal cannot get
|
|
9
|
+
* past the checks the factory makes; the factory only moves the
|
|
10
|
+
* refusal to the declaration, which is where the fix is.
|
|
11
|
+
* Emits: N/A.
|
|
12
|
+
*
|
|
13
|
+
* ## Why it freezes
|
|
14
|
+
*
|
|
15
|
+
* A recipe is handed to `.recipe()` on one agent and, typically, to `.recipe()`
|
|
16
|
+
* on several more. A mutable one is a shared object that a single consumer can
|
|
17
|
+
* edit for everybody — and the edit would be invisible on the record, because
|
|
18
|
+
* the manifest reports the id and the version, both of which would still say
|
|
19
|
+
* what they always said. `Object.freeze` is shallow, which is exactly the
|
|
20
|
+
* depth that matters here: the four fields are three strings and a function.
|
|
21
|
+
*/
|
|
22
|
+
import { isPlainRecipeId, recipeIdRefusal } from './identifier.js';
|
|
23
|
+
import { isSemverVersion, versionRefusal } from './version.js';
|
|
24
|
+
/** The fields a recipe declares. Anything else is a typo — see the refusal. */
|
|
25
|
+
const RECIPE_KEYS = ['id', 'version', 'description', 'configure'];
|
|
26
|
+
/**
|
|
27
|
+
* A recipe declaration that cannot be honoured. Thrown by
|
|
28
|
+
* {@link defineAgentRecipe} and by `AgentBuilder.recipe()` — the same class
|
|
29
|
+
* from both doors, because it is the same mistake wherever it is caught.
|
|
30
|
+
*/
|
|
31
|
+
export class InvalidAgentRecipeError extends Error {
|
|
32
|
+
code = 'ERR_INVALID_AGENT_RECIPE';
|
|
33
|
+
/** Which field was refused (`'id'`, `'version'`, `'configure'`, `'shape'`). */
|
|
34
|
+
field;
|
|
35
|
+
constructor(field, message) {
|
|
36
|
+
super(message);
|
|
37
|
+
this.name = 'InvalidAgentRecipeError';
|
|
38
|
+
this.field = field;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Validate a recipe declaration, or refuse it by name.
|
|
43
|
+
*
|
|
44
|
+
* Total over `unknown`: this is the one gate, and it is called from the
|
|
45
|
+
* factory AND from `.recipe()`, so no recipe reaches an agent unvalidated.
|
|
46
|
+
*
|
|
47
|
+
* @param value - the candidate declaration.
|
|
48
|
+
* @param callSite - the API the author called, named in every refusal.
|
|
49
|
+
*/
|
|
50
|
+
export function assertAgentRecipe(value, callSite) {
|
|
51
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
52
|
+
throw new InvalidAgentRecipeError('shape', `${callSite}: a recipe is an object { id, version, description?, configure }, not ` +
|
|
53
|
+
`${value === null ? 'null' : Array.isArray(value) ? 'an array' : typeof value}.`);
|
|
54
|
+
}
|
|
55
|
+
const record = value;
|
|
56
|
+
// Unknown keys first: `name:` instead of `id:` fails every later check with a
|
|
57
|
+
// message about the field that is MISSING, which sends the reader looking for
|
|
58
|
+
// a field they can plainly see they wrote.
|
|
59
|
+
const unknown = Object.keys(record).filter((key) => !RECIPE_KEYS.includes(key));
|
|
60
|
+
if (unknown.length > 0) {
|
|
61
|
+
throw new InvalidAgentRecipeError('shape', `${callSite}: unknown field${unknown.length > 1 ? 's' : ''} ${unknown
|
|
62
|
+
.map((k) => `'${k}'`)
|
|
63
|
+
.join(', ')}. A recipe declares exactly ${RECIPE_KEYS.map((k) => `\`${k}\``).join(', ')} ` +
|
|
64
|
+
`— everything else about the agent is expressed by the builder calls \`configure\` ` +
|
|
65
|
+
`makes, which is the whole point of composing over the builder instead of inventing a ` +
|
|
66
|
+
`second configuration format.`);
|
|
67
|
+
}
|
|
68
|
+
if (!isPlainRecipeId(record.id)) {
|
|
69
|
+
throw new InvalidAgentRecipeError('id', recipeIdRefusal(callSite, record.id));
|
|
70
|
+
}
|
|
71
|
+
if (!isSemverVersion(record.version)) {
|
|
72
|
+
throw new InvalidAgentRecipeError('version', versionRefusal(callSite, record.version));
|
|
73
|
+
}
|
|
74
|
+
if (record.description !== undefined && typeof record.description !== 'string') {
|
|
75
|
+
throw new InvalidAgentRecipeError('description', `${callSite}: description must be a string (one sentence saying what this composition is ` +
|
|
76
|
+
`for), or omitted. Got ${typeof record.description}.`);
|
|
77
|
+
}
|
|
78
|
+
if (typeof record.configure !== 'function') {
|
|
79
|
+
throw new InvalidAgentRecipeError('configure', `${callSite}: configure must be a function (builder) => void — the builder calls this ` +
|
|
80
|
+
`composition stands for. Got ${record.configure === undefined ? 'nothing' : typeof record.configure}. A recipe with no \`configure\` configures nothing: it would apply cleanly, change ` +
|
|
81
|
+
`no behaviour, and still put a row on the run manifest claiming it shaped the agent.`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Declare a recipe: a name, a version, and the builder calls it stands for.
|
|
86
|
+
*
|
|
87
|
+
* Validates every field and returns a frozen object. Refusals name the field
|
|
88
|
+
* and the fix — `defineAgentRecipe` is where a bad id or version costs one
|
|
89
|
+
* line, and `.recipe()` is where the same mistake costs a stack trace through
|
|
90
|
+
* somebody else's app.
|
|
91
|
+
*
|
|
92
|
+
* @example the composition an app imports and applies
|
|
93
|
+
* ```ts
|
|
94
|
+
* import { defineAgentRecipe } from 'agentfootprint/recipes';
|
|
95
|
+
*
|
|
96
|
+
* export const supportDesk = defineAgentRecipe({
|
|
97
|
+
* id: 'support-desk',
|
|
98
|
+
* version: '1.2.0',
|
|
99
|
+
* description: 'Order lookup + refund policy, the way support runs it.',
|
|
100
|
+
* configure: (agent) => {
|
|
101
|
+
* agent.system('You answer support questions.').tool(lookupOrder);
|
|
102
|
+
* },
|
|
103
|
+
* });
|
|
104
|
+
*
|
|
105
|
+
* const agent = Agent.create({ provider, model }).recipe(supportDesk).build();
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
export function defineAgentRecipe(recipe) {
|
|
109
|
+
assertAgentRecipe(recipe, 'defineAgentRecipe');
|
|
110
|
+
return Object.freeze({ ...recipe });
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=defineAgentRecipe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defineAgentRecipe.js","sourceRoot":"","sources":["../../../src/recipes/defineAgentRecipe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAG/D,+EAA+E;AAC/E,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,aAAa,EAAE,WAAW,CAAU,CAAC;AAE3E;;;;GAIG;AACH,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IACvC,IAAI,GAAG,0BAAmC,CAAC;IACpD,+EAA+E;IACtE,KAAK,CAAS;IAEvB,YAAY,KAAa,EAAE,OAAe;QACxC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;QACtC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;CACF;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAc,EAAE,QAAgB;IAChE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,uBAAuB,CAC/B,OAAO,EACP,GAAG,QAAQ,wEAAwE;YACjF,GAAG,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,KAAK,GAAG,CACnF,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,KAAgC,CAAC;IAEhD,8EAA8E;IAC9E,8EAA8E;IAC9E,2CAA2C;IAC3C,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CACxC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAE,WAAiC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAC3D,CAAC;IACF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,uBAAuB,CAC/B,OAAO,EACP,GAAG,QAAQ,kBAAkB,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,OAAO;aAClE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;aACpB,IAAI,CAAC,IAAI,CAAC,+BAA+B,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;YAC1F,oFAAoF;YACpF,uFAAuF;YACvF,8BAA8B,CACjC,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,uBAAuB,CAAC,IAAI,EAAE,eAAe,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IAChF,CAAC;IACD,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,uBAAuB,CAAC,SAAS,EAAE,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACzF,CAAC;IACD,IAAI,MAAM,CAAC,WAAW,KAAK,SAAS,IAAI,OAAO,MAAM,CAAC,WAAW,KAAK,QAAQ,EAAE,CAAC;QAC/E,MAAM,IAAI,uBAAuB,CAC/B,aAAa,EACb,GAAG,QAAQ,+EAA+E;YACxF,yBAAyB,OAAO,MAAM,CAAC,WAAW,GAAG,CACxD,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,MAAM,CAAC,SAAS,KAAK,UAAU,EAAE,CAAC;QAC3C,MAAM,IAAI,uBAAuB,CAC/B,WAAW,EACX,GAAG,QAAQ,4EAA4E;YACrF,+BACE,MAAM,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,MAAM,CAAC,SAC7D,sFAAsF;YACtF,qFAAqF,CACxF,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAmB;IACnD,iBAAiB,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAC/C,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;AACtC,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* identifier — the plain-name rule for a recipe id.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: a total predicate + a refusal sentence. Pure, no dependencies.
|
|
5
|
+
* Role: recipes/ layer. Used by `defineAgentRecipe` and by the same
|
|
6
|
+
* validation `AgentBuilder.recipe()` runs on a hand-written literal.
|
|
7
|
+
* Emits: N/A.
|
|
8
|
+
*
|
|
9
|
+
* ## Two rules, and the second one is the interesting one
|
|
10
|
+
*
|
|
11
|
+
* 1. **A plain name.** Lower-case words joined by single hyphens:
|
|
12
|
+
* `support-desk`, `triage`, `refund-policy`. The id is what a person reads
|
|
13
|
+
* on a run manifest, in a conflict refusal and in a bug report, so it is
|
|
14
|
+
* spelled the way the rest of this library's public names are — for the
|
|
15
|
+
* common reader, not for the implementation.
|
|
16
|
+
*
|
|
17
|
+
* 2. **No version suffix.** `support-desk-2` and `support-desk-v2` are refused.
|
|
18
|
+
* A recipe already HAS a version field; an id that also encodes one produces
|
|
19
|
+
* two names for one composition, and then nothing groups: runs of `-2` and
|
|
20
|
+
* runs of the original look like two unrelated agents on the record, while
|
|
21
|
+
* the field that exists to tell them apart says `1.0.0` on both.
|
|
22
|
+
*
|
|
23
|
+
* ## The honest limit of rule 2
|
|
24
|
+
*
|
|
25
|
+
* It matches the version-suffix SHAPES — a final hyphen-separated segment that
|
|
26
|
+
* is nothing but digits, optionally preceded by `v`. It does NOT catch an id
|
|
27
|
+
* that merely ends in a digit, because `oauth2`, `s3-archive` and `sha256` are
|
|
28
|
+
* real words and refusing them would be worse than missing `triage2`. Stated
|
|
29
|
+
* rather than implied: this check narrows a mistake, it does not eliminate it.
|
|
30
|
+
*/
|
|
31
|
+
/** Whether `value` is a plain recipe id. Total: any input, no throw. */
|
|
32
|
+
export declare function isPlainRecipeId(value: unknown): value is string;
|
|
33
|
+
/**
|
|
34
|
+
* The sentence a bad id gets. Names the value and the specific rule it broke —
|
|
35
|
+
* the version-suffix case in particular, because the fix there is not
|
|
36
|
+
* "spell it differently" but "use the field that already exists".
|
|
37
|
+
*
|
|
38
|
+
* @param callSite - the API the author called, e.g. `defineAgentRecipe`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function recipeIdRefusal(callSite: string, value: unknown): string;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* identifier — the plain-name rule for a recipe id.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: a total predicate + a refusal sentence. Pure, no dependencies.
|
|
5
|
+
* Role: recipes/ layer. Used by `defineAgentRecipe` and by the same
|
|
6
|
+
* validation `AgentBuilder.recipe()` runs on a hand-written literal.
|
|
7
|
+
* Emits: N/A.
|
|
8
|
+
*
|
|
9
|
+
* ## Two rules, and the second one is the interesting one
|
|
10
|
+
*
|
|
11
|
+
* 1. **A plain name.** Lower-case words joined by single hyphens:
|
|
12
|
+
* `support-desk`, `triage`, `refund-policy`. The id is what a person reads
|
|
13
|
+
* on a run manifest, in a conflict refusal and in a bug report, so it is
|
|
14
|
+
* spelled the way the rest of this library's public names are — for the
|
|
15
|
+
* common reader, not for the implementation.
|
|
16
|
+
*
|
|
17
|
+
* 2. **No version suffix.** `support-desk-2` and `support-desk-v2` are refused.
|
|
18
|
+
* A recipe already HAS a version field; an id that also encodes one produces
|
|
19
|
+
* two names for one composition, and then nothing groups: runs of `-2` and
|
|
20
|
+
* runs of the original look like two unrelated agents on the record, while
|
|
21
|
+
* the field that exists to tell them apart says `1.0.0` on both.
|
|
22
|
+
*
|
|
23
|
+
* ## The honest limit of rule 2
|
|
24
|
+
*
|
|
25
|
+
* It matches the version-suffix SHAPES — a final hyphen-separated segment that
|
|
26
|
+
* is nothing but digits, optionally preceded by `v`. It does NOT catch an id
|
|
27
|
+
* that merely ends in a digit, because `oauth2`, `s3-archive` and `sha256` are
|
|
28
|
+
* real words and refusing them would be worse than missing `triage2`. Stated
|
|
29
|
+
* rather than implied: this check narrows a mistake, it does not eliminate it.
|
|
30
|
+
*/
|
|
31
|
+
/** Lower-kebab: starts with a letter, single hyphens, no trailing hyphen. */
|
|
32
|
+
const PLAIN_NAME = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
33
|
+
/** A final segment that is a version: `2`, `07`, `v2`. */
|
|
34
|
+
const VERSION_SUFFIX = /(?:^|-)v?\d+$/;
|
|
35
|
+
/** Long enough for a sentence-like name, short enough to read in a refusal. */
|
|
36
|
+
const MAX_LENGTH = 64;
|
|
37
|
+
/** Whether `value` is a plain recipe id. Total: any input, no throw. */
|
|
38
|
+
export function isPlainRecipeId(value) {
|
|
39
|
+
return (typeof value === 'string' &&
|
|
40
|
+
value.length <= MAX_LENGTH &&
|
|
41
|
+
PLAIN_NAME.test(value) &&
|
|
42
|
+
!VERSION_SUFFIX.test(value));
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The sentence a bad id gets. Names the value and the specific rule it broke —
|
|
46
|
+
* the version-suffix case in particular, because the fix there is not
|
|
47
|
+
* "spell it differently" but "use the field that already exists".
|
|
48
|
+
*
|
|
49
|
+
* @param callSite - the API the author called, e.g. `defineAgentRecipe`.
|
|
50
|
+
*/
|
|
51
|
+
export function recipeIdRefusal(callSite, value) {
|
|
52
|
+
const shown = typeof value === 'string' ? `'${value}'` : `${typeof value}`;
|
|
53
|
+
if (typeof value === 'string' && PLAIN_NAME.test(value) && VERSION_SUFFIX.test(value)) {
|
|
54
|
+
const base = value.replace(VERSION_SUFFIX, '');
|
|
55
|
+
return (`${callSite}: id ${shown} ends in a version suffix. A recipe carries its version in the ` +
|
|
56
|
+
`\`version\` field, so an id that encodes one too gives a single composition two names — ` +
|
|
57
|
+
`and then nothing groups: runs of ${shown} and runs of '${base}' read as two unrelated ` +
|
|
58
|
+
`agents on the manifest, while the field that exists to tell them apart says the same ` +
|
|
59
|
+
`thing on both.\n\n` +
|
|
60
|
+
`Use id: '${base || 'a-plain-name'}' and bump \`version\` instead.`);
|
|
61
|
+
}
|
|
62
|
+
return (`${callSite}: id ${shown} is not a plain name. A recipe id is lower-case words joined by ` +
|
|
63
|
+
`single hyphens, starting with a letter, at most ${MAX_LENGTH} characters — ` +
|
|
64
|
+
`'support-desk', 'triage', 'refund-policy'.\n\n` +
|
|
65
|
+
`${diagnose(value)}\n\n` +
|
|
66
|
+
`It is spelled for the person who reads it on a run manifest, in a conflict refusal and in ` +
|
|
67
|
+
`a bug report — not for the code.`);
|
|
68
|
+
}
|
|
69
|
+
/** The specific rule broken, when the shape names one. */
|
|
70
|
+
function diagnose(value) {
|
|
71
|
+
if (typeof value !== 'string') {
|
|
72
|
+
return `An id is a string; this was ${value === null ? 'null' : typeof value}.`;
|
|
73
|
+
}
|
|
74
|
+
if (value === '')
|
|
75
|
+
return 'This one is empty.';
|
|
76
|
+
if (value.length > MAX_LENGTH)
|
|
77
|
+
return `This one is ${value.length} characters.`;
|
|
78
|
+
if (value !== value.trim())
|
|
79
|
+
return 'This one has leading or trailing whitespace.';
|
|
80
|
+
if (/[A-Z]/.test(value)) {
|
|
81
|
+
return `Lower-case only — write '${value
|
|
82
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
83
|
+
.toLowerCase()}'.`;
|
|
84
|
+
}
|
|
85
|
+
if (/\s/.test(value))
|
|
86
|
+
return `Join the words with hyphens — '${value.trim().replace(/\s+/g, '-')}'.`;
|
|
87
|
+
if (/^[^a-z]/.test(value))
|
|
88
|
+
return 'It has to start with a letter.';
|
|
89
|
+
if (/-$/.test(value))
|
|
90
|
+
return 'It cannot end with a hyphen.';
|
|
91
|
+
if (/--/.test(value))
|
|
92
|
+
return 'Single hyphens only.';
|
|
93
|
+
return 'Allowed characters are a–z, 0–9 and single hyphens between them.';
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=identifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identifier.js","sourceRoot":"","sources":["../../../src/recipes/identifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,6EAA6E;AAC7E,MAAM,UAAU,GAAG,iCAAiC,CAAC;AAErD,0DAA0D;AAC1D,MAAM,cAAc,GAAG,eAAe,CAAC;AAEvC,+EAA+E;AAC/E,MAAM,UAAU,GAAG,EAAE,CAAC;AAEtB,wEAAwE;AACxE,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,CAAC,MAAM,IAAI,UAAU;QAC1B,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC;QACtB,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAC5B,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,QAAgB,EAAE,KAAc;IAC9D,MAAM,KAAK,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,KAAK,EAAE,CAAC;IAC3E,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACtF,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;QAC/C,OAAO,CACL,GAAG,QAAQ,QAAQ,KAAK,iEAAiE;YACzF,0FAA0F;YAC1F,oCAAoC,KAAK,iBAAiB,IAAI,0BAA0B;YACxF,uFAAuF;YACvF,oBAAoB;YACpB,YAAY,IAAI,IAAI,cAAc,iCAAiC,CACpE,CAAC;IACJ,CAAC;IACD,OAAO,CACL,GAAG,QAAQ,QAAQ,KAAK,kEAAkE;QAC1F,mDAAmD,UAAU,gBAAgB;QAC7E,gDAAgD;QAChD,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM;QACxB,4FAA4F;QAC5F,kCAAkC,CACnC,CAAC;AACJ,CAAC;AAED,0DAA0D;AAC1D,SAAS,QAAQ,CAAC,KAAc;IAC9B,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,+BAA+B,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,KAAK,GAAG,CAAC;IAClF,CAAC;IACD,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,oBAAoB,CAAC;IAC9C,IAAI,KAAK,CAAC,MAAM,GAAG,UAAU;QAAE,OAAO,eAAe,KAAK,CAAC,MAAM,cAAc,CAAC;IAChF,IAAI,KAAK,KAAK,KAAK,CAAC,IAAI,EAAE;QAAE,OAAO,8CAA8C,CAAC;IAClF,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,4BAA4B,KAAK;aACrC,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC;aACtC,WAAW,EAAE,IAAI,CAAC;IACvB,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;QAClB,OAAO,kCAAkC,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC;IACjF,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,gCAAgC,CAAC;IACnE,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,8BAA8B,CAAC;IAC5D,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,sBAAsB,CAAC;IACpD,OAAO,kEAAkE,CAAC;AAC5E,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* recipes — the declared unit of agent configuration.
|
|
3
|
+
*
|
|
4
|
+
* The door: what `agentfootprint/recipes` publishes. Read `types.ts` for the
|
|
5
|
+
* argument (including the no-lifecycle limit) and README.md for the worked
|
|
6
|
+
* example.
|
|
7
|
+
*
|
|
8
|
+
* ## What is NOT here, and why
|
|
9
|
+
*
|
|
10
|
+
* The validators (`isPlainRecipeId`, `isSemverVersion`), the provenance
|
|
11
|
+
* formatter and every refusal sentence are internal. `AgentBuilder` imports
|
|
12
|
+
* them by module path, and this door stays the AUTHORING vocabulary: declare a
|
|
13
|
+
* recipe, catch the refusal, name the types. Publishing the plumbing would be
|
|
14
|
+
* surface nobody imports and everybody has to keep documented — and a recipe's
|
|
15
|
+
* whole claim is that it is a composition over primitives, not a second
|
|
16
|
+
* framework with an API of its own.
|
|
17
|
+
*/
|
|
18
|
+
export { defineAgentRecipe, InvalidAgentRecipeError } from './defineAgentRecipe.js';
|
|
19
|
+
export type { AgentRecipe, AppliedRecipe, RecipeConflictPolicy, RecipeOptions } from './types.js';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* recipes — the declared unit of agent configuration.
|
|
3
|
+
*
|
|
4
|
+
* The door: what `agentfootprint/recipes` publishes. Read `types.ts` for the
|
|
5
|
+
* argument (including the no-lifecycle limit) and README.md for the worked
|
|
6
|
+
* example.
|
|
7
|
+
*
|
|
8
|
+
* ## What is NOT here, and why
|
|
9
|
+
*
|
|
10
|
+
* The validators (`isPlainRecipeId`, `isSemverVersion`), the provenance
|
|
11
|
+
* formatter and every refusal sentence are internal. `AgentBuilder` imports
|
|
12
|
+
* them by module path, and this door stays the AUTHORING vocabulary: declare a
|
|
13
|
+
* recipe, catch the refusal, name the types. Publishing the plumbing would be
|
|
14
|
+
* surface nobody imports and everybody has to keep documented — and a recipe's
|
|
15
|
+
* whole claim is that it is a composition over primitives, not a second
|
|
16
|
+
* framework with an API of its own.
|
|
17
|
+
*/
|
|
18
|
+
export { defineAgentRecipe, InvalidAgentRecipeError } from './defineAgentRecipe.js';
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/recipes/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* provenance — who registered this name.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: a small value type + one formatter. Pure, no dependencies beyond
|
|
5
|
+
* the recipe types.
|
|
6
|
+
* Role: recipes/ layer. `AgentBuilder` records a {@link RecipeSource} beside
|
|
7
|
+
* every tool name and injection id it takes, and reads it back when
|
|
8
|
+
* two registrations collide.
|
|
9
|
+
* Emits: N/A.
|
|
10
|
+
*
|
|
11
|
+
* ## Why provenance at all
|
|
12
|
+
*
|
|
13
|
+
* The builder has always refused a duplicate tool name — the model dispatches
|
|
14
|
+
* by name, so two tools called `search` is a coin flip. What it could not say
|
|
15
|
+
* was WHERE each one came from. With one recipe in the chain that answer stops
|
|
16
|
+
* being obvious ("I never registered a `search` tool") and with two it is
|
|
17
|
+
* unrecoverable without reading both recipes' source. A refusal that names both
|
|
18
|
+
* sides turns a hunt into a sentence.
|
|
19
|
+
*
|
|
20
|
+
* ## Three arms, because three things are true and one of them is "I do not know"
|
|
21
|
+
*
|
|
22
|
+
* `local` and `unattributed` are NOT the same fact, and collapsing them would
|
|
23
|
+
* make the refusal lie. `local` means: this builder watched you call
|
|
24
|
+
* `.tool()` / `.injection()` yourself. `unattributed` means: the name was
|
|
25
|
+
* already taken when the ledger was consulted and no source was recorded for
|
|
26
|
+
* it — the honest answer is that this builder does not know, and saying
|
|
27
|
+
* "you registered it" would send the reader to look in the one place it is not.
|
|
28
|
+
*
|
|
29
|
+
* The arm exists because there is more than one way into the injection list:
|
|
30
|
+
* `.injection()` is the funnel for the named flavors, and `.outputSchema()`
|
|
31
|
+
* mounts an instruction of its own. Both record a source today. A third site
|
|
32
|
+
* added later would not, and this arm is what keeps that omission honest
|
|
33
|
+
* instead of blaming the caller.
|
|
34
|
+
*/
|
|
35
|
+
import type { AppliedRecipe } from './types.js';
|
|
36
|
+
/**
|
|
37
|
+
* Where a registered tool name or injection id came from.
|
|
38
|
+
*
|
|
39
|
+
* `stack` is the recipe application chain, OUTERMOST first: a recipe may apply
|
|
40
|
+
* another recipe, and the innermost one is the code that literally called the
|
|
41
|
+
* builder method.
|
|
42
|
+
*/
|
|
43
|
+
export type RecipeSource = {
|
|
44
|
+
readonly kind: 'local';
|
|
45
|
+
} | {
|
|
46
|
+
readonly kind: 'recipe';
|
|
47
|
+
readonly stack: readonly AppliedRecipe[];
|
|
48
|
+
};
|
|
49
|
+
/** The one `local` value. A frozen singleton — it carries no data. */
|
|
50
|
+
export declare const LOCAL_SOURCE: RecipeSource;
|
|
51
|
+
/**
|
|
52
|
+
* Describe a source in a sentence fragment that reads inside a refusal.
|
|
53
|
+
*
|
|
54
|
+
* `undefined` is the unattributed case — see the header. It is a separate
|
|
55
|
+
* answer, never folded into `local`.
|
|
56
|
+
*/
|
|
57
|
+
export declare function describeRecipeSource(source: RecipeSource | undefined): string;
|