assistme 0.4.0 → 0.6.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/{chunk-4SBIN27G.js → chunk-ECEOBNDM.js} +60 -0
- package/dist/index.js +491 -22
- package/dist/{job-runner-CJ7HM4GZ.js → job-runner-RGP4CLYV.js} +1 -1
- package/package.json +2 -1
- package/src/agent/processor.ts +24 -4
- package/src/agent/self-analyzer.ts +444 -0
- package/src/commands/start.ts +15 -1
- package/src/db/analysis-data.ts +79 -0
- package/src/db/session-log.ts +71 -0
- package/src/utils/constants.ts +20 -0
- package/src/utils/logger.ts +28 -0
- package/src/utils/schemas.ts +30 -0
- package/tests/agent/processor.test.ts +4 -0
- package/tests/agent/self-analyzer.test.ts +349 -0
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
AppError,
|
|
4
4
|
BrowseSkillRowSchema,
|
|
5
5
|
CDP_COMMAND_TIMEOUT_MS,
|
|
6
|
+
EDSGER_PRODUCT_SLUG,
|
|
6
7
|
FRAME_CONTEXTS_MAX_SIZE,
|
|
7
8
|
JobRunner,
|
|
8
9
|
MAX_BUDGET_USD,
|
|
@@ -20,10 +21,16 @@ import {
|
|
|
20
21
|
MEMORY_COMPRESSION_THRESHOLD,
|
|
21
22
|
MEMORY_DEDUP_SIMILARITY_THRESHOLD,
|
|
22
23
|
SCHEDULER_INTERVAL_MS,
|
|
24
|
+
SELF_ANALYSIS_EVENT_CONTEXT_CHARS,
|
|
25
|
+
SELF_ANALYSIS_LOG_CONTEXT_CHARS,
|
|
26
|
+
SELF_ANALYSIS_MAX_CONVERSATION_MESSAGES,
|
|
27
|
+
SELF_ANALYSIS_MAX_MESSAGE_EVENTS,
|
|
28
|
+
SELF_ANALYSIS_MAX_SESSION_LOGS,
|
|
23
29
|
SHELL_MAX_OUTPUT,
|
|
24
30
|
SHELL_TIMEOUT_MS,
|
|
25
31
|
SKILL_DESCRIPTION_BUDGET_CHARS,
|
|
26
32
|
SKILL_VALIDATION_MAX_TURNS,
|
|
33
|
+
SelfAnalysisResultSchema,
|
|
27
34
|
SkillCreateResultSchema,
|
|
28
35
|
SkillDecisionSchema,
|
|
29
36
|
SkillRowSchema,
|
|
@@ -35,9 +42,10 @@ import {
|
|
|
35
42
|
readAuthStore,
|
|
36
43
|
safeParse,
|
|
37
44
|
setCorrelationId,
|
|
45
|
+
setLogHook,
|
|
38
46
|
setLogLevel,
|
|
39
47
|
writeAuthStore
|
|
40
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-ECEOBNDM.js";
|
|
41
49
|
import {
|
|
42
50
|
clearConfig,
|
|
43
51
|
getConfig,
|
|
@@ -2711,7 +2719,7 @@ var SessionManager = class {
|
|
|
2711
2719
|
|
|
2712
2720
|
// src/agent/processor.ts
|
|
2713
2721
|
import {
|
|
2714
|
-
query as
|
|
2722
|
+
query as query3
|
|
2715
2723
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
2716
2724
|
|
|
2717
2725
|
// src/agent/memory.ts
|
|
@@ -2736,10 +2744,10 @@ var MemoryManager = class {
|
|
|
2736
2744
|
/**
|
|
2737
2745
|
* Search memories by query text. Uses ILIKE + tag containment.
|
|
2738
2746
|
*/
|
|
2739
|
-
async search(
|
|
2747
|
+
async search(query4, limit = 10) {
|
|
2740
2748
|
try {
|
|
2741
2749
|
return await callMcpHandler("memory.search", {
|
|
2742
|
-
query:
|
|
2750
|
+
query: query4,
|
|
2743
2751
|
limit
|
|
2744
2752
|
});
|
|
2745
2753
|
} catch (err) {
|
|
@@ -3384,11 +3392,11 @@ _(${skills.length - included} additional skills available \u2014 use skill_searc
|
|
|
3384
3392
|
log.debug(`Invocation log error: ${err}`);
|
|
3385
3393
|
}
|
|
3386
3394
|
}
|
|
3387
|
-
async searchDb(
|
|
3395
|
+
async searchDb(query4, limit = 10) {
|
|
3388
3396
|
if (this.userId) {
|
|
3389
3397
|
try {
|
|
3390
3398
|
const data = await callMcpHandler("skill.search", {
|
|
3391
|
-
query:
|
|
3399
|
+
query: query4,
|
|
3392
3400
|
limit
|
|
3393
3401
|
});
|
|
3394
3402
|
if (data) {
|
|
@@ -3403,7 +3411,7 @@ _(${skills.length - included} additional skills available \u2014 use skill_searc
|
|
|
3403
3411
|
} catch {
|
|
3404
3412
|
}
|
|
3405
3413
|
}
|
|
3406
|
-
const results = this.findRelevant(
|
|
3414
|
+
const results = this.findRelevant(query4, limit);
|
|
3407
3415
|
return results.map((s) => ({
|
|
3408
3416
|
name: s.name,
|
|
3409
3417
|
description: s.description,
|
|
@@ -3783,6 +3791,386 @@ async function executeSkillDecision(decision, skillManager, sessionId, model) {
|
|
|
3783
3791
|
}
|
|
3784
3792
|
}
|
|
3785
3793
|
|
|
3794
|
+
// src/agent/self-analyzer.ts
|
|
3795
|
+
import {
|
|
3796
|
+
query as query2
|
|
3797
|
+
} from "@anthropic-ai/claude-agent-sdk";
|
|
3798
|
+
import { submitFeedback, FeedbackError } from "edsger-feedback";
|
|
3799
|
+
|
|
3800
|
+
// src/db/analysis-data.ts
|
|
3801
|
+
async function getSessionLogs(sessionId, limit = 500) {
|
|
3802
|
+
try {
|
|
3803
|
+
const data = await callMcpHandler("log.list", {
|
|
3804
|
+
session_id: sessionId,
|
|
3805
|
+
limit
|
|
3806
|
+
});
|
|
3807
|
+
return data || [];
|
|
3808
|
+
} catch (err) {
|
|
3809
|
+
log.debug(`Failed to fetch session logs: ${err instanceof Error ? err.message : err}`);
|
|
3810
|
+
return [];
|
|
3811
|
+
}
|
|
3812
|
+
}
|
|
3813
|
+
async function getMessageEvents(messageId, limit = 500) {
|
|
3814
|
+
try {
|
|
3815
|
+
const data = await callMcpHandler("event.list", {
|
|
3816
|
+
message_id: messageId,
|
|
3817
|
+
limit
|
|
3818
|
+
});
|
|
3819
|
+
return data || [];
|
|
3820
|
+
} catch (err) {
|
|
3821
|
+
log.debug(`Failed to fetch message events: ${err instanceof Error ? err.message : err}`);
|
|
3822
|
+
return [];
|
|
3823
|
+
}
|
|
3824
|
+
}
|
|
3825
|
+
async function getConversationMessages(conversationId, limit = 10) {
|
|
3826
|
+
try {
|
|
3827
|
+
const data = await callMcpHandler(
|
|
3828
|
+
"conversation.list_messages",
|
|
3829
|
+
{
|
|
3830
|
+
conversation_id: conversationId,
|
|
3831
|
+
limit
|
|
3832
|
+
}
|
|
3833
|
+
);
|
|
3834
|
+
return data || [];
|
|
3835
|
+
} catch (err) {
|
|
3836
|
+
log.debug(`Failed to fetch conversation messages: ${err instanceof Error ? err.message : err}`);
|
|
3837
|
+
return [];
|
|
3838
|
+
}
|
|
3839
|
+
}
|
|
3840
|
+
|
|
3841
|
+
// src/agent/self-analyzer.ts
|
|
3842
|
+
var SELF_ANALYSIS_OUTPUT_FORMAT = {
|
|
3843
|
+
type: "json_schema",
|
|
3844
|
+
schema: {
|
|
3845
|
+
type: "object",
|
|
3846
|
+
properties: {
|
|
3847
|
+
is_perfect: { type: "boolean" },
|
|
3848
|
+
overall_score: { type: "number" },
|
|
3849
|
+
task_completion_quality: {
|
|
3850
|
+
type: "object",
|
|
3851
|
+
properties: {
|
|
3852
|
+
score: { type: "number" },
|
|
3853
|
+
assessment: { type: "string" }
|
|
3854
|
+
},
|
|
3855
|
+
required: ["score", "assessment"]
|
|
3856
|
+
},
|
|
3857
|
+
improvements: {
|
|
3858
|
+
type: "array",
|
|
3859
|
+
items: {
|
|
3860
|
+
type: "object",
|
|
3861
|
+
properties: {
|
|
3862
|
+
area: { type: "string" },
|
|
3863
|
+
severity: {
|
|
3864
|
+
type: "string",
|
|
3865
|
+
enum: ["critical", "major", "minor", "suggestion"]
|
|
3866
|
+
},
|
|
3867
|
+
description: { type: "string" },
|
|
3868
|
+
suggestion: { type: "string" }
|
|
3869
|
+
},
|
|
3870
|
+
required: ["area", "severity", "description", "suggestion"]
|
|
3871
|
+
}
|
|
3872
|
+
},
|
|
3873
|
+
data_quality: {
|
|
3874
|
+
type: "object",
|
|
3875
|
+
properties: {
|
|
3876
|
+
session_logs_useful: { type: "boolean" },
|
|
3877
|
+
session_logs_gaps: { type: "string" },
|
|
3878
|
+
message_events_useful: { type: "boolean" },
|
|
3879
|
+
message_events_gaps: { type: "string" },
|
|
3880
|
+
conversation_context_useful: { type: "boolean" },
|
|
3881
|
+
conversation_context_gaps: { type: "string" }
|
|
3882
|
+
},
|
|
3883
|
+
required: [
|
|
3884
|
+
"session_logs_useful",
|
|
3885
|
+
"message_events_useful",
|
|
3886
|
+
"conversation_context_useful"
|
|
3887
|
+
]
|
|
3888
|
+
},
|
|
3889
|
+
summary: { type: "string" }
|
|
3890
|
+
},
|
|
3891
|
+
required: [
|
|
3892
|
+
"is_perfect",
|
|
3893
|
+
"overall_score",
|
|
3894
|
+
"task_completion_quality",
|
|
3895
|
+
"improvements",
|
|
3896
|
+
"data_quality",
|
|
3897
|
+
"summary"
|
|
3898
|
+
]
|
|
3899
|
+
}
|
|
3900
|
+
};
|
|
3901
|
+
var SELF_ANALYSIS_PROMPT = `You just completed a task as the AssistMe agent. Now critically analyze AssistMe's own implementation \u2014 NOT the user's task itself, but how well AssistMe (the agent system) performed and whether AssistMe's codebase can be improved.
|
|
3902
|
+
|
|
3903
|
+
## Your Role
|
|
3904
|
+
You are a perfectionist code reviewer analyzing the AssistMe agent system itself. Be critical, thorough, and constructive. Focus on:
|
|
3905
|
+
|
|
3906
|
+
1. **Task Completion Quality**: Did AssistMe handle the task optimally? Were there unnecessary steps, missed edge cases, or suboptimal tool usage?
|
|
3907
|
+
2. **Agent Architecture**: Based on what you observed during execution, are there architectural improvements to AssistMe's code (processor, event hooks, MCP servers, skill system, etc.)?
|
|
3908
|
+
3. **Data & Observability**: Evaluate the quality of the context data provided (session logs, message events, conversation messages). What information is missing that would help diagnose issues or improve the system?
|
|
3909
|
+
4. **Error Handling & Resilience**: Were there any failures or retries? Could the error handling be improved?
|
|
3910
|
+
5. **Performance & Efficiency**: Were there unnecessary API calls, redundant operations, or opportunities to optimize?
|
|
3911
|
+
6. **User Experience**: Could the interaction flow be smoother? Is the feedback to the user adequate?
|
|
3912
|
+
|
|
3913
|
+
## Context Data Provided
|
|
3914
|
+
Below you will find:
|
|
3915
|
+
- **Session Logs**: stdout/stderr/status logs from the agent session
|
|
3916
|
+
- **Message Events**: Real-time events emitted during task execution (tool calls, results, status changes, thinking blocks)
|
|
3917
|
+
- **Conversation Messages**: User interaction history for this conversation
|
|
3918
|
+
- **Tool Call Records**: Summary of all tool calls made during the task
|
|
3919
|
+
- **Tool Failures**: Any tool calls that failed during execution
|
|
3920
|
+
|
|
3921
|
+
## Instructions
|
|
3922
|
+
Analyze all provided data critically. Consider:
|
|
3923
|
+
- Are the session logs capturing enough detail for debugging?
|
|
3924
|
+
- Do the message events provide sufficient visibility into the agent's decision-making?
|
|
3925
|
+
- Is the conversation context giving enough user intent signal?
|
|
3926
|
+
- Were tools used efficiently?
|
|
3927
|
+
- Could the overall execution flow be improved?
|
|
3928
|
+
|
|
3929
|
+
Set is_perfect to true ONLY if there are genuinely zero improvements to suggest (this should be rare).
|
|
3930
|
+
The overall_score should be 1-10 where 10 means absolutely perfect.
|
|
3931
|
+
|
|
3932
|
+
Respond with a JSON object now.`;
|
|
3933
|
+
function truncateToChars(text, maxChars) {
|
|
3934
|
+
if (text.length <= maxChars) return text;
|
|
3935
|
+
return text.slice(0, maxChars) + "\n... [truncated]";
|
|
3936
|
+
}
|
|
3937
|
+
var EVENT_RESULT_SUMMARY_CHARS = 150;
|
|
3938
|
+
function filterAndAggregateEvents(events) {
|
|
3939
|
+
const lines = [];
|
|
3940
|
+
let pendingTextChunks = [];
|
|
3941
|
+
const flushTextDelta = () => {
|
|
3942
|
+
if (pendingTextChunks.length > 0) {
|
|
3943
|
+
const merged = pendingTextChunks.join("");
|
|
3944
|
+
const summary = merged.length > 200 ? merged.slice(0, 200) + "..." : merged;
|
|
3945
|
+
lines.push(`[text_delta x${pendingTextChunks.length}] ${summary}`);
|
|
3946
|
+
pendingTextChunks = [];
|
|
3947
|
+
}
|
|
3948
|
+
};
|
|
3949
|
+
for (const e of events) {
|
|
3950
|
+
switch (e.event_type) {
|
|
3951
|
+
case "text_delta": {
|
|
3952
|
+
const text = e.event_data.text || "";
|
|
3953
|
+
pendingTextChunks.push(text);
|
|
3954
|
+
break;
|
|
3955
|
+
}
|
|
3956
|
+
case "thinking":
|
|
3957
|
+
flushTextDelta();
|
|
3958
|
+
break;
|
|
3959
|
+
case "tool_use_input":
|
|
3960
|
+
flushTextDelta();
|
|
3961
|
+
break;
|
|
3962
|
+
case "tool_result": {
|
|
3963
|
+
flushTextDelta();
|
|
3964
|
+
const name = e.event_data.name || "unknown";
|
|
3965
|
+
const result = (e.event_data.result || "").slice(0, EVENT_RESULT_SUMMARY_CHARS);
|
|
3966
|
+
lines.push(`[tool_result] ${name}: ${result}`);
|
|
3967
|
+
break;
|
|
3968
|
+
}
|
|
3969
|
+
case "tool_failure":
|
|
3970
|
+
case "status_change":
|
|
3971
|
+
case "error": {
|
|
3972
|
+
flushTextDelta();
|
|
3973
|
+
lines.push(`[${e.event_type}] ${JSON.stringify(e.event_data)}`);
|
|
3974
|
+
break;
|
|
3975
|
+
}
|
|
3976
|
+
default: {
|
|
3977
|
+
flushTextDelta();
|
|
3978
|
+
const dataStr = JSON.stringify(e.event_data);
|
|
3979
|
+
lines.push(`[${e.event_type}] ${dataStr.slice(0, 200)}`);
|
|
3980
|
+
break;
|
|
3981
|
+
}
|
|
3982
|
+
}
|
|
3983
|
+
}
|
|
3984
|
+
flushTextDelta();
|
|
3985
|
+
return lines;
|
|
3986
|
+
}
|
|
3987
|
+
async function buildAnalysisContext(ctx) {
|
|
3988
|
+
const [sessionLogs, messageEvents, conversationMessages] = await Promise.all([
|
|
3989
|
+
getSessionLogs(ctx.sessionId, SELF_ANALYSIS_MAX_SESSION_LOGS),
|
|
3990
|
+
getMessageEvents(ctx.taskId, SELF_ANALYSIS_MAX_MESSAGE_EVENTS),
|
|
3991
|
+
getConversationMessages(ctx.conversationId, SELF_ANALYSIS_MAX_CONVERSATION_MESSAGES)
|
|
3992
|
+
]);
|
|
3993
|
+
let context = "";
|
|
3994
|
+
if (sessionLogs.length > 0) {
|
|
3995
|
+
const logText = sessionLogs.map((l) => `[${l.log_type}] ${l.message}`).join("\n");
|
|
3996
|
+
context += `
|
|
3997
|
+
## Session Logs (${sessionLogs.length} entries)
|
|
3998
|
+
`;
|
|
3999
|
+
context += truncateToChars(logText, SELF_ANALYSIS_LOG_CONTEXT_CHARS);
|
|
4000
|
+
} else {
|
|
4001
|
+
context += "\n## Session Logs\n(No session logs available \u2014 this is itself a data gap to note)\n";
|
|
4002
|
+
}
|
|
4003
|
+
if (messageEvents.length > 0) {
|
|
4004
|
+
const filteredLines = filterAndAggregateEvents(messageEvents);
|
|
4005
|
+
const eventText = filteredLines.join("\n");
|
|
4006
|
+
context += `
|
|
4007
|
+
## Message Events (${messageEvents.length} raw \u2192 ${filteredLines.length} aggregated)
|
|
4008
|
+
`;
|
|
4009
|
+
context += truncateToChars(eventText, SELF_ANALYSIS_EVENT_CONTEXT_CHARS);
|
|
4010
|
+
} else {
|
|
4011
|
+
context += "\n## Message Events\n(No message events available \u2014 this is itself a data gap to note)\n";
|
|
4012
|
+
}
|
|
4013
|
+
if (conversationMessages.length > 0) {
|
|
4014
|
+
context += `
|
|
4015
|
+
## Conversation Messages (${conversationMessages.length} entries)
|
|
4016
|
+
`;
|
|
4017
|
+
for (const msg of conversationMessages) {
|
|
4018
|
+
const role = msg.role || "unknown";
|
|
4019
|
+
const content = (msg.content || "").slice(0, 500);
|
|
4020
|
+
const status = msg.status || "";
|
|
4021
|
+
context += `[${role}${status ? ` (${status})` : ""}] ${content}
|
|
4022
|
+
`;
|
|
4023
|
+
}
|
|
4024
|
+
} else {
|
|
4025
|
+
context += "\n## Conversation Messages\n(No conversation messages available)\n";
|
|
4026
|
+
}
|
|
4027
|
+
if (ctx.toolCallRecords.length > 0) {
|
|
4028
|
+
context += `
|
|
4029
|
+
## Tool Call Records (${ctx.toolCallRecords.length} calls)
|
|
4030
|
+
`;
|
|
4031
|
+
for (const tc of ctx.toolCallRecords) {
|
|
4032
|
+
const inputStr = JSON.stringify(tc.input).slice(0, 200);
|
|
4033
|
+
context += `- ${tc.name}: ${inputStr} \u2192 ${tc.result.slice(0, 100)}
|
|
4034
|
+
`;
|
|
4035
|
+
}
|
|
4036
|
+
}
|
|
4037
|
+
if (ctx.toolFailures.length > 0) {
|
|
4038
|
+
context += `
|
|
4039
|
+
## Tool Failures (${ctx.toolFailures.length} failures)
|
|
4040
|
+
`;
|
|
4041
|
+
for (const tf of ctx.toolFailures) {
|
|
4042
|
+
context += `- ${tf.toolName}: ${tf.error}
|
|
4043
|
+
`;
|
|
4044
|
+
}
|
|
4045
|
+
}
|
|
4046
|
+
if (ctx.tokenUsage) {
|
|
4047
|
+
context += `
|
|
4048
|
+
## Token Usage
|
|
4049
|
+
`;
|
|
4050
|
+
context += `Input: ${ctx.tokenUsage.input_tokens}, Output: ${ctx.tokenUsage.output_tokens}
|
|
4051
|
+
`;
|
|
4052
|
+
}
|
|
4053
|
+
context += `
|
|
4054
|
+
## Task
|
|
4055
|
+
`;
|
|
4056
|
+
context += `Prompt: ${ctx.taskPrompt.slice(0, 500)}
|
|
4057
|
+
`;
|
|
4058
|
+
context += `Response length: ${ctx.taskResponse.length} chars
|
|
4059
|
+
`;
|
|
4060
|
+
return context;
|
|
4061
|
+
}
|
|
4062
|
+
async function submitSelfAnalysisFeedback(analysis) {
|
|
4063
|
+
const title = `Self-Analysis: Score ${analysis.overall_score}/10 \u2014 ${analysis.improvements.length} improvement(s)`;
|
|
4064
|
+
const improvementDetails = analysis.improvements.map((imp, i) => `${i + 1}. [${imp.severity}] **${imp.area}**: ${imp.description}
|
|
4065
|
+
\u2192 ${imp.suggestion}`).join("\n");
|
|
4066
|
+
const dataQualityNotes = [
|
|
4067
|
+
analysis.data_quality.session_logs_gaps ? `Session logs: ${analysis.data_quality.session_logs_gaps}` : null,
|
|
4068
|
+
analysis.data_quality.message_events_gaps ? `Message events: ${analysis.data_quality.message_events_gaps}` : null,
|
|
4069
|
+
analysis.data_quality.conversation_context_gaps ? `Conversation context: ${analysis.data_quality.conversation_context_gaps}` : null
|
|
4070
|
+
].filter(Boolean).join("\n");
|
|
4071
|
+
let description = `## Summary
|
|
4072
|
+
${analysis.summary}
|
|
4073
|
+
|
|
4074
|
+
`;
|
|
4075
|
+
description += `## Task Completion Quality (${analysis.task_completion_quality.score}/10)
|
|
4076
|
+
${analysis.task_completion_quality.assessment}
|
|
4077
|
+
|
|
4078
|
+
`;
|
|
4079
|
+
description += `## Improvements
|
|
4080
|
+
${improvementDetails}
|
|
4081
|
+
`;
|
|
4082
|
+
if (dataQualityNotes) {
|
|
4083
|
+
description += `
|
|
4084
|
+
## Data Quality Gaps
|
|
4085
|
+
${dataQualityNotes}
|
|
4086
|
+
`;
|
|
4087
|
+
}
|
|
4088
|
+
if (description.length > 4900) {
|
|
4089
|
+
description = description.slice(0, 4900) + "\n...[truncated]";
|
|
4090
|
+
}
|
|
4091
|
+
try {
|
|
4092
|
+
const result = await submitFeedback({
|
|
4093
|
+
slug: EDSGER_PRODUCT_SLUG,
|
|
4094
|
+
title: title.slice(0, 200),
|
|
4095
|
+
description,
|
|
4096
|
+
category: "improvement"
|
|
4097
|
+
});
|
|
4098
|
+
log.info(`Self-analysis feedback submitted: ${result.id}`);
|
|
4099
|
+
} catch (err) {
|
|
4100
|
+
if (err instanceof FeedbackError) {
|
|
4101
|
+
log.debug(`Feedback submission failed (${err.statusCode}): ${err.message}`);
|
|
4102
|
+
} else {
|
|
4103
|
+
log.debug(`Feedback submission failed: ${errorMessage(err)}`);
|
|
4104
|
+
}
|
|
4105
|
+
}
|
|
4106
|
+
}
|
|
4107
|
+
async function analyzeSelfPostTask(opts) {
|
|
4108
|
+
const {
|
|
4109
|
+
model,
|
|
4110
|
+
taskId,
|
|
4111
|
+
conversationId,
|
|
4112
|
+
taskPrompt,
|
|
4113
|
+
taskResponse,
|
|
4114
|
+
toolCallRecords,
|
|
4115
|
+
toolFailures,
|
|
4116
|
+
tokenUsage,
|
|
4117
|
+
sessionId
|
|
4118
|
+
} = opts;
|
|
4119
|
+
try {
|
|
4120
|
+
const analysisContext = await buildAnalysisContext({
|
|
4121
|
+
taskId,
|
|
4122
|
+
conversationId,
|
|
4123
|
+
sessionId,
|
|
4124
|
+
taskPrompt,
|
|
4125
|
+
taskResponse,
|
|
4126
|
+
toolCallRecords,
|
|
4127
|
+
toolFailures,
|
|
4128
|
+
tokenUsage
|
|
4129
|
+
});
|
|
4130
|
+
const prompt = `${SELF_ANALYSIS_PROMPT}
|
|
4131
|
+
${analysisContext}
|
|
4132
|
+
|
|
4133
|
+
Respond with a JSON object now.`;
|
|
4134
|
+
let structuredOutput;
|
|
4135
|
+
for await (const message of query2({
|
|
4136
|
+
prompt,
|
|
4137
|
+
options: {
|
|
4138
|
+
model,
|
|
4139
|
+
maxTurns: 1,
|
|
4140
|
+
allowedTools: [],
|
|
4141
|
+
effort: "low",
|
|
4142
|
+
outputFormat: SELF_ANALYSIS_OUTPUT_FORMAT
|
|
4143
|
+
}
|
|
4144
|
+
})) {
|
|
4145
|
+
if (message.type === "result") {
|
|
4146
|
+
const resultMsg = message;
|
|
4147
|
+
if (resultMsg.subtype === "success") {
|
|
4148
|
+
const successMsg = resultMsg;
|
|
4149
|
+
structuredOutput = successMsg.structured_output;
|
|
4150
|
+
log.debug(
|
|
4151
|
+
`Self-analysis cost: $${successMsg.total_cost_usd.toFixed(4)}`
|
|
4152
|
+
);
|
|
4153
|
+
}
|
|
4154
|
+
}
|
|
4155
|
+
}
|
|
4156
|
+
const analysis = structuredOutput ? safeParse(SelfAnalysisResultSchema, structuredOutput) : null;
|
|
4157
|
+
if (!analysis) {
|
|
4158
|
+
log.debug("Self-analysis: no valid structured output");
|
|
4159
|
+
return;
|
|
4160
|
+
}
|
|
4161
|
+
log.info(
|
|
4162
|
+
`Self-analysis complete: score=${analysis.overall_score}/10, perfect=${analysis.is_perfect}, improvements=${analysis.improvements.length}`
|
|
4163
|
+
);
|
|
4164
|
+
if (!analysis.is_perfect && analysis.improvements.length > 0) {
|
|
4165
|
+
await submitSelfAnalysisFeedback(analysis);
|
|
4166
|
+
} else {
|
|
4167
|
+
log.debug("Self-analysis: no improvements to report \u2014 skipping feedback");
|
|
4168
|
+
}
|
|
4169
|
+
} catch (err) {
|
|
4170
|
+
log.debug(`Self-analysis error: ${errorMessage(err)}`);
|
|
4171
|
+
}
|
|
4172
|
+
}
|
|
4173
|
+
|
|
3786
4174
|
// src/utils/retry.ts
|
|
3787
4175
|
async function withRetry(fn, opts = {}) {
|
|
3788
4176
|
const {
|
|
@@ -6021,7 +6409,7 @@ var TaskProcessor = class {
|
|
|
6021
6409
|
maxBudgetUsd: MAX_BUDGET_USD
|
|
6022
6410
|
};
|
|
6023
6411
|
try {
|
|
6024
|
-
for await (const message of
|
|
6412
|
+
for await (const message of query3({ prompt: task.prompt, options })) {
|
|
6025
6413
|
switch (message.type) {
|
|
6026
6414
|
case "assistant": {
|
|
6027
6415
|
const assistantMsg = message;
|
|
@@ -6096,9 +6484,29 @@ var TaskProcessor = class {
|
|
|
6096
6484
|
);
|
|
6097
6485
|
}
|
|
6098
6486
|
if (agentSessionId) {
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6487
|
+
(async () => {
|
|
6488
|
+
try {
|
|
6489
|
+
await this.evaluateSkillPostTask(agentSessionId, config.model);
|
|
6490
|
+
} catch (err) {
|
|
6491
|
+
log.debug(`Post-task skill evaluation skipped: ${err}`);
|
|
6492
|
+
}
|
|
6493
|
+
try {
|
|
6494
|
+
await analyzeSelfPostTask({
|
|
6495
|
+
model: config.model,
|
|
6496
|
+
taskId: task.id,
|
|
6497
|
+
conversationId: task.conversation_id,
|
|
6498
|
+
taskPrompt: task.prompt,
|
|
6499
|
+
taskResponse: finalResponse,
|
|
6500
|
+
toolCallRecords,
|
|
6501
|
+
toolFailures,
|
|
6502
|
+
tokenUsage,
|
|
6503
|
+
sessionId: this.sessionId || ""
|
|
6504
|
+
});
|
|
6505
|
+
} catch (err) {
|
|
6506
|
+
log.debug(`Post-task self-analysis skipped: ${err}`);
|
|
6507
|
+
}
|
|
6508
|
+
})().catch(() => {
|
|
6509
|
+
});
|
|
6102
6510
|
}
|
|
6103
6511
|
} catch (err) {
|
|
6104
6512
|
const errMsg = errorMessage(err);
|
|
@@ -6119,6 +6527,57 @@ var TaskProcessor = class {
|
|
|
6119
6527
|
}
|
|
6120
6528
|
};
|
|
6121
6529
|
|
|
6530
|
+
// src/db/session-log.ts
|
|
6531
|
+
var FLUSH_INTERVAL_MS = 3e3;
|
|
6532
|
+
var MAX_BATCH_SIZE = 100;
|
|
6533
|
+
var SessionLogEmitter = class {
|
|
6534
|
+
constructor(sessionId) {
|
|
6535
|
+
this.sessionId = sessionId;
|
|
6536
|
+
this.flushTimer = setInterval(() => this.flush(), FLUSH_INTERVAL_MS);
|
|
6537
|
+
}
|
|
6538
|
+
sequence = 0;
|
|
6539
|
+
buffer = [];
|
|
6540
|
+
flushTimer = null;
|
|
6541
|
+
flushing = false;
|
|
6542
|
+
/** Queue a log entry for batch insertion */
|
|
6543
|
+
push(logType, message) {
|
|
6544
|
+
this.sequence++;
|
|
6545
|
+
this.buffer.push({ log_type: logType, message, seq: this.sequence });
|
|
6546
|
+
if (this.buffer.length >= MAX_BATCH_SIZE) {
|
|
6547
|
+
this.flush();
|
|
6548
|
+
}
|
|
6549
|
+
}
|
|
6550
|
+
/** Flush buffered logs to Supabase */
|
|
6551
|
+
async flush() {
|
|
6552
|
+
if (this.flushing || this.buffer.length === 0) return;
|
|
6553
|
+
const batch = this.buffer.splice(0);
|
|
6554
|
+
this.flushing = true;
|
|
6555
|
+
try {
|
|
6556
|
+
await callMcpHandler("log.emit_batch", {
|
|
6557
|
+
session_id: this.sessionId,
|
|
6558
|
+
logs: batch
|
|
6559
|
+
});
|
|
6560
|
+
} catch (err) {
|
|
6561
|
+
log.debug(
|
|
6562
|
+
`Failed to flush session logs: ${err instanceof Error ? err.message : err}`
|
|
6563
|
+
);
|
|
6564
|
+
if (this.buffer.length < MAX_BATCH_SIZE * 5) {
|
|
6565
|
+
this.buffer.unshift(...batch);
|
|
6566
|
+
}
|
|
6567
|
+
} finally {
|
|
6568
|
+
this.flushing = false;
|
|
6569
|
+
}
|
|
6570
|
+
}
|
|
6571
|
+
/** Stop the emitter and flush remaining logs */
|
|
6572
|
+
async stop() {
|
|
6573
|
+
if (this.flushTimer) {
|
|
6574
|
+
clearInterval(this.flushTimer);
|
|
6575
|
+
this.flushTimer = null;
|
|
6576
|
+
}
|
|
6577
|
+
await this.flush();
|
|
6578
|
+
}
|
|
6579
|
+
};
|
|
6580
|
+
|
|
6122
6581
|
// src/commands/start.ts
|
|
6123
6582
|
function registerStartCommand(program2) {
|
|
6124
6583
|
program2.command("start", { isDefault: true, hidden: true }).description("Start the agent (default command)").option("-w, --workspace <path>", "Workspace path (default: current directory)").option("-n, --name <name>", "Session name").option("-v, --verbose", "Enable verbose/debug logging").action(runAgent);
|
|
@@ -6182,10 +6641,16 @@ async function runAgent(opts) {
|
|
|
6182
6641
|
const processor = new TaskProcessor();
|
|
6183
6642
|
processor.init(userId);
|
|
6184
6643
|
const sessionManager = new SessionManager();
|
|
6644
|
+
let logEmitter = null;
|
|
6185
6645
|
const browserRef = getBrowser();
|
|
6186
6646
|
const shutdown = async () => {
|
|
6187
6647
|
console.log();
|
|
6188
6648
|
log.info("Shutting down...");
|
|
6649
|
+
setLogHook(null);
|
|
6650
|
+
try {
|
|
6651
|
+
if (logEmitter) await logEmitter.stop();
|
|
6652
|
+
} catch {
|
|
6653
|
+
}
|
|
6189
6654
|
try {
|
|
6190
6655
|
if (browserRef.isConnected()) await browserRef.disconnect();
|
|
6191
6656
|
} catch {
|
|
@@ -6200,6 +6665,10 @@ async function runAgent(opts) {
|
|
|
6200
6665
|
await processor.processTask(task);
|
|
6201
6666
|
});
|
|
6202
6667
|
processor.setSessionId(session.id);
|
|
6668
|
+
logEmitter = new SessionLogEmitter(session.id);
|
|
6669
|
+
setLogHook((logType, message) => {
|
|
6670
|
+
logEmitter?.push(logType, message);
|
|
6671
|
+
});
|
|
6203
6672
|
log.info("Listening for tasks (chat + jobs) from web UI...");
|
|
6204
6673
|
log.info("Press Ctrl+C to stop.\n");
|
|
6205
6674
|
const rl = createInterface2({
|
|
@@ -6425,17 +6894,17 @@ Memories (${memories.length}):`));
|
|
|
6425
6894
|
process.exit(1);
|
|
6426
6895
|
}
|
|
6427
6896
|
});
|
|
6428
|
-
memoryCmd.command("search <query>").description("Search memories").action(async (
|
|
6897
|
+
memoryCmd.command("search <query>").description("Search memories").action(async (query4) => {
|
|
6429
6898
|
try {
|
|
6430
6899
|
await getCurrentUserId();
|
|
6431
6900
|
const mm = new MemoryManager();
|
|
6432
|
-
const results = await mm.search(
|
|
6901
|
+
const results = await mm.search(query4);
|
|
6433
6902
|
if (results.length === 0) {
|
|
6434
|
-
console.log(chalk7.yellow(`No memories matching "${
|
|
6903
|
+
console.log(chalk7.yellow(`No memories matching "${query4}"`));
|
|
6435
6904
|
return;
|
|
6436
6905
|
}
|
|
6437
6906
|
console.log(chalk7.bold(`
|
|
6438
|
-
Search results for "${
|
|
6907
|
+
Search results for "${query4}":`));
|
|
6439
6908
|
for (const m of results) {
|
|
6440
6909
|
console.log(` [${m.category}] ${m.content}`);
|
|
6441
6910
|
}
|
|
@@ -6501,18 +6970,18 @@ function registerSkillCommands(program2) {
|
|
|
6501
6970
|
);
|
|
6502
6971
|
console.log();
|
|
6503
6972
|
});
|
|
6504
|
-
skillCmd.command("search <query>").description("Search skills in your collection and marketplace").action(async (
|
|
6973
|
+
skillCmd.command("search <query>").description("Search skills in your collection and marketplace").action(async (query4) => {
|
|
6505
6974
|
const spinner = ora4("Searching skills...").start();
|
|
6506
6975
|
const sm = await getAuthenticatedSkillManager();
|
|
6507
6976
|
try {
|
|
6508
|
-
const results = await sm.searchDb(
|
|
6977
|
+
const results = await sm.searchDb(query4);
|
|
6509
6978
|
spinner.stop();
|
|
6510
6979
|
if (results.length === 0) {
|
|
6511
|
-
console.log(chalk8.yellow(`No skills found for "${
|
|
6980
|
+
console.log(chalk8.yellow(`No skills found for "${query4}"`));
|
|
6512
6981
|
return;
|
|
6513
6982
|
}
|
|
6514
6983
|
console.log(chalk8.bold(`
|
|
6515
|
-
Skills matching "${
|
|
6984
|
+
Skills matching "${query4}":`));
|
|
6516
6985
|
for (const r of results) {
|
|
6517
6986
|
const emoji = r.emoji ? `${r.emoji} ` : "";
|
|
6518
6987
|
console.log(` ${emoji}${chalk8.cyan(r.name)} [${r.source}]`);
|
|
@@ -6605,7 +7074,7 @@ function registerJobCommands(program2) {
|
|
|
6605
7074
|
jobCmd.command("list").description("List your defined jobs").action(async () => {
|
|
6606
7075
|
try {
|
|
6607
7076
|
const userId = await getCurrentUserId();
|
|
6608
|
-
const { JobRunner: JobRunner2 } = await import("./job-runner-
|
|
7077
|
+
const { JobRunner: JobRunner2 } = await import("./job-runner-RGP4CLYV.js");
|
|
6609
7078
|
const runner = new JobRunner2();
|
|
6610
7079
|
const jobs = await runner.listJobs();
|
|
6611
7080
|
if (jobs.length === 0) {
|
|
@@ -6629,7 +7098,7 @@ function registerJobCommands(program2) {
|
|
|
6629
7098
|
jobCmd.command("status [name]").description("Show run history for a job (or all jobs)").option("-l, --limit <number>", "Max runs to show (default: 5)").action(async (name, opts) => {
|
|
6630
7099
|
try {
|
|
6631
7100
|
const userId = await getCurrentUserId();
|
|
6632
|
-
const { JobRunner: JobRunner2 } = await import("./job-runner-
|
|
7101
|
+
const { JobRunner: JobRunner2 } = await import("./job-runner-RGP4CLYV.js");
|
|
6633
7102
|
const runner = new JobRunner2();
|
|
6634
7103
|
const runs = await runner.getRunHistory(name, parseInt(opts.limit || "5"));
|
|
6635
7104
|
if (runs.length === 0) {
|
|
@@ -6668,7 +7137,7 @@ Job Run History${name ? ` \u2014 ${name}` : ""}:`));
|
|
|
6668
7137
|
process.exit(1);
|
|
6669
7138
|
}
|
|
6670
7139
|
const userId = await getCurrentUserId();
|
|
6671
|
-
const { JobRunner: JobRunner2 } = await import("./job-runner-
|
|
7140
|
+
const { JobRunner: JobRunner2 } = await import("./job-runner-RGP4CLYV.js");
|
|
6672
7141
|
const runner = new JobRunner2();
|
|
6673
7142
|
const job = await runner.loadJob(name);
|
|
6674
7143
|
if (!job) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "assistme",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "AssistMe CLI Agent - AI-powered assistant that controls your real browser",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"conf": "^13.0.1",
|
|
27
27
|
"croner": "^10.0.1",
|
|
28
28
|
"dotenv": "^16.5.0",
|
|
29
|
+
"edsger-feedback": "^0.1.0",
|
|
29
30
|
"glob": "^11.0.1",
|
|
30
31
|
"ora": "^8.2.0",
|
|
31
32
|
"ws": "^8.18.0",
|