@steerable/agent-ui 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +91 -0
- package/README.md +118 -0
- package/dist/components/ChatPanel.d.ts +42 -0
- package/dist/components/ChatPanel.d.ts.map +1 -0
- package/dist/components/ChatPanel.js +46 -0
- package/dist/components/ChatPanel.js.map +1 -0
- package/dist/components/ChatPanel.stories.d.ts +17 -0
- package/dist/components/ChatPanel.stories.d.ts.map +1 -0
- package/dist/components/ChatPanel.stories.js +143 -0
- package/dist/components/ChatPanel.stories.js.map +1 -0
- package/dist/components/MessageList.d.ts +44 -0
- package/dist/components/MessageList.d.ts.map +1 -0
- package/dist/components/MessageList.js +43 -0
- package/dist/components/MessageList.js.map +1 -0
- package/dist/components/MessageList.stories.d.ts +12 -0
- package/dist/components/MessageList.stories.d.ts.map +1 -0
- package/dist/components/MessageList.stories.js +88 -0
- package/dist/components/MessageList.stories.js.map +1 -0
- package/dist/components/OrchestrationPlanCard.d.ts +32 -0
- package/dist/components/OrchestrationPlanCard.d.ts.map +1 -0
- package/dist/components/OrchestrationPlanCard.js +48 -0
- package/dist/components/OrchestrationPlanCard.js.map +1 -0
- package/dist/components/OrchestrationPlanCard.stories.d.ts +12 -0
- package/dist/components/OrchestrationPlanCard.stories.d.ts.map +1 -0
- package/dist/components/OrchestrationPlanCard.stories.js +84 -0
- package/dist/components/OrchestrationPlanCard.stories.js.map +1 -0
- package/dist/components/SSEStreamView.d.ts +29 -0
- package/dist/components/SSEStreamView.d.ts.map +1 -0
- package/dist/components/SSEStreamView.js +78 -0
- package/dist/components/SSEStreamView.js.map +1 -0
- package/dist/components/SSEStreamView.stories.d.ts +12 -0
- package/dist/components/SSEStreamView.stories.d.ts.map +1 -0
- package/dist/components/SSEStreamView.stories.js +86 -0
- package/dist/components/SSEStreamView.stories.js.map +1 -0
- package/dist/components/ToolCallRenderer.d.ts +42 -0
- package/dist/components/ToolCallRenderer.d.ts.map +1 -0
- package/dist/components/ToolCallRenderer.js +70 -0
- package/dist/components/ToolCallRenderer.js.map +1 -0
- package/dist/components/ToolCallRenderer.stories.d.ts +13 -0
- package/dist/components/ToolCallRenderer.stories.d.ts.map +1 -0
- package/dist/components/ToolCallRenderer.stories.js +112 -0
- package/dist/components/ToolCallRenderer.stories.js.map +1 -0
- package/dist/components/cn.d.ts +7 -0
- package/dist/components/cn.d.ts.map +1 -0
- package/dist/components/cn.js +9 -0
- package/dist/components/cn.js.map +1 -0
- package/dist/components/index.d.ts +11 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +6 -0
- package/dist/components/index.js.map +1 -0
- package/dist/hooks/index.d.ts +7 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +4 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/useAgentSession.d.ts +56 -0
- package/dist/hooks/useAgentSession.d.ts.map +1 -0
- package/dist/hooks/useAgentSession.js +128 -0
- package/dist/hooks/useAgentSession.js.map +1 -0
- package/dist/hooks/useChatStream.d.ts +59 -0
- package/dist/hooks/useChatStream.d.ts.map +1 -0
- package/dist/hooks/useChatStream.js +251 -0
- package/dist/hooks/useChatStream.js.map +1 -0
- package/dist/hooks/useToolCallStatus.d.ts +47 -0
- package/dist/hooks/useToolCallStatus.d.ts.map +1 -0
- package/dist/hooks/useToolCallStatus.js +57 -0
- package/dist/hooks/useToolCallStatus.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/tailwind-preset.d.ts +76 -0
- package/dist/tailwind-preset.d.ts.map +1 -0
- package/dist/tailwind-preset.js +104 -0
- package/dist/tailwind-preset.js.map +1 -0
- package/package.json +97 -0
- package/src/Introduction.mdx +71 -0
- package/src/components/ChatPanel.stories.tsx +173 -0
- package/src/components/ChatPanel.tsx +188 -0
- package/src/components/MessageList.stories.tsx +112 -0
- package/src/components/MessageList.test.tsx +57 -0
- package/src/components/MessageList.tsx +144 -0
- package/src/components/OrchestrationPlanCard.stories.tsx +98 -0
- package/src/components/OrchestrationPlanCard.test.tsx +34 -0
- package/src/components/OrchestrationPlanCard.tsx +159 -0
- package/src/components/SSEStreamView.stories.tsx +99 -0
- package/src/components/SSEStreamView.test.tsx +42 -0
- package/src/components/SSEStreamView.tsx +155 -0
- package/src/components/ToolCallRenderer.stories.tsx +152 -0
- package/src/components/ToolCallRenderer.test.tsx +93 -0
- package/src/components/ToolCallRenderer.tsx +213 -0
- package/src/components/cn.ts +10 -0
- package/src/components/index.ts +18 -0
- package/src/hooks/index.ts +21 -0
- package/src/hooks/useAgentSession.mdx +115 -0
- package/src/hooks/useAgentSession.test.ts +127 -0
- package/src/hooks/useAgentSession.ts +181 -0
- package/src/hooks/useChatStream.mdx +129 -0
- package/src/hooks/useChatStream.test.ts +233 -0
- package/src/hooks/useChatStream.ts +341 -0
- package/src/hooks/useToolCallStatus.mdx +102 -0
- package/src/hooks/useToolCallStatus.test.ts +99 -0
- package/src/hooks/useToolCallStatus.ts +99 -0
- package/src/index.ts +12 -0
- package/src/tailwind-preset.ts +109 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `useToolCallStatus`.
|
|
3
|
+
*
|
|
4
|
+
* The status hook combines two axes — lifecycle (pending/running/done/error)
|
|
5
|
+
* and mode (read/safe_write/destructive/local/…) — and exposes the booleans
|
|
6
|
+
* (`isDestructive`, `requiresApproval`) the renderer cares about. We verify
|
|
7
|
+
* both the explicit `mode` override path and the name-pattern fallback that
|
|
8
|
+
* mirrors the framework's `decide_tool_mode`.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { renderHook } from '@testing-library/react';
|
|
12
|
+
import { describe, expect, it } from 'vitest';
|
|
13
|
+
import { useToolCallStatus } from './useToolCallStatus';
|
|
14
|
+
|
|
15
|
+
const baseCall = { id: 'c1', name: 'noop', arguments: {} };
|
|
16
|
+
|
|
17
|
+
describe('useToolCallStatus', () => {
|
|
18
|
+
it('returns pending when no result has arrived', () => {
|
|
19
|
+
const { result } = renderHook(() =>
|
|
20
|
+
useToolCallStatus({ call: { ...baseCall } }),
|
|
21
|
+
);
|
|
22
|
+
expect(result.current.status).toBe('pending');
|
|
23
|
+
expect(result.current.mode).toBe('unknown');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('returns running when runningHint=true and no result yet', () => {
|
|
27
|
+
const { result } = renderHook(() =>
|
|
28
|
+
useToolCallStatus({ call: { ...baseCall }, runningHint: true }),
|
|
29
|
+
);
|
|
30
|
+
expect(result.current.status).toBe('running');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('returns done when result.success=true', () => {
|
|
34
|
+
const { result } = renderHook(() =>
|
|
35
|
+
useToolCallStatus({
|
|
36
|
+
call: { ...baseCall },
|
|
37
|
+
result: { success: true },
|
|
38
|
+
}),
|
|
39
|
+
);
|
|
40
|
+
expect(result.current.status).toBe('done');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('returns error when result.success=false', () => {
|
|
44
|
+
const { result } = renderHook(() =>
|
|
45
|
+
useToolCallStatus({
|
|
46
|
+
call: { ...baseCall },
|
|
47
|
+
result: { success: false, error: 'nope' },
|
|
48
|
+
}),
|
|
49
|
+
);
|
|
50
|
+
expect(result.current.status).toBe('error');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it.each([
|
|
54
|
+
['get_user', 'read'],
|
|
55
|
+
['list_files', 'read'],
|
|
56
|
+
['search_messages', 'read'],
|
|
57
|
+
['create_event', 'safe_write'],
|
|
58
|
+
['update_task', 'safe_write'],
|
|
59
|
+
['delete_chat', 'destructive'],
|
|
60
|
+
['archive_project', 'destructive'],
|
|
61
|
+
['local_run_script', 'local'],
|
|
62
|
+
['shell_exec', 'local'],
|
|
63
|
+
['something_unrecognised', 'unknown'],
|
|
64
|
+
])('infers mode for %s -> %s', (name, expected) => {
|
|
65
|
+
const { result } = renderHook(() =>
|
|
66
|
+
useToolCallStatus({ call: { id: 'x', name, arguments: {} } }),
|
|
67
|
+
);
|
|
68
|
+
expect(result.current.mode).toBe(expected);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('honours an explicit mode override even if the name suggests something else', () => {
|
|
72
|
+
const { result } = renderHook(() =>
|
|
73
|
+
useToolCallStatus({
|
|
74
|
+
call: { id: 'x', name: 'delete_everything', arguments: {} },
|
|
75
|
+
mode: 'safe_write',
|
|
76
|
+
}),
|
|
77
|
+
);
|
|
78
|
+
expect(result.current.mode).toBe('safe_write');
|
|
79
|
+
expect(result.current.isDestructive).toBe(false);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('marks local-mode calls as requiring approval and destructive calls as destructive', () => {
|
|
83
|
+
const local = renderHook(() =>
|
|
84
|
+
useToolCallStatus({
|
|
85
|
+
call: { id: 'x', name: 'local_write_file', arguments: {} },
|
|
86
|
+
}),
|
|
87
|
+
);
|
|
88
|
+
expect(local.result.current.requiresApproval).toBe(true);
|
|
89
|
+
|
|
90
|
+
const destr = renderHook(() =>
|
|
91
|
+
useToolCallStatus({
|
|
92
|
+
call: { id: 'x', name: 'delete_event', arguments: {} },
|
|
93
|
+
}),
|
|
94
|
+
);
|
|
95
|
+
expect(destr.result.current.isDestructive).toBe(true);
|
|
96
|
+
// Destructive does not auto-flip requiresApproval; that's a separate decision.
|
|
97
|
+
expect(destr.result.current.requiresApproval).toBe(false);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `useToolCallStatus` — derive the rendering state for a single tool call.
|
|
3
|
+
*
|
|
4
|
+
* Tool calls in the framework go through four UI states:
|
|
5
|
+
* `pending` The model has emitted the call but no result has arrived.
|
|
6
|
+
* `running` Optional intermediate state when the runtime emits a
|
|
7
|
+
* `tool.running` notification (sidecar / harness can do this).
|
|
8
|
+
* `done` A successful result has been attached.
|
|
9
|
+
* `error` The result reports `success: false` or an error envelope.
|
|
10
|
+
*
|
|
11
|
+
* Plus an additional axis — the `mode` (read / safe_write / destructive / local)
|
|
12
|
+
* — which decides whether the UI should render an approval prompt or an
|
|
13
|
+
* informational badge.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { useMemo } from 'react';
|
|
17
|
+
import type { ToolCall, ToolResult } from '@steerable/agent-protocol';
|
|
18
|
+
|
|
19
|
+
export type ToolCallStatus = 'pending' | 'running' | 'done' | 'error';
|
|
20
|
+
|
|
21
|
+
export type ToolCallMode =
|
|
22
|
+
| 'read'
|
|
23
|
+
| 'safe_write'
|
|
24
|
+
| 'destructive'
|
|
25
|
+
| 'local'
|
|
26
|
+
| 'external'
|
|
27
|
+
| 'ui'
|
|
28
|
+
| 'synthetic'
|
|
29
|
+
| 'unknown';
|
|
30
|
+
|
|
31
|
+
export interface UseToolCallStatusOptions {
|
|
32
|
+
call: ToolCall;
|
|
33
|
+
result?: ToolResult;
|
|
34
|
+
/**
|
|
35
|
+
* Optional override; when omitted the hook infers the mode from the tool
|
|
36
|
+
* name pattern (`get_*` / `list_*` → read, `delete_*` / `archive_*` →
|
|
37
|
+
* destructive, `local_*` → local, etc.). Mirrors the framework's harness
|
|
38
|
+
* `decide_tool_mode`.
|
|
39
|
+
*/
|
|
40
|
+
mode?: ToolCallMode;
|
|
41
|
+
/**
|
|
42
|
+
* If the runtime can emit `tool.running` notifications, set this true to
|
|
43
|
+
* keep `status` in `running` until a result arrives. Defaults to false
|
|
44
|
+
* (status flips straight from `pending` → `done`/`error`).
|
|
45
|
+
*/
|
|
46
|
+
runningHint?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface UseToolCallStatusReturn {
|
|
50
|
+
status: ToolCallStatus;
|
|
51
|
+
mode: ToolCallMode;
|
|
52
|
+
/** True when the UI should ask for explicit user approval before running. */
|
|
53
|
+
requiresApproval: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* True when the runtime should mark the call as destructive in the UI
|
|
56
|
+
* (red tone, undo affordance).
|
|
57
|
+
*/
|
|
58
|
+
isDestructive: boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const READ_PATTERNS = [/^get[_-]/, /^list[_-]/, /^read[_-]/, /^search[_-]/];
|
|
62
|
+
const DESTRUCTIVE_PATTERNS = [
|
|
63
|
+
/^delete[_-]/,
|
|
64
|
+
/^remove[_-]/,
|
|
65
|
+
/^archive[_-]/,
|
|
66
|
+
/^purge[_-]/,
|
|
67
|
+
/^drop[_-]/,
|
|
68
|
+
];
|
|
69
|
+
const SAFE_WRITE_PATTERNS = [/^create[_-]/, /^update[_-]/, /^add[_-]/, /^set[_-]/];
|
|
70
|
+
const LOCAL_PATTERNS = [/^local[_-]/, /^shell[_-]/, /^exec[_-]/];
|
|
71
|
+
|
|
72
|
+
function inferMode(name: string): ToolCallMode {
|
|
73
|
+
const lower = name.toLowerCase();
|
|
74
|
+
if (READ_PATTERNS.some((re) => re.test(lower))) return 'read';
|
|
75
|
+
if (LOCAL_PATTERNS.some((re) => re.test(lower))) return 'local';
|
|
76
|
+
if (DESTRUCTIVE_PATTERNS.some((re) => re.test(lower))) return 'destructive';
|
|
77
|
+
if (SAFE_WRITE_PATTERNS.some((re) => re.test(lower))) return 'safe_write';
|
|
78
|
+
return 'unknown';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function useToolCallStatus(
|
|
82
|
+
options: UseToolCallStatusOptions,
|
|
83
|
+
): UseToolCallStatusReturn {
|
|
84
|
+
return useMemo<UseToolCallStatusReturn>(() => {
|
|
85
|
+
const mode = options.mode ?? inferMode(options.call.name);
|
|
86
|
+
const isDestructive = mode === 'destructive';
|
|
87
|
+
// `local` tools touch the user's machine and need explicit consent.
|
|
88
|
+
const requiresApproval = mode === 'local';
|
|
89
|
+
let status: ToolCallStatus;
|
|
90
|
+
if (!options.result) {
|
|
91
|
+
status = options.runningHint ? 'running' : 'pending';
|
|
92
|
+
} else if (options.result.success === false) {
|
|
93
|
+
status = 'error';
|
|
94
|
+
} else {
|
|
95
|
+
status = 'done';
|
|
96
|
+
}
|
|
97
|
+
return { status, mode, requiresApproval, isDestructive };
|
|
98
|
+
}, [options.call.name, options.mode, options.result, options.runningHint]);
|
|
99
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @steerable/agent-ui
|
|
3
|
+
*
|
|
4
|
+
* Entry point. Re-exports the public hooks + components surface.
|
|
5
|
+
*
|
|
6
|
+
* The Tailwind preset is exposed via the `./tailwind-preset` subpath, not from
|
|
7
|
+
* the root, so consumers don't pull a build-time dependency into their app
|
|
8
|
+
* runtime bundle. See `tsconfig.json` `exports` map.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export * from './hooks/index.js';
|
|
12
|
+
export * from './components/index.js';
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Steerable Tailwind preset.
|
|
3
|
+
*
|
|
4
|
+
* Exposes design tokens (CSS variables) and a small theme extension covering
|
|
5
|
+
* the colors, spacing, and animations used by `@steerable/agent-ui` components.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
*
|
|
9
|
+
* // tailwind.config.ts
|
|
10
|
+
* import preset from '@steerable/agent-ui/tailwind-preset';
|
|
11
|
+
* export default { presets: [preset], content: ['./src/**\/*.{ts,tsx}'] };
|
|
12
|
+
*
|
|
13
|
+
* Override any token in your project's `globals.css`:
|
|
14
|
+
*
|
|
15
|
+
* :root {
|
|
16
|
+
* --agent-canvas: 0 0% 100%;
|
|
17
|
+
* --agent-foreground: 240 10% 3.9%;
|
|
18
|
+
* --agent-accent: 217 91% 60%;
|
|
19
|
+
* }
|
|
20
|
+
* .dark {
|
|
21
|
+
* --agent-canvas: 240 10% 3.9%;
|
|
22
|
+
* --agent-foreground: 0 0% 98%;
|
|
23
|
+
* }
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
const preset = {
|
|
27
|
+
// Tell Tailwind we use the `class` darkmode toggle so consumers can opt-in.
|
|
28
|
+
darkMode: 'class' as const,
|
|
29
|
+
theme: {
|
|
30
|
+
extend: {
|
|
31
|
+
colors: {
|
|
32
|
+
'agent-canvas': 'hsl(var(--agent-canvas) / <alpha-value>)',
|
|
33
|
+
'agent-foreground': 'hsl(var(--agent-foreground) / <alpha-value>)',
|
|
34
|
+
'agent-muted': 'hsl(var(--agent-muted) / <alpha-value>)',
|
|
35
|
+
'agent-muted-foreground':
|
|
36
|
+
'hsl(var(--agent-muted-foreground) / <alpha-value>)',
|
|
37
|
+
'agent-border': 'hsl(var(--agent-border) / <alpha-value>)',
|
|
38
|
+
'agent-accent': 'hsl(var(--agent-accent) / <alpha-value>)',
|
|
39
|
+
'agent-accent-foreground':
|
|
40
|
+
'hsl(var(--agent-accent-foreground) / <alpha-value>)',
|
|
41
|
+
'agent-destructive':
|
|
42
|
+
'hsl(var(--agent-destructive) / <alpha-value>)',
|
|
43
|
+
'agent-tool-read': 'hsl(var(--agent-tool-read) / <alpha-value>)',
|
|
44
|
+
'agent-tool-write': 'hsl(var(--agent-tool-write) / <alpha-value>)',
|
|
45
|
+
'agent-tool-destructive':
|
|
46
|
+
'hsl(var(--agent-tool-destructive) / <alpha-value>)',
|
|
47
|
+
},
|
|
48
|
+
borderRadius: {
|
|
49
|
+
'agent-sm': 'calc(var(--agent-radius) - 4px)',
|
|
50
|
+
'agent-md': 'calc(var(--agent-radius) - 2px)',
|
|
51
|
+
'agent-lg': 'var(--agent-radius)',
|
|
52
|
+
},
|
|
53
|
+
animation: {
|
|
54
|
+
'agent-cursor-blink': 'agent-cursor-blink 1s steps(1) infinite',
|
|
55
|
+
'agent-shimmer': 'agent-shimmer 2s linear infinite',
|
|
56
|
+
},
|
|
57
|
+
keyframes: {
|
|
58
|
+
'agent-cursor-blink': {
|
|
59
|
+
'0%, 100%': { opacity: '1' },
|
|
60
|
+
'50%': { opacity: '0' },
|
|
61
|
+
},
|
|
62
|
+
'agent-shimmer': {
|
|
63
|
+
'0%': { backgroundPosition: '-1000px 0' },
|
|
64
|
+
'100%': { backgroundPosition: '1000px 0' },
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
// Default token values; consumers can override in their own CSS.
|
|
70
|
+
// The defaults below satisfy WCAG 2 AA color-contrast (≥ 4.5:1 for normal
|
|
71
|
+
// text) against `--agent-canvas` (light) and `--agent-muted`. Override at
|
|
72
|
+
// your own risk — the framework's Storybook a11y suite will start failing
|
|
73
|
+
// if a regression is introduced.
|
|
74
|
+
plugins: [
|
|
75
|
+
function injectDefaultTokens(api: { addBase: (rules: Record<string, unknown>) => void }) {
|
|
76
|
+
api.addBase({
|
|
77
|
+
':root': {
|
|
78
|
+
'--agent-canvas': '0 0% 100%',
|
|
79
|
+
'--agent-foreground': '240 10% 3.9%',
|
|
80
|
+
'--agent-muted': '240 4.8% 95.9%',
|
|
81
|
+
'--agent-muted-foreground': '240 5% 32%',
|
|
82
|
+
'--agent-border': '240 5.9% 90%',
|
|
83
|
+
'--agent-accent': '217 91% 42%',
|
|
84
|
+
'--agent-accent-foreground': '0 0% 100%',
|
|
85
|
+
'--agent-destructive': '0 75% 42%',
|
|
86
|
+
'--agent-tool-read': '142 71% 22%',
|
|
87
|
+
'--agent-tool-write': '28 90% 28%',
|
|
88
|
+
'--agent-tool-destructive': '0 75% 42%',
|
|
89
|
+
'--agent-radius': '0.75rem',
|
|
90
|
+
},
|
|
91
|
+
'.dark': {
|
|
92
|
+
'--agent-canvas': '240 10% 3.9%',
|
|
93
|
+
'--agent-foreground': '0 0% 98%',
|
|
94
|
+
'--agent-muted': '240 3.7% 15.9%',
|
|
95
|
+
'--agent-muted-foreground': '240 5% 75%',
|
|
96
|
+
'--agent-border': '240 3.7% 15.9%',
|
|
97
|
+
'--agent-accent': '217 91% 65%',
|
|
98
|
+
'--agent-accent-foreground': '240 10% 3.9%',
|
|
99
|
+
'--agent-destructive': '0 75% 60%',
|
|
100
|
+
'--agent-tool-read': '142 71% 60%',
|
|
101
|
+
'--agent-tool-write': '38 92% 60%',
|
|
102
|
+
'--agent-tool-destructive': '0 75% 60%',
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export default preset;
|