kward 0.84.0 → 0.85.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -0
- data/Gemfile.lock +2 -2
- data/README.md +2 -2
- data/doc/agent-tools.md +13 -1
- data/doc/api.md +17 -2
- data/doc/composer.md +1 -1
- data/doc/configuration.md +27 -4
- data/doc/extensibility.md +2 -1
- data/doc/git.md +3 -1
- data/doc/pan.md +6 -0
- data/doc/permissions.md +4 -4
- data/doc/plugins.md +464 -15
- data/doc/rpc.md +154 -16
- data/doc/sandboxing.md +11 -5
- data/doc/security.md +7 -1
- data/doc/session-management.md +5 -4
- data/doc/tabs.md +6 -2
- data/doc/transports.md +15 -0
- data/doc/usage.md +4 -1
- data/doc/workspace-tools.md +9 -0
- data/examples/plugins/space_invaders.rb +1 -1
- data/examples/plugins/stardate_footer.rb +2 -2
- data/examples/plugins/telegram/plugin.rb +1 -1
- data/lib/kward/agent.rb +24 -11
- data/lib/kward/cli/compaction.rb +9 -3
- data/lib/kward/cli/plugins.rb +81 -12
- data/lib/kward/cli/prompt_interface.rb +25 -5
- data/lib/kward/cli/rendering.rb +3 -0
- data/lib/kward/cli/runtime_helpers.rb +121 -27
- data/lib/kward/cli/sessions.rb +9 -5
- data/lib/kward/cli/settings/menus.rb +745 -0
- data/lib/kward/cli/settings/model.rb +327 -0
- data/lib/kward/cli/settings.rb +6 -1055
- data/lib/kward/cli/slash_commands.rb +45 -4
- data/lib/kward/cli/tabs.rb +167 -27
- data/lib/kward/{cli_transcript_formatter.rb → cli/transcript_formatter.rb} +3 -3
- data/lib/kward/cli/worktrees.rb +65 -2
- data/lib/kward/cli.rb +26 -24
- data/lib/kward/compactor.rb +18 -7
- data/lib/kward/config/core.rb +389 -0
- data/lib/kward/config/extensions.rb +96 -0
- data/lib/kward/config/prompts.rb +313 -0
- data/lib/kward/config/settings.rb +250 -0
- data/lib/kward/config_files.rb +14 -1008
- data/lib/kward/conversation.rb +31 -2
- data/lib/kward/image_attachments.rb +1 -1
- data/lib/kward/model/client.rb +2 -2
- data/lib/kward/model/copilot_models.rb +2 -2
- data/lib/kward/model/model_info.rb +20 -3
- data/lib/kward/{openrouter_model_cache.rb → model/openrouter_model_cache.rb} +3 -3
- data/lib/kward/model/payloads.rb +12 -3
- data/lib/kward/model/typesafe_client.rb +78 -0
- data/lib/kward/pan/server.rb +10 -7
- data/lib/kward/permissions/policy.rb +6 -2
- data/lib/kward/plugin_registry.rb +2 -659
- data/lib/kward/plugins/actions.rb +453 -0
- data/lib/kward/plugins/chat_contract.rb +121 -0
- data/lib/kward/{plugin_chat_runtime.rb → plugins/chat_runtime.rb} +62 -18
- data/lib/kward/plugins/host.rb +232 -0
- data/lib/kward/plugins/registry.rb +1190 -0
- data/lib/kward/plugins/resources.rb +206 -0
- data/lib/kward/plugins/turn_request.rb +36 -0
- data/lib/kward/plugins/ui.rb +219 -0
- data/lib/kward/prompt_interface/composer_renderer.rb +1 -1
- data/lib/kward/prompt_interface/composer_state.rb +1 -1
- data/lib/kward/prompt_interface/editor/controller.rb +1 -1
- data/lib/kward/prompt_interface/editor/runner.rb +1 -1
- data/lib/kward/prompt_interface/editor/state.rb +1 -1
- data/lib/kward/prompt_interface/layout.rb +1 -1
- data/lib/kward/prompt_interface/overlay_renderer.rb +1 -1
- data/lib/kward/prompt_interface/plugin_ui_requests.rb +82 -0
- data/lib/kward/prompt_interface/runtime_state.rb +6 -1
- data/lib/kward/prompt_interface/screen.rb +9 -2
- data/lib/kward/prompt_interface.rb +60 -11
- data/lib/kward/prompts/commands.rb +2 -1
- data/lib/kward/{adaptive_pty_output_sink.rb → pty/adaptive_output_sink.rb} +1 -1
- data/lib/kward/{interactive_pty_runner.rb → pty/interactive_runner.rb} +2 -2
- data/lib/kward/{local_command_runner.rb → pty/local_command_runner.rb} +1 -1
- data/lib/kward/{local_pty_command_runner.rb → pty/local_pty_runner.rb} +3 -9
- data/lib/kward/{pty_output_sink.rb → pty/output_sink.rb} +9 -4
- data/lib/kward/{pty_transcript_normalizer.rb → pty/transcript_normalizer.rb} +1 -1
- data/lib/kward/rpc/plugin_chat_manager.rb +30 -10
- data/lib/kward/rpc/prompt_bridge.rb +25 -0
- data/lib/kward/rpc/server.rb +91 -12
- data/lib/kward/rpc/session_manager.rb +147 -44
- data/lib/kward/rpc/session_tree_rows.rb +2 -2
- data/lib/kward/rpc/tool_metadata.rb +1 -1
- data/lib/kward/sandbox/command_runner.rb +1 -1
- data/lib/kward/{session_catalog.rb → sessions/catalog.rb} +1 -1
- data/lib/kward/{session_store.rb → sessions/store.rb} +9 -9
- data/lib/kward/{session_tree_nodes.rb → sessions/tree_nodes.rb} +3 -3
- data/lib/kward/{session_tree_renderer.rb → sessions/tree_renderer.rb} +4 -4
- data/lib/kward/{session_tree_tool_display.rb → sessions/tree_tool_display.rb} +1 -1
- data/lib/kward/{kwsh.rb → shell/kwsh.rb} +3 -3
- data/lib/kward/{persistent_shell_session.rb → shell/persistent_session.rb} +3 -3
- data/lib/kward/{shell_prompt_session.rb → shell/prompt_session.rb} +1 -1
- data/lib/kward/skills/trust_store.rb +1 -1
- data/lib/kward/tabs/driver.rb +194 -0
- data/lib/kward/{tab_store.rb → tabs/store.rb} +2 -2
- data/lib/kward/{clipboard.rb → terminal/clipboard.rb} +1 -1
- data/lib/kward/{terminal_image_support.rb → terminal/image_support.rb} +1 -1
- data/lib/kward/tools/base.rb +18 -0
- data/lib/kward/tools/context_for_task.rb +15 -6
- data/lib/kward/tools/edit_file.rb +9 -6
- data/lib/kward/tools/git_commit.rb +13 -7
- data/lib/kward/tools/list_directory.rb +4 -4
- data/lib/kward/tools/plugin_tool.rb +41 -0
- data/lib/kward/tools/prepare_shell_command.rb +1 -1
- data/lib/kward/tools/read_file.rb +7 -6
- data/lib/kward/tools/registry.rb +96 -13
- data/lib/kward/tools/run_shell_command.rb +10 -8
- data/lib/kward/tools/search/code.rb +1 -1
- data/lib/kward/tools/summarize_file_structure.rb +5 -5
- data/lib/kward/tools/tool_call.rb +2 -1
- data/lib/kward/tools/typesafe_evaluate.rb +81 -0
- data/lib/kward/tools/workspace_targets.rb +58 -0
- data/lib/kward/tools/write_file.rb +9 -6
- data/lib/kward/{export_path.rb → transcripts/export_path.rb} +1 -1
- data/lib/kward/{markdown_transcript.rb → transcripts/markdown_transcript.rb} +2 -2
- data/lib/kward/transport/contracts.rb +200 -0
- data/lib/kward/transport/gateway.rb +79 -35
- data/lib/kward/transport/plugin_chat_gateway.rb +3 -2
- data/lib/kward/transport.rb +1 -200
- data/lib/kward/version.rb +1 -1
- data/lib/kward/{workspace_factory.rb → workspace/factory.rb} +2 -2
- data/lib/kward/{git_worktree_manager.rb → workspace/git_worktree_manager.rb} +28 -0
- data/lib/kward/{workspace.rb → workspace/workspace.rb} +3 -3
- data/templates/default/fulldoc/html/css/kward.css +125 -0
- data/templates/default/fulldoc/html/images/kward_screen_1.png +0 -0
- data/templates/default/fulldoc/html/setup.rb +1 -1
- data/templates/default/layout/html/layout.erb +16 -4
- metadata +67 -48
- data/lib/kward/tab_driver.rb +0 -90
- data/templates/default/fulldoc/html/images/kward_workflow.svg +0 -52
- /data/lib/kward/{editor_prompt.rb → cli/editor_prompt.rb} +0 -0
- /data/lib/kward/{editor_prompt_session.rb → cli/editor_prompt_session.rb} +0 -0
- /data/lib/kward/{diff_view_mode.rb → prompt_interface/editor/diff_view_mode.rb} +0 -0
- /data/lib/kward/{editor_mode.rb → prompt_interface/editor/editor_mode.rb} +0 -0
- /data/lib/kward/{markdown_code_block.rb → prompt_interface/editor/markdown_code_block.rb} +0 -0
- /data/lib/kward/{scratchpad_languages.rb → prompt_interface/editor/scratchpad_languages.rb} +0 -0
- /data/lib/kward/{scratchpad_runner.rb → prompt_interface/editor/scratchpad_runner.rb} +0 -0
- /data/lib/kward/{detached_run.rb → pty/detached_run.rb} +0 -0
- /data/lib/kward/{session_diff.rb → sessions/diff.rb} +0 -0
- /data/lib/kward/{session_naming.rb → sessions/naming.rb} +0 -0
- /data/lib/kward/{session_trash.rb → sessions/trash.rb} +0 -0
- /data/lib/kward/{kwshrc.rb → shell/kwshrc.rb} +0 -0
- /data/lib/kward/{shell_prompt.rb → shell/prompt.rb} +0 -0
- /data/lib/kward/{ansi.rb → terminal/ansi.rb} +0 -0
- /data/lib/kward/{terminal_keys.rb → terminal/keys.rb} +0 -0
- /data/lib/kward/{terminal_sequences.rb → terminal/sequences.rb} +0 -0
- /data/lib/kward/{terminal_text.rb → terminal/text.rb} +0 -0
- /data/lib/kward/{transcript_export.rb → transcripts/transcript_export.rb} +0 -0
- /data/lib/kward/{project_files.rb → workspace/files.rb} +0 -0
- /data/lib/kward/{path_guard.rb → workspace/path_guard.rb} +0 -0
|
@@ -9,24 +9,24 @@ require_relative "../config_files"
|
|
|
9
9
|
require_relative "../model/context_usage"
|
|
10
10
|
require_relative "../conversation"
|
|
11
11
|
require_relative "../events"
|
|
12
|
-
require_relative "../export_path"
|
|
12
|
+
require_relative "../transcripts/export_path"
|
|
13
13
|
require_relative "../memory/manager"
|
|
14
14
|
require_relative "../memory/turn_context"
|
|
15
15
|
require_relative "../message_access"
|
|
16
16
|
require_relative "../message_text"
|
|
17
|
-
require_relative "../
|
|
17
|
+
require_relative "../sessions/tree_tool_display"
|
|
18
18
|
require_relative "../skills/capture"
|
|
19
19
|
require_relative "../model/model_info"
|
|
20
|
-
require_relative "../
|
|
20
|
+
require_relative "../plugins/registry"
|
|
21
21
|
require_relative "../prompts/commands"
|
|
22
|
-
require_relative "../
|
|
23
|
-
require_relative "../
|
|
24
|
-
require_relative "../
|
|
22
|
+
require_relative "../sessions/store"
|
|
23
|
+
require_relative "../sessions/naming"
|
|
24
|
+
require_relative "../sessions/trash"
|
|
25
25
|
require_relative "../steering"
|
|
26
26
|
require_relative "../tools/tool_call"
|
|
27
27
|
require_relative "../tools/registry"
|
|
28
|
-
require_relative "../transcript_export"
|
|
29
|
-
require_relative "../
|
|
28
|
+
require_relative "../transcripts/transcript_export"
|
|
29
|
+
require_relative "../workspace/factory"
|
|
30
30
|
require_relative "attachment_normalizer"
|
|
31
31
|
require_relative "config_manager"
|
|
32
32
|
require_relative "memory_methods"
|
|
@@ -66,7 +66,7 @@ module Kward
|
|
|
66
66
|
WORKER_STOP_TIMEOUT = 2.0
|
|
67
67
|
WORKER_STOP = Object.new.freeze
|
|
68
68
|
|
|
69
|
-
RpcSession = Struct.new(:id, :workspace_root, :store, :session, :conversation, :agent, :tool_registry, :execution_profile, :prompt, :plugin_output, :queue, :worker, :running_turn_id, :footer_worker, :last_footer_text, keyword_init: true)
|
|
69
|
+
RpcSession = Struct.new(:id, :workspace_root, :store, :session, :conversation, :agent, :tool_registry, :execution_profile, :prompt, :plugin_output, :queue, :worker, :running_turn_id, :footer_worker, :last_footer_text, :last_footer_segments, keyword_init: true)
|
|
70
70
|
Turn = Struct.new(:id, :session_id, :input, :display_input, :status, :cancel_requested, :cancellation, :created_at, :started_at, :finished_at, :events, :next_sequence, :error, :streaming_behavior, :plugin_command_name, :plugin_arguments, :steering, :options, :tool_registry, :execution_profile, :mutex, keyword_init: true)
|
|
71
71
|
|
|
72
72
|
# Creates an object for RPC session lifecycle and turn coordination.
|
|
@@ -215,7 +215,7 @@ module Kward
|
|
|
215
215
|
|
|
216
216
|
# Returns the plugin registry shared by RPC sessions and plugin chats.
|
|
217
217
|
def plugin_registry
|
|
218
|
-
@plugin_registry ||= PluginRegistry.load(reserved_commands: reserved_plugin_command_names)
|
|
218
|
+
@plugin_registry ||= PluginRegistry.load(reserved_commands: reserved_plugin_command_names).tap(&:start!)
|
|
219
219
|
end
|
|
220
220
|
|
|
221
221
|
# Renames the persisted session attached to an RPC session id.
|
|
@@ -392,6 +392,11 @@ module Kward
|
|
|
392
392
|
{ closed: true }
|
|
393
393
|
end
|
|
394
394
|
|
|
395
|
+
def shutdown_plugins
|
|
396
|
+
@plugin_registry&.shutdown!
|
|
397
|
+
{ closed: true }
|
|
398
|
+
end
|
|
399
|
+
|
|
395
400
|
# Returns the normalized transcript for the active RPC session.
|
|
396
401
|
def transcript(session_id:)
|
|
397
402
|
rpc_session = fetch_session(session_id)
|
|
@@ -490,6 +495,12 @@ module Kward
|
|
|
490
495
|
{ ok: true }
|
|
491
496
|
end
|
|
492
497
|
|
|
498
|
+
def answer_plugin_ui(session_id:, request_id:, value:)
|
|
499
|
+
rpc_session = fetch_session(session_id)
|
|
500
|
+
rpc_session.prompt.answer_plugin_ui(request_id, value)
|
|
501
|
+
{ ok: true }
|
|
502
|
+
end
|
|
503
|
+
|
|
493
504
|
def answer_tool_approval(session_id:, approval_request_id:, approved:)
|
|
494
505
|
rpc_session = fetch_session(session_id)
|
|
495
506
|
rpc_session.prompt.answer_tool_approval(approval_request_id, approved: approved)
|
|
@@ -525,18 +536,39 @@ module Kward
|
|
|
525
536
|
|
|
526
537
|
def run_plugin_command(session_id:, command:, arguments: "")
|
|
527
538
|
rpc_session = fetch_session(session_id)
|
|
539
|
+
raise ArgumentError, "Plugin commands are disabled for this session" if rpc_session.execution_profile && !rpc_session.execution_profile.plugin_commands
|
|
540
|
+
|
|
528
541
|
command = plugin_registry.command_for(command.to_s.delete_prefix("/")) || raise(ArgumentError, "Unknown plugin command: #{command}")
|
|
542
|
+
arguments = command.parse_arguments(arguments)
|
|
529
543
|
output = []
|
|
530
|
-
context = plugin_context(rpc_session, args: arguments
|
|
531
|
-
result = command.handler.call(arguments
|
|
544
|
+
context = plugin_context(rpc_session, args: arguments, say_callback: lambda { |message| output << message.to_s })
|
|
545
|
+
result = command.normalize_result(command.handler.call(arguments, context))
|
|
532
546
|
output = rpc_session.plugin_output.shift(rpc_session.plugin_output.length) + output
|
|
533
|
-
|
|
547
|
+
serialized_result = command.typed? ? result.to_h : (result.nil? ? nil : result.to_s)
|
|
548
|
+
{ command: command.name, output: output, result: serialized_result }
|
|
534
549
|
end
|
|
535
550
|
|
|
536
551
|
def plugin_commands
|
|
537
552
|
plugin_registry.commands
|
|
538
553
|
end
|
|
539
554
|
|
|
555
|
+
def plugin_actions
|
|
556
|
+
plugin_registry.actions
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
def run_plugin_action(session_id:, id:, arguments: {})
|
|
560
|
+
rpc_session = fetch_session(session_id)
|
|
561
|
+
raise ArgumentError, "Plugin actions are disabled for this session" if rpc_session.execution_profile && !rpc_session.execution_profile.plugin_commands
|
|
562
|
+
|
|
563
|
+
action = plugin_registry.action_for(id) || raise(ArgumentError, "Unknown plugin action: #{id}")
|
|
564
|
+
arguments = action.parse_arguments(arguments)
|
|
565
|
+
output = []
|
|
566
|
+
context = plugin_context(rpc_session, args: arguments, say_callback: lambda { |message| output << message.to_s })
|
|
567
|
+
result = action.normalize_result(action.handler.call(arguments, context))
|
|
568
|
+
output = rpc_session.plugin_output.shift(rpc_session.plugin_output.length) + output
|
|
569
|
+
{ action: action.id, output: output, result: result.to_h }
|
|
570
|
+
end
|
|
571
|
+
|
|
540
572
|
def available_models
|
|
541
573
|
models = @client.respond_to?(:available_models) ? Array(@client.available_models) : []
|
|
542
574
|
normalized = models.map { |model| normalize_model(model) }
|
|
@@ -628,6 +660,8 @@ module Kward
|
|
|
628
660
|
|
|
629
661
|
def reload_plugins
|
|
630
662
|
registry = PluginRegistry.load(reserved_commands: reserved_plugin_command_names)
|
|
663
|
+
@plugin_registry&.reload!
|
|
664
|
+
registry.start!
|
|
631
665
|
sessions = @mutex.synchronize do
|
|
632
666
|
@plugin_registry = registry
|
|
633
667
|
@sessions.values
|
|
@@ -636,7 +670,7 @@ module Kward
|
|
|
636
670
|
rpc_session.conversation.plugin_registry = registry if rpc_session.conversation.respond_to?(:plugin_registry=)
|
|
637
671
|
rpc_session.conversation.refresh_system_message! if rpc_session.conversation.respond_to?(:refresh_system_message!)
|
|
638
672
|
rebuild_session_tools(rpc_session)
|
|
639
|
-
if registry.
|
|
673
|
+
if registry.status?
|
|
640
674
|
start_footer_worker(rpc_session)
|
|
641
675
|
emit_footer_update(rpc_session)
|
|
642
676
|
else
|
|
@@ -714,7 +748,9 @@ module Kward
|
|
|
714
748
|
hook_context = lifecycle_hook_context(
|
|
715
749
|
conversation: rpc_session.conversation,
|
|
716
750
|
session: rpc_session.session,
|
|
717
|
-
workspace_root: rpc_session.workspace_root
|
|
751
|
+
workspace_root: rpc_session.workspace_root,
|
|
752
|
+
prompt: rpc_session.prompt,
|
|
753
|
+
session_id: rpc_session.id
|
|
718
754
|
)
|
|
719
755
|
hook_manager = lifecycle_hook_manager(rpc_session.workspace_root)
|
|
720
756
|
tool_registry = build_tool_registry(
|
|
@@ -1042,7 +1078,13 @@ module Kward
|
|
|
1042
1078
|
conversation.plugin_registry ||= plugin_registry if conversation.respond_to?(:plugin_registry)
|
|
1043
1079
|
id = SecureRandom.uuid
|
|
1044
1080
|
prompt = PromptBridge.new(notify: method(:notify), session_id: id)
|
|
1045
|
-
hook_context = lifecycle_hook_context(
|
|
1081
|
+
hook_context = lifecycle_hook_context(
|
|
1082
|
+
conversation: conversation,
|
|
1083
|
+
session: session,
|
|
1084
|
+
workspace_root: workspace_root,
|
|
1085
|
+
prompt: prompt,
|
|
1086
|
+
session_id: id
|
|
1087
|
+
)
|
|
1046
1088
|
hook_manager = lifecycle_hook_manager(workspace_root)
|
|
1047
1089
|
tool_registry = build_tool_registry(workspace_root, prompt, hook_manager: hook_manager, hook_context: hook_context)
|
|
1048
1090
|
agent = Agent.new(
|
|
@@ -1074,6 +1116,7 @@ module Kward
|
|
|
1074
1116
|
workspace: configured_workspace(workspace_root),
|
|
1075
1117
|
prompt: prompt,
|
|
1076
1118
|
allowed_tool_names: allowed_tool_names,
|
|
1119
|
+
plugin_tools: plugin_registry.tools,
|
|
1077
1120
|
tool_approval: tool_approval,
|
|
1078
1121
|
approval_for_allowed_tools: !tool_approval.nil?,
|
|
1079
1122
|
hook_manager: hook_manager,
|
|
@@ -1115,13 +1158,16 @@ module Kward
|
|
|
1115
1158
|
})
|
|
1116
1159
|
end
|
|
1117
1160
|
|
|
1118
|
-
def lifecycle_hook_context(conversation:, session:, workspace_root:)
|
|
1161
|
+
def lifecycle_hook_context(conversation:, session:, workspace_root:, prompt: nil, session_id: nil)
|
|
1162
|
+
say_callback = lambda { |message| notify("hook/message", { message: message.to_s }) }
|
|
1119
1163
|
PluginRegistry::Context.new(
|
|
1120
1164
|
conversation: conversation,
|
|
1121
1165
|
args: "",
|
|
1122
1166
|
session: session,
|
|
1123
1167
|
workspace_root: workspace_root,
|
|
1124
|
-
say_callback:
|
|
1168
|
+
say_callback: say_callback,
|
|
1169
|
+
ui: rpc_plugin_ui(prompt: prompt, session_id: session_id, say_callback: say_callback, requests: false),
|
|
1170
|
+
tool_ui: rpc_plugin_ui(prompt: prompt, session_id: session_id, say_callback: say_callback, requests: !prompt.nil?)
|
|
1125
1171
|
)
|
|
1126
1172
|
end
|
|
1127
1173
|
|
|
@@ -1212,7 +1258,7 @@ module Kward
|
|
|
1212
1258
|
end
|
|
1213
1259
|
|
|
1214
1260
|
def start_footer_worker(rpc_session)
|
|
1215
|
-
return unless plugin_registry.
|
|
1261
|
+
return unless plugin_registry.status?
|
|
1216
1262
|
return if rpc_session.footer_worker&.alive?
|
|
1217
1263
|
|
|
1218
1264
|
rpc_session.footer_worker = Thread.new do
|
|
@@ -1290,18 +1336,7 @@ module Kward
|
|
|
1290
1336
|
if turn.plugin_command_name
|
|
1291
1337
|
run_plugin_turn(rpc_session, turn)
|
|
1292
1338
|
else
|
|
1293
|
-
|
|
1294
|
-
prepare_memory_context(rpc_session.conversation, turn.input) unless memory_disabled?(turn)
|
|
1295
|
-
rpc_session.agent.ask(turn.input, display_input: turn_display_input(turn), cancellation: turn.cancellation, steering: turn.steering, options: turn.options || {}, tool_registry: turn.tool_registry) do |event|
|
|
1296
|
-
next if turn.cancel_requested
|
|
1297
|
-
|
|
1298
|
-
notify_plugin_transcript_event(rpc_session, event)
|
|
1299
|
-
handle_agent_event(turn, event)
|
|
1300
|
-
end
|
|
1301
|
-
unless memory_disabled?(turn)
|
|
1302
|
-
persist_memory_state(rpc_session)
|
|
1303
|
-
auto_summarize_memory(rpc_session) unless turn.cancel_requested
|
|
1304
|
-
end
|
|
1339
|
+
run_session_model_turn(rpc_session, turn, input: turn.input, display_input: turn_display_input(turn))
|
|
1305
1340
|
finish_turn(turn, turn.cancel_requested ? "canceled" : "completed")
|
|
1306
1341
|
end
|
|
1307
1342
|
rescue Cancellation::CancelledError
|
|
@@ -1316,6 +1351,22 @@ module Kward
|
|
|
1316
1351
|
rpc_session.running_turn_id = nil
|
|
1317
1352
|
end
|
|
1318
1353
|
|
|
1354
|
+
def run_session_model_turn(rpc_session, turn, input:, display_input: nil)
|
|
1355
|
+
auto_name_session(rpc_session, display_input || turn.display_input || input)
|
|
1356
|
+
memory_input = input.is_a?(PluginTurnRequest) ? input.to_s : input
|
|
1357
|
+
prepare_memory_context(rpc_session.conversation, memory_input) unless memory_disabled?(turn)
|
|
1358
|
+
rpc_session.agent.ask(input, display_input: display_input, cancellation: turn.cancellation, steering: turn.steering, options: turn.options || {}, tool_registry: turn.tool_registry) do |event|
|
|
1359
|
+
next if turn.cancel_requested
|
|
1360
|
+
|
|
1361
|
+
notify_plugin_transcript_event(rpc_session, event)
|
|
1362
|
+
handle_agent_event(turn, event)
|
|
1363
|
+
end
|
|
1364
|
+
unless memory_disabled?(turn)
|
|
1365
|
+
persist_memory_state(rpc_session)
|
|
1366
|
+
auto_summarize_memory(rpc_session) unless turn.cancel_requested
|
|
1367
|
+
end
|
|
1368
|
+
end
|
|
1369
|
+
|
|
1319
1370
|
def build_steering(_turn)
|
|
1320
1371
|
Steering.new
|
|
1321
1372
|
end
|
|
@@ -1371,12 +1422,44 @@ module Kward
|
|
|
1371
1422
|
turn_payload(turn)
|
|
1372
1423
|
end
|
|
1373
1424
|
|
|
1374
|
-
def plugin_context(rpc_session, args: nil, say_callback:)
|
|
1425
|
+
def plugin_context(rpc_session, args: nil, say_callback:, requests: false, cancellation: nil, turn_command: nil)
|
|
1375
1426
|
PluginRegistry::Context.new(
|
|
1376
1427
|
conversation: rpc_session.conversation,
|
|
1377
1428
|
args: args,
|
|
1378
1429
|
session: rpc_session.session,
|
|
1379
1430
|
workspace_root: rpc_session.workspace_root,
|
|
1431
|
+
say_callback: say_callback,
|
|
1432
|
+
cancellation: cancellation,
|
|
1433
|
+
turn_command: turn_command,
|
|
1434
|
+
ui: rpc_plugin_ui(
|
|
1435
|
+
prompt: rpc_session.prompt,
|
|
1436
|
+
session_id: rpc_session.id,
|
|
1437
|
+
say_callback: say_callback,
|
|
1438
|
+
requests: requests
|
|
1439
|
+
)
|
|
1440
|
+
)
|
|
1441
|
+
end
|
|
1442
|
+
|
|
1443
|
+
def rpc_plugin_ui(prompt:, session_id:, say_callback:, requests:)
|
|
1444
|
+
capabilities = { progress: true, notify: true }
|
|
1445
|
+
requester = nil
|
|
1446
|
+
if requests && prompt
|
|
1447
|
+
capabilities.merge!(question: true, select: true, confirm: true, input: true)
|
|
1448
|
+
requester = lambda do |kind, payload, cancellation: nil|
|
|
1449
|
+
if kind == :question
|
|
1450
|
+
prompt.ask_user_question(payload.fetch(:questions), cancellation: cancellation)
|
|
1451
|
+
else
|
|
1452
|
+
prompt.request_plugin_ui(kind, payload, cancellation: cancellation)
|
|
1453
|
+
end
|
|
1454
|
+
end
|
|
1455
|
+
end
|
|
1456
|
+
emitter = lambda do |kind, payload|
|
|
1457
|
+
method = kind == :progress ? "ui/progress" : "ui/notification"
|
|
1458
|
+
payload = payload.merge(level: payload[:level].to_s) if payload[:level]
|
|
1459
|
+
notify(method, { sessionId: session_id }.merge(payload))
|
|
1460
|
+
end
|
|
1461
|
+
PluginUI.new(
|
|
1462
|
+
backend: PluginUI::Backend.new(capabilities: capabilities, requester: requester, emitter: emitter),
|
|
1380
1463
|
say_callback: say_callback
|
|
1381
1464
|
)
|
|
1382
1465
|
end
|
|
@@ -1384,13 +1467,30 @@ module Kward
|
|
|
1384
1467
|
def run_plugin_turn(rpc_session, turn)
|
|
1385
1468
|
turn.cancellation&.raise_if_cancelled!
|
|
1386
1469
|
command = plugin_registry.command_for(turn.plugin_command_name) || raise(ArgumentError, "Unknown plugin command: #{turn.plugin_command_name}")
|
|
1470
|
+
arguments = command.parse_arguments(turn.plugin_arguments)
|
|
1387
1471
|
output = []
|
|
1388
|
-
context = plugin_context(
|
|
1389
|
-
|
|
1472
|
+
context = plugin_context(
|
|
1473
|
+
rpc_session,
|
|
1474
|
+
args: arguments,
|
|
1475
|
+
say_callback: lambda { |message| output << message.to_s },
|
|
1476
|
+
requests: true,
|
|
1477
|
+
cancellation: turn.cancellation,
|
|
1478
|
+
turn_command: command
|
|
1479
|
+
)
|
|
1480
|
+
result = command.normalize_result(command.handler.call(arguments, context))
|
|
1481
|
+
turn.cancellation&.raise_if_cancelled!
|
|
1482
|
+
if command.typed?
|
|
1483
|
+
emit_turn_event(turn, "pluginCommandResult", { command: command.name, result: result.to_h })
|
|
1484
|
+
result = result.message
|
|
1485
|
+
end
|
|
1390
1486
|
answer = (output + [result]).compact.map(&:to_s).reject(&:empty?).join("\n")
|
|
1391
1487
|
unless answer.empty?
|
|
1392
1488
|
emit_turn_event(turn, "assistantDelta", { delta: answer })
|
|
1393
|
-
emit_turn_event(turn, "answer", { content: answer })
|
|
1489
|
+
emit_turn_event(turn, "answer", { content: answer }) unless context.requested_turn
|
|
1490
|
+
end
|
|
1491
|
+
if context.requested_turn
|
|
1492
|
+
turn.steering = build_steering(turn) if supports_in_flight_steer?
|
|
1493
|
+
run_session_model_turn(rpc_session, turn, input: context.requested_turn, display_input: context.requested_turn.to_s)
|
|
1394
1494
|
end
|
|
1395
1495
|
finish_turn(turn, turn.cancel_requested ? "canceled" : "completed")
|
|
1396
1496
|
end
|
|
@@ -1457,27 +1557,30 @@ module Kward
|
|
|
1457
1557
|
end
|
|
1458
1558
|
|
|
1459
1559
|
def emit_footer_update(rpc_session)
|
|
1460
|
-
|
|
1461
|
-
return clear_footer_update(rpc_session) unless renderer
|
|
1560
|
+
return clear_footer_update(rpc_session) unless plugin_registry.status?
|
|
1462
1561
|
|
|
1463
|
-
|
|
1562
|
+
segments = begin
|
|
1464
1563
|
context = plugin_context(rpc_session, say_callback: lambda { |message| rpc_session.plugin_output << message.to_s })
|
|
1465
|
-
|
|
1564
|
+
plugin_registry.status_segments(context)
|
|
1466
1565
|
rescue StandardError => e
|
|
1467
1566
|
warn "Warning: Kward plugin footer error: #{e.message}"
|
|
1468
|
-
|
|
1567
|
+
[]
|
|
1469
1568
|
end
|
|
1470
|
-
|
|
1569
|
+
segment_payloads = segments.map(&:to_h)
|
|
1570
|
+
text = plugin_registry.compose_status(segments)
|
|
1571
|
+
return if rpc_session.last_footer_text == text && rpc_session.last_footer_segments == segment_payloads
|
|
1471
1572
|
|
|
1472
1573
|
rpc_session.last_footer_text = text
|
|
1473
|
-
|
|
1574
|
+
rpc_session.last_footer_segments = segment_payloads
|
|
1575
|
+
notify("ui/footer", { sessionId: rpc_session.id, text: text, segments: segment_payloads })
|
|
1474
1576
|
end
|
|
1475
1577
|
|
|
1476
1578
|
def clear_footer_update(rpc_session)
|
|
1477
|
-
return if rpc_session.last_footer_text.to_s.empty?
|
|
1579
|
+
return if rpc_session.last_footer_text.to_s.empty? && Array(rpc_session.last_footer_segments).empty?
|
|
1478
1580
|
|
|
1479
1581
|
rpc_session.last_footer_text = ""
|
|
1480
|
-
|
|
1582
|
+
rpc_session.last_footer_segments = []
|
|
1583
|
+
notify("ui/footer", { sessionId: rpc_session.id, text: "", segments: [] })
|
|
1481
1584
|
end
|
|
1482
1585
|
|
|
1483
1586
|
def emit_turn_event(turn, type, payload)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require_relative "../message_access"
|
|
2
2
|
require_relative "../message_text"
|
|
3
|
-
require_relative "../
|
|
4
|
-
require_relative "../
|
|
3
|
+
require_relative "../sessions/tree_nodes"
|
|
4
|
+
require_relative "../sessions/tree_tool_display"
|
|
5
5
|
|
|
6
6
|
# Namespace for the Kward CLI agent runtime.
|
|
7
7
|
module Kward
|
|
@@ -3,15 +3,15 @@ require "digest"
|
|
|
3
3
|
require "json"
|
|
4
4
|
require "securerandom"
|
|
5
5
|
require "time"
|
|
6
|
-
require_relative "config_files"
|
|
7
|
-
require_relative "conversation"
|
|
8
|
-
require_relative "message_access"
|
|
9
|
-
require_relative "message_text"
|
|
10
|
-
require_relative "private_file"
|
|
11
|
-
require_relative "rpc/tool_event_normalizer"
|
|
12
|
-
require_relative "
|
|
13
|
-
require_relative "tools/tool_call"
|
|
14
|
-
require_relative "workspace"
|
|
6
|
+
require_relative "../config_files"
|
|
7
|
+
require_relative "../conversation"
|
|
8
|
+
require_relative "../message_access"
|
|
9
|
+
require_relative "../message_text"
|
|
10
|
+
require_relative "../private_file"
|
|
11
|
+
require_relative "../rpc/tool_event_normalizer"
|
|
12
|
+
require_relative "catalog"
|
|
13
|
+
require_relative "../tools/tool_call"
|
|
14
|
+
require_relative "../workspace/workspace"
|
|
15
15
|
|
|
16
16
|
# Namespace for the Kward CLI agent runtime.
|
|
17
17
|
module Kward
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
require_relative "message_access"
|
|
2
|
-
require_relative "message_text"
|
|
3
|
-
require_relative "tools/tool_call"
|
|
1
|
+
require_relative "../message_access"
|
|
2
|
+
require_relative "../message_text"
|
|
3
|
+
require_relative "../tools/tool_call"
|
|
4
4
|
|
|
5
5
|
# Namespace for the Kward CLI agent runtime.
|
|
6
6
|
module Kward
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
require_relative "message_access"
|
|
2
|
-
require_relative "message_text"
|
|
3
|
-
require_relative "
|
|
4
|
-
require_relative "
|
|
1
|
+
require_relative "../message_access"
|
|
2
|
+
require_relative "../message_text"
|
|
3
|
+
require_relative "tree_nodes"
|
|
4
|
+
require_relative "tree_tool_display"
|
|
5
5
|
|
|
6
6
|
# Namespace for the Kward CLI agent runtime.
|
|
7
7
|
module Kward
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
require "shellwords"
|
|
2
|
-
require_relative "ansi"
|
|
3
|
-
require_relative "local_command_runner"
|
|
2
|
+
require_relative "../terminal/ansi"
|
|
3
|
+
require_relative "../pty/local_command_runner"
|
|
4
4
|
begin
|
|
5
|
-
require_relative "
|
|
5
|
+
require_relative "../pty/local_pty_runner"
|
|
6
6
|
rescue LoadError
|
|
7
7
|
nil
|
|
8
8
|
end
|
|
@@ -2,11 +2,11 @@ require "io/console"
|
|
|
2
2
|
require "pty"
|
|
3
3
|
require "securerandom"
|
|
4
4
|
require "shellwords"
|
|
5
|
-
require_relative "ansi"
|
|
5
|
+
require_relative "../terminal/ansi"
|
|
6
6
|
require_relative "kwsh"
|
|
7
7
|
require_relative "kwshrc"
|
|
8
|
-
require_relative "
|
|
9
|
-
require_relative "detached_run"
|
|
8
|
+
require_relative "../pty/local_pty_runner"
|
|
9
|
+
require_relative "../pty/detached_run"
|
|
10
10
|
|
|
11
11
|
# Namespace for the Kward CLI agent runtime.
|
|
12
12
|
module Kward
|