@pi-in-go/pigpen-ahp 0.1.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.
- package/CREDITS.md +27 -0
- package/LICENSE +23 -0
- package/README.md +148 -0
- package/extensions/ahp/access_options_test.go +116 -0
- package/extensions/ahp/extension.go +54 -0
- package/extensions/ahp/go.mod +12 -0
- package/extensions/ahp/go.sum +2 -0
- package/extensions/ahp/go.work +8 -0
- package/extensions/ahp/internal/channels/chat.go +110 -0
- package/extensions/ahp/internal/channels/root.go +32 -0
- package/extensions/ahp/internal/channels/session.go +128 -0
- package/extensions/ahp/internal/channels/session_test.go +108 -0
- package/extensions/ahp/internal/compose/compose.go +153 -0
- package/extensions/ahp/internal/compose/compose_test.go +120 -0
- package/extensions/ahp/internal/gaps/gaps_test.go +88 -0
- package/extensions/ahp/internal/host/handshake_test.go +206 -0
- package/extensions/ahp/internal/host/host.go +1305 -0
- package/extensions/ahp/internal/host/reconnect_test.go +197 -0
- package/extensions/ahp/internal/host/replaywindow_test.go +31 -0
- package/extensions/ahp/internal/host/schema_test.go +79 -0
- package/extensions/ahp/internal/host/store.go +291 -0
- package/extensions/ahp/internal/host/store_update_test.go +85 -0
- package/extensions/ahp/internal/host/subscriptions_test.go +160 -0
- package/extensions/ahp/internal/host/surface_test.go +95 -0
- package/extensions/ahp/internal/host/workarounds.go +458 -0
- package/extensions/ahp/internal/host/workarounds_test.go +96 -0
- package/extensions/ahp/internal/live/live.go +328 -0
- package/extensions/ahp/internal/live/live_test.go +91 -0
- package/extensions/ahp/internal/live/store.go +113 -0
- package/extensions/ahp/internal/mapper/activity.go +199 -0
- package/extensions/ahp/internal/mapper/activity_test.go +188 -0
- package/extensions/ahp/internal/mapper/bench_test.go +38 -0
- package/extensions/ahp/internal/mapper/event_mapper_test.go +380 -0
- package/extensions/ahp/internal/mapper/fixtures_test.go +387 -0
- package/extensions/ahp/internal/mapper/helpers_test.go +251 -0
- package/extensions/ahp/internal/mapper/js.go +131 -0
- package/extensions/ahp/internal/mapper/mapper.go +600 -0
- package/extensions/ahp/internal/mapper/message_input_test.go +196 -0
- package/extensions/ahp/internal/mapper/messageinput.go +272 -0
- package/extensions/ahp/internal/mapper/schema_helpers_test.go +5 -0
- package/extensions/ahp/internal/mapper/testdata/LICENSE-pi-ahp +21 -0
- package/extensions/ahp/internal/mapper/testdata/fixtures/abort.json +711 -0
- package/extensions/ahp/internal/mapper/testdata/fixtures/bash-long-output.json +1312 -0
- package/extensions/ahp/internal/mapper/testdata/fixtures/compaction.json +6285 -0
- package/extensions/ahp/internal/mapper/testdata/fixtures/parallel-tools.json +2622 -0
- package/extensions/ahp/internal/mapper/testdata/fixtures/plain-text.json +1856 -0
- package/extensions/ahp/internal/mapper/testdata/fixtures/single-tool.json +1128 -0
- package/extensions/ahp/internal/mapper/testdata/fixtures/steering.json +3926 -0
- package/extensions/ahp/internal/mapper/testdata/fixtures/tool-bash.json +2372 -0
- package/extensions/ahp/internal/mapper/testdata/fixtures/tool-edit.json +3983 -0
- package/extensions/ahp/internal/mapper/testdata/fixtures/tool-error.json +2850 -0
- package/extensions/ahp/internal/mapper/testdata/fixtures/tool-find.json +1162 -0
- package/extensions/ahp/internal/mapper/testdata/fixtures/tool-grep.json +2783 -0
- package/extensions/ahp/internal/mapper/testdata/fixtures/tool-loop.json +1693 -0
- package/extensions/ahp/internal/mapper/testdata/fixtures/tool-ls.json +2427 -0
- package/extensions/ahp/internal/mapper/testdata/fixtures/tool-write.json +1271 -0
- package/extensions/ahp/internal/mapper/title.go +34 -0
- package/extensions/ahp/internal/mapper/usermsg.go +87 -0
- package/extensions/ahp/internal/pi/activeturn_test.go +296 -0
- package/extensions/ahp/internal/pi/backend.go +64 -0
- package/extensions/ahp/internal/pi/catalogue.go +370 -0
- package/extensions/ahp/internal/pi/catalogue_test.go +271 -0
- package/extensions/ahp/internal/pi/chatdriver.go +533 -0
- package/extensions/ahp/internal/pi/chatdriver_test.go +416 -0
- package/extensions/ahp/internal/pi/clientactions.go +212 -0
- package/extensions/ahp/internal/pi/clientactions_test.go +273 -0
- package/extensions/ahp/internal/pi/completions.go +230 -0
- package/extensions/ahp/internal/pi/completions_test.go +272 -0
- package/extensions/ahp/internal/pi/config_test.go +269 -0
- package/extensions/ahp/internal/pi/deletesession.go +69 -0
- package/extensions/ahp/internal/pi/deletesession_test.go +153 -0
- package/extensions/ahp/internal/pi/disposal_test.go +362 -0
- package/extensions/ahp/internal/pi/fixture_test.go +308 -0
- package/extensions/ahp/internal/pi/foreign_test.go +122 -0
- package/extensions/ahp/internal/pi/harness_test.go +83 -0
- package/extensions/ahp/internal/pi/history.go +269 -0
- package/extensions/ahp/internal/pi/hydrated_test.go +171 -0
- package/extensions/ahp/internal/pi/hydratedlifecycle_test.go +196 -0
- package/extensions/ahp/internal/pi/hydration_test.go +209 -0
- package/extensions/ahp/internal/pi/hydrator.go +212 -0
- package/extensions/ahp/internal/pi/imageinput.go +224 -0
- package/extensions/ahp/internal/pi/imageinput_test.go +57 -0
- package/extensions/ahp/internal/pi/lifecycle_test.go +381 -0
- package/extensions/ahp/internal/pi/models.go +212 -0
- package/extensions/ahp/internal/pi/models_test.go +128 -0
- package/extensions/ahp/internal/pi/paging.go +95 -0
- package/extensions/ahp/internal/pi/pagingtruncate_test.go +323 -0
- package/extensions/ahp/internal/pi/projecttrust.go +222 -0
- package/extensions/ahp/internal/pi/registry.go +924 -0
- package/extensions/ahp/internal/pi/restart_test.go +185 -0
- package/extensions/ahp/internal/pi/services.go +70 -0
- package/extensions/ahp/internal/pi/sessionconfig.go +64 -0
- package/extensions/ahp/internal/pi/sessionfiles_test.go +74 -0
- package/extensions/ahp/internal/pi/sessionstore.go +83 -0
- package/extensions/ahp/internal/pi/summary_test.go +426 -0
- package/extensions/ahp/internal/pi/workarounds_wire_test.go +208 -0
- package/extensions/ahp/internal/pisession/fromentries_test.go +32 -0
- package/extensions/ahp/internal/pisession/json.go +31 -0
- package/extensions/ahp/internal/pisession/pisession.go +580 -0
- package/extensions/ahp/internal/settings/access.go +63 -0
- package/extensions/ahp/internal/settings/access_test.go +52 -0
- package/extensions/ahp/internal/settings/settings.go +117 -0
- package/extensions/ahp/internal/settings/settings_test.go +104 -0
- package/extensions/ahp/internal/svc/etag_other.go +15 -0
- package/extensions/ahp/internal/svc/etag_unix.go +34 -0
- package/extensions/ahp/internal/svc/glob.go +197 -0
- package/extensions/ahp/internal/svc/mime.go +37 -0
- package/extensions/ahp/internal/svc/paths.go +108 -0
- package/extensions/ahp/internal/svc/pty.go +64 -0
- package/extensions/ahp/internal/svc/pty_darwin.go +42 -0
- package/extensions/ahp/internal/svc/pty_linux.go +36 -0
- package/extensions/ahp/internal/svc/pty_other.go +9 -0
- package/extensions/ahp/internal/svc/pty_test.go +114 -0
- package/extensions/ahp/internal/svc/pty_unix.go +147 -0
- package/extensions/ahp/internal/svc/resource.go +495 -0
- package/extensions/ahp/internal/svc/resource_test.go +501 -0
- package/extensions/ahp/internal/svc/stat_bsd.go +9 -0
- package/extensions/ahp/internal/svc/stat_linux.go +9 -0
- package/extensions/ahp/internal/svc/terminal.go +454 -0
- package/extensions/ahp/internal/svc/terminal_test.go +512 -0
- package/extensions/ahp/internal/svc/watch.go +590 -0
- package/extensions/ahp/internal/svc/watch_test.go +627 -0
- package/extensions/ahp/internal/svc/watchevents_test.go +452 -0
- package/extensions/ahp/internal/svc/watchpolicy.go +81 -0
- package/extensions/ahp/internal/svc/watchpolicy_test.go +151 -0
- package/extensions/ahp/internal/svc/watchracy_test.go +61 -0
- package/extensions/ahp/internal/testkit/schema/LICENSE-agent-host-protocol +21 -0
- package/extensions/ahp/internal/testkit/schema/actions.schema.json +9208 -0
- package/extensions/ahp/internal/testkit/schema/commands.schema.json +10862 -0
- package/extensions/ahp/internal/testkit/schema/errors.schema.json +10928 -0
- package/extensions/ahp/internal/testkit/schema/notifications.schema.json +6667 -0
- package/extensions/ahp/internal/testkit/schema/state.schema.json +6392 -0
- package/extensions/ahp/internal/testkit/schema.go +360 -0
- package/extensions/ahp/internal/testkit/testkit.go +367 -0
- package/extensions/ahp/internal/twin/twin.go +27 -0
- package/extensions/ahp/internal/wire/dispatchable.go +106 -0
- package/extensions/ahp/internal/wire/dispatchable_list.go +15 -0
- package/extensions/ahp/internal/wire/helpers_test.go +5 -0
- package/extensions/ahp/internal/wire/wire.go +218 -0
- package/extensions/ahp/internal/wire/wire_test.go +123 -0
- package/extensions/ahp/internal/ws/helpers_test.go +7 -0
- package/extensions/ahp/internal/ws/vscode_test.go +198 -0
- package/extensions/ahp/internal/ws/ws.go +674 -0
- package/extensions/ahp/internal/ws/ws_test.go +324 -0
- package/extensions/ahp/realpig_test.go +589 -0
- package/extensions/ahp/runtime.go +311 -0
- package/extensions/ahp/runtime_test.go +110 -0
- package/extensions/ahp/testdata/upstream-tests.json +3833 -0
- package/extensions/ahp/third_party/agent-host-protocol-go/LICENSE +21 -0
- package/extensions/ahp/third_party/agent-host-protocol-go/NOTICE-PIGPEN.md +18 -0
- package/extensions/ahp/third_party/agent-host-protocol-go/ahp/client.go +1011 -0
- package/extensions/ahp/third_party/agent-host-protocol-go/ahp/error.go +111 -0
- package/extensions/ahp/third_party/agent-host-protocol-go/ahp/multi_host_state_mirror.go +239 -0
- package/extensions/ahp/third_party/agent-host-protocol-go/ahp/reducers.go +1939 -0
- package/extensions/ahp/third_party/agent-host-protocol-go/ahp/transport.go +176 -0
- package/extensions/ahp/third_party/agent-host-protocol-go/ahptypes/actions.generated.go +2447 -0
- package/extensions/ahp/third_party/agent-host-protocol-go/ahptypes/commands.generated.go +1546 -0
- package/extensions/ahp/third_party/agent-host-protocol-go/ahptypes/common.go +205 -0
- package/extensions/ahp/third_party/agent-host-protocol-go/ahptypes/errors.generated.go +65 -0
- package/extensions/ahp/third_party/agent-host-protocol-go/ahptypes/messages.generated.go +138 -0
- package/extensions/ahp/third_party/agent-host-protocol-go/ahptypes/notifications.generated.go +264 -0
- package/extensions/ahp/third_party/agent-host-protocol-go/ahptypes/state.generated.go +6171 -0
- package/extensions/ahp/third_party/agent-host-protocol-go/ahptypes/version.generated.go +31 -0
- package/extensions/ahp/third_party/agent-host-protocol-go/go.mod +3 -0
- package/extensions/ahp/twins_test.go +91 -0
- package/package.json +40 -0
- package/proof/PORT.md +121 -0
- package/proof/mutations.json +152 -0
- package/proof/oracle/LICENSE +21 -0
- package/proof/oracle/README.md +120 -0
- package/proof/oracle/UPSTREAM.md +7 -0
- package/proof/oracle/package.json +68 -0
- package/proof/oracle/src/bin/cli.ts +66 -0
- package/proof/oracle/src/bin/tunnel.ts +128 -0
- package/proof/oracle/src/channels/chat.ts +134 -0
- package/proof/oracle/src/channels/root.ts +39 -0
- package/proof/oracle/src/channels/session.ts +118 -0
- package/proof/oracle/src/channels/terminal.ts +11 -0
- package/proof/oracle/src/core/channels.ts +108 -0
- package/proof/oracle/src/core/client-workarounds.ts +358 -0
- package/proof/oracle/src/core/connection.ts +41 -0
- package/proof/oracle/src/core/host.ts +882 -0
- package/proof/oracle/src/core/sequencer.ts +75 -0
- package/proof/oracle/src/core/state-store.ts +162 -0
- package/proof/oracle/src/core/uri.ts +25 -0
- package/proof/oracle/src/host/direct-settings.ts +110 -0
- package/proof/oracle/src/host/pi-host.ts +220 -0
- package/proof/oracle/src/host/serve.ts +71 -0
- package/proof/oracle/src/host/terminal-service.ts +346 -0
- package/proof/oracle/src/pi/activity.ts +171 -0
- package/proof/oracle/src/pi/changeset-service.ts +646 -0
- package/proof/oracle/src/pi/changeset-uri.ts +64 -0
- package/proof/oracle/src/pi/chat-driver.ts +529 -0
- package/proof/oracle/src/pi/completions.ts +160 -0
- package/proof/oracle/src/pi/delete-session.ts +53 -0
- package/proof/oracle/src/pi/event-mapper.ts +648 -0
- package/proof/oracle/src/pi/git-changes.ts +605 -0
- package/proof/oracle/src/pi/history.ts +305 -0
- package/proof/oracle/src/pi/image-input.ts +56 -0
- package/proof/oracle/src/pi/image-mime.ts +8 -0
- package/proof/oracle/src/pi/in-process-backend.ts +155 -0
- package/proof/oracle/src/pi/message-input.ts +212 -0
- package/proof/oracle/src/pi/models.ts +124 -0
- package/proof/oracle/src/pi/project-trust.ts +66 -0
- package/proof/oracle/src/pi/provider.ts +2 -0
- package/proof/oracle/src/pi/resource-paths.ts +80 -0
- package/proof/oracle/src/pi/resource-service.ts +356 -0
- package/proof/oracle/src/pi/resource-watch-policy.ts +35 -0
- package/proof/oracle/src/pi/resource-watch.ts +357 -0
- package/proof/oracle/src/pi/session-catalogue.ts +318 -0
- package/proof/oracle/src/pi/session-config.ts +84 -0
- package/proof/oracle/src/pi/session-history.ts +75 -0
- package/proof/oracle/src/pi/session-hydrator.ts +219 -0
- package/proof/oracle/src/pi/session-registry.ts +815 -0
- package/proof/oracle/src/pi/session-storage.ts +29 -0
- package/proof/oracle/src/pi/session-title.ts +18 -0
- package/proof/oracle/src/pi/turn-paging.ts +94 -0
- package/proof/oracle/src/pi/user-message.ts +66 -0
- package/proof/oracle/src/protocol/errors.ts +44 -0
- package/proof/oracle/src/protocol/jsonrpc.ts +89 -0
- package/proof/oracle/src/protocol/version.ts +39 -0
- package/proof/oracle/src/transport/websocket.ts +136 -0
- package/proof/oracle/src/tunnel/devtunnel.ts +293 -0
- package/proof/oracle/src/tunnel/discovery.ts +37 -0
- package/proof/oracle/test/active-turn-reconnect.test.ts +274 -0
- package/proof/oracle/test/activity.test.ts +171 -0
- package/proof/oracle/test/changeset-lifecycle.test.serial.ts +303 -0
- package/proof/oracle/test/changeset-uri.test.ts +37 -0
- package/proof/oracle/test/changeset.test.serial.ts +487 -0
- package/proof/oracle/test/chat-driver.test.ts +691 -0
- package/proof/oracle/test/client-actions.test.ts +409 -0
- package/proof/oracle/test/client-workarounds.test.ts +320 -0
- package/proof/oracle/test/completions.test.ts +323 -0
- package/proof/oracle/test/delete-session.test.ts +142 -0
- package/proof/oracle/test/direct-settings.test.ts +81 -0
- package/proof/oracle/test/event-mapper.test.ts +619 -0
- package/proof/oracle/test/fetch-turns.test.ts +207 -0
- package/proof/oracle/test/fixtures/abort.json +711 -0
- package/proof/oracle/test/fixtures/bash-long-output.json +1312 -0
- package/proof/oracle/test/fixtures/compaction.json +6285 -0
- package/proof/oracle/test/fixtures/parallel-tools.json +2622 -0
- package/proof/oracle/test/fixtures/plain-text.json +1856 -0
- package/proof/oracle/test/fixtures/single-tool.json +1128 -0
- package/proof/oracle/test/fixtures/steering.json +3926 -0
- package/proof/oracle/test/fixtures/tool-bash.json +2372 -0
- package/proof/oracle/test/fixtures/tool-edit.json +3983 -0
- package/proof/oracle/test/fixtures/tool-error.json +2850 -0
- package/proof/oracle/test/fixtures/tool-find.json +1162 -0
- package/proof/oracle/test/fixtures/tool-grep.json +2783 -0
- package/proof/oracle/test/fixtures/tool-loop.json +1693 -0
- package/proof/oracle/test/fixtures/tool-ls.json +2427 -0
- package/proof/oracle/test/fixtures/tool-write.json +1271 -0
- package/proof/oracle/test/handshake.test.ts +275 -0
- package/proof/oracle/test/harness.ts +153 -0
- package/proof/oracle/test/hydrated-session-lifecycle.test.ts +211 -0
- package/proof/oracle/test/image-input.test.ts +28 -0
- package/proof/oracle/test/image-session.test.ts +69 -0
- package/proof/oracle/test/live-turn.test.ts +304 -0
- package/proof/oracle/test/mapper-fixtures.test.ts +293 -0
- package/proof/oracle/test/message-input.test.ts +181 -0
- package/proof/oracle/test/model-discovery.test.ts +170 -0
- package/proof/oracle/test/models.test.ts +109 -0
- package/proof/oracle/test/pi-host.test.ts +91 -0
- package/proof/oracle/test/pi-replay.test.ts +141 -0
- package/proof/oracle/test/project-trust.test.ts +125 -0
- package/proof/oracle/test/protocol-surface.test.ts +121 -0
- package/proof/oracle/test/pty.test.ts +100 -0
- package/proof/oracle/test/reconnect.test.ts +389 -0
- package/proof/oracle/test/resource-watch-policy.test.ts +78 -0
- package/proof/oracle/test/resource-watch.test.serial.ts +536 -0
- package/proof/oracle/test/resource.test.ts +499 -0
- package/proof/oracle/test/schema.test.ts +88 -0
- package/proof/oracle/test/session-catalogue.test.ts +314 -0
- package/proof/oracle/test/session-config.test.ts +224 -0
- package/proof/oracle/test/session-disposal.test.ts +364 -0
- package/proof/oracle/test/session-hydration.test.ts +194 -0
- package/proof/oracle/test/session-lifecycle.test.ts +416 -0
- package/proof/oracle/test/session-storage.test.serial.ts +32 -0
- package/proof/oracle/test/session-summary.test.ts +418 -0
- package/proof/oracle/test/subscriptions.test.ts +198 -0
- package/proof/oracle/test/support/assertions.ts +32 -0
- package/proof/oracle/test/support/async.ts +26 -0
- package/proof/oracle/test/support/hydrated-session.ts +205 -0
- package/proof/oracle/test/support/images.ts +8 -0
- package/proof/oracle/test/support/recorded-fixtures.ts +60 -0
- package/proof/oracle/test/support/recorded-scenarios.ts +145 -0
- package/proof/oracle/test/support/replay.ts +194 -0
- package/proof/oracle/test/support/schema.ts +221 -0
- package/proof/oracle/test/support/session-files.ts +12 -0
- package/proof/oracle/test/support/session-storage.ts +22 -0
- package/proof/oracle/test/support/upstream.ts +25 -0
- package/proof/oracle/test/support/watch-events.ts +108 -0
- package/proof/oracle/test/terminal-service.test.ts +495 -0
- package/proof/oracle/test/truncate.test.ts +230 -0
- package/proof/oracle/test/tunnel.test.ts +258 -0
- package/proof/oracle/test/upstream-workarounds.test.ts +37 -0
- package/proof/oracle/test/uri.test.ts +24 -0
- package/proof/oracle/test/watch-events.test.ts +118 -0
- package/proof/oracle/tsconfig.json +32 -0
- package/proof/proof-piglet/piglet.yaml +18 -0
- package/proof/tools/fakellm/go.mod +3 -0
- package/proof/tools/fakellm/main.go +109 -0
- package/proof/tools/gen-dispatchable.py +30 -0
- package/provenance.json +28 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
// Package testkit is the test harness shared by the twin tests: an in-memory JSON-RPC
|
|
2
|
+
// client for a host.Host, the counterpart of upstream test/harness.ts (which drives a
|
|
3
|
+
// live host with the Microsoft AHP client). It is test support only and is not imported by
|
|
4
|
+
// the extension.
|
|
5
|
+
package testkit
|
|
6
|
+
|
|
7
|
+
import (
|
|
8
|
+
"bytes"
|
|
9
|
+
"encoding/json"
|
|
10
|
+
"errors"
|
|
11
|
+
"fmt"
|
|
12
|
+
"sync"
|
|
13
|
+
"testing"
|
|
14
|
+
"time"
|
|
15
|
+
|
|
16
|
+
"github.com/microsoft/agent-host-protocol/clients/go/ahptypes"
|
|
17
|
+
|
|
18
|
+
"github.com/MichaelKinsy/pigpen/ahp/internal/host"
|
|
19
|
+
"github.com/MichaelKinsy/pigpen/ahp/internal/wire"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
// Timeout is how long a request or an awaited message may take before the test fails.
|
|
23
|
+
const Timeout = 2 * time.Second
|
|
24
|
+
|
|
25
|
+
// RPCError is a JSON-RPC error response.
|
|
26
|
+
type RPCError struct {
|
|
27
|
+
Code int `json:"code"`
|
|
28
|
+
Message string `json:"message"`
|
|
29
|
+
Data json.RawMessage `json:"data,omitempty"`
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
func (e *RPCError) Error() string { return fmt.Sprintf("rpc error %d: %s", e.Code, e.Message) }
|
|
33
|
+
|
|
34
|
+
// Notification is a server notification received by a client.
|
|
35
|
+
type Notification struct {
|
|
36
|
+
Method string
|
|
37
|
+
Params json.RawMessage
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Envelope decodes an "action" notification.
|
|
41
|
+
func (n Notification) Envelope() (ahptypes.ActionEnvelope, bool) {
|
|
42
|
+
var env ahptypes.ActionEnvelope
|
|
43
|
+
if n.Method != "action" || json.Unmarshal(n.Params, &env) != nil {
|
|
44
|
+
return env, false
|
|
45
|
+
}
|
|
46
|
+
return env, true
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
type memTransport struct{ c *Client }
|
|
50
|
+
|
|
51
|
+
func (m *memTransport) Send(frame []byte) error {
|
|
52
|
+
m.c.deliver(append([]byte(nil), frame...))
|
|
53
|
+
return nil
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
func (m *memTransport) Close() error {
|
|
57
|
+
m.c.mu.Lock()
|
|
58
|
+
m.c.serverClosed = true
|
|
59
|
+
m.c.cond.Broadcast()
|
|
60
|
+
m.c.mu.Unlock()
|
|
61
|
+
return nil
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Client is an in-memory AHP client attached to a host.
|
|
65
|
+
type Client struct {
|
|
66
|
+
t *testing.T
|
|
67
|
+
conn *host.Conn
|
|
68
|
+
|
|
69
|
+
mu sync.Mutex
|
|
70
|
+
cond *sync.Cond
|
|
71
|
+
nextID uint64
|
|
72
|
+
clientSeq int64
|
|
73
|
+
pending map[uint64]chan response
|
|
74
|
+
notes []Notification
|
|
75
|
+
cursor int
|
|
76
|
+
serverClosed bool
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
type response struct {
|
|
80
|
+
result json.RawMessage
|
|
81
|
+
err *RPCError
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Connect attaches a new client to the host.
|
|
85
|
+
func Connect(t *testing.T, h *host.Host) *Client {
|
|
86
|
+
t.Helper()
|
|
87
|
+
c := &Client{t: t, pending: map[uint64]chan response{}}
|
|
88
|
+
c.cond = sync.NewCond(&c.mu)
|
|
89
|
+
c.conn = h.Accept(&memTransport{c: c})
|
|
90
|
+
return c
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
func (c *Client) deliver(frame []byte) {
|
|
94
|
+
var msg struct {
|
|
95
|
+
ID *uint64 `json:"id"`
|
|
96
|
+
Method string `json:"method"`
|
|
97
|
+
Params json.RawMessage `json:"params"`
|
|
98
|
+
Result json.RawMessage `json:"result"`
|
|
99
|
+
Error *RPCError `json:"error"`
|
|
100
|
+
}
|
|
101
|
+
if err := json.Unmarshal(frame, &msg); err != nil {
|
|
102
|
+
c.t.Errorf("host sent an unparseable frame: %v: %s", err, frame)
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
c.mu.Lock()
|
|
106
|
+
defer c.mu.Unlock()
|
|
107
|
+
if msg.Method == "" && msg.ID != nil {
|
|
108
|
+
if ch, ok := c.pending[*msg.ID]; ok {
|
|
109
|
+
delete(c.pending, *msg.ID)
|
|
110
|
+
ch <- response{result: msg.Result, err: msg.Error}
|
|
111
|
+
}
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
c.notes = append(c.notes, Notification{Method: msg.Method, Params: msg.Params})
|
|
115
|
+
c.cond.Broadcast()
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Request sends a request and waits for its response.
|
|
119
|
+
func (c *Client) Request(method string, params any) (json.RawMessage, *RPCError) {
|
|
120
|
+
c.t.Helper()
|
|
121
|
+
ch := make(chan response, 1)
|
|
122
|
+
c.mu.Lock()
|
|
123
|
+
c.nextID++
|
|
124
|
+
id := c.nextID
|
|
125
|
+
c.pending[id] = ch
|
|
126
|
+
c.mu.Unlock()
|
|
127
|
+
frame, err := json.Marshal(map[string]any{"jsonrpc": "2.0", "id": id, "method": method, "params": params})
|
|
128
|
+
if err != nil {
|
|
129
|
+
c.t.Fatalf("marshal %s: %v", method, err)
|
|
130
|
+
}
|
|
131
|
+
c.conn.Receive(frame)
|
|
132
|
+
select {
|
|
133
|
+
case r := <-ch:
|
|
134
|
+
return r.result, r.err
|
|
135
|
+
case <-time.After(Timeout):
|
|
136
|
+
c.t.Fatalf("%s: no response within %v", method, Timeout)
|
|
137
|
+
return nil, nil
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Must returns a successful result or fails the test.
|
|
142
|
+
func (c *Client) Must(method string, params any) json.RawMessage {
|
|
143
|
+
c.t.Helper()
|
|
144
|
+
result, err := c.Request(method, params)
|
|
145
|
+
if err != nil {
|
|
146
|
+
c.t.Fatalf("%s: %v", method, err)
|
|
147
|
+
}
|
|
148
|
+
return result
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ExpectError requires the request to fail with the given code and returns the error.
|
|
152
|
+
func (c *Client) ExpectError(method string, params any, code int) *RPCError {
|
|
153
|
+
c.t.Helper()
|
|
154
|
+
result, err := c.Request(method, params)
|
|
155
|
+
if err == nil {
|
|
156
|
+
c.t.Fatalf("%s: expected error %d, got result %s", method, code, result)
|
|
157
|
+
}
|
|
158
|
+
if err.Code != code {
|
|
159
|
+
c.t.Fatalf("%s: expected error %d, got %d: %s", method, code, err.Code, err.Message)
|
|
160
|
+
}
|
|
161
|
+
return err
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Notify sends a notification.
|
|
165
|
+
func (c *Client) Notify(method string, params any) {
|
|
166
|
+
c.t.Helper()
|
|
167
|
+
frame, err := json.Marshal(map[string]any{"jsonrpc": "2.0", "method": method, "params": params})
|
|
168
|
+
if err != nil {
|
|
169
|
+
c.t.Fatalf("marshal %s: %v", method, err)
|
|
170
|
+
}
|
|
171
|
+
c.conn.Receive(frame)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Raw sends a frame exactly as given.
|
|
175
|
+
func (c *Client) Raw(frame []byte) { c.conn.Receive(frame) }
|
|
176
|
+
|
|
177
|
+
// Initialize performs the handshake with the supported versions and returns the result.
|
|
178
|
+
func (c *Client) Initialize(clientID string, extra map[string]any) ahptypes.InitializeResult {
|
|
179
|
+
c.t.Helper()
|
|
180
|
+
params := map[string]any{
|
|
181
|
+
"channel": wire.RootChannel, "clientId": clientID,
|
|
182
|
+
"protocolVersions": SupportedVersions(),
|
|
183
|
+
}
|
|
184
|
+
for k, v := range extra {
|
|
185
|
+
params[k] = v
|
|
186
|
+
}
|
|
187
|
+
var result ahptypes.InitializeResult
|
|
188
|
+
c.decode(c.Must("initialize", params), &result)
|
|
189
|
+
return result
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// SupportedVersions is SUPPORTED_PROTOCOL_VERSIONS of the pinned spec.
|
|
193
|
+
func SupportedVersions() []string {
|
|
194
|
+
return []string{"0.9.0", "0.8.0", "0.7.0", "0.6.0", "0.5.2", "0.5.1"}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
func (c *Client) decode(raw json.RawMessage, into any) {
|
|
198
|
+
c.t.Helper()
|
|
199
|
+
if err := json.Unmarshal(raw, into); err != nil {
|
|
200
|
+
c.t.Fatalf("decode %s into %T: %v", raw, into, err)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Decode decodes a result.
|
|
205
|
+
func (c *Client) Decode(raw json.RawMessage, into any) { c.t.Helper(); c.decode(raw, into) }
|
|
206
|
+
|
|
207
|
+
// Subscribe subscribes to a channel and returns the result.
|
|
208
|
+
func (c *Client) Subscribe(channel string) ahptypes.SubscribeResult {
|
|
209
|
+
c.t.Helper()
|
|
210
|
+
var result ahptypes.SubscribeResult
|
|
211
|
+
c.decode(c.Must("subscribe", map[string]any{"channel": channel}), &result)
|
|
212
|
+
return result
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Unsubscribe releases a channel.
|
|
216
|
+
func (c *Client) Unsubscribe(channel string) {
|
|
217
|
+
c.t.Helper()
|
|
218
|
+
c.Notify("unsubscribe", map[string]any{"channel": channel})
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Ping round-trips a ping; with in-order delivery it is a barrier for earlier notifications.
|
|
222
|
+
func (c *Client) Ping() { c.t.Helper(); c.Must("ping", map[string]any{"channel": wire.RootChannel}) }
|
|
223
|
+
|
|
224
|
+
// Dispatch sends a dispatchAction notification and returns its clientSeq. The action is any
|
|
225
|
+
// JSON-marshalable value (a typed action or a map).
|
|
226
|
+
func (c *Client) Dispatch(channel string, action any) int64 {
|
|
227
|
+
c.t.Helper()
|
|
228
|
+
c.mu.Lock()
|
|
229
|
+
c.clientSeq++
|
|
230
|
+
seq := c.clientSeq
|
|
231
|
+
c.mu.Unlock()
|
|
232
|
+
c.Notify("dispatchAction", map[string]any{"channel": channel, "clientSeq": seq, "action": action})
|
|
233
|
+
return seq
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Notifications returns a copy of every notification received so far.
|
|
237
|
+
func (c *Client) Notifications() []Notification {
|
|
238
|
+
c.mu.Lock()
|
|
239
|
+
defer c.mu.Unlock()
|
|
240
|
+
return append([]Notification(nil), c.notes...)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Await waits for the next unconsumed notification matching pred; ok is false on timeout.
|
|
244
|
+
func (c *Client) Await(pred func(Notification) bool, timeout time.Duration) (Notification, bool) {
|
|
245
|
+
deadline := time.Now().Add(timeout)
|
|
246
|
+
stop := time.AfterFunc(timeout, func() { c.mu.Lock(); c.cond.Broadcast(); c.mu.Unlock() })
|
|
247
|
+
defer stop.Stop()
|
|
248
|
+
c.mu.Lock()
|
|
249
|
+
defer c.mu.Unlock()
|
|
250
|
+
for {
|
|
251
|
+
for c.cursor < len(c.notes) {
|
|
252
|
+
n := c.notes[c.cursor]
|
|
253
|
+
c.cursor++
|
|
254
|
+
if pred(n) {
|
|
255
|
+
return n, true
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if !time.Now().Before(deadline) {
|
|
259
|
+
return Notification{}, false
|
|
260
|
+
}
|
|
261
|
+
c.cond.Wait()
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// NextEnvelope waits for the next action envelope on channel (any channel when empty) that
|
|
266
|
+
// matches clientSeq (any when 0).
|
|
267
|
+
func (c *Client) NextEnvelope(channel string, clientSeq int64) ahptypes.ActionEnvelope {
|
|
268
|
+
c.t.Helper()
|
|
269
|
+
n, ok := c.Await(func(n Notification) bool {
|
|
270
|
+
env, isAction := n.Envelope()
|
|
271
|
+
if !isAction {
|
|
272
|
+
return false
|
|
273
|
+
}
|
|
274
|
+
return (channel == "" || env.Channel == channel) && (clientSeq == 0 || (env.Origin != nil && env.Origin.ClientSeq == clientSeq))
|
|
275
|
+
}, Timeout)
|
|
276
|
+
if !ok {
|
|
277
|
+
c.t.Fatalf("timed out waiting for an action envelope on %q (clientSeq %d)", channel, clientSeq)
|
|
278
|
+
}
|
|
279
|
+
env, _ := n.Envelope()
|
|
280
|
+
return env
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Silent reports whether no matching notification arrives within d.
|
|
284
|
+
func (c *Client) Silent(pred func(Notification) bool, d time.Duration) bool {
|
|
285
|
+
_, ok := c.Await(pred, d)
|
|
286
|
+
return !ok
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// ServerClosed reports whether the host closed this client's transport.
|
|
290
|
+
func (c *Client) ServerClosed() bool {
|
|
291
|
+
c.mu.Lock()
|
|
292
|
+
defer c.mu.Unlock()
|
|
293
|
+
return c.serverClosed
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Close simulates the transport going away.
|
|
297
|
+
func (c *Client) Close() { c.conn.Closed() }
|
|
298
|
+
|
|
299
|
+
// JSON marshals v, failing the test on error.
|
|
300
|
+
func JSON(t *testing.T, v any) string {
|
|
301
|
+
t.Helper()
|
|
302
|
+
b, err := json.Marshal(v)
|
|
303
|
+
if err != nil {
|
|
304
|
+
t.Fatal(err)
|
|
305
|
+
}
|
|
306
|
+
return string(b)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Normalize decodes JSON into generic values, with numbers as float64, for deep comparison.
|
|
310
|
+
func Normalize(t *testing.T, v any) any {
|
|
311
|
+
t.Helper()
|
|
312
|
+
var b []byte
|
|
313
|
+
switch x := v.(type) {
|
|
314
|
+
case []byte:
|
|
315
|
+
b = x
|
|
316
|
+
case json.RawMessage:
|
|
317
|
+
b = x
|
|
318
|
+
case string:
|
|
319
|
+
b = []byte(x)
|
|
320
|
+
default:
|
|
321
|
+
var err error
|
|
322
|
+
if b, err = json.Marshal(v); err != nil {
|
|
323
|
+
t.Fatal(err)
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
var out any
|
|
327
|
+
dec := json.NewDecoder(bytes.NewReader(b))
|
|
328
|
+
if err := dec.Decode(&out); err != nil {
|
|
329
|
+
t.Fatalf("normalize %s: %v", b, err)
|
|
330
|
+
}
|
|
331
|
+
return out
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// TestAgent is upstream's TEST_AGENT.
|
|
335
|
+
func TestAgent() ahptypes.AgentInfo {
|
|
336
|
+
return ahptypes.AgentInfo{
|
|
337
|
+
Provider: "pi", DisplayName: "pi", Description: "pi coding agent",
|
|
338
|
+
Models: []ahptypes.SessionModelInfo{{Id: "test-model", Provider: "pi", Name: "Test Model"}},
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// NewHost returns a host with the root channel installed and TestAgent as its only agent.
|
|
343
|
+
func NewHost(opts host.Options) *host.Host {
|
|
344
|
+
if opts.ServerInfo == nil {
|
|
345
|
+
opts.ServerInfo = &ahptypes.Implementation{Name: "pi-ahp", Version: strPtr("0.0.1-test")}
|
|
346
|
+
}
|
|
347
|
+
h := host.New(opts)
|
|
348
|
+
h.InstallRootChannel([]ahptypes.AgentInfo{TestAgent()})
|
|
349
|
+
return h
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
func strPtr(s string) *string { return &s }
|
|
353
|
+
|
|
354
|
+
// Eventually polls cond until it holds or the timeout passes.
|
|
355
|
+
func Eventually(t *testing.T, what string, cond func() bool) {
|
|
356
|
+
t.Helper()
|
|
357
|
+
deadline := time.Now().Add(Timeout)
|
|
358
|
+
for !cond() {
|
|
359
|
+
if time.Now().After(deadline) {
|
|
360
|
+
t.Fatalf("timed out waiting for %s", what)
|
|
361
|
+
}
|
|
362
|
+
time.Sleep(2 * time.Millisecond)
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// ErrTimeout is returned by helpers that give up.
|
|
367
|
+
var ErrTimeout = errors.New("timed out")
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Package twin records which upstream (pi-ahp) test cases have a Go twin.
|
|
2
|
+
//
|
|
3
|
+
// Every upstream case has a twin named after it: [Run] runs one, [Skip] declares a case
|
|
4
|
+
// that cannot be ported and says why. Both take the upstream file (without the
|
|
5
|
+
// .test.ts suffix) and the exact case title as string literals, which the
|
|
6
|
+
// completeness test (../../twins_test.go) reads from the source and compares with
|
|
7
|
+
// testdata/upstream-tests.json. A skipped twin is the visible record of a gap.
|
|
8
|
+
package twin
|
|
9
|
+
|
|
10
|
+
import "testing"
|
|
11
|
+
|
|
12
|
+
// Run runs fn as the subtest for the upstream case (file, title).
|
|
13
|
+
func Run(t *testing.T, file, title string, fn func(t *testing.T)) {
|
|
14
|
+
t.Helper()
|
|
15
|
+
t.Run(title, func(t *testing.T) {
|
|
16
|
+
t.Helper()
|
|
17
|
+
fn(t)
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Skip declares the upstream case (file, title) as a skipped twin with a named reason.
|
|
22
|
+
func Skip(t *testing.T, file, title, reason string) {
|
|
23
|
+
t.Helper()
|
|
24
|
+
t.Run(title, func(t *testing.T) {
|
|
25
|
+
t.Skip(reason)
|
|
26
|
+
})
|
|
27
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Code generated by proof/tools/gen-dispatchable.py from microsoft/agent-host-protocol
|
|
2
|
+
// types/action-origin.generated.ts (IS_CLIENT_DISPATCHABLE) at the pinned commit; DO NOT EDIT.
|
|
3
|
+
|
|
4
|
+
package wire
|
|
5
|
+
|
|
6
|
+
var clientDispatchable = map[string]bool{
|
|
7
|
+
"annotations/entryRemoved": true,
|
|
8
|
+
"annotations/entrySet": true,
|
|
9
|
+
"annotations/removed": true,
|
|
10
|
+
"annotations/set": true,
|
|
11
|
+
"annotations/updated": true,
|
|
12
|
+
"automation/createRequested": true,
|
|
13
|
+
"automation/removed": true,
|
|
14
|
+
"automation/set": false,
|
|
15
|
+
"automation/updateRequested": true,
|
|
16
|
+
"automationRun/cancelRequested": true,
|
|
17
|
+
"automationRun/lifecycleChanged": false,
|
|
18
|
+
"automationRun/primarySessionChanged": false,
|
|
19
|
+
"automationRun/sessionRemoved": false,
|
|
20
|
+
"automationRun/sessionSet": false,
|
|
21
|
+
"changeset/cleared": false,
|
|
22
|
+
"changeset/contentChanged": false,
|
|
23
|
+
"changeset/fileRemoved": false,
|
|
24
|
+
"changeset/fileSet": false,
|
|
25
|
+
"changeset/filesReviewChanged": true,
|
|
26
|
+
"changeset/operationStatusChanged": false,
|
|
27
|
+
"changeset/operationsChanged": false,
|
|
28
|
+
"changeset/statusChanged": false,
|
|
29
|
+
"chat/activityChanged": false,
|
|
30
|
+
"chat/changesetsChanged": false,
|
|
31
|
+
"chat/delta": false,
|
|
32
|
+
"chat/draftChanged": true,
|
|
33
|
+
"chat/error": false,
|
|
34
|
+
"chat/inputAnswerChanged": true,
|
|
35
|
+
"chat/inputCompleted": true,
|
|
36
|
+
"chat/inputRequested": false,
|
|
37
|
+
"chat/isArchivedChanged": true,
|
|
38
|
+
"chat/pendingMessageRemoved": true,
|
|
39
|
+
"chat/pendingMessageSet": true,
|
|
40
|
+
"chat/queuedMessagesReordered": true,
|
|
41
|
+
"chat/reasoning": false,
|
|
42
|
+
"chat/responsePart": false,
|
|
43
|
+
"chat/toolCallAuthRequired": false,
|
|
44
|
+
"chat/toolCallAuthResolved": false,
|
|
45
|
+
"chat/toolCallComplete": true,
|
|
46
|
+
"chat/toolCallConfirmed": true,
|
|
47
|
+
"chat/toolCallContentChanged": true,
|
|
48
|
+
"chat/toolCallDelta": false,
|
|
49
|
+
"chat/toolCallReady": false,
|
|
50
|
+
"chat/toolCallResultConfirmed": true,
|
|
51
|
+
"chat/toolCallStart": false,
|
|
52
|
+
"chat/truncated": true,
|
|
53
|
+
"chat/turnCancelled": true,
|
|
54
|
+
"chat/turnComplete": false,
|
|
55
|
+
"chat/turnResume": true,
|
|
56
|
+
"chat/turnStarted": true,
|
|
57
|
+
"chat/turnsLoaded": false,
|
|
58
|
+
"chat/usage": false,
|
|
59
|
+
"chat/workingDirectoryRemoved": true,
|
|
60
|
+
"chat/workingDirectorySet": true,
|
|
61
|
+
"resourceWatch/changed": false,
|
|
62
|
+
"root/activeSessionsChanged": false,
|
|
63
|
+
"root/agentsChanged": false,
|
|
64
|
+
"root/configChanged": true,
|
|
65
|
+
"root/terminalsChanged": false,
|
|
66
|
+
"session/activeClientRemoved": true,
|
|
67
|
+
"session/activeClientSet": true,
|
|
68
|
+
"session/activityChanged": false,
|
|
69
|
+
"session/changesetsChanged": false,
|
|
70
|
+
"session/chatAdded": false,
|
|
71
|
+
"session/chatRemoved": false,
|
|
72
|
+
"session/chatUpdated": false,
|
|
73
|
+
"session/configChanged": true,
|
|
74
|
+
"session/creationFailed": false,
|
|
75
|
+
"session/customizationRemoved": false,
|
|
76
|
+
"session/customizationToggled": true,
|
|
77
|
+
"session/customizationUpdated": false,
|
|
78
|
+
"session/customizationsChanged": false,
|
|
79
|
+
"session/defaultChatChanged": false,
|
|
80
|
+
"session/inputNeededRemoved": false,
|
|
81
|
+
"session/inputNeededSet": false,
|
|
82
|
+
"session/isArchivedChanged": true,
|
|
83
|
+
"session/isReadChanged": true,
|
|
84
|
+
"session/mcpServerBackgroundRequested": true,
|
|
85
|
+
"session/mcpServerStartRequested": true,
|
|
86
|
+
"session/mcpServerStateChanged": false,
|
|
87
|
+
"session/mcpServerStopRequested": true,
|
|
88
|
+
"session/metaChanged": false,
|
|
89
|
+
"session/ready": false,
|
|
90
|
+
"session/serverToolsChanged": false,
|
|
91
|
+
"session/titleChanged": true,
|
|
92
|
+
"session/workingDirectoryRemoved": true,
|
|
93
|
+
"session/workingDirectoryReplaced": true,
|
|
94
|
+
"session/workingDirectorySet": true,
|
|
95
|
+
"terminal/claimed": true,
|
|
96
|
+
"terminal/cleared": true,
|
|
97
|
+
"terminal/commandDetectionAvailable": false,
|
|
98
|
+
"terminal/commandExecuted": false,
|
|
99
|
+
"terminal/commandFinished": false,
|
|
100
|
+
"terminal/cwdChanged": false,
|
|
101
|
+
"terminal/data": false,
|
|
102
|
+
"terminal/exited": false,
|
|
103
|
+
"terminal/input": true,
|
|
104
|
+
"terminal/resized": true,
|
|
105
|
+
"terminal/titleChanged": true,
|
|
106
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
package wire
|
|
2
|
+
|
|
3
|
+
import "sort"
|
|
4
|
+
|
|
5
|
+
// ClientDispatchableTypes lists every action type a client may dispatch, sorted.
|
|
6
|
+
func ClientDispatchableTypes() []string {
|
|
7
|
+
var out []string
|
|
8
|
+
for t, ok := range clientDispatchable {
|
|
9
|
+
if ok {
|
|
10
|
+
out = append(out, t)
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
sort.Strings(out)
|
|
14
|
+
return out
|
|
15
|
+
}
|