@stackmemoryai/stackmemory 1.5.4 → 1.5.6
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/dist/src/cli/commands/orchestrate.js +396 -0
- package/dist/src/cli/commands/orchestrator.js +293 -6
- package/package.json +2 -1
- package/scripts/gepa/.before-optimize.md +110 -142
- package/scripts/gepa/generations/gen-000/baseline.md +111 -147
- package/scripts/gepa/generations/gen-001/baseline.md +111 -147
- package/scripts/gepa/generations/gen-001/variant-a.md +1 -176
- package/scripts/gepa/generations/gen-001/variant-b.md +1 -280
- package/scripts/gepa/generations/gen-001/variant-c.md +1 -122
- package/scripts/gepa/generations/gen-001/variant-d.md +1 -138
- package/scripts/gepa/results/eval-1-baseline.json +80 -80
- package/scripts/gepa/results/eval-1-variant-a.json +65 -65
- package/scripts/gepa/results/eval-1-variant-b.json +78 -78
- package/scripts/gepa/results/eval-1-variant-c.json +69 -69
- package/scripts/gepa/results/eval-1-variant-d.json +71 -71
- package/scripts/gepa/state.json +6 -22
|
@@ -8,10 +8,13 @@ import {
|
|
|
8
8
|
mkdirSync,
|
|
9
9
|
rmSync,
|
|
10
10
|
writeFileSync,
|
|
11
|
+
readdirSync,
|
|
11
12
|
createWriteStream
|
|
12
13
|
} from "fs";
|
|
13
14
|
import { join, dirname } from "path";
|
|
14
15
|
import { tmpdir, homedir } from "os";
|
|
16
|
+
import { createReadStream } from "fs";
|
|
17
|
+
import { createInterface } from "readline";
|
|
15
18
|
import { fileURLToPath } from "url";
|
|
16
19
|
import { Transform } from "stream";
|
|
17
20
|
import { logger } from "../../core/monitoring/logger.js";
|
|
@@ -128,7 +131,7 @@ const DEFAULT_CONFIG = {
|
|
|
128
131
|
inProgressState: "In Progress",
|
|
129
132
|
inReviewState: "In Review",
|
|
130
133
|
pollIntervalMs: 3e4,
|
|
131
|
-
maxConcurrent:
|
|
134
|
+
maxConcurrent: 5,
|
|
132
135
|
workspaceRoot: join(tmpdir(), "conductor_workspaces"),
|
|
133
136
|
repoRoot: process.cwd(),
|
|
134
137
|
baseBranch: "main",
|
|
@@ -158,6 +161,23 @@ class Conductor {
|
|
|
158
161
|
stateCache = /* @__PURE__ */ new Map();
|
|
159
162
|
activeStatesLower;
|
|
160
163
|
terminalStatesLower;
|
|
164
|
+
/** Global rate limit backoff state */
|
|
165
|
+
rateLimit = {
|
|
166
|
+
inBackoff: false,
|
|
167
|
+
backoffUntil: 0,
|
|
168
|
+
totalHits: 0,
|
|
169
|
+
consecutiveHits: 0,
|
|
170
|
+
lastHitAt: 0
|
|
171
|
+
};
|
|
172
|
+
/** Aggregated usage stats */
|
|
173
|
+
usage = {
|
|
174
|
+
inputTokens: 0,
|
|
175
|
+
outputTokens: 0,
|
|
176
|
+
cacheCreationTokens: 0,
|
|
177
|
+
cacheReadTokens: 0,
|
|
178
|
+
estimatedMessages: 0,
|
|
179
|
+
perAgent: /* @__PURE__ */ new Map()
|
|
180
|
+
};
|
|
161
181
|
constructor(config = {}) {
|
|
162
182
|
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
163
183
|
this.activeStatesLower = this.config.activeStates.map(
|
|
@@ -294,7 +314,9 @@ class Conductor {
|
|
|
294
314
|
failed: this.failCount,
|
|
295
315
|
totalAttempts: this.totalAttempts,
|
|
296
316
|
uptime: Date.now() - this.startedAt,
|
|
297
|
-
issues
|
|
317
|
+
issues,
|
|
318
|
+
rateLimit: { ...this.rateLimit },
|
|
319
|
+
usage: { ...this.usage, perAgent: new Map(this.usage.perAgent) }
|
|
298
320
|
};
|
|
299
321
|
}
|
|
300
322
|
// ── Status File ──
|
|
@@ -324,7 +346,31 @@ class Conductor {
|
|
|
324
346
|
failed: this.failCount,
|
|
325
347
|
totalAttempts: this.totalAttempts,
|
|
326
348
|
maxConcurrent: this.config.maxConcurrent,
|
|
327
|
-
stopping: this.stopping
|
|
349
|
+
stopping: this.stopping,
|
|
350
|
+
rateLimit: {
|
|
351
|
+
inBackoff: this.rateLimit.inBackoff,
|
|
352
|
+
backoffUntil: this.rateLimit.backoffUntil,
|
|
353
|
+
backoffRemainingSec: this.rateLimit.inBackoff ? Math.max(
|
|
354
|
+
0,
|
|
355
|
+
Math.ceil((this.rateLimit.backoffUntil - Date.now()) / 1e3)
|
|
356
|
+
) : 0,
|
|
357
|
+
totalHits: this.rateLimit.totalHits
|
|
358
|
+
},
|
|
359
|
+
usage: (() => {
|
|
360
|
+
const summary = this.getUsageSummary();
|
|
361
|
+
return {
|
|
362
|
+
inputTokens: summary.inputTokens,
|
|
363
|
+
outputTokens: summary.outputTokens,
|
|
364
|
+
totalTokens: summary.totalTokens,
|
|
365
|
+
estimatedMessages: summary.estimatedMessages,
|
|
366
|
+
tokensPerMin: summary.tokensPerMin,
|
|
367
|
+
budgetPct5x: summary.budgetPct5x,
|
|
368
|
+
budgetPct20x: summary.budgetPct20x,
|
|
369
|
+
minutesRemaining5x: summary.minutesRemaining5x,
|
|
370
|
+
minutesRemaining20x: summary.minutesRemaining20x,
|
|
371
|
+
cacheHitRate: summary.cacheHitRate
|
|
372
|
+
};
|
|
373
|
+
})()
|
|
328
374
|
};
|
|
329
375
|
try {
|
|
330
376
|
writeFileSync(
|
|
@@ -360,7 +406,8 @@ class Conductor {
|
|
|
360
406
|
phase: run.phase,
|
|
361
407
|
filesModified: run.filesModified,
|
|
362
408
|
toolCalls: run.toolCalls,
|
|
363
|
-
tokensUsed: run.tokensUsed
|
|
409
|
+
tokensUsed: run.tokensUsed,
|
|
410
|
+
workspacePath: run.workspacePath || void 0
|
|
364
411
|
};
|
|
365
412
|
writeFileSync(join(dir, "status.json"), JSON.stringify(status, null, 2));
|
|
366
413
|
} catch {
|
|
@@ -390,6 +437,21 @@ class Conductor {
|
|
|
390
437
|
}
|
|
391
438
|
async poll() {
|
|
392
439
|
if (!this.client || this.stopping) return;
|
|
440
|
+
if (this.rateLimit.inBackoff) {
|
|
441
|
+
const remaining = this.rateLimit.backoffUntil - Date.now();
|
|
442
|
+
if (remaining > 0) {
|
|
443
|
+
logger.info("Rate limit backoff active, skipping poll", {
|
|
444
|
+
remainingMs: remaining,
|
|
445
|
+
remainingSec: Math.ceil(remaining / 1e3),
|
|
446
|
+
totalHits: this.rateLimit.totalHits
|
|
447
|
+
});
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
this.rateLimit.inBackoff = false;
|
|
451
|
+
this.rateLimit.backoffUntil = 0;
|
|
452
|
+
logger.info("Rate limit backoff expired, resuming dispatch");
|
|
453
|
+
console.log("[rate-limit] Backoff expired, resuming dispatch");
|
|
454
|
+
}
|
|
393
455
|
await this.reconcile();
|
|
394
456
|
const available = this.config.maxConcurrent - this.running.size;
|
|
395
457
|
if (available <= 0) {
|
|
@@ -407,6 +469,29 @@ class Conductor {
|
|
|
407
469
|
if (eligible.length === 0) return;
|
|
408
470
|
const sorted = eligible.sort((a, b) => (a.priority || 4) - (b.priority || 4)).slice(0, available);
|
|
409
471
|
const toDispatch = this.preflightFilter(sorted);
|
|
472
|
+
if (this.usage.inputTokens > 0) {
|
|
473
|
+
const summary = this.getUsageSummary();
|
|
474
|
+
logger.info("Usage summary", {
|
|
475
|
+
totalTokens: summary.totalTokens,
|
|
476
|
+
estimatedMessages: summary.estimatedMessages,
|
|
477
|
+
tokensPerMin: summary.tokensPerMin,
|
|
478
|
+
budgetPct5x: `${summary.budgetPct5x}%`,
|
|
479
|
+
budgetPct20x: `${summary.budgetPct20x}%`,
|
|
480
|
+
minutesRemaining5x: summary.minutesRemaining5x,
|
|
481
|
+
minutesRemaining20x: summary.minutesRemaining20x,
|
|
482
|
+
rateLimitHits: this.rateLimit.totalHits
|
|
483
|
+
});
|
|
484
|
+
if (summary.budgetPct5x >= 75 && summary.budgetPct5x < 100) {
|
|
485
|
+
console.log(
|
|
486
|
+
`[usage] \u26A0 ${summary.budgetPct5x}% of Max 5x budget used \u2014 ~${summary.minutesRemaining5x}min remaining at ${summary.tokensPerMin} tok/min`
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
if (summary.budgetPct5x >= 100) {
|
|
490
|
+
console.log(
|
|
491
|
+
`[usage] \u{1F6D1} Max 5x budget likely exhausted (${summary.estimatedMessages} est. messages / 225 limit). Expect 429s.`
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
410
495
|
logger.info("Dispatching issues", {
|
|
411
496
|
count: toDispatch.length,
|
|
412
497
|
identifiers: toDispatch.map((i) => i.identifier),
|
|
@@ -569,6 +654,9 @@ class Conductor {
|
|
|
569
654
|
error: run.error,
|
|
570
655
|
attempt: run.attempt
|
|
571
656
|
});
|
|
657
|
+
if (this.handleRateLimitError(run.error, issue.identifier)) {
|
|
658
|
+
throw err;
|
|
659
|
+
}
|
|
572
660
|
if (run.workspacePath) {
|
|
573
661
|
await this.runHook(
|
|
574
662
|
"after-run",
|
|
@@ -595,6 +683,179 @@ class Conductor {
|
|
|
595
683
|
}
|
|
596
684
|
}
|
|
597
685
|
}
|
|
686
|
+
// ── Rate Limit Detection ──
|
|
687
|
+
/**
|
|
688
|
+
* Check if an error indicates a rate limit (429) or usage cap.
|
|
689
|
+
* Triggers global backoff so no new agents are dispatched.
|
|
690
|
+
*
|
|
691
|
+
* Claude Max limits (approximate, shared between claude.ai + Claude Code):
|
|
692
|
+
* - Max 5x: ~225 messages per 5h window
|
|
693
|
+
* - Max 20x: ~900 messages per 5h window
|
|
694
|
+
* Messages are token-weighted: heavy agent usage ≈ 5-10x a casual message.
|
|
695
|
+
*/
|
|
696
|
+
handleRateLimitError(error, identifier) {
|
|
697
|
+
const rateLimitPatterns = [
|
|
698
|
+
"usage limits",
|
|
699
|
+
"rate_limit",
|
|
700
|
+
"rate limit",
|
|
701
|
+
"overloaded",
|
|
702
|
+
"429",
|
|
703
|
+
"too many requests",
|
|
704
|
+
"quota exceeded",
|
|
705
|
+
"capacity"
|
|
706
|
+
];
|
|
707
|
+
const isRateLimit = rateLimitPatterns.some(
|
|
708
|
+
(p) => error.toLowerCase().includes(p)
|
|
709
|
+
);
|
|
710
|
+
if (!isRateLimit) return false;
|
|
711
|
+
this.rateLimit.totalHits++;
|
|
712
|
+
this.rateLimit.lastHitAt = Date.now();
|
|
713
|
+
const timeSinceLast = Date.now() - this.rateLimit.lastHitAt;
|
|
714
|
+
if (timeSinceLast < 6e5) {
|
|
715
|
+
this.rateLimit.consecutiveHits++;
|
|
716
|
+
} else {
|
|
717
|
+
this.rateLimit.consecutiveHits = 1;
|
|
718
|
+
}
|
|
719
|
+
const backoffSec = Math.min(
|
|
720
|
+
60 * Math.pow(2, this.rateLimit.consecutiveHits - 1),
|
|
721
|
+
900
|
|
722
|
+
);
|
|
723
|
+
this.rateLimit.inBackoff = true;
|
|
724
|
+
this.rateLimit.backoffUntil = Date.now() + backoffSec * 1e3;
|
|
725
|
+
logger.warn("Rate limit hit \u2014 global backoff", {
|
|
726
|
+
identifier,
|
|
727
|
+
backoffSec,
|
|
728
|
+
consecutiveHits: this.rateLimit.consecutiveHits,
|
|
729
|
+
totalHits: this.rateLimit.totalHits,
|
|
730
|
+
error: error.slice(0, 200)
|
|
731
|
+
});
|
|
732
|
+
console.log(
|
|
733
|
+
`[rate-limit] Hit rate limit on ${identifier} \u2014 backing off ${backoffSec}s (hit #${this.rateLimit.totalHits})`
|
|
734
|
+
);
|
|
735
|
+
return true;
|
|
736
|
+
}
|
|
737
|
+
// ── Usage Tracking ──
|
|
738
|
+
/**
|
|
739
|
+
* Track token usage from a stream-json event (CLI mode) or JSON-RPC message (adapter mode).
|
|
740
|
+
* Updates both global and per-agent counters.
|
|
741
|
+
*/
|
|
742
|
+
trackUsage(identifier, usage) {
|
|
743
|
+
const input = usage.input_tokens || 0;
|
|
744
|
+
const output = usage.output_tokens || 0;
|
|
745
|
+
const cacheCreate = usage.cache_creation_input_tokens || 0;
|
|
746
|
+
const cacheRead = usage.cache_read_input_tokens || 0;
|
|
747
|
+
this.usage.inputTokens += input;
|
|
748
|
+
this.usage.outputTokens += output;
|
|
749
|
+
this.usage.cacheCreationTokens += cacheCreate;
|
|
750
|
+
this.usage.cacheReadTokens += cacheRead;
|
|
751
|
+
this.usage.estimatedMessages = Math.ceil(
|
|
752
|
+
(this.usage.inputTokens + this.usage.outputTokens) / 1e4
|
|
753
|
+
);
|
|
754
|
+
const agent = this.usage.perAgent.get(identifier) || {
|
|
755
|
+
inputTokens: 0,
|
|
756
|
+
outputTokens: 0
|
|
757
|
+
};
|
|
758
|
+
agent.inputTokens += input;
|
|
759
|
+
agent.outputTokens += output;
|
|
760
|
+
this.usage.perAgent.set(identifier, agent);
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Scan Claude Code JSONL logs for token usage from conductor-spawned sessions.
|
|
764
|
+
* Reads logs from ~/.claude/projects/ matching conductor workspace paths.
|
|
765
|
+
*/
|
|
766
|
+
async scanUsageLogs() {
|
|
767
|
+
const logDirs = [
|
|
768
|
+
join(homedir(), ".claude", "projects"),
|
|
769
|
+
join(homedir(), ".config", "claude", "projects")
|
|
770
|
+
];
|
|
771
|
+
for (const logDir of logDirs) {
|
|
772
|
+
if (!existsSync(logDir)) continue;
|
|
773
|
+
try {
|
|
774
|
+
const entries = readdirSync(logDir);
|
|
775
|
+
const conductorDirs = entries.filter(
|
|
776
|
+
(e) => e.includes("conductor_workspaces") || e.includes("conductor-")
|
|
777
|
+
);
|
|
778
|
+
for (const dir of conductorDirs) {
|
|
779
|
+
const projectDir = join(logDir, dir);
|
|
780
|
+
if (!existsSync(projectDir)) continue;
|
|
781
|
+
const jsonlFiles = readdirSync(projectDir).filter(
|
|
782
|
+
(f) => f.endsWith(".jsonl")
|
|
783
|
+
);
|
|
784
|
+
for (const file of jsonlFiles) {
|
|
785
|
+
await this.parseUsageFromJsonl(join(projectDir, file));
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
} catch (err) {
|
|
789
|
+
logger.debug("Usage log scan failed", {
|
|
790
|
+
error: err.message
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
return this.usage;
|
|
795
|
+
}
|
|
796
|
+
async parseUsageFromJsonl(filePath) {
|
|
797
|
+
try {
|
|
798
|
+
const rl = createInterface({
|
|
799
|
+
input: createReadStream(filePath),
|
|
800
|
+
crlfDelay: Infinity
|
|
801
|
+
});
|
|
802
|
+
for await (const line of rl) {
|
|
803
|
+
if (!line.trim()) continue;
|
|
804
|
+
try {
|
|
805
|
+
const entry = JSON.parse(line);
|
|
806
|
+
if (entry.type === "assistant" && entry.message?.usage) {
|
|
807
|
+
const usage = entry.message.usage;
|
|
808
|
+
const identifier = entry.sessionId?.slice(0, 8) || "unknown";
|
|
809
|
+
this.trackUsage(identifier, usage);
|
|
810
|
+
}
|
|
811
|
+
} catch {
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
} catch {
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Get current usage summary with time-to-exhaustion estimate.
|
|
819
|
+
*
|
|
820
|
+
* Claude Max limits (approximate, shared between claude.ai + Claude Code):
|
|
821
|
+
* - Max 5x: ~225 messages per 5h window (~10k tokens per "message")
|
|
822
|
+
* - Max 20x: ~900 messages per 5h window
|
|
823
|
+
*
|
|
824
|
+
* Heavy agent usage burns 5-10x faster than casual chat.
|
|
825
|
+
*/
|
|
826
|
+
getUsageSummary() {
|
|
827
|
+
const totalCache = this.usage.cacheCreationTokens + this.usage.cacheReadTokens;
|
|
828
|
+
const cacheHitRate = totalCache > 0 ? this.usage.cacheReadTokens / totalCache : 0;
|
|
829
|
+
const uptimeMin = Math.max(1, (Date.now() - this.startedAt) / 6e4);
|
|
830
|
+
const totalTokens = this.usage.inputTokens + this.usage.outputTokens;
|
|
831
|
+
const tokensPerMin = Math.round(totalTokens / uptimeMin);
|
|
832
|
+
const estMessages = this.usage.estimatedMessages;
|
|
833
|
+
const MAX_5X_MESSAGES = 225;
|
|
834
|
+
const MAX_20X_MESSAGES = 900;
|
|
835
|
+
const budgetPct5x = Math.round(estMessages / MAX_5X_MESSAGES * 100);
|
|
836
|
+
const budgetPct20x = Math.round(estMessages / MAX_20X_MESSAGES * 100);
|
|
837
|
+
const msgsPerMin = tokensPerMin > 0 ? tokensPerMin / 1e4 : 0;
|
|
838
|
+
const minutesRemaining5x = msgsPerMin > 0 ? Math.round((MAX_5X_MESSAGES - estMessages) / msgsPerMin) : -1;
|
|
839
|
+
const minutesRemaining20x = msgsPerMin > 0 ? Math.round((MAX_20X_MESSAGES - estMessages) / msgsPerMin) : -1;
|
|
840
|
+
return {
|
|
841
|
+
totalTokens,
|
|
842
|
+
inputTokens: this.usage.inputTokens,
|
|
843
|
+
outputTokens: this.usage.outputTokens,
|
|
844
|
+
estimatedMessages: estMessages,
|
|
845
|
+
cacheHitRate: Math.round(cacheHitRate * 100),
|
|
846
|
+
tokensPerMin,
|
|
847
|
+
budgetPct5x,
|
|
848
|
+
budgetPct20x,
|
|
849
|
+
minutesRemaining5x: minutesRemaining5x < 0 ? -1 : minutesRemaining5x,
|
|
850
|
+
minutesRemaining20x: minutesRemaining20x < 0 ? -1 : minutesRemaining20x,
|
|
851
|
+
perAgent: Array.from(this.usage.perAgent.entries()).map(
|
|
852
|
+
([id, stats]) => ({
|
|
853
|
+
id,
|
|
854
|
+
...stats
|
|
855
|
+
})
|
|
856
|
+
)
|
|
857
|
+
};
|
|
858
|
+
}
|
|
598
859
|
// ── Workspace Management ──
|
|
599
860
|
async createWorkspace(issue) {
|
|
600
861
|
const wsKey = this.sanitizeIdentifier(issue.identifier);
|
|
@@ -672,6 +933,21 @@ class Conductor {
|
|
|
672
933
|
sanitizeIdentifier(identifier) {
|
|
673
934
|
return identifier.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
674
935
|
}
|
|
936
|
+
/**
|
|
937
|
+
* Find the real claude binary, skipping shell wrappers (cmux, claude-smd).
|
|
938
|
+
* Wrappers inject --settings with hooks that block headless -p mode.
|
|
939
|
+
*/
|
|
940
|
+
findClaudeBinary() {
|
|
941
|
+
const candidates = [
|
|
942
|
+
join(homedir(), ".local", "bin", "claude"),
|
|
943
|
+
"/usr/local/bin/claude",
|
|
944
|
+
"/opt/homebrew/bin/claude"
|
|
945
|
+
];
|
|
946
|
+
for (const c of candidates) {
|
|
947
|
+
if (existsSync(c)) return c;
|
|
948
|
+
}
|
|
949
|
+
return "claude";
|
|
950
|
+
}
|
|
675
951
|
// ── Agent Execution ──
|
|
676
952
|
async runAgent(issue, run) {
|
|
677
953
|
if (this.config.agentMode === "cli") {
|
|
@@ -679,7 +955,10 @@ class Conductor {
|
|
|
679
955
|
return await this.runAgentCLI(issue, run);
|
|
680
956
|
} catch (err) {
|
|
681
957
|
const msg = err.message || "";
|
|
682
|
-
if (
|
|
958
|
+
if (this.handleRateLimitError(msg, issue.identifier)) {
|
|
959
|
+
throw err;
|
|
960
|
+
}
|
|
961
|
+
if (msg.includes("usage limits") || msg.includes("overloaded")) {
|
|
683
962
|
logger.warn("CLI mode hit limits, falling back to adapter", {
|
|
684
963
|
identifier: issue.identifier,
|
|
685
964
|
error: msg.slice(0, 200)
|
|
@@ -702,13 +981,16 @@ class Conductor {
|
|
|
702
981
|
runAgentCLI(issue, run) {
|
|
703
982
|
return new Promise((resolve, reject) => {
|
|
704
983
|
const prompt = this.buildPrompt(issue, run.attempt);
|
|
984
|
+
const claudeBin = this.findClaudeBinary();
|
|
705
985
|
const proc = spawn(
|
|
706
|
-
|
|
986
|
+
claudeBin,
|
|
707
987
|
[
|
|
708
988
|
"-p",
|
|
709
989
|
"--output-format",
|
|
710
990
|
"stream-json",
|
|
711
991
|
"--dangerously-skip-permissions",
|
|
992
|
+
"--settings",
|
|
993
|
+
'{"hooks":{}}',
|
|
712
994
|
prompt
|
|
713
995
|
],
|
|
714
996
|
{
|
|
@@ -729,6 +1011,7 @@ class Conductor {
|
|
|
729
1011
|
}
|
|
730
1012
|
);
|
|
731
1013
|
run.process = proc;
|
|
1014
|
+
proc.stdin.end();
|
|
732
1015
|
const logStream = this.openAgentLogStream(issue.identifier);
|
|
733
1016
|
run.logStream = logStream;
|
|
734
1017
|
const tee = new TeeTransform(logStream);
|
|
@@ -759,6 +1042,10 @@ class Conductor {
|
|
|
759
1042
|
}
|
|
760
1043
|
if (event.type === "assistant") {
|
|
761
1044
|
const message = event.message;
|
|
1045
|
+
const msgUsage = message?.usage;
|
|
1046
|
+
if (msgUsage) {
|
|
1047
|
+
this.trackUsage(issue.identifier, msgUsage);
|
|
1048
|
+
}
|
|
762
1049
|
const content = message?.content || [];
|
|
763
1050
|
for (const block of content) {
|
|
764
1051
|
if (block.type === "tool_use") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackmemoryai/stackmemory",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.6",
|
|
4
4
|
"description": "Lossless, project-scoped memory for AI coding tools. Durable context across sessions with 56 MCP tools, FTS5 search, conductor orchestrator, loop/watch monitoring, snapshot capture, pre-flight overlap checks, Claude/Codex/OpenCode wrappers, Linear sync, and automatic hooks.",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20.0.0",
|
|
@@ -100,6 +100,7 @@
|
|
|
100
100
|
"postinstall": "node scripts/install-claude-hooks-auto.js || true",
|
|
101
101
|
"init": "node dist/scripts/initialize.js",
|
|
102
102
|
"build": "rm -rf dist && node esbuild.config.js",
|
|
103
|
+
"typecheck": "NODE_OPTIONS='--max-old-space-size=8192' tsc --noEmit",
|
|
103
104
|
"lint": "eslint 'src/**/*.ts' 'scripts/**/*.ts'",
|
|
104
105
|
"lint:fix": "eslint 'src/**/*.ts' 'scripts/**/*.ts' --fix --max-warnings=-1",
|
|
105
106
|
"lint:fast": "oxlint src scripts",
|