diff --git a/cmd/pitot/doctor_host.go b/cmd/pitot/doctor_host.go index 17db862..5b88221 100644 --- a/cmd/pitot/doctor_host.go +++ b/cmd/pitot/doctor_host.go @@ -157,12 +157,17 @@ func doctorHost(host adapters.Host, stdout, stderr io.Writer) error { fmt.Fprintf(stdout, " config file: ABSENT (%v)\n", readErr) problems = append(problems, "config file absent") default: - text := string(data) - hasEvent := strings.Contains(text, `event = "`+probe.hookEvent+`"`) - hasHookCmd := strings.Contains(text, "pitot "+hookCmd) || strings.Contains(text, hookCmd) - if hasEvent && hasHookCmd { - fmt.Fprintf(stdout, " %s hook: FOUND invoking `pitot %s`\n", probe.hookEvent, hookCmd) - } else { + status := currentKimiHookStatus(data) + switch status { + case kimiHookConfigured: + fmt.Fprintf(stdout, " %s hook: FOUND declaring `pitot %s` for %s\n", probe.hookEvent, hookCmd, probe.matcher) + case kimiHookInvalid: + fmt.Fprintln(stdout, " PreToolUse hook: INVALID — check TOML syntax and hook field names, types, events and timeout") + problems = append(problems, "hook configuration invalid") + case kimiHookUnverified: + fmt.Fprintln(stdout, " PreToolUse hook: UNVERIFIED — manually verify the matcher and command; wrappers, expansions and unsupported syntax cannot establish readiness") + problems = append(problems, "hook routing unverified") + default: fmt.Fprintf(stdout, " %s hook: MISSING — add a [[hooks]] entry (event = %q, matcher = %q, command = \"pitot %s\")\n", probe.hookEvent, probe.hookEvent, probe.matcher, hookCmd) problems = append(problems, "hook not configured") @@ -193,7 +198,7 @@ func doctorHost(host adapters.Host, stdout, stderr io.Writer) error { if len(problems) > 0 { return fmt.Errorf("pitot doctor: %s host check found %d issue(s): %s", host, len(problems), strings.Join(problems, "; ")) } - fmt.Fprintf(stdout, " ready: %s can route its shell boundary to Pitot\n", host) + fmt.Fprintf(stdout, " ready: %s declares a shell hook to Pitot; hook execution and controller connection are not checked\n", host) return nil } diff --git a/cmd/pitot/doctor_kimi.go b/cmd/pitot/doctor_kimi.go new file mode 100644 index 0000000..e35efa0 --- /dev/null +++ b/cmd/pitot/doctor_kimi.go @@ -0,0 +1,204 @@ +package main + +import ( + "path" + "runtime" + "strings" + + "github.com/pelletier/go-toml/v2" +) + +type kimiHookStatus string + +const ( + kimiHookConfigured kimiHookStatus = "FOUND" + kimiHookMissing kimiHookStatus = "MISSING" + kimiHookInvalid kimiHookStatus = "INVALID" + kimiHookUnverified kimiHookStatus = "UNVERIFIED" +) + +// inspectKimiHooks checks declarations only. It never executes a hook command, +// and recognizes a bounded command/matcher subset rather than guessing how the +// host interprets arbitrary shell scripts or regular expressions. +func inspectKimiHooks(data []byte, platform string) kimiHookStatus { + var document map[string]any + if err := toml.Unmarshal(data, &document); err != nil { + // Parser errors can contain source text, including provider credentials. + return kimiHookInvalid + } + value, present := document["hooks"] + if !present { + return kimiHookMissing + } + hooks, ok := value.([]any) + if !ok { + return kimiHookInvalid + } + configured, unverified := false, false + for _, value := range hooks { + hook, ok := value.(map[string]any) + if !ok || !validKimiHook(hook) { + return kimiHookInvalid + } + if hook["event"] != "PreToolUse" { + continue + } + matcherStatus := kimiBashMatcher(hook) + if matcherStatus == kimiHookMissing { + continue + } + commandStatus := kimiHookCommand(hook["command"].(string), platform) + if commandStatus == kimiHookMissing { + continue + } + if matcherStatus == kimiHookConfigured && commandStatus == kimiHookConfigured { + configured = true + } else { + unverified = true + } + } + if configured { + return kimiHookConfigured + } + if unverified { + return kimiHookUnverified + } + return kimiHookMissing +} + +func validKimiHook(hook map[string]any) bool { + for key := range hook { + switch key { + case "event", "matcher", "command", "timeout": + default: + return false + } + } + event, ok := hook["event"].(string) + if !ok { + return false + } + // These event names are the published Kimi hook configuration contract. + switch event { + case "UserPromptSubmit", "UserPromptQueued", "PreToolUse", "Stop", "TurnStarted", + "PostToolUse", "PostToolUseFailure", "PermissionRequest", "PermissionResult", + "SessionStart", "SessionEnd", "SessionHeartbeat", "SubagentStart", "SubagentStop", + "TaskStarted", "StopFailure", "Interrupt", "PreCompact", "PostCompact", "Notification": + default: + return false + } + if _, ok := hook["command"].(string); !ok { + return false + } + if value, present := hook["matcher"]; present { + if _, ok := value.(string); !ok { + return false + } + } + if value, present := hook["timeout"]; present { + timeout, ok := value.(int64) + if !ok || timeout < 1 || timeout > 600 { + return false + } + } + return true +} + +func kimiBashMatcher(hook map[string]any) kimiHookStatus { + value, present := hook["matcher"] + if !present { + return kimiHookConfigured + } + switch value.(string) { + case "Bash", "^Bash$", ".*": + return kimiHookConfigured + case "ReadFile": + return kimiHookMissing + default: + return kimiHookUnverified + } +} + +func kimiHookCommand(command, platform string) kimiHookStatus { + words, ok := kimiLiteralWords(command, platform) + if !ok || len(words) == 0 { + return kimiHookUnverified + } + executable := words[0] + if executable != "pitot" && !(platform != "windows" && strings.Contains(executable, "/") && path.Base(executable) == "pitot") { + if executable == "echo" || executable == "printf" { + return kimiHookMissing + } + return kimiHookUnverified + } + if len(words) < 3 || words[1] != "hook" || words[2] != "kimi" { + return kimiHookMissing + } + for i := 3; i < len(words); i += 2 { + if words[i] != "--runtime" || i+1 >= len(words) || words[i+1] == "" { + return kimiHookUnverified + } + } + return kimiHookConfigured +} + +// kimiLiteralWords accepts literal POSIX words with spaces/tabs and quotes. +// Expansions, escapes, operators and other shell syntax require manual review. +// Windows accepts the canonical bare command; quoting and executable-path +// interpretation remain unverified until the host's shell contract is known. +func kimiLiteralWords(command, platform string) ([]string, bool) { + var words []string + var word strings.Builder + var quote byte + started := false + for i := 0; i < len(command); i++ { + c := command[i] + if platform == "windows" && !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || strings.ContainsRune("-_/.: \t", rune(c))) { + return nil, false + } + if c == '\\' || c == '\n' || c == '\r' || c == 0 { + return nil, false + } + if quote != 0 { + if c == quote { + quote = 0 + } else { + if quote == '"' && (c == '$' || c == '`') { + return nil, false + } + word.WriteByte(c) + } + continue + } + switch c { + case ' ', '\t': + if started { + words = append(words, word.String()) + word.Reset() + started = false + } + case '\'', '"': + if platform == "windows" { + return nil, false + } + quote, started = c, true + case '$', '`', '|', '&', ';', '<', '>', '(', ')', '*', '?', '[', ']', '#', '~', '{', '}': + return nil, false + default: + word.WriteByte(c) + started = true + } + } + if quote != 0 { + return nil, false + } + if started { + words = append(words, word.String()) + } + return words, true +} + +func currentKimiHookStatus(data []byte) kimiHookStatus { + return inspectKimiHooks(data, runtime.GOOS) +} diff --git a/cmd/pitot/doctor_kimi_test.go b/cmd/pitot/doctor_kimi_test.go new file mode 100644 index 0000000..8b21612 --- /dev/null +++ b/cmd/pitot/doctor_kimi_test.go @@ -0,0 +1,103 @@ +package main + +import ( + "bytes" + "fmt" + "os" + "path/filepath" + "strings" + "testing" +) + +func TestKimiHookDeclarations(t *testing.T) { + hook := func(matcher, command string) string { + return fmt.Sprintf("[[hooks]]\nevent = 'PreToolUse'\nmatcher = %q\ncommand = %q\n", matcher, command) + } + for _, tc := range []struct { + name, config string + want kimiHookStatus + }{ + {"canonical", kimiHookConfig, kimiHookConfigured}, + {"literal TOML quotes", "[[hooks]]\nevent='PreToolUse'\nmatcher='Bash'\ncommand='pitot hook kimi'\n", kimiHookConfigured}, + {"inline array", "hooks=[{event='PreToolUse', matcher='Bash', command='pitot hook kimi'}]", kimiHookConfigured}, + {"comments", "# event = \"PreToolUse\"\n# command = \"pitot hook kimi\"", kimiHookMissing}, + {"unrelated values", "description='event = \"PreToolUse\"'\nnotes='pitot hook kimi'", kimiHookMissing}, + {"different entries", "[[hooks]]\nevent='PreToolUse'\ncommand='echo ordinary'\n[[hooks]]\nevent='PostToolUse'\ncommand='pitot hook kimi'", kimiHookMissing}, + {"different target", hook("ReadFile", "pitot hook kimi"), kimiHookMissing}, + {"echo", hook("Bash", "echo pitot hook kimi"), kimiHookMissing}, + {"wrong subcommand", hook("Bash", "pitot echo 'hook kimi'"), kimiHookMissing}, + {"wrong host", hook("Bash", "pitot hook claude"), kimiHookMissing}, + {"omitted matcher", "[[hooks]]\nevent='PreToolUse'\ncommand='pitot hook kimi'", kimiHookConfigured}, + {"wildcard", hook(".*", "pitot hook kimi"), kimiHookConfigured}, + {"anchored target", hook("^Bash$", "pitot hook kimi"), kimiHookConfigured}, + {"unknown regex", hook("Bash|ReadFile", "pitot hook kimi"), kimiHookUnverified}, + {"lookaround", hook("(?<=x)Bash", "pitot hook kimi"), kimiHookUnverified}, + {"empty matcher", hook("", "pitot hook kimi"), kimiHookUnverified}, + {"quoted words", hook("Bash", "'pitot' 'hook' 'kimi'"), kimiHookConfigured}, + {"quoted path", hook("Bash", "'/opt/Pitot installation/pitot' hook kimi"), kimiHookConfigured}, + {"runtime path", hook("Bash", "pitot hook kimi --runtime '/tmp/runtime path'"), kimiHookConfigured}, + {"whitespace", hook("Bash", " pitot\t hook kimi "), kimiHookConfigured}, + {"dynamic command", hook("Bash", "$(which pitot) hook kimi"), kimiHookUnverified}, + {"runtime expansion", hook("Bash", "pitot hook kimi --runtime \"$RUNTIME\""), kimiHookUnverified}, + {"pipeline", hook("Bash", "pitot hook kimi | cat"), kimiHookUnverified}, + {"separate commands", hook("Bash", "pitot hook kimi\necho ordinary"), kimiHookUnverified}, + {"wrapper", hook("Bash", "sh -c 'pitot hook kimi'"), kimiHookUnverified}, + {"script", hook("Bash", "/opt/hooks/kimi.sh"), kimiHookUnverified}, + {"unsupported arguments", hook("Bash", "pitot hook kimi --other-option"), kimiHookUnverified}, + {"missing runtime value", hook("Bash", "pitot hook kimi --runtime"), kimiHookUnverified}, + {"missing command", "[[hooks]]\nevent='PreToolUse'", kimiHookInvalid}, + {"syntax", "[[hooks]]\nevent='PreToolUse'\ncommand='pitot hook kimi", kimiHookInvalid}, + {"hooks string", "hooks='pitot hook kimi'", kimiHookInvalid}, + {"non-table", "hooks=[1]", kimiHookInvalid}, + {"command type", "[[hooks]]\nevent='PreToolUse'\ncommand=1", kimiHookInvalid}, + {"matcher type", "[[hooks]]\nevent='PreToolUse'\ncommand='pitot hook kimi'\nmatcher=1", kimiHookInvalid}, + {"timeout range", hook("Bash", "pitot hook kimi") + "timeout=601", kimiHookInvalid}, + {"timeout type", hook("Bash", "pitot hook kimi") + "timeout=true", kimiHookInvalid}, + {"extra field", hook("Bash", "pitot hook kimi") + "extra='value'", kimiHookInvalid}, + {"unknown event", "[[hooks]]\nevent='Unknown'\ncommand='pitot hook kimi'", kimiHookInvalid}, + {"invalid peer", hook("Bash", "pitot hook kimi") + "[[hooks]]\nevent='PostToolUse'\ncommand=1", kimiHookInvalid}, + {"valid peer", hook("Bash", "pitot hook kimi") + "[[hooks]]\nevent='PostToolUse'\ncommand='echo ordinary'", kimiHookConfigured}, + } { + t.Run(tc.name, func(t *testing.T) { + if got := inspectKimiHooks([]byte(tc.config), "linux"); got != tc.want { + t.Fatalf("got %s, want %s", got, tc.want) + } + }) + } +} + +func TestKimiCommandPlatformBoundaries(t *testing.T) { + for _, platform := range []string{"linux", "darwin", "windows"} { + if got := kimiHookCommand("pitot hook kimi", platform); got != kimiHookConfigured { + t.Fatalf("canonical command on %s: %s", platform, got) + } + } + for _, command := range []string{"'pitot' hook kimi", `"C:\Program Files\Pitot\pitot.exe" hook kimi`, "pitot.exe hook kimi", + "pitot hook kimi --runtime %RUNTIME%", "pitot hook kimi --runtime !RUNTIME!", + "pitot hook kimi --runtime C:/foo^bar", "pitot hook kimi --runtime C:/foo%bar", + "pitot hook kimi --runtime C:/foo!bar", "pitot hook kimi --runtime /tmp/runtime+path"} { + if got := kimiHookCommand(command, "windows"); got != kimiHookUnverified { + t.Fatalf("ambiguous Windows command %q: %s", command, got) + } + } +} + +func TestDoctorKimiParserDiagnosticDoesNotExposeConfig(t *testing.T) { + home := t.TempDir() + t.Setenv("KIMI_CODE_HOME", home) + const secret = "test-provider-value-do-not-print" + config := "provider_secret='" + secret + "'\n[[hooks]]\nevent='PreToolUse'\ncommand='unterminated" + if err := os.WriteFile(filepath.Join(home, "config.toml"), []byte(config), 0o600); err != nil { + t.Fatal(err) + } + // Keep the external agent absent: this test concerns Pitot's parser output. + t.Setenv("PATH", t.TempDir()) + var stdout, stderr bytes.Buffer + if err := doctorHost("kimi", &stdout, &stderr); err == nil { + t.Fatal("invalid configuration must fail the diagnostic") + } + output := stdout.String() + stderr.String() + if !strings.Contains(output, "hook: INVALID") || strings.Contains(output, secret) || strings.Contains(output, "unterminated") { + t.Fatalf("unexpected parser diagnostic: %s", output) + } +} diff --git a/go.mod b/go.mod index 72c1e79..9fbad07 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.26 require ( github.com/invopop/jsonschema v0.14.0 + github.com/pelletier/go-toml/v2 v2.4.3 go.yaml.in/yaml/v4 v4.0.0-rc.2 golang.org/x/sys v0.42.0 ) diff --git a/go.sum b/go.sum index c9dfa4b..4f215ef 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/invopop/jsonschema v0.14.0 h1:MHQqLhvpNUZfw+hM3AZDYK7jxO8FZoQeQM77g8i github.com/invopop/jsonschema v0.14.0/go.mod h1:ygm6C2EaVNMBDPpaPlnOA2pFAxBnxGjFlMZABxm9n2I= github.com/pb33f/ordered-map/v2 v2.3.1 h1:5319HDO0aw4DA4gzi+zv4FXU9UlSs3xGZ40wcP1nBjY= github.com/pb33f/ordered-map/v2 v2.3.1/go.mod h1:qxFQgd0PkVUtOMCkTapqotNgzRhMPL7VvaHKbd1HnmQ= +github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY= +github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= diff --git a/tests/endpoint-provenance.json b/tests/endpoint-provenance.json index a847311..0378393 100644 --- a/tests/endpoint-provenance.json +++ b/tests/endpoint-provenance.json @@ -1564,531 +1564,531 @@ "artifacts": [ { "agent": "claude", - "archive_sha256": "d69195afe769363245241f292c185409226d081bf0bbd564999bf99453f759c9", - "artifact_id": 11785968881, + "archive_sha256": "7ad0439e08756543d13d56d4290d73d69f4f8417b6d5d574d2565d941c67b195", + "artifact_id": 11789380313, "capture_sha256": "6eb5f870d6d9eec0fd8bdfce34693ce86e057feb07df5aa9a4af3f3bd706ea8c", - "check_run_id": 115107707605, + "check_run_id": 115126832928, "conclusion": "success", - "job_id": 115107707605, + "job_id": 115126832928, "platform": "macos", "runner": "macos-latest", - "runner_id": 1000035349, + "runner_id": 1000035485, "runner_labels": [ "macos-latest" ], - "runner_name": "GitHub Actions 1000035349" + "runner_name": "GitHub Actions 1000035485" }, { "agent": "claude", - "archive_sha256": "ddcd51653a271ee70a389d87cc13d87486e360f401c5a327249ff4f29a1d8761", - "artifact_id": 11786053325, + "archive_sha256": "1b07ebe545d4897ef10d19a54916c1087be97099f3372ac3997a4b47948da9c1", + "artifact_id": 11789265199, "capture_sha256": "21fa65e59854f6623ddc764b14463d23d1bf87527e4a4173501a0dc354a7dce9", - "check_run_id": 115107707777, + "check_run_id": 115126833127, "conclusion": "success", - "job_id": 115107707777, + "job_id": 115126833127, "platform": "ubuntu", "runner": "ubuntu-latest", - "runner_id": 1000035336, + "runner_id": 1000035479, "runner_labels": [ "ubuntu-latest" ], - "runner_name": "GitHub Actions 1000035336" + "runner_name": "GitHub Actions 1000035479" }, { "agent": "claude", - "archive_sha256": "f949e2e3a4a27af272048c82d2ecbf2ab5485b7772aa413e250d524bfc9c8243", - "artifact_id": 11786497938, + "archive_sha256": "20b0e44e252cc6e9559315b2c00d88faa1a9becdd22ff8f7208f85ec4783f383", + "artifact_id": 11789330077, "capture_sha256": "de00faa86bcc17a3858475a91044c6a28b24a1af11650bb7758044643de2aa1f", - "check_run_id": 115107707788, + "check_run_id": 115126833207, "conclusion": "success", - "job_id": 115107707788, + "job_id": 115126833207, "platform": "windows", "runner": "windows-latest", - "runner_id": 1000035345, + "runner_id": 1000035478, "runner_labels": [ "windows-latest" ], - "runner_name": "GitHub Actions 1000035345" + "runner_name": "GitHub Actions 1000035478" }, { "agent": "codex", - "archive_sha256": "8e8bcbd939ad46ac7f117c9028b94e7f27e75a4787b1e5ca930083e8b1d274c2", - "artifact_id": 11786782742, + "archive_sha256": "675b2fc462c858a444368d4c5bdc938dd25bca6c8185b72cd25eae48361cf0a5", + "artifact_id": 11788950380, "capture_sha256": "5e39c8858cfe781d5febb081b393afd0c385a73c33bf6609ca7d7b5a4749852e", - "check_run_id": 115107707871, + "check_run_id": 115126832959, "conclusion": "success", - "job_id": 115107707871, + "job_id": 115126832959, "platform": "macos", "runner": "macos-latest", - "runner_id": 1000035352, + "runner_id": 1000035482, "runner_labels": [ "macos-latest" ], - "runner_name": "GitHub Actions 1000035352" + "runner_name": "GitHub Actions 1000035482" }, { "agent": "codex", - "archive_sha256": "3e307ed8a8107320e9c17aac94934df74b3338d5621980d2060f466c3b241d20", - "artifact_id": 11786098231, + "archive_sha256": "b91a280b11e0c72368699546c9e926fb7d639577984b6d10fadd2d870a74c960", + "artifact_id": 11788613795, "capture_sha256": "a45c25b9a94213356abc092f187117258099981423758f74e27cf6398efea157", - "check_run_id": 115107707667, + "check_run_id": 115126832738, "conclusion": "success", - "job_id": 115107707667, + "job_id": 115126832738, "platform": "ubuntu", "runner": "ubuntu-latest", - "runner_id": 1000035334, + "runner_id": 1000035470, "runner_labels": [ "ubuntu-latest" ], - "runner_name": "GitHub Actions 1000035334" + "runner_name": "GitHub Actions 1000035470" }, { "agent": "codex", - "archive_sha256": "fcdcac0fa3ab2d41dbd1df5f42562348c9451effb6f80f7941756774cb68fa8f", - "artifact_id": 11786118312, + "archive_sha256": "aa38a711b6fd47684b80a3947447b1b4aefa9fdda73082629bf99e5b84ba2bf7", + "artifact_id": 11788950195, "capture_sha256": "1f90c8f26662b2532208cf6abe8bc263e22cadc0a05fd966dc5cfe13652afb5c", - "check_run_id": 115107707518, + "check_run_id": 115126832980, "conclusion": "success", - "job_id": 115107707518, + "job_id": 115126832980, "platform": "windows", "runner": "windows-latest", - "runner_id": 1000035325, + "runner_id": 1000035474, "runner_labels": [ "windows-latest" ], - "runner_name": "GitHub Actions 1000035325" + "runner_name": "GitHub Actions 1000035474" }, { "agent": "copilot", - "archive_sha256": "4808b04d3ced1b7cf834a3df4d9e81ee97cb99fd39e630ed354c7c9f8784e576", - "artifact_id": 11786633306, + "archive_sha256": "981612914a20895f6ae41a7e3e401ba5df092ce9c8909550413bf752f3f10ce1", + "artifact_id": 11788289925, "capture_sha256": "c922e1edecb25cf7909b1fdb0bf8144a7c05ea841ad2315f5fc7656873571dd8", - "check_run_id": 115107707884, + "check_run_id": 115126833030, "conclusion": "success", - "job_id": 115107707884, + "job_id": 115126833030, "platform": "macos", "runner": "macos-latest", - "runner_id": 1000035353, + "runner_id": 1000035483, "runner_labels": [ "macos-latest" ], - "runner_name": "GitHub Actions 1000035353" + "runner_name": "GitHub Actions 1000035483" }, { "agent": "copilot", - "archive_sha256": "576af44333134fc09f3f1ee09911b012a340ac7a258a0049f277325b59e9b54b", - "artifact_id": 11786712537, + "archive_sha256": "cd927956ba4c4c6f7c6edf8f5155d3a605236fa280e63b73f012105334a1a347", + "artifact_id": 11788825326, "capture_sha256": "d17ae1033aafd6c8c7eb5ee2cf2cb134a661ecc37944169574d60806038fa103", - "check_run_id": 115107707664, + "check_run_id": 115126832820, "conclusion": "success", - "job_id": 115107707664, + "job_id": 115126832820, "platform": "ubuntu", "runner": "ubuntu-latest", - "runner_id": 1000035326, + "runner_id": 1000035463, "runner_labels": [ "ubuntu-latest" ], - "runner_name": "GitHub Actions 1000035326" + "runner_name": "GitHub Actions 1000035463" }, { "agent": "copilot", - "archive_sha256": "b89d39e9beeb8a55d595bdd516f391875f9462366e9a9097d9bcba40071ab533", - "artifact_id": 11786426905, + "archive_sha256": "35017f5f2146ff97da291fa79838cb4a7e29a8a9c8395423e4bef3550df0c7a6", + "artifact_id": 11788514117, "capture_sha256": "01594240b7ce34526bf7ccbc15bcf9f2ba4b2fff552d6e949c501720df7a0868", - "check_run_id": 115107707545, + "check_run_id": 115126832521, "conclusion": "success", - "job_id": 115107707545, + "job_id": 115126832521, "platform": "windows", "runner": "windows-latest", - "runner_id": 1000035324, + "runner_id": 1000035458, "runner_labels": [ "windows-latest" ], - "runner_name": "GitHub Actions 1000035324" + "runner_name": "GitHub Actions 1000035458" }, { "agent": "cursor", - "archive_sha256": "c1815f49f84e76967febf624d1dc5cc51e3a2f81a08a1bdc51636e577b575aa9", - "artifact_id": 11785828861, + "archive_sha256": "92c06003012f277c0b1ba099a46ac4442b84a42133ca013ff2f761f07dbfd827", + "artifact_id": 11789245348, "capture_sha256": "45b8810fda9834e0a492a9926dffb9be57ee1338d513286ef17d3abe005751b2", - "check_run_id": 115107707576, + "check_run_id": 115126832785, "conclusion": "success", - "job_id": 115107707576, + "job_id": 115126832785, "platform": "macos", "runner": "macos-latest", - "runner_id": 1000035348, + "runner_id": 1000035484, "runner_labels": [ "macos-latest" ], - "runner_name": "GitHub Actions 1000035348" + "runner_name": "GitHub Actions 1000035484" }, { "agent": "cursor", - "archive_sha256": "e0ac95563d4a850b8e1eee53e6e4113f84697d6ce676755301cd1e8f692786db", - "artifact_id": 11786211931, + "archive_sha256": "fc1287dd8d8d7300b20929566ab95d766aefe7079795b5762be58a23d853bd79", + "artifact_id": 11789205399, "capture_sha256": "e10c16ce205909adef763903de2d0ac84eabb2e1b496347992ad3c3360213908", - "check_run_id": 115107707604, + "check_run_id": 115126833110, "conclusion": "success", - "job_id": 115107707604, + "job_id": 115126833110, "platform": "ubuntu", "runner": "ubuntu-latest", - "runner_id": 1000035335, + "runner_id": 1000035480, "runner_labels": [ "ubuntu-latest" ], - "runner_name": "GitHub Actions 1000035335" + "runner_name": "GitHub Actions 1000035480" }, { "agent": "cursor", - "archive_sha256": "c87ad8c2bf3f9d97aefeeb3d3b239c177b2af7c54ec6d797600380f1e39a08fe", - "artifact_id": 11785698934, + "archive_sha256": "16db3158f58dc0f33902fce1cf80cb5efb948abb1a27e0c4956e83ffe1e1772b", + "artifact_id": 11789310061, "capture_sha256": "b8af04ff98e36d1f956dd00742a51aed5325e2fef51e867edca544ae8da00555", - "check_run_id": 115107707674, + "check_run_id": 115126832470, "conclusion": "success", - "job_id": 115107707674, + "job_id": 115126832470, "platform": "windows", "runner": "windows-latest", - "runner_id": 1000035330, + "runner_id": 1000035466, "runner_labels": [ "windows-latest" ], - "runner_name": "GitHub Actions 1000035330" + "runner_name": "GitHub Actions 1000035466" }, { "agent": "deepseek", - "archive_sha256": "40c5d41265d83c934b5a41a112853c08096110c364293fc8d43d0277b15e3d8f", - "artifact_id": 11786068764, + "archive_sha256": "d4c76e718ea4bc48f693cdea928408c53f9e5af72f93e5db3f69460b9996521f", + "artifact_id": 11789215801, "capture_sha256": "9f36db049a9f7ac3a58356a9aa17e1bc3727e1a41badd1e2e3b3af30491f672c", - "check_run_id": 115107707447, + "check_run_id": 115126833106, "conclusion": "success", - "job_id": 115107707447, + "job_id": 115126833106, "platform": "macos", "runner": "macos-latest", - "runner_id": 1000035321, + "runner_id": 1000035487, "runner_labels": [ "macos-latest" ], - "runner_name": "GitHub Actions 1000035321" + "runner_name": "GitHub Actions 1000035487" }, { "agent": "deepseek", - "archive_sha256": "ea84a33dd5d981bc82871944f1e56c8d9920c9f7f049bf9126fd374a98e9d994", - "artifact_id": 11785847314, + "archive_sha256": "7b4578203df7dd3ea79d1bca3d627efc8ec5ffc93587a53893bb5ba03184beef", + "artifact_id": 11788623724, "capture_sha256": "5c08fb7a1644f87521c9321c99b6deca046ba0c46993d7d747f51215c587f4b2", - "check_run_id": 115107707527, + "check_run_id": 115126832674, "conclusion": "success", - "job_id": 115107707527, + "job_id": 115126832674, "platform": "ubuntu", "runner": "ubuntu-latest", - "runner_id": 1000035327, + "runner_id": 1000035459, "runner_labels": [ "ubuntu-latest" ], - "runner_name": "GitHub Actions 1000035327" + "runner_name": "GitHub Actions 1000035459" }, { "agent": "deepseek", - "archive_sha256": "06d922eda25c7e935863adec1a7df89ea1357339c45bc4b4c265fd13b9557585", - "artifact_id": 11785683142, + "archive_sha256": "6056f5f9dc1d927acf6d308c613d533bfa1f41c4de5448f3bf177a4a67aa51dd", + "artifact_id": 11789030778, "capture_sha256": "5b26b5c0c269b5fd0392a298d3e238da8ea197f382698c5e3df71ab81101f4be", - "check_run_id": 115107707863, + "check_run_id": 115126833078, "conclusion": "success", - "job_id": 115107707863, + "job_id": 115126833078, "platform": "windows", "runner": "windows-latest", - "runner_id": 1000035346, + "runner_id": 1000035475, "runner_labels": [ "windows-latest" ], - "runner_name": "GitHub Actions 1000035346" + "runner_name": "GitHub Actions 1000035475" }, { "agent": "devin", - "archive_sha256": "b55b11fc1aa8590032910c004b597b285969b71658252e2965c0af4eb755a13c", - "artifact_id": 11786298580, + "archive_sha256": "930479e62c79dd7beb20c93e9c65877b000ed188a6979a79c6c8b2992dbe39f8", + "artifact_id": 11789185028, "capture_sha256": "b162effcbc4be932b051e18ae73139dbaa9d4611e7cb1bdf795fd79a1ce24fed", - "check_run_id": 115107707811, + "check_run_id": 115126832531, "conclusion": "success", - "job_id": 115107707811, + "job_id": 115126832531, "platform": "macos", "runner": "macos-latest", - "runner_id": 1000035350, + "runner_id": 1000035460, "runner_labels": [ "macos-latest" ], - "runner_name": "GitHub Actions 1000035350" + "runner_name": "GitHub Actions 1000035460" }, { "agent": "devin", - "archive_sha256": "d27b04db7af0af140d9db1e83a0ba26f07abc9c81fe9ffd391ee9448d91e06a6", - "artifact_id": 11786028417, + "archive_sha256": "d6a0c17ee7604296776ed31fcec9e20e653cf5dd7ddb3e0645c9bbae1f57ee70", + "artifact_id": 11789090360, "capture_sha256": "a87dc314284a5502d80d34de2b532667198652a256bcf85e22211e4bd502bf8f", - "check_run_id": 115107707844, + "check_run_id": 115126832766, "conclusion": "success", - "job_id": 115107707844, + "job_id": 115126832766, "platform": "ubuntu", "runner": "ubuntu-latest", - "runner_id": 1000035347, + "runner_id": 1000035468, "runner_labels": [ "ubuntu-latest" ], - "runner_name": "GitHub Actions 1000035347" + "runner_name": "GitHub Actions 1000035468" }, { "agent": "devin", - "archive_sha256": "6c202c7d7d6efe9240ba92f9facc8dbaae03e88684110a26127d89c748ab29c8", - "artifact_id": 11786318056, + "archive_sha256": "7edff181104c1a2202f4ca7c7613ff27d95e8adf82baa5af5d52b39012f789db", + "artifact_id": 11788623703, "capture_sha256": "8b1dbc6de5b80be42494d1b0066082b7e6c99bea96043bae9960409c0dc776d2", - "check_run_id": 115107707639, + "check_run_id": 115126832767, "conclusion": "success", - "job_id": 115107707639, + "job_id": 115126832767, "platform": "windows", "runner": "windows-latest", - "runner_id": 1000035338, + "runner_id": 1000035473, "runner_labels": [ "windows-latest" ], - "runner_name": "GitHub Actions 1000035338" + "runner_name": "GitHub Actions 1000035473" }, { "agent": "gemini", - "archive_sha256": "2d9d8b7a5f95e4ce998f9b129ca6d3921c461d7fcfb486b65ed0640e9230814a", - "artifact_id": 11785842761, + "archive_sha256": "9ae17d586ae45e159e681fe610131289ec8ae1f3e8700ed82a6464ba129a2e8d", + "artifact_id": 11789225158, "capture_sha256": "0b9396676f937c13f30aa871d2874b3f438e1c91b801dcd16264529de39d4762", - "check_run_id": 115107707772, + "check_run_id": 115126832845, "conclusion": "success", - "job_id": 115107707772, + "job_id": 115126832845, "platform": "macos", "runner": "macos-latest", - "runner_id": 1000035351, + "runner_id": 1000035486, "runner_labels": [ "macos-latest" ], - "runner_name": "GitHub Actions 1000035351" + "runner_name": "GitHub Actions 1000035486" }, { "agent": "gemini", - "archive_sha256": "5c7ad45650b7cabb7c8b67517235c79cfe736adf79ff2a2b893e03bf86c88cca", - "artifact_id": 11786177076, + "archive_sha256": "dc5e5865019b7faba43a4437f25a9921516ddf7602ae237e666230dcfcb86f49", + "artifact_id": 11788499106, "capture_sha256": "6cd688f58722a757df69a82543f4c988a5930479012e63baf936a887fa93419d", - "check_run_id": 115107707606, + "check_run_id": 115126832446, "conclusion": "success", - "job_id": 115107707606, + "job_id": 115126832446, "platform": "ubuntu", "runner": "ubuntu-latest", - "runner_id": 1000035332, + "runner_id": 1000035455, "runner_labels": [ "ubuntu-latest" ], - "runner_name": "GitHub Actions 1000035332" + "runner_name": "GitHub Actions 1000035455" }, { "agent": "gemini", - "archive_sha256": "54448edc510fdf41780d3585ea3c15287bce3c854f40c8bba7bdebd14239f33b", - "artifact_id": 11786217184, + "archive_sha256": "53561ead19e9ccbf66d7c7e73149fb582a2362446cc248c85fedd5645fefc478", + "artifact_id": 11789305300, "capture_sha256": "1f6d00cc96e00f3cb972f8e1089b13935ffd149e6b8aa67911848068f77d668b", - "check_run_id": 115107707647, + "check_run_id": 115126832590, "conclusion": "success", - "job_id": 115107707647, + "job_id": 115126832590, "platform": "windows", "runner": "windows-latest", - "runner_id": 1000035329, + "runner_id": 1000035456, "runner_labels": [ "windows-latest" ], - "runner_name": "GitHub Actions 1000035329" + "runner_name": "GitHub Actions 1000035456" }, { "agent": "kimi", - "archive_sha256": "b437a8881c22fdd882dfd7b228f293f317f72a2e6c121a56eb43e73b5124ee49", - "artifact_id": 11785867531, + "archive_sha256": "4b5968f95395a4c7ac58b32bb3309f18e9f6bde76e06bbfd70ede33ce6ab3c5d", + "artifact_id": 11788633834, "capture_sha256": "4ca011a5faccac9d135d75220d2738d19a9df347a0b975546c560afff1e0915b", - "check_run_id": 115107707506, + "check_run_id": 115126832734, "conclusion": "success", - "job_id": 115107707506, + "job_id": 115126832734, "platform": "macos", "runner": "macos-latest", - "runner_id": 1000035322, + "runner_id": 1000035462, "runner_labels": [ "macos-latest" ], - "runner_name": "GitHub Actions 1000035322" + "runner_name": "GitHub Actions 1000035462" }, { "agent": "kimi", - "archive_sha256": "9e81abbb71bcfb90738c411642bf03b7a1d5f97f71baf433b24b968f8fddc009", - "artifact_id": 11785733793, + "archive_sha256": "fdea84644237c1234e92c8ecdfbc8fe41fcdec188fbcef65542525841adc048b", + "artifact_id": 11788359377, "capture_sha256": "a3c2d0a3c96e99de4f74ad40970e3fb082250efab11cbaeb80b8ecca357c7de4", - "check_run_id": 115107707658, + "check_run_id": 115126832752, "conclusion": "success", - "job_id": 115107707658, + "job_id": 115126832752, "platform": "ubuntu", "runner": "ubuntu-latest", - "runner_id": 1000035333, + "runner_id": 1000035464, "runner_labels": [ "ubuntu-latest" ], - "runner_name": "GitHub Actions 1000035333" + "runner_name": "GitHub Actions 1000035464" }, { "agent": "kimi", - "archive_sha256": "53c687ac2021af50bfcd13f917bf31a301a453295d6f7698fc12f3c336a513dd", - "artifact_id": 11785807604, + "archive_sha256": "4b7beb07069d36936ad380490e9f2a61308ca03c40ffb7998035c1e8e566be60", + "artifact_id": 11788736137, "capture_sha256": "08c897a74d0946a64a448578de24169eb65736289fd1eb7222274de539c14c24", - "check_run_id": 115107707783, + "check_run_id": 115126832423, "conclusion": "success", - "job_id": 115107707783, + "job_id": 115126832423, "platform": "windows", "runner": "windows-latest", - "runner_id": 1000035341, + "runner_id": 1000035457, "runner_labels": [ "windows-latest" ], - "runner_name": "GitHub Actions 1000035341" + "runner_name": "GitHub Actions 1000035457" }, { "agent": "opencode", - "archive_sha256": "9a9544a5a98a446a36eed71a444ee6c300179d6064c64d9919424b97d0a47f3a", - "artifact_id": 11786098425, + "archive_sha256": "df3a6fff445f30bbdcbd53b9e3b62c41942dd0a18d019b8a25be06ebe7e43cf5", + "artifact_id": 11789380028, "capture_sha256": "0197c1c312d6532e4a8d05dce595bc6a86e3a393ba74b3f8c6a0a1459a1aaf5c", - "check_run_id": 115107707735, + "check_run_id": 115126832839, "conclusion": "success", - "job_id": 115107707735, + "job_id": 115126832839, "platform": "macos", "runner": "macos-latest", - "runner_id": 1000035328, + "runner_id": 1000035461, "runner_labels": [ "macos-latest" ], - "runner_name": "GitHub Actions 1000035328" + "runner_name": "GitHub Actions 1000035461" }, { "agent": "opencode", - "archive_sha256": "e28fc529010b6479916ad8560c4d6b2853e4ea10752a89d8a71b3c76165c780a", - "artifact_id": 11786497803, + "archive_sha256": "1e5eb6f96ba90afa9e0a44b8a5e55cc3bf6fec4025700cd77c5a8577ba4f5a8b", + "artifact_id": 11788394267, "capture_sha256": "f9a06f9015188a22a73e7d1717185489dca98151e57448533e53ad05e03810bb", - "check_run_id": 115107707839, + "check_run_id": 115126832651, "conclusion": "success", - "job_id": 115107707839, + "job_id": 115126832651, "platform": "ubuntu", "runner": "ubuntu-latest", - "runner_id": 1000035343, + "runner_id": 1000035471, "runner_labels": [ "ubuntu-latest" ], - "runner_name": "GitHub Actions 1000035343" + "runner_name": "GitHub Actions 1000035471" }, { "agent": "opencode", - "archive_sha256": "f94d0890936e37db1adb73105156f178acb49a4d04983eb49009c51d6ace3e42", - "artifact_id": 11786147190, + "archive_sha256": "905e5d000cdeefe9af0978222d2a83223e87ea83d023778614dd8475c8b4460a", + "artifact_id": 11788469264, "capture_sha256": "31ce073b2034222781b9a4519bae2ffd62b72ffe167aa70bc136aae29d55cfe6", - "check_run_id": 115107707702, + "check_run_id": 115126833095, "conclusion": "success", - "job_id": 115107707702, + "job_id": 115126833095, "platform": "windows", "runner": "windows-latest", - "runner_id": 1000035342, + "runner_id": 1000035476, "runner_labels": [ "windows-latest" ], - "runner_name": "GitHub Actions 1000035342" + "runner_name": "GitHub Actions 1000035476" }, { "agent": "pi", - "archive_sha256": "0bcc222733b379073e772b7c0181886e602b467696fadde02f03b2ccea96026d", - "artifact_id": 11786282071, + "archive_sha256": "74b5a9b2405512d30afacd9fa4dbb0667dd62664b05fad55ee281233fd85b827", + "artifact_id": 11789025209, "capture_sha256": "f90ded0ea820aaf27a2e025956367f2d4f1c1271a4c8c0797f3ad0975e70f55e", - "check_run_id": 115107707622, + "check_run_id": 115126832817, "conclusion": "success", - "job_id": 115107707622, + "job_id": 115126832817, "platform": "macos", "runner": "macos-latest", - "runner_id": 1000035331, + "runner_id": 1000035465, "runner_labels": [ "macos-latest" ], - "runner_name": "GitHub Actions 1000035331" + "runner_name": "GitHub Actions 1000035465" }, { "agent": "pi", - "archive_sha256": "67ecd816f981da73c1bcd552a104028db648a26143ca38f3c45d87dca0ef8f68", - "artifact_id": 11786587728, + "archive_sha256": "473e4828685def0bdb5a3254c5632f8add34164efc4f793ddc960779d7aa41c3", + "artifact_id": 11788359457, "capture_sha256": "e0316503ed761604e429919e7593590bc1b2f329b13b0c604864ae2a88a3ec87", - "check_run_id": 115107707808, + "check_run_id": 115126832921, "conclusion": "success", - "job_id": 115107707808, + "job_id": 115126832921, "platform": "ubuntu", "runner": "ubuntu-latest", - "runner_id": 1000035340, + "runner_id": 1000035481, "runner_labels": [ "ubuntu-latest" ], - "runner_name": "GitHub Actions 1000035340" + "runner_name": "GitHub Actions 1000035481" }, { "agent": "pi", - "archive_sha256": "87f52ec4ba147d9d26f1f0f4077dcf1d066e31e39295817e45199c642443d79c", - "artifact_id": 11786137440, + "archive_sha256": "c5c14da7ee2409392d5ccf2499c95eb76775ec81a41eec228243ec955edbfcae", + "artifact_id": 11787999813, "capture_sha256": "453d9e32581184d83cb26ba6b529db15f08662fd17fea6b1cc75f8b49a34c7e4", - "check_run_id": 115107707784, + "check_run_id": 115126833171, "conclusion": "success", - "job_id": 115107707784, + "job_id": 115126833171, "platform": "windows", "runner": "windows-latest", - "runner_id": 1000035344, + "runner_id": 1000035477, "runner_labels": [ "windows-latest" ], - "runner_name": "GitHub Actions 1000035344" + "runner_name": "GitHub Actions 1000035477" }, { "agent": "qwen", - "archive_sha256": "c60672f66f35b5b0016885f2e553a88535697168f2d330a72a5edb1783200d1f", - "artifact_id": 11785927418, + "archive_sha256": "ca0f9c91c4c79e204fa9cd40389e4f42867593b5949bc5b59b5d22383e0b9046", + "artifact_id": 11789190014, "capture_sha256": "bf800127313bc7c5e43bb483ee6d8cfee8ca53c2057a150ac5e1c939bb7d1336", - "check_run_id": 115107707761, + "check_run_id": 115126832626, "conclusion": "success", - "job_id": 115107707761, + "job_id": 115126832626, "platform": "macos", "runner": "macos-latest", - "runner_id": 1000035339, + "runner_id": 1000035472, "runner_labels": [ "macos-latest" ], - "runner_name": "GitHub Actions 1000035339" + "runner_name": "GitHub Actions 1000035472" }, { "agent": "qwen", - "archive_sha256": "14a210b0e2502ef3ed91550bd0ddb0ca4239e841381344efe296a19e93758bf0", - "artifact_id": 11786662510, + "archive_sha256": "a4dd4387bb0f61e28b3acdba6c28c7b7a7aa526417762f2358a581eb285ee939", + "artifact_id": 11787979734, "capture_sha256": "0a9133db580f0861d1fe583fbb0fd81196581af4d4141ab793015a1e8ac516de", - "check_run_id": 115107707769, + "check_run_id": 115126832600, "conclusion": "success", - "job_id": 115107707769, + "job_id": 115126832600, "platform": "ubuntu", "runner": "ubuntu-latest", - "runner_id": 1000035337, + "runner_id": 1000035469, "runner_labels": [ "ubuntu-latest" ], - "runner_name": "GitHub Actions 1000035337" + "runner_name": "GitHub Actions 1000035469" }, { "agent": "qwen", - "archive_sha256": "8f9fe187a19dbb4f80ed934a16f18c6bef89f8b100cc8c6b0cc337fc68334588", - "artifact_id": 11786717551, + "archive_sha256": "e733360ea79919201e6394ee022f21629636a5aa8ce9b136ddb07769f80fe362", + "artifact_id": 11788329486, "capture_sha256": "71a9c5e9463a5ea1b7050053286f4fa11484c2c30c7b509ddac86c16527a1e13", - "check_run_id": 115107707468, + "check_run_id": 115126832648, "conclusion": "success", - "job_id": 115107707468, + "job_id": 115126832648, "platform": "windows", "runner": "windows-latest", - "runner_id": 1000035323, + "runner_id": 1000035467, "runner_labels": [ "windows-latest" ], - "runner_name": "GitHub Actions 1000035323" + "runner_name": "GitHub Actions 1000035467" } ], "catalog_sha256": "f92926d7a643aed5dfb151ba8af1ff570f1a8e46a3587a91e2ae4498868abe82", @@ -2262,8 +2262,8 @@ "repository": "operatorstack/pitot", "repository_id": 1305805198, "run_attempt": 1, - "run_id": 38339689686, - "source_sha": "946da99b944a61e3b2d6babf3aba96ba9fb565b9", + "run_id": 38345794247, + "source_sha": "87fc7ede73adb092f4d50ebdd9d86ee8976de6a3", "workflow_id": 381486275, "workflow_path": ".github/workflows/pitot-e2e.yml" }