@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,656 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "listener-starts-without-config",
|
|
4
|
+
"file": "extension.go",
|
|
5
|
+
"find": "\tif !cfg.Enabled() {\n\t\treturn\n\t}",
|
|
6
|
+
"replace": "\tif false {\n\t\treturn\n\t}"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"name": "worker-child-may-listen",
|
|
10
|
+
"file": "config.go",
|
|
11
|
+
"find": "\t\tcfg.Listen = \"\"\n\t}\n\tif cfg.StateDir",
|
|
12
|
+
"replace": "\t\t_ = 0\n\t}\n\tif cfg.StateDir"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"name": "flag-not-highest-precedence",
|
|
16
|
+
"file": "config.go",
|
|
17
|
+
"find": "\tif o.FlagListen != \"\" {\n\t\tcfg.Listen = o.FlagListen\n\t}",
|
|
18
|
+
"replace": "\tif o.FlagListen != \"\" && cfg.Listen == \"\" {\n\t\tcfg.Listen = o.FlagListen\n\t}"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "env-not-over-file",
|
|
22
|
+
"file": "config.go",
|
|
23
|
+
"find": "\tif v := getenv(\"PIG_A2A_LISTEN\"); v != \"\" {",
|
|
24
|
+
"replace": "\tif v := getenv(\"PIG_A2A_LISTEN\"); v != \"\" && cfg.Listen == \"\" {"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": "unknown-keys-and-inline-secrets-accepted",
|
|
28
|
+
"file": "config.go",
|
|
29
|
+
"find": "\t\t\tdec.DisallowUnknownFields()\n",
|
|
30
|
+
"replace": ""
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "listener-without-tokens-allowed",
|
|
34
|
+
"file": "config.go",
|
|
35
|
+
"find": "\t\tcase !c.InsecureNoAuth && len(c.Tokens) == 0:",
|
|
36
|
+
"replace": "\t\tcase false:"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"name": "insecure-no-auth-off-loopback",
|
|
40
|
+
"file": "config.go",
|
|
41
|
+
"find": "\t\tcase c.InsecureNoAuth && !isLoopbackHost(host):",
|
|
42
|
+
"replace": "\t\tcase false:"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"name": "insecure-and-tokens-both",
|
|
46
|
+
"file": "config.go",
|
|
47
|
+
"find": "\t\tcase c.InsecureNoAuth && len(c.Tokens) > 0:",
|
|
48
|
+
"replace": "\t\tcase false:"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"name": "short-tokens-allowed",
|
|
52
|
+
"file": "config.go",
|
|
53
|
+
"find": "\t\tif len(v) < minTokenLength {",
|
|
54
|
+
"replace": "\t\tif false {"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "duplicate-token-values-allowed",
|
|
58
|
+
"file": "config.go",
|
|
59
|
+
"find": "\t\tif values[v] {",
|
|
60
|
+
"replace": "\t\tif false {"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"name": "duplicate-token-names-allowed",
|
|
64
|
+
"file": "config.go",
|
|
65
|
+
"find": "\t\tif names[t.Name] || envs[t.TokenEnv] {",
|
|
66
|
+
"replace": "\t\tif false {"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"name": "tenant-not-validated",
|
|
70
|
+
"file": "config.go",
|
|
71
|
+
"find": "\t\tif t.Tenant != \"\" && !identRE.MatchString(t.Tenant) {",
|
|
72
|
+
"replace": "\t\tif false {"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"name": "missing-token-env-allowed",
|
|
76
|
+
"file": "config.go",
|
|
77
|
+
"find": "\t\tif v == \"\" {\n\t\t\treturn fmt.Errorf(\"a2a: token %q: environment variable %s is not set\", t.Name, t.TokenEnv)",
|
|
78
|
+
"replace": "\t\tif false {\n\t\t\treturn fmt.Errorf(\"a2a: token %q: environment variable %s is not set\", t.Name, t.TokenEnv)"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"name": "listen-not-host-port",
|
|
82
|
+
"file": "config.go",
|
|
83
|
+
"find": "\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"a2a: listen %q is not host:port: %w\", c.Listen, err)\n\t\t}",
|
|
84
|
+
"replace": "\t\t_ = err"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"name": "worker-gets-pig-default-tools",
|
|
88
|
+
"file": "worker.go",
|
|
89
|
+
"find": "\t} else {\n\t\t// PiG's file tools are not confined to the working directory, so a worker gets no tools unless named.\n\t\targs = append(args, \"--no-tools\")\n\t}",
|
|
90
|
+
"replace": "\t}"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"name": "remote-url-scheme-unchecked",
|
|
94
|
+
"file": "config.go",
|
|
95
|
+
"find": "\tif err != nil || u.Host == \"\" || (u.Scheme != \"http\" && u.Scheme != \"https\") {",
|
|
96
|
+
"replace": "\tif err != nil || u.Host == \"\" {"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"name": "remote-name-unchecked",
|
|
100
|
+
"file": "config.go",
|
|
101
|
+
"find": "\t\tif !identRE.MatchString(name) {\n\t\t\treturn fmt.Errorf(\"a2a: remote name",
|
|
102
|
+
"replace": "\t\tif false {\n\t\t\treturn fmt.Errorf(\"a2a: remote name"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"name": "basic-scheme-accepted",
|
|
106
|
+
"file": "auth.go",
|
|
107
|
+
"find": "!strings.EqualFold(fields[0], \"bearer\")",
|
|
108
|
+
"replace": "false"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"name": "extra-fields-accepted",
|
|
112
|
+
"file": "auth.go",
|
|
113
|
+
"find": "len(fields) != 2 ||",
|
|
114
|
+
"replace": "len(fields) < 2 ||"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"name": "token-comparison-inverted",
|
|
118
|
+
"file": "auth.go",
|
|
119
|
+
"find": "subtle.ConstantTimeCompare(digest[:], e.digest[:]) == 1",
|
|
120
|
+
"replace": "subtle.ConstantTimeCompare(digest[:], e.digest[:]) != 1"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"name": "insecure-flag-ignored",
|
|
124
|
+
"file": "auth.go",
|
|
125
|
+
"find": "\tif a.insecure {\n\t\treturn Principal{Name: \"anonymous\"}, true\n\t}\n\tfields",
|
|
126
|
+
"replace": "\tfields"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"name": "tenant-key-uses-token-name",
|
|
130
|
+
"file": "auth.go",
|
|
131
|
+
"find": "\t\treturn \"tenant:\" + p.Tenant",
|
|
132
|
+
"replace": "\t\treturn \"tenant:\" + p.Name"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"name": "key-namespaces-collide",
|
|
136
|
+
"file": "auth.go",
|
|
137
|
+
"find": "\treturn \"token:\" + p.Name",
|
|
138
|
+
"replace": "\treturn \"tenant:\" + p.Name"
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"name": "everything-public",
|
|
142
|
+
"file": "auth.go",
|
|
143
|
+
"find": "\t\tif isPublic != nil && isPublic(r) {",
|
|
144
|
+
"replace": "\t\tif true {"
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"name": "no-bearer-challenge",
|
|
148
|
+
"file": "auth.go",
|
|
149
|
+
"find": "\t\t\tw.Header().Set(\"WWW-Authenticate\", \"Bearer\")\n",
|
|
150
|
+
"replace": ""
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"name": "empty-authenticator-allowed",
|
|
154
|
+
"file": "auth.go",
|
|
155
|
+
"find": "\tif len(tokens) == 0 {\n\t\treturn nil, errors.New",
|
|
156
|
+
"replace": "\tif false {\n\t\treturn nil, errors.New"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"name": "file-parts-dropped",
|
|
160
|
+
"file": "executor.go",
|
|
161
|
+
"find": "\t\tif _, ok := p.Content.(a2a.Text); !ok {",
|
|
162
|
+
"replace": "\t\tif _, ok := p.Content.(a2a.Text); false && !ok {"
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"name": "empty-prompt-accepted",
|
|
166
|
+
"file": "executor.go",
|
|
167
|
+
"find": "\tif strings.TrimSpace(prompt) == \"\" {",
|
|
168
|
+
"replace": "\tif false {"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"name": "prompt-size-unbounded",
|
|
172
|
+
"file": "executor.go",
|
|
173
|
+
"find": "\tif len(prompt) > maxPromptBytes {",
|
|
174
|
+
"replace": "\tif false {"
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"name": "timeout-never-fires",
|
|
178
|
+
"file": "executor.go",
|
|
179
|
+
"find": "timeoutTimer := time.AfterFunc(e.timeout, func() {",
|
|
180
|
+
"replace": "timeoutTimer := time.AfterFunc(24*time.Hour, func() {"
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"name": "worker-error-detail-leaks",
|
|
184
|
+
"file": "executor.go",
|
|
185
|
+
"find": "yield(e.status(ec, a2a.TaskStateFailed, \"PiG could not complete the task.\"), nil)",
|
|
186
|
+
"replace": "yield(e.status(ec, a2a.TaskStateFailed, \"PiG could not complete the task: \"+ev.err.Error()), nil)"
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"name": "context-not-serialised",
|
|
190
|
+
"file": "executor.go",
|
|
191
|
+
"find": "\t\tunlock, err := e.lockContext(runCtx, principal.Key()+\"\\x00\"+ec.ContextID)",
|
|
192
|
+
"replace": "\t\tunlock, err := e.lockContext(runCtx, string(ec.TaskID))"
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"name": "concurrency-limit-ignored",
|
|
196
|
+
"file": "executor.go",
|
|
197
|
+
"find": "\t\tcase e.sem <- struct{}{}:\n\t\t\tdefer func() { <-e.sem }()",
|
|
198
|
+
"replace": "\t\tcase e.sem <- struct{}{}:\n\t\t\t<-e.sem"
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"name": "cancel-does-not-stop-worker",
|
|
202
|
+
"file": "executor.go",
|
|
203
|
+
"find": "\t\t\tr.reason.CompareAndSwap(int32(stopNone), int32(stopCanceled))\n\t\t\tr.cancel()",
|
|
204
|
+
"replace": "\t\t\tr.reason.CompareAndSwap(int32(stopNone), int32(stopCanceled))"
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"name": "failure-reason-dropped",
|
|
208
|
+
"file": "executor.go",
|
|
209
|
+
"find": "\"PiG could not complete the task: \"+ev.result.Failure",
|
|
210
|
+
"replace": "\"PiG could not complete the task.\""
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"name": "principal-not-checked",
|
|
214
|
+
"file": "executor.go",
|
|
215
|
+
"find": "\tif p.Key() != ec.User.Name {\n\t\treturn Principal{}, a2a.ErrUnauthenticated\n\t}",
|
|
216
|
+
"replace": ""
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"name": "version-not-enforced",
|
|
220
|
+
"file": "server.go",
|
|
221
|
+
"find": "!ok || !versionsOK(v)",
|
|
222
|
+
"replace": "false && (!ok || !versionsOK(v))"
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"name": "absent-version-accepted",
|
|
226
|
+
"file": "server.go",
|
|
227
|
+
"find": "if v, ok := callCtx.ServiceParams().Get(a2a.SvcParamVersion); !ok || !versionsOK(v) {",
|
|
228
|
+
"replace": "if v, ok := callCtx.ServiceParams().Get(a2a.SvcParamVersion); ok && !versionsOK(v) {"
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
"name": "minor-versions-accepted",
|
|
232
|
+
"file": "server.go",
|
|
233
|
+
"find": "return v == string(ProtocolVersion) || strings.HasPrefix(v, string(ProtocolVersion)+\".\")",
|
|
234
|
+
"replace": "return strings.HasPrefix(v, \"1\") || strings.HasPrefix(v, \"0\")"
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
"name": "mixed-version-values-accepted",
|
|
238
|
+
"file": "server.go",
|
|
239
|
+
"find": "\t\t\tif !versionOK(strings.TrimSpace(one)) {\n\t\t\t\treturn false\n\t\t\t}",
|
|
240
|
+
"replace": "\t\t\tif !versionOK(strings.TrimSpace(one)) {\n\t\t\t\tcontinue\n\t\t\t}"
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
"name": "tenant-not-enforced",
|
|
244
|
+
"file": "server.go",
|
|
245
|
+
"find": "t != \"\" && t != p.Tenant",
|
|
246
|
+
"replace": "false && t != \"\" && t != p.Tenant"
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
"name": "user-not-attached",
|
|
250
|
+
"file": "server.go",
|
|
251
|
+
"find": "\tcallCtx.User = a2asrv.NewAuthenticatedUser(p.Key(), map[string]any{\"name\": p.Name, \"tenant\": p.Tenant})\n",
|
|
252
|
+
"replace": ""
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
"name": "body-size-unbounded",
|
|
256
|
+
"file": "server.go",
|
|
257
|
+
"find": "http.MaxBytesHandler(a2asrv.NewJSONRPCHandler(handler), maxBodyBytes)",
|
|
258
|
+
"replace": "a2asrv.NewJSONRPCHandler(handler)"
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
"name": "card-requires-auth",
|
|
262
|
+
"file": "server.go",
|
|
263
|
+
"find": "func(r *http.Request) bool { return r.URL.Path == a2asrv.WellKnownAgentCardPath }",
|
|
264
|
+
"replace": "func(r *http.Request) bool { return false }"
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
"name": "card-drops-streaming",
|
|
268
|
+
"file": "server.go",
|
|
269
|
+
"find": "Capabilities: a2a.AgentCapabilities{Streaming: true},",
|
|
270
|
+
"replace": "Capabilities: a2a.AgentCapabilities{Streaming: false},"
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
"name": "card-claims-push",
|
|
274
|
+
"file": "server.go",
|
|
275
|
+
"find": "Capabilities: a2a.AgentCapabilities{Streaming: true},",
|
|
276
|
+
"replace": "Capabilities: a2a.AgentCapabilities{Streaming: true, PushNotifications: true},"
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
"name": "card-ignores-external-url",
|
|
280
|
+
"file": "server.go",
|
|
281
|
+
"find": "\tif s.cfg.ExternalURL != \"\" {\n\t\treturn strings.TrimRight(s.cfg.ExternalURL, \"/\")\n\t}\n",
|
|
282
|
+
"replace": ""
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
"name": "card-without-security-requirement",
|
|
286
|
+
"file": "server.go",
|
|
287
|
+
"find": "\t\t\ta2a.SecurityRequirements{\"bearer\": a2a.SecuritySchemeScopes{}},\n",
|
|
288
|
+
"replace": ""
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
"name": "plain-http-on-tls",
|
|
292
|
+
"file": "server.go",
|
|
293
|
+
"find": "\t\tln = tls.NewListener(ln, srv.TLSConfig)\n",
|
|
294
|
+
"replace": ""
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
"name": "second-start-allowed",
|
|
298
|
+
"file": "server.go",
|
|
299
|
+
"find": "\tif s.srv != nil {\n\t\treturn errors.New(\"a2a: server already started\")\n\t}\n",
|
|
300
|
+
"replace": ""
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
"name": "worker-keeps-extensions",
|
|
304
|
+
"file": "worker.go",
|
|
305
|
+
"find": "\"--no-extensions\", ",
|
|
306
|
+
"replace": ""
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
"name": "worker-not-marked",
|
|
310
|
+
"file": "worker.go",
|
|
311
|
+
"find": "\treturn append(env, \"PIG_A2A_WORKER=1\", \"PI_SKIP_VERSION_CHECK=1\")",
|
|
312
|
+
"replace": "\treturn append(env, \"PI_SKIP_VERSION_CHECK=1\")"
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
"name": "worker-inherits-secrets",
|
|
316
|
+
"file": "worker.go",
|
|
317
|
+
"find": "\t\"PATH\", \"HOME\", \"USER\",",
|
|
318
|
+
"replace": "\t\"TOKEN_A\", \"PATH\", \"HOME\", \"USER\","
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
"name": "passenv-ignored",
|
|
322
|
+
"file": "worker.go",
|
|
323
|
+
"find": "\tfor _, name := range append(append([]string{}, baseEnv...), w.cfg.PassEnv...) {",
|
|
324
|
+
"replace": "\tfor _, name := range append([]string{}, baseEnv...) {"
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
"name": "session-id-ignores-principal",
|
|
328
|
+
"file": "worker.go",
|
|
329
|
+
"find": "\treturn keyDigest(\"a2a-session\\x00\" + p.Key() + \"\\x00\" + contextID)[:32]",
|
|
330
|
+
"replace": "\treturn keyDigest(\"a2a-session\\x00\" + contextID)[:32]"
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
"name": "session-id-ignores-context",
|
|
334
|
+
"file": "worker.go",
|
|
335
|
+
"find": "\treturn keyDigest(\"a2a-session\\x00\" + p.Key() + \"\\x00\" + contextID)[:32]",
|
|
336
|
+
"replace": "\treturn keyDigest(\"a2a-session\\x00\" + p.Key())[:32]"
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
"name": "session-dir-shared",
|
|
340
|
+
"file": "worker.go",
|
|
341
|
+
"find": "keyDigest(\"a2a-dir\\x00\" + p.Key())[:16])\n}\n\nfunc (w *ProcessWorker) cwd",
|
|
342
|
+
"replace": "\"shared\")\n}\n\nfunc (w *ProcessWorker) cwd"
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
"name": "worker-cwd-not-set",
|
|
346
|
+
"file": "worker.go",
|
|
347
|
+
"find": "\tcmd.Dir, cmd.Env = w.cwd(t.Principal), w.env()",
|
|
348
|
+
"replace": "\tcmd.Env = w.env()"
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
"name": "tools-allowlist-dropped",
|
|
352
|
+
"file": "worker.go",
|
|
353
|
+
"find": "\t\targs = append(args, \"--tools\", strings.Join(w.cfg.Tools, \",\"))",
|
|
354
|
+
"replace": "\t\t_ = args"
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
"name": "session-not-kept",
|
|
358
|
+
"file": "worker.go",
|
|
359
|
+
"find": "\targs = append(args, \"--session-dir\", w.sessionDir(t.Principal), \"--session-id\", SessionID(t.Principal, t.ContextID))",
|
|
360
|
+
"replace": "\targs = append(args, \"--no-session\")"
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
"name": "abort-not-sent",
|
|
364
|
+
"file": "worker.go",
|
|
365
|
+
"find": "\t\t\t_ = write(map[string]any{\"id\": \"abort\", \"type\": \"abort\"})",
|
|
366
|
+
"replace": "\t\t\t_ = 0"
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
"name": "no-kill-after-grace",
|
|
370
|
+
"file": "worker.go",
|
|
371
|
+
"find": "\t\t\t\t_ = killTree(cmd.Process)\n\t\t\t\t<-exited",
|
|
372
|
+
"replace": "\t\t\t\t<-exited"
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
"name": "no-terminate",
|
|
376
|
+
"file": "worker.go",
|
|
377
|
+
"find": "\t\t\t_ = terminateTree(cmd.Process)\n",
|
|
378
|
+
"replace": ""
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
"name": "model-error-text-copied",
|
|
382
|
+
"file": "worker.go",
|
|
383
|
+
"find": "return Result{Text: text.String(), Failure: \"the model call failed\"}, nil",
|
|
384
|
+
"replace": "return Result{Text: text.String(), Failure: \"the model call failed\" + lastStop}, nil"
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
"name": "model-error-not-a-failure",
|
|
388
|
+
"file": "worker.go",
|
|
389
|
+
"find": "\t\t\t\tcase \"error\":\n\t\t\t\t\treturn Result{Text: text.String(), Failure: \"the model call failed\"}, nil",
|
|
390
|
+
"replace": "\t\t\t\tcase \"error\":\n\t\t\t\t\treturn Result{Text: text.String()}, nil"
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
"name": "streamed-and-final-text-duplicated",
|
|
394
|
+
"file": "worker.go",
|
|
395
|
+
"find": "if f := final.String(); len(f) > len(msgText) && strings.HasPrefix(f, msgText) {\n\t\t\t\t\t\temit(f[len(msgText):])",
|
|
396
|
+
"replace": "if f := final.String(); len(f) > 0 {\n\t\t\t\t\t\temit(f)"
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
"name": "empty-prompt-sent",
|
|
400
|
+
"file": "worker.go",
|
|
401
|
+
"find": "\tif strings.TrimSpace(t.Prompt) == \"\" {",
|
|
402
|
+
"replace": "\tif false {"
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
"name": "tool-updates-dropped",
|
|
406
|
+
"file": "worker.go",
|
|
407
|
+
"find": "\t\t\t\tif !aborting && ev.ToolName != \"\" {\n\t\t\t\t\tonUpdate(Update{Tool: ev.ToolName})",
|
|
408
|
+
"replace": "\t\t\t\tif false {\n\t\t\t\t\tonUpdate(Update{Tool: ev.ToolName})"
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
"name": "ui-request-hangs",
|
|
412
|
+
"file": "worker.go",
|
|
413
|
+
"find": "\t\t\tcase \"extension_ui_request\":\n\t\t\t\tresult.Failure = \"PiG asked for interactive input, which an A2A task cannot provide\"\n\t\t\t\treturn result, runErr",
|
|
414
|
+
"replace": "\t\t\tcase \"extension_ui_request\":"
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
"name": "rejected-prompt-ignored",
|
|
418
|
+
"file": "worker.go",
|
|
419
|
+
"find": "\t\t\t\t\tif !ev.Success {\n\t\t\t\t\t\treturn Result{Failure: \"PiG rejected the prompt\"}, nil\n\t\t\t\t\t}",
|
|
420
|
+
"replace": "\t\t\t\t\t_ = ev.Success"
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
"name": "any-protocol-version-accepted",
|
|
424
|
+
"file": "client.go",
|
|
425
|
+
"find": "if i.ProtocolBinding != a2a.TransportProtocolJSONRPC || !versionOK(string(i.ProtocolVersion)) {",
|
|
426
|
+
"replace": "if i.ProtocolBinding != a2a.TransportProtocolJSONRPC {"
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
"name": "credentials-follow-the-card",
|
|
430
|
+
"file": "client.go",
|
|
431
|
+
"find": "if ra.hasCredentials() && originOf(i.URL) != originOf(ra.URL) {",
|
|
432
|
+
"replace": "if false {"
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
"name": "transport-host-not-checked",
|
|
436
|
+
"file": "client.go",
|
|
437
|
+
"find": "\tif req.URL.Host != t.host {",
|
|
438
|
+
"replace": "\tif false {"
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
"name": "redirects-followed-with-credentials",
|
|
442
|
+
"file": "client.go",
|
|
443
|
+
"find": "CheckRedirect: func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse }",
|
|
444
|
+
"replace": "CheckRedirect: nil"
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
"name": "bearer-not-sent",
|
|
448
|
+
"file": "client.go",
|
|
449
|
+
"find": "\t\th.Set(\"Authorization\", \"Bearer \"+v)",
|
|
450
|
+
"replace": "\t\t_ = v"
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
"name": "missing-bearer-env-tolerated",
|
|
454
|
+
"file": "client.go",
|
|
455
|
+
"find": "\t\tif v == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"a2a: remote %q needs the environment variable %s (bearerTokenEnv), which is not set\", name, ra.BearerTokenEnv)\n\t\t}",
|
|
456
|
+
"replace": ""
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
"name": "custom-headers-dropped",
|
|
460
|
+
"file": "client.go",
|
|
461
|
+
"find": "\t\th.Set(header, v)",
|
|
462
|
+
"replace": "\t\t_ = v"
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
"name": "remote-not-cancelled-on-abort",
|
|
466
|
+
"file": "client.go",
|
|
467
|
+
"find": "if sum.TaskID != \"\" && (caller.Err() != nil || call.Err() != nil) {",
|
|
468
|
+
"replace": "if false {"
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
"name": "timeout-ignored",
|
|
472
|
+
"file": "client.go",
|
|
473
|
+
"find": "\treturn context.WithTimeout(ctx, time.Duration(ra.TimeoutSeconds)*time.Second)",
|
|
474
|
+
"replace": "\treturn context.WithCancel(ctx)"
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
"name": "unknown-agent-unnamed",
|
|
478
|
+
"file": "client.go",
|
|
479
|
+
"find": "return RemoteAgent{}, fmt.Errorf(\"a2a: unknown agent %q; configured remotes: %s\", name, strings.Join(r.Names(), \", \"))",
|
|
480
|
+
"replace": "return RemoteAgent{}, fmt.Errorf(\"a2a: unknown agent %q\", name)"
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
"name": "names-unsorted",
|
|
484
|
+
"file": "client.go",
|
|
485
|
+
"find": "\tsort.Strings(names)\n",
|
|
486
|
+
"replace": "\t_ = sort.Strings\n"
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
"name": "empty-message-sent",
|
|
490
|
+
"file": "client.go",
|
|
491
|
+
"find": "\tif strings.TrimSpace(args.Message) == \"\" {",
|
|
492
|
+
"replace": "\tif false {"
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
"name": "input-required-called-terminal",
|
|
496
|
+
"file": "client.go",
|
|
497
|
+
"find": "Terminal: t.Status.State.Terminal()},",
|
|
498
|
+
"replace": "Terminal: true},"
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
"name": "failure-text-hidden",
|
|
502
|
+
"file": "client.go",
|
|
503
|
+
"find": "\tif statusText != \"\" && state != a2a.TaskStateCompleted {",
|
|
504
|
+
"replace": "\tif false {"
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
"name": "auth-error-shows-token-context",
|
|
508
|
+
"file": "client.go",
|
|
509
|
+
"find": "\tcase strings.Contains(err.Error(), \"401\"):",
|
|
510
|
+
"replace": "\tcase false:"
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
"name": "switch-drops-listener",
|
|
514
|
+
"file": "extension.go",
|
|
515
|
+
"find": "\t\tcase \"new\", \"resume\", \"fork\":",
|
|
516
|
+
"replace": "\t\tcase \"resume\", \"fork\":"
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
"name": "shutdown-keeps-listener",
|
|
520
|
+
"file": "extension.go",
|
|
521
|
+
"find": "\t\tm.stop()\n\t\treturn nil, nil\n\t})",
|
|
522
|
+
"replace": "\t\treturn nil, nil\n\t})"
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
"name": "config-error-silent",
|
|
526
|
+
"file": "extension.go",
|
|
527
|
+
"find": "\tcfg, err := m.load(ctx)\n\tif err != nil {\n\t\tctx.Notify(err.Error(), \"error\")\n\t\treturn\n\t}",
|
|
528
|
+
"replace": "\tcfg, err := m.load(ctx)\n\tif err != nil {\n\t\treturn\n\t}"
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
"name": "start-failure-silent",
|
|
532
|
+
"file": "extension.go",
|
|
533
|
+
"find": "\tif err != nil {\n\t\tctx.Notify(err.Error(), \"error\")\n\t\treturn\n\t}\n\tm.mu.Lock()\n\tm.server = srv",
|
|
534
|
+
"replace": "\tif err != nil {\n\t\treturn\n\t}\n\tm.mu.Lock()\n\tm.server = srv"
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
"name": "flag-not-read",
|
|
538
|
+
"file": "extension.go",
|
|
539
|
+
"find": "\tif v, err := ctx.GetFlag(\"a2a-listen\"); err == nil {",
|
|
540
|
+
"replace": "\tif v, err := ctx.GetFlag(\"a2a-listen\"); false && err == nil {"
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
"name": "agent-dir-ignored",
|
|
544
|
+
"file": "extension.go",
|
|
545
|
+
"find": "\tif d := m.getenv(\"PIG_CODING_AGENT_DIR\"); d != \"\" {\n\t\treturn d\n\t}\n",
|
|
546
|
+
"replace": ""
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
"name": "stop-does-not-reload-config",
|
|
550
|
+
"file": "extension.go",
|
|
551
|
+
"find": "\tm.remotes, m.cfgErr = nil, nil // the next use reloads the configuration",
|
|
552
|
+
"replace": "\t_ = 0"
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
"name": "status-hides-remotes",
|
|
556
|
+
"file": "extension.go",
|
|
557
|
+
"find": "\t\t\tfmt.Fprintf(&b, \"\\nremotes: %s\", strings.Join(names, \", \"))",
|
|
558
|
+
"replace": "\t\t\t_ = names"
|
|
559
|
+
},
|
|
560
|
+
{
|
|
561
|
+
"name": "tool-required-not-enforced",
|
|
562
|
+
"file": "extension.go",
|
|
563
|
+
"find": "\t\tif required {\n\t\t\treturn \"\", sdk.NewToolError(fmt.Sprintf(\"a2a: %s is required\", name))\n\t\t}",
|
|
564
|
+
"replace": "\t\t_ = required"
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
"name": "tool-empty-message-allowed",
|
|
568
|
+
"file": "extension.go",
|
|
569
|
+
"find": "\tif required && strings.TrimSpace(s) == \"\" {",
|
|
570
|
+
"replace": "\tif false {"
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
"name": "tool-errors-not-errors",
|
|
574
|
+
"file": "extension.go",
|
|
575
|
+
"find": "\t\treturn nil, sdk.NewToolError(\"a2a: no remotes are configured; add a \\\"remotes\\\" object to a2a.json\")",
|
|
576
|
+
"replace": "\t\treturn nil, nil"
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
"name": "unfinished-task-not-flagged",
|
|
580
|
+
"file": "extension.go",
|
|
581
|
+
"find": "\tif !s.Terminal {\n\t\tb.WriteString",
|
|
582
|
+
"replace": "\tif false {\n\t\tb.WriteString"
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
"name": "task-action-unchecked",
|
|
586
|
+
"file": "extension.go",
|
|
587
|
+
"find": "\treturn nil, sdk.NewToolError(fmt.Sprintf(\"a2a: unknown action %q (get, cancel or list)\", action))",
|
|
588
|
+
"replace": "\treturn \"ok\", nil"
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
"name": "send-drops-context",
|
|
592
|
+
"file": "extension.go",
|
|
593
|
+
"find": "SendArgs{Agent: agent, Message: message, ContextID: contextID, TaskID: taskID}",
|
|
594
|
+
"replace": "SendArgs{Agent: agent, Message: message, ContextID: contextID[:0], TaskID: taskID}"
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
"name": "tls-load-failure-ignored",
|
|
598
|
+
"file": "server.go",
|
|
599
|
+
"find": "\t\tcert, err := tls.LoadX509KeyPair(s.cfg.TLS.CertFile, s.cfg.TLS.KeyFile)\n\t\tif err != nil {\n\t\t\t_ = ln.Close()\n\t\t\treturn fmt.Errorf(\"a2a: load TLS key pair: %w\", err)\n\t\t}",
|
|
600
|
+
"replace": "\t\tcert, err := tls.LoadX509KeyPair(s.cfg.TLS.CertFile, s.cfg.TLS.KeyFile)\n\t\tif err != nil {\n\t\t\t_ = ln.Close()\n\t\t\treturn nil\n\t\t}"
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
"name": "subscribe-ownership-unchecked",
|
|
604
|
+
"file": "server.go",
|
|
605
|
+
"find": "\t\tif _, err := g.store.Get(ctx, sub.ID); err != nil {",
|
|
606
|
+
"replace": "\t\tif _, err := g.store.Get(ctx, sub.ID); false && err != nil {"
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
"name": "queued-cancel-ignored-at-slot",
|
|
610
|
+
"file": "executor.go",
|
|
611
|
+
"find": "\t\tcase <-runCtx.Done():\n\t\t\te.finishStopped(ec, r, yield, runCtx)\n\t\t\treturn\n\t\t}\n\t\tunlock, err",
|
|
612
|
+
"replace": "\t\tcase <-time.After(24 * time.Hour):\n\t\t\te.finishStopped(ec, r, yield, runCtx)\n\t\t\treturn\n\t\t}\n\t\tunlock, err"
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
"name": "queued-cancel-ignored-at-context-lock",
|
|
616
|
+
"file": "executor.go",
|
|
617
|
+
"find": "\tcase <-ctx.Done():\n\t\trelease()\n\t\treturn nil, ctx.Err()",
|
|
618
|
+
"replace": "\tcase <-time.After(24 * time.Hour):\n\t\trelease()\n\t\treturn nil, ctx.Err()"
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
"name": "shutdown-does-not-close-the-port",
|
|
622
|
+
"file": "server.go",
|
|
623
|
+
"find": "\t\t_ = ln.Close()\n\t\t<-served\n",
|
|
624
|
+
"replace": "\t\t_, _ = ln, served\n"
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
"name": "insecure-host-unchecked",
|
|
628
|
+
"file": "auth.go",
|
|
629
|
+
"find": "\t\tif a.insecure && !loopbackHostHeader(r.Host) {",
|
|
630
|
+
"replace": "\t\tif false {"
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
"name": "transport-scheme-not-checked",
|
|
634
|
+
"file": "client.go",
|
|
635
|
+
"find": "\tif req.URL.Scheme != t.scheme {",
|
|
636
|
+
"replace": "\tif false {"
|
|
637
|
+
},
|
|
638
|
+
{
|
|
639
|
+
"name": "card-scheme-downgrade-accepted",
|
|
640
|
+
"file": "client.go",
|
|
641
|
+
"find": "if ra.hasCredentials() && originOf(i.URL) != originOf(ra.URL) {",
|
|
642
|
+
"replace": "if ra.hasCredentials() && hostOf(i.URL) != hostOf(ra.URL) {"
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
"name": "context-lock-shared-across-tenants",
|
|
646
|
+
"file": "executor.go",
|
|
647
|
+
"find": "e.lockContext(runCtx, principal.Key()+\"\\x00\"+ec.ContextID)",
|
|
648
|
+
"replace": "e.lockContext(runCtx, ec.ContextID)"
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
"name": "left-behind-child-survives",
|
|
652
|
+
"file": "worker.go",
|
|
653
|
+
"find": "\t\t// A tool the worker started can outlive it.\n\t\t_ = killTree(cmd.Process)\n",
|
|
654
|
+
"replace": ""
|
|
655
|
+
}
|
|
656
|
+
]
|