amalgm 0.1.83 → 0.1.84

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amalgm",
3
- "version": "0.1.83",
3
+ "version": "0.1.84",
4
4
  "description": "Amalgm local computer runtime: login, MCP, chat, events, previews, and tunnels.",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
@@ -3,8 +3,8 @@
3
3
  const crypto = require('crypto');
4
4
 
5
5
  const CONFIG_VERSION = 1;
6
- const DEFAULT_RUNTIME_HOME_LAYOUT = 'legacy';
7
6
  const PER_AGENT_RUNTIME_HOME_LAYOUT = 'per-agent';
7
+ const DEFAULT_RUNTIME_HOME_LAYOUT = PER_AGENT_RUNTIME_HOME_LAYOUT;
8
8
  const {
9
9
  normalizeLoadout,
10
10
  uniqueStrings,
@@ -17,6 +17,8 @@ const {
17
17
  resolveAgent,
18
18
  updateAgent,
19
19
  } = require('../agents/store');
20
+ const agentsRest = require('../agents/rest');
21
+ const { getAgentConfig } = require('../agent-config/store');
20
22
 
21
23
  test.after(() => {
22
24
  closeLocalDb();
@@ -78,3 +80,43 @@ test('custom agent auth and model settings survive normalization', () => {
78
80
  assert.deepEqual(agent.loadout, { toolIds: ['tool.search'] });
79
81
  assert.equal(Object.prototype.hasOwnProperty.call(agent, 'tools'), false);
80
82
  });
83
+
84
+ function captureSendJson() {
85
+ const response = {};
86
+ return {
87
+ response,
88
+ sendJson(status, payload) {
89
+ response.status = status;
90
+ response.payload = payload;
91
+ },
92
+ };
93
+ }
94
+
95
+ test('agents create REST preserves caller id and creates per-agent config', async () => {
96
+ const agentId = 'custom-first-class-ui-agent';
97
+ const { response, sendJson } = captureSendJson();
98
+
99
+ await agentsRest.handleCreate({
100
+ id: agentId,
101
+ name: 'First Class UI Agent',
102
+ baseHarnessId: 'codex',
103
+ baseModelId: 'openai/gpt-5.5',
104
+ authMethod: 'amalgm',
105
+ config: {
106
+ version: 1,
107
+ agentId,
108
+ instructions: 'Use the per-agent home.',
109
+ skills: [],
110
+ loadout: { toolIds: [] },
111
+ hooks: [],
112
+ subagents: [],
113
+ },
114
+ }, sendJson);
115
+
116
+ assert.equal(response.status, 200);
117
+ assert.equal(response.payload.agent.id, agentId);
118
+ assert.equal(resolveAgent(agentId).id, agentId);
119
+ assert.equal(response.payload.agent.config.agentId, agentId);
120
+ assert.equal(response.payload.agent.config.runtimeHomeLayout, 'per-agent');
121
+ assert.equal(getAgentConfig(agentId).runtimeHomeLayout, 'per-agent');
122
+ });
@@ -111,15 +111,13 @@ function providerAuthIdentity(harness) {
111
111
 
112
112
  function runtimeHome({ amalgmDir, harness, agentConfigId, runtimeHomeLayout }) {
113
113
  const harnessHome = path.join(amalgmDir, 'cli-homes', safePathSegment(harness));
114
- if (runtimeHomeLayout === 'per-agent') {
115
- return path.join(
116
- harnessHome,
117
- 'agents',
118
- safePathSegment(agentConfigId || harness),
119
- 'home',
120
- );
121
- }
122
- return path.join(harnessHome, 'home');
114
+ if (runtimeHomeLayout === 'legacy') return path.join(harnessHome, 'home');
115
+ return path.join(
116
+ harnessHome,
117
+ 'agents',
118
+ safePathSegment(agentConfigId || harness),
119
+ 'home',
120
+ );
123
121
  }
124
122
 
125
123
  function providerAuthFingerprint(harness) {
@@ -410,7 +410,7 @@ function createContract(payload, options = {}) {
410
410
  || payloadChatAgent?.customAgentId,
411
411
  ) || harness;
412
412
  const agentConfig = normalizeAgentConfig(payload.agentConfig || { agentId: agentConfigId });
413
- const runtimeHomeLayout = agentConfig.runtimeHomeLayout === 'per-agent' ? 'per-agent' : 'legacy';
413
+ const runtimeHomeLayout = 'per-agent';
414
414
  const auth = authEnvelope({
415
415
  harness,
416
416
  authMethod,
@@ -7,10 +7,11 @@ const path = require('node:path');
7
7
  const test = require('node:test');
8
8
  const { authEnvelope, runtimeEnv } = require('../auth');
9
9
 
10
- test('codex keeps legacy CLI home unless per-agent layout is explicit', () => {
10
+ test('codex uses one stable per-agent CLI home across auth methods', () => {
11
11
  const amalgmDir = fs.mkdtempSync(path.join(os.tmpdir(), 'amalgm-auth-test-'));
12
12
  fs.writeFileSync(path.join(amalgmDir, '.amalgm-credentials'), 'OPENAI_API_KEY=sk-test\n');
13
13
  try {
14
+ const agentConfigId = 'debugging-agent';
14
15
  const first = authEnvelope({
15
16
  harness: 'codex',
16
17
  authMethod: 'amalgm',
@@ -20,6 +21,7 @@ test('codex keeps legacy CLI home unless per-agent layout is explicit', () => {
20
21
  proxyBaseUrl: 'https://proxy.example.test',
21
22
  amalgmDir,
22
23
  userId: 'user-123',
24
+ agentConfigId,
23
25
  });
24
26
  const second = authEnvelope({
25
27
  harness: 'codex',
@@ -30,6 +32,7 @@ test('codex keeps legacy CLI home unless per-agent layout is explicit', () => {
30
32
  proxyBaseUrl: 'https://proxy.example.test',
31
33
  amalgmDir,
32
34
  userId: 'user-123',
35
+ agentConfigId,
33
36
  });
34
37
  const third = authEnvelope({
35
38
  harness: 'codex',
@@ -37,6 +40,7 @@ test('codex keeps legacy CLI home unless per-agent layout is explicit', () => {
37
40
  sessionId: 'session-three',
38
41
  amalgmDir,
39
42
  userId: 'user-123',
43
+ agentConfigId,
40
44
  });
41
45
  const otherAgent = authEnvelope({
42
46
  harness: 'codex',
@@ -49,31 +53,18 @@ test('codex keeps legacy CLI home unless per-agent layout is explicit', () => {
49
53
  userId: 'user-123',
50
54
  agentConfigId: 'agent-two',
51
55
  });
52
- const perAgent = authEnvelope({
53
- harness: 'codex',
54
- authMethod: 'provider_auth',
55
- sessionId: 'session-five',
56
- proxyToken: 'proxy-token-three',
57
- localBaseUrl: 'http://127.0.0.1:8084',
58
- proxyBaseUrl: 'https://proxy.example.test',
59
- amalgmDir,
60
- userId: 'user-123',
61
- agentConfigId: 'agent-two',
62
- runtimeHomeLayout: 'per-agent',
63
- });
64
56
  assert.notEqual(first.tokenFingerprint, second.tokenFingerprint);
65
57
  assert.equal(first.runtimeHome, second.runtimeHome);
66
58
  assert.equal(second.runtimeHome, third.runtimeHome);
67
- assert.equal(first.runtimeHome, path.join(amalgmDir, 'cli-homes', 'codex', 'home'));
68
- assert.equal(otherAgent.runtimeHome, first.runtimeHome);
69
- assert.equal(perAgent.runtimeHome, path.join(amalgmDir, 'cli-homes', 'codex', 'agents', 'agent-two', 'home'));
70
- assert.notEqual(first.runtimeHome, perAgent.runtimeHome);
59
+ assert.equal(first.runtimeHome, path.join(amalgmDir, 'cli-homes', 'codex', 'agents', agentConfigId, 'home'));
60
+ assert.equal(otherAgent.runtimeHome, path.join(amalgmDir, 'cli-homes', 'codex', 'agents', 'agent-two', 'home'));
61
+ assert.notEqual(first.runtimeHome, otherAgent.runtimeHome);
71
62
  } finally {
72
63
  fs.rmSync(amalgmDir, { recursive: true, force: true });
73
64
  }
74
65
  });
75
66
 
76
- test('opencode always uses one canonical CLI home across auth methods and token refreshes', () => {
67
+ test('opencode uses one stable per-agent CLI home across auth methods', () => {
77
68
  const first = authEnvelope({
78
69
  harness: 'opencode',
79
70
  authMethod: 'amalgm',
@@ -98,15 +89,15 @@ test('opencode always uses one canonical CLI home across auth methods and token
98
89
 
99
90
  assert.notEqual(first.tokenFingerprint, second.tokenFingerprint);
100
91
  assert.equal(first.runtimeHome, second.runtimeHome);
101
- assert.equal(first.runtimeHome, '/tmp/amalgm-test/cli-homes/opencode/home');
92
+ assert.equal(first.runtimeHome, '/tmp/amalgm-test/cli-homes/opencode/agents/opencode/home');
102
93
  assert.equal(env.HOME, first.runtimeHome);
103
94
  assert.equal(env.OPENCODE_HOME, first.runtimeHome);
104
95
  assert.equal(env.OPENCODE_CONFIG_DIR, first.runtimeHome);
105
- assert.equal(env.XDG_CONFIG_HOME, '/tmp/amalgm-test/cli-homes/opencode/home/.config');
106
- assert.equal(env.XDG_DATA_HOME, '/tmp/amalgm-test/cli-homes/opencode/home/.local/share');
96
+ assert.equal(env.XDG_CONFIG_HOME, '/tmp/amalgm-test/cli-homes/opencode/agents/opencode/home/.config');
97
+ assert.equal(env.XDG_DATA_HOME, '/tmp/amalgm-test/cli-homes/opencode/agents/opencode/home/.local/share');
107
98
  });
108
99
 
109
- test('claude always uses one canonical CLI home', () => {
100
+ test('claude uses one stable per-agent CLI home', () => {
110
101
  const envelope = authEnvelope({
111
102
  harness: 'claude_code',
112
103
  authMethod: 'provider_auth',
@@ -114,7 +105,7 @@ test('claude always uses one canonical CLI home', () => {
114
105
  amalgmDir: '/tmp/amalgm-test',
115
106
  });
116
107
 
117
- assert.equal(envelope.runtimeHome, '/tmp/amalgm-test/cli-homes/claude_code/home');
108
+ assert.equal(envelope.runtimeHome, '/tmp/amalgm-test/cli-homes/claude_code/agents/claude_code/home');
118
109
 
119
110
  const env = runtimeEnv({
120
111
  harness: 'claude_code',
@@ -146,5 +137,5 @@ test('provided CLI home path cannot move an agent off its canonical home', () =>
146
137
  authProfileId: 'manual-profile',
147
138
  });
148
139
 
149
- assert.equal(envelope.runtimeHome, '/tmp/amalgm-test/cli-homes/codex/home');
140
+ assert.equal(envelope.runtimeHome, '/tmp/amalgm-test/cli-homes/codex/agents/codex/home');
150
141
  });