klyro 0.1.63 → 1.0.1

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.
Files changed (117) hide show
  1. package/dist/agent/anthropic-adapter.d.ts +36 -0
  2. package/dist/agent/anthropic-adapter.js +73 -16
  3. package/dist/agent/capabilities.d.ts +145 -0
  4. package/dist/agent/capabilities.js +191 -0
  5. package/dist/agent/child-worker.d.ts +104 -0
  6. package/dist/agent/child-worker.js +250 -0
  7. package/dist/agent/orchestrator.d.ts +232 -0
  8. package/dist/agent/orchestrator.js +589 -0
  9. package/dist/agent/provider-adapter.d.ts +17 -0
  10. package/dist/agent/provider-adapter.js +35 -3
  11. package/dist/agent/registry.d.ts +1 -0
  12. package/dist/agent/registry.js +1 -0
  13. package/dist/agent/retry.d.ts +13 -2
  14. package/dist/agent/retry.js +21 -3
  15. package/dist/agent/runtime.d.ts +70 -8
  16. package/dist/agent/runtime.js +205 -34
  17. package/dist/agent/scoped-registry.d.ts +22 -0
  18. package/dist/agent/scoped-registry.js +42 -0
  19. package/dist/agent/task-manager.d.ts +115 -0
  20. package/dist/agent/task-manager.js +250 -0
  21. package/dist/agent/worker-spawner.d.ts +17 -12
  22. package/dist/agent/worker-spawner.js +26 -20
  23. package/dist/agent/worktree-manager.d.ts +74 -0
  24. package/dist/agent/worktree-manager.js +189 -0
  25. package/dist/checkpoints/store.js +30 -5
  26. package/dist/cli/auth.js +16 -1
  27. package/dist/cli/config.d.ts +9 -3
  28. package/dist/cli/config.js +64 -3
  29. package/dist/cli/dotenv.d.ts +3 -0
  30. package/dist/cli/dotenv.js +57 -0
  31. package/dist/cli/eval.d.ts +6 -1
  32. package/dist/cli/eval.js +9 -0
  33. package/dist/cli/repl.js +183 -17
  34. package/dist/cli/run.d.ts +10 -11
  35. package/dist/cli/run.js +176 -14
  36. package/dist/cli/update.d.ts +5 -0
  37. package/dist/cli/update.js +62 -10
  38. package/dist/context/import-graph.d.ts +2 -0
  39. package/dist/context/import-graph.js +31 -3
  40. package/dist/context/klyro-md.d.ts +6 -0
  41. package/dist/context/klyro-md.js +25 -16
  42. package/dist/context/memory.d.ts +8 -0
  43. package/dist/context/memory.js +50 -2
  44. package/dist/context/project-map.d.ts +6 -0
  45. package/dist/context/project-map.js +50 -2
  46. package/dist/context/repo-map.d.ts +2 -0
  47. package/dist/context/repo-map.js +31 -1
  48. package/dist/context/trust.d.ts +42 -0
  49. package/dist/context/trust.js +111 -0
  50. package/dist/events/catalog.d.ts +99 -0
  51. package/dist/index.js +93 -4
  52. package/dist/mcp/client.d.ts +55 -0
  53. package/dist/mcp/client.js +294 -0
  54. package/dist/mcp/config.d.ts +40 -0
  55. package/dist/mcp/config.js +99 -0
  56. package/dist/mcp/policy.d.ts +13 -0
  57. package/dist/mcp/policy.js +12 -0
  58. package/dist/mcp/registry.d.ts +72 -0
  59. package/dist/mcp/registry.js +244 -0
  60. package/dist/mcp/schema.d.ts +18 -0
  61. package/dist/mcp/schema.js +57 -0
  62. package/dist/mcp/trust.d.ts +20 -0
  63. package/dist/mcp/trust.js +74 -0
  64. package/dist/persistence/audit.d.ts +28 -0
  65. package/dist/persistence/audit.js +101 -1
  66. package/dist/persistence/store.d.ts +26 -2
  67. package/dist/persistence/store.js +140 -13
  68. package/dist/policy/approval.d.ts +14 -0
  69. package/dist/policy/approval.js +44 -2
  70. package/dist/policy/engine.d.ts +1 -0
  71. package/dist/policy/engine.js +91 -10
  72. package/dist/policy/secret-redactor.js +4 -0
  73. package/dist/providers/model-info.d.ts +17 -0
  74. package/dist/providers/model-info.js +35 -2
  75. package/dist/repl.d.ts +6 -0
  76. package/dist/repl.js +12 -7
  77. package/dist/tools/agent/spawn-agent.d.ts +9 -0
  78. package/dist/tools/agent/spawn-agent.js +50 -0
  79. package/dist/tools/agent/task-apply.d.ts +4 -0
  80. package/dist/tools/agent/task-apply.js +44 -0
  81. package/dist/tools/agent/task-get.d.ts +8 -0
  82. package/dist/tools/agent/task-get.js +40 -0
  83. package/dist/tools/agent/task-list.d.ts +4 -0
  84. package/dist/tools/agent/task-list.js +41 -0
  85. package/dist/tools/agent/task-stop.d.ts +6 -0
  86. package/dist/tools/agent/task-stop.js +39 -0
  87. package/dist/tools/agent/task-wait.d.ts +17 -0
  88. package/dist/tools/agent/task-wait.js +79 -0
  89. package/dist/tools/fs/apply-patch.js +71 -0
  90. package/dist/tools/fs/edit-file.js +65 -0
  91. package/dist/tools/fs/multi-edit.d.ts +4 -0
  92. package/dist/tools/fs/multi-edit.js +66 -0
  93. package/dist/tools/fs/write-file.js +67 -0
  94. package/dist/tools/plan/todo-write.d.ts +1 -1
  95. package/dist/tools/registry.js +12 -0
  96. package/dist/tools/shell/background.js +6 -3
  97. package/dist/tools/shell/sandbox.d.ts +51 -0
  98. package/dist/tools/shell/sandbox.js +143 -0
  99. package/dist/tools/shell/shell-exec.d.ts +1 -0
  100. package/dist/tools/shell/shell-exec.js +83 -11
  101. package/dist/tools/shell/worker-entry.d.ts +12 -0
  102. package/dist/tools/shell/worker-entry.js +43 -0
  103. package/dist/tools/types.d.ts +18 -0
  104. package/dist/tools/verify/run-verify.js +3 -1
  105. package/dist/trace/writer.d.ts +13 -0
  106. package/dist/trace/writer.js +55 -4
  107. package/dist/tui/app.js +1 -1
  108. package/dist/tui/approval.js +20 -21
  109. package/dist/util.d.ts +1 -0
  110. package/dist/util.js +1 -0
  111. package/dist/verification/baseline.js +17 -3
  112. package/dist/verification/classify.js +23 -12
  113. package/dist/verification/engine.js +3 -1
  114. package/dist/verification/registry.d.ts +2 -0
  115. package/dist/verification/registry.js +33 -0
  116. package/dist/verification/scoped.js +28 -6
  117. package/package.json +1 -1
@@ -12,6 +12,25 @@
12
12
  */
13
13
  import { redact } from '../policy/secret-redactor.js';
14
14
  const DEFAULT_TIMEOUT_MS = 120_000;
15
+ /**
16
+ * Parse a `Retry-After` response header value into milliseconds.
17
+ * Returns undefined when absent or unparseable. Handles both forms:
18
+ * delay-seconds ("120") and HTTP-date ("Wed, 21 Oct 2015 07:28:00 GMT",
19
+ * clamped at 0 when the date is in the past).
20
+ */
21
+ export function parseRetryAfterMs(value) {
22
+ if (value == null)
23
+ return undefined;
24
+ const v = value.trim();
25
+ if (!v)
26
+ return undefined;
27
+ if (/^\d+$/.test(v))
28
+ return Number(v) * 1000;
29
+ const t = Date.parse(v);
30
+ if (!Number.isNaN(t))
31
+ return Math.max(0, t - Date.now());
32
+ return undefined;
33
+ }
15
34
  /** Convert a Zod schema to a permissive JSON Schema object for tool defs. */
16
35
  export function zodToJsonSchema(schema) {
17
36
  // We keep this simple: zod's own _def is enough to give the model a
@@ -89,8 +108,12 @@ function zodFieldSchema(s) {
89
108
  */
90
109
  export function buildChatCompletionsBody(req) {
91
110
  const messages = [];
111
+ // OpenAI has no system array form: the volatile suffix rides along as a
112
+ // plain concatenation (behavior-preserving when absent).
92
113
  if (req.system)
93
- messages.push({ role: 'system', content: req.system });
114
+ messages.push({ role: 'system', content: req.systemSuffix ? `${req.system}\n\n${req.systemSuffix}` : req.system });
115
+ else if (req.systemSuffix)
116
+ messages.push({ role: 'system', content: req.systemSuffix });
94
117
  for (const m of req.messages) {
95
118
  if (m.role === 'assistant') {
96
119
  const text = m.content.filter((b) => b.kind === 'text').map((b) => b.text).join('');
@@ -183,11 +206,20 @@ async function* streamChatCompletions(url, opts, req, fetchImpl) {
183
206
  req.signal?.removeEventListener('abort', onAbort);
184
207
  const rawErr = await res.text().catch(() => '');
185
208
  const errText = redact(rawErr).slice(0, 500);
209
+ // Context overflow is a client-side budget problem, not a transient
210
+ // failure: surface a dedicated code (never retryable here — the runtime
211
+ // owns the compress-and-retry recovery) so callers can distinguish it
212
+ // from 429/5xx backoff cases.
213
+ const isOverflow = res.status === 413 || /request_too_large|too_large|prompt_too_long|context_length|maximum context length/i.test(rawErr);
214
+ const retryable = !isOverflow && (res.status >= 500 || res.status === 429);
215
+ const retryAfterMs = retryable ? parseRetryAfterMs(res.headers?.get('retry-after')) : undefined;
186
216
  yield {
187
217
  kind: 'error',
188
- code: `HTTP_${res.status}`,
218
+ code: isOverflow ? 'REQUEST_TOO_LARGE' : `HTTP_${res.status}`,
189
219
  message: `provider returned ${res.status}: ${errText}`,
190
- retryable: res.status >= 500 || res.status === 429,
220
+ retryable,
221
+ status: String(res.status),
222
+ ...(retryAfterMs !== undefined ? { retryAfterMs } : {}),
191
223
  };
192
224
  return;
193
225
  }
@@ -39,4 +39,5 @@ export declare function buildProviderFromCli(args: {
39
39
  baseUrl?: string;
40
40
  apiKey?: string;
41
41
  timeoutMs?: number;
42
+ retry?: Partial<RetryOptions> | false;
42
43
  }): ProviderAdapter;
@@ -190,5 +190,6 @@ export function buildProviderFromCli(args) {
190
190
  baseURL: args.baseUrl,
191
191
  apiKey: args.apiKey,
192
192
  timeoutMs: args.timeoutMs,
193
+ ...(args.retry !== undefined ? { retry: args.retry } : {}),
193
194
  });
194
195
  }
@@ -15,7 +15,7 @@
15
15
  *
16
16
  * delay = min(maxMs, baseMs * 2^attempt) ± jitter
17
17
  *
18
- * The default policy matches the L6 plan: 3 attempts, 500ms base, 8s cap.
18
+ * The default policy matches the L6 plan: 5 attempts, 500ms base, 8s cap.
19
19
  */
20
20
  import type { ProviderAdapter } from './provider-adapter.js';
21
21
  export interface RetryOptions {
@@ -28,8 +28,19 @@ export interface RetryOptions {
28
28
  sleep?: (ms: number) => Promise<void>;
29
29
  /** Test hook: called once per attempt with 0-indexed attempt number. */
30
30
  onAttempt?: (attempt: number) => void;
31
+ /**
32
+ * Retry telemetry hook (operator-visible; the model stays blind).
33
+ * Called each time a retryable error is buffered and another attempt
34
+ * will follow (NOT on the terminal failure). `attempt` is the
35
+ * 1-indexed retry number (1 = first retry after the initial failure).
36
+ */
37
+ onRetry?: (info: {
38
+ attempt: number;
39
+ status: string;
40
+ retryAfterMs?: number;
41
+ }) => void;
31
42
  }
32
- export declare const DEFAULT_RETRY: Required<Omit<RetryOptions, 'signal' | 'onAttempt'>>;
43
+ export declare const DEFAULT_RETRY: Required<Omit<RetryOptions, 'signal' | 'onAttempt' | 'onRetry'>>;
33
44
  /**
34
45
  * Sleep that resolves early when `signal` aborts (never rejects — callers
35
46
  * check `signal.aborted` themselves after waking).
@@ -15,10 +15,10 @@
15
15
  *
16
16
  * delay = min(maxMs, baseMs * 2^attempt) ± jitter
17
17
  *
18
- * The default policy matches the L6 plan: 3 attempts, 500ms base, 8s cap.
18
+ * The default policy matches the L6 plan: 5 attempts, 500ms base, 8s cap.
19
19
  */
20
20
  export const DEFAULT_RETRY = {
21
- maxAttempts: 3,
21
+ maxAttempts: 5,
22
22
  baseMs: 500,
23
23
  maxMs: 8_000,
24
24
  sleep: defaultSleep,
@@ -152,7 +152,25 @@ export function retryingAdapter(inner, opts = {}) {
152
152
  yield lastError;
153
153
  return;
154
154
  }
155
- const delay = computeBackoff(attempt, cfg.baseMs, cfg.maxMs);
155
+ // Telemetry for the operator: a retryable error was buffered and
156
+ // another attempt will follow. The consumer stream never sees the
157
+ // buffered error, so the model stays blind.
158
+ const errRec = lastError;
159
+ const statusRaw = errRec?.['status'];
160
+ const codeRaw = errRec?.['code'];
161
+ const status = typeof statusRaw === 'string' && statusRaw.length > 0
162
+ ? statusRaw
163
+ : typeof codeRaw === 'string' && codeRaw.length > 0
164
+ ? codeRaw
165
+ : 'retryable';
166
+ const retryAfterRaw = errRec?.['retryAfterMs'];
167
+ const retryAfterMs = typeof retryAfterRaw === 'number' && Number.isFinite(retryAfterRaw) && retryAfterRaw >= 0
168
+ ? retryAfterRaw
169
+ : undefined;
170
+ opts.onRetry?.({ attempt: attempt + 1, status, ...(retryAfterMs !== undefined ? { retryAfterMs } : {}) });
171
+ // Honor a server-provided Retry-After delay when present; otherwise
172
+ // fall back to exponential backoff with jitter.
173
+ const delay = retryAfterMs ?? computeBackoff(attempt, cfg.baseMs, cfg.maxMs);
156
174
  // Abort-aware backoff: Ctrl+C during the sleep must stop promptly
157
175
  // instead of stalling up to maxMs before noticing.
158
176
  if (delay > 0)
@@ -19,6 +19,16 @@ import type { Message } from './message.js';
19
19
  import type { ToolRegistry } from '../tools/registry.js';
20
20
  import type { PolicyEngine } from '../policy/engine.js';
21
21
  import type { ApprovalPrompt } from '../policy/approval.js';
22
+ /**
23
+ * Verification mode. The canonical definition lives in verification/engine.ts
24
+ * (sibling-owned: `export type VerifyMode = 'strict'|'advisory'|'off'`). It is
25
+ * resolved conditionally here so this file typechecks regardless of sibling
26
+ * landing order — and converges to the sibling type automatically once the
27
+ * sibling export exists.
28
+ */
29
+ export type VerifyMode = typeof import('../verification/engine.js') extends {
30
+ VerifyMode: infer V;
31
+ } ? V : 'strict' | 'advisory' | 'off';
22
32
  export interface RuntimeDeps {
23
33
  adapter: ProviderAdapter;
24
34
  registry: ToolRegistry;
@@ -26,15 +36,34 @@ export interface RuntimeDeps {
26
36
  approval: ApprovalPrompt;
27
37
  /**
28
38
  * Build a system prompt given cwd + the current Level-7 runtime telemetry.
29
- * The telemetry block is a compact, in-memory summary of the run so far
30
- * (step count, last tool calls, recent errors). Injected as part of the
31
- * system prompt so the model can see its own state mid-run.
39
+ *
40
+ * TELEMETRY SPLIT: the fn may return either a plain string (legacy —
41
+ * telemetry already concatenated, cache-unfriendly) or
42
+ * `{system, suffix}` where `suffix` is the volatile telemetry block.
43
+ * The runtime forwards both halves via CallRequest (`system` +
44
+ * `systemSuffix`) so adapters can keep the suffix out of the cacheable
45
+ * prefix (Anthropic array form) or concatenate it (OpenAI — unchanged).
46
+ * Both shapes are accepted so custom fns keep compiling.
32
47
  */
33
- systemPrompt: (ctx: {
34
- cwd: string;
35
- telemetry?: string;
36
- }) => string;
48
+ systemPrompt: SystemPromptFn;
49
+ }
50
+ /** Split system-prompt result: stable prefix + volatile telemetry suffix. */
51
+ export interface SystemPromptResult {
52
+ system: string;
53
+ suffix?: string;
37
54
  }
55
+ export type SystemPromptFn = (ctx: {
56
+ cwd: string;
57
+ telemetry?: string;
58
+ }) => string | SystemPromptResult;
59
+ /** Normalize either systemPrompt shape into {system, suffix}. */
60
+ export declare function resolveSystemPrompt(fn: SystemPromptFn, ctx: {
61
+ cwd: string;
62
+ telemetry?: string;
63
+ }): {
64
+ system: string;
65
+ suffix?: string;
66
+ };
38
67
  export type Phase = 'understanding' | 'exploring' | 'planning' | 'implementing' | 'verifying' | 'done' | 'blocked' | 'limit';
39
68
  export interface RunOptions {
40
69
  task: string;
@@ -77,6 +106,9 @@ export interface RunOptions {
77
106
  maxRepairAttempts?: number;
78
107
  timeoutMs?: number;
79
108
  requireVerify?: boolean;
109
+ /** Verification mode (default 'strict'). 'off' skips the pipeline;
110
+ * 'advisory' runs verify once on completion without repair turns. */
111
+ mode?: VerifyMode;
80
112
  };
81
113
  /**
82
114
  * Level 9 — persistence. When a SessionStore is provided, every message
@@ -87,6 +119,28 @@ export interface RunOptions {
87
119
  store?: import('../persistence/store.js').SessionStore;
88
120
  sessionId?: string;
89
121
  };
122
+ /**
123
+ * Orchestration context (P0). Present for any agent that is itself managed
124
+ * by an AgentOrchestrator — so a child knows who its parent is, how deep the
125
+ * call stack is, which tools it may use, and which task/session it belongs to.
126
+ */
127
+ parentContext?: {
128
+ taskId?: string;
129
+ parentTaskId?: string;
130
+ sessionId: string;
131
+ depth: number;
132
+ maxDepth: number;
133
+ allowedTools?: ReadonlySet<string>;
134
+ /** Path allow-set inherited from the parent (sibling C contract). */
135
+ allowedPaths?: readonly string[];
136
+ model?: string;
137
+ };
138
+ /**
139
+ * Delegation bridge (P0). Present on the root run so the model can call
140
+ * spawn_agent / task_list / task_get. The tool layer reads it from the
141
+ * ToolContext.
142
+ */
143
+ agentBridge?: import('./orchestrator.js').AgentSpawnBridge;
90
144
  }
91
145
  /** A single plan step emitted by the agent. */
92
146
  export interface PlanStep {
@@ -140,6 +194,8 @@ export type RuntimeEvent = {
140
194
  input: number;
141
195
  output: number;
142
196
  estimated?: boolean;
197
+ cacheRead?: number;
198
+ cacheWrite?: number;
143
199
  } | {
144
200
  kind: 'final_text';
145
201
  text: string;
@@ -183,6 +239,8 @@ export interface RunResult {
183
239
  input: number;
184
240
  output: number;
185
241
  estimated?: boolean;
242
+ cacheRead?: number;
243
+ cacheWrite?: number;
186
244
  };
187
245
  /** Number of policy-driven user prompts the user accepted. */
188
246
  repairs?: number;
@@ -192,6 +250,10 @@ export interface RunResult {
192
250
  command?: string;
193
251
  attempts: number;
194
252
  failureType?: string;
253
+ repairTokens?: {
254
+ input: number;
255
+ output: number;
256
+ };
195
257
  };
196
258
  /** 5.1 phase */
197
259
  phase?: Phase;
@@ -208,4 +270,4 @@ export declare function run(opts: RunOptions, deps: RuntimeDeps): Promise<RunRes
208
270
  export declare function defaultSystemPrompt(ctx: {
209
271
  cwd: string;
210
272
  telemetry?: string;
211
- }): string;
273
+ }): SystemPromptResult;