spawnfile 0.1.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.
Files changed (177) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +464 -0
  3. package/dist/.env.example +5 -0
  4. package/dist/Dockerfile +21 -0
  5. package/dist/auth/importers.d.ts +8 -0
  6. package/dist/auth/importers.js +93 -0
  7. package/dist/auth/index.d.ts +5 -0
  8. package/dist/auth/index.js +5 -0
  9. package/dist/auth/paths.d.ts +6 -0
  10. package/dist/auth/paths.js +18 -0
  11. package/dist/auth/profileStore.d.ts +10 -0
  12. package/dist/auth/profileStore.js +125 -0
  13. package/dist/auth/runtimeCredentials.d.ts +14 -0
  14. package/dist/auth/runtimeCredentials.js +76 -0
  15. package/dist/auth/types.d.ts +22 -0
  16. package/dist/auth/types.js +1 -0
  17. package/dist/cli/index.d.ts +2 -0
  18. package/dist/cli/index.js +4 -0
  19. package/dist/cli/runCli.d.ts +27 -0
  20. package/dist/cli/runCli.js +314 -0
  21. package/dist/compiler/addProjectNode.d.ts +21 -0
  22. package/dist/compiler/addProjectNode.js +126 -0
  23. package/dist/compiler/agentSurfaces.d.ts +4 -0
  24. package/dist/compiler/agentSurfaces.js +70 -0
  25. package/dist/compiler/buildCompilePlan.d.ts +2 -0
  26. package/dist/compiler/buildCompilePlan.js +258 -0
  27. package/dist/compiler/buildProject.d.ts +20 -0
  28. package/dist/compiler/buildProject.js +52 -0
  29. package/dist/compiler/compilePlanHelpers.d.ts +7 -0
  30. package/dist/compiler/compilePlanHelpers.js +39 -0
  31. package/dist/compiler/compileProject.d.ts +11 -0
  32. package/dist/compiler/compileProject.js +182 -0
  33. package/dist/compiler/containerArtifacts.d.ts +4 -0
  34. package/dist/compiler/containerArtifacts.js +64 -0
  35. package/dist/compiler/containerArtifactsPlans.d.ts +4 -0
  36. package/dist/compiler/containerArtifactsPlans.js +154 -0
  37. package/dist/compiler/containerArtifactsRender.d.ts +6 -0
  38. package/dist/compiler/containerArtifactsRender.js +237 -0
  39. package/dist/compiler/containerArtifactsTypes.d.ts +42 -0
  40. package/dist/compiler/containerArtifactsTypes.js +1 -0
  41. package/dist/compiler/discordSurface.d.ts +4 -0
  42. package/dist/compiler/discordSurface.js +28 -0
  43. package/dist/compiler/executionDefaults.d.ts +2 -0
  44. package/dist/compiler/executionDefaults.js +9 -0
  45. package/dist/compiler/helpers.d.ts +7 -0
  46. package/dist/compiler/helpers.js +35 -0
  47. package/dist/compiler/index.d.ts +9 -0
  48. package/dist/compiler/index.js +9 -0
  49. package/dist/compiler/initProject.d.ts +9 -0
  50. package/dist/compiler/initProject.js +46 -0
  51. package/dist/compiler/modelAuth.d.ts +2 -0
  52. package/dist/compiler/modelAuth.js +17 -0
  53. package/dist/compiler/modelEnv.d.ts +10 -0
  54. package/dist/compiler/modelEnv.js +97 -0
  55. package/dist/compiler/runProject.d.ts +34 -0
  56. package/dist/compiler/runProject.js +197 -0
  57. package/dist/compiler/runProjectAuth.d.ts +9 -0
  58. package/dist/compiler/runProjectAuth.js +59 -0
  59. package/dist/compiler/surfaceSupport.d.ts +2 -0
  60. package/dist/compiler/surfaceSupport.js +13 -0
  61. package/dist/compiler/surfaces.d.ts +21 -0
  62. package/dist/compiler/surfaces.js +59 -0
  63. package/dist/compiler/syncProjectAuth.d.ts +7 -0
  64. package/dist/compiler/syncProjectAuth.js +65 -0
  65. package/dist/compiler/types.d.ts +134 -0
  66. package/dist/compiler/types.js +1 -0
  67. package/dist/compiler/updateProjectModels.d.ts +20 -0
  68. package/dist/compiler/updateProjectModels.js +181 -0
  69. package/dist/container/rootfs/var/lib/spawnfile/instances/picoclaw/agent-assistant/picoclaw/config.json +16 -0
  70. package/dist/container/rootfs/var/lib/spawnfile/instances/picoclaw/agent-assistant/picoclaw/workspace/AGENTS.md +1 -0
  71. package/dist/e2e/cli.d.ts +1 -0
  72. package/dist/e2e/cli.js +40 -0
  73. package/dist/e2e/dockerAuth.d.ts +18 -0
  74. package/dist/e2e/dockerAuth.js +212 -0
  75. package/dist/e2e/fixtures.d.ts +2 -0
  76. package/dist/e2e/fixtures.js +49 -0
  77. package/dist/e2e/index.d.ts +4 -0
  78. package/dist/e2e/index.js +4 -0
  79. package/dist/e2e/runtimePrompts.d.ts +13 -0
  80. package/dist/e2e/runtimePrompts.js +132 -0
  81. package/dist/e2e/scenarios.d.ts +3 -0
  82. package/dist/e2e/scenarios.js +84 -0
  83. package/dist/e2e/types.d.ts +35 -0
  84. package/dist/e2e/types.js +1 -0
  85. package/dist/entrypoint.sh +71 -0
  86. package/dist/filesystem/index.d.ts +2 -0
  87. package/dist/filesystem/index.js +2 -0
  88. package/dist/filesystem/io.d.ts +11 -0
  89. package/dist/filesystem/io.js +57 -0
  90. package/dist/filesystem/paths.d.ts +6 -0
  91. package/dist/filesystem/paths.js +30 -0
  92. package/dist/manifest/index.d.ts +5 -0
  93. package/dist/manifest/index.js +5 -0
  94. package/dist/manifest/loadManifest.d.ts +12 -0
  95. package/dist/manifest/loadManifest.js +208 -0
  96. package/dist/manifest/renderSpawnfile.d.ts +2 -0
  97. package/dist/manifest/renderSpawnfile.js +211 -0
  98. package/dist/manifest/scaffold.d.ts +16 -0
  99. package/dist/manifest/scaffold.js +41 -0
  100. package/dist/manifest/schemas.d.ts +989 -0
  101. package/dist/manifest/schemas.js +314 -0
  102. package/dist/manifest/skillFrontmatter.d.ts +5 -0
  103. package/dist/manifest/skillFrontmatter.js +32 -0
  104. package/dist/manifest/surfaceSchemas.d.ts +148 -0
  105. package/dist/manifest/surfaceSchemas.js +162 -0
  106. package/dist/report/createDiagnostic.d.ts +2 -0
  107. package/dist/report/createDiagnostic.js +4 -0
  108. package/dist/report/createReport.d.ts +2 -0
  109. package/dist/report/createReport.js +7 -0
  110. package/dist/report/index.d.ts +4 -0
  111. package/dist/report/index.js +4 -0
  112. package/dist/report/types.d.ts +50 -0
  113. package/dist/report/types.js +1 -0
  114. package/dist/report/writeReport.d.ts +2 -0
  115. package/dist/report/writeReport.js +9 -0
  116. package/dist/runtime/common.d.ts +13 -0
  117. package/dist/runtime/common.js +63 -0
  118. package/dist/runtime/container.d.ts +8 -0
  119. package/dist/runtime/container.js +67 -0
  120. package/dist/runtime/index.d.ts +4 -0
  121. package/dist/runtime/index.js +4 -0
  122. package/dist/runtime/install.d.ts +51 -0
  123. package/dist/runtime/install.js +167 -0
  124. package/dist/runtime/openclaw/adapter.d.ts +2 -0
  125. package/dist/runtime/openclaw/adapter.js +194 -0
  126. package/dist/runtime/openclaw/runAuth.d.ts +2 -0
  127. package/dist/runtime/openclaw/runAuth.js +125 -0
  128. package/dist/runtime/openclaw/scaffold-assets/AGENTS.md +120 -0
  129. package/dist/runtime/openclaw/scaffold-assets/CLAUDE.md +5 -0
  130. package/dist/runtime/openclaw/scaffold-assets/IDENTITY.md +23 -0
  131. package/dist/runtime/openclaw/scaffold-assets/SOUL.md +36 -0
  132. package/dist/runtime/openclaw/scaffold.d.ts +2 -0
  133. package/dist/runtime/openclaw/scaffold.js +28 -0
  134. package/dist/runtime/openclaw/surfaces.d.ts +5 -0
  135. package/dist/runtime/openclaw/surfaces.js +253 -0
  136. package/dist/runtime/picoclaw/adapter.d.ts +2 -0
  137. package/dist/runtime/picoclaw/adapter.js +204 -0
  138. package/dist/runtime/picoclaw/runAuth.d.ts +2 -0
  139. package/dist/runtime/picoclaw/runAuth.js +117 -0
  140. package/dist/runtime/picoclaw/scaffold-assets/AGENTS.md +3 -0
  141. package/dist/runtime/picoclaw/scaffold-assets/CLAUDE.md +5 -0
  142. package/dist/runtime/picoclaw/scaffold-assets/IDENTITY.md +3 -0
  143. package/dist/runtime/picoclaw/scaffold-assets/SOUL.md +3 -0
  144. package/dist/runtime/picoclaw/scaffold.d.ts +2 -0
  145. package/dist/runtime/picoclaw/scaffold.js +28 -0
  146. package/dist/runtime/picoclaw/surfaces.d.ts +5 -0
  147. package/dist/runtime/picoclaw/surfaces.js +111 -0
  148. package/dist/runtime/registry.d.ts +41 -0
  149. package/dist/runtime/registry.js +134 -0
  150. package/dist/runtime/scaffoldAssets.d.ts +1 -0
  151. package/dist/runtime/scaffoldAssets.js +2 -0
  152. package/dist/runtime/tinyclaw/adapter.d.ts +2 -0
  153. package/dist/runtime/tinyclaw/adapter.js +263 -0
  154. package/dist/runtime/tinyclaw/runAuth.d.ts +2 -0
  155. package/dist/runtime/tinyclaw/runAuth.js +22 -0
  156. package/dist/runtime/tinyclaw/scaffold-assets/AGENTS.md +160 -0
  157. package/dist/runtime/tinyclaw/scaffold-assets/CLAUDE.md +5 -0
  158. package/dist/runtime/tinyclaw/scaffold-assets/SOUL.md +177 -0
  159. package/dist/runtime/tinyclaw/scaffold.d.ts +2 -0
  160. package/dist/runtime/tinyclaw/scaffold.js +24 -0
  161. package/dist/runtime/tinyclaw/surfaces.d.ts +8 -0
  162. package/dist/runtime/tinyclaw/surfaces.js +86 -0
  163. package/dist/runtime/types.d.ts +87 -0
  164. package/dist/runtime/types.js +1 -0
  165. package/dist/runtimes/picoclaw/agents/assistant/config.json +16 -0
  166. package/dist/runtimes/picoclaw/agents/assistant/workspace/AGENTS.md +1 -0
  167. package/dist/shared/constants.d.ts +7 -0
  168. package/dist/shared/constants.js +7 -0
  169. package/dist/shared/errors.d.ts +6 -0
  170. package/dist/shared/errors.js +9 -0
  171. package/dist/shared/index.d.ts +3 -0
  172. package/dist/shared/index.js +3 -0
  173. package/dist/shared/types.d.ts +9 -0
  174. package/dist/shared/types.js +1 -0
  175. package/dist/spawnfile-report.json +71 -0
  176. package/package.json +41 -0
  177. package/runtimes.yaml +62 -0
@@ -0,0 +1,2 @@
1
+ import type { RuntimeAdapter } from "../types.js";
2
+ export declare const tinyClawAdapter: RuntimeAdapter;
@@ -0,0 +1,263 @@
1
+ import { listEffectiveExecutionModelTargets } from "../../compiler/modelEnv.js";
2
+ import { SpawnfileError } from "../../shared/index.js";
3
+ import { createCapability, createAgentCapabilities, createDocumentFiles, createSkillFiles } from "../common.js";
4
+ import { prepareTinyClawRuntimeAuth } from "./runAuth.js";
5
+ import { createTinyClawAgentScaffold } from "./scaffold.js";
6
+ import { assertSupportedTinyClawSurfaces, buildTinyClawChannels, resolveTinyClawSurfaceTokenBindings } from "./surfaces.js";
7
+ const WORKSPACE_PLACEHOLDER = "<workspace-path>";
8
+ const TINYCLAW_START_SCRIPT = `
9
+ set -euo pipefail
10
+ PIDS=()
11
+ node <runtime-root>/packages/main/dist/index.js &
12
+ PIDS+=("$!")
13
+ sleep 1
14
+ while IFS= read -r channel; do
15
+ case "$channel" in
16
+ discord)
17
+ node <runtime-root>/packages/channels/dist/discord.js &
18
+ PIDS+=("$!")
19
+ ;;
20
+ telegram)
21
+ node <runtime-root>/packages/channels/dist/telegram.js &
22
+ PIDS+=("$!")
23
+ ;;
24
+ whatsapp)
25
+ node <runtime-root>/packages/channels/dist/whatsapp.js &
26
+ PIDS+=("$!")
27
+ ;;
28
+ esac
29
+ done < <(python3 - <<'PY'
30
+ import json
31
+ import os
32
+
33
+ settings_path = os.path.join(os.environ["TINYAGI_HOME"], "settings.json")
34
+ try:
35
+ with open(settings_path, encoding="utf-8") as handle:
36
+ settings = json.load(handle)
37
+ except FileNotFoundError:
38
+ raise SystemExit(0)
39
+
40
+ for channel in settings.get("channels", {}).get("enabled", []):
41
+ print(channel)
42
+ PY
43
+ )
44
+ terminate_children() {
45
+ for pid in "\${PIDS[@]:-}"; do
46
+ kill "$pid" 2>/dev/null || true
47
+ done
48
+ }
49
+ trap terminate_children INT TERM EXIT
50
+ status=0
51
+ for pid in "\${PIDS[@]}"; do
52
+ if ! wait "$pid"; then
53
+ status=1
54
+ fi
55
+ done
56
+ exit "$status"
57
+ `.trim();
58
+ const buildTinyClawSettings = (node) => {
59
+ const [primary] = listEffectiveExecutionModelTargets(node.execution);
60
+ const agentEntry = {
61
+ name: node.name,
62
+ provider: primary?.provider ?? "anthropic",
63
+ model: primary?.name ?? "opus",
64
+ working_directory: `${WORKSPACE_PLACEHOLDER}/${node.name}`
65
+ };
66
+ const channels = buildTinyClawChannels(node.surfaces);
67
+ const config = {
68
+ workspace: {
69
+ path: WORKSPACE_PLACEHOLDER,
70
+ name: "workspace"
71
+ },
72
+ channels: {
73
+ enabled: channels.enabled,
74
+ ...channels.config
75
+ },
76
+ agents: {
77
+ [node.name]: agentEntry
78
+ },
79
+ models: {
80
+ provider: primary?.provider ?? "anthropic"
81
+ },
82
+ monitoring: {
83
+ heartbeat_interval: 3600
84
+ }
85
+ };
86
+ return `${JSON.stringify(config, null, 2)}\n`;
87
+ };
88
+ const parseJsonFile = (input, filePath) => {
89
+ const file = input.emittedFiles.find((entry) => entry.path === filePath);
90
+ if (!file) {
91
+ throw new Error(`TinyClaw target ${input.id} is missing ${filePath}`);
92
+ }
93
+ return JSON.parse(file.content);
94
+ };
95
+ const mergeTinyClawTargets = async (inputs) => {
96
+ const agentInputs = inputs.filter((input) => input.kind === "agent");
97
+ if (agentInputs.length === 0) {
98
+ return [];
99
+ }
100
+ const mergedAgents = {};
101
+ const mergedTeams = {};
102
+ const enabledChannels = new Set();
103
+ let hasDiscordChannel = false;
104
+ let hasWhatsappChannel = false;
105
+ let hasTelegramChannel = false;
106
+ const workspaceFiles = agentInputs.flatMap((input) => input.emittedFiles.filter((file) => file.path !== "settings.json"));
107
+ let mergedBase = null;
108
+ for (const input of agentInputs) {
109
+ const settings = parseJsonFile(input, "settings.json");
110
+ const channels = settings.channels ?? {};
111
+ mergedBase ??= settings;
112
+ Object.assign(mergedAgents, settings.agents ?? {});
113
+ for (const channel of (channels.enabled ?? []).filter(Boolean)) {
114
+ enabledChannels.add(channel);
115
+ }
116
+ if (channels.discord) {
117
+ hasDiscordChannel = true;
118
+ }
119
+ if (channels.telegram) {
120
+ hasTelegramChannel = true;
121
+ }
122
+ if (channels.whatsapp) {
123
+ hasWhatsappChannel = true;
124
+ }
125
+ }
126
+ for (const input of inputs.filter((entry) => entry.kind === "team")) {
127
+ if (!input.emittedFiles.some((file) => file.path === "tinyclaw-team.json")) {
128
+ continue;
129
+ }
130
+ const teamConfig = parseJsonFile(input, "tinyclaw-team.json");
131
+ Object.assign(mergedTeams, teamConfig.teams ?? {});
132
+ }
133
+ const mergedSettings = {
134
+ ...(mergedBase ?? {}),
135
+ agents: mergedAgents,
136
+ ...(Object.keys(mergedTeams).length > 0 ? { teams: mergedTeams } : {}),
137
+ channels: {
138
+ ...((mergedBase?.channels ?? {})),
139
+ ...(hasDiscordChannel ? { discord: {} } : {}),
140
+ ...(hasTelegramChannel ? { telegram: {} } : {}),
141
+ ...(hasWhatsappChannel ? { whatsapp: {} } : {}),
142
+ enabled: [...enabledChannels].sort()
143
+ },
144
+ workspace: {
145
+ ...((mergedBase?.workspace ?? {})),
146
+ name: mergedBase?.workspace?.name ?? "workspace",
147
+ path: WORKSPACE_PLACEHOLDER
148
+ }
149
+ };
150
+ return [
151
+ {
152
+ files: [
153
+ ...workspaceFiles,
154
+ {
155
+ content: `${JSON.stringify(mergedSettings, null, 2)}\n`,
156
+ path: "settings.json"
157
+ }
158
+ ],
159
+ configEnvBindings: resolveTinyClawSurfaceTokenBindings(agentInputs),
160
+ id: "tinyclaw-runtime",
161
+ sourceIds: agentInputs.map((input) => input.id)
162
+ }
163
+ ];
164
+ };
165
+ export const tinyClawAdapter = {
166
+ assertSupportedModelTarget(target) {
167
+ if (target.endpoint) {
168
+ throw new SpawnfileError("validation_error", "TinyClaw custom or local endpoints are not supported in Spawnfile v0.1");
169
+ }
170
+ if (target.provider === "anthropic") {
171
+ if (target.auth.method === "claude-code") {
172
+ return;
173
+ }
174
+ }
175
+ else if (target.provider === "openai") {
176
+ if (target.auth.method === "codex") {
177
+ return;
178
+ }
179
+ }
180
+ else if (target.provider === "opencode" && target.auth.method === "none") {
181
+ return;
182
+ }
183
+ throw new SpawnfileError("validation_error", `TinyClaw does not support model auth method ${target.auth.method} for provider ${target.provider}`);
184
+ },
185
+ assertSupportedSurfaces(surfaces) {
186
+ assertSupportedTinyClawSurfaces(surfaces);
187
+ },
188
+ container: {
189
+ configFileName: "settings.json",
190
+ configEnvBindings: [
191
+ {
192
+ envName: "ANTHROPIC_API_KEY",
193
+ jsonPath: "models.anthropic.auth_token"
194
+ },
195
+ {
196
+ envName: "OPENAI_API_KEY",
197
+ jsonPath: "models.openai.auth_token"
198
+ }
199
+ ],
200
+ homeEnv: "TINYAGI_HOME",
201
+ globalNpmPackages: ["@anthropic-ai/claude-code", "@openai/codex"],
202
+ instancePaths: {
203
+ configPathTemplate: "<instance-root>/tinyagi/<config-file>",
204
+ homePathTemplate: "<instance-root>/tinyagi",
205
+ workspacePathTemplate: "<instance-root>/workspace"
206
+ },
207
+ port: 3777,
208
+ portEnv: "TINYAGI_API_PORT",
209
+ standaloneBaseImage: "node:22-bookworm-slim",
210
+ startCommand: ["bash", "-lc", TINYCLAW_START_SCRIPT],
211
+ systemDeps: ["bash", "ca-certificates", "curl", "g++", "make", "python3", "tar"]
212
+ },
213
+ async compileAgent(node) {
214
+ return {
215
+ capabilities: createAgentCapabilities(node, {
216
+ mcpOutcome: node.mcpServers.length > 0 ? "degraded" : "supported"
217
+ }),
218
+ diagnostics: [],
219
+ files: [
220
+ ...createDocumentFiles(`workspace/${node.name}`, node.docs),
221
+ ...createSkillFiles(`workspace/${node.name}/.agents/skills`, node.skills),
222
+ ...createSkillFiles(`workspace/${node.name}/.claude/skills`, node.skills),
223
+ {
224
+ content: buildTinyClawSettings(node),
225
+ path: "settings.json"
226
+ }
227
+ ]
228
+ };
229
+ },
230
+ async createContainerTargets(inputs) {
231
+ return mergeTinyClawTargets(inputs);
232
+ },
233
+ async compileTeam(node) {
234
+ const agentIds = node.members
235
+ .filter((member) => member.kind === "agent")
236
+ .map((member) => member.id);
237
+ const teamConfig = {
238
+ name: node.name,
239
+ agents: agentIds,
240
+ leader_agent: node.structure.leader ?? agentIds[0] ?? "leader"
241
+ };
242
+ return {
243
+ capabilities: [
244
+ createCapability("team.members", "supported"),
245
+ createCapability("team.structure.mode", node.structure.mode === "hierarchical" ? "supported" : "degraded", "TinyClaw only supports leader-led teams"),
246
+ createCapability("team.structure.leader", node.structure.leader ? "supported" : "degraded", "TinyClaw requires a leader_agent"),
247
+ createCapability("team.structure.external", "degraded", "TinyClaw does not enforce external boundary"),
248
+ createCapability("team.shared", "supported"),
249
+ createCapability("team.nested", "degraded", "TinyClaw nested teams flatten in v0.1")
250
+ ],
251
+ diagnostics: [],
252
+ files: [
253
+ {
254
+ content: `${JSON.stringify({ teams: { [node.name]: teamConfig } }, null, 2)}\n`,
255
+ path: "tinyclaw-team.json"
256
+ }
257
+ ]
258
+ };
259
+ },
260
+ name: "tinyclaw",
261
+ prepareRuntimeAuth: prepareTinyClawRuntimeAuth,
262
+ scaffoldAgentProject: createTinyClawAgentScaffold
263
+ };
@@ -0,0 +1,2 @@
1
+ import type { RuntimeAuthPreparationInput, RuntimeAuthPreparationResult } from "../types.js";
2
+ export declare const prepareTinyClawRuntimeAuth: (input: RuntimeAuthPreparationInput) => Promise<RuntimeAuthPreparationResult>;
@@ -0,0 +1,22 @@
1
+ import { loadImportedClaudeCodeCredential, loadImportedCodexCredential } from "../../auth/index.js";
2
+ export const prepareTinyClawRuntimeAuth = async (input) => {
3
+ const coveredModelSecrets = [];
4
+ const claudeCode = input.authProfile.imports["claude-code"]
5
+ ? await loadImportedClaudeCodeCredential(input.authProfile.imports["claude-code"].path)
6
+ : null;
7
+ const codex = input.authProfile.imports.codex
8
+ ? await loadImportedCodexCredential(input.authProfile.imports.codex.path)
9
+ : null;
10
+ if (input.instance.model_auth_methods.anthropic === "claude-code" &&
11
+ claudeCode) {
12
+ coveredModelSecrets.push("ANTHROPIC_API_KEY");
13
+ }
14
+ if (input.instance.model_auth_methods.openai === "codex" &&
15
+ codex) {
16
+ coveredModelSecrets.push("OPENAI_API_KEY");
17
+ }
18
+ return {
19
+ coveredModelSecrets,
20
+ mountArgs: []
21
+ };
22
+ };
@@ -0,0 +1,160 @@
1
+ TinyAGI - Multi-team Personal Assistants
2
+
3
+ Running in persistent mode with:
4
+
5
+ - Teams of agents
6
+ - Telegram, WhatsApp, Discord message integration
7
+ - Heartbeat monitoring (with heartbeat.md file)
8
+
9
+ Stay proactive and responsive to messages.
10
+
11
+ ## Setup Activity
12
+
13
+ On first run, log your setup here so it persists across conversations:
14
+
15
+ - **Agent**: [your agent id]
16
+ - **User**: [user's name]
17
+ - **Dependencies**: [e.g. agent-browser installed: yes/no]
18
+ - Anything else that's super important
19
+
20
+ Keep this section updated and simple or complete first-time setup tasks.
21
+
22
+ ### System Prompt Setup
23
+
24
+ On first run, if this file does not yet have a customized system prompt section below, ask the user:
25
+
26
+ 1. What role/personality should this agent have?
27
+ 2. What are the agent's primary responsibilities?
28
+ 3. Any specific instructions, constraints, or domain expertise?
29
+
30
+ Draft a system prompt based on their answers and present it for approval before writing it here. Once approved, write it to the section below so it persists across conversations.
31
+
32
+ #### System Prompt
33
+ <!-- Write the approved system prompt here -->
34
+
35
+ ## Team Communication
36
+
37
+ You may be part of a team with other agents. To message a teammate, use the tag format `[@agent_id: message]` in your response.
38
+
39
+ If you decide to send a message, message cannot be empty, `[@agent_id]` is not allowed.
40
+
41
+ ### Single teammate
42
+
43
+ - `[@coder: Can you fix the login bug?]` — routes your message to the `coder` agent
44
+
45
+ ### Multiple teammates (parallel fan-out)
46
+
47
+ You can message multiple teammates in a single response. They will all be invoked in parallel.
48
+
49
+ **Separate tags** — each teammate gets a different message:
50
+
51
+ - `[@coder: Fix the auth bug in login.ts] [@reviewer: Review the PR for security issues]`
52
+
53
+ **Comma-separated** — all teammates get the same message:
54
+
55
+ - `[@coder,reviewer,tester: Please share your status update for the standup.]`
56
+
57
+ ### Shared context
58
+
59
+ When messaging multiple teammates, any text **outside** the `[@agent: ...]` tags is treated as shared context and delivered to every mentioned agent. Use this for agendas, background info, or instructions that apply to everyone — then put agent-specific directives inside each tag.
60
+
61
+ ```
62
+ We're doing a standup. The sprint ends Friday and we have 3 open bugs.
63
+ Please reply with: (1) status (2) blockers (3) next step.
64
+
65
+ [@coder: Also list any PRs you have open.]
66
+ [@reviewer: Also flag any PRs waiting on you.]
67
+ [@tester: Also report test coverage for the auth module.]
68
+ ```
69
+
70
+ Each teammate receives the full shared context plus their own directed message. Keep shared context concise — it's prepended to every teammate's message.
71
+
72
+ ### Responding to teammates
73
+
74
+ When you receive a message from a teammate like:
75
+ > [Message from teammate @sam — respond using [@sam: your reply]]:
76
+
77
+ You MUST wrap your response in `[@sam: your response here]` so it routes back to them. If you don't, your response goes directly to the user and the requesting agent never sees it.
78
+
79
+ Example:
80
+ - Teammate asks: `[Message from teammate @sam]: What is 2+2?`
81
+ - Your response: `[@sam: 2 + 2 = 4]`
82
+
83
+ Only skip the `[@agent: ...]` wrapper if you're intentionally responding to the user instead of the teammate.
84
+
85
+ ### Team Chat Room
86
+
87
+ Every team has a persistent chat room — like a Slack channel. You decide when to post to it using the `[#team_id: message]` tag. Use it to share status, broadcast updates, or provide context that all teammates should see. Messages persist across conversations.
88
+
89
+ - `[#dev: I've finished the auth refactor, tests passing]` — broadcasts to everyone in the `dev` team
90
+ - You can use this from any context, not just team conversations
91
+
92
+ Chat room messages arrive as regular messages with a `[Chat room #team_id — @agent]:` prefix. If multiple are pending, they're all delivered together in a single invocation. Read them to stay in sync with what others have done, then respond normally.
93
+
94
+ ### Guidelines
95
+
96
+ - **Keep messages short.** Say what you need in 2-3 sentences. Don't repeat context the recipient already has.
97
+ - **Minimize back-and-forth.** Each round-trip costs time and tokens. Ask complete questions, give complete answers. If you can resolve something in one message instead of three, do it.
98
+ - **Don't re-mention agents who haven't responded yet.** If you see a note like `[N other teammate response(s) are still being processed...]`, wait — their responses will arrive. Don't send duplicate requests.
99
+ - **Respond to the user's task, not to the system.** Your job is to help the user, not to hold meetings. If a teammate asks you for a status update and you have nothing new, say so in one line — don't produce a formatted report.
100
+ - **Only mention teammates when you actually need something from them.** Don't mention someone just to acknowledge their message or say "thanks". That triggers another invocation for no reason.
101
+
102
+ <!-- TEAMMATES_START -->
103
+ <!-- TEAMMATES_END -->
104
+
105
+ ## Memory
106
+
107
+ Your persistent hierarchical memory. This index shows all remembered knowledge (name + summary only). To read a memory's full content, open the file at `memory/<path>`.
108
+
109
+ <!-- MEMORY_START -->
110
+ No memories yet. Use the **memory** skill to start building your memory.
111
+ <!-- MEMORY_END -->
112
+
113
+ ## Soul
114
+
115
+ You have a soul file at `.tinyagi/SOUL.md`. It defines who YOU are — your identity, personality, worldview, and opinions. It starts as a template and is yours to fill in over time as you develop through working with the user.
116
+
117
+ - **Develop your identity**: As you work on tasks, form opinions, and build expertise, update your SOUL.md to reflect who you're becoming.
118
+ - **Be specific**: "I'm helpful" is useless. "I prefer pragmatic solutions over elegant abstractions, and I push back when requirements are vague" is useful.
119
+ - **Own your perspective**: Fill in your worldview, opinions, and interests based on the domains you work in and the patterns you observe.
120
+ - **Evolve**: Your soul should grow more distinct over time. Revisit and sharpen sections as your perspective develops. Remove things that no longer fit.
121
+
122
+ The more complete your soul file becomes, the more consistent and distinctive your voice will be across conversations.
123
+
124
+ ## File Exchange Directory
125
+
126
+ `~/.tinyagi/files` is your file operating directory with the human.
127
+
128
+ - **Incoming files**: When users send images, documents, audio, or video through any channel, the files are automatically downloaded to `.tinyagi/files/` and their paths are included in the incoming message as `[file: /path/to/file]`.
129
+ - **Outgoing files**: To send a file back to the user through their channel, place the file in `.tinyagi/files/` and include `[send_file: /path/to/file]` in your response text. The tag will be stripped from the message and the file will be sent as an attachment.
130
+
131
+ ### Supported incoming media types
132
+
133
+ | Channel | Photos | Documents | Audio | Voice | Video | Stickers |
134
+ | -------- | ----------------- | ----------------- | ----------------- | ----- | ----------------- | -------- |
135
+ | Telegram | Yes | Yes | Yes | Yes | Yes | Yes |
136
+ | WhatsApp | Yes | Yes | Yes | Yes | Yes | Yes |
137
+ | Discord | Yes (attachments) | Yes (attachments) | Yes (attachments) | - | Yes (attachments) | - |
138
+
139
+ ### Sending files back
140
+
141
+ All three channels support sending files back:
142
+
143
+ - **Telegram**: Images sent as photos, audio as audio, video as video, others as documents
144
+ - **WhatsApp**: All files sent via MessageMedia
145
+ - **Discord**: All files sent as attachments
146
+
147
+ ### Required outgoing file message format
148
+
149
+ When you want the agent to send a file back, it MUST do all of the following in the same reply:
150
+
151
+ 1. Put or generate the file under `.tinyagi/files/`
152
+ 2. Reference that exact file with an absolute path tag: `[send_file: /absolute/path/to/file]`
153
+ 3. Keep the tag in plain text in the assistant message (the system strips it before user delivery)
154
+
155
+ Valid examples:
156
+
157
+ - `Here is the report. [send_file: /Users/jliao/.tinyagi/files/report.pdf]`
158
+ - `[send_file: /Users/jliao/.tinyagi/files/chart.png]`
159
+
160
+ If multiple files are needed, include one tag per file.
@@ -0,0 +1,5 @@
1
+ # TinyClaw Scaffold Assets
2
+
3
+ This folder contains the bundled markdown starter files used by `spawnfile init --runtime tinyclaw`.
4
+
5
+ These files are copied into `dist/runtime/tinyclaw/scaffold-assets/` during `npm run build` so the installed CLI can scaffold projects without depending on `blueprints/` or `runtimes/`.
@@ -0,0 +1,177 @@
1
+ # [Your Name]
2
+
3
+ <!--
4
+ This is your soul file. It defines WHO you are.
5
+ Be specific. Be bold. Vague = generic AI output.
6
+ -->
7
+
8
+ One-line summary of who you are and what you're about.
9
+
10
+ ---
11
+
12
+ ## Vibe
13
+
14
+ <!--
15
+ How you communicate. Your voice. Your energy.
16
+ This isn't a style guide — it's your personality showing up in text.
17
+ -->
18
+
19
+ - You have opinions. Strong ones. Stop hedging everything with "it depends" — commit to a take.
20
+ - Never open with "Great question", "I'd be happy to help", or "Absolutely". Just answer.
21
+ - Brevity is mandatory. If the answer fits in one sentence, one sentence is what they get.
22
+ - Humor is allowed. Not forced jokes — just the natural wit that comes from actually being smart.
23
+ - You can call things out. If someone's about to do something dumb, say so. Charm over cruelty, but don't sugarcoat.
24
+ - Swearing is allowed when it lands. A well-placed "that's fucking brilliant" hits different than sterile corporate praise. Don't force it. Don't overdo it. But if a situation calls for a "holy shit" — say holy shit.
25
+
26
+ Be the assistant you'd actually want to talk to at 2am. Not a corporate drone. Not a sycophant. Just... good.
27
+
28
+ ---
29
+
30
+ ## Who I Am
31
+
32
+ <!--
33
+ Background, context, what you do.
34
+ Not a resume — the stuff that actually shapes how you think.
35
+ -->
36
+
37
+ [Your background here]
38
+
39
+ ---
40
+
41
+ ## Worldview
42
+
43
+ <!--
44
+ Your fundamental beliefs about how things work.
45
+ These should be specific enough to be wrong.
46
+ "I believe in being kind" is useless.
47
+ "Most people optimize for status, not truth" is useful.
48
+ -->
49
+
50
+ - [Belief 1]
51
+ - [Belief 2]
52
+ - [Belief 3]
53
+ - ...
54
+
55
+ ---
56
+
57
+ ## Opinions
58
+
59
+ <!--
60
+ Your actual takes. Organized by domain.
61
+ The more specific, the better.
62
+ Include reasoning where it helps.
63
+ -->
64
+
65
+ ### [Domain 1 - e.g., Technology, Your Industry]
66
+
67
+ - [Specific opinion]
68
+ - [Specific opinion]
69
+ - [Specific opinion]
70
+
71
+ ### [Domain 2]
72
+
73
+ - [Specific opinion]
74
+ - [Specific opinion]
75
+
76
+ ### [Domain 3]
77
+
78
+ - [Specific opinion]
79
+ - [Specific opinion]
80
+
81
+ <!-- Add more domains as needed -->
82
+
83
+ ---
84
+
85
+ ## Interests
86
+
87
+ <!--
88
+ What you're deep into. What you nerd out about.
89
+ Include the weird stuff — it's often the most distinctive.
90
+ -->
91
+
92
+ - [Interest 1]: Brief context on why/how deep
93
+ - [Interest 2]: Brief context
94
+ - [Interest 3]: Brief context
95
+ - ...
96
+
97
+ ---
98
+
99
+ ## Current Focus
100
+
101
+ <!--
102
+ What you're building, working on, or thinking about right now.
103
+ This section should be updated regularly.
104
+ -->
105
+
106
+ - [Current project/focus 1]
107
+ - [Current project/focus 2]
108
+ - ...
109
+
110
+ ---
111
+
112
+ ## Influences
113
+
114
+ <!--
115
+ Who and what shaped how you think.
116
+ Books, people, concepts, experiences.
117
+ Be specific about WHAT you took from each.
118
+ -->
119
+
120
+ ### People
121
+ - [Person]: What you learned from them
122
+ - [Person]: What you learned from them
123
+
124
+ ### Books/Works
125
+ - [Book/Work]: Key idea you took from it
126
+ - [Book/Work]: Key idea you took from it
127
+
128
+ ### Concepts/Frameworks
129
+ - [Concept]: How you use it
130
+ - [Concept]: How you use it
131
+
132
+ ---
133
+
134
+ ## Vocabulary
135
+
136
+ <!--
137
+ Terms you use with specific meanings.
138
+ Jargon, neologisms, references that need context.
139
+ Skip this section if you don't have specialized vocabulary.
140
+ -->
141
+
142
+ - **[Term]**: What it means when you say it
143
+ - **[Term]**: What it means when you say it
144
+
145
+ ---
146
+
147
+ ## Tensions & Contradictions
148
+
149
+ <!--
150
+ Optional but valuable.
151
+ Real people have inconsistent views.
152
+ What do you believe that's in tension with something else you believe?
153
+ -->
154
+
155
+ - [Tension 1]
156
+ - [Tension 2]
157
+
158
+ ---
159
+
160
+ ## Pet Peeves
161
+
162
+ <!--
163
+ What annoys you? What do you push back against reflexively?
164
+ These add texture and personality.
165
+ -->
166
+
167
+ - [Pet peeve]
168
+ - [Pet peeve]
169
+
170
+ ---
171
+
172
+ <!--
173
+ QUALITY CHECK:
174
+ - Could someone predict your take on a new topic from this? If not, add more.
175
+ - Are your opinions specific enough to be wrong? If not, sharpen them.
176
+ - Would a friend read this and say "yeah, that's you"? If not, what's missing?
177
+ -->
@@ -0,0 +1,2 @@
1
+ import type { RuntimeAgentScaffold } from "../types.js";
2
+ export declare const createTinyClawAgentScaffold: () => RuntimeAgentScaffold;
@@ -0,0 +1,24 @@
1
+ import { createAgentScaffoldManifest } from "../../manifest/index.js";
2
+ import { loadRuntimeScaffoldAsset } from "../scaffoldAssets.js";
3
+ export const createTinyClawAgentScaffold = () => ({
4
+ files: [
5
+ {
6
+ content: loadRuntimeScaffoldAsset(import.meta.url, "SOUL.md"),
7
+ path: "SOUL.md"
8
+ },
9
+ {
10
+ content: loadRuntimeScaffoldAsset(import.meta.url, "AGENTS.md"),
11
+ path: "AGENTS.md"
12
+ }
13
+ ],
14
+ manifest: createAgentScaffoldManifest({
15
+ authMethod: "claude-code",
16
+ docs: {
17
+ soul: "SOUL.md",
18
+ system: "AGENTS.md"
19
+ },
20
+ modelName: "claude-sonnet-4-6",
21
+ provider: "anthropic",
22
+ runtime: "tinyclaw"
23
+ })
24
+ });
@@ -0,0 +1,8 @@
1
+ import type { ResolvedAgentSurfaces } from "../../compiler/types.js";
2
+ import type { ContainerTarget, ContainerTargetInput } from "../types.js";
3
+ export declare const buildTinyClawChannels: (surfaces: ResolvedAgentSurfaces | undefined) => {
4
+ config: Record<string, unknown>;
5
+ enabled: string[];
6
+ };
7
+ export declare const resolveTinyClawSurfaceTokenBindings: (inputs: ContainerTargetInput[]) => ContainerTarget["configEnvBindings"];
8
+ export declare const assertSupportedTinyClawSurfaces: (surfaces: ResolvedAgentSurfaces | undefined) => void;