stagent 0.9.6 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -44
- package/dist/cli.js +66 -18
- package/docs/.coverage-gaps.json +144 -56
- package/docs/.last-generated +1 -1
- package/docs/features/agent-intelligence.md +12 -2
- package/docs/features/chat.md +40 -5
- package/docs/features/cost-usage.md +1 -1
- package/docs/features/documents.md +5 -2
- package/docs/features/inbox-notifications.md +10 -2
- package/docs/features/keyboard-navigation.md +12 -3
- package/docs/features/provider-runtimes.md +20 -2
- package/docs/features/schedules.md +32 -4
- package/docs/features/settings.md +28 -5
- package/docs/features/shared-components.md +7 -3
- package/docs/features/tables.md +11 -2
- package/docs/features/tool-permissions.md +6 -2
- package/docs/features/workflows.md +14 -4
- package/docs/index.md +1 -1
- package/docs/journeys/developer.md +39 -2
- package/docs/journeys/personal-use.md +32 -8
- package/docs/journeys/power-user.md +45 -14
- package/docs/journeys/work-use.md +17 -8
- package/docs/manifest.json +15 -15
- package/docs/superpowers/plans/2026-04-07-instance-bootstrap.md +1691 -0
- package/docs/superpowers/plans/2026-04-08-schedule-orchestration.md +2983 -0
- package/docs/superpowers/plans/2026-04-11-schedule-maxturns-api-control.md +551 -0
- package/docs/superpowers/plans/2026-04-11-task-create-profile-validation.md +864 -0
- package/docs/superpowers/plans/2026-04-11-task-runtime-stagent-mcp-injection.md +739 -0
- package/docs/superpowers/plans/2026-04-14-chat-command-namespace-refactor.md +1390 -0
- package/docs/superpowers/plans/2026-04-14-chat-environment-integration.md +1561 -0
- package/docs/superpowers/plans/2026-04-14-chat-polish-bundle-v1.md +1219 -0
- package/docs/superpowers/plans/2026-04-14-chat-session-persistence-provider-closeout.md +399 -0
- package/docs/superpowers/specs/2026-04-08-chat-sse-resilience-hotfix-design.md +201 -0
- package/docs/superpowers/specs/2026-04-08-schedule-orchestration-design.md +371 -0
- package/docs/superpowers/specs/2026-04-08-swarm-visibility-design.md +213 -0
- package/next.config.mjs +1 -0
- package/package.json +3 -2
- package/src/__tests__/instrumentation-smoke.test.ts +15 -0
- package/src/app/analytics/page.tsx +1 -21
- package/src/app/api/chat/conversations/[id]/messages/route.ts +22 -1
- package/src/app/api/chat/conversations/[id]/skills/__tests__/activate.test.ts +141 -0
- package/src/app/api/chat/conversations/[id]/skills/activate/route.ts +74 -0
- package/src/app/api/chat/conversations/[id]/skills/deactivate/route.ts +33 -0
- package/src/app/api/chat/export/route.ts +52 -0
- package/src/app/api/chat/files/search/route.ts +50 -0
- package/src/app/api/diagnostics/chat-streams/route.ts +65 -0
- package/src/app/api/environment/rescan-if-stale/__tests__/route.test.ts +45 -0
- package/src/app/api/environment/rescan-if-stale/route.ts +23 -0
- package/src/app/api/environment/skills/route.ts +13 -0
- package/src/app/api/instance/config/route.ts +41 -0
- package/src/app/api/instance/init/route.ts +34 -0
- package/src/app/api/instance/upgrade/check/route.ts +26 -0
- package/src/app/api/instance/upgrade/route.ts +96 -0
- package/src/app/api/instance/upgrade/status/route.ts +35 -0
- package/src/app/api/memory/route.ts +0 -11
- package/src/app/api/notifications/route.ts +4 -2
- package/src/app/api/projects/[id]/route.ts +5 -155
- package/src/app/api/projects/__tests__/delete-project.test.ts +10 -19
- package/src/app/api/schedules/[id]/execute/route.ts +111 -0
- package/src/app/api/schedules/[id]/route.ts +9 -1
- package/src/app/api/schedules/__tests__/execute-route.test.ts +118 -0
- package/src/app/api/schedules/route.ts +3 -12
- package/src/app/api/settings/chat/pins/route.ts +94 -0
- package/src/app/api/settings/chat/saved-searches/__tests__/route.test.ts +119 -0
- package/src/app/api/settings/chat/saved-searches/route.ts +79 -0
- package/src/app/api/settings/environment/route.ts +26 -0
- package/src/app/api/settings/openai/login/route.ts +22 -0
- package/src/app/api/settings/openai/logout/route.ts +7 -0
- package/src/app/api/settings/openai/route.ts +21 -1
- package/src/app/api/settings/providers/route.ts +35 -8
- package/src/app/api/tables/[id]/enrich/__tests__/route.test.ts +153 -0
- package/src/app/api/tables/[id]/enrich/plan/route.ts +98 -0
- package/src/app/api/tables/[id]/enrich/route.ts +147 -0
- package/src/app/api/tables/[id]/enrich/runs/route.ts +25 -0
- package/src/app/api/tasks/[id]/execute/route.ts +52 -33
- package/src/app/api/tasks/[id]/respond/route.ts +31 -15
- package/src/app/api/tasks/[id]/resume/route.ts +24 -3
- package/src/app/api/workflows/[id]/resume/route.ts +59 -0
- package/src/app/api/workflows/[id]/status/route.ts +22 -8
- package/src/app/api/workspace/context/route.ts +2 -0
- package/src/app/api/workspace/fix-data-dir/route.ts +81 -0
- package/src/app/chat/page.tsx +11 -0
- package/src/app/documents/page.tsx +4 -1
- package/src/app/inbox/page.tsx +12 -5
- package/src/app/layout.tsx +42 -21
- package/src/app/page.tsx +0 -2
- package/src/app/settings/page.tsx +8 -9
- package/src/components/chat/__tests__/capability-banner.test.tsx +38 -0
- package/src/components/chat/__tests__/chat-session-provider.test.tsx +573 -0
- package/src/components/chat/__tests__/skill-row.test.tsx +91 -0
- package/src/components/chat/capability-banner.tsx +68 -0
- package/src/components/chat/chat-command-popover.tsx +670 -49
- package/src/components/chat/chat-input.tsx +104 -10
- package/src/components/chat/chat-message.tsx +12 -3
- package/src/components/chat/chat-session-provider.tsx +790 -0
- package/src/components/chat/chat-shell.tsx +151 -401
- package/src/components/chat/command-tab-bar.tsx +68 -0
- package/src/components/chat/conversation-template-picker.tsx +421 -0
- package/src/components/chat/help-dialog.tsx +39 -0
- package/src/components/chat/skill-composition-conflict-dialog.tsx +96 -0
- package/src/components/chat/skill-row.tsx +147 -0
- package/src/components/documents/document-browser.tsx +37 -19
- package/src/components/instance/__tests__/instance-section.test.tsx +125 -0
- package/src/components/instance/instance-section.tsx +382 -0
- package/src/components/instance/upgrade-badge.tsx +219 -0
- package/src/components/notifications/__tests__/batch-proposal-review.test.tsx +95 -0
- package/src/components/notifications/__tests__/notification-item.test.tsx +106 -0
- package/src/components/notifications/__tests__/permission-response-actions.test.tsx +70 -0
- package/src/components/notifications/batch-proposal-review.tsx +20 -5
- package/src/components/notifications/inbox-list.tsx +11 -2
- package/src/components/notifications/notification-item.tsx +56 -2
- package/src/components/notifications/pending-approval-host.tsx +56 -37
- package/src/components/notifications/permission-response-actions.tsx +155 -1
- package/src/components/schedules/schedule-create-sheet.tsx +19 -1
- package/src/components/schedules/schedule-edit-sheet.tsx +20 -1
- package/src/components/schedules/schedule-form.tsx +31 -0
- package/src/components/settings/__tests__/providers-runtimes-section.test.tsx +149 -0
- package/src/components/settings/auth-method-selector.tsx +19 -4
- package/src/components/settings/auth-status-badge.tsx +28 -3
- package/src/components/settings/environment-section.tsx +102 -0
- package/src/components/settings/openai-chatgpt-auth-control.tsx +278 -0
- package/src/components/settings/openai-runtime-section.tsx +7 -1
- package/src/components/settings/providers-runtimes-section.tsx +138 -19
- package/src/components/shared/__tests__/filter-hint.test.tsx +40 -0
- package/src/components/shared/__tests__/saved-searches-manager.test.tsx +147 -0
- package/src/components/shared/app-sidebar.tsx +4 -3
- package/src/components/shared/command-palette.tsx +266 -7
- package/src/components/shared/filter-hint.tsx +70 -0
- package/src/components/shared/filter-input.tsx +59 -0
- package/src/components/shared/saved-searches-manager.tsx +199 -0
- package/src/components/shared/theme-toggle.tsx +5 -24
- package/src/components/shared/workspace-indicator.tsx +61 -2
- package/src/components/tables/__tests__/table-enrichment-sheet.test.tsx +130 -0
- package/src/components/tables/table-create-sheet.tsx +4 -0
- package/src/components/tables/table-enrichment-runs.tsx +103 -0
- package/src/components/tables/table-enrichment-sheet.tsx +538 -0
- package/src/components/tables/table-spreadsheet.tsx +29 -5
- package/src/components/tables/table-toolbar.tsx +10 -1
- package/src/components/tasks/kanban-board.tsx +1 -0
- package/src/components/tasks/kanban-column.tsx +53 -14
- package/src/components/tasks/task-bento-grid.tsx +31 -2
- package/src/components/tasks/task-card.tsx +29 -3
- package/src/components/tasks/task-chip-bar.tsx +54 -1
- package/src/components/tasks/task-result-renderer.tsx +1 -1
- package/src/components/workflows/delay-step-body.tsx +109 -0
- package/src/components/workflows/hooks/use-workflow-status.ts +50 -0
- package/src/components/workflows/loop-status-view.tsx +1 -1
- package/src/components/workflows/shared/step-result.tsx +78 -0
- package/src/components/workflows/shared/workflow-header.tsx +141 -0
- package/src/components/workflows/shared/workflow-loading-skeleton.tsx +36 -0
- package/src/components/workflows/swarm-dashboard.tsx +2 -15
- package/src/components/workflows/views/loop-pattern-view.tsx +137 -0
- package/src/components/workflows/views/sequence-pattern-view.tsx +511 -0
- package/src/components/workflows/workflow-form-view.tsx +133 -16
- package/src/components/workflows/workflow-status-view.tsx +30 -740
- package/src/hooks/__tests__/use-chat-autocomplete-tabs.test.ts +47 -0
- package/src/hooks/__tests__/use-saved-searches.test.ts +70 -0
- package/src/hooks/use-active-skills.ts +110 -0
- package/src/hooks/use-chat-autocomplete.ts +120 -7
- package/src/hooks/use-enriched-skills.ts +19 -0
- package/src/hooks/use-pinned-entries.ts +104 -0
- package/src/hooks/use-recent-user-messages.ts +19 -0
- package/src/hooks/use-saved-searches.ts +142 -0
- package/src/instrumentation-node.ts +94 -0
- package/src/instrumentation.ts +4 -48
- package/src/lib/agents/__tests__/claude-agent-sdk-options.test.ts +56 -0
- package/src/lib/agents/__tests__/claude-agent.test.ts +212 -0
- package/src/lib/agents/__tests__/execution-manager.test.ts +1 -27
- package/src/lib/agents/__tests__/failure-reason.test.ts +68 -0
- package/src/lib/agents/__tests__/learned-context.test.ts +0 -11
- package/src/lib/agents/__tests__/learning-session.test.ts +158 -0
- package/src/lib/agents/__tests__/pattern-extractor.test.ts +48 -0
- package/src/lib/agents/__tests__/task-dispatch.test.ts +166 -0
- package/src/lib/agents/__tests__/tool-permissions.test.ts +60 -0
- package/src/lib/agents/claude-agent.ts +217 -21
- package/src/lib/agents/execution-manager.ts +0 -35
- package/src/lib/agents/handoff/bus.ts +2 -2
- package/src/lib/agents/learned-context.ts +0 -12
- package/src/lib/agents/learning-session.ts +18 -5
- package/src/lib/agents/profiles/__tests__/list-fused-profiles.test.ts +110 -0
- package/src/lib/agents/profiles/__tests__/registry.test.ts +53 -4
- package/src/lib/agents/profiles/builtins/upgrade-assistant/SKILL.md +97 -0
- package/src/lib/agents/profiles/builtins/upgrade-assistant/profile.yaml +36 -0
- package/src/lib/agents/profiles/list-fused-profiles.ts +104 -0
- package/src/lib/agents/profiles/registry.ts +18 -0
- package/src/lib/agents/profiles/types.ts +7 -1
- package/src/lib/agents/router.ts +3 -6
- package/src/lib/agents/runtime/__tests__/catalog.test.ts +130 -0
- package/src/lib/agents/runtime/__tests__/execution-target.test.ts +183 -0
- package/src/lib/agents/runtime/__tests__/openai-codex-auth.test.ts +118 -0
- package/src/lib/agents/runtime/anthropic-direct.ts +8 -0
- package/src/lib/agents/runtime/catalog.ts +121 -0
- package/src/lib/agents/runtime/claude-sdk.ts +32 -0
- package/src/lib/agents/runtime/codex-app-server-client.ts +11 -5
- package/src/lib/agents/runtime/execution-target.ts +456 -0
- package/src/lib/agents/runtime/index.ts +4 -0
- package/src/lib/agents/runtime/launch-failure.ts +101 -0
- package/src/lib/agents/runtime/openai-codex-auth.ts +389 -0
- package/src/lib/agents/runtime/openai-codex.ts +64 -60
- package/src/lib/agents/runtime/openai-direct.ts +8 -0
- package/src/lib/agents/runtime/types.ts +8 -0
- package/src/lib/agents/task-dispatch.ts +220 -0
- package/src/lib/agents/tool-permissions.ts +16 -1
- package/src/lib/book/chapter-mapping.ts +11 -0
- package/src/lib/book/content.ts +10 -0
- package/src/lib/chat/__tests__/active-skill-injection.test.ts +261 -0
- package/src/lib/chat/__tests__/active-streams.test.ts +49 -0
- package/src/lib/chat/__tests__/clean-filter-input.test.ts +68 -0
- package/src/lib/chat/__tests__/command-tabs.test.ts +68 -0
- package/src/lib/chat/__tests__/context-builder-files.test.ts +112 -0
- package/src/lib/chat/__tests__/dismissals.test.ts +65 -0
- package/src/lib/chat/__tests__/engine-sdk-options.test.ts +117 -0
- package/src/lib/chat/__tests__/finalize-safety-net.test.ts +139 -0
- package/src/lib/chat/__tests__/reconcile.test.ts +137 -0
- package/src/lib/chat/__tests__/skill-conflict.test.ts +35 -0
- package/src/lib/chat/__tests__/stream-telemetry.test.ts +151 -0
- package/src/lib/chat/__tests__/types.test.ts +28 -0
- package/src/lib/chat/active-skills.ts +31 -0
- package/src/lib/chat/active-streams.ts +27 -0
- package/src/lib/chat/clean-filter-input.ts +30 -0
- package/src/lib/chat/codex-engine.ts +46 -24
- package/src/lib/chat/command-tabs.ts +61 -0
- package/src/lib/chat/context-builder.ts +146 -4
- package/src/lib/chat/dismissals.ts +73 -0
- package/src/lib/chat/engine.ts +159 -18
- package/src/lib/chat/files/__tests__/search.test.ts +135 -0
- package/src/lib/chat/files/expand-mention.ts +76 -0
- package/src/lib/chat/files/search.ts +99 -0
- package/src/lib/chat/reconcile.ts +117 -0
- package/src/lib/chat/skill-composition.ts +210 -0
- package/src/lib/chat/skill-conflict.ts +105 -0
- package/src/lib/chat/stagent-tools.ts +7 -19
- package/src/lib/chat/stream-telemetry.ts +137 -0
- package/src/lib/chat/suggested-prompts.ts +28 -1
- package/src/lib/chat/system-prompt.ts +48 -1
- package/src/lib/chat/tool-catalog.ts +35 -4
- package/src/lib/chat/tools/__tests__/enrich-table-tool.test.ts +127 -0
- package/src/lib/chat/tools/__tests__/profile-tools.test.ts +51 -0
- package/src/lib/chat/tools/__tests__/schedule-tools.test.ts +261 -0
- package/src/lib/chat/tools/__tests__/settings-tools.test.ts +294 -0
- package/src/lib/chat/tools/__tests__/skill-tools.test.ts +474 -0
- package/src/lib/chat/tools/__tests__/task-tools.test.ts +399 -0
- package/src/lib/chat/tools/__tests__/workflow-tools-dedup.test.ts +351 -0
- package/src/lib/chat/tools/blueprint-tools.ts +190 -0
- package/src/lib/chat/tools/document-tools.ts +29 -13
- package/src/lib/chat/tools/helpers.ts +41 -0
- package/src/lib/chat/tools/notification-tools.ts +9 -5
- package/src/lib/chat/tools/profile-tools.ts +120 -23
- package/src/lib/chat/tools/project-tools.ts +33 -0
- package/src/lib/chat/tools/schedule-tools.ts +44 -11
- package/src/lib/chat/tools/skill-tools.ts +183 -0
- package/src/lib/chat/tools/table-tools.ts +71 -0
- package/src/lib/chat/tools/task-tools.ts +89 -21
- package/src/lib/chat/tools/workflow-tools.ts +275 -32
- package/src/lib/chat/types.ts +15 -0
- package/src/lib/constants/settings.ts +10 -18
- package/src/lib/data/__tests__/clear.test.ts +56 -2
- package/src/lib/data/clear.ts +17 -16
- package/src/lib/data/delete-project.ts +171 -0
- package/src/lib/db/__tests__/bootstrap.test.ts +1 -1
- package/src/lib/db/bootstrap.ts +62 -16
- package/src/lib/db/index.ts +5 -0
- package/src/lib/db/migrations/0009_add_app_instances.sql +25 -0
- package/src/lib/db/migrations/0024_add_workflow_resume_at.sql +10 -0
- package/src/lib/db/migrations/0025_drop_app_instances.sql +3 -0
- package/src/lib/db/migrations/0026_drop_license.sql +3 -0
- package/src/lib/db/migrations/meta/_journal.json +21 -0
- package/src/lib/db/schema.ts +94 -23
- package/src/lib/environment/__tests__/auto-promote.test.ts +132 -0
- package/src/lib/environment/__tests__/list-skills-enriched.test.ts +55 -0
- package/src/lib/environment/__tests__/skill-enrichment.test.ts +129 -0
- package/src/lib/environment/__tests__/skill-recommendations.test.ts +87 -0
- package/src/lib/environment/data.ts +9 -0
- package/src/lib/environment/list-skills.ts +176 -0
- package/src/lib/environment/parsers/__tests__/skill.test.ts +54 -0
- package/src/lib/environment/parsers/skill.ts +26 -5
- package/src/lib/environment/profile-generator.ts +54 -0
- package/src/lib/environment/skill-enrichment.ts +106 -0
- package/src/lib/environment/skill-recommendations.ts +66 -0
- package/src/lib/environment/workspace-context.ts +13 -1
- package/src/lib/filters/__tests__/parse.quoted.test.ts +40 -0
- package/src/lib/filters/__tests__/parse.test.ts +135 -0
- package/src/lib/filters/parse.ts +86 -0
- package/src/lib/import/dedup.ts +4 -54
- package/src/lib/instance/__tests__/bootstrap.test.ts +362 -0
- package/src/lib/instance/__tests__/detect.test.ts +115 -0
- package/src/lib/instance/__tests__/fingerprint.test.ts +48 -0
- package/src/lib/instance/__tests__/git-ops.test.ts +95 -0
- package/src/lib/instance/__tests__/settings.test.ts +83 -0
- package/src/lib/instance/__tests__/upgrade-poller.test.ts +181 -0
- package/src/lib/instance/bootstrap.ts +270 -0
- package/src/lib/instance/detect.ts +49 -0
- package/src/lib/instance/fingerprint.ts +76 -0
- package/src/lib/instance/git-ops.ts +95 -0
- package/src/lib/instance/settings.ts +61 -0
- package/src/lib/instance/types.ts +77 -0
- package/src/lib/instance/upgrade-poller.ts +205 -0
- package/src/lib/notifications/__tests__/visibility.test.ts +51 -0
- package/src/lib/notifications/visibility.ts +33 -0
- package/src/lib/schedules/__tests__/collision-check.test.ts +93 -0
- package/src/lib/schedules/__tests__/config.test.ts +62 -0
- package/src/lib/schedules/__tests__/firing-metrics.test.ts +99 -0
- package/src/lib/schedules/__tests__/integration.test.ts +82 -0
- package/src/lib/schedules/__tests__/slot-claim.test.ts +242 -0
- package/src/lib/schedules/__tests__/tick-scheduler.test.ts +102 -0
- package/src/lib/schedules/__tests__/turn-budget.test.ts +228 -0
- package/src/lib/schedules/collision-check.ts +105 -0
- package/src/lib/schedules/config.ts +53 -0
- package/src/lib/schedules/scheduler.ts +236 -17
- package/src/lib/schedules/slot-claim.ts +105 -0
- package/src/lib/settings/__tests__/openai-auth.test.ts +101 -0
- package/src/lib/settings/__tests__/openai-login-manager.test.ts +64 -0
- package/src/lib/settings/__tests__/runtime-setup.test.ts +33 -0
- package/src/lib/settings/openai-auth.ts +105 -10
- package/src/lib/settings/openai-login-manager.ts +260 -0
- package/src/lib/settings/runtime-setup.ts +14 -4
- package/src/lib/tables/__tests__/enrichment-planner.test.ts +124 -0
- package/src/lib/tables/__tests__/enrichment.test.ts +147 -0
- package/src/lib/tables/enrichment-planner.ts +454 -0
- package/src/lib/tables/enrichment.ts +328 -0
- package/src/lib/tables/query-builder.ts +5 -2
- package/src/lib/tables/trigger-evaluator.ts +3 -2
- package/src/lib/theme.ts +71 -0
- package/src/lib/usage/ledger.ts +2 -18
- package/src/lib/util/__tests__/similarity.test.ts +106 -0
- package/src/lib/util/similarity.ts +77 -0
- package/src/lib/utils/format-timestamp.ts +24 -0
- package/src/lib/utils/stagent-paths.ts +12 -0
- package/src/lib/validators/__tests__/blueprint.test.ts +172 -0
- package/src/lib/validators/__tests__/settings.test.ts +10 -0
- package/src/lib/validators/blueprint.ts +70 -9
- package/src/lib/validators/profile.ts +2 -2
- package/src/lib/validators/settings.ts +3 -1
- package/src/lib/workflows/__tests__/delay.test.ts +196 -0
- package/src/lib/workflows/__tests__/engine.test.ts +8 -0
- package/src/lib/workflows/__tests__/loop-executor.test.ts +54 -0
- package/src/lib/workflows/__tests__/post-action.test.ts +108 -0
- package/src/lib/workflows/blueprints/__tests__/render-prompt.test.ts +124 -0
- package/src/lib/workflows/blueprints/instantiator.ts +22 -1
- package/src/lib/workflows/blueprints/render-prompt.ts +71 -0
- package/src/lib/workflows/blueprints/types.ts +16 -2
- package/src/lib/workflows/delay.ts +106 -0
- package/src/lib/workflows/engine.ts +212 -7
- package/src/lib/workflows/loop-executor.ts +349 -24
- package/src/lib/workflows/post-action.ts +91 -0
- package/src/lib/workflows/types.ts +166 -1
- package/src/test/setup.ts +10 -0
- package/src/app/api/license/checkout/route.ts +0 -28
- package/src/app/api/license/portal/route.ts +0 -26
- package/src/app/api/license/route.ts +0 -89
- package/src/app/api/license/usage/route.ts +0 -63
- package/src/app/api/marketplace/browse/route.ts +0 -15
- package/src/app/api/marketplace/import/route.ts +0 -28
- package/src/app/api/marketplace/publish/route.ts +0 -40
- package/src/app/api/onboarding/email/route.ts +0 -53
- package/src/app/api/settings/telemetry/route.ts +0 -14
- package/src/app/api/sync/export/route.ts +0 -54
- package/src/app/api/sync/restore/route.ts +0 -37
- package/src/app/api/sync/sessions/route.ts +0 -24
- package/src/app/auth/callback/route.ts +0 -73
- package/src/app/marketplace/page.tsx +0 -19
- package/src/components/analytics/analytics-gate-card.tsx +0 -101
- package/src/components/marketplace/blueprint-card.tsx +0 -61
- package/src/components/marketplace/marketplace-browser.tsx +0 -131
- package/src/components/onboarding/email-capture-card.tsx +0 -104
- package/src/components/settings/activation-form.tsx +0 -95
- package/src/components/settings/cloud-account-section.tsx +0 -147
- package/src/components/settings/cloud-sync-section.tsx +0 -155
- package/src/components/settings/subscription-section.tsx +0 -410
- package/src/components/settings/telemetry-section.tsx +0 -80
- package/src/components/shared/premium-gate-overlay.tsx +0 -50
- package/src/components/shared/schedule-gate-dialog.tsx +0 -64
- package/src/components/shared/upgrade-banner.tsx +0 -112
- package/src/hooks/use-supabase-auth.ts +0 -79
- package/src/lib/billing/email.ts +0 -54
- package/src/lib/billing/products.ts +0 -80
- package/src/lib/billing/stripe.ts +0 -101
- package/src/lib/cloud/supabase-browser.ts +0 -32
- package/src/lib/cloud/supabase-client.ts +0 -56
- package/src/lib/license/__tests__/features.test.ts +0 -56
- package/src/lib/license/__tests__/key-format.test.ts +0 -88
- package/src/lib/license/__tests__/manager.test.ts +0 -64
- package/src/lib/license/__tests__/tier-limits.test.ts +0 -79
- package/src/lib/license/cloud-validation.ts +0 -60
- package/src/lib/license/features.ts +0 -44
- package/src/lib/license/key-format.ts +0 -101
- package/src/lib/license/limit-check.ts +0 -111
- package/src/lib/license/limit-queries.ts +0 -51
- package/src/lib/license/manager.ts +0 -345
- package/src/lib/license/notifications.ts +0 -59
- package/src/lib/license/tier-limits.ts +0 -71
- package/src/lib/marketplace/marketplace-client.ts +0 -107
- package/src/lib/sync/cloud-sync.ts +0 -235
- package/src/lib/telemetry/conversion-events.ts +0 -71
- package/src/lib/telemetry/queue.ts +0 -122
- package/src/lib/validators/license.ts +0 -33
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import { mkdir, readFile, rm, writeFile } from "fs/promises";
|
|
2
|
+
import { dirname } from "path";
|
|
3
|
+
import { getLaunchCwd } from "@/lib/environment/workspace-context";
|
|
4
|
+
import {
|
|
5
|
+
getOpenAIApiKey,
|
|
6
|
+
getOpenAIAuthSettings,
|
|
7
|
+
updateOpenAIAuthStatus,
|
|
8
|
+
clearOpenAIOAuthStatus,
|
|
9
|
+
updateOpenAIOAuthStatus,
|
|
10
|
+
type OpenAIAccountInfo,
|
|
11
|
+
type OpenAIAuthMode,
|
|
12
|
+
type OpenAIRateLimitInfo,
|
|
13
|
+
type OpenAIRateLimitWindow,
|
|
14
|
+
} from "@/lib/settings/openai-auth";
|
|
15
|
+
import {
|
|
16
|
+
getStagentCodexAuthPath,
|
|
17
|
+
getStagentCodexConfigPath,
|
|
18
|
+
getStagentCodexDir,
|
|
19
|
+
} from "@/lib/utils/stagent-paths";
|
|
20
|
+
import { CodexAppServerClient } from "./codex-app-server-client";
|
|
21
|
+
|
|
22
|
+
const STAGENT_CODEX_CONFIG = `cli_auth_credentials_store = "file"
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
interface AccountReadResult {
|
|
26
|
+
account?: {
|
|
27
|
+
type?: string;
|
|
28
|
+
email?: string | null;
|
|
29
|
+
planType?: string | null;
|
|
30
|
+
} | null;
|
|
31
|
+
requiresOpenaiAuth?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface RateLimitsReadResult {
|
|
35
|
+
rateLimits?: {
|
|
36
|
+
limitId?: string | null;
|
|
37
|
+
limitName?: string | null;
|
|
38
|
+
primary?: RateLimitWindowLike | null;
|
|
39
|
+
secondary?: RateLimitWindowLike | null;
|
|
40
|
+
} | null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface RateLimitWindowLike {
|
|
44
|
+
usedPercent?: unknown;
|
|
45
|
+
windowDurationMins?: unknown;
|
|
46
|
+
resetsAt?: unknown;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function parseRateLimitWindow(
|
|
50
|
+
value: RateLimitWindowLike | null | undefined
|
|
51
|
+
): OpenAIRateLimitWindow | null {
|
|
52
|
+
if (!value || typeof value !== "object") return null;
|
|
53
|
+
return {
|
|
54
|
+
usedPercent:
|
|
55
|
+
typeof value.usedPercent === "number" ? value.usedPercent : null,
|
|
56
|
+
windowDurationMins:
|
|
57
|
+
typeof value.windowDurationMins === "number" ? value.windowDurationMins : null,
|
|
58
|
+
resetsAt: typeof value.resetsAt === "number" ? value.resetsAt : null,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function parseAccountInfo(
|
|
63
|
+
value: AccountReadResult["account"]
|
|
64
|
+
): OpenAIAccountInfo | null {
|
|
65
|
+
if (!value?.type) return null;
|
|
66
|
+
if (
|
|
67
|
+
value.type !== "apiKey" &&
|
|
68
|
+
value.type !== "chatgpt" &&
|
|
69
|
+
value.type !== "chatgptAuthTokens"
|
|
70
|
+
) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
type: value.type,
|
|
76
|
+
email: value.email ?? null,
|
|
77
|
+
planType:
|
|
78
|
+
value.planType && value.planType.toLowerCase() !== "unknown"
|
|
79
|
+
? value.planType
|
|
80
|
+
: null,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function extractPlanTypeFromError(error: unknown): string | null {
|
|
85
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
86
|
+
const match = message.match(/"plan_type"\s*:\s*"([^"]+)"/);
|
|
87
|
+
return match?.[1] ?? null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function decodeBase64Url(value: string): string | null {
|
|
91
|
+
try {
|
|
92
|
+
const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
93
|
+
const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, "=");
|
|
94
|
+
return Buffer.from(padded, "base64").toString("utf8");
|
|
95
|
+
} catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function extractPlanTypeFromIdToken(idToken: string): string | null {
|
|
101
|
+
const [, payload] = idToken.split(".");
|
|
102
|
+
if (!payload) return null;
|
|
103
|
+
|
|
104
|
+
const decoded = decodeBase64Url(payload);
|
|
105
|
+
if (!decoded) return null;
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
const parsed = JSON.parse(decoded) as {
|
|
109
|
+
"https://api.openai.com/auth"?: {
|
|
110
|
+
chatgpt_plan_type?: string | null;
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
return parsed["https://api.openai.com/auth"]?.chatgpt_plan_type ?? null;
|
|
114
|
+
} catch {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function readStagentCodexPlanTypeFromAuthFile(): Promise<string | null> {
|
|
120
|
+
try {
|
|
121
|
+
const raw = await readFile(getStagentCodexAuthPath(), "utf8");
|
|
122
|
+
const parsed = JSON.parse(raw) as {
|
|
123
|
+
tokens?: {
|
|
124
|
+
id_token?: string | null;
|
|
125
|
+
} | null;
|
|
126
|
+
};
|
|
127
|
+
const idToken = parsed.tokens?.id_token;
|
|
128
|
+
return idToken ? extractPlanTypeFromIdToken(idToken) : null;
|
|
129
|
+
} catch {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function ensureCodexHomeConfig() {
|
|
135
|
+
const codexDir = getStagentCodexDir();
|
|
136
|
+
const configPath = getStagentCodexConfigPath();
|
|
137
|
+
|
|
138
|
+
await mkdir(codexDir, { recursive: true });
|
|
139
|
+
await mkdir(dirname(configPath), { recursive: true });
|
|
140
|
+
|
|
141
|
+
let current = "";
|
|
142
|
+
try {
|
|
143
|
+
current = await readFile(configPath, "utf8");
|
|
144
|
+
} catch {
|
|
145
|
+
// File will be created below.
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (current.includes('cli_auth_credentials_store = "file"')) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const next = current.trim().length > 0
|
|
153
|
+
? `${current.trimEnd()}\n\n${STAGENT_CODEX_CONFIG}`
|
|
154
|
+
: STAGENT_CODEX_CONFIG;
|
|
155
|
+
await writeFile(configPath, next, "utf8");
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export async function buildCodexAuthEnv(
|
|
159
|
+
env?: Record<string, string | undefined>
|
|
160
|
+
): Promise<Record<string, string | undefined>> {
|
|
161
|
+
await ensureCodexHomeConfig();
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
...env,
|
|
165
|
+
CODEX_HOME: getStagentCodexDir(),
|
|
166
|
+
OPENAI_API_KEY: env?.OPENAI_API_KEY,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export async function connectStagentCodexClient(options: {
|
|
171
|
+
cwd?: string;
|
|
172
|
+
env?: Record<string, string | undefined>;
|
|
173
|
+
} = {}) {
|
|
174
|
+
const env = await buildCodexAuthEnv(options.env);
|
|
175
|
+
return CodexAppServerClient.connect({
|
|
176
|
+
cwd: options.cwd ?? getLaunchCwd(),
|
|
177
|
+
env,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export async function initializeCodexClient(client: CodexAppServerClient) {
|
|
182
|
+
await client.request("initialize", {
|
|
183
|
+
clientInfo: {
|
|
184
|
+
name: "Stagent",
|
|
185
|
+
version: "0.1.1",
|
|
186
|
+
},
|
|
187
|
+
capabilities: null,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export async function readCodexAuthStateFromClient(
|
|
192
|
+
client: CodexAppServerClient,
|
|
193
|
+
options: { refreshToken?: boolean } = {}
|
|
194
|
+
) {
|
|
195
|
+
const accountResult = (await client.request("account/read", {
|
|
196
|
+
refreshToken: options.refreshToken ?? false,
|
|
197
|
+
})) as AccountReadResult;
|
|
198
|
+
|
|
199
|
+
const account = parseAccountInfo(accountResult.account ?? null);
|
|
200
|
+
if (account?.type === "chatgpt" && !account.planType) {
|
|
201
|
+
account.planType = await readStagentCodexPlanTypeFromAuthFile();
|
|
202
|
+
}
|
|
203
|
+
let rateLimits: OpenAIRateLimitInfo | null = null;
|
|
204
|
+
if (account?.type === "chatgpt") {
|
|
205
|
+
try {
|
|
206
|
+
const rateLimitResult = (await client.request(
|
|
207
|
+
"account/rateLimits/read"
|
|
208
|
+
)) as RateLimitsReadResult;
|
|
209
|
+
const payload = rateLimitResult.rateLimits ?? null;
|
|
210
|
+
if (payload) {
|
|
211
|
+
rateLimits = {
|
|
212
|
+
limitId: payload.limitId ?? null,
|
|
213
|
+
limitName: payload.limitName ?? null,
|
|
214
|
+
primary: parseRateLimitWindow(payload.primary),
|
|
215
|
+
secondary: parseRateLimitWindow(payload.secondary),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
} catch (error) {
|
|
219
|
+
if (!account.planType) {
|
|
220
|
+
account.planType = extractPlanTypeFromError(error);
|
|
221
|
+
}
|
|
222
|
+
rateLimits = null;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const authMode: OpenAIAuthMode =
|
|
227
|
+
account?.type === "apiKey"
|
|
228
|
+
? "apikey"
|
|
229
|
+
: account?.type === "chatgpt"
|
|
230
|
+
? "chatgpt"
|
|
231
|
+
: account?.type === "chatgptAuthTokens"
|
|
232
|
+
? "chatgptAuthTokens"
|
|
233
|
+
: null;
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
connected: account?.type === "chatgpt",
|
|
237
|
+
account,
|
|
238
|
+
rateLimits,
|
|
239
|
+
requiresOpenaiAuth: Boolean(accountResult.requiresOpenaiAuth),
|
|
240
|
+
authMode,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export type ResolvedOpenAICodexAuthContext =
|
|
245
|
+
| {
|
|
246
|
+
method: "api_key";
|
|
247
|
+
apiKeySource: "db" | "env" | "unknown";
|
|
248
|
+
connect: (cwd?: string) => Promise<CodexAppServerClient>;
|
|
249
|
+
}
|
|
250
|
+
| {
|
|
251
|
+
method: "oauth";
|
|
252
|
+
apiKeySource: "oauth";
|
|
253
|
+
connect: (cwd?: string) => Promise<CodexAppServerClient>;
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
export async function resolveOpenAICodexAuthContext(): Promise<ResolvedOpenAICodexAuthContext> {
|
|
257
|
+
const settings = await getOpenAIAuthSettings();
|
|
258
|
+
|
|
259
|
+
if (settings.method === "oauth") {
|
|
260
|
+
if (!settings.oauthConnected) {
|
|
261
|
+
try {
|
|
262
|
+
const state = await readStagentCodexAuthState({ refreshToken: false });
|
|
263
|
+
if (!state.connected) {
|
|
264
|
+
throw new Error("OpenAI ChatGPT sign-in is not configured.");
|
|
265
|
+
}
|
|
266
|
+
} catch {
|
|
267
|
+
throw new Error(
|
|
268
|
+
"OpenAI ChatGPT sign-in is not configured. Sign in from Settings > Providers & Runtimes."
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return {
|
|
274
|
+
method: "oauth",
|
|
275
|
+
apiKeySource: "oauth",
|
|
276
|
+
connect: (cwd?: string) =>
|
|
277
|
+
connectStagentCodexClient({
|
|
278
|
+
cwd,
|
|
279
|
+
env: { OPENAI_API_KEY: undefined },
|
|
280
|
+
}),
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const { apiKey, source } = await getOpenAIApiKey();
|
|
285
|
+
if (!apiKey) {
|
|
286
|
+
throw new Error("OpenAI API key is not configured");
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return {
|
|
290
|
+
method: "api_key",
|
|
291
|
+
apiKeySource: source,
|
|
292
|
+
connect: (cwd?: string) =>
|
|
293
|
+
connectStagentCodexClient({
|
|
294
|
+
cwd,
|
|
295
|
+
env: { OPENAI_API_KEY: apiKey },
|
|
296
|
+
}),
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export async function ensureOpenAICodexClientAuthenticated(
|
|
301
|
+
client: CodexAppServerClient,
|
|
302
|
+
auth: ResolvedOpenAICodexAuthContext
|
|
303
|
+
) {
|
|
304
|
+
await initializeCodexClient(client);
|
|
305
|
+
|
|
306
|
+
if (auth.method === "api_key") {
|
|
307
|
+
const { apiKey } = await getOpenAIApiKey();
|
|
308
|
+
if (!apiKey) {
|
|
309
|
+
throw new Error("OpenAI API key is not configured");
|
|
310
|
+
}
|
|
311
|
+
await client.request("account/login/start", {
|
|
312
|
+
type: "apiKey",
|
|
313
|
+
apiKey,
|
|
314
|
+
});
|
|
315
|
+
await updateOpenAIAuthStatus(auth.apiKeySource);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const state = await readCodexAuthStateFromClient(client, {
|
|
320
|
+
refreshToken: true,
|
|
321
|
+
});
|
|
322
|
+
if (!state.connected || state.account?.type !== "chatgpt") {
|
|
323
|
+
throw new Error(
|
|
324
|
+
"OpenAI ChatGPT sign-in is not configured. Sign in from Settings > Providers & Runtimes."
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
await updateOpenAIOAuthStatus({
|
|
328
|
+
connected: true,
|
|
329
|
+
account: state.account,
|
|
330
|
+
authMode: state.authMode,
|
|
331
|
+
rateLimits: state.rateLimits,
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export async function readStagentCodexAuthState(options: {
|
|
336
|
+
refreshToken?: boolean;
|
|
337
|
+
cwd?: string;
|
|
338
|
+
} = {}) {
|
|
339
|
+
let client: CodexAppServerClient | null = null;
|
|
340
|
+
|
|
341
|
+
try {
|
|
342
|
+
client = await connectStagentCodexClient({ cwd: options.cwd });
|
|
343
|
+
await initializeCodexClient(client);
|
|
344
|
+
|
|
345
|
+
const state = await readCodexAuthStateFromClient(client, {
|
|
346
|
+
refreshToken: options.refreshToken,
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
await updateOpenAIOAuthStatus({
|
|
350
|
+
connected: state.connected,
|
|
351
|
+
account: state.account,
|
|
352
|
+
authMode: state.authMode,
|
|
353
|
+
rateLimits: state.rateLimits,
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
return state;
|
|
357
|
+
} catch (error) {
|
|
358
|
+
await clearOpenAIOAuthStatus();
|
|
359
|
+
throw error;
|
|
360
|
+
} finally {
|
|
361
|
+
if (client) {
|
|
362
|
+
await client.close();
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export async function logoutStagentCodexAuth() {
|
|
368
|
+
let client: CodexAppServerClient | null = null;
|
|
369
|
+
|
|
370
|
+
try {
|
|
371
|
+
client = await connectStagentCodexClient();
|
|
372
|
+
await initializeCodexClient(client);
|
|
373
|
+
await client.request("account/logout");
|
|
374
|
+
} catch {
|
|
375
|
+
// Even if app-server logout fails, clear the isolated credential cache below.
|
|
376
|
+
} finally {
|
|
377
|
+
if (client) {
|
|
378
|
+
await client.close();
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
try {
|
|
383
|
+
await rm(getStagentCodexAuthPath(), { force: true });
|
|
384
|
+
} catch {
|
|
385
|
+
// Ignore cleanup failures.
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
await clearOpenAIOAuthStatus();
|
|
389
|
+
}
|
|
@@ -14,14 +14,21 @@ import {
|
|
|
14
14
|
prepareTaskOutputDirectory,
|
|
15
15
|
scanTaskOutputDocuments,
|
|
16
16
|
} from "@/lib/documents/output-scanner";
|
|
17
|
-
import {
|
|
18
|
-
getOpenAIApiKey,
|
|
19
|
-
updateOpenAIAuthStatus,
|
|
20
|
-
} from "@/lib/settings/openai-auth";
|
|
21
17
|
import { isToolAllowed } from "@/lib/settings/permissions";
|
|
22
18
|
import { getRuntimeCatalogEntry } from "./catalog";
|
|
23
19
|
import { getLaunchCwd } from "@/lib/environment/workspace-context";
|
|
24
20
|
import { CodexAppServerClient } from "./codex-app-server-client";
|
|
21
|
+
import {
|
|
22
|
+
ensureOpenAICodexClientAuthenticated,
|
|
23
|
+
readCodexAuthStateFromClient,
|
|
24
|
+
resolveOpenAICodexAuthContext,
|
|
25
|
+
} from "./openai-codex-auth";
|
|
26
|
+
import {
|
|
27
|
+
classifyTaskFailureReason,
|
|
28
|
+
RetryableRuntimeLaunchError,
|
|
29
|
+
toRetryableRuntimeLaunchError,
|
|
30
|
+
type RuntimeLaunchProgress,
|
|
31
|
+
} from "./launch-failure";
|
|
25
32
|
import type {
|
|
26
33
|
AgentRuntimeAdapter,
|
|
27
34
|
RuntimeConnectionResult,
|
|
@@ -227,6 +234,14 @@ async function finalizeTaskUsage(
|
|
|
227
234
|
startedAt: state.startedAt,
|
|
228
235
|
finishedAt: new Date(),
|
|
229
236
|
});
|
|
237
|
+
|
|
238
|
+
await db
|
|
239
|
+
.update(tasks)
|
|
240
|
+
.set({
|
|
241
|
+
effectiveModelId: state.modelId ?? null,
|
|
242
|
+
updatedAt: new Date(),
|
|
243
|
+
})
|
|
244
|
+
.where(eq(tasks.id, state.taskId));
|
|
230
245
|
}
|
|
231
246
|
|
|
232
247
|
function buildTurnInput(prompt: string) {
|
|
@@ -372,6 +387,7 @@ async function markTaskFailed(taskId: string, title: string, message: string) {
|
|
|
372
387
|
.set({
|
|
373
388
|
status: "failed",
|
|
374
389
|
result: message,
|
|
390
|
+
failureReason: classifyTaskFailureReason(new Error(message)),
|
|
375
391
|
updatedAt: new Date(),
|
|
376
392
|
})
|
|
377
393
|
.where(eq(tasks.id, taskId));
|
|
@@ -585,43 +601,19 @@ async function handleServerRequest(
|
|
|
585
601
|
}
|
|
586
602
|
}
|
|
587
603
|
|
|
588
|
-
async function initializeOpenAIClient(
|
|
589
|
-
client: CodexAppServerClient,
|
|
590
|
-
apiKey: string
|
|
591
|
-
) {
|
|
592
|
-
await client.request("initialize", {
|
|
593
|
-
clientInfo: {
|
|
594
|
-
name: "Stagent",
|
|
595
|
-
version: "0.1.1",
|
|
596
|
-
},
|
|
597
|
-
capabilities: null,
|
|
598
|
-
});
|
|
599
|
-
|
|
600
|
-
await client.request("account/login/start", {
|
|
601
|
-
type: "apiKey",
|
|
602
|
-
apiKey,
|
|
603
|
-
});
|
|
604
|
-
}
|
|
605
|
-
|
|
606
604
|
async function runAssistTurn({
|
|
607
605
|
prompt,
|
|
608
606
|
developerInstructions,
|
|
609
607
|
cwd,
|
|
610
608
|
}: AssistTurnOptions): Promise<{ text: string; usage: UsageSnapshot }> {
|
|
611
|
-
const
|
|
612
|
-
if (!apiKey) {
|
|
613
|
-
throw new Error("OpenAI API key is not configured");
|
|
614
|
-
}
|
|
609
|
+
const auth = await resolveOpenAICodexAuthContext();
|
|
615
610
|
|
|
616
611
|
let client: CodexAppServerClient | null = null;
|
|
617
612
|
let text = "";
|
|
618
613
|
let usage: UsageSnapshot = {};
|
|
619
614
|
|
|
620
615
|
try {
|
|
621
|
-
client = await
|
|
622
|
-
cwd,
|
|
623
|
-
env: { OPENAI_API_KEY: apiKey },
|
|
624
|
-
});
|
|
616
|
+
client = await auth.connect(cwd);
|
|
625
617
|
|
|
626
618
|
client.onNotification = (notification: JsonRpcLikeNotification) => {
|
|
627
619
|
if (notification.method !== "item/agentMessage/delta") return;
|
|
@@ -631,8 +623,7 @@ async function runAssistTurn({
|
|
|
631
623
|
}
|
|
632
624
|
};
|
|
633
625
|
|
|
634
|
-
await
|
|
635
|
-
await updateOpenAIAuthStatus(source);
|
|
626
|
+
await ensureOpenAICodexClientAuthenticated(client, auth);
|
|
636
627
|
|
|
637
628
|
const threadResponse = (await client.request("thread/start", {
|
|
638
629
|
cwd,
|
|
@@ -706,11 +697,7 @@ async function executeOpenAICodexTask(
|
|
|
706
697
|
): Promise<void> {
|
|
707
698
|
const { task, profileId, instructions, prompt, cwd } =
|
|
708
699
|
await resolveTaskExecutionContext(taskId, options);
|
|
709
|
-
const
|
|
710
|
-
|
|
711
|
-
if (!apiKey) {
|
|
712
|
-
throw new Error("OpenAI API key is not configured");
|
|
713
|
-
}
|
|
700
|
+
const auth = await resolveOpenAICodexAuthContext();
|
|
714
701
|
|
|
715
702
|
const abortController = new AbortController();
|
|
716
703
|
let client: CodexAppServerClient | null = null;
|
|
@@ -718,6 +705,7 @@ async function executeOpenAICodexTask(
|
|
|
718
705
|
let turnId: string | null = null;
|
|
719
706
|
let agentOutput = "";
|
|
720
707
|
let settled = false;
|
|
708
|
+
const launchProgress: RuntimeLaunchProgress = {};
|
|
721
709
|
let resolveCompletion: (() => void) | null = null;
|
|
722
710
|
let rejectCompletion: ((error: Error) => void) | null = null;
|
|
723
711
|
const usageState = createTaskUsageState(task, Boolean(task.sessionId));
|
|
@@ -729,12 +717,21 @@ async function executeOpenAICodexTask(
|
|
|
729
717
|
};
|
|
730
718
|
|
|
731
719
|
try {
|
|
732
|
-
client = await
|
|
733
|
-
cwd,
|
|
734
|
-
env: { OPENAI_API_KEY: apiKey },
|
|
735
|
-
});
|
|
720
|
+
client = await auth.connect(cwd);
|
|
736
721
|
|
|
737
722
|
client.onProcessError = (error) => {
|
|
723
|
+
const retryableLaunchError =
|
|
724
|
+
!options.resume
|
|
725
|
+
? toRetryableRuntimeLaunchError({
|
|
726
|
+
runtimeId: "openai-codex-app-server",
|
|
727
|
+
error,
|
|
728
|
+
progress: launchProgress,
|
|
729
|
+
})
|
|
730
|
+
: null;
|
|
731
|
+
if (retryableLaunchError) {
|
|
732
|
+
rejectCompletion?.(retryableLaunchError);
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
738
735
|
if (settled) return;
|
|
739
736
|
void settle(async () => {
|
|
740
737
|
await markTaskFailed(taskId, task.title, error.message);
|
|
@@ -775,6 +772,7 @@ async function executeOpenAICodexTask(
|
|
|
775
772
|
|
|
776
773
|
case "turn/started": {
|
|
777
774
|
turnId = extractTurnId(params);
|
|
775
|
+
launchProgress.hasTurnStarted = true;
|
|
778
776
|
setExecution(taskId, {
|
|
779
777
|
abortController,
|
|
780
778
|
sessionId: threadId,
|
|
@@ -813,6 +811,7 @@ async function executeOpenAICodexTask(
|
|
|
813
811
|
}
|
|
814
812
|
|
|
815
813
|
case "item/commandExecution/outputDelta": {
|
|
814
|
+
launchProgress.hasToolUse = true;
|
|
816
815
|
await insertLog(taskId, "command_output_delta", {
|
|
817
816
|
threadId,
|
|
818
817
|
turnId,
|
|
@@ -834,6 +833,7 @@ async function executeOpenAICodexTask(
|
|
|
834
833
|
|
|
835
834
|
case "turn/completed": {
|
|
836
835
|
const { status, errorMessage } = extractTurnStatus(params);
|
|
836
|
+
launchProgress.hasResult = true;
|
|
837
837
|
|
|
838
838
|
if (status === "completed") {
|
|
839
839
|
const finalResult =
|
|
@@ -879,8 +879,7 @@ async function executeOpenAICodexTask(
|
|
|
879
879
|
};
|
|
880
880
|
});
|
|
881
881
|
|
|
882
|
-
await
|
|
883
|
-
await updateOpenAIAuthStatus(source);
|
|
882
|
+
await ensureOpenAICodexClientAuthenticated(client, auth);
|
|
884
883
|
|
|
885
884
|
if (threadId) {
|
|
886
885
|
await client.request("thread/resume", {
|
|
@@ -949,6 +948,10 @@ async function executeOpenAICodexTask(
|
|
|
949
948
|
return;
|
|
950
949
|
}
|
|
951
950
|
|
|
951
|
+
if (error instanceof RetryableRuntimeLaunchError) {
|
|
952
|
+
throw error;
|
|
953
|
+
}
|
|
954
|
+
|
|
952
955
|
const message = error instanceof Error ? error.message : String(error);
|
|
953
956
|
await settle(async () => {
|
|
954
957
|
await markTaskFailed(taskId, task.title, message);
|
|
@@ -1051,29 +1054,30 @@ async function runOpenAITaskAssist(
|
|
|
1051
1054
|
}
|
|
1052
1055
|
|
|
1053
1056
|
async function testOpenAIConnection(): Promise<RuntimeConnectionResult> {
|
|
1054
|
-
const { apiKey, source } = await getOpenAIApiKey();
|
|
1055
|
-
if (!apiKey) {
|
|
1056
|
-
return {
|
|
1057
|
-
connected: false,
|
|
1058
|
-
apiKeySource: "unknown",
|
|
1059
|
-
error: "OpenAI API key is not configured",
|
|
1060
|
-
};
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
1057
|
let client: CodexAppServerClient | null = null;
|
|
1064
1058
|
try {
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1059
|
+
const auth = await resolveOpenAICodexAuthContext();
|
|
1060
|
+
client = await auth.connect(getLaunchCwd());
|
|
1061
|
+
await ensureOpenAICodexClientAuthenticated(client, auth);
|
|
1062
|
+
const accountState = await readCodexAuthStateFromClient(client, {
|
|
1063
|
+
refreshToken: auth.apiKeySource === "oauth",
|
|
1068
1064
|
});
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1065
|
+
|
|
1066
|
+
return {
|
|
1067
|
+
connected: auth.apiKeySource === "oauth" ? accountState.connected : true,
|
|
1068
|
+
apiKeySource: auth.apiKeySource,
|
|
1069
|
+
account: accountState.account,
|
|
1070
|
+
rateLimits: accountState.rateLimits,
|
|
1071
|
+
authMode: accountState.authMode,
|
|
1072
|
+
};
|
|
1073
1073
|
} catch (error) {
|
|
1074
1074
|
return {
|
|
1075
1075
|
connected: false,
|
|
1076
|
-
apiKeySource:
|
|
1076
|
+
apiKeySource:
|
|
1077
|
+
error instanceof Error &&
|
|
1078
|
+
error.message.includes("ChatGPT sign-in is not configured")
|
|
1079
|
+
? "oauth"
|
|
1080
|
+
: "unknown",
|
|
1077
1081
|
error: error instanceof Error ? error.message : String(error),
|
|
1078
1082
|
};
|
|
1079
1083
|
} finally {
|
|
@@ -396,6 +396,14 @@ async function executeOpenAIDirectTask(taskId: string, isResume = false): Promis
|
|
|
396
396
|
startedAt: usageState.startedAt,
|
|
397
397
|
finishedAt: new Date(),
|
|
398
398
|
});
|
|
399
|
+
|
|
400
|
+
await db
|
|
401
|
+
.update(tasks)
|
|
402
|
+
.set({
|
|
403
|
+
effectiveModelId: result.totalUsage.modelId ?? modelId,
|
|
404
|
+
updatedAt: new Date(),
|
|
405
|
+
})
|
|
406
|
+
.where(eq(tasks.id, taskId));
|
|
399
407
|
} catch (err) {
|
|
400
408
|
if (!abortController.signal.aborted) {
|
|
401
409
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -3,10 +3,18 @@ import type { ProfileTestReport } from "@/lib/agents/profiles/test-types";
|
|
|
3
3
|
import type { RuntimeCapabilities, RuntimeCatalogEntry } from "./catalog";
|
|
4
4
|
import type { TaskAssistResponse } from "./task-assist-types";
|
|
5
5
|
import type { ProfileAssistRequest, ProfileAssistResponse } from "./profile-assist-types";
|
|
6
|
+
import type {
|
|
7
|
+
OpenAIAccountInfo,
|
|
8
|
+
OpenAIAuthMode,
|
|
9
|
+
OpenAIRateLimitInfo,
|
|
10
|
+
} from "@/lib/settings/openai-auth";
|
|
6
11
|
|
|
7
12
|
export interface RuntimeConnectionResult {
|
|
8
13
|
connected: boolean;
|
|
9
14
|
apiKeySource?: ApiKeySource;
|
|
15
|
+
account?: OpenAIAccountInfo | null;
|
|
16
|
+
rateLimits?: OpenAIRateLimitInfo | null;
|
|
17
|
+
authMode?: OpenAIAuthMode;
|
|
10
18
|
error?: string;
|
|
11
19
|
}
|
|
12
20
|
|