native macOS codings agent orchestrator prowl.onev.cat
Something went wrong. Try again.
9.0 kB · 300 lines
Swift
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301import Foundationimport Testing
@testable import supacode
struct AgentTranscriptResultReaderTests { @Test func decodesLatestClosedCodexTaskCompletion() throws { let result = AgentTranscriptResultReader.decode( agent: .codex, jsonl: try jsonl([ codexCompletion(turnID: "turn-1", completedAt: 1_786_435_200, text: "First result"), codexCompletion(turnID: "turn-2", completedAt: 1_786_435_260, text: "Final result"), ]), maxBytes: 1_024 )
#expect(result.state == .complete) #expect(result.text == "Final result") }
@Test func decodesCodexCompletionWithNumericTimestamp() throws { let result = AgentTranscriptResultReader.decode( agent: .codex, jsonl: try jsonl([ codexCompletion(turnID: "turn-1", completedAt: 1_786_438_335, text: "Final result") ]), maxBytes: 1_024 )
#expect(result.state == .complete) #expect(result.text == "Final result") }
@Test func rejectsCodexCompletionWithoutUsableIdentityOrText() throws { let emptyTurnID = AgentTranscriptResultReader.decode( agent: .codex, jsonl: try jsonl([ codexCompletion(turnID: "", completedAt: 1_786_438_335, text: "Final result") ]), maxBytes: 1_024 ) let emptyText = AgentTranscriptResultReader.decode( agent: .codex, jsonl: try jsonl([ codexCompletion(turnID: "turn-1", completedAt: 1_786_438_335, text: "") ]), maxBytes: 1_024 )
#expect(emptyTurnID.state == .incomplete) #expect(emptyText.state == .incomplete) }
@Test func rejectsCodexCompletionWithNonIntegerTimestamp() throws { let result = AgentTranscriptResultReader.decode( agent: .codex, jsonl: try jsonl([ codexCompletion(turnID: "turn-1", completedAt: 1_786_438_335.5, text: "Final result") ]), maxBytes: 1_024 )
#expect(result.state == .incomplete) #expect(result.text == nil) }
@Test func rejectsCodexCompletionWithErrorOrMissingMessage() throws { var errorPayload = codexCompletion( turnID: "turn-1", completedAt: 1_786_438_335, text: "Partial result" )["payload"] as? [String: Any] ?? [:] errorPayload["error"] = ["message": "stream disconnected"] let errored = AgentTranscriptResultReader.decode( agent: .codex, jsonl: try jsonl([["type": "event_msg", "payload": errorPayload]]), maxBytes: 1_024 ) let missingMessage = AgentTranscriptResultReader.decode( agent: .codex, jsonl: try jsonl([ [ "type": "event_msg", "payload": [ "type": "task_complete", "turn_id": "turn-1", "completed_at": 1_786_438_335, "last_agent_message": NSNull(), ], ] ]), maxBytes: 1_024 )
#expect(errored.state == .incomplete) #expect(missingMessage.state == .incomplete) }
@Test func acceptsCompletedCodexResultMentioningMaxTokens() throws { let result = AgentTranscriptResultReader.decode( agent: .codex, jsonl: try jsonl([ codexCompletion( turnID: "turn-1", completedAt: 1_786_435_200, text: "Use max_tokens to configure the output limit." ) ]), maxBytes: 1_024 )
#expect(result.state == .complete) #expect(result.text == "Use max_tokens to configure the output limit.") }
@Test func rejectsLatestBudgetLimitedCodexTurnWithoutReturningEarlierResult() throws { let result = AgentTranscriptResultReader.decode( agent: .codex, jsonl: try jsonl([ codexCompletion(turnID: "turn-1", completedAt: 1_786_435_200, text: "Earlier result"), codexAbort(turnID: "turn-2", completedAt: 1_786_435_260, reason: "budget_limited"), ]), maxBytes: 1_024 )
#expect(result.state == .incomplete) #expect(result.text == nil) }
@Test func followsClaudeTurnDurationParentChainToClosedAssistantText() throws { let result = AgentTranscriptResultReader.decode( agent: .claude, jsonl: try jsonl([ claudeAssistant(uuid: "assistant-1", sessionID: "session-1", text: "Claude final result"), claudeSystem( subtype: "stop_hook_summary", uuid: "summary-1", sessionID: "session-1", parentUUID: "assistant-1" ), claudeSystem( subtype: "turn_duration", uuid: "duration-1", sessionID: "session-1", parentUUID: "summary-1" ), ]), maxBytes: 1_024 )
#expect(result.state == .complete) #expect(result.text == "Claude final result") }
@Test func rejectsClaudeToolOnlyAssistantTurnAsIncomplete() throws { let result = AgentTranscriptResultReader.decode( agent: .claude, jsonl: try jsonl([ claudeAssistant(uuid: "assistant-1", sessionID: "session-1", content: [["type": "tool_use", "name": "Bash"]]), claudeSystem( subtype: "turn_duration", uuid: "duration-1", sessionID: "session-1", parentUUID: "assistant-1" ), ]), maxBytes: 1_024 )
#expect(result.state == .incomplete) #expect(result.text == nil) }
@Test func rejectsUnclosedJSONLWithoutReturningPartialText() throws { let jsonl = try jsonl([ codexCompletion(turnID: "turn-1", completedAt: 1_786_435_200, text: "Final result") ]) + "{\"type\":\"event_msg\"\n" let result = AgentTranscriptResultReader.decode(agent: .codex, jsonl: jsonl, maxBytes: 1_024)
#expect(result.state == .incomplete) #expect(result.text == nil) }
@Test func reportsOversizedCompleteResultWithoutTruncatingIt() throws { let result = AgentTranscriptResultReader.decode( agent: .codex, jsonl: try jsonl([ codexCompletion(turnID: "turn-1", completedAt: 1_786_435_200, text: "0123456789") ]), maxBytes: 5 )
#expect(result.state == .tooLarge) #expect(result.text == nil) }
@Test func readsCompleteResultAtWorstCaseJSONEscapingSize() throws { let url = FileManager.default.temporaryDirectory.appending(path: "prowl-result-\(UUID().uuidString).jsonl") defer { try? FileManager.default.removeItem(at: url) } let text = String(repeating: "\u{1}", count: 400_000) try jsonl([ codexCompletion(turnID: "turn-1", completedAt: 1_786_435_200, text: text) ]).write(to: url, atomically: true, encoding: .utf8)
let result = AgentTranscriptResultReader.read(agent: .codex, at: url, maxBytes: 400_000)
#expect(result.state == .complete) #expect(result.text == text) }
@Test func readsOnlyTheBoundedTranscriptTail() throws { let url = FileManager.default.temporaryDirectory.appending(path: "prowl-result-\(UUID().uuidString).jsonl") defer { try? FileManager.default.removeItem(at: url) } let oversizedHistory = "{\"type\":\"event_msg\",\"payload\":{\"type\":\"agent_message\",\"text\":\"" + String(repeating: "x", count: 1_100_000) + "\"}}\n" let completion = try jsonl([ codexCompletion(turnID: "turn-1", completedAt: 1_786_435_200, text: "Final result") ]) try (oversizedHistory + completion).write(to: url, atomically: true, encoding: .utf8)
let result = AgentTranscriptResultReader.read(agent: .codex, at: url, maxBytes: 1_024)
#expect(result.state == .complete) #expect(result.text == "Final result") }
private func codexCompletion( turnID: String, completedAt: Any, text: String ) -> [String: Any] { [ "type": "event_msg", "payload": [ "type": "task_complete", "turn_id": turnID, "completed_at": completedAt, "last_agent_message": text, ], ] }
private func codexAbort( turnID: String, completedAt: Any, reason: String ) -> [String: Any] { [ "type": "event_msg", "payload": [ "type": "turn_aborted", "turn_id": turnID, "completed_at": completedAt, "reason": reason, ], ] }
private func claudeAssistant( uuid: String, sessionID: String, text: String? = nil, content: [[String: Any]]? = nil ) -> [String: Any] { [ "type": "assistant", "uuid": uuid, "sessionId": sessionID, "message": [ "stop_reason": "end_turn", "content": content ?? [["type": "text", "text": text ?? ""]], ], ] }
private func claudeSystem( subtype: String, uuid: String, sessionID: String, parentUUID: String ) -> [String: Any] { [ "type": "system", "subtype": subtype, "uuid": uuid, "sessionId": sessionID, "parentUuid": parentUUID, ] }
private func jsonl(_ records: [[String: Any]]) throws -> String { try records.map { record in let data = try JSONSerialization.data(withJSONObject: record, options: [.sortedKeys]) return try #require(String(data: data, encoding: .utf8)) + "\n" }.joined() }}