teapot-coding-agent 0.37.92 → 0.37.93
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/agent/agent.js
CHANGED
|
@@ -7,7 +7,6 @@ import { auditPrompt, parseAuditVerdict } from "./goal-audit.js";
|
|
|
7
7
|
import { createCatalog } from "./tool-catalog.js";
|
|
8
8
|
import { resetToolCatalog, executeTool, toolSpecs, currentSkills, safeJoin, isContextOverflow, isRequestNodeLimit, hasRunningBgShells, hasRunningBgShellsFor, killBgShells } from "./tools.js";
|
|
9
9
|
import { effortChoosable } from "./llm.js";
|
|
10
|
-
import { sessionTitleFrom } from "../session-title.js";
|
|
11
10
|
import { mcp } from "./mcp-registry.js";
|
|
12
11
|
import { flattenToolName } from "./mcp-registry.js";
|
|
13
12
|
import { metaToolSpecs } from "./meta-tools.js";
|
|
@@ -298,10 +297,6 @@ export class Agent {
|
|
|
298
297
|
skillsStamp = "";
|
|
299
298
|
lastCompactSummary;
|
|
300
299
|
compactRules = [];
|
|
301
|
-
setSessionTitle(title) {
|
|
302
|
-
this.sessionTitle = title?.trim() || undefined;
|
|
303
|
-
}
|
|
304
|
-
sessionTitle;
|
|
305
300
|
overflowRecoveries = 0;
|
|
306
301
|
consecutiveCompactFailures = 0;
|
|
307
302
|
memoryDigestText() {
|
|
@@ -747,9 +742,6 @@ export class Agent {
|
|
|
747
742
|
const last = lineage[lineage.length - 1];
|
|
748
743
|
const msgs = await rebuildMessagesFrom(lineage, this.imagesDir);
|
|
749
744
|
for (const e of own) {
|
|
750
|
-
if (this.sessionTitle === undefined && e.type === "prompt" && e.data?.source === "user") {
|
|
751
|
-
this.sessionTitle = sessionTitleFrom(e.data?.text) ?? undefined;
|
|
752
|
-
}
|
|
753
745
|
if (e.type === "state") {
|
|
754
746
|
const d = e.data;
|
|
755
747
|
if (d.detail === "llm turn start")
|
|
@@ -1059,6 +1051,16 @@ export class Agent {
|
|
|
1059
1051
|
});
|
|
1060
1052
|
}
|
|
1061
1053
|
snapshot() {
|
|
1054
|
+
/**
|
|
1055
|
+
* The session's name comes from ONE place — `config.sessionTitles`, via the resolver Master hands
|
|
1056
|
+
* down. The agent used to keep its own copy, seeded at creation and never updated when the map
|
|
1057
|
+
* learned the real title, so the panel showed the internal id while `config.json` had the name
|
|
1058
|
+
* ("config.jsonにチャットタイトルが生成されているのに表示が内部IDになってる").
|
|
1059
|
+
*
|
|
1060
|
+
* A copy that has to be PUSHED is a copy that goes stale; deriving it means there is nothing to
|
|
1061
|
+
* push. The session id is the fallback so the panel is never blank.
|
|
1062
|
+
*/
|
|
1063
|
+
const title = this.opts.sessionTitleOf?.(this.currentSession) || this.currentSession;
|
|
1062
1064
|
return {
|
|
1063
1065
|
id: this.opts.id,
|
|
1064
1066
|
status: this.status === "idle" && this.isLive() ? "waiting" : this.status,
|
|
@@ -1067,7 +1069,7 @@ export class Agent {
|
|
|
1067
1069
|
workspace: this.workspace,
|
|
1068
1070
|
workspaceMissing: this.isGhost,
|
|
1069
1071
|
session: this.currentSession,
|
|
1070
|
-
...(
|
|
1072
|
+
...(title ? { sessionTitle: title } : {}),
|
|
1071
1073
|
branch: this.currentBranch,
|
|
1072
1074
|
goal: {
|
|
1073
1075
|
status: this.goal.status,
|
|
@@ -1155,7 +1157,7 @@ export class Agent {
|
|
|
1155
1157
|
})
|
|
1156
1158
|
.then(() => {
|
|
1157
1159
|
if (source === "user")
|
|
1158
|
-
this.opts.onFirstUserPrompt?.(this.currentSession);
|
|
1160
|
+
this.opts.onFirstUserPrompt?.(this.currentSession, text);
|
|
1159
1161
|
})
|
|
1160
1162
|
.then(() => bus.emit("update", { kind: "agent-update", agentId: this.opts.id }))
|
|
1161
1163
|
.catch(() => { });
|
package/dist/master.js
CHANGED
|
@@ -648,9 +648,11 @@ export class Master {
|
|
|
648
648
|
const agent = new Agent({
|
|
649
649
|
id: ac.id,
|
|
650
650
|
workspace: resolveWorkspace(ac.workspace, path.dirname(this.configPath)),
|
|
651
|
-
onFirstUserPrompt: (sessionId) => {
|
|
652
|
-
void this.ensureSessionTitle(ac.id, sessionId);
|
|
651
|
+
onFirstUserPrompt: (sessionId, firstText) => {
|
|
652
|
+
void this.ensureSessionTitle(ac.id, sessionId, firstText);
|
|
653
653
|
},
|
|
654
|
+
// ONE owner for a session's name: this map. The agent derives it on every snapshot.
|
|
655
|
+
sessionTitleOf: (sessionId) => this.readSessionTitle(sessionId) ?? undefined,
|
|
654
656
|
llm,
|
|
655
657
|
sessionDir,
|
|
656
658
|
// boot must touch ONLY config.json and skills; this agent's directory is opened when it is used
|
|
@@ -740,27 +742,13 @@ export class Master {
|
|
|
740
742
|
}
|
|
741
743
|
await agent.init();
|
|
742
744
|
/**
|
|
743
|
-
*
|
|
745
|
+
* No title is seeded, and nothing is derived here.
|
|
744
746
|
*
|
|
745
|
-
*
|
|
746
|
-
* `
|
|
747
|
-
*
|
|
747
|
+
* The agent derives its name from `config.sessionTitles` on every `snapshot()` (see
|
|
748
|
+
* `sessionTitleOf`), so there is no copy to seed. Deriving a missing one here would read
|
|
749
|
+
* `chat.jsonl`, and boot must not open session directories — "起動中に読んでいいのはconfig.jsonと
|
|
750
|
+
* skillsだけ / セッションディレクトリはセッションがロードされるまで開かない".
|
|
748
751
|
*/
|
|
749
|
-
const seedTitle = this.readSessionTitle(path.basename(sessionDir));
|
|
750
|
-
if (seedTitle)
|
|
751
|
-
agent.setSessionTitle(seedTitle);
|
|
752
|
-
else {
|
|
753
|
-
/**
|
|
754
|
-
* NOT derived here. Deriving a missing title reads `chat.jsonl`, and boot must not open session
|
|
755
|
-
* directories at all — "起動中に読んでいいのはconfig.jsonとskillsだけ / セッションディレクトリは
|
|
756
|
-
* セッションがロードされるまで開かない".
|
|
757
|
-
*
|
|
758
|
-
* The session ID is a perfectly good fallback for the panel, and the real derivation happens
|
|
759
|
-
* when that session is actually loaded (`ensureSessionTitle`, via the agent's first prompt).
|
|
760
|
-
*/
|
|
761
|
-
const id = path.basename(sessionDir);
|
|
762
|
-
agent.setSessionTitle(this.readSessionTitle(id) ?? id);
|
|
763
|
-
}
|
|
764
752
|
this.agents.set(ac.id, agent);
|
|
765
753
|
if (opts.persist) {
|
|
766
754
|
this.config.agents.push(ac);
|
|
@@ -1105,6 +1093,17 @@ When this task is done (or truly blocked), call finish() with a summary for @${p
|
|
|
1105
1093
|
}
|
|
1106
1094
|
this.config.sessionTitles ??= {};
|
|
1107
1095
|
this.config.sessionTitles[sessionId] = title;
|
|
1096
|
+
/**
|
|
1097
|
+
* …and the LIVE agent has to be told, or the panel keeps showing the id.
|
|
1098
|
+
*
|
|
1099
|
+
* `addAgent` seeds an agent's title from this map when it is created, falling back to the session id.
|
|
1100
|
+
* A NEW session gets its entry written later (on the first prompt, from `chat.jsonl`), and nothing
|
|
1101
|
+
* pushed it back — so the agent still held the id and the panel displayed it while `config.json` had
|
|
1102
|
+
* the real name. Reported as "config.jsonにチャットタイトルが生成されているのに表示が内部IDになってる".
|
|
1103
|
+
*
|
|
1104
|
+
* Pushing here rather than in each caller means every writer (the derivation, a rename) is covered by
|
|
1105
|
+
* one rule: the map changed, so the agent that is showing it changes too.
|
|
1106
|
+
*/
|
|
1108
1107
|
this.saveConfig();
|
|
1109
1108
|
}
|
|
1110
1109
|
/**
|
|
@@ -1114,14 +1113,23 @@ When this task is done (or truly blocked), call finish() with a summary for @${p
|
|
|
1114
1113
|
* per session on every call (MEASURED earlier: 0.87s for one archive toggle). Writing it down once,
|
|
1115
1114
|
* where the session lives, keeps that scan off the hot path and keeps the answer in ONE place.
|
|
1116
1115
|
*/
|
|
1117
|
-
async ensureSessionTitle(agentId, sessionId) {
|
|
1116
|
+
async ensureSessionTitle(agentId, sessionId, firstText) {
|
|
1118
1117
|
const a = this.config.agents.find((x) => x.id === agentId);
|
|
1119
1118
|
if (!a || a.session !== sessionId)
|
|
1120
1119
|
return;
|
|
1120
|
+
// the map is the master: once it has a name, nothing may replace it
|
|
1121
1121
|
if (this.readSessionTitle(sessionId))
|
|
1122
1122
|
return;
|
|
1123
|
-
|
|
1124
|
-
|
|
1123
|
+
/**
|
|
1124
|
+
* A session is named from its FIRST USER MESSAGE — that is the whole point of this hook, and it must
|
|
1125
|
+
* keep working for a session created at runtime.
|
|
1126
|
+
*
|
|
1127
|
+
* The text arrives from the agent, which already has it, so this needs no read of `chat.jsonl`. The
|
|
1128
|
+
* log is only a fallback for a caller that could not supply it.
|
|
1129
|
+
*/
|
|
1130
|
+
const fromText = sessionTitleFrom(firstText) ?? undefined;
|
|
1131
|
+
const derived = fromText ?? this.sessionTitleOf(path.join(this.sessionsRoot(), sessionId)) ?? sessionId;
|
|
1132
|
+
this.writeSessionTitle(sessionId, derived);
|
|
1125
1133
|
}
|
|
1126
1134
|
sessionTitleOf(dirPath) {
|
|
1127
1135
|
let fd;
|
|
@@ -1355,7 +1363,7 @@ When this task is done (or truly blocked), call finish() with a summary for @${p
|
|
|
1355
1363
|
*/
|
|
1356
1364
|
this.writeSessionTitle(sessionId, clean);
|
|
1357
1365
|
delete a.sessionTitle;
|
|
1358
|
-
|
|
1366
|
+
// no field to set: the panel derives the name from the map, so the bus nudge is the whole update
|
|
1359
1367
|
bus.emit("update", { kind: "agent-update", agentId });
|
|
1360
1368
|
return { title: clean };
|
|
1361
1369
|
}
|