spawnfile 0.1.4 → 0.1.5

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 (73) hide show
  1. package/dist/cli/lifecycleCommands.d.ts +3 -0
  2. package/dist/cli/lifecycleCommands.js +80 -0
  3. package/dist/cli/runCli.d.ts +2 -1
  4. package/dist/cli/runCli.js +4 -48
  5. package/dist/compiler/buildCompilePlan.js +12 -202
  6. package/dist/compiler/buildCompilePlanRuntime.d.ts +6 -1
  7. package/dist/compiler/buildCompilePlanRuntime.js +9 -0
  8. package/dist/compiler/buildCompilePlanTeams.js +16 -10
  9. package/dist/compiler/buildCompilePlanTraversal.d.ts +16 -0
  10. package/dist/compiler/buildCompilePlanTraversal.js +214 -0
  11. package/dist/compiler/buildCompilePlanTraversalHelpers.d.ts +18 -0
  12. package/dist/compiler/buildCompilePlanTraversalHelpers.js +22 -0
  13. package/dist/compiler/compilePlanHelpers.d.ts +3 -1
  14. package/dist/compiler/compilePlanHelpers.js +37 -1
  15. package/dist/compiler/compileProject.js +14 -0
  16. package/dist/compiler/containerArtifacts.js +18 -3
  17. package/dist/compiler/containerArtifactsPlans.js +32 -0
  18. package/dist/compiler/containerArtifactsRender.js +86 -3
  19. package/dist/compiler/containerArtifactsTypes.d.ts +7 -3
  20. package/dist/compiler/containerEntrypointRender.d.ts +1 -1
  21. package/dist/compiler/containerEntrypointRender.js +34 -24
  22. package/dist/compiler/containerTargetResources.d.ts +4 -0
  23. package/dist/compiler/containerTargetResources.js +54 -0
  24. package/dist/compiler/containerWorkspaceResourceRender.d.ts +3 -0
  25. package/dist/compiler/containerWorkspaceResourceRender.js +128 -0
  26. package/dist/compiler/executionDefaults.js +0 -3
  27. package/dist/compiler/helpers.d.ts +1 -1
  28. package/dist/compiler/index.d.ts +1 -0
  29. package/dist/compiler/index.js +1 -0
  30. package/dist/compiler/moltnetArtifacts.d.ts +11 -5
  31. package/dist/compiler/moltnetArtifacts.js +133 -117
  32. package/dist/compiler/moltnetClientConfig.js +8 -2
  33. package/dist/compiler/moltnetConfigLowering.d.ts +36 -0
  34. package/dist/compiler/moltnetConfigLowering.js +125 -0
  35. package/dist/compiler/moltnetRuntimeConfig.d.ts +2 -0
  36. package/dist/compiler/moltnetRuntimeConfig.js +69 -0
  37. package/dist/compiler/surfaces.d.ts +3 -13
  38. package/dist/compiler/surfaces.js +1 -6
  39. package/dist/compiler/syncProjectAuth.js +14 -0
  40. package/dist/compiler/types.d.ts +16 -1
  41. package/dist/compiler/upProject.d.ts +19 -0
  42. package/dist/compiler/upProject.js +37 -0
  43. package/dist/compiler/view/buildOrganizationView.js +22 -2
  44. package/dist/compiler/view/renderNetworks.js +14 -3
  45. package/dist/compiler/view/renderTree.js +8 -2
  46. package/dist/compiler/view/types.d.ts +18 -3
  47. package/dist/compiler/workspaceResources.d.ts +34 -0
  48. package/dist/compiler/workspaceResources.js +120 -0
  49. package/dist/manifest/executionSchemas.d.ts +106 -0
  50. package/dist/manifest/executionSchemas.js +140 -0
  51. package/dist/manifest/loadManifest.js +15 -27
  52. package/dist/manifest/renderSpawnfile.js +44 -52
  53. package/dist/manifest/renderSpawnfileNetworks.d.ts +2 -0
  54. package/dist/manifest/renderSpawnfileNetworks.js +63 -0
  55. package/dist/manifest/renderSpawnfileWorkspace.d.ts +2 -0
  56. package/dist/manifest/renderSpawnfileWorkspace.js +47 -0
  57. package/dist/manifest/scaffold.js +12 -6
  58. package/dist/manifest/scheduleSchemas.d.ts +15 -0
  59. package/dist/manifest/scheduleSchemas.js +26 -0
  60. package/dist/manifest/schemas.d.ts +626 -368
  61. package/dist/manifest/schemas.js +51 -191
  62. package/dist/manifest/teamNetworkSchemas.d.ts +228 -0
  63. package/dist/manifest/teamNetworkSchemas.js +295 -0
  64. package/dist/manifest/workspaceSchemas.d.ts +96 -0
  65. package/dist/manifest/workspaceSchemas.js +166 -0
  66. package/dist/report/types.d.ts +10 -0
  67. package/dist/runtime/common.d.ts +2 -1
  68. package/dist/runtime/common.js +3 -3
  69. package/dist/runtime/tinyclaw/adapter.js +9 -2
  70. package/dist/runtime/tinyclaw/schedules.d.ts +9 -0
  71. package/dist/runtime/tinyclaw/schedules.js +62 -0
  72. package/dist/runtime/types.d.ts +1 -0
  73. package/package.json +2 -1
@@ -0,0 +1,295 @@
1
+ import { z } from "zod";
2
+ export { teamWorkspaceDocsSchema, teamWorkspaceSchema } from "./workspaceSchemas.js";
3
+ const moltnetScopeSchema = z.enum(["observe", "write", "admin", "attach", "pair"]);
4
+ const countTruthy = (value) => value.filter((entry) => Boolean(entry)).length;
5
+ const teamNetworkAuthTokenSchema = z
6
+ .object({
7
+ agents: z.array(z.string().trim().min(1)).optional(),
8
+ id: z.string().trim().min(1),
9
+ secret: z.string().trim().min(1),
10
+ scopes: z.array(moltnetScopeSchema).min(1)
11
+ })
12
+ .strict();
13
+ const teamNetworkAuthClientSchema = z
14
+ .object({
15
+ static_token: z.boolean().optional(),
16
+ token_env: z.string().trim().min(1).optional(),
17
+ token_id: z.string().trim().min(1).optional(),
18
+ token_path: z.string().trim().min(1).optional()
19
+ })
20
+ .strict()
21
+ .superRefine((value, context) => {
22
+ const tokenSourceCount = countTruthy([value.token_id, value.token_env, value.token_path]);
23
+ if (tokenSourceCount > 1) {
24
+ context.addIssue({
25
+ code: z.ZodIssueCode.custom,
26
+ message: "auth.client must declare exactly one of token_id, token_env, or token_path"
27
+ });
28
+ }
29
+ if (value.static_token === true && tokenSourceCount === 0) {
30
+ context.addIssue({
31
+ code: z.ZodIssueCode.custom,
32
+ message: "auth.client.static_token requires exactly one token source"
33
+ });
34
+ }
35
+ });
36
+ const teamNetworkAuthSchema = z
37
+ .object({
38
+ client: teamNetworkAuthClientSchema.optional(),
39
+ mode: z.enum(["none", "bearer", "open"]),
40
+ tokens: z.array(teamNetworkAuthTokenSchema).optional()
41
+ })
42
+ .strict()
43
+ .superRefine((value, context) => {
44
+ if (value.mode === "none") {
45
+ if (value.tokens && value.tokens.length > 0) {
46
+ context.addIssue({
47
+ code: z.ZodIssueCode.custom,
48
+ message: "auth.mode none must not declare tokens"
49
+ });
50
+ }
51
+ if (value.client) {
52
+ context.addIssue({
53
+ code: z.ZodIssueCode.custom,
54
+ message: "auth.mode none must not declare client"
55
+ });
56
+ }
57
+ return;
58
+ }
59
+ if (value.mode === "bearer" && !value.client) {
60
+ context.addIssue({
61
+ code: z.ZodIssueCode.custom,
62
+ message: "auth.mode bearer requires auth.client"
63
+ });
64
+ }
65
+ if (value.mode === "open" && value.client) {
66
+ const tokenSourceCount = countTruthy([
67
+ value.client.token_id,
68
+ value.client.token_env,
69
+ value.client.token_path
70
+ ]);
71
+ if (value.client.static_token !== true) {
72
+ context.addIssue({
73
+ code: z.ZodIssueCode.custom,
74
+ message: "open auth with auth.client requires static_token: true"
75
+ });
76
+ }
77
+ if (tokenSourceCount === 0) {
78
+ context.addIssue({
79
+ code: z.ZodIssueCode.custom,
80
+ message: "open auth with auth.client requires exactly one token source"
81
+ });
82
+ }
83
+ }
84
+ if (value.tokens) {
85
+ const tokenIds = value.tokens.map((token) => token.id);
86
+ if (new Set(tokenIds).size !== tokenIds.length) {
87
+ context.addIssue({
88
+ code: z.ZodIssueCode.custom,
89
+ message: "auth.tokens ids must be unique"
90
+ });
91
+ }
92
+ }
93
+ });
94
+ const teamNetworkStoreSqliteSchema = z
95
+ .object({
96
+ kind: z.literal("sqlite"),
97
+ path: z.string().trim().min(1)
98
+ })
99
+ .strict();
100
+ const teamNetworkStoreJsonSchema = z
101
+ .object({
102
+ kind: z.literal("json"),
103
+ path: z.string().trim().min(1)
104
+ })
105
+ .strict();
106
+ const teamNetworkStorePostgresSchema = z
107
+ .object({
108
+ kind: z.literal("postgres"),
109
+ dsn_secret: z.string().trim().min(1)
110
+ })
111
+ .strict();
112
+ const teamNetworkStoreMemorySchema = z.object({ kind: z.literal("memory") }).strict();
113
+ const teamNetworkStoreSchema = z.discriminatedUnion("kind", [
114
+ teamNetworkStoreSqliteSchema,
115
+ teamNetworkStoreJsonSchema,
116
+ teamNetworkStorePostgresSchema,
117
+ teamNetworkStoreMemorySchema
118
+ ]);
119
+ const teamNetworkListenSchema = z
120
+ .object({
121
+ bind: z
122
+ .string()
123
+ .trim()
124
+ .min(1)
125
+ .superRefine((value, context) => {
126
+ if (value.startsWith("[") || value.endsWith("]") || /\[.*\]/.test(value)) {
127
+ context.addIssue({
128
+ code: z.ZodIssueCode.custom,
129
+ message: "listen.bind must be unbracketed"
130
+ });
131
+ }
132
+ }),
133
+ port: z.number().int().min(1).max(65535)
134
+ })
135
+ .strict();
136
+ const teamNetworkPairingSchema = z
137
+ .object({
138
+ id: z.string().trim().min(1),
139
+ remote_base_url: z.string().trim().min(1),
140
+ remote_network_id: z.string().trim().min(1),
141
+ remote_network_name: z.string().trim().min(1),
142
+ token_secret: z.string().trim().min(1)
143
+ })
144
+ .strict();
145
+ const teamNetworkManagedServerSchema = z
146
+ .object({
147
+ allowed_origins: z.array(z.string().trim().min(1)).optional(),
148
+ auth: teamNetworkAuthSchema,
149
+ direct_messages: z.boolean().optional(),
150
+ human_ingress: z.boolean().optional(),
151
+ listen: teamNetworkListenSchema,
152
+ mode: z.literal("managed"),
153
+ pairings: z.array(teamNetworkPairingSchema).optional(),
154
+ store: teamNetworkStoreSchema,
155
+ trust_forwarded_proto: z.boolean().optional(),
156
+ url: z.string().trim().optional()
157
+ })
158
+ .strict()
159
+ .superRefine((value, context) => {
160
+ if (value.auth.mode === "bearer") {
161
+ if (!value.auth.tokens || value.auth.tokens.length === 0) {
162
+ context.addIssue({
163
+ code: z.ZodIssueCode.custom,
164
+ message: "managed server with auth.mode bearer requires at least one auth token"
165
+ });
166
+ }
167
+ if (!value.auth.client?.token_id) {
168
+ context.addIssue({
169
+ code: z.ZodIssueCode.custom,
170
+ message: "managed bearer auth requires auth.client.token_id"
171
+ });
172
+ }
173
+ if (value.auth.client?.token_env || value.auth.client?.token_path) {
174
+ context.addIssue({
175
+ code: z.ZodIssueCode.custom,
176
+ message: "managed bearer auth requires auth.client.token_id"
177
+ });
178
+ }
179
+ if (value.auth.client?.token_id && value.auth.tokens) {
180
+ const tokenById = new Map(value.auth.tokens.map((token) => [token.id, token]));
181
+ const selected = tokenById.get(value.auth.client.token_id);
182
+ if (!selected) {
183
+ context.addIssue({
184
+ code: z.ZodIssueCode.custom,
185
+ message: `managed bearer auth references unknown token: ${value.auth.client.token_id}`
186
+ });
187
+ }
188
+ else if (!(selected.scopes.includes("attach") && selected.scopes.includes("write"))) {
189
+ context.addIssue({
190
+ code: z.ZodIssueCode.custom,
191
+ message: "managed bearer auth.client.token_id must reference a token with attach and write scopes"
192
+ });
193
+ }
194
+ }
195
+ }
196
+ if (value.auth.mode === "open" && value.auth.client) {
197
+ if (!value.auth.client.token_id) {
198
+ context.addIssue({
199
+ code: z.ZodIssueCode.custom,
200
+ message: "managed open auth requires auth.client.token_id"
201
+ });
202
+ }
203
+ if (value.auth.client.token_env || value.auth.client.token_path) {
204
+ context.addIssue({
205
+ code: z.ZodIssueCode.custom,
206
+ message: "managed open auth client token source must be token_id"
207
+ });
208
+ }
209
+ }
210
+ });
211
+ const teamNetworkExternalServerSchema = z
212
+ .object({
213
+ auth: teamNetworkAuthSchema,
214
+ mode: z.literal("external"),
215
+ url: z.string().trim().min(1)
216
+ })
217
+ .strict()
218
+ .superRefine((value, context) => {
219
+ if (value.auth.tokens && value.auth.tokens.length > 0) {
220
+ context.addIssue({
221
+ code: z.ZodIssueCode.custom,
222
+ message: "external server does not accept auth.tokens"
223
+ });
224
+ }
225
+ if (value.auth.mode === "none" && value.auth.client) {
226
+ context.addIssue({
227
+ code: z.ZodIssueCode.custom,
228
+ message: "auth.mode none must not declare client"
229
+ });
230
+ }
231
+ if (!value.auth.client) {
232
+ return;
233
+ }
234
+ if (value.auth.client.token_id) {
235
+ context.addIssue({
236
+ code: z.ZodIssueCode.custom,
237
+ message: "auth.client.token_id is only valid for managed servers"
238
+ });
239
+ }
240
+ if (value.auth.mode === "open" && value.auth.client.static_token !== true) {
241
+ context.addIssue({
242
+ code: z.ZodIssueCode.custom,
243
+ message: "open auth with auth.client requires static_token: true"
244
+ });
245
+ }
246
+ if (value.auth.mode === "open" &&
247
+ countTruthy([value.auth.client.token_env, value.auth.client.token_path]) !== 1) {
248
+ context.addIssue({
249
+ code: z.ZodIssueCode.custom,
250
+ message: "external open auth requires token_env or token_path"
251
+ });
252
+ }
253
+ if (value.auth.mode === "bearer") {
254
+ const sourceCount = countTruthy([
255
+ value.auth.client.token_id,
256
+ value.auth.client.token_env,
257
+ value.auth.client.token_path
258
+ ]);
259
+ if (sourceCount !== 1 || !value.auth.client.token_env && !value.auth.client.token_path) {
260
+ context.addIssue({
261
+ code: z.ZodIssueCode.custom,
262
+ message: "external bearer auth requires exactly one external token source"
263
+ });
264
+ }
265
+ }
266
+ });
267
+ const teamNetworkServerSchema = z.discriminatedUnion("mode", [
268
+ teamNetworkManagedServerSchema,
269
+ teamNetworkExternalServerSchema
270
+ ]);
271
+ const teamNetworkRoomSchema = z
272
+ .object({
273
+ id: z.string().trim().min(1),
274
+ members: z.array(z.string().trim().min(1)).min(1),
275
+ name: z.string().trim().min(1).optional()
276
+ })
277
+ .strict();
278
+ export const teamNetworkSchema = z
279
+ .object({
280
+ id: z.string().trim().min(1),
281
+ name: z.string().trim().min(1).optional(),
282
+ provider: z.literal("moltnet"),
283
+ rooms: z.array(teamNetworkRoomSchema).min(1),
284
+ server: teamNetworkServerSchema
285
+ })
286
+ .strict()
287
+ .superRefine((value, context) => {
288
+ const roomIds = value.rooms.map((room) => room.id);
289
+ if (new Set(roomIds).size !== roomIds.length) {
290
+ context.addIssue({
291
+ code: z.ZodIssueCode.custom,
292
+ message: `network ${value.id} declares duplicate room ids`
293
+ });
294
+ }
295
+ });
@@ -0,0 +1,96 @@
1
+ import { z } from "zod";
2
+ export declare const teamWorkspaceDocsSchema: z.ZodObject<{
3
+ extras: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
4
+ heartbeat: z.ZodOptional<z.ZodString>;
5
+ identity: z.ZodOptional<z.ZodString>;
6
+ memory: z.ZodOptional<z.ZodString>;
7
+ soul: z.ZodOptional<z.ZodString>;
8
+ system: z.ZodOptional<z.ZodString>;
9
+ }, z.core.$strict>;
10
+ declare const teamWorkspaceResourceSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
11
+ branch: z.ZodOptional<z.ZodString>;
12
+ id: z.ZodString;
13
+ kind: z.ZodLiteral<"git">;
14
+ mount: z.ZodString;
15
+ mode: z.ZodEnum<{
16
+ readonly: "readonly";
17
+ mutable: "mutable";
18
+ }>;
19
+ ref: z.ZodOptional<z.ZodString>;
20
+ sharing: z.ZodOptional<z.ZodEnum<{
21
+ per_agent: "per_agent";
22
+ team: "team";
23
+ }>>;
24
+ tag: z.ZodOptional<z.ZodString>;
25
+ url: z.ZodString;
26
+ }, z.core.$strict>, z.ZodObject<{
27
+ id: z.ZodString;
28
+ kind: z.ZodLiteral<"volume">;
29
+ mount: z.ZodString;
30
+ mode: z.ZodEnum<{
31
+ readonly: "readonly";
32
+ mutable: "mutable";
33
+ }>;
34
+ name: z.ZodOptional<z.ZodString>;
35
+ sharing: z.ZodOptional<z.ZodEnum<{
36
+ per_agent: "per_agent";
37
+ team: "team";
38
+ }>>;
39
+ }, z.core.$strict>], "kind">;
40
+ declare const workspaceSkillReferenceSchema: z.ZodObject<{
41
+ ref: z.ZodString;
42
+ requires: z.ZodOptional<z.ZodObject<{
43
+ mcp: z.ZodOptional<z.ZodArray<z.ZodString>>;
44
+ }, z.core.$strict>>;
45
+ }, z.core.$strict>;
46
+ export declare const teamWorkspaceSchema: z.ZodObject<{
47
+ docs: z.ZodOptional<z.ZodObject<{
48
+ extras: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
49
+ heartbeat: z.ZodOptional<z.ZodString>;
50
+ identity: z.ZodOptional<z.ZodString>;
51
+ memory: z.ZodOptional<z.ZodString>;
52
+ soul: z.ZodOptional<z.ZodString>;
53
+ system: z.ZodOptional<z.ZodString>;
54
+ }, z.core.$strict>>;
55
+ resources: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
56
+ branch: z.ZodOptional<z.ZodString>;
57
+ id: z.ZodString;
58
+ kind: z.ZodLiteral<"git">;
59
+ mount: z.ZodString;
60
+ mode: z.ZodEnum<{
61
+ readonly: "readonly";
62
+ mutable: "mutable";
63
+ }>;
64
+ ref: z.ZodOptional<z.ZodString>;
65
+ sharing: z.ZodOptional<z.ZodEnum<{
66
+ per_agent: "per_agent";
67
+ team: "team";
68
+ }>>;
69
+ tag: z.ZodOptional<z.ZodString>;
70
+ url: z.ZodString;
71
+ }, z.core.$strict>, z.ZodObject<{
72
+ id: z.ZodString;
73
+ kind: z.ZodLiteral<"volume">;
74
+ mount: z.ZodString;
75
+ mode: z.ZodEnum<{
76
+ readonly: "readonly";
77
+ mutable: "mutable";
78
+ }>;
79
+ name: z.ZodOptional<z.ZodString>;
80
+ sharing: z.ZodOptional<z.ZodEnum<{
81
+ per_agent: "per_agent";
82
+ team: "team";
83
+ }>>;
84
+ }, z.core.$strict>], "kind">>>;
85
+ skills: z.ZodOptional<z.ZodArray<z.ZodObject<{
86
+ ref: z.ZodString;
87
+ requires: z.ZodOptional<z.ZodObject<{
88
+ mcp: z.ZodOptional<z.ZodArray<z.ZodString>>;
89
+ }, z.core.$strict>>;
90
+ }, z.core.$strict>>>;
91
+ }, z.core.$strict>;
92
+ export type TeamWorkspace = z.infer<typeof teamWorkspaceSchema>;
93
+ export type TeamWorkspaceDocs = z.infer<typeof teamWorkspaceDocsSchema>;
94
+ export type TeamWorkspaceResource = z.infer<typeof teamWorkspaceResourceSchema>;
95
+ export type TeamWorkspaceSkill = z.infer<typeof workspaceSkillReferenceSchema>;
96
+ export {};
@@ -0,0 +1,166 @@
1
+ import { z } from "zod";
2
+ const workspaceResourceModeSchema = z.enum(["mutable", "readonly"]);
3
+ const workspaceResourceSharingSchema = z.enum(["per_agent", "team"]);
4
+ export const teamWorkspaceDocsSchema = z
5
+ .object({
6
+ extras: z.record(z.string(), z.string()).optional(),
7
+ heartbeat: z.string().min(1).optional(),
8
+ identity: z.string().min(1).optional(),
9
+ memory: z.string().min(1).optional(),
10
+ soul: z.string().min(1).optional(),
11
+ system: z.string().min(1).optional()
12
+ })
13
+ .strict();
14
+ const normalizeMount = (value) => {
15
+ const trimmed = value.trim();
16
+ const workspaceRelative = trimmed.startsWith("${workspace}/")
17
+ ? `./${trimmed.slice("${workspace}/".length)}`
18
+ : trimmed;
19
+ const collapsed = workspaceRelative.replace(/\/+/g, "/");
20
+ if (collapsed.startsWith("./")) {
21
+ const relativePath = collapsed.slice(2).replace(/\/+$/u, "");
22
+ return `./${relativePath}`;
23
+ }
24
+ return collapsed.length > 1 ? collapsed.replace(/\/+$/u, "") : "/";
25
+ };
26
+ const mountHasParentSegment = (value) => value.split("/").some((segment) => segment === "..");
27
+ const resourceMountSchema = z
28
+ .string()
29
+ .trim()
30
+ .min(1)
31
+ .superRefine((value, context) => {
32
+ const normalized = normalizeMount(value);
33
+ if (mountHasParentSegment(normalized)) {
34
+ context.addIssue({
35
+ code: z.ZodIssueCode.custom,
36
+ message: "mount must not contain parent path segments"
37
+ });
38
+ }
39
+ if (normalized === "." || normalized === "./" || normalized === "${workspace}") {
40
+ context.addIssue({
41
+ code: z.ZodIssueCode.custom,
42
+ message: "mount must point inside the workspace, not at the workspace root"
43
+ });
44
+ }
45
+ if (!normalized.startsWith("/") &&
46
+ !normalized.startsWith("./") &&
47
+ !normalized.startsWith("${workspace}/")) {
48
+ context.addIssue({
49
+ code: z.ZodIssueCode.custom,
50
+ message: "mount must be an absolute POSIX path, ./ workspace path, or ${workspace}/ path"
51
+ });
52
+ }
53
+ });
54
+ const teamWorkspaceResourceGitSchema = z
55
+ .object({
56
+ branch: z.string().trim().optional(),
57
+ id: z.string().trim().min(1),
58
+ kind: z.literal("git"),
59
+ mount: resourceMountSchema,
60
+ mode: workspaceResourceModeSchema,
61
+ ref: z.string().trim().optional(),
62
+ sharing: workspaceResourceSharingSchema.optional(),
63
+ tag: z.string().trim().optional(),
64
+ url: z.string().trim().min(1)
65
+ })
66
+ .strict()
67
+ .superRefine((value, context) => {
68
+ const selectors = [value.branch, value.tag, value.ref].filter(Boolean).length;
69
+ if (selectors > 1) {
70
+ context.addIssue({
71
+ code: z.ZodIssueCode.custom,
72
+ message: "git resources may declare at most one of branch, tag, or ref"
73
+ });
74
+ }
75
+ if (value.sharing === "team") {
76
+ context.addIssue({
77
+ code: z.ZodIssueCode.custom,
78
+ message: "git resources do not support team sharing"
79
+ });
80
+ }
81
+ });
82
+ const teamWorkspaceResourceVolumeSchema = z
83
+ .object({
84
+ id: z.string().trim().min(1),
85
+ kind: z.literal("volume"),
86
+ mount: resourceMountSchema,
87
+ mode: workspaceResourceModeSchema,
88
+ name: z.string().trim().optional(),
89
+ sharing: workspaceResourceSharingSchema.optional()
90
+ })
91
+ .strict();
92
+ const teamWorkspaceResourceSchema = z.discriminatedUnion("kind", [
93
+ teamWorkspaceResourceGitSchema,
94
+ teamWorkspaceResourceVolumeSchema
95
+ ]);
96
+ const workspaceSkillRequirementSchema = z
97
+ .object({
98
+ mcp: z.array(z.string()).optional()
99
+ })
100
+ .strict();
101
+ const workspaceSkillReferenceSchema = z
102
+ .object({
103
+ ref: z.string(),
104
+ requires: workspaceSkillRequirementSchema.optional()
105
+ })
106
+ .strict();
107
+ export const teamWorkspaceSchema = z
108
+ .object({
109
+ docs: teamWorkspaceDocsSchema.optional(),
110
+ resources: z.array(teamWorkspaceResourceSchema).optional(),
111
+ skills: z.array(workspaceSkillReferenceSchema).optional()
112
+ })
113
+ .strict()
114
+ .superRefine((value, context) => {
115
+ const resources = value.resources;
116
+ if (!resources || resources.length === 0) {
117
+ return;
118
+ }
119
+ const normalizeResourceIdentity = (resource) => {
120
+ if (resource.kind === "git") {
121
+ return JSON.stringify({
122
+ branch: resource.branch?.trim() ?? "",
123
+ kind: "git",
124
+ mode: resource.mode,
125
+ mount: normalizeMount(resource.mount),
126
+ ref: resource.ref?.trim() ?? "",
127
+ sharing: resource.sharing ?? "per_agent",
128
+ tag: resource.tag?.trim() ?? "",
129
+ url: resource.url
130
+ });
131
+ }
132
+ return JSON.stringify({
133
+ kind: "volume",
134
+ mode: resource.mode,
135
+ mount: normalizeMount(resource.mount),
136
+ name: resource.name?.trim() ?? "",
137
+ sharing: resource.sharing ?? "per_agent"
138
+ });
139
+ };
140
+ for (let leftIndex = 0; leftIndex < resources.length; leftIndex += 1) {
141
+ const leftResource = resources[leftIndex];
142
+ const leftNormalizedIdentity = normalizeResourceIdentity(leftResource);
143
+ const leftMount = normalizeMount(leftResource.mount);
144
+ for (let rightIndex = leftIndex + 1; rightIndex < resources.length; rightIndex += 1) {
145
+ const rightResource = resources[rightIndex];
146
+ const rightNormalizedIdentity = normalizeResourceIdentity(rightResource);
147
+ const rightMount = normalizeMount(rightResource.mount);
148
+ if (leftResource.id === rightResource.id && leftNormalizedIdentity !== rightNormalizedIdentity) {
149
+ context.addIssue({
150
+ code: z.ZodIssueCode.custom,
151
+ message: `resource id ${leftResource.id} must use identical resource declarations`
152
+ });
153
+ }
154
+ if (leftMount === rightMount ||
155
+ leftMount.startsWith(`${rightMount}/`) ||
156
+ rightMount.startsWith(`${leftMount}/`)) {
157
+ if (leftResource.id !== rightResource.id) {
158
+ context.addIssue({
159
+ code: z.ZodIssueCode.custom,
160
+ message: `resources ${leftResource.id} and ${rightResource.id} use overlapping mounts`
161
+ });
162
+ }
163
+ }
164
+ }
165
+ }
166
+ });
@@ -18,6 +18,15 @@ export interface ContainerRuntimeInstanceReport {
18
18
  model_secrets_required: string[];
19
19
  runtime: string;
20
20
  }
21
+ export interface ContainerWorkspaceResourceReport {
22
+ backing_path: string;
23
+ id: string;
24
+ kind: "git" | "volume";
25
+ link_path: string;
26
+ mode: "mutable" | "readonly";
27
+ mount: string;
28
+ sharing: "per_agent" | "team";
29
+ }
21
30
  export interface NodeReport {
22
31
  capabilities: CapabilityReport[];
23
32
  diagnostics: DiagnosticReport[];
@@ -40,6 +49,7 @@ export interface ContainerReport {
40
49
  runtime_secrets_required: string[];
41
50
  runtimes_installed: string[];
42
51
  secrets_required: string[];
52
+ workspace_resources?: ContainerWorkspaceResourceReport[];
43
53
  }
44
54
  export interface CompileReport {
45
55
  container?: ContainerReport;
@@ -8,6 +8,7 @@ export declare const createSkillFiles: (baseDirectory: string, skills: ResolvedS
8
8
  export declare const createAgentCapabilities: (node: ResolvedAgentNode, options?: {
9
9
  mcpOutcome?: CapabilityReport["outcome"];
10
10
  sandboxOutcome?: CapabilityReport["outcome"];
11
+ scheduleMessage?: string;
12
+ scheduleOutcome?: CapabilityReport["outcome"];
11
13
  subagentOutcome?: CapabilityReport["outcome"];
12
- workspaceOutcome?: CapabilityReport["outcome"];
13
14
  }) => CapabilityReport[];
@@ -38,12 +38,12 @@ export const createAgentCapabilities = (node, options = {}) => {
38
38
  if (node.execution?.model) {
39
39
  capabilities.push(createCapability("execution.model", "supported"));
40
40
  }
41
- if (node.execution?.workspace) {
42
- capabilities.push(createCapability("execution.workspace", options.workspaceOutcome ?? "supported"));
43
- }
44
41
  if (node.execution?.sandbox) {
45
42
  capabilities.push(createCapability("execution.sandbox", options.sandboxOutcome ?? "supported"));
46
43
  }
44
+ if (node.schedule) {
45
+ capabilities.push(createCapability("agent.schedule", options.scheduleOutcome ?? "degraded", options.scheduleMessage ?? "Schedule intent is validated but no runtime scheduler is emitted yet"));
46
+ }
47
47
  if (node.surfaces?.discord) {
48
48
  capabilities.push(createCapability("surfaces.discord", "supported"));
49
49
  }
@@ -4,6 +4,7 @@ import { createCapability, createAgentCapabilities, createDocumentFiles, createS
4
4
  import { prepareTinyClawRuntimeAuth } from "./runAuth.js";
5
5
  import { createTinyClawAgentScaffold } from "./scaffold.js";
6
6
  import { assertSupportedTinyClawSurfaces, buildTinyClawChannels, resolveTinyClawSurfaceTokenBindings } from "./surfaces.js";
7
+ import { createScheduleDiagnostics, createTinyClawSchedulesFile, scheduleOutcomeFor } from "./schedules.js";
7
8
  const WORKSPACE_PLACEHOLDER = "<workspace-path>";
8
9
  const SUPPORTED_TINYCLAW_OPENAI_MODEL_PREFIXES = ["gpt-5"];
9
10
  const TINYCLAW_START_SCRIPT = `
@@ -106,6 +107,7 @@ const mergeTinyClawTargets = async (inputs) => {
106
107
  let hasWhatsappChannel = false;
107
108
  let hasTelegramChannel = false;
108
109
  const workspaceFiles = agentInputs.flatMap((input) => input.emittedFiles.filter((file) => file.path !== "settings.json"));
110
+ const schedulesFile = createTinyClawSchedulesFile(agentInputs);
109
111
  let mergedBase = null;
110
112
  for (const input of agentInputs) {
111
113
  const settings = parseJsonFile(input, "settings.json");
@@ -153,6 +155,7 @@ const mergeTinyClawTargets = async (inputs) => {
153
155
  {
154
156
  files: [
155
157
  ...workspaceFiles,
158
+ ...(schedulesFile ? [schedulesFile] : []),
156
159
  {
157
160
  content: `${JSON.stringify(mergedSettings, null, 2)}\n`,
158
161
  path: "settings.json"
@@ -207,6 +210,7 @@ export const tinyClawAdapter = {
207
210
  instancePaths: {
208
211
  configPathTemplate: "<instance-root>/tinyagi/<config-file>",
209
212
  homePathTemplate: "<instance-root>/tinyagi",
213
+ sourceWorkspacePathTemplate: "<instance-root>/workspace/<agent-name>",
210
214
  workspacePathTemplate: "<instance-root>/workspace"
211
215
  },
212
216
  port: 3777,
@@ -222,11 +226,14 @@ export const tinyClawAdapter = {
222
226
  }
223
227
  },
224
228
  async compileAgent(node) {
229
+ const scheduleOutcome = scheduleOutcomeFor(node);
225
230
  return {
226
231
  capabilities: createAgentCapabilities(node, {
227
- mcpOutcome: node.mcpServers.length > 0 ? "degraded" : "supported"
232
+ mcpOutcome: node.mcpServers.length > 0 ? "degraded" : "supported",
233
+ scheduleMessage: scheduleOutcome.message,
234
+ scheduleOutcome: scheduleOutcome.outcome
228
235
  }),
229
- diagnostics: [],
236
+ diagnostics: createScheduleDiagnostics(node),
230
237
  files: [
231
238
  ...createDocumentFiles(`workspace/${node.name}`, node.docs),
232
239
  ...createSkillFiles(`workspace/${node.name}/.agents/skills`, node.skills),
@@ -0,0 +1,9 @@
1
+ import type { ResolvedAgentNode } from "../../compiler/types.js";
2
+ import type { CapabilityReport } from "../../report/index.js";
3
+ import type { ContainerTargetInput, EmittedFile } from "../types.js";
4
+ export declare const createTinyClawSchedulesFile: (inputs: ContainerTargetInput[]) => EmittedFile | null;
5
+ export declare const scheduleOutcomeFor: (node: ResolvedAgentNode) => {
6
+ message?: string;
7
+ outcome?: CapabilityReport["outcome"];
8
+ };
9
+ export declare const createScheduleDiagnostics: (node: ResolvedAgentNode) => import("../../report/types.js").DiagnosticReport[];