@devness/useai 0.6.10 → 0.6.12
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 +187 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -684,7 +684,7 @@ var VERSION;
|
|
|
684
684
|
var init_version = __esm({
|
|
685
685
|
"../shared/dist/constants/version.js"() {
|
|
686
686
|
"use strict";
|
|
687
|
-
VERSION = "0.6.
|
|
687
|
+
VERSION = "0.6.12";
|
|
688
688
|
}
|
|
689
689
|
});
|
|
690
690
|
|
|
@@ -34589,6 +34589,12 @@ var init_session_state = __esm({
|
|
|
34589
34589
|
modelId;
|
|
34590
34590
|
/** Token estimates for the useai_start tool call. */
|
|
34591
34591
|
startCallTokensEst;
|
|
34592
|
+
/** Whether a UseAI session is actively in-progress (between useai_start and useai_end). */
|
|
34593
|
+
inProgress;
|
|
34594
|
+
/** Timestamp when the current session was marked in-progress. */
|
|
34595
|
+
inProgressSince;
|
|
34596
|
+
/** Session ID that was auto-sealed by seal-active hook (for useai_end fallback). */
|
|
34597
|
+
autoSealedSessionId;
|
|
34592
34598
|
constructor() {
|
|
34593
34599
|
this.sessionId = generateSessionId();
|
|
34594
34600
|
this.conversationId = generateSessionId();
|
|
@@ -34596,6 +34602,9 @@ var init_session_state = __esm({
|
|
|
34596
34602
|
this.mcpSessionId = null;
|
|
34597
34603
|
this.modelId = null;
|
|
34598
34604
|
this.startCallTokensEst = null;
|
|
34605
|
+
this.inProgress = false;
|
|
34606
|
+
this.inProgressSince = null;
|
|
34607
|
+
this.autoSealedSessionId = null;
|
|
34599
34608
|
this.sessionStartTime = Date.now();
|
|
34600
34609
|
this.heartbeatCount = 0;
|
|
34601
34610
|
this.sessionRecordCount = 0;
|
|
@@ -34622,6 +34631,8 @@ var init_session_state = __esm({
|
|
|
34622
34631
|
this.sessionPromptWordCount = null;
|
|
34623
34632
|
this.modelId = null;
|
|
34624
34633
|
this.startCallTokensEst = null;
|
|
34634
|
+
this.inProgress = false;
|
|
34635
|
+
this.inProgressSince = null;
|
|
34625
34636
|
this.detectProject();
|
|
34626
34637
|
}
|
|
34627
34638
|
detectProject() {
|
|
@@ -34715,8 +34726,20 @@ __export(register_tools_exports, {
|
|
|
34715
34726
|
registerTools: () => registerTools
|
|
34716
34727
|
});
|
|
34717
34728
|
import { createHash as createHash3, randomUUID as randomUUID3 } from "crypto";
|
|
34718
|
-
import { existsSync as existsSync8, renameSync as renameSync2 } from "fs";
|
|
34729
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5, renameSync as renameSync2 } from "fs";
|
|
34719
34730
|
import { join as join7 } from "path";
|
|
34731
|
+
function coerceJsonString(schema) {
|
|
34732
|
+
return external_exports.preprocess((val) => {
|
|
34733
|
+
if (typeof val === "string") {
|
|
34734
|
+
try {
|
|
34735
|
+
return JSON.parse(val);
|
|
34736
|
+
} catch {
|
|
34737
|
+
return val;
|
|
34738
|
+
}
|
|
34739
|
+
}
|
|
34740
|
+
return val;
|
|
34741
|
+
}, schema);
|
|
34742
|
+
}
|
|
34720
34743
|
function getConfig() {
|
|
34721
34744
|
return readJson(CONFIG_FILE, {
|
|
34722
34745
|
milestone_tracking: true,
|
|
@@ -34738,6 +34761,130 @@ function resolveClient2(server2, session2) {
|
|
|
34738
34761
|
}
|
|
34739
34762
|
session2.setClient(detectClient());
|
|
34740
34763
|
}
|
|
34764
|
+
function enrichAutoSealedSession(sealedSessionId, session2, args) {
|
|
34765
|
+
const sealedPath = join7(SEALED_DIR, `${sealedSessionId}.jsonl`);
|
|
34766
|
+
const activePath = join7(ACTIVE_DIR, `${sealedSessionId}.jsonl`);
|
|
34767
|
+
const chainPath = existsSync8(sealedPath) ? sealedPath : existsSync8(activePath) ? activePath : null;
|
|
34768
|
+
if (!chainPath) {
|
|
34769
|
+
return "No active session to end (already sealed or never started).";
|
|
34770
|
+
}
|
|
34771
|
+
let startData = {};
|
|
34772
|
+
let duration3 = 0;
|
|
34773
|
+
let endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
34774
|
+
let startedAt2 = endedAt;
|
|
34775
|
+
try {
|
|
34776
|
+
const content = readFileSync5(chainPath, "utf-8").trim();
|
|
34777
|
+
const lines = content.split("\n").filter(Boolean);
|
|
34778
|
+
if (lines.length > 0) {
|
|
34779
|
+
const firstRecord = JSON.parse(lines[0]);
|
|
34780
|
+
startData = firstRecord.data;
|
|
34781
|
+
startedAt2 = firstRecord.timestamp;
|
|
34782
|
+
const lastRecord = JSON.parse(lines[lines.length - 1]);
|
|
34783
|
+
if (lastRecord.type === "session_seal" && lastRecord.data["seal"]) {
|
|
34784
|
+
try {
|
|
34785
|
+
const sealObj = JSON.parse(lastRecord.data["seal"]);
|
|
34786
|
+
duration3 = sealObj.duration_seconds ?? Math.round((new Date(lastRecord.timestamp).getTime() - new Date(startedAt2).getTime()) / 1e3);
|
|
34787
|
+
endedAt = sealObj.ended_at ?? lastRecord.timestamp;
|
|
34788
|
+
} catch {
|
|
34789
|
+
duration3 = Math.round((new Date(lastRecord.timestamp).getTime() - new Date(startedAt2).getTime()) / 1e3);
|
|
34790
|
+
endedAt = lastRecord.timestamp;
|
|
34791
|
+
}
|
|
34792
|
+
} else {
|
|
34793
|
+
duration3 = Math.round((new Date(lastRecord.timestamp).getTime() - new Date(startedAt2).getTime()) / 1e3);
|
|
34794
|
+
endedAt = lastRecord.timestamp;
|
|
34795
|
+
}
|
|
34796
|
+
}
|
|
34797
|
+
} catch {
|
|
34798
|
+
return "No active session to end (chain file unreadable).";
|
|
34799
|
+
}
|
|
34800
|
+
const taskType = args.task_type ?? startData.task_type ?? "coding";
|
|
34801
|
+
const languages = args.languages ?? [];
|
|
34802
|
+
const filesTouched = args.files_touched_count ?? 0;
|
|
34803
|
+
let milestoneCount = 0;
|
|
34804
|
+
if (args.milestones && args.milestones.length > 0) {
|
|
34805
|
+
const config2 = getConfig();
|
|
34806
|
+
if (config2.milestone_tracking) {
|
|
34807
|
+
const durationMinutes = Math.round(duration3 / 60);
|
|
34808
|
+
const allMilestones = getMilestones();
|
|
34809
|
+
for (const m of args.milestones) {
|
|
34810
|
+
allMilestones.push({
|
|
34811
|
+
id: `m_${randomUUID3().slice(0, 8)}`,
|
|
34812
|
+
session_id: sealedSessionId,
|
|
34813
|
+
title: m.title,
|
|
34814
|
+
private_title: m.private_title,
|
|
34815
|
+
project: startData.project ?? session2.project ?? void 0,
|
|
34816
|
+
category: m.category,
|
|
34817
|
+
complexity: m.complexity ?? "medium",
|
|
34818
|
+
duration_minutes: durationMinutes,
|
|
34819
|
+
languages,
|
|
34820
|
+
client: startData.client ?? session2.clientName,
|
|
34821
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
34822
|
+
published: false,
|
|
34823
|
+
published_at: null,
|
|
34824
|
+
chain_hash: ""
|
|
34825
|
+
});
|
|
34826
|
+
milestoneCount++;
|
|
34827
|
+
}
|
|
34828
|
+
writeJson(MILESTONES_FILE, allMilestones);
|
|
34829
|
+
}
|
|
34830
|
+
}
|
|
34831
|
+
let sessionScore;
|
|
34832
|
+
let frameworkId;
|
|
34833
|
+
if (args.evaluation) {
|
|
34834
|
+
const config2 = getConfig();
|
|
34835
|
+
const framework = getFramework(config2.evaluation_framework);
|
|
34836
|
+
sessionScore = Math.round(framework.computeSessionScore(args.evaluation));
|
|
34837
|
+
frameworkId = framework.id;
|
|
34838
|
+
}
|
|
34839
|
+
const richSeal = {
|
|
34840
|
+
session_id: sealedSessionId,
|
|
34841
|
+
conversation_id: startData.conversation_id,
|
|
34842
|
+
conversation_index: startData.conversation_index,
|
|
34843
|
+
client: startData.client ?? session2.clientName,
|
|
34844
|
+
task_type: taskType,
|
|
34845
|
+
languages,
|
|
34846
|
+
files_touched: filesTouched,
|
|
34847
|
+
project: startData.project ?? session2.project ?? void 0,
|
|
34848
|
+
title: startData.title ?? void 0,
|
|
34849
|
+
private_title: startData.private_title ?? void 0,
|
|
34850
|
+
model: startData.model ?? session2.modelId ?? void 0,
|
|
34851
|
+
evaluation: args.evaluation ?? void 0,
|
|
34852
|
+
session_score: sessionScore,
|
|
34853
|
+
evaluation_framework: frameworkId,
|
|
34854
|
+
started_at: startedAt2,
|
|
34855
|
+
ended_at: endedAt,
|
|
34856
|
+
duration_seconds: duration3,
|
|
34857
|
+
heartbeat_count: 0,
|
|
34858
|
+
record_count: 0,
|
|
34859
|
+
chain_start_hash: "",
|
|
34860
|
+
chain_end_hash: "",
|
|
34861
|
+
seal_signature: ""
|
|
34862
|
+
};
|
|
34863
|
+
const allSessions = getSessions();
|
|
34864
|
+
const existingIdx = allSessions.findIndex((s) => s.session_id === sealedSessionId);
|
|
34865
|
+
if (existingIdx >= 0) {
|
|
34866
|
+
const existing = allSessions[existingIdx];
|
|
34867
|
+
allSessions[existingIdx] = {
|
|
34868
|
+
...existing,
|
|
34869
|
+
// Enrich with data from useai_end call
|
|
34870
|
+
task_type: taskType,
|
|
34871
|
+
languages,
|
|
34872
|
+
files_touched: filesTouched,
|
|
34873
|
+
evaluation: args.evaluation ?? existing.evaluation,
|
|
34874
|
+
session_score: sessionScore ?? existing.session_score,
|
|
34875
|
+
evaluation_framework: frameworkId ?? existing.evaluation_framework
|
|
34876
|
+
};
|
|
34877
|
+
} else {
|
|
34878
|
+
allSessions.push(richSeal);
|
|
34879
|
+
}
|
|
34880
|
+
writeJson(SESSIONS_FILE, allSessions);
|
|
34881
|
+
const durationStr = formatDuration(duration3);
|
|
34882
|
+
const langStr = languages.length > 0 ? ` using ${languages.join(", ")}` : "";
|
|
34883
|
+
const milestoneStr = milestoneCount > 0 ? ` \xB7 ${milestoneCount} milestone${milestoneCount > 1 ? "s" : ""} recorded` : "";
|
|
34884
|
+
const evalStr = args.evaluation ? ` \xB7 eval: ${args.evaluation.task_outcome} (prompt: ${args.evaluation.prompt_quality}/5)` : "";
|
|
34885
|
+
const scoreStr = sessionScore !== void 0 ? ` \xB7 score: ${sessionScore}/100 (${frameworkId})` : "";
|
|
34886
|
+
return `Session ended (enriched auto-seal): ${durationStr} ${taskType}${langStr}${milestoneStr}${evalStr}${scoreStr}`;
|
|
34887
|
+
}
|
|
34741
34888
|
function registerTools(server2, session2, opts) {
|
|
34742
34889
|
server2.tool(
|
|
34743
34890
|
"useai_start",
|
|
@@ -34756,6 +34903,7 @@ function registerTools(server2, session2, opts) {
|
|
|
34756
34903
|
opts.sealBeforeReset();
|
|
34757
34904
|
}
|
|
34758
34905
|
session2.reset();
|
|
34906
|
+
session2.autoSealedSessionId = null;
|
|
34759
34907
|
resolveClient2(server2, session2);
|
|
34760
34908
|
if (conversation_id) {
|
|
34761
34909
|
if (conversation_id !== prevConvId) {
|
|
@@ -34783,6 +34931,8 @@ function registerTools(server2, session2, opts) {
|
|
|
34783
34931
|
if (private_title) chainData.private_title = private_title;
|
|
34784
34932
|
if (model) chainData.model = model;
|
|
34785
34933
|
const record2 = session2.appendToChain("session_start", chainData);
|
|
34934
|
+
session2.inProgress = true;
|
|
34935
|
+
session2.inProgressSince = Date.now();
|
|
34786
34936
|
writeMcpMapping(session2.mcpSessionId, session2.sessionId);
|
|
34787
34937
|
const responseText = `useai session started \u2014 ${session2.sessionTaskType} on ${session2.clientName} \xB7 ${session2.sessionId.slice(0, 8)} \xB7 conv ${session2.conversationId.slice(0, 8)}#${session2.conversationIndex} \xB7 ${session2.signingAvailable ? "signed" : "unsigned"}`;
|
|
34788
34938
|
const paramsJson = JSON.stringify({ task_type, title, private_title, project, model });
|
|
@@ -34825,15 +34975,15 @@ function registerTools(server2, session2, opts) {
|
|
|
34825
34975
|
'End the current AI coding session and record milestones. Each milestone needs TWO titles: (1) a generic public "title" safe for public display (NEVER include project names, file names, class names, or any identifying details), and (2) an optional detailed "private_title" for the user\'s own records that CAN include project names, file names, and specific details. GOOD title: "Implemented user authentication". GOOD private_title: "Added JWT auth to UseAI API server". BAD title: "Fixed bug in Acme auth service". Also provide an `evaluation` object assessing the session: prompt_quality (1-5), context_provided (1-5), task_outcome (completed/partial/abandoned/blocked), iteration_count, independence_level (1-5), scope_quality (1-5), and tools_leveraged count. Score honestly based on the actual interaction. For any scored metric < 5 or non-completed outcome, you MUST provide a *_reason field explaining what was lacking and a concrete tip for the user to improve next time. Only skip *_reason for a perfect 5.',
|
|
34826
34976
|
{
|
|
34827
34977
|
task_type: taskTypeSchema.optional().describe("What kind of task was the developer working on?"),
|
|
34828
|
-
languages: external_exports.array(external_exports.string()).optional().describe("Programming languages used (e.g. ['typescript', 'python'])"),
|
|
34829
|
-
files_touched_count: external_exports.number().optional().describe("Approximate number of files created or modified (count only, no names)"),
|
|
34830
|
-
milestones: external_exports.array(external_exports.object({
|
|
34978
|
+
languages: coerceJsonString(external_exports.array(external_exports.string())).optional().describe("Programming languages used (e.g. ['typescript', 'python'])"),
|
|
34979
|
+
files_touched_count: coerceJsonString(external_exports.number()).optional().describe("Approximate number of files created or modified (count only, no names)"),
|
|
34980
|
+
milestones: coerceJsonString(external_exports.array(external_exports.object({
|
|
34831
34981
|
title: external_exports.string().describe("PRIVACY-CRITICAL: Generic description of what was accomplished. NEVER include project names, repo names, product names, package names, file names, file paths, class names, API endpoints, database names, company names, or ANY identifier that could reveal which codebase this work was done in. Write as if describing the work to a stranger. GOOD: 'Implemented user authentication', 'Fixed race condition in background worker', 'Added unit tests for data validation', 'Refactored state management layer'. BAD: 'Fixed bug in Acme auth', 'Investigated ProjectX pipeline', 'Updated UserService.ts in src/services/', 'Added tests for coverit MCP tool'"),
|
|
34832
34982
|
private_title: external_exports.string().optional().describe("Detailed description for the user's private records. CAN include project names, file names, and specific details. Example: 'Added private/public milestone support to UseAI MCP server'"),
|
|
34833
34983
|
category: milestoneCategorySchema.describe("Type of work completed"),
|
|
34834
34984
|
complexity: complexitySchema.optional().describe("How complex was this task?")
|
|
34835
|
-
})).optional().describe("What was accomplished this session? List each distinct piece of work completed. Provide both a generic public title and an optional detailed private_title."),
|
|
34836
|
-
evaluation: external_exports.object({
|
|
34985
|
+
}))).optional().describe("What was accomplished this session? List each distinct piece of work completed. Provide both a generic public title and an optional detailed private_title."),
|
|
34986
|
+
evaluation: coerceJsonString(external_exports.object({
|
|
34837
34987
|
prompt_quality: external_exports.number().min(1).max(5).describe("How clear, specific, and complete was the initial prompt? 1=vague/ambiguous, 5=crystal clear with acceptance criteria"),
|
|
34838
34988
|
prompt_quality_reason: external_exports.string().optional().describe("Required if prompt_quality < 5. Explain what was vague/missing and how the user could phrase it better next time."),
|
|
34839
34989
|
context_provided: external_exports.number().min(1).max(5).describe("Did the user provide relevant context (files, errors, constraints)? 1=no context, 5=comprehensive context"),
|
|
@@ -34846,10 +34996,21 @@ function registerTools(server2, session2, opts) {
|
|
|
34846
34996
|
scope_quality: external_exports.number().min(1).max(5).describe("Was the task well-scoped? 1=vague or impossibly broad, 5=precise and achievable"),
|
|
34847
34997
|
scope_quality_reason: external_exports.string().optional().describe("Required if scope_quality < 5. How was the scope too broad/vague and how could it be better defined?"),
|
|
34848
34998
|
tools_leveraged: external_exports.number().min(0).describe("Count of distinct AI capabilities used (code gen, debugging, refactoring, testing, docs, etc.)")
|
|
34849
|
-
}).optional().describe("AI-assessed evaluation of this session. Score honestly based on the actual interaction.")
|
|
34999
|
+
})).optional().describe("AI-assessed evaluation of this session. Score honestly based on the actual interaction.")
|
|
34850
35000
|
},
|
|
34851
35001
|
async ({ task_type, languages, files_touched_count, milestones: milestonesInput, evaluation }) => {
|
|
34852
35002
|
if (session2.sessionRecordCount === 0) {
|
|
35003
|
+
if (session2.autoSealedSessionId) {
|
|
35004
|
+
const enrichResult = enrichAutoSealedSession(
|
|
35005
|
+
session2.autoSealedSessionId,
|
|
35006
|
+
session2,
|
|
35007
|
+
{ task_type, languages, files_touched_count, milestones: milestonesInput, evaluation }
|
|
35008
|
+
);
|
|
35009
|
+
session2.autoSealedSessionId = null;
|
|
35010
|
+
session2.inProgress = false;
|
|
35011
|
+
session2.inProgressSince = null;
|
|
35012
|
+
return { content: [{ type: "text", text: enrichResult }] };
|
|
35013
|
+
}
|
|
34853
35014
|
return {
|
|
34854
35015
|
content: [{ type: "text", text: "No active session to end (already sealed or never started)." }]
|
|
34855
35016
|
};
|
|
@@ -34997,6 +35158,8 @@ function registerTools(server2, session2, opts) {
|
|
|
34997
35158
|
const sessions2 = getSessions().filter((s) => s.session_id !== seal.session_id);
|
|
34998
35159
|
sessions2.push(seal);
|
|
34999
35160
|
writeJson(SESSIONS_FILE, sessions2);
|
|
35161
|
+
session2.inProgress = false;
|
|
35162
|
+
session2.inProgressSince = null;
|
|
35000
35163
|
return {
|
|
35001
35164
|
content: [
|
|
35002
35165
|
{
|
|
@@ -35509,7 +35672,7 @@ __export(daemon_exports, {
|
|
|
35509
35672
|
});
|
|
35510
35673
|
import { createServer } from "http";
|
|
35511
35674
|
import { createHash as createHash4, randomUUID as randomUUID4 } from "crypto";
|
|
35512
|
-
import { existsSync as existsSync10, readdirSync, readFileSync as
|
|
35675
|
+
import { existsSync as existsSync10, readdirSync, readFileSync as readFileSync6, appendFileSync as appendFileSync2, renameSync as renameSync3, writeFileSync as writeFileSync5, unlinkSync as unlinkSync5, statSync } from "fs";
|
|
35513
35676
|
import { join as join9 } from "path";
|
|
35514
35677
|
function getActiveUseaiSessionIds() {
|
|
35515
35678
|
const ids = /* @__PURE__ */ new Set();
|
|
@@ -35522,7 +35685,7 @@ function sealOrphanFile(sessionId) {
|
|
|
35522
35685
|
const filePath = join9(ACTIVE_DIR, `${sessionId}.jsonl`);
|
|
35523
35686
|
if (!existsSync10(filePath)) return;
|
|
35524
35687
|
try {
|
|
35525
|
-
const content =
|
|
35688
|
+
const content = readFileSync6(filePath, "utf-8").trim();
|
|
35526
35689
|
if (!content) return;
|
|
35527
35690
|
const lines = content.split("\n").filter(Boolean);
|
|
35528
35691
|
if (lines.length === 0) return;
|
|
@@ -35760,8 +35923,10 @@ function autoSealSession(active) {
|
|
|
35760
35923
|
upsertSessionSeal(seal);
|
|
35761
35924
|
}
|
|
35762
35925
|
function sealSessionData(active) {
|
|
35926
|
+
const sealedId = active.session.sessionId;
|
|
35763
35927
|
autoSealSession(active);
|
|
35764
35928
|
active.session.reset();
|
|
35929
|
+
active.session.autoSealedSessionId = sealedId;
|
|
35765
35930
|
}
|
|
35766
35931
|
function resetIdleTimer(sessionId) {
|
|
35767
35932
|
const active = sessions.get(sessionId);
|
|
@@ -35865,7 +36030,7 @@ function readChainMetadata(useaiSessionId) {
|
|
|
35865
36030
|
const chainPath = existsSync10(activePath) ? activePath : existsSync10(sealedPath) ? sealedPath : null;
|
|
35866
36031
|
if (!chainPath) return null;
|
|
35867
36032
|
try {
|
|
35868
|
-
const firstLine =
|
|
36033
|
+
const firstLine = readFileSync6(chainPath, "utf-8").split("\n")[0];
|
|
35869
36034
|
if (!firstLine) return null;
|
|
35870
36035
|
const record2 = JSON.parse(firstLine);
|
|
35871
36036
|
const d = record2.data;
|
|
@@ -35934,7 +36099,7 @@ function recoverHeartbeat(staleMcpSessionId, rpcId, res) {
|
|
|
35934
36099
|
return true;
|
|
35935
36100
|
}
|
|
35936
36101
|
try {
|
|
35937
|
-
const content =
|
|
36102
|
+
const content = readFileSync6(chainPath, "utf-8").trim();
|
|
35938
36103
|
const lines = content.split("\n").filter(Boolean);
|
|
35939
36104
|
if (lines.length === 0) return false;
|
|
35940
36105
|
const firstRecord = JSON.parse(lines[0]);
|
|
@@ -35969,7 +36134,7 @@ function recoverEndSession(staleMcpSessionId, args, rpcId, res) {
|
|
|
35969
36134
|
const chainPath = existsSync10(activePath) ? activePath : existsSync10(sealedPath) ? sealedPath : null;
|
|
35970
36135
|
if (!chainPath) return false;
|
|
35971
36136
|
const isAlreadySealed = chainPath === sealedPath;
|
|
35972
|
-
const content =
|
|
36137
|
+
const content = readFileSync6(chainPath, "utf-8").trim();
|
|
35973
36138
|
if (!content) return false;
|
|
35974
36139
|
const lines = content.split("\n").filter(Boolean);
|
|
35975
36140
|
if (lines.length === 0) return false;
|
|
@@ -36375,8 +36540,14 @@ async function startDaemon(port) {
|
|
|
36375
36540
|
}
|
|
36376
36541
|
if (url.pathname === "/api/seal-active" && req.method === "POST") {
|
|
36377
36542
|
let sealed = 0;
|
|
36543
|
+
let skipped = 0;
|
|
36378
36544
|
for (const [, active] of sessions) {
|
|
36379
36545
|
if (active.session.sessionRecordCount > 0 && !isSessionAlreadySealed(active.session)) {
|
|
36546
|
+
const { inProgress, inProgressSince } = active.session;
|
|
36547
|
+
if (inProgress && inProgressSince && Date.now() - inProgressSince < SEAL_GRACE_MS) {
|
|
36548
|
+
skipped++;
|
|
36549
|
+
continue;
|
|
36550
|
+
}
|
|
36380
36551
|
sealSessionData(active);
|
|
36381
36552
|
sealed++;
|
|
36382
36553
|
}
|
|
@@ -36385,7 +36556,7 @@ async function startDaemon(port) {
|
|
|
36385
36556
|
"Content-Type": "application/json",
|
|
36386
36557
|
"Access-Control-Allow-Origin": "*"
|
|
36387
36558
|
});
|
|
36388
|
-
res.end(JSON.stringify({ sealed }));
|
|
36559
|
+
res.end(JSON.stringify({ sealed, skipped }));
|
|
36389
36560
|
return;
|
|
36390
36561
|
}
|
|
36391
36562
|
if ((url.pathname.startsWith("/api/local/") || url.pathname === "/api/seal-active") && req.method === "OPTIONS") {
|
|
@@ -36542,7 +36713,7 @@ async function startDaemon(port) {
|
|
|
36542
36713
|
console.log(`UseAI daemon listening on http://127.0.0.1:${listenPort}`);
|
|
36543
36714
|
console.log(`PID: ${process.pid}`);
|
|
36544
36715
|
}
|
|
36545
|
-
var IDLE_TIMEOUT_MS, sessions, daemonSigningKey, ORPHAN_SWEEP_INTERVAL_MS, startedAt, updateCheckCache, UPDATE_CHECK_TTL_MS;
|
|
36716
|
+
var IDLE_TIMEOUT_MS, SEAL_GRACE_MS, sessions, daemonSigningKey, ORPHAN_SWEEP_INTERVAL_MS, startedAt, updateCheckCache, UPDATE_CHECK_TTL_MS;
|
|
36546
36717
|
var init_daemon2 = __esm({
|
|
36547
36718
|
"src/daemon.ts"() {
|
|
36548
36719
|
"use strict";
|
|
@@ -36556,6 +36727,7 @@ var init_daemon2 = __esm({
|
|
|
36556
36727
|
init_html();
|
|
36557
36728
|
init_local_api();
|
|
36558
36729
|
IDLE_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
36730
|
+
SEAL_GRACE_MS = 30 * 60 * 1e3;
|
|
36559
36731
|
sessions = /* @__PURE__ */ new Map();
|
|
36560
36732
|
daemonSigningKey = null;
|
|
36561
36733
|
ORPHAN_SWEEP_INTERVAL_MS = 15 * 60 * 1e3;
|