@kkelly-offical/kkcode 0.1.7 → 0.2.3-preview.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 (166) hide show
  1. package/LICENSE +674 -674
  2. package/README.md +474 -387
  3. package/package.json +50 -46
  4. package/src/agent/agent.mjs +228 -220
  5. package/src/agent/custom-agent-loader.mjs +6 -3
  6. package/src/agent/generator.mjs +2 -2
  7. package/src/agent/prompt/assistant.txt +12 -0
  8. package/src/agent/prompt/bug-hunter.txt +89 -89
  9. package/src/agent/prompt/frontend-designer.txt +58 -58
  10. package/src/agent/prompt/guide.txt +1 -1
  11. package/src/agent/prompt/longagent-blueprint-agent.txt +83 -83
  12. package/src/agent/prompt/longagent-coding-agent.txt +37 -37
  13. package/src/agent/prompt/longagent-debugging-agent.txt +46 -46
  14. package/src/agent/prompt/longagent-preview-agent.txt +63 -63
  15. package/src/command/custom-commands.mjs +2 -2
  16. package/src/commands/agent.mjs +1 -1
  17. package/src/commands/background.mjs +145 -4
  18. package/src/commands/chat.mjs +117 -76
  19. package/src/commands/config.mjs +148 -1
  20. package/src/commands/doctor.mjs +30 -6
  21. package/src/commands/init.mjs +32 -6
  22. package/src/commands/longagent.mjs +117 -0
  23. package/src/commands/mcp.mjs +275 -43
  24. package/src/commands/permission.mjs +1 -1
  25. package/src/commands/session.mjs +195 -140
  26. package/src/commands/skill.mjs +63 -0
  27. package/src/commands/theme.mjs +1 -1
  28. package/src/commands/update.mjs +32 -0
  29. package/src/config/defaults.mjs +289 -260
  30. package/src/config/import-config.mjs +1 -1
  31. package/src/config/load-config.mjs +61 -4
  32. package/src/config/schema.mjs +604 -574
  33. package/src/context.mjs +4 -1
  34. package/src/core/constants.mjs +97 -91
  35. package/src/core/types.mjs +1 -1
  36. package/src/github/api.mjs +78 -78
  37. package/src/github/auth.mjs +294 -286
  38. package/src/github/flow.mjs +298 -298
  39. package/src/github/workspace.mjs +225 -212
  40. package/src/index.mjs +87 -82
  41. package/src/knowledge/frontend-aesthetics.txt +38 -38
  42. package/src/mcp/client-http.mjs +139 -141
  43. package/src/mcp/client-sse.mjs +297 -288
  44. package/src/mcp/client-stdio.mjs +534 -533
  45. package/src/mcp/constants.mjs +4 -2
  46. package/src/mcp/registry.mjs +498 -479
  47. package/src/mcp/stdio-framing.mjs +135 -133
  48. package/src/mcp/tool-result.mjs +24 -24
  49. package/src/observability/edit-diagnostics.mjs +449 -0
  50. package/src/observability/index.mjs +42 -42
  51. package/src/observability/metrics.mjs +165 -137
  52. package/src/observability/tracer.mjs +137 -137
  53. package/src/onboarding.mjs +209 -0
  54. package/src/orchestration/background-manager.mjs +567 -372
  55. package/src/orchestration/background-worker.mjs +419 -305
  56. package/src/orchestration/interruption-reason.mjs +21 -0
  57. package/src/orchestration/longagent-manager.mjs +197 -171
  58. package/src/orchestration/stage-scheduler.mjs +733 -728
  59. package/src/orchestration/subagent-router.mjs +7 -1
  60. package/src/orchestration/task-scheduler.mjs +219 -7
  61. package/src/permission/engine.mjs +1 -1
  62. package/src/permission/exec-policy.mjs +370 -370
  63. package/src/permission/file-edit-policy.mjs +108 -0
  64. package/src/permission/prompt.mjs +1 -1
  65. package/src/permission/rules.mjs +116 -7
  66. package/src/plugin/builtin-hooks/post-edit-format.mjs +2 -1
  67. package/src/plugin/builtin-hooks/post-edit-typecheck.mjs +104 -40
  68. package/src/plugin/hook-bus.mjs +19 -5
  69. package/src/plugin/manifest-loader.mjs +222 -0
  70. package/src/provider/anthropic.mjs +396 -390
  71. package/src/provider/ollama.mjs +7 -1
  72. package/src/provider/openai.mjs +382 -340
  73. package/src/provider/retry-policy.mjs +74 -68
  74. package/src/provider/router.mjs +242 -241
  75. package/src/provider/sse.mjs +104 -104
  76. package/src/provider/wizard.mjs +556 -0
  77. package/src/repl/capability-facade.mjs +30 -0
  78. package/src/repl/command-surface.mjs +23 -0
  79. package/src/repl/controller-entry.mjs +40 -0
  80. package/src/repl/core-shell.mjs +208 -0
  81. package/src/repl/dialog-router.mjs +87 -0
  82. package/src/repl/input-engine.mjs +76 -0
  83. package/src/repl/keymap.mjs +7 -0
  84. package/src/repl/operator-surface.mjs +15 -0
  85. package/src/repl/permission-flow.mjs +49 -0
  86. package/src/repl/runtime-facade.mjs +36 -0
  87. package/src/repl/slash-router.mjs +62 -0
  88. package/src/repl/state-store.mjs +29 -0
  89. package/src/repl/turn-controller.mjs +58 -0
  90. package/src/repl/verification.mjs +23 -0
  91. package/src/repl.mjs +3371 -2981
  92. package/src/rules/load-rules.mjs +3 -3
  93. package/src/runtime.mjs +1 -1
  94. package/src/session/agent-transaction.mjs +86 -0
  95. package/src/session/checkpoint.mjs +302 -302
  96. package/src/session/compaction.mjs +298 -298
  97. package/src/session/engine.mjs +417 -232
  98. package/src/session/longagent-4stage.mjs +467 -460
  99. package/src/session/longagent-hybrid.mjs +1344 -1097
  100. package/src/session/longagent-plan.mjs +376 -365
  101. package/src/session/longagent-project-memory.mjs +53 -53
  102. package/src/session/longagent-scaffold.mjs +291 -291
  103. package/src/session/longagent-task-bus.mjs +138 -54
  104. package/src/session/longagent-utils.mjs +828 -472
  105. package/src/session/longagent.mjs +911 -900
  106. package/src/session/loop.mjs +1005 -930
  107. package/src/session/prompt/agent.txt +25 -25
  108. package/src/session/prompt/anthropic.txt +150 -150
  109. package/src/session/prompt/beast.txt +1 -1
  110. package/src/session/prompt/plan.txt +31 -31
  111. package/src/session/prompt/qwen.txt +46 -46
  112. package/src/session/recovery.mjs +21 -0
  113. package/src/session/rollback.mjs +196 -195
  114. package/src/session/routing-observability.mjs +72 -0
  115. package/src/session/runtime-state.mjs +47 -0
  116. package/src/session/store.mjs +523 -519
  117. package/src/session/system-prompt.mjs +308 -273
  118. package/src/session/task-validator.mjs +267 -267
  119. package/src/session/usability-gates.mjs +2 -2
  120. package/src/skill/builtin/commit.mjs +64 -64
  121. package/src/skill/builtin/design.mjs +76 -76
  122. package/src/skill/generator.mjs +18 -2
  123. package/src/skill/registry.mjs +642 -390
  124. package/src/storage/audit-store.mjs +18 -11
  125. package/src/storage/event-log.mjs +7 -1
  126. package/src/storage/ghost-commit-store.mjs +243 -245
  127. package/src/storage/paths.mjs +17 -0
  128. package/src/theme/default-theme.mjs +1 -1
  129. package/src/theme/markdown.mjs +4 -0
  130. package/src/theme/schema.mjs +1 -1
  131. package/src/theme/status-bar.mjs +162 -158
  132. package/src/tool/audit-wrapper.mjs +18 -2
  133. package/src/tool/edit-transaction.mjs +23 -0
  134. package/src/tool/executor.mjs +26 -1
  135. package/src/tool/file-read-state.mjs +65 -0
  136. package/src/tool/git-auto.mjs +526 -526
  137. package/src/tool/git-full-auto.mjs +487 -478
  138. package/src/tool/mutation-guard.mjs +54 -0
  139. package/src/tool/prompt/edit.txt +3 -3
  140. package/src/tool/prompt/multiedit.txt +1 -0
  141. package/src/tool/prompt/notebookedit.txt +2 -1
  142. package/src/tool/prompt/patch.txt +25 -24
  143. package/src/tool/prompt/read.txt +3 -3
  144. package/src/tool/prompt/sysinfo.txt +29 -0
  145. package/src/tool/prompt/task.txt +66 -4
  146. package/src/tool/prompt/write.txt +2 -2
  147. package/src/tool/question-prompt.mjs +99 -93
  148. package/src/tool/registry.mjs +1701 -1343
  149. package/src/tool/task-tool.mjs +14 -6
  150. package/src/ui/activity-renderer.mjs +667 -664
  151. package/src/ui/repl-background-panel.mjs +7 -0
  152. package/src/ui/repl-capability-panel.mjs +9 -0
  153. package/src/ui/repl-dashboard.mjs +54 -4
  154. package/src/ui/repl-help.mjs +110 -0
  155. package/src/ui/repl-operator-panel.mjs +12 -0
  156. package/src/ui/repl-route-feedback.mjs +35 -0
  157. package/src/ui/repl-status-view.mjs +76 -0
  158. package/src/ui/repl-task-panel.mjs +5 -0
  159. package/src/ui/repl-transcript-panel.mjs +56 -0
  160. package/src/ui/repl-turn-summary.mjs +135 -0
  161. package/src/update/checker.mjs +184 -0
  162. package/src/usage/pricing.mjs +122 -121
  163. package/src/usage/usage-meter.mjs +1 -0
  164. package/src/util/git.mjs +562 -519
  165. package/src/util/template.mjs +6 -1
  166. package/src/version.mjs +3 -0
@@ -33,7 +33,13 @@ export function resolveSubagent({ config, subagentType = null, category = null }
33
33
  mode: "agent"
34
34
  }
35
35
  }
36
- throw new Error(`unknown subagent_type: ${subagentType}`)
36
+ // Unknown subagent type with configured subagents — return structured error fallback
37
+ return {
38
+ name: subagentType,
39
+ mode: "agent",
40
+ fallback: true,
41
+ reason: `unknown subagent_type: ${subagentType}`
42
+ }
37
43
  }
38
44
 
39
45
  if (category) {
@@ -1,8 +1,209 @@
1
1
  import { BackgroundManager } from "./background-manager.mjs"
2
2
  import { resolveSubagent } from "./subagent-router.mjs"
3
+ import { flushNow, forkSession, getSession } from "../session/store.mjs"
4
+ import { extractEditFeedbackFromToolEvents } from "../observability/edit-diagnostics.mjs"
5
+
6
+ const SUPPORTED_EXECUTION_MODES = new Set(["fresh_agent", "fork_context"])
7
+ const SUPPORTED_ISOLATION_MODES = new Set(["default", "worktree"])
8
+
9
+ function extractFileChanges(toolEvents = []) {
10
+ return toolEvents
11
+ .flatMap((event) => Array.isArray(event?.metadata?.fileChanges) ? event.metadata.fileChanges : [])
12
+ .map((item) => ({
13
+ path: String(item?.path || "").trim(),
14
+ addedLines: Math.max(0, Number(item?.addedLines || 0)),
15
+ removedLines: Math.max(0, Number(item?.removedLines || 0)),
16
+ stageId: item?.stageId ? String(item.stageId) : "",
17
+ taskId: item?.taskId ? String(item.taskId) : ""
18
+ }))
19
+ .filter((item) => item.path)
20
+ }
21
+
22
+ function normalizeExecutionMode(raw) {
23
+ const mode = String(raw || "fresh_agent").trim().toLowerCase() || "fresh_agent"
24
+ if (!SUPPORTED_EXECUTION_MODES.has(mode)) {
25
+ return { error: `unsupported task.execution_mode: ${raw}` }
26
+ }
27
+ return { mode }
28
+ }
29
+
30
+ function normalizeIsolation(raw) {
31
+ const mode = String(raw || "default").trim().toLowerCase() || "default"
32
+ if (!SUPPORTED_ISOLATION_MODES.has(mode)) {
33
+ return { error: `unsupported task.isolation: ${raw}` }
34
+ }
35
+ return { mode }
36
+ }
37
+
38
+ function normalizeList(input) {
39
+ if (Array.isArray(input)) {
40
+ return input
41
+ .map((item) => String(item || "").trim())
42
+ .filter(Boolean)
43
+ }
44
+ if (typeof input === "string") {
45
+ const value = input.trim()
46
+ return value ? [value] : []
47
+ }
48
+ return []
49
+ }
50
+
51
+ function normalizeWriteScope(input) {
52
+ return String(input || "").trim().toLowerCase()
53
+ }
54
+
55
+ function isReadOnlyWriteScope(input) {
56
+ const scope = normalizeWriteScope(input)
57
+ if (!scope) return false
58
+ return [
59
+ "read-only",
60
+ "readonly",
61
+ "no-mutation",
62
+ "no-mutations",
63
+ "no mutation",
64
+ "no mutations",
65
+ "no-write",
66
+ "no-writes",
67
+ "no write",
68
+ "no writes"
69
+ ].includes(scope) || scope.includes("read-only") || scope.includes("no mutation") || scope.includes("no write")
70
+ }
71
+
72
+ function validateDelegationArgs(args = {}, executionMode) {
73
+ const explicitPrompt = String(args.prompt || "").trim()
74
+ const objective = String(args.objective || "").trim()
75
+ const writeScope = String(args.write_scope || "").trim()
76
+ const deliverable = String(args.deliverable || "").trim()
77
+ const isContinuation = Boolean(args.session_id)
78
+ const hasStructuredContinuationFields =
79
+ objective
80
+ || String(args.why || "").trim()
81
+ || writeScope
82
+ || deliverable
83
+ || normalizeList(args.starting_points).length
84
+ || normalizeList(args.constraints).length
85
+ || normalizeList(args.planned_files).length
86
+
87
+ if (!explicitPrompt && !objective && !isContinuation) {
88
+ return "task.prompt or task.objective is required when session_id is not provided"
89
+ }
90
+ if (isContinuation && hasStructuredContinuationFields) {
91
+ return "task.session_id cannot be combined with structured brief fields; use a short continuation prompt instead"
92
+ }
93
+ if (isContinuation && !explicitPrompt) {
94
+ return "task.prompt is required when continuing an existing delegated session"
95
+ }
96
+ if (isContinuation && args.execution_mode) {
97
+ return "task.execution_mode only applies when starting a new delegated session"
98
+ }
99
+ if (!explicitPrompt && objective && !writeScope) {
100
+ return "task.write_scope is required when synthesizing a new delegation brief"
101
+ }
102
+ if (!explicitPrompt && objective && !deliverable) {
103
+ return "task.deliverable is required when synthesizing a new delegation brief"
104
+ }
105
+ if (executionMode === "fork_context" && !isReadOnlyWriteScope(writeScope) && !isContinuation) {
106
+ return "task.execution_mode=fork_context is reserved for read-only sidecar work; use fresh_agent for implementation"
107
+ }
108
+ if (args.run_in_background && args.allow_question === true) {
109
+ return "task.run_in_background does not support allow_question=true"
110
+ }
111
+ const isolation = String(args.isolation || "default").trim().toLowerCase() || "default"
112
+ if (isolation === "worktree" && executionMode !== "fresh_agent") {
113
+ return "task.isolation=worktree currently requires execution_mode='fresh_agent'"
114
+ }
115
+ if (isolation === "worktree" && args.run_in_background !== true) {
116
+ return "task.isolation=worktree currently requires run_in_background=true"
117
+ }
118
+ return null
119
+ }
120
+
121
+ function buildDelegationPrompt(args = {}) {
122
+ const explicitPrompt = String(args.prompt || "").trim()
123
+ if (explicitPrompt) return explicitPrompt
124
+
125
+ const objective = String(args.objective || "").trim()
126
+ if (!objective) return ""
127
+ const executionMode = String(args.execution_mode || "fresh_agent").trim().toLowerCase() || "fresh_agent"
128
+ const isolation = String(args.isolation || "default").trim().toLowerCase() || "default"
129
+
130
+ const why = String(args.why || "").trim()
131
+ const writeScope = String(args.write_scope || "").trim()
132
+ const startingPoints = normalizeList(args.starting_points)
133
+ const constraints = normalizeList(args.constraints)
134
+ const deliverable = String(args.deliverable || "").trim()
135
+ const plannedFiles = normalizeList(args.planned_files)
136
+
137
+ const lines = [`Objective: ${objective}`]
138
+ if (why) lines.push(`Why: ${why}`)
139
+ if (writeScope) lines.push(`Write scope: ${writeScope}`)
140
+ if (startingPoints.length) {
141
+ lines.push("Starting points:")
142
+ for (const item of startingPoints) lines.push(`- ${item}`)
143
+ }
144
+ if (constraints.length) {
145
+ lines.push("Constraints:")
146
+ for (const item of constraints) lines.push(`- ${item}`)
147
+ }
148
+ if (plannedFiles.length) {
149
+ lines.push("Planned files:")
150
+ for (const item of plannedFiles) lines.push(`- ${item}`)
151
+ }
152
+ if (deliverable) lines.push(`Deliverable: ${deliverable}`)
153
+
154
+ lines.push("Execution contract:")
155
+ lines.push("- Stay local instead of delegating if a direct read/edit/run action would finish the next step faster.")
156
+ if (executionMode === "fork_context") {
157
+ lines.push("- This is a forked-context sidecar: inherit parent context, keep the brief directive-style, and avoid restating the full parent thread.")
158
+ } else {
159
+ lines.push("- This is a fresh agent: assume zero inherited context and include all required context in the brief.")
160
+ }
161
+ if (isolation === "worktree") {
162
+ lines.push("- Run this delegated slice inside a local detached git worktree. Keep all execution local and self-contained.")
163
+ }
164
+ lines.push("- Never delegate understanding of the problem itself; delegate execution, verification, or bounded research against an already-understood objective.")
165
+ lines.push("- Do not guess unfinished results or treat background work as completed before it settles.")
166
+ lines.push("- Do not fabricate completion or present unfinished work as done.")
167
+ lines.push("- Do not peek at unfinished sibling work and turn guesses into facts.")
168
+ lines.push("- Background delegates must stay non-interactive; if clarification is needed, keep the work in the foreground.")
169
+
170
+ return lines.join("\n")
171
+ }
172
+
173
+ async function ensureDelegatedSession({ executionMode, parentSessionId, subSessionId }) {
174
+ if (executionMode !== "fork_context") return
175
+
176
+ if (!parentSessionId) {
177
+ throw new Error("fork_context requires a parent session")
178
+ }
179
+
180
+ const existing = await getSession(subSessionId)
181
+ if (existing) return
182
+
183
+ const forked = await forkSession({
184
+ sessionId: parentSessionId,
185
+ newSessionId: subSessionId,
186
+ title: `fork:${subSessionId}`
187
+ })
188
+
189
+ if (!forked) {
190
+ throw new Error(`fork_context parent session not found: ${parentSessionId}`)
191
+ }
192
+
193
+ await flushNow()
194
+ }
3
195
 
4
196
  export function createTaskDelegate({ config, parentSessionId, model, providerType, runSubtask }) {
5
197
  return async function delegateTask(args = {}) {
198
+ const executionModeResult = normalizeExecutionMode(args.execution_mode)
199
+ if (executionModeResult.error) return { error: executionModeResult.error }
200
+ const executionMode = executionModeResult.mode
201
+ const isolationResult = normalizeIsolation(args.isolation)
202
+ if (isolationResult.error) return { error: isolationResult.error }
203
+ const validationError = validateDelegationArgs(args, executionMode)
204
+ if (validationError) return { error: validationError }
205
+ const isolation = isolationResult.mode
206
+
6
207
  const subagent = resolveSubagent({
7
208
  config,
8
209
  subagentType: args.subagent_type || null,
@@ -10,16 +211,17 @@ export function createTaskDelegate({ config, parentSessionId, model, providerTyp
10
211
  })
11
212
 
12
213
  const subSessionId = String(args.session_id || `sub_${parentSessionId}_${Date.now()}`)
13
- const prompt = String(args.prompt || "").trim() || (args.session_id ? "Continue from existing sub-session context." : "")
14
-
15
- if (!prompt) {
16
- return { error: "task.prompt is required when session_id is not provided" }
17
- }
214
+ const prompt = buildDelegationPrompt(args)
18
215
 
19
216
  const subModel = subagent.model || model
20
217
  const subProvider = subagent.providerType || providerType
21
218
 
22
219
  const run = async ({ isCancelled, log }) => {
220
+ await ensureDelegatedSession({
221
+ executionMode,
222
+ parentSessionId,
223
+ subSessionId
224
+ })
23
225
  await log(`task started (${subagent.name})`)
24
226
  const out = await runSubtask({
25
227
  prompt,
@@ -31,11 +233,17 @@ export function createTaskDelegate({ config, parentSessionId, model, providerTyp
31
233
  })
32
234
  await log(out.reply)
33
235
  if (isCancelled()) return { cancelled: true }
236
+ const fileChanges = extractFileChanges(out.toolEvents || [])
237
+ const editFeedback = extractEditFeedbackFromToolEvents(out.toolEvents || [])
34
238
  return {
35
239
  session_id: subSessionId,
240
+ parent_session_id: parentSessionId,
36
241
  subagent: subagent.name,
242
+ execution_mode: executionMode,
37
243
  reply: out.reply,
38
- tool_events: out.toolEvents?.length || 0
244
+ tool_events: out.toolEvents?.length || 0,
245
+ file_changes: fileChanges,
246
+ edit_feedback: editFeedback
39
247
  }
40
248
  }
41
249
 
@@ -49,6 +257,8 @@ export function createTaskDelegate({ config, parentSessionId, model, providerTyp
49
257
  cwd: process.cwd(),
50
258
  model: subModel,
51
259
  providerType: subProvider,
260
+ executionMode,
261
+ isolation,
52
262
  subagent: subagent.name,
53
263
  category: args.category || null,
54
264
  subagentType: subagent.name,
@@ -62,7 +272,9 @@ export function createTaskDelegate({ config, parentSessionId, model, providerTyp
62
272
  return {
63
273
  background_task_id: task.id,
64
274
  status: task.status,
65
- session_id: subSessionId
275
+ session_id: subSessionId,
276
+ execution_mode: executionMode,
277
+ isolation
66
278
  }
67
279
  }
68
280
 
@@ -5,7 +5,7 @@ import { evaluatePermission } from "./rules.mjs"
5
5
  import { askPermissionInteractive } from "./prompt.mjs"
6
6
 
7
7
  const sessionAllow = new Map()
8
- let workspaceTrusted = true
8
+ let workspaceTrusted = false
9
9
 
10
10
  function cacheKey(tool, pattern) {
11
11
  return `${tool}::${pattern || "*"}`