kward 0.79.0 → 0.80.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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +6 -0
- data/CHANGELOG.md +65 -1
- data/Gemfile.lock +2 -2
- data/README.md +31 -38
- data/doc/agent-tools.md +9 -10
- data/doc/api.md +3 -3
- data/doc/authentication.md +79 -110
- data/doc/code-search.md +1 -1
- data/doc/composer.md +1 -1
- data/doc/configuration.md +69 -26
- data/doc/context-budgeting.md +6 -6
- data/doc/context-tools.md +2 -2
- data/doc/editor.md +7 -7
- data/doc/extensibility.md +7 -29
- data/doc/files.md +6 -6
- data/doc/getting-started.md +2 -2
- data/doc/git.md +8 -2
- data/doc/lifecycle-hooks.md +5 -4
- data/doc/local-models.md +2 -2
- data/doc/mcp.md +5 -5
- data/doc/permissions.md +13 -4
- data/doc/plugins.md +39 -4
- data/doc/prompt-templates.md +105 -0
- data/doc/providers.md +140 -0
- data/doc/releasing.md +10 -2
- data/doc/rpc.md +54 -31
- data/doc/sandboxing.md +22 -0
- data/doc/security.md +22 -7
- data/doc/session-management.md +2 -4
- data/doc/skills.md +2 -0
- data/doc/tabs.md +37 -0
- data/doc/telegram.md +138 -0
- data/doc/transports.md +209 -0
- data/doc/usage.md +11 -3
- data/doc/web-search.md +3 -3
- data/doc/workspace-tools.md +2 -2
- data/examples/plugins/stardate_footer.rb +10 -0
- data/examples/plugins/telegram/plugin.rb +39 -0
- data/examples/plugins/telegram/telegram_api.rb +136 -0
- data/examples/plugins/telegram/telegram_transport.rb +304 -0
- data/lib/kward/agent.rb +11 -6
- data/lib/kward/ansi.rb +1 -0
- data/lib/kward/auth/api_key_store.rb +99 -0
- data/lib/kward/cli/auth_commands.rb +72 -13
- data/lib/kward/cli/commands.rb +7 -0
- data/lib/kward/cli/git.rb +31 -8
- data/lib/kward/cli/memory_commands.rb +2 -2
- data/lib/kward/cli/plugins.rb +1 -0
- data/lib/kward/cli/prompt_interface.rb +44 -1
- data/lib/kward/cli/rendering.rb +6 -2
- data/lib/kward/cli/runtime_helpers.rb +5 -2
- data/lib/kward/cli/sessions.rb +1 -1
- data/lib/kward/cli/settings.rb +97 -33
- data/lib/kward/cli/slash_commands.rb +1 -1
- data/lib/kward/cli/tabs.rb +103 -37
- data/lib/kward/cli/transports.rb +67 -0
- data/lib/kward/cli/worktrees.rb +360 -0
- data/lib/kward/cli.rb +22 -0
- data/lib/kward/compactor.rb +7 -2
- data/lib/kward/config_files.rb +51 -12
- data/lib/kward/conversation.rb +13 -7
- data/lib/kward/git_worktree_manager.rb +291 -0
- data/lib/kward/hooks/audit_log.rb +5 -2
- data/lib/kward/model/azure_openai_config.rb +54 -0
- data/lib/kward/model/catalog.rb +113 -0
- data/lib/kward/model/client.rb +225 -20
- data/lib/kward/model/model_info.rb +49 -0
- data/lib/kward/model/payloads.rb +63 -4
- data/lib/kward/model/provider_catalog.rb +114 -0
- data/lib/kward/model/sources.rb +70 -0
- data/lib/kward/model/stream_parser.rb +83 -13
- data/lib/kward/permissions/policy.rb +18 -4
- data/lib/kward/plugin_chat_runtime.rb +374 -0
- data/lib/kward/plugin_registry.rb +99 -19
- data/lib/kward/private_file.rb +9 -3
- data/lib/kward/prompt_interface/approval_prompt.rb +2 -0
- data/lib/kward/prompt_interface/composer_renderer.rb +1 -5
- data/lib/kward/prompt_interface/editor/auto_indent.rb +46 -0
- data/lib/kward/prompt_interface/editor/buffer.rb +18 -4
- data/lib/kward/prompt_interface/editor/controller.rb +7 -7
- data/lib/kward/prompt_interface/editor/modes/vibe.rb +2 -2
- data/lib/kward/prompt_interface/editor/renderer.rb +8 -8
- data/lib/kward/prompt_interface/editor/state.rb +3 -9
- data/lib/kward/prompt_interface/editor/syntax_highlighter.rb +219 -19
- data/lib/kward/prompt_interface/file_overlay.rb +2 -2
- data/lib/kward/prompt_interface/git_prompt.rb +7 -3
- data/lib/kward/prompt_interface/interactive/controller.rb +4 -4
- data/lib/kward/prompt_interface/interactive/renderer.rb +4 -1
- data/lib/kward/prompt_interface/interactive/state.rb +2 -2
- data/lib/kward/prompt_interface/project_browser.rb +25 -6
- data/lib/kward/prompt_interface/runtime_state.rb +22 -6
- data/lib/kward/prompt_interface/screen.rb +3 -0
- data/lib/kward/prompt_interface/selection_prompt.rb +7 -1
- data/lib/kward/prompt_interface/transcript_buffer.rb +24 -2
- data/lib/kward/prompt_interface.rb +53 -12
- data/lib/kward/prompts/commands.rb +1 -1
- data/lib/kward/prompts/templates.rb +11 -6
- data/lib/kward/prompts.rb +6 -5
- data/lib/kward/rpc/auth_manager.rb +112 -152
- data/lib/kward/rpc/config_manager.rb +46 -6
- data/lib/kward/rpc/plugin_chat_manager.rb +55 -196
- data/lib/kward/rpc/prompt_bridge.rb +8 -4
- data/lib/kward/rpc/redactor.rb +1 -1
- data/lib/kward/rpc/server.rb +60 -10
- data/lib/kward/rpc/session_manager.rb +109 -30
- data/lib/kward/session_store.rb +84 -24
- data/lib/kward/skills/registry.rb +16 -11
- data/lib/kward/tab_driver.rb +6 -3
- data/lib/kward/telemetry/logger.rb +5 -2
- data/lib/kward/tools/git_commit.rb +39 -0
- data/lib/kward/tools/registry.rb +7 -2
- data/lib/kward/tools/tool_call.rb +1 -0
- data/lib/kward/transport/gateway.rb +253 -0
- data/lib/kward/transport/host.rb +343 -0
- data/lib/kward/transport/manager.rb +179 -0
- data/lib/kward/transport/plugin_chat_gateway.rb +186 -0
- data/lib/kward/transport/runtime.rb +41 -0
- data/lib/kward/transport/store.rb +101 -0
- data/lib/kward/transport.rb +204 -0
- data/lib/kward/version.rb +1 -1
- data/lib/kward/workspace_factory.rb +19 -2
- data/templates/default/fulldoc/html/js/kward.js +2 -0
- data/templates/default/kward_navigation.rb +7 -2
- data/templates/default/layout/html/setup.rb +4 -0
- metadata +26 -1
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require "stringio"
|
|
2
|
+
require_relative "../config_files"
|
|
3
|
+
require_relative "../rpc/server"
|
|
4
|
+
require_relative "gateway"
|
|
5
|
+
require_relative "manager"
|
|
6
|
+
require_relative "plugin_chat_gateway"
|
|
7
|
+
|
|
8
|
+
module Kward
|
|
9
|
+
module Transport
|
|
10
|
+
# Builds the Kward runtime needed by foreground transport processes.
|
|
11
|
+
class Runtime
|
|
12
|
+
attr_reader :manager
|
|
13
|
+
|
|
14
|
+
def initialize(client:)
|
|
15
|
+
@server = RPC::Server.new(
|
|
16
|
+
input: StringIO.new,
|
|
17
|
+
output: StringIO.new,
|
|
18
|
+
error_output: $stderr,
|
|
19
|
+
client: client
|
|
20
|
+
)
|
|
21
|
+
@manager = Manager.new(
|
|
22
|
+
registry: @server.session_manager.plugin_registry,
|
|
23
|
+
gateway: lambda { |transport_id|
|
|
24
|
+
Gateway.new(session_manager: @server.session_manager, transport_id: transport_id)
|
|
25
|
+
},
|
|
26
|
+
plugin_chat_gateway: lambda { |transport_id|
|
|
27
|
+
PluginChatGateway.new(runtime: @server.plugin_chat_manager.runtime, transport_id: transport_id)
|
|
28
|
+
},
|
|
29
|
+
config_root: ConfigFiles.config_dir,
|
|
30
|
+
config_provider: ->(transport_id) { ConfigFiles.transport_config(transport_id) }
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def shutdown
|
|
35
|
+
@manager.shutdown
|
|
36
|
+
@server.shutdown
|
|
37
|
+
nil
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
require "fileutils"
|
|
2
|
+
require "json"
|
|
3
|
+
require "thread"
|
|
4
|
+
require "time"
|
|
5
|
+
require_relative "../config_files"
|
|
6
|
+
require_relative "../deep_copy"
|
|
7
|
+
require_relative "../private_file"
|
|
8
|
+
|
|
9
|
+
module Kward
|
|
10
|
+
module Transport
|
|
11
|
+
# Small private JSON store scoped to one transport plugin.
|
|
12
|
+
class Store
|
|
13
|
+
KEY_PATTERN = /\A[A-Za-z0-9][A-Za-z0-9:._-]*\z/.freeze
|
|
14
|
+
DEFAULT_MAX_PROCESSED_KEYS = 10_000
|
|
15
|
+
|
|
16
|
+
attr_reader :transport_id
|
|
17
|
+
|
|
18
|
+
def initialize(transport_id, root: ConfigFiles.config_dir)
|
|
19
|
+
@transport_id = validate_key(transport_id, "transport id")
|
|
20
|
+
@path = File.join(File.expand_path(root), "transports", @transport_id, "state.json")
|
|
21
|
+
@mutex = Mutex.new
|
|
22
|
+
@values, @processed = load_state
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def get(key)
|
|
26
|
+
key = validate_key(key, "storage key")
|
|
27
|
+
@mutex.synchronize { copy(@values[key]) }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def put(key, value)
|
|
31
|
+
key = validate_key(key, "storage key")
|
|
32
|
+
@mutex.synchronize do
|
|
33
|
+
@values[key] = copy(value)
|
|
34
|
+
persist
|
|
35
|
+
end
|
|
36
|
+
value
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def delete(key)
|
|
40
|
+
key = validate_key(key, "storage key")
|
|
41
|
+
@mutex.synchronize do
|
|
42
|
+
present = @values.key?(key)
|
|
43
|
+
value = @values.delete(key)
|
|
44
|
+
persist if present
|
|
45
|
+
copy(value)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Claims an external event key. Returns false when the key was already
|
|
50
|
+
# claimed, making duplicate webhook and polling deliveries harmless.
|
|
51
|
+
def claim(key, max_keys: DEFAULT_MAX_PROCESSED_KEYS)
|
|
52
|
+
key = validate_key(key, "idempotency key")
|
|
53
|
+
max_keys = Integer(max_keys)
|
|
54
|
+
raise ArgumentError, "max_keys must be positive" unless max_keys.positive?
|
|
55
|
+
|
|
56
|
+
@mutex.synchronize do
|
|
57
|
+
return false if @processed.key?(key)
|
|
58
|
+
|
|
59
|
+
@processed[key] = Time.now.utc.iso8601
|
|
60
|
+
@processed.shift while @processed.length > max_keys
|
|
61
|
+
persist
|
|
62
|
+
true
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def load_state
|
|
69
|
+
return [{}, {}] unless File.file?(@path)
|
|
70
|
+
|
|
71
|
+
state = JSON.parse(File.read(@path))
|
|
72
|
+
values = state.fetch("values", {})
|
|
73
|
+
processed = state.fetch("processed", {})
|
|
74
|
+
unless values.is_a?(Hash) && processed.is_a?(Hash)
|
|
75
|
+
raise JSON::ParserError, "transport state must contain JSON objects"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
[values, processed]
|
|
79
|
+
rescue JSON::ParserError => e
|
|
80
|
+
raise "Invalid transport state #{@path}: #{e.message}"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def persist
|
|
84
|
+
PrivateFile.write_json(@path, "values" => @values, "processed" => @processed)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def validate_key(value, name)
|
|
88
|
+
value = value.to_s
|
|
89
|
+
raise ArgumentError, "#{name} is required" unless value.match?(KEY_PATTERN)
|
|
90
|
+
|
|
91
|
+
value
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def copy(value)
|
|
95
|
+
return nil if value.nil?
|
|
96
|
+
|
|
97
|
+
DeepCopy.dup(value)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
require "logger"
|
|
2
|
+
require_relative "deep_copy"
|
|
3
|
+
|
|
4
|
+
# Namespace for frontend-neutral external transport contracts.
|
|
5
|
+
module Kward
|
|
6
|
+
module Transport
|
|
7
|
+
STREAMING_MODES = %i[native edit aggregate none].freeze
|
|
8
|
+
|
|
9
|
+
Actor = Data.define(:id, :display_name, :metadata)
|
|
10
|
+
Attachment = Data.define(:name, :mime_type, :data, :url, :metadata)
|
|
11
|
+
ConversationKey = Data.define(:transport_id, :external_id)
|
|
12
|
+
InboundMessage = Data.define(
|
|
13
|
+
:conversation,
|
|
14
|
+
:message_id,
|
|
15
|
+
:actor,
|
|
16
|
+
:text,
|
|
17
|
+
:attachments,
|
|
18
|
+
:reply_context,
|
|
19
|
+
:idempotency_key,
|
|
20
|
+
:metadata
|
|
21
|
+
)
|
|
22
|
+
TurnEvent = Data.define(:type, :session_id, :turn_id, :sequence, :payload)
|
|
23
|
+
InteractionRequest = Data.define(
|
|
24
|
+
:id,
|
|
25
|
+
:session_id,
|
|
26
|
+
:turn_id,
|
|
27
|
+
:kind,
|
|
28
|
+
:prompt,
|
|
29
|
+
:choices,
|
|
30
|
+
:expires_at,
|
|
31
|
+
:metadata
|
|
32
|
+
)
|
|
33
|
+
Capabilities = Data.define(:inbound, :outbound, :streaming, :limits)
|
|
34
|
+
ExecutionProfile = Data.define(
|
|
35
|
+
:id,
|
|
36
|
+
:tool_mode,
|
|
37
|
+
:allowed_tools,
|
|
38
|
+
:disabled_tools,
|
|
39
|
+
:plugin_commands,
|
|
40
|
+
:approval_mode,
|
|
41
|
+
:memory,
|
|
42
|
+
:attachments,
|
|
43
|
+
:workspace_mode,
|
|
44
|
+
:prompt_context
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
TOOL_MODES = %i[all none allowlist].freeze
|
|
48
|
+
APPROVAL_MODES = %i[default ask deny].freeze
|
|
49
|
+
MEMORY_MODES = %i[default none].freeze
|
|
50
|
+
WORKSPACE_MODES = %i[session fixed].freeze
|
|
51
|
+
|
|
52
|
+
module_function
|
|
53
|
+
|
|
54
|
+
def actor(id:, display_name: nil, metadata: {})
|
|
55
|
+
Actor.new(
|
|
56
|
+
id: required_string(id, "actor id"),
|
|
57
|
+
display_name: optional_string(display_name),
|
|
58
|
+
metadata: frozen_copy(metadata)
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def attachment(name: nil, mime_type:, data: nil, url: nil, metadata: {})
|
|
63
|
+
raise ArgumentError, "attachment requires data or url" if data.nil? && url.nil?
|
|
64
|
+
raise ArgumentError, "attachment cannot contain both data and url" unless data.nil? || url.nil?
|
|
65
|
+
|
|
66
|
+
Attachment.new(
|
|
67
|
+
name: optional_string(name),
|
|
68
|
+
mime_type: required_string(mime_type, "attachment MIME type"),
|
|
69
|
+
data: data&.dup&.freeze,
|
|
70
|
+
url: url && required_string(url, "attachment URL"),
|
|
71
|
+
metadata: frozen_copy(metadata)
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def conversation_key(transport_id:, external_id:)
|
|
76
|
+
ConversationKey.new(
|
|
77
|
+
transport_id: required_string(transport_id, "transport id"),
|
|
78
|
+
external_id: required_string(external_id, "external conversation id")
|
|
79
|
+
)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def inbound_message(conversation:, message_id:, actor:, text: "", attachments: [], reply_context: {}, idempotency_key:, metadata: {})
|
|
83
|
+
unless conversation.is_a?(ConversationKey)
|
|
84
|
+
raise ArgumentError, "conversation must be a Transport::ConversationKey"
|
|
85
|
+
end
|
|
86
|
+
unless actor.is_a?(Actor)
|
|
87
|
+
raise ArgumentError, "actor must be a Transport::Actor"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
InboundMessage.new(
|
|
91
|
+
conversation: conversation,
|
|
92
|
+
message_id: required_string(message_id, "message id"),
|
|
93
|
+
actor: actor,
|
|
94
|
+
text: text.to_s.freeze,
|
|
95
|
+
attachments: frozen_copy(Array(attachments)),
|
|
96
|
+
reply_context: frozen_copy(reply_context),
|
|
97
|
+
idempotency_key: required_string(idempotency_key, "idempotency key"),
|
|
98
|
+
metadata: frozen_copy(metadata)
|
|
99
|
+
)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def turn_event(type:, session_id:, turn_id:, sequence:, payload: {})
|
|
103
|
+
TurnEvent.new(
|
|
104
|
+
type: required_string(type, "turn event type"),
|
|
105
|
+
session_id: required_string(session_id, "session id"),
|
|
106
|
+
turn_id: required_string(turn_id, "turn id"),
|
|
107
|
+
sequence: Integer(sequence),
|
|
108
|
+
payload: frozen_copy(payload)
|
|
109
|
+
)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def interaction_request(id:, session_id:, turn_id:, kind:, prompt:, choices: [], expires_at: nil, metadata: {})
|
|
113
|
+
InteractionRequest.new(
|
|
114
|
+
id: required_string(id, "interaction id"),
|
|
115
|
+
session_id: required_string(session_id, "session id"),
|
|
116
|
+
turn_id: required_string(turn_id, "turn id"),
|
|
117
|
+
kind: required_string(kind, "interaction kind"),
|
|
118
|
+
prompt: required_string(prompt, "interaction prompt"),
|
|
119
|
+
choices: frozen_copy(Array(choices)),
|
|
120
|
+
expires_at: expires_at,
|
|
121
|
+
metadata: frozen_copy(metadata)
|
|
122
|
+
)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def execution_profile(
|
|
126
|
+
id:,
|
|
127
|
+
tool_mode: :all,
|
|
128
|
+
allowed_tools: nil,
|
|
129
|
+
disabled_tools: nil,
|
|
130
|
+
plugin_commands: true,
|
|
131
|
+
approval_mode: :default,
|
|
132
|
+
memory: :default,
|
|
133
|
+
attachments: true,
|
|
134
|
+
workspace_mode: :session,
|
|
135
|
+
prompt_context: nil
|
|
136
|
+
)
|
|
137
|
+
id = required_string(id, "execution profile id")
|
|
138
|
+
tool_mode = tool_mode.to_sym
|
|
139
|
+
approval_mode = approval_mode.to_sym
|
|
140
|
+
memory = memory.to_sym
|
|
141
|
+
workspace_mode = workspace_mode.to_sym
|
|
142
|
+
raise ArgumentError, "unsupported tool mode: #{tool_mode}" unless TOOL_MODES.include?(tool_mode)
|
|
143
|
+
raise ArgumentError, "unsupported approval mode: #{approval_mode}" unless APPROVAL_MODES.include?(approval_mode)
|
|
144
|
+
raise ArgumentError, "unsupported memory mode: #{memory}" unless MEMORY_MODES.include?(memory)
|
|
145
|
+
raise ArgumentError, "unsupported workspace mode: #{workspace_mode}" unless WORKSPACE_MODES.include?(workspace_mode)
|
|
146
|
+
raise ArgumentError, "allowlist tool mode requires allowed_tools" if tool_mode == :allowlist && Array(allowed_tools).empty?
|
|
147
|
+
raise ArgumentError, "none tool mode cannot define allowed_tools" if tool_mode == :none && allowed_tools
|
|
148
|
+
raise ArgumentError, "allowed_tools and disabled_tools cannot both be set" if allowed_tools && disabled_tools
|
|
149
|
+
|
|
150
|
+
ExecutionProfile.new(
|
|
151
|
+
id: id,
|
|
152
|
+
tool_mode: tool_mode,
|
|
153
|
+
allowed_tools: frozen_symbols(allowed_tools),
|
|
154
|
+
disabled_tools: frozen_symbols(disabled_tools),
|
|
155
|
+
plugin_commands: !!plugin_commands,
|
|
156
|
+
approval_mode: approval_mode,
|
|
157
|
+
memory: memory,
|
|
158
|
+
attachments: !!attachments,
|
|
159
|
+
workspace_mode: workspace_mode,
|
|
160
|
+
prompt_context: prompt_context.nil? ? nil : prompt_context.to_s.freeze
|
|
161
|
+
)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def capabilities(inbound: [], outbound: [], streaming: :none, limits: {})
|
|
165
|
+
streaming = streaming.to_sym
|
|
166
|
+
raise ArgumentError, "unsupported streaming mode: #{streaming}" unless STREAMING_MODES.include?(streaming)
|
|
167
|
+
|
|
168
|
+
Capabilities.new(
|
|
169
|
+
inbound: frozen_symbols(inbound),
|
|
170
|
+
outbound: frozen_symbols(outbound),
|
|
171
|
+
streaming: streaming,
|
|
172
|
+
limits: frozen_copy(limits)
|
|
173
|
+
)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def required_string(value, name)
|
|
177
|
+
value = value.to_s
|
|
178
|
+
raise ArgumentError, "#{name} is required" if value.empty?
|
|
179
|
+
|
|
180
|
+
value.freeze
|
|
181
|
+
end
|
|
182
|
+
private_class_method :required_string
|
|
183
|
+
|
|
184
|
+
def optional_string(value)
|
|
185
|
+
value.nil? ? nil : value.to_s.freeze
|
|
186
|
+
end
|
|
187
|
+
private_class_method :optional_string
|
|
188
|
+
|
|
189
|
+
def frozen_symbols(values)
|
|
190
|
+
Array(values).map { |value| value.to_sym }.uniq.freeze
|
|
191
|
+
end
|
|
192
|
+
private_class_method :frozen_symbols
|
|
193
|
+
|
|
194
|
+
def frozen_copy(value)
|
|
195
|
+
DeepCopy.freeze(DeepCopy.dup(value))
|
|
196
|
+
end
|
|
197
|
+
private_class_method :frozen_copy
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
require_relative "transport/store"
|
|
202
|
+
require_relative "transport/host"
|
|
203
|
+
require_relative "transport/manager"
|
|
204
|
+
require_relative "transport/gateway"
|
data/lib/kward/version.rb
CHANGED
|
@@ -8,10 +8,27 @@ module Kward
|
|
|
8
8
|
module WorkspaceFactory
|
|
9
9
|
module_function
|
|
10
10
|
|
|
11
|
-
def build(root:, guardrails: true, config: ConfigFiles.read_config)
|
|
11
|
+
def build(root:, guardrails: true, config: ConfigFiles.read_config, strict: false)
|
|
12
12
|
policy = ConfigFiles.sandbox_policy(root, config)
|
|
13
|
+
policy = strict_policy(root, policy) if strict
|
|
13
14
|
runner = Sandbox::RunnerFactory.build(policy)
|
|
14
|
-
|
|
15
|
+
if strict && !runner.capabilities.filesystem_enforced?
|
|
16
|
+
reason = runner.capabilities.reason || "filesystem enforcement is unavailable"
|
|
17
|
+
raise Sandbox::UnavailableError, "Strict workspace sandbox is unavailable: #{reason}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
Workspace.new(root:, guardrails: strict ? true : guardrails, command_runner: runner)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def strict_policy(root, configured_policy)
|
|
24
|
+
Sandbox::Policy.new(
|
|
25
|
+
mode: "workspace_write",
|
|
26
|
+
network: configured_policy.network,
|
|
27
|
+
workspace_root: root,
|
|
28
|
+
writable_roots: [],
|
|
29
|
+
protect_git_metadata: configured_policy.protect_git_metadata?
|
|
30
|
+
)
|
|
15
31
|
end
|
|
32
|
+
private_class_method :strict_policy
|
|
16
33
|
end
|
|
17
34
|
end
|
|
@@ -4,6 +4,7 @@ const guideLinks = {
|
|
|
4
4
|
'doc/usage.md': 'file.usage.html',
|
|
5
5
|
'doc/configuration.md': 'file.configuration.html',
|
|
6
6
|
'doc/authentication.md': 'file.authentication.html',
|
|
7
|
+
'doc/providers.md': 'file.providers.html',
|
|
7
8
|
'doc/sandboxing.md': 'file.sandboxing.html',
|
|
8
9
|
'doc/troubleshooting.md': 'file.troubleshooting.html',
|
|
9
10
|
'doc/session-management.md': 'file.session-management.html',
|
|
@@ -19,6 +20,7 @@ const guideLinks = {
|
|
|
19
20
|
'doc/code-search.md': 'file.code-search.html',
|
|
20
21
|
'doc/context-tools.md': 'file.context-tools.html',
|
|
21
22
|
'doc/rpc.md': 'file.rpc.html',
|
|
23
|
+
'doc/transports.md': 'file.transports.html',
|
|
22
24
|
'doc/releasing.md': 'file.releasing.html'
|
|
23
25
|
}
|
|
24
26
|
|
|
@@ -5,10 +5,12 @@ module KwardDocsNavigationData
|
|
|
5
5
|
[
|
|
6
6
|
"Start here",
|
|
7
7
|
[
|
|
8
|
+
["Overview", "file.README.html"],
|
|
8
9
|
["Getting started", "file.getting-started.html"],
|
|
9
10
|
["Usage", "file.usage.html"],
|
|
10
11
|
["Configuration", "file.configuration.html"],
|
|
11
12
|
["Authentication", "file.authentication.html"],
|
|
13
|
+
["Model providers", "file.providers.html"],
|
|
12
14
|
["Security and trust", "file.security.html"],
|
|
13
15
|
["Troubleshooting", "file.troubleshooting.html"]
|
|
14
16
|
]
|
|
@@ -22,8 +24,8 @@ module KwardDocsNavigationData
|
|
|
22
24
|
["Memory", "file.memory.html"],
|
|
23
25
|
["Personas", "file.personas.html"],
|
|
24
26
|
["Skills", "file.skills.html"],
|
|
25
|
-
["
|
|
26
|
-
["
|
|
27
|
+
["Prompt templates", "file.prompt-templates.html"],
|
|
28
|
+
["Pan mode", "file.pan.html"]
|
|
27
29
|
]
|
|
28
30
|
],
|
|
29
31
|
[
|
|
@@ -52,7 +54,10 @@ module KwardDocsNavigationData
|
|
|
52
54
|
"Integrate",
|
|
53
55
|
[
|
|
54
56
|
["MCP servers", "file.mcp.html"],
|
|
57
|
+
["Local models", "file.local-models.html"],
|
|
55
58
|
["RPC protocol", "file.rpc.html"],
|
|
59
|
+
["Transports", "file.transports.html"],
|
|
60
|
+
["Telegram transport", "file.telegram.html"],
|
|
56
61
|
["Releasing", "file.releasing.html"]
|
|
57
62
|
]
|
|
58
63
|
],
|
|
@@ -29,6 +29,7 @@ module KwardDocsNavigation
|
|
|
29
29
|
"doc/usage.md" => "file.usage.html",
|
|
30
30
|
"doc/configuration.md" => "file.configuration.html",
|
|
31
31
|
"doc/authentication.md" => "file.authentication.html",
|
|
32
|
+
"doc/providers.md" => "file.providers.html",
|
|
32
33
|
"doc/permissions.md" => "file.permissions.html",
|
|
33
34
|
"doc/sandboxing.md" => "file.sandboxing.html",
|
|
34
35
|
"doc/troubleshooting.md" => "file.troubleshooting.html",
|
|
@@ -41,6 +42,7 @@ module KwardDocsNavigation
|
|
|
41
42
|
"doc/memory.md" => "file.memory.html",
|
|
42
43
|
"doc/personas.md" => "file.personas.html",
|
|
43
44
|
"doc/skills.md" => "file.skills.html",
|
|
45
|
+
"doc/local-models.md" => "file.local-models.html",
|
|
44
46
|
"doc/extensibility.md" => "file.extensibility.html",
|
|
45
47
|
"doc/plugins.md" => "file.plugins.html",
|
|
46
48
|
"doc/lifecycle-hooks.md" => "file.lifecycle-hooks.html",
|
|
@@ -51,6 +53,8 @@ module KwardDocsNavigation
|
|
|
51
53
|
"doc/code-search.md" => "file.code-search.html",
|
|
52
54
|
"doc/context-tools.md" => "file.context-tools.html",
|
|
53
55
|
"doc/rpc.md" => "file.rpc.html",
|
|
56
|
+
"doc/transports.md" => "file.transports.html",
|
|
57
|
+
"doc/telegram.md" => "file.telegram.html",
|
|
54
58
|
"doc/releasing.md" => "file.releasing.html"
|
|
55
59
|
}.freeze
|
|
56
60
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kward
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.80.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kai Wood
|
|
@@ -146,6 +146,8 @@ files:
|
|
|
146
146
|
- doc/permissions.md
|
|
147
147
|
- doc/personas.md
|
|
148
148
|
- doc/plugins.md
|
|
149
|
+
- doc/prompt-templates.md
|
|
150
|
+
- doc/providers.md
|
|
149
151
|
- doc/releasing.md
|
|
150
152
|
- doc/rpc.md
|
|
151
153
|
- doc/sandboxing.md
|
|
@@ -154,17 +156,24 @@ files:
|
|
|
154
156
|
- doc/shell.md
|
|
155
157
|
- doc/skills.md
|
|
156
158
|
- doc/tabs.md
|
|
159
|
+
- doc/telegram.md
|
|
160
|
+
- doc/transports.md
|
|
157
161
|
- doc/troubleshooting.md
|
|
158
162
|
- doc/usage.md
|
|
159
163
|
- doc/web-search.md
|
|
160
164
|
- doc/workspace-tools.md
|
|
161
165
|
- examples/plugins/space_invaders.rb
|
|
166
|
+
- examples/plugins/stardate_footer.rb
|
|
167
|
+
- examples/plugins/telegram/plugin.rb
|
|
168
|
+
- examples/plugins/telegram/telegram_api.rb
|
|
169
|
+
- examples/plugins/telegram/telegram_transport.rb
|
|
162
170
|
- exe/kward
|
|
163
171
|
- kward.gemspec
|
|
164
172
|
- lib/kward.rb
|
|
165
173
|
- lib/kward/agent.rb
|
|
166
174
|
- lib/kward/ansi.rb
|
|
167
175
|
- lib/kward/auth/anthropic_oauth.rb
|
|
176
|
+
- lib/kward/auth/api_key_store.rb
|
|
168
177
|
- lib/kward/auth/file.rb
|
|
169
178
|
- lib/kward/auth/github_oauth.rb
|
|
170
179
|
- lib/kward/auth/oauth_helpers.rb
|
|
@@ -192,6 +201,8 @@ files:
|
|
|
192
201
|
- lib/kward/cli/sysprompt.rb
|
|
193
202
|
- lib/kward/cli/tabs.rb
|
|
194
203
|
- lib/kward/cli/tool_summaries.rb
|
|
204
|
+
- lib/kward/cli/transports.rb
|
|
205
|
+
- lib/kward/cli/worktrees.rb
|
|
195
206
|
- lib/kward/cli_transcript_formatter.rb
|
|
196
207
|
- lib/kward/clipboard.rb
|
|
197
208
|
- lib/kward/compaction/file_operation_tracker.rb
|
|
@@ -207,6 +218,7 @@ files:
|
|
|
207
218
|
- lib/kward/events.rb
|
|
208
219
|
- lib/kward/export_path.rb
|
|
209
220
|
- lib/kward/frontmatter.rb
|
|
221
|
+
- lib/kward/git_worktree_manager.rb
|
|
210
222
|
- lib/kward/hooks.rb
|
|
211
223
|
- lib/kward/hooks/audit_log.rb
|
|
212
224
|
- lib/kward/hooks/catalog.rb
|
|
@@ -230,6 +242,8 @@ files:
|
|
|
230
242
|
- lib/kward/memory/turn_context.rb
|
|
231
243
|
- lib/kward/message_access.rb
|
|
232
244
|
- lib/kward/message_text.rb
|
|
245
|
+
- lib/kward/model/azure_openai_config.rb
|
|
246
|
+
- lib/kward/model/catalog.rb
|
|
233
247
|
- lib/kward/model/chat_invocation.rb
|
|
234
248
|
- lib/kward/model/client.rb
|
|
235
249
|
- lib/kward/model/context_overflow.rb
|
|
@@ -237,7 +251,9 @@ files:
|
|
|
237
251
|
- lib/kward/model/copilot_models.rb
|
|
238
252
|
- lib/kward/model/model_info.rb
|
|
239
253
|
- lib/kward/model/payloads.rb
|
|
254
|
+
- lib/kward/model/provider_catalog.rb
|
|
240
255
|
- lib/kward/model/retry_message.rb
|
|
256
|
+
- lib/kward/model/sources.rb
|
|
241
257
|
- lib/kward/model/stream_parser.rb
|
|
242
258
|
- lib/kward/openrouter_model_cache.rb
|
|
243
259
|
- lib/kward/pan/index.html.erb
|
|
@@ -245,6 +261,7 @@ files:
|
|
|
245
261
|
- lib/kward/pan/server.rb
|
|
246
262
|
- lib/kward/path_guard.rb
|
|
247
263
|
- lib/kward/permissions/policy.rb
|
|
264
|
+
- lib/kward/plugin_chat_runtime.rb
|
|
248
265
|
- lib/kward/plugin_registry.rb
|
|
249
266
|
- lib/kward/private_file.rb
|
|
250
267
|
- lib/kward/project_files.rb
|
|
@@ -354,6 +371,7 @@ files:
|
|
|
354
371
|
- lib/kward/tools/edit_file.rb
|
|
355
372
|
- lib/kward/tools/fetch_content.rb
|
|
356
373
|
- lib/kward/tools/fetch_raw.rb
|
|
374
|
+
- lib/kward/tools/git_commit.rb
|
|
357
375
|
- lib/kward/tools/list_directory.rb
|
|
358
376
|
- lib/kward/tools/mcp_tool.rb
|
|
359
377
|
- lib/kward/tools/read_file.rb
|
|
@@ -369,6 +387,13 @@ files:
|
|
|
369
387
|
- lib/kward/tools/web_search.rb
|
|
370
388
|
- lib/kward/tools/write_file.rb
|
|
371
389
|
- lib/kward/transcript_export.rb
|
|
390
|
+
- lib/kward/transport.rb
|
|
391
|
+
- lib/kward/transport/gateway.rb
|
|
392
|
+
- lib/kward/transport/host.rb
|
|
393
|
+
- lib/kward/transport/manager.rb
|
|
394
|
+
- lib/kward/transport/plugin_chat_gateway.rb
|
|
395
|
+
- lib/kward/transport/runtime.rb
|
|
396
|
+
- lib/kward/transport/store.rb
|
|
372
397
|
- lib/kward/update_check.rb
|
|
373
398
|
- lib/kward/version.rb
|
|
374
399
|
- lib/kward/workspace.rb
|