klyro 0.1.62 → 1.0.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/dist/agent/anthropic-adapter.d.ts +30 -9
- package/dist/agent/anthropic-adapter.js +107 -49
- package/dist/agent/capabilities.d.ts +122 -0
- package/dist/agent/capabilities.js +150 -0
- package/dist/agent/orchestrator.d.ts +131 -0
- package/dist/agent/orchestrator.js +269 -0
- package/dist/agent/provider-adapter.d.ts +9 -0
- package/dist/agent/provider-adapter.js +85 -39
- package/dist/agent/registry.d.ts +1 -0
- package/dist/agent/registry.js +1 -0
- package/dist/agent/retry.d.ts +12 -1
- package/dist/agent/retry.js +19 -1
- package/dist/agent/runtime.d.ts +23 -1
- package/dist/agent/runtime.js +236 -26
- package/dist/agent/scoped-registry.d.ts +22 -0
- package/dist/agent/scoped-registry.js +42 -0
- package/dist/agent/task-manager.d.ts +115 -0
- package/dist/agent/task-manager.js +250 -0
- package/dist/agent/worker-spawner.d.ts +17 -12
- package/dist/agent/worker-spawner.js +26 -20
- package/dist/cli/config.d.ts +21 -0
- package/dist/cli/config.js +31 -0
- package/dist/cli/dotenv.d.ts +3 -0
- package/dist/cli/dotenv.js +57 -0
- package/dist/cli/repl.js +72 -4
- package/dist/cli/run.d.ts +3 -0
- package/dist/cli/run.js +127 -7
- package/dist/context/klyro-md.d.ts +6 -0
- package/dist/context/klyro-md.js +21 -15
- package/dist/context/trust.d.ts +42 -0
- package/dist/context/trust.js +111 -0
- package/dist/events/catalog.d.ts +71 -0
- package/dist/index.js +4 -0
- package/dist/mcp/client.d.ts +53 -0
- package/dist/mcp/client.js +225 -0
- package/dist/mcp/config.d.ts +30 -0
- package/dist/mcp/config.js +82 -0
- package/dist/mcp/policy.d.ts +13 -0
- package/dist/mcp/policy.js +12 -0
- package/dist/mcp/registry.d.ts +50 -0
- package/dist/mcp/registry.js +172 -0
- package/dist/mcp/schema.d.ts +11 -0
- package/dist/mcp/schema.js +46 -0
- package/dist/persistence/store.d.ts +1 -1
- package/dist/policy/approval.d.ts +27 -6
- package/dist/policy/approval.js +36 -8
- package/dist/policy/engine.d.ts +13 -0
- package/dist/policy/engine.js +31 -2
- package/dist/policy/patterns.d.ts +16 -0
- package/dist/policy/patterns.js +26 -0
- package/dist/tools/agent/spawn-agent.d.ts +9 -0
- package/dist/tools/agent/spawn-agent.js +50 -0
- package/dist/tools/agent/task-get.d.ts +8 -0
- package/dist/tools/agent/task-get.js +40 -0
- package/dist/tools/agent/task-list.d.ts +4 -0
- package/dist/tools/agent/task-list.js +41 -0
- package/dist/tools/plan/todo-write.d.ts +1 -1
- package/dist/tools/registry.js +6 -0
- package/dist/tools/types.d.ts +12 -0
- package/dist/tui/approval.d.ts +1 -1
- package/dist/tui/approval.js +2 -2
- package/package.json +2 -2
|
@@ -5,12 +5,22 @@
|
|
|
5
5
|
* Non-TTY: returns deny. Callers should treat non-TTY as the default
|
|
6
6
|
* (the user opted out of prompts with `--yes` or pipe input).
|
|
7
7
|
*/
|
|
8
|
-
export type ApprovalChoice =
|
|
8
|
+
export type ApprovalChoice =
|
|
9
|
+
/** Yes, just this once. */
|
|
10
|
+
'allow' | 'deny'
|
|
11
|
+
/** Yes, and auto-allow this pattern for the rest of the session. */
|
|
12
|
+
| 'always'
|
|
13
|
+
/** Yes, session-allow AND persist the pattern to settings (survives restarts). */
|
|
14
|
+
| 'always-persist';
|
|
9
15
|
export interface ApprovalRequest {
|
|
10
16
|
toolName: string;
|
|
11
17
|
reason: string;
|
|
12
18
|
/** Best-effort summary of the call (command or path). */
|
|
13
19
|
summary: string;
|
|
20
|
+
/** Raw tool input — used to derive the approval pattern when `pattern` is absent. */
|
|
21
|
+
input?: Record<string, unknown>;
|
|
22
|
+
/** Pre-derived approval pattern (`tool(glob)` grammar). */
|
|
23
|
+
pattern?: string;
|
|
14
24
|
}
|
|
15
25
|
export interface ApprovalPrompt {
|
|
16
26
|
ask(req: ApprovalRequest): Promise<ApprovalChoice>;
|
|
@@ -24,12 +34,23 @@ export declare class DenyAllApprovalPrompt implements ApprovalPrompt {
|
|
|
24
34
|
ask(_req: ApprovalRequest): Promise<ApprovalChoice>;
|
|
25
35
|
}
|
|
26
36
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
37
|
+
* Pattern approval cache — ask-once-per-pattern (Claude-Code-like).
|
|
38
|
+
*
|
|
39
|
+
* Wraps any inner prompt (TUI modal, stdin). The first `ask` for a pattern
|
|
40
|
+
* delegates to the user; `always` records the pattern for the session,
|
|
41
|
+
* `always-persist` additionally calls `onPersist` (the CLI wires this to
|
|
42
|
+
* append the pattern to ~/.klyro/settings.json and the live engine).
|
|
43
|
+
* Re-prompts never happen for a recorded pattern within the session.
|
|
44
|
+
*
|
|
45
|
+
* The pattern comes from `req.pattern` (pre-derived by the runtime via
|
|
46
|
+
* `patternForCall`) or is derived from `req.input` here as a fallback.
|
|
29
47
|
*/
|
|
30
|
-
export declare class
|
|
48
|
+
export declare class PatternApprovalCache implements ApprovalPrompt {
|
|
31
49
|
private readonly inner;
|
|
32
|
-
private readonly
|
|
33
|
-
|
|
50
|
+
private readonly opts;
|
|
51
|
+
private readonly session;
|
|
52
|
+
constructor(inner?: ApprovalPrompt, opts?: {
|
|
53
|
+
onPersist?: (pattern: string) => void | Promise<void>;
|
|
54
|
+
});
|
|
34
55
|
ask(req: ApprovalRequest): Promise<ApprovalChoice>;
|
|
35
56
|
}
|
package/dist/policy/approval.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import * as readline from 'node:readline/promises';
|
|
9
9
|
import { stdin as input, stdout as output } from 'node:process';
|
|
10
|
+
import { patternForCall } from './patterns.js';
|
|
10
11
|
/** Default prompt backed by readline on stdin/stdout. */
|
|
11
12
|
export class StdinApprovalPrompt {
|
|
12
13
|
async ask(req) {
|
|
@@ -34,21 +35,48 @@ export class DenyAllApprovalPrompt {
|
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
37
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
38
|
+
* Pattern approval cache — ask-once-per-pattern (Claude-Code-like).
|
|
39
|
+
*
|
|
40
|
+
* Wraps any inner prompt (TUI modal, stdin). The first `ask` for a pattern
|
|
41
|
+
* delegates to the user; `always` records the pattern for the session,
|
|
42
|
+
* `always-persist` additionally calls `onPersist` (the CLI wires this to
|
|
43
|
+
* append the pattern to ~/.klyro/settings.json and the live engine).
|
|
44
|
+
* Re-prompts never happen for a recorded pattern within the session.
|
|
45
|
+
*
|
|
46
|
+
* The pattern comes from `req.pattern` (pre-derived by the runtime via
|
|
47
|
+
* `patternForCall`) or is derived from `req.input` here as a fallback.
|
|
39
48
|
*/
|
|
40
|
-
export class
|
|
49
|
+
export class PatternApprovalCache {
|
|
41
50
|
inner;
|
|
42
|
-
|
|
43
|
-
|
|
51
|
+
opts;
|
|
52
|
+
session = new Set();
|
|
53
|
+
constructor(inner = new DenyAllApprovalPrompt(), opts = {}) {
|
|
44
54
|
this.inner = inner;
|
|
55
|
+
this.opts = opts;
|
|
45
56
|
}
|
|
46
57
|
async ask(req) {
|
|
47
|
-
|
|
58
|
+
const key = req.pattern ?? patternFromRequest(req);
|
|
59
|
+
if (key && this.session.has(key))
|
|
48
60
|
return 'allow';
|
|
49
61
|
const choice = await this.inner.ask(req);
|
|
50
|
-
if (choice === 'always')
|
|
51
|
-
this.
|
|
62
|
+
if ((choice === 'always' || choice === 'always-persist') && key) {
|
|
63
|
+
this.session.add(key);
|
|
64
|
+
}
|
|
65
|
+
if (choice === 'always-persist' && key) {
|
|
66
|
+
// Best-effort: the current call is already approved via the session
|
|
67
|
+
// set above — a persist failure must not retro-deny it.
|
|
68
|
+
try {
|
|
69
|
+
await this.opts.onPersist?.(key);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
/* ignore */
|
|
73
|
+
}
|
|
74
|
+
}
|
|
52
75
|
return choice;
|
|
53
76
|
}
|
|
54
77
|
}
|
|
78
|
+
function patternFromRequest(req) {
|
|
79
|
+
if (!req.input)
|
|
80
|
+
return undefined;
|
|
81
|
+
return patternForCall(req.toolName, req.input);
|
|
82
|
+
}
|
package/dist/policy/engine.d.ts
CHANGED
|
@@ -67,6 +67,19 @@ export declare class PolicyEngine {
|
|
|
67
67
|
nonInteractive: boolean;
|
|
68
68
|
}): Promise<Decision>;
|
|
69
69
|
private evaluateGlobRules;
|
|
70
|
+
/**
|
|
71
|
+
* Session rule management — appends a glob rule (`tool(glob)` grammar)
|
|
72
|
+
* if not already present. Used for persisted "always allow" patterns
|
|
73
|
+
* (loaded at startup) and live additions (approval `always→settings`).
|
|
74
|
+
*/
|
|
75
|
+
addAllow(rule: string): void;
|
|
76
|
+
addDeny(rule: string): void;
|
|
77
|
+
addAsk(rule: string): void;
|
|
78
|
+
applyRules(rules: {
|
|
79
|
+
allow?: string[];
|
|
80
|
+
deny?: string[];
|
|
81
|
+
ask?: string[];
|
|
82
|
+
}): void;
|
|
70
83
|
}
|
|
71
84
|
/** Builtin set of rules. Order matters: first match wins. */
|
|
72
85
|
export declare function builtinRules(): PolicyRule[];
|
package/dist/policy/engine.js
CHANGED
|
@@ -116,6 +116,34 @@ export class PolicyEngine {
|
|
|
116
116
|
// Precedence: deny → allow → ask
|
|
117
117
|
return check(this.config.deny, 'deny') ?? check(this.config.allow, 'allow') ?? check(this.config.ask, 'ask') ?? null;
|
|
118
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Session rule management — appends a glob rule (`tool(glob)` grammar)
|
|
121
|
+
* if not already present. Used for persisted "always allow" patterns
|
|
122
|
+
* (loaded at startup) and live additions (approval `always→settings`).
|
|
123
|
+
*/
|
|
124
|
+
addAllow(rule) {
|
|
125
|
+
const list = (this.config.allow ??= []);
|
|
126
|
+
if (!list.includes(rule))
|
|
127
|
+
list.push(rule);
|
|
128
|
+
}
|
|
129
|
+
addDeny(rule) {
|
|
130
|
+
const list = (this.config.deny ??= []);
|
|
131
|
+
if (!list.includes(rule))
|
|
132
|
+
list.push(rule);
|
|
133
|
+
}
|
|
134
|
+
addAsk(rule) {
|
|
135
|
+
const list = (this.config.ask ??= []);
|
|
136
|
+
if (!list.includes(rule))
|
|
137
|
+
list.push(rule);
|
|
138
|
+
}
|
|
139
|
+
applyRules(rules) {
|
|
140
|
+
for (const r of rules.allow ?? [])
|
|
141
|
+
this.addAllow(r);
|
|
142
|
+
for (const r of rules.deny ?? [])
|
|
143
|
+
this.addDeny(r);
|
|
144
|
+
for (const r of rules.ask ?? [])
|
|
145
|
+
this.addAsk(r);
|
|
146
|
+
}
|
|
119
147
|
}
|
|
120
148
|
/** Builtin set of rules. Order matters: first match wins. */
|
|
121
149
|
export function builtinRules() {
|
|
@@ -130,8 +158,9 @@ function asString(v) {
|
|
|
130
158
|
return typeof v === 'string' ? v : '';
|
|
131
159
|
}
|
|
132
160
|
function matchesGlobRule(call, rule) {
|
|
133
|
-
// Rule grammar: tool or tool(glob). e.g. "write_file", "write_file(.env)", "shell_exec(npm *)"
|
|
134
|
-
|
|
161
|
+
// Rule grammar: tool or tool(glob). e.g. "write_file", "write_file(.env)", "shell_exec(npm *)".
|
|
162
|
+
// Digits are allowed so generated names (e.g. mcp__server__tool2) can be targeted.
|
|
163
|
+
const m = /^([a-z0-9_]+)(?:\((.*)\))?$/.exec(rule.trim());
|
|
135
164
|
if (!m)
|
|
136
165
|
return false;
|
|
137
166
|
const tool = m[1];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Approval patterns — the unit of "always allow this pattern".
|
|
3
|
+
*
|
|
4
|
+
* A pattern uses the same `tool(glob)` grammar the policy engine matches
|
|
5
|
+
* (`matchesGlobRule` in engine.ts), e.g. `shell_exec(npm *)`,
|
|
6
|
+
* `write_file(src/index.ts)`. Derivation is deterministic: the same call
|
|
7
|
+
* shape always yields the same pattern, so session caching can key on exact
|
|
8
|
+
* pattern equality and persisted patterns re-match across sessions.
|
|
9
|
+
*
|
|
10
|
+
* Scoping (Claude-Code-like):
|
|
11
|
+
* - shell_exec / run_verify → first command word + ` *`
|
|
12
|
+
* (`npm test` and `npm run build` share `shell_exec(npm *)`)
|
|
13
|
+
* - file tools with a path → the exact path (safest: no wildcards)
|
|
14
|
+
* - everything else → the bare tool name (whole-tool session allow)
|
|
15
|
+
*/
|
|
16
|
+
export declare function patternForCall(name: string, input: Record<string, unknown>): string;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Approval patterns — the unit of "always allow this pattern".
|
|
3
|
+
*
|
|
4
|
+
* A pattern uses the same `tool(glob)` grammar the policy engine matches
|
|
5
|
+
* (`matchesGlobRule` in engine.ts), e.g. `shell_exec(npm *)`,
|
|
6
|
+
* `write_file(src/index.ts)`. Derivation is deterministic: the same call
|
|
7
|
+
* shape always yields the same pattern, so session caching can key on exact
|
|
8
|
+
* pattern equality and persisted patterns re-match across sessions.
|
|
9
|
+
*
|
|
10
|
+
* Scoping (Claude-Code-like):
|
|
11
|
+
* - shell_exec / run_verify → first command word + ` *`
|
|
12
|
+
* (`npm test` and `npm run build` share `shell_exec(npm *)`)
|
|
13
|
+
* - file tools with a path → the exact path (safest: no wildcards)
|
|
14
|
+
* - everything else → the bare tool name (whole-tool session allow)
|
|
15
|
+
*/
|
|
16
|
+
export function patternForCall(name, input) {
|
|
17
|
+
if ((name === 'shell_exec' || name === 'run_verify') && typeof input.command === 'string') {
|
|
18
|
+
const first = input.command.trim().split(/\s+/, 1)[0] ?? '';
|
|
19
|
+
if (first)
|
|
20
|
+
return `${name}(${first} *)`;
|
|
21
|
+
}
|
|
22
|
+
if (typeof input.path === 'string' && input.path.length > 0) {
|
|
23
|
+
return `${name}(${input.path})`;
|
|
24
|
+
}
|
|
25
|
+
return name;
|
|
26
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ChildSummary } from '../../agent/orchestrator.js';
|
|
2
|
+
export declare const spawnAgentTool: import("../types.js").Tool<{
|
|
3
|
+
agent: string;
|
|
4
|
+
task: string;
|
|
5
|
+
cwd?: string | undefined;
|
|
6
|
+
model?: string | undefined;
|
|
7
|
+
timeoutMs?: number | undefined;
|
|
8
|
+
}, ChildSummary>;
|
|
9
|
+
export type SpawnAgentOutput = ChildSummary;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* P1.1 — spawn_agent tool (6-10fix.md §5).
|
|
3
|
+
*
|
|
4
|
+
* Delegates to `AgentSpawnBridge.spawnAgent`, which runs the child to
|
|
5
|
+
* completion (P0/P1 scope: blocking) and returns a compact ChildSummary —
|
|
6
|
+
* never the child transcript. True background `task_wait` / `task_stop`
|
|
7
|
+
* arrives in P2.
|
|
8
|
+
*/
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
import { defineTool } from '../types.js';
|
|
11
|
+
import { toToolError } from '../normalize.js';
|
|
12
|
+
const InputSchema = z.object({
|
|
13
|
+
agent: z.string().min(1).describe('Agent id (e.g. explorer, implementer, tester, reviewer)'),
|
|
14
|
+
task: z.string().min(1).describe('Task brief for the child agent'),
|
|
15
|
+
cwd: z.string().optional().describe('Working directory override (defaults to parent cwd)'),
|
|
16
|
+
model: z.string().optional().describe('Model override for the child (must be honoured or error)'),
|
|
17
|
+
timeoutMs: z.number().int().positive().optional().describe('Child runtime timeout in ms'),
|
|
18
|
+
});
|
|
19
|
+
export const spawnAgentTool = defineTool({
|
|
20
|
+
name: 'spawn_agent',
|
|
21
|
+
description: 'Spawn a child agent and return its compact summary { taskId, status, ... }. Child transcript stays separate; use task_get for status.',
|
|
22
|
+
inputSchema: InputSchema,
|
|
23
|
+
permission: 'admin',
|
|
24
|
+
isConcurrencySafe: false,
|
|
25
|
+
renderCall: (input) => `spawn_agent(${input.agent}): ${input.task.slice(0, 80)}`,
|
|
26
|
+
execute: async (input, ctx) => {
|
|
27
|
+
try {
|
|
28
|
+
const bridge = ctx.agentBridge;
|
|
29
|
+
if (!bridge) {
|
|
30
|
+
return {
|
|
31
|
+
ok: false,
|
|
32
|
+
error: {
|
|
33
|
+
code: 'NO_ORCHESTRATOR',
|
|
34
|
+
message: 'spawn_agent requires an active orchestrator (run with --agent or via a parent agent)',
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return await bridge.spawnAgent({
|
|
39
|
+
agent: input.agent,
|
|
40
|
+
task: input.task,
|
|
41
|
+
...(input.cwd ? { cwd: input.cwd } : {}),
|
|
42
|
+
...(input.model ? { model: input.model } : {}),
|
|
43
|
+
...(input.timeoutMs ? { timeoutMs: input.timeoutMs } : {}),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
return { ok: false, error: toToolError(err) };
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* P1.3 — task_get tool (6-10fix.md §5). Read-only + concurrency-safe.
|
|
3
|
+
* Returns one task's status and summary by id.
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { defineTool } from '../types.js';
|
|
7
|
+
import { toToolError } from '../normalize.js';
|
|
8
|
+
const InputSchema = z.object({
|
|
9
|
+
taskId: z.string().min(1).describe('Task id (e.g. task_1)'),
|
|
10
|
+
});
|
|
11
|
+
export const taskGetTool = defineTool({
|
|
12
|
+
name: 'task_get',
|
|
13
|
+
description: "Return one child-agent task's status and summary by id.",
|
|
14
|
+
inputSchema: InputSchema,
|
|
15
|
+
permission: 'read',
|
|
16
|
+
isConcurrencySafe: true,
|
|
17
|
+
renderCall: (input) => `task_get(${input.taskId})`,
|
|
18
|
+
execute: async (input, ctx) => {
|
|
19
|
+
try {
|
|
20
|
+
const bridge = ctx.agentBridge;
|
|
21
|
+
if (!bridge) {
|
|
22
|
+
return {
|
|
23
|
+
ok: false,
|
|
24
|
+
error: { code: 'NO_ORCHESTRATOR', message: 'task_get requires an active orchestrator' },
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const rec = bridge.getTask(input.taskId);
|
|
28
|
+
if (!rec) {
|
|
29
|
+
return {
|
|
30
|
+
ok: false,
|
|
31
|
+
error: { code: 'NOT_FOUND', message: `Unknown task: ${input.taskId}` },
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return { ok: true, value: rec };
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
return { ok: false, error: toToolError(err) };
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* P1.2 — task_list tool (6-10fix.md §5). Read-only + concurrency-safe.
|
|
3
|
+
* Returns TaskSummary[] snapshots from the orchestrator's TaskManager.
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { defineTool } from '../types.js';
|
|
7
|
+
import { toToolError } from '../normalize.js';
|
|
8
|
+
const InputSchema = z.object({
|
|
9
|
+
parentTaskId: z.string().optional().describe('Filter by parent task id'),
|
|
10
|
+
status: z
|
|
11
|
+
.enum(['queued', 'running', 'succeeded', 'failed', 'cancelled', 'timed_out', 'blocked'])
|
|
12
|
+
.optional()
|
|
13
|
+
.describe('Filter by status'),
|
|
14
|
+
});
|
|
15
|
+
export const taskListTool = defineTool({
|
|
16
|
+
name: 'task_list',
|
|
17
|
+
description: 'List child-agent tasks (running and completed) with status summaries.',
|
|
18
|
+
inputSchema: InputSchema,
|
|
19
|
+
permission: 'read',
|
|
20
|
+
isConcurrencySafe: true,
|
|
21
|
+
renderCall: () => 'task_list',
|
|
22
|
+
execute: async (input, ctx) => {
|
|
23
|
+
try {
|
|
24
|
+
const bridge = ctx.agentBridge;
|
|
25
|
+
if (!bridge) {
|
|
26
|
+
return {
|
|
27
|
+
ok: false,
|
|
28
|
+
error: { code: 'NO_ORCHESTRATOR', message: 'task_list requires an active orchestrator' },
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const tasks = bridge.listTasks({
|
|
32
|
+
...(input.parentTaskId ? { parentTaskId: input.parentTaskId } : {}),
|
|
33
|
+
...(input.status ? { status: input.status } : {}),
|
|
34
|
+
});
|
|
35
|
+
return { ok: true, value: tasks };
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
return { ok: false, error: toToolError(err) };
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
});
|
|
@@ -2,7 +2,7 @@ export declare const todoWriteTool: import("../types.js").Tool<{
|
|
|
2
2
|
todos: {
|
|
3
3
|
id: string;
|
|
4
4
|
title: string;
|
|
5
|
-
status: "done" | "pending" | "
|
|
5
|
+
status: "done" | "pending" | "failed" | "in_progress" | "skipped";
|
|
6
6
|
files?: string[] | undefined;
|
|
7
7
|
}[];
|
|
8
8
|
}, {
|
package/dist/tools/registry.js
CHANGED
|
@@ -25,6 +25,9 @@ import { findSymbolTool } from './symbols/find-symbol.js';
|
|
|
25
25
|
import { lspDiagnosticsTool, lspGotoDefinitionTool } from './lsp/diagnostics.js';
|
|
26
26
|
import { expandResultTool } from './expand-result.js';
|
|
27
27
|
import { memoryWriteTool } from './memory-write.js';
|
|
28
|
+
import { spawnAgentTool } from './agent/spawn-agent.js';
|
|
29
|
+
import { taskListTool } from './agent/task-list.js';
|
|
30
|
+
import { taskGetTool } from './agent/task-get.js';
|
|
28
31
|
import { zodToJsonSchema } from './schema.js';
|
|
29
32
|
export class ToolRegistry {
|
|
30
33
|
tools = new Map();
|
|
@@ -107,5 +110,8 @@ export const builtinRegistry = () => {
|
|
|
107
110
|
r.register(lspGotoDefinitionTool);
|
|
108
111
|
r.register(expandResultTool);
|
|
109
112
|
r.register(memoryWriteTool);
|
|
113
|
+
r.register(spawnAgentTool);
|
|
114
|
+
r.register(taskListTool);
|
|
115
|
+
r.register(taskGetTool);
|
|
110
116
|
return r;
|
|
111
117
|
};
|
package/dist/tools/types.d.ts
CHANGED
|
@@ -48,6 +48,18 @@ export interface ToolContext {
|
|
|
48
48
|
};
|
|
49
49
|
/** Emit KlyroEvent */
|
|
50
50
|
emit?: (ev: import('../events/catalog.js').KlyroEvent) => void;
|
|
51
|
+
/** Delegation bridge (set when an orchestrator is active). Powers spawn_agent/task_* tools. */
|
|
52
|
+
agentBridge?: import('../agent/orchestrator.js').AgentSpawnBridge;
|
|
53
|
+
/** The task id this agent belongs to, when running under an orchestrator. */
|
|
54
|
+
parentTaskId?: string;
|
|
55
|
+
/** Depth of the current agent in the spawn tree (0 = root). Used to build ParentContextRef for spawn_agent. */
|
|
56
|
+
agentDepth?: number;
|
|
57
|
+
/** Max allowed spawn depth for this branch. */
|
|
58
|
+
agentMaxDepth?: number;
|
|
59
|
+
/** Effective tool allow-set of the current agent (null = all). Child can never exceed this. */
|
|
60
|
+
agentAllowedTools?: ReadonlySet<string> | null;
|
|
61
|
+
/** Model override active for the current agent. */
|
|
62
|
+
agentModel?: string;
|
|
51
63
|
}
|
|
52
64
|
/**
|
|
53
65
|
* A typed tool definition. TInput is the validated input shape; TOutput is
|
package/dist/tui/approval.d.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* a Promise.
|
|
14
14
|
* 3. The App polls getPending() on every render to show a modal.
|
|
15
15
|
* 4. The App's useInput handler sees a pending prompt and routes
|
|
16
|
-
* y/n
|
|
16
|
+
* y/a/A/n to the resolver (A = always-persist to settings).
|
|
17
17
|
*
|
|
18
18
|
* Non-TTY mode (no Ink mounted) keeps using StdinApprovalPrompt; this
|
|
19
19
|
* module is only used when the TUI is active.
|
package/dist/tui/approval.js
CHANGED
|
@@ -14,7 +14,7 @@ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
|
14
14
|
* a Promise.
|
|
15
15
|
* 3. The App polls getPending() on every render to show a modal.
|
|
16
16
|
* 4. The App's useInput handler sees a pending prompt and routes
|
|
17
|
-
* y/n
|
|
17
|
+
* y/a/A/n to the resolver (A = always-persist to settings).
|
|
18
18
|
*
|
|
19
19
|
* Non-TTY mode (no Ink mounted) keeps using StdinApprovalPrompt; this
|
|
20
20
|
* module is only used when the TUI is active.
|
|
@@ -85,7 +85,7 @@ export function ApprovalModal({ bridge }) {
|
|
|
85
85
|
return;
|
|
86
86
|
}
|
|
87
87
|
if (c === 'A') {
|
|
88
|
-
bridge.resolve('always');
|
|
88
|
+
bridge.resolve('always-persist');
|
|
89
89
|
return;
|
|
90
90
|
}
|
|
91
91
|
if (c === 'd' || c === 'n') {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "klyro",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Klyro
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Klyro — autonomous coding harness CLI that streams from any OpenAI-compatible or Anthropic LLM endpoint.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|