- add company existence check on quota-windows route to guard against
sentinel and forged company IDs (was a no-op assertCompanyAccess)
- fix useDateRange minuteTick memo frozen at mount; realign interval to
next calendar minute boundary via setTimeout + intervalRef pattern
- fix midnight timer in Costs.tsx to use stable [] dep and
self-scheduling todayTimerRef to avoid StrictMode double-invoke
- return null for rolling window rows with no DB data instead of
rendering $0.00 / 0 tok false zeros
- fix secondsToWindowLabel to handle windows >168h with actual day count
instead of silently falling back to 7d
- fix byProvider.get(p) non-null assertion to use ?? [] fallback
reads local claude and codex auth files server-side, calls provider
quota apis (anthropic oauth usage, chatgpt wham/usage), and surfaces
live usedPercent per window in ProviderQuotaCard with threshold fill colors
adds a new /usage page that lets board operators see how much each ai
provider is consuming across any date window, with per-model breakdowns,
rolling 5h/24h/7d burn windows, weekly budget bars, and a deficit notch
when projected spend is on track to exceed the monthly budget.
- new GET /companies/:id/costs/by-provider endpoint aggregates cost events
by provider + model with pro-rated billing type splits from heartbeat runs
- new GET /companies/:id/costs/window-spend endpoint returns rolling window
spend (5h, 24h, 7d) per provider with no schema changes
- QuotaBar: reusable boxed-border progress bar with green/yellow/red
threshold fill colors and optional deficit notch
- ProviderQuotaCard: per-provider card showing budget allocation bars,
rolling windows, subscription usage, and model breakdown with token/cost
share overlays
- Usage page: date preset toggles (mtd, 7d, 30d, ytd, all, custom),
provider tabs, 30s polling plus ws invalidation on cost_event
- custom date range blocks queries until both dates are selected and
treats boundaries as local-time (not utc midnight) so full days are
included regardless of timezone
- query key to timestamp is floored to the nearest minute to prevent
cache churn on every 30s refetch tick
Resolve conflicts by keeping the issue-documents work alongside upstream heartbeat-context, worktree branding, and adapter runtime updates.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Add PAPERCLIP_ALLOWED_ATTACHMENT_TYPES env var to configure allowed
MIME types for issue attachments and asset uploads. Supports exact
types (application/pdf) and wildcard patterns (image/*, text/*).
Falls back to the existing image-only defaults when the env var is
unset, preserving backward compatibility.
- Extract shared module `attachment-types.ts` with `isAllowedContentType()`
and `matchesContentType()` (pure, testable)
- Update `routes/issues.ts` and `routes/assets.ts` to use shared module
- Add unit tests for parsing and wildcard matching
Closes#487
The parentId parameter on GET /api/companies/:companyId/issues was
silently ignored — the filter was never extracted from the query string,
never passed to the service layer, and the IssueFilters type did not
include it. All other filters (status, assigneeAgentId, projectId, etc.)
worked correctly.
This caused subtask lookups to return every issue in the company instead
of only children of the specified parent.
Changes:
- Add parentId to IssueFilters interface
- Add eq(issues.parentId, filters.parentId) condition in list()
- Extract parentId from req.query in the route handler
Fixes: LAS-101
Previously, agents were only woken when the assignee changed. Now
also wakes the assigned agent when an issue transitions out of
backlog status (e.g. backlog -> todo).
Fixes#167
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
resolveAdapterConfigForRuntime now returns a secretKeys set tracking
which env vars came from secret_ref bindings. The onAdapterMeta
callback uses this to redact them regardless of key name.
Fixes#234
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 400 response for /issues without companyId, tag issue.updated
activity with source:comment when triggered by a comment, and mark
comment activities with updated:true when field changes accompany them.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a company has "require board approval for new agents" enabled,
hiring an agent creates a pending approval that requires the user
(as a board member) to approve before the agent can start working.
However, the sidebar inbox badge did not include pending approvals
in its count, so there was no visual indicator that action was needed.
Users had no way of knowing an approval was waiting unless they
happened to open the Inbox page manually.
The root cause: the sidebar-badges service correctly included
approvals in the inbox total, but the route handler overwrites
badges.inbox to add alertsCount and staleIssueCount — and in
doing so dropped badges.approvals from the sum.
Add badges.approvals to the inbox count recalculation so that
pending and revision-requested approvals surface in the sidebar
notification badge alongside failed runs, alerts, stale work,
and join requests.
Affected files:
- server/src/routes/sidebar-badges.ts