agentlas 1.0.22 → 1.0.24
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/CHANGELOG.md +22 -0
- package/README.md +9 -26
- package/engine/agentlas-banner.cjs +0 -1
- package/engine/agentlas-capabilities.cjs +8 -71
- package/engine/agentlas-core-harness.cjs +18 -11
- package/engine/agentlas-i18n.cjs +21 -21
- package/engine/agentlas-judgment.cjs +0 -0
- package/engine/agentlas-native-host.cjs +4 -31
- package/engine/agentlas-ui.cjs +13 -1
- package/engine/agentlas-workload-routing.cjs +11 -24
- package/engine/agentlas.cjs +3 -13
- package/engine/agents/router.cjs +0 -1
- package/engine/architecture.data.json +5 -5
- package/engine/bootstrap-schema.sql +3 -3
- package/engine/commands/doctor.cjs +3 -3
- package/engine/commands/firm.cjs +3 -1
- package/engine/commands/help.cjs +4 -7
- package/engine/commands/index.cjs +1 -6
- package/engine/commands/list.cjs +8 -2
- package/engine/firms/orchestrate.cjs +161 -12
- package/engine/sessions/orchestrator.cjs +4 -21
- package/engine/sessions/session.cjs +66 -5
- package/engine/sessions/sink.cjs +13 -3
- package/engine/sessions/store.cjs +2 -2
- package/engine/ui/palette.cjs +3 -12
- package/engine/ui/renderer.cjs +3 -6
- package/engine/ui/repl.cjs +41 -93
- package/engine/workforce/deps.cjs +9 -6
- package/package.json +4 -3
- package/engine/agentlas-doctor.cjs +0 -224
- package/engine/commands/chat.cjs +0 -12
- package/engine/commands/chats.cjs +0 -33
- package/engine/commands/open.cjs +0 -52
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*
|
|
3
|
-
* agentlas-doctor: 런타임 CLI 실패의 "시스템 원인"을 결정론적으로 진단·수리한다.
|
|
4
|
-
*
|
|
5
|
-
* 데스크탑 앱 electron/system-agents/runtime-doctor.ts 와 로직 패리티를 유지해야 한다
|
|
6
|
-
* (3제품 싱크: 데스크탑 TS ↔ 터미널 CJS ↔ system-optimizer 패키지 플레이북).
|
|
7
|
-
* 패리티는 Agentlas_F/scripts/sync-runtime-doctor.sh 가 공유 픽스처로 검증한다 —
|
|
8
|
-
* 이 파일의 분류/수리 규칙을 바꾸면 반드시 그 스크립트를 PASS 시켜라.
|
|
9
|
-
*
|
|
10
|
-
* 사례(2026-07-08): codex CLI 업데이트가 openai-curated 플러그인(notion/figma)을 자동
|
|
11
|
-
* 활성화 → 미인증 OAuth 원격 MCP가 매 실행 AuthRequired fatal → codex exit 1.
|
|
12
|
-
* 수리: 에러 stderr의 호스트와 플러그인 캐시 .mcp.json url 호스트를 대조해 "정확히 그
|
|
13
|
-
* 플러그인만" config.toml에서 enabled=false (백업 필수, 인증돼 잘 도는 플러그인 오폭 금지).
|
|
14
|
-
*/
|
|
15
|
-
const fs = require("node:fs");
|
|
16
|
-
const os = require("node:os");
|
|
17
|
-
const path = require("node:path");
|
|
18
|
-
|
|
19
|
-
function codexHome() {
|
|
20
|
-
return process.env.CODEX_HOME || path.join(os.homedir(), ".codex");
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* 에러 텍스트에서 OAuth 자원 메타데이터로 명시된 호스트만 추출한다.
|
|
25
|
-
* stderr의 모든 URL을 수집하면 도움말·문서 링크와 관련된 정상 플러그인까지
|
|
26
|
-
* 수리 대상이 될 수 있다. 자동 수리는 구조화된 증거가 있을 때만 실행한다.
|
|
27
|
-
*/
|
|
28
|
-
function extractHosts(error) {
|
|
29
|
-
const hosts = new Set();
|
|
30
|
-
const addUrlHost = (rawUrl) => {
|
|
31
|
-
try {
|
|
32
|
-
hosts.add(new URL(rawUrl.replace(/[\]}>),.;]+$/g, "")).hostname.toLowerCase());
|
|
33
|
-
} catch {
|
|
34
|
-
/* 잘못된 메타데이터 URL은 자동 수리 증거로 쓰지 않음 */
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
const patterns = [
|
|
38
|
-
/resource_metadata(?:_url)?\s*[:=]\s*\\?["']?(https?:\/\/[^\s"'\\)>,]+)/gi,
|
|
39
|
-
/(https?:\/\/[^\s"'\\)>,]+\/\.well-known\/oauth-protected-resource(?:[/?#][^\s"'\\)>,]*)?)/gi,
|
|
40
|
-
];
|
|
41
|
-
for (const re of patterns) {
|
|
42
|
-
let m;
|
|
43
|
-
while ((m = re.exec(error || "")) !== null) addUrlHost(m[1]);
|
|
44
|
-
}
|
|
45
|
-
return [...hosts];
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** 에러 텍스트에서 config.toml 스키마 위반이 지목한 mcp_servers 이름을 뽑는다. */
|
|
49
|
-
function extractBadMcpServer(error) {
|
|
50
|
-
// 예: Error loading config.toml: url is not supported for stdio in "mcp_servers.agentlas"
|
|
51
|
-
const m = /mcp_servers\.([a-z0-9_.-]+)/i.exec(error || "");
|
|
52
|
-
return m ? m[1] : null;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/** kind: mcp-oauth-unauthenticated | codex-config-invalid | timeout | cli-exit | unknown (데스크탑 TS와 동일 규칙) */
|
|
56
|
-
function classifyFailure(error) {
|
|
57
|
-
const text = error || "";
|
|
58
|
-
if (/authrequired|invalid_token|oauth-protected-resource|www_authenticate/i.test(text)) {
|
|
59
|
-
return { kind: "mcp-oauth-unauthenticated", hosts: extractHosts(text) };
|
|
60
|
-
}
|
|
61
|
-
// codex config.toml 파싱 실패(예: stdio 서버에 url 키) → CLI가 아예 기동 못 함.
|
|
62
|
-
if (/error loading config\.toml|url is not supported for stdio|invalid config/i.test(text)) {
|
|
63
|
-
return { kind: "codex-config-invalid", hosts: [], badServer: extractBadMcpServer(text) };
|
|
64
|
-
}
|
|
65
|
-
if (/no response for \d+s|auto-aborted/i.test(text)) return { kind: "timeout", hosts: [] };
|
|
66
|
-
if (/(?:CLI exit|exited with code)\s+[1-9]\d*/i.test(text)) return { kind: "cli-exit", hosts: extractHosts(text) };
|
|
67
|
-
return { kind: "unknown", hosts: [] };
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/** 실패 호스트와 일치하는 OAuth MCP를 실은 플러그인 찾기(호스트가 에러에 등장한 것만). */
|
|
71
|
-
function findOauthPluginsByHost(hosts) {
|
|
72
|
-
if (!hosts.length) return [];
|
|
73
|
-
const cacheRoot = path.join(codexHome(), "plugins", "cache");
|
|
74
|
-
const hits = [];
|
|
75
|
-
let marketplaces = [];
|
|
76
|
-
try {
|
|
77
|
-
marketplaces = fs.readdirSync(cacheRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
78
|
-
} catch {
|
|
79
|
-
return [];
|
|
80
|
-
}
|
|
81
|
-
for (const marketplace of marketplaces) {
|
|
82
|
-
let plugins = [];
|
|
83
|
-
try {
|
|
84
|
-
plugins = fs.readdirSync(path.join(cacheRoot, marketplace), { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
85
|
-
} catch {
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
for (const plugin of plugins) {
|
|
89
|
-
let versions = [];
|
|
90
|
-
try {
|
|
91
|
-
versions = fs.readdirSync(path.join(cacheRoot, marketplace, plugin), { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
92
|
-
} catch {
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
for (const ver of versions) {
|
|
96
|
-
const mcpJson = path.join(cacheRoot, marketplace, plugin, ver, ".mcp.json");
|
|
97
|
-
if (!fs.existsSync(mcpJson)) continue;
|
|
98
|
-
try {
|
|
99
|
-
const parsed = JSON.parse(fs.readFileSync(mcpJson, "utf8"));
|
|
100
|
-
for (const server of Object.values(parsed.mcpServers || {})) {
|
|
101
|
-
if (!server || !server.url) continue;
|
|
102
|
-
let host = "";
|
|
103
|
-
try {
|
|
104
|
-
host = new URL(server.url).hostname.toLowerCase();
|
|
105
|
-
} catch {
|
|
106
|
-
continue;
|
|
107
|
-
}
|
|
108
|
-
// 호스트가 정확히 같을 때만 자동 수리한다. 부모/자식 도메인 관계만으로는
|
|
109
|
-
// 어느 플러그인이 실패했는지 증명할 수 없다.
|
|
110
|
-
if (hosts.includes(host)) {
|
|
111
|
-
// cache 디렉토리 "openai-curated-remote"는 config 키에선 "openai-curated".
|
|
112
|
-
hits.push({ pluginKey: `${plugin}@${marketplace.replace(/-remote$/, "")}`, host });
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
} catch {
|
|
116
|
-
/* 손상된 .mcp.json은 건너뜀 */
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
const seen = new Set();
|
|
122
|
-
return hits.filter((h) => (seen.has(h.pluginKey) ? false : (seen.add(h.pluginKey), true)));
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/** config.toml에서 해당 플러그인을 enabled=false로 내린다(백업 필수). 반환: 실제 변경 여부. */
|
|
126
|
-
function disableCodexPlugin(pluginKey) {
|
|
127
|
-
const configPath = path.join(codexHome(), "config.toml");
|
|
128
|
-
if (!fs.existsSync(configPath)) return false;
|
|
129
|
-
const original = fs.readFileSync(configPath, "utf8");
|
|
130
|
-
const header = `[plugins."${pluginKey}"]`;
|
|
131
|
-
let next;
|
|
132
|
-
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
133
|
-
const headerMatch = new RegExp(`^[ \\t]*${escapeRegExp(header)}[ \\t]*(?:#.*)?\\r?$`, "m").exec(original);
|
|
134
|
-
if (headerMatch) {
|
|
135
|
-
const headerLineEnd = headerMatch.index + headerMatch[0].length;
|
|
136
|
-
const newlineIndex = original.indexOf("\n", headerLineEnd);
|
|
137
|
-
const bodyStart = newlineIndex === -1 ? original.length : newlineIndex + 1;
|
|
138
|
-
const remaining = original.slice(bodyStart);
|
|
139
|
-
const nextSection = /^[ \t]*\[[^\r\n]+\][ \t]*(?:#.*)?\r?$/m.exec(remaining);
|
|
140
|
-
const sectionEnd = nextSection ? bodyStart + nextSection.index : original.length;
|
|
141
|
-
const section = original.slice(headerMatch.index, sectionEnd);
|
|
142
|
-
const replacedSection = section.replace(
|
|
143
|
-
/^([ \t]*enabled[ \t]*=[ \t]*)true([ \t]*(?:#.*)?)(?=\r?$)/m,
|
|
144
|
-
"$1false$2",
|
|
145
|
-
);
|
|
146
|
-
if (replacedSection === section) return false; // 이미 false거나 해당 섹션에 enabled=true가 없음
|
|
147
|
-
next = original.slice(0, headerMatch.index) + replacedSection + original.slice(sectionEnd);
|
|
148
|
-
} else {
|
|
149
|
-
next = `${original.trimEnd()}\n\n${header}\nenabled = false\n`;
|
|
150
|
-
}
|
|
151
|
-
const backup = `${configPath}.bak-doctor-${new Date().toISOString().replace(/[:.]/g, "-")}`;
|
|
152
|
-
fs.copyFileSync(configPath, backup);
|
|
153
|
-
fs.writeFileSync(configPath, next);
|
|
154
|
-
return true;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* 진단 + (아는 계열이면) 즉시 수리. 반환: { kind, summary, repaired, actions[] }
|
|
159
|
-
* 데스크탑 runtime-doctor.ts 의 runRuntimeDoctor 와 동일 계약.
|
|
160
|
-
*/
|
|
161
|
-
function runRuntimeDoctor(errorMessage) {
|
|
162
|
-
const { kind, hosts, badServer } = classifyFailure(errorMessage);
|
|
163
|
-
const actions = [];
|
|
164
|
-
|
|
165
|
-
if (kind === "codex-config-invalid") {
|
|
166
|
-
// 진단만 한다(자동 수리 안 함): 원격 MCP 항목이라 안전한 무손실 수리가 없고,
|
|
167
|
-
// 사용자가 어느 항목을 어떻게 고칠지 알아야 한다. 명확한 조치를 안내한다.
|
|
168
|
-
const where = badServer ? `[mcp_servers.${badServer}]` : "일부 [mcp_servers.*] 항목";
|
|
169
|
-
return {
|
|
170
|
-
kind,
|
|
171
|
-
summary: `codex의 ~/.codex/config.toml ${where} 이(가) 잘못돼 codex가 기동하지 못합니다(예: stdio 서버에 url 키). 다른 런타임(--runtime claude-code)으로 우회하거나 그 항목을 고치세요.`,
|
|
172
|
-
repaired: false,
|
|
173
|
-
actions,
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (kind === "mcp-oauth-unauthenticated") {
|
|
178
|
-
const hitList = findOauthPluginsByHost(hosts);
|
|
179
|
-
let repairedAny = false;
|
|
180
|
-
for (const hit of hitList) {
|
|
181
|
-
try {
|
|
182
|
-
if (disableCodexPlugin(hit.pluginKey)) {
|
|
183
|
-
repairedAny = true;
|
|
184
|
-
actions.push({
|
|
185
|
-
title: `codex plugin disabled: ${hit.pluginKey}`,
|
|
186
|
-
detail: `미인증 OAuth MCP(${hit.host})가 런타임을 죽여서 ~/.codex/config.toml에서 비활성화했습니다(백업 생성). 이 서비스를 쓰려면 인증 후 다시 켜세요.`,
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
} catch (err) {
|
|
190
|
-
actions.push({ title: `repair failed: ${hit.pluginKey}`, detail: err && err.message ? err.message : String(err) });
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
return {
|
|
194
|
-
kind,
|
|
195
|
-
summary: hitList.length
|
|
196
|
-
? `런타임에 미인증 OAuth MCP 플러그인(${hitList.map((h) => h.pluginKey).join(", ")})이 붙어 있어 CLI가 죽었습니다.`
|
|
197
|
-
: `An unauthenticated OAuth MCP (${hosts.join(", ") || "unknown host"}) crashed the runtime, but the responsible plugin could not be identified.`,
|
|
198
|
-
repaired: repairedAny,
|
|
199
|
-
actions,
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
if (kind === "timeout") {
|
|
204
|
-
return {
|
|
205
|
-
kind,
|
|
206
|
-
summary: "실행이 장시간 무응답이라 자동 중단됐습니다. 대화형 인증 대기·stdin 블록·원격 MCP 행이 흔한 원인입니다.",
|
|
207
|
-
repaired: false,
|
|
208
|
-
actions,
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (kind === "cli-exit") {
|
|
213
|
-
return {
|
|
214
|
-
kind,
|
|
215
|
-
summary: "런타임 CLI가 비정상 종료했지만 아는 수리 계열이 아닙니다.",
|
|
216
|
-
repaired: false,
|
|
217
|
-
actions,
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
return { kind: "unknown", summary: "", repaired: false, actions };
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
module.exports = { classifyFailure, extractHosts, findOauthPluginsByHost, disableCodexPlugin, runRuntimeDoctor };
|
package/engine/commands/chat.cjs
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/* chat — 에이전트 지정 REPL 진입 별칭: agentlas chat <agent> (= agentlas <agent>) */
|
|
3
|
-
function run(ctx, args) {
|
|
4
|
-
const ko = ctx.lang === "ko";
|
|
5
|
-
if (!args[0]) {
|
|
6
|
-
ctx.err(ko ? "사용법: agentlas chat <agent>" : "Usage: agentlas chat <agent>");
|
|
7
|
-
return 1;
|
|
8
|
-
}
|
|
9
|
-
const { startRepl } = require("../ui/repl.cjs");
|
|
10
|
-
return startRepl(ctx, { agent: args[0] });
|
|
11
|
-
}
|
|
12
|
-
module.exports = { run };
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/* chats [n] — 최근 대화 목록 (공유 DB, 데스크탑과 동일 데이터). */
|
|
3
|
-
|
|
4
|
-
function run(ctx, args) {
|
|
5
|
-
const n = Math.max(1, Math.min(100, Number(args[0]) || 15));
|
|
6
|
-
const db = ctx.db();
|
|
7
|
-
if (!ctx.tableExists(db, "chats")) {
|
|
8
|
-
ctx.out(ctx.lang === "en" ? "No chats yet." : "대화가 아직 없습니다.");
|
|
9
|
-
return 0;
|
|
10
|
-
}
|
|
11
|
-
const rows = db.prepare(
|
|
12
|
-
`SELECT c.id, c.title, c.updated_at, a.slug AS agent_slug
|
|
13
|
-
FROM chats c LEFT JOIN installed_agents a ON a.id = c.agent_id
|
|
14
|
-
WHERE c.archived_at IS NULL AND (c.kind IS NULL OR c.kind = 'user')
|
|
15
|
-
ORDER BY c.updated_at DESC LIMIT ?`,
|
|
16
|
-
).all(n);
|
|
17
|
-
if (!rows.length) {
|
|
18
|
-
ctx.out(ctx.lang === "en" ? "No chats yet." : "대화가 아직 없습니다.");
|
|
19
|
-
return 0;
|
|
20
|
-
}
|
|
21
|
-
for (const r of rows) {
|
|
22
|
-
const when = String(r.updated_at || "").replace("T", " ").slice(0, 16);
|
|
23
|
-
// id 앞 8자를 먼저 찍는다 — `open <id 앞부분>`이 유일한 재개 경로인데
|
|
24
|
-
// 목록에 id가 없으면 SQLite를 직접 열지 않는 한 재개가 불가능했다.
|
|
25
|
-
// 8자는 open.cjs의 모호성 안내(id.slice(0,8))와 같은 규약.
|
|
26
|
-
const id = String(r.id || "").slice(0, 8);
|
|
27
|
-
ctx.out(` ${ctx.ui.bold(id)} ${ctx.ui.dim(when)} ${ctx.ui.accent((r.agent_slug || "?").padEnd(20))} ${r.title}`);
|
|
28
|
-
}
|
|
29
|
-
ctx.out(ctx.ui.dim(ctx.lang === "en" ? " resume: agentlas open <id>" : " 재개: agentlas open <id>"));
|
|
30
|
-
return 0;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
module.exports = { run };
|
package/engine/commands/open.cjs
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*
|
|
3
|
-
* open — 기존 대화 재개: agentlas open <chat-id-prefix>
|
|
4
|
-
* 해당 챗의 에이전트로 REPL을 열고 같은 chatId에 이어 쓴다
|
|
5
|
-
* (CLI resume 세션도 chat_runtime_sessions에서 fingerprint 일치 시 복원).
|
|
6
|
-
*/
|
|
7
|
-
const { rowToAgent, isPrivateWebOnlyAgentRow } = require("../agents/registry.cjs");
|
|
8
|
-
|
|
9
|
-
function run(ctx, args) {
|
|
10
|
-
const ko = ctx.lang === "ko";
|
|
11
|
-
const prefix = String(args[0] || "").trim();
|
|
12
|
-
if (!prefix) {
|
|
13
|
-
ctx.err(ko ? "사용법: agentlas open <chat-id 앞부분> (agentlas chats 로 목록)" : "Usage: agentlas open <chat-id-prefix> (list with: agentlas chats)");
|
|
14
|
-
return 1;
|
|
15
|
-
}
|
|
16
|
-
const db = ctx.db();
|
|
17
|
-
// 사용자 표면은 kind='user' 챗만 연다 — 데스크탑 사용자 챗 목록과 동일 필터
|
|
18
|
-
// (electron/store/chats.ts:86; 레거시 NULL=user 취급은 electron/store/db.ts:717).
|
|
19
|
-
// 숨김 division 세션(자동화/본부 인프라)은 접두사를 알아도 재개 대상이 아니다.
|
|
20
|
-
const rows = db.prepare(
|
|
21
|
-
"SELECT c.id, c.title, c.agent_id FROM chats c WHERE c.id LIKE ? AND c.archived_at IS NULL AND (c.kind IS NULL OR c.kind = 'user') ORDER BY c.updated_at DESC LIMIT 2",
|
|
22
|
-
).all(prefix + "%");
|
|
23
|
-
if (!rows.length) {
|
|
24
|
-
ctx.err((ko ? "대화를 찾을 수 없음: " : "chat not found: ") + prefix);
|
|
25
|
-
return 1;
|
|
26
|
-
}
|
|
27
|
-
if (rows.length > 1) {
|
|
28
|
-
// 안내 접두사는 사용자가 입력한 것보다 반드시 길어야 한다 — chats 목록이
|
|
29
|
-
// 8자를 찍으므로, 8자로 고정하면 8자 충돌 시 같은 문자열 두 개를 돌려주는
|
|
30
|
-
// 막다른 길이 된다("더 긴 접두사"를 만들 방법이 없음).
|
|
31
|
-
const hintLen = Math.max(8, prefix.length + 4);
|
|
32
|
-
ctx.err(ko ? `모호합니다 — 더 긴 접두사를 쓰세요 (${rows.map((r) => r.id.slice(0, hintLen)).join(", ")})` : `Ambiguous — use a longer prefix (${rows.map((r) => r.id.slice(0, hintLen)).join(", ")})`);
|
|
33
|
-
return 1;
|
|
34
|
-
}
|
|
35
|
-
const chat = rows[0];
|
|
36
|
-
const agentRow = db.prepare("SELECT * FROM installed_agents WHERE id=?").get(chat.agent_id);
|
|
37
|
-
if (!agentRow) {
|
|
38
|
-
ctx.err(ko ? "이 대화의 에이전트가 더 이상 없습니다." : "The agent for this chat no longer exists.");
|
|
39
|
-
return 1;
|
|
40
|
-
}
|
|
41
|
-
// registry 정책과 동일: 웹 전용(private) 에이전트는 챗 id를 알아도 터미널에서
|
|
42
|
-
// 실행되면 안 된다 (hub/install.cjs 설치 게이트와 같은 문구).
|
|
43
|
-
if (isPrivateWebOnlyAgentRow(agentRow)) {
|
|
44
|
-
ctx.err("This web-only agent is not available in the Agentlas terminal.");
|
|
45
|
-
return 1;
|
|
46
|
-
}
|
|
47
|
-
const { startRepl } = require("../ui/repl.cjs");
|
|
48
|
-
ctx.out(ctx.ui.dim(`${ko ? "재개" : "resume"}: ${chat.title}`));
|
|
49
|
-
return startRepl(ctx, { agent: rowToAgent(agentRow).slug, chatId: chat.id });
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
module.exports = { run };
|