@devness/useai 0.6.12 → 0.6.14
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 +152 -13
- 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.14";
|
|
688
688
|
}
|
|
689
689
|
});
|
|
690
690
|
|
|
@@ -34595,6 +34595,8 @@ var init_session_state = __esm({
|
|
|
34595
34595
|
inProgressSince;
|
|
34596
34596
|
/** Session ID that was auto-sealed by seal-active hook (for useai_end fallback). */
|
|
34597
34597
|
autoSealedSessionId;
|
|
34598
|
+
/** Saved parent session state when a child (subagent) session is active. */
|
|
34599
|
+
parentState;
|
|
34598
34600
|
constructor() {
|
|
34599
34601
|
this.sessionId = generateSessionId();
|
|
34600
34602
|
this.conversationId = generateSessionId();
|
|
@@ -34605,6 +34607,7 @@ var init_session_state = __esm({
|
|
|
34605
34607
|
this.inProgress = false;
|
|
34606
34608
|
this.inProgressSince = null;
|
|
34607
34609
|
this.autoSealedSessionId = null;
|
|
34610
|
+
this.parentState = null;
|
|
34608
34611
|
this.sessionStartTime = Date.now();
|
|
34609
34612
|
this.heartbeatCount = 0;
|
|
34610
34613
|
this.sessionRecordCount = 0;
|
|
@@ -34665,6 +34668,56 @@ var init_session_state = __esm({
|
|
|
34665
34668
|
getSessionDuration() {
|
|
34666
34669
|
return Math.round((Date.now() - this.sessionStartTime) / 1e3);
|
|
34667
34670
|
}
|
|
34671
|
+
/**
|
|
34672
|
+
* Save the current session state as the parent, so it can be restored
|
|
34673
|
+
* after a child (subagent) session finishes.
|
|
34674
|
+
*/
|
|
34675
|
+
saveParentState() {
|
|
34676
|
+
this.parentState = {
|
|
34677
|
+
sessionId: this.sessionId,
|
|
34678
|
+
sessionStartTime: this.sessionStartTime,
|
|
34679
|
+
heartbeatCount: this.heartbeatCount,
|
|
34680
|
+
sessionRecordCount: this.sessionRecordCount,
|
|
34681
|
+
chainTipHash: this.chainTipHash,
|
|
34682
|
+
conversationId: this.conversationId,
|
|
34683
|
+
conversationIndex: this.conversationIndex,
|
|
34684
|
+
sessionTaskType: this.sessionTaskType,
|
|
34685
|
+
sessionTitle: this.sessionTitle,
|
|
34686
|
+
sessionPrivateTitle: this.sessionPrivateTitle,
|
|
34687
|
+
sessionPromptWordCount: this.sessionPromptWordCount,
|
|
34688
|
+
project: this.project,
|
|
34689
|
+
modelId: this.modelId,
|
|
34690
|
+
startCallTokensEst: this.startCallTokensEst,
|
|
34691
|
+
inProgress: this.inProgress,
|
|
34692
|
+
inProgressSince: this.inProgressSince
|
|
34693
|
+
};
|
|
34694
|
+
}
|
|
34695
|
+
/**
|
|
34696
|
+
* Restore the parent session state after a child session ends.
|
|
34697
|
+
* Returns true if parent state was restored, false if no parent state was saved.
|
|
34698
|
+
*/
|
|
34699
|
+
restoreParentState() {
|
|
34700
|
+
if (!this.parentState) return false;
|
|
34701
|
+
const p = this.parentState;
|
|
34702
|
+
this.sessionId = p.sessionId;
|
|
34703
|
+
this.sessionStartTime = p.sessionStartTime;
|
|
34704
|
+
this.heartbeatCount = p.heartbeatCount;
|
|
34705
|
+
this.sessionRecordCount = p.sessionRecordCount;
|
|
34706
|
+
this.chainTipHash = p.chainTipHash;
|
|
34707
|
+
this.conversationId = p.conversationId;
|
|
34708
|
+
this.conversationIndex = p.conversationIndex;
|
|
34709
|
+
this.sessionTaskType = p.sessionTaskType;
|
|
34710
|
+
this.sessionTitle = p.sessionTitle;
|
|
34711
|
+
this.sessionPrivateTitle = p.sessionPrivateTitle;
|
|
34712
|
+
this.sessionPromptWordCount = p.sessionPromptWordCount;
|
|
34713
|
+
this.project = p.project;
|
|
34714
|
+
this.modelId = p.modelId;
|
|
34715
|
+
this.startCallTokensEst = p.startCallTokensEst;
|
|
34716
|
+
this.inProgress = p.inProgress;
|
|
34717
|
+
this.inProgressSince = p.inProgressSince;
|
|
34718
|
+
this.parentState = null;
|
|
34719
|
+
return true;
|
|
34720
|
+
}
|
|
34668
34721
|
initializeKeystore() {
|
|
34669
34722
|
ensureDir();
|
|
34670
34723
|
if (existsSync7(KEYSTORE_FILE)) {
|
|
@@ -34899,8 +34952,14 @@ function registerTools(server2, session2, opts) {
|
|
|
34899
34952
|
},
|
|
34900
34953
|
async ({ task_type, title, private_title, project, model, conversation_id }) => {
|
|
34901
34954
|
const prevConvId = session2.conversationId;
|
|
34902
|
-
|
|
34903
|
-
|
|
34955
|
+
const isChildSession = session2.inProgress && session2.sessionRecordCount > 0;
|
|
34956
|
+
const parentSessionId = isChildSession ? session2.sessionId : null;
|
|
34957
|
+
if (isChildSession) {
|
|
34958
|
+
session2.saveParentState();
|
|
34959
|
+
} else {
|
|
34960
|
+
if (session2.sessionRecordCount > 0 && opts?.sealBeforeReset) {
|
|
34961
|
+
opts.sealBeforeReset();
|
|
34962
|
+
}
|
|
34904
34963
|
}
|
|
34905
34964
|
session2.reset();
|
|
34906
34965
|
session2.autoSealedSessionId = null;
|
|
@@ -34910,7 +34969,7 @@ function registerTools(server2, session2, opts) {
|
|
|
34910
34969
|
session2.conversationId = conversation_id;
|
|
34911
34970
|
session2.conversationIndex = 0;
|
|
34912
34971
|
}
|
|
34913
|
-
} else {
|
|
34972
|
+
} else if (!isChildSession) {
|
|
34914
34973
|
session2.conversationId = generateSessionId();
|
|
34915
34974
|
session2.conversationIndex = 0;
|
|
34916
34975
|
}
|
|
@@ -34930,11 +34989,13 @@ function registerTools(server2, session2, opts) {
|
|
|
34930
34989
|
if (title) chainData.title = title;
|
|
34931
34990
|
if (private_title) chainData.private_title = private_title;
|
|
34932
34991
|
if (model) chainData.model = model;
|
|
34992
|
+
if (parentSessionId) chainData.parent_session_id = parentSessionId;
|
|
34933
34993
|
const record2 = session2.appendToChain("session_start", chainData);
|
|
34934
34994
|
session2.inProgress = true;
|
|
34935
34995
|
session2.inProgressSince = Date.now();
|
|
34936
34996
|
writeMcpMapping(session2.mcpSessionId, session2.sessionId);
|
|
34937
|
-
const
|
|
34997
|
+
const childSuffix = parentSessionId ? ` \xB7 child of ${parentSessionId.slice(0, 8)}` : "";
|
|
34998
|
+
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}${childSuffix} \xB7 ${session2.signingAvailable ? "signed" : "unsigned"}`;
|
|
34938
34999
|
const paramsJson = JSON.stringify({ task_type, title, private_title, project, model });
|
|
34939
35000
|
session2.startCallTokensEst = {
|
|
34940
35001
|
output: Math.ceil(paramsJson.length / 4),
|
|
@@ -34972,17 +35033,18 @@ function registerTools(server2, session2, opts) {
|
|
|
34972
35033
|
);
|
|
34973
35034
|
server2.tool(
|
|
34974
35035
|
"useai_end",
|
|
34975
|
-
'End the current AI coding session and record milestones. Each milestone
|
|
35036
|
+
'End the current AI coding session and record milestones. Each milestone is an object with required fields: "title" (generic, no project names), "category" (e.g. "feature", "bugfix", "investigation", "analysis", "refactor", "test", "docs"), and optional "private_title" and "complexity". Example: [{"title": "Implemented auth flow", "category": "feature"}]. 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.',
|
|
34976
35037
|
{
|
|
35038
|
+
session_id: external_exports.string().optional().describe("Session ID to end. If omitted, ends the current active session. Pass the session_id from your useai_start response to explicitly target your own session (important when subagents may have started their own sessions on the same connection)."),
|
|
34977
35039
|
task_type: taskTypeSchema.optional().describe("What kind of task was the developer working on?"),
|
|
34978
35040
|
languages: coerceJsonString(external_exports.array(external_exports.string())).optional().describe("Programming languages used (e.g. ['typescript', 'python'])"),
|
|
34979
35041
|
files_touched_count: coerceJsonString(external_exports.number()).optional().describe("Approximate number of files created or modified (count only, no names)"),
|
|
34980
35042
|
milestones: coerceJsonString(external_exports.array(external_exports.object({
|
|
34981
|
-
title: external_exports.string().describe("PRIVACY-CRITICAL: Generic description of what was accomplished. NEVER include project names,
|
|
34982
|
-
private_title: external_exports.string().optional().describe("Detailed description for the user's private records. CAN include project names
|
|
34983
|
-
category: milestoneCategorySchema.describe("Type of work
|
|
34984
|
-
complexity: complexitySchema.optional().describe("
|
|
34985
|
-
}))).optional().describe(
|
|
35043
|
+
title: external_exports.string().describe("PRIVACY-CRITICAL: Generic description of what was accomplished. NEVER include project names, file paths, class names, or identifying details. GOOD: 'Implemented user authentication'. BAD: 'Fixed bug in Acme auth'."),
|
|
35044
|
+
private_title: external_exports.string().optional().describe("Detailed description for the user's private records. CAN include project names and specifics."),
|
|
35045
|
+
category: milestoneCategorySchema.describe("Required. Type of work: feature, bugfix, refactor, test, docs, investigation, analysis, research, setup, deployment, performance, cleanup, chore, security, migration, design, devops, config, other"),
|
|
35046
|
+
complexity: complexitySchema.optional().describe("Optional. simple, medium, or complex. Defaults to medium.")
|
|
35047
|
+
}))).optional().describe('Array of milestone objects. Each MUST have "title" (string) and "category" (string). Example: [{"title": "Implemented auth flow", "category": "feature"}, {"title": "Fixed race condition", "category": "bugfix"}]'),
|
|
34986
35048
|
evaluation: coerceJsonString(external_exports.object({
|
|
34987
35049
|
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"),
|
|
34988
35050
|
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."),
|
|
@@ -34998,7 +35060,79 @@ function registerTools(server2, session2, opts) {
|
|
|
34998
35060
|
tools_leveraged: external_exports.number().min(0).describe("Count of distinct AI capabilities used (code gen, debugging, refactoring, testing, docs, etc.)")
|
|
34999
35061
|
})).optional().describe("AI-assessed evaluation of this session. Score honestly based on the actual interaction.")
|
|
35000
35062
|
},
|
|
35001
|
-
async ({ task_type, languages, files_touched_count, milestones: milestonesInput, evaluation }) => {
|
|
35063
|
+
async ({ session_id: targetSessionId, task_type, languages, files_touched_count, milestones: milestonesInput, evaluation }) => {
|
|
35064
|
+
if (targetSessionId && session2.parentState && session2.parentState.sessionId.startsWith(targetSessionId)) {
|
|
35065
|
+
if (session2.sessionRecordCount > 0) {
|
|
35066
|
+
const childDuration = session2.getSessionDuration();
|
|
35067
|
+
const childNow = (/* @__PURE__ */ new Date()).toISOString();
|
|
35068
|
+
const childEndRecord = session2.appendToChain("session_end", {
|
|
35069
|
+
duration_seconds: childDuration,
|
|
35070
|
+
task_type: session2.sessionTaskType,
|
|
35071
|
+
languages: [],
|
|
35072
|
+
files_touched: 0,
|
|
35073
|
+
heartbeat_count: session2.heartbeatCount,
|
|
35074
|
+
auto_sealed: true,
|
|
35075
|
+
parent_ended: true
|
|
35076
|
+
});
|
|
35077
|
+
const childSealData = JSON.stringify({
|
|
35078
|
+
session_id: session2.sessionId,
|
|
35079
|
+
parent_session_id: session2.parentState.sessionId,
|
|
35080
|
+
conversation_id: session2.conversationId,
|
|
35081
|
+
conversation_index: session2.conversationIndex,
|
|
35082
|
+
client: session2.clientName,
|
|
35083
|
+
task_type: session2.sessionTaskType,
|
|
35084
|
+
languages: [],
|
|
35085
|
+
files_touched: 0,
|
|
35086
|
+
project: session2.project ?? void 0,
|
|
35087
|
+
title: session2.sessionTitle ?? void 0,
|
|
35088
|
+
private_title: session2.sessionPrivateTitle ?? void 0,
|
|
35089
|
+
model: session2.modelId ?? void 0,
|
|
35090
|
+
started_at: new Date(session2.sessionStartTime).toISOString(),
|
|
35091
|
+
ended_at: childNow,
|
|
35092
|
+
duration_seconds: childDuration,
|
|
35093
|
+
heartbeat_count: session2.heartbeatCount,
|
|
35094
|
+
record_count: session2.sessionRecordCount,
|
|
35095
|
+
chain_end_hash: childEndRecord.hash
|
|
35096
|
+
});
|
|
35097
|
+
const childSealSig = signHash(
|
|
35098
|
+
createHash3("sha256").update(childSealData).digest("hex"),
|
|
35099
|
+
session2.signingKey
|
|
35100
|
+
);
|
|
35101
|
+
session2.appendToChain("session_seal", { seal: childSealData, seal_signature: childSealSig });
|
|
35102
|
+
const childActivePath = join7(ACTIVE_DIR, `${session2.sessionId}.jsonl`);
|
|
35103
|
+
const childSealedPath = join7(SEALED_DIR, `${session2.sessionId}.jsonl`);
|
|
35104
|
+
try {
|
|
35105
|
+
if (existsSync8(childActivePath)) renameSync2(childActivePath, childSealedPath);
|
|
35106
|
+
} catch {
|
|
35107
|
+
}
|
|
35108
|
+
const childSeal = {
|
|
35109
|
+
session_id: session2.sessionId,
|
|
35110
|
+
parent_session_id: session2.parentState.sessionId,
|
|
35111
|
+
conversation_id: session2.conversationId,
|
|
35112
|
+
conversation_index: session2.conversationIndex,
|
|
35113
|
+
client: session2.clientName,
|
|
35114
|
+
task_type: session2.sessionTaskType,
|
|
35115
|
+
languages: [],
|
|
35116
|
+
files_touched: 0,
|
|
35117
|
+
project: session2.project ?? void 0,
|
|
35118
|
+
title: session2.sessionTitle ?? void 0,
|
|
35119
|
+
private_title: session2.sessionPrivateTitle ?? void 0,
|
|
35120
|
+
model: session2.modelId ?? void 0,
|
|
35121
|
+
started_at: new Date(session2.sessionStartTime).toISOString(),
|
|
35122
|
+
ended_at: childNow,
|
|
35123
|
+
duration_seconds: childDuration,
|
|
35124
|
+
heartbeat_count: session2.heartbeatCount,
|
|
35125
|
+
record_count: session2.sessionRecordCount,
|
|
35126
|
+
chain_start_hash: "GENESIS",
|
|
35127
|
+
chain_end_hash: childEndRecord.hash,
|
|
35128
|
+
seal_signature: childSealSig
|
|
35129
|
+
};
|
|
35130
|
+
const childSessions = getSessions().filter((s) => s.session_id !== childSeal.session_id);
|
|
35131
|
+
childSessions.push(childSeal);
|
|
35132
|
+
writeJson(SESSIONS_FILE, childSessions);
|
|
35133
|
+
}
|
|
35134
|
+
session2.restoreParentState();
|
|
35135
|
+
}
|
|
35002
35136
|
if (session2.sessionRecordCount === 0) {
|
|
35003
35137
|
if (session2.autoSealedSessionId) {
|
|
35004
35138
|
const enrichResult = enrichAutoSealedSession(
|
|
@@ -35078,8 +35212,10 @@ function registerTools(server2, session2, opts) {
|
|
|
35078
35212
|
...session2.modelId ? { model: session2.modelId } : {}
|
|
35079
35213
|
});
|
|
35080
35214
|
const startEst = session2.startCallTokensEst ?? { input: 0, output: 0 };
|
|
35215
|
+
const parentId = session2.parentState?.sessionId;
|
|
35081
35216
|
const sealData = JSON.stringify({
|
|
35082
35217
|
session_id: session2.sessionId,
|
|
35218
|
+
...parentId ? { parent_session_id: parentId } : {},
|
|
35083
35219
|
conversation_id: session2.conversationId,
|
|
35084
35220
|
conversation_index: session2.conversationIndex,
|
|
35085
35221
|
client: session2.clientName,
|
|
@@ -35131,6 +35267,7 @@ function registerTools(server2, session2, opts) {
|
|
|
35131
35267
|
};
|
|
35132
35268
|
const seal = {
|
|
35133
35269
|
session_id: session2.sessionId,
|
|
35270
|
+
...parentId ? { parent_session_id: parentId } : {},
|
|
35134
35271
|
conversation_id: session2.conversationId,
|
|
35135
35272
|
conversation_index: session2.conversationIndex,
|
|
35136
35273
|
client: session2.clientName,
|
|
@@ -35160,11 +35297,13 @@ function registerTools(server2, session2, opts) {
|
|
|
35160
35297
|
writeJson(SESSIONS_FILE, sessions2);
|
|
35161
35298
|
session2.inProgress = false;
|
|
35162
35299
|
session2.inProgressSince = null;
|
|
35300
|
+
const restoredParent = session2.restoreParentState();
|
|
35301
|
+
const parentRestoredStr = restoredParent ? ` \xB7 parent ${session2.sessionId.slice(0, 8)} restored` : "";
|
|
35163
35302
|
return {
|
|
35164
35303
|
content: [
|
|
35165
35304
|
{
|
|
35166
35305
|
type: "text",
|
|
35167
|
-
text: responseText
|
|
35306
|
+
text: responseText + parentRestoredStr
|
|
35168
35307
|
}
|
|
35169
35308
|
]
|
|
35170
35309
|
};
|