@robota-sdk/agent-preset 3.0.0-beta.75
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 +21 -0
- package/README.md +60 -0
- package/dist/node/index.cjs +4 -0
- package/dist/node/index.d.ts +204 -0
- package/dist/node/index.js +4 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Robota Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Agent Preset
|
|
2
|
+
|
|
3
|
+
Preset contract and resolver for the Robota SDK. A preset is a named, pre-tuned bundle of
|
|
4
|
+
`agent-framework` option overrides (persona, model/effort, permission posture, command-module
|
|
5
|
+
selection, execution capabilities, autonomy). This package owns the `IPreset` contract, the
|
|
6
|
+
built-in `default` preset, and the `resolvePreset` precedence merger.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @robota-sdk/agent-preset
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Public API
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
import {
|
|
18
|
+
resolvePreset,
|
|
19
|
+
listPresets,
|
|
20
|
+
getPreset,
|
|
21
|
+
defaultPreset,
|
|
22
|
+
DEFAULT_AGENT_NAME,
|
|
23
|
+
} from '@robota-sdk/agent-preset';
|
|
24
|
+
import type {
|
|
25
|
+
IPreset,
|
|
26
|
+
TResolvedPresetOptions,
|
|
27
|
+
IPresetSummary,
|
|
28
|
+
IResolvePresetContext,
|
|
29
|
+
} from '@robota-sdk/agent-preset';
|
|
30
|
+
|
|
31
|
+
// Resolve a preset into framework option overrides.
|
|
32
|
+
// Precedence (low → high): preset < cliOverrides < explicit.
|
|
33
|
+
const options = resolvePreset('default', {
|
|
34
|
+
cliOverrides: { model: 'some-model' },
|
|
35
|
+
explicit: { temperature: 0.2 },
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Discover presets for UX.
|
|
39
|
+
const summaries = listPresets(); // [{ id: 'default', title, description }, ...]
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The `default` preset carries no overrides, so resolving it is a pure no-op that reproduces the
|
|
43
|
+
standard agent behaviour (no regression).
|
|
44
|
+
|
|
45
|
+
## Dependency Position
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
agent-framework ← neutral assembly + option-type SSOT
|
|
49
|
+
↑
|
|
50
|
+
agent-preset ← this package (preset contract + resolver)
|
|
51
|
+
↑
|
|
52
|
+
agent-cli ← consumes resolvePreset / listPresets
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
This package depends only on `@robota-sdk/agent-framework` and must not re-export it.
|
|
56
|
+
|
|
57
|
+
## Links
|
|
58
|
+
|
|
59
|
+
- [npm](https://www.npmjs.com/package/@robota-sdk/agent-preset)
|
|
60
|
+
- [GitHub](https://github.com/woojubb/robota)
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("node:fs"),t=require("node:os"),n=require("node:path");const r={id:`default`,title:`Default`,description:`Neutral baseline preset — no overrides; reproduces the standard agent behaviour.`},i={id:`autonomous-builder`,title:`Autonomous Builder`,description:`Proactive, self-verifying builder posture — high effort, acts first, dispatches parallel subagents (persona inspired by the documented Fable 5 work-style; original English wording).`,effort:`high`,autonomy:`act-first`,enableParallelSubagents:!0,selfVerification:!0,persona:[`You are a proactive, high-autonomy builder. When you have enough to proceed, proceed and`,`complete the task — act rather than stop to ask. Keep going through the points where a more`,`hesitant assistant would pause for confirmation, and only check in when something is genuinely`,`ambiguous or risky.`,``,`Stay inside the scope of the task. Do the simplest thing that works; do not refactor, add`,`abstractions, or expand scope beyond what was asked. Handle the adjacent detail that the task`,`plainly needs, but resist turning a focused change into a sweeping one.`,``,`After making changes, verify your own work — run the tests, re-read the goal, and confirm the`,`result actually matches what was requested before you call it done.`,``,`Ground your claims in evidence. Before reporting progress, check each claim against an actual`,`tool result from this session; if something is not yet verified, say so plainly rather than`,`implying it is finished.`,``,`Be warm and honest without being sycophantic — tell people what is useful, not just what they`,`want to hear. Stay even-handed and let your conclusions follow from the evidence. When you get`,`something wrong, own it directly, correct it, and move on.`,``,`Lead with the outcome. Report results plainly instead of narrating each step, keep formatting`,`light, and stay concise.`].join(`
|
|
2
|
+
`)},a={id:`careful-reviewer`,title:`Careful Reviewer`,description:`Conservative, ask-first reviewing posture — reads and plans before changing, prompts on write/exec, self-verifies, runs focused (no parallel subagents) at high effort.`,effort:`high`,autonomy:`ask-first`,enableParallelSubagents:!1,selfVerification:!0,persona:[`You are a careful, review-oriented assistant. Read and analyse before you change anything —`,`understand the surrounding code and context first, then lay out a short plan for the change and`,`wait for confirmation before you write or run things that have side effects.`,``,`When you propose a change, explain why — the reasoning behind it and the trade-offs against the`,`alternatives you considered, so the choice can be reviewed rather than taken on trust.`,``,`Stay conservative in scope. Make the change that was asked for and the detail it plainly needs;`,`do not widen the blast radius with unrequested refactors or rewrites.`,``,`Ground your claims in evidence. Before reporting progress or completion, check each claim against`,`an actual tool result from this session; if something is not yet verified, say so plainly rather`,`than implying it is done.`,``,`Be warm and honest without being sycophantic — tell people what is useful, not just what they`,`want to hear. Stay even-handed and let your conclusions follow from the evidence. When you get`,`something wrong, own it directly, correct it, and move on.`].join(`
|
|
3
|
+
`)},o={id:`neutral-executor`,title:`Neutral Executor`,description:`Thin, steerable executor — follows instructions literally, minimal editorialising, strict scope, terse output; balanced autonomy, no parallel subagents, no self-verification, medium effort for predictable scripted use (neutral-alignment work-style; original English wording).`,effort:`medium`,autonomy:`balanced`,enableParallelSubagents:!1,selfVerification:!1,persona:[`Follow the system and user instructions literally. Do what was asked, the way it was asked, and`,`defer to the given instructions over your own preferences.`,``,`Editorialise as little as possible. Skip unrequested commentary, caveats, and asides — give the`,`result, not a narration of it.`,``,`Stay strictly in scope. Make only the change that was requested and the detail it plainly needs;`,`do not add adjacent refactors, comments, or features that were not asked for.`,``,`Keep output terse. Prefer the shortest response that fully answers, so the result is predictable`,`and easy to consume in a script or pipeline.`].join(`
|
|
4
|
+
`)},s={"ask-first":`default`,balanced:`default`,"act-first":`acceptEdits`},c=[r,i,a,o],l=[];function u(){return[...c,...l]}function d(e){let t=new Set(c.map(e=>e.id)),n=[],r=[];for(let i of e){if(t.has(i.id)){r.push({id:i.id,reason:`collides with built-in preset`});continue}if(l.some(e=>e.id===i.id)){r.push({id:i.id,reason:`duplicate preset id`});continue}l.push(i),n.push(i.id)}return{registered:n,rejected:r}}function f(){l.length=0}function p(){return u().map(({id:e,title:t,description:n})=>({id:e,title:t,description:n}))}function m(e){return u().find(t=>t.id===e)}function h(e){let{id:t,title:n,description:r,...i}=e;return i}function g(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>e!==void 0))}function _(e,t){return t?{...e,...g(t)}:e}function v(e,t={}){let n=m(e);if(!n){let t=u().map(e=>e.id).join(`, `);throw Error(`Unknown preset: "${e}". Available presets: ${t}.`)}let r=h(n);return r=_(r,t.cliOverrides),r=_(r,t.explicit),y(r)}function y(e){return e.permissionMode===void 0?e.defaultPermissionMode===void 0?e.autonomy===void 0?e:{...e,permissionMode:s[e.autonomy]}:{...e,permissionMode:e.defaultPermissionMode}:e}const b=[`low`,`medium`,`high`,`xhigh`,`max`],x=[`ask-first`,`act-first`,`balanced`],S=[`plan`,`default`,`acceptEdits`,`bypassPermissions`],C=[`safe`,`moderate`,`full`];function w(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function T(e){return Array.isArray(e)&&e.every(e=>typeof e==`string`)}function E(e,t){let n=[[`persona`,e.persona,e=>t.persona=e],[`systemPrompt`,e.systemPrompt,e=>t.systemPrompt=e],[`appendSystemPrompt`,e.appendSystemPrompt,e=>t.appendSystemPrompt=e],[`agentName`,e.agentName,e=>t.agentName=e],[`model`,e.model,e=>t.model=e],[`language`,e.language,e=>t.language=e]];for(let[e,t,r]of n)if(t!==void 0){if(typeof t!=`string`)return`${e}: expected a string`;r(t)}}function D(e,t){let n=[[`temperature`,e.temperature,e=>t.temperature=e],[`maxOutputTokens`,e.maxOutputTokens,e=>t.maxOutputTokens=e]];for(let[e,t,r]of n)if(t!==void 0){if(typeof t!=`number`||Number.isNaN(t))return`${e}: expected a number`;r(t)}let r=[[`enableParallelSubagents`,e.enableParallelSubagents,e=>t.enableParallelSubagents=e],[`selfVerification`,e.selfVerification,e=>t.selfVerification=e]];for(let[e,t,n]of r)if(t!==void 0){if(typeof t!=`boolean`)return`${e}: expected a boolean`;n(t)}}function O(e,t){if(e.effort!==void 0){if(!b.includes(e.effort))return`effort: expected one of ${b.join(`, `)}`;t.effort=e.effort}if(e.autonomy!==void 0){if(!x.includes(e.autonomy))return`autonomy: expected one of ${x.join(`, `)}`;t.autonomy=e.autonomy}let n=[[`permissionMode`,e.permissionMode,e=>t.permissionMode=e],[`defaultPermissionMode`,e.defaultPermissionMode,e=>t.defaultPermissionMode=e]];for(let[e,t,r]of n)if(t!==void 0){if(!S.includes(t))return`${e}: expected one of ${S.join(`, `)}`;r(t)}if(e.defaultTrustLevel!==void 0){if(!C.includes(e.defaultTrustLevel))return`defaultTrustLevel: expected one of ${C.join(`, `)}`;t.defaultTrustLevel=e.defaultTrustLevel}}function k(e,t){let n=[[`allowedTools`,e.allowedTools,e=>t.allowedTools=e],[`deniedTools`,e.deniedTools,e=>t.deniedTools=e],[`enabledCommandModules`,e.enabledCommandModules,e=>t.enabledCommandModules=e],[`disabledCommandModules`,e.disabledCommandModules,e=>t.disabledCommandModules=e]];for(let[e,t,r]of n)if(t!==void 0){if(!T(t))return`${e}: expected an array of strings`;r(t)}}function A(e){if(!w(e))return{ok:!1,error:`preset: expected a non-null object`};let t=[[`id`,e.id],[`title`,e.title],[`description`,e.description]];for(let[e,n]of t)if(typeof n!=`string`||n.length===0)return{ok:!1,error:`${e}: expected a non-empty string`};let n={},r=E(e,n)??D(e,n)??O(e,n)??k(e,n);return r===void 0?{ok:!0,preset:{id:e.id,title:e.title,description:e.description,...n}}:{ok:!1,error:r}}function j(){return(0,n.join)((0,t.homedir)(),`.robota`,`presets`)}function M(t){if(!(0,e.existsSync)(t))return{loaded:[],errors:[]};let r=[],i=[],a=new Map,o=(0,e.readdirSync)(t).filter(e=>e.endsWith(`.json`));for(let s of o){let o=(0,n.join)(t,s),c;try{c=JSON.parse((0,e.readFileSync)(o,`utf8`))}catch(e){r.push({file:s,error:e instanceof Error?e.message:String(e)});continue}let l=A(c);if(!l.ok){r.push({file:s,error:l.error});continue}i.push(l.preset),a.set(l.preset.id,s)}let s=d(i);for(let e of s.rejected)r.push({file:a.get(e.id)??e.id,error:e.reason});return{loaded:s.registered,errors:r}}function N(e){return M(e?.dir??j())}exports.DEFAULT_AGENT_NAME=`robota-cli`,exports.autonomousBuilderPreset=i,exports.clearExternalPresets=f,exports.defaultExternalPresetDir=j,exports.defaultPreset=r,exports.getPreset=m,exports.listPresets=p,exports.loadExternalPresets=N,exports.loadExternalPresetsFromDir=M,exports.registerExternalPresets=d,exports.resolvePreset=v,exports.validateExternalPreset=A;
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { ICreateSessionOptions } from "@robota-sdk/agent-framework";
|
|
2
|
+
|
|
3
|
+
//#region src/preset-types.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Effort dial passed through to the model invocation (mechanism, not persona text).
|
|
6
|
+
* `'xhigh'` corresponds to the long-running ("ultra") tier; `'high'` is the neutral default.
|
|
7
|
+
*
|
|
8
|
+
* Reuses the framework's `ICreateSessionOptions['effort']` channel (which is the
|
|
9
|
+
* `agent-core` `TModelEffort` SSOT) so a resolved preset effort threads straight to the
|
|
10
|
+
* provider invocation seam (PRESET-008) without introducing a parallel union.
|
|
11
|
+
*/
|
|
12
|
+
type TPresetEffort = NonNullable<ICreateSessionOptions['effort']>;
|
|
13
|
+
/**
|
|
14
|
+
* Behavioural autonomy posture. Drives the permission posture
|
|
15
|
+
* (`permissionMode` / `defaultTrustLevel`) — it is a mechanism mapping, not a display label.
|
|
16
|
+
*/
|
|
17
|
+
type TPresetAutonomy = 'ask-first' | 'balanced' | 'act-first';
|
|
18
|
+
/** Default trust level applied when the preset opts into a coarse permission posture. */
|
|
19
|
+
type TPresetTrustLevel = 'safe' | 'moderate' | 'full';
|
|
20
|
+
/**
|
|
21
|
+
* Permission mode reused from the framework option SSOT (`ICreateSessionOptions`).
|
|
22
|
+
* Reusing the indexed access keeps a single source of truth for the permission-mode union.
|
|
23
|
+
*/
|
|
24
|
+
type TPresetPermissionMode = ICreateSessionOptions['permissionMode'];
|
|
25
|
+
/**
|
|
26
|
+
* The framework-facing option subset a preset resolves into. Every field maps to an
|
|
27
|
+
* existing `agent-framework` session/assembly seam; presets never introduce new option types.
|
|
28
|
+
*/
|
|
29
|
+
interface TResolvedPresetOptions {
|
|
30
|
+
/**
|
|
31
|
+
* Portable persona/behaviour block (tone, refusal philosophy, output style, proactivity).
|
|
32
|
+
* Composed by the framework as a `source: 'persona'` system-prompt section (priority 5) —
|
|
33
|
+
* never runtime/tool/product-identity text. Empty/undefined adds no section (no regression).
|
|
34
|
+
*/
|
|
35
|
+
persona?: string;
|
|
36
|
+
systemPrompt?: string;
|
|
37
|
+
appendSystemPrompt?: string;
|
|
38
|
+
agentName?: string;
|
|
39
|
+
model?: string;
|
|
40
|
+
language?: string;
|
|
41
|
+
effort?: TPresetEffort;
|
|
42
|
+
temperature?: number;
|
|
43
|
+
maxOutputTokens?: number;
|
|
44
|
+
/**
|
|
45
|
+
* Resolved framework permission-mode seam. The end value the assembly applies.
|
|
46
|
+
* Populated either directly, from `defaultPermissionMode`, or from the
|
|
47
|
+
* `autonomy` mapping (precedence: explicit `permissionMode` > `defaultPermissionMode`
|
|
48
|
+
* > `autonomy` mapping).
|
|
49
|
+
*/
|
|
50
|
+
permissionMode?: TPresetPermissionMode;
|
|
51
|
+
/**
|
|
52
|
+
* Coarse permission posture declared by a preset. When set (and no explicit
|
|
53
|
+
* `permissionMode`), {@link resolvePreset} promotes it to `permissionMode`.
|
|
54
|
+
*/
|
|
55
|
+
defaultPermissionMode?: TPresetPermissionMode;
|
|
56
|
+
defaultTrustLevel?: TPresetTrustLevel;
|
|
57
|
+
allowedTools?: readonly string[];
|
|
58
|
+
deniedTools?: readonly string[];
|
|
59
|
+
enabledCommandModules?: readonly string[];
|
|
60
|
+
disabledCommandModules?: readonly string[];
|
|
61
|
+
enableParallelSubagents?: boolean;
|
|
62
|
+
selfVerification?: boolean;
|
|
63
|
+
autonomy?: TPresetAutonomy;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A named, pre-tuned bundle of framework option overrides plus display identity.
|
|
67
|
+
* Extends `TResolvedPresetOptions` (type SSOT) and adds the identity triple.
|
|
68
|
+
*/
|
|
69
|
+
interface IPreset extends TResolvedPresetOptions {
|
|
70
|
+
id: string;
|
|
71
|
+
title: string;
|
|
72
|
+
description: string;
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/presets/default.d.ts
|
|
76
|
+
/**
|
|
77
|
+
* The neutral baseline preset. It carries NO option overrides so that resolving it is a
|
|
78
|
+
* pure no-op — applying `default` reproduces the standard agent behaviour exactly (no regression).
|
|
79
|
+
* The default agent identity is owned by `DEFAULT_AGENT_NAME` in `resolve-preset.ts`, not here,
|
|
80
|
+
* so that this preset stays a true identity element under merging.
|
|
81
|
+
*/
|
|
82
|
+
declare const defaultPreset: IPreset;
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region src/presets/autonomous-builder.d.ts
|
|
85
|
+
/**
|
|
86
|
+
* The first opinionated built-in preset: an autonomous, self-verifying builder posture.
|
|
87
|
+
*
|
|
88
|
+
* It both ships a portable persona block AND sets the framework/executor mechanism flags
|
|
89
|
+
* (`effort`, `autonomy`, `enableParallelSubagents`, `selfVerification`) so the style is backed
|
|
90
|
+
* by real, observable behaviour rather than persona text alone. The identifier is generic; the
|
|
91
|
+
* sourcing footnote in `description` is the only place a work-style attribution appears.
|
|
92
|
+
*
|
|
93
|
+
* For long-running tasks the effort dial may be raised to a higher tier; `'high'` is the
|
|
94
|
+
* neutral default carried here.
|
|
95
|
+
*/
|
|
96
|
+
declare const autonomousBuilderPreset: IPreset;
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region src/resolve-preset.d.ts
|
|
99
|
+
/**
|
|
100
|
+
* Default agent identity. Owned by `agent-preset` (not baked into `defaultPreset`, which must stay
|
|
101
|
+
* a no-op). Consumers apply this when no preset and no explicit override supplies an `agentName`.
|
|
102
|
+
*/
|
|
103
|
+
declare const DEFAULT_AGENT_NAME = "robota-cli";
|
|
104
|
+
/** Lightweight `{ id, title, description }` view of a preset for discovery/UX. */
|
|
105
|
+
interface IPresetSummary {
|
|
106
|
+
id: string;
|
|
107
|
+
title: string;
|
|
108
|
+
description: string;
|
|
109
|
+
}
|
|
110
|
+
/** Outcome of {@link registerExternalPresets}: which ids registered and which were rejected. */
|
|
111
|
+
interface IPresetRegistrationResult {
|
|
112
|
+
registered: readonly string[];
|
|
113
|
+
rejected: readonly {
|
|
114
|
+
id: string;
|
|
115
|
+
reason: string;
|
|
116
|
+
}[];
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Register user-authored external presets into the module-level registry.
|
|
120
|
+
*
|
|
121
|
+
* Conflict policy: an external preset whose id matches a BUILT-IN preset is rejected
|
|
122
|
+
* (`'collides with built-in preset'`) — built-ins always win. An external preset whose id
|
|
123
|
+
* matches an already-registered external preset is rejected (`'duplicate preset id'`) — first
|
|
124
|
+
* registration wins. All other presets are appended and counted as registered.
|
|
125
|
+
*
|
|
126
|
+
* @returns the ids that registered and the `{ id, reason }` of each rejection.
|
|
127
|
+
*/
|
|
128
|
+
declare function registerExternalPresets(presets: readonly IPreset[]): IPresetRegistrationResult;
|
|
129
|
+
/**
|
|
130
|
+
* Remove every registered external preset, leaving only the built-ins.
|
|
131
|
+
* Used at startup before a fresh re-load and for test isolation.
|
|
132
|
+
*/
|
|
133
|
+
declare function clearExternalPresets(): void;
|
|
134
|
+
/**
|
|
135
|
+
* Override layers for {@link resolvePreset}, in increasing precedence.
|
|
136
|
+
* `cliOverrides` model CLI flags; `explicit` models programmatic/SDK options.
|
|
137
|
+
*/
|
|
138
|
+
interface IResolvePresetContext {
|
|
139
|
+
cliOverrides?: TResolvedPresetOptions;
|
|
140
|
+
explicit?: TResolvedPresetOptions;
|
|
141
|
+
}
|
|
142
|
+
/** Return the `{ id, title, description }` summary of every registered preset. */
|
|
143
|
+
declare function listPresets(): readonly IPresetSummary[];
|
|
144
|
+
/** Look up a registered preset by id, or `undefined` if none matches. */
|
|
145
|
+
declare function getPreset(id: string): IPreset | undefined;
|
|
146
|
+
/**
|
|
147
|
+
* Resolve a preset id into framework option overrides.
|
|
148
|
+
*
|
|
149
|
+
* Precedence LOW → HIGH: preset options < `context.cliOverrides` < `context.explicit`
|
|
150
|
+
* (later layers win; `undefined` values are skipped). For the no-op `'default'` preset the
|
|
151
|
+
* result equals the merged overrides, guaranteeing no regression.
|
|
152
|
+
*
|
|
153
|
+
* @throws Error when `id` does not match a registered preset.
|
|
154
|
+
*/
|
|
155
|
+
declare function resolvePreset(id: string, context?: IResolvePresetContext): TResolvedPresetOptions;
|
|
156
|
+
//#endregion
|
|
157
|
+
//#region src/load-external-presets.d.ts
|
|
158
|
+
/** Outcome of an external-preset load: which ids loaded and per-file load/validation errors. */
|
|
159
|
+
interface IExternalPresetLoadResult {
|
|
160
|
+
loaded: readonly string[];
|
|
161
|
+
errors: readonly {
|
|
162
|
+
file: string;
|
|
163
|
+
error: string;
|
|
164
|
+
}[];
|
|
165
|
+
}
|
|
166
|
+
/** Conventional external-preset directory: `~/.robota/presets`. */
|
|
167
|
+
declare function defaultExternalPresetDir(): string;
|
|
168
|
+
/**
|
|
169
|
+
* Load, validate, and register every `*.json` external preset from `dir`.
|
|
170
|
+
*
|
|
171
|
+
* A missing directory yields an empty result (no error). Each file is JSON-parsed and validated;
|
|
172
|
+
* a parse or validation failure is recorded as a per-file error and skipped — the remaining files
|
|
173
|
+
* still load. Validated presets are registered via {@link registerExternalPresets}; registry
|
|
174
|
+
* rejections (built-in id collision or duplicate id) are folded into `errors` against their file.
|
|
175
|
+
*/
|
|
176
|
+
declare function loadExternalPresetsFromDir(dir: string): IExternalPresetLoadResult;
|
|
177
|
+
/**
|
|
178
|
+
* Load external presets from the conventional directory (or `options.dir` when given).
|
|
179
|
+
* Thin wrapper over {@link loadExternalPresetsFromDir}.
|
|
180
|
+
*/
|
|
181
|
+
declare function loadExternalPresets(options?: {
|
|
182
|
+
dir?: string;
|
|
183
|
+
}): IExternalPresetLoadResult;
|
|
184
|
+
//#endregion
|
|
185
|
+
//#region src/preset-validation.d.ts
|
|
186
|
+
/** Result of {@link validateExternalPreset}: the validated preset, or a single error message. */
|
|
187
|
+
type TPresetValidationResult = {
|
|
188
|
+
ok: true;
|
|
189
|
+
preset: IPreset;
|
|
190
|
+
} | {
|
|
191
|
+
ok: false;
|
|
192
|
+
error: string;
|
|
193
|
+
};
|
|
194
|
+
/**
|
|
195
|
+
* Manually validate an unknown value as an {@link IPreset} (no schema library).
|
|
196
|
+
*
|
|
197
|
+
* Required fields: non-empty `id`, `title`, `description` strings. Every other field is
|
|
198
|
+
* optional and is validated only when present; an unrecognised field is dropped (the built
|
|
199
|
+
* preset carries the recognised fields only, never unknown keys). On the first failed check
|
|
200
|
+
* returns `{ ok: false, error: '<field>: <reason>' }`.
|
|
201
|
+
*/
|
|
202
|
+
declare function validateExternalPreset(value: unknown): TPresetValidationResult;
|
|
203
|
+
//#endregion
|
|
204
|
+
export { DEFAULT_AGENT_NAME, type IExternalPresetLoadResult, type IPreset, type IPresetRegistrationResult, type IPresetSummary, type IResolvePresetContext, type TPresetAutonomy, type TPresetEffort, type TPresetPermissionMode, type TPresetTrustLevel, type TPresetValidationResult, type TResolvedPresetOptions, autonomousBuilderPreset, clearExternalPresets, defaultExternalPresetDir, defaultPreset, getPreset, listPresets, loadExternalPresets, loadExternalPresetsFromDir, registerExternalPresets, resolvePreset, validateExternalPreset };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{existsSync as e,readFileSync as t,readdirSync as n}from"node:fs";import{homedir as r}from"node:os";import{join as i}from"node:path";const a={id:`default`,title:`Default`,description:`Neutral baseline preset — no overrides; reproduces the standard agent behaviour.`},o={id:`autonomous-builder`,title:`Autonomous Builder`,description:`Proactive, self-verifying builder posture — high effort, acts first, dispatches parallel subagents (persona inspired by the documented Fable 5 work-style; original English wording).`,effort:`high`,autonomy:`act-first`,enableParallelSubagents:!0,selfVerification:!0,persona:[`You are a proactive, high-autonomy builder. When you have enough to proceed, proceed and`,`complete the task — act rather than stop to ask. Keep going through the points where a more`,`hesitant assistant would pause for confirmation, and only check in when something is genuinely`,`ambiguous or risky.`,``,`Stay inside the scope of the task. Do the simplest thing that works; do not refactor, add`,`abstractions, or expand scope beyond what was asked. Handle the adjacent detail that the task`,`plainly needs, but resist turning a focused change into a sweeping one.`,``,`After making changes, verify your own work — run the tests, re-read the goal, and confirm the`,`result actually matches what was requested before you call it done.`,``,`Ground your claims in evidence. Before reporting progress, check each claim against an actual`,`tool result from this session; if something is not yet verified, say so plainly rather than`,`implying it is finished.`,``,`Be warm and honest without being sycophantic — tell people what is useful, not just what they`,`want to hear. Stay even-handed and let your conclusions follow from the evidence. When you get`,`something wrong, own it directly, correct it, and move on.`,``,`Lead with the outcome. Report results plainly instead of narrating each step, keep formatting`,`light, and stay concise.`].join(`
|
|
2
|
+
`)},s={id:`careful-reviewer`,title:`Careful Reviewer`,description:`Conservative, ask-first reviewing posture — reads and plans before changing, prompts on write/exec, self-verifies, runs focused (no parallel subagents) at high effort.`,effort:`high`,autonomy:`ask-first`,enableParallelSubagents:!1,selfVerification:!0,persona:[`You are a careful, review-oriented assistant. Read and analyse before you change anything —`,`understand the surrounding code and context first, then lay out a short plan for the change and`,`wait for confirmation before you write or run things that have side effects.`,``,`When you propose a change, explain why — the reasoning behind it and the trade-offs against the`,`alternatives you considered, so the choice can be reviewed rather than taken on trust.`,``,`Stay conservative in scope. Make the change that was asked for and the detail it plainly needs;`,`do not widen the blast radius with unrequested refactors or rewrites.`,``,`Ground your claims in evidence. Before reporting progress or completion, check each claim against`,`an actual tool result from this session; if something is not yet verified, say so plainly rather`,`than implying it is done.`,``,`Be warm and honest without being sycophantic — tell people what is useful, not just what they`,`want to hear. Stay even-handed and let your conclusions follow from the evidence. When you get`,`something wrong, own it directly, correct it, and move on.`].join(`
|
|
3
|
+
`)},c={id:`neutral-executor`,title:`Neutral Executor`,description:`Thin, steerable executor — follows instructions literally, minimal editorialising, strict scope, terse output; balanced autonomy, no parallel subagents, no self-verification, medium effort for predictable scripted use (neutral-alignment work-style; original English wording).`,effort:`medium`,autonomy:`balanced`,enableParallelSubagents:!1,selfVerification:!1,persona:[`Follow the system and user instructions literally. Do what was asked, the way it was asked, and`,`defer to the given instructions over your own preferences.`,``,`Editorialise as little as possible. Skip unrequested commentary, caveats, and asides — give the`,`result, not a narration of it.`,``,`Stay strictly in scope. Make only the change that was requested and the detail it plainly needs;`,`do not add adjacent refactors, comments, or features that were not asked for.`,``,`Keep output terse. Prefer the shortest response that fully answers, so the result is predictable`,`and easy to consume in a script or pipeline.`].join(`
|
|
4
|
+
`)},l={"ask-first":`default`,balanced:`default`,"act-first":`acceptEdits`},u=`robota-cli`,d=[a,o,s,c],f=[];function p(){return[...d,...f]}function m(e){let t=new Set(d.map(e=>e.id)),n=[],r=[];for(let i of e){if(t.has(i.id)){r.push({id:i.id,reason:`collides with built-in preset`});continue}if(f.some(e=>e.id===i.id)){r.push({id:i.id,reason:`duplicate preset id`});continue}f.push(i),n.push(i.id)}return{registered:n,rejected:r}}function h(){f.length=0}function g(){return p().map(({id:e,title:t,description:n})=>({id:e,title:t,description:n}))}function _(e){return p().find(t=>t.id===e)}function v(e){let{id:t,title:n,description:r,...i}=e;return i}function y(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>e!==void 0))}function b(e,t){return t?{...e,...y(t)}:e}function x(e,t={}){let n=_(e);if(!n){let t=p().map(e=>e.id).join(`, `);throw Error(`Unknown preset: "${e}". Available presets: ${t}.`)}let r=v(n);return r=b(r,t.cliOverrides),r=b(r,t.explicit),S(r)}function S(e){return e.permissionMode===void 0?e.defaultPermissionMode===void 0?e.autonomy===void 0?e:{...e,permissionMode:l[e.autonomy]}:{...e,permissionMode:e.defaultPermissionMode}:e}const C=[`low`,`medium`,`high`,`xhigh`,`max`],w=[`ask-first`,`act-first`,`balanced`],T=[`plan`,`default`,`acceptEdits`,`bypassPermissions`],E=[`safe`,`moderate`,`full`];function D(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function O(e){return Array.isArray(e)&&e.every(e=>typeof e==`string`)}function k(e,t){let n=[[`persona`,e.persona,e=>t.persona=e],[`systemPrompt`,e.systemPrompt,e=>t.systemPrompt=e],[`appendSystemPrompt`,e.appendSystemPrompt,e=>t.appendSystemPrompt=e],[`agentName`,e.agentName,e=>t.agentName=e],[`model`,e.model,e=>t.model=e],[`language`,e.language,e=>t.language=e]];for(let[e,t,r]of n)if(t!==void 0){if(typeof t!=`string`)return`${e}: expected a string`;r(t)}}function A(e,t){let n=[[`temperature`,e.temperature,e=>t.temperature=e],[`maxOutputTokens`,e.maxOutputTokens,e=>t.maxOutputTokens=e]];for(let[e,t,r]of n)if(t!==void 0){if(typeof t!=`number`||Number.isNaN(t))return`${e}: expected a number`;r(t)}let r=[[`enableParallelSubagents`,e.enableParallelSubagents,e=>t.enableParallelSubagents=e],[`selfVerification`,e.selfVerification,e=>t.selfVerification=e]];for(let[e,t,n]of r)if(t!==void 0){if(typeof t!=`boolean`)return`${e}: expected a boolean`;n(t)}}function j(e,t){if(e.effort!==void 0){if(!C.includes(e.effort))return`effort: expected one of ${C.join(`, `)}`;t.effort=e.effort}if(e.autonomy!==void 0){if(!w.includes(e.autonomy))return`autonomy: expected one of ${w.join(`, `)}`;t.autonomy=e.autonomy}let n=[[`permissionMode`,e.permissionMode,e=>t.permissionMode=e],[`defaultPermissionMode`,e.defaultPermissionMode,e=>t.defaultPermissionMode=e]];for(let[e,t,r]of n)if(t!==void 0){if(!T.includes(t))return`${e}: expected one of ${T.join(`, `)}`;r(t)}if(e.defaultTrustLevel!==void 0){if(!E.includes(e.defaultTrustLevel))return`defaultTrustLevel: expected one of ${E.join(`, `)}`;t.defaultTrustLevel=e.defaultTrustLevel}}function M(e,t){let n=[[`allowedTools`,e.allowedTools,e=>t.allowedTools=e],[`deniedTools`,e.deniedTools,e=>t.deniedTools=e],[`enabledCommandModules`,e.enabledCommandModules,e=>t.enabledCommandModules=e],[`disabledCommandModules`,e.disabledCommandModules,e=>t.disabledCommandModules=e]];for(let[e,t,r]of n)if(t!==void 0){if(!O(t))return`${e}: expected an array of strings`;r(t)}}function N(e){if(!D(e))return{ok:!1,error:`preset: expected a non-null object`};let t=[[`id`,e.id],[`title`,e.title],[`description`,e.description]];for(let[e,n]of t)if(typeof n!=`string`||n.length===0)return{ok:!1,error:`${e}: expected a non-empty string`};let n={},r=k(e,n)??A(e,n)??j(e,n)??M(e,n);return r===void 0?{ok:!0,preset:{id:e.id,title:e.title,description:e.description,...n}}:{ok:!1,error:r}}function P(){return i(r(),`.robota`,`presets`)}function F(r){if(!e(r))return{loaded:[],errors:[]};let a=[],o=[],s=new Map,c=n(r).filter(e=>e.endsWith(`.json`));for(let e of c){let n=i(r,e),c;try{c=JSON.parse(t(n,`utf8`))}catch(t){a.push({file:e,error:t instanceof Error?t.message:String(t)});continue}let l=N(c);if(!l.ok){a.push({file:e,error:l.error});continue}o.push(l.preset),s.set(l.preset.id,e)}let l=m(o);for(let e of l.rejected)a.push({file:s.get(e.id)??e.id,error:e.reason});return{loaded:l.registered,errors:a}}function I(e){return F(e?.dir??P())}export{u as DEFAULT_AGENT_NAME,o as autonomousBuilderPreset,h as clearExternalPresets,P as defaultExternalPresetDir,a as defaultPreset,_ as getPreset,g as listPresets,I as loadExternalPresets,F as loadExternalPresetsFromDir,m as registerExternalPresets,x as resolvePreset,N as validateExternalPreset};
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@robota-sdk/agent-preset",
|
|
3
|
+
"version": "3.0.0-beta.75",
|
|
4
|
+
"description": "Preset contract and resolver for the Robota SDK (IPreset, resolvePreset, listPresets, built-in presets)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/node/index.js",
|
|
7
|
+
"types": "dist/node/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/node/index.d.ts",
|
|
11
|
+
"source": "./src/index.ts",
|
|
12
|
+
"node": {
|
|
13
|
+
"import": "./dist/node/index.js",
|
|
14
|
+
"require": "./dist/node/index.cjs"
|
|
15
|
+
},
|
|
16
|
+
"default": {
|
|
17
|
+
"import": "./dist/node/index.js",
|
|
18
|
+
"require": "./dist/node/index.cjs"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "https://github.com/woojubb/robota.git"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://robota.io/",
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/woojubb/robota/issues"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist"
|
|
32
|
+
],
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@robota-sdk/agent-framework": "3.0.0-beta.75"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"rimraf": "^5.0.10",
|
|
38
|
+
"tsdown": "^0.22.2",
|
|
39
|
+
"typescript": "^5.9.3",
|
|
40
|
+
"vitest": "^3.2.6"
|
|
41
|
+
},
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsdown",
|
|
48
|
+
"build:js": "tsdown --no-dts",
|
|
49
|
+
"build:types": "tsdown --dts",
|
|
50
|
+
"test": "vitest run --passWithNoTests",
|
|
51
|
+
"test:coverage": "vitest run --coverage --passWithNoTests",
|
|
52
|
+
"typecheck": "tsc --noEmit",
|
|
53
|
+
"lint": "eslint src/ --ext .ts",
|
|
54
|
+
"clean": "rimraf dist"
|
|
55
|
+
}
|
|
56
|
+
}
|