@tryarcanist/cli 0.1.13 → 0.1.15
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 +129 -17
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -427,6 +427,7 @@ async function messageCommand(sessionId, promptArg, options = {}, command) {
|
|
|
427
427
|
|
|
428
428
|
// ../../shared/transcript/projector.ts
|
|
429
429
|
var DUPLICATE_TEXT_DELTA_MIN_CHARS = 24;
|
|
430
|
+
var SUBAGENT_EVENT_TYPES = /* @__PURE__ */ new Set(["subagent_start", "subagent_complete", "subagent_tool_call", "subagent_text"]);
|
|
430
431
|
var RAW_OPENCODE_NOISE = /* @__PURE__ */ new Set([
|
|
431
432
|
"session.updated",
|
|
432
433
|
"session.diff",
|
|
@@ -440,6 +441,12 @@ function shouldAppendTextDelta(existingText, incomingText) {
|
|
|
440
441
|
if (incomingText.length < DUPLICATE_TEXT_DELTA_MIN_CHARS) return true;
|
|
441
442
|
return !existingText.endsWith(incomingText);
|
|
442
443
|
}
|
|
444
|
+
function streamableKey(type, streamId) {
|
|
445
|
+
return `${type}:${streamId}`;
|
|
446
|
+
}
|
|
447
|
+
function resolveSegmentId(streamId, segmentOrdinal) {
|
|
448
|
+
return segmentOrdinal === 0 ? streamId : `${streamId}#${segmentOrdinal}`;
|
|
449
|
+
}
|
|
443
450
|
function isRecord(value) {
|
|
444
451
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
445
452
|
}
|
|
@@ -474,6 +481,8 @@ function normalizeToolStatus(value) {
|
|
|
474
481
|
function flattenSessionEvents(raw) {
|
|
475
482
|
const merged = [];
|
|
476
483
|
const streamableIndexById = /* @__PURE__ */ new Map();
|
|
484
|
+
const segmentOrdinalByStreamId = /* @__PURE__ */ new Map();
|
|
485
|
+
const concatByStreamId = /* @__PURE__ */ new Map();
|
|
477
486
|
const toolCallIndexById = /* @__PURE__ */ new Map();
|
|
478
487
|
const questionIndexById = /* @__PURE__ */ new Map();
|
|
479
488
|
for (const event of raw) {
|
|
@@ -526,7 +535,11 @@ function flattenSessionEvents(raw) {
|
|
|
526
535
|
message: typeof data?.message === "string" ? data.message : "Retrying...",
|
|
527
536
|
...typeof data?.nextRetryAt === "string" ? { nextRetryAt: data.nextRetryAt } : {},
|
|
528
537
|
...typeof data?.provider === "string" ? { provider: data.provider } : {},
|
|
529
|
-
...typeof data?.errorCode === "string" ? { errorCode: data.errorCode } : {}
|
|
538
|
+
...typeof data?.errorCode === "string" ? { errorCode: data.errorCode } : {},
|
|
539
|
+
...typeof data?.scope === "string" ? { scope: data.scope } : {},
|
|
540
|
+
...typeof data?.maxAttempts === "number" ? { maxAttempts: data.maxAttempts } : {},
|
|
541
|
+
...typeof data?.reason === "string" ? { reason: data.reason } : {},
|
|
542
|
+
...typeof data?.retryAfterMs === "number" ? { retryAfterMs: data.retryAfterMs } : {}
|
|
530
543
|
});
|
|
531
544
|
continue;
|
|
532
545
|
}
|
|
@@ -565,21 +578,30 @@ function flattenSessionEvents(raw) {
|
|
|
565
578
|
continue;
|
|
566
579
|
}
|
|
567
580
|
if (event.type === "reasoning") {
|
|
568
|
-
const
|
|
581
|
+
const streamId = resolveEventId(data, "reasoning", merged.length);
|
|
582
|
+
const key = streamableKey("reasoning", streamId);
|
|
569
583
|
const text = resolveTextValue(data);
|
|
570
584
|
const promptId = resolvePromptId(data);
|
|
571
|
-
const
|
|
572
|
-
|
|
585
|
+
const existingText = concatByStreamId.get(key) ?? "";
|
|
586
|
+
const existingIdx = streamableIndexById.get(key);
|
|
587
|
+
if (existingIdx !== void 0 && existingIdx === merged.length - 1) {
|
|
573
588
|
const existing = merged[existingIdx];
|
|
574
|
-
if (existing.type === "reasoning" && shouldAppendTextDelta(
|
|
589
|
+
if (existing.type === "reasoning" && shouldAppendTextDelta(existingText, text)) {
|
|
575
590
|
existing.text += text;
|
|
591
|
+
concatByStreamId.set(key, existingText + text);
|
|
576
592
|
if (!existing.promptId && promptId) existing.promptId = promptId;
|
|
577
593
|
}
|
|
578
594
|
} else {
|
|
579
|
-
|
|
595
|
+
if (existingText && !shouldAppendTextDelta(existingText, text)) continue;
|
|
596
|
+
const previousOrdinal = segmentOrdinalByStreamId.get(key);
|
|
597
|
+
const segmentOrdinal = previousOrdinal === void 0 ? 0 : previousOrdinal + 1;
|
|
598
|
+
segmentOrdinalByStreamId.set(key, segmentOrdinal);
|
|
599
|
+
streamableIndexById.set(key, merged.length);
|
|
600
|
+
concatByStreamId.set(key, existingText + text);
|
|
580
601
|
merged.push({
|
|
581
602
|
type: "reasoning",
|
|
582
|
-
id,
|
|
603
|
+
id: resolveSegmentId(streamId, segmentOrdinal),
|
|
604
|
+
...segmentOrdinal > 0 ? { streamId } : {},
|
|
583
605
|
text,
|
|
584
606
|
...promptId ? { promptId } : {}
|
|
585
607
|
});
|
|
@@ -629,21 +651,30 @@ function flattenSessionEvents(raw) {
|
|
|
629
651
|
continue;
|
|
630
652
|
}
|
|
631
653
|
if (event.type === "text") {
|
|
632
|
-
const
|
|
654
|
+
const streamId = resolveEventId(data, "text", merged.length);
|
|
655
|
+
const key = streamableKey("text", streamId);
|
|
633
656
|
const text = resolveTextValue(data);
|
|
634
657
|
const promptId = resolvePromptId(data);
|
|
635
|
-
const
|
|
636
|
-
|
|
658
|
+
const existingText = concatByStreamId.get(key) ?? "";
|
|
659
|
+
const existingIdx = streamableIndexById.get(key);
|
|
660
|
+
if (existingIdx !== void 0 && existingIdx === merged.length - 1) {
|
|
637
661
|
const existing = merged[existingIdx];
|
|
638
|
-
if (existing.type === "text" && shouldAppendTextDelta(
|
|
662
|
+
if (existing.type === "text" && shouldAppendTextDelta(existingText, text)) {
|
|
639
663
|
existing.text += text;
|
|
664
|
+
concatByStreamId.set(key, existingText + text);
|
|
640
665
|
if (!existing.promptId && promptId) existing.promptId = promptId;
|
|
641
666
|
}
|
|
642
667
|
} else {
|
|
643
|
-
|
|
668
|
+
if (existingText && !shouldAppendTextDelta(existingText, text)) continue;
|
|
669
|
+
const previousOrdinal = segmentOrdinalByStreamId.get(key);
|
|
670
|
+
const segmentOrdinal = previousOrdinal === void 0 ? 0 : previousOrdinal + 1;
|
|
671
|
+
segmentOrdinalByStreamId.set(key, segmentOrdinal);
|
|
672
|
+
streamableIndexById.set(key, merged.length);
|
|
673
|
+
concatByStreamId.set(key, existingText + text);
|
|
644
674
|
merged.push({
|
|
645
675
|
type: "text",
|
|
646
|
-
id,
|
|
676
|
+
id: resolveSegmentId(streamId, segmentOrdinal),
|
|
677
|
+
...segmentOrdinal > 0 ? { streamId } : {},
|
|
647
678
|
text,
|
|
648
679
|
...promptId ? { promptId } : {}
|
|
649
680
|
});
|
|
@@ -737,6 +768,65 @@ function getEmbeddedTerminalHistory(raw) {
|
|
|
737
768
|
function resolveAuthoritativePromptEvents(raw) {
|
|
738
769
|
return getEmbeddedTerminalHistory(raw) ?? raw;
|
|
739
770
|
}
|
|
771
|
+
function extractSubagentInfo(raw) {
|
|
772
|
+
const empty = {
|
|
773
|
+
names: /* @__PURE__ */ new Map(),
|
|
774
|
+
activity: /* @__PURE__ */ new Map(),
|
|
775
|
+
toolToChildSessions: /* @__PURE__ */ new Map(),
|
|
776
|
+
subagentUsage: /* @__PURE__ */ new Map()
|
|
777
|
+
};
|
|
778
|
+
if (!raw.some((event) => SUBAGENT_EVENT_TYPES.has(event.type))) return empty;
|
|
779
|
+
const { names, activity, toolToChildSessions, subagentUsage } = empty;
|
|
780
|
+
for (const event of raw) {
|
|
781
|
+
const data = event.data;
|
|
782
|
+
if (!data) continue;
|
|
783
|
+
const childSessionId = typeof data.childSessionId === "string" ? data.childSessionId : void 0;
|
|
784
|
+
const parentToolId = typeof data.parentToolId === "string" ? data.parentToolId : void 0;
|
|
785
|
+
const key = childSessionId || parentToolId;
|
|
786
|
+
if (!key) continue;
|
|
787
|
+
if (event.type === "subagent_start" || event.type === "subagent_complete") {
|
|
788
|
+
const name = typeof data.name === "string" ? data.name : "";
|
|
789
|
+
if (name) names.set(key, name);
|
|
790
|
+
if (parentToolId && childSessionId) {
|
|
791
|
+
const existing = toolToChildSessions.get(parentToolId) ?? [];
|
|
792
|
+
if (!existing.includes(childSessionId)) existing.push(childSessionId);
|
|
793
|
+
toolToChildSessions.set(parentToolId, existing);
|
|
794
|
+
}
|
|
795
|
+
if (event.type === "subagent_complete" && isRecord(data.tokenUsage)) {
|
|
796
|
+
subagentUsage.set(key, {
|
|
797
|
+
input: Number(data.tokenUsage.input ?? 0),
|
|
798
|
+
output: Number(data.tokenUsage.output ?? 0),
|
|
799
|
+
cacheRead: Number(data.tokenUsage.cacheRead ?? 0),
|
|
800
|
+
cacheWrite: Number(data.tokenUsage.cacheWrite ?? 0)
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
continue;
|
|
804
|
+
}
|
|
805
|
+
if (event.type === "subagent_tool_call") {
|
|
806
|
+
const items = activity.get(key) ?? [];
|
|
807
|
+
items.push({
|
|
808
|
+
type: "tool",
|
|
809
|
+
tool: typeof data.tool === "string" ? data.tool : "unknown",
|
|
810
|
+
summary: typeof data.summary === "string" ? data.summary : ""
|
|
811
|
+
});
|
|
812
|
+
activity.set(key, items);
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
if (event.type === "subagent_text") {
|
|
816
|
+
const delta = typeof data.delta === "string" ? data.delta : typeof data.text === "string" ? data.text : "";
|
|
817
|
+
if (!delta) continue;
|
|
818
|
+
const items = activity.get(key) ?? [];
|
|
819
|
+
const last = items[items.length - 1];
|
|
820
|
+
if (last?.type === "text") {
|
|
821
|
+
items[items.length - 1] = { ...last, text: last.text + delta };
|
|
822
|
+
} else {
|
|
823
|
+
items.push({ type: "text", text: delta });
|
|
824
|
+
}
|
|
825
|
+
activity.set(key, items);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
return { names, activity, toolToChildSessions, subagentUsage };
|
|
829
|
+
}
|
|
740
830
|
|
|
741
831
|
// src/utils/session-output.ts
|
|
742
832
|
function formatDate(value) {
|
|
@@ -744,13 +834,33 @@ function formatDate(value) {
|
|
|
744
834
|
if (Number.isNaN(date.getTime())) return value;
|
|
745
835
|
return date.toISOString().replace("T", " ").replace(/\.\d+Z$/, " UTC");
|
|
746
836
|
}
|
|
747
|
-
function
|
|
837
|
+
function renderSubagentActivityItem(item) {
|
|
838
|
+
if (item.type === "text") return ` - ${item.text}
|
|
839
|
+
`;
|
|
840
|
+
return ` - **Tool:** ${item.tool}${item.summary ? ` - ${item.summary}` : ""}
|
|
841
|
+
`;
|
|
842
|
+
}
|
|
843
|
+
function renderSubagentActivityForTool(toolId, subagents) {
|
|
844
|
+
const childSessionIds = subagents.toolToChildSessions.get(toolId) ?? [];
|
|
845
|
+
if (childSessionIds.length === 0) return "";
|
|
846
|
+
const lines = [];
|
|
847
|
+
for (const childSessionId of childSessionIds) {
|
|
848
|
+
const name = subagents.names.get(childSessionId);
|
|
849
|
+
lines.push(` - **Subagent:** ${name ?? childSessionId}`);
|
|
850
|
+
for (const item of subagents.activity.get(childSessionId) ?? []) {
|
|
851
|
+
lines.push(renderSubagentActivityItem(item).trimEnd());
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
return `${lines.join("\n")}
|
|
855
|
+
`;
|
|
856
|
+
}
|
|
857
|
+
function renderTranscriptEvent(event, subagents) {
|
|
748
858
|
switch (event.type) {
|
|
749
859
|
case "text":
|
|
750
860
|
return event.text;
|
|
751
861
|
case "tool_call":
|
|
752
862
|
return `**Tool call:** ${event.tool}${event.summary ? ` - ${event.summary}` : ""}${event.toolStatus ? ` (${event.toolStatus})` : ""}
|
|
753
|
-
`;
|
|
863
|
+
${subagents ? renderSubagentActivityForTool(event.id, subagents) : ""}`;
|
|
754
864
|
case "reasoning":
|
|
755
865
|
return `<details><summary>Reasoning</summary>
|
|
756
866
|
|
|
@@ -813,7 +923,9 @@ function renderSessionTranscript(exportData) {
|
|
|
813
923
|
for (let i = 0; i < exportData.prompts.length; i++) {
|
|
814
924
|
const prompt = exportData.prompts[i];
|
|
815
925
|
const rawEvents = eventBuckets.get(prompt.id) ?? [];
|
|
816
|
-
const
|
|
926
|
+
const authoritativeEvents = resolveAuthoritativePromptEvents(rawEvents);
|
|
927
|
+
const events = flattenSessionEvents(authoritativeEvents);
|
|
928
|
+
const subagents = extractSubagentInfo(authoritativeEvents);
|
|
817
929
|
lines.push("---\n");
|
|
818
930
|
lines.push(`## Turn ${i + 1}
|
|
819
931
|
`);
|
|
@@ -827,7 +939,7 @@ function renderSessionTranscript(exportData) {
|
|
|
827
939
|
lines.push("**Assistant:**\n");
|
|
828
940
|
let pendingText = "";
|
|
829
941
|
for (const event of events) {
|
|
830
|
-
const rendered = renderTranscriptEvent(event);
|
|
942
|
+
const rendered = renderTranscriptEvent(event, subagents);
|
|
831
943
|
if (!rendered) continue;
|
|
832
944
|
if (event.type === "text") {
|
|
833
945
|
pendingText += rendered;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tryarcanist/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"description": "CLI for Arcanist — create and manage coding agent sessions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"prepublishOnly": "npm run build"
|
|
16
16
|
},
|
|
17
17
|
"publishConfig": {
|
|
18
|
-
"access": "
|
|
18
|
+
"access": "restricted"
|
|
19
19
|
},
|
|
20
20
|
"repository": {
|
|
21
21
|
"type": "git",
|