opencode-acp 1.15.0 → 1.16.0-pr.376.121
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/index.js +314 -67
- package/dist/index.js.map +1 -1
- package/dist/lib/config-validation.d.ts.map +1 -1
- package/dist/lib/config.d.ts +7 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/state/fork-transfer.d.ts +42 -0
- package/dist/lib/state/fork-transfer.d.ts.map +1 -0
- package/dist/lib/state/persistence.d.ts +14 -3
- package/dist/lib/state/persistence.d.ts.map +1 -1
- package/dist/lib/state/state.d.ts +3 -2
- package/dist/lib/state/state.d.ts.map +1 -1
- package/dist/lib/state/types.d.ts +6 -0
- package/dist/lib/state/types.d.ts.map +1 -1
- package/dist/lib/state/utils.d.ts +1 -0
- package/dist/lib/state/utils.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -869,6 +869,7 @@ var VALID_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
869
869
|
"autoUpdate",
|
|
870
870
|
"debug",
|
|
871
871
|
"logLevel",
|
|
872
|
+
"storagePath",
|
|
872
873
|
"showUpdateToasts",
|
|
873
874
|
"allowSubAgents",
|
|
874
875
|
"pruneNotification",
|
|
@@ -959,6 +960,9 @@ function validateConfigTypes(config) {
|
|
|
959
960
|
if (config.debug !== void 0 && typeof config.debug !== "boolean") {
|
|
960
961
|
errors.push({ key: "debug", expected: "boolean", actual: typeof config.debug });
|
|
961
962
|
}
|
|
963
|
+
if (config.storagePath !== void 0 && typeof config.storagePath !== "string") {
|
|
964
|
+
errors.push({ key: "storagePath", expected: "string", actual: typeof config.storagePath });
|
|
965
|
+
}
|
|
962
966
|
if (config.allowSubAgents !== void 0 && typeof config.allowSubAgents !== "boolean") {
|
|
963
967
|
errors.push({ key: "allowSubAgents", expected: "boolean", actual: typeof config.allowSubAgents });
|
|
964
968
|
}
|
|
@@ -1995,6 +1999,7 @@ function mergeLayer(config, data) {
|
|
|
1995
1999
|
autoUpdate: data.autoUpdate ?? config.autoUpdate,
|
|
1996
2000
|
debug: data.debug ?? config.debug,
|
|
1997
2001
|
logLevel: data.logLevel ?? config.logLevel,
|
|
2002
|
+
storagePath: data.storagePath ?? config.storagePath,
|
|
1998
2003
|
allowSubAgents: data.allowSubAgents ?? data.experimental?.allowSubAgents ?? config.allowSubAgents,
|
|
1999
2004
|
pruneNotification: data.pruneNotification ?? config.pruneNotification,
|
|
2000
2005
|
pruneNotificationType: data.pruneNotificationType ?? config.pruneNotificationType,
|
|
@@ -2349,7 +2354,7 @@ Each entry needs a topic \u2014 either its own or the top-level fallback.`;
|
|
|
2349
2354
|
import * as fs from "fs/promises";
|
|
2350
2355
|
import { existsSync as existsSync2 } from "fs";
|
|
2351
2356
|
import { homedir as homedir2 } from "os";
|
|
2352
|
-
import { join as join2 } from "path";
|
|
2357
|
+
import { isAbsolute, join as join2 } from "path";
|
|
2353
2358
|
|
|
2354
2359
|
// lib/state/utils.ts
|
|
2355
2360
|
var isMessageCompacted = (state, msg) => {
|
|
@@ -2385,12 +2390,12 @@ function serializePruneMessagesState(messagesState) {
|
|
|
2385
2390
|
markedForCleanup: Array.from(messagesState.markedForCleanup)
|
|
2386
2391
|
};
|
|
2387
2392
|
}
|
|
2388
|
-
async function
|
|
2393
|
+
async function getForkParentId(client, sessionID) {
|
|
2389
2394
|
try {
|
|
2390
2395
|
const result = await client.session.get({ path: { id: sessionID } });
|
|
2391
|
-
return
|
|
2396
|
+
return result.data?.parentID ?? null;
|
|
2392
2397
|
} catch (error) {
|
|
2393
|
-
return
|
|
2398
|
+
return null;
|
|
2394
2399
|
}
|
|
2395
2400
|
}
|
|
2396
2401
|
function findLastCompactionTimestamp(messages) {
|
|
@@ -2632,7 +2637,7 @@ function resetOnCompaction(state) {
|
|
|
2632
2637
|
}
|
|
2633
2638
|
|
|
2634
2639
|
// lib/state/persistence.ts
|
|
2635
|
-
function
|
|
2640
|
+
function getDefaultStorageDir() {
|
|
2636
2641
|
return join2(
|
|
2637
2642
|
process.env.XDG_DATA_HOME || join2(homedir2(), ".local", "share"),
|
|
2638
2643
|
"opencode",
|
|
@@ -2641,14 +2646,33 @@ function getStorageDir() {
|
|
|
2641
2646
|
"acp"
|
|
2642
2647
|
);
|
|
2643
2648
|
}
|
|
2644
|
-
function
|
|
2645
|
-
|
|
2649
|
+
function resolveStorageDir(configured, projectDir) {
|
|
2650
|
+
const trimmed = configured?.trim();
|
|
2651
|
+
if (!trimmed) {
|
|
2652
|
+
return getDefaultStorageDir();
|
|
2653
|
+
}
|
|
2654
|
+
if (trimmed === "~") {
|
|
2655
|
+
return homedir2();
|
|
2656
|
+
}
|
|
2657
|
+
if (trimmed.startsWith("~/")) {
|
|
2658
|
+
return join2(homedir2(), trimmed.slice(2));
|
|
2659
|
+
}
|
|
2660
|
+
if (isAbsolute(trimmed)) {
|
|
2661
|
+
return trimmed;
|
|
2662
|
+
}
|
|
2663
|
+
return join2(projectDir, trimmed);
|
|
2664
|
+
}
|
|
2665
|
+
function getStorageDir(override) {
|
|
2666
|
+
return override || getDefaultStorageDir();
|
|
2667
|
+
}
|
|
2668
|
+
function getSessionFilePath(sessionId, storageDir) {
|
|
2669
|
+
return join2(getStorageDir(storageDir), `${sessionId}.json`);
|
|
2646
2670
|
}
|
|
2647
|
-
async function writePersistedSessionState(sessionId, state, logger) {
|
|
2648
|
-
const filePath = getSessionFilePath(sessionId);
|
|
2649
|
-
const
|
|
2650
|
-
if (!existsSync2(
|
|
2651
|
-
await fs.mkdir(
|
|
2671
|
+
async function writePersistedSessionState(sessionId, state, logger, storageDir) {
|
|
2672
|
+
const filePath = getSessionFilePath(sessionId, storageDir);
|
|
2673
|
+
const dir = getStorageDir(storageDir);
|
|
2674
|
+
if (!existsSync2(dir)) {
|
|
2675
|
+
await fs.mkdir(dir, { recursive: true });
|
|
2652
2676
|
}
|
|
2653
2677
|
const content = JSON.stringify(state, null, 2);
|
|
2654
2678
|
await fs.writeFile(filePath, content, "utf-8");
|
|
@@ -2690,11 +2714,11 @@ async function saveSessionState(sessionState, logger, sessionName) {
|
|
|
2690
2714
|
modelProviderID: sessionState.modelProviderID,
|
|
2691
2715
|
modelID: sessionState.modelID
|
|
2692
2716
|
};
|
|
2693
|
-
await writePersistedSessionState(sessionState.sessionId, state, logger);
|
|
2717
|
+
await writePersistedSessionState(sessionState.sessionId, state, logger, sessionState.storageDir);
|
|
2694
2718
|
}
|
|
2695
|
-
async function loadSessionState(sessionId, logger) {
|
|
2719
|
+
async function loadSessionState(sessionId, logger, storageDir) {
|
|
2696
2720
|
try {
|
|
2697
|
-
const filePath = getSessionFilePath(sessionId);
|
|
2721
|
+
const filePath = getSessionFilePath(sessionId, storageDir);
|
|
2698
2722
|
if (!existsSync2(filePath)) {
|
|
2699
2723
|
return null;
|
|
2700
2724
|
}
|
|
@@ -2770,6 +2794,11 @@ async function loadSessionState(sessionId, logger) {
|
|
|
2770
2794
|
}
|
|
2771
2795
|
}
|
|
2772
2796
|
|
|
2797
|
+
// lib/state/state.ts
|
|
2798
|
+
import { existsSync as existsSync3 } from "fs";
|
|
2799
|
+
import { join as join3 } from "path";
|
|
2800
|
+
import { cwd } from "process";
|
|
2801
|
+
|
|
2773
2802
|
// lib/message-ids.ts
|
|
2774
2803
|
var MESSAGE_REF_REGEX = /^m(\d{4,5})$/;
|
|
2775
2804
|
var BLOCK_REF_REGEX = /^b([1-9]\d*)$/;
|
|
@@ -4427,6 +4456,205 @@ function rebuildCompressionState(state, messages, config, logger) {
|
|
|
4427
4456
|
return rebuilt;
|
|
4428
4457
|
}
|
|
4429
4458
|
|
|
4459
|
+
// lib/state/fork-transfer.ts
|
|
4460
|
+
function extractIdentity(message) {
|
|
4461
|
+
const id = message?.info?.id;
|
|
4462
|
+
if (typeof id !== "string" || id.length === 0) {
|
|
4463
|
+
return null;
|
|
4464
|
+
}
|
|
4465
|
+
if (isSyntheticMessage(message)) {
|
|
4466
|
+
return null;
|
|
4467
|
+
}
|
|
4468
|
+
const role = message.info.role;
|
|
4469
|
+
const created = message.info.time?.created;
|
|
4470
|
+
if (typeof created !== "number") {
|
|
4471
|
+
return null;
|
|
4472
|
+
}
|
|
4473
|
+
return { id, role, created };
|
|
4474
|
+
}
|
|
4475
|
+
function buildSharedPrefixMapping(parentMessages, forkMessages) {
|
|
4476
|
+
const parentIds = parentMessages.map(extractIdentity).filter((x) => x !== null);
|
|
4477
|
+
const forkIds = forkMessages.map(extractIdentity).filter((x) => x !== null);
|
|
4478
|
+
const mapping = /* @__PURE__ */ new Map();
|
|
4479
|
+
const limit = Math.min(parentIds.length, forkIds.length);
|
|
4480
|
+
for (let i = 0; i < limit; i++) {
|
|
4481
|
+
const p = parentIds[i];
|
|
4482
|
+
const f = forkIds[i];
|
|
4483
|
+
if (p.created === f.created && p.role === f.role) {
|
|
4484
|
+
mapping.set(p.id, f.id);
|
|
4485
|
+
} else {
|
|
4486
|
+
break;
|
|
4487
|
+
}
|
|
4488
|
+
}
|
|
4489
|
+
return mapping;
|
|
4490
|
+
}
|
|
4491
|
+
function remapBoundaryRef(ref, mapping, forkByRawId, parentByRef) {
|
|
4492
|
+
const parsed = parseBoundaryId(ref);
|
|
4493
|
+
if (!parsed) {
|
|
4494
|
+
return ref;
|
|
4495
|
+
}
|
|
4496
|
+
if (parsed.kind === "compressed-block") {
|
|
4497
|
+
return parsed.ref;
|
|
4498
|
+
}
|
|
4499
|
+
const parentRawId = parentByRef.get(parsed.ref);
|
|
4500
|
+
if (!parentRawId) {
|
|
4501
|
+
return ref;
|
|
4502
|
+
}
|
|
4503
|
+
const forkRawId = mapping.get(parentRawId);
|
|
4504
|
+
if (!forkRawId) {
|
|
4505
|
+
return ref;
|
|
4506
|
+
}
|
|
4507
|
+
const forkRef = forkByRawId.get(forkRawId);
|
|
4508
|
+
return forkRef ?? ref;
|
|
4509
|
+
}
|
|
4510
|
+
function translateBlock(block, mapping, forkByRawId, parentByRef) {
|
|
4511
|
+
const anchorForkId = mapping.get(block.anchorMessageId);
|
|
4512
|
+
if (!anchorForkId) {
|
|
4513
|
+
return null;
|
|
4514
|
+
}
|
|
4515
|
+
const compressForkId = mapping.get(block.compressMessageId);
|
|
4516
|
+
if (!compressForkId) {
|
|
4517
|
+
return null;
|
|
4518
|
+
}
|
|
4519
|
+
const translateList = (ids) => ids.map((id) => mapping.get(id)).filter((id) => id !== void 0);
|
|
4520
|
+
const preserveList = (ids) => [...new Set(ids)];
|
|
4521
|
+
return {
|
|
4522
|
+
...block,
|
|
4523
|
+
anchorMessageId: anchorForkId,
|
|
4524
|
+
compressMessageId: compressForkId,
|
|
4525
|
+
directMessageIds: translateList(block.directMessageIds),
|
|
4526
|
+
effectiveMessageIds: translateList(block.effectiveMessageIds),
|
|
4527
|
+
directToolIds: preserveList(block.directToolIds),
|
|
4528
|
+
effectiveToolIds: preserveList(block.effectiveToolIds),
|
|
4529
|
+
startId: remapBoundaryRef(block.startId, mapping, forkByRawId, parentByRef),
|
|
4530
|
+
endId: remapBoundaryRef(block.endId, mapping, forkByRawId, parentByRef)
|
|
4531
|
+
};
|
|
4532
|
+
}
|
|
4533
|
+
function translateByMessageId(parentByMessageId, mapping, translatedBlockIds) {
|
|
4534
|
+
const result = /* @__PURE__ */ new Map();
|
|
4535
|
+
for (const [parentMsgId, entry] of Object.entries(parentByMessageId)) {
|
|
4536
|
+
if (!entry || typeof entry !== "object") {
|
|
4537
|
+
continue;
|
|
4538
|
+
}
|
|
4539
|
+
const forkMsgId = mapping.get(parentMsgId);
|
|
4540
|
+
if (!forkMsgId) {
|
|
4541
|
+
continue;
|
|
4542
|
+
}
|
|
4543
|
+
const allBlockIds = (entry.allBlockIds ?? []).filter((id) => translatedBlockIds.has(id));
|
|
4544
|
+
const activeBlockIds = (entry.activeBlockIds ?? []).filter(
|
|
4545
|
+
(id) => translatedBlockIds.has(id)
|
|
4546
|
+
);
|
|
4547
|
+
result.set(forkMsgId, {
|
|
4548
|
+
tokenCount: typeof entry.tokenCount === "number" ? entry.tokenCount : 0,
|
|
4549
|
+
allBlockIds,
|
|
4550
|
+
activeBlockIds
|
|
4551
|
+
});
|
|
4552
|
+
}
|
|
4553
|
+
return result;
|
|
4554
|
+
}
|
|
4555
|
+
async function recoverFromParentState(client, state, forkMessages, parentId, logger) {
|
|
4556
|
+
const parentState = await loadSessionState(parentId, logger);
|
|
4557
|
+
if (!parentState) {
|
|
4558
|
+
logger.info("fork-transfer: no parent state found, falling back to replay", {
|
|
4559
|
+
parentId
|
|
4560
|
+
});
|
|
4561
|
+
return 0;
|
|
4562
|
+
}
|
|
4563
|
+
const parentMessagesState = parentState.prune?.messages;
|
|
4564
|
+
const parentBlocks = parentMessagesState?.blocksById;
|
|
4565
|
+
if (!parentBlocks) {
|
|
4566
|
+
logger.info("fork-transfer: parent state has no blocks, falling back to replay", {
|
|
4567
|
+
parentId
|
|
4568
|
+
});
|
|
4569
|
+
return 0;
|
|
4570
|
+
}
|
|
4571
|
+
const parentBlockEntries = Object.entries(parentBlocks).filter(
|
|
4572
|
+
([id, block]) => Number.isInteger(Number.parseInt(id, 10)) && Number.parseInt(id, 10) >= 1 && !!block
|
|
4573
|
+
);
|
|
4574
|
+
if (parentBlockEntries.length === 0) {
|
|
4575
|
+
logger.info("fork-transfer: parent state has no blocks, falling back to replay", {
|
|
4576
|
+
parentId
|
|
4577
|
+
});
|
|
4578
|
+
return 0;
|
|
4579
|
+
}
|
|
4580
|
+
let parentMessages;
|
|
4581
|
+
try {
|
|
4582
|
+
const response = await client.session.messages({ path: { id: parentId } });
|
|
4583
|
+
parentMessages = filterMessages(response?.data || response);
|
|
4584
|
+
} catch (error) {
|
|
4585
|
+
logger.warn("fork-transfer: failed to fetch parent messages, falling back to replay", {
|
|
4586
|
+
parentId,
|
|
4587
|
+
error: error?.message || String(error)
|
|
4588
|
+
});
|
|
4589
|
+
return 0;
|
|
4590
|
+
}
|
|
4591
|
+
const mapping = buildSharedPrefixMapping(parentMessages, forkMessages);
|
|
4592
|
+
if (mapping.size === 0) {
|
|
4593
|
+
logger.info("fork-transfer: no shared prefix detected, falling back to replay", {
|
|
4594
|
+
parentId
|
|
4595
|
+
});
|
|
4596
|
+
return 0;
|
|
4597
|
+
}
|
|
4598
|
+
assignMessageRefs(state, forkMessages);
|
|
4599
|
+
const forkByRawId = state.messageIds.byRawId;
|
|
4600
|
+
const parentByRef = new Map(Object.entries(parentState.messageIds?.byRef ?? {}));
|
|
4601
|
+
const translatedBlocks = /* @__PURE__ */ new Map();
|
|
4602
|
+
let skipped = 0;
|
|
4603
|
+
for (const [blockIdStr, block] of parentBlockEntries) {
|
|
4604
|
+
const blockId = Number.parseInt(blockIdStr, 10);
|
|
4605
|
+
const translated = translateBlock(block, mapping, forkByRawId, parentByRef);
|
|
4606
|
+
if (translated) {
|
|
4607
|
+
translatedBlocks.set(blockId, translated);
|
|
4608
|
+
} else {
|
|
4609
|
+
skipped++;
|
|
4610
|
+
}
|
|
4611
|
+
}
|
|
4612
|
+
if (translatedBlocks.size === 0) {
|
|
4613
|
+
logger.info("fork-transfer: no blocks translatable, falling back to replay", {
|
|
4614
|
+
parentId,
|
|
4615
|
+
parentBlockCount: parentBlockEntries.length,
|
|
4616
|
+
mappingSize: mapping.size
|
|
4617
|
+
});
|
|
4618
|
+
return 0;
|
|
4619
|
+
}
|
|
4620
|
+
const activeCount = Array.from(translatedBlocks.values()).filter((b) => b.active).length;
|
|
4621
|
+
if (activeCount === 0) {
|
|
4622
|
+
logger.info("fork-transfer: no active blocks translatable, falling back to replay", {
|
|
4623
|
+
parentId,
|
|
4624
|
+
recoveredBlocks: translatedBlocks.size,
|
|
4625
|
+
skippedBlocks: skipped,
|
|
4626
|
+
mappingSize: mapping.size
|
|
4627
|
+
});
|
|
4628
|
+
return 0;
|
|
4629
|
+
}
|
|
4630
|
+
const translatedByMessageId = translateByMessageId(
|
|
4631
|
+
parentMessagesState.byMessageId ?? {},
|
|
4632
|
+
mapping,
|
|
4633
|
+
new Set(translatedBlocks.keys())
|
|
4634
|
+
);
|
|
4635
|
+
state.prune.messages = loadPruneMessagesState({
|
|
4636
|
+
byMessageId: Object.fromEntries(translatedByMessageId),
|
|
4637
|
+
blocksById: Object.fromEntries(
|
|
4638
|
+
Array.from(translatedBlocks.entries()).map(
|
|
4639
|
+
([id, block]) => [String(id), block]
|
|
4640
|
+
)
|
|
4641
|
+
),
|
|
4642
|
+
activeBlockIds: [],
|
|
4643
|
+
activeByAnchorMessageId: {},
|
|
4644
|
+
nextBlockId: 1,
|
|
4645
|
+
nextRunId: 1,
|
|
4646
|
+
markedForCleanup: []
|
|
4647
|
+
});
|
|
4648
|
+
logger.info("fork-transfer: recovered compression state from parent", {
|
|
4649
|
+
parentId,
|
|
4650
|
+
recoveredBlocks: translatedBlocks.size,
|
|
4651
|
+
activeBlocks: activeCount,
|
|
4652
|
+
skippedBlocks: skipped,
|
|
4653
|
+
mappingSize: mapping.size
|
|
4654
|
+
});
|
|
4655
|
+
return activeCount;
|
|
4656
|
+
}
|
|
4657
|
+
|
|
4430
4658
|
// lib/state/state.ts
|
|
4431
4659
|
async function updatePerTurnState(state, logger, messages) {
|
|
4432
4660
|
const lastCompactionTimestamp = findLastCompactionTimestamp(messages);
|
|
@@ -4446,10 +4674,12 @@ async function updatePerTurnState(state, logger, messages) {
|
|
|
4446
4674
|
}
|
|
4447
4675
|
var REGISTRY_SOFT_CAP = 32;
|
|
4448
4676
|
var SessionStateRegistry = class {
|
|
4449
|
-
constructor(logger) {
|
|
4677
|
+
constructor(logger, projectDir) {
|
|
4450
4678
|
this.logger = logger;
|
|
4679
|
+
this.projectDir = projectDir;
|
|
4451
4680
|
}
|
|
4452
4681
|
logger;
|
|
4682
|
+
projectDir;
|
|
4453
4683
|
states = /* @__PURE__ */ new Map();
|
|
4454
4684
|
compressionTiming = {
|
|
4455
4685
|
startsByCallId: /* @__PURE__ */ new Map(),
|
|
@@ -4502,7 +4732,8 @@ var SessionStateRegistry = class {
|
|
|
4502
4732
|
sessionId,
|
|
4503
4733
|
this.logger,
|
|
4504
4734
|
messages,
|
|
4505
|
-
config
|
|
4735
|
+
config,
|
|
4736
|
+
this.projectDir
|
|
4506
4737
|
);
|
|
4507
4738
|
} catch (err) {
|
|
4508
4739
|
this.logger.error("Failed to initialize session state", {
|
|
@@ -4566,6 +4797,7 @@ function createSessionState() {
|
|
|
4566
4797
|
modelProviderID: void 0,
|
|
4567
4798
|
modelID: void 0,
|
|
4568
4799
|
systemPromptTokens: void 0,
|
|
4800
|
+
storageDir: void 0,
|
|
4569
4801
|
qualityGateRetryPending: false
|
|
4570
4802
|
};
|
|
4571
4803
|
}
|
|
@@ -4607,24 +4839,39 @@ function resetSessionState(state) {
|
|
|
4607
4839
|
state.modelProviderID = void 0;
|
|
4608
4840
|
state.modelID = void 0;
|
|
4609
4841
|
state.systemPromptTokens = void 0;
|
|
4842
|
+
state.storageDir = void 0;
|
|
4610
4843
|
state.qualityGateRetryPending = false;
|
|
4611
4844
|
}
|
|
4612
|
-
async function ensureSessionInitialized(client, state, sessionId, logger, messages, config) {
|
|
4845
|
+
async function ensureSessionInitialized(client, state, sessionId, logger, messages, config, projectDir) {
|
|
4613
4846
|
if (state.sessionId === sessionId) {
|
|
4614
4847
|
return;
|
|
4615
4848
|
}
|
|
4616
4849
|
resetSessionState(state);
|
|
4617
4850
|
state.sessionId = sessionId;
|
|
4618
|
-
|
|
4619
|
-
|
|
4851
|
+
state.storageDir = config?.storagePath ? resolveStorageDir(config.storagePath, projectDir ?? cwd()) : void 0;
|
|
4852
|
+
const parentId = await getForkParentId(client, sessionId);
|
|
4853
|
+
state.isSubAgent = parentId !== null;
|
|
4620
4854
|
state.lastCompaction = findLastCompactionTimestamp(messages);
|
|
4621
4855
|
state.currentTurn = countTurns(state, messages);
|
|
4622
4856
|
state.nudges.turnNudgeAnchors = collectTurnNudgeAnchors(messages);
|
|
4623
|
-
const persisted = await loadSessionState(sessionId, logger);
|
|
4857
|
+
const persisted = await loadSessionState(sessionId, logger, state.storageDir);
|
|
4624
4858
|
if (persisted === null) {
|
|
4859
|
+
const defaultPath = join3(getDefaultStorageDir(), `${sessionId}.json`);
|
|
4860
|
+
if (state.storageDir && existsSync3(defaultPath)) {
|
|
4861
|
+
logger.warn(
|
|
4862
|
+
"storagePath is set but no valid state was found there; a state file exists at the default location \u2014 move it manually to keep history",
|
|
4863
|
+
{ sessionId, storageDir: state.storageDir, defaultPath }
|
|
4864
|
+
);
|
|
4865
|
+
}
|
|
4625
4866
|
if (config) {
|
|
4626
|
-
|
|
4627
|
-
if (
|
|
4867
|
+
let recovered = 0;
|
|
4868
|
+
if (parentId) {
|
|
4869
|
+
recovered = await recoverFromParentState(client, state, messages, parentId, logger);
|
|
4870
|
+
}
|
|
4871
|
+
if (recovered === 0) {
|
|
4872
|
+
recovered = rebuildCompressionState(state, messages, config, logger);
|
|
4873
|
+
}
|
|
4874
|
+
if (recovered > 0) {
|
|
4628
4875
|
await saveSessionState(state, logger);
|
|
4629
4876
|
}
|
|
4630
4877
|
}
|
|
@@ -9485,10 +9732,10 @@ function hideFailedCompressCalls(messages) {
|
|
|
9485
9732
|
|
|
9486
9733
|
// lib/logger.ts
|
|
9487
9734
|
import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
9488
|
-
import { join as
|
|
9489
|
-
import { existsSync as
|
|
9735
|
+
import { join as join4 } from "path";
|
|
9736
|
+
import { existsSync as existsSync4 } from "fs";
|
|
9490
9737
|
import { homedir as homedir3 } from "os";
|
|
9491
|
-
var LOG_VERSION = true ? "1.
|
|
9738
|
+
var LOG_VERSION = true ? "1.16.0-pr.376.121" : "dev";
|
|
9492
9739
|
var LEVEL_RANK = {
|
|
9493
9740
|
debug: 10,
|
|
9494
9741
|
info: 20,
|
|
@@ -9508,8 +9755,8 @@ var Logger = class {
|
|
|
9508
9755
|
constructor(enabled, level) {
|
|
9509
9756
|
const resolved = level !== void 0 && LEVEL_RANK[level] !== void 0 ? level : void 0;
|
|
9510
9757
|
this.level = resolved ?? (enabled ? "debug" : "warn");
|
|
9511
|
-
const configHome = process.env.XDG_CONFIG_HOME ||
|
|
9512
|
-
this.logDir =
|
|
9758
|
+
const configHome = process.env.XDG_CONFIG_HOME || join4(homedir3(), ".config");
|
|
9759
|
+
this.logDir = join4(configHome, "opencode", "logs", "acp");
|
|
9513
9760
|
}
|
|
9514
9761
|
/** Back-compat view of the old flag: true only at full debug verbosity. */
|
|
9515
9762
|
get enabled() {
|
|
@@ -9519,7 +9766,7 @@ var Logger = class {
|
|
|
9519
9766
|
return LEVEL_RANK[level] >= LEVEL_RANK[this.level];
|
|
9520
9767
|
}
|
|
9521
9768
|
async ensureLogDir() {
|
|
9522
|
-
if (!
|
|
9769
|
+
if (!existsSync4(this.logDir)) {
|
|
9523
9770
|
await mkdir2(this.logDir, { recursive: true });
|
|
9524
9771
|
}
|
|
9525
9772
|
}
|
|
@@ -9571,11 +9818,11 @@ var Logger = class {
|
|
|
9571
9818
|
const dataStr = this.formatData(data);
|
|
9572
9819
|
const logLine = `${timestamp} ${level.padEnd(5)} ${component}: ${message}${dataStr ? " | " + dataStr : ""} | v=${LOG_VERSION}
|
|
9573
9820
|
`;
|
|
9574
|
-
const dailyLogDir =
|
|
9575
|
-
if (!
|
|
9821
|
+
const dailyLogDir = join4(this.logDir, "daily");
|
|
9822
|
+
if (!existsSync4(dailyLogDir)) {
|
|
9576
9823
|
await mkdir2(dailyLogDir, { recursive: true });
|
|
9577
9824
|
}
|
|
9578
|
-
const logFile =
|
|
9825
|
+
const logFile = join4(dailyLogDir, `${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.log`);
|
|
9579
9826
|
await writeFile2(logFile, logLine, { flag: "a" });
|
|
9580
9827
|
} catch (error) {
|
|
9581
9828
|
}
|
|
@@ -9686,18 +9933,18 @@ var Logger = class {
|
|
|
9686
9933
|
async saveContext(sessionId, messages) {
|
|
9687
9934
|
if (this.level !== "debug") return;
|
|
9688
9935
|
try {
|
|
9689
|
-
const contextDir =
|
|
9690
|
-
if (!
|
|
9936
|
+
const contextDir = join4(this.logDir, "context", sessionId);
|
|
9937
|
+
if (!existsSync4(contextDir)) {
|
|
9691
9938
|
await mkdir2(contextDir, { recursive: true });
|
|
9692
9939
|
}
|
|
9693
9940
|
const minimized = this.minimizeForDebug(messages).filter(
|
|
9694
9941
|
(msg) => msg.parts && msg.parts.length > 0
|
|
9695
9942
|
);
|
|
9696
9943
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
9697
|
-
const contextFile =
|
|
9944
|
+
const contextFile = join4(contextDir, `${timestamp}.json`);
|
|
9698
9945
|
await writeFile2(contextFile, JSON.stringify(minimized, null, 2));
|
|
9699
|
-
const versionFile =
|
|
9700
|
-
if (!
|
|
9946
|
+
const versionFile = join4(contextDir, "_version");
|
|
9947
|
+
if (!existsSync4(versionFile)) {
|
|
9701
9948
|
await writeFile2(versionFile, LOG_VERSION);
|
|
9702
9949
|
}
|
|
9703
9950
|
} catch (error) {
|
|
@@ -9706,8 +9953,8 @@ var Logger = class {
|
|
|
9706
9953
|
};
|
|
9707
9954
|
|
|
9708
9955
|
// lib/prompts/store.ts
|
|
9709
|
-
import { existsSync as
|
|
9710
|
-
import { join as
|
|
9956
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, statSync as statSync2 } from "fs";
|
|
9957
|
+
import { join as join5, dirname as dirname2 } from "path";
|
|
9711
9958
|
import { homedir as homedir4 } from "os";
|
|
9712
9959
|
|
|
9713
9960
|
// lib/prompts/system.ts
|
|
@@ -10037,8 +10284,8 @@ function createBundledRuntimePrompts() {
|
|
|
10037
10284
|
function findOpencodeDir2(startDir) {
|
|
10038
10285
|
let current = startDir;
|
|
10039
10286
|
while (current !== "/") {
|
|
10040
|
-
const candidate =
|
|
10041
|
-
if (
|
|
10287
|
+
const candidate = join5(current, ".opencode");
|
|
10288
|
+
if (existsSync5(candidate)) {
|
|
10042
10289
|
try {
|
|
10043
10290
|
if (statSync2(candidate).isDirectory()) {
|
|
10044
10291
|
return candidate;
|
|
@@ -10055,13 +10302,13 @@ function findOpencodeDir2(startDir) {
|
|
|
10055
10302
|
return null;
|
|
10056
10303
|
}
|
|
10057
10304
|
function resolvePromptPaths(workingDirectory) {
|
|
10058
|
-
const configHome = process.env.XDG_CONFIG_HOME ||
|
|
10059
|
-
const globalRoot =
|
|
10060
|
-
const defaultsDir =
|
|
10061
|
-
const globalOverridesDir =
|
|
10062
|
-
const configDirOverridesDir = process.env.OPENCODE_CONFIG_DIR ?
|
|
10305
|
+
const configHome = process.env.XDG_CONFIG_HOME || join5(homedir4(), ".config");
|
|
10306
|
+
const globalRoot = join5(configHome, "opencode", "acp-prompts");
|
|
10307
|
+
const defaultsDir = join5(globalRoot, "defaults");
|
|
10308
|
+
const globalOverridesDir = join5(globalRoot, "overrides");
|
|
10309
|
+
const configDirOverridesDir = process.env.OPENCODE_CONFIG_DIR ? join5(process.env.OPENCODE_CONFIG_DIR, "acp-prompts", "overrides") : null;
|
|
10063
10310
|
const opencodeDir = findOpencodeDir2(workingDirectory);
|
|
10064
|
-
const projectOverridesDir = opencodeDir ?
|
|
10311
|
+
const projectOverridesDir = opencodeDir ? join5(opencodeDir, "acp-prompts", "overrides") : null;
|
|
10065
10312
|
return {
|
|
10066
10313
|
defaultsDir,
|
|
10067
10314
|
globalOverridesDir,
|
|
@@ -10161,7 +10408,7 @@ function buildDefaultsReadmeContent() {
|
|
|
10161
10408
|
`;
|
|
10162
10409
|
}
|
|
10163
10410
|
function readFileIfExists(filePath) {
|
|
10164
|
-
if (!
|
|
10411
|
+
if (!existsSync5(filePath)) {
|
|
10165
10412
|
return null;
|
|
10166
10413
|
}
|
|
10167
10414
|
try {
|
|
@@ -10232,16 +10479,16 @@ var PromptStore = class {
|
|
|
10232
10479
|
const candidates = [];
|
|
10233
10480
|
if (this.paths.projectOverridesDir) {
|
|
10234
10481
|
candidates.push({
|
|
10235
|
-
path:
|
|
10482
|
+
path: join5(this.paths.projectOverridesDir, fileName)
|
|
10236
10483
|
});
|
|
10237
10484
|
}
|
|
10238
10485
|
if (this.paths.configDirOverridesDir) {
|
|
10239
10486
|
candidates.push({
|
|
10240
|
-
path:
|
|
10487
|
+
path: join5(this.paths.configDirOverridesDir, fileName)
|
|
10241
10488
|
});
|
|
10242
10489
|
}
|
|
10243
10490
|
candidates.push({
|
|
10244
|
-
path:
|
|
10491
|
+
path: join5(this.paths.globalOverridesDir, fileName)
|
|
10245
10492
|
});
|
|
10246
10493
|
return candidates;
|
|
10247
10494
|
}
|
|
@@ -10264,7 +10511,7 @@ var PromptStore = class {
|
|
|
10264
10511
|
const managedContent = buildDefaultPromptFileContent(
|
|
10265
10512
|
bundledEditable || BUNDLED_EDITABLE_PROMPTS[definition.runtimeField]
|
|
10266
10513
|
);
|
|
10267
|
-
const filePath =
|
|
10514
|
+
const filePath = join5(this.paths.defaultsDir, definition.fileName);
|
|
10268
10515
|
try {
|
|
10269
10516
|
const existing = readFileIfExists(filePath);
|
|
10270
10517
|
if (existing === managedContent) {
|
|
@@ -10278,7 +10525,7 @@ var PromptStore = class {
|
|
|
10278
10525
|
});
|
|
10279
10526
|
}
|
|
10280
10527
|
}
|
|
10281
|
-
const readmePath =
|
|
10528
|
+
const readmePath = join5(this.paths.defaultsDir, DEFAULTS_README_FILE);
|
|
10282
10529
|
const readmeContent = buildDefaultsReadmeContent();
|
|
10283
10530
|
try {
|
|
10284
10531
|
const existing = readFileIfExists(readmePath);
|
|
@@ -10575,8 +10822,8 @@ ${report}`;
|
|
|
10575
10822
|
|
|
10576
10823
|
// lib/commands/export.ts
|
|
10577
10824
|
import * as fs2 from "fs/promises";
|
|
10578
|
-
import { existsSync as
|
|
10579
|
-
import { dirname as dirname3, isAbsolute, join as
|
|
10825
|
+
import { existsSync as existsSync6 } from "fs";
|
|
10826
|
+
import { dirname as dirname3, isAbsolute as isAbsolute2, join as join6, resolve } from "path";
|
|
10580
10827
|
var ALL_TIERS = [1, 2, 3];
|
|
10581
10828
|
var TIER_NAMES = {
|
|
10582
10829
|
1: "Tier 1 \u2014 Capture",
|
|
@@ -10687,9 +10934,9 @@ function parseTiers(raw) {
|
|
|
10687
10934
|
}
|
|
10688
10935
|
return result;
|
|
10689
10936
|
}
|
|
10690
|
-
function resolveDefaultOutputPath(sessionId,
|
|
10937
|
+
function resolveDefaultOutputPath(sessionId, cwd2) {
|
|
10691
10938
|
const short = (sessionId || "session").slice(0, 8);
|
|
10692
|
-
return
|
|
10939
|
+
return join6(cwd2, ".opencode", `acp-export-${short}.md`);
|
|
10693
10940
|
}
|
|
10694
10941
|
function formatTokens2(n) {
|
|
10695
10942
|
if (!Number.isFinite(n) || n <= 0) return "0";
|
|
@@ -10816,15 +11063,15 @@ async function handleExportCommand(ctx, args) {
|
|
|
10816
11063
|
await sendExportNotice(client, sessionId, ctx, markdown);
|
|
10817
11064
|
return;
|
|
10818
11065
|
}
|
|
10819
|
-
const
|
|
10820
|
-
const targetPath = options.outputPath !== "" ?
|
|
11066
|
+
const cwd2 = ctx.workingDirectory || process.cwd();
|
|
11067
|
+
const targetPath = options.outputPath !== "" ? isAbsolute2(options.outputPath) ? options.outputPath : resolve(cwd2, options.outputPath) : resolveDefaultOutputPath(sessionId, cwd2);
|
|
10821
11068
|
try {
|
|
10822
11069
|
const dir = dirname3(targetPath);
|
|
10823
|
-
if (!
|
|
11070
|
+
if (!existsSync6(dir)) {
|
|
10824
11071
|
await fs2.mkdir(dir, { recursive: true });
|
|
10825
11072
|
}
|
|
10826
11073
|
const flag = options.append ? "a" : "w";
|
|
10827
|
-
if (options.append &&
|
|
11074
|
+
if (options.append && existsSync6(targetPath)) {
|
|
10828
11075
|
await fs2.appendFile(targetPath, `
|
|
10829
11076
|
|
|
10830
11077
|
---
|
|
@@ -11767,7 +12014,7 @@ function findBiliProxyProviders(provider) {
|
|
|
11767
12014
|
|
|
11768
12015
|
// lib/update.ts
|
|
11769
12016
|
import { readFile as readFile2, rm } from "fs/promises";
|
|
11770
|
-
import { basename, dirname as dirname4, join as
|
|
12017
|
+
import { basename, dirname as dirname4, join as join7 } from "path";
|
|
11771
12018
|
import { fileURLToPath } from "url";
|
|
11772
12019
|
var PACKAGE_NAME = "opencode-acp";
|
|
11773
12020
|
function startAutoUpdate(ctx, enabled, logger) {
|
|
@@ -11814,7 +12061,7 @@ async function checkAutoUpdate(signal, logger) {
|
|
|
11814
12061
|
logger?.info("Auto-update skipped: package dir not found");
|
|
11815
12062
|
return { updated: false };
|
|
11816
12063
|
}
|
|
11817
|
-
const pkg = await readPackageJson(
|
|
12064
|
+
const pkg = await readPackageJson(join7(packageDir, "package.json"));
|
|
11818
12065
|
if (!pkg?.name || !pkg.version) {
|
|
11819
12066
|
logger?.info("Auto-update skipped: package.json unreadable", { packageDir });
|
|
11820
12067
|
return { updated: false };
|
|
@@ -11855,7 +12102,7 @@ async function checkAutoUpdate(signal, logger) {
|
|
|
11855
12102
|
async function findPackageDir(name) {
|
|
11856
12103
|
let dir = dirname4(fileURLToPath(import.meta.url));
|
|
11857
12104
|
for (; ; ) {
|
|
11858
|
-
const pkg = await readPackageJson(
|
|
12105
|
+
const pkg = await readPackageJson(join7(dir, "package.json"));
|
|
11859
12106
|
if (pkg?.name === name) return dir;
|
|
11860
12107
|
const parent = dirname4(dir);
|
|
11861
12108
|
if (parent === dir) return void 0;
|
|
@@ -11867,7 +12114,7 @@ async function updateTarget(packageDir, name) {
|
|
|
11867
12114
|
const nodeModulesDir = basename(packageParent).startsWith("@") ? dirname4(packageParent) : packageParent;
|
|
11868
12115
|
if (basename(nodeModulesDir) !== "node_modules") return void 0;
|
|
11869
12116
|
const wrapperDir = dirname4(nodeModulesDir);
|
|
11870
|
-
const wrapperPkg = await readPackageJson(
|
|
12117
|
+
const wrapperPkg = await readPackageJson(join7(wrapperDir, "package.json"));
|
|
11871
12118
|
const spec = wrapperSpec(wrapperDir, name) ?? wrapperPkg?.dependencies?.[name];
|
|
11872
12119
|
if (!spec || !isAutoUpdatableSpec(spec)) return void 0;
|
|
11873
12120
|
return { removeDir: wrapperDir, spec };
|
|
@@ -11979,14 +12226,14 @@ var server = (async (ctx) => {
|
|
|
11979
12226
|
}
|
|
11980
12227
|
const logger = new Logger(config.debug, config.debug ? "debug" : config.logLevel);
|
|
11981
12228
|
logger.info("ACP plugin initialized", {
|
|
11982
|
-
version: true ? "1.
|
|
12229
|
+
version: true ? "1.16.0-pr.376.121" : "dev",
|
|
11983
12230
|
workspace: ctx.directory,
|
|
11984
12231
|
logLevel: logger.level,
|
|
11985
12232
|
debug: config.debug,
|
|
11986
12233
|
autoUpdate: config.autoUpdate,
|
|
11987
12234
|
secureMode: isSecureMode()
|
|
11988
12235
|
});
|
|
11989
|
-
const registry4 = new SessionStateRegistry(logger);
|
|
12236
|
+
const registry4 = new SessionStateRegistry(logger, ctx.directory);
|
|
11990
12237
|
const prompts = new PromptStore(logger, ctx.directory, config.experimental.customPrompts);
|
|
11991
12238
|
const hostPermissions = {
|
|
11992
12239
|
global: void 0,
|