-
Notifications
You must be signed in to change notification settings - Fork 1
feat: add queue SDK and CLI commands #627
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
📝 WalkthroughWalkthroughAdds a complete Queue subsystem: server API (types, validation, errors, analytics, DLQ, destinations, sources), CLI commands, core QueueService and HTTP/local implementations, runtime wiring, SDK test app and scripts, APIClient per-request headers, and two new CI smoke-test jobs. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 🚥 Pre-merge checks | ✅ 1✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 📜 Recent review detailsConfiguration used: Organization UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
✏️ Tip: You can disable this entire section by setting Comment |
📦 Canary Packages Publishedversion: PackagesInstallAdd to your {
"dependencies": {
"@agentuity/opencode": "https://agentuity-sdk-objects.t3.storage.dev/npm/0.1.15-5d8199c/agentuity-opencode-0.1.15-5d8199c.tgz",
"@agentuity/runtime": "https://agentuity-sdk-objects.t3.storage.dev/npm/0.1.15-5d8199c/agentuity-runtime-0.1.15-5d8199c.tgz",
"@agentuity/evals": "https://agentuity-sdk-objects.t3.storage.dev/npm/0.1.15-5d8199c/agentuity-evals-0.1.15-5d8199c.tgz",
"@agentuity/frontend": "https://agentuity-sdk-objects.t3.storage.dev/npm/0.1.15-5d8199c/agentuity-frontend-0.1.15-5d8199c.tgz",
"@agentuity/cli": "https://agentuity-sdk-objects.t3.storage.dev/npm/0.1.15-5d8199c/agentuity-cli-0.1.15-5d8199c.tgz",
"@agentuity/core": "https://agentuity-sdk-objects.t3.storage.dev/npm/0.1.15-5d8199c/agentuity-core-0.1.15-5d8199c.tgz",
"@agentuity/react": "https://agentuity-sdk-objects.t3.storage.dev/npm/0.1.15-5d8199c/agentuity-react-0.1.15-5d8199c.tgz",
"@agentuity/auth": "https://agentuity-sdk-objects.t3.storage.dev/npm/0.1.15-5d8199c/agentuity-auth-0.1.15-5d8199c.tgz",
"@agentuity/schema": "https://agentuity-sdk-objects.t3.storage.dev/npm/0.1.15-5d8199c/agentuity-schema-0.1.15-5d8199c.tgz",
"@agentuity/server": "https://agentuity-sdk-objects.t3.storage.dev/npm/0.1.15-5d8199c/agentuity-server-0.1.15-5d8199c.tgz",
"@agentuity/workbench": "https://agentuity-sdk-objects.t3.storage.dev/npm/0.1.15-5d8199c/agentuity-workbench-0.1.15-5d8199c.tgz"
}
}Or install directly: bun add https://agentuity-sdk-objects.t3.storage.dev/npm/0.1.15-5d8199c/agentuity-opencode-0.1.15-5d8199c.tgz
bun add https://agentuity-sdk-objects.t3.storage.dev/npm/0.1.15-5d8199c/agentuity-runtime-0.1.15-5d8199c.tgz
bun add https://agentuity-sdk-objects.t3.storage.dev/npm/0.1.15-5d8199c/agentuity-evals-0.1.15-5d8199c.tgz
bun add https://agentuity-sdk-objects.t3.storage.dev/npm/0.1.15-5d8199c/agentuity-frontend-0.1.15-5d8199c.tgz
bun add https://agentuity-sdk-objects.t3.storage.dev/npm/0.1.15-5d8199c/agentuity-cli-0.1.15-5d8199c.tgz
bun add https://agentuity-sdk-objects.t3.storage.dev/npm/0.1.15-5d8199c/agentuity-core-0.1.15-5d8199c.tgz
bun add https://agentuity-sdk-objects.t3.storage.dev/npm/0.1.15-5d8199c/agentuity-react-0.1.15-5d8199c.tgz
bun add https://agentuity-sdk-objects.t3.storage.dev/npm/0.1.15-5d8199c/agentuity-auth-0.1.15-5d8199c.tgz
bun add https://agentuity-sdk-objects.t3.storage.dev/npm/0.1.15-5d8199c/agentuity-schema-0.1.15-5d8199c.tgz
bun add https://agentuity-sdk-objects.t3.storage.dev/npm/0.1.15-5d8199c/agentuity-server-0.1.15-5d8199c.tgz
bun add https://agentuity-sdk-objects.t3.storage.dev/npm/0.1.15-5d8199c/agentuity-workbench-0.1.15-5d8199c.tgzCLI Executables
Run Canary CLIagentuity canary 0.1.15-5d8199c [command] [...args] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 13
🤖 Fix all issues with AI agents
In `@apps/testing/queues/README.md`:
- Line 47: Fix the minor formatting in the numbered list by removing the extra
spaces after the numeral in the line "10. Deletes the queue" so it reads "10.
Deletes the queue"; update the README entry accordingly to maintain consistent
list formatting.
In `@packages/cli/src/cmd/cloud/queue/delete.ts`:
- Around line 20-31: The example command uses --yes but the schema only defines
the options.confirm flag, causing a mismatch; update the example to use
--confirm (change getCommand('cloud queue delete my-queue --yes') to --confirm)
or add a --yes/-y alias for the existing confirm option (ensure the CLI option
definition for confirm maps aliases --yes and -y to the same schema key),
referencing the examples block and the schema.options.confirm symbol so the help
and runtime behavior match.
In `@packages/cli/src/cmd/cloud/queue/destinations.ts`:
- Around line 122-126: Replace the raw console.log calls in the non-JSON branch
with the CLI tui helpers to keep output consistent: inside the if
(!options.json) block (where tui.success is used and destination is available),
change the two console.log lines that print destination.config.url and
destination.config.method to equivalent tui.info (or another appropriate tui.*
helper) calls, passing the same formatted strings so the output is produced via
the tui layer rather than console.log.
In `@packages/cli/src/cmd/cloud/queue/list.ts`:
- Around line 31-48: The schema currently declares pagination under schema.args
and the handler reads args.limit/args.offset, but pagination should be CLI
flags; move the limit and offset z.coerce.number().optional() descriptors from
schema.args to schema.options (mirroring sandbox/list.ts) and update the handler
to read them from the options object (e.g., const { args, options } = ctx and
pass options.limit/options.offset into listQueues) while keeping
createQueueAPIClient and listQueues calls intact.
In `@packages/cli/src/cmd/cloud/queue/messages.ts`:
- Around line 58-64: The payload fallback currently writes directly to stdout
with console.log(message.payload); replace that with a tui helper to keep CLI
output consistent: in the try/catch around JSON.parse(message.payload) (the
parsed variable and tui.json call), change the catch block to use a tui helper
(e.g., tui.info(message.payload) or another appropriate tui.* method used
elsewhere) instead of console.log so all CLI output goes through tui.
In `@packages/cli/src/cmd/cloud/queue/publish.ts`:
- Around line 71-74: Replace the raw console.log calls used to show published
message details with the CLI tui helpers to maintain consistent formatting: in
the publish command where you call tui.success(`Published message:
${message.id}`), change the subsequent console.log lines that print `Offset:
${message.offset}` and `State: ${message.state}` to use tui.info (or another
appropriate tui.* helper) so output remains styled by the CLI; keep the same
text content/spacing and only swap console.log -> tui.info, ensuring this
happens only in the non-JSON branch controlled by options.json.
- Around line 49-55: The metadata parsing block using opts.metadata should
validate that JSON.parse returns a plain object (not null, not an array, not a
primitive) and route errors through ctx.logger.fatal instead of tui.fatal; after
parsing, check typeof metadata === 'object' && metadata !== null &&
!Array.isArray(metadata') and if invalid call ctx.logger.fatal('Invalid metadata
JSON shape', ErrorCode.INVALID_ARGUMENT). Also replace any raw console.log
output later in this command with the CLI tui helpers (e.g., tui.info or
tui.success) to standardize output handling.
In `@packages/cli/src/cmd/cloud/queue/receive.ts`:
- Around line 60-66: The code prints non-JSON payloads using console.log which
bypasses CLI formatting; update the catch block in the receive handler that
currently parses message.payload (look for message.payload and the JSON.parse
block) to use the tui helper (e.g., tui.info or another tui.* method consistent
with surrounding usage) instead of console.log so all payload output uses the
CLI's formatting utilities.
- Around line 28-48: Move the timeout flag from the args schema into the options
schema and update handler to read it from opts: modify the zod schema so timeout
is defined under options (use z.coerce.number().default(30).optional()) instead
of args, then in async handler(ctx) destructure opts (or options) and pass
opts.timeout into receiveMessage (instead of args.timeout); also replace any
console.log fallback used to print the received payload with tui.info(...) or
tui.json(...) to use the CLI tui helpers. Ensure changes reference the existing
handler(ctx), schema object, receiveMessage, createQueueAPIClient, and
getQueueApiOptions symbols.
In `@packages/opencode/README.md`:
- Around line 94-95: The README JSON snippet contains hard tab characters
between entries causing markdown lint MD010; replace any hard tabs in the shown
JSON (the lines with "$schema": "https://opencode.ai/config.json" and "plugin":
["/path/to/agentuity/sdk/packages/opencode"]) with spaces so indentation uses
only spaces, preserving the same structure and alignment.
In `@packages/server/src/api/queue/dlq.ts`:
- Around line 243-276: deleteDeadLetterMessage currently only checks for
message-not-found but should also detect when the queue itself is missing like
replayDeadLetterMessage does; add a check after receiving resp that if
resp.message contains 'queue' and 'not found' you throw a QueueNotFoundError
populated with queueName and resp.message (place this check before the
MessageNotFoundError branch), keeping the existing MessageNotFoundError and
final QueueError behavior intact and referencing resp.message, queueName, and
messageId in the thrown error objects.
In `@packages/server/src/api/queue/validation.ts`:
- Around line 153-167: validatePayload currently compares payload.length (UTF-16
code units) to MAX_PAYLOAD_SIZE, which misreports byte size; change the size
check to compute UTF-8 bytes (const payloadBytes = new
TextEncoder().encode(payload).length) and use payloadBytes in the comparison
against MAX_PAYLOAD_SIZE and in the QueueValidationError value/message so the
error reports actual byte count; keep the empty-string check but replace any
usage of payload.length for sizing with the new payloadBytes variable in
validatePayload.
In `@scripts/test-queue.sh`:
- Around line 143-148: The test extracts MESSAGE_ID from PUBLISH_OUTPUT but
never uses it; remove the unused extraction or rename it to a conventional
discard (e.g., _MESSAGE_ID) to indicate intentional ignore. Update the if-block
around the PUBLISH_OUTPUT check (the clause that calls pass/fail and currently
sets MESSAGE_ID) by deleting the MESSAGE_ID assignment or changing the variable
name to _MESSAGE_ID so there are no unused variable warnings while preserving
the pass/fail behavior.
🧹 Nitpick comments (28)
packages/cli/test/dependency-checker.test.ts (1)
10-36: Remove dead TTY mocking code.Since
checkAndUpgradeDependenciesno longer checks TTY internally (as documented in the comment at line 164), the TTY mocking inbeforeEach/afterEachand theoriginalIsTTYvariable are now dead code.♻️ Suggested cleanup
describe('dependency-checker', () => { let testDir: string; - let originalIsTTY: boolean; beforeEach(() => { // Create a temporary test directory testDir = join(tmpdir(), `dependency-checker-test-${Date.now()}`); mkdirSync(testDir, { recursive: true }); mkdirSync(join(testDir, 'node_modules', '@agentuity', 'core'), { recursive: true }); - - // Mock stdin.isTTY for testing - originalIsTTY = process.stdin.isTTY; - Object.defineProperty(process.stdin, 'isTTY', { - value: true, - writable: true, - configurable: true, - }); }); afterEach(() => { // Clean up test directory rmSync(testDir, { recursive: true, force: true }); - - // Restore stdin.isTTY - Object.defineProperty(process.stdin, 'isTTY', { - value: originalIsTTY, - writable: true, - configurable: true, - }); });packages/cli/test/cmd/cloud/region-requirements.test.ts (2)
70-97: Add explicit org-not-required assertions for DB commands.
The comments say org is auto-discovered, but the tests don’t enforce that. Consider asserting it’s not required to prevent regressions.Proposed diff
test('db list does not require region', () => { const requires = dbListSubcommand.requires as Record<string, boolean> | undefined; expect(requires?.region).toBeUndefined(); expect(requires?.auth).toBe(true); + expect(requires?.org).toBeFalsy(); // org is not required - auto-discovered from resource }); test('db get does not require region', () => { const requires = dbGetSubcommand.requires as Record<string, boolean> | undefined; expect(requires?.region).toBeUndefined(); expect(requires?.auth).toBe(true); + expect(requires?.org).toBeFalsy(); // org is not required - auto-discovered from resource }); test('db delete does not require region', () => { const requires = dbDeleteSubcommand.requires as Record<string, boolean> | undefined; expect(requires?.region).toBeUndefined(); expect(requires?.auth).toBe(true); + expect(requires?.org).toBeFalsy(); // org is not required - auto-discovered from resource }); test('db logs does not require region', () => { const requires = dbLogsSubcommand.requires as Record<string, boolean> | undefined; expect(requires?.region).toBeUndefined(); expect(requires?.auth).toBe(true); + expect(requires?.org).toBeFalsy(); // org is not required - auto-discovered from resource });
100-135: Add explicit org-not-required assertions for storage get/delete/upload/download.
The tests state org is auto-discovered but don’t assert it. Locking this in will catch regressions.Proposed diff
test('storage get does not require region or org (auto-discovered)', () => { const requires = storageGetSubcommand.requires as Record<string, boolean> | undefined; expect(requires?.region).toBeUndefined(); expect(requires?.auth).toBe(true); + expect(requires?.org).toBeFalsy(); // org is optional - auto-discovered from resource name via cache or API }); test('storage delete does not require region or org (auto-discovered)', () => { const requires = storageDeleteSubcommand.requires as Record<string, boolean> | undefined; expect(requires?.region).toBeUndefined(); expect(requires?.auth).toBe(true); + expect(requires?.org).toBeFalsy(); // org is optional - auto-discovered from resource name via cache or API }); test('storage upload does not require region or org (auto-discovered)', () => { const requires = storageUploadSubcommand.requires as Record<string, boolean> | undefined; expect(requires?.region).toBeUndefined(); expect(requires?.auth).toBe(true); + expect(requires?.org).toBeFalsy(); // org is optional - auto-discovered from resource name via cache or API }); test('storage download does not require region or org (auto-discovered)', () => { const requires = storageDownloadSubcommand.requires as Record<string, boolean> | undefined; expect(requires?.region).toBeUndefined(); expect(requires?.auth).toBe(true); + expect(requires?.org).toBeFalsy(); // org is optional - auto-discovered from resource name via cache or API });packages/cli/src/cmd/cloud/env/list.ts (1)
176-178: Consider using a tui helper instead of console.log().The code uses
console.log()for formatted output, which violates the coding guideline: "Usetui.*helpers for formatted output instead of raw console logs". While this may be intentional to allow piping output, it's inconsistent with the guideline and other parts of the code that usetui.info().As per coding guidelines, formatted output should use tui helpers. If a specific tui helper for stdout (without prefixes) doesn't exist, this could be an exception, but it would be worth documenting.
apps/testing/queues/tsconfig.json (1)
9-12: Consider removingdeclarationanddeclarationMapwhennoEmitis true.With
noEmit: true, thedeclarationanddeclarationMapoptions on lines 11-12 have no effect since TypeScript won't emit any output files. These can be safely removed to avoid confusion.Suggested cleanup
"compilerOptions": { "target": "ESNext", "module": "ESNext", "moduleResolution": "bundler", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "noEmit": true, "resolveJsonModule": true, - "declaration": true, - "declarationMap": true, - "outDir": "dist", - "rootDir": ".", "types": ["bun"] },apps/testing/queues/package.json (1)
15-17: Consider pinning@types/bunto a specific version.Using
"latest"for@types/buncan lead to non-reproducible builds if the type definitions change unexpectedly. For a test app this is low risk, but pinning to a specific version (e.g.,"^1.1.0") ensures consistent type checking across environments.Suggested change
"devDependencies": { - "@types/bun": "latest" + "@types/bun": "^1.1.0" }scripts/test-queue.sh (2)
206-214: Confusing|| [ $? -eq 0 ]pattern.The
$?after the pipe refers to grep's exit code, not the ACK command's. If grep matches, the condition passes anyway; if grep fails,$?is non-zero so[ $? -eq 0 ]is false. This makes the|| [ $? -eq 0 ]clause effectively dead code.Consider simplifying to rely solely on the grep match or capture the command's exit code separately.
Proposed simplification
if [ -n "$RECEIVED_ID" ]; then info "Test: queue ack" - ACK_OUTPUT=$($CLI cloud queue ack "$QUEUE_NAME" "$RECEIVED_ID" 2>&1) || true - if echo "$ACK_OUTPUT" | grep -qi "acknowledged\|success" || [ $? -eq 0 ]; then + if ACK_OUTPUT=$($CLI cloud queue ack "$QUEUE_NAME" "$RECEIVED_ID" 2>&1); then + pass "queue ack succeeds" + elif echo "$ACK_OUTPUT" | grep -qi "acknowledged\|success"; then pass "queue ack succeeds" else fail "queue ack failed" "$ACK_OUTPUT" fi fi
349-359: Test verifies API error handling, not specific queue deletion.The test at line 352 generates a new timestamp-based queue name rather than using the deleted queue's name. The comment acknowledges this, but the test is essentially checking that the API handles non-existent queues gracefully rather than verifying the specific queue was deleted.
Consider storing the original queue name before clearing it to verify the specific deleted queue is inaccessible, or update the pass message to better reflect what's being tested.
packages/runtime/src/services/local/queue.ts (1)
45-49: Consider adding input validation forqueueName.The
publishmethod doesn't validate thequeueNameparameter. Other queue API implementations (e.g.,publishMessageinpackages/server/src/api/queue/messages.ts) validate queue names before operations. Consider adding validation to maintain consistency and provide better error messages.packages/server/src/api/api.ts (1)
226-233: Consider addingextraHeaderstorawPostfor consistency.All other HTTP methods now support
extraHeaders, butrawPostdoes not. While this may not be needed currently, adding it would maintain API consistency.♻️ Suggested change
async rawPost( endpoint: string, body: Uint8Array | ArrayBuffer | ReadableStream<Uint8Array> | string, contentType: string, - signal?: AbortSignal + signal?: AbortSignal, + extraHeaders?: Record<string, string> ): Promise<Response> { - return this.#makeRequest('POST', endpoint, body, signal, contentType); + return this.#makeRequest('POST', endpoint, body, signal, contentType, extraHeaders); }apps/testing/queues/index.ts (2)
103-117: Consider adding a timeout toreceiveMessage.The
receiveMessagecall at line 105 doesn't specify a timeout, which could cause the test to hang if no messages are available (depending on API behavior). Based on thereceiveMessagesignature inpackages/server/src/api/queue/receive.ts, a timeout parameter is supported.♻️ Suggested change
// 7. Receive and acknowledge a message console.log('📥 Receiving message...'); -const received = await receiveMessage(client, queueName); +const received = await receiveMessage(client, queueName, 5); // 5 second timeout
119-128: Same timeout consideration for secondreceiveMessagecall.♻️ Suggested change
// 8. Receive and nack a message (return to queue) console.log('📥 Receiving another message...'); -const received2 = await receiveMessage(client, queueName); +const received2 = await receiveMessage(client, queueName, 5); // 5 second timeoutapps/testing/queues/README.md (1)
51-105: Add language identifier to fenced code block.Per the static analysis hint, consider adding a language identifier (e.g.,
textorconsole) to the expected output code block for better markdown rendering.📝 Suggested fix
-``` +```text 🚀 Starting Queue API Test...packages/server/src/api/queue/validation.ts (1)
427-441: Consider using URL constructor for more robust validation.The current check only verifies the protocol prefix. Using
new URL(url)would catch malformed URLs (e.g.,http://with no host).♻️ Optional: More robust URL validation
export function validateWebhookUrl(url: string): void { if (!url) { throw new QueueValidationError({ message: 'Webhook URL is required', field: 'url', }); } - if (!url.startsWith('http://') && !url.startsWith('https://')) { + try { + const parsed = new URL(url); + if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') { + throw new QueueValidationError({ + message: 'Webhook URL must use HTTP or HTTPS protocol', + field: 'url', + value: url, + }); + } + } catch (e) { + if (e instanceof QueueValidationError) throw e; throw new QueueValidationError({ message: 'Webhook URL must be a valid HTTP or HTTPS URL', field: 'url', value: url, }); } }packages/cli/src/cmd/cloud/queue/delete.ts (1)
36-41: Use ctx.logger.fatal + StructuredError for validation exits.Line 39-40 uses
tui.fatal; the CLI guidelines call for logging viactx.logger.fatal()and StructuredError so failures are captured consistently. As per coding guidelines, use the logger-based fatal path with StructuredError.packages/cli/src/cmd/cloud/queue/create.ts (1)
76-80: Usetui.*helpers instead of rawconsole.log.As per coding guidelines for
packages/cli/src/cmd/**/*.ts, usetui.*helpers for formatted output instead of raw console logs.♻️ Suggested fix
if (!options.json) { tui.success(`Created queue: ${queue.name}`); - console.log(` ID: ${queue.id}`); - console.log(` Type: ${queue.queue_type}`); + tui.info(` ID: ${queue.id}`); + tui.info(` Type: ${queue.queue_type}`); }packages/runtime/src/_services.ts (1)
210-213: Consider supporting custom queue service override for consistency.Other services (kv, stream, vector) support custom implementations via
config?.services?.keyvalue, etc. The queue service is always instantiated directly without checking for a custom override. This may be intentional for now, but consider adding the same pattern for consistency if custom queue implementations are expected in the future.♻️ Optional pattern for consistency
- queue = new LocalQueueStorage(db, projectPath); + queue = config?.services?.queue || new LocalQueueStorage(db, projectPath);This would require adding
queue?: QueueServiceto the services config type.packages/cli/src/cmd/cloud/queue/dlq.ts (1)
40-47: Pagination parameters should be options, not args.
limitandoffsetare currently defined as positional args, but they are typically CLI options (e.g.,--limit 10 --offset 20). This also makes the command signature clearer:cloud queue dlq list my-queue --limit 10vscloud queue dlq list my-queue 10 20.♻️ Suggested fix
schema: { args: z.object({ queue_name: z.string().min(1).describe('Queue name'), - limit: z.coerce.number().optional().describe('Maximum number of messages to return'), - offset: z.coerce.number().optional().describe('Offset for pagination'), }), + options: z.object({ + limit: z.coerce.number().optional().describe('Maximum number of messages to return'), + offset: z.coerce.number().optional().describe('Offset for pagination'), + }), response: DlqListResponseSchema, },Then update the handler to use
opts.limitandopts.offsetinstead ofargs.limitandargs.offset.packages/cli/src/cmd/cloud/queue/stats.ts (1)
237-237: Usetui.newline()instead oftui.info('')for empty lines.For consistency, use
tui.newline()to output empty lines instead oftui.info('').♻️ Suggested fix
- tui.info(''); + tui.newline();Apply the same change at line 240.
packages/cli/src/cmd/cloud/queue/get.ts (1)
88-92: Usetui.fatal()instead oftui.error()+process.exit(1).Per coding guidelines, use
ctx.logger.fatal()ortui.fatal()to log and exit with code 1 in a single call.♻️ Suggested fix
if (args.identifier.startsWith('msg_')) { if (!opts.queue) { - tui.error('--queue is required when getting a message by ID'); - process.exit(1); + tui.fatal('--queue is required when getting a message by ID'); }Note: You may need to import
ErrorCodeiftui.fatalrequires an error code parameter (like in dlq.ts).packages/core/src/services/queue.ts (1)
351-382: Prefer a portable timeout strategy overAbortSignal.timeout.Line 351 uses
AbortSignal.timeout, which is not universally available in all browser runtimes. Since this package must be browser-compatible, consider a simpleAbortController+setTimeoutfallback (or an adapter-provided timeout). Please verify runtime support for your target environments. As per coding guidelines, core code must be runtime-agnostic.🔧 Proposed change
- const signal = AbortSignal.timeout(30_000); - const res = await this.#adapter.invoke<QueuePublishResult>(url, { - method: 'POST', - signal, - body: JSON.stringify(requestBody), - contentType: 'application/json', - telemetry: { - name: 'agentuity.queue.publish', - attributes: { - queueName, - }, - }, - }); - - if (res.ok) { - const data = res.data as unknown as { - message: { id: string; offset: number; published_at: string }; - }; - return { - id: data.message.id, - offset: data.message.offset, - publishedAt: data.message.published_at, - }; - } - - if (res.response.status === 404) { - throw new QueueNotFoundError({ - message: `Queue not found: ${queueName}`, - }); - } - - throw await toServiceException('POST', url, res.response); + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 30_000); + try { + const res = await this.#adapter.invoke<QueuePublishResult>(url, { + method: 'POST', + signal: controller.signal, + body: JSON.stringify(requestBody), + contentType: 'application/json', + telemetry: { + name: 'agentuity.queue.publish', + attributes: { + queueName, + }, + }, + }); + + if (res.ok) { + const data = res.data as unknown as { + message: { id: string; offset: number; published_at: string }; + }; + return { + id: data.message.id, + offset: data.message.offset, + publishedAt: data.message.published_at, + }; + } + + if (res.response.status === 404) { + throw new QueueNotFoundError({ + message: `Queue not found: ${queueName}`, + }); + } + + throw await toServiceException('POST', url, res.response); + } finally { + clearTimeout(timeoutId); + }packages/server/src/api/queue/queues.ts (2)
83-84: Redundant nullish coalescing in TTL validation.The expression
params.settings.default_ttl_seconds ?? undefinedis redundant since the value is already checked for!== undefinedin the condition on line 83.Suggested fix
if (params.settings.default_ttl_seconds !== undefined) { - validateTTL(params.settings.default_ttl_seconds ?? undefined); + validateTTL(params.settings.default_ttl_seconds); }
244-296: Same redundant nullish coalescing pattern in updateQueue.Line 258 has the same redundant
?? undefinedpattern as increateQueue.Suggested fix
if (params.settings.default_ttl_seconds !== undefined) { - validateTTL(params.settings.default_ttl_seconds ?? undefined); + validateTTL(params.settings.default_ttl_seconds); }packages/server/src/api/queue/analytics.ts (2)
276-306: Consider logging or reporting malformed SSE events instead of silent skip.The
streamSSEhelper silently swallows parse errors (lines 296-298). While this prevents a single malformed event from breaking the stream, it could mask issues in production. Consider at least logging the malformed data for debugging purposes.Suggested improvement
try { const parsed = JSON.parse(jsonStr); const event = SSEStatsEventSchema.parse(parsed); yield event; - } catch { - // Skip malformed events + } catch (e) { + // Skip malformed events, but could log for debugging + // console.debug('Malformed SSE event:', jsonStr, e); }
279-302: Potential incomplete event handling when stream ends mid-line.After the
whileloop exits ondone, any remaining data inbufferis not processed. If the server sends a final event without a trailing newline, it would be lost.Suggested fix to handle final buffer content
try { while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const lines = buffer.split('\n'); buffer = lines.pop() || ''; for (const line of lines) { if (line.startsWith('data: ')) { const jsonStr = line.slice(6).trim(); if (jsonStr && jsonStr !== '{}') { try { const parsed = JSON.parse(jsonStr); const event = SSEStatsEventSchema.parse(parsed); yield event; } catch { // Skip malformed events } } } } } + // Process any remaining data in buffer after stream ends + if (buffer.startsWith('data: ')) { + const jsonStr = buffer.slice(6).trim(); + if (jsonStr && jsonStr !== '{}') { + try { + const parsed = JSON.parse(jsonStr); + const event = SSEStatsEventSchema.parse(parsed); + yield event; + } catch { + // Skip malformed events + } + } + } } finally { reader.releaseLock(); }packages/server/src/api/queue/messages.ts (1)
706-743: Consider validating the timeout parameter.The
receiveMessagefunction accepts an optionaltimeoutparameter (documented as 0-30 seconds), but there's no validation to ensure it's within acceptable bounds. While the server likely validates this, client-side validation would provide better error messages.Suggested improvement
export async function receiveMessage( client: APIClient, queueName: string, timeout?: number, options?: QueueApiOptions ): Promise<Message | null> { validateQueueName(queueName); + if (timeout !== undefined && (timeout < 0 || timeout > 30)) { + throw new QueueValidationError({ + message: 'Timeout must be between 0 and 30 seconds', + field: 'timeout', + value: timeout, + }); + } const searchParams = new URLSearchParams();Note: This would require importing
QueueValidationErrorfrom./validation.packages/server/src/api/queue/types.ts (2)
254-260: Consider adding URL format validation.The
urlfield accepts any string. For webhook destinations, validating this as a proper URL would catch configuration errors at parse time rather than at delivery time.♻️ Suggested improvement
export const HttpDestinationConfigSchema = z.object({ /** The URL to send messages to. */ - url: z.string(), + url: z.string().url(), /** Optional custom headers to include in requests. */ headers: z.record(z.string(), z.string()).optional(), /** HTTP method to use (default: POST). */ - method: z.string().default('POST'), + method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']).default('POST'),
277-285: Signing configuration allows enabled without secret_key.When
signing.enabledistrue, thesecret_keyshould be required to prevent runtime failures during HMAC signing. Consider adding a refinement or restructuring.♻️ Possible refinement
/** Optional request signing configuration. */ signing: z .object({ /** Whether signing is enabled (default: false). */ enabled: z.boolean().default(false), /** Secret key for HMAC signing. */ secret_key: z.string().optional(), }) + .refine( + (data) => !data.enabled || data.secret_key, + { message: 'secret_key is required when signing is enabled' } + ) .optional(),
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In `@packages/server/src/api/queue/analytics.ts`:
- Around line 209-307: Update the SSE streaming path to properly close the HTTP
stream and map 404s for queue-specific streams: modify streamQueueAnalytics to
pass the queue name into streamSSE (i.e., call yield* streamSSE(client, url,
options?.orgId, name)), update streamSSE signature to accept an optional
queueName, and after calling client.rawGet (APIClient.rawGet) check if
response.status === 404 and throw QueueNotFoundError({ name: queueName })
instead of a generic QueueError; keep the existing non-404 error throw for other
statuses. In streamSSE's finally block call reader.cancel() before
reader.releaseLock() to ensure the underlying connection is closed when
consumers stop early. Use the existing QueueNotFoundError, QueueError,
streamSSE, streamQueueAnalytics, and streamOrgAnalytics symbols to locate the
changes.
In `@packages/server/src/api/queue/dlq.ts`:
- Around line 44-51: The example in the listDeadLetterMessages doc uses
non-existent fields (`error`, `attempts`, `last_attempt_at`) which mismatch the
DeadLetterMessage schema; update the snippet that demonstrates
listDeadLetterMessages (and any examples referencing DeadLetterMessage) to use
the actual fields `failure_reason`, `delivery_attempts`, and `moved_at` instead
so the example code matches the DeadLetterMessage type and avoids confusion.
In `@scripts/test-queue.sh`:
- Around line 338-357: The deletion test is checking a newly generated queue
instead of the one just deleted; before you clear QUEUE_NAME, save it (e.g.,
DELETED_QUEUE="$QUEUE_NAME"), then after deletion use that saved name when
calling $CLI cloud queue get (use DELETED_QUEUE for GONE_OUTPUT) to verify the
specific deleted queue is gone; update references to QUEUE_NAME clearing so you
only unset QUEUE_NAME after saving it and adjust the conditional that checks
GONE_OUTPUT accordingly.
- Around line 205-212: The ACK block (and similar nack, delete, destination
delete, dlq blocks) currently pipes the CLI call into ACK_OUTPUT with "|| true"
and then tests grep's exit status via "[ $? -eq 0 ]", which checks grep not the
CLI; instead, capture the CLI exit code immediately after running the command
(e.g., store the exit into a variable like ACK_STATUS right after the CLI
invocation that produces ACK_OUTPUT), then use a conditional that passes if
either grep finds the success keyword in ACK_OUTPUT or the saved ACK_STATUS
equals 0. Apply the same pattern to the NACK, DELETE, DESTINATION DELETE and DLQ
blocks using appropriately named variables (e.g., NACK_OUTPUT/NACK_STATUS,
DELETE_OUTPUT/DELETE_STATUS, etc.) to ensure the original CLI exit status is
checked when grep doesn't match.
🧹 Nitpick comments (1)
scripts/test-queue.sh (1)
245-249: Avoid hard dependency on an external destination endpoint in CI.Line 247 hardcodes an external URL, which can introduce flaky tests or network policy failures. Consider making it configurable via an environment variable with a safe default.
🛠️ Suggested tweak
+DESTINATION_URL="${QUEUE_TEST_DESTINATION_URL:-https://httpbin.org/post}" -DEST_OUTPUT=$($CLI cloud queue destinations create "$QUEUE_NAME" --url "https://httpbin.org/post" --json 2>&1) || true +DEST_OUTPUT=$($CLI cloud queue destinations create "$QUEUE_NAME" --url "$DESTINATION_URL" --json 2>&1) || true
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
packages/cli/src/cmd/cloud/queue/delete.tspackages/cli/src/cmd/cloud/queue/list.tspackages/cli/src/cmd/cloud/queue/messages.tspackages/cli/src/cmd/cloud/queue/receive.tspackages/server/src/api/queue/analytics.tspackages/server/src/api/queue/dlq.tspackages/server/src/api/queue/validation.tsscripts/test-queue.sh
🚧 Files skipped from review as they are similar to previous changes (4)
- packages/cli/src/cmd/cloud/queue/delete.ts
- packages/cli/src/cmd/cloud/queue/list.ts
- packages/cli/src/cmd/cloud/queue/receive.ts
- packages/cli/src/cmd/cloud/queue/messages.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use Prettier formatter with tabs (width 3), single quotes, and semicolons for TypeScript files
Use TypeScript strict mode with ESNext target and bundler moduleResolution
UseStructuredErrorfrom@agentuity/corefor error handling
Files:
packages/server/src/api/queue/validation.tspackages/server/src/api/queue/dlq.tspackages/server/src/api/queue/analytics.ts
packages/server/src/**/*.ts
📄 CodeRabbit inference engine (packages/server/AGENTS.md)
packages/server/src/**/*.ts: Build TypeScript withbun run buildcommand using tsc for compilation
Run TypeScript type checking withbun run typecheck
All code must be runtime-agnostic - no Bun-specific or Node-specific APIs
No browser APIs allowed - server-side only
Prefer interfaces for public APIs in TypeScript
Files:
packages/server/src/api/queue/validation.tspackages/server/src/api/queue/dlq.tspackages/server/src/api/queue/analytics.ts
🧠 Learnings (3)
📚 Learning: 2025-12-19T14:19:33.765Z
Learnt from: jhaynie
Repo: agentuity/sdk PR: 259
File: packages/cli/src/cmd/build/vite/registry-generator.ts:306-312
Timestamp: 2025-12-19T14:19:33.765Z
Learning: Route files under src/api should use the .ts extension only (no .tsx) and regex patterns for such paths should anchor to \.ts$ (e.g., /\/.ts$/). Agent files may support both .ts and .tsx, but route files in the Agentuity SDK codebase are restricted to .ts. This guideline applies to all similar route files under src/api across the repository.
Applied to files:
packages/server/src/api/queue/validation.tspackages/server/src/api/queue/dlq.tspackages/server/src/api/queue/analytics.ts
📚 Learning: 2025-12-21T00:31:41.858Z
Learnt from: jhaynie
Repo: agentuity/sdk PR: 274
File: packages/cli/src/cmd/build/vite/server-bundler.ts:12-41
Timestamp: 2025-12-21T00:31:41.858Z
Learning: In Bun runtime, BuildMessage and ResolveMessage are global types and are not exported from the bun module. Do not import { BuildMessage } from 'bun' or similar; these types are available globally and should be used without import. This applies to all TypeScript files that target the Bun runtime within the repository.
Applied to files:
packages/server/src/api/queue/validation.tspackages/server/src/api/queue/dlq.tspackages/server/src/api/queue/analytics.ts
📚 Learning: 2025-12-30T00:13:37.849Z
Learnt from: jhaynie
Repo: agentuity/sdk PR: 355
File: packages/server/src/api/sandbox/util.ts:2-6
Timestamp: 2025-12-30T00:13:37.849Z
Learning: In the packages/server tree, treat code as runtime-agnostic between Node.js and Bun. Ensure TypeScript files (e.g., util.ts) import and use APIs in a way that works under both runtimes. It is acceptable to rely on Bun’s Node.js compatibility for built-ins accessed via the node: namespace (e.g., node:events, node:stream, node:buffer). During reviews, prefer patterns and imports that remain compatible with Bun's environment, and flag any hard dependencies on runtime-specific globals or non-portable Node APIs.
Applied to files:
packages/server/src/api/queue/validation.tspackages/server/src/api/queue/dlq.tspackages/server/src/api/queue/analytics.ts
🧬 Code graph analysis (2)
packages/server/src/api/queue/validation.ts (2)
packages/core/src/services/queue.ts (1)
QueueValidationError(198-203)packages/core/src/index.ts (2)
QueueValidationError(74-74)StructuredError(2-2)
packages/server/src/api/queue/dlq.ts (5)
packages/server/src/api/api.ts (2)
APIResponseSchema(696-706)APIResponseSchemaNoData(720-729)packages/server/src/api/queue/index.ts (15)
DeadLetterMessageSchema(62-62)MessageSchema(57-57)listDeadLetterMessages(177-177)ListDlqRequest(93-93)QueueApiOptions(94-94)DeadLetterMessage(83-83)validateQueueName(212-212)validateLimit(225-225)validateOffset(224-224)QueueNotFoundError(132-132)replayDeadLetterMessage(178-178)Message(78-78)validateMessageId(215-215)purgeDeadLetter(179-179)deleteDeadLetterMessage(180-180)packages/server/src/api/queue/types.ts (6)
DeadLetterMessageSchema(368-391)MessageSchema(188-223)ListDlqRequest(602-602)QueueApiOptions(408-414)DeadLetterMessage(396-396)Message(228-228)packages/server/src/api/queue/validation.ts (4)
validateQueueName(104-127)validateLimit(380-395)validateOffset(364-372)validateMessageId(178-186)packages/server/src/api/queue/util.ts (4)
queueApiPathWithQuery(125-132)buildQueueHeaders(142-147)QueueNotFoundError(35-35)queueApiPath(107-113)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: SDK Integration Test Suite
- GitHub Check: Playwright E2E Smoke Test
- GitHub Check: Cloud Deployment Tests
- GitHub Check: Sandbox CLI Tests
🔇 Additional comments (13)
packages/server/src/api/queue/dlq.ts (4)
54-100: LGTM forlistDeadLetterMessages.
Validation, pagination, and error mapping are consistent.
125-167: LGTM forreplayDeadLetterMessage.
Clear validation and error differentiation.
190-218: LGTM forpurgeDeadLetter.
Consistent handling with other queue APIs.
243-282: LGTM fordeleteDeadLetterMessage.
Error mapping covers both queue and message cases.packages/server/src/api/queue/analytics.ts (3)
27-39: Query builder is clean and avoids empty query strings.
61-81: Org analytics fetch looks good.
104-189: Queue analytics and time-series handlers are consistent.
Validation and error mapping align with the rest of the API surface.packages/server/src/api/queue/validation.ts (6)
15-56: Validation constants and regexes are clear and well-scoped.
61-143: QueueValidationError and name/type checks look consistent.
153-167: Payload validation looks solid.
178-204: Message/destination ID format checks are clear.
212-418: Bounds checks for descriptions and numeric limits are consistent.
428-490: Webhook and destination config validation is straightforward.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@packages/cli/src/cmd/cloud/queue/dlq.ts`:
- Around line 40-60: The schema currently defines pagination as positional args
(args.queue_name, args.limit, args.offset) but should use CLI flags in options
like the other command; update the z schema to move limit and offset out of args
and into options (use options: z.object({ limit: z.coerce.number().optional(),
offset: z.coerce.number().optional() }) and keep queue_name in args), then
update the handler to read pagination from ctx.options (e.g.
options.limit/options.offset) instead of ctx.args so the call to
listDeadLetterMessages(client, args.queue_name, { limit, offset },
getQueueApiOptions(ctx)) uses the values from options; ensure
DlqListResponseSchema remains the response schema and keep createQueueAPIClient
and getQueueApiOptions usages unchanged.
♻️ Duplicate comments (3)
packages/cli/src/cmd/cloud/queue/publish.ts (2)
56-63: Validate that parsed metadata is a plain object.The metadata parsing accepts any valid JSON, but the server expects a
Record<string, unknown>. Passing an array ([]) or primitive ("string") will fail server-side validation with a less helpful error.🔧 Suggested validation
let metadata: Record<string, unknown> | undefined; if (opts.metadata) { try { - metadata = JSON.parse(opts.metadata); + const parsed = JSON.parse(opts.metadata); + if (parsed === null || Array.isArray(parsed) || typeof parsed !== 'object') { + tui.fatal('Metadata must be a JSON object', ErrorCode.INVALID_ARGUMENT); + } + metadata = parsed as Record<string, unknown>; } catch { tui.fatal('Invalid metadata JSON', ErrorCode.INVALID_ARGUMENT); } }
78-82: Usetui.*helpers for published message details.Lines 80-81 use raw
console.log()which violates CLI output guidelines. Replace withtui.info()for consistent formatting.🔧 Suggested fix
if (!options.json) { tui.success(`Published message: ${message.id}`); - console.log(` Offset: ${message.offset}`); - console.log(` State: ${message.state}`); + tui.info(` Offset: ${message.offset}`); + tui.info(` State: ${message.state}`); }packages/cli/src/cmd/cloud/queue/destinations.ts (1)
126-127: Usetui.*helpers for destination details output.Lines 126-127 use
console.loginstead oftui.*helpers. Per coding guidelines, CLI output should go throughtui.*helpers for consistent formatting.🔧 Proposed fix
if (!options.json) { tui.success(`Created destination: ${destination.id}`); - console.log(` URL: ${destination.config.url}`); - console.log(` Method: ${destination.config.method}`); + tui.info(` URL: ${destination.config.url}`); + tui.info(` Method: ${destination.config.method}`); }
🧹 Nitpick comments (4)
packages/cli/src/cmd/cloud/queue/pause.ts (1)
32-35: Consider handling potentially nullpaused_atvalue.According to
QueueSchema,paused_atisz.string().nullable().optional(). WhilepauseQueueshould always return a queue withpaused_atset on success, a defensive check would prevent displaying "Paused at: null" in edge cases.🔧 Suggested defensive check
if (!options.json) { tui.success(`Paused queue: ${queue.name}`); - tui.info(` Paused at: ${queue.paused_at}`); + if (queue.paused_at) { + tui.info(` Paused at: ${queue.paused_at}`); + } }packages/cli/src/cmd/cloud/queue/destinations.ts (1)
131-139: Usectx.logger.fatal()instead ofprocess.exit(1).Per coding guidelines, use
ctx.logger.fatal()to log and exit with code 1 rather than callingprocess.exit(1)directly.🔧 Proposed fix
} catch (error) { if (error instanceof DestinationAlreadyExistsError) { - tui.error( - `A destination with URL "${opts.url}" already exists for queue "${args.queue_name}"` - ); - tui.info('Hint: Use a different URL or delete the existing destination first'); - process.exit(1); + ctx.logger.fatal( + `A destination with URL "${opts.url}" already exists for queue "${args.queue_name}"\n` + + 'Hint: Use a different URL or delete the existing destination first' + ); } throw error; }packages/cli/src/cmd/cloud/queue/stats.ts (1)
45-81: Consider usingtui.*helpers consistently for org analytics display.The
displayOrgAnalyticsfunction mixesconsole.logwithtuihelpers (e.g.,tui.muted,tui.colorPrimary). Per coding guidelines, CLI output should go throughtui.*helpers. Consider usingtui.info()or similar for consistency.packages/server/src/api/queue/messages.ts (1)
706-743: Consider validating the timeout parameter.The documentation mentions the timeout should be between 0-30 seconds, but there's no client-side validation. While the server will likely reject invalid values, adding validation here would provide faster feedback to the caller and be consistent with how other parameters are validated in this module.
💡 Optional: Add timeout validation
export async function receiveMessage( client: APIClient, queueName: string, timeout?: number, options?: QueueApiOptions ): Promise<Message | null> { validateQueueName(queueName); + if (timeout !== undefined && (timeout < 0 || timeout > 30)) { + throw new QueueValidationError('Timeout must be between 0 and 30 seconds'); + } const searchParams = new URLSearchParams();
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (21)
packages/cli/src/cmd/cloud/queue/ack.tspackages/cli/src/cmd/cloud/queue/delete.tspackages/cli/src/cmd/cloud/queue/destinations.tspackages/cli/src/cmd/cloud/queue/dlq.tspackages/cli/src/cmd/cloud/queue/get.tspackages/cli/src/cmd/cloud/queue/index.tspackages/cli/src/cmd/cloud/queue/list.tspackages/cli/src/cmd/cloud/queue/messages.tspackages/cli/src/cmd/cloud/queue/nack.tspackages/cli/src/cmd/cloud/queue/pause.tspackages/cli/src/cmd/cloud/queue/publish.tspackages/cli/src/cmd/cloud/queue/receive.tspackages/cli/src/cmd/cloud/queue/resume.tspackages/cli/src/cmd/cloud/queue/stats.tspackages/cli/src/cmd/cloud/queue/util.tspackages/server/src/api/queue/destinations.tspackages/server/src/api/queue/index.tspackages/server/src/api/queue/messages.tspackages/server/src/api/queue/types.tspackages/server/src/api/queue/util.tspackages/server/src/api/queue/validation.ts
🚧 Files skipped from review as they are similar to previous changes (4)
- packages/server/src/api/queue/destinations.ts
- packages/cli/src/cmd/cloud/queue/index.ts
- packages/server/src/api/queue/validation.ts
- packages/cli/src/cmd/cloud/queue/delete.ts
🧰 Additional context used
📓 Path-based instructions (4)
packages/cli/src/cmd/**/*.ts
📄 CodeRabbit inference engine (packages/cli/AGENTS.md)
packages/cli/src/cmd/**/*.ts: Usetui.*helpers for formatted output instead of raw console logs
Usectx.loggerfor logging; calllogger.fatal()to log and exit with code 1
Files:
packages/cli/src/cmd/cloud/queue/util.tspackages/cli/src/cmd/cloud/queue/receive.tspackages/cli/src/cmd/cloud/queue/pause.tspackages/cli/src/cmd/cloud/queue/resume.tspackages/cli/src/cmd/cloud/queue/messages.tspackages/cli/src/cmd/cloud/queue/stats.tspackages/cli/src/cmd/cloud/queue/list.tspackages/cli/src/cmd/cloud/queue/nack.tspackages/cli/src/cmd/cloud/queue/dlq.tspackages/cli/src/cmd/cloud/queue/destinations.tspackages/cli/src/cmd/cloud/queue/get.tspackages/cli/src/cmd/cloud/queue/ack.tspackages/cli/src/cmd/cloud/queue/publish.ts
packages/cli/src/**/*.ts
📄 CodeRabbit inference engine (packages/cli/AGENTS.md)
Use
Bun.file(f).exists()instead ofexistsSync(f)for file existence checks
Files:
packages/cli/src/cmd/cloud/queue/util.tspackages/cli/src/cmd/cloud/queue/receive.tspackages/cli/src/cmd/cloud/queue/pause.tspackages/cli/src/cmd/cloud/queue/resume.tspackages/cli/src/cmd/cloud/queue/messages.tspackages/cli/src/cmd/cloud/queue/stats.tspackages/cli/src/cmd/cloud/queue/list.tspackages/cli/src/cmd/cloud/queue/nack.tspackages/cli/src/cmd/cloud/queue/dlq.tspackages/cli/src/cmd/cloud/queue/destinations.tspackages/cli/src/cmd/cloud/queue/get.tspackages/cli/src/cmd/cloud/queue/ack.tspackages/cli/src/cmd/cloud/queue/publish.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use Prettier formatter with tabs (width 3), single quotes, and semicolons for TypeScript files
Use TypeScript strict mode with ESNext target and bundler moduleResolution
UseStructuredErrorfrom@agentuity/corefor error handling
Files:
packages/cli/src/cmd/cloud/queue/util.tspackages/cli/src/cmd/cloud/queue/receive.tspackages/cli/src/cmd/cloud/queue/pause.tspackages/cli/src/cmd/cloud/queue/resume.tspackages/cli/src/cmd/cloud/queue/messages.tspackages/cli/src/cmd/cloud/queue/stats.tspackages/cli/src/cmd/cloud/queue/list.tspackages/cli/src/cmd/cloud/queue/nack.tspackages/server/src/api/queue/util.tspackages/cli/src/cmd/cloud/queue/dlq.tspackages/cli/src/cmd/cloud/queue/destinations.tspackages/cli/src/cmd/cloud/queue/get.tspackages/server/src/api/queue/index.tspackages/cli/src/cmd/cloud/queue/ack.tspackages/cli/src/cmd/cloud/queue/publish.tspackages/server/src/api/queue/messages.tspackages/server/src/api/queue/types.ts
packages/server/src/**/*.ts
📄 CodeRabbit inference engine (packages/server/AGENTS.md)
packages/server/src/**/*.ts: Build TypeScript withbun run buildcommand using tsc for compilation
Run TypeScript type checking withbun run typecheck
All code must be runtime-agnostic - no Bun-specific or Node-specific APIs
No browser APIs allowed - server-side only
Prefer interfaces for public APIs in TypeScript
Files:
packages/server/src/api/queue/util.tspackages/server/src/api/queue/index.tspackages/server/src/api/queue/messages.tspackages/server/src/api/queue/types.ts
🧠 Learnings (4)
📚 Learning: 2025-12-21T00:31:41.858Z
Learnt from: jhaynie
Repo: agentuity/sdk PR: 274
File: packages/cli/src/cmd/build/vite/server-bundler.ts:12-41
Timestamp: 2025-12-21T00:31:41.858Z
Learning: In Bun runtime, BuildMessage and ResolveMessage are global types and are not exported from the bun module. Do not import { BuildMessage } from 'bun' or similar; these types are available globally and should be used without import. This applies to all TypeScript files that target the Bun runtime within the repository.
Applied to files:
packages/cli/src/cmd/cloud/queue/util.tspackages/cli/src/cmd/cloud/queue/receive.tspackages/cli/src/cmd/cloud/queue/pause.tspackages/cli/src/cmd/cloud/queue/resume.tspackages/cli/src/cmd/cloud/queue/messages.tspackages/cli/src/cmd/cloud/queue/stats.tspackages/cli/src/cmd/cloud/queue/list.tspackages/cli/src/cmd/cloud/queue/nack.tspackages/server/src/api/queue/util.tspackages/cli/src/cmd/cloud/queue/dlq.tspackages/cli/src/cmd/cloud/queue/destinations.tspackages/cli/src/cmd/cloud/queue/get.tspackages/server/src/api/queue/index.tspackages/cli/src/cmd/cloud/queue/ack.tspackages/cli/src/cmd/cloud/queue/publish.tspackages/server/src/api/queue/messages.tspackages/server/src/api/queue/types.ts
📚 Learning: 2026-01-13T04:32:02.691Z
Learnt from: jhaynie
Repo: agentuity/sdk PR: 565
File: packages/cli/src/cmd/cloud/region-lookup.ts:14-26
Timestamp: 2026-01-13T04:32:02.691Z
Learning: Enforce sandbox identifier prefixes in new code within the CLI cloud region lookup: new sandboxes must use the sbx_ prefix. The snbx_ prefix may appear in legacy code or examples, but do not use snbx_ for new sandboxes. When reviewing changes in packages/cli/src/cmd/cloud/, ensure any created sandbox identifiers use sbx_ and remove or migrate any snbx_ usages in newly added code.
Applied to files:
packages/cli/src/cmd/cloud/queue/util.tspackages/cli/src/cmd/cloud/queue/receive.tspackages/cli/src/cmd/cloud/queue/pause.tspackages/cli/src/cmd/cloud/queue/resume.tspackages/cli/src/cmd/cloud/queue/messages.tspackages/cli/src/cmd/cloud/queue/stats.tspackages/cli/src/cmd/cloud/queue/list.tspackages/cli/src/cmd/cloud/queue/nack.tspackages/cli/src/cmd/cloud/queue/dlq.tspackages/cli/src/cmd/cloud/queue/destinations.tspackages/cli/src/cmd/cloud/queue/get.tspackages/cli/src/cmd/cloud/queue/ack.tspackages/cli/src/cmd/cloud/queue/publish.ts
📚 Learning: 2025-12-19T14:19:33.765Z
Learnt from: jhaynie
Repo: agentuity/sdk PR: 259
File: packages/cli/src/cmd/build/vite/registry-generator.ts:306-312
Timestamp: 2025-12-19T14:19:33.765Z
Learning: Route files under src/api should use the .ts extension only (no .tsx) and regex patterns for such paths should anchor to \.ts$ (e.g., /\/.ts$/). Agent files may support both .ts and .tsx, but route files in the Agentuity SDK codebase are restricted to .ts. This guideline applies to all similar route files under src/api across the repository.
Applied to files:
packages/server/src/api/queue/util.tspackages/server/src/api/queue/index.tspackages/server/src/api/queue/messages.tspackages/server/src/api/queue/types.ts
📚 Learning: 2025-12-30T00:13:37.849Z
Learnt from: jhaynie
Repo: agentuity/sdk PR: 355
File: packages/server/src/api/sandbox/util.ts:2-6
Timestamp: 2025-12-30T00:13:37.849Z
Learning: In the packages/server tree, treat code as runtime-agnostic between Node.js and Bun. Ensure TypeScript files (e.g., util.ts) import and use APIs in a way that works under both runtimes. It is acceptable to rely on Bun’s Node.js compatibility for built-ins accessed via the node: namespace (e.g., node:events, node:stream, node:buffer). During reviews, prefer patterns and imports that remain compatible with Bun's environment, and flag any hard dependencies on runtime-specific globals or non-portable Node APIs.
Applied to files:
packages/server/src/api/queue/util.tspackages/server/src/api/queue/index.tspackages/server/src/api/queue/messages.tspackages/server/src/api/queue/types.ts
🧬 Code graph analysis (11)
packages/cli/src/cmd/cloud/queue/util.ts (4)
packages/cli/src/types.ts (2)
Logger(12-12)AuthData(275-279)packages/server/src/api/api.ts (1)
APIClient(90-662)packages/cli/src/config.ts (1)
getGlobalCatalystAPIClient(799-806)packages/server/src/api/queue/types.ts (1)
QueueApiOptions(410-416)
packages/cli/src/cmd/cloud/queue/receive.ts (4)
packages/server/src/api/queue/types.ts (1)
MessageSchema(188-225)packages/cli/src/types.ts (1)
createCommand(487-532)packages/cli/src/cmd/cloud/queue/util.ts (2)
createQueueAPIClient(23-25)getQueueApiOptions(31-34)packages/server/src/api/queue/messages.ts (1)
receiveMessage(706-743)
packages/cli/src/cmd/cloud/queue/pause.ts (4)
packages/cli/src/types.ts (1)
createCommand(487-532)packages/server/src/api/queue/types.ts (1)
QueueSchema(111-148)packages/cli/src/cmd/cloud/queue/util.ts (2)
createQueueAPIClient(23-25)getQueueApiOptions(31-34)packages/server/src/api/queue/queues.ts (1)
pauseQueue(365-396)
packages/cli/src/cmd/cloud/queue/stats.ts (2)
packages/server/src/api/queue/types.ts (3)
OrgAnalytics(1002-1002)QueueAnalytics(901-901)SSEStatsEvent(1122-1122)packages/server/src/api/queue/analytics.ts (4)
streamQueueAnalytics(240-254)streamOrgAnalytics(209-220)getQueueAnalytics(104-134)getOrgAnalytics(61-81)
packages/cli/src/cmd/cloud/queue/list.ts (4)
packages/cli/src/types.ts (1)
createCommand(487-532)packages/cli/src/cmd/cloud/queue/util.ts (2)
createQueueAPIClient(23-25)getQueueApiOptions(31-34)packages/server/src/api/queue/queues.ts (1)
listQueues(184-221)packages/server/src/api/queue/types.ts (1)
Queue(153-153)
packages/cli/src/cmd/cloud/queue/nack.ts (3)
packages/cli/src/types.ts (1)
createCommand(487-532)packages/cli/src/cmd/cloud/queue/util.ts (2)
createQueueAPIClient(23-25)getQueueApiOptions(31-34)packages/server/src/api/queue/messages.ts (1)
nackMessage(840-875)
packages/cli/src/cmd/cloud/queue/dlq.ts (7)
packages/cli/src/types.ts (2)
createSubcommand(444-485)createCommand(487-532)apps/testing/nextjs-app/agentuity/.agentuity/app.js (1)
client(131324-131324)packages/cli/src/cmd/cloud/queue/util.ts (2)
createQueueAPIClient(23-25)getQueueApiOptions(31-34)packages/server/src/api/queue/dlq.ts (3)
listDeadLetterMessages(54-100)replayDeadLetterMessage(125-167)purgeDeadLetter(190-219)apps/create-agentuity/bin.js (1)
args(4-4)packages/server/src/api/queue/types.ts (2)
DeadLetterMessage(398-398)MessageSchema(188-225)packages/cli/src/index.ts (1)
ErrorCode(12-12)
packages/cli/src/cmd/cloud/queue/get.ts (4)
packages/server/src/api/queue/types.ts (3)
QueueSchema(111-148)MessageSchema(188-225)Message(230-230)packages/cli/src/cmd/cloud/queue/util.ts (2)
createQueueAPIClient(23-25)getQueueApiOptions(31-34)packages/server/src/api/queue/messages.ts (1)
getMessage(219-259)packages/server/src/api/queue/queues.ts (1)
getQueue(134-163)
packages/cli/src/cmd/cloud/queue/ack.ts (3)
packages/cli/src/types.ts (1)
createCommand(487-532)packages/cli/src/cmd/cloud/queue/util.ts (2)
createQueueAPIClient(23-25)getQueueApiOptions(31-34)packages/server/src/api/queue/messages.ts (1)
ackMessage(773-808)
packages/cli/src/cmd/cloud/queue/publish.ts (4)
packages/cli/src/types.ts (1)
createCommand(487-532)packages/server/src/api/queue/types.ts (1)
MessageSchema(188-225)packages/cli/src/cmd/cloud/queue/util.ts (2)
createQueueAPIClient(23-25)getQueueApiOptions(31-34)packages/server/src/api/queue/messages.ts (1)
publishMessage(80-124)
packages/server/src/api/queue/types.ts (3)
packages/server/src/api/queue/index.ts (68)
QueueTypeSchema(52-52)QueueType(73-73)QueueSettingsSchema(53-53)QueueSettings(74-74)QueueStatsSchema(54-54)QueueStats(75-75)QueueSchema(55-55)Queue(76-76)MessageStateSchema(56-56)MessageState(77-77)MessageSchema(57-57)Message(78-78)DestinationTypeSchema(58-58)DestinationType(79-79)HttpDestinationConfigSchema(59-59)HttpDestinationConfig(80-80)DestinationStatsSchema(60-60)DestinationStats(81-81)DestinationSchema(61-61)Destination(82-82)DeadLetterMessageSchema(62-62)DeadLetterMessage(83-83)CreateQueueRequestSchema(63-63)CreateQueueRequest(84-84)UpdateQueueRequestSchema(64-64)UpdateQueueRequest(85-85)ListQueuesRequestSchema(65-65)ListQueuesRequest(86-86)PublishMessageRequestSchema(66-66)PublishMessageRequest(87-87)BatchPublishMessagesRequestSchema(67-67)BatchPublishMessagesRequest(88-88)ListMessagesRequestSchema(68-68)ListMessagesRequest(89-89)ConsumeMessagesRequestSchema(69-69)ConsumeMessagesRequest(90-90)CreateDestinationRequestSchema(70-70)CreateDestinationRequest(91-91)UpdateDestinationRequestSchema(71-71)UpdateDestinationRequest(92-92)ListDlqRequestSchema(72-72)ListDlqRequest(93-93)AnalyticsGranularitySchema(96-96)AnalyticsGranularity(109-109)TimePeriodSchema(97-97)TimePeriod(112-112)LatencyStatsSchema(98-98)LatencyStats(113-113)QueueCurrentStatsSchema(99-99)QueueCurrentStats(114-114)QueuePeriodStatsSchema(100-100)QueuePeriodStats(115-115)DestinationAnalyticsSchema(101-101)DestinationAnalytics(116-116)QueueAnalyticsSchema(102-102)QueueAnalytics(117-117)QueueSummarySchema(103-103)QueueSummary(118-118)OrgAnalyticsSummarySchema(104-104)OrgAnalyticsSummary(119-119)OrgAnalyticsSchema(105-105)OrgAnalytics(120-120)TimeSeriesPointSchema(106-106)TimeSeriesPoint(121-121)TimeSeriesDataSchema(107-107)TimeSeriesData(122-122)SSEStatsEventSchema(108-108)SSEStatsEvent(123-123)packages/server/src/index.ts (1)
z(30-30)packages/schema/src/index.ts (1)
infer(177-177)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: Playwright E2E Smoke Test
- GitHub Check: Pack & Upload
- GitHub Check: SDK Integration Test Suite
- GitHub Check: Framework Integration Tests (TanStack & Next.js)
- GitHub Check: Cloud Deployment Tests
- GitHub Check: Sandbox CLI Tests
- GitHub Check: Build
🔇 Additional comments (36)
packages/cli/src/cmd/cloud/queue/ack.ts (1)
1-50: LGTM!The implementation follows the established pattern for queue CLI subcommands correctly:
- Proper use of
tui.successfor formatted output (per coding guidelines)- Consistent schema definition with Zod
- Handler correctly uses shared utilities (
createQueueAPIClient,getQueueApiOptions)- Error handling is delegated to
ackMessagewhich throws appropriate errorspackages/cli/src/cmd/cloud/queue/util.ts (1)
1-34: LGTM!Well-structured utility module with clear separation of concerns:
QueueContextinterface properly encapsulates all required context for queue operationscreateQueueAPIClientcorrectly delegates to the global Catalyst API clientgetQueueApiOptionsimplements proper precedence fororgIdresolution- Good documentation explaining that queues are global resources
packages/cli/src/cmd/cloud/queue/nack.ts (1)
1-50: LGTM!Implementation correctly mirrors the
acksubcommand pattern:
- Consistent schema definition and handler structure
- Appropriate success message for nack operation ("Returned message to queue")
- Error handling delegated to
nackMessagewhich throwsMessageNotFoundErrororQueueErrorpackages/cli/src/cmd/cloud/queue/resume.ts (1)
1-40: LGTM!Clean implementation that correctly complements the
pausesubcommand:
- Uses
QueueSchemaas response type (matchesresumeQueuereturn type)- Proper success message output via
tui.success- Consistent handler pattern with other queue subcommands
packages/cli/src/cmd/cloud/queue/pause.ts (1)
1-41: Overall implementation looks good.The subcommand correctly mirrors the
resumecounterpart and follows established patterns for queue CLI commands.packages/cli/src/cmd/cloud/queue/receive.ts (1)
1-76: LGTM!The implementation correctly:
- Places
timeoutin theoptionsschema (notargs) and accesses it viaopts.timeout- Uses
tui.*helpers throughout for output (no rawconsole.log)- Handles both JSON and human-readable output modes appropriately
packages/cli/src/cmd/cloud/queue/list.ts (1)
1-81: LGTM!The implementation correctly:
- Places pagination parameters (
limit,offset) in theoptionsschema and accesses them viaopts- Uses
tui.*helpers for all output- Returns a normalized response matching the schema
packages/cli/src/cmd/cloud/queue/get.ts (1)
1-158: LGTM!Well-structured implementation:
- Clean separation with
displayMessagehelper for message rendering- Proper discriminated union response schema for queue vs message results
- Consistent use of
tui.*helpers and vertical table layouts for detailed output- Correctly handles optional
message_idargument to branch logicpackages/cli/src/cmd/cloud/queue/dlq.ts (2)
95-134: LGTM!The
replaysubcommand is well-implemented with proper argument handling and output formatting.
141-181: LGTM!The
purgesubcommand correctly:
- Places the
--yesconfirmation flag inoptionsand accesses it viaopts- Uses
tui.fatalfor the confirmation requirement- Returns a consistent response structure
packages/server/src/api/queue/types.ts (5)
1-19: Well-structured schema foundation with proper Zod 4 usage.The file establishes a comprehensive type system using Zod 4. The
QueueTypeSchemaenum and type inference pattern (z.infer<typeof Schema>) are idiomatic and correct.
21-78: Good separation between base and defaulted settings schemas.The pattern of having
QueueSettingsSchemaBasewithout defaults for PATCH operations andQueueSettingsSchemawith defaults is well-designed. This prevents Zod from overwriting existing values with defaults during partial updates.
155-230: Comprehensive message lifecycle tracking.The
MessageStateSchemacovers all queue message states, andMessageSchemaincludes thorough tracking fields for delivery attempts, timestamps, and optional deduplication/partition keys.
278-287: Consider validation for signing configuration.When
signing.enabledis true,secret_keyshould logically be required. However, this validation may be intentionally deferred to server-side logic for flexibility during destination updates. Verify this is handled appropriately in the server implementation.
649-681: Clean interface design for analytics options.
AnalyticsOptionsproperly extendsQueueApiOptionsand uses interfaces as recommended by coding guidelines for public APIs in the server package.packages/cli/src/cmd/cloud/queue/stats.ts (3)
239-244: SIGINT handler bypasses return value.The
handleInterruptcallback callsprocess.exit(0)directly, which prevents thefinallyblock from completing and the accumulatedeventsfrom being returned. This is acceptable for interactive CLI streaming, but note that--jsonmode won't receive accumulated events if the user interrupts.
16-20: Response schema usesz.unknown()for analytics.The
StatsResponseSchemausesz.unknown()for the analytics payloads. While this loses schema-level validation, the actual return values are properly typed (OrgAnalytics,QueueAnalytics,SSEStatsEvent[]). This is a pragmatic trade-off to avoid duplicating large schemas.
166-179: Good use of stdout.write for real-time streaming display.The
displayStreamEventfunction appropriately usesprocess.stdout.writewith ANSI escape codes (\x1b[2K\r) for in-place updates during live streaming. This is correct for the streaming UX pattern.packages/cli/src/cmd/cloud/queue/messages.ts (2)
26-66: Well-implemented message display helper.The
displayMessagefunction properly usestui.*helpers (tui.table,tui.newline,tui.info,tui.json) for all output, following CLI output conventions.
104-157: Clean dual-mode handler implementation.The handler correctly distinguishes between fetching a single message by ID and listing messages. The response types are properly discriminated with
type: 'message'vstype: 'list'.packages/server/src/api/queue/util.ts (3)
1-19: Proper use ofStructuredErrorfrom@agentuity/core.Error types are correctly defined using
StructuredErroras required by coding guidelines. The generic type parameters provide good context for error handling.
126-151: Secure path construction with proper encoding.The
queueApiPathandqueueApiPathWithQueryfunctions correctly useencodeURIComponentto safely encode path arguments, preventing injection issues with special characters in queue names.
161-166: Clean header builder implementation.The function correctly returns
undefinedwhen noorgIdis provided, avoiding unnecessary empty objects in API calls.packages/server/src/api/queue/index.ts (1)
1-241: Well-structured barrel file with comprehensive documentation.The index file is well-organized with clear section markers, proper type-only exports using the
typekeyword, and helpful JSDoc documentation with usage examples. The re-exports are logically grouped by functionality (types, errors, queue operations, messages, DLQ, destinations, analytics, validation).packages/server/src/api/queue/messages.ts (12)
1-54: Imports and response schemas are well-defined.The response schemas correctly use Zod for type-safe API response parsing. The
ReceiveResponseSchemaappropriately usesnullable()for cases where no message is available. All imports are runtime-agnostic as required.
80-124: Clean publish implementation with proper validation.The validation flow correctly handles optional fields conditionally and the error mapping is consistent with the rest of the module. The string-based error detection (checking for "not found") is a pragmatic approach given the API response format.
150-197: Batch publish correctly validates each message individually.The function properly validates the batch size constraint and iterates through all messages to validate individual fields before making the API call. This fail-fast approach prevents partial failures at the API level.
219-259: Good error differentiation between message and queue not found.The function correctly distinguishes between
MessageNotFoundErrorandQueueNotFoundErrorby checking for both keywords in the error message. This provides more specific error information to callers.
281-314: LGTM.The function correctly validates inputs and provides useful error context by including the offset in the
messageIdfield of the error.
343-392: Clean query parameter handling with URLSearchParams.The function correctly uses
URLSearchParams(runtime-agnostic) for building query strings and properly validates optional parameters only when provided.
413-446: Consistent delete implementation.The function follows the established patterns for validation and error handling.
469-504: LGTM.The replay function correctly posts to the replay endpoint and returns the updated message state.
536-577: Correct consume implementation for log-style consumption.The function appropriately requires an offset parameter since this is a log-style consumption pattern where the caller tracks their position. The optional limit parameter provides flexibility for batch sizes.
598-627: LGTM.Simple and correct implementation for retrieving the queue head offset.
648-677: LGTM.Consistent implementation with
getQueueHead, returning the tail offset.
773-875: Clean ack/nack implementations with consistent error handling.Both functions follow the established patterns, properly validate inputs, and correctly differentiate between message not found and general queue errors. The symmetry between ack and nack operations makes the API intuitive to use.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@packages/cli/src/cmd/cloud/queue/sinks.ts`:
- Around line 249-250: The flags handling for --enabled/--disabled silently lets
--disabled win because both are applied; in the code that sets
updateParams.enabled (the block referencing opts.enabled and opts.disabled) add
a validation that detects if both opts.enabled and opts.disabled are true and
aborts with a clear error (e.g., call ctx.logger.fatal or throw) rejecting the
conflicting flags; alternatively consolidate to a single flag interface, but at
minimum prevent applying both by checking opts.enabled && opts.disabled before
assigning updateParams.enabled.
- Around line 138-148: The catch block handling SinkAlreadyExistsError should
use ctx.logger.fatal() to log and exit instead of calling process.exit(1);
update the code in the catch for SinkAlreadyExistsError (where
tui.error/tui.info are used with opts.name and args.queue_name) to call
ctx.logger.fatal() with a clear message that includes the sink name and queue
name (and any hint text if desired), which will both log and terminate with exit
code 1.
🧹 Nitpick comments (8)
packages/server/src/api/queue/validation.ts (3)
387-425: Minor inconsistency in minimum value checks.
validateLimitcheckslimit < 1(line 388) whilevalidateBatchSizecheckssize <= 0(line 411). Both effectively require a minimum of 1, but the inconsistent style may confuse maintainers. Consider aligning:export function validateBatchSize(size: number): void { - if (size <= 0) { + if (size < 1) { throw new QueueValidationError({ - message: 'Batch size must be greater than 0', + message: 'Batch size must be at least 1',
435-449: Consider more robust URL validation.The current check only verifies the URL starts with
http://orhttps://. Malformed URLs likehttp://(no host) or URLs with invalid characters would pass. Consider usingURLconstructor for stricter validation:try { const parsed = new URL(url); if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') { throw new QueueValidationError({ ... }); } } catch { throw new QueueValidationError({ message: 'Webhook URL must be a valid HTTP or HTTPS URL', field: 'url', value: url, }); }This ensures the URL is syntactically valid and prevents edge cases.
83-86: Consider importingQueueValidationErrorfrom@agentuity/coreinstead of defining it locally.This file duplicates the
QueueValidationErroralready exported from@agentuity/core. Having two separate definitions can causeinstanceofchecks to fail when errors cross package boundaries.Suggested change
-import { StructuredError } from '@agentuity/core'; +import { StructuredError, QueueValidationError } from '@agentuity/core';Remove the local definition at lines 83-86.
packages/cli/src/cmd/cloud/queue/sinks.ts (3)
131-135: Usetui.*helpers instead of rawconsole.log.Per coding guidelines, CLI commands should use
tui.*helpers for formatted output instead of raw console logs.🔧 Suggested fix
if (!options.json) { tui.success(`Created sink: ${sink.id}`); - console.log(` Name: ${sink.name}`); - console.log(` URL: ${sink.url}`); + tui.info(` Name: ${sink.name}`); + tui.info(` URL: ${sink.url}`); }As per coding guidelines: "Use
tui.*helpers for formatted output instead of raw console logs".
176-197: Replaceconsole.logwithtui.*helpers.This handler uses raw
console.logcalls extensively. Per coding guidelines, usetui.*helpers for formatted output.
260-265: Replaceconsole.logwithtui.*helpers.Same issue as the other handlers - use
tui.info()or similar instead of rawconsole.log.packages/server/src/api/queue/types.ts (2)
256-287: Require a signing secret when signing is enabled.Right now
signing.enabledcan betruewithout asecret_key(Lines 279-285). That allows invalid configs to pass schema validation and fails later at runtime.♻️ Proposed schema refinement
-export const HttpDestinationConfigSchema = z.object({ +export const HttpDestinationConfigSchema = z + .object({ /** The URL to send messages to. */ url: z.string(), /** Optional custom headers to include in requests. */ headers: z.record(z.string(), z.string()).optional(), @@ signing: z .object({ /** Whether signing is enabled (default: false). */ enabled: z.boolean().default(false), /** Secret key for HMAC signing. */ secret_key: z.string().optional(), }) .optional(), -}); + }) + .refine( + (config) => !config.signing?.enabled || !!config.signing.secret_key, + { + message: 'secret_key is required when signing.enabled is true', + path: ['signing', 'secret_key'], + }, + );
1204-1213: Validate auth_value when auth_type requires it.
CreateSinkRequestSchemaallowsauth_typelikebasic/digestwithoutauth_value(Line 1209-1212), which can produce invalid configurations.♻️ Proposed schema refinement
-export const CreateSinkRequestSchema = z.object({ +export const CreateSinkRequestSchema = z + .object({ /** Human-readable name for the sink. */ name: z.string().min(1).max(256), /** Optional description. */ description: z.string().max(1024).optional(), /** Authentication type (default: none). */ auth_type: SinkAuthTypeSchema.optional().default('none'), /** Authentication value (format depends on auth_type). */ auth_value: z.string().optional(), -}); + }) + .refine( + (req) => req.auth_type === 'none' || !!req.auth_value, + { + message: 'auth_value is required when auth_type is not "none"', + path: ['auth_value'], + }, + );
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
packages/cli/src/cmd/cloud/queue/index.tspackages/cli/src/cmd/cloud/queue/sinks.tspackages/core/src/services/sandbox.tspackages/server/src/api/queue/index.tspackages/server/src/api/queue/sinks.tspackages/server/src/api/queue/types.tspackages/server/src/api/queue/util.tspackages/server/src/api/queue/validation.tspackages/vscode/src/core/cliClient.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use Prettier formatter with tabs (width 3), single quotes, and semicolons for TypeScript files
Use TypeScript strict mode with ESNext target and bundler moduleResolution
UseStructuredErrorfrom@agentuity/corefor error handling
Files:
packages/core/src/services/sandbox.tspackages/vscode/src/core/cliClient.tspackages/cli/src/cmd/cloud/queue/sinks.tspackages/server/src/api/queue/sinks.tspackages/server/src/api/queue/util.tspackages/cli/src/cmd/cloud/queue/index.tspackages/server/src/api/queue/index.tspackages/server/src/api/queue/validation.tspackages/server/src/api/queue/types.ts
packages/core/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (packages/core/AGENTS.md)
packages/core/src/**/*.{ts,tsx}: Code must be compatible with both Browser and Node/Bun runtime environments - no runtime-specific code allowed
Preferinterfacefor public APIs instead oftypealiases
Use generic types for reusable type utilities
Ensure no code has side effects or global mutations - all exports must be pure
Avoid side effects in module-level code - all exports must be pure and have no global mutations
Files:
packages/core/src/services/sandbox.ts
packages/cli/src/cmd/**/*.ts
📄 CodeRabbit inference engine (packages/cli/AGENTS.md)
packages/cli/src/cmd/**/*.ts: Usetui.*helpers for formatted output instead of raw console logs
Usectx.loggerfor logging; calllogger.fatal()to log and exit with code 1
Files:
packages/cli/src/cmd/cloud/queue/sinks.tspackages/cli/src/cmd/cloud/queue/index.ts
packages/cli/src/**/*.ts
📄 CodeRabbit inference engine (packages/cli/AGENTS.md)
Use
Bun.file(f).exists()instead ofexistsSync(f)for file existence checks
Files:
packages/cli/src/cmd/cloud/queue/sinks.tspackages/cli/src/cmd/cloud/queue/index.ts
packages/server/src/**/*.ts
📄 CodeRabbit inference engine (packages/server/AGENTS.md)
packages/server/src/**/*.ts: Build TypeScript withbun run buildcommand using tsc for compilation
Run TypeScript type checking withbun run typecheck
All code must be runtime-agnostic - no Bun-specific or Node-specific APIs
No browser APIs allowed - server-side only
Prefer interfaces for public APIs in TypeScript
Files:
packages/server/src/api/queue/sinks.tspackages/server/src/api/queue/util.tspackages/server/src/api/queue/index.tspackages/server/src/api/queue/validation.tspackages/server/src/api/queue/types.ts
🧠 Learnings (4)
📚 Learning: 2025-12-21T00:31:41.858Z
Learnt from: jhaynie
Repo: agentuity/sdk PR: 274
File: packages/cli/src/cmd/build/vite/server-bundler.ts:12-41
Timestamp: 2025-12-21T00:31:41.858Z
Learning: In Bun runtime, BuildMessage and ResolveMessage are global types and are not exported from the bun module. Do not import { BuildMessage } from 'bun' or similar; these types are available globally and should be used without import. This applies to all TypeScript files that target the Bun runtime within the repository.
Applied to files:
packages/core/src/services/sandbox.tspackages/vscode/src/core/cliClient.tspackages/cli/src/cmd/cloud/queue/sinks.tspackages/server/src/api/queue/sinks.tspackages/server/src/api/queue/util.tspackages/cli/src/cmd/cloud/queue/index.tspackages/server/src/api/queue/index.tspackages/server/src/api/queue/validation.tspackages/server/src/api/queue/types.ts
📚 Learning: 2026-01-13T04:32:02.691Z
Learnt from: jhaynie
Repo: agentuity/sdk PR: 565
File: packages/cli/src/cmd/cloud/region-lookup.ts:14-26
Timestamp: 2026-01-13T04:32:02.691Z
Learning: Enforce sandbox identifier prefixes in new code within the CLI cloud region lookup: new sandboxes must use the sbx_ prefix. The snbx_ prefix may appear in legacy code or examples, but do not use snbx_ for new sandboxes. When reviewing changes in packages/cli/src/cmd/cloud/, ensure any created sandbox identifiers use sbx_ and remove or migrate any snbx_ usages in newly added code.
Applied to files:
packages/cli/src/cmd/cloud/queue/sinks.tspackages/cli/src/cmd/cloud/queue/index.ts
📚 Learning: 2025-12-19T14:19:33.765Z
Learnt from: jhaynie
Repo: agentuity/sdk PR: 259
File: packages/cli/src/cmd/build/vite/registry-generator.ts:306-312
Timestamp: 2025-12-19T14:19:33.765Z
Learning: Route files under src/api should use the .ts extension only (no .tsx) and regex patterns for such paths should anchor to \.ts$ (e.g., /\/.ts$/). Agent files may support both .ts and .tsx, but route files in the Agentuity SDK codebase are restricted to .ts. This guideline applies to all similar route files under src/api across the repository.
Applied to files:
packages/server/src/api/queue/sinks.tspackages/server/src/api/queue/util.tspackages/server/src/api/queue/index.tspackages/server/src/api/queue/validation.tspackages/server/src/api/queue/types.ts
📚 Learning: 2025-12-30T00:13:37.849Z
Learnt from: jhaynie
Repo: agentuity/sdk PR: 355
File: packages/server/src/api/sandbox/util.ts:2-6
Timestamp: 2025-12-30T00:13:37.849Z
Learning: In the packages/server tree, treat code as runtime-agnostic between Node.js and Bun. Ensure TypeScript files (e.g., util.ts) import and use APIs in a way that works under both runtimes. It is acceptable to rely on Bun’s Node.js compatibility for built-ins accessed via the node: namespace (e.g., node:events, node:stream, node:buffer). During reviews, prefer patterns and imports that remain compatible with Bun's environment, and flag any hard dependencies on runtime-specific globals or non-portable Node APIs.
Applied to files:
packages/server/src/api/queue/sinks.tspackages/server/src/api/queue/util.tspackages/server/src/api/queue/index.tspackages/server/src/api/queue/validation.tspackages/server/src/api/queue/types.ts
🧬 Code graph analysis (4)
packages/cli/src/cmd/cloud/queue/sinks.ts (4)
packages/cli/src/cmd/cloud/queue/util.ts (2)
createQueueAPIClient(23-25)getQueueApiOptions(31-34)packages/server/src/api/queue/sinks.ts (5)
listSinks(149-178)createSink(57-124)getSink(203-243)updateSink(275-321)deleteSink(344-384)packages/server/src/api/queue/types.ts (2)
Sink(1189-1189)SinkSchema(1151-1184)packages/server/src/api/queue/util.ts (1)
SinkAlreadyExistsError(146-149)
packages/cli/src/cmd/cloud/queue/index.ts (17)
packages/cli/src/cmd/cloud/index.ts (1)
command(22-51)packages/cli/src/types.ts (2)
createCommand(487-532)createSubcommand(444-485)packages/cli/src/cmd/cloud/queue/list.ts (1)
listSubcommand(21-79)packages/cli/src/cmd/cloud/queue/create.ts (1)
createSubcommand(15-89)packages/cli/src/cmd/cloud/queue/get.ts (1)
getSubcommand(55-156)packages/cli/src/cmd/cloud/queue/delete.ts (1)
deleteSubcommand(14-55)packages/cli/src/cmd/cloud/queue/publish.ts (1)
publishSubcommand(9-86)packages/cli/src/cmd/cloud/queue/messages.ts (1)
messagesSubcommand(68-158)packages/cli/src/cmd/cloud/queue/receive.ts (1)
receiveSubcommand(12-74)packages/cli/src/cmd/cloud/queue/ack.ts (1)
ackSubcommand(14-48)packages/cli/src/cmd/cloud/queue/nack.ts (1)
nackSubcommand(14-48)packages/cli/src/cmd/cloud/queue/dlq.ts (1)
dlqSubcommand(183-199)packages/cli/src/cmd/cloud/queue/destinations.ts (1)
destinationsSubcommand(192-215)packages/cli/src/cmd/cloud/queue/sinks.ts (1)
sinksSubcommand(314-339)packages/cli/src/cmd/cloud/queue/pause.ts (1)
pauseSubcommand(8-39)packages/cli/src/cmd/cloud/queue/resume.ts (1)
resumeSubcommand(8-38)packages/cli/src/cmd/cloud/queue/stats.ts (1)
statsSubcommand(181-295)
packages/server/src/api/queue/validation.ts (2)
packages/core/src/index.ts (2)
QueueValidationError(74-74)StructuredError(2-2)packages/core/src/services/queue.ts (1)
QueueValidationError(198-203)
packages/server/src/api/queue/types.ts (2)
packages/server/src/api/queue/index.ts (17)
QueueTypeSchema(52-52)QueueType(73-73)QueueSettingsSchema(53-53)QueueSettings(74-74)QueueSchema(55-55)Queue(76-76)MessageStateSchema(56-56)MessageState(77-77)MessageSchema(57-57)Message(78-78)DestinationTypeSchema(58-58)DestinationType(79-79)HttpDestinationConfigSchema(59-59)HttpDestinationConfig(80-80)Destination(82-82)DeadLetterMessageSchema(62-62)DeadLetterMessage(83-83)packages/schema/src/index.ts (1)
infer(177-177)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: Framework Integration Tests (TanStack & Next.js)
- GitHub Check: Playwright E2E Smoke Test
- GitHub Check: Package Installation & Usage Test
- GitHub Check: Sandbox CLI Tests
- GitHub Check: Cloud Deployment Tests
- GitHub Check: SDK Integration Test Suite
- GitHub Check: Template Integration Tests
- GitHub Check: Pack & Upload
- GitHub Check: Build
🔇 Additional comments (23)
packages/core/src/services/sandbox.ts (1)
473-476: LGTM — additive runtime branding metadata.Optional field with clear intent; no compatibility concerns.
packages/vscode/src/core/cliClient.ts (1)
1223-1224: LGTM — CLI sandbox metadata now includes runtime branding.Adds optional fields only; no behavior change.
packages/server/src/api/queue/validation.ts (3)
1-62: LGTM! Well-structured validation constants.The constants are clearly documented and match the PR description (1MB payload, etc.). The regex patterns are appropriately restrictive for identifiers.
110-133: LGTM!The validation logic is correct with proper boundary checks and clear error messages.
159-175: LGTM! Payload validation correctly uses byte count.The fix from the previous review has been properly applied -
TextEncoder().encode(serialized).lengthcorrectly measures UTF-8 byte length rather than character count.packages/cli/src/cmd/cloud/queue/index.ts (1)
1-66: LGTM! Clean command aggregation.The queue command properly aggregates all subcommands with clear examples and appropriate authentication requirements. The structure follows the established CLI patterns.
packages/server/src/api/queue/util.ts (3)
1-150: LGTM! Well-structured error types.The error hierarchy is comprehensive, covering all queue-related failure scenarios. Each error type includes relevant context (queueName, messageId, sinkId, etc.) for debugging.
164-170: LGTM! Path construction is correct.The
queueApiPathfunction properly URL-encodes path arguments to prevent injection and handles the optional args case correctly.
199-204: LGTM!The header builder correctly returns
undefinedwhen no orgId is provided, allowing callers to skip headers entirely.packages/server/src/api/queue/sinks.ts (6)
22-28: LGTM! Response schemas are well-defined.The schemas properly wrap the Sink type and handle list/delete responses appropriately.
57-124: LGTM! Comprehensive error handling increateSink.The function properly handles both successful responses with
resp.successand API exceptions via the try/catch block. The error mapping for "already exists" and "not found" cases is thorough.
203-243: LGTM!The
getSinkfunction has proper validation and error mapping for both sink and queue not-found scenarios.
275-321: LGTM!The
updateSinkfunction correctly validates the optional name parameter and follows the established error handling pattern.
344-384: LGTM!The
deleteSinkfunction properly returns void on success and maps errors appropriately.
149-178: The review comment is based on an incorrect assumption about error handling consistency. Examining the codebase reveals that the standard pattern across API operations is to not use try/catch blocks—this applies uniformly to POST operations (e.g.,apikeyCreate), GET operations (e.g.,apikeyList,apikeyGet), and DELETE operations (e.g.,apikeyDelete). All these functions follow the same pattern: checkresp.successand throw custom errors without catchingAPIError.
createSinkis the anomaly with its try/catch block, notlistSinksand other GET/PATCH/DELETE operations. The current implementation oflistSinks,getSink,updateSink, anddeleteSinkis consistent with the established codebase pattern.Likely an incorrect or invalid review comment.
packages/server/src/api/queue/index.ts (4)
1-45: Clear, helpful module docs and examples.The module-level docs and examples read well and make the entry point easy to discover and use.
51-133: Type and schema re-exports look well organized.This block provides a clean public surface for consumers without forcing deep imports.
139-222: Operational exports are neatly grouped.Errors and operation helpers are centralized cleanly for queues/messages/DLQ/destinations/sinks/analytics.
228-261: Validation utilities are exported consistently.Nice to see validation helpers and constants exposed alongside the API surface.
packages/server/src/api/queue/types.ts (4)
1-153: Queue schemas and settings are well documented and structured.The separation between base settings and defaults is clear and matches the intended PATCH behavior.
155-231: Message state and message schemas look solid.The lifecycle enum and message fields are comprehensive and consistent with the docs.
355-605: DLQ and request schemas are clean and consistent.The request schemas and DLQ models are easy to follow and well documented.
610-1122: Analytics type modeling is comprehensive.The analytics schema coverage (time periods, latency, summaries, SSE) is thorough and consistent.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| } catch (error) { | ||
| if (error instanceof SinkAlreadyExistsError) { | ||
| tui.error( | ||
| `A sink with name "${opts.name}" already exists for queue "${args.queue_name}"` | ||
| ); | ||
| tui.info('Hint: Use a different name or delete the existing sink first'); | ||
| process.exit(1); | ||
| } | ||
| throw error; | ||
| } | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use ctx.logger.fatal() instead of process.exit(1).
Per coding guidelines for CLI commands, use ctx.logger.fatal() to log and exit with code 1 instead of calling process.exit(1) directly. This ensures consistent error handling and logging.
🔧 Suggested fix
} catch (error) {
if (error instanceof SinkAlreadyExistsError) {
- tui.error(
- `A sink with name "${opts.name}" already exists for queue "${args.queue_name}"`
- );
- tui.info('Hint: Use a different name or delete the existing sink first');
- process.exit(1);
+ ctx.logger.fatal(
+ `A sink with name "${opts.name}" already exists for queue "${args.queue_name}". Hint: Use a different name or delete the existing sink first`
+ );
}
throw error;
}As per coding guidelines: "Use ctx.logger for logging; call logger.fatal() to log and exit with code 1".
🤖 Prompt for AI Agents
In `@packages/cli/src/cmd/cloud/queue/sinks.ts` around lines 138 - 148, The catch
block handling SinkAlreadyExistsError should use ctx.logger.fatal() to log and
exit instead of calling process.exit(1); update the code in the catch for
SinkAlreadyExistsError (where tui.error/tui.info are used with opts.name and
args.queue_name) to call ctx.logger.fatal() with a clear message that includes
the sink name and queue name (and any hint text if desired), which will both log
and terminate with exit code 1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
🤖 Fix all issues with AI agents
In `@packages/cli/src/cmd/cloud/queue/destinations.ts`:
- Around line 132-139: The catch block handling DestinationAlreadyExistsError
should not call process.exit(1); instead call ctx.logger.fatal(...) to perform a
consistent logged fatal exit. Locate the catch handling
DestinationAlreadyExistsError in the function that references opts.url and
args.queue_name (the block that currently calls tui.error and tui.info) and
replace the process.exit(1) call with ctx.logger.fatal using an appropriate
fatal message (include the same context about the destination URL and queue
name) so the code uses ctx.logger for fatal exits per guidelines.
In `@packages/server/src/api/queue/sources.ts`:
- Around line 282-286: Replace the truthy check on the optional name so empty
strings are validated: in the block where validateQueueName(queueName),
validateSourceId(sourceId) are called, change the condition that currently reads
if (params.name) to an explicit undefined check (params.name !== undefined) so
validateSourceName(params.name) runs for all explicitly provided values
including empty string; keep calling validateSourceName with params.name when
not undefined.
In `@packages/server/src/api/queue/types.ts`:
- Around line 256-287: The schemas lack cross-field validation: update
HttpDestinationConfigSchema, CreateSourceRequestSchema, and
UpdateSourceRequestSchema to add Zod refinements that enforce dependent
fields—use .superRefine or .refine on each schema so HttpDestinationConfigSchema
checks if obj.signing?.enabled === true then obj.signing?.secret_key is present
and non-empty; make CreateSourceRequestSchema require auth_value when auth_type
!== 'none'; and make UpdateSourceRequestSchema require auth_value when auth_type
is provided and auth_type !== 'none' (treat absent auth_type as ok). Reference
the schema symbols HttpDestinationConfigSchema, CreateSourceRequestSchema, and
UpdateSourceRequestSchema when locating and adding these refinements.
In `@packages/server/src/api/queue/validation.ts`:
- Around line 159-175: The validatePayload function must reject
non-JSON-serializable values: wrap JSON.stringify(payload) in a try/catch and on
error throw a QueueValidationError (use QueueValidationError) with a clear
message and field:'payload'; additionally, if JSON.stringify returns undefined
(e.g., functions/symbols), treat that as invalid and throw QueueValidationError
before computing payloadBytes; keep existing MAX_PAYLOAD_SIZE check and error
shape but ensure you reference validatePayload, MAX_PAYLOAD_SIZE, and
QueueValidationError when making these changes.
In `@scripts/test-queue.sh`:
- Around line 333-335: The curl payload uses an unquoted command substitution
for the timestamp which can cause word-splitting; update the INGEST_OUTPUT
assignment (the curl invocation that posts to "$SOURCE_URL") to quote the
timestamp substitution or construct the JSON with printf so the timestamp is a
single JSON string (i.e., ensure the '"timestamp":"$(date ...)"' portion is
quoted or replaced by a printf-generated JSON string) while preserving the
existing -d and 2>&1 behavior.
♻️ Duplicate comments (6)
packages/cli/src/cmd/cloud/queue/destinations.ts (1)
125-129: Usetui.*helpers instead ofconsole.logfor destination output.Switch to
tui.info/tui.tableto keep CLI output consistent. As per coding guidelines, usetui.*helpers for formatted output.♻️ Example change
if (!options.json) { tui.success(`Created destination: ${destination.id}`); - console.log(` URL: ${destination.config.url}`); - console.log(` Method: ${destination.config.method}`); + tui.table( + [{ URL: destination.config.url, Method: destination.config.method }], + undefined, + { layout: 'vertical' } + ); }Also applies to: 195-199
scripts/test-queue.sh (2)
205-212: Capture CLI exit status beforegrepchecks.
|| truediscards the CLI exit code, so$?reflectsgrep, not the CLI. This makes successful calls without matching keywords fail spuriously. Save the CLI status and use it in the conditional for ack/nack/destination delete/source delete/DLQ purge/queue delete.🛠️ Example fix (apply same pattern to other blocks)
-ACK_OUTPUT=$($CLI cloud queue ack "$QUEUE_NAME" "$RECEIVED_ID" 2>&1) || true -if echo "$ACK_OUTPUT" | grep -qi "acknowledged\|success" || [ $? -eq 0 ]; then +if ACK_OUTPUT=$($CLI cloud queue ack "$QUEUE_NAME" "$RECEIVED_ID" 2>&1); then + ACK_STATUS=0 +else + ACK_STATUS=$? +fi +if echo "$ACK_OUTPUT" | grep -qi "acknowledged\|success" || [ $ACK_STATUS -eq 0 ]; then pass "queue ack succeeds" else fail "queue ack failed" "$ACK_OUTPUT" fiAlso applies to: 230-238, 277-284, 370-377, 418-425, 431-439
431-450: Verify deletion using the actual queue name.The post-delete check uses a newly generated queue name, so it can pass even if the real queue still exists. Capture the deleted name before clearing
QUEUE_NAMEand use it for the check.🛠️ Suggested fix
-DELETE_OUTPUT=$($CLI cloud queue delete "$QUEUE_NAME" --confirm 2>&1) || true +DELETED_QUEUE_NAME="$QUEUE_NAME" +DELETE_OUTPUT=$($CLI cloud queue delete "$QUEUE_NAME" --confirm 2>&1) || true if echo "$DELETE_OUTPUT" | grep -qi "deleted\|success" || [ $? -eq 0 ]; then pass "queue delete succeeds" QUEUE_NAME="" else fail "queue delete failed" "$DELETE_OUTPUT" fi # Verify queue no longer accessible if [ -z "$QUEUE_NAME" ]; then info "Test: deleted queue not accessible" - GONE_OUTPUT=$($CLI cloud queue get "test_queue_$(date +%s)" 2>&1) || true + GONE_OUTPUT=$($CLI cloud queue get "$DELETED_QUEUE_NAME" 2>&1) || true if echo "$GONE_OUTPUT" | grep -qi "not found\|404\|error"; then pass "deleted queue returns not found" else - pass "queue get for non-existent queue handled" + fail "deleted queue still accessible" "$GONE_OUTPUT" fi fipackages/cli/src/cmd/cloud/queue/dlq.ts (1)
40-58: Move pagination params to options flags.
limit/offsetshould be CLI flags, not positional args, to match other list commands.🛠️ Suggested fix
schema: { args: z.object({ queue_name: z.string().min(1).describe('Queue name'), - limit: z.coerce.number().optional().describe('Maximum number of messages to return'), - offset: z.coerce.number().optional().describe('Offset for pagination'), }), + options: z.object({ + limit: z.coerce.number().optional().describe('Maximum number of messages to return'), + offset: z.coerce.number().optional().describe('Offset for pagination'), + }), response: DlqListResponseSchema, }, async handler(ctx) { - const { args, options } = ctx; + const { args, opts, options } = ctx; const client = await createQueueAPIClient(ctx); const result = await listDeadLetterMessages( client, args.queue_name, { - limit: args.limit, - offset: args.offset, + limit: opts.limit, + offset: opts.offset, }, getQueueApiOptions(ctx) );packages/cli/src/cmd/cloud/queue/sources.ts (2)
138-145: Preferctx.logger.fatal()overprocess.exit(1).Use the CLI logger to emit the error and exit consistently. As per coding guidelines,
ctx.logger.fatal()should handle the exit.🛠️ Suggested fix
if (error instanceof SourceAlreadyExistsError) { - tui.error( - `A source with name "${opts.name}" already exists for queue "${args.queue_name}"` - ); - tui.info('Hint: Use a different name or delete the existing source first'); - process.exit(1); + ctx.logger.fatal( + `A source with name "${opts.name}" already exists for queue "${args.queue_name}". ` + + 'Hint: Use a different name or delete the existing source first' + ); }
245-250: Reject conflicting--enabledand--disabledflags.If both are passed, the last assignment silently wins. Explicitly reject the conflict.
🛠️ Suggested fix
if (opts['auth-type']) updateParams.auth_type = opts['auth-type']; if (opts['auth-value']) updateParams.auth_value = opts['auth-value']; +if (opts.enabled && opts.disabled) { + ctx.logger.fatal('Cannot specify both --enabled and --disabled'); +} if (opts.enabled) updateParams.enabled = true; if (opts.disabled) updateParams.enabled = false;
🧹 Nitpick comments (1)
packages/cli/src/cmd/cloud/queue/sources.ts (1)
131-135: Usetui.*helpers for human output instead ofconsole.log.This file uses
console.logfor user-facing output in multiple subcommands. Please switch totui.info/tui.table(or similar) for consistent formatting. As per coding guidelines, usetui.*helpers for CLI output.♻️ Example change (apply similarly elsewhere)
if (!options.json) { tui.success(`Created source: ${source.id}`); - console.log(` Name: ${source.name}`); - console.log(` URL: ${source.url}`); + tui.table( + [{ Name: source.name, URL: source.url }], + undefined, + { layout: 'vertical' } + ); }Also applies to: 176-197, 260-265
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
packages/cli/src/cmd/cloud/queue/destinations.tspackages/cli/src/cmd/cloud/queue/dlq.tspackages/cli/src/cmd/cloud/queue/index.tspackages/cli/src/cmd/cloud/queue/sources.tspackages/server/src/api/queue/index.tspackages/server/src/api/queue/sources.tspackages/server/src/api/queue/types.tspackages/server/src/api/queue/util.tspackages/server/src/api/queue/validation.tsscripts/test-queue.sh
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/server/src/api/queue/index.ts
🧰 Additional context used
📓 Path-based instructions (4)
packages/cli/src/cmd/**/*.ts
📄 CodeRabbit inference engine (packages/cli/AGENTS.md)
packages/cli/src/cmd/**/*.ts: Usetui.*helpers for formatted output instead of raw console logs
Usectx.loggerfor logging; calllogger.fatal()to log and exit with code 1
Files:
packages/cli/src/cmd/cloud/queue/sources.tspackages/cli/src/cmd/cloud/queue/index.tspackages/cli/src/cmd/cloud/queue/dlq.tspackages/cli/src/cmd/cloud/queue/destinations.ts
packages/cli/src/**/*.ts
📄 CodeRabbit inference engine (packages/cli/AGENTS.md)
Use
Bun.file(f).exists()instead ofexistsSync(f)for file existence checks
Files:
packages/cli/src/cmd/cloud/queue/sources.tspackages/cli/src/cmd/cloud/queue/index.tspackages/cli/src/cmd/cloud/queue/dlq.tspackages/cli/src/cmd/cloud/queue/destinations.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use Prettier formatter with tabs (width 3), single quotes, and semicolons for TypeScript files
Use TypeScript strict mode with ESNext target and bundler moduleResolution
UseStructuredErrorfrom@agentuity/corefor error handling
Files:
packages/cli/src/cmd/cloud/queue/sources.tspackages/server/src/api/queue/util.tspackages/cli/src/cmd/cloud/queue/index.tspackages/cli/src/cmd/cloud/queue/dlq.tspackages/cli/src/cmd/cloud/queue/destinations.tspackages/server/src/api/queue/sources.tspackages/server/src/api/queue/types.tspackages/server/src/api/queue/validation.ts
packages/server/src/**/*.ts
📄 CodeRabbit inference engine (packages/server/AGENTS.md)
packages/server/src/**/*.ts: Build TypeScript withbun run buildcommand using tsc for compilation
Run TypeScript type checking withbun run typecheck
All code must be runtime-agnostic - no Bun-specific or Node-specific APIs
No browser APIs allowed - server-side only
Prefer interfaces for public APIs in TypeScript
Files:
packages/server/src/api/queue/util.tspackages/server/src/api/queue/sources.tspackages/server/src/api/queue/types.tspackages/server/src/api/queue/validation.ts
🧠 Learnings (4)
📚 Learning: 2025-12-21T00:31:41.858Z
Learnt from: jhaynie
Repo: agentuity/sdk PR: 274
File: packages/cli/src/cmd/build/vite/server-bundler.ts:12-41
Timestamp: 2025-12-21T00:31:41.858Z
Learning: In Bun runtime, BuildMessage and ResolveMessage are global types and are not exported from the bun module. Do not import { BuildMessage } from 'bun' or similar; these types are available globally and should be used without import. This applies to all TypeScript files that target the Bun runtime within the repository.
Applied to files:
packages/cli/src/cmd/cloud/queue/sources.tspackages/server/src/api/queue/util.tspackages/cli/src/cmd/cloud/queue/index.tspackages/cli/src/cmd/cloud/queue/dlq.tspackages/cli/src/cmd/cloud/queue/destinations.tspackages/server/src/api/queue/sources.tspackages/server/src/api/queue/types.tspackages/server/src/api/queue/validation.ts
📚 Learning: 2026-01-13T04:32:02.691Z
Learnt from: jhaynie
Repo: agentuity/sdk PR: 565
File: packages/cli/src/cmd/cloud/region-lookup.ts:14-26
Timestamp: 2026-01-13T04:32:02.691Z
Learning: Enforce sandbox identifier prefixes in new code within the CLI cloud region lookup: new sandboxes must use the sbx_ prefix. The snbx_ prefix may appear in legacy code or examples, but do not use snbx_ for new sandboxes. When reviewing changes in packages/cli/src/cmd/cloud/, ensure any created sandbox identifiers use sbx_ and remove or migrate any snbx_ usages in newly added code.
Applied to files:
packages/cli/src/cmd/cloud/queue/sources.tspackages/cli/src/cmd/cloud/queue/index.tspackages/cli/src/cmd/cloud/queue/dlq.tspackages/cli/src/cmd/cloud/queue/destinations.ts
📚 Learning: 2025-12-19T14:19:33.765Z
Learnt from: jhaynie
Repo: agentuity/sdk PR: 259
File: packages/cli/src/cmd/build/vite/registry-generator.ts:306-312
Timestamp: 2025-12-19T14:19:33.765Z
Learning: Route files under src/api should use the .ts extension only (no .tsx) and regex patterns for such paths should anchor to \.ts$ (e.g., /\/.ts$/). Agent files may support both .ts and .tsx, but route files in the Agentuity SDK codebase are restricted to .ts. This guideline applies to all similar route files under src/api across the repository.
Applied to files:
packages/server/src/api/queue/util.tspackages/server/src/api/queue/sources.tspackages/server/src/api/queue/types.tspackages/server/src/api/queue/validation.ts
📚 Learning: 2025-12-30T00:13:37.849Z
Learnt from: jhaynie
Repo: agentuity/sdk PR: 355
File: packages/server/src/api/sandbox/util.ts:2-6
Timestamp: 2025-12-30T00:13:37.849Z
Learning: In the packages/server tree, treat code as runtime-agnostic between Node.js and Bun. Ensure TypeScript files (e.g., util.ts) import and use APIs in a way that works under both runtimes. It is acceptable to rely on Bun’s Node.js compatibility for built-ins accessed via the node: namespace (e.g., node:events, node:stream, node:buffer). During reviews, prefer patterns and imports that remain compatible with Bun's environment, and flag any hard dependencies on runtime-specific globals or non-portable Node APIs.
Applied to files:
packages/server/src/api/queue/util.tspackages/server/src/api/queue/sources.tspackages/server/src/api/queue/types.tspackages/server/src/api/queue/validation.ts
🧬 Code graph analysis (4)
scripts/test-queue.sh (2)
packages/core/src/services/queue.ts (1)
publish(288-383)packages/runtime/src/services/local/queue.ts (1)
publish(45-126)
packages/cli/src/cmd/cloud/queue/sources.ts (4)
packages/cli/src/cmd/cloud/queue/util.ts (2)
createQueueAPIClient(23-25)getQueueApiOptions(31-34)packages/server/src/api/queue/sources.ts (5)
listSources(149-178)createSource(57-124)getSource(203-243)updateSource(275-321)deleteSource(344-384)packages/server/src/api/queue/types.ts (2)
Source(1189-1189)SourceSchema(1151-1184)packages/server/src/api/queue/util.ts (1)
SourceAlreadyExistsError(146-149)
packages/server/src/api/queue/util.ts (2)
packages/core/src/index.ts (2)
StructuredError(2-2)QueueNotFoundError(73-73)packages/core/src/services/queue.ts (1)
QueueNotFoundError(191-191)
packages/server/src/api/queue/types.ts (1)
packages/schema/src/index.ts (1)
infer(177-177)
🪛 Shellcheck (0.11.0)
scripts/test-queue.sh
[warning] 335-335: Quote this to prevent word splitting.
(SC2046)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
- GitHub Check: Framework Integration Tests (TanStack & Next.js)
- GitHub Check: Sandbox CLI Tests
- GitHub Check: Queue CLI Tests
- GitHub Check: Playwright E2E Smoke Test
- GitHub Check: Queue SDK Tests
- GitHub Check: SDK Integration Test Suite
- GitHub Check: Package Installation & Usage Test
- GitHub Check: Cloud Deployment Tests
- GitHub Check: Storage CLI Tests
- GitHub Check: Template Integration Tests
- GitHub Check: Pack & Upload
- GitHub Check: Build
🔇 Additional comments (6)
packages/server/src/api/queue/types.ts (2)
14-399: Schema coverage for queues/messages/destinations/DLQ is solid.Clear separation of concerns and well-documented models; the schema surface looks consistent and usable.
606-1123: Analytics/SSE schema set is comprehensive.The granularity, time-series, and SSE event shapes look cohesive and ready for client use.
packages/server/src/api/queue/util.ts (1)
151-203: Centralized queue API helpers look good.Versioned path construction and org-scoped headers are clean and consistent.
packages/server/src/api/queue/sources.ts (2)
57-123: createSource flow is clean and well-mapped to structured errors.Validation and response parsing are consistent with the rest of the queue API surface.
149-243: List/get/delete flows are consistent and straightforward.Good reuse of the path builder, headers, and error translation.
Also applies to: 344-384
packages/server/src/api/queue/validation.ts (1)
15-149: Validation constants and helpers are comprehensive.The ranges, regexes, and per-field checks align well with the intended API constraints.
Also applies to: 177-559
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| } catch (error) { | ||
| if (error instanceof DestinationAlreadyExistsError) { | ||
| tui.error( | ||
| `A destination with URL "${opts.url}" already exists for queue "${args.queue_name}"` | ||
| ); | ||
| tui.info('Hint: Use a different URL or delete the existing destination first'); | ||
| process.exit(1); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use ctx.logger.fatal() for create conflicts.
Replace process.exit(1) with ctx.logger.fatal() to ensure consistent logging and exit behavior. As per coding guidelines, use ctx.logger for fatal exits.
🛠️ Suggested fix
if (error instanceof DestinationAlreadyExistsError) {
- tui.error(
- `A destination with URL "${opts.url}" already exists for queue "${args.queue_name}"`
- );
- tui.info('Hint: Use a different URL or delete the existing destination first');
- process.exit(1);
+ ctx.logger.fatal(
+ `A destination with URL "${opts.url}" already exists for queue "${args.queue_name}". ` +
+ 'Hint: Use a different URL or delete the existing destination first'
+ );
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| } catch (error) { | |
| if (error instanceof DestinationAlreadyExistsError) { | |
| tui.error( | |
| `A destination with URL "${opts.url}" already exists for queue "${args.queue_name}"` | |
| ); | |
| tui.info('Hint: Use a different URL or delete the existing destination first'); | |
| process.exit(1); | |
| } | |
| } catch (error) { | |
| if (error instanceof DestinationAlreadyExistsError) { | |
| ctx.logger.fatal( | |
| `A destination with URL "${opts.url}" already exists for queue "${args.queue_name}". ` + | |
| 'Hint: Use a different URL or delete the existing destination first' | |
| ); | |
| } |
🤖 Prompt for AI Agents
In `@packages/cli/src/cmd/cloud/queue/destinations.ts` around lines 132 - 139, The
catch block handling DestinationAlreadyExistsError should not call
process.exit(1); instead call ctx.logger.fatal(...) to perform a consistent
logged fatal exit. Locate the catch handling DestinationAlreadyExistsError in
the function that references opts.url and args.queue_name (the block that
currently calls tui.error and tui.info) and replace the process.exit(1) call
with ctx.logger.fatal using an appropriate fatal message (include the same
context about the destination URL and queue name) so the code uses ctx.logger
for fatal exits per guidelines.
| validateQueueName(queueName); | ||
| validateSourceId(sourceId); | ||
| if (params.name) { | ||
| validateSourceName(params.name); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
cat -n packages/server/src/api/queue/sources.ts | head -300 | tail -50Repository: agentuity/sdk
Length of output: 1843
🏁 Script executed:
wc -l packages/server/src/api/queue/sources.tsRepository: agentuity/sdk
Length of output: 101
🏁 Script executed:
# Let's see the full function context around lines 282-286
sed -n '270,300p' packages/server/src/api/queue/sources.tsRepository: agentuity/sdk
Length of output: 702
🏁 Script executed:
# Let's search for the validateSourceName function definition
rg -n "validateSourceName" packages/server/src/api/queue/sources.tsRepository: agentuity/sdk
Length of output: 223
🏁 Script executed:
# Search for validateSourceName definition in the codebase
rg -n "function validateSourceName|const validateSourceName" packages/server/src/Repository: agentuity/sdk
Length of output: 161
🏁 Script executed:
sed -n '544,570p' packages/server/src/api/queue/validation.tsRepository: agentuity/sdk
Length of output: 491
🏁 Script executed:
# Let's also check the UpdateSourceRequest type to understand what params.name can be
rg -n "UpdateSourceRequest" packages/server/src/api/queue/sources.tsRepository: agentuity/sdk
Length of output: 183
🏁 Script executed:
# Search for UpdateSourceRequest type definition
rg -n "type UpdateSourceRequest|interface UpdateSourceRequest" packages/server/src/Repository: agentuity/sdk
Length of output: 317
🏁 Script executed:
# Find UpdateSourceRequestSchema definition
rg -n "UpdateSourceRequestSchema" packages/server/src/api/queue/types.ts -A 15Repository: agentuity/sdk
Length of output: 697
🏁 Script executed:
# Let's also check how createSource handles this (line 64 uses validateSourceName)
sed -n '50,75p' packages/server/src/api/queue/sources.tsRepository: agentuity/sdk
Length of output: 713
Use explicit undefined check for optional name parameter.
if (params.name) skips validation when the name is an empty string. Since the schema requires .min(1), empty strings should be caught during manual validation. Check for params.name !== undefined instead to validate all explicitly provided values.
🛠️ Suggested fix
- if (params.name) {
+ if (params.name !== undefined) {
validateSourceName(params.name);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| validateQueueName(queueName); | |
| validateSourceId(sourceId); | |
| if (params.name) { | |
| validateSourceName(params.name); | |
| } | |
| validateQueueName(queueName); | |
| validateSourceId(sourceId); | |
| if (params.name !== undefined) { | |
| validateSourceName(params.name); | |
| } |
🤖 Prompt for AI Agents
In `@packages/server/src/api/queue/sources.ts` around lines 282 - 286, Replace the
truthy check on the optional name so empty strings are validated: in the block
where validateQueueName(queueName), validateSourceId(sourceId) are called,
change the condition that currently reads if (params.name) to an explicit
undefined check (params.name !== undefined) so validateSourceName(params.name)
runs for all explicitly provided values including empty string; keep calling
validateSourceName with params.name when not undefined.
| export const HttpDestinationConfigSchema = z.object({ | ||
| /** The URL to send messages to. */ | ||
| url: z.string(), | ||
| /** Optional custom headers to include in requests. */ | ||
| headers: z.record(z.string(), z.string()).optional(), | ||
| /** HTTP method to use (default: POST). */ | ||
| method: z.string().default('POST'), | ||
| /** Request timeout in milliseconds (default: 30000). */ | ||
| timeout_ms: z.number().default(30000), | ||
| /** Optional retry policy for failed deliveries. */ | ||
| retry_policy: z | ||
| .object({ | ||
| /** Maximum number of delivery attempts (default: 5). */ | ||
| max_attempts: z.number().default(5), | ||
| /** Initial backoff delay in milliseconds (default: 1000). */ | ||
| initial_backoff_ms: z.number().default(1000), | ||
| /** Maximum backoff delay in milliseconds (default: 60000). */ | ||
| max_backoff_ms: z.number().default(60000), | ||
| /** Backoff multiplier for exponential backoff (default: 2.0). */ | ||
| backoff_multiplier: z.number().default(2.0), | ||
| }) | ||
| .optional(), | ||
| /** Optional request signing configuration. */ | ||
| signing: z | ||
| .object({ | ||
| /** Whether signing is enabled (default: false). */ | ||
| enabled: z.boolean().default(false), | ||
| /** Secret key for HMAC signing. */ | ||
| secret_key: z.string().optional(), | ||
| }) | ||
| .optional(), | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, verify file exists and check its size
wc -l packages/server/src/api/queue/types.tsRepository: agentuity/sdk
Length of output: 100
🏁 Script executed:
# Check the schemas at the mentioned lines (256-287)
sed -n '256,287p' packages/server/src/api/queue/types.tsRepository: agentuity/sdk
Length of output: 1268
🏁 Script executed:
# Check the related schemas at lines 1204-1248
sed -n '1200,1250p' packages/server/src/api/queue/types.tsRepository: agentuity/sdk
Length of output: 1465
🏁 Script executed:
# Search for HttpDestinationConfigSchema, CreateSourceRequestSchema, and UpdateSourceRequestSchema in the file
rg -n "HttpDestinationConfigSchema|CreateSourceRequestSchema|UpdateSourceRequestSchema" packages/server/src/api/queue/types.ts -A 5Repository: agentuity/sdk
Length of output: 2175
Add conditional validation for signing/auth secrets.
The HttpDestinationConfigSchema, CreateSourceRequestSchema, and UpdateSourceRequestSchema lack refinements to enforce dependencies between configuration fields. signing.enabled can be true without secret_key, and auth_type can be set without auth_value—these will likely fail at runtime or silently disable security mechanisms. Add Zod refinements to enforce these dependencies:
HttpDestinationConfigSchema: requiresigning.secret_keywhensigning.enabledis trueCreateSourceRequestSchema: requireauth_valuewhenauth_typeis not 'none'UpdateSourceRequestSchema: requireauth_valuewhenauth_typeis provided and not 'none'
🛠️ Suggested refinements
-export const HttpDestinationConfigSchema = z.object({
+export const HttpDestinationConfigSchema = z
+ .object({
/** The URL to send messages to. */
url: z.string(),
/** Optional custom headers to include in requests. */
headers: z.record(z.string(), z.string()).optional(),
/** HTTP method to use (default: POST). */
method: z.string().default('POST'),
/** Request timeout in milliseconds (default: 30000). */
timeout_ms: z.number().default(30000),
/** Optional retry policy for failed deliveries. */
retry_policy: z
.object({
/** Maximum number of delivery attempts (default: 5). */
max_attempts: z.number().default(5),
/** Initial backoff delay in milliseconds (default: 1000). */
initial_backoff_ms: z.number().default(1000),
/** Maximum backoff delay in milliseconds (default: 60000). */
max_backoff_ms: z.number().default(60000),
/** Backoff multiplier for exponential backoff (default: 2.0). */
backoff_multiplier: z.number().default(2.0),
})
.optional(),
/** Optional request signing configuration. */
signing: z
.object({
/** Whether signing is enabled (default: false). */
enabled: z.boolean().default(false),
/** Secret key for HMAC signing. */
secret_key: z.string().optional(),
})
.optional(),
-});
+ })
+ .refine(
+ (config) => !config.signing?.enabled || !!config.signing.secret_key,
+ {
+ message: 'signing.secret_key is required when signing.enabled is true',
+ path: ['signing', 'secret_key'],
+ }
+ );-export const CreateSourceRequestSchema = z.object({
+export const CreateSourceRequestSchema = z
+ .object({
/** Human-readable name for the source. */
name: z.string().min(1).max(256),
/** Optional description. */
description: z.string().max(1024).optional(),
/** Authentication type (default: none). */
auth_type: SourceAuthTypeSchema.optional().default('none'),
/** Authentication value (format depends on auth_type). */
auth_value: z.string().optional(),
-});
+ })
+ .refine((req) => req.auth_type === 'none' || !!req.auth_value, {
+ message: 'auth_value is required when auth_type is not "none"',
+ path: ['auth_value'],
+ });-export const UpdateSourceRequestSchema = z.object({
+export const UpdateSourceRequestSchema = z
+ .object({
/** New name for the source. */
name: z.string().min(1).max(256).optional(),
/** New description. */
description: z.string().max(1024).nullable().optional(),
/** New authentication type. */
auth_type: SourceAuthTypeSchema.optional(),
/** New authentication value. */
auth_value: z.string().optional(),
/** Whether the source is enabled. */
enabled: z.boolean().optional(),
-});
+ })
+ .refine(
+ (req) => req.auth_type === undefined || req.auth_type === 'none' || !!req.auth_value,
+ {
+ message: 'auth_value is required when auth_type is not "none"',
+ path: ['auth_value'],
+ }
+ );📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export const HttpDestinationConfigSchema = z.object({ | |
| /** The URL to send messages to. */ | |
| url: z.string(), | |
| /** Optional custom headers to include in requests. */ | |
| headers: z.record(z.string(), z.string()).optional(), | |
| /** HTTP method to use (default: POST). */ | |
| method: z.string().default('POST'), | |
| /** Request timeout in milliseconds (default: 30000). */ | |
| timeout_ms: z.number().default(30000), | |
| /** Optional retry policy for failed deliveries. */ | |
| retry_policy: z | |
| .object({ | |
| /** Maximum number of delivery attempts (default: 5). */ | |
| max_attempts: z.number().default(5), | |
| /** Initial backoff delay in milliseconds (default: 1000). */ | |
| initial_backoff_ms: z.number().default(1000), | |
| /** Maximum backoff delay in milliseconds (default: 60000). */ | |
| max_backoff_ms: z.number().default(60000), | |
| /** Backoff multiplier for exponential backoff (default: 2.0). */ | |
| backoff_multiplier: z.number().default(2.0), | |
| }) | |
| .optional(), | |
| /** Optional request signing configuration. */ | |
| signing: z | |
| .object({ | |
| /** Whether signing is enabled (default: false). */ | |
| enabled: z.boolean().default(false), | |
| /** Secret key for HMAC signing. */ | |
| secret_key: z.string().optional(), | |
| }) | |
| .optional(), | |
| }); | |
| export const HttpDestinationConfigSchema = z | |
| .object({ | |
| /** The URL to send messages to. */ | |
| url: z.string(), | |
| /** Optional custom headers to include in requests. */ | |
| headers: z.record(z.string(), z.string()).optional(), | |
| /** HTTP method to use (default: POST). */ | |
| method: z.string().default('POST'), | |
| /** Request timeout in milliseconds (default: 30000). */ | |
| timeout_ms: z.number().default(30000), | |
| /** Optional retry policy for failed deliveries. */ | |
| retry_policy: z | |
| .object({ | |
| /** Maximum number of delivery attempts (default: 5). */ | |
| max_attempts: z.number().default(5), | |
| /** Initial backoff delay in milliseconds (default: 1000). */ | |
| initial_backoff_ms: z.number().default(1000), | |
| /** Maximum backoff delay in milliseconds (default: 60000). */ | |
| max_backoff_ms: z.number().default(60000), | |
| /** Backoff multiplier for exponential backoff (default: 2.0). */ | |
| backoff_multiplier: z.number().default(2.0), | |
| }) | |
| .optional(), | |
| /** Optional request signing configuration. */ | |
| signing: z | |
| .object({ | |
| /** Whether signing is enabled (default: false). */ | |
| enabled: z.boolean().default(false), | |
| /** Secret key for HMAC signing. */ | |
| secret_key: z.string().optional(), | |
| }) | |
| .optional(), | |
| }) | |
| .refine( | |
| (config) => !config.signing?.enabled || !!config.signing.secret_key, | |
| { | |
| message: 'signing.secret_key is required when signing.enabled is true', | |
| path: ['signing', 'secret_key'], | |
| } | |
| ); |
🤖 Prompt for AI Agents
In `@packages/server/src/api/queue/types.ts` around lines 256 - 287, The schemas
lack cross-field validation: update HttpDestinationConfigSchema,
CreateSourceRequestSchema, and UpdateSourceRequestSchema to add Zod refinements
that enforce dependent fields—use .superRefine or .refine on each schema so
HttpDestinationConfigSchema checks if obj.signing?.enabled === true then
obj.signing?.secret_key is present and non-empty; make CreateSourceRequestSchema
require auth_value when auth_type !== 'none'; and make UpdateSourceRequestSchema
require auth_value when auth_type is provided and auth_type !== 'none' (treat
absent auth_type as ok). Reference the schema symbols
HttpDestinationConfigSchema, CreateSourceRequestSchema, and
UpdateSourceRequestSchema when locating and adding these refinements.
| export function validatePayload(payload: unknown): void { | ||
| if (payload === undefined || payload === null) { | ||
| throw new QueueValidationError({ | ||
| message: 'Payload cannot be empty', | ||
| field: 'payload', | ||
| }); | ||
| } | ||
| const serialized = JSON.stringify(payload); | ||
| const payloadBytes = new TextEncoder().encode(serialized).length; | ||
| if (payloadBytes > MAX_PAYLOAD_SIZE) { | ||
| throw new QueueValidationError({ | ||
| message: `Payload size exceeds ${MAX_PAYLOAD_SIZE} byte limit (${payloadBytes} bytes)`, | ||
| field: 'payload', | ||
| value: payloadBytes, | ||
| }); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
cat -n packages/server/src/api/queue/validation.tsRepository: agentuity/sdk
Length of output: 19607
Handle non-JSON-serializable payloads explicitly.
JSON.stringify throws for BigInt or circular references, and returns undefined for functions/symbols. The current code surfaces unstructured errors in the first case and silently accepts invalid payloads in the second case (the undefined value gets converted to the string "undefined" by TextEncoder, allowing it through as 9 bytes).
🛠️ Suggested fix
- const serialized = JSON.stringify(payload);
- const payloadBytes = new TextEncoder().encode(serialized).length;
+ let serialized: string | undefined;
+ try {
+ serialized = JSON.stringify(payload);
+ } catch {
+ throw new QueueValidationError({
+ message: 'Payload must be JSON-serializable',
+ field: 'payload',
+ });
+ }
+ if (serialized === undefined) {
+ throw new QueueValidationError({
+ message: 'Payload must be JSON-serializable',
+ field: 'payload',
+ });
+ }
+ const payloadBytes = new TextEncoder().encode(serialized).length;🤖 Prompt for AI Agents
In `@packages/server/src/api/queue/validation.ts` around lines 159 - 175, The
validatePayload function must reject non-JSON-serializable values: wrap
JSON.stringify(payload) in a try/catch and on error throw a QueueValidationError
(use QueueValidationError) with a clear message and field:'payload';
additionally, if JSON.stringify returns undefined (e.g., functions/symbols),
treat that as invalid and throw QueueValidationError before computing
payloadBytes; keep existing MAX_PAYLOAD_SIZE check and error shape but ensure
you reference validatePayload, MAX_PAYLOAD_SIZE, and QueueValidationError when
making these changes.
| INGEST_OUTPUT=$(curl -s -X POST "$SOURCE_URL" \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"source":"http_ingest_test","timestamp":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' 2>&1) || true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quote the timestamp substitution in the curl payload.
Unquoted $(date ...) triggers word-splitting warnings and is fragile if formats change. Quote the substitution or build the JSON with printf.
🛠️ Suggested fix
-INGEST_OUTPUT=$(curl -s -X POST "$SOURCE_URL" \
- -H "Content-Type: application/json" \
- -d '{"source":"http_ingest_test","timestamp":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' 2>&1) || true
+INGEST_OUTPUT=$(curl -s -X POST "$SOURCE_URL" \
+ -H "Content-Type: application/json" \
+ -d "{\"source\":\"http_ingest_test\",\"timestamp\":\"$(date -u '+%Y-%m-%dT%H:%M:%SZ')\"}" 2>&1) || true📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| INGEST_OUTPUT=$(curl -s -X POST "$SOURCE_URL" \ | |
| -H "Content-Type: application/json" \ | |
| -d '{"source":"http_ingest_test","timestamp":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' 2>&1) || true | |
| INGEST_OUTPUT=$(curl -s -X POST "$SOURCE_URL" \ | |
| -H "Content-Type: application/json" \ | |
| -d "{\"source\":\"http_ingest_test\",\"timestamp\":\"$(date -u '+%Y-%m-%dT%H:%M:%SZ')\"}" 2>&1) || true |
🧰 Tools
🪛 Shellcheck (0.11.0)
[warning] 335-335: Quote this to prevent word splitting.
(SC2046)
🤖 Prompt for AI Agents
In `@scripts/test-queue.sh` around lines 333 - 335, The curl payload uses an
unquoted command substitution for the timestamp which can cause word-splitting;
update the INGEST_OUTPUT assignment (the curl invocation that posts to
"$SOURCE_URL") to quote the timestamp substitution or construct the JSON with
printf so the timestamp is a single JSON string (i.e., ensure the
'"timestamp":"$(date ...)"' portion is quoted or replaced by a printf-generated
JSON string) while preserving the existing -d and 2>&1 behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
🤖 Fix all issues with AI agents
In `@packages/cli/src/cmd/cloud/queue/destinations.ts`:
- Around line 205-206: The two console.log calls that print destination details
should use the CLI tui helpers for consistent output; replace console.log(`
URL: ${destination.config.url}`) and console.log(` Enabled:
${destination.enabled ? 'Yes' : 'No'}`) with tui.info(...) calls (matching the
pattern used in createDestinationSubcommand) so the messages go through the same
formatting/logger; ensure tui is imported/available in the scope where the
printing occurs (e.g., in the function that handles listing destinations).
In `@packages/cli/src/cmd/cloud/queue/sources.ts`:
- Around line 133-136: The output uses direct console.log calls after
tui.success which bypasses CLI formatting; replace those console.log calls
(e.g., the ones printing source.name and source.url after creating a source that
reference source.id, source.name, source.url) with the appropriate tui helper
(such as tui.info or another tui.* helper used elsewhere) to keep formatting
consistent, and apply the same change to the other similar blocks mentioned
(around the segments covering lines 182-201 and 271-275) so all CLI output flows
through the tui layer.
In `@packages/cli/src/cmd/cloud/queue/stats.ts`:
- Around line 50-79: The code is directly using console.log for summary and
queue output (lines that call console.log around tui.colorPrimary/tui.muted and
the queues table generation), which bypasses the CLI formatter; replace those
console.log calls with the appropriate tui helpers (e.g., use tui.print or an
existing tui.info/tui.success helper, and use tui.newline/tui.table as already
used) so all formatted strings (the Summary block and each line using
formatNumber/formatLatency/formatPercent) go through tui; update the Summary
block and the per-queue output (the tableData mapping that produces
Name/Type/Published/etc.) to call tui printing helpers instead of console.log,
or add a small tui.print wrapper and use it for every console.log instance in
this file (including the later occurrences noted in the comment) so output
styling is consistent.
- Around line 231-275: When opts.live is true, avoid unbounded buffering by only
collecting events into the events array when options.json is true (move
events.push(event) behind a JSON check) and stop calling process.exit(0) in
handleInterrupt; instead set a shared flag (e.g., let stopped = false and set
stopped = true in handleInterrupt) and break out of the async for-await loops
when stopped so streamQueueAnalytics/streamOrgAnalytics can finish and cleanup;
keep process.on('SIGINT', handleInterrupt) and process.off(...) but let the
function return { type: 'stream', events } (events will be empty unless JSON
mode) so JSON output is produced and long-running live sessions do not grow
memory unbounded.
In `@scripts/test-queue.sh`:
- Around line 205-213: The ACK block uses command substitution
(ACK_OUTPUT=$($CLI cloud queue ack ...); ACK_STATUS=$?) which can abort under
set -e; change it to the safe if-pattern: run the CLI invocation as if
ACK_OUTPUT=$($CLI cloud queue ack "$QUEUE_NAME" "$RECEIVED_ID" 2>&1); then set
ACK_STATUS=0 in the then branch and ACK_STATUS=$? in the else branch so the
script won't exit before capturing the status, and keep the subsequent
grep/STATUS check unchanged. Apply the same change to the corresponding CLI
invocations: the cloud queue nack block (NACK_OUTPUT/NACK_STATUS), cloud
destinations delete block, cloud queue dlq purge block, and cloud queue delete
block so each uses the if OUTPUT=...; then STATUS=0; else STATUS=$?; fi pattern.
♻️ Duplicate comments (6)
packages/cli/src/cmd/cloud/queue/destinations.ts (1)
128-129: Replaceconsole.logwithtui.*helpers.These lines use raw
console.loginstead oftui.*helpers for formatted output. As per coding guidelines, CLI output should usetui.*helpers for consistency.🔧 Suggested fix
if (!options.json) { tui.success(`Created destination: ${destination.id}`); - console.log(` URL: ${destination.config.url}`); - console.log(` Method: ${destination.config.method}`); + tui.info(` URL: ${destination.config.url}`); + tui.info(` Method: ${destination.config.method}`); }packages/cli/src/cmd/cloud/queue/sources.ts (1)
140-145: Usectx.logger.fatal()for fatal exits.CLI fatal exits should go through the logger pipeline;
tui.fatalbypasses it. Please switch toctx.logger.fatal()(preserving the error code if supported).🔧 Suggested change
} catch (error) { if (error instanceof SourceAlreadyExistsError) { - tui.fatal( + ctx.logger.fatal( `A source with name "${opts.name}" already exists for queue "${args.queue_name}". Use a different name or delete the existing source first.`, ErrorCode.RESOURCE_ALREADY_EXISTS ); } throw error; } @@ if (opts.enabled && opts.disabled) { - tui.fatal( + ctx.logger.fatal( 'Cannot specify both --enabled and --disabled flags', ErrorCode.INVALID_ARGUMENT ); }As per coding guidelines, use
ctx.logger.fatal()to log and exit with code 1.Also applies to: 254-258
scripts/test-queue.sh (2)
372-380: Fix success fallback:$?is grep’s status, not the CLI’s.
The|| truediscards the CLI exit code, and$?reflectsgrep, so the fallback never accepts a successful CLI response without keywords. Capture the CLI status explicitly (same pattern as the ACK fix).🛠️ Suggested fix
- SOURCE_DELETE_OUTPUT=$($CLI cloud queue sources delete "$QUEUE_NAME" "$SOURCE_ID" 2>&1) || true - if echo "$SOURCE_DELETE_OUTPUT" | grep -qi "deleted\|success" || [ $? -eq 0 ]; then + if SOURCE_DELETE_OUTPUT=$($CLI cloud queue sources delete "$QUEUE_NAME" "$SOURCE_ID" 2>&1); then + SOURCE_DELETE_STATUS=0 + else + SOURCE_DELETE_STATUS=$? + fi + if echo "$SOURCE_DELETE_OUTPUT" | grep -qi "deleted\|success" || [ "$SOURCE_DELETE_STATUS" -eq 0 ]; then pass "queue sources delete succeeds" else fail "queue sources delete failed" "$SOURCE_DELETE_OUTPUT" fi
336-338: Quote the timestamp substitution in the curl payload.
Unquoted$(date ...)risks word-splitting; quote it or build JSON withprintf.🛠️ Suggested fix
- INGEST_OUTPUT=$(curl -s -X POST "$SOURCE_URL" \ - -H "Content-Type: application/json" \ - -d '{"source":"http_ingest_test","timestamp":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' 2>&1) || true + INGEST_OUTPUT=$(curl -s -X POST "$SOURCE_URL" \ + -H "Content-Type: application/json" \ + -d "{\"source\":\"http_ingest_test\",\"timestamp\":\"$(date -u '+%Y-%m-%dT%H:%M:%SZ')\"}" 2>&1) || truepackages/server/src/api/queue/validation.ts (1)
159-174: Guard against non‑JSON‑serializable payloads.
JSON.stringifycan throw (BigInt/circular) or returnundefined(functions/symbols), which currently slips through and yields misleading size checks.🛠️ Suggested fix
export function validatePayload(payload: unknown): void { if (payload === undefined || payload === null) { throw new QueueValidationError({ message: 'Payload cannot be empty', field: 'payload', }); } - const serialized = JSON.stringify(payload); + let serialized: string | undefined; + try { + serialized = JSON.stringify(payload); + } catch { + throw new QueueValidationError({ + message: 'Payload must be JSON-serializable', + field: 'payload', + }); + } + if (serialized === undefined) { + throw new QueueValidationError({ + message: 'Payload must be JSON-serializable', + field: 'payload', + }); + } const payloadBytes = new TextEncoder().encode(serialized).length; if (payloadBytes > MAX_PAYLOAD_SIZE) { throw new QueueValidationError({ message: `Payload size exceeds ${MAX_PAYLOAD_SIZE} byte limit (${payloadBytes} bytes)`, field: 'payload', value: payloadBytes, }); } }packages/server/src/api/queue/types.ts (1)
256-287: Add conditional validation for signing/auth fields.
signing.enabledcan be true without asecret_key, and source auth can be set without anauth_value. Add Zod refinements to enforce those dependencies. Please confirm the refinement behavior against Zod v4.Also applies to: 1204-1248
🧹 Nitpick comments (3)
packages/cli/src/cmd/cloud/queue/destinations.ts (1)
100-100: Redundant.optional()after.default().The
.default('POST')already handles undefined input by providing a default value. Adding.optional()is redundant and makes the intent unclear. Also, line 118 has a duplicate fallbackopts.method || 'POST'that becomes unnecessary if the schema default works correctly.♻️ Suggested fix
- method: z.string().default('POST').optional().describe('HTTP method (default: POST)'), + method: z.string().default('POST').describe('HTTP method (default: POST)'),And at line 118:
- method: opts.method || 'POST', + method: opts.method,packages/cli/src/cmd/cloud/queue/sources.ts (1)
119-128: Validateauth-type/auth-valuecombinations before API calls.Right now a user can set
--auth-type basic|headerwithout--auth-value, or pass--auth-valuewithauth-typeofnone. Consider rejecting these combinations early to avoid confusing server-side errors.🔧 Suggested guard (apply to create and update)
+ if (opts['auth-type'] && opts['auth-type'] !== 'none' && !opts['auth-value']) { + ctx.logger.fatal('auth-value is required when auth-type is basic or header'); + } + if (opts['auth-value'] && (!opts['auth-type'] || opts['auth-type'] === 'none')) { + ctx.logger.fatal('auth-type must be basic or header when auth-value is provided'); + }Please confirm the server/API contract for
auth_typeandauth_valueto ensure these validations align with expected behavior.Also applies to: 250-253
packages/cli/src/cmd/cloud/queue/dlq.ts (1)
164-169: Usectx.logger.fatalfor the confirmation guard.CLI guidelines ask for
ctx.loggerandlogger.fatal()for error exits;tui.fatalbypasses that pattern.♻️ Proposed adjustment
- async handler(ctx) { - const { args, opts, options } = ctx; + async handler(ctx) { + const { args, opts, options, logger } = ctx; if (!opts.confirm) { - tui.fatal('Use --confirm to confirm DLQ purge', ErrorCode.INVALID_ARGUMENT); + logger.fatal('Use --confirm to confirm DLQ purge', ErrorCode.INVALID_ARGUMENT); }As per coding guidelines, use
ctx.loggerfor logging.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
packages/cli/src/cmd/cloud/queue/destinations.tspackages/cli/src/cmd/cloud/queue/dlq.tspackages/cli/src/cmd/cloud/queue/sources.tspackages/cli/src/cmd/cloud/queue/stats.tspackages/server/src/api/queue/analytics.tspackages/server/src/api/queue/dlq.tspackages/server/src/api/queue/types.tspackages/server/src/api/queue/validation.tsscripts/test-queue.sh
🧰 Additional context used
📓 Path-based instructions (4)
packages/cli/src/cmd/**/*.ts
📄 CodeRabbit inference engine (packages/cli/AGENTS.md)
packages/cli/src/cmd/**/*.ts: Usetui.*helpers for formatted output instead of raw console logs
Usectx.loggerfor logging; calllogger.fatal()to log and exit with code 1
Files:
packages/cli/src/cmd/cloud/queue/dlq.tspackages/cli/src/cmd/cloud/queue/sources.tspackages/cli/src/cmd/cloud/queue/stats.tspackages/cli/src/cmd/cloud/queue/destinations.ts
packages/cli/src/**/*.ts
📄 CodeRabbit inference engine (packages/cli/AGENTS.md)
Use
Bun.file(f).exists()instead ofexistsSync(f)for file existence checks
Files:
packages/cli/src/cmd/cloud/queue/dlq.tspackages/cli/src/cmd/cloud/queue/sources.tspackages/cli/src/cmd/cloud/queue/stats.tspackages/cli/src/cmd/cloud/queue/destinations.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use Prettier formatter with tabs (width 3), single quotes, and semicolons for TypeScript files
Use TypeScript strict mode with ESNext target and bundler moduleResolution
UseStructuredErrorfrom@agentuity/corefor error handling
Files:
packages/cli/src/cmd/cloud/queue/dlq.tspackages/cli/src/cmd/cloud/queue/sources.tspackages/cli/src/cmd/cloud/queue/stats.tspackages/cli/src/cmd/cloud/queue/destinations.tspackages/server/src/api/queue/analytics.tspackages/server/src/api/queue/validation.tspackages/server/src/api/queue/dlq.tspackages/server/src/api/queue/types.ts
packages/server/src/**/*.ts
📄 CodeRabbit inference engine (packages/server/AGENTS.md)
packages/server/src/**/*.ts: Build TypeScript withbun run buildcommand using tsc for compilation
Run TypeScript type checking withbun run typecheck
All code must be runtime-agnostic - no Bun-specific or Node-specific APIs
No browser APIs allowed - server-side only
Prefer interfaces for public APIs in TypeScript
Files:
packages/server/src/api/queue/analytics.tspackages/server/src/api/queue/validation.tspackages/server/src/api/queue/dlq.tspackages/server/src/api/queue/types.ts
🧠 Learnings (4)
📚 Learning: 2025-12-21T00:31:41.858Z
Learnt from: jhaynie
Repo: agentuity/sdk PR: 274
File: packages/cli/src/cmd/build/vite/server-bundler.ts:12-41
Timestamp: 2025-12-21T00:31:41.858Z
Learning: In Bun runtime, BuildMessage and ResolveMessage are global types and are not exported from the bun module. Do not import { BuildMessage } from 'bun' or similar; these types are available globally and should be used without import. This applies to all TypeScript files that target the Bun runtime within the repository.
Applied to files:
packages/cli/src/cmd/cloud/queue/dlq.tspackages/cli/src/cmd/cloud/queue/sources.tspackages/cli/src/cmd/cloud/queue/stats.tspackages/cli/src/cmd/cloud/queue/destinations.tspackages/server/src/api/queue/analytics.tspackages/server/src/api/queue/validation.tspackages/server/src/api/queue/dlq.tspackages/server/src/api/queue/types.ts
📚 Learning: 2026-01-13T04:32:02.691Z
Learnt from: jhaynie
Repo: agentuity/sdk PR: 565
File: packages/cli/src/cmd/cloud/region-lookup.ts:14-26
Timestamp: 2026-01-13T04:32:02.691Z
Learning: Enforce sandbox identifier prefixes in new code within the CLI cloud region lookup: new sandboxes must use the sbx_ prefix. The snbx_ prefix may appear in legacy code or examples, but do not use snbx_ for new sandboxes. When reviewing changes in packages/cli/src/cmd/cloud/, ensure any created sandbox identifiers use sbx_ and remove or migrate any snbx_ usages in newly added code.
Applied to files:
packages/cli/src/cmd/cloud/queue/dlq.tspackages/cli/src/cmd/cloud/queue/sources.tspackages/cli/src/cmd/cloud/queue/stats.tspackages/cli/src/cmd/cloud/queue/destinations.ts
📚 Learning: 2025-12-19T14:19:33.765Z
Learnt from: jhaynie
Repo: agentuity/sdk PR: 259
File: packages/cli/src/cmd/build/vite/registry-generator.ts:306-312
Timestamp: 2025-12-19T14:19:33.765Z
Learning: Route files under src/api should use the .ts extension only (no .tsx) and regex patterns for such paths should anchor to \.ts$ (e.g., /\/.ts$/). Agent files may support both .ts and .tsx, but route files in the Agentuity SDK codebase are restricted to .ts. This guideline applies to all similar route files under src/api across the repository.
Applied to files:
packages/server/src/api/queue/analytics.tspackages/server/src/api/queue/validation.tspackages/server/src/api/queue/dlq.tspackages/server/src/api/queue/types.ts
📚 Learning: 2025-12-30T00:13:37.849Z
Learnt from: jhaynie
Repo: agentuity/sdk PR: 355
File: packages/server/src/api/sandbox/util.ts:2-6
Timestamp: 2025-12-30T00:13:37.849Z
Learning: In the packages/server tree, treat code as runtime-agnostic between Node.js and Bun. Ensure TypeScript files (e.g., util.ts) import and use APIs in a way that works under both runtimes. It is acceptable to rely on Bun’s Node.js compatibility for built-ins accessed via the node: namespace (e.g., node:events, node:stream, node:buffer). During reviews, prefer patterns and imports that remain compatible with Bun's environment, and flag any hard dependencies on runtime-specific globals or non-portable Node APIs.
Applied to files:
packages/server/src/api/queue/analytics.tspackages/server/src/api/queue/validation.tspackages/server/src/api/queue/dlq.tspackages/server/src/api/queue/types.ts
🧬 Code graph analysis (4)
packages/cli/src/cmd/cloud/queue/stats.ts (3)
packages/server/src/api/queue/types.ts (3)
OrgAnalytics(1002-1002)QueueAnalytics(901-901)SSEStatsEvent(1122-1122)packages/cli/src/cmd/cloud/queue/util.ts (2)
createQueueAPIClient(23-25)getQueueApiOptions(31-34)packages/server/src/api/queue/analytics.ts (4)
streamQueueAnalytics(240-254)streamOrgAnalytics(209-220)getQueueAnalytics(104-134)getOrgAnalytics(61-81)
packages/cli/src/cmd/cloud/queue/destinations.ts (3)
packages/server/src/api/queue/destinations.ts (4)
listDestinations(149-178)createDestination(57-126)updateDestination(212-258)deleteDestination(281-321)packages/server/src/api/queue/types.ts (2)
Destination(353-353)DestinationSchema(331-348)packages/server/src/api/queue/util.ts (1)
DestinationAlreadyExistsError(89-92)
scripts/test-queue.sh (2)
packages/core/src/services/queue.ts (1)
publish(288-383)packages/runtime/src/services/local/queue.ts (1)
publish(45-126)
packages/server/src/api/queue/validation.ts (2)
packages/core/src/index.ts (2)
QueueValidationError(74-74)StructuredError(2-2)packages/core/src/services/queue.ts (1)
QueueValidationError(198-203)
🪛 GitHub Actions: Build Packages & Test
packages/cli/src/cmd/cloud/queue/dlq.ts
[error] 58-59: TS2339: Property 'limit' does not exist on type 'GlobalOptions'.
🪛 GitHub Actions: NPM Pack & Upload
packages/cli/src/cmd/cloud/queue/dlq.ts
[error] 58-59: TypeScript error: Property 'limit' does not exist on type 'GlobalOptions'. (TS2339) at dlq.ts(58,20) and Property 'offset' does not exist on type 'GlobalOptions'. (TS2339) at dlq.ts(59,21). While running 'bun run --filter='./packages/cli' build'.
🪛 GitHub Actions: Package Smoke Test
packages/cli/src/cmd/cloud/queue/dlq.ts
[error] 58-58: Property 'limit' does not exist on type 'GlobalOptions'. (bunx tsc --build)
[error] 59-59: Property 'offset' does not exist on type 'GlobalOptions'. (bunx tsc --build)
🪛 Shellcheck (0.11.0)
scripts/test-queue.sh
[warning] 338-338: Quote this to prevent word splitting.
(SC2046)
🔇 Additional comments (40)
packages/cli/src/cmd/cloud/queue/destinations.ts (4)
29-79: LGTM!The list subcommand correctly uses
tui.*helpers for output and properly maps theDestinationtype to a CLI-friendly table format.
186-191: LGTM on validation logic.Good validation to prevent conflicting
--enabledand--disabledflags with clear error messaging.
219-259: LGTM!The delete subcommand is cleanly implemented with proper use of
tui.success()and a well-structured response payload.
261-287: LGTM!The parent command correctly aggregates all subcommands with helpful examples and appropriate metadata.
scripts/test-queue.sh (1)
26-67: Clean harness setup & cleanup flow.
The helper functions + cleanup/trap structure make the test output consistent and easy to follow.packages/cli/src/cmd/cloud/queue/dlq.ts (3)
97-135: Replay subcommand looks solid.The handler flow is clear and the JSON vs. human output split is consistent with other CLI commands.
185-201: Command registration is clean.The top-level
dlqcommand wiring and examples are consistent with the CLI structure.
51-60: The code is already correct. Lines 51-60 properly destructureoptsfromctxand useopts.limitandopts.offsetwhen callinglistDeadLetterMessages. No changes are needed—this handler follows the correct pattern used throughout the codebase.Likely an incorrect or invalid review comment.
packages/server/src/api/queue/dlq.ts (5)
21-28: Schema definitions are aligned and concise.
54-100: List DLQ implementation reads clean and consistent.
125-167: Replay flow and error mapping look good.
190-219: Purge handling is straightforward and consistent.
243-283: Delete message flow is well-structured.packages/server/src/api/queue/validation.ts (19)
15-63: Validation constants are clear and well-scoped.
83-86: Structured error definition is appropriate.
110-133: Queue name validation is tight and readable.
141-148: Queue type validation is clear.
185-211: ID validators are consistent and clear.
219-227: Description validation looks good.
237-245: Partition key validation is straightforward.
255-263: Idempotency key validation is solid.
273-281: TTL validation is clear and bounded.
292-309: Visibility timeout checks are well-scoped.
317-334: Max retries validation is consistent.
344-361: Max in-flight validation is consistent.
371-379: Offset validation is correct.
387-402: Limit validation aligns with batch constraints.
410-425: Batch size validation is clear.
435-448: Webhook URL validation is straightforward.
459-497: Destination config validation is comprehensive.
513-529: Source ID validation is consistent.
545-559: Source name validation is clear.packages/cli/src/cmd/cloud/queue/stats.ts (3)
16-20: Response schema wiring looks good.The unioned org/queue/stream response shapes are clear and explicit for JSON output. Please sanity-check against Zod v4 to ensure the union matches actual handler returns.
22-43: Formatting helpers are clear and consistent.
166-179: Stream line rendering is compact and readable.packages/server/src/api/queue/types.ts (2)
14-231: Core queue/message schemas look solid.The separation between base settings (no defaults) and full settings is clean, and the message schema is well documented. Please verify Zod v4 default/optional behavior matches the intended PATCH vs output semantics.
610-1184: Analytics/time-series schemas are comprehensive.The analytics, time-series, and SSE event shapes are thorough and well documented. Please spot-check Zod v4 parsing on these schemas.
packages/server/src/api/queue/analytics.ts (3)
18-39: Response schema wiring and query building are straightforward.The response schemas and URLSearchParams-based query assembly are clean. Please verify Zod v4 parsing for the response schemas.
61-189: Non-stream analytics error mapping is consistent.
209-313: Streaming helper cleanup looks good.Queue-specific 404 mapping plus reader cancel/release should keep SSE tidy; please smoke-test streaming under both Node and Bun runtimes.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| console.log(` URL: ${destination.config.url}`); | ||
| console.log(` Enabled: ${destination.enabled ? 'Yes' : 'No'}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace console.log with tui.* helpers.
Same issue as in createDestinationSubcommand — use tui.info() instead of raw console.log for consistent CLI output.
🔧 Suggested fix
if (!options.json) {
tui.success(`Updated destination: ${destination.id}`);
- console.log(` URL: ${destination.config.url}`);
- console.log(` Enabled: ${destination.enabled ? 'Yes' : 'No'}`);
+ tui.info(` URL: ${destination.config.url}`);
+ tui.info(` Enabled: ${destination.enabled ? 'Yes' : 'No'}`);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| console.log(` URL: ${destination.config.url}`); | |
| console.log(` Enabled: ${destination.enabled ? 'Yes' : 'No'}`); | |
| tui.info(` URL: ${destination.config.url}`); | |
| tui.info(` Enabled: ${destination.enabled ? 'Yes' : 'No'}`); |
🤖 Prompt for AI Agents
In `@packages/cli/src/cmd/cloud/queue/destinations.ts` around lines 205 - 206, The
two console.log calls that print destination details should use the CLI tui
helpers for consistent output; replace console.log(` URL:
${destination.config.url}`) and console.log(` Enabled: ${destination.enabled ?
'Yes' : 'No'}`) with tui.info(...) calls (matching the pattern used in
createDestinationSubcommand) so the messages go through the same
formatting/logger; ensure tui is imported/available in the scope where the
printing occurs (e.g., in the function that handles listing destinations).
| tui.success(`Created source: ${source.id}`); | ||
| console.log(` Name: ${source.name}`); | ||
| console.log(` URL: ${source.url}`); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace console.log with tui.* output helpers.
Direct console.log bypasses the CLI formatting layer and makes output inconsistent. Please route these through tui (e.g., tui.info) and keep formatting centralized. Apply similarly across the other output blocks.
🔧 Example adjustment (apply similarly elsewhere)
if (!options.json) {
tui.success(`Created source: ${source.id}`);
- console.log(` Name: ${source.name}`);
- console.log(` URL: ${source.url}`);
+ tui.info(` Name: ${source.name}`);
+ tui.info(` URL: ${source.url}`);
}As per coding guidelines, use tui.* helpers for formatted output instead of raw console logs.
Also applies to: 182-201, 271-275
🤖 Prompt for AI Agents
In `@packages/cli/src/cmd/cloud/queue/sources.ts` around lines 133 - 136, The
output uses direct console.log calls after tui.success which bypasses CLI
formatting; replace those console.log calls (e.g., the ones printing source.name
and source.url after creating a source that reference source.id, source.name,
source.url) with the appropriate tui helper (such as tui.info or another tui.*
helper used elsewhere) to keep formatting consistent, and apply the same change
to the other similar blocks mentioned (around the segments covering lines
182-201 and 271-275) so all CLI output flows through the tui layer.
| console.log(tui.colorPrimary('Summary:')); | ||
| console.log(` ${tui.muted('Total Queues:')} ${summary.total_queues}`); | ||
| console.log( | ||
| ` ${tui.muted('Published:')} ${formatNumber(summary.total_messages_published)}` | ||
| ); | ||
| console.log( | ||
| ` ${tui.muted('Delivered:')} ${formatNumber(summary.total_messages_delivered)}` | ||
| ); | ||
| console.log( | ||
| ` ${tui.muted('Acknowledged:')} ${formatNumber(summary.total_messages_acknowledged)}` | ||
| ); | ||
| console.log(` ${tui.muted('DLQ Messages:')} ${formatNumber(summary.total_dlq_messages)}`); | ||
| console.log(` ${tui.muted('Avg Latency:')} ${formatLatency(summary.avg_latency_ms)}`); | ||
| console.log(` ${tui.muted('P95 Latency:')} ${formatLatency(summary.p95_latency_ms)}`); | ||
| console.log(` ${tui.muted('Error Rate:')} ${formatPercent(summary.error_rate_percent)}`); | ||
|
|
||
| if (queues.length > 0) { | ||
| tui.newline(); | ||
| console.log(tui.colorPrimary('Queues:')); | ||
| const tableData = queues.map((q) => ({ | ||
| Name: q.name, | ||
| Type: q.queue_type, | ||
| Published: formatNumber(q.messages_published), | ||
| Delivered: formatNumber(q.messages_delivered), | ||
| Backlog: formatNumber(q.backlog), | ||
| DLQ: formatNumber(q.dlq_count), | ||
| 'Avg Latency': formatLatency(q.avg_latency_ms), | ||
| 'Error %': formatPercent(q.error_rate_percent), | ||
| })); | ||
| tui.table(tableData); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace raw console output with tui helpers.
console.log bypasses the CLI formatter and makes output styling inconsistent. Route these lines through appropriate tui.* helpers (or add a small tui.print-style helper if needed). As per coding guidelines, please route formatted output through tui.* helpers.
Also applies to: 87-163
🤖 Prompt for AI Agents
In `@packages/cli/src/cmd/cloud/queue/stats.ts` around lines 50 - 79, The code is
directly using console.log for summary and queue output (lines that call
console.log around tui.colorPrimary/tui.muted and the queues table generation),
which bypasses the CLI formatter; replace those console.log calls with the
appropriate tui helpers (e.g., use tui.print or an existing tui.info/tui.success
helper, and use tui.newline/tui.table as already used) so all formatted strings
(the Summary block and each line using formatNumber/formatLatency/formatPercent)
go through tui; update the Summary block and the per-queue output (the tableData
mapping that produces Name/Type/Published/etc.) to call tui printing helpers
instead of console.log, or add a small tui.print wrapper and use it for every
console.log instance in this file (including the later occurrences noted in the
comment) so output styling is consistent.
| if (opts.live) { | ||
| const events: SSEStatsEvent[] = []; | ||
|
|
||
| tui.info( | ||
| `Streaming stats every ${opts.interval}s... ${tui.colorMuted('(Ctrl+C to exit)')}` | ||
| ); | ||
| tui.info(''); | ||
|
|
||
| const handleInterrupt = () => { | ||
| tui.info(''); | ||
| tui.info('Stream stopped.'); | ||
| process.exit(0); | ||
| }; | ||
| process.on('SIGINT', handleInterrupt); | ||
|
|
||
| try { | ||
| if (args.name) { | ||
| const stream = streamQueueAnalytics(client, args.name, { | ||
| interval: opts.interval, | ||
| orgId: apiOptions?.orgId, | ||
| }); | ||
| for await (const event of stream) { | ||
| events.push(event); | ||
| if (!options.json) { | ||
| displayStreamEvent(event, args.name); | ||
| } | ||
| } | ||
| } else { | ||
| const stream = streamOrgAnalytics(client, { | ||
| interval: opts.interval, | ||
| orgId: apiOptions?.orgId, | ||
| }); | ||
| for await (const event of stream) { | ||
| events.push(event); | ||
| if (!options.json) { | ||
| displayStreamEvent(event); | ||
| } | ||
| } | ||
| } | ||
| } finally { | ||
| process.off('SIGINT', handleInterrupt); | ||
| } | ||
|
|
||
| return { type: 'stream' as const, events }; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid unbounded buffering in live mode (and exit gracefully).
Live streaming pushes every event into events even when --json is off, so long-running sessions grow memory. Also process.exit(0) prevents returning JSON and bypasses graceful cleanup. Consider buffering only in JSON mode and exiting the loop on SIGINT instead of hard-exiting.
🧰 Suggested adjustment
- if (opts.live) {
- const events: SSEStatsEvent[] = [];
+ if (opts.live) {
+ const collectEvents = options.json;
+ const events: SSEStatsEvent[] = [];
+ let interrupted = false;
@@
- const handleInterrupt = () => {
- tui.info('');
- tui.info('Stream stopped.');
- process.exit(0);
- };
+ const handleInterrupt = () => {
+ tui.info('');
+ tui.info('Stream stopped.');
+ interrupted = true;
+ };
@@
- for await (const event of stream) {
- events.push(event);
+ for await (const event of stream) {
+ if (collectEvents) events.push(event);
if (!options.json) {
displayStreamEvent(event, args.name);
}
+ if (interrupted) break;
}
@@
- for await (const event of stream) {
- events.push(event);
+ for await (const event of stream) {
+ if (collectEvents) events.push(event);
if (!options.json) {
displayStreamEvent(event);
}
+ if (interrupted) break;
}🤖 Prompt for AI Agents
In `@packages/cli/src/cmd/cloud/queue/stats.ts` around lines 231 - 275, When
opts.live is true, avoid unbounded buffering by only collecting events into the
events array when options.json is true (move events.push(event) behind a JSON
check) and stop calling process.exit(0) in handleInterrupt; instead set a shared
flag (e.g., let stopped = false and set stopped = true in handleInterrupt) and
break out of the async for-await loops when stopped so
streamQueueAnalytics/streamOrgAnalytics can finish and cleanup; keep
process.on('SIGINT', handleInterrupt) and process.off(...) but let the function
return { type: 'stream', events } (events will be empty unless JSON mode) so
JSON output is produced and long-running live sessions do not grow memory
unbounded.
| if [ -n "$RECEIVED_ID" ]; then | ||
| info "Test: queue ack" | ||
| ACK_OUTPUT=$($CLI cloud queue ack "$QUEUE_NAME" "$RECEIVED_ID" 2>&1) | ||
| ACK_STATUS=$? | ||
| if echo "$ACK_OUTPUT" | grep -qi "acknowledged\|success" || [ "$ACK_STATUS" -eq 0 ]; then | ||
| pass "queue ack succeeds" | ||
| else | ||
| fail "queue ack failed" "$ACK_OUTPUT" | ||
| fi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
head -20 scripts/test-queue.shRepository: agentuity/sdk
Length of output: 647
🏁 Script executed:
sed -n '205,220p' scripts/test-queue.shRepository: agentuity/sdk
Length of output: 535
🏁 Script executed:
sed -n '233,245p' scripts/test-queue.shRepository: agentuity/sdk
Length of output: 480
🏁 Script executed:
sed -n '279,295p' scripts/test-queue.shRepository: agentuity/sdk
Length of output: 642
🏁 Script executed:
bash << 'EOF'
# Test 1: set -e with command substitution in assignment
set -e
echo "Test 1: With set -e, command substitution in assignment"
OUTPUT=$(false 2>&1)
STATUS=$?
echo "STATUS=$STATUS, OUTPUT=$OUTPUT"
echo "Script continued after false command"
EOFRepository: agentuity/sdk
Length of output: 112
🏁 Script executed:
bash << 'EOF'
# Test 2: Verify $? captures the exit code correctly
set -e
echo "Test 2: With set -e, capturing exit status"
OUTPUT=$(exit 42 2>&1) || true
STATUS=$?
echo "STATUS=$STATUS"
echo "Script continued"
EOFRepository: agentuity/sdk
Length of output: 125
🏁 Script executed:
bash << 'EOF'
# Test 3: What happens if we don't use || true?
set -e
echo "Test 3: Without || true after command substitution"
OUTPUT=$(exit 42 2>&1)
STATUS=$?
echo "STATUS=$STATUS"
echo "This line may not execute"
EOFRepository: agentuity/sdk
Length of output: 108
🏁 Script executed:
bash << 'EOF'
# Test 4: Verify the suggested if pattern works
set -e
echo "Test 4: Using if statement pattern"
if OUTPUT=$(exit 42 2>&1); then
STATUS=0
else
STATUS=$?
fi
echo "STATUS=$STATUS, OUTPUT=$OUTPUT"
echo "Script continued successfully"
EOFRepository: agentuity/sdk
Length of output: 140
🏁 Script executed:
bash << 'EOF'
# Test 5: Verify if pattern with successful command
set -e
echo "Test 5: Using if statement pattern with success"
if OUTPUT=$(echo "success" 2>&1); then
STATUS=0
else
STATUS=$?
fi
echo "STATUS=$STATUS, OUTPUT=$OUTPUT"
echo "Script continued successfully"
EOFRepository: agentuity/sdk
Length of output: 159
🏁 Script executed:
sed -n '443,460p' scripts/test-queue.shRepository: agentuity/sdk
Length of output: 699
🏁 Script executed:
sed -n '457,475p' scripts/test-queue.shRepository: agentuity/sdk
Length of output: 723
Wrap CLI calls in if statements to safely capture status under set -e (line 7).
With set -e enabled, a failing command substitution in VAR=$(command); STATUS=$? terminates the script before STATUS=$? executes, preventing error handlers from running. Use the if pattern to capture status safely:
if OUTPUT=$($CLI cloud queue ack "$QUEUE_NAME" "$RECEIVED_ID" 2>&1); then
STATUS=0
else
STATUS=$?
fi
if echo "$OUTPUT" | grep -qi "acknowledged\|success" || [ "$STATUS" -eq 0 ]; then
pass "queue ack succeeds"
else
fail "queue ack failed" "$OUTPUT"
fiApply to: lines 205–213 (ack), 233–240 (nack), 279–287 (destinations delete), 443–451 (dlq purge), 457–467 (delete).
🤖 Prompt for AI Agents
In `@scripts/test-queue.sh` around lines 205 - 213, The ACK block uses command
substitution (ACK_OUTPUT=$($CLI cloud queue ack ...); ACK_STATUS=$?) which can
abort under set -e; change it to the safe if-pattern: run the CLI invocation as
if ACK_OUTPUT=$($CLI cloud queue ack "$QUEUE_NAME" "$RECEIVED_ID" 2>&1); then
set ACK_STATUS=0 in the then branch and ACK_STATUS=$? in the else branch so the
script won't exit before capturing the status, and keep the subsequent
grep/STATUS check unchanged. Apply the same change to the corresponding CLI
invocations: the cloud queue nack block (NACK_OUTPUT/NACK_STATUS), cloud
destinations delete block, cloud queue dlq purge block, and cloud queue delete
block so each uses the if OUTPUT=...; then STATUS=0; else STATUS=$?; fi pattern.
add message queue system with SDK integration
Overview
This PR implements a complete PostgreSQL-based message queue system for the Agentuity platform, providing native queue infrastructure for agent-to-agent communication and background job processing.
Features
Queue Types
FOR UPDATE SKIP LOCKED)Delivery Mechanisms
Additional Features
Changes
Catalyst
internal/queue/- Core queue logic (24 Go files)db/postgres/queries/queue*.sql- SQLC queriesserver/apis/queue/- Versioned API handlersinternal/kv/tenant_migrations.sql- Migration system for existing DBscmd/kv.go- Addedmigratesubcommandserver/apis/billing/- Queue billing integrationSDK
packages/core/src/services/queue.ts- QueueService interfacepackages/server/src/api/queue/- HTTP client librarypackages/runtime/- AgentContext queue integrationpackages/cli/src/cmd/cloud/queue/- 16 CLI subcommandsAPI Endpoints
POST /queue/2026-01-15GET /queue/2026-01-15/:namePOST /queue/messages/publish/2026-01-15/:nameGET /queue/messages/receive/2026-01-15/:namePOST /queue/messages/ack/2026-01-15/:name/:idWS /queue/2026-01-15/:name/wsBilling
Testing
Migration
Existing tenant databases require migration to add queue tables:
Repository-Specific Changes
QueueServiceinterface andQueueStorageServiceimplementation in@agentuity/core@agentuity/server/src/api/queue/with CRUD, messages, DLQ, destinations, and analyticsctx.queue.publish()to AgentContext in@agentuity/runtimefor agent workflowspackages/runtime/src/services/local/queue.tsagentuity queue) with 16 subcommands for queue managementapps/testing/queues/for integration testingSummary by CodeRabbit
New Features
Documentation
Tests
✏️ Tip: You can customize this high-level summary in your review settings.