@oar-sh/oar 0.9.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/.github/extensions/web-relay/README.md +190 -0
- package/.github/extensions/web-relay/extension.mjs +972 -0
- package/.github/extensions/web-relay/model-api/model-switching.mjs +338 -0
- package/.github/extensions/web-relay/polling/heartbeat.mjs +55 -0
- package/.github/extensions/web-relay/polling/polling-loop.mjs +1105 -0
- package/.github/extensions/web-relay/runtime/api-client.mjs +34 -0
- package/.github/extensions/web-relay/runtime/banner-state.mjs +126 -0
- package/.github/extensions/web-relay/runtime/config-loader.mjs +104 -0
- package/.github/extensions/web-relay/runtime/debug-log.mjs +27 -0
- package/.github/extensions/web-relay/runtime/send-and-wait-errors.mjs +170 -0
- package/.github/extensions/web-relay/runtime/session-binding.mjs +78 -0
- package/.github/extensions/web-relay/runtime/session-io.mjs +349 -0
- package/.github/extensions/web-relay/runtime/session-join-retry.mjs +44 -0
- package/.github/extensions/web-relay/runtime/session-registry.mjs +29 -0
- package/.github/extensions/web-relay/runtime/session-runtime-manager.mjs +156 -0
- package/.github/extensions/web-relay/runtime/session-sync-bridge.mjs +74 -0
- package/.github/extensions/web-relay/runtime/worker-websocket-link.mjs +325 -0
- package/.github/extensions/web-relay/runtime/workspace-root.mjs +55 -0
- package/.github/extensions/web-relay/server-lifecycle/managed-server.mjs +386 -0
- package/.github/extensions/web-relay/skills/prompt-context.mjs +160 -0
- package/.github/extensions/web-relay/skills/question-bridge.mjs +141 -0
- package/.github/extensions/web-relay/skills/question-routing-hooks.mjs +463 -0
- package/.github/extensions/web-relay/skills/reasoning-stream.mjs +285 -0
- package/.github/extensions/web-relay/skills/subagent-lifecycle.mjs +345 -0
- package/.github/extensions/web-relay/skills/tool-activity.mjs +243 -0
- package/.github/extensions/web-relay/utils/process-utils.mjs +25 -0
- package/.github/extensions/web-relay/utils/token-generation.mjs +58 -0
- package/CHANGELOG.md +25 -0
- package/LICENSE +21 -0
- package/README.md +717 -0
- package/bin/bat/oar.bat +46 -0
- package/bin/oar.js +665 -0
- package/package.json +68 -0
- package/server/README.md +1379 -0
- package/server/claude-worker/claude-attachments.mjs +134 -0
- package/server/claude-worker/claude-preview-tool.mjs +57 -0
- package/server/claude-worker/claude-sdk-adapter.mjs +420 -0
- package/server/claude-worker/claude-session-process.mjs +2391 -0
- package/server/claude-worker/claude-session-test-harness.mjs +236 -0
- package/server/claude-worker/claude-session-worker.mjs +186 -0
- package/server/claude-worker/claude-transcript-relocator.mjs +189 -0
- package/server/claude-worker/claude-turn-publisher.mjs +291 -0
- package/server/claude-worker/fixtures/workflow-run-record.json +1 -0
- package/server/claude-worker/sdk-message-normalizer.mjs +629 -0
- package/server/claude-worker/workflow-progress-digest.mjs +269 -0
- package/server/copilot-sdk-runtime.mjs +126 -0
- package/server/copilot-worker/copilot-attachments.mjs +97 -0
- package/server/copilot-worker/copilot-byok-provider.mjs +121 -0
- package/server/copilot-worker/copilot-continuation-signals.mjs +349 -0
- package/server/copilot-worker/copilot-plan-board.mjs +110 -0
- package/server/copilot-worker/copilot-prompt-context.mjs +86 -0
- package/server/copilot-worker/copilot-question-bridge.mjs +216 -0
- package/server/copilot-worker/copilot-sdk-adapter.mjs +494 -0
- package/server/copilot-worker/copilot-sdk-event-normalizer.mjs +800 -0
- package/server/copilot-worker/copilot-sdk-session-process.mjs +1729 -0
- package/server/copilot-worker/copilot-sdk-session-worker.mjs +167 -0
- package/server/copilot-worker/copilot-sdk-test-harness.mjs +329 -0
- package/server/copilot-worker/fixtures/abort-turn.json +588 -0
- package/server/copilot-worker/fixtures/ask-user-turn.json +1154 -0
- package/server/copilot-worker/fixtures/background-timer-continuation.json +445 -0
- package/server/copilot-worker/fixtures/background-timer-turn.json +497 -0
- package/server/copilot-worker/fixtures/happy-turn.json +566 -0
- package/server/copilot-worker/fixtures/quota-turn.json +647 -0
- package/server/copilot-worker/fixtures/reasoning-turn.json +625 -0
- package/server/copilot-worker/fixtures/resume-turn.json +700 -0
- package/server/copilot-worker/fixtures/subagent-turn.json +1378 -0
- package/server/copilot-worker/fixtures/tool-permission-turn.json +1293 -0
- package/server/cursor-worker/cursor-ask-user-tool.mjs +83 -0
- package/server/cursor-worker/cursor-attachments.mjs +88 -0
- package/server/cursor-worker/cursor-context-usage.mjs +85 -0
- package/server/cursor-worker/cursor-preview-tool.mjs +44 -0
- package/server/cursor-worker/cursor-sdk-adapter.mjs +554 -0
- package/server/cursor-worker/cursor-session-worker.mjs +145 -0
- package/server/cursor-worker/cursor-subagent-roster.mjs +81 -0
- package/server/cursor-worker/cursor-turn-runner.mjs +717 -0
- package/server/cursor-worker/sdk-message-normalizer.mjs +485 -0
- package/server/db-schema.mjs +932 -0
- package/server/features.mjs +106 -0
- package/server/grok-worker/acp-client.mjs +434 -0
- package/server/grok-worker/acp-host-services.mjs +277 -0
- package/server/grok-worker/grok-context-usage.mjs +77 -0
- package/server/grok-worker/grok-sdk-adapter.mjs +334 -0
- package/server/grok-worker/grok-session-worker.mjs +145 -0
- package/server/grok-worker/grok-turn-runner.mjs +523 -0
- package/server/grok-worker/sdk-message-normalizer.mjs +311 -0
- package/server/migrations/0001-add-structured-answer.mjs +80 -0
- package/server/migrations/0002-recent-workspace-roots-path-key.mjs +121 -0
- package/server/migrations/0003-push-subscriptions.mjs +87 -0
- package/server/public/app/action-confirmations.js +267 -0
- package/server/public/app/activity-replay-state.mjs +149 -0
- package/server/public/app/api-client.js +1082 -0
- package/server/public/app/ask-user-view.js +545 -0
- package/server/public/app/attachments-view.js +2112 -0
- package/server/public/app/auto-compact-window-options.mjs +59 -0
- package/server/public/app/background-tasks-view.mjs +494 -0
- package/server/public/app/bootstrap.js +4347 -0
- package/server/public/app/claude-auth-ui.js +394 -0
- package/server/public/app/claude-thinking-options.mjs +38 -0
- package/server/public/app/cli-install-ui.js +455 -0
- package/server/public/app/code-copy.mjs +85 -0
- package/server/public/app/composer-attachment-cache.mjs +151 -0
- package/server/public/app/composer-control-state.mjs +107 -0
- package/server/public/app/composer-paste.mjs +132 -0
- package/server/public/app/context-tier-options.mjs +68 -0
- package/server/public/app/context-usage-view.mjs +338 -0
- package/server/public/app/conversation-draft-timestamp-utils.mjs +11 -0
- package/server/public/app/conversation-preferences.mjs +91 -0
- package/server/public/app/conversation-provider-indicator.mjs +88 -0
- package/server/public/app/conversation-view.js +2898 -0
- package/server/public/app/cwd-menu-placement.mjs +85 -0
- package/server/public/app/cwd-picker.js +605 -0
- package/server/public/app/emoji-view.js +101 -0
- package/server/public/app/external-link-policy.mjs +80 -0
- package/server/public/app/file-preview-navigation.mjs +51 -0
- package/server/public/app/font-scaling.js +262 -0
- package/server/public/app/git-changes-view.js +339 -0
- package/server/public/app/git-diff-model.mjs +109 -0
- package/server/public/app/grok-auth-ui.js +387 -0
- package/server/public/app/image-downscale.mjs +174 -0
- package/server/public/app/in-flight-snapshot.mjs +40 -0
- package/server/public/app/infinite-loader.js +164 -0
- package/server/public/app/journal-view.js +1141 -0
- package/server/public/app/known-cwd-options.mjs +41 -0
- package/server/public/app/launch-cwd-path.mjs +23 -0
- package/server/public/app/live-message-dedupe.mjs +57 -0
- package/server/public/app/message-search-view.js +242 -0
- package/server/public/app/model-selector-options.mjs +31 -0
- package/server/public/app/model-vision-support.mjs +56 -0
- package/server/public/app/new-conversation-model-choice.mjs +67 -0
- package/server/public/app/plan-usage-view.mjs +307 -0
- package/server/public/app/preview-cards.mjs +273 -0
- package/server/public/app/preview-command.mjs +103 -0
- package/server/public/app/push-settings.js +370 -0
- package/server/public/app/pwa-install.js +456 -0
- package/server/public/app/question-schema-view.mjs +71 -0
- package/server/public/app/reasoning-effort-labels.mjs +42 -0
- package/server/public/app/relay-board-view.js +117 -0
- package/server/public/app/relay-dot-state.mjs +69 -0
- package/server/public/app/relay-error-ctas.mjs +52 -0
- package/server/public/app/relay-prompt-sanitizer.mjs +114 -0
- package/server/public/app/relay-thoughts.mjs +98 -0
- package/server/public/app/relay-watchdog-policy.mjs +29 -0
- package/server/public/app/repo-browser-preferences.mjs +79 -0
- package/server/public/app/repo-browser-tree-state.mjs +97 -0
- package/server/public/app/router.js +657 -0
- package/server/public/app/selection-guard.mjs +93 -0
- package/server/public/app/settings-modal.js +1484 -0
- package/server/public/app/settings-tabs.js +129 -0
- package/server/public/app/slash-autocomplete.mjs +188 -0
- package/server/public/app/slash-commands.mjs +183 -0
- package/server/public/app/socket-handlers.js +703 -0
- package/server/public/app/status-store.mjs +242 -0
- package/server/public/app/status-view.mjs +315 -0
- package/server/public/app/store.js +1454 -0
- package/server/public/app/stream-state.mjs +73 -0
- package/server/public/app/streaming-dom-patch.mjs +23 -0
- package/server/public/app/sync-outbox.mjs +208 -0
- package/server/public/app/tap-actions.js +62 -0
- package/server/public/app/thread-order.mjs +54 -0
- package/server/public/app/tmux-inspector-view.js +422 -0
- package/server/public/app/transcript-separators.mjs +282 -0
- package/server/public/app-icon-192.png +0 -0
- package/server/public/app-icon-512.png +0 -0
- package/server/public/app-icon.svg +4 -0
- package/server/public/favicon.ico +0 -0
- package/server/public/index.html +5743 -0
- package/server/public/manifest.webmanifest +32 -0
- package/server/public/sw.js +307 -0
- package/server/relay-exit-codes.mjs +4 -0
- package/server/relay-self-restart.mjs +170 -0
- package/server/relay-tools.md +27 -0
- package/server/relay.mjs +1026 -0
- package/server/repositories/image-conversation-repository.mjs +317 -0
- package/server/repositories/message-repository.mjs +434 -0
- package/server/repositories/question-repository.mjs +161 -0
- package/server/repositories/session-repository.mjs +320 -0
- package/server/repositories/share-visibility-statements.mjs +24 -0
- package/server/routes/ask-user-routes.mjs +292 -0
- package/server/routes/cache-routes.mjs +44 -0
- package/server/routes/git-routes.mjs +92 -0
- package/server/routes/messages-routes-test-harness.mjs +111 -0
- package/server/routes/messages-routes.mjs +7107 -0
- package/server/routes/preview-routes.mjs +108 -0
- package/server/routes/push-routes.mjs +59 -0
- package/server/routes/relay-board-routes.mjs +268 -0
- package/server/routes/sessions-routes.mjs +6150 -0
- package/server/server-runtime.mjs +7463 -0
- package/server/server.js +24 -0
- package/server/services/ask-user-routing-service.mjs +117 -0
- package/server/services/cache-rebuild-service.mjs +236 -0
- package/server/services/claude-auth-service.mjs +578 -0
- package/server/services/claude-context-usage.mjs +240 -0
- package/server/services/claude-session-root-service.mjs +228 -0
- package/server/services/cli-install-service.mjs +1152 -0
- package/server/services/cli-process-runner.mjs +181 -0
- package/server/services/cloudflared-tunnel-service.mjs +358 -0
- package/server/services/context-snapshot-service.mjs +499 -0
- package/server/services/context-usage-view.mjs +160 -0
- package/server/services/conversation-preferences-service.mjs +208 -0
- package/server/services/cursor-dashboard-usage.mjs +217 -0
- package/server/services/cursor-session-root-service.mjs +62 -0
- package/server/services/delete-archive-service.mjs +154 -0
- package/server/services/drives-path-helpers.mjs +66 -0
- package/server/services/fixtures/claude-auth-stub.sh +62 -0
- package/server/services/fixtures/cli-install-stub.sh +75 -0
- package/server/services/fixtures/grok-stub.sh +86 -0
- package/server/services/generated-image-cleanup-service.mjs +114 -0
- package/server/services/git-changes-service.mjs +195 -0
- package/server/services/github-billing-usage.mjs +133 -0
- package/server/services/grok-auth-service.mjs +593 -0
- package/server/services/grok-billing-usage.mjs +120 -0
- package/server/services/image-context-service.mjs +106 -0
- package/server/services/image-operation-service.mjs +135 -0
- package/server/services/keyed-mutex-service.mjs +82 -0
- package/server/services/mime-sniffer.mjs +118 -0
- package/server/services/oar-cli-helpers.mjs +109 -0
- package/server/services/oar-state-migration-service.mjs +243 -0
- package/server/services/plan-usage-claude.mjs +387 -0
- package/server/services/plan-usage-contract.mjs +271 -0
- package/server/services/plan-usage-copilot.mjs +421 -0
- package/server/services/plan-usage-cursor.mjs +566 -0
- package/server/services/plan-usage-grok.mjs +436 -0
- package/server/services/plan-usage-service.mjs +487 -0
- package/server/services/preview-health-probe.mjs +82 -0
- package/server/services/preview-proxy-server.mjs +498 -0
- package/server/services/preview-registry-service.mjs +457 -0
- package/server/services/preview-static-handler.mjs +237 -0
- package/server/services/provider-model-selection.mjs +91 -0
- package/server/services/provider-reasoning-effort.mjs +94 -0
- package/server/services/push-active-device-service.mjs +57 -0
- package/server/services/push-dispatch-service.mjs +459 -0
- package/server/services/rate-limit-service.mjs +63 -0
- package/server/services/relay-bridge-owner-service.mjs +84 -0
- package/server/services/relay-cli-launcher-service.mjs +209 -0
- package/server/services/relay-prompt-sanitizer.mjs +137 -0
- package/server/services/relay-queue-gate-service.mjs +38 -0
- package/server/services/relay-restart-orchestrator-service.mjs +935 -0
- package/server/services/relay-singleton-guard.mjs +124 -0
- package/server/services/safe-served-content.mjs +57 -0
- package/server/services/sdk-session-import-service.mjs +271 -0
- package/server/services/sdk-session-sync-service.mjs +207 -0
- package/server/services/session-history-refresh-service.mjs +497 -0
- package/server/services/session-transcript-service.mjs +419 -0
- package/server/services/session-worker-launch-service.mjs +786 -0
- package/server/services/session-worker-process-service.mjs +319 -0
- package/server/services/session-worker-registry-service.mjs +194 -0
- package/server/services/session-worker-stop-service.mjs +141 -0
- package/server/services/session-worker-supervisor-service.mjs +898 -0
- package/server/services/session-worker-websocket-service.mjs +483 -0
- package/server/services/ssh-tunnel-manager-service.mjs +423 -0
- package/server/services/status-event-service.mjs +158 -0
- package/server/services/tmux-inspector-access-policy.mjs +77 -0
- package/server/services/tmux-inspector-socket-service.mjs +191 -0
- package/server/services/tmux-inspector-stream-service.mjs +552 -0
- package/server/services/tunnel-worker-path-guard.mjs +107 -0
- package/server/services/upload-sweep.mjs +60 -0
- package/server/services/usage-snapshot-helpers.mjs +209 -0
- package/server/services/windows-autostart-service.mjs +115 -0
- package/server/services/workspace-root-defaults-service.mjs +34 -0
- package/server/services/workspace-root-path-policy.mjs +196 -0
- package/server/services/workspace-root-relaunch-service.mjs +132 -0
- package/server/services/workspace-symlink-guard.mjs +54 -0
- package/server/tools/capture-proxy.mjs +173 -0
- package/server/tty-console-bootstrap.mjs +75 -0
- package/server/uploads/README.md +36 -0
- package/server/uploads/image_input.md +540 -0
- package/server/windows-terminal-launcher.mjs +29 -0
- package/server/workspace-root.mjs +87 -0
- package/shared/ask-user-bridge.mjs +131 -0
- package/shared/auto-compact-window.mjs +116 -0
- package/shared/background-task-timeout.mjs +52 -0
- package/shared/claude-thinking.mjs +101 -0
- package/shared/context-window-fallbacks.mjs +148 -0
- package/shared/control-poller.mjs +115 -0
- package/shared/empty-turn-completion.mjs +20 -0
- package/shared/model-catalog-freshness.mjs +11 -0
- package/shared/model-descriptors.mjs +118 -0
- package/shared/model-id.mjs +147 -0
- package/shared/model-refresh.mjs +24 -0
- package/shared/openai-reasoning.mjs +13 -0
- package/shared/plan-lines.mjs +13 -0
- package/shared/preview-instructions.mjs +77 -0
- package/shared/preview-tool-core.mjs +191 -0
- package/shared/provider-routing.mjs +29 -0
- package/shared/question-schema.mjs +315 -0
- package/shared/question-timeout.mjs +18 -0
- package/shared/stream-emit-gating.mjs +28 -0
- package/shared/subagent-run-id.mjs +17 -0
- package/shared/thought-cap.mjs +13 -0
- package/shared/tool-activity.mjs +98 -0
- package/shared/turn-ceiling.mjs +53 -0
- package/shared/worker-bootstrap.mjs +44 -0
- package/shared/worker-crash-guard.mjs +60 -0
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
formatStoreMemoryActivity,
|
|
5
|
+
formatToolResultActivity,
|
|
6
|
+
parseToolPayload,
|
|
7
|
+
formatVoteMemoryActivity,
|
|
8
|
+
} from '../../shared/tool-activity.mjs';
|
|
9
|
+
|
|
10
|
+
export function createSessionTranscriptService({ fs, path, resolveSessionStateRoot }) {
|
|
11
|
+
const TRANSCRIPT_CACHE_MAX = 128;
|
|
12
|
+
const transcriptCache = new Map();
|
|
13
|
+
|
|
14
|
+
function cacheTranscript(sessionId, record) {
|
|
15
|
+
transcriptCache.set(sessionId, record);
|
|
16
|
+
if (transcriptCache.size <= TRANSCRIPT_CACHE_MAX) return;
|
|
17
|
+
let oldestKey = null;
|
|
18
|
+
let oldestAt = Number.POSITIVE_INFINITY;
|
|
19
|
+
for (const [key, value] of transcriptCache.entries()) {
|
|
20
|
+
const at = Number(value?.cachedAt || 0);
|
|
21
|
+
if (at < oldestAt) {
|
|
22
|
+
oldestAt = at;
|
|
23
|
+
oldestKey = key;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (oldestKey) transcriptCache.delete(oldestKey);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function sliceMessages(messages, options = {}) {
|
|
30
|
+
const limit = Math.max(1, Number(options.limit) || 300);
|
|
31
|
+
const beforeId = String(options.before || options.beforeMessageId || '').trim();
|
|
32
|
+
const totalCount = Array.isArray(messages) ? messages.length : 0;
|
|
33
|
+
const safeMessages = Array.isArray(messages) ? messages : [];
|
|
34
|
+
|
|
35
|
+
let startIndex = Math.max(0, safeMessages.length - limit);
|
|
36
|
+
let endIndex = safeMessages.length;
|
|
37
|
+
if (beforeId) {
|
|
38
|
+
const beforeIndex = safeMessages.findIndex((message) => String(message?.id || '').trim() === beforeId);
|
|
39
|
+
if (beforeIndex >= 0) {
|
|
40
|
+
endIndex = beforeIndex;
|
|
41
|
+
startIndex = Math.max(0, endIndex - limit);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const windowMessages = safeMessages.slice(startIndex, endIndex);
|
|
46
|
+
return {
|
|
47
|
+
messages: windowMessages,
|
|
48
|
+
hasMoreHistory: startIndex > 0,
|
|
49
|
+
historyCursor: String(windowMessages[0]?.id || '').trim() || null,
|
|
50
|
+
newestMessageId: String(windowMessages[windowMessages.length - 1]?.id || '').trim() || null,
|
|
51
|
+
totalCount,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function appendTurnActivity(map, turnId, text) {
|
|
56
|
+
const key = String(turnId || '').trim();
|
|
57
|
+
const value = String(text || '').trim();
|
|
58
|
+
if (!key || !value) return;
|
|
59
|
+
const list = map.get(key) || [];
|
|
60
|
+
if (list[list.length - 1] === value) {
|
|
61
|
+
map.set(key, list);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
list.push(value);
|
|
65
|
+
map.set(key, list);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function appendTurnThought(map, turnId, thought) {
|
|
69
|
+
const key = String(turnId || '').trim();
|
|
70
|
+
const text = String(thought?.text || '').trim();
|
|
71
|
+
if (!key || !text) return;
|
|
72
|
+
const list = map.get(key) || [];
|
|
73
|
+
if (list.some((entry) => entry.text === text)) return;
|
|
74
|
+
list.push({
|
|
75
|
+
reasoningId: `session-thought-${list.length + 1}`,
|
|
76
|
+
text,
|
|
77
|
+
done: true,
|
|
78
|
+
timestamp: String(thought?.timestamp || '').trim() || null,
|
|
79
|
+
});
|
|
80
|
+
map.set(key, list);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function parseApplyPatchTarget(rawPatch) {
|
|
84
|
+
const patch = String(rawPatch || '');
|
|
85
|
+
if (!patch) return '';
|
|
86
|
+
const match =
|
|
87
|
+
patch.match(/^\*\*\* Update File:\s+(.+)$/m)
|
|
88
|
+
|| patch.match(/^\*\*\* Add File:\s+(.+)$/m)
|
|
89
|
+
|| patch.match(/^\*\*\* Delete File:\s+(.+)$/m)
|
|
90
|
+
|| patch.match(/^\*\*\* Move to:\s+(.+)$/m);
|
|
91
|
+
if (!match) return '';
|
|
92
|
+
return String(match[1] || '').trim();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function stripSystemReminderBlocks(text) {
|
|
96
|
+
const value = String(text || '');
|
|
97
|
+
if (!value) return '';
|
|
98
|
+
return value
|
|
99
|
+
.replace(/<system_reminder>[\s\S]*?<\/system_reminder>/gi, '')
|
|
100
|
+
.replace(/<current_datetime>[\s\S]*?<\/current_datetime>/gi, '')
|
|
101
|
+
.replace(/\[Attached file references\][\s\S]*?\[\/Attached file references\]/gi, '')
|
|
102
|
+
.replace(/\[Attached image(?:s)?\s*:[\s\S]*?\]/gi, '')
|
|
103
|
+
.replace(/\[Attached file\s*:[\s\S]*?\]/gi, '')
|
|
104
|
+
.trim();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function collectEventTextSegments(value, out = [], { omitSystemReminders = false } = {}) {
|
|
108
|
+
if (value === null || value === undefined) return out;
|
|
109
|
+
if (typeof value === 'string') {
|
|
110
|
+
const text = value.trim();
|
|
111
|
+
if (text) out.push(text);
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
if (Array.isArray(value)) {
|
|
115
|
+
for (const item of value) collectEventTextSegments(item, out, { omitSystemReminders });
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
118
|
+
if (typeof value !== 'object') return out;
|
|
119
|
+
|
|
120
|
+
const type = String(value?.type || value?.kind || '').trim().toLowerCase();
|
|
121
|
+
if (
|
|
122
|
+
omitSystemReminders
|
|
123
|
+
&& ['system_reminder', 'system-reminder', 'system', 'system_message', 'system-message'].includes(type)
|
|
124
|
+
) {
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
collectEventTextSegments(value.text, out, { omitSystemReminders });
|
|
129
|
+
collectEventTextSegments(value.content, out, { omitSystemReminders });
|
|
130
|
+
collectEventTextSegments(value.input_text, out, { omitSystemReminders });
|
|
131
|
+
collectEventTextSegments(value.inputText, out, { omitSystemReminders });
|
|
132
|
+
collectEventTextSegments(value.output_text, out, { omitSystemReminders });
|
|
133
|
+
collectEventTextSegments(value.outputText, out, { omitSystemReminders });
|
|
134
|
+
collectEventTextSegments(value.message, out, { omitSystemReminders });
|
|
135
|
+
collectEventTextSegments(value.summary, out, { omitSystemReminders });
|
|
136
|
+
collectEventTextSegments(value.result, out, { omitSystemReminders });
|
|
137
|
+
collectEventTextSegments(value.response, out, { omitSystemReminders });
|
|
138
|
+
collectEventTextSegments(value.output, out, { omitSystemReminders });
|
|
139
|
+
collectEventTextSegments(value.answer, out, { omitSystemReminders });
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function normalizeEventContentText(value, { omitSystemReminders = false } = {}) {
|
|
144
|
+
const segments = collectEventTextSegments(value, [], { omitSystemReminders });
|
|
145
|
+
if (!segments.length) {
|
|
146
|
+
const fallback = String(value || '').trim();
|
|
147
|
+
return omitSystemReminders ? stripSystemReminderBlocks(fallback) : fallback;
|
|
148
|
+
}
|
|
149
|
+
const joined = segments.join('\n').trim();
|
|
150
|
+
return omitSystemReminders ? stripSystemReminderBlocks(joined) : joined;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function summarizeToolDetail(toolName, args, result) {
|
|
154
|
+
const name = String(toolName || '').trim();
|
|
155
|
+
if (!name) return '';
|
|
156
|
+
const parsedArgs = parseToolPayload(args);
|
|
157
|
+
const a = parsedArgs && typeof parsedArgs === 'object' ? parsedArgs : null;
|
|
158
|
+
const r = result && typeof result === 'object' ? result : null;
|
|
159
|
+
const lowerName = name.toLowerCase();
|
|
160
|
+
|
|
161
|
+
if (name === 'apply_patch') {
|
|
162
|
+
if (typeof args === 'string') {
|
|
163
|
+
const target = parseApplyPatchTarget(args);
|
|
164
|
+
if (target) return target;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const content = String(r?.content || '').trim();
|
|
168
|
+
const modified = content.match(/Modified\s+\d+\s+file\(s\):\s+(.+)$/m);
|
|
169
|
+
if (modified) return String(modified[1] || '').trim();
|
|
170
|
+
return '';
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (name === 'view') return String(a?.path || '').trim();
|
|
174
|
+
if (name === 'powershell') return String(a?.description || a?.command || '').trim();
|
|
175
|
+
if (name === 'rg') return String(a?.pattern || '').trim();
|
|
176
|
+
if (name === 'glob') return String(a?.pattern || '').trim();
|
|
177
|
+
if (name === 'sql') return String(a?.description || '').trim();
|
|
178
|
+
if (name === 'ask_user') return String(a?.question || '').trim();
|
|
179
|
+
if (name === 'report_intent') return String(a?.intent || '').trim();
|
|
180
|
+
if (lowerName.includes('web_fetch') || lowerName.includes('web fetch')) {
|
|
181
|
+
return String(a?.url || '').trim();
|
|
182
|
+
}
|
|
183
|
+
if (lowerName.includes('web_search') || lowerName.includes('web search')) {
|
|
184
|
+
return String(a?.query || a?.prompt || '').trim();
|
|
185
|
+
}
|
|
186
|
+
if (name === 'task') return String(a?.description || '').trim();
|
|
187
|
+
return '';
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function parseSessionEventsToMessages(events = []) {
|
|
191
|
+
const messages = [];
|
|
192
|
+
const toolNameByCallId = new Map();
|
|
193
|
+
const toolArgsByCallId = new Map();
|
|
194
|
+
const turnActivities = new Map();
|
|
195
|
+
const turnThoughts = new Map();
|
|
196
|
+
const sourceEvents = Array.isArray(events) ? events : [];
|
|
197
|
+
for (const event of sourceEvents) {
|
|
198
|
+
const data = event?.data && typeof event.data === 'object' ? event.data : null;
|
|
199
|
+
if (!data) continue;
|
|
200
|
+
const timestamp = String(event?.timestamp || '').trim() || new Date().toISOString();
|
|
201
|
+
|
|
202
|
+
if (event.type === 'session.model_change') {
|
|
203
|
+
const turnId = String(data.turnId || '').trim();
|
|
204
|
+
const model = String(data.newModel || data.model || '').trim();
|
|
205
|
+
if (turnId && model) {
|
|
206
|
+
appendTurnActivity(turnActivities, turnId, `Model selected: ${model}`);
|
|
207
|
+
}
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (event.type === 'tool.execution_start') {
|
|
212
|
+
const toolCallId = String(data.toolCallId || '').trim();
|
|
213
|
+
const toolName = String(data.toolName || '').trim();
|
|
214
|
+
if (toolCallId && toolName) {
|
|
215
|
+
toolNameByCallId.set(toolCallId, toolName);
|
|
216
|
+
if (data.arguments !== undefined) {
|
|
217
|
+
toolArgsByCallId.set(toolCallId, data.arguments);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (event.type === 'tool.execution_complete') {
|
|
224
|
+
const turnId = String(data.turnId || '').trim();
|
|
225
|
+
const toolCallId = String(data.toolCallId || '').trim();
|
|
226
|
+
const toolName = String(toolNameByCallId.get(toolCallId) || data.toolName || '').trim();
|
|
227
|
+
if (!turnId || !toolName || toolName === 'report_intent') continue;
|
|
228
|
+
const detail = summarizeToolDetail(toolName, toolArgsByCallId.get(toolCallId), data.result);
|
|
229
|
+
const toolArgs = toolArgsByCallId.get(toolCallId);
|
|
230
|
+
if (toolName.toLowerCase().includes('store_memory')) {
|
|
231
|
+
appendTurnActivity(turnActivities, turnId, formatStoreMemoryActivity(toolName, toolArgs));
|
|
232
|
+
} else if (toolName.toLowerCase().includes('vote_memory')) {
|
|
233
|
+
appendTurnActivity(turnActivities, turnId, formatVoteMemoryActivity(toolName, toolArgs));
|
|
234
|
+
} else {
|
|
235
|
+
appendTurnActivity(turnActivities, turnId, detail ? `Tool (${toolName}): ${detail}` : `Tool (${toolName})`);
|
|
236
|
+
}
|
|
237
|
+
const resultActivity = formatToolResultActivity(toolName, data.result);
|
|
238
|
+
if (resultActivity) appendTurnActivity(turnActivities, turnId, resultActivity);
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (event.type === 'user.message') {
|
|
243
|
+
const text = normalizeEventContentText(data.content, { omitSystemReminders: true });
|
|
244
|
+
if (!text) continue;
|
|
245
|
+
const attachments = (Array.isArray(data.attachments) ? data.attachments : [])
|
|
246
|
+
.map((attachment) => {
|
|
247
|
+
if (!attachment || typeof attachment !== 'object') return null;
|
|
248
|
+
const name = String(attachment.displayName || attachment.name || '').trim();
|
|
249
|
+
const type = String(attachment.mimeType || attachment.type || '').trim().toLowerCase();
|
|
250
|
+
const assetId = String(attachment.assetId || '').trim();
|
|
251
|
+
const size = Number(attachment.byteLength || attachment.size || 0);
|
|
252
|
+
if (!name && !type && !assetId) return null;
|
|
253
|
+
return {
|
|
254
|
+
name,
|
|
255
|
+
type,
|
|
256
|
+
size: Number.isFinite(size) && size >= 0 ? size : 0,
|
|
257
|
+
sdkAssetId: assetId || undefined,
|
|
258
|
+
};
|
|
259
|
+
})
|
|
260
|
+
.filter(Boolean);
|
|
261
|
+
messages.push({
|
|
262
|
+
id: String(event?.id || data?.interactionId || `user-${messages.length + 1}`),
|
|
263
|
+
role: 'user',
|
|
264
|
+
text,
|
|
265
|
+
attachments,
|
|
266
|
+
timestamp,
|
|
267
|
+
});
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (event.type === 'assistant.message') {
|
|
272
|
+
const text = normalizeEventContentText(data.content);
|
|
273
|
+
if (!text) continue;
|
|
274
|
+
const toolRequests = Array.isArray(data.toolRequests) ? data.toolRequests : [];
|
|
275
|
+
const turnId = String(data.turnId || '').trim();
|
|
276
|
+
if (toolRequests.length > 0) {
|
|
277
|
+
// Tool-request messages are intermediate reasoning sections. Preserve their full
|
|
278
|
+
// markdown text as thoughts so history refresh does not flatten or truncate them.
|
|
279
|
+
appendTurnThought(turnThoughts, turnId, { text, timestamp });
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
const activities = turnId ? (turnActivities.get(turnId) || []) : [];
|
|
283
|
+
const thoughts = turnId ? (turnThoughts.get(turnId) || []) : [];
|
|
284
|
+
messages.push({
|
|
285
|
+
id: String(data.messageId || event?.id || `assistant-${messages.length + 1}`),
|
|
286
|
+
role: 'assistant',
|
|
287
|
+
text,
|
|
288
|
+
model: String(data.model || '').trim() || undefined,
|
|
289
|
+
activities,
|
|
290
|
+
thoughts,
|
|
291
|
+
timestamp,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return messages;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function toFiniteNumber(value) {
|
|
299
|
+
const numeric = Number(value);
|
|
300
|
+
return Number.isFinite(numeric) ? numeric : null;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function parseSessionUsageSummaryFromEvents(events = []) {
|
|
304
|
+
const sourceEvents = Array.isArray(events) ? events : [];
|
|
305
|
+
let latest = null;
|
|
306
|
+
for (const event of sourceEvents) {
|
|
307
|
+
if (String(event?.type || '').trim() !== 'session.shutdown') continue;
|
|
308
|
+
const data = event?.data && typeof event.data === 'object' ? event.data : null;
|
|
309
|
+
if (!data) continue;
|
|
310
|
+
latest = { event, data };
|
|
311
|
+
}
|
|
312
|
+
if (!latest) return null;
|
|
313
|
+
const shutdownAt = String(latest.event?.timestamp || '').trim() || null;
|
|
314
|
+
const totalNanoAiu = toFiniteNumber(latest.data?.totalNanoAiu);
|
|
315
|
+
const totalPremiumRequests = toFiniteNumber(latest.data?.totalPremiumRequests);
|
|
316
|
+
const totalApiDurationMs = toFiniteNumber(latest.data?.totalApiDurationMs);
|
|
317
|
+
const requestCount = toFiniteNumber(latest.data?.modelMetrics && typeof latest.data.modelMetrics === 'object'
|
|
318
|
+
? Object.values(latest.data.modelMetrics).reduce((sum, metric) => {
|
|
319
|
+
const count = toFiniteNumber(metric?.requests?.count);
|
|
320
|
+
return sum + Number(count || 0);
|
|
321
|
+
}, 0)
|
|
322
|
+
: null);
|
|
323
|
+
const aicUsed = totalNanoAiu != null ? (totalNanoAiu / 1_000_000_000) : null;
|
|
324
|
+
return {
|
|
325
|
+
shutdownAt,
|
|
326
|
+
totalNanoAiu,
|
|
327
|
+
aicUsed,
|
|
328
|
+
totalPremiumRequests,
|
|
329
|
+
totalApiDurationMs,
|
|
330
|
+
requestCount,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function readSessionTranscriptMessages(sessionId, options = {}) {
|
|
335
|
+
const sid = String(sessionId || '').trim();
|
|
336
|
+
if (!sid) return [];
|
|
337
|
+
const root = resolveSessionStateRoot();
|
|
338
|
+
const eventsPath = path.join(root, sid, 'events.jsonl');
|
|
339
|
+
if (!fs.existsSync(eventsPath)) return [];
|
|
340
|
+
let stat = null;
|
|
341
|
+
try {
|
|
342
|
+
stat = fs.statSync(eventsPath);
|
|
343
|
+
} catch {
|
|
344
|
+
return [];
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const mtimeMs = Number(stat?.mtimeMs || 0);
|
|
348
|
+
const sizeBytes = Number(stat?.size || 0);
|
|
349
|
+
const cached = transcriptCache.get(sid);
|
|
350
|
+
if (cached && cached.mtimeMs === mtimeMs && cached.sizeBytes === sizeBytes) {
|
|
351
|
+
cached.cachedAt = Date.now();
|
|
352
|
+
const windowed = sliceMessages(cached.messages, options);
|
|
353
|
+
return options.withMeta === true ? windowed : windowed.messages;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
let content = '';
|
|
357
|
+
try {
|
|
358
|
+
content = fs.readFileSync(eventsPath, 'utf8');
|
|
359
|
+
} catch {
|
|
360
|
+
return [];
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const events = [];
|
|
364
|
+
const lines = String(content || '').split(/\r?\n/).filter(Boolean);
|
|
365
|
+
for (const line of lines) {
|
|
366
|
+
let event = null;
|
|
367
|
+
try {
|
|
368
|
+
event = JSON.parse(line);
|
|
369
|
+
} catch {
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
events.push(event);
|
|
373
|
+
}
|
|
374
|
+
const messages = parseSessionEventsToMessages(events);
|
|
375
|
+
const usageSummary = parseSessionUsageSummaryFromEvents(events);
|
|
376
|
+
|
|
377
|
+
cacheTranscript(sid, {
|
|
378
|
+
mtimeMs,
|
|
379
|
+
sizeBytes,
|
|
380
|
+
cachedAt: Date.now(),
|
|
381
|
+
messages,
|
|
382
|
+
usageSummary,
|
|
383
|
+
});
|
|
384
|
+
const windowed = sliceMessages(messages, options);
|
|
385
|
+
return options.withMeta === true ? windowed : windowed.messages;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function readSessionUsageSummary(sessionId) {
|
|
389
|
+
const sid = String(sessionId || '').trim();
|
|
390
|
+
if (!sid) return null;
|
|
391
|
+
const root = resolveSessionStateRoot();
|
|
392
|
+
const eventsPath = path.join(root, sid, 'events.jsonl');
|
|
393
|
+
if (!fs.existsSync(eventsPath)) return null;
|
|
394
|
+
let stat = null;
|
|
395
|
+
try {
|
|
396
|
+
stat = fs.statSync(eventsPath);
|
|
397
|
+
} catch {
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const mtimeMs = Number(stat?.mtimeMs || 0);
|
|
402
|
+
const sizeBytes = Number(stat?.size || 0);
|
|
403
|
+
const cached = transcriptCache.get(sid);
|
|
404
|
+
if (cached && cached.mtimeMs === mtimeMs && cached.sizeBytes === sizeBytes) {
|
|
405
|
+
cached.cachedAt = Date.now();
|
|
406
|
+
return cached.usageSummary || null;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Reuse transcript read path to refresh cache and extract summary.
|
|
410
|
+
void readSessionTranscriptMessages(sid, { limit: 1 });
|
|
411
|
+
return transcriptCache.get(sid)?.usageSummary || null;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return {
|
|
415
|
+
parseSessionEventsToMessages,
|
|
416
|
+
readSessionTranscriptMessages,
|
|
417
|
+
readSessionUsageSummary,
|
|
418
|
+
};
|
|
419
|
+
}
|