@pasko70/pibo 3.2.6 → 3.3.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/dist/agent-runtime/history.js +5 -16
- package/dist/agent-runtimes/codex-native/adapter.js +44 -14
- package/dist/agent-runtimes/codex-native/process.js +1 -1
- package/dist/agent-runtimes/codex-native/protocol-version.js +6 -6
- package/dist/agent-runtimes/omp/process.js +23 -3
- package/dist/agent-runtimes/omp/protocol-version.js +2 -0
- package/dist/agent-runtimes/pi/adapter.js +1 -1
- package/dist/agent-runtimes/pi/model-catalog.js +2 -2
- package/dist/agent-runtimes/pi/runtime.js +6 -3
- package/dist/apps/chat/chat-api-routes.js +1 -1
- package/dist/apps/chat/chat-request-normalizers.js +7 -2
- package/dist/apps/chat/data/read-state-service.js +13 -11
- package/dist/apps/chat/session-metadata.js +30 -0
- package/dist/apps/chat/trace.js +15 -2
- package/dist/apps/chat/web-app.js +125 -17
- package/dist/apps/chat-ui/assets/{dist-CLxh9uSM.js → dist-BbCjOLiw.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-B9gnHZqL.js → dist-Bd9gWCbk.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CR75V9VE.js → dist-C9a0_446.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BUupvbV9.js → dist-CcAkJ8iv.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DXdSiTyu.js → dist-Kc3zbvQi.js} +1 -1
- package/dist/apps/chat-ui/assets/index-DWJnUye8.css +1 -0
- package/dist/apps/chat-ui/assets/index-exohEOUE.js +228 -0
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/core/session-router.js +12 -6
- package/dist/debug/agents.js +9 -3
- package/dist/plugins/builtin.js +2 -0
- package/dist/plugins/registry.js +1 -0
- package/dist/providers/openai-gpt56.js +41 -3
- package/dist/subagents/context.js +2 -2
- package/dist/subagents/observation-query.js +49 -6
- package/dist/subagents/observation-text-regex.js +124 -0
- package/dist/subagents/tool.js +3 -2
- package/dist/tools/hashline.js +64 -0
- package/npm-shrinkwrap.json +781 -280
- package/package.json +6 -5
- package/dist/apps/chat-ui/assets/index-BMkUGVRS.js +0 -228
- package/dist/apps/chat-ui/assets/index-BwT5grLv.css +0 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
/**
|
|
2
3
|
* Build a structural complete-history claim for compatibility callers.
|
|
3
4
|
*
|
|
@@ -17,24 +18,12 @@ export function createCompleteHistoryReconciliationProof(entries, scopeId) {
|
|
|
17
18
|
};
|
|
18
19
|
}
|
|
19
20
|
export function historyReconciliationDigest(entries) {
|
|
20
|
-
const
|
|
21
|
-
const prime = 0x100000001b3n;
|
|
22
|
-
const hashes = [
|
|
23
|
-
0xcbf29ce484222325n,
|
|
24
|
-
0x84222325cbf29ce4n,
|
|
25
|
-
0x9e3779b97f4a7c15n,
|
|
26
|
-
0x517cc1b727220a95n,
|
|
27
|
-
];
|
|
21
|
+
const hash = createHash("sha256");
|
|
28
22
|
for (const entry of entries) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const codeUnit = BigInt(signature.charCodeAt(index));
|
|
32
|
-
for (let hashIndex = 0; hashIndex < hashes.length; hashIndex += 1) {
|
|
33
|
-
hashes[hashIndex] = ((hashes[hashIndex] ^ codeUnit) * prime) & mask;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
23
|
+
hash.update(historyReconciliationEntrySignature(entry));
|
|
24
|
+
hash.update("\n");
|
|
36
25
|
}
|
|
37
|
-
return
|
|
26
|
+
return hash.digest("hex");
|
|
38
27
|
}
|
|
39
28
|
export function historyReconciliationEntrySignature(entry) {
|
|
40
29
|
const { sequence: _pageLocalSequence, ...proofContent } = entry;
|
|
@@ -112,7 +112,7 @@ function codexNativeCapabilities(structuredUserInput) {
|
|
|
112
112
|
nativeToolInspection: {
|
|
113
113
|
support: "degraded",
|
|
114
114
|
mode: "observed-runtime-items",
|
|
115
|
-
reason: "Stable Codex App Server 0.
|
|
115
|
+
reason: "Stable Codex App Server 0.153.2 does not expose a complete pre-turn native-tool inventory; Pibo reports selected MCP tools immediately and harness-native tools after stable item notifications prove they are active.",
|
|
116
116
|
},
|
|
117
117
|
nativeToolYielding: unsupportedAgentRuntimeCapability("Codex native tools remain harness-owned and are not wrapped as Pibo yielded tools."),
|
|
118
118
|
intentTracing: {
|
|
@@ -776,6 +776,7 @@ export class CodexNativeThreadSession {
|
|
|
776
776
|
}
|
|
777
777
|
const codexNativeCompleteHistoryProofs = new WeakMap();
|
|
778
778
|
const codexNativeHistoryReaders = new WeakMap();
|
|
779
|
+
const CODEX_NATIVE_HISTORY_INSPECTION_CACHE_TTL_MS = 5_000;
|
|
779
780
|
const startCodexNativeHistoryProcess = startCodexNativeAppServer;
|
|
780
781
|
const readCodexNativeHistoryThread = CodexNativeThreadController.read;
|
|
781
782
|
function bindCodexNativeCompleteHistoryProof(page) {
|
|
@@ -807,6 +808,7 @@ class CodexNativeAgentRuntimeAdapter {
|
|
|
807
808
|
displayName;
|
|
808
809
|
enabled;
|
|
809
810
|
modelCatalogCache;
|
|
811
|
+
historyInspectionCache = new Map();
|
|
810
812
|
authController;
|
|
811
813
|
constructor(instanceId, config, displayName, enabled) {
|
|
812
814
|
this.instanceId = instanceId;
|
|
@@ -871,6 +873,29 @@ class CodexNativeAgentRuntimeAdapter {
|
|
|
871
873
|
async disposeAuth() {
|
|
872
874
|
await this.authController.dispose();
|
|
873
875
|
}
|
|
876
|
+
historyInspectionCacheKey(input, threadId) {
|
|
877
|
+
return `${input.binding.piboSessionId}\0${input.workspace}\0${threadId}`;
|
|
878
|
+
}
|
|
879
|
+
cacheHistoryInspection(cacheKey, inspection) {
|
|
880
|
+
this.historyInspectionCache.set(cacheKey, {
|
|
881
|
+
expiresAt: Date.now() + CODEX_NATIVE_HISTORY_INSPECTION_CACHE_TTL_MS,
|
|
882
|
+
value: Promise.resolve(inspection),
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
async readHistoryInspection(input, threadId) {
|
|
886
|
+
try {
|
|
887
|
+
const readHistory = codexNativeHistoryReaders.get(this);
|
|
888
|
+
if (!readHistory)
|
|
889
|
+
throw new Error("Codex built-in history reader is unavailable.");
|
|
890
|
+
const thread = await readHistory(input.binding.piboSessionId, input.workspace, threadId, false);
|
|
891
|
+
return inspectCodexThreadHistory(this.instanceId, input.binding, thread);
|
|
892
|
+
}
|
|
893
|
+
catch (error) {
|
|
894
|
+
return unavailableCodexThreadHistoryInspection(this.instanceId, input.binding, error instanceof CodexNativeThreadMissingError ? "codex_native_history_not_found" : "codex_native_history_unavailable", error instanceof CodexNativeThreadMissingError
|
|
895
|
+
? "The bound Codex thread is unavailable for native history inspection."
|
|
896
|
+
: "Codex native history inspection failed safely.");
|
|
897
|
+
}
|
|
898
|
+
}
|
|
874
899
|
validateProfile(input) {
|
|
875
900
|
const diagnostics = [];
|
|
876
901
|
if (input.profile.runtimeInstanceId !== this.instanceId) {
|
|
@@ -1123,18 +1148,18 @@ class CodexNativeAgentRuntimeAdapter {
|
|
|
1123
1148
|
if (!threadId) {
|
|
1124
1149
|
return unavailableCodexThreadHistoryInspection(this.instanceId, input.binding, "codex_native_history_thread_id_missing", "The Codex runtime binding has no native thread id for history lookup.");
|
|
1125
1150
|
}
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
: "Codex native history inspection failed safely.");
|
|
1151
|
+
const cacheKey = this.historyInspectionCacheKey(input, threadId);
|
|
1152
|
+
const cached = this.historyInspectionCache.get(cacheKey);
|
|
1153
|
+
if (cached && cached.expiresAt > Date.now())
|
|
1154
|
+
return await cached.value;
|
|
1155
|
+
const value = this.readHistoryInspection(input, threadId);
|
|
1156
|
+
const entry = { expiresAt: Date.now() + CODEX_NATIVE_HISTORY_INSPECTION_CACHE_TTL_MS, value };
|
|
1157
|
+
this.historyInspectionCache.set(cacheKey, entry);
|
|
1158
|
+
const inspection = await value;
|
|
1159
|
+
if (!inspection.available && this.historyInspectionCache.get(cacheKey) === entry) {
|
|
1160
|
+
this.historyInspectionCache.delete(cacheKey);
|
|
1137
1161
|
}
|
|
1162
|
+
return inspection;
|
|
1138
1163
|
}
|
|
1139
1164
|
async readHistory(input) {
|
|
1140
1165
|
const threadId = input.binding.nativeSessionId;
|
|
@@ -1149,21 +1174,26 @@ class CodexNativeAgentRuntimeAdapter {
|
|
|
1149
1174
|
inspection,
|
|
1150
1175
|
};
|
|
1151
1176
|
}
|
|
1177
|
+
const cacheKey = this.historyInspectionCacheKey(input, threadId);
|
|
1152
1178
|
try {
|
|
1153
1179
|
const readHistory = codexNativeHistoryReaders.get(this);
|
|
1154
1180
|
if (!readHistory)
|
|
1155
1181
|
throw new Error("Codex built-in history reader is unavailable.");
|
|
1156
1182
|
const thread = await readHistory(input.binding.piboSessionId, input.workspace, threadId, true);
|
|
1157
|
-
|
|
1183
|
+
const page = pageCodexThreadHistory({
|
|
1158
1184
|
runtimeInstanceId: this.instanceId,
|
|
1159
1185
|
binding: input.binding,
|
|
1160
1186
|
thread,
|
|
1161
1187
|
cursor: input.cursor,
|
|
1162
1188
|
beforeTimestamp: input.beforeTimestamp,
|
|
1163
1189
|
limit: input.limit,
|
|
1164
|
-
})
|
|
1190
|
+
});
|
|
1191
|
+
if (page.inspection)
|
|
1192
|
+
this.cacheHistoryInspection(cacheKey, page.inspection);
|
|
1193
|
+
return bindCodexNativeCompleteHistoryProof(page);
|
|
1165
1194
|
}
|
|
1166
1195
|
catch (error) {
|
|
1196
|
+
this.historyInspectionCache.delete(cacheKey);
|
|
1167
1197
|
if (error instanceof CodexNativeThreadMissingError) {
|
|
1168
1198
|
const inspection = unavailableCodexThreadHistoryInspection(this.instanceId, input.binding, "codex_native_history_not_found", "The bound Codex thread is unavailable for native history reads.");
|
|
1169
1199
|
return {
|
|
@@ -277,7 +277,7 @@ function parseCodexVersion(output) {
|
|
|
277
277
|
};
|
|
278
278
|
}
|
|
279
279
|
function versionSupported(version) {
|
|
280
|
-
return !version.prerelease && version.major === 0 && version.minor ===
|
|
280
|
+
return !version.prerelease && version.major === 0 && version.minor === 153 && version.patch >= 2;
|
|
281
281
|
}
|
|
282
282
|
export async function diagnoseCodexNativeRuntime(config, runtimeInstanceId, options = {}) {
|
|
283
283
|
const diagnostics = [];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export const CODEX_APP_SERVER_VERSION = "0.
|
|
2
|
-
export const CODEX_APP_SERVER_SUPPORTED_RANGE = ">=0.
|
|
1
|
+
export const CODEX_APP_SERVER_VERSION = "0.153.2";
|
|
2
|
+
export const CODEX_APP_SERVER_SUPPORTED_RANGE = ">=0.153.2 <0.154.0";
|
|
3
3
|
export const CODEX_APP_SERVER_PROTOCOL_NAME = "codex-app-server-v2";
|
|
4
|
-
export const CODEX_APP_SERVER_SCHEMA_SHA256 = "
|
|
5
|
-
export const CODEX_APP_SERVER_V2_SCHEMA_SHA256 = "
|
|
6
|
-
export const CODEX_APP_SERVER_GENERATED_TYPES_INDEX_SHA256 = "
|
|
7
|
-
export const CODEX_APP_SERVER_GENERATED_BUNDLE_SHA256 = "
|
|
4
|
+
export const CODEX_APP_SERVER_SCHEMA_SHA256 = "e8284c5cb8157554a3dd1e035aadbd4325aea501af56887e9c2e12eb1b9b9448";
|
|
5
|
+
export const CODEX_APP_SERVER_V2_SCHEMA_SHA256 = "d3eace08be5dca386bfd1f1e8df650058b4113f1e10870a284d775d75517576a";
|
|
6
|
+
export const CODEX_APP_SERVER_GENERATED_TYPES_INDEX_SHA256 = "cad2c5b82900b6bc58cdc36fdb756e2ab8a49274147af96a7ca7ac2aecd0db08";
|
|
7
|
+
export const CODEX_APP_SERVER_GENERATED_BUNDLE_SHA256 = "7dc2b38ce79ce8e08014ae129363a4ae9626d1f0d8af47b960da11221eab5676";
|
|
@@ -4,6 +4,7 @@ import { mkdir, rm, stat, writeFile } from "node:fs/promises";
|
|
|
4
4
|
import { dirname, join, resolve } from "node:path";
|
|
5
5
|
import { protectPrivateFileSync, protectPrivatePathsSync } from "../../core/private-path.js";
|
|
6
6
|
import { OmpRpcClient } from "./client.js";
|
|
7
|
+
import { OMP_CLI_SUPPORTED_RANGE, OMP_CLI_VERSION } from "./protocol-version.js";
|
|
7
8
|
const MAX_VERSION_OUTPUT_BYTES = 256 * 1024;
|
|
8
9
|
const PRIVATE_DIRECTORY_MODE = 0o700;
|
|
9
10
|
const PRIVATE_FILE_MODE = 0o600;
|
|
@@ -258,12 +259,31 @@ export async function diagnoseOmpRuntime(config, runtimeInstanceId, options = {}
|
|
|
258
259
|
});
|
|
259
260
|
return diagnostics;
|
|
260
261
|
}
|
|
261
|
-
const
|
|
262
|
+
const versionOutput = probe.output.trim().split("\n").slice(-1)[0] ?? "";
|
|
263
|
+
const versionMatch = versionOutput.match(/\bomp(?:\/|\s+)v?(\d+\.\d+\.\d+)\b/i);
|
|
264
|
+
const version = versionMatch?.[1];
|
|
265
|
+
if (!version) {
|
|
266
|
+
diagnostics.push({
|
|
267
|
+
severity: "error",
|
|
268
|
+
code: "omp_version_unreadable",
|
|
269
|
+
message: `OMP CLI returned an unrecognized version for runtime instance "${runtimeInstanceId}".`,
|
|
270
|
+
});
|
|
271
|
+
return diagnostics;
|
|
272
|
+
}
|
|
273
|
+
if (version !== OMP_CLI_VERSION) {
|
|
274
|
+
diagnostics.push({
|
|
275
|
+
severity: "error",
|
|
276
|
+
code: "omp_version_unsupported",
|
|
277
|
+
message: `OMP CLI ${version} is outside the supported range for runtime instance "${runtimeInstanceId}".`,
|
|
278
|
+
details: { version, supportedRange: OMP_CLI_SUPPORTED_RANGE },
|
|
279
|
+
});
|
|
280
|
+
return diagnostics;
|
|
281
|
+
}
|
|
262
282
|
diagnostics.push({
|
|
263
283
|
severity: "info",
|
|
264
284
|
code: "omp_version_ok",
|
|
265
|
-
message: `OMP CLI is available for runtime instance "${runtimeInstanceId}".`,
|
|
266
|
-
details: { version:
|
|
285
|
+
message: `OMP CLI ${version} is available for runtime instance "${runtimeInstanceId}".`,
|
|
286
|
+
details: { version, supportedRange: OMP_CLI_SUPPORTED_RANGE, private: true },
|
|
267
287
|
});
|
|
268
288
|
return diagnostics;
|
|
269
289
|
}
|
|
@@ -14,7 +14,7 @@ import { importPortableHistoryIntoPi } from "./portable-history.js";
|
|
|
14
14
|
import { piIntentTracingEnabled } from "./intent-tracing.js";
|
|
15
15
|
import { isWebSearchProviderTool } from "../../tools/web-search.js";
|
|
16
16
|
const PI_ADAPTER_ID = "pi";
|
|
17
|
-
export const PI_PROTOCOL_VERSION = "0.
|
|
17
|
+
export const PI_PROTOCOL_VERSION = "0.85.0";
|
|
18
18
|
const piCompleteHistoryProofs = new WeakMap();
|
|
19
19
|
function bindPiCompleteHistoryProof(page) {
|
|
20
20
|
const claim = page.reconciliationProof;
|
|
@@ -2,7 +2,7 @@ import { ModelRegistry, createAgentSessionServices, } from "@earendil-works/pi-c
|
|
|
2
2
|
import { registerMiniMaxProvider } from "../../providers/minimax.js";
|
|
3
3
|
import { registerGlmProvider } from "../../providers/glm.js";
|
|
4
4
|
import { registerQwenTokenPlanProvider } from "../../providers/qwen-token-plan.js";
|
|
5
|
-
import {
|
|
5
|
+
import { registerOpenAiSupplementalModels } from "../../providers/openai-gpt56.js";
|
|
6
6
|
import { piAuthMethodsForProvider } from "./auth.js";
|
|
7
7
|
export function buildModelCatalogFromRegistry(registry) {
|
|
8
8
|
const providers = new Map();
|
|
@@ -52,7 +52,7 @@ export async function loadModelCatalogWithServices(createServices, cwd = process
|
|
|
52
52
|
}
|
|
53
53
|
export async function loadModelCatalog(cwd = process.cwd()) {
|
|
54
54
|
return loadModelCatalogWithServices(createAgentSessionServices, cwd, (registry) => {
|
|
55
|
-
|
|
55
|
+
registerOpenAiSupplementalModels(registry);
|
|
56
56
|
registerMiniMaxProvider(registry);
|
|
57
57
|
registerGlmProvider(registry);
|
|
58
58
|
registerQwenTokenPlanProvider(registry);
|
|
@@ -21,7 +21,7 @@ import { DEFAULT_USER_TIMEZONE } from "../../core/user-settings.js";
|
|
|
21
21
|
import { registerMiniMaxProvider } from "../../providers/minimax.js";
|
|
22
22
|
import { registerGlmProvider } from "../../providers/glm.js";
|
|
23
23
|
import { registerQwenTokenPlanProvider } from "../../providers/qwen-token-plan.js";
|
|
24
|
-
import {
|
|
24
|
+
import { registerOpenAiSupplementalModels } from "../../providers/openai-gpt56.js";
|
|
25
25
|
import { PIBO_APP_CONTEXT } from "../../app-context.js";
|
|
26
26
|
import { RuntimeSessionRegistry } from "../../tools/runtime/registry.js";
|
|
27
27
|
import { CodexBrowserSessionController } from "../../tools/codex-browser.js";
|
|
@@ -123,7 +123,10 @@ function getBuiltinToolAllowlist(profile, customTools) {
|
|
|
123
123
|
if (profile.builtinTools === "disabled")
|
|
124
124
|
return undefined;
|
|
125
125
|
const defaultBuiltinTools = new Set(DEFAULT_BUILTIN_TOOL_NAMES);
|
|
126
|
-
const
|
|
126
|
+
const replacedBuiltinTools = new Set(profile.tools
|
|
127
|
+
.filter((tool) => tool.enabled !== false)
|
|
128
|
+
.flatMap((tool) => tool.replacesBuiltinTools ?? []));
|
|
129
|
+
const selectedBuiltinTools = profile.builtinToolNames.filter((name) => defaultBuiltinTools.has(name) && !replacedBuiltinTools.has(name));
|
|
127
130
|
if (selectedBuiltinTools.length === DEFAULT_BUILTIN_TOOL_NAMES.length)
|
|
128
131
|
return undefined;
|
|
129
132
|
return [...selectedBuiltinTools, ...customTools.map((tool) => tool.name)];
|
|
@@ -253,7 +256,7 @@ export async function createPiboRuntime(options = {}) {
|
|
|
253
256
|
runtimeSettingsManager = services.settingsManager;
|
|
254
257
|
applyPiboRuntimeRetryDefaults(services.settingsManager, options.retryDefaults);
|
|
255
258
|
const modelRegistry = new ModelRegistry(services.modelRuntime);
|
|
256
|
-
|
|
259
|
+
registerOpenAiSupplementalModels(modelRegistry);
|
|
257
260
|
registerMiniMaxProvider(modelRegistry);
|
|
258
261
|
registerGlmProvider(modelRegistry);
|
|
259
262
|
registerQwenTokenPlanProvider(modelRegistry);
|
|
@@ -314,7 +314,7 @@ export function sessionActionResource(pathname) {
|
|
|
314
314
|
if (parts.length !== 2 || !parts[0])
|
|
315
315
|
return undefined;
|
|
316
316
|
const action = parts[1];
|
|
317
|
-
if (action !== "read" && action !== "kill" && action !== "kill-all" && action !== "runtime-binding" && action !== "fork-candidates")
|
|
317
|
+
if (action !== "read" && action !== "kill" && action !== "kill-all" && action !== "runtime-binding" && action !== "fork-candidates" && action !== "order")
|
|
318
318
|
return undefined;
|
|
319
319
|
try {
|
|
320
320
|
return { piboSessionId: decodeURIComponent(parts[0]), action };
|
|
@@ -5,7 +5,7 @@ import { DEFAULT_AGENT_RUNTIME_INSTANCE_ID } from "../../core/profiles.js";
|
|
|
5
5
|
import { isPiboThinkingLevel } from "../../core/thinking.js";
|
|
6
6
|
import { PiboWebHttpError } from "../../web/http.js";
|
|
7
7
|
import { listPiPackages } from "../../pi-packages/store.js";
|
|
8
|
-
import { withChatWebArchived } from "./session-metadata.js";
|
|
8
|
+
import { withChatWebArchived, withChatWebSessionPinned } from "./session-metadata.js";
|
|
9
9
|
import { isDefaultPiboRoom, withPiboRoomArchived, withPiboRoomWorkspace } from "./types/rooms.js";
|
|
10
10
|
import { isValidCustomAgentName } from "./agent-store.js";
|
|
11
11
|
export function normalizeRoomName(value, fallback = "New Chat") {
|
|
@@ -670,7 +670,12 @@ export function createSessionUpdate(context, session, body) {
|
|
|
670
670
|
const title = normalizeSessionTitle(body.title);
|
|
671
671
|
if (title !== undefined)
|
|
672
672
|
update.title = title;
|
|
673
|
-
|
|
673
|
+
let metadata = metadataWithArchiveState(session, body.archived);
|
|
674
|
+
if (body.pinned !== undefined) {
|
|
675
|
+
if (typeof body.pinned !== "boolean")
|
|
676
|
+
throw new PiboWebHttpError("Session pinned flag must be boolean", 400);
|
|
677
|
+
metadata = withChatWebSessionPinned(metadata ?? session.metadata, body.pinned);
|
|
678
|
+
}
|
|
674
679
|
if (metadata)
|
|
675
680
|
update.metadata = metadata;
|
|
676
681
|
if (body.profile !== undefined) {
|
|
@@ -16,21 +16,23 @@ export class ChatReadStateService {
|
|
|
16
16
|
}
|
|
17
17
|
countUnreadMessagesBySession(input) {
|
|
18
18
|
const counts = new Map();
|
|
19
|
-
|
|
19
|
+
const uniqueIds = [...new Set(input.piboSessionIds)];
|
|
20
|
+
if (!uniqueIds.length)
|
|
20
21
|
return counts;
|
|
21
|
-
for (let offset = 0; offset <
|
|
22
|
-
const ids =
|
|
23
|
-
const
|
|
22
|
+
for (let offset = 0; offset < uniqueIds.length; offset += 400) {
|
|
23
|
+
const ids = uniqueIds.slice(offset, offset + 400);
|
|
24
|
+
const requested = ids.map(() => "(?)").join(", ");
|
|
24
25
|
const rows = this.store.db.prepare(`
|
|
26
|
+
WITH requested(session_id) AS (VALUES ${requested})
|
|
25
27
|
SELECT e.session_id, COUNT(*) AS count
|
|
26
|
-
FROM
|
|
27
|
-
LEFT JOIN app_session_read_state reads ON reads.session_id =
|
|
28
|
-
|
|
28
|
+
FROM requested r
|
|
29
|
+
LEFT JOIN app_session_read_state reads ON reads.session_id = r.session_id
|
|
30
|
+
JOIN event_log e
|
|
31
|
+
ON e.session_id = r.session_id
|
|
29
32
|
AND e.stream_id > COALESCE(reads.last_read_stream_id, 0)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
)
|
|
33
|
+
WHERE
|
|
34
|
+
(e.retention_class = 'chat_message' AND e.type IN ('user.message.accepted', 'assistant_message'))
|
|
35
|
+
OR e.type = 'session_error'
|
|
34
36
|
GROUP BY e.session_id
|
|
35
37
|
`).all(...ids);
|
|
36
38
|
for (const row of rows)
|
|
@@ -1,7 +1,26 @@
|
|
|
1
1
|
const CHAT_WEB_ARCHIVED_AT_KEY = "chatWebArchivedAt";
|
|
2
|
+
const CHAT_WEB_PINNED_AT_KEY = "chatWebPinnedAt";
|
|
3
|
+
const CHAT_WEB_SIDEBAR_ORDER_KEY = "chatWebSidebarOrder";
|
|
2
4
|
export function isChatWebSessionArchived(session) {
|
|
3
5
|
return typeof session.metadata?.[CHAT_WEB_ARCHIVED_AT_KEY] === "string";
|
|
4
6
|
}
|
|
7
|
+
export function isChatWebSessionPinned(session) {
|
|
8
|
+
return typeof session.metadata?.[CHAT_WEB_PINNED_AT_KEY] === "string";
|
|
9
|
+
}
|
|
10
|
+
export function chatWebSessionSidebarOrder(session) {
|
|
11
|
+
const value = session.metadata?.[CHAT_WEB_SIDEBAR_ORDER_KEY];
|
|
12
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
13
|
+
}
|
|
14
|
+
export function compareChatWebSessionsBySidebarOrder(left, right) {
|
|
15
|
+
const pinnedDifference = Number(isChatWebSessionPinned(right)) - Number(isChatWebSessionPinned(left));
|
|
16
|
+
if (pinnedDifference !== 0)
|
|
17
|
+
return pinnedDifference;
|
|
18
|
+
const leftOrder = chatWebSessionSidebarOrder(left) ?? (Date.parse(left.createdAt) || 0);
|
|
19
|
+
const rightOrder = chatWebSessionSidebarOrder(right) ?? (Date.parse(right.createdAt) || 0);
|
|
20
|
+
if (leftOrder !== rightOrder)
|
|
21
|
+
return rightOrder - leftOrder;
|
|
22
|
+
return right.id.localeCompare(left.id);
|
|
23
|
+
}
|
|
5
24
|
export function withChatWebArchived(metadata, archived) {
|
|
6
25
|
const next = { ...(metadata ?? {}) };
|
|
7
26
|
if (archived) {
|
|
@@ -12,3 +31,14 @@ export function withChatWebArchived(metadata, archived) {
|
|
|
12
31
|
}
|
|
13
32
|
return next;
|
|
14
33
|
}
|
|
34
|
+
export function withChatWebSessionPinned(metadata, pinned, order = Date.now()) {
|
|
35
|
+
const next = { ...(metadata ?? {}), [CHAT_WEB_SIDEBAR_ORDER_KEY]: order };
|
|
36
|
+
if (pinned)
|
|
37
|
+
next[CHAT_WEB_PINNED_AT_KEY] = new Date().toISOString();
|
|
38
|
+
else
|
|
39
|
+
delete next[CHAT_WEB_PINNED_AT_KEY];
|
|
40
|
+
return next;
|
|
41
|
+
}
|
|
42
|
+
export function withChatWebSessionSidebarOrder(metadata, order) {
|
|
43
|
+
return { ...(metadata ?? {}), [CHAT_WEB_SIDEBAR_ORDER_KEY]: order };
|
|
44
|
+
}
|
package/dist/apps/chat/trace.js
CHANGED
|
@@ -3,7 +3,7 @@ import { PI_HISTORY_PAGE_MAX_BYTES, PI_HISTORY_SCAN_MAX_BYTES, PI_HISTORY_TAIL_M
|
|
|
3
3
|
import { isPiboThinkingLevel } from "../../core/thinking.js";
|
|
4
4
|
import { isBuiltInHistoryReconciliationProof } from "../../agent-runtimes/history-proof.js";
|
|
5
5
|
import { buildTraceViewFromEvents, traceNodesFromHistoryEntries } from "../../shared/trace-engine.js";
|
|
6
|
-
import { isChatWebSessionArchived } from "./session-metadata.js";
|
|
6
|
+
import { chatWebSessionSidebarOrder, isChatWebSessionArchived, isChatWebSessionPinned } from "./session-metadata.js";
|
|
7
7
|
import { workflowSessionKindFromMetadata } from "../../sessions/workflow-session-kind.js";
|
|
8
8
|
export const TRACE_PROJECTION_VERSION = "runtime-history-v2-incomplete-status";
|
|
9
9
|
export { compareTraceNodes, sortTraceNodes, nestTraceNodes, flattenTraceNodes, mapTraceNodesById, buildTraceViewFromEvents, traceNodesFromHistoryEntries, } from "../../shared/trace-engine.js";
|
|
@@ -34,7 +34,10 @@ export async function buildSessionNodes(sessions, indexItems, _cwd = process.cwd
|
|
|
34
34
|
workflowSessionKind: workflowSessionKindFromMetadata(session.metadata),
|
|
35
35
|
title: createSessionTitle(session, historyMetadataFromInspection(inspection)),
|
|
36
36
|
subtitle: session.id,
|
|
37
|
+
createdAt: session.createdAt,
|
|
37
38
|
archived: isChatWebSessionArchived(session),
|
|
39
|
+
pinned: isChatWebSessionPinned(session),
|
|
40
|
+
sidebarOrder: chatWebSessionSidebarOrder(session),
|
|
38
41
|
status: sessionNodeStatus(indexed?.status),
|
|
39
42
|
lastActivityAt: indexed?.lastActivityAt ?? indexed?.createdAt ?? session.createdAt,
|
|
40
43
|
unreadCount: unreadCounts.get(session.id) || undefined,
|
|
@@ -70,7 +73,7 @@ export async function buildSessionNodes(sessions, indexItems, _cwd = process.cwd
|
|
|
70
73
|
});
|
|
71
74
|
}
|
|
72
75
|
const sortNodes = (items) => {
|
|
73
|
-
items.sort(
|
|
76
|
+
items.sort(compareSessionNodesBySidebarOrder);
|
|
74
77
|
for (const item of items) {
|
|
75
78
|
item.derivedSessions.sort((left, right) => (right.lastActivityAt ?? "").localeCompare(left.lastActivityAt ?? ""));
|
|
76
79
|
sortNodes(item.children);
|
|
@@ -79,6 +82,16 @@ export async function buildSessionNodes(sessions, indexItems, _cwd = process.cwd
|
|
|
79
82
|
sortNodes(roots);
|
|
80
83
|
return roots;
|
|
81
84
|
}
|
|
85
|
+
function compareSessionNodesBySidebarOrder(left, right) {
|
|
86
|
+
const pinnedDifference = Number(Boolean(right.pinned)) - Number(Boolean(left.pinned));
|
|
87
|
+
if (pinnedDifference !== 0)
|
|
88
|
+
return pinnedDifference;
|
|
89
|
+
const leftOrder = left.sidebarOrder ?? (Date.parse(left.createdAt ?? "") || 0);
|
|
90
|
+
const rightOrder = right.sidebarOrder ?? (Date.parse(right.createdAt ?? "") || 0);
|
|
91
|
+
if (leftOrder !== rightOrder)
|
|
92
|
+
return rightOrder - leftOrder;
|
|
93
|
+
return right.piboSessionId.localeCompare(left.piboSessionId);
|
|
94
|
+
}
|
|
82
95
|
function sessionNodeStatus(indexedStatus) {
|
|
83
96
|
return indexedStatus ?? "idle";
|
|
84
97
|
}
|