@skj1724/oh-my-opencode 3.19.3 → 3.19.4
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/cli/index.js +48 -1
- package/dist/features/background-agent/index.d.ts +1 -0
- package/dist/features/background-agent/manager.d.ts +1 -1
- package/dist/features/background-agent/perf-aggregator.d.ts +26 -0
- package/dist/features/background-agent/types.d.ts +15 -0
- package/dist/index.js +232 -299
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/perf-timer.d.ts +26 -0
- package/dist/shared/perf-timer.test.d.ts +1 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -6117,6 +6117,53 @@ var init_model_resolver = __esm(() => {
|
|
|
6117
6117
|
init_model_availability();
|
|
6118
6118
|
});
|
|
6119
6119
|
|
|
6120
|
+
// src/shared/perf-timer.ts
|
|
6121
|
+
class PerfTimer {
|
|
6122
|
+
marks = new Map;
|
|
6123
|
+
spans = [];
|
|
6124
|
+
mark(name) {
|
|
6125
|
+
this.marks.set(name, performance.now());
|
|
6126
|
+
}
|
|
6127
|
+
measure(name, startMark, endMark) {
|
|
6128
|
+
const start = this.marks.get(startMark);
|
|
6129
|
+
if (start === undefined)
|
|
6130
|
+
return 0;
|
|
6131
|
+
const end = endMark ? this.marks.get(endMark) ?? performance.now() : performance.now();
|
|
6132
|
+
const duration = end - start;
|
|
6133
|
+
this.spans.push({ name, durationMs: duration, start, end });
|
|
6134
|
+
return duration;
|
|
6135
|
+
}
|
|
6136
|
+
getReport() {
|
|
6137
|
+
return [...this.spans];
|
|
6138
|
+
}
|
|
6139
|
+
reset() {
|
|
6140
|
+
this.marks.clear();
|
|
6141
|
+
this.spans = [];
|
|
6142
|
+
}
|
|
6143
|
+
static formatDuration(start, end, options) {
|
|
6144
|
+
const ms = (end ?? new Date).getTime() - start.getTime();
|
|
6145
|
+
const absMs = Math.abs(ms);
|
|
6146
|
+
const totalSeconds = absMs / 1000;
|
|
6147
|
+
const seconds = Math.floor(totalSeconds);
|
|
6148
|
+
const minutes = Math.floor(seconds / 60);
|
|
6149
|
+
const hours = Math.floor(minutes / 60);
|
|
6150
|
+
const precision = options?.precision ?? "full";
|
|
6151
|
+
if (hours > 0) {
|
|
6152
|
+
if (precision === "compact") {
|
|
6153
|
+
return `${hours}h ${minutes % 60}m`;
|
|
6154
|
+
}
|
|
6155
|
+
return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
|
|
6156
|
+
}
|
|
6157
|
+
if (minutes > 0) {
|
|
6158
|
+
return `${minutes}m ${seconds % 60}s`;
|
|
6159
|
+
}
|
|
6160
|
+
if (seconds === 0 && absMs > 0) {
|
|
6161
|
+
return `${(absMs / 1000).toFixed(1)}s`;
|
|
6162
|
+
}
|
|
6163
|
+
return `${seconds}s`;
|
|
6164
|
+
}
|
|
6165
|
+
}
|
|
6166
|
+
|
|
6120
6167
|
// src/shared/index.ts
|
|
6121
6168
|
var init_shared = __esm(() => {
|
|
6122
6169
|
init_frontmatter();
|
|
@@ -8375,7 +8422,7 @@ var import_picocolors2 = __toESM(require_picocolors(), 1);
|
|
|
8375
8422
|
// package.json
|
|
8376
8423
|
var package_default = {
|
|
8377
8424
|
name: "@skj1724/oh-my-opencode",
|
|
8378
|
-
version: "3.19.
|
|
8425
|
+
version: "3.19.4",
|
|
8379
8426
|
description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
|
|
8380
8427
|
main: "dist/index.js",
|
|
8381
8428
|
types: "dist/index.d.ts",
|
|
@@ -25,6 +25,7 @@ export declare class BackgroundManager {
|
|
|
25
25
|
private concurrencyManager;
|
|
26
26
|
private shutdownTriggered;
|
|
27
27
|
private config?;
|
|
28
|
+
private perfAggregator;
|
|
28
29
|
private queuesByKey;
|
|
29
30
|
private processingKeys;
|
|
30
31
|
constructor(ctx: PluginInput, config?: BackgroundTaskConfig);
|
|
@@ -89,7 +90,6 @@ export declare class BackgroundManager {
|
|
|
89
90
|
*/
|
|
90
91
|
private tryCompleteTask;
|
|
91
92
|
private notifyParentSession;
|
|
92
|
-
private formatDuration;
|
|
93
93
|
private hasRunningTasks;
|
|
94
94
|
private pruneStaleTasksAndNotifications;
|
|
95
95
|
private checkAndInterruptStaleTasks;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { BackgroundTask } from "./types";
|
|
2
|
+
export interface AgentStat {
|
|
3
|
+
count: number;
|
|
4
|
+
avgMs: number;
|
|
5
|
+
p50Ms: number;
|
|
6
|
+
p95Ms: number;
|
|
7
|
+
totalMs: number;
|
|
8
|
+
}
|
|
9
|
+
export interface AggregatedReport {
|
|
10
|
+
totalTasks: number;
|
|
11
|
+
tasksByAgent: Map<string, AgentStat>;
|
|
12
|
+
totalToolCalls: number;
|
|
13
|
+
queueWaitStats: {
|
|
14
|
+
avgMs: number;
|
|
15
|
+
p50Ms: number;
|
|
16
|
+
p95Ms: number;
|
|
17
|
+
maxMs: number;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export declare class PerformanceAggregator {
|
|
21
|
+
private tasks;
|
|
22
|
+
recordTaskCompletion(task: BackgroundTask): void;
|
|
23
|
+
getReport(): AggregatedReport;
|
|
24
|
+
reset(): void;
|
|
25
|
+
get taskCount(): number;
|
|
26
|
+
}
|
|
@@ -1,10 +1,25 @@
|
|
|
1
1
|
export type BackgroundTaskStatus = "pending" | "running" | "completed" | "error" | "cancelled";
|
|
2
|
+
export interface PhaseTiming {
|
|
3
|
+
/** queuedAt -> startedAt in ms */
|
|
4
|
+
queueWaitMs: number;
|
|
5
|
+
/** startedAt -> completedAt in ms */
|
|
6
|
+
totalRunMs: number;
|
|
7
|
+
/** startedAt -> first assistant message in ms */
|
|
8
|
+
firstResponseMs?: number;
|
|
9
|
+
/** startedAt -> last message in ms */
|
|
10
|
+
lastResponseMs?: number;
|
|
11
|
+
/** Total tool call count */
|
|
12
|
+
toolCallCount: number;
|
|
13
|
+
/** toolCallCount / totalRunMs * 60000 (calls per minute) */
|
|
14
|
+
toolCallRate?: number;
|
|
15
|
+
}
|
|
2
16
|
export interface TaskProgress {
|
|
3
17
|
toolCalls: number;
|
|
4
18
|
lastTool?: string;
|
|
5
19
|
lastUpdate: Date;
|
|
6
20
|
lastMessage?: string;
|
|
7
21
|
lastMessageAt?: Date;
|
|
22
|
+
phaseTiming?: PhaseTiming;
|
|
8
23
|
}
|
|
9
24
|
export interface BackgroundTask {
|
|
10
25
|
id: string;
|
package/dist/index.js
CHANGED
|
@@ -4781,7 +4781,7 @@ var init_agent_tool_restrictions = __esm(() => {
|
|
|
4781
4781
|
});
|
|
4782
4782
|
|
|
4783
4783
|
// src/shared/model-requirements.ts
|
|
4784
|
-
var AGENT_MODEL_REQUIREMENTS
|
|
4784
|
+
var AGENT_MODEL_REQUIREMENTS;
|
|
4785
4785
|
var init_model_requirements = __esm(() => {
|
|
4786
4786
|
AGENT_MODEL_REQUIREMENTS = {
|
|
4787
4787
|
sisyphus: {
|
|
@@ -4850,58 +4850,6 @@ var init_model_requirements = __esm(() => {
|
|
|
4850
4850
|
]
|
|
4851
4851
|
}
|
|
4852
4852
|
};
|
|
4853
|
-
CATEGORY_MODEL_REQUIREMENTS = {
|
|
4854
|
-
"visual-engineering": {
|
|
4855
|
-
fallbackChain: [
|
|
4856
|
-
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" },
|
|
4857
|
-
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
|
|
4858
|
-
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" }
|
|
4859
|
-
]
|
|
4860
|
-
},
|
|
4861
|
-
ultrabrain: {
|
|
4862
|
-
fallbackChain: [
|
|
4863
|
-
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2-codex", variant: "xhigh" },
|
|
4864
|
-
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
|
|
4865
|
-
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" }
|
|
4866
|
-
]
|
|
4867
|
-
},
|
|
4868
|
-
artistry: {
|
|
4869
|
-
fallbackChain: [
|
|
4870
|
-
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "max" },
|
|
4871
|
-
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
|
|
4872
|
-
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2" }
|
|
4873
|
-
]
|
|
4874
|
-
},
|
|
4875
|
-
quick: {
|
|
4876
|
-
fallbackChain: [
|
|
4877
|
-
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-haiku-4-5" },
|
|
4878
|
-
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
|
|
4879
|
-
{ providers: ["opencode"], model: "gpt-5-nano" }
|
|
4880
|
-
]
|
|
4881
|
-
},
|
|
4882
|
-
"unspecified-low": {
|
|
4883
|
-
fallbackChain: [
|
|
4884
|
-
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" },
|
|
4885
|
-
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2-codex", variant: "medium" },
|
|
4886
|
-
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" }
|
|
4887
|
-
]
|
|
4888
|
-
},
|
|
4889
|
-
"unspecified-high": {
|
|
4890
|
-
fallbackChain: [
|
|
4891
|
-
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
|
|
4892
|
-
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
|
|
4893
|
-
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" }
|
|
4894
|
-
]
|
|
4895
|
-
},
|
|
4896
|
-
writing: {
|
|
4897
|
-
fallbackChain: [
|
|
4898
|
-
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
|
|
4899
|
-
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" },
|
|
4900
|
-
{ providers: ["zai-coding-plan"], model: "glm-4.7" },
|
|
4901
|
-
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2" }
|
|
4902
|
-
]
|
|
4903
|
-
}
|
|
4904
|
-
};
|
|
4905
4853
|
});
|
|
4906
4854
|
|
|
4907
4855
|
// src/shared/model-availability.ts
|
|
@@ -5038,6 +4986,53 @@ var init_model_resolver = __esm(() => {
|
|
|
5038
4986
|
init_model_availability();
|
|
5039
4987
|
});
|
|
5040
4988
|
|
|
4989
|
+
// src/shared/perf-timer.ts
|
|
4990
|
+
class PerfTimer {
|
|
4991
|
+
marks = new Map;
|
|
4992
|
+
spans = [];
|
|
4993
|
+
mark(name) {
|
|
4994
|
+
this.marks.set(name, performance.now());
|
|
4995
|
+
}
|
|
4996
|
+
measure(name, startMark, endMark) {
|
|
4997
|
+
const start = this.marks.get(startMark);
|
|
4998
|
+
if (start === undefined)
|
|
4999
|
+
return 0;
|
|
5000
|
+
const end = endMark ? this.marks.get(endMark) ?? performance.now() : performance.now();
|
|
5001
|
+
const duration = end - start;
|
|
5002
|
+
this.spans.push({ name, durationMs: duration, start, end });
|
|
5003
|
+
return duration;
|
|
5004
|
+
}
|
|
5005
|
+
getReport() {
|
|
5006
|
+
return [...this.spans];
|
|
5007
|
+
}
|
|
5008
|
+
reset() {
|
|
5009
|
+
this.marks.clear();
|
|
5010
|
+
this.spans = [];
|
|
5011
|
+
}
|
|
5012
|
+
static formatDuration(start, end, options) {
|
|
5013
|
+
const ms = (end ?? new Date).getTime() - start.getTime();
|
|
5014
|
+
const absMs = Math.abs(ms);
|
|
5015
|
+
const totalSeconds = absMs / 1000;
|
|
5016
|
+
const seconds = Math.floor(totalSeconds);
|
|
5017
|
+
const minutes = Math.floor(seconds / 60);
|
|
5018
|
+
const hours = Math.floor(minutes / 60);
|
|
5019
|
+
const precision = options?.precision ?? "full";
|
|
5020
|
+
if (hours > 0) {
|
|
5021
|
+
if (precision === "compact") {
|
|
5022
|
+
return `${hours}h ${minutes % 60}m`;
|
|
5023
|
+
}
|
|
5024
|
+
return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
|
|
5025
|
+
}
|
|
5026
|
+
if (minutes > 0) {
|
|
5027
|
+
return `${minutes}m ${seconds % 60}s`;
|
|
5028
|
+
}
|
|
5029
|
+
if (seconds === 0 && absMs > 0) {
|
|
5030
|
+
return `${(absMs / 1000).toFixed(1)}s`;
|
|
5031
|
+
}
|
|
5032
|
+
return `${seconds}s`;
|
|
5033
|
+
}
|
|
5034
|
+
}
|
|
5035
|
+
|
|
5041
5036
|
// src/shared/index.ts
|
|
5042
5037
|
var init_shared = __esm(() => {
|
|
5043
5038
|
init_frontmatter();
|
|
@@ -19819,7 +19814,9 @@ var KEYWORD_DETECTORS = [
|
|
|
19819
19814
|
\u5982\u679C\u590D\u6742\uFF08\u67B6\u6784\u3001\u591A\u7CFB\u7EDF\u30012 \u6B21\u4EE5\u4E0A\u5931\u8D25\u540E\u7684\u8C03\u8BD5\uFF09\uFF1A
|
|
19820
19815
|
- \u54A8\u8BE2 oracle \u83B7\u53D6\u6218\u7565\u6307\u5BFC
|
|
19821
19816
|
|
|
19822
|
-
\u7EFC\u5408\u53D1\u73B0\u540E\u518D\u7EE7\u7EED\u3002
|
|
19817
|
+
\u7EFC\u5408\u53D1\u73B0\u540E\u518D\u7EE7\u7EED\u3002
|
|
19818
|
+
**\u4E2D\u6587\u8BED\u5883\u601D\u8003\u56DE\u590D**
|
|
19819
|
+
`
|
|
19823
19820
|
}
|
|
19824
19821
|
];
|
|
19825
19822
|
|
|
@@ -41888,19 +41885,6 @@ var BACKGROUND_CANCEL_DESCRIPTION = `\u53D6\u6D88\u6B63\u5728\u8FD0\u884C\u7684\
|
|
|
41888
41885
|
// src/tools/background-task/tools.ts
|
|
41889
41886
|
init_logger();
|
|
41890
41887
|
init_session_cursor();
|
|
41891
|
-
function formatDuration(start, end) {
|
|
41892
|
-
const duration3 = (end ?? new Date).getTime() - start.getTime();
|
|
41893
|
-
const seconds = Math.floor(duration3 / 1000);
|
|
41894
|
-
const minutes = Math.floor(seconds / 60);
|
|
41895
|
-
const hours = Math.floor(minutes / 60);
|
|
41896
|
-
if (hours > 0) {
|
|
41897
|
-
return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
|
|
41898
|
-
} else if (minutes > 0) {
|
|
41899
|
-
return `${minutes}m ${seconds % 60}s`;
|
|
41900
|
-
} else {
|
|
41901
|
-
return `${seconds}s`;
|
|
41902
|
-
}
|
|
41903
|
-
}
|
|
41904
41888
|
function delay(ms) {
|
|
41905
41889
|
return new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
41906
41890
|
}
|
|
@@ -41912,9 +41896,9 @@ function truncateText(text, maxLength) {
|
|
|
41912
41896
|
function formatTaskStatus(task) {
|
|
41913
41897
|
let duration3;
|
|
41914
41898
|
if (task.status === "pending" && task.queuedAt) {
|
|
41915
|
-
duration3 = formatDuration(task.queuedAt, undefined);
|
|
41899
|
+
duration3 = PerfTimer.formatDuration(task.queuedAt, undefined);
|
|
41916
41900
|
} else if (task.startedAt) {
|
|
41917
|
-
duration3 = formatDuration(task.startedAt, task.completedAt);
|
|
41901
|
+
duration3 = PerfTimer.formatDuration(task.startedAt, task.completedAt);
|
|
41918
41902
|
} else {
|
|
41919
41903
|
duration3 = "N/A";
|
|
41920
41904
|
}
|
|
@@ -41951,6 +41935,18 @@ ${truncated}
|
|
|
41951
41935
|
> **Failed**: The task encountered an error. Check the last message for details.`;
|
|
41952
41936
|
}
|
|
41953
41937
|
const durationLabel = task.status === "pending" ? "Queued for" : "Duration";
|
|
41938
|
+
let perfBlock = "";
|
|
41939
|
+
if (task.progress?.phaseTiming) {
|
|
41940
|
+
const pt = task.progress.phaseTiming;
|
|
41941
|
+
const queueWait = PerfTimer.formatDuration(new Date(0), new Date(pt.queueWaitMs));
|
|
41942
|
+
perfBlock = `
|
|
41943
|
+
|
|
41944
|
+
### Performance
|
|
41945
|
+
| Metric | Value |
|
|
41946
|
+
|--------|-------|
|
|
41947
|
+
| Queue wait | ${queueWait} |
|
|
41948
|
+
| Tool calls | ${pt.toolCallCount} |`;
|
|
41949
|
+
}
|
|
41954
41950
|
return `# Task Status
|
|
41955
41951
|
|
|
41956
41952
|
| Field | Value |
|
|
@@ -41966,7 +41962,8 @@ ${statusNote}
|
|
|
41966
41962
|
|
|
41967
41963
|
\`\`\`
|
|
41968
41964
|
${promptPreview}
|
|
41969
|
-
\`\`\`${lastMessageSection}
|
|
41965
|
+
\`\`\`${lastMessageSection}${perfBlock}
|
|
41966
|
+
}`;
|
|
41970
41967
|
}
|
|
41971
41968
|
async function formatTaskResult(task, client2) {
|
|
41972
41969
|
if (!task.sessionID) {
|
|
@@ -41980,12 +41977,25 @@ async function formatTaskResult(task, client2) {
|
|
|
41980
41977
|
}
|
|
41981
41978
|
const messages = messagesResult.data ?? messagesResult;
|
|
41982
41979
|
if (!Array.isArray(messages) || messages.length === 0) {
|
|
41980
|
+
const duration4 = PerfTimer.formatDuration(task.startedAt ?? new Date, task.completedAt);
|
|
41981
|
+
let perfBlock2 = "";
|
|
41982
|
+
if (task.progress?.phaseTiming) {
|
|
41983
|
+
const pt = task.progress.phaseTiming;
|
|
41984
|
+
const queueWait = PerfTimer.formatDuration(new Date(0), new Date(pt.queueWaitMs));
|
|
41985
|
+
perfBlock2 = `
|
|
41986
|
+
|
|
41987
|
+
### Performance
|
|
41988
|
+
| Metric | Value |
|
|
41989
|
+
|--------|-------|
|
|
41990
|
+
| Queue wait | ${queueWait} |
|
|
41991
|
+
| Tool calls | ${pt.toolCallCount} |`;
|
|
41992
|
+
}
|
|
41983
41993
|
return `Task Result
|
|
41984
41994
|
|
|
41985
41995
|
Task ID: ${task.id}
|
|
41986
41996
|
Description: ${task.description}
|
|
41987
|
-
Duration: ${
|
|
41988
|
-
Session ID: ${task.sessionID}
|
|
41997
|
+
Duration: ${duration4}
|
|
41998
|
+
Session ID: ${task.sessionID}${perfBlock2}
|
|
41989
41999
|
|
|
41990
42000
|
---
|
|
41991
42001
|
|
|
@@ -41993,12 +42003,25 @@ Session ID: ${task.sessionID}
|
|
|
41993
42003
|
}
|
|
41994
42004
|
const relevantMessages = messages.filter((m) => m.info?.role === "assistant" || m.info?.role === "tool");
|
|
41995
42005
|
if (relevantMessages.length === 0) {
|
|
42006
|
+
const duration4 = PerfTimer.formatDuration(task.startedAt ?? new Date, task.completedAt);
|
|
42007
|
+
let perfBlock2 = "";
|
|
42008
|
+
if (task.progress?.phaseTiming) {
|
|
42009
|
+
const pt = task.progress.phaseTiming;
|
|
42010
|
+
const queueWait = PerfTimer.formatDuration(new Date(0), new Date(pt.queueWaitMs));
|
|
42011
|
+
perfBlock2 = `
|
|
42012
|
+
|
|
42013
|
+
### Performance
|
|
42014
|
+
| Metric | Value |
|
|
42015
|
+
|--------|-------|
|
|
42016
|
+
| Queue wait | ${queueWait} |
|
|
42017
|
+
| Tool calls | ${pt.toolCallCount} |`;
|
|
42018
|
+
}
|
|
41996
42019
|
return `Task Result
|
|
41997
42020
|
|
|
41998
42021
|
Task ID: ${task.id}
|
|
41999
42022
|
Description: ${task.description}
|
|
42000
|
-
Duration: ${
|
|
42001
|
-
Session ID: ${task.sessionID}
|
|
42023
|
+
Duration: ${duration4}
|
|
42024
|
+
Session ID: ${task.sessionID}${perfBlock2}
|
|
42002
42025
|
|
|
42003
42026
|
---
|
|
42004
42027
|
|
|
@@ -42011,13 +42034,25 @@ Session ID: ${task.sessionID}
|
|
|
42011
42034
|
});
|
|
42012
42035
|
const newMessages = consumeNewMessages(task.sessionID, sortedMessages);
|
|
42013
42036
|
if (newMessages.length === 0) {
|
|
42014
|
-
const duration4 = formatDuration(task.startedAt ?? new Date, task.completedAt);
|
|
42037
|
+
const duration4 = PerfTimer.formatDuration(task.startedAt ?? new Date, task.completedAt);
|
|
42038
|
+
let perfBlock2 = "";
|
|
42039
|
+
if (task.progress?.phaseTiming) {
|
|
42040
|
+
const pt = task.progress.phaseTiming;
|
|
42041
|
+
const queueWait = PerfTimer.formatDuration(new Date(0), new Date(pt.queueWaitMs));
|
|
42042
|
+
perfBlock2 = `
|
|
42043
|
+
|
|
42044
|
+
### Performance
|
|
42045
|
+
| Metric | Value |
|
|
42046
|
+
|--------|-------|
|
|
42047
|
+
| Queue wait | ${queueWait} |
|
|
42048
|
+
| Tool calls | ${pt.toolCallCount} |`;
|
|
42049
|
+
}
|
|
42015
42050
|
return `Task Result
|
|
42016
42051
|
|
|
42017
42052
|
Task ID: ${task.id}
|
|
42018
42053
|
Description: ${task.description}
|
|
42019
42054
|
Duration: ${duration4}
|
|
42020
|
-
Session ID: ${task.sessionID}
|
|
42055
|
+
Session ID: ${task.sessionID}${perfBlock2}
|
|
42021
42056
|
|
|
42022
42057
|
---
|
|
42023
42058
|
|
|
@@ -42045,13 +42080,25 @@ Session ID: ${task.sessionID}
|
|
|
42045
42080
|
const textContent = extractedContent.filter((text) => text.length > 0).join(`
|
|
42046
42081
|
|
|
42047
42082
|
`);
|
|
42048
|
-
const duration3 = formatDuration(task.startedAt ?? new Date, task.completedAt);
|
|
42083
|
+
const duration3 = PerfTimer.formatDuration(task.startedAt ?? new Date, task.completedAt);
|
|
42084
|
+
let perfBlock = "";
|
|
42085
|
+
if (task.progress?.phaseTiming) {
|
|
42086
|
+
const pt = task.progress.phaseTiming;
|
|
42087
|
+
const queueWait = PerfTimer.formatDuration(new Date(0), new Date(pt.queueWaitMs));
|
|
42088
|
+
perfBlock = `
|
|
42089
|
+
|
|
42090
|
+
### Performance
|
|
42091
|
+
| Metric | Value |
|
|
42092
|
+
|--------|-------|
|
|
42093
|
+
| Queue wait | ${queueWait} |
|
|
42094
|
+
| Tool calls | ${pt.toolCallCount} |`;
|
|
42095
|
+
}
|
|
42049
42096
|
return `Task Result
|
|
42050
42097
|
|
|
42051
42098
|
Task ID: ${task.id}
|
|
42052
42099
|
Description: ${task.description}
|
|
42053
42100
|
Duration: ${duration3}
|
|
42054
|
-
Session ID: ${task.sessionID}
|
|
42101
|
+
Session ID: ${task.sessionID}${perfBlock}
|
|
42055
42102
|
|
|
42056
42103
|
---
|
|
42057
42104
|
|
|
@@ -42687,14 +42734,7 @@ class TaskToastManager {
|
|
|
42687
42734
|
return Array.from(this.tasks.values()).filter((t) => t.status === "queued").sort((a, b) => a.startedAt.getTime() - b.startedAt.getTime());
|
|
42688
42735
|
}
|
|
42689
42736
|
formatDuration(startedAt) {
|
|
42690
|
-
|
|
42691
|
-
if (seconds < 60)
|
|
42692
|
-
return `${seconds}s`;
|
|
42693
|
-
const minutes = Math.floor(seconds / 60);
|
|
42694
|
-
if (minutes < 60)
|
|
42695
|
-
return `${minutes}m ${seconds % 60}s`;
|
|
42696
|
-
const hours = Math.floor(minutes / 60);
|
|
42697
|
-
return `${hours}h ${minutes % 60}m`;
|
|
42737
|
+
return PerfTimer.formatDuration(startedAt, undefined, { precision: "compact" });
|
|
42698
42738
|
}
|
|
42699
42739
|
getConcurrencyInfo() {
|
|
42700
42740
|
if (!this.concurrencyManager)
|
|
@@ -42798,9 +42838,6 @@ function initTaskToastManager(client2, concurrencyManager) {
|
|
|
42798
42838
|
}
|
|
42799
42839
|
// src/tools/delegate-task/tools.ts
|
|
42800
42840
|
init_shared();
|
|
42801
|
-
init_model_availability();
|
|
42802
|
-
init_model_resolver();
|
|
42803
|
-
init_model_requirements();
|
|
42804
42841
|
var SISYPHUS_JUNIOR_AGENT = "sisyphus-junior";
|
|
42805
42842
|
function parseModelString(model) {
|
|
42806
42843
|
const parts = model.split("/");
|
|
@@ -42822,17 +42859,6 @@ function getMessageDir9(sessionID) {
|
|
|
42822
42859
|
}
|
|
42823
42860
|
return null;
|
|
42824
42861
|
}
|
|
42825
|
-
function formatDuration2(start, end) {
|
|
42826
|
-
const duration3 = (end ?? new Date).getTime() - start.getTime();
|
|
42827
|
-
const seconds = Math.floor(duration3 / 1000);
|
|
42828
|
-
const minutes = Math.floor(seconds / 60);
|
|
42829
|
-
const hours = Math.floor(minutes / 60);
|
|
42830
|
-
if (hours > 0)
|
|
42831
|
-
return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
|
|
42832
|
-
if (minutes > 0)
|
|
42833
|
-
return `${minutes}m ${seconds % 60}s`;
|
|
42834
|
-
return `${seconds}s`;
|
|
42835
|
-
}
|
|
42836
42862
|
function formatDetailedError(error45, ctx) {
|
|
42837
42863
|
const message = error45 instanceof Error ? error45.message : String(error45);
|
|
42838
42864
|
const stack = error45 instanceof Error ? error45.stack : undefined;
|
|
@@ -43145,7 +43171,7 @@ Session ID: ${args.session_id}`;
|
|
|
43145
43171
|
const textParts = lastMessage?.parts?.filter((p) => p.type === "text" || p.type === "reasoning") ?? [];
|
|
43146
43172
|
const textContent = textParts.map((p) => p.text ?? "").filter(Boolean).join(`
|
|
43147
43173
|
`);
|
|
43148
|
-
const duration3 =
|
|
43174
|
+
const duration3 = PerfTimer.formatDuration(startTime);
|
|
43149
43175
|
return `\u4EFB\u52A1\u5DF2\u7EE7\u7EED\u5E76\u5728 ${duration3} \u5185\u5B8C\u6210\u3002
|
|
43150
43176
|
|
|
43151
43177
|
Session ID: ${args.session_id}
|
|
@@ -43186,195 +43212,21 @@ ${textContent || "(\u65E0\u6587\u672C\u8F93\u51FA)"}
|
|
|
43186
43212
|
|
|
43187
43213
|
` + "\uFF08\u66FF\u6362\u4E3A\u4F60\u504F\u597D\u7684 provider/model\uFF09";
|
|
43188
43214
|
}
|
|
43189
|
-
const availableModels = await fetchAvailableModels(client2);
|
|
43190
43215
|
const resolved = resolveCategoryConfig(args.category, {
|
|
43191
43216
|
userCategories,
|
|
43192
43217
|
inheritedModel,
|
|
43193
43218
|
systemDefaultModel
|
|
43194
43219
|
});
|
|
43195
43220
|
if (!resolved) {
|
|
43196
|
-
return `\u672A\u77E5\u7684\u5206\u7C7B\uFF1A
|
|
43197
|
-
}
|
|
43198
|
-
const requirement = CATEGORY_MODEL_REQUIREMENTS[args.category];
|
|
43199
|
-
let actualModel;
|
|
43200
|
-
if (!requirement) {
|
|
43201
|
-
actualModel = resolved.model;
|
|
43202
|
-
modelInfo = { model: actualModel, type: "system-default", source: "system-default" };
|
|
43203
|
-
} else {
|
|
43204
|
-
const { model: resolvedModel, source, variant: resolvedVariant } = resolveModelWithFallback({
|
|
43205
|
-
userModel: userCategories?.[args.category]?.model ?? sisyphusJuniorModel,
|
|
43206
|
-
fallbackChain: requirement.fallbackChain,
|
|
43207
|
-
availableModels,
|
|
43208
|
-
systemDefaultModel
|
|
43209
|
-
});
|
|
43210
|
-
actualModel = resolvedModel;
|
|
43211
|
-
if (!parseModelString(actualModel)) {
|
|
43212
|
-
return `\u65E0\u6548\u7684\u6A21\u578B\u683C\u5F0F"${actualModel}"\u3002\u671F\u671B"provider/model"\u683C\u5F0F\uFF08\u4F8B\u5982 "anthropic/claude-sonnet-4-5"\uFF09\u3002`;
|
|
43213
|
-
}
|
|
43214
|
-
let type2;
|
|
43215
|
-
switch (source) {
|
|
43216
|
-
case "override":
|
|
43217
|
-
type2 = "user-defined";
|
|
43218
|
-
break;
|
|
43219
|
-
case "provider-fallback":
|
|
43220
|
-
type2 = "category-default";
|
|
43221
|
-
break;
|
|
43222
|
-
case "system-default":
|
|
43223
|
-
type2 = "system-default";
|
|
43224
|
-
break;
|
|
43225
|
-
}
|
|
43226
|
-
modelInfo = { model: actualModel, type: type2, source };
|
|
43227
|
-
const parsedModel = parseModelString(actualModel);
|
|
43228
|
-
const variantToUse = userCategories?.[args.category]?.variant ?? resolvedVariant;
|
|
43229
|
-
categoryModel = parsedModel ? variantToUse ? { ...parsedModel, variant: variantToUse } : parsedModel : undefined;
|
|
43221
|
+
return `\u672A\u77E5\u7684\u5206\u7C7B\uFF1A${args.category}\u3002\u53EF\u7528\u7684\u5206\u7C7B\uFF1A${categoryExamples}`;
|
|
43230
43222
|
}
|
|
43231
43223
|
agentToUse = SISYPHUS_JUNIOR_AGENT;
|
|
43232
|
-
|
|
43233
|
-
|
|
43234
|
-
|
|
43235
|
-
|
|
43236
|
-
|
|
43237
|
-
|
|
43238
|
-
const isRunInBackgroundExplicitlyFalse = args.run_in_background === false || args.run_in_background === "false";
|
|
43239
|
-
log("[delegate_task] unstable agent detection", {
|
|
43240
|
-
category: args.category,
|
|
43241
|
-
actualModel,
|
|
43242
|
-
isUnstableAgent,
|
|
43243
|
-
run_in_background_value: args.run_in_background,
|
|
43244
|
-
run_in_background_type: typeof args.run_in_background,
|
|
43245
|
-
isRunInBackgroundExplicitlyFalse,
|
|
43246
|
-
willForceBackground: isUnstableAgent && isRunInBackgroundExplicitlyFalse
|
|
43247
|
-
});
|
|
43248
|
-
if (isUnstableAgent && isRunInBackgroundExplicitlyFalse) {
|
|
43249
|
-
const systemContent2 = buildSystemContent({ skillContent, categoryPromptAppend });
|
|
43250
|
-
try {
|
|
43251
|
-
const task = await manager.launch({
|
|
43252
|
-
description: args.description,
|
|
43253
|
-
prompt: args.prompt,
|
|
43254
|
-
agent: agentToUse,
|
|
43255
|
-
parentSessionID: ctx.sessionID,
|
|
43256
|
-
parentMessageID: ctx.messageID,
|
|
43257
|
-
parentModel,
|
|
43258
|
-
parentAgent,
|
|
43259
|
-
model: categoryModel,
|
|
43260
|
-
skills: args.load_skills.length > 0 ? args.load_skills : undefined,
|
|
43261
|
-
skillContent: systemContent2
|
|
43262
|
-
});
|
|
43263
|
-
const WAIT_FOR_SESSION_INTERVAL_MS = 100;
|
|
43264
|
-
const WAIT_FOR_SESSION_TIMEOUT_MS = 30000;
|
|
43265
|
-
const waitStart = Date.now();
|
|
43266
|
-
while (!task.sessionID && Date.now() - waitStart < WAIT_FOR_SESSION_TIMEOUT_MS) {
|
|
43267
|
-
if (ctx.abort?.aborted) {
|
|
43268
|
-
return `\u7B49\u5F85 session \u542F\u52A8\u65F6\u88AB\u4E2D\u6B62\u3002
|
|
43269
|
-
|
|
43270
|
-
Task ID: ${task.id}`;
|
|
43271
|
-
}
|
|
43272
|
-
await new Promise((resolve10) => setTimeout(resolve10, WAIT_FOR_SESSION_INTERVAL_MS));
|
|
43273
|
-
}
|
|
43274
|
-
const sessionID = task.sessionID;
|
|
43275
|
-
if (!sessionID) {
|
|
43276
|
-
return formatDetailedError(new Error(`\u4EFB\u52A1\u5728\u8D85\u65F6\uFF0830\u79D2\uFF09\u5185\u672A\u80FD\u542F\u52A8\u3002Task ID: ${task.id}\uFF0CStatus: ${task.status}`), {
|
|
43277
|
-
operation: "\u542F\u52A8\u53D7\u76D1\u63A7\u7684\u540E\u53F0\u4EFB\u52A1",
|
|
43278
|
-
args,
|
|
43279
|
-
agent: agentToUse,
|
|
43280
|
-
category: args.category
|
|
43281
|
-
});
|
|
43282
|
-
}
|
|
43283
|
-
ctx.metadata?.({
|
|
43284
|
-
title: args.description,
|
|
43285
|
-
metadata: {
|
|
43286
|
-
prompt: args.prompt,
|
|
43287
|
-
agent: agentToUse,
|
|
43288
|
-
category: args.category,
|
|
43289
|
-
load_skills: args.load_skills,
|
|
43290
|
-
description: args.description,
|
|
43291
|
-
run_in_background: args.run_in_background,
|
|
43292
|
-
sessionId: sessionID,
|
|
43293
|
-
command: args.command
|
|
43294
|
-
}
|
|
43295
|
-
});
|
|
43296
|
-
const startTime = new Date;
|
|
43297
|
-
const POLL_INTERVAL_MS = 500;
|
|
43298
|
-
const MAX_POLL_TIME_MS = 10 * 60 * 1000;
|
|
43299
|
-
const MIN_STABILITY_TIME_MS = 1e4;
|
|
43300
|
-
const STABILITY_POLLS_REQUIRED = 3;
|
|
43301
|
-
const pollStart = Date.now();
|
|
43302
|
-
let lastMsgCount = 0;
|
|
43303
|
-
let stablePolls = 0;
|
|
43304
|
-
while (Date.now() - pollStart < MAX_POLL_TIME_MS) {
|
|
43305
|
-
if (ctx.abort?.aborted) {
|
|
43306
|
-
return `\u4EFB\u52A1\u5DF2\u4E2D\u6B62\uFF08\u6B63\u5728\u540E\u53F0\u6A21\u5F0F\u8FD0\u884C\uFF09\u3002
|
|
43307
|
-
|
|
43308
|
-
Session ID: ${sessionID}`;
|
|
43309
|
-
}
|
|
43310
|
-
await new Promise((resolve10) => setTimeout(resolve10, POLL_INTERVAL_MS));
|
|
43311
|
-
const statusResult = await client2.session.status();
|
|
43312
|
-
const allStatuses = statusResult.data ?? {};
|
|
43313
|
-
const sessionStatus = allStatuses[sessionID];
|
|
43314
|
-
if (sessionStatus && sessionStatus.type !== "idle") {
|
|
43315
|
-
stablePolls = 0;
|
|
43316
|
-
lastMsgCount = 0;
|
|
43317
|
-
continue;
|
|
43318
|
-
}
|
|
43319
|
-
if (Date.now() - pollStart < MIN_STABILITY_TIME_MS)
|
|
43320
|
-
continue;
|
|
43321
|
-
const messagesCheck = await client2.session.messages({ path: { id: sessionID } });
|
|
43322
|
-
const msgs = messagesCheck.data ?? messagesCheck;
|
|
43323
|
-
const currentMsgCount = msgs.length;
|
|
43324
|
-
if (currentMsgCount === lastMsgCount) {
|
|
43325
|
-
stablePolls++;
|
|
43326
|
-
if (stablePolls >= STABILITY_POLLS_REQUIRED)
|
|
43327
|
-
break;
|
|
43328
|
-
} else {
|
|
43329
|
-
stablePolls = 0;
|
|
43330
|
-
lastMsgCount = currentMsgCount;
|
|
43331
|
-
}
|
|
43332
|
-
}
|
|
43333
|
-
const messagesResult = await client2.session.messages({ path: { id: sessionID } });
|
|
43334
|
-
const messages = messagesResult.data ?? messagesResult;
|
|
43335
|
-
const assistantMessages = messages.filter((m) => m.info?.role === "assistant").sort((a, b) => (b.info?.time?.created ?? 0) - (a.info?.time?.created ?? 0));
|
|
43336
|
-
const lastMessage = assistantMessages[0];
|
|
43337
|
-
if (!lastMessage) {
|
|
43338
|
-
return `\u672A\u627E\u5230 assistant \u7684\u54CD\u5E94\uFF08\u4EFB\u52A1\u5728\u540E\u53F0\u6A21\u5F0F\u8FD0\u884C\uFF09\u3002
|
|
43339
|
-
|
|
43340
|
-
Session ID: ${sessionID}`;
|
|
43341
|
-
}
|
|
43342
|
-
const textParts = lastMessage?.parts?.filter((p) => p.type === "text" || p.type === "reasoning") ?? [];
|
|
43343
|
-
const textContent = textParts.map((p) => p.text ?? "").filter(Boolean).join(`
|
|
43344
|
-
`);
|
|
43345
|
-
const duration3 = formatDuration2(startTime);
|
|
43346
|
-
return `\u53D7\u76D1\u63A7\u4EFB\u52A1\u6210\u529F\u5B8C\u6210
|
|
43347
|
-
|
|
43348
|
-
\u91CD\u8981\u63D0\u793A\uFF1A\u6B64\u6A21\u578B\uFF08${actualModel}\uFF09\u88AB\u6807\u8BB0\u4E3A\u4E0D\u7A33\u5B9A/\u5B9E\u9A8C\u6027\u3002
|
|
43349
|
-
\u4F60\u7684 run_in_background=false \u5DF2\u81EA\u52A8\u8F6C\u6362\u4E3A\u540E\u53F0\u6A21\u5F0F\u4EE5\u8FDB\u884C\u53EF\u9760\u6027\u76D1\u63A7\u3002
|
|
43350
|
-
|
|
43351
|
-
\u8017\u65F6\uFF1A${duration3}
|
|
43352
|
-
Agent: ${agentToUse}${args.category ? ` (category: ${args.category})` : ""}
|
|
43353
|
-
Session ID: ${sessionID}
|
|
43354
|
-
|
|
43355
|
-
\u76D1\u63A7\u8BF4\u660E\uFF1A
|
|
43356
|
-
- \u4EFB\u52A1\u5DF2\u88AB\u76D1\u63A7\u5E76\u6210\u529F\u5B8C\u6210
|
|
43357
|
-
- \u5982\u679C\u4F60\u53D1\u73B0\u6B64 agent \u5728\u540E\u7EED\u8C03\u7528\u4E2D\u884C\u4E3A\u5F02\u5E38\uFF0C\u8BF7\u4E3B\u52A8\u76D1\u63A7\u5176\u8FDB\u5EA6
|
|
43358
|
-
- \u5982 agent \u4F3C\u4E4E\u5361\u4F4F\u6216\u4EA7\u751F\u5783\u573E\u8F93\u51FA\uFF0C\u4F7F\u7528 background_cancel(task_id="...") \u4E2D\u6B62
|
|
43359
|
-
- \u770B\u5230\u6B64\u6D88\u606F\u65F6\u4E0D\u8981\u81EA\u52A8\u91CD\u8BD5 \u2014 \u4EFB\u52A1\u5DF2\u6210\u529F\u5B8C\u6210
|
|
43360
|
-
|
|
43361
|
-
---
|
|
43362
|
-
|
|
43363
|
-
\u7ED3\u679C\uFF1A
|
|
43364
|
-
|
|
43365
|
-
${textContent || "(\u65E0\u6587\u672C\u8F93\u51FA)"}
|
|
43366
|
-
|
|
43367
|
-
---
|
|
43368
|
-
\u7EE7\u7EED\u6B64 session\uFF1Asession_id="${sessionID}"`;
|
|
43369
|
-
} catch (error45) {
|
|
43370
|
-
return formatDetailedError(error45, {
|
|
43371
|
-
operation: "\u542F\u52A8\u53D7\u76D1\u63A7\u7684\u540E\u53F0\u4EFB\u52A1",
|
|
43372
|
-
args,
|
|
43373
|
-
agent: agentToUse,
|
|
43374
|
-
category: args.category
|
|
43375
|
-
});
|
|
43376
|
-
}
|
|
43377
|
-
}
|
|
43224
|
+
categoryModel = parseModelString(resolved.model);
|
|
43225
|
+
categoryPromptAppend = resolved.promptAppend;
|
|
43226
|
+
modelInfo = {
|
|
43227
|
+
type: "category-default",
|
|
43228
|
+
model: resolved.model
|
|
43229
|
+
};
|
|
43378
43230
|
} else {
|
|
43379
43231
|
if (!args.subagent_type?.trim()) {
|
|
43380
43232
|
return `Agent \u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A\u3002`;
|
|
@@ -43612,7 +43464,7 @@ Session ID: ${sessionID}`;
|
|
|
43612
43464
|
const textParts = lastMessage?.parts?.filter((p) => p.type === "text" || p.type === "reasoning") ?? [];
|
|
43613
43465
|
const textContent = textParts.map((p) => p.text ?? "").filter(Boolean).join(`
|
|
43614
43466
|
`);
|
|
43615
|
-
const duration3 =
|
|
43467
|
+
const duration3 = PerfTimer.formatDuration(startTime);
|
|
43616
43468
|
if (toastManager) {
|
|
43617
43469
|
toastManager.removeTask(taskId);
|
|
43618
43470
|
}
|
|
@@ -43770,6 +43622,65 @@ class ConcurrencyManager {
|
|
|
43770
43622
|
}
|
|
43771
43623
|
}
|
|
43772
43624
|
|
|
43625
|
+
// src/features/background-agent/perf-aggregator.ts
|
|
43626
|
+
function percentile(sorted, p) {
|
|
43627
|
+
if (sorted.length === 0)
|
|
43628
|
+
return 0;
|
|
43629
|
+
const index = Math.ceil(p / 100 * sorted.length) - 1;
|
|
43630
|
+
return sorted[Math.max(0, Math.min(index, sorted.length - 1))];
|
|
43631
|
+
}
|
|
43632
|
+
|
|
43633
|
+
class PerformanceAggregator {
|
|
43634
|
+
tasks = [];
|
|
43635
|
+
recordTaskCompletion(task) {
|
|
43636
|
+
if (!task.progress?.phaseTiming)
|
|
43637
|
+
return;
|
|
43638
|
+
this.tasks.push({
|
|
43639
|
+
agent: task.agent,
|
|
43640
|
+
toolCalls: task.progress.phaseTiming.toolCallCount,
|
|
43641
|
+
phaseTiming: task.progress.phaseTiming
|
|
43642
|
+
});
|
|
43643
|
+
}
|
|
43644
|
+
getReport() {
|
|
43645
|
+
const byAgent = new Map;
|
|
43646
|
+
const queueWaits = [];
|
|
43647
|
+
let totalToolCalls = 0;
|
|
43648
|
+
for (const t of this.tasks) {
|
|
43649
|
+
const list = byAgent.get(t.agent) ?? [];
|
|
43650
|
+
list.push(t.phaseTiming.totalRunMs);
|
|
43651
|
+
byAgent.set(t.agent, list);
|
|
43652
|
+
queueWaits.push(t.phaseTiming.queueWaitMs);
|
|
43653
|
+
totalToolCalls += t.toolCalls;
|
|
43654
|
+
}
|
|
43655
|
+
const tasksByAgent = new Map;
|
|
43656
|
+
for (const [agent, durations] of byAgent) {
|
|
43657
|
+
const sorted = [...durations].sort((a, b) => a - b);
|
|
43658
|
+
const totalMs = durations.reduce((s, d) => s + d, 0);
|
|
43659
|
+
tasksByAgent.set(agent, {
|
|
43660
|
+
count: durations.length,
|
|
43661
|
+
avgMs: Math.round(totalMs / durations.length),
|
|
43662
|
+
p50Ms: Math.round(percentile(sorted, 50)),
|
|
43663
|
+
p95Ms: Math.round(percentile(sorted, 95)),
|
|
43664
|
+
totalMs
|
|
43665
|
+
});
|
|
43666
|
+
}
|
|
43667
|
+
const sortedQueue = [...queueWaits].sort((a, b) => a - b);
|
|
43668
|
+
const queueWaitStats = {
|
|
43669
|
+
avgMs: queueWaits.length > 0 ? Math.round(queueWaits.reduce((s, q) => s + q, 0) / queueWaits.length) : 0,
|
|
43670
|
+
p50Ms: Math.round(percentile(sortedQueue, 50)),
|
|
43671
|
+
p95Ms: Math.round(percentile(sortedQueue, 95)),
|
|
43672
|
+
maxMs: queueWaits.length > 0 ? Math.max(...queueWaits) : 0
|
|
43673
|
+
};
|
|
43674
|
+
return { totalTasks: this.tasks.length, tasksByAgent, totalToolCalls, queueWaitStats };
|
|
43675
|
+
}
|
|
43676
|
+
reset() {
|
|
43677
|
+
this.tasks = [];
|
|
43678
|
+
}
|
|
43679
|
+
get taskCount() {
|
|
43680
|
+
return this.tasks.length;
|
|
43681
|
+
}
|
|
43682
|
+
}
|
|
43683
|
+
|
|
43773
43684
|
// src/features/background-agent/manager.ts
|
|
43774
43685
|
import { existsSync as existsSync46, readdirSync as readdirSync16 } from "fs";
|
|
43775
43686
|
import { join as join55 } from "path";
|
|
@@ -43791,6 +43702,7 @@ class BackgroundManager {
|
|
|
43791
43702
|
concurrencyManager;
|
|
43792
43703
|
shutdownTriggered = false;
|
|
43793
43704
|
config;
|
|
43705
|
+
perfAggregator = new PerformanceAggregator;
|
|
43794
43706
|
queuesByKey = new Map;
|
|
43795
43707
|
processingKeys = new Set;
|
|
43796
43708
|
constructor(ctx, config3) {
|
|
@@ -44096,6 +44008,9 @@ class BackgroundManager {
|
|
|
44096
44008
|
existingTask.parentModel = input.parentModel;
|
|
44097
44009
|
existingTask.parentAgent = input.parentAgent;
|
|
44098
44010
|
existingTask.startedAt = new Date;
|
|
44011
|
+
if (existingTask.progress) {
|
|
44012
|
+
existingTask.progress.phaseTiming = undefined;
|
|
44013
|
+
}
|
|
44099
44014
|
existingTask.progress = {
|
|
44100
44015
|
toolCalls: existingTask.progress?.toolCalls ?? 0,
|
|
44101
44016
|
lastUpdate: new Date
|
|
@@ -44397,6 +44312,7 @@ class BackgroundManager {
|
|
|
44397
44312
|
log("[background-agent] Task already completed, skipping:", { taskId: task.id, status: task.status, source });
|
|
44398
44313
|
return false;
|
|
44399
44314
|
}
|
|
44315
|
+
const perfSnapshot = task.progress?.phaseTiming ? { ...task.progress.phaseTiming } : undefined;
|
|
44400
44316
|
task.status = "completed";
|
|
44401
44317
|
task.completedAt = new Date;
|
|
44402
44318
|
if (task.concurrencyKey) {
|
|
@@ -44405,15 +44321,16 @@ class BackgroundManager {
|
|
|
44405
44321
|
}
|
|
44406
44322
|
this.markForNotification(task);
|
|
44407
44323
|
try {
|
|
44408
|
-
await this.notifyParentSession(task);
|
|
44324
|
+
await this.notifyParentSession(task, perfSnapshot);
|
|
44409
44325
|
log(`[background-agent] Task completed via ${source}:`, task.id);
|
|
44410
44326
|
} catch (err) {
|
|
44411
44327
|
log("[background-agent] Error in notifyParentSession:", { taskId: task.id, error: err });
|
|
44412
44328
|
}
|
|
44329
|
+
this.perfAggregator.recordTaskCompletion(task);
|
|
44413
44330
|
return true;
|
|
44414
44331
|
}
|
|
44415
|
-
async notifyParentSession(task) {
|
|
44416
|
-
const duration3 =
|
|
44332
|
+
async notifyParentSession(task, perfSnapshot) {
|
|
44333
|
+
const duration3 = PerfTimer.formatDuration(task.startedAt ?? new Date, task.completedAt);
|
|
44417
44334
|
log("[background-agent] notifyParentSession called for task:", task.id);
|
|
44418
44335
|
const toastManager = getTaskToastManager();
|
|
44419
44336
|
if (toastManager) {
|
|
@@ -44435,6 +44352,16 @@ class BackgroundManager {
|
|
|
44435
44352
|
const statusText = task.status === "completed" ? "COMPLETED" : "CANCELLED";
|
|
44436
44353
|
const errorInfo = task.error ? `
|
|
44437
44354
|
**Error:** ${task.error}` : "";
|
|
44355
|
+
const fallbackTaskLine = "- `" + task.id + "`: " + task.description;
|
|
44356
|
+
const perfSummary = (() => {
|
|
44357
|
+
if (!perfSnapshot)
|
|
44358
|
+
return "";
|
|
44359
|
+
const completed = Array.from(this.tasks.values()).filter((t) => t.parentSessionID === task.parentSessionID && t.status !== "running" && t.status !== "pending");
|
|
44360
|
+
const ms = completed.reduce((s, t) => s + (t.progress?.phaseTiming?.totalRunMs ?? 0), 0);
|
|
44361
|
+
const avgSec = completed.length > 0 ? Math.round(ms / completed.length / 1000) : 0;
|
|
44362
|
+
const toolTotal = completed.reduce((s, t) => s + (t.progress?.phaseTiming?.toolCallCount ?? 0), 0);
|
|
44363
|
+
return "\u5E73\u5747\u8017\u65F6: " + avgSec + "s | \u603BTool: " + toolTotal;
|
|
44364
|
+
})();
|
|
44438
44365
|
let notification;
|
|
44439
44366
|
if (allComplete) {
|
|
44440
44367
|
const completedTasks = Array.from(this.tasks.values()).filter((t) => t.parentSessionID === task.parentSessionID && t.status !== "running" && t.status !== "pending").map((t) => `- \`${t.id}\`: ${t.description}`).join(`
|
|
@@ -44443,7 +44370,8 @@ class BackgroundManager {
|
|
|
44443
44370
|
[\u6240\u6709\u540E\u53F0\u4EFB\u52A1\u5DF2\u5B8C\u6210]
|
|
44444
44371
|
|
|
44445
44372
|
**\u5DF2\u5B8C\u6210\uFF1A**
|
|
44446
|
-
${completedTasks ||
|
|
44373
|
+
${completedTasks || fallbackTaskLine}
|
|
44374
|
+
${perfSummary}
|
|
44447
44375
|
|
|
44448
44376
|
\u4F7F\u7528 \`background_output(task_id="<id>")\` \u83B7\u53D6\u6BCF\u4E2A\u4EFB\u52A1\u7684\u7ED3\u679C\u3002
|
|
44449
44377
|
</system-reminder>`;
|
|
@@ -44453,6 +44381,7 @@ ${completedTasks || `- \`${task.id}\`: ${task.description}`}
|
|
|
44453
44381
|
**ID:** \`${task.id}\`
|
|
44454
44382
|
**\u63CF\u8FF0\uFF1A** ${task.description}
|
|
44455
44383
|
**\u8017\u65F6\uFF1A** ${duration3}${errorInfo}
|
|
44384
|
+
${perfSnapshot ? `| \u6392\u961F ${PerfTimer.formatDuration(new Date(0), new Date(perfSnapshot.queueWaitMs))} | Tool ${perfSnapshot.toolCallCount} \u6B21` : ""}
|
|
44456
44385
|
|
|
44457
44386
|
**\u8FD8\u6709 ${remainingCount} \u4E2A\u4EFB\u52A1\u6B63\u5728\u8FDB\u884C\u4E2D\u3002** \u6240\u6709\u4EFB\u52A1\u5B8C\u6210\u540E\u4F60\u4F1A\u6536\u5230\u901A\u77E5\u3002
|
|
44458
44387
|
\u4E0D\u8981\u8F6E\u8BE2\u2014\u2014\u7EE7\u7EED\u6709\u6548\u7387\u7684\u5DE5\u4F5C\u3002
|
|
@@ -44511,18 +44440,6 @@ ${completedTasks || `- \`${task.id}\`: ${task.description}`}
|
|
|
44511
44440
|
}
|
|
44512
44441
|
}, 5 * 60 * 1000);
|
|
44513
44442
|
}
|
|
44514
|
-
formatDuration(start, end) {
|
|
44515
|
-
const duration3 = (end ?? new Date).getTime() - start.getTime();
|
|
44516
|
-
const seconds = Math.floor(duration3 / 1000);
|
|
44517
|
-
const minutes = Math.floor(seconds / 60);
|
|
44518
|
-
const hours = Math.floor(minutes / 60);
|
|
44519
|
-
if (hours > 0) {
|
|
44520
|
-
return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
|
|
44521
|
-
} else if (minutes > 0) {
|
|
44522
|
-
return `${minutes}m ${seconds % 60}s`;
|
|
44523
|
-
}
|
|
44524
|
-
return `${seconds}s`;
|
|
44525
|
-
}
|
|
44526
44443
|
hasRunningTasks() {
|
|
44527
44444
|
for (const task of this.tasks.values()) {
|
|
44528
44445
|
if (task.status === "running")
|
|
@@ -44666,6 +44583,17 @@ ${completedTasks || `- \`${task.id}\`: ${task.description}`}
|
|
|
44666
44583
|
if (!task.progress) {
|
|
44667
44584
|
task.progress = { toolCalls: 0, lastUpdate: new Date };
|
|
44668
44585
|
}
|
|
44586
|
+
if (task.startedAt && !task.progress.phaseTiming) {
|
|
44587
|
+
const now = Date.now();
|
|
44588
|
+
task.progress.phaseTiming = {
|
|
44589
|
+
queueWaitMs: task.queuedAt ? now - task.queuedAt.getTime() : 0,
|
|
44590
|
+
totalRunMs: now - task.startedAt.getTime(),
|
|
44591
|
+
toolCallCount: 0
|
|
44592
|
+
};
|
|
44593
|
+
}
|
|
44594
|
+
if (task.progress.phaseTiming && task.progress.phaseTiming.firstResponseMs === undefined && assistantMsgs.length > 0) {
|
|
44595
|
+
task.progress.phaseTiming.firstResponseMs = Date.now() - task.startedAt.getTime();
|
|
44596
|
+
}
|
|
44669
44597
|
task.progress.toolCalls = toolCalls;
|
|
44670
44598
|
task.progress.lastTool = lastTool;
|
|
44671
44599
|
task.progress.lastUpdate = new Date;
|
|
@@ -44718,6 +44646,10 @@ ${completedTasks || `- \`${task.id}\`: ${task.description}`}
|
|
|
44718
44646
|
}
|
|
44719
44647
|
if (!this.hasRunningTasks()) {
|
|
44720
44648
|
this.stopPolling();
|
|
44649
|
+
if (this.perfAggregator.taskCount >= 2) {
|
|
44650
|
+
const report = this.perfAggregator.getReport();
|
|
44651
|
+
log("[perf] Session report:", report);
|
|
44652
|
+
}
|
|
44721
44653
|
}
|
|
44722
44654
|
}
|
|
44723
44655
|
shutdown() {
|
|
@@ -44738,6 +44670,7 @@ ${completedTasks || `- \`${task.id}\`: ${task.description}`}
|
|
|
44738
44670
|
this.pendingByParent.clear();
|
|
44739
44671
|
this.queuesByKey.clear();
|
|
44740
44672
|
this.processingKeys.clear();
|
|
44673
|
+
this.perfAggregator.reset();
|
|
44741
44674
|
this.unregisterProcessCleanup();
|
|
44742
44675
|
log("[background-agent] Shutdown complete");
|
|
44743
44676
|
}
|
package/dist/shared/index.d.ts
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface SpanData {
|
|
2
|
+
name: string;
|
|
3
|
+
durationMs: number;
|
|
4
|
+
start: number;
|
|
5
|
+
end: number;
|
|
6
|
+
}
|
|
7
|
+
export interface FormatDurationOptions {
|
|
8
|
+
precision?: "full" | "compact";
|
|
9
|
+
}
|
|
10
|
+
export declare class PerfTimer {
|
|
11
|
+
private marks;
|
|
12
|
+
private spans;
|
|
13
|
+
mark(name: string): void;
|
|
14
|
+
measure(name: string, startMark: string, endMark?: string): number;
|
|
15
|
+
getReport(): SpanData[];
|
|
16
|
+
reset(): void;
|
|
17
|
+
/**
|
|
18
|
+
* Format a duration between two Date objects into a human-readable string.
|
|
19
|
+
*
|
|
20
|
+
* @param start - Start date
|
|
21
|
+
* @param end - End date (defaults to now if undefined)
|
|
22
|
+
* @param options - Formatting options
|
|
23
|
+
* @returns "Xs", "Xm Ys", "Xh Ym Ys" or "Xh Ym" (compact)
|
|
24
|
+
*/
|
|
25
|
+
static formatDuration(start: Date, end?: Date, options?: FormatDurationOptions): string;
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skj1724/oh-my-opencode",
|
|
3
|
-
"version": "3.19.
|
|
3
|
+
"version": "3.19.4",
|
|
4
4
|
"description": "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|