@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.
- package/README.md +3 -2
- package/dist/astrale.js +339 -242
- package/package.json +2 -3
- package/src/commands/__tests__/admin-instance.test.ts +49 -2
- package/src/commands/__tests__/domain-install-owned.test.ts +122 -0
- package/src/commands/__tests__/help-contract.test.ts +15 -0
- package/src/commands/domain/install.ts +15 -4
- package/src/commands/instance/active.ts +4 -7
- package/src/commands/instance/list.ts +4 -8
- package/src/commands/instance/status.ts +14 -9
- package/src/commands/instance/use.ts +13 -11
- package/src/commands/studio.ts +13 -0
- package/src/commands/view.ts +40 -13
- package/src/kernel/__tests__/client-owned-lookup.test.ts +51 -0
- package/src/kernel/client.ts +47 -20
- package/src/kernel/expand.ts +3 -22
- package/src/lib/__tests__/instance-candidates.test.ts +94 -19
- package/src/lib/__tests__/instance-target.test.ts +9 -1
- package/src/lib/__tests__/view-assets.test.ts +114 -0
- package/src/lib/__tests__/view-port-allocation.test.ts +82 -0
- package/src/lib/admin-instance.ts +27 -2
- package/src/lib/instance-candidates.ts +3 -3
- package/src/lib/instance-target.ts +1 -0
- package/src/lib/instance.ts +0 -11
- package/src/lib/login-flow.ts +4 -41
- package/src/lib/view/assets.ts +64 -0
- package/src/lib/view/port-allocation.ts +19 -0
- package/src/lib/view/server.ts +4 -37
- package/src/setup/__tests__/instance-step.test.ts +239 -0
- package/src/setup/steps/instance.ts +141 -61
- package/studio/client/dist/assets/index-BaqEuIQJ.js +109 -0
- package/studio/client/dist/assets/index-jRdExahh.css +1 -0
- package/studio/client/dist/index.html +2 -2
- package/studio/server/agent/ask.test.ts +74 -0
- package/studio/server/agent/ask.ts +20 -22
- package/studio/server/agent/bridge/client.test.ts +49 -0
- package/studio/server/agent/bridge/client.ts +37 -0
- package/studio/server/agent/bridge/grant.test.ts +62 -0
- package/studio/server/agent/bridge/grant.ts +84 -0
- package/studio/server/agent/bridge/routes.test.ts +151 -0
- package/studio/server/agent/bridge/routes.ts +148 -0
- package/studio/server/agent/{bridge-mcp.ts → bridge/stdio.ts} +27 -21
- package/studio/server/agent/conversation.test.ts +84 -0
- package/studio/server/agent/conversation.ts +110 -0
- package/studio/server/agent/{types.ts → harness/adapter.ts} +63 -12
- package/studio/server/agent/harness/claude/adapter.test.ts +338 -0
- package/studio/server/agent/harness/claude/adapter.ts +71 -0
- package/studio/server/agent/harness/claude/ask.ts +114 -0
- package/studio/server/agent/harness/claude/capabilities.ts +27 -0
- package/studio/server/agent/harness/claude/command.ts +63 -0
- package/studio/server/agent/harness/claude/events.ts +215 -0
- package/studio/server/agent/harness/claude/loadout.ts +132 -0
- package/studio/server/agent/harness/claude/mcp.test.ts +36 -0
- package/studio/server/agent/harness/claude/mcp.ts +41 -0
- package/studio/server/agent/harness/claude/skills.ts +43 -0
- package/studio/server/agent/harness/codex/adapter.test.ts +269 -0
- package/studio/server/agent/harness/codex/adapter.ts +116 -0
- package/studio/server/agent/harness/codex/ask.test.ts +176 -0
- package/studio/server/agent/harness/codex/ask.ts +181 -0
- package/studio/server/agent/harness/codex/command.test.ts +69 -0
- package/studio/server/agent/harness/codex/command.ts +43 -0
- package/studio/server/agent/harness/codex/events.test.ts +94 -0
- package/studio/server/agent/harness/codex/events.ts +132 -0
- package/studio/server/agent/harness/codex/exec.ts +113 -0
- package/studio/server/agent/harness/codex/loadout.ts +63 -0
- package/studio/server/agent/harness/codex/mcp.test.ts +32 -0
- package/studio/server/agent/harness/codex/mcp.ts +27 -0
- package/studio/server/agent/harness/codex/models.test.ts +167 -0
- package/studio/server/agent/harness/codex/models.ts +211 -0
- package/studio/server/agent/harness/codex/skills.ts +33 -0
- package/studio/server/agent/harness/gateway/config.test.ts +98 -0
- package/studio/server/{state/harness-gateway.ts → agent/harness/gateway/config.ts} +30 -8
- package/studio/server/agent/harness/gateway/token.test.ts +133 -0
- package/studio/server/agent/harness/gateway/token.ts +166 -0
- package/studio/server/agent/harness/mock/adapter.ts +190 -0
- package/studio/server/agent/harness/mock/domain-edit.ts +41 -0
- package/studio/server/agent/harness/process.test.ts +46 -0
- package/studio/server/agent/harness/process.ts +104 -0
- package/studio/server/agent/harness/registry.ts +37 -0
- package/studio/server/agent/harness/selection.test.ts +75 -0
- package/studio/server/agent/harness/selection.ts +77 -0
- package/studio/server/agent/harness/skills.test.ts +95 -0
- package/studio/server/agent/harness/skills.ts +100 -0
- package/studio/server/agent/layout.test.ts +95 -0
- package/studio/server/agent/notify.ts +12 -0
- package/studio/server/agent/{schema-map.ts → prompts/anchors.ts} +2 -2
- package/studio/server/agent/prompts/ask.ts +34 -0
- package/studio/server/agent/{prompt.ts → prompts/system.ts} +3 -107
- package/studio/server/agent/prompts/turn.ts +57 -0
- package/studio/server/agent/routes.test.ts +176 -0
- package/studio/server/agent/routes.ts +214 -0
- package/studio/server/agent/run/completion.ts +193 -0
- package/studio/server/agent/run/coordinator.test.ts +337 -0
- package/studio/server/agent/run/coordinator.ts +104 -0
- package/studio/server/agent/run/live-state.ts +73 -0
- package/studio/server/agent/run/preparation.ts +162 -0
- package/studio/server/agent/run/transcript.test.ts +47 -0
- package/studio/server/agent/run/transcript.ts +30 -0
- package/studio/server/agent/run/usage.test.ts +46 -0
- package/studio/server/{state → agent/run}/usage.ts +4 -4
- package/studio/server/agent/stream.ts +37 -0
- package/studio/server/agent/{session-id.ts → telemetry.ts} +1 -1
- package/studio/server/api-agent-loadout.test.ts +69 -0
- package/studio/server/api.ts +3 -155
- package/studio/server/index.ts +6 -5
- package/studio/server/state/comments.test.ts +29 -0
- package/studio/server/state/comments.ts +3 -3
- package/studio/server/state/settings.test.ts +78 -0
- package/studio/server/state/settings.ts +28 -1
- package/studio/shared/agent-effort.test.ts +12 -0
- package/studio/shared/agent-effort.ts +15 -0
- package/studio/shared/agent-models.test.ts +9 -0
- package/studio/shared/agent-models.ts +14 -0
- package/studio/shared/settings-values.test.ts +18 -0
- package/studio/shared/settings-values.ts +20 -0
- package/studio/shared/types.ts +58 -7
- package/src/connect-core.test.ts +0 -42
- package/src/connect-core.ts +0 -53
- package/studio/client/dist/assets/index-CyN5G8IA.js +0 -109
- package/studio/client/dist/assets/index-DKKMHBBC.css +0 -1
- package/studio/server/agent/bridge.ts +0 -188
- package/studio/server/agent/claude.ts +0 -678
- package/studio/server/agent/mock.ts +0 -186
- package/studio/server/agent/registry.ts +0 -29
- package/studio/server/agent/runner.ts +0 -487
- package/studio/server/state/harness-token.ts +0 -0
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* agent/mock.ts — a free, deterministic stand-in for a real harness. It exists
|
|
3
|
-
* so the whole live loop (submit → stream events → edit → re-render → reply →
|
|
4
|
-
* merge) can be exercised end-to-end without spending real agent credits.
|
|
5
|
-
*
|
|
6
|
-
* It behaves like a minimal real agent: reads the open threads, makes ONE real
|
|
7
|
-
* edit to the domain schema (so the studio's watch→re-render fires), narrates a
|
|
8
|
-
* few activity events, then returns a final message carrying the same
|
|
9
|
-
* machine-state ```json``` reply block a real agent would emit (so the runner's
|
|
10
|
-
* existing mergeReply path is exercised identically). It writes domain source
|
|
11
|
-
* directly — it is standing in for the external agent actor, not the studio.
|
|
12
|
-
*/
|
|
13
|
-
import { readFileSync, writeFileSync, readdirSync, existsSync } from 'node:fs'
|
|
14
|
-
import { join } from 'node:path'
|
|
15
|
-
|
|
16
|
-
import type { Comment } from '../../shared/types'
|
|
17
|
-
import type { AgentHarness, AgentTurnInput, AgentTurnResult, AskInput, AskResult } from './types'
|
|
18
|
-
|
|
19
|
-
import { readComments } from '../state/comments'
|
|
20
|
-
|
|
21
|
-
function sleep(ms: number, signal: AbortSignal): Promise<void> {
|
|
22
|
-
return new Promise((resolve) => {
|
|
23
|
-
const t = setTimeout(resolve, ms)
|
|
24
|
-
signal.addEventListener(
|
|
25
|
-
'abort',
|
|
26
|
-
() => {
|
|
27
|
-
clearTimeout(t)
|
|
28
|
-
resolve()
|
|
29
|
-
},
|
|
30
|
-
{ once: true },
|
|
31
|
-
)
|
|
32
|
-
})
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/** A camelCase identifier derived from free text, for a synthesized prop name. */
|
|
36
|
-
function identFromText(text: string, fallback: string): string {
|
|
37
|
-
const words = text
|
|
38
|
-
.toLowerCase()
|
|
39
|
-
.replace(/[^a-z0-9 ]+/g, ' ')
|
|
40
|
-
.trim()
|
|
41
|
-
.split(/\s+/)
|
|
42
|
-
.filter(Boolean)
|
|
43
|
-
.slice(0, 3)
|
|
44
|
-
if (words.length === 0) return fallback
|
|
45
|
-
return words.map((w, i) => (i === 0 ? w : w[0].toUpperCase() + w.slice(1))).join('')
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** Insert a new optional prop into the first `props: {` block of the first
|
|
49
|
-
* non-index schema file. Returns the edited file (relative) + prop name. */
|
|
50
|
-
function applyMockEdit(root: string, propName: string): { file: string; prop: string } | null {
|
|
51
|
-
const schemaDir = join(root, 'schema')
|
|
52
|
-
if (!existsSync(schemaDir)) return null
|
|
53
|
-
const files = readdirSync(schemaDir).filter((f) => f.endsWith('.ts') && f !== 'index.ts')
|
|
54
|
-
for (const f of files) {
|
|
55
|
-
const abs = join(schemaDir, f)
|
|
56
|
-
const src = readFileSync(abs, 'utf8')
|
|
57
|
-
const idx = src.indexOf('props: {')
|
|
58
|
-
if (idx < 0) continue
|
|
59
|
-
let prop = propName
|
|
60
|
-
let n = 2
|
|
61
|
-
while (new RegExp(`\\b${prop}\\b\\s*:`).test(src)) prop = `${propName}${n++}`
|
|
62
|
-
const insertAt = idx + 'props: {'.length
|
|
63
|
-
const line = `\n /** Added by the agent in response to a studio comment. */\n ${prop}: z.string().optional(),`
|
|
64
|
-
const next = src.slice(0, insertAt) + line + src.slice(insertAt)
|
|
65
|
-
writeFileSync(abs, next)
|
|
66
|
-
return { file: `schema/${f}`, prop }
|
|
67
|
-
}
|
|
68
|
-
return null
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export class MockHarness implements AgentHarness {
|
|
72
|
-
id = 'mock'
|
|
73
|
-
label = 'Mock agent (free)'
|
|
74
|
-
|
|
75
|
-
async isAvailable(): Promise<boolean> {
|
|
76
|
-
return true
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
async run(input: AgentTurnInput): Promise<AgentTurnResult> {
|
|
80
|
-
const { root, signal, onEvent } = input
|
|
81
|
-
// test knobs (env): MODE=error|noblock|openreply|badblock|resumefail, DELAY_MS=extra latency for cancel/concurrency tests
|
|
82
|
-
const mode = process.env.DOMAIN_STUDIO_MOCK_MODE || 'normal'
|
|
83
|
-
const extraDelay = Number(process.env.DOMAIN_STUDIO_MOCK_DELAY_MS || 0)
|
|
84
|
-
// 'resumefail' rejects a RESUME (only when a sessionId is passed) so the runner's
|
|
85
|
-
// auto-restart-fresh path can be exercised; the fresh retry (no sessionId) proceeds.
|
|
86
|
-
if (mode === 'resumefail' && input.sessionId) {
|
|
87
|
-
onEvent({ kind: 'status', text: 'resuming…' })
|
|
88
|
-
return {
|
|
89
|
-
sessionId: input.sessionId,
|
|
90
|
-
finalText: '',
|
|
91
|
-
isError: true,
|
|
92
|
-
errorMessage: 'mock: no conversation found with session id',
|
|
93
|
-
resumeRejected: true,
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
const store = readComments(root)
|
|
97
|
-
const open = store.comments.filter(
|
|
98
|
-
(c) => c.status === 'open' && c.thread.at(-1)?.role !== 'author',
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
onEvent({ kind: 'status', text: 'session started' })
|
|
102
|
-
await sleep(250, signal)
|
|
103
|
-
if (extraDelay > 0) await sleep(extraDelay, signal)
|
|
104
|
-
if (mode === 'error') throw new Error('mock harness failure (test)')
|
|
105
|
-
onEvent({
|
|
106
|
-
kind: 'thinking',
|
|
107
|
-
text: `Reviewing ${open.length} open thread(s) and the current schema.`,
|
|
108
|
-
})
|
|
109
|
-
await sleep(300, signal)
|
|
110
|
-
onEvent({ kind: 'tool', text: 'Read', tool: 'Read', target: '.domain-studio/comments.json' })
|
|
111
|
-
await sleep(250, signal)
|
|
112
|
-
|
|
113
|
-
// one real edit so the studio re-renders
|
|
114
|
-
const seed = open[0]?.thread.at(-1)?.text ?? 'note'
|
|
115
|
-
const editRes = signal.aborted ? null : applyMockEdit(root, identFromText(seed, 'agentNote'))
|
|
116
|
-
if (editRes) {
|
|
117
|
-
onEvent({ kind: 'tool', text: 'Edit', tool: 'Edit', target: editRes.file })
|
|
118
|
-
await sleep(300, signal)
|
|
119
|
-
}
|
|
120
|
-
onEvent({
|
|
121
|
-
kind: 'message',
|
|
122
|
-
text: editRes
|
|
123
|
-
? `Added a \`${editRes.prop}\` property to \`${editRes.file}\` and answered the open threads.`
|
|
124
|
-
: 'Answered the open threads.',
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
// final machine-state block (identical shape to a real agent reply)
|
|
128
|
-
const replied: Comment[] = open.map((c) => ({
|
|
129
|
-
...c,
|
|
130
|
-
status: 'closed',
|
|
131
|
-
thread: [
|
|
132
|
-
...c.thread,
|
|
133
|
-
{
|
|
134
|
-
id: crypto.randomUUID(),
|
|
135
|
-
role: 'author' as const,
|
|
136
|
-
type: 'text' as const,
|
|
137
|
-
text: editRes
|
|
138
|
-
? `Done — implemented this by adding \`${editRes.prop}\` to \`${editRes.file}\`. (mock agent)`
|
|
139
|
-
: 'Acknowledged. (mock agent)',
|
|
140
|
-
},
|
|
141
|
-
],
|
|
142
|
-
}))
|
|
143
|
-
const machine = {
|
|
144
|
-
schemaVersion: store.schemaVersion,
|
|
145
|
-
// 'openreply' leaves threads OPEN with an author entry (clarifying-question loop test)
|
|
146
|
-
comments: replied.map((c) => ({
|
|
147
|
-
id: c.id,
|
|
148
|
-
anchors: c.anchors,
|
|
149
|
-
status: mode === 'openreply' ? 'open' : c.status,
|
|
150
|
-
thread: c.thread,
|
|
151
|
-
})),
|
|
152
|
-
}
|
|
153
|
-
const finalText =
|
|
154
|
-
mode === 'noblock'
|
|
155
|
-
? 'I reviewed the open threads and made the edit. (no machine-state block — resilience test)'
|
|
156
|
-
: mode === 'badblock'
|
|
157
|
-
? 'I made the edit.\n\n```json\n{ this is : not valid json, ]\n```\n' // malformed-block resilience test
|
|
158
|
-
: `I reviewed the open threads and made the edit.\n\n\`\`\`json\n${JSON.stringify(machine, null, 2)}\n\`\`\`\n`
|
|
159
|
-
|
|
160
|
-
return {
|
|
161
|
-
sessionId: input.sessionId ?? 'mock-session',
|
|
162
|
-
finalText,
|
|
163
|
-
costUsd: 0,
|
|
164
|
-
numTurns: 1,
|
|
165
|
-
isError: false,
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/** Fake streamed answer for a side-question (free plumbing test of the Ask loop). */
|
|
170
|
-
async ask(input: AskInput): Promise<AskResult> {
|
|
171
|
-
const forked = input.sessionId ? `(forked from ${input.sessionId.slice(0, 8)}…) ` : '(fresh) '
|
|
172
|
-
const parts = [
|
|
173
|
-
forked,
|
|
174
|
-
'This is a mock answer to your side question. ',
|
|
175
|
-
'In a real run, Haiku would answer here from the inherited conversation context.',
|
|
176
|
-
]
|
|
177
|
-
let text = ''
|
|
178
|
-
for (const p of parts) {
|
|
179
|
-
if (input.signal.aborted) break
|
|
180
|
-
text += p
|
|
181
|
-
input.onDelta(p)
|
|
182
|
-
await sleep(180, input.signal)
|
|
183
|
-
}
|
|
184
|
-
return { text, isError: false }
|
|
185
|
-
}
|
|
186
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { AgentHarness } from './types'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* agent/registry.ts — picks the active harness. Default is Claude Code; set
|
|
5
|
-
* DOMAIN_STUDIO_HARNESS=mock for the free deterministic stand-in (tests/dev),
|
|
6
|
-
* or =claude explicitly. New harnesses (codex, …) register here.
|
|
7
|
-
*/
|
|
8
|
-
import { ClaudeCodeHarness } from './claude'
|
|
9
|
-
import { MockHarness } from './mock'
|
|
10
|
-
|
|
11
|
-
const harnesses: Record<string, () => AgentHarness> = {
|
|
12
|
-
claude: () => new ClaudeCodeHarness(),
|
|
13
|
-
mock: () => new MockHarness(),
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
let active: AgentHarness | null = null
|
|
17
|
-
|
|
18
|
-
export function getHarness(): AgentHarness {
|
|
19
|
-
if (active) return active
|
|
20
|
-
const want = (process.env.DOMAIN_STUDIO_HARNESS || 'claude').toLowerCase()
|
|
21
|
-
const make = harnesses[want] ?? harnesses.claude
|
|
22
|
-
active = make()
|
|
23
|
-
return active
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** All registered harnesses (id + label) — for the (currently locked) UI selector. */
|
|
27
|
-
export function listHarnesses(): { id: string; label: string }[] {
|
|
28
|
-
return Object.entries(harnesses).map(([id, make]) => ({ id, label: make().label }))
|
|
29
|
-
}
|