kward 0.66.0 → 0.67.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.
@@ -9,6 +9,7 @@ module Kward
9
9
  { name: "resume", description: "Resume a saved session.", argument_hint: "[path]" },
10
10
  { name: "name", description: "Name or clear the current session.", argument_hint: "[name]" },
11
11
  { name: "clone", description: "Clone the current session.", argument_hint: "" },
12
+ { name: "tree", description: "Navigate the current session tree.", argument_hint: "" },
12
13
  { name: "copy", description: "Copy clean session text to the clipboard.", argument_hint: "[last|transcript]" },
13
14
  { name: "export", description: "Export the current session as Markdown.", argument_hint: "[path]" },
14
15
  { name: "compact", description: "Compact the current conversation context.", argument_hint: "[instructions]" },
@@ -18,10 +19,10 @@ module Kward
18
19
  { name: "model", description: "Select the default model.", argument_hint: "" },
19
20
  { name: "openrouter/catalog", description: "List the full OpenRouter model catalog.", argument_hint: "" },
20
21
  { name: "reasoning", description: "Select reasoning effort.", argument_hint: "" },
22
+ { name: "reload", description: "Reload installed plugins.", argument_hint: "" },
21
23
  { name: "status", description: "Show the current status message.", argument_hint: "" },
22
24
  { name: "stats", description: "Show telemetry logging stats.", argument_hint: "[range]" },
23
- { name: "crew", description: "Reserved for future crew commands.", argument_hint: "" },
24
- { name: "memory", description: "Inspect and manage Kward memory.", argument_hint: "[enable|disable|auto-summary|core|add|list|forget|promote|inspect|why|summarize]" }
25
+ { name: "memory", description: "Inspect and manage Kward memory.", argument_hint: "[enable|disable|auto-summary|core|add|list|forget|promote|relax|inspect|why|summarize]" }
25
26
  ].freeze
26
27
  BUILTIN_RESERVED_COMMAND_NAMES = BUILTIN_COMMANDS.map { |command| command[:name] }.freeze
27
28
  SLASH_COMMAND_PATTERN = %r{\A/(\S+)(?:\s+(.*))?\z}m
@@ -0,0 +1,79 @@
1
+ module Kward
2
+ module RPC
3
+ module RuntimePayloads
4
+ module_function
5
+
6
+ def state(session:, model:, streaming:, steering_supported:, auto_compaction_reserve_tokens:, active_persona_label:, message_count:, pending_count:, compaction_enabled:, workspace_guardrails_enabled:)
7
+ {
8
+ model: model,
9
+ thinkingLevel: model[:reasoningEffort],
10
+ isStreaming: streaming,
11
+ isCompacting: false,
12
+ steeringMode: steering_supported ? "in-flight" : "one-at-a-time",
13
+ followUpMode: "one-at-a-time",
14
+ sessionFile: session[:path],
15
+ rpcSessionId: session[:id],
16
+ persistentSessionId: session[:persistentId],
17
+ sessionName: session[:name],
18
+ autoCompactionEnabled: compaction_enabled,
19
+ autoCompactionReserveTokens: auto_compaction_reserve_tokens,
20
+ workspaceGuardrailsEnabled: workspace_guardrails_enabled,
21
+ autoRetryEnabled: false,
22
+ defaultProvider: model[:provider],
23
+ defaultModel: default_model_label(model),
24
+ defaultThinkingLevel: model[:reasoningEffort],
25
+ activePersonaLabel: active_persona_label,
26
+ hideThinkingBlock: false,
27
+ quietStartup: false,
28
+ transport: "kward-rpc",
29
+ imageAutoResize: false,
30
+ blockImages: false,
31
+ enabledModels: [],
32
+ enableSkillCommands: true,
33
+ messageCount: message_count,
34
+ pendingMessageCount: pending_count
35
+ }.compact
36
+ end
37
+
38
+ def stats(session:, counts:, model:, auto_compaction_reserve_tokens:, context_usage:, compaction_enabled:)
39
+ {
40
+ sessionFile: session[:path],
41
+ rpcSessionId: session[:id],
42
+ persistentSessionId: session[:persistentId],
43
+ sessionName: session[:name],
44
+ userMessages: counts[:userMessages],
45
+ assistantMessages: counts[:assistantMessages],
46
+ toolCalls: counts[:toolCalls],
47
+ toolResults: counts[:toolResults],
48
+ totalMessages: counts[:totalMessages],
49
+ usingSubscription: model[:provider] == "Codex",
50
+ autoCompactionEnabled: compaction_enabled,
51
+ autoCompactionReserveTokens: auto_compaction_reserve_tokens,
52
+ contextUsage: context_usage
53
+ }.compact
54
+ end
55
+
56
+ def session(rpc_session, modified_at:, active_persona_label: nil)
57
+ {
58
+ id: rpc_session.id,
59
+ persistentId: rpc_session.session.id,
60
+ path: rpc_session.session.path,
61
+ workspaceRoot: rpc_session.workspace_root,
62
+ cwd: rpc_session.session.cwd.to_s.empty? ? rpc_session.workspace_root : rpc_session.session.cwd,
63
+ name: rpc_session.session.name,
64
+ createdAt: rpc_session.session.created_at&.utc&.iso8601(3),
65
+ modifiedAt: modified_at&.utc&.iso8601(3),
66
+ parentId: rpc_session.session.parent_id,
67
+ parentPath: rpc_session.session.parent_path,
68
+ activePersonaLabel: active_persona_label
69
+ }.compact
70
+ end
71
+
72
+ def default_model_label(model)
73
+ return nil if model[:provider].to_s.empty? || model[:id].to_s.empty?
74
+
75
+ "#{model[:provider]}/#{model[:id]}"
76
+ end
77
+ end
78
+ end
79
+ end
@@ -132,7 +132,7 @@ module Kward
132
132
  when "workspace/info"
133
133
  workspace_info(params["workspaceRoot"] || Dir.pwd)
134
134
  when "tools/list"
135
- { tools: ToolRegistry.new.schemas }
135
+ { tools: ToolRegistry.new(workspace: configured_workspace).schemas }
136
136
  when "prompts/list"
137
137
  prompts_list
138
138
  when "prompts/expand"
@@ -180,7 +180,7 @@ module Kward
180
180
  when "memory/autoSummary/disable"
181
181
  @session_manager.memory_auto_summary_disable
182
182
  when "memory/list"
183
- @session_manager.memory_list(include_inactive: params["includeInactive"] || false)
183
+ @session_manager.memory_list(include_inactive: params["includeInactive"] || false, workspace_root: params["workspaceRoot"] || Dir.pwd)
184
184
  when "memory/add"
185
185
  @session_manager.memory_add(text: params.fetch("text"), scope: params["scope"], tags: params["tags"] || [])
186
186
  when "memory/addCore"
@@ -189,6 +189,8 @@ module Kward
189
189
  @session_manager.memory_forget(id: params.fetch("id"))
190
190
  when "memory/promote"
191
191
  @session_manager.memory_promote(id: params.fetch("id"))
192
+ when "memory/relax"
193
+ @session_manager.memory_relax(id: params.fetch("id"), workspace_root: params["workspaceRoot"] || Dir.pwd)
192
194
  when "memory/inspect"
193
195
  @session_manager.memory_inspect
194
196
  when "memory/why"
@@ -212,11 +214,11 @@ module Kward
212
214
  when "auth/loginStatus"
213
215
  @auth_manager.login_status(login_id: params.fetch("loginId"))
214
216
  when "sessions/create"
215
- @session_manager.create_session(workspace_root: params["workspaceRoot"] || Dir.pwd, name: params["name"])
217
+ @session_manager.create_session(workspace_root: params["workspaceRoot"] || Dir.pwd, name: params["name"], resume_last: params["resumeLast"] != false)
216
218
  when "sessions/resume"
217
219
  @session_manager.resume_session(path: params.fetch("path"), workspace_root: params["workspaceRoot"])
218
220
  when "sessions/list"
219
- { sessions: @session_manager.list_sessions(workspace_root: params["workspaceRoot"] || Dir.pwd, limit: params["limit"] || 20) }
221
+ { sessions: @session_manager.list_sessions(workspace_root: params["workspaceRoot"] || Dir.pwd, limit: params["limit"]) }
220
222
  when "sessions/rename"
221
223
  @session_manager.rename_session(session_id: params.fetch("sessionId"), name: params["name"])
222
224
  when "sessions/clone"
@@ -227,6 +229,12 @@ module Kward
227
229
  @session_manager.fork_messages(session_id: params.fetch("sessionId"))
228
230
  when "sessions/fork"
229
231
  @session_manager.fork_session(session_id: params.fetch("sessionId"), entry_id: params.fetch("entryId"))
232
+ when "sessions/tree"
233
+ @session_manager.session_tree(session_id: params.fetch("sessionId"))
234
+ when "sessions/tree/setLabel"
235
+ @session_manager.set_tree_label(session_id: params.fetch("sessionId"), entry_id: params.fetch("entryId"), label: params["label"])
236
+ when "sessions/tree/navigate"
237
+ @session_manager.navigate_tree(session_id: params.fetch("sessionId"), entry_id: params.fetch("entryId"), summarize: params["summarize"], custom_instructions: params["customInstructions"])
230
238
  when "sessions/export"
231
239
  @session_manager.export_session(session_id: params.fetch("sessionId"), path: params["path"], format: params["format"])
232
240
  when "sessions/delete"
@@ -267,12 +275,6 @@ module Kward
267
275
  def capabilities
268
276
  {
269
277
  framing: "content-length",
270
- asyncTurns: true,
271
- turnCancellation: "best-effort",
272
- turnEventReplay: true,
273
- uiQuestions: true,
274
- authLogin: true,
275
- configUpdate: true,
276
278
  transcript: {
277
279
  format: "tauren-transcript-v1",
278
280
  messagesNormalized: true,
@@ -285,12 +287,13 @@ module Kward
285
287
  sessions: {
286
288
  mode: "explicit",
287
289
  persistence: "jsonl",
288
- methods: ["sessions/create", "sessions/resume", "sessions/list", "sessions/rename", "sessions/clone", "sessions/compact", "sessions/forkMessages", "sessions/fork", "sessions/export", "sessions/delete", "sessions/close", "sessions/transcript"],
290
+ methods: ["sessions/create", "sessions/resume", "sessions/list", "sessions/rename", "sessions/clone", "sessions/compact", "sessions/forkMessages", "sessions/fork", "sessions/tree", "sessions/tree/setLabel", "sessions/tree/navigate", "sessions/export", "sessions/delete", "sessions/close", "sessions/transcript"],
291
+ startupResume: { supported: true, method: "sessions/create", parameter: "resumeLast", default: session_auto_resume_enabled?, immediateTranscript: true, sessionActivePersonaLabel: true },
289
292
  list: { supported: true, source: "rpc", ancestry: true, treeFields: true },
290
- fork: { supported: true, methods: ["sessions/forkMessages", "sessions/fork"], entryIdFormat: "message-index", selectedMessage: "excludedFromForkComposerTextReturned" },
293
+ fork: { supported: true, methods: ["sessions/forkMessages", "sessions/fork"], entryIdFormat: "entry-id", selectedMessage: "excludedFromForkComposerTextReturned" },
291
294
  compact: { supported: true, method: "sessions/compact", notification: "session/event", events: ["compactionStart", "compactionEnd"] },
292
295
  import: { supported: false },
293
- tree: { supported: false, labels: false, navigate: false, summarize: false },
296
+ tree: { supported: true, method: "sessions/tree", labels: true, labelTimestamps: true, navigate: true, summarize: true, shape: "tauren-tree-items-v1" },
294
297
  updates: { supported: false, notification: "session/updated" }
295
298
  },
296
299
  turns: {
@@ -315,7 +318,7 @@ module Kward
315
318
  reasoning: { start: false, delta: true, end: false },
316
319
  modelRetry: { supported: true, event: "modelRetry" },
317
320
  steering: { supported: @session_manager.in_flight_steer_supported?, event: "turnSteered", mode: @session_manager.in_flight_steer_supported? ? "native" : "unsupported" },
318
- tools: { call: true, update: false, result: true, normalizedMetadata: true, diffs: true, changedFiles: false },
321
+ tools: { call: true, update: false, result: true, normalizedMetadata: true, diffs: true, changedFiles: false, workspaceGuardrails: workspace_guardrails_enabled? },
319
322
  errors: true,
320
323
  sessionUpdates: false
321
324
  },
@@ -354,7 +357,7 @@ module Kward
354
357
  apiKeyProviders: ["openrouter"],
355
358
  logout: true
356
359
  },
357
- memory: { supported: true, optIn: true, defaultEnabled: false, autoSummaryDefaultEnabled: false, promptInjection: "interactive", storage: { core: "json", soft: "jsonl", events: "jsonl" }, methods: ["memory/status", "memory/enable", "memory/disable", "memory/autoSummary/enable", "memory/autoSummary/disable", "memory/list", "memory/add", "memory/addCore", "memory/forget", "memory/promote", "memory/inspect", "memory/why", "memory/summarize"] },
360
+ memory: { supported: true, optIn: true, defaultEnabled: false, autoSummaryDefaultEnabled: false, promptInjection: "interactive", storage: { core: "json", soft: "jsonl", events: "jsonl" }, methods: ["memory/status", "memory/enable", "memory/disable", "memory/autoSummary/enable", "memory/autoSummary/disable", "memory/list", "memory/add", "memory/addCore", "memory/forget", "memory/promote", "memory/relax", "memory/inspect", "memory/why", "memory/summarize"] },
358
361
  commands: { supported: true, methods: ["commands/list", "commands/run"], method: "commands/list", runMethod: "commands/run", sources: ["builtin", "prompt", "skill", "plugin"], executableSources: ["builtin", "plugin"] },
359
362
  startupResources: { supported: true, method: "resources/startup" },
360
363
  starterPack: { supported: false, reason: "cliOnlyInstallCommand" },
@@ -365,7 +368,7 @@ module Kward
365
368
  input: false,
366
369
  editor: false,
367
370
  widgets: false,
368
- footer: false,
371
+ footer: { supported: true, notification: "ui/footer" },
369
372
  custom: false,
370
373
  terminalInput: false
371
374
  },
@@ -393,15 +396,7 @@ module Kward
393
396
  categories: ["tokens", "performance", "tools", "errors"],
394
397
  rotation: { maxBytes: TelemetryLogger::DEFAULT_MAX_BYTES, retention: "manual" },
395
398
  content: "redacted-metadata-only"
396
- },
397
- session: { mode: "explicit", persistence: "jsonl" },
398
- cancellation: { behavior: "best-effort", queuedTurns: "cancel-before-run", runningTurns: "stop-emitting-events-when-possible" },
399
- eventReplay: { behavior: "recent-in-memory", persisted: false, limit: SessionManager::RECENT_EVENT_LIMIT },
400
- uiQuestion: { supported: true, method: "ui/answerQuestion", notification: "ui/question", maxQuestions: 4, multiSelect: false },
401
- prompts: { supported: true, methods: ["prompts/list", "prompts/expand"] },
402
- skills: { supported: true, tool: "read_skill" },
403
- tools: { supported: true, method: "tools/list", eventMetadata: true },
404
- config: { supported: true, methods: ["config/read", "config/update"] }
399
+ }
405
400
  }
406
401
  end
407
402
 
@@ -467,6 +462,7 @@ module Kward
467
462
  def runtime_reload(params)
468
463
  @session_manager.runtime_state(session_id: params.fetch("sessionId"))
469
464
  @session_manager.refresh_client_config
465
+ @session_manager.reload_plugins
470
466
  { ok: true, message: "Resources reloaded." }
471
467
  end
472
468
 
@@ -503,15 +499,6 @@ module Kward
503
499
  }
504
500
  end
505
501
  builtins = [
506
- {
507
- name: "crew",
508
- description: "Reserved for future crew commands.",
509
- argumentHint: "",
510
- source: "builtin",
511
- executable: false,
512
- unsupported: true,
513
- reason: "notImplemented"
514
- },
515
502
  {
516
503
  name: "copy",
517
504
  description: "CLI-only clipboard copy; RPC clients own their clipboard.",
@@ -608,6 +595,18 @@ module Kward
608
595
  end
609
596
  end
610
597
 
598
+ def configured_workspace(root = Dir.pwd)
599
+ Workspace.new(root: root, guardrails: workspace_guardrails_enabled?)
600
+ end
601
+
602
+ def workspace_guardrails_enabled?
603
+ ConfigFiles.workspace_guardrails_enabled?(@config_manager.read(redacted: false))
604
+ end
605
+
606
+ def session_auto_resume_enabled?
607
+ ConfigFiles.session_auto_resume_enabled?(@config_manager.read(redacted: false))
608
+ end
609
+
611
610
  def write_result(id, result)
612
611
  @transport.write_message({ jsonrpc: JSONRPC_VERSION, id: id, result: Redactor.redact(result) })
613
612
  end