amicus 1.9.0 → 2.0.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/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +149 -0
- package/README.md +40 -170
- package/bin/amicus.js +14 -20
- package/commands/council.md +3 -1
- package/electron/fold.js +10 -1
- package/electron/ipc-setup.js +10 -15
- package/electron/main.js +21 -16
- package/electron/preload-setup.js +0 -1
- package/electron/setup-ui-council.js +64 -10
- package/electron/setup-ui-styles.js +34 -3
- package/electron/setup-ui.js +44 -12
- package/package.json +2 -5
- package/skills/second-opinion/MODEL-NOTES.md +2 -2
- package/skills/second-opinion/SKILL.md +24 -23
- package/skills/sidecar/SKILL.md +3 -3
- package/src/cli-handlers-council.js +101 -1
- package/src/cli-handlers-doctor.js +7 -0
- package/src/cli-handlers-run.js +4 -4
- package/src/cli-handlers-spend.js +198 -0
- package/src/cli.js +35 -0
- package/src/council/presets-cli.js +141 -0
- package/src/headless.js +146 -38
- package/src/index.js +1 -9
- package/src/mcp-server.js +132 -108
- package/src/mcp-tools.js +27 -3
- package/src/mcp-wait.js +8 -5
- package/src/opencode-client.js +33 -10
- package/src/prompt-builder.js +32 -11
- package/src/session-manager.js +7 -14
- package/src/sidecar/continue.js +12 -5
- package/src/sidecar/conversation-mirror.js +22 -1
- package/src/sidecar/crash-handler.js +2 -1
- package/src/sidecar/fanout-leg.js +12 -3
- package/src/sidecar/fanout.js +27 -10
- package/src/sidecar/interactive-process.js +6 -17
- package/src/sidecar/interactive.js +5 -6
- package/src/sidecar/models.js +33 -4
- package/src/sidecar/progress.js +2 -1
- package/src/sidecar/read.js +4 -6
- package/src/sidecar/resume.js +19 -4
- package/src/sidecar/session-finalize.js +2 -1
- package/src/sidecar/session-utils.js +13 -35
- package/src/sidecar/setup-window.js +2 -3
- package/src/sidecar/start.js +22 -7
- package/src/utils/abort-coordinator.js +57 -7
- package/src/utils/api-key-store.js +2 -13
- package/src/utils/config.js +30 -43
- package/src/utils/council-presets.js +87 -0
- package/src/utils/env-loader.js +1 -2
- package/src/utils/fold-marker.js +79 -0
- package/src/utils/idle-watchdog.js +9 -12
- package/src/utils/lifecycle.js +1 -1
- package/src/utils/mcp-discovery.js +29 -5
- package/src/utils/mcp-self-identity.js +12 -5
- package/src/utils/model-catalog.js +54 -6
- package/src/utils/read-slice.js +73 -0
- package/src/utils/remediation-hints.js +9 -0
- package/src/utils/result-schema.js +8 -2
- package/src/utils/session-abort.js +1 -1
- package/src/utils/session-index-tmp-sweep.js +80 -0
- package/src/utils/session-index.js +4 -5
- package/src/utils/session-path.js +6 -10
- package/src/utils/shared-server.js +7 -5
- package/src/utils/spend-ledger.js +80 -0
- package/src/utils/updater.js +2 -3
- package/src/utils/env-compat.js +0 -38
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// src/utils/spend-ledger.js
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @module spend-ledger
|
|
6
|
+
* Cross-run cost ledger (B24). One JSONL row per completed RUN, appended at
|
|
7
|
+
* the same points the per-run `usage` block is already resolved and written
|
|
8
|
+
* into that run's own `metadata.json` (src/utils/pricing.js resolveUsage):
|
|
9
|
+
* currently `start` (src/sidecar/start.js, mode headless|interactive) and
|
|
10
|
+
* each fanout leg (src/sidecar/fanout-leg.js, mode leg). `continue`/`resume`
|
|
11
|
+
* do NOT currently call resolveUsage at all — no usage block exists at their
|
|
12
|
+
* finalize points to append here either, a pre-existing gap outside this
|
|
13
|
+
* module's scope (see the B24 task report for the inventory).
|
|
14
|
+
* `amicus spend` (src/cli-handlers-spend.js) reads this file to build a
|
|
15
|
+
* cross-run rollup; nothing else consumes it.
|
|
16
|
+
*
|
|
17
|
+
* Precedent: src/council/ledger.js (council-ledger.jsonl). Same tradeoffs:
|
|
18
|
+
* - plain fs.appendFileSync — a torn/lost row on a hard crash mid-write is
|
|
19
|
+
* an acceptable loss for a ledger (best-effort spend visibility, not a
|
|
20
|
+
* billing record of truth — the per-run metadata.json is that).
|
|
21
|
+
* - corrupt/partial lines are skipped on read, never thrown.
|
|
22
|
+
* Departure from council/ledger.js: appendSpend() itself is wrapped so it
|
|
23
|
+
* NEVER throws — a run's usage must never fail the run it's recording. Every
|
|
24
|
+
* call site is expected to call this fire-and-forget with its own try/catch
|
|
25
|
+
* as a second belt (defense in depth), but the ledger guarantees it too.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const fs = require('fs');
|
|
29
|
+
const path = require('path');
|
|
30
|
+
const { getConfigDir } = require('./config');
|
|
31
|
+
const { logger } = require('./logger');
|
|
32
|
+
|
|
33
|
+
const SPEND_LEDGER_SCHEMA_VERSION = 1;
|
|
34
|
+
const SPEND_LEDGER_FILE = 'spend-ledger.jsonl';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Append one row for a completed run. Best-effort: swallows any failure
|
|
38
|
+
* (unwritable config dir, disk full, etc.) and logs at debug — never throws,
|
|
39
|
+
* never rejects, never blocks/fails the run it's recording. A no-op when
|
|
40
|
+
* `usage` is null (nothing priced to record, e.g. an errored run that never
|
|
41
|
+
* reached resolveUsage).
|
|
42
|
+
*
|
|
43
|
+
* @param {object} opts
|
|
44
|
+
* @param {string} opts.taskId
|
|
45
|
+
* @param {string} [opts.waveId] present for a fanout leg
|
|
46
|
+
* @param {string} opts.model resolved model id (or alias, if that's all the caller has)
|
|
47
|
+
* @param {'headless'|'interactive'|'leg'} opts.mode
|
|
48
|
+
* @param {{tokens:object, cost:{amount:number|null,currency:string,source:string}}|null} opts.usage
|
|
49
|
+
* @param {{dir?:string}} [ctx] test seam — dir overrides getConfigDir()
|
|
50
|
+
*/
|
|
51
|
+
function appendSpend({ taskId, waveId, model, mode, usage }, ctx = {}) {
|
|
52
|
+
if (!usage) { return; }
|
|
53
|
+
try {
|
|
54
|
+
const dir = ctx.dir || getConfigDir();
|
|
55
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
56
|
+
const row = {
|
|
57
|
+
schemaVersion: SPEND_LEDGER_SCHEMA_VERSION,
|
|
58
|
+
ts: new Date().toISOString(),
|
|
59
|
+
taskId: taskId || null,
|
|
60
|
+
waveId: waveId || null,
|
|
61
|
+
model: model || null,
|
|
62
|
+
mode: mode || null,
|
|
63
|
+
tokens: usage.tokens || null,
|
|
64
|
+
cost: usage.cost || null,
|
|
65
|
+
};
|
|
66
|
+
fs.appendFileSync(path.join(dir, SPEND_LEDGER_FILE), JSON.stringify(row) + '\n');
|
|
67
|
+
} catch (e) {
|
|
68
|
+
logger.debug('spend-ledger append failed (best-effort, run unaffected)', { taskId, error: e.message });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** @param {string} [dir] @returns {Array<object>} parsed rows; corrupt lines skipped */
|
|
73
|
+
function readSpendRows(dir) {
|
|
74
|
+
const file = path.join(dir || getConfigDir(), SPEND_LEDGER_FILE);
|
|
75
|
+
if (!fs.existsSync(file)) { return []; }
|
|
76
|
+
return fs.readFileSync(file, 'utf-8').split('\n').map(l => l.trim()).filter(Boolean)
|
|
77
|
+
.map(l => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
module.exports = { appendSpend, readSpendRows, SPEND_LEDGER_FILE, SPEND_LEDGER_SCHEMA_VERSION };
|
package/src/utils/updater.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Checks for new versions via update-notifier and performs
|
|
6
6
|
* updates via npm install -g.
|
|
7
7
|
*
|
|
8
|
-
* Supports AMICUS_MOCK_UPDATE
|
|
8
|
+
* Supports AMICUS_MOCK_UPDATE env var for testing:
|
|
9
9
|
* "available" — getUpdateInfo returns fake update
|
|
10
10
|
* "updating" — getUpdateInfo returns fake update
|
|
11
11
|
* "success" — performUpdate resolves immediately
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
const { spawn } = require('child_process');
|
|
16
16
|
const path = require('path');
|
|
17
17
|
const { logger } = require('./logger');
|
|
18
|
-
const { getCompatEnv } = require('./env-compat');
|
|
19
18
|
const { loadUpdateNotifier } = require('./update-notifier-loader');
|
|
20
19
|
|
|
21
20
|
const pkg = require(path.join(__dirname, '..', '..', 'package.json'));
|
|
@@ -31,7 +30,7 @@ let notifier = null;
|
|
|
31
30
|
* @returns {string|null} The mock mode or null
|
|
32
31
|
*/
|
|
33
32
|
function getMockMode() {
|
|
34
|
-
const mode =
|
|
33
|
+
const mode = process.env.AMICUS_MOCK_UPDATE;
|
|
35
34
|
if (mode && MOCK_MODES.includes(mode)) {
|
|
36
35
|
return mode;
|
|
37
36
|
}
|
package/src/utils/env-compat.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Environment-variable compatibility shim (Amicus rebrand).
|
|
3
|
-
*
|
|
4
|
-
* DEPRECATED(amicus-shim): the SIDECAR_* fallbacks exist only for backward
|
|
5
|
-
* compatibility with pre-rebrand setups. Remove in a future revision once users
|
|
6
|
-
* have migrated to the AMICUS_* names. See docs/SHIMS.md.
|
|
7
|
-
*/
|
|
8
|
-
const { logger } = require('./logger');
|
|
9
|
-
|
|
10
|
-
const warned = new Set();
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Read an env var by its canonical AMICUS_<suffix> name, falling back to the
|
|
14
|
-
* legacy SIDECAR_<suffix> name (with a one-time deprecation warning) if unset.
|
|
15
|
-
*
|
|
16
|
-
* @param {string} suffix - e.g. 'CONFIG_DIR' (no AMICUS_/SIDECAR_ prefix)
|
|
17
|
-
* @returns {string|undefined}
|
|
18
|
-
*/
|
|
19
|
-
function getCompatEnv(suffix) {
|
|
20
|
-
const amicusName = `AMICUS_${suffix}`;
|
|
21
|
-
if (process.env[amicusName] !== undefined) {
|
|
22
|
-
return process.env[amicusName];
|
|
23
|
-
}
|
|
24
|
-
const legacyName = `SIDECAR_${suffix}`;
|
|
25
|
-
if (process.env[legacyName] !== undefined) {
|
|
26
|
-
if (!warned.has(legacyName)) {
|
|
27
|
-
warned.add(legacyName);
|
|
28
|
-
logger.warn(
|
|
29
|
-
`${legacyName} is deprecated; use ${amicusName} instead. ` +
|
|
30
|
-
'Support will be removed in a future Amicus release.'
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
return process.env[legacyName];
|
|
34
|
-
}
|
|
35
|
-
return undefined;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
module.exports = { getCompatEnv };
|