@pi-in-go/pigpen-a2a 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 +26 -0
- package/LICENSE +21 -0
- package/README.md +97 -0
- package/extensions/a2a/auth.go +120 -0
- package/extensions/a2a/auth_test.go +133 -0
- package/extensions/a2a/bench_test.go +70 -0
- package/extensions/a2a/binary_test.go +225 -0
- package/extensions/a2a/cancelqueued_test.go +142 -0
- package/extensions/a2a/card_cache_test.go +42 -0
- package/extensions/a2a/client.go +499 -0
- package/extensions/a2a/client_test.go +402 -0
- package/extensions/a2a/config.go +282 -0
- package/extensions/a2a/config_test.go +194 -0
- package/extensions/a2a/e2e_test.go +311 -0
- package/extensions/a2a/executor.go +318 -0
- package/extensions/a2a/extension.go +382 -0
- package/extensions/a2a/extension_test.go +396 -0
- package/extensions/a2a/fakehost_test.go +548 -0
- package/extensions/a2a/fakepig_test.go +212 -0
- package/extensions/a2a/gaps_test.go +59 -0
- package/extensions/a2a/go.mod +15 -0
- package/extensions/a2a/go.sum +14 -0
- package/extensions/a2a/interop_test.go +163 -0
- package/extensions/a2a/procattr_other.go +25 -0
- package/extensions/a2a/procattr_windows.go +26 -0
- package/extensions/a2a/resubscribe_test.go +113 -0
- package/extensions/a2a/review_test.go +293 -0
- package/extensions/a2a/server.go +267 -0
- package/extensions/a2a/server_test.go +792 -0
- package/extensions/a2a/survivors_test.go +302 -0
- package/extensions/a2a/worker.go +388 -0
- package/extensions/a2a/worker_test.go +257 -0
- package/package.json +41 -0
- package/port/PORT.md +126 -0
- package/port/a2a-go-LICENSE +201 -0
- package/port/golden/flag-without-auth-refused.jsonl +5 -0
- package/port/golden/listener-off.jsonl +4 -0
- package/port/golden/send-missing-message.jsonl +18 -0
- package/port/golden/send-without-remotes.jsonl +18 -0
- package/port/golden/task-unknown-action.jsonl +18 -0
- package/port/golden/tools-visible-to-model.jsonl +11 -0
- package/port/interop/kagent/main.go +73 -0
- package/port/mutation-run.txt +222 -0
- package/port/mutations.json +656 -0
- package/port/red.txt +105 -0
- package/port/scenarios/flag-without-auth-refused.json +8 -0
- package/port/scenarios/listener-off.json +7 -0
- package/port/scenarios/send-missing-message.json +11 -0
- package/port/scenarios/send-without-remotes.json +11 -0
- package/port/scenarios/task-unknown-action.json +11 -0
- package/port/scenarios/tools-visible-to-model.json +8 -0
- package/provenance.json +18 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
package a2aext
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"path/filepath"
|
|
6
|
+
"regexp"
|
|
7
|
+
"runtime"
|
|
8
|
+
"strings"
|
|
9
|
+
"testing"
|
|
10
|
+
"time"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
var alice = Principal{Name: "alice", Tenant: "team-a"}
|
|
14
|
+
|
|
15
|
+
func newTestWorker(t *testing.T) (*ProcessWorker, string, string) {
|
|
16
|
+
t.Helper()
|
|
17
|
+
cfg, logPath, getenv := fakeWorkerConfig(t)
|
|
18
|
+
state := t.TempDir()
|
|
19
|
+
w, err := NewProcessWorker(cfg, state, getenv)
|
|
20
|
+
if err != nil {
|
|
21
|
+
t.Fatal(err)
|
|
22
|
+
}
|
|
23
|
+
return w, logPath, state
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
func TestSessionIDIsStableScopedAndSafe(t *testing.T) {
|
|
27
|
+
hex := regexp.MustCompile(`^[0-9a-f]{32,64}$`)
|
|
28
|
+
a := SessionID(alice, "ctx-1")
|
|
29
|
+
if !hex.MatchString(a) {
|
|
30
|
+
t.Fatalf("session id %q must be lowercase hex", a)
|
|
31
|
+
}
|
|
32
|
+
if a != SessionID(alice, "ctx-1") {
|
|
33
|
+
t.Fatal("the same context must map to the same session")
|
|
34
|
+
}
|
|
35
|
+
if a == SessionID(alice, "ctx-2") {
|
|
36
|
+
t.Fatal("different contexts must not share a session")
|
|
37
|
+
}
|
|
38
|
+
if a == SessionID(Principal{Name: "mallory", Tenant: "team-b"}, "ctx-1") {
|
|
39
|
+
t.Fatal("the same context id under another tenant must not share a session")
|
|
40
|
+
}
|
|
41
|
+
if id := SessionID(alice, "../../etc/passwd"); !hex.MatchString(id) {
|
|
42
|
+
t.Fatalf("a hostile context id must not reach the file name: %q", id)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
func TestWorkerPassesFlagsSessionAndCwd(t *testing.T) {
|
|
47
|
+
w, logPath, state := newTestWorker(t)
|
|
48
|
+
res, err := w.Run(context.Background(), Turn{Principal: alice, ContextID: "c1", TaskID: "t1", Prompt: "hello there"}, func(Update) {})
|
|
49
|
+
if err != nil {
|
|
50
|
+
t.Fatal(err)
|
|
51
|
+
}
|
|
52
|
+
if res.Text != "echo: hello there" || res.Failure != "" {
|
|
53
|
+
t.Fatalf("result %+v", res)
|
|
54
|
+
}
|
|
55
|
+
l := readFakeLog(t, logPath)
|
|
56
|
+
for _, want := range []string{"--mode", "rpc", "--no-extensions", "--no-skills", "--no-context-files", "--no-prompt-templates", "--offline"} {
|
|
57
|
+
if !contains(l.Args, want) {
|
|
58
|
+
t.Errorf("argv lacks %s: %v", want, l.Args)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if contains(l.Args, "--no-session") {
|
|
62
|
+
t.Error("contexts continue across tasks, so the session must be kept")
|
|
63
|
+
}
|
|
64
|
+
if got := argValue(l.Args, "--tools"); got != "read,grep,find,ls" {
|
|
65
|
+
t.Errorf("--tools = %q", got)
|
|
66
|
+
}
|
|
67
|
+
if got := argValue(l.Args, "--session-id"); got != SessionID(alice, "c1") {
|
|
68
|
+
t.Errorf("--session-id = %q", got)
|
|
69
|
+
}
|
|
70
|
+
dir := argValue(l.Args, "--session-dir")
|
|
71
|
+
if !strings.HasPrefix(dir, state) || dir == state {
|
|
72
|
+
t.Errorf("--session-dir %q must be a per-principal directory under %q", dir, state)
|
|
73
|
+
}
|
|
74
|
+
if other := w.sessionDir(Principal{Name: "bob"}); other == dir {
|
|
75
|
+
t.Error("principals must not share a session directory")
|
|
76
|
+
}
|
|
77
|
+
if l.Prompt != "hello there" {
|
|
78
|
+
t.Errorf("prompt = %q", l.Prompt)
|
|
79
|
+
}
|
|
80
|
+
// The child reports the directory the OS resolved (macOS's TMPDIR is a symlink: /var -> /private/var).
|
|
81
|
+
if got, want := realPath(t, l.Cwd), realPath(t, w.cfg.Cwd); got != want {
|
|
82
|
+
t.Errorf("cwd = %q (%q), want %q (%q)", l.Cwd, got, w.cfg.Cwd, want)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// realPath is p with symlinks resolved, so two spellings of one directory compare equal.
|
|
87
|
+
func realPath(t *testing.T, p string) string {
|
|
88
|
+
t.Helper()
|
|
89
|
+
r, err := filepath.EvalSymlinks(p)
|
|
90
|
+
if err != nil {
|
|
91
|
+
t.Fatal(err)
|
|
92
|
+
}
|
|
93
|
+
return r
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
func TestWorkerModelAndExtraArgs(t *testing.T) {
|
|
97
|
+
cfg, logPath, getenv := fakeWorkerConfig(t)
|
|
98
|
+
cfg.Provider, cfg.Model, cfg.Args = "p", "m", []string{"--thinking", "off"}
|
|
99
|
+
w, err := NewProcessWorker(cfg, t.TempDir(), getenv)
|
|
100
|
+
if err != nil {
|
|
101
|
+
t.Fatal(err)
|
|
102
|
+
}
|
|
103
|
+
if _, err := w.Run(context.Background(), Turn{Principal: alice, ContextID: "c", Prompt: "x y"}, func(Update) {}); err != nil {
|
|
104
|
+
t.Fatal(err)
|
|
105
|
+
}
|
|
106
|
+
l := readFakeLog(t, logPath)
|
|
107
|
+
if argValue(l.Args, "--provider") != "p" || argValue(l.Args, "--model") != "m" || argValue(l.Args, "--thinking") != "off" {
|
|
108
|
+
t.Fatalf("argv %v", l.Args)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
func TestWorkerEnvironmentIsAllowlisted(t *testing.T) {
|
|
113
|
+
w, logPath, _ := newTestWorker(t)
|
|
114
|
+
if _, err := w.Run(context.Background(), Turn{Principal: alice, ContextID: "c", Prompt: "ab"}, func(Update) {}); err != nil {
|
|
115
|
+
t.Fatal(err)
|
|
116
|
+
}
|
|
117
|
+
env := readFakeLog(t, logPath).Env
|
|
118
|
+
if env["PIG_A2A_WORKER"] != "1" {
|
|
119
|
+
t.Error("the child must be marked as a worker so it never opens a listener")
|
|
120
|
+
}
|
|
121
|
+
if env["OPENAI_API_KEY"] != "sk-passthrough" || env["PIG_HOME"] != "/pig/home" || env["PIG_CODING_AGENT_DIR"] != "/pig/agent" {
|
|
122
|
+
t.Errorf("configured passthrough and PiG's config directories must reach the child: %v", env)
|
|
123
|
+
}
|
|
124
|
+
for _, leak := range []string{"TOKEN_A", "SOMETHING_SECRET"} {
|
|
125
|
+
if _, ok := env[leak]; ok {
|
|
126
|
+
t.Errorf("%s leaked into the worker environment", leak)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
func TestWorkerStreamsUpdatesAndFinalText(t *testing.T) {
|
|
132
|
+
w, _, _ := newTestWorker(t)
|
|
133
|
+
var text strings.Builder
|
|
134
|
+
var tools []string
|
|
135
|
+
res, err := w.Run(context.Background(), Turn{Principal: alice, ContextID: "c", Prompt: "TOOL please"}, func(u Update) {
|
|
136
|
+
text.WriteString(u.Text)
|
|
137
|
+
if u.Tool != "" {
|
|
138
|
+
tools = append(tools, u.Tool)
|
|
139
|
+
}
|
|
140
|
+
})
|
|
141
|
+
if err != nil {
|
|
142
|
+
t.Fatal(err)
|
|
143
|
+
}
|
|
144
|
+
if res.Text != "done" || text.String() != "done" || strings.Join(tools, ",") != "read" {
|
|
145
|
+
t.Fatalf("res %+v streamed %q tools %v", res, text.String(), tools)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
func TestWorkerStreamedTextEqualsFinalTextWithoutDuplication(t *testing.T) {
|
|
150
|
+
w, _, _ := newTestWorker(t)
|
|
151
|
+
var text strings.Builder
|
|
152
|
+
res, err := w.Run(context.Background(), Turn{Principal: alice, ContextID: "c", Prompt: "abcdef"}, func(u Update) { text.WriteString(u.Text) })
|
|
153
|
+
if err != nil {
|
|
154
|
+
t.Fatal(err)
|
|
155
|
+
}
|
|
156
|
+
if text.String() != res.Text {
|
|
157
|
+
t.Fatalf("streamed %q, final %q", text.String(), res.Text)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
func TestWorkerModelFailureIsAFailureWithoutProviderDetail(t *testing.T) {
|
|
162
|
+
w, _, _ := newTestWorker(t)
|
|
163
|
+
res, err := w.Run(context.Background(), Turn{Principal: alice, ContextID: "c", Prompt: "FAIL now"}, func(Update) {})
|
|
164
|
+
if err != nil {
|
|
165
|
+
t.Fatal(err)
|
|
166
|
+
}
|
|
167
|
+
if res.Failure == "" {
|
|
168
|
+
t.Fatal("a model error must fail the task")
|
|
169
|
+
}
|
|
170
|
+
if strings.Contains(res.Failure, "sk-123") {
|
|
171
|
+
t.Fatalf("provider error text can carry secrets; it must not reach the A2A peer: %q", res.Failure)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
func TestWorkerCancelAbortsThenReaps(t *testing.T) {
|
|
176
|
+
if runtime.GOOS == "windows" {
|
|
177
|
+
t.Skip("process liveness probe is POSIX")
|
|
178
|
+
}
|
|
179
|
+
w, logPath, _ := newTestWorker(t)
|
|
180
|
+
ctx, cancel := context.WithCancel(context.Background())
|
|
181
|
+
done := make(chan error, 1)
|
|
182
|
+
go func() {
|
|
183
|
+
_, err := w.Run(ctx, Turn{Principal: alice, ContextID: "c", Prompt: "SLOW task"}, func(Update) {})
|
|
184
|
+
done <- err
|
|
185
|
+
}()
|
|
186
|
+
l := waitFakeLog(t, logPath, func(l fakeLog) bool { return l.Started })
|
|
187
|
+
cancel()
|
|
188
|
+
select {
|
|
189
|
+
case err := <-done:
|
|
190
|
+
if err == nil {
|
|
191
|
+
t.Fatal("cancellation must surface as an error")
|
|
192
|
+
}
|
|
193
|
+
case <-time.After(10 * time.Second):
|
|
194
|
+
t.Fatal("Run did not return after cancellation")
|
|
195
|
+
}
|
|
196
|
+
if l2 := readFakeLog(t, logPath); !l2.Aborted {
|
|
197
|
+
t.Error("the worker must send the RPC abort before killing anything")
|
|
198
|
+
}
|
|
199
|
+
if processAlive(l.PID) {
|
|
200
|
+
t.Error("the child process must be reaped")
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
func TestWorkerKillsAChildThatIgnoresAbort(t *testing.T) {
|
|
205
|
+
if runtime.GOOS == "windows" {
|
|
206
|
+
t.Skip("process liveness probe is POSIX")
|
|
207
|
+
}
|
|
208
|
+
w, logPath, _ := newTestWorker(t)
|
|
209
|
+
ctx, cancel := context.WithCancel(context.Background())
|
|
210
|
+
done := make(chan error, 1)
|
|
211
|
+
go func() {
|
|
212
|
+
_, err := w.Run(ctx, Turn{Principal: alice, ContextID: "c", Prompt: "IGNORE_ABORT"}, func(Update) {})
|
|
213
|
+
done <- err
|
|
214
|
+
}()
|
|
215
|
+
l := waitFakeLog(t, logPath, func(l fakeLog) bool { return l.Started })
|
|
216
|
+
cancel()
|
|
217
|
+
select {
|
|
218
|
+
case <-done:
|
|
219
|
+
case <-time.After(15 * time.Second):
|
|
220
|
+
t.Fatal("a child that ignores abort must be killed after the grace period")
|
|
221
|
+
}
|
|
222
|
+
deadline := time.Now().Add(5 * time.Second)
|
|
223
|
+
for processAlive(l.PID) && time.Now().Before(deadline) {
|
|
224
|
+
time.Sleep(20 * time.Millisecond)
|
|
225
|
+
}
|
|
226
|
+
if processAlive(l.PID) {
|
|
227
|
+
t.Fatal("child survived")
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
func TestWorkerCommandThatCannotStartIsReported(t *testing.T) {
|
|
232
|
+
cfg, _, getenv := fakeWorkerConfig(t)
|
|
233
|
+
cfg.Command = "/nonexistent/pig"
|
|
234
|
+
w, err := NewProcessWorker(cfg, t.TempDir(), getenv)
|
|
235
|
+
if err != nil {
|
|
236
|
+
return // refusing at construction is fine too
|
|
237
|
+
}
|
|
238
|
+
if _, err := w.Run(context.Background(), Turn{Principal: alice, ContextID: "c", Prompt: "x"}, func(Update) {}); err == nil {
|
|
239
|
+
t.Fatal("want an error")
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
func TestWorkerRefusesEmptyPrompt(t *testing.T) {
|
|
244
|
+
w, _, _ := newTestWorker(t)
|
|
245
|
+
if _, err := w.Run(context.Background(), Turn{Principal: alice, ContextID: "c", Prompt: " "}, func(Update) {}); err == nil {
|
|
246
|
+
t.Fatal("an empty prompt is not a task")
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
func contains(list []string, s string) bool {
|
|
251
|
+
for _, x := range list {
|
|
252
|
+
if x == s {
|
|
253
|
+
return true
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return false
|
|
257
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pi-in-go/pigpen-a2a",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A2A (Agent2Agent) for PiG: serve PiG tasks to A2A agents and call remote A2A agents, on the upstream Go SDK a2a-go. The listener is off unless configured.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pig-package",
|
|
7
|
+
"a2a",
|
|
8
|
+
"agent2agent",
|
|
9
|
+
"protocol",
|
|
10
|
+
"kagent",
|
|
11
|
+
"extension",
|
|
12
|
+
"pigpen"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "Michael Kinsy",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/MichaelKinsy/pigpen.git",
|
|
19
|
+
"directory": "components/a2a"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://github.com/MichaelKinsy/pigpen/tree/main/components/a2a#readme",
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/MichaelKinsy/pigpen/issues"
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"extensions",
|
|
30
|
+
"port",
|
|
31
|
+
"README.md",
|
|
32
|
+
"LICENSE",
|
|
33
|
+
"CREDITS.md",
|
|
34
|
+
"provenance.json"
|
|
35
|
+
],
|
|
36
|
+
"pi": {
|
|
37
|
+
"extensions": [
|
|
38
|
+
"extensions/a2a"
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
}
|
package/port/PORT.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# a2a: port record
|
|
2
|
+
|
|
3
|
+
**Kind of port (Skill, "What kind of port is this?"): 3, new code with no original.** a2a is an *original adapter* on the upstream
|
|
4
|
+
Go SDK [a2a-go](https://github.com/a2aproject/a2a-go) v2.6.0 (commit `ebf17c56ef7e63c72883a45454a538bbc0df66b8`, Apache-2.0). There
|
|
5
|
+
is no TypeScript or Pi original to twin, so there is **no Pi oracle and no equivalence claim**. The golden traces in `golden/` are
|
|
6
|
+
**self-recorded** regression baselines (lane `pig-go-self`): the port was recorded from itself and reviewed by hand, and `pigeq check`
|
|
7
|
+
labels them that way. What replaces the oracle for protocol facts is a2a-go's own contract plus an independent A2A peer (below).
|
|
8
|
+
|
|
9
|
+
## Identities
|
|
10
|
+
|
|
11
|
+
| Item | Value |
|
|
12
|
+
|---|---|
|
|
13
|
+
| Package | `components/a2a` (module `github.com/MichaelKinsy/pigpen/a2a`, Go extension `a2a`), Piglet `piglets/a2a` |
|
|
14
|
+
| Upstream SDK | `github.com/a2aproject/a2a-go/v2` **v2.6.0**, A2A protocol **1.0** (JSON-RPC binding), Apache-2.0, `port/a2a-go-LICENSE` |
|
|
15
|
+
| Host | PiG 0.3.0 (commit `63c6ba456`), upstream Pi 0.87.1, go1.27.1 |
|
|
16
|
+
| Interop peer | kagent upstream `main` at `e4516302` (its Go ADK server package `go/adk/pkg/a2a/server`, which pins a2a-go v2.6.0) |
|
|
17
|
+
| Gap check | `pig docs list` / `pig docs show built-in-extensions` in 0.3.0: PiG ships no A2A. The older port map lists A2A remote sessions as "not ported (spec 482)". `pigeq gaps --go`: PASS (`port-gaps`), public SDK only, no process-global calls |
|
|
18
|
+
|
|
19
|
+
## Gap check against the SDK surface
|
|
20
|
+
|
|
21
|
+
Everything the extension uses is fully provided by the 0.3.0 Go SDK: `sdk.New`, `RegisterTool`, `RegisterCommand`, `RegisterFlag`,
|
|
22
|
+
`OnEvent("session_start"|"session_shutdown")`, `Context.Notify`, `sdk.NewToolError`. No `PARTIAL` or `MISSING` (`pigeq gaps --go`).
|
|
23
|
+
Fused-extension rule found in `pig docs show extensions`: a fused extension shares PiG's process, so a2a discards the HTTP server's and
|
|
24
|
+
a2a-go's logs (`ErrorLog` and slog to `io.Discard`, `RecentLogs` ring instead) rather than writing to PiG's terminal.
|
|
25
|
+
|
|
26
|
+
## Contract
|
|
27
|
+
|
|
28
|
+
Every behaviour is a row; each has a test and, where a mutation applies, a named mutant that the tests kill (`mutations.json`).
|
|
29
|
+
|
|
30
|
+
| # | Contract | Tests | Mutants |
|
|
31
|
+
|---|---|---|---|
|
|
32
|
+
| C01 | Listener is off unless configured; installing opens no port | `TestListenerIsOffUnlessConfigured`, `TestNoConfigurationOpensNoListener`, scenario `listener-off` | `listener-on-by-default` |
|
|
33
|
+
| C02 | Config: `a2a.json` in the agent dir, `PIG_A2A_CONFIG`, env `PIG_A2A_LISTEN`, flag `--a2a-listen` (flag > env > file); strict JSON | `TestConfigFileEnablesListener`, `TestPrecedenceFlagOverEnvOverFile`, `TestConfigPathFromEnv`, `TestConfigIsReadFromTheAgentDirectory`, `TestFlagOverridesTheConfigurationFile` | precedence, agent-dir, flag mutants |
|
|
34
|
+
| C03 | Secrets are named by env var, never inline; never echoed | `TestInlineSecretsAreRejected`, `TestTokenEnvMustBeSetAndIsNotEchoed`, `TestRemoteAuthFailureDoesNotEchoTheToken` | inline-secret, echo mutants |
|
|
35
|
+
| C04 | A listener needs authentication; `insecureNoAuth` only on loopback; not together with tokens; tokens ≥16 chars, unique names and values | `TestListenerNeedsAuthentication`, `TestInsecureNoAuthOnlyOnLoopback`, `TestTokenConfigValidation`, `TestTwoVariablesWithOneValueAreRejected`, `TestInsecureNoAuthWithTokensIsAmbiguous`, scenario `flag-without-auth-refused` | auth/loopback/duplicate mutants |
|
|
36
|
+
| C05 | Bearer tokens compared by digest, constant time; token in a query string ignored; 401 + `WWW-Authenticate: Bearer`; agent card public | `TestAuthenticateBearer`, `TestAuthenticateRejects`, `TestTokenInQueryStringIsIgnored`, `TestWrapRejectsWith401AndChallenge`, `TestWrapPassesPrincipalAndPublicPaths`, `TestUnauthenticatedRequestIsRejectedBeforeTheWorker` | compare, query, 401 mutants |
|
|
37
|
+
| C06 | Protocol pinned to A2A 1.0: `A2A-Version: 1.0` required (an absent header, which the specification reads as 0.3, is refused), other versions refused, repeated `1.0, 1.0` accepted (kagent's client), `1.0, 0.3` refused | `TestProtocolVersionIsPinned`, `TestMixedVersionHeaderIsRefused`, `TestAgentCardIsPublicAndPinnedToProtocol10` | version mutants |
|
|
38
|
+
| C07 | Agent card: name, Bearer scheme, streaming true, push false, one 1.0 JSON-RPC interface, external URL | `TestAgentCardIsPublicAndPinnedToProtocol10`, `TestAgentCardAdvertisesExternalURL` | card mutants |
|
|
39
|
+
| C08 | Task identity: one message is one task on one worker turn; result is a completed task with the reply as artifact | `TestSendMessageRunsATurnAndReturnsACompletedTask`, `TestE2E_TaskRunsRealPiGWithAToolCall` | `worker-keeps-extensions`, `worker-not-marked`, `tool-updates-dropped` |
|
|
40
|
+
| C09 | Context identity: a `contextId` is a PiG session; the same id continues it, across tasks | `TestSameContextIDContinuesTheSameWorkerContext`, `TestE2E_ContextContinuesAcrossTasksAndTenantsAreIsolated`, `TestSessionIDIsStableScopedAndSafe` | session-id mutants |
|
|
41
|
+
| C10 | Tenant isolation: contexts, tasks and sessions are scoped to the caller's principal; another caller cannot read, attach to or subscribe to a task | `TestTenantsDoNotShareContextsOrTasks`, `TestBobCannotAttachToAliceTaskWithAMessage`, `TestSubscribeToALiveTaskDeliversTheRest`, `TestPrincipalKeysIsolate`, `TestExecutorRefusesAForgedUser` | `principal-not-checked`, key mutants |
|
|
42
|
+
| C11 | A request's tenant must match the token's | `TestRequestTenantMustMatchTheTokensTenant` | `tenant-not-enforced` |
|
|
43
|
+
| C12 | Cancellation aborts the worker (RPC abort, grace, SIGTERM, SIGKILL of the process group), ends `canceled`, reaps the child | `TestCancelTaskAbortsTheWorkerAndEndsCanceled`, `TestWorkerCancelAbortsThenReaps`, `TestWorkerKillsAChildThatIgnoresAbort`, `TestWorkerAsksForTermBeforeKill`, `TestWorkerEscalatesToKillWhenTermIsIgnored`, `TestE2E_CancelAbortsRealPiG` | `no-terminate`, `no-kill-after-grace`, `abort-not-sent`, `cancel-does-not-stop-worker` |
|
|
44
|
+
| C13 | Streaming: artifact chunks arrive before completion; streamed text equals the final text | `TestStreamingDeliversArtifactChunksBeforeCompletion`, `TestWorkerStreamsUpdatesAndFinalText…` | stream mutants |
|
|
45
|
+
| C14 | Failures end `failed` without provider detail sent to the peer | `TestWorkerFailureBecomesAFailedTask`, `TestWorkerErrorDetailIsNotSentToThePeer`, `TestModelFailureMessageIsExactlyGeneric`, `TestWorkerModelFailureIsAFailureWithoutProviderDetail` | `model-error-text-copied` |
|
|
46
|
+
| C15 | Limits: concurrent tasks, one task at a time per context, task timeout, prompt 256 KiB, body 1 MiB | `TestConcurrencyLimitQueuesTasks`, `TestTasksInOneContextRunOneAtATime`, `TestTaskTimeoutFailsTheTask`, `TestOversizedPromptIsRefusedBelowTheBodyLimit`, `TestBodyLimitAppliesEvenWithASmallPrompt`, `TestOversizedRequestBodyIsRefused` | limit mutants |
|
|
47
|
+
| C16 | Only text parts; empty message refused; attachments refused, not dropped | `TestNonTextPartsAreRejected`, `TestEmptyMessageIsRejected`, `TestTextPlusFilePartIsRefusedNotTrimmed` | `file-parts-dropped` |
|
|
48
|
+
| C17 | Worker has no tools by default (`--no-tools`; PiG's file tools take absolute paths, so even `read` reaches every file the account can), configured tools are the allowlist, no extensions, no context files, own session dir, allowlisted env; the worker never opens a listener | `TestWorkerDefaultsHaveNoTools`, `TestReview_WorkerHasNoToolsUnlessConfigured`, `TestE2E_DefaultWorkerCannotReadFilesOutsideItsWorkspace`, `TestWorkerPassesFlagsSessionAndCwd`, `TestWorkerEnvironmentIsAllowlisted`, `TestWorkerProcessDoesNotOpenAListener`, `TestWorkerChildNeverListens` | tool/env mutants |
|
|
49
|
+
| C18 | A turn that needs interactive input or a rejected prompt fails, never hangs | `TestWorkerFailsATurnThatNeedsInteractiveInput`, `TestWorkerReportsARejectedPromptWithoutTheReason` | `ui-request-hangs`, `rejected-prompt-ignored` |
|
|
50
|
+
| C27 | A cancel that arrives before the worker starts (queued behind the concurrency limit, or behind another task of the same context) ends the task `canceled`, never starts a worker, and leaves the task ahead untouched | `TestCancelBeforeTheWorkerStartsBehindTheConcurrencyLimit`, `TestCancelBeforeTheWorkerStartsBehindTheSameContext` | `queued-cancel-ignored-at-slot`, `queued-cancel-ignored-at-context-lock` |
|
|
51
|
+
| C28 | An `insecureNoAuth` listener answers only a loopback `Host` (DNS rebinding); a token listener serves any `Host` (proxies) | `TestReview_InsecureLoopbackListenerRefusesAForeignHostHeader`, `TestReview_TokenListenerServesAnyHostHeader` | `insecure-host-unchecked` |
|
|
52
|
+
| C29 | The per-context lock is per principal (two tenants with one `contextId` do not queue behind each other); a process a turn left behind is killed | `TestReview_SameContextIDInTwoTenantsRunsConcurrently`, `TestReview_WorkerKillsWhatATurnLeftBehind` | `context-lock-shared-across-tenants`, `left-behind-child-survives` |
|
|
53
|
+
| C19 | Shutdown cancels running tasks, waits, and closes the listener; a second start and a busy port are errors; TLS load failure is an error | `TestShutdownCancelsRunningTasksAndStopsListening`, `TestStartFailsOnAnAddressInUse`, `TestSecondStartIsAnError`, `TestShutdownClosesThePortEvenRightAfterStart`, `TestTLSListener`, `TestUnreadableTLSKeyPairFailsStart` | lifecycle mutants |
|
|
54
|
+
| C20 | Client: named remotes, card resolution, direct endpoint (`skipCard`), pinned 1.0 JSON-RPC interface only, `A2A-Version: 1.0` sent | `TestCardResolvesAndSummarises`, `TestSkipCardUsesTheEndpointDirectly`, `TestCardWithoutAProtocol10InterfaceIsRefused`, `TestSendReusesContextAndSendsProtocolVersion` | client mutants |
|
|
55
|
+
| C21 | Client credentials go only to the configured origin, scheme and host (transport check and card pinning, two independent defences; https is never downgraded to http); redirects are not followed | `TestReview_CredentialsAreNotDowngradedToPlainHTTP`, `TestCredentialsAreNotSentToAHostTheCardNamed`, `TestCredentialTransportRefusesAnotherHost`, `TestRemoteRedirectsAreNotFollowed`, `TestInterop_KagentCardHostIsNotTrustedWithCredentials` | `transport-host-not-checked`, `transport-scheme-not-checked`, `card-scheme-downgrade-accepted`, `redirects-followed-with-credentials` |
|
|
56
|
+
| C22 | Client: cancelling the call (or its timeout) cancels the remote task; states failed/input-required reported; terminal task reported | `TestCancellingTheCallCancelsTheRemoteTask`, `TestSendHonoursTheConfiguredTimeout`, `TestFailedAndInputRequiredStates`, `TestSummaryOfAnInputRequiredTaskIsNotTerminal`, `TestSendToATerminalTaskStillReportsItsState`, `TestGetAndCancelTask` | client-cancel mutants |
|
|
57
|
+
| C23 | Tools `a2a_agents`, `a2a_send`, `a2a_task`, command `/a2a`; argument errors name the argument; no remotes says how to configure | `TestRegistersToolsCommandAndLifecycleHandlers`, `TestToolsRejectBadArguments`, `TestToolsWithoutRemotesSayWhy`, `TestSendToolPassesTheContextIDThrough`, `TestStatusListsRemotes`, scenarios `tools-visible-to-model`, `send-missing-message`, `send-without-remotes`, `task-unknown-action` | tool mutants |
|
|
58
|
+
| C24 | Lifecycle: new/resume/fork keep the listener, other reasons stop it; reload re-reads the configuration; invalid config is reported, not fatal | `TestSessionSwitchKeepsTheListenerReloadRestartsIt`, `TestReloadAppliesChangedConfiguration`, `TestToolAfterShutdownUsesTheRewrittenConfiguration`, `TestInvalidConfigurationIsReportedNotFatal`, `TestListenFailureIsReportedToTheUser` | lifecycle mutants |
|
|
59
|
+
| C26 | The Piglet Binary hosts the listener itself: configured, it serves the card publicly, refuses unauthenticated calls, runs a real worker (the same Binary) on a local model, isolates tenants; unconfigured or flag-only it stays off | `TestBinary_*` (3, `PIG_A2A_BINARY`) | (external) |
|
|
60
|
+
| C25 | Interop with kagent's A2A server package (as a remote for PiG) and a kagent-shaped client (a2a-go v2, repeated version header) against PiG's server | `TestInterop_*` (4), `scripts/interop-kagent.mjs` | (external) |
|
|
61
|
+
|
|
62
|
+
## Named gaps (skipped tests, `gaps_test.go`)
|
|
63
|
+
|
|
64
|
+
Each is a `t.Skip` with a reason; none is silent. 13 gaps: `TestGap_GRPCTransportBinding`, `TestGap_RESTTransportBinding`, `TestGap_PushNotifications`,
|
|
65
|
+
`TestGap_ProtocolV03Compatibility`, `TestGap_ExtendedAgentCard`, `TestGap_PersistentTaskStore`, `TestGap_InputRequiredFromPiG`,
|
|
66
|
+
`TestGap_FileAndDataParts`, `TestGap_OAuth2AndMutualTLSAuthentication`, `TestGap_SignedAgentCards`, `TestGap_SubscribeToTaskAfterRestart`,
|
|
67
|
+
`TestGap_ClientStreamingProgressToTheModel`, `TestGap_ClientResubscribeTool`. Conditional skips (not gaps), each with a named reason: three kagent tests unless `KAGENT_A2A_ECHO` is set (`scripts/interop-kagent.mjs`), four e2e tests unless `PIG_A2A_E2E_BIN` is set, four Binary tests unless `PIG_A2A_BINARY` is set.
|
|
68
|
+
|
|
69
|
+
## Deviations from the Skill (recorded, not hidden)
|
|
70
|
+
|
|
71
|
+
- **Red-green.** RED commit `16ddb5c` (74 tests fail on a signature stub; `red.txt`), then GREEN. Two RED tests were fixed in GREEN because the *test double* was wrong, not
|
|
72
|
+
the expectation: the fake remote lacked an authenticated user for a2a-go's task store, and one cancel test raced the stream's first event (it now waits for it). Tests were added
|
|
73
|
+
after the first mutation run (survivors below); they only add assertions.
|
|
74
|
+
- **No twins.** There is no upstream suite; a2a-go's conformance behaviour is exercised through its own client against the server, and the fake remote (an independent
|
|
75
|
+
`a2asrv` server) against the client.
|
|
76
|
+
- **Golden traces** are self-recorded (`pig-go-self`), six scenarios, hand reviewed.
|
|
77
|
+
|
|
78
|
+
## Host findings (for the porter and PiG)
|
|
79
|
+
|
|
80
|
+
- The fake-host template's `Command` sent the wrong request shape (fixed in the porter branch); a tool's own argument check runs, since the host does not validate schemas in the fake.
|
|
81
|
+
- A stale `go.mod` passes workspace tests but fails PiG's packed build (missing `go.sum` entries): always `go mod tidy` with the full source, then build a Binary.
|
|
82
|
+
- kagent's ADK server hard-codes its readiness port `:8081`; kagent's client repeats the `A2A-Version` header (`1.0, 1.0`); a2a-go's in-memory task store lists tasks only for an authenticated user.
|
|
83
|
+
- **a2a-go v2.6.0 defect (reported here, not fixed upstream):** `SubscribeToTask` on a *live* task attaches to the task's event queue without an ownership check
|
|
84
|
+
(`a2asrv/handler.go`, `execManager.Resubscribe` before `taskStore.Get`), so any authenticated caller who knows a task id can read another tenant's live stream. `GetTask`,
|
|
85
|
+
`CancelTask` and `ListTasks` are masked. The port owns the task store and checks ownership in its call interceptor (`guard`); `TestSubscribeToALiveTaskDeliversTheRest`
|
|
86
|
+
and mutant `subscribe-ownership-unchecked` pin it. Found only by writing the roadmap's "stream resubscription" item with a second principal.
|
|
87
|
+
- a2a-go's client ends the event stream with an error on cancel, so a "cancel after clean end" branch would be dead code (removed).
|
|
88
|
+
- `go.work` written by `pigeq` uses `go 1.26`, which is below `go 1.26.0`; needed a local fix.
|
|
89
|
+
- PiG 0.3.0's `read`, `grep`, `find` and `ls` resolve absolute paths and `~` outside the working directory
|
|
90
|
+
(`internal/codingagent/tools/path_utils.go` `resolveToCwd`), so a tool allowlist is not a file-access boundary. The worker
|
|
91
|
+
therefore defaults to `--no-tools`; a PiG option that confines file tools to the working directory would let a served
|
|
92
|
+
worker have read tools safely.
|
|
93
|
+
|
|
94
|
+
## What PiG 0.4.0 (Pi 0.99.1) will change
|
|
95
|
+
|
|
96
|
+
Not built or run against 0.4.0. Expected: the SDK module path and `sdk.New` surface are the public API this port already uses, so a rebuild is the test; the worker's RPC
|
|
97
|
+
protocol (`--mode rpc` JSONL: `prompt`, `abort`, `message_update`, `agent_end`, `extension_ui_request`) and flags (`--session-dir`, `--session-id`, `--tools`, `--no-*`) are read by
|
|
98
|
+
`worker.go` and are the surface to re-check. The kagent interop does not depend on a PiG version (it speaks A2A only).
|
|
99
|
+
|
|
100
|
+
## Proof results
|
|
101
|
+
|
|
102
|
+
Host: PiG 0.3.0+0.87.1 built from `63c6ba456`; Piglet Binary `piglets/a2a` built with `pig piglet build --format binary` from the staged tree
|
|
103
|
+
(58.9 MB, fused, a2a-go linked; the Binary is not committed). Lane build: sha256 `17eff7afd824d1b5…`; review rebuild after the fixes: sha256 `8e0f00bd44bbdab7…`.
|
|
104
|
+
|
|
105
|
+
| Layer | Result |
|
|
106
|
+
|---|---|
|
|
107
|
+
| Unit tests, `go test -race -count=3` | 122 pass, 24 skip (13 named gaps, 11 conditional), 0 fail (review re-run; the lane's run was 116 pass, 22 skip); also `GOMAXPROCS=4 taskset -c 0-3 -race -count=24`: pass (a first 24-run found a real race: `Server.Shutdown` could return before the Serve goroutine had closed the port; fixed, pinned by `TestShutdownClosesThePortEvenRightAfterStart`); `-count=8` under 6 CPU burners: pass |
|
|
108
|
+
| Real PiG worker end to end (`PIG_A2A_E2E_BIN`) | 4 pass with `pig` 0.3.0 and with the Binary: tool call (tools named), context continuity, tenant isolation, cancel reaps the child, and a worker configured only by `a2a.json` cannot read a file outside its workspace (this last test failed on the lane's code: the default `read` tool returned the file to the peer) |
|
|
109
|
+
| The Binary hosts the listener (`PIG_A2A_BINARY`) | 4 pass: configured (card public, 401, task on a real worker that is the same Binary, the tools named in `a2a.json` and no others, tenant isolation), off without configuration, flag without tokens refused, and its default worker cannot read outside its workspace (fails against the lane's Binary, passes against the review's) |
|
|
110
|
+
| kagent interop (`KAGENT_A2A_ECHO`, `scripts/interop-kagent.mjs`, kagent upstream `e4516302`) | 4 pass: kagent's own A2A server package as a remote for PiG (send, stream, cancel; credentials not sent to the card's host), a kagent-shaped client (a2a-go v2, repeated version header) against PiG's server. **No live kagent cluster**: `docker` and `kubectl` exist here, `kind` does not |
|
|
111
|
+
| Host scenarios, `pigeq check` (self-recorded, `pig-go-self`) | 6 pass + `port-gaps` + `exec-coverage` |
|
|
112
|
+
| Mutation (`mutation-run.txt`) | **109 mutants, 109 killed, 0 survived** (review re-run with an exact find/replace runner; the review replaced `default-tools-not-read-only` with `worker-gets-pig-default-tools` and added six: `insecure-host-unchecked`, `transport-scheme-not-checked`, `card-scheme-downgrade-accepted`, `context-lock-shared-across-tenants` and `left-behind-child-survives`, the last two surviving the lane's tests). Lane's run, `pigeq mutate --unit-only --jobs 4`: 104/104. First run: 65 killed of 102; after adding tests (`survivors_test.go` and additions) and fixing 6 mutants that did not compile: 96/100; the last 5 (plus one new mutant for the SubscribeToTask defect) killed. One mutant judged equivalent and dropped (`cancel-does-not-wait`: a2a-go's cancel already waits for the executor). Two dropped as dead code (`cancel-after-stream-end-missed`) or equivalent (`shutdown-leaves-workers`) |
|
|
113
|
+
| `go vet` | linux, windows, darwin |
|
|
114
|
+
| Repository | `npm run generate`/`check`, `npm test` (62), `npm run validate`, `npm run test:go-ports -- -race -count=3`: run against the porter-driver's scripts (this branch keeps the shared scripts at base, so merge the porter-driver branch first), all pass. `pig install components/a2a/extensions/a2a --validate-only`: packable |
|
|
115
|
+
|
|
116
|
+
### Mutants (109)
|
|
117
|
+
|
|
118
|
+
`listener-starts-without-config`, `worker-child-may-listen`, `flag-not-highest-precedence`, `env-not-over-file`, `unknown-keys-and-inline-secrets-accepted`, `listener-without-tokens-allowed`, `insecure-no-auth-off-loopback`, `insecure-and-tokens-both`, `short-tokens-allowed`, `duplicate-token-values-allowed`, `duplicate-token-names-allowed`, `tenant-not-validated`, `missing-token-env-allowed`, `listen-not-host-port`, `worker-gets-pig-default-tools`, `remote-url-scheme-unchecked`, `remote-name-unchecked`, `basic-scheme-accepted`, `extra-fields-accepted`, `token-comparison-inverted`, `insecure-flag-ignored`, `tenant-key-uses-token-name`, `key-namespaces-collide`, `everything-public`, `no-bearer-challenge`, `empty-authenticator-allowed`, `file-parts-dropped`, `empty-prompt-accepted`, `prompt-size-unbounded`, `timeout-never-fires`, `worker-error-detail-leaks`, `context-not-serialised`, `concurrency-limit-ignored`, `cancel-does-not-stop-worker`, `failure-reason-dropped`, `principal-not-checked`, `version-not-enforced`, `absent-version-accepted`, `minor-versions-accepted`, `mixed-version-values-accepted`, `tenant-not-enforced`, `user-not-attached`, `body-size-unbounded`, `card-requires-auth`, `card-drops-streaming`, `card-claims-push`, `card-ignores-external-url`, `card-without-security-requirement`, `plain-http-on-tls`, `second-start-allowed`, `worker-keeps-extensions`, `worker-not-marked`, `worker-inherits-secrets`, `passenv-ignored`, `session-id-ignores-principal`, `session-id-ignores-context`, `session-dir-shared`, `worker-cwd-not-set`, `tools-allowlist-dropped`, `session-not-kept`, `abort-not-sent`, `no-kill-after-grace`, `no-terminate`, `model-error-text-copied`, `model-error-not-a-failure`, `streamed-and-final-text-duplicated`, `empty-prompt-sent`, `tool-updates-dropped`, `ui-request-hangs`, `rejected-prompt-ignored`, `any-protocol-version-accepted`, `credentials-follow-the-card`, `transport-host-not-checked`, `redirects-followed-with-credentials`, `bearer-not-sent`, `missing-bearer-env-tolerated`, `custom-headers-dropped`, `remote-not-cancelled-on-abort`, `timeout-ignored`, `unknown-agent-unnamed`, `names-unsorted`, `empty-message-sent`, `input-required-called-terminal`, `failure-text-hidden`, `auth-error-shows-token-context`, `switch-drops-listener`, `shutdown-keeps-listener`, `config-error-silent`, `start-failure-silent`, `flag-not-read`, `agent-dir-ignored`, `stop-does-not-reload-config`, `status-hides-remotes`, `tool-required-not-enforced`, `tool-empty-message-allowed`, `tool-errors-not-errors`, `unfinished-task-not-flagged`, `task-action-unchecked`, `send-drops-context`, `tls-load-failure-ignored`, `subscribe-ownership-unchecked`, `queued-cancel-ignored-at-slot`, `queued-cancel-ignored-at-context-lock`, `shutdown-does-not-close-the-port`, `insecure-host-unchecked`, `transport-scheme-not-checked`, `card-scheme-downgrade-accepted`, `context-lock-shared-across-tenants`, `left-behind-child-survives`
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
## Re-verified on PiG 0.4.1 (porter-verify)
|
|
122
|
+
|
|
123
|
+
The golden traces were recorded again from the port itself (`--self`, a regression baseline), with PiG 0.4.1 content (`5f948f86a`, `pig --version`
|
|
124
|
+
`0.3.1+1.0.1`) and normalizer v3, because Pi 1.0.x changed the trace format: a `prompt` response now carries
|
|
125
|
+
`data.disposition` (Pi 0.99.0, #9098). An event-by-event diff against the previous traces shows no other
|
|
126
|
+
difference, and `pigeq check` passes on the Go port. Details: `docs/plan/progress/porter-verify.md`.
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
{"kind":"header","scenario":"flag-without-auth-refused","lane":"pig-go-self","host":"pig 0.3.1+1.0.1","extension":"sha256:766f7013020ffc915c35c324d2997a21f7274842e4e3a5c598801682e171d7fd","normalizer":"v3"}
|
|
2
|
+
{"step":"00-startup","ch":"ui","data":{"message":"a2a: a listener needs at least one entry in tokens (or insecureNoAuth on a loopback address)","method":"notify","notifyType":"error"}}
|
|
3
|
+
{"step":"01-status","ch":"ui","data":{"message":"a2a: listener off (a2a: a listener needs at least one entry in tokens (or insecureNoAuth on a loopback address))","method":"notify","notifyType":"info"}}
|
|
4
|
+
{"step":"01-status","ch":"response","data":{"command":"prompt","data":{"disposition":"handled"},"success":true}}
|
|
5
|
+
{"step":"99-shutdown","ch":"exit","data":{"code":0}}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
{"kind":"header","scenario":"listener-off","lane":"pig-go-self","host":"pig 0.3.1+1.0.1","extension":"sha256:766f7013020ffc915c35c324d2997a21f7274842e4e3a5c598801682e171d7fd","normalizer":"v3"}
|
|
2
|
+
{"step":"01-status","ch":"ui","data":{"message":"a2a: listener off (set listen in a2a.json, PIG_A2A_LISTEN or --a2a-listen to serve tasks)","method":"notify","notifyType":"info"}}
|
|
3
|
+
{"step":"01-status","ch":"response","data":{"command":"prompt","data":{"disposition":"handled"},"success":true}}
|
|
4
|
+
{"step":"99-shutdown","ch":"exit","data":{"code":0}}
|