@ai-ecoverse/slicc-agent 3.48.1 → 3.50.0
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/README.md +38 -6
- package/bin/agent +1 -0
- package/dist/control.js +1 -1
- package/dist/control.js.map +2 -2
- package/dist/freezer/index.js +29 -8
- package/dist/freezer/index.js.map +2 -2
- package/dist/freezer/title.js +2 -2
- package/dist/freezer/title.js.map +2 -2
- package/dist/gelatiere/catalog.js +1 -1
- package/dist/gelatiere/catalog.js.map +2 -2
- package/dist/gelatiere/index.js +44 -21
- package/dist/gelatiere/index.js.map +2 -2
- package/dist/gelatiere/store.js +9 -5
- package/dist/gelatiere/store.js.map +2 -2
- package/dist/index.js.map +2 -2
- package/dist/kernel/accounts.js +141 -0
- package/dist/kernel/accounts.js.map +7 -0
- package/dist/kernel/agent-users.js +180 -0
- package/dist/kernel/agent-users.js.map +7 -0
- package/dist/kernel/env.js +9 -3
- package/dist/kernel/env.js.map +2 -2
- package/dist/kernel/users.js +16 -1
- package/dist/kernel/users.js.map +2 -2
- package/dist/layout.js +62 -0
- package/dist/layout.js.map +7 -0
- package/dist/licks/index.js.map +2 -2
- package/dist/licks/sources.js +69 -20
- package/dist/licks/sources.js.map +2 -2
- package/dist/main.js +70 -36
- package/dist/main.js.map +2 -2
- package/dist/mcp/index.js +5 -3
- package/dist/mcp/index.js.map +2 -2
- package/dist/mcp/paths.js +15 -0
- package/dist/mcp/paths.js.map +7 -0
- package/dist/mcp/runtime.js +16 -14
- package/dist/mcp/runtime.js.map +3 -3
- package/dist/memory/extract.js +2 -1
- package/dist/memory/extract.js.map +2 -2
- package/dist/memory/prompt.js +5 -1
- package/dist/memory/prompt.js.map +2 -2
- package/dist/memory/runtime.js +9 -1
- package/dist/memory/runtime.js.map +2 -2
- package/dist/roles/roles.js +54 -15
- package/dist/roles/roles.js.map +2 -2
- package/dist/scoops/cli.js +40 -19
- package/dist/scoops/cli.js.map +2 -2
- package/dist/scoops/extension.js +19 -15
- package/dist/scoops/extension.js.map +2 -2
- package/dist/scoops/index.js +82 -11
- package/dist/scoops/index.js.map +2 -2
- package/dist/scoops/requests.js +2 -1
- package/dist/scoops/requests.js.map +2 -2
- package/dist/scoops/service.js +45 -38
- package/dist/scoops/service.js.map +2 -2
- package/dist/scoops/sync.js +63 -13
- package/dist/scoops/sync.js.map +2 -2
- package/dist/skills/index.js +76 -34
- package/dist/skills/index.js.map +3 -3
- package/dist/sprinkles/index.js +114 -25
- package/dist/sprinkles/index.js.map +2 -2
- package/dist/sprinkles/sprinkles.js +13 -4
- package/dist/sprinkles/sprinkles.js.map +2 -2
- package/dist/types/freezer/index.d.ts +3 -1
- package/dist/types/freezer/title.d.ts +1 -1
- package/dist/types/gelatiere/index.d.ts +8 -0
- package/dist/types/gelatiere/store.d.ts +3 -2
- package/dist/types/index.d.ts +1 -1
- package/dist/types/kernel/accounts.d.ts +29 -0
- package/dist/types/kernel/agent-users.d.ts +37 -0
- package/dist/types/kernel/env.d.ts +6 -0
- package/dist/types/kernel/users.d.ts +15 -0
- package/dist/types/layout.d.ts +26 -0
- package/dist/types/licks/index.d.ts +2 -1
- package/dist/types/licks/sources.d.ts +11 -1
- package/dist/types/mcp/paths.d.ts +3 -0
- package/dist/types/mcp/runtime.d.ts +3 -3
- package/dist/types/memory/prompt.d.ts +1 -0
- package/dist/types/memory/runtime.d.ts +1 -0
- package/dist/types/roles/roles.d.ts +14 -1
- package/dist/types/scoops/cli.d.ts +5 -0
- package/dist/types/scoops/extension.d.ts +6 -3
- package/dist/types/scoops/index.d.ts +9 -0
- package/dist/types/scoops/requests.d.ts +2 -0
- package/dist/types/scoops/service.d.ts +5 -3
- package/dist/types/scoops/sync.d.ts +9 -6
- package/dist/types/skills/index.d.ts +10 -2
- package/dist/types/sprinkles/index.d.ts +15 -0
- package/dist/types/sprinkles/sprinkles.d.ts +7 -1
- package/package.json +4 -4
package/dist/layout.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { CONE_PREFIX, homeOf, SCOOP_USER, scoopUser } from "./kernel/accounts.js";
|
|
2
|
+
import { quote } from "./kernel/env.js";
|
|
3
|
+
const SCOOPS_ROOT = "/scoops";
|
|
4
|
+
const SESSIONS = { tmp: "/tmp/agent-sessions", durable: "/home/sessions" };
|
|
5
|
+
const ROOT_USER = "root";
|
|
6
|
+
const AGENT_DIR = "/etc/slicc/agent";
|
|
7
|
+
const ROOT_HOME = "/root";
|
|
8
|
+
const SHARED_DIR = "/var/lib/slicc/agent/shared";
|
|
9
|
+
const LEGACY = {
|
|
10
|
+
users: false,
|
|
11
|
+
scoopRoot: (folder) => `${SCOOPS_ROOT}/${folder}`,
|
|
12
|
+
workspace: (folder) => `${SCOOPS_ROOT}/${folder}/workspace`,
|
|
13
|
+
reports: (folder) => `${SCOOPS_ROOT}/${folder}/reports`,
|
|
14
|
+
sessions: (persist) => SESSIONS[persist],
|
|
15
|
+
homeOf: () => "/home",
|
|
16
|
+
envFor: async () => void 0,
|
|
17
|
+
ownDir: async () => void 0
|
|
18
|
+
};
|
|
19
|
+
function agentOfUser(state, user) {
|
|
20
|
+
if (user.startsWith(SCOOP_USER)) {
|
|
21
|
+
const folder = user.slice(SCOOP_USER.length);
|
|
22
|
+
return Object.entries(state.scoops).find(([, record]) => record.folder === folder)?.[0];
|
|
23
|
+
}
|
|
24
|
+
return state.cones[user] || Object.values(state.frozen ?? {}).some((f) => f.cone === user) ? `${CONE_PREFIX}${user}` : void 0;
|
|
25
|
+
}
|
|
26
|
+
function usersLayout(state, envOf, root) {
|
|
27
|
+
const homeOfUser = (user) => user && user !== ROOT_USER ? homeOf(user) : ROOT_HOME;
|
|
28
|
+
return {
|
|
29
|
+
users: true,
|
|
30
|
+
scoopRoot: (folder) => homeOf(scoopUser(folder)),
|
|
31
|
+
workspace: (folder) => homeOf(scoopUser(folder)),
|
|
32
|
+
reports: (folder, cone) => `${homeOf(cone)}/.slicc/reports/${folder}`,
|
|
33
|
+
sessions: (persist, user) => persist === "tmp" ? `${SESSIONS.tmp}/${user ?? ROOT_USER}` : `${homeOfUser(user)}/sessions`,
|
|
34
|
+
homeOf: homeOfUser,
|
|
35
|
+
async ownDir(path, user, context) {
|
|
36
|
+
if (!user || user === ROOT_USER) return;
|
|
37
|
+
const dir = quote(path);
|
|
38
|
+
await root.exec(
|
|
39
|
+
`mkdir -p ${dir} && chown ${quote(user)} ${dir} && chmod 700 ${dir}`,
|
|
40
|
+
void 0,
|
|
41
|
+
context
|
|
42
|
+
);
|
|
43
|
+
},
|
|
44
|
+
async envFor(user) {
|
|
45
|
+
if (!user || user === ROOT_USER) return void 0;
|
|
46
|
+
const agentId = agentOfUser(state(), user);
|
|
47
|
+
return agentId ? envOf(agentId, homeOf(user)) : void 0;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export {
|
|
52
|
+
AGENT_DIR,
|
|
53
|
+
LEGACY,
|
|
54
|
+
ROOT_HOME,
|
|
55
|
+
ROOT_USER,
|
|
56
|
+
SCOOPS_ROOT,
|
|
57
|
+
SESSIONS,
|
|
58
|
+
SHARED_DIR,
|
|
59
|
+
agentOfUser,
|
|
60
|
+
usersLayout
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=layout.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/layout.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport type { AgentsState } from './agents.ts';\nimport { CONE_PREFIX, homeOf, SCOOP_USER, scoopUser } from './kernel/accounts.ts';\nimport { quote } from './kernel/env.ts';\n\nexport const SCOOPS_ROOT = '/scoops';\nexport const SESSIONS = { tmp: '/tmp/agent-sessions', durable: '/home/sessions' };\nexport const ROOT_USER = 'root';\nexport const AGENT_DIR = '/etc/slicc/agent';\nexport const ROOT_HOME = '/root';\nexport const SHARED_DIR = '/var/lib/slicc/agent/shared';\n\nexport type Persist = 'tmp' | 'durable';\n\nexport interface Layout {\n readonly users: boolean;\n scoopRoot(folder: string): string;\n workspace(folder: string): string;\n reports(folder: string, cone: string): string;\n sessions(persist: Persist, user: string | null): string;\n homeOf(user: string | null): string;\n envFor(user: string | null, context: Context): Promise<ExecutionEnv | undefined>;\n ownDir(path: string, user: string | null, context: Context): Promise<void>;\n}\n\nexport const LEGACY: Layout = {\n users: false,\n scoopRoot: (folder) => `${SCOOPS_ROOT}/${folder}`,\n workspace: (folder) => `${SCOOPS_ROOT}/${folder}/workspace`,\n reports: (folder) => `${SCOOPS_ROOT}/${folder}/reports`,\n sessions: (persist) => SESSIONS[persist],\n homeOf: () => '/home',\n envFor: async () => undefined,\n ownDir: async () => undefined,\n};\n\nexport function agentOfUser(state: Readonly<AgentsState>, user: string): string | undefined {\n if (user.startsWith(SCOOP_USER)) {\n const folder = user.slice(SCOOP_USER.length);\n return Object.entries(state.scoops).find(([, record]) => record.folder === folder)?.[0];\n }\n return state.cones[user] || Object.values(state.frozen ?? {}).some((f) => f.cone === user)\n ? `${CONE_PREFIX}${user}`\n : undefined;\n}\n\nexport function usersLayout(\n state: () => Readonly<AgentsState>,\n envOf: (agentId: string, home: string) => Promise<ExecutionEnv>,\n root: Pick<ExecutionEnv, 'exec'>\n): Layout {\n const homeOfUser = (user: string | null) =>\n user && user !== ROOT_USER ? homeOf(user) : ROOT_HOME;\n return {\n users: true,\n scoopRoot: (folder) => homeOf(scoopUser(folder)),\n workspace: (folder) => homeOf(scoopUser(folder)),\n reports: (folder, cone) => `${homeOf(cone)}/.slicc/reports/${folder}`,\n sessions: (persist, user) =>\n persist === 'tmp' ? `${SESSIONS.tmp}/${user ?? ROOT_USER}` : `${homeOfUser(user)}/sessions`,\n homeOf: homeOfUser,\n async ownDir(path, user, context) {\n if (!user || user === ROOT_USER) return;\n const dir = quote(path);\n await root.exec(\n `mkdir -p ${dir} && chown ${quote(user)} ${dir} && chmod 700 ${dir}`,\n undefined,\n context\n );\n },\n async envFor(user) {\n if (!user || user === ROOT_USER) return undefined;\n const agentId = agentOfUser(state(), user);\n return agentId ? envOf(agentId, homeOf(user)) : undefined;\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "AAGA,SAAS,aAAa,QAAQ,YAAY,iBAAiB;AAC3D,SAAS,aAAa;AAEf,MAAM,cAAc;AACpB,MAAM,WAAW,EAAE,KAAK,uBAAuB,SAAS,iBAAiB;AACzE,MAAM,YAAY;AAClB,MAAM,YAAY;AAClB,MAAM,YAAY;AAClB,MAAM,aAAa;AAenB,MAAM,SAAiB;AAAA,EAC5B,OAAO;AAAA,EACP,WAAW,CAAC,WAAW,GAAG,WAAW,IAAI,MAAM;AAAA,EAC/C,WAAW,CAAC,WAAW,GAAG,WAAW,IAAI,MAAM;AAAA,EAC/C,SAAS,CAAC,WAAW,GAAG,WAAW,IAAI,MAAM;AAAA,EAC7C,UAAU,CAAC,YAAY,SAAS,OAAO;AAAA,EACvC,QAAQ,MAAM;AAAA,EACd,QAAQ,YAAY;AAAA,EACpB,QAAQ,YAAY;AACtB;AAEO,SAAS,YAAY,OAA8B,MAAkC;AAC1F,MAAI,KAAK,WAAW,UAAU,GAAG;AAC/B,UAAM,SAAS,KAAK,MAAM,WAAW,MAAM;AAC3C,WAAO,OAAO,QAAQ,MAAM,MAAM,EAAE,KAAK,CAAC,CAAC,EAAE,MAAM,MAAM,OAAO,WAAW,MAAM,IAAI,CAAC;AAAA,EACxF;AACA,SAAO,MAAM,MAAM,IAAI,KAAK,OAAO,OAAO,MAAM,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI,IACrF,GAAG,WAAW,GAAG,IAAI,KACrB;AACN;AAEO,SAAS,YACd,OACA,OACA,MACQ;AACR,QAAM,aAAa,CAAC,SAClB,QAAQ,SAAS,YAAY,OAAO,IAAI,IAAI;AAC9C,SAAO;AAAA,IACL,OAAO;AAAA,IACP,WAAW,CAAC,WAAW,OAAO,UAAU,MAAM,CAAC;AAAA,IAC/C,WAAW,CAAC,WAAW,OAAO,UAAU,MAAM,CAAC;AAAA,IAC/C,SAAS,CAAC,QAAQ,SAAS,GAAG,OAAO,IAAI,CAAC,mBAAmB,MAAM;AAAA,IACnE,UAAU,CAAC,SAAS,SAClB,YAAY,QAAQ,GAAG,SAAS,GAAG,IAAI,QAAQ,SAAS,KAAK,GAAG,WAAW,IAAI,CAAC;AAAA,IAClF,QAAQ;AAAA,IACR,MAAM,OAAO,MAAM,MAAM,SAAS;AAChC,UAAI,CAAC,QAAQ,SAAS,UAAW;AACjC,YAAM,MAAM,MAAM,IAAI;AACtB,YAAM,KAAK;AAAA,QACT,YAAY,GAAG,aAAa,MAAM,IAAI,CAAC,IAAI,GAAG,iBAAiB,GAAG;AAAA,QAClE;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IACA,MAAM,OAAO,MAAM;AACjB,UAAI,CAAC,QAAQ,SAAS,UAAW,QAAO;AACxC,YAAM,UAAU,YAAY,MAAM,GAAG,IAAI;AACzC,aAAO,UAAU,MAAM,SAAS,OAAO,IAAI,CAAC,IAAI;AAAA,IAClD;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/licks/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/licks/index.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { Conversation, ConversationId, Harness, Registry } from '@earendil-works/pi-durable';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport { type Agents, live } from '../agents.ts';\nimport type { Activity } from '../kernel/activity.ts';\nimport { cronTask, licksExtension } from './extension.ts';\nimport type { LickChannel, LickTarget } from './lick.ts';\nimport { createLicks, type Licks, type LicksHost } from './licks.ts';\nimport { createLickSources, type LickSources } from './sources.ts';\n\nexport interface LicksAgent {\n harness: Harness;\n root: Conversation;\n agents: Agents;\n cone(): Conversation;\n}\n\nconst UNTRUSTED = new Set<LickChannel>(['webhook']);\n\nexport function lickResolver(agent: LicksAgent) {\n return async (target: LickTarget, channel: LickChannel, context: Context) => {\n const cone = agent.cone();\n if (target === 'cone') return cone;\n if (target.startsWith('cone:'))\n return (await agent.agents.conversation(target.slice('cone:'.length), context)) ?? cone;\n if (UNTRUSTED.has(channel)) return cone;\n const scoop = agent.agents.state().scoops[target];\n if (!scoop || !live(scoop)) return cone;\n return (await agent.agents.conversation(target, context)) ?? cone;\n };\n}\n\nexport interface LicksSetup {\n licks: Licks;\n attach(\n agent: LicksAgent,\n options: {\n env: ExecutionEnv;\n home: string;\n now?: () => number;\n flushEvery?: number;\n activity?: Activity;\n }\n ): LickSources;\n}\n\nexport function setupLicks(registry: Registry): LicksSetup {\n let bind: (host: LicksHost) => void = () => undefined;\n const host = new Promise<LicksHost>((resolve) => {\n bind = resolve;\n });\n const licks = createLicks(host);\n const cron = cronTask(licks);\n const lookup = async (id: ConversationId, context: Context) => {\n const found = await (await host).harness.conversation(id, context);\n if (!found) throw new Error(`conversation ${id} is gone`);\n return found;\n };\n registry.install(licksExtension(licks, lookup, cron));\n return {\n licks,\n attach(agent, options) {\n const { harness, root } = agent;\n bind({ harness, resolve: lickResolver(agent) });\n return createLickSources({\n ...options,\n harness,\n root,\n cone: () => agent.cone(),\n licks,\n cron,\n });\n },\n };\n}\n\nexport type { Lick, LickChannel } from './lick.ts';\nexport type { LickEvent, Licks } from './licks.ts';\nexport type { LickSources, WebhookDelivery } from './sources.ts';\n"],
|
|
5
|
-
"mappings": "AAGA,SAAsB,YAAY;AAElC,SAAS,UAAU,sBAAsB;AAEzC,SAAS,mBAA+C;AACxD,SAAS,
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { Conversation, ConversationId, Harness, Registry } from '@earendil-works/pi-durable';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport { type Agents, live } from '../agents.ts';\nimport type { Activity } from '../kernel/activity.ts';\nimport { cronTask, licksExtension } from './extension.ts';\nimport type { LickChannel, LickTarget } from './lick.ts';\nimport { createLicks, type Licks, type LicksHost } from './licks.ts';\nimport { createLickSources, type LickDir, type LickSources } from './sources.ts';\n\nexport interface LicksAgent {\n harness: Harness;\n root: Conversation;\n agents: Agents;\n cone(): Conversation;\n}\n\nconst UNTRUSTED = new Set<LickChannel>(['webhook']);\n\nexport function lickResolver(agent: LicksAgent) {\n return async (target: LickTarget, channel: LickChannel, context: Context) => {\n const cone = agent.cone();\n if (target === 'cone') return cone;\n if (target.startsWith('cone:'))\n return (await agent.agents.conversation(target.slice('cone:'.length), context)) ?? cone;\n if (UNTRUSTED.has(channel)) return cone;\n const scoop = agent.agents.state().scoops[target];\n if (!scoop || !live(scoop)) return cone;\n return (await agent.agents.conversation(target, context)) ?? cone;\n };\n}\n\nexport interface LicksSetup {\n licks: Licks;\n attach(\n agent: LicksAgent,\n options: {\n env: ExecutionEnv;\n home: string;\n now?: () => number;\n flushEvery?: number;\n activity?: Activity;\n dirs?: () => Promise<readonly LickDir[]>;\n }\n ): LickSources;\n}\n\nexport function setupLicks(registry: Registry): LicksSetup {\n let bind: (host: LicksHost) => void = () => undefined;\n const host = new Promise<LicksHost>((resolve) => {\n bind = resolve;\n });\n const licks = createLicks(host);\n const cron = cronTask(licks);\n const lookup = async (id: ConversationId, context: Context) => {\n const found = await (await host).harness.conversation(id, context);\n if (!found) throw new Error(`conversation ${id} is gone`);\n return found;\n };\n registry.install(licksExtension(licks, lookup, cron));\n return {\n licks,\n attach(agent, options) {\n const { harness, root } = agent;\n bind({ harness, resolve: lickResolver(agent) });\n return createLickSources({\n ...options,\n harness,\n root,\n cone: () => agent.cone(),\n licks,\n cron,\n });\n },\n };\n}\n\nexport type { Lick, LickChannel } from './lick.ts';\nexport type { LickEvent, Licks } from './licks.ts';\nexport type { LickSources, WebhookDelivery } from './sources.ts';\n"],
|
|
5
|
+
"mappings": "AAGA,SAAsB,YAAY;AAElC,SAAS,UAAU,sBAAsB;AAEzC,SAAS,mBAA+C;AACxD,SAAS,yBAAyD;AASlE,MAAM,YAAY,oBAAI,IAAiB,CAAC,SAAS,CAAC;AAE3C,SAAS,aAAa,OAAmB;AAC9C,SAAO,OAAO,QAAoB,SAAsB,YAAqB;AAC3E,UAAM,OAAO,MAAM,KAAK;AACxB,QAAI,WAAW,OAAQ,QAAO;AAC9B,QAAI,OAAO,WAAW,OAAO;AAC3B,aAAQ,MAAM,MAAM,OAAO,aAAa,OAAO,MAAM,QAAQ,MAAM,GAAG,OAAO,KAAM;AACrF,QAAI,UAAU,IAAI,OAAO,EAAG,QAAO;AACnC,UAAM,QAAQ,MAAM,OAAO,MAAM,EAAE,OAAO,MAAM;AAChD,QAAI,CAAC,SAAS,CAAC,KAAK,KAAK,EAAG,QAAO;AACnC,WAAQ,MAAM,MAAM,OAAO,aAAa,QAAQ,OAAO,KAAM;AAAA,EAC/D;AACF;AAiBO,SAAS,WAAW,UAAgC;AACzD,MAAI,OAAkC,MAAM;AAC5C,QAAM,OAAO,IAAI,QAAmB,CAAC,YAAY;AAC/C,WAAO;AAAA,EACT,CAAC;AACD,QAAM,QAAQ,YAAY,IAAI;AAC9B,QAAM,OAAO,SAAS,KAAK;AAC3B,QAAM,SAAS,OAAO,IAAoB,YAAqB;AAC7D,UAAM,QAAQ,OAAO,MAAM,MAAM,QAAQ,aAAa,IAAI,OAAO;AACjE,QAAI,CAAC,MAAO,OAAM,IAAI,MAAM,gBAAgB,EAAE,UAAU;AACxD,WAAO;AAAA,EACT;AACA,WAAS,QAAQ,eAAe,OAAO,QAAQ,IAAI,CAAC;AACpD,SAAO;AAAA,IACL;AAAA,IACA,OAAO,OAAO,SAAS;AACrB,YAAM,EAAE,SAAS,KAAK,IAAI;AAC1B,WAAK,EAAE,SAAS,SAAS,aAAa,KAAK,EAAE,CAAC;AAC9C,aAAO,kBAAkB;AAAA,QACvB,GAAG;AAAA,QACH;AAAA,QACA;AAAA,QACA,MAAM,MAAM,MAAM,KAAK;AAAA,QACvB;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/licks/sources.js
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import { bootLicks } from "./boot.js";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
parseCrontab,
|
|
4
|
+
parseWebhook
|
|
5
|
+
} from "./config.js";
|
|
3
6
|
import { configFiles, Watches } from "./fswatch.js";
|
|
4
7
|
import { reconcileCron } from "./schedules.js";
|
|
5
8
|
const SLICC_DIR = ".slicc";
|
|
9
|
+
function scopedTarget(target, cone) {
|
|
10
|
+
return cone && target === "cone" ? `cone:${cone}` : target;
|
|
11
|
+
}
|
|
12
|
+
function scopedName(name, cone) {
|
|
13
|
+
return cone ? `${cone}:${name}` : name;
|
|
14
|
+
}
|
|
15
|
+
function scoped(entry, cone) {
|
|
16
|
+
return { ...entry, name: scopedName(entry.name, cone), target: scopedTarget(entry.target, cone) };
|
|
17
|
+
}
|
|
6
18
|
function hash(text) {
|
|
7
19
|
let value = 2166136261;
|
|
8
20
|
for (let at = 0; at < text.length; at++) value = Math.imul(value ^ text.charCodeAt(at), 16777619);
|
|
@@ -11,7 +23,7 @@ function hash(text) {
|
|
|
11
23
|
function shortPath(path, home) {
|
|
12
24
|
return path === home || path.startsWith(`${home}/`) ? `~${path.slice(home.length)}` : path;
|
|
13
25
|
}
|
|
14
|
-
function configLick(error, home) {
|
|
26
|
+
function configLick(error, home, cone = null) {
|
|
15
27
|
const file = shortPath(error.file, home);
|
|
16
28
|
const channel = error.file.includes("/watches/") ? "fswatch" : error.file.includes("/webhooks/") ? "webhook" : "cron";
|
|
17
29
|
const text = `${error.line ? `line ${error.line}: ` : ""}${error.error}`;
|
|
@@ -21,7 +33,7 @@ function configLick(error, home) {
|
|
|
21
33
|
title: `Invalid ${file}`,
|
|
22
34
|
text,
|
|
23
35
|
body: "Ignored until the file is fixed.\nThe formats are in the slicc-agent README.",
|
|
24
|
-
target: "cone",
|
|
36
|
+
target: scopedTarget("cone", cone),
|
|
25
37
|
severity: "error",
|
|
26
38
|
eventId: hash(`${file}
|
|
27
39
|
${text}`)
|
|
@@ -60,13 +72,23 @@ async function webhooksIn(files, dir, context) {
|
|
|
60
72
|
}
|
|
61
73
|
function createLickSources(options) {
|
|
62
74
|
const { harness, env, licks, home } = options;
|
|
63
|
-
const dir = `${home}/${SLICC_DIR}`;
|
|
64
75
|
const now = options.now ?? Date.now;
|
|
65
76
|
const files = configFiles(env);
|
|
66
77
|
const deliver = (event, context) => licks.deliver(event, context);
|
|
67
|
-
const
|
|
78
|
+
const dirsOf = options.dirs ?? (() => [{ dir: `${home}/${SLICC_DIR}`, home, cone: null }]);
|
|
79
|
+
const watching = /* @__PURE__ */ new Map();
|
|
80
|
+
const watchesOf = (place) => {
|
|
81
|
+
let found = watching.get(place.dir);
|
|
82
|
+
if (!found) {
|
|
83
|
+
const retarget = (event, context) => deliver({ ...event, target: scopedTarget(event.target, place.cone) }, context);
|
|
84
|
+
found = new Watches(place.env ?? env, retarget, place.home, options.activity);
|
|
85
|
+
watching.set(place.dir, found);
|
|
86
|
+
}
|
|
87
|
+
return found;
|
|
88
|
+
};
|
|
68
89
|
let webhooks = /* @__PURE__ */ new Map();
|
|
69
90
|
let control;
|
|
91
|
+
let controlled = "";
|
|
70
92
|
let reconcileTimer;
|
|
71
93
|
let flushTimer;
|
|
72
94
|
let closed = false;
|
|
@@ -78,14 +100,45 @@ function createLickSources(options) {
|
|
|
78
100
|
};
|
|
79
101
|
async function reconcileNow(context) {
|
|
80
102
|
if (closed) return;
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
const
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
103
|
+
const places = await dirsOf();
|
|
104
|
+
const entries = [];
|
|
105
|
+
const errors = [];
|
|
106
|
+
const hooks = /* @__PURE__ */ new Map();
|
|
107
|
+
for (const place of places) {
|
|
108
|
+
const crontabFile = `${place.dir}/crontab`;
|
|
109
|
+
const crontab = parseCrontab(crontabFile, await files.read(crontabFile, context) ?? "");
|
|
110
|
+
entries.push(...crontab.entries.map((entry) => scoped(entry, place.cone)));
|
|
111
|
+
const watchErrors = await watchesOf(place).reconcile(`${place.dir}/watches`, context);
|
|
112
|
+
const found = await webhooksIn(files, `${place.dir}/webhooks`, context);
|
|
113
|
+
for (const entry of found.entries.values()) {
|
|
114
|
+
const named = scoped(entry, place.cone);
|
|
115
|
+
hooks.set(named.name, named);
|
|
116
|
+
}
|
|
117
|
+
for (const error of [...crontab.errors, ...watchErrors, ...found.errors])
|
|
118
|
+
errors.push({ error, place });
|
|
119
|
+
}
|
|
120
|
+
for (const [dir, watches] of [...watching])
|
|
121
|
+
if (!places.some((place) => place.dir === dir)) {
|
|
122
|
+
watching.delete(dir);
|
|
123
|
+
await watches.close(context);
|
|
124
|
+
}
|
|
125
|
+
await reconcileCron({ ...options, now }, entries, context);
|
|
126
|
+
webhooks = hooks;
|
|
127
|
+
for (const { error, place } of errors)
|
|
128
|
+
await deliver(configLick(error, place.home, place.cone), context);
|
|
129
|
+
await watchControl(places, context);
|
|
130
|
+
}
|
|
131
|
+
async function watchControl(places, context) {
|
|
132
|
+
const key = places.map((place) => place.dir).join("\n");
|
|
133
|
+
if (key === controlled || closed) return;
|
|
134
|
+
controlled = key;
|
|
135
|
+
await control?.close(context);
|
|
136
|
+
const result = await env.watch(
|
|
137
|
+
places.map((place) => ({ path: place.dir, recursive: true })),
|
|
138
|
+
() => scheduleReconcile(context),
|
|
139
|
+
context
|
|
140
|
+
);
|
|
141
|
+
control = result.ok ? result.value : void 0;
|
|
89
142
|
}
|
|
90
143
|
const reconcile = (context) => serial(() => reconcileNow(context));
|
|
91
144
|
function scheduleReconcile(context) {
|
|
@@ -101,12 +154,6 @@ function createLickSources(options) {
|
|
|
101
154
|
async start(context) {
|
|
102
155
|
await licks.flush(context);
|
|
103
156
|
await reconcile(context);
|
|
104
|
-
const result = await env.watch(
|
|
105
|
-
[{ path: dir, recursive: true }],
|
|
106
|
-
() => scheduleReconcile(context),
|
|
107
|
-
context
|
|
108
|
-
);
|
|
109
|
-
if (result.ok) control = result.value;
|
|
110
157
|
flushTimer = setInterval(() => {
|
|
111
158
|
void flushIfWaiting(context).catch(() => void 0);
|
|
112
159
|
}, options.flushEvery ?? 2e3);
|
|
@@ -124,7 +171,7 @@ function createLickSources(options) {
|
|
|
124
171
|
clearTimeout(reconcileTimer);
|
|
125
172
|
clearInterval(flushTimer);
|
|
126
173
|
await control?.close(context);
|
|
127
|
-
await watches.close(context);
|
|
174
|
+
for (const watches of watching.values()) await watches.close(context);
|
|
128
175
|
}
|
|
129
176
|
};
|
|
130
177
|
}
|
|
@@ -133,6 +180,8 @@ export {
|
|
|
133
180
|
configLick,
|
|
134
181
|
createLickSources,
|
|
135
182
|
hash,
|
|
183
|
+
scopedName,
|
|
184
|
+
scopedTarget,
|
|
136
185
|
shortPath,
|
|
137
186
|
webhookLick
|
|
138
187
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/licks/sources.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { Conversation, Harness } from '@earendil-works/pi-durable';\nimport type { ExecutionEnv, FileWatcher } from '@earendil-works/pi-durable/env';\nimport type { Activity } from '../kernel/activity.ts';\nimport { type BootFacts, bootLicks } from './boot.ts';\nimport {
|
|
5
|
-
"mappings": "AAIA,SAAyB,iBAAiB;AAC1C,
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { Conversation, Harness } from '@earendil-works/pi-durable';\nimport type { ExecutionEnv, FileWatcher } from '@earendil-works/pi-durable/env';\nimport type { Activity } from '../kernel/activity.ts';\nimport { type BootFacts, bootLicks } from './boot.ts';\nimport {\n type ConfigError,\n type CronEntry,\n parseCrontab,\n parseWebhook,\n type WebhookEntry,\n} from './config.ts';\nimport type { cronTask } from './extension.ts';\nimport { type ConfigFiles, configFiles, Watches } from './fswatch.ts';\nimport type { LickChannel, LickTarget } from './lick.ts';\nimport type { LickEvent, Licks } from './licks.ts';\nimport { reconcileCron } from './schedules.ts';\n\nexport const SLICC_DIR = '.slicc';\n\nexport interface LickSourcesOptions {\n harness: Harness;\n root: Conversation;\n cone: () => Conversation;\n env: ExecutionEnv;\n home: string;\n licks: Licks;\n cron: ReturnType<typeof cronTask>;\n now?: () => number;\n flushEvery?: number;\n activity?: Activity;\n dirs?: () => readonly LickDir[] | Promise<readonly LickDir[]>;\n}\n\nexport interface LickDir {\n dir: string;\n home: string;\n cone: string | null;\n env?: ExecutionEnv;\n}\n\nexport function scopedTarget(target: LickTarget, cone: string | null): LickTarget {\n return cone && target === 'cone' ? (`cone:${cone}` as LickTarget) : target;\n}\n\nexport function scopedName(name: string, cone: string | null): string {\n return cone ? `${cone}:${name}` : name;\n}\n\nfunction scoped<T extends { name: string; target: LickTarget }>(entry: T, cone: string | null): T {\n return { ...entry, name: scopedName(entry.name, cone), target: scopedTarget(entry.target, cone) };\n}\n\nexport interface WebhookDelivery {\n id?: string;\n headers?: Record<string, string>;\n body: unknown;\n}\n\nexport interface LickSources {\n start(context: Context): Promise<void>;\n reconcile(context: Context): Promise<void>;\n boot(facts: BootFacts, context: Context): Promise<void>;\n webhook(name: string, delivery: WebhookDelivery, context: Context): Promise<boolean>;\n close(context: Context): Promise<void>;\n}\n\nexport function hash(text: string): string {\n let value = 2166136261;\n for (let at = 0; at < text.length; at++) value = Math.imul(value ^ text.charCodeAt(at), 16777619);\n return (value >>> 0).toString(36);\n}\n\nexport function shortPath(path: string, home: string): string {\n return path === home || path.startsWith(`${home}/`) ? `~${path.slice(home.length)}` : path;\n}\n\nexport function configLick(\n error: ConfigError,\n home: string,\n cone: string | null = null\n): LickEvent {\n const file = shortPath(error.file, home);\n const channel: LickChannel = error.file.includes('/watches/')\n ? 'fswatch'\n : error.file.includes('/webhooks/')\n ? 'webhook'\n : 'cron';\n const text = `${error.line ? `line ${error.line}: ` : ''}${error.error}`;\n return {\n channel,\n source: `config:${file}`,\n title: `Invalid ${file}`,\n text,\n body: 'Ignored until the file is fixed.\\nThe formats are in the slicc-agent README.',\n target: scopedTarget('cone', cone),\n severity: 'error',\n eventId: hash(`${file}\\n${text}`),\n };\n}\n\nexport function webhookLick(\n name: string,\n entry: WebhookEntry,\n delivery: WebhookDelivery\n): LickEvent {\n const payload =\n typeof delivery.body === 'string' ? delivery.body : JSON.stringify(delivery.body, null, 2);\n const headers = Object.entries(delivery.headers ?? {})\n .map(([key, value]) => `${key}: ${value}`)\n .join('\\n');\n return {\n channel: 'webhook',\n source: name,\n title: name,\n text: entry.message || `Webhook ${name} received a request`,\n body: [headers, payload].filter(Boolean).join('\\n\\n'),\n target: entry.target,\n coalesce: false,\n ...(delivery.id ? { eventId: delivery.id } : {}),\n ...(entry.triage ? { triage: entry.triage } : {}),\n };\n}\n\nasync function webhooksIn(\n files: ConfigFiles,\n dir: string,\n context: Context\n): Promise<{ entries: Map<string, WebhookEntry>; errors: ConfigError[] }> {\n const errors: ConfigError[] = [];\n const into = new Map<string, WebhookEntry>();\n for (const name of await files.list(dir, context)) {\n const file = `${dir}/${name}`;\n const json = await files.read(file, context);\n if (json === undefined) continue;\n try {\n const entry = parseWebhook(file, json);\n into.set(entry.name, entry);\n } catch (error) {\n errors.push({ file, error: (error as Error).message });\n }\n }\n return { entries: into, errors };\n}\n\nexport function createLickSources(options: LickSourcesOptions): LickSources {\n const { harness, env, licks, home } = options;\n const now = options.now ?? Date.now;\n const files = configFiles(env);\n const deliver = (event: LickEvent, context: Context) => licks.deliver(event, context);\n const dirsOf = options.dirs ?? (() => [{ dir: `${home}/${SLICC_DIR}`, home, cone: null }]);\n const watching = new Map<string, Watches>();\n const watchesOf = (place: LickDir) => {\n let found = watching.get(place.dir);\n if (!found) {\n const retarget = (event: LickEvent, context: Context) =>\n deliver({ ...event, target: scopedTarget(event.target, place.cone) }, context);\n found = new Watches(place.env ?? env, retarget, place.home, options.activity);\n watching.set(place.dir, found);\n }\n return found;\n };\n let webhooks = new Map<string, WebhookEntry>();\n let control: FileWatcher | undefined;\n let controlled = '';\n let reconcileTimer: ReturnType<typeof setTimeout> | undefined;\n let flushTimer: ReturnType<typeof setInterval> | undefined;\n let closed = false;\n let lock: Promise<unknown> = Promise.resolve();\n const serial = <T>(operation: () => Promise<T>): Promise<T> => {\n const run = lock.then(operation, operation);\n lock = run.catch(() => undefined);\n return run;\n };\n\n async function reconcileNow(context: Context): Promise<void> {\n if (closed) return;\n const places = await dirsOf();\n const entries: CronEntry[] = [];\n const errors: { error: ConfigError; place: LickDir }[] = [];\n const hooks = new Map<string, WebhookEntry>();\n for (const place of places) {\n const crontabFile = `${place.dir}/crontab`;\n const crontab = parseCrontab(crontabFile, (await files.read(crontabFile, context)) ?? '');\n entries.push(...crontab.entries.map((entry) => scoped(entry, place.cone)));\n const watchErrors = await watchesOf(place).reconcile(`${place.dir}/watches`, context);\n const found = await webhooksIn(files, `${place.dir}/webhooks`, context);\n for (const entry of found.entries.values()) {\n const named = scoped(entry, place.cone);\n hooks.set(named.name, named);\n }\n for (const error of [...crontab.errors, ...watchErrors, ...found.errors])\n errors.push({ error, place });\n }\n for (const [dir, watches] of [...watching])\n if (!places.some((place) => place.dir === dir)) {\n watching.delete(dir);\n await watches.close(context);\n }\n await reconcileCron({ ...options, now }, entries, context);\n webhooks = hooks;\n for (const { error, place } of errors)\n await deliver(configLick(error, place.home, place.cone), context);\n await watchControl(places, context);\n }\n\n async function watchControl(places: readonly LickDir[], context: Context): Promise<void> {\n const key = places.map((place) => place.dir).join('\\n');\n if (key === controlled || closed) return;\n controlled = key;\n await control?.close(context);\n const result = await env.watch(\n places.map((place) => ({ path: place.dir, recursive: true })),\n () => scheduleReconcile(context),\n context\n );\n control = result.ok ? result.value : undefined;\n }\n\n const reconcile = (context: Context) => serial(() => reconcileNow(context));\n\n function scheduleReconcile(context: Context): void {\n clearTimeout(reconcileTimer);\n reconcileTimer = setTimeout(() => {\n void reconcile(context).catch(() => undefined);\n }, 300);\n }\n\n async function flushIfWaiting(context: Context): Promise<void> {\n if (await licks.pending(context)) await licks.flush(context);\n }\n\n return {\n async start(context) {\n await licks.flush(context);\n await reconcile(context);\n flushTimer = setInterval(() => {\n void flushIfWaiting(context).catch(() => undefined);\n }, options.flushEvery ?? 2000);\n },\n reconcile,\n boot: (facts, context) => bootLicks(harness, options.cone(), deliver, facts, context),\n async webhook(name, delivery, context) {\n const entry = webhooks.get(name);\n if (!entry) return false;\n await deliver(webhookLick(name, entry, delivery), context);\n return true;\n },\n async close(context) {\n closed = true;\n clearTimeout(reconcileTimer);\n clearInterval(flushTimer);\n await control?.close(context);\n for (const watches of watching.values()) await watches.close(context);\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/main.js
CHANGED
|
@@ -21,9 +21,11 @@ import { lateGelatiere } from "./gelatiere/index.js";
|
|
|
21
21
|
import { hostAgent } from "./host.js";
|
|
22
22
|
import { imageReads } from "./images/index.js";
|
|
23
23
|
import { createActivity } from "./kernel/activity.js";
|
|
24
|
+
import { agentUsers, warnSupport } from "./kernel/agent-users.js";
|
|
24
25
|
import { HOME, kernelEnvironment, SliccKernelEnv } from "./kernel/env.js";
|
|
25
26
|
import { processGroups } from "./kernel/groups.js";
|
|
26
27
|
import { adminOver, userClients } from "./kernel/users.js";
|
|
28
|
+
import { AGENT_DIR, ROOT_HOME } from "./layout.js";
|
|
27
29
|
import { setupLicks } from "./licks/index.js";
|
|
28
30
|
import { lickTriage } from "./licks/triage.js";
|
|
29
31
|
import { setupMcp } from "./mcp/index.js";
|
|
@@ -68,13 +70,17 @@ async function attached(ports, options) {
|
|
|
68
70
|
const attach = options.attach ?? attachKernel;
|
|
69
71
|
const { kernel, admin } = await ports;
|
|
70
72
|
const client = await attach(kernel);
|
|
71
|
-
|
|
73
|
+
const users = usersOf(admin, options.users, attach);
|
|
74
|
+
await warnSupport(users, client, options.users);
|
|
75
|
+
const home = new SliccKernelEnv(client, { cwd: HOME });
|
|
76
|
+
await privateStore(home);
|
|
77
|
+
return { client, users, home, at: { home: HOME, ...users ? { agentDir: AGENT_DIR } : {} } };
|
|
72
78
|
}
|
|
73
79
|
function privateStore(env) {
|
|
74
80
|
return env.exec(`mkdir -p ${STORE_DIR} && chmod 700 ${STORE_DIR}`, void 0, BACKGROUND_CONTEXT);
|
|
75
81
|
}
|
|
76
|
-
async function codemodeTools(client, options, native, extra) {
|
|
77
|
-
const settings = await client.fs.readFile(`${
|
|
82
|
+
async function codemodeTools(client, options, native, extra, dir = `${HOME}/.pi/agent`) {
|
|
83
|
+
const settings = await client.fs.readFile(`${dir}/settings.json`).catch(() => void 0);
|
|
78
84
|
if (disabledBySettings(settings === void 0 ? void 0 : new TextDecoder().decode(settings)))
|
|
79
85
|
return [];
|
|
80
86
|
const sandbox = sandboxFactory(
|
|
@@ -104,42 +110,50 @@ async function secretsFor(home, vault) {
|
|
|
104
110
|
await names.refresh(BACKGROUND_CONTEXT);
|
|
105
111
|
return names;
|
|
106
112
|
}
|
|
113
|
+
async function modelsOf(options) {
|
|
114
|
+
const credentials = await (options.credentials ?? (() => EncryptedCredentialStore.open()))();
|
|
115
|
+
const providers = options.providers ?? sliccProviders();
|
|
116
|
+
return { credentials, providers, models: createSliccModels(credentials, providers) };
|
|
117
|
+
}
|
|
118
|
+
function baseRegistry(facts) {
|
|
119
|
+
const registry = createRegistry();
|
|
120
|
+
registry.install(CodingTools);
|
|
121
|
+
registry.install(sliccPrompt(facts));
|
|
122
|
+
const read = CodingTools.tools.find(
|
|
123
|
+
(tool) => tool.name === "read"
|
|
124
|
+
);
|
|
125
|
+
registry.install(imageReads(read));
|
|
126
|
+
return registry;
|
|
127
|
+
}
|
|
128
|
+
async function factsOf(client, native) {
|
|
129
|
+
return {
|
|
130
|
+
version: await agentVersion((url) => native(url)),
|
|
131
|
+
commands: await commandsOnPath(client),
|
|
132
|
+
transport: transportName(client.transport),
|
|
133
|
+
browser: await browserConnected(client)
|
|
134
|
+
};
|
|
135
|
+
}
|
|
107
136
|
function routeFetch(scope, client) {
|
|
108
137
|
const native = scope.fetch.bind(scope);
|
|
109
138
|
const loopback = client.loopbackFetch?.bind(client);
|
|
110
139
|
scope.fetch = transportFetch(client.transport, scope.location.origin, native, loopback);
|
|
111
140
|
return native;
|
|
112
141
|
}
|
|
142
|
+
async function prepare(scope, kernel, options) {
|
|
143
|
+
const names = await secretsFor(kernel.home, kernel.vault);
|
|
144
|
+
const native = routeFetch(scope, kernel.client);
|
|
145
|
+
const facts = await factsOf(kernel.client, native);
|
|
146
|
+
return { names, native, facts, ...await modelsOf(options), registry: baseRegistry(facts) };
|
|
147
|
+
}
|
|
113
148
|
async function start(scope, ports, options, vault) {
|
|
114
|
-
const { client, users } = await attached(ports, options);
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
const names = await secretsFor(home, vault);
|
|
118
|
-
const native = routeFetch(scope, client);
|
|
119
|
-
const facts = {
|
|
120
|
-
version: await agentVersion((url) => native(url)),
|
|
121
|
-
commands: await commandsOnPath(client),
|
|
122
|
-
transport: transportName(client.transport),
|
|
123
|
-
browser: await browserConnected(client)
|
|
124
|
-
};
|
|
125
|
-
const credentials = await (options.credentials ?? (() => EncryptedCredentialStore.open()))();
|
|
126
|
-
const providers = options.providers ?? sliccProviders();
|
|
127
|
-
const models = createSliccModels(credentials, providers);
|
|
128
|
-
const registry = createRegistry();
|
|
129
|
-
registry.install(CodingTools);
|
|
130
|
-
registry.install(sliccPrompt(facts));
|
|
131
|
-
registry.install(
|
|
132
|
-
imageReads(
|
|
133
|
-
CodingTools.tools.find(
|
|
134
|
-
(tool) => tool.name === "read"
|
|
135
|
-
)
|
|
136
|
-
)
|
|
137
|
-
);
|
|
149
|
+
const { client, users, home, at } = await attached(ports, options);
|
|
150
|
+
const ready = await prepare(scope, { client, home, vault }, options);
|
|
151
|
+
const { names, native, facts, credentials, providers, models, registry } = ready;
|
|
138
152
|
const memory = setupMemory(registry);
|
|
139
153
|
const coding = CodingTools.tools;
|
|
140
154
|
registry.install(browserGuard([scope.location.origin], coding));
|
|
141
155
|
const mcp = setupMcp(registry);
|
|
142
|
-
const codemode = await codemodeTools(client, options, native, mcp.codemode);
|
|
156
|
+
const codemode = await codemodeTools(client, options, native, mcp.codemode, at.agentDir);
|
|
143
157
|
registry.install(codemodeExtension(codemode));
|
|
144
158
|
const licks = setupLicks(registry);
|
|
145
159
|
licks.licks.triage(lickTriage(models));
|
|
@@ -150,29 +164,43 @@ async function start(scope, ports, options, vault) {
|
|
|
150
164
|
const groups = processGroups(client);
|
|
151
165
|
const model = options.model ?? DEFAULT_MODEL;
|
|
152
166
|
let opened;
|
|
167
|
+
const people = users ? agentUsers(users, client, home, () => opened?.agents) : void 0;
|
|
153
168
|
const environment = kernelEnvironment(client, {
|
|
154
169
|
activity,
|
|
155
170
|
groups,
|
|
156
171
|
identify: identity(() => opened?.agents, model),
|
|
157
172
|
scrub: vault,
|
|
158
|
-
spawn: shellGrants(names)
|
|
173
|
+
spawn: shellGrants(names),
|
|
174
|
+
...people ? { route: people.route } : {}
|
|
159
175
|
});
|
|
160
176
|
const agent = await openRepinned(options, { models, model, registry, env: environment });
|
|
161
177
|
opened = agent;
|
|
162
|
-
|
|
178
|
+
await people?.start(agent.agents);
|
|
179
|
+
const sources = licks.attach(agent, {
|
|
180
|
+
env: home,
|
|
181
|
+
home: HOME,
|
|
182
|
+
activity,
|
|
183
|
+
...people ? { dirs: () => people.lickDirs() } : {}
|
|
184
|
+
});
|
|
163
185
|
await sources.start(BACKGROUND_CONTEXT);
|
|
164
186
|
await sources.boot(
|
|
165
187
|
{ version: facts.version, boot: await kernelBoot(client) },
|
|
166
188
|
BACKGROUND_CONTEXT
|
|
167
189
|
);
|
|
168
190
|
const skillsRuntime = await skills.attach(
|
|
169
|
-
{
|
|
191
|
+
{
|
|
192
|
+
env: home,
|
|
193
|
+
...at,
|
|
194
|
+
assets: options.assets ?? packageAssets(),
|
|
195
|
+
reloadMs: RELOAD_MS,
|
|
196
|
+
...people ? { people: people.people } : {}
|
|
197
|
+
},
|
|
170
198
|
BACKGROUND_CONTEXT
|
|
171
199
|
);
|
|
172
200
|
const mcpRuntime = await mcp.attach(
|
|
173
201
|
{
|
|
174
202
|
env: home,
|
|
175
|
-
|
|
203
|
+
...at,
|
|
176
204
|
credentials,
|
|
177
205
|
licks: licks.licks,
|
|
178
206
|
fetch: scope.fetch,
|
|
@@ -193,17 +221,20 @@ async function start(scope, ports, options, vault) {
|
|
|
193
221
|
licks: licks.licks,
|
|
194
222
|
env: home,
|
|
195
223
|
assets: options.assets ?? packageAssets(),
|
|
196
|
-
reloadMs: RELOAD_MS
|
|
224
|
+
reloadMs: RELOAD_MS,
|
|
225
|
+
...people?.sprinkles() ?? {}
|
|
197
226
|
},
|
|
198
227
|
BACKGROUND_CONTEXT
|
|
199
228
|
);
|
|
229
|
+
people?.follow([sources, skillsRuntime, sprinkles]);
|
|
200
230
|
let roles;
|
|
201
231
|
const memoryRuntime = await memory.attach(
|
|
202
232
|
{
|
|
203
233
|
harness: agent.harness,
|
|
204
234
|
agents: agent.agents,
|
|
205
235
|
env: home,
|
|
206
|
-
|
|
236
|
+
...at,
|
|
237
|
+
...people ? { home: ROOT_HOME } : {},
|
|
207
238
|
reloadMs: RELOAD_MS,
|
|
208
239
|
roles: async (using) => roles?.roles(using)
|
|
209
240
|
},
|
|
@@ -214,7 +245,8 @@ async function start(scope, ports, options, vault) {
|
|
|
214
245
|
agents: agent.agents,
|
|
215
246
|
env: home,
|
|
216
247
|
extract: memory.extract,
|
|
217
|
-
title: titler(models, settingsText(home, HOME))
|
|
248
|
+
title: titler(models, settingsText(home, HOME, at.agentDir)),
|
|
249
|
+
...people ? { layout: people.layout } : {}
|
|
218
250
|
});
|
|
219
251
|
licks.licks.guard(freezer.frozenTarget);
|
|
220
252
|
const settings = await createAgentSettings(models, credentials, providers, { mcp: mcpRuntime });
|
|
@@ -225,7 +257,8 @@ async function start(scope, ports, options, vault) {
|
|
|
225
257
|
agents: agent.agents,
|
|
226
258
|
groups,
|
|
227
259
|
env: home,
|
|
228
|
-
|
|
260
|
+
...at,
|
|
261
|
+
...people ? { layout: people.layout, spools: people.spools, enforced: people.enforced } : {},
|
|
229
262
|
alive: alive(client),
|
|
230
263
|
reads: skillsRuntime.dirs,
|
|
231
264
|
memory: memoryRuntime.command,
|
|
@@ -244,6 +277,7 @@ async function start(scope, ports, options, vault) {
|
|
|
244
277
|
licks: licks.licks,
|
|
245
278
|
env: home,
|
|
246
279
|
home: HOME,
|
|
280
|
+
...await people?.gelatiere(home),
|
|
247
281
|
scoops: scoops.scoops,
|
|
248
282
|
roles: runtime.roles,
|
|
249
283
|
assets: options.assets ?? packageAssets(),
|