agentlas 1.0.65 → 1.0.67
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 +94 -0
- package/README.md +12 -4
- package/bin/agentlas.cjs +354 -16
- package/engine/acp/server.cjs +34 -3
- package/engine/agentlas-api-agent.cjs +151 -120
- package/engine/agentlas-banner.cjs +6 -2
- package/engine/agentlas-capabilities.cjs +17 -10
- package/engine/agentlas-cloud-runtime.cjs +4 -5
- package/engine/agentlas-config.cjs +19 -54
- package/engine/agentlas-core-harness.cjs +59 -5
- package/engine/agentlas-desktop-loadout.cjs +17 -3
- package/engine/agentlas-evolution.cjs +75 -18
- package/engine/agentlas-experience-exchange.cjs +145 -14
- package/engine/agentlas-experience-intake.cjs +2 -2
- package/engine/agentlas-i18n.cjs +0 -2
- package/engine/agentlas-input.cjs +273 -20
- package/engine/agentlas-judgment.cjs +0 -0
- package/engine/agentlas-mcp-env.cjs +84 -23
- package/engine/agentlas-mcp-wrapper.cjs +36 -4
- package/engine/agentlas-memory-governance.cjs +2 -1
- package/engine/agentlas-memory-import.cjs +49 -12
- package/engine/agentlas-native-host.cjs +279 -44
- package/engine/agentlas-onboard.cjs +23 -1
- package/engine/agentlas-permissions.cjs +5 -1
- package/engine/agentlas-sqlite-policy.cjs +40 -2
- package/engine/agentlas-tasks.cjs +7 -0
- package/engine/agentlas-tools.cjs +37 -10
- package/engine/agentlas-ui.cjs +26 -4
- package/engine/agentlas-workforce.cjs +30 -5
- package/engine/agentlas-workload-routing.cjs +83 -10
- package/engine/agentlas.cjs +21 -4
- package/engine/agents/builder.cjs +35 -10
- package/engine/agents/files.cjs +81 -10
- package/engine/agents/import-local.cjs +10 -2
- package/engine/agents/registry.cjs +10 -3
- package/engine/agents/router.cjs +7 -7
- package/engine/agents/routes.cjs +31 -9
- package/engine/automation/daemon.cjs +154 -40
- package/engine/automation/launchd.cjs +395 -16
- package/engine/automation/schedule.cjs +90 -3
- package/engine/automation/store.cjs +163 -59
- package/engine/browser/cdp.cjs +190 -17
- package/engine/browser/vault.cjs +1 -0
- package/engine/cli-output.cjs +96 -30
- package/engine/cloud/auth.cjs +131 -13
- package/engine/cloud/hub-client.cjs +41 -16
- package/engine/cloud-assets/cas.cjs +18 -9
- package/engine/cloud-assets/commands.cjs +108 -30
- package/engine/cloud-assets/package.cjs +66 -3
- package/engine/cloud-assets/restore.cjs +12 -9
- package/engine/cloud-assets/state.cjs +609 -116
- package/engine/commands/acp.cjs +13 -2
- package/engine/commands/automation.cjs +75 -24
- package/engine/commands/billing.cjs +6 -1
- package/engine/commands/browser.cjs +61 -29
- package/engine/commands/build.cjs +72 -14
- package/engine/commands/call.cjs +1 -1
- package/engine/commands/cd.cjs +1 -1
- package/engine/commands/cloud.cjs +2 -1
- package/engine/commands/connect.cjs +38 -13
- package/engine/commands/creds.cjs +74 -29
- package/engine/commands/doctor.cjs +17 -1
- package/engine/commands/document.cjs +38 -10
- package/engine/commands/env.cjs +6 -1
- package/engine/commands/firm.cjs +42 -12
- package/engine/commands/graph.cjs +36 -32
- package/engine/commands/help.cjs +11 -1
- package/engine/commands/hep.cjs +1 -1
- package/engine/commands/import.cjs +5 -4
- package/engine/commands/index.cjs +1 -1
- package/engine/commands/install.cjs +6 -4
- package/engine/commands/list.cjs +56 -16
- package/engine/commands/login.cjs +5 -0
- package/engine/commands/logout.cjs +6 -1
- package/engine/commands/mcp.cjs +150 -37
- package/engine/commands/memory.cjs +3 -2
- package/engine/commands/multimodal.cjs +12 -0
- package/engine/commands/native.cjs +5 -1
- package/engine/commands/one.cjs +78 -33
- package/engine/commands/plugin.cjs +13 -2
- package/engine/commands/project.cjs +15 -12
- package/engine/commands/research.cjs +18 -7
- package/engine/commands/roles.cjs +104 -28
- package/engine/commands/route.cjs +2 -2
- package/engine/commands/run.cjs +42 -9
- package/engine/commands/search.cjs +23 -2
- package/engine/commands/setup.cjs +6 -1
- package/engine/commands/storm.cjs +44 -6
- package/engine/commands/swarm.cjs +47 -9
- package/engine/commands/telegram.cjs +6 -1
- package/engine/commands/uninstall.cjs +12 -6
- package/engine/commands/update.cjs +5 -0
- package/engine/commands/upload.cjs +2 -1
- package/engine/commands/usage.cjs +6 -1
- package/engine/commands/version.cjs +6 -1
- package/engine/commands/whoami.cjs +6 -1
- package/engine/commands/workforce.cjs +60 -16
- package/engine/core/capability-grants.cjs +7 -2
- package/engine/core/daemon-client.cjs +36 -7
- package/engine/core/db.cjs +10 -2
- package/engine/core/desktop-core-fetch.cjs +477 -77
- package/engine/core/schema-ensure.cjs +23 -11
- package/engine/experience/build.cjs +5 -1
- package/engine/experience/intents.cjs +35 -82
- package/engine/experience/runtime.cjs +7 -1
- package/engine/experience/variant.cjs +58 -4
- package/engine/firms/orchestrate.cjs +9 -6
- package/engine/graph/ask-model.cjs +22 -11
- package/engine/graph/interview.cjs +74 -23
- package/engine/graph/package.cjs +63 -8
- package/engine/hephaestus/local-core.cjs +18 -5
- package/engine/hephaestus/runtime.cjs +48 -43
- package/engine/hub/install.cjs +484 -63
- package/engine/hub/plugins.cjs +391 -29
- package/engine/mcp/consent.cjs +25 -17
- package/engine/mcp/contract.cjs +229 -24
- package/engine/mcp/inventory.cjs +46 -10
- package/engine/mcp/plan.cjs +10 -1
- package/engine/mcp/probe.cjs +24 -9
- package/engine/memory-cli/curate.cjs +74 -16
- package/engine/oberon/common.cjs +37 -12
- package/engine/oberon/manifest.cjs +25 -2
- package/engine/oberon/outputs.cjs +49 -19
- package/engine/oberon/render.cjs +665 -40
- package/engine/project/career-graph.cjs +15 -2
- package/engine/project/controller.cjs +133 -15
- package/engine/project/credentials.cjs +382 -34
- package/engine/project/env-file.cjs +93 -8
- package/engine/project/memory-context.cjs +11 -57
- package/engine/project/ontology.cjs +66 -25
- package/engine/project/paths.cjs +42 -8
- package/engine/project/seed.cjs +88 -51
- package/engine/project/state.cjs +41 -16
- package/engine/project/team.cjs +26 -1
- package/engine/runtime-refusal.cjs +4 -1
- package/engine/runtimes/acp-driver.cjs +56 -9
- package/engine/runtimes/auth-evidence.cjs +21 -2
- package/engine/runtimes/detect.cjs +28 -8
- package/engine/runtimes/kinds.cjs +12 -4
- package/engine/runtimes/overrides.cjs +4 -3
- package/engine/runtimes/resolve.cjs +8 -7
- package/engine/runtimes/roles.cjs +7 -5
- package/engine/sessions/apply-fences.cjs +396 -56
- package/engine/sessions/fences.cjs +111 -27
- package/engine/sessions/memory-turn.cjs +62 -3
- package/engine/sessions/orchestrator.cjs +11 -1
- package/engine/sessions/prompt.cjs +66 -9
- package/engine/sessions/session.cjs +70 -10
- package/engine/sessions/sink.cjs +78 -12
- package/engine/sessions/store.cjs +29 -7
- package/engine/storm/storm.cjs +26 -3
- package/engine/storm/swarm.cjs +31 -3
- package/engine/telegram/connect.cjs +185 -22
- package/engine/ui/commands-catalog.cjs +1 -1
- package/engine/ui/palette.cjs +12 -2
- package/engine/ui/repl.cjs +57 -9
- package/engine/ui/screens.cjs +2 -4
- package/engine/vendor/desktop-core.manifest.json +5 -5
- package/engine/workforce/capture.cjs +9 -27
- package/engine/workforce/deps.cjs +18 -6
- package/engine/workforce/local-core-transport.cjs +68 -5
- package/package.json +1 -1
- package/engine/commands/career-graph.cjs +0 -51
|
@@ -21,9 +21,9 @@ const { captureCoreJsonSync, resolveContextMapCoreRoot } = require("../agentlas-
|
|
|
21
21
|
const terminalMemoryGovernance = require("../agentlas-memory-governance.cjs");
|
|
22
22
|
const terminalExperienceIntake = require("../agentlas-experience-intake.cjs");
|
|
23
23
|
const terminalExperienceExchange = require("../agentlas-experience-exchange.cjs");
|
|
24
|
+
const permissions = require("../agentlas-permissions.cjs");
|
|
24
25
|
const { routesMap } = require("../agents/routes.cjs");
|
|
25
26
|
const { agentFolder } = require("../agents/files.cjs");
|
|
26
|
-
const { ensureProjectMemoryCli } = require("./seed.cjs");
|
|
27
27
|
const { projectCwd } = require("./paths.cjs");
|
|
28
28
|
|
|
29
29
|
const SECRET_RE = [/\b(?:sk|pk|rk)-[A-Za-z0-9]{16,}/, /AKIA[0-9A-Z]{16}/, /ghp_[A-Za-z0-9]{20,}/, /xox[baprs]-[A-Za-z0-9-]{10,}/, /-----BEGIN [A-Z ]*PRIVATE KEY-----/, /\b(?:password|passwd|secret|api[_-]?key|access[_-]?token|bearer)\b\s*[:=]\s*\S+/i];
|
|
@@ -50,16 +50,7 @@ function langDirective(lang) {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
function logCli(projectPath, rec) {
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
// 0.9.10 경계: 초기화되지 않은 프로젝트에 시드를 만들지 않는다. 로그는
|
|
56
|
-
// 이미 존재하는 .agentlas/ 에만 덧붙인다 (seed는 project init 전용).
|
|
57
|
-
const { initializedAgentlasProjectPathCli } = require("./state.cjs");
|
|
58
|
-
if (!initializedAgentlasProjectPathCli(projectPath)) return;
|
|
59
|
-
const dir = ensureProjectMemoryCli(projectPath);
|
|
60
|
-
if (!dir) return;
|
|
61
|
-
fs.appendFileSync(path.join(dir, loadArch().logFile || "memory-log.jsonl"), JSON.stringify(rec) + "\n", "utf8");
|
|
62
|
-
} catch { /* ignore */ }
|
|
53
|
+
return require("../memory-cli/curate.cjs").logCli(projectPath, rec);
|
|
63
54
|
}
|
|
64
55
|
|
|
65
56
|
function coerceText(v, max) {
|
|
@@ -242,51 +233,7 @@ function parseMemoryEventsCli(text) {
|
|
|
242
233
|
}
|
|
243
234
|
|
|
244
235
|
function curateCliReply(db, text, ctx) {
|
|
245
|
-
|
|
246
|
-
const style = require("../agentlas-style.cjs");
|
|
247
|
-
// read 권한: 어떤 durable write도 없이 숨김 메타데이터만 제거해 돌려준다.
|
|
248
|
-
if (ctx && ctx.permission === "read") return style.sanitizeAssistantText(cleaned);
|
|
249
|
-
if (!events.length || !tableExists(db, "memory_entries")) return style.sanitizeAssistantText(cleaned);
|
|
250
|
-
ensureMemoryContextColumn(db);
|
|
251
|
-
const arch = loadArch();
|
|
252
|
-
const { randomUUID } = require("node:crypto");
|
|
253
|
-
const now = new Date().toISOString();
|
|
254
|
-
const rememberCurated = (memory) => {
|
|
255
|
-
if (!ctx || !Array.isArray(ctx.curatedMemories) || !memory) return;
|
|
256
|
-
if (!ctx.curatedMemories.some((item) => item.id === memory.id)) ctx.curatedMemories.push(memory);
|
|
257
|
-
};
|
|
258
|
-
const kinds = Array.isArray(arch.kinds) ? arch.kinds : [];
|
|
259
|
-
const scopes = Array.isArray(arch.scopes) ? arch.scopes : [];
|
|
260
|
-
for (const ev of events) {
|
|
261
|
-
const content = ev && typeof ev.content === "string" ? ev.content.trim() : "";
|
|
262
|
-
if (!content) continue;
|
|
263
|
-
if (ev.sensitivity === "secret" || SECRET_RE.some((re) => re.test(content))) continue;
|
|
264
|
-
const kind = kinds.includes(ev.memory_kind) ? ev.memory_kind : "fact";
|
|
265
|
-
let scope = ev.suggested_scope === "agent_team"
|
|
266
|
-
? "team_memory"
|
|
267
|
-
: scopes.includes(ev.suggested_scope) ? ev.suggested_scope : "session";
|
|
268
|
-
const kindAllowsUserIdentity = ["fact", "decision", "preference", "procedure"].includes(kind);
|
|
269
|
-
if (scope === "user_identity" && (ev.confidence !== "high" || !kindAllowsUserIdentity)) scope = "session";
|
|
270
|
-
if (scope === "discard" || scope === "session") { logCli(ctx.projectPath, { action: scope, kind, content, at: now }); continue; }
|
|
271
|
-
if (scope === "project" && !ctx.projectPath) scope = "team_memory";
|
|
272
|
-
const ppath = scope === "project" ? ctx.projectPath : null;
|
|
273
|
-
const scopedAgentId = scope === "agent_repo" ? (ctx.agentId || null) : null;
|
|
274
|
-
const requestContext = normalizeRequestContext(ev, ctx, ppath);
|
|
275
|
-
try {
|
|
276
|
-
const dup = db.prepare("SELECT id,scope,kind,content,confidence,sensitivity,context_json FROM memory_entries WHERE scope=? AND kind=? AND lower(trim(content))=? AND superseded_at IS NULL AND (project_path IS ? OR project_path=?) AND (agent_id IS ? OR agent_id=?) LIMIT 1").get(scope, kind, content.toLowerCase(), ppath, ppath, scopedAgentId, scopedAgentId);
|
|
277
|
-
if (dup) {
|
|
278
|
-
rememberCurated({ ...dup, requestContext });
|
|
279
|
-
continue;
|
|
280
|
-
}
|
|
281
|
-
const memoryId = randomUUID();
|
|
282
|
-
const confidence = ev.confidence || "medium";
|
|
283
|
-
const sensitivity = ev.sensitivity || "internal";
|
|
284
|
-
db.prepare("INSERT INTO memory_entries (id,scope,kind,content,project_id,project_path,agent_id,chat_id,confidence,sensitivity,evidence_json,context_json,superseded_at,created_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,NULL,?)").run(memoryId, scope, kind, content, ctx.projectId || null, ppath, scopedAgentId, null, confidence, sensitivity, JSON.stringify(Array.isArray(ev.evidence_refs) ? ev.evidence_refs : []), JSON.stringify(requestContext), now);
|
|
285
|
-
rememberCurated({ id: memoryId, scope, kind, content, confidence, sensitivity, requestContext });
|
|
286
|
-
logCli(ctx.projectPath, { action: "written", scope, kind, content, request_context: requestContext, at: now });
|
|
287
|
-
} catch { /* ignore */ }
|
|
288
|
-
}
|
|
289
|
-
return style.sanitizeAssistantText(cleaned);
|
|
236
|
+
return require("../memory-cli/curate.cjs").curateCliReply(db, text, ctx);
|
|
290
237
|
}
|
|
291
238
|
|
|
292
239
|
const TERMINAL_MEMORY_CORE_MAX_TOKENS = 150;
|
|
@@ -382,9 +329,16 @@ function exactAgentBaseForExecution(db, agent, runtimeExperience = null) {
|
|
|
382
329
|
const packageHash = /^[a-f0-9]{64}$/.test(rawHash) ? `sha256:${rawHash}` : null;
|
|
383
330
|
const explicitDefinition = String(runtimeExperience?.agentDefinitionId || "");
|
|
384
331
|
const explicitRelease = String(runtimeExperience?.baseAgentReleaseId || "");
|
|
332
|
+
const hasExplicitBinding = !!runtimeExperience && (
|
|
333
|
+
Object.prototype.hasOwnProperty.call(runtimeExperience, "agentDefinitionId") ||
|
|
334
|
+
Object.prototype.hasOwnProperty.call(runtimeExperience, "baseAgentReleaseId")
|
|
335
|
+
);
|
|
385
336
|
if (portableId.test(explicitDefinition) && portableId.test(explicitRelease)) {
|
|
386
337
|
return { agentDefinitionId: explicitDefinition, agentReleaseId: explicitRelease, packageHash, authority: "explicit-runtime-binding" };
|
|
387
338
|
}
|
|
339
|
+
// A partially supplied runtime binding is an attempted exact authority, not
|
|
340
|
+
// permission to fall through to an unrelated installed/local identity.
|
|
341
|
+
if (hasExplicitBinding) return null;
|
|
388
342
|
if (binding && portableId.test(String(binding.agent_definition_id)) && portableId.test(String(binding.agent_release_id))) {
|
|
389
343
|
return { agentDefinitionId: binding.agent_definition_id, agentReleaseId: binding.agent_release_id, packageHash, authority: "installed-hub-binding" };
|
|
390
344
|
}
|
|
@@ -400,7 +354,7 @@ function exactAgentBaseForExecution(db, agent, runtimeExperience = null) {
|
|
|
400
354
|
}
|
|
401
355
|
|
|
402
356
|
function finalizeExperienceExecutionCli(db, input) {
|
|
403
|
-
if (input.permission === "read") return null;
|
|
357
|
+
if (permissions.normalize(input.permission) === "read") return null;
|
|
404
358
|
if (!input.agentId) return null;
|
|
405
359
|
let agent;
|
|
406
360
|
try { agent = db.prepare("SELECT * FROM installed_agents WHERE id=?").get(input.agentId); }
|
|
@@ -20,23 +20,31 @@ const { initializedAgentlasProjectPathCli } = require("./state.cjs");
|
|
|
20
20
|
|
|
21
21
|
const ONTOLOGY_SUPPORTED_EXTS = new Set([".txt", ".md", ".markdown", ".json", ".csv", ".tsv"]);
|
|
22
22
|
|
|
23
|
-
/**
|
|
24
|
-
function parseFlagsCli(args) {
|
|
23
|
+
/** Ontology source registration options. Unknown/duplicate flags fail closed. */
|
|
24
|
+
function parseFlagsCli(args, schema = {}) {
|
|
25
25
|
const flags = { _: [] };
|
|
26
|
+
const values = new Set(schema.values || ["kind", "scope"]);
|
|
27
|
+
const booleans = new Set(schema.booleans || []);
|
|
28
|
+
let passthrough = false;
|
|
26
29
|
for (let i = 0; i < args.length; i++) {
|
|
27
|
-
const
|
|
28
|
-
if (
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
30
|
+
const token = String(args[i]);
|
|
31
|
+
if (passthrough) { flags._.push(token); continue; }
|
|
32
|
+
if (token === "--") { passthrough = true; continue; }
|
|
33
|
+
if (!token.startsWith("--")) { flags._.push(token); continue; }
|
|
34
|
+
const at = token.indexOf("=");
|
|
35
|
+
const key = token.slice(2, at > 2 ? at : undefined);
|
|
36
|
+
if (!values.has(key) && !booleans.has(key)) throw new Error(`unknown option: --${key}`);
|
|
37
|
+
if (Object.prototype.hasOwnProperty.call(flags, key)) throw new Error(`duplicate option: --${key}`);
|
|
38
|
+
if (booleans.has(key)) {
|
|
39
|
+
if (at > 2) throw new Error(`--${key} does not take a value`);
|
|
40
|
+
flags[key] = true;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const value = at > 2 ? token.slice(at + 1) : args[++i];
|
|
44
|
+
if (value === undefined || value === "" || (at <= 2 && String(value).startsWith("--"))) {
|
|
45
|
+
throw new Error(`--${key} requires a value`);
|
|
39
46
|
}
|
|
47
|
+
flags[key] = String(value);
|
|
40
48
|
}
|
|
41
49
|
return flags;
|
|
42
50
|
}
|
|
@@ -233,18 +241,24 @@ function resolveOntologyPathCli(value, cwd) {
|
|
|
233
241
|
function inferOntologyKindCli(value, text) {
|
|
234
242
|
const v = String(value || "").toLowerCase();
|
|
235
243
|
const hay = String(text || "").toLowerCase();
|
|
236
|
-
if (["company", "work", "business", "corp", "team", "회사", "업무", "팀", "조직"].includes(v)
|
|
237
|
-
if (["personal", "private-life", "life", "me", "개인", "내자료", "일상"].includes(v)
|
|
238
|
-
if (["project", "repo", "프로젝트", "레포"].includes(v)
|
|
244
|
+
if (["company", "work", "business", "corp", "team", "회사", "업무", "팀", "조직"].includes(v)) return "company";
|
|
245
|
+
if (["personal", "private-life", "life", "me", "개인", "내자료", "일상"].includes(v)) return "personal";
|
|
246
|
+
if (["project", "repo", "프로젝트", "레포"].includes(v)) return "project";
|
|
247
|
+
if (/(company|work|business|corp|team|회사|업무|조직)/i.test(hay)) return "company";
|
|
248
|
+
if (/(personal|private-life|\bme\b|개인|내\s*자료|일상)/i.test(hay)) return "personal";
|
|
249
|
+
if (/(project|repo|프로젝트|레포)/i.test(hay)) return "project";
|
|
239
250
|
return "project";
|
|
240
251
|
}
|
|
241
252
|
|
|
242
253
|
function inferOntologyScopeCli(value, text, kind) {
|
|
243
254
|
const v = String(value || "").toLowerCase();
|
|
244
255
|
const hay = String(text || "").toLowerCase();
|
|
245
|
-
if (["public", "open", "공개"].includes(v)
|
|
246
|
-
if (["internal", "team", "내부", "팀"].includes(v)
|
|
247
|
-
if (["private", "secret", "local", "비공개", "개인"].includes(v)
|
|
256
|
+
if (["public", "open", "공개"].includes(v)) return "public";
|
|
257
|
+
if (["internal", "team", "내부", "팀"].includes(v)) return "internal";
|
|
258
|
+
if (["private", "secret", "local", "비공개", "개인"].includes(v)) return "private";
|
|
259
|
+
if (/(public|open|공개)/i.test(hay)) return "public";
|
|
260
|
+
if (/(internal|team-only|company-wide|내부|팀\s*공유|회사\s*공유)/i.test(hay)) return "internal";
|
|
261
|
+
if (/(private|secret|local-only|비공개|개인만|나만)/i.test(hay)) return "private";
|
|
248
262
|
return kind === "company" || kind === "personal" ? "private" : "private";
|
|
249
263
|
}
|
|
250
264
|
|
|
@@ -366,7 +380,7 @@ function formatOntologyStatusCli(paths, lang) {
|
|
|
366
380
|
lines.push("", `${ko ? "소스" : "Sources"} (${sources.length}):`);
|
|
367
381
|
for (const source of sources) {
|
|
368
382
|
const sourcePath = path.resolve(String(source.path || ""));
|
|
369
|
-
lines.push(` ${fs.existsSync(sourcePath) ? "✓" : "!"} ${sourcePath} ${source.kind || "project"} / ${source.scope || "
|
|
383
|
+
lines.push(` ${fs.existsSync(sourcePath) ? "✓" : "!"} ${sourcePath} ${source.kind || "project"} / ${source.scope || "private"}`);
|
|
370
384
|
}
|
|
371
385
|
if (!sources.length) lines.push(ko ? " (없음)" : " (none)");
|
|
372
386
|
lines.push(
|
|
@@ -438,6 +452,7 @@ async function runOntologyCli(args, opts) {
|
|
|
438
452
|
const sub = normalizedArgs[0] || "status";
|
|
439
453
|
const passivePaths = ontologyPathsForCli(projectPath);
|
|
440
454
|
if (sub === "status" || sub === "list") {
|
|
455
|
+
if (normalizedArgs.length > 1) throw new Error(`usage: agentlas ontology ${sub}`);
|
|
441
456
|
if (!initializedAgentlasProjectPathCli(projectPath)) {
|
|
442
457
|
return [
|
|
443
458
|
ko ? "온톨로지: 초기화되지 않음" : "Ontology: not initialized",
|
|
@@ -448,7 +463,10 @@ async function runOntologyCli(args, opts) {
|
|
|
448
463
|
}
|
|
449
464
|
return formatOntologyStatusCli(passivePaths, opts.lang);
|
|
450
465
|
}
|
|
451
|
-
if (sub === "help" || sub === "--help" || sub === "-h")
|
|
466
|
+
if (sub === "help" || sub === "--help" || sub === "-h") {
|
|
467
|
+
if (normalizedArgs.length > 1) throw new Error("usage: agentlas ontology help");
|
|
468
|
+
return ontologyUsageLinesCli(opts.lang);
|
|
469
|
+
}
|
|
452
470
|
const directOntologyCommand = ["open", "add", "company", "personal", "project"].includes(String(sub).toLowerCase())
|
|
453
471
|
|| isOntologyPathishCli(sub, cwd, true);
|
|
454
472
|
if (!directOntologyCommand) {
|
|
@@ -456,6 +474,22 @@ async function runOntologyCli(args, opts) {
|
|
|
456
474
|
const parsed = await parseOntologyNaturalArgsCli(normalizedArgs.join(" "), cwd);
|
|
457
475
|
return runOntologyCli(parsed, opts);
|
|
458
476
|
}
|
|
477
|
+
let directFlags = null;
|
|
478
|
+
if (sub === "open") {
|
|
479
|
+
if (normalizedArgs.length !== 1) throw new Error("usage: agentlas ontology open");
|
|
480
|
+
} else if (sub === "add") {
|
|
481
|
+
directFlags = parseFlagsCli(normalizedArgs.slice(1));
|
|
482
|
+
if (directFlags._.length < 1 || directFlags._.length > 3) throw new Error("usage: agentlas ontology add <path> [kind] [scope] [--kind <kind>] [--scope <scope>]");
|
|
483
|
+
if (directFlags.kind !== undefined && directFlags._[1] !== undefined) throw new Error("kind was provided twice; use either the positional value or --kind");
|
|
484
|
+
if (directFlags.scope !== undefined && directFlags._[2] !== undefined) throw new Error("scope was provided twice; use either the positional value or --scope");
|
|
485
|
+
} else if (["company", "personal", "project"].includes(String(sub).toLowerCase())) {
|
|
486
|
+
directFlags = parseFlagsCli(normalizedArgs.slice(1), { values: ["scope"] });
|
|
487
|
+
if (directFlags._.length < 1 || directFlags._.length > 2) throw new Error(`usage: agentlas ontology ${sub} <path> [scope] [--scope <scope>]`);
|
|
488
|
+
if (directFlags.scope !== undefined && directFlags._[1] !== undefined) throw new Error("scope was provided twice; use either the positional value or --scope");
|
|
489
|
+
} else if (isOntologyPathishCli(sub, cwd, true)) {
|
|
490
|
+
directFlags = parseFlagsCli(normalizedArgs.slice(1));
|
|
491
|
+
if (directFlags._.length) throw new Error("unexpected positional argument after ontology source path");
|
|
492
|
+
}
|
|
459
493
|
const paths = ensureOntologyCli(projectPath, opts.lang);
|
|
460
494
|
if (sub === "open") {
|
|
461
495
|
const opened = opts.noOpen ? true : openLocalPathCli(paths.inboxPath, opts.notify);
|
|
@@ -464,20 +498,27 @@ async function runOntologyCli(args, opts) {
|
|
|
464
498
|
: (ko ? "온톨로지 수신함을 자동으로 열지 못했습니다. 직접 여세요" : "Could not open ontology inbox automatically; open it manually")}: ${paths.inboxPath}`];
|
|
465
499
|
}
|
|
466
500
|
if (sub === "add") {
|
|
467
|
-
const flags =
|
|
501
|
+
const flags = directFlags;
|
|
468
502
|
const source = flags._[0];
|
|
469
503
|
const kind = inferOntologyKindCli(flags.kind || flags._[1], normalizedArgs.join(" "));
|
|
470
504
|
const scope = inferOntologyScopeCli(flags.scope || flags._[2], normalizedArgs.join(" "), kind);
|
|
471
505
|
return registerOntologySourceCli(paths, source, kind, scope, cwd, opts.lang);
|
|
472
506
|
}
|
|
473
507
|
if (["company", "personal", "project"].includes(String(sub).toLowerCase())) {
|
|
474
|
-
const flags =
|
|
508
|
+
const flags = directFlags;
|
|
475
509
|
const kind = inferOntologyKindCli(sub, normalizedArgs.join(" "));
|
|
476
510
|
const scope = inferOntologyScopeCli(flags.scope || flags._[1], normalizedArgs.join(" "), kind);
|
|
477
511
|
return registerOntologySourceCli(paths, flags._[0], kind, scope, cwd, opts.lang);
|
|
478
512
|
}
|
|
479
513
|
if (isOntologyPathishCli(sub, cwd, true)) {
|
|
480
|
-
return registerOntologySourceCli(
|
|
514
|
+
return registerOntologySourceCli(
|
|
515
|
+
paths,
|
|
516
|
+
sub,
|
|
517
|
+
inferOntologyKindCli(directFlags.kind, normalizedArgs.join(" ")),
|
|
518
|
+
inferOntologyScopeCli(directFlags.scope, normalizedArgs.join(" "), directFlags.kind || "project"),
|
|
519
|
+
cwd,
|
|
520
|
+
opts.lang,
|
|
521
|
+
);
|
|
481
522
|
}
|
|
482
523
|
throw new Error(ko
|
|
483
524
|
? "사용법: /ontology status|list|open|add <경로>"
|
package/engine/project/paths.cjs
CHANGED
|
@@ -2,23 +2,50 @@
|
|
|
2
2
|
/*
|
|
3
3
|
* project/paths — 프로젝트 경계 판정용 경로 헬퍼 (v1 runCwd/projectCwd 포팅).
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* 바꿔야 한다 (v1 monolith 9961–9981 라인이 원본 계약).
|
|
5
|
+
* 이 파일이 Terminal 전체의 단일 정본이다. Workforce와 Hephaestus가 각자 복제하면
|
|
6
|
+
* 폴백 보안 경계가 갈리므로 둘 다 여기의 함수를 그대로 export한다.
|
|
8
7
|
*/
|
|
9
8
|
const fs = require("node:fs");
|
|
10
9
|
const os = require("node:os");
|
|
11
10
|
const path = require("node:path");
|
|
12
11
|
const { userDataDir } = require("../core/paths.cjs");
|
|
13
12
|
|
|
13
|
+
let temporaryRunCwd = null;
|
|
14
|
+
|
|
15
|
+
function ensurePrivateDirectory(directory) {
|
|
16
|
+
fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
17
|
+
const stat = fs.lstatSync(directory);
|
|
18
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
19
|
+
throw new Error("agent run folder is not a private directory");
|
|
20
|
+
}
|
|
21
|
+
if (process.platform !== "win32") {
|
|
22
|
+
fs.chmodSync(directory, 0o700);
|
|
23
|
+
if ((fs.statSync(directory).mode & 0o777) !== 0o700) {
|
|
24
|
+
throw new Error("agent run folder permissions could not be restricted");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return fs.realpathSync.native(directory);
|
|
28
|
+
}
|
|
29
|
+
|
|
14
30
|
/** 에이전트 격리 실행용 전용 폴더 — "프로젝트 아님" 위치의 안전한 폴백. */
|
|
15
31
|
function runCwd() {
|
|
16
32
|
const dir = path.join(userDataDir(), "agent-cwd");
|
|
17
33
|
try {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
34
|
+
return ensurePrivateDirectory(dir);
|
|
35
|
+
} catch (primaryError) {
|
|
36
|
+
try {
|
|
37
|
+
if (!temporaryRunCwd) {
|
|
38
|
+
temporaryRunCwd = fs.mkdtempSync(path.join(os.tmpdir(), "agentlas-agent-cwd-"));
|
|
39
|
+
}
|
|
40
|
+
return ensurePrivateDirectory(temporaryRunCwd);
|
|
41
|
+
} catch (fallbackError) {
|
|
42
|
+
const error = new Error(
|
|
43
|
+
"Agentlas could not create a private agent working directory; refusing to use the home folder.",
|
|
44
|
+
);
|
|
45
|
+
error.code = "AGENTLAS_RUN_CWD_UNAVAILABLE";
|
|
46
|
+
error.cause = fallbackError || primaryError;
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
22
49
|
}
|
|
23
50
|
}
|
|
24
51
|
|
|
@@ -29,7 +56,14 @@ function runCwd() {
|
|
|
29
56
|
function projectCwd() {
|
|
30
57
|
try {
|
|
31
58
|
const cwd = process.cwd();
|
|
32
|
-
|
|
59
|
+
const neutral = runCwd();
|
|
60
|
+
const canonical = cwd ? fs.realpathSync.native(cwd) : "";
|
|
61
|
+
const home = fs.realpathSync.native(os.homedir());
|
|
62
|
+
let data = path.resolve(userDataDir());
|
|
63
|
+
try { data = fs.realpathSync.native(userDataDir()); } catch { /* compare the unresolved configured path */ }
|
|
64
|
+
if (!canonical || canonical === home || canonical === data || canonical === neutral || path.parse(canonical).root === canonical) {
|
|
65
|
+
return neutral;
|
|
66
|
+
}
|
|
33
67
|
return cwd;
|
|
34
68
|
} catch {
|
|
35
69
|
return runCwd();
|
package/engine/project/seed.cjs
CHANGED
|
@@ -60,34 +60,83 @@ function removeLegacySuperOntologyFiles(dir) {
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
function privateSeedName(value, fallback) {
|
|
64
|
+
const name = String(value || fallback || "").trim();
|
|
65
|
+
if (!name || name !== path.basename(name) || name === "." || name === ".." || name.length > 180) {
|
|
66
|
+
throw new Error("Agentlas project seed contains an unsafe file name");
|
|
67
|
+
}
|
|
68
|
+
return name;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function ensurePrivateDirectory(dir) {
|
|
72
|
+
try { fs.mkdirSync(dir, { recursive: false, mode: 0o700 }); }
|
|
73
|
+
catch (error) { if (!error || error.code !== "EEXIST") throw error; }
|
|
74
|
+
const listed = fs.lstatSync(dir);
|
|
75
|
+
if (!listed.isDirectory() || listed.isSymbolicLink()) {
|
|
76
|
+
throw new Error("Agentlas project state path must be a real directory");
|
|
77
|
+
}
|
|
78
|
+
if (process.platform !== "win32") fs.chmodSync(dir, 0o700);
|
|
79
|
+
return dir;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function ensurePrivateSeedFile(file, content) {
|
|
83
|
+
let fd = null;
|
|
84
|
+
const noFollow = fs.constants.O_NOFOLLOW || 0;
|
|
85
|
+
try {
|
|
86
|
+
try {
|
|
87
|
+
fd = fs.openSync(file, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL | noFollow, 0o600);
|
|
88
|
+
const bytes = Buffer.from(String(content), "utf8");
|
|
89
|
+
const written = fs.writeSync(fd, bytes, 0, bytes.length, null);
|
|
90
|
+
if (written !== bytes.length) throw new Error("Agentlas project seed write was incomplete");
|
|
91
|
+
fs.fsyncSync(fd);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
if (!error || error.code !== "EEXIST") throw error;
|
|
94
|
+
if (fd != null) { try { fs.closeSync(fd); } catch { /* ignore */ } fd = null; }
|
|
95
|
+
fd = fs.openSync(file, fs.constants.O_RDONLY | noFollow);
|
|
96
|
+
}
|
|
97
|
+
const opened = fs.fstatSync(fd);
|
|
98
|
+
const listed = fs.lstatSync(file);
|
|
99
|
+
if (
|
|
100
|
+
!opened.isFile() || opened.nlink !== 1 ||
|
|
101
|
+
!listed.isFile() || listed.isSymbolicLink() || listed.nlink !== 1 ||
|
|
102
|
+
opened.dev !== listed.dev || opened.ino !== listed.ino
|
|
103
|
+
) throw new Error("Agentlas project seed target must be a single-link regular file");
|
|
104
|
+
try { fs.fchmodSync(fd, 0o600); } catch { /* Windows/ACL-only host */ }
|
|
105
|
+
return file;
|
|
106
|
+
} finally {
|
|
107
|
+
if (fd != null) try { fs.closeSync(fd); } catch { /* ignore */ }
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
63
111
|
function ensureProjectMemoryCli(projectPath, projectName) {
|
|
64
112
|
const arch = loadArch();
|
|
65
113
|
try {
|
|
66
|
-
const
|
|
67
|
-
fs.
|
|
114
|
+
const root = path.resolve(projectPath);
|
|
115
|
+
const canonicalRoot = fs.realpathSync.native(root);
|
|
116
|
+
const rootStat = fs.lstatSync(root);
|
|
117
|
+
if (!rootStat.isDirectory() || rootStat.isSymbolicLink() || path.parse(canonicalRoot).root === canonicalRoot) return null;
|
|
118
|
+
const memoryDirName = privateSeedName(arch.memoryDir, ".agentlas");
|
|
119
|
+
const dir = ensurePrivateDirectory(path.join(root, memoryDirName));
|
|
68
120
|
removeLegacySuperOntologyFiles(dir);
|
|
69
|
-
const name = projectName || path.basename(
|
|
70
|
-
ensureLocalCredentialStoreCli(
|
|
71
|
-
ensureSoulCredentialIndexCli(
|
|
72
|
-
const sitemap = path.join(dir, arch.sitemapFile
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
const
|
|
86
|
-
const careerGraphInboxDir = arch.careerGraphInboxDir || "career-graph-inbox";
|
|
87
|
-
const careerGraphDbFile = arch.careerGraphDbFile || "career-graph.sqlite";
|
|
121
|
+
const name = String(projectName || path.basename(root) || "Project").slice(0, 200);
|
|
122
|
+
ensureLocalCredentialStoreCli(root, name, arch);
|
|
123
|
+
ensureSoulCredentialIndexCli(root, name, arch);
|
|
124
|
+
const sitemap = path.join(dir, privateSeedName(arch.sitemapFile, "sitemap.json"));
|
|
125
|
+
const now = new Date().toISOString();
|
|
126
|
+
ensurePrivateSeedFile(sitemap, JSON.stringify({ project: name, created_at: now, updated_at: now, nodes: [] }, null, 2));
|
|
127
|
+
const skillRegistryFile = privateSeedName(arch.skillRegistryFile, "skill-registry.json");
|
|
128
|
+
const skillTrialsFile = privateSeedName(arch.skillTrialsFile, "skill-trials.jsonl");
|
|
129
|
+
const curatorDecisionsFile = privateSeedName(arch.curatorDecisionsFile, "curator-decisions.jsonl");
|
|
130
|
+
const ontologyRuntimeFile = privateSeedName(arch.ontologyRuntimeFile, "ontology-runtime.json");
|
|
131
|
+
const ontologySourceManifestFile = privateSeedName(arch.ontologySourceManifestFile, "ontology-sources.json");
|
|
132
|
+
const ontologyInboxDir = privateSeedName(arch.ontologyInboxDir, "ontology-inbox");
|
|
133
|
+
const ontologyDbFile = privateSeedName(arch.ontologyDbFile, "ontology-runtime.sqlite");
|
|
134
|
+
const careerGraphConfigFile = privateSeedName(arch.careerGraphConfigFile, "career-graph.json");
|
|
135
|
+
const careerGraphSourceManifestFile = privateSeedName(arch.careerGraphSourceManifestFile, "career-graph-sources.json");
|
|
136
|
+
const careerGraphInboxDir = privateSeedName(arch.careerGraphInboxDir, "career-graph-inbox");
|
|
137
|
+
const careerGraphDbFile = privateSeedName(arch.careerGraphDbFile, "career-graph.sqlite");
|
|
88
138
|
const skillRegistry = path.join(dir, skillRegistryFile);
|
|
89
|
-
|
|
90
|
-
fs.writeFileSync(skillRegistry, JSON.stringify({
|
|
139
|
+
ensurePrivateSeedFile(skillRegistry, JSON.stringify({
|
|
91
140
|
schemaVersion: "1.0",
|
|
92
141
|
kind: "agentlas-skill-lifecycle-registry",
|
|
93
142
|
state: "local_candidate",
|
|
@@ -127,18 +176,15 @@ function ensureProjectMemoryCli(projectPath, projectName) {
|
|
|
127
176
|
lowRiskCanaryOnly: true,
|
|
128
177
|
severeFailureTolerance: 0,
|
|
129
178
|
},
|
|
130
|
-
}, null, 2)
|
|
131
|
-
|
|
132
|
-
const ontologyInbox = path.join(dir, ontologyInboxDir);
|
|
133
|
-
if (!fs.existsSync(ontologyInbox)) fs.mkdirSync(ontologyInbox, { recursive: true });
|
|
179
|
+
}, null, 2));
|
|
180
|
+
const ontologyInbox = ensurePrivateDirectory(path.join(dir, ontologyInboxDir));
|
|
134
181
|
const ontologyRuntime = path.join(dir, ontologyRuntimeFile);
|
|
135
|
-
|
|
136
|
-
fs.writeFileSync(ontologyRuntime, JSON.stringify({
|
|
182
|
+
ensurePrivateSeedFile(ontologyRuntime, JSON.stringify({
|
|
137
183
|
schemaVersion: "1.0",
|
|
138
184
|
kind: "agentlas-ontology-runtime",
|
|
139
185
|
state: "active",
|
|
140
186
|
activation: "automatic",
|
|
141
|
-
projectRoot:
|
|
187
|
+
projectRoot: root,
|
|
142
188
|
projectName: name,
|
|
143
189
|
dbPath: path.join(dir, ontologyDbFile),
|
|
144
190
|
inboxPath: ontologyInbox,
|
|
@@ -161,27 +207,22 @@ function ensureProjectMemoryCli(projectPath, projectName) {
|
|
|
161
207
|
durableWrites: "candidate-ticket-only",
|
|
162
208
|
workingMemory: "runtime-cache-only",
|
|
163
209
|
},
|
|
164
|
-
}, null, 2)
|
|
165
|
-
}
|
|
210
|
+
}, null, 2));
|
|
166
211
|
const ontologySources = path.join(dir, ontologySourceManifestFile);
|
|
167
|
-
|
|
168
|
-
fs.writeFileSync(ontologySources, JSON.stringify({
|
|
212
|
+
ensurePrivateSeedFile(ontologySources, JSON.stringify({
|
|
169
213
|
schemaVersion: "1.0",
|
|
170
214
|
kind: "agentlas-ontology-source-manifest",
|
|
171
|
-
projectRoot:
|
|
215
|
+
projectRoot: root,
|
|
172
216
|
sources: [],
|
|
173
|
-
}, null, 2)
|
|
174
|
-
|
|
175
|
-
const careerGraphInbox = path.join(dir, careerGraphInboxDir);
|
|
176
|
-
if (!fs.existsSync(careerGraphInbox)) fs.mkdirSync(careerGraphInbox, { recursive: true });
|
|
217
|
+
}, null, 2));
|
|
218
|
+
const careerGraphInbox = ensurePrivateDirectory(path.join(dir, careerGraphInboxDir));
|
|
177
219
|
const careerGraphConfig = path.join(dir, careerGraphConfigFile);
|
|
178
|
-
|
|
179
|
-
fs.writeFileSync(careerGraphConfig, JSON.stringify({
|
|
220
|
+
ensurePrivateSeedFile(careerGraphConfig, JSON.stringify({
|
|
180
221
|
schemaVersion: "1.0",
|
|
181
222
|
kind: "agentlas-career-graph",
|
|
182
223
|
state: "active",
|
|
183
224
|
model: "ledger_first_derived_index",
|
|
184
|
-
projectRoot:
|
|
225
|
+
projectRoot: root,
|
|
185
226
|
projectName: name,
|
|
186
227
|
dbPath: path.join(dir, careerGraphDbFile),
|
|
187
228
|
inboxPath: careerGraphInbox,
|
|
@@ -193,20 +234,16 @@ function ensureProjectMemoryCli(projectPath, projectName) {
|
|
|
193
234
|
neverScanHomeDirectory: true,
|
|
194
235
|
neverScanSiblingProjects: true,
|
|
195
236
|
},
|
|
196
|
-
}, null, 2)
|
|
197
|
-
}
|
|
237
|
+
}, null, 2));
|
|
198
238
|
const careerGraphSources = path.join(dir, careerGraphSourceManifestFile);
|
|
199
|
-
|
|
200
|
-
fs.writeFileSync(careerGraphSources, JSON.stringify({
|
|
239
|
+
ensurePrivateSeedFile(careerGraphSources, JSON.stringify({
|
|
201
240
|
schemaVersion: "1.0",
|
|
202
241
|
kind: "agentlas-career-graph-source-manifest",
|
|
203
|
-
projectRoot:
|
|
242
|
+
projectRoot: root,
|
|
204
243
|
sources: [],
|
|
205
|
-
}, null, 2)
|
|
206
|
-
}
|
|
244
|
+
}, null, 2));
|
|
207
245
|
for (const fileName of [skillTrialsFile, curatorDecisionsFile]) {
|
|
208
|
-
|
|
209
|
-
if (!fs.existsSync(filePath)) fs.writeFileSync(filePath, "", "utf8");
|
|
246
|
+
ensurePrivateSeedFile(path.join(dir, fileName), "");
|
|
210
247
|
}
|
|
211
248
|
return dir;
|
|
212
249
|
} catch { return null; }
|