loopengine 0.1.58 → 0.1.60
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/bin/ability-manager.d.ts +11 -0
- package/dist/core/gateway-tools.d.ts +19 -0
- package/dist/core/gateway-tools.js +32 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/web/agents-config-page.js +1 -1
- package/dist/web/env-admin.d.ts +10 -0
- package/dist/web/env-admin.js +46 -18
- package/package.json +1 -1
|
@@ -26,6 +26,17 @@ export interface AbilityEnvDecl {
|
|
|
26
26
|
* env-admin.ts's own serializeEnvValue doc comment). Omit for anything
|
|
27
27
|
* genuinely single-line. */
|
|
28
28
|
multiline?: boolean;
|
|
29
|
+
/** True when this setting is meaningfully different per agent (e.g.
|
|
30
|
+
* which Slack channel to post to) rather than one real shared
|
|
31
|
+
* credential every installed copy should agree on (e.g. an API key
|
|
32
|
+
* for the one external service they all call). The Admin UI's
|
|
33
|
+
* Environment tab then also offers a per-agent override — a second,
|
|
34
|
+
* separate var named `<AGENT_NAME>_<this name>` (env-admin.ts's own
|
|
35
|
+
* agentScopedEnvVarName) — that an ability's own tool code can check
|
|
36
|
+
* first, falling back to this bare name when the override isn't set.
|
|
37
|
+
* Omit (the default, false) for anything that's actually fine shared
|
|
38
|
+
* across every agent that installs this ability. */
|
|
39
|
+
perAgent?: boolean;
|
|
29
40
|
}
|
|
30
41
|
/** `name`/`version` deliberately aren't declared here — they're read off
|
|
31
42
|
* the ability's own sibling `package.json` instead (see readManifest),
|
|
@@ -35,6 +35,25 @@ export type GatewayToolEntry = ComposioGatewayToolEntry;
|
|
|
35
35
|
* module's own registry read/writes do, rather than re-deriving it (e.g.
|
|
36
36
|
* from process.cwd()) and risking the two drifting apart. */
|
|
37
37
|
export declare function agentDir(agentName: string): string;
|
|
38
|
+
/** agentDir's own inverse, for a tool file that needs to know which
|
|
39
|
+
* agent it was installed under without an env var to tell it — an
|
|
40
|
+
* ability's own tool file (copied to agents/<name>/tools/<file>.ts by
|
|
41
|
+
* add-ability) calls this with its own `import.meta.url`, and gets
|
|
42
|
+
* `<name>` back, by walking back up from the file's real on-disk
|
|
43
|
+
* location rather than trusting anything passed in (there's nothing
|
|
44
|
+
* else to trust: this runs from inside the tool file itself, before
|
|
45
|
+
* any agent-scoped state exists). Returns undefined for a tool file
|
|
46
|
+
* that isn't actually at that expected agents/<name>/tools/ depth (a
|
|
47
|
+
* test harness loading the file from somewhere else, say) — a caller
|
|
48
|
+
* needing a default when this can't be determined should fall back
|
|
49
|
+
* explicitly, not treat undefined as a crash. Mirrors the inferAgentName
|
|
50
|
+
* helper lp-task-scheduler's own tool files (schedule_task.ts et al.)
|
|
51
|
+
* already hand-duplicate per the ability system's own "no shared
|
|
52
|
+
* modules between tool files" rule — this is the one place that's
|
|
53
|
+
* actually safe to centralize, since every ability's tool file already
|
|
54
|
+
* imports real functions from this package (see e.g. lp-task-scheduler's
|
|
55
|
+
* own createCheckpointStore/runAgent imports), just not yet this. */
|
|
56
|
+
export declare function inferAgentNameFromToolUrl(toolFileUrl: string): string | undefined;
|
|
38
57
|
/** `dir` is the agent's real folder — `agents/<name>` for a top-level
|
|
39
58
|
* agent, or the nested `agents/<parent>/subagents/<child>` a subagent
|
|
40
59
|
* actually lives in (see run-agent.ts's resolveSubagentConfig, which
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
// the only intended writer of that file; loadGatewayToolsFromDir is what
|
|
9
9
|
// run-agent.ts calls to actually resolve it into ToolDefinitions.
|
|
10
10
|
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
11
|
-
import { join } from 'node:path';
|
|
11
|
+
import { basename, dirname, join } from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
12
13
|
import { execFile } from 'node:child_process';
|
|
13
14
|
import { promisify } from 'node:util';
|
|
14
15
|
import { parse as parseYaml, parseDocument, stringify as stringifyYaml } from 'yaml';
|
|
@@ -34,6 +35,36 @@ function gatewayToolsPath(dir) {
|
|
|
34
35
|
export function agentDir(agentName) {
|
|
35
36
|
return join(agentsRootDir, agentName);
|
|
36
37
|
}
|
|
38
|
+
/** agentDir's own inverse, for a tool file that needs to know which
|
|
39
|
+
* agent it was installed under without an env var to tell it — an
|
|
40
|
+
* ability's own tool file (copied to agents/<name>/tools/<file>.ts by
|
|
41
|
+
* add-ability) calls this with its own `import.meta.url`, and gets
|
|
42
|
+
* `<name>` back, by walking back up from the file's real on-disk
|
|
43
|
+
* location rather than trusting anything passed in (there's nothing
|
|
44
|
+
* else to trust: this runs from inside the tool file itself, before
|
|
45
|
+
* any agent-scoped state exists). Returns undefined for a tool file
|
|
46
|
+
* that isn't actually at that expected agents/<name>/tools/ depth (a
|
|
47
|
+
* test harness loading the file from somewhere else, say) — a caller
|
|
48
|
+
* needing a default when this can't be determined should fall back
|
|
49
|
+
* explicitly, not treat undefined as a crash. Mirrors the inferAgentName
|
|
50
|
+
* helper lp-task-scheduler's own tool files (schedule_task.ts et al.)
|
|
51
|
+
* already hand-duplicate per the ability system's own "no shared
|
|
52
|
+
* modules between tool files" rule — this is the one place that's
|
|
53
|
+
* actually safe to centralize, since every ability's tool file already
|
|
54
|
+
* imports real functions from this package (see e.g. lp-task-scheduler's
|
|
55
|
+
* own createCheckpointStore/runAgent imports), just not yet this. */
|
|
56
|
+
export function inferAgentNameFromToolUrl(toolFileUrl) {
|
|
57
|
+
try {
|
|
58
|
+
const toolsDir = dirname(fileURLToPath(toolFileUrl));
|
|
59
|
+
const agentDir = dirname(toolsDir);
|
|
60
|
+
if (basename(dirname(agentDir)) !== 'agents')
|
|
61
|
+
return undefined;
|
|
62
|
+
return basename(agentDir);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
37
68
|
/** `dir` is the agent's real folder — `agents/<name>` for a top-level
|
|
38
69
|
* agent, or the nested `agents/<parent>/subagents/<child>` a subagent
|
|
39
70
|
* actually lives in (see run-agent.ts's resolveSubagentConfig, which
|
package/dist/index.d.ts
CHANGED
|
@@ -36,7 +36,7 @@ export { LLMSummarizer } from './core/llm-summarizer.js';
|
|
|
36
36
|
export type { LLMSummarizerOptions } from './core/llm-summarizer.js';
|
|
37
37
|
export { createCheckpointStore } from './core/durable-approvals.js';
|
|
38
38
|
export type { TurnCheckpoint, CheckpointStore, OutstandingItem } from './core/durable-approvals.js';
|
|
39
|
-
export { addGatewayTool, agentDir, disconnectComposioAccount, describeGatewayTools, listComposioConnections, listComposioTools, loadGatewayToolsFromDir, removeGatewayTool, removeGatewayToolSlug, GatewayToolExistsError, GatewayToolNotFoundError, } from './core/gateway-tools.js';
|
|
39
|
+
export { addGatewayTool, agentDir, inferAgentNameFromToolUrl, disconnectComposioAccount, describeGatewayTools, listComposioConnections, listComposioTools, loadGatewayToolsFromDir, removeGatewayTool, removeGatewayToolSlug, GatewayToolExistsError, GatewayToolNotFoundError, } from './core/gateway-tools.js';
|
|
40
40
|
export type { GatewayToolEntry, GatewayToolDecision } from './core/gateway-tools.js';
|
|
41
41
|
export { connectComposioSource } from './core/mcpplug.js';
|
|
42
42
|
export type { ToolSource, ComposioSourceOptions } from './core/mcpplug.js';
|
|
@@ -55,7 +55,7 @@ export { globalConfigPageHtml } from './web/global-config-page.js';
|
|
|
55
55
|
export { playgroundHtml } from './web/playground.js';
|
|
56
56
|
export { readSkill, writeSkill, deleteSkill, SkillInvalidIdError, SkillNotFoundError } from './web/skills-admin.js';
|
|
57
57
|
export type { SkillContent } from './web/skills-admin.js';
|
|
58
|
-
export { listDeclaredEnvVars, setEnvVar, unsetEnvVar, EnvVarNameError } from './web/env-admin.js';
|
|
58
|
+
export { listDeclaredEnvVars, setEnvVar, unsetEnvVar, agentScopedEnvVarName, EnvVarNameError } from './web/env-admin.js';
|
|
59
59
|
export type { DeclaredEnvVar } from './web/env-admin.js';
|
|
60
60
|
export { readActauthConfig, addActauthRule, updateActauthRule, removeActauthRule, setDefaultDecision, ActauthRuleExistsError, ActauthRuleNotFoundError, } from './web/actauth-admin.js';
|
|
61
61
|
export type { ActauthRuleInput, ActauthConfigView } from './web/actauth-admin.js';
|
package/dist/index.js
CHANGED
|
@@ -35,7 +35,7 @@ export { resumeAgent, loadRules, loadDefaultTools, loadSubagentAsTools, systemTo
|
|
|
35
35
|
export { Compactor, TruncatingSummarizer } from './core/compaction.js';
|
|
36
36
|
export { LLMSummarizer } from './core/llm-summarizer.js';
|
|
37
37
|
export { createCheckpointStore } from './core/durable-approvals.js';
|
|
38
|
-
export { addGatewayTool, agentDir, disconnectComposioAccount, describeGatewayTools, listComposioConnections, listComposioTools, loadGatewayToolsFromDir, removeGatewayTool, removeGatewayToolSlug, GatewayToolExistsError, GatewayToolNotFoundError, } from './core/gateway-tools.js';
|
|
38
|
+
export { addGatewayTool, agentDir, inferAgentNameFromToolUrl, disconnectComposioAccount, describeGatewayTools, listComposioConnections, listComposioTools, loadGatewayToolsFromDir, removeGatewayTool, removeGatewayToolSlug, GatewayToolExistsError, GatewayToolNotFoundError, } from './core/gateway-tools.js';
|
|
39
39
|
export { connectComposioSource } from './core/mcpplug.js';
|
|
40
40
|
export { ToolLane, buildLanes, runLanes } from './core/toollane.js';
|
|
41
41
|
export { WebhookNotifier } from './core/http-notify-triggers/webhook.js';
|
|
@@ -50,7 +50,7 @@ export { agentsListPageHtml } from './web/agents-list-page.js';
|
|
|
50
50
|
export { globalConfigPageHtml } from './web/global-config-page.js';
|
|
51
51
|
export { playgroundHtml } from './web/playground.js';
|
|
52
52
|
export { readSkill, writeSkill, deleteSkill, SkillInvalidIdError, SkillNotFoundError } from './web/skills-admin.js';
|
|
53
|
-
export { listDeclaredEnvVars, setEnvVar, unsetEnvVar, EnvVarNameError } from './web/env-admin.js';
|
|
53
|
+
export { listDeclaredEnvVars, setEnvVar, unsetEnvVar, agentScopedEnvVarName, EnvVarNameError } from './web/env-admin.js';
|
|
54
54
|
export { readActauthConfig, addActauthRule, updateActauthRule, removeActauthRule, setDefaultDecision, ActauthRuleExistsError, ActauthRuleNotFoundError, } from './web/actauth-admin.js';
|
|
55
55
|
export { installAbility, upgradeAbility, listInstalledAbilities, backfillDependencies, AbilityManifestError, AbilityVersionError, AbilityCollisionError, AbilityAlreadyInstalledError, AbilityNotInstalledError, } from './bin/ability-manager.js';
|
|
56
56
|
export { searchPublicAbilities, fetchLatestAbilityVersion } from './web/abilities-admin.js';
|
|
@@ -1308,7 +1308,7 @@ export const agentsConfigPageHtml = `<!doctype html>
|
|
|
1308
1308
|
// secret nothing else has a copy of) gets the one confirm() this
|
|
1309
1309
|
// whole tab uses — every other action here is a plain upsert,
|
|
1310
1310
|
// nothing else to lose by clicking it again.
|
|
1311
|
-
if (!confirm('Remove ' + varName + '? Any tool/ability relying on it falls back to its own default (if it has one) or starts failing until it
|
|
1311
|
+
if (!confirm('Remove ' + varName + '? Any tool/ability relying on it falls back to its own default (if it has one) or starts failing until it is set again.')) return;
|
|
1312
1312
|
btn.disabled = true;
|
|
1313
1313
|
fetch('/agents/' + encodeURIComponent(name) + '/env/' + encodeURIComponent(varName), { method: 'DELETE' })
|
|
1314
1314
|
.then(function (r) { return r.json().then(function (j) { return { ok: r.ok, body: j }; }); })
|
package/dist/web/env-admin.d.ts
CHANGED
|
@@ -48,6 +48,16 @@ export interface DeclaredEnvVar {
|
|
|
48
48
|
* live off `process.env`, not cached, so it reflects whatever the last
|
|
49
49
|
* `setEnvVar` call — or a plain restart picking up `.env` — actually
|
|
50
50
|
* did. */
|
|
51
|
+
/** `varName`'s own per-agent override name (see AbilityEnvDecl.perAgent)
|
|
52
|
+
* — `<AGENT_NAME>_<varName>`, with `agentName` upper-cased and anything
|
|
53
|
+
* that isn't `[A-Z0-9_]` (a hyphen, most likely — agent directory names
|
|
54
|
+
* are kebab-case) turned into `_`, same alphabet ENV_VAR_NAME_PATTERN
|
|
55
|
+
* already requires. Guards against a result starting with a digit (a
|
|
56
|
+
* from-digits agent name, or an agent name that's entirely punctuation)
|
|
57
|
+
* the same way that pattern would otherwise reject — by prefixing one
|
|
58
|
+
* more `_` — though in practice agent names are never actually bare
|
|
59
|
+
* digits. */
|
|
60
|
+
export declare function agentScopedEnvVarName(agentName: string, varName: string): string;
|
|
51
61
|
export declare function listDeclaredEnvVars(agentName: string): DeclaredEnvVar[];
|
|
52
62
|
/** Upserts `NAME=VALUE` into the project's `.env` file — preserving
|
|
53
63
|
* every other line (comments, blank lines, unrelated keys) — and
|
package/dist/web/env-admin.js
CHANGED
|
@@ -41,33 +41,61 @@ function provenancePath(agentName) {
|
|
|
41
41
|
* live off `process.env`, not cached, so it reflects whatever the last
|
|
42
42
|
* `setEnvVar` call — or a plain restart picking up `.env` — actually
|
|
43
43
|
* did. */
|
|
44
|
+
/** `varName`'s own per-agent override name (see AbilityEnvDecl.perAgent)
|
|
45
|
+
* — `<AGENT_NAME>_<varName>`, with `agentName` upper-cased and anything
|
|
46
|
+
* that isn't `[A-Z0-9_]` (a hyphen, most likely — agent directory names
|
|
47
|
+
* are kebab-case) turned into `_`, same alphabet ENV_VAR_NAME_PATTERN
|
|
48
|
+
* already requires. Guards against a result starting with a digit (a
|
|
49
|
+
* from-digits agent name, or an agent name that's entirely punctuation)
|
|
50
|
+
* the same way that pattern would otherwise reject — by prefixing one
|
|
51
|
+
* more `_` — though in practice agent names are never actually bare
|
|
52
|
+
* digits. */
|
|
53
|
+
export function agentScopedEnvVarName(agentName, varName) {
|
|
54
|
+
const prefix = agentName.toUpperCase().replace(/[^A-Z0-9_]/g, '_');
|
|
55
|
+
return `${/^[0-9]/.test(prefix) ? '_' : ''}${prefix}_${varName}`;
|
|
56
|
+
}
|
|
44
57
|
export function listDeclaredEnvVars(agentName) {
|
|
45
58
|
const path = provenancePath(agentName);
|
|
46
59
|
if (!existsSync(path))
|
|
47
60
|
return [];
|
|
48
61
|
const provenance = JSON.parse(readFileSync(path, 'utf8'));
|
|
49
62
|
const byName = new Map();
|
|
63
|
+
function upsert(name, abilityName, decl, description) {
|
|
64
|
+
const existing = byName.get(name);
|
|
65
|
+
if (existing) {
|
|
66
|
+
existing.abilityNames.push(abilityName);
|
|
67
|
+
existing.secret = existing.secret || decl.secret === true;
|
|
68
|
+
if (existing.secret)
|
|
69
|
+
existing.value = undefined;
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const rawValue = process.env[name];
|
|
73
|
+
byName.set(name, {
|
|
74
|
+
name,
|
|
75
|
+
description,
|
|
76
|
+
secret: decl.secret === true,
|
|
77
|
+
abilityNames: [abilityName],
|
|
78
|
+
set: rawValue !== undefined,
|
|
79
|
+
value: decl.secret === true ? undefined : rawValue,
|
|
80
|
+
options: decl.options,
|
|
81
|
+
multiline: decl.multiline,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
50
84
|
for (const [abilityName, record] of Object.entries(provenance)) {
|
|
51
85
|
for (const decl of record.env) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
86
|
+
upsert(decl.name, abilityName, decl, decl.description);
|
|
87
|
+
// A perAgent var also gets its own separate, independently
|
|
88
|
+
// set/unset row for this one agent's override — see
|
|
89
|
+
// AbilityEnvDecl.perAgent's own doc comment for why this is a
|
|
90
|
+
// second row, not a replacement for the shared one above (an
|
|
91
|
+
// ability's own tool code checks the override first and falls
|
|
92
|
+
// back to the shared value, so both need to stay independently
|
|
93
|
+
// visible and settable here).
|
|
94
|
+
if (decl.perAgent) {
|
|
95
|
+
const scopedName = agentScopedEnvVarName(agentName, decl.name);
|
|
96
|
+
const overrideDescription = `Overrides ${decl.name} for this agent only — if unset, falls back to the shared value above.${decl.description ? ` ${decl.description}` : ''}`;
|
|
97
|
+
upsert(scopedName, abilityName, decl, overrideDescription);
|
|
59
98
|
}
|
|
60
|
-
const rawValue = process.env[decl.name];
|
|
61
|
-
byName.set(decl.name, {
|
|
62
|
-
name: decl.name,
|
|
63
|
-
description: decl.description,
|
|
64
|
-
secret: decl.secret === true,
|
|
65
|
-
abilityNames: [abilityName],
|
|
66
|
-
set: rawValue !== undefined,
|
|
67
|
-
value: decl.secret === true ? undefined : rawValue,
|
|
68
|
-
options: decl.options,
|
|
69
|
-
multiline: decl.multiline,
|
|
70
|
-
});
|
|
71
99
|
}
|
|
72
100
|
}
|
|
73
101
|
return [...byName.values()];
|