@astrale-os/cli 0.6.3-alpha.0 → 0.8.0-alpha.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.
Files changed (126) hide show
  1. package/README.md +3 -2
  2. package/dist/astrale.js +339 -242
  3. package/package.json +2 -3
  4. package/src/commands/__tests__/admin-instance.test.ts +49 -2
  5. package/src/commands/__tests__/domain-install-owned.test.ts +122 -0
  6. package/src/commands/__tests__/help-contract.test.ts +15 -0
  7. package/src/commands/domain/install.ts +15 -4
  8. package/src/commands/instance/active.ts +4 -7
  9. package/src/commands/instance/list.ts +4 -8
  10. package/src/commands/instance/status.ts +14 -9
  11. package/src/commands/instance/use.ts +13 -11
  12. package/src/commands/studio.ts +13 -0
  13. package/src/commands/view.ts +40 -13
  14. package/src/kernel/__tests__/client-owned-lookup.test.ts +51 -0
  15. package/src/kernel/client.ts +47 -20
  16. package/src/kernel/expand.ts +3 -22
  17. package/src/lib/__tests__/instance-candidates.test.ts +94 -19
  18. package/src/lib/__tests__/instance-target.test.ts +9 -1
  19. package/src/lib/__tests__/view-assets.test.ts +114 -0
  20. package/src/lib/__tests__/view-port-allocation.test.ts +82 -0
  21. package/src/lib/admin-instance.ts +27 -2
  22. package/src/lib/instance-candidates.ts +3 -3
  23. package/src/lib/instance-target.ts +1 -0
  24. package/src/lib/instance.ts +0 -11
  25. package/src/lib/login-flow.ts +4 -41
  26. package/src/lib/view/assets.ts +64 -0
  27. package/src/lib/view/port-allocation.ts +19 -0
  28. package/src/lib/view/server.ts +4 -37
  29. package/src/setup/__tests__/instance-step.test.ts +239 -0
  30. package/src/setup/steps/instance.ts +141 -61
  31. package/studio/client/dist/assets/index-BaqEuIQJ.js +109 -0
  32. package/studio/client/dist/assets/index-jRdExahh.css +1 -0
  33. package/studio/client/dist/index.html +2 -2
  34. package/studio/server/agent/ask.test.ts +74 -0
  35. package/studio/server/agent/ask.ts +20 -22
  36. package/studio/server/agent/bridge/client.test.ts +49 -0
  37. package/studio/server/agent/bridge/client.ts +37 -0
  38. package/studio/server/agent/bridge/grant.test.ts +62 -0
  39. package/studio/server/agent/bridge/grant.ts +84 -0
  40. package/studio/server/agent/bridge/routes.test.ts +151 -0
  41. package/studio/server/agent/bridge/routes.ts +148 -0
  42. package/studio/server/agent/{bridge-mcp.ts → bridge/stdio.ts} +27 -21
  43. package/studio/server/agent/conversation.test.ts +84 -0
  44. package/studio/server/agent/conversation.ts +110 -0
  45. package/studio/server/agent/{types.ts → harness/adapter.ts} +63 -12
  46. package/studio/server/agent/harness/claude/adapter.test.ts +338 -0
  47. package/studio/server/agent/harness/claude/adapter.ts +71 -0
  48. package/studio/server/agent/harness/claude/ask.ts +114 -0
  49. package/studio/server/agent/harness/claude/capabilities.ts +27 -0
  50. package/studio/server/agent/harness/claude/command.ts +63 -0
  51. package/studio/server/agent/harness/claude/events.ts +215 -0
  52. package/studio/server/agent/harness/claude/loadout.ts +132 -0
  53. package/studio/server/agent/harness/claude/mcp.test.ts +36 -0
  54. package/studio/server/agent/harness/claude/mcp.ts +41 -0
  55. package/studio/server/agent/harness/claude/skills.ts +43 -0
  56. package/studio/server/agent/harness/codex/adapter.test.ts +269 -0
  57. package/studio/server/agent/harness/codex/adapter.ts +116 -0
  58. package/studio/server/agent/harness/codex/ask.test.ts +176 -0
  59. package/studio/server/agent/harness/codex/ask.ts +181 -0
  60. package/studio/server/agent/harness/codex/command.test.ts +69 -0
  61. package/studio/server/agent/harness/codex/command.ts +43 -0
  62. package/studio/server/agent/harness/codex/events.test.ts +94 -0
  63. package/studio/server/agent/harness/codex/events.ts +132 -0
  64. package/studio/server/agent/harness/codex/exec.ts +113 -0
  65. package/studio/server/agent/harness/codex/loadout.ts +63 -0
  66. package/studio/server/agent/harness/codex/mcp.test.ts +32 -0
  67. package/studio/server/agent/harness/codex/mcp.ts +27 -0
  68. package/studio/server/agent/harness/codex/models.test.ts +167 -0
  69. package/studio/server/agent/harness/codex/models.ts +211 -0
  70. package/studio/server/agent/harness/codex/skills.ts +33 -0
  71. package/studio/server/agent/harness/gateway/config.test.ts +98 -0
  72. package/studio/server/{state/harness-gateway.ts → agent/harness/gateway/config.ts} +30 -8
  73. package/studio/server/agent/harness/gateway/token.test.ts +133 -0
  74. package/studio/server/agent/harness/gateway/token.ts +166 -0
  75. package/studio/server/agent/harness/mock/adapter.ts +190 -0
  76. package/studio/server/agent/harness/mock/domain-edit.ts +41 -0
  77. package/studio/server/agent/harness/process.test.ts +46 -0
  78. package/studio/server/agent/harness/process.ts +104 -0
  79. package/studio/server/agent/harness/registry.ts +37 -0
  80. package/studio/server/agent/harness/selection.test.ts +75 -0
  81. package/studio/server/agent/harness/selection.ts +77 -0
  82. package/studio/server/agent/harness/skills.test.ts +95 -0
  83. package/studio/server/agent/harness/skills.ts +100 -0
  84. package/studio/server/agent/layout.test.ts +95 -0
  85. package/studio/server/agent/notify.ts +12 -0
  86. package/studio/server/agent/{schema-map.ts → prompts/anchors.ts} +2 -2
  87. package/studio/server/agent/prompts/ask.ts +34 -0
  88. package/studio/server/agent/{prompt.ts → prompts/system.ts} +3 -107
  89. package/studio/server/agent/prompts/turn.ts +57 -0
  90. package/studio/server/agent/routes.test.ts +176 -0
  91. package/studio/server/agent/routes.ts +214 -0
  92. package/studio/server/agent/run/completion.ts +193 -0
  93. package/studio/server/agent/run/coordinator.test.ts +337 -0
  94. package/studio/server/agent/run/coordinator.ts +104 -0
  95. package/studio/server/agent/run/live-state.ts +73 -0
  96. package/studio/server/agent/run/preparation.ts +162 -0
  97. package/studio/server/agent/run/transcript.test.ts +47 -0
  98. package/studio/server/agent/run/transcript.ts +30 -0
  99. package/studio/server/agent/run/usage.test.ts +46 -0
  100. package/studio/server/{state → agent/run}/usage.ts +4 -4
  101. package/studio/server/agent/stream.ts +37 -0
  102. package/studio/server/agent/{session-id.ts → telemetry.ts} +1 -1
  103. package/studio/server/api-agent-loadout.test.ts +69 -0
  104. package/studio/server/api.ts +3 -155
  105. package/studio/server/index.ts +6 -5
  106. package/studio/server/state/comments.test.ts +29 -0
  107. package/studio/server/state/comments.ts +3 -3
  108. package/studio/server/state/settings.test.ts +78 -0
  109. package/studio/server/state/settings.ts +28 -1
  110. package/studio/shared/agent-effort.test.ts +12 -0
  111. package/studio/shared/agent-effort.ts +15 -0
  112. package/studio/shared/agent-models.test.ts +9 -0
  113. package/studio/shared/agent-models.ts +14 -0
  114. package/studio/shared/settings-values.test.ts +18 -0
  115. package/studio/shared/settings-values.ts +20 -0
  116. package/studio/shared/types.ts +58 -7
  117. package/src/connect-core.test.ts +0 -42
  118. package/src/connect-core.ts +0 -53
  119. package/studio/client/dist/assets/index-CyN5G8IA.js +0 -109
  120. package/studio/client/dist/assets/index-DKKMHBBC.css +0 -1
  121. package/studio/server/agent/bridge.ts +0 -188
  122. package/studio/server/agent/claude.ts +0 -678
  123. package/studio/server/agent/mock.ts +0 -186
  124. package/studio/server/agent/registry.ts +0 -29
  125. package/studio/server/agent/runner.ts +0 -487
  126. package/studio/server/state/harness-token.ts +0 -0
@@ -0,0 +1,15 @@
1
+ import type { AgentEffort } from './types'
2
+
3
+ /** Map a persisted cross-harness effort onto the selected harness's vocabulary. */
4
+ export function effectiveAgentEffort(
5
+ levels: readonly AgentEffort[],
6
+ requested?: string,
7
+ ): AgentEffort | undefined {
8
+ if (!requested) return undefined
9
+ if (levels.includes(requested as AgentEffort)) return requested as AgentEffort
10
+ if (requested === 'max' && levels.includes('xhigh')) return 'xhigh'
11
+ if (requested === 'ultracode' && levels.includes('xhigh')) return 'xhigh'
12
+ if (requested === 'minimal' && levels.includes('low')) return 'low'
13
+ if (levels.includes('high')) return 'high'
14
+ return levels[0]
15
+ }
@@ -0,0 +1,9 @@
1
+ import { expect, test } from 'bun:test'
2
+
3
+ import { updateAgentModel } from './agent-models'
4
+
5
+ test('switching model overrides preserves independent harness selections', () => {
6
+ const selected = updateAgentModel({ claude: 'opus' }, 'Codex', ' gpt-5.4-mini ')
7
+ expect(selected).toEqual({ claude: 'opus', codex: 'gpt-5.4-mini' })
8
+ expect(updateAgentModel(selected, 'claude', '')).toEqual({ codex: 'gpt-5.4-mini' })
9
+ })
@@ -0,0 +1,14 @@
1
+ /** Update one harness-specific model override without disturbing sibling harnesses. */
2
+ export function updateAgentModel(
3
+ current: Record<string, string>,
4
+ harness: string,
5
+ model: string,
6
+ ): Record<string, string> {
7
+ const key = harness.trim().toLowerCase()
8
+ if (!key) return current
9
+ const next = { ...current }
10
+ const normalized = model.trim()
11
+ if (normalized) next[key] = normalized
12
+ else delete next[key]
13
+ return next
14
+ }
@@ -0,0 +1,18 @@
1
+ import { expect, test } from 'bun:test'
2
+
3
+ import { parseStudioNumericSetting } from './settings-values'
4
+
5
+ test('accepts bounded integer Studio timing settings', () => {
6
+ expect(parseStudioNumericSetting('instancePollMs', '1000')).toBe(1000)
7
+ expect(parseStudioNumericSetting('instancePollMs', 3_600_000)).toBe(3_600_000)
8
+ })
9
+
10
+ test('rejects blank, fractional, non-finite, and out-of-range Studio timing settings', () => {
11
+ expect(parseStudioNumericSetting('instancePollMs', '')).toBeNull()
12
+ expect(parseStudioNumericSetting('instancePollMs', 999)).toBeNull()
13
+ expect(parseStudioNumericSetting('instancePollMs', 1_000.5)).toBeNull()
14
+ expect(parseStudioNumericSetting('instancePollMs', Number.POSITIVE_INFINITY)).toBeNull()
15
+ expect(parseStudioNumericSetting('instancePollMs', 3_600_001)).toBeNull()
16
+ expect(parseStudioNumericSetting('instancePollMs', [1_000])).toBeNull()
17
+ expect(parseStudioNumericSetting('instancePollMs', { valueOf: () => 1_000 })).toBeNull()
18
+ })
@@ -0,0 +1,20 @@
1
+ export const STUDIO_NUMERIC_LIMITS = {
2
+ introspectTimeoutMs: { min: 250, max: 300_000 },
3
+ instancePollMs: { min: 1_000, max: 3_600_000 },
4
+ updatesPollMs: { min: 10_000, max: 86_400_000 },
5
+ viewProbeTimeoutMs: { min: 250, max: 120_000 },
6
+ } as const
7
+
8
+ export type NumericStudioSetting = keyof typeof STUDIO_NUMERIC_LIMITS
9
+
10
+ /** Parse one user/disk numeric setting without permitting destructive intervals. */
11
+ export function parseStudioNumericSetting(
12
+ key: NumericStudioSetting,
13
+ value: unknown,
14
+ ): number | null {
15
+ if (typeof value !== 'number' && typeof value !== 'string') return null
16
+ if (typeof value === 'string' && !value.trim()) return null
17
+ const parsed = typeof value === 'number' ? value : Number(value)
18
+ const { min, max } = STUDIO_NUMERIC_LIMITS[key]
19
+ return Number.isInteger(parsed) && parsed >= min && parsed <= max ? parsed : null
20
+ }
@@ -605,8 +605,18 @@ export interface AgentEvent {
605
605
  commentId?: string
606
606
  }
607
607
 
608
- export const AGENT_EFFORT_LEVELS = ['low', 'medium', 'high', 'xhigh', 'max', 'ultracode'] as const
608
+ export const AGENT_EFFORT_LEVELS = [
609
+ 'minimal',
610
+ 'low',
611
+ 'medium',
612
+ 'high',
613
+ 'xhigh',
614
+ 'max',
615
+ 'ultracode',
616
+ ] as const
609
617
  export type AgentEffort = (typeof AGENT_EFFORT_LEVELS)[number]
618
+ export const AGENT_ACCESS_LEVELS = ['workspace', 'full'] as const
619
+ export type AgentAccess = (typeof AGENT_ACCESS_LEVELS)[number]
610
620
 
611
621
  export interface AgentPromptSnapshot {
612
622
  createdAt: string
@@ -619,8 +629,12 @@ export interface AgentPromptSnapshot {
619
629
  /** true when this turn used a prior harness session id */
620
630
  resumed: boolean
621
631
  sessionId?: string
622
- /** Claude Code --effort value used for this turn. */
632
+ /** Explicit Studio model override used for this turn; absent means harness-native default. */
633
+ model?: string
634
+ /** Harness-native reasoning effort used for this turn. */
623
635
  effort?: AgentEffort
636
+ /** Filesystem/network authority granted to the harness for this turn. */
637
+ access?: AgentAccess
624
638
  /** generated MCP bridge tools exposed to the harness for live write-back */
625
639
  mcpTools: string[]
626
640
  }
@@ -659,7 +673,7 @@ export interface AgentRun {
659
673
  events: AgentEvent[]
660
674
  costUsd?: number
661
675
  numTurns?: number
662
- /** total tokens processed this turn (input + output + cache) */
676
+ /** total token usage reported by the harness for this turn */
663
677
  tokens?: number
664
678
  error?: string
665
679
  /** how many threads the agent answered live via the bridge tools this turn */
@@ -670,8 +684,8 @@ export interface AgentRun {
670
684
  prompt?: AgentPromptSnapshot
671
685
  }
672
686
 
673
- /** The ongoing, resumable conversation for a domain one continuous Claude Code
674
- * session threaded across submits. Survives studio restarts (persisted on disk). */
687
+ /** The ongoing, resumable conversation for a domain and selected harness.
688
+ * Survives studio restarts (persisted on disk). */
675
689
  export interface ConversationInfo {
676
690
  /** a resumable session exists → the next submit continues it (vs. starting fresh) */
677
691
  active: boolean
@@ -688,7 +702,18 @@ export interface AgentSessionInfo {
688
702
  harness?: string
689
703
  }
690
704
 
691
- /** Whether the selected agent harness (e.g. Claude Code) is installed & invokable. */
705
+ export interface HarnessCapabilities {
706
+ effortLevels: readonly AgentEffort[]
707
+ accessLevels: readonly AgentAccess[]
708
+ /** Stable aliases advertised even when the harness has no catalog API. */
709
+ modelOptions?: readonly HarnessModelOption[]
710
+ ask: boolean
711
+ loadout: boolean
712
+ /** Custom model-gateway wire contract this harness can consume in Studio. */
713
+ gateway: 'anthropic' | 'responses' | 'none'
714
+ }
715
+
716
+ /** Whether the selected agent harness is installed, invokable, and configurable. */
692
717
  export interface HarnessStatus {
693
718
  id: string
694
719
  label: string
@@ -700,6 +725,10 @@ export interface HarnessStatus {
700
725
  message: string
701
726
  /** known harnesses for the selector (locked to one for now) */
702
727
  options: { id: string; label: string }[]
728
+ /** an environment/CLI override owns the selection, so the GUI cannot change it */
729
+ locked: boolean
730
+ source: 'environment' | 'domain' | 'default'
731
+ capabilities: HarnessCapabilities
703
732
  }
704
733
 
705
734
  /** One MCP server the harness loaded, with its live connection status. */
@@ -736,7 +765,13 @@ export interface HarnessLoadout {
736
765
  ok: boolean
737
766
  /** reason when !ok (binary missing / probe timed out / no init event) */
738
767
  detail?: string
768
+ /** Model the harness resolves before Studio applies its optional override. */
769
+ nativeModel?: string
739
770
  model?: string
771
+ /** Where the effective model came from. */
772
+ modelSource?: 'studio' | 'config' | 'default' | 'runtime'
773
+ /** Models the harness currently advertises for easy selection. */
774
+ models?: HarnessModelOption[]
740
775
  permissionMode?: string
741
776
  /** how the harness is authed: 'none' | 'ANTHROPIC_API_KEY' | … */
742
777
  apiKeySource?: string
@@ -752,6 +787,18 @@ export interface HarnessLoadout {
752
787
  builtinCommandCount: number
753
788
  /** epoch ms when probed */
754
789
  probedAt: number
790
+ /** Claude exposes a live init event; Codex exposes configured/installed state. */
791
+ source?: 'runtime' | 'configured'
792
+ }
793
+
794
+ export interface HarnessModelOption {
795
+ /** Stable model slug passed to the harness, e.g. `gpt-5.6-sol`. */
796
+ id: string
797
+ /** Human-friendly catalog label. */
798
+ label: string
799
+ description?: string
800
+ /** The harness catalog's built-in default when no config layer overrides it. */
801
+ isDefault?: boolean
755
802
  }
756
803
 
757
804
  /** Domain-attributable agent spend — accumulated from this studio's own runs on
@@ -837,8 +884,12 @@ export type TypeDescriptor =
837
884
  /** Per-domain overrides for values the studio otherwise hard-codes. Stored at
838
885
  * `.domain-studio/settings.json`; missing keys fall back to defaults. */
839
886
  export interface StudioSettings {
840
- /** Claude Code reasoning effort passed as --effort (default 'high') */
887
+ /** Harness-native reasoning effort (default 'high') */
841
888
  agentEffort: AgentEffort
889
+ /** workspace = sandboxed edits; full = unrestricted local automation */
890
+ agentAccess: AgentAccess
891
+ /** Optional model override, independently remembered for each harness id. */
892
+ agentModels: Record<string, string>
842
893
  /** folder under the domain root scanned for integrations (default 'integrations') */
843
894
  integrationsDir: string
844
895
  /** schema/core extraction subprocess timeout in ms (default 20000) */
@@ -1,42 +0,0 @@
1
- import { describe, expect, test } from 'bun:test'
2
-
3
- import * as C from './connect-core'
4
-
5
- // Guards the frozen export surface `@astrale-os/connect-host` borrows. If a
6
- // re-export name drifts (renamed / dropped upstream), this smoke fails loudly
7
- // instead of the connect-host adapter breaking at type-check time in another
8
- // submodule.
9
- describe('connect-core barrel', () => {
10
- test('re-exports every borrowed function', () => {
11
- expect(C.readIdentities).toBeTypeOf('function')
12
- expect(C.getDefault).toBeTypeOf('function')
13
- expect(C.getIdentity).toBeTypeOf('function')
14
- expect(C.signAs).toBeTypeOf('function')
15
- expect(C.listIdentityKeys).toBeTypeOf('function')
16
- expect(C.readInstances).toBeTypeOf('function')
17
- expect(C.resetInstancesMemo).toBeTypeOf('function')
18
- expect(C.resolveInstance).toBeTypeOf('function')
19
- expect(C.normalizeInstanceKernelUrl).toBeTypeOf('function')
20
- expect(C.orgIdForAudience).toBeTypeOf('function')
21
- expect(C.resolveInstanceTarget).toBeTypeOf('function')
22
- expect(C.readConfig).toBeTypeOf('function')
23
- expect(C.resolveCredential).toBeTypeOf('function')
24
- expect(C.fetchWithCaFile).toBeTypeOf('function')
25
- expect(C.createPaths).toBeTypeOf('function')
26
- expect(C.loginViaIdp).toBeTypeOf('function')
27
- expect(C.resolveIdpName).toBeTypeOf('function')
28
- expect(C.readIdpSession).toBeTypeOf('function')
29
- expect(C.isSessionExpired).toBeTypeOf('function')
30
- })
31
-
32
- test('re-exports the error classes', () => {
33
- expect(C.AuthError).toBeTypeOf('function')
34
- expect(C.IdpRefreshTransientError).toBeTypeOf('function')
35
- expect(C.IdpOrgMembershipError).toBeTypeOf('function')
36
- })
37
-
38
- test('re-exports the paths singleton', () => {
39
- expect(C.paths).toBeTypeOf('object')
40
- expect(C.paths.home).toBeTypeOf('string')
41
- })
42
- })
@@ -1,53 +0,0 @@
1
- /**
2
- * connect-core — the frozen, curated re-export surface the connect-host adapter
3
- * borrows from the CLI. It is the ONE public seam between `@astrale-os/cli` and
4
- * `@astrale-os/connect-host`; nothing else in cli/src is contract.
5
- *
6
- * Consumed by `@astrale-os/connect-host`; temps-2 seed of a future
7
- * `@astrale-os/client-core`. Re-export ONLY — no logic lives here.
8
- */
9
-
10
- // ── identity registry (~/.astrale/identities.json) ──────────────────────────
11
- export { readIdentities, getDefault, getIdentity } from './lib/identity'
12
- export type { Identity, IdentityStore } from './lib/identity'
13
-
14
- // ── keypairs on disk + self-grant mint ──────────────────────────────────────
15
- export { signAs, listIdentityKeys } from './lib/keys'
16
-
17
- // ── instance bookmarks (~/.astrale/instances.json) ──────────────────────────
18
- export {
19
- readInstances,
20
- resetInstancesMemo,
21
- resolveInstance,
22
- normalizeInstanceKernelUrl,
23
- orgIdForAudience,
24
- } from './lib/instance'
25
- export type { InstanceEntry, InstanceStore, ResolvedInstance } from './lib/instance'
26
-
27
- // ── instance-target resolution (bookmark | managed | admin | free url) ──────
28
- export { resolveInstanceTarget } from './lib/instance-target'
29
- export type { ResolvedInstanceTarget } from './lib/instance-target'
30
-
31
- // ── global config (~/.astrale/config.json) ──────────────────────────────────
32
- export { readConfig } from './lib/config'
33
- export type { AstraleConfig } from './lib/config'
34
-
35
- // ── credential resolution (self-grant + IdP) ────────────────────────────────
36
- export { resolveCredential, IdpRefreshTransientError, IdpOrgMembershipError } from './kernel/auth'
37
-
38
- // ── IdP login (device flow) — driven by connect-host's `auth.login` op ───────
39
- export { loginViaIdp, resolveIdpName } from './lib/login-flow'
40
- export type { LoginFlowOpts, LoginResult, DeviceVerification } from './lib/login-flow'
41
-
42
- // ── IdP session state (so connect-host can flag "login required" rows) ───────
43
- export { readIdpSession, isSessionExpired } from './lib/idp'
44
- export type { IdpSession } from './lib/idp'
45
-
46
- // ── typed errors ────────────────────────────────────────────────────────────
47
- export { AuthError } from './errors'
48
-
49
- // ── custom-CA fetch (private-CA local stacks) ───────────────────────────────
50
- export { fetchWithCaFile } from './kernel/ca-fetch'
51
-
52
- // ── path resolution (~/.astrale layout) ─────────────────────────────────────
53
- export { paths, createPaths } from './lib/env'