@lunnoa/client 2.4.0 → 3.0.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 +24 -1
- package/dist/cli.js +1242 -107
- package/dist/index.cjs +473 -56
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1294 -195
- package/dist/index.d.ts +1294 -195
- package/dist/index.js +471 -57
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +253 -26
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1051 -143
- package/dist/react.d.ts +1051 -143
- package/dist/react.js +253 -26
- package/dist/react.js.map +1 -1
- package/package.json +4 -2
- package/schemas/agent.schema.json +128 -0
- package/schemas/workflow.schema.json +156 -0
- package/skills/lunnoa-client/SKILL.md +178 -9
- package/skills/lunnoa-solution-design/SKILL.md +12 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunnoa/client",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "TypeScript SDK for building applications on top of Lunnoa Automate — typed client for the Public API (agents, workflows, executions, entities, knowledge, queues), SSE agent chat streaming, and deployment-specific codegen",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
"dist",
|
|
27
|
-
"skills"
|
|
27
|
+
"skills",
|
|
28
|
+
"schemas"
|
|
28
29
|
],
|
|
29
30
|
"scripts": {
|
|
30
31
|
"build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsup",
|
|
@@ -34,6 +35,7 @@
|
|
|
34
35
|
"typecheck": "tsc --noEmit",
|
|
35
36
|
"check:codegen-sample": "node --experimental-strip-types scripts/check-codegen-sample.ts && tsc --noEmit -p scripts/tsconfig.codegen-sample.json",
|
|
36
37
|
"test:acceptance": "tsx scripts/acceptance-soak.ts",
|
|
38
|
+
"test:sync-acceptance": "tsx scripts/sync-acceptance.ts",
|
|
37
39
|
"prepare": "node -e \"for (const d of ['.cursor/skills', '.claude/skills']) require('fs').cpSync('skills', d, { recursive: true, force: true })\"",
|
|
38
40
|
"prepublishOnly": "pnpm run build",
|
|
39
41
|
"release": "semantic-release"
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://lunnoa.com/schemas/agent.schema.json",
|
|
4
|
+
"title": "Lunnoa agent definition",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["kind", "slug", "name", "instructions"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"$schema": { "type": "string" },
|
|
10
|
+
"kind": { "const": "agent" },
|
|
11
|
+
"slug": { "type": "string", "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$" },
|
|
12
|
+
"name": { "type": "string" },
|
|
13
|
+
"description": { "type": "string" },
|
|
14
|
+
"model": { "type": "string" },
|
|
15
|
+
"aiConnection": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"additionalProperties": false,
|
|
18
|
+
"required": ["id"],
|
|
19
|
+
"properties": { "id": { "type": "string" } }
|
|
20
|
+
},
|
|
21
|
+
"instructions": {
|
|
22
|
+
"oneOf": [
|
|
23
|
+
{ "type": "string" },
|
|
24
|
+
{
|
|
25
|
+
"type": "object",
|
|
26
|
+
"additionalProperties": false,
|
|
27
|
+
"required": ["file"],
|
|
28
|
+
"properties": { "file": { "type": "string" } }
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
"settings": { "$ref": "#/$defs/settings" },
|
|
33
|
+
"tools": {
|
|
34
|
+
"type": "array",
|
|
35
|
+
"items": { "$ref": "#/$defs/tool" }
|
|
36
|
+
},
|
|
37
|
+
"workflows": { "type": "array", "items": { "$ref": "#/$defs/slugOrId" } },
|
|
38
|
+
"agents": { "type": "array", "items": { "$ref": "#/$defs/slugOrId" } },
|
|
39
|
+
"connections": { "type": "array", "items": { "$ref": "#/$defs/idRef" } },
|
|
40
|
+
"knowledge": { "type": "array", "items": { "$ref": "#/$defs/idRef" } },
|
|
41
|
+
"variables": { "type": "array", "items": { "$ref": "#/$defs/idRef" } },
|
|
42
|
+
"objects": { "type": "array", "items": { "$ref": "#/$defs/idRef" } },
|
|
43
|
+
"objectTypes": { "type": "array", "items": { "$ref": "#/$defs/idRef" } },
|
|
44
|
+
"mcpServers": {
|
|
45
|
+
"type": "array",
|
|
46
|
+
"items": {
|
|
47
|
+
"type": "object",
|
|
48
|
+
"additionalProperties": false,
|
|
49
|
+
"required": ["id"],
|
|
50
|
+
"properties": {
|
|
51
|
+
"id": { "type": "string" },
|
|
52
|
+
"connectionId": { "type": "string" }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"a2aAgents": { "type": "array", "items": { "$ref": "#/$defs/idRef" } }
|
|
57
|
+
},
|
|
58
|
+
"$defs": {
|
|
59
|
+
"idRef": {
|
|
60
|
+
"type": "object",
|
|
61
|
+
"additionalProperties": false,
|
|
62
|
+
"required": ["id"],
|
|
63
|
+
"properties": { "id": { "type": "string" } }
|
|
64
|
+
},
|
|
65
|
+
"slugOrId": {
|
|
66
|
+
"type": "object",
|
|
67
|
+
"additionalProperties": false,
|
|
68
|
+
"minProperties": 1,
|
|
69
|
+
"properties": {
|
|
70
|
+
"slug": { "type": "string" },
|
|
71
|
+
"id": { "type": "string" }
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"tool": {
|
|
75
|
+
"type": "object",
|
|
76
|
+
"additionalProperties": false,
|
|
77
|
+
"required": ["key", "id", "name", "input"],
|
|
78
|
+
"properties": {
|
|
79
|
+
"key": { "type": "string" },
|
|
80
|
+
"id": { "type": "string" },
|
|
81
|
+
"appId": { "type": "string" },
|
|
82
|
+
"connection": { "$ref": "#/$defs/idRef" },
|
|
83
|
+
"name": { "type": "string" },
|
|
84
|
+
"description": { "type": "string" },
|
|
85
|
+
"input": { "type": "object" }
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
"settings": {
|
|
89
|
+
"type": "object",
|
|
90
|
+
"additionalProperties": false,
|
|
91
|
+
"properties": {
|
|
92
|
+
"temperature": { "type": "number" },
|
|
93
|
+
"topP": { "type": "number" },
|
|
94
|
+
"maxOutputTokens": { "type": ["number", "null"] },
|
|
95
|
+
"frequencyPenalty": { "type": "number" },
|
|
96
|
+
"presencePenalty": { "type": "number" },
|
|
97
|
+
"seed": { "type": ["number", "null"] },
|
|
98
|
+
"maxRetries": { "type": "number" },
|
|
99
|
+
"maxToolRoundtrips": { "type": "number" },
|
|
100
|
+
"messageLookbackLimit": { "type": "number" },
|
|
101
|
+
"taskNamingInstructions": { "type": ["string", "null"] },
|
|
102
|
+
"enabledBuiltInTools": { "type": "array", "items": { "type": "string" } },
|
|
103
|
+
"enabledPlatformTools": { "type": "array", "items": { "type": "string" } },
|
|
104
|
+
"enabledToolPolicies": { "type": "array", "items": { "type": "string" } },
|
|
105
|
+
"forcedTools": {},
|
|
106
|
+
"suggestedPrompts": { "type": "array", "items": { "type": "string" } },
|
|
107
|
+
"dynamicActionDiscoveryEnabled": { "type": "boolean" },
|
|
108
|
+
"dynamicMcpDiscoveryEnabled": { "type": "boolean" },
|
|
109
|
+
"dynamicSelfInstructionsEnabled": { "type": "boolean" },
|
|
110
|
+
"memoryEnabled": { "type": "boolean" },
|
|
111
|
+
"memoryScope": { "type": "string" },
|
|
112
|
+
"memoryRetentionDays": { "type": "number" },
|
|
113
|
+
"enablePlanning": { "type": "boolean" },
|
|
114
|
+
"requirePlanApproval": { "type": "boolean" },
|
|
115
|
+
"enableToolResultOffload": { "type": "boolean" },
|
|
116
|
+
"toolResultOffloadThresholdBytes": { "type": "number" },
|
|
117
|
+
"longHorizonMode": { "type": "boolean" },
|
|
118
|
+
"longHorizonMaxSteps": { "type": ["number", "null"] },
|
|
119
|
+
"longHorizonMaxTokensPerRun": { "type": ["number", "null"] },
|
|
120
|
+
"piiRoutingMode": { "type": "string" },
|
|
121
|
+
"piiFallbackConnectionId": { "type": ["string", "null"] },
|
|
122
|
+
"piiFallbackModelId": { "type": ["string", "null"] },
|
|
123
|
+
"webAccess": { "type": ["boolean", "null"] },
|
|
124
|
+
"phoneAccess": { "type": ["boolean", "null"] }
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://lunnoa.com/schemas/workflow.schema.json",
|
|
4
|
+
"title": "Lunnoa workflow definition",
|
|
5
|
+
"oneOf": [
|
|
6
|
+
{ "$ref": "#/$defs/steps" },
|
|
7
|
+
{ "$ref": "#/$defs/graph" }
|
|
8
|
+
],
|
|
9
|
+
"$defs": {
|
|
10
|
+
"idRef": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"additionalProperties": false,
|
|
13
|
+
"required": ["id"],
|
|
14
|
+
"properties": { "id": { "type": "string" } }
|
|
15
|
+
},
|
|
16
|
+
"retrySettings": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"additionalProperties": false,
|
|
19
|
+
"required": ["enabled", "maxAttempts", "delaySeconds"],
|
|
20
|
+
"properties": {
|
|
21
|
+
"enabled": { "type": "boolean" },
|
|
22
|
+
"maxAttempts": { "type": "number" },
|
|
23
|
+
"delaySeconds": { "type": "number" }
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"continueOnFailure": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"additionalProperties": false,
|
|
29
|
+
"required": ["enabled"],
|
|
30
|
+
"properties": { "enabled": { "type": "boolean" } }
|
|
31
|
+
},
|
|
32
|
+
"use": {
|
|
33
|
+
"type": "object",
|
|
34
|
+
"additionalProperties": false,
|
|
35
|
+
"required": ["id"],
|
|
36
|
+
"properties": {
|
|
37
|
+
"id": { "type": "string" },
|
|
38
|
+
"appId": { "type": "string" },
|
|
39
|
+
"connection": { "$ref": "#/$defs/idRef" }
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"step": {
|
|
43
|
+
"type": "object",
|
|
44
|
+
"additionalProperties": false,
|
|
45
|
+
"required": ["key", "name", "use", "input"],
|
|
46
|
+
"properties": {
|
|
47
|
+
"key": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_-]*$" },
|
|
48
|
+
"name": { "type": "string" },
|
|
49
|
+
"description": { "type": "string" },
|
|
50
|
+
"use": { "$ref": "#/$defs/use" },
|
|
51
|
+
"input": { "type": "object" },
|
|
52
|
+
"retrySettings": { "$ref": "#/$defs/retrySettings" },
|
|
53
|
+
"continueOnFailure": { "$ref": "#/$defs/continueOnFailure" },
|
|
54
|
+
"paths": {
|
|
55
|
+
"type": "array",
|
|
56
|
+
"items": {
|
|
57
|
+
"type": "object",
|
|
58
|
+
"additionalProperties": false,
|
|
59
|
+
"required": ["label", "steps"],
|
|
60
|
+
"properties": {
|
|
61
|
+
"label": { "type": "string" },
|
|
62
|
+
"when": { "type": "array" },
|
|
63
|
+
"steps": {
|
|
64
|
+
"type": "array",
|
|
65
|
+
"items": { "$ref": "#/$defs/step" }
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"steps": {
|
|
73
|
+
"type": "object",
|
|
74
|
+
"additionalProperties": false,
|
|
75
|
+
"required": ["kind", "format", "slug", "name", "trigger", "steps"],
|
|
76
|
+
"properties": {
|
|
77
|
+
"$schema": { "type": "string" },
|
|
78
|
+
"kind": { "const": "workflow" },
|
|
79
|
+
"format": { "const": "steps" },
|
|
80
|
+
"slug": { "type": "string", "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$" },
|
|
81
|
+
"name": { "type": "string" },
|
|
82
|
+
"description": { "type": "string" },
|
|
83
|
+
"trigger": {
|
|
84
|
+
"type": "object",
|
|
85
|
+
"additionalProperties": false,
|
|
86
|
+
"required": ["id", "name"],
|
|
87
|
+
"properties": {
|
|
88
|
+
"id": { "type": "string" },
|
|
89
|
+
"appId": { "type": "string" },
|
|
90
|
+
"connection": { "$ref": "#/$defs/idRef" },
|
|
91
|
+
"input": { "type": "object" },
|
|
92
|
+
"inputs": { "type": "array" },
|
|
93
|
+
"name": { "type": "string" },
|
|
94
|
+
"description": { "type": "string" }
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"steps": {
|
|
98
|
+
"type": "array",
|
|
99
|
+
"minItems": 1,
|
|
100
|
+
"items": { "$ref": "#/$defs/step" }
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"graphNode": {
|
|
105
|
+
"type": "object",
|
|
106
|
+
"required": ["key", "nodeType", "appId", "name", "value"],
|
|
107
|
+
"properties": {
|
|
108
|
+
"id": { "type": "string" },
|
|
109
|
+
"key": { "type": "string" },
|
|
110
|
+
"nodeType": { "enum": ["trigger", "action"] },
|
|
111
|
+
"appId": { "type": "string" },
|
|
112
|
+
"actionId": { "type": "string" },
|
|
113
|
+
"triggerId": { "type": "string" },
|
|
114
|
+
"name": { "type": "string" },
|
|
115
|
+
"description": { "type": "string" },
|
|
116
|
+
"value": { "type": "object" },
|
|
117
|
+
"retrySettings": { "type": "object" },
|
|
118
|
+
"continueOnFailure": { "type": "object" }
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
"graph": {
|
|
122
|
+
"type": "object",
|
|
123
|
+
"additionalProperties": false,
|
|
124
|
+
"required": ["kind", "format", "slug", "name", "nodes", "edges"],
|
|
125
|
+
"properties": {
|
|
126
|
+
"$schema": { "type": "string" },
|
|
127
|
+
"kind": { "const": "workflow" },
|
|
128
|
+
"format": { "const": "graph" },
|
|
129
|
+
"slug": { "type": "string", "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$" },
|
|
130
|
+
"name": { "type": "string" },
|
|
131
|
+
"description": { "type": "string" },
|
|
132
|
+
"nodes": {
|
|
133
|
+
"type": "array",
|
|
134
|
+
"minItems": 1,
|
|
135
|
+
"items": { "$ref": "#/$defs/graphNode" }
|
|
136
|
+
},
|
|
137
|
+
"edges": {
|
|
138
|
+
"type": "array",
|
|
139
|
+
"items": {
|
|
140
|
+
"type": "object",
|
|
141
|
+
"additionalProperties": false,
|
|
142
|
+
"required": ["source", "target"],
|
|
143
|
+
"properties": {
|
|
144
|
+
"id": { "type": "string" },
|
|
145
|
+
"source": { "type": "string" },
|
|
146
|
+
"target": { "type": "string" },
|
|
147
|
+
"sourceHandle": { "type": "string" },
|
|
148
|
+
"targetHandle": { "type": "string" },
|
|
149
|
+
"type": { "type": "string" }
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -4,7 +4,7 @@ description: >-
|
|
|
4
4
|
Builds custom frontends and backends on top of a Lunnoa Automate deployment
|
|
5
5
|
using the @lunnoa/client TypeScript SDK. App login is always a Lunnoa user
|
|
6
6
|
(createBrowserClient + lunnoa.auth.login / refresh / SSO). lna_ API keys are
|
|
7
|
-
for the CLI only (discovery, codegen, deploy), never for the app session and
|
|
7
|
+
for the CLI only (discovery, codegen, deploy, pull, push), never for the app session and
|
|
8
8
|
never in a browser. Covers discovery of the deployment's agents, workflows, and entity
|
|
9
9
|
types, running the deployment codegen first for typed accessors, querying
|
|
10
10
|
and creating entities, triggering workflows and polling executions,
|
|
@@ -52,7 +52,7 @@ Run these before inventing resources or shipping UI code:
|
|
|
52
52
|
1. **Auth** — Lunnoa user JWT via `createBrowserClient` + `lunnoa.auth.login`
|
|
53
53
|
(or SSO). **Never** call Lunnoa as the app's users with an `lna_` key, and
|
|
54
54
|
**never** put `lna_` in the browser (SDK throws when `window` exists).
|
|
55
|
-
`lna_` keys are for CLI discovery, codegen, and
|
|
55
|
+
`lna_` keys are for CLI discovery, codegen, deploy, pull, and push only.
|
|
56
56
|
2. **Codegen** — When building typed UIs, run
|
|
57
57
|
`npx @lunnoa/client codegen` against the deployment first.
|
|
58
58
|
3. **Discover before inventing** — prefer the CLI (JSON for agents), then
|
|
@@ -67,10 +67,16 @@ Run these before inventing resources or shipping UI code:
|
|
|
67
67
|
Do not invent catalogue action ids or model ids. `connections list` is
|
|
68
68
|
app connections (Gmail, HTTP). `ai-connections list` is the model
|
|
69
69
|
provider connection and the `models[].id` values `defineAgent` accepts.
|
|
70
|
-
4. **Run vs
|
|
71
|
-
`workflows.execute`. Durable
|
|
72
|
-
`npx
|
|
73
|
-
|
|
70
|
+
4. **Run vs save** — Ad-hoc: `actions.run` / `runs.start` /
|
|
71
|
+
`workflows.execute`. Durable agents and workflows default to JSON in
|
|
72
|
+
`lunnoa/` (`npx lunnoa-client status`, then `pull`, then `push`).
|
|
73
|
+
That sets `codeSource: synced`. TypeScript
|
|
74
|
+
`npx @lunnoa/client workflows|agents deploy` is only for a definition
|
|
75
|
+
that needs code. That sets `codeSource: generated` and locks the UI.
|
|
76
|
+
A `.json` deploy is a push. `codeSource` replaces `managedByCode` on
|
|
77
|
+
agents and workflows. Tables and entity types still use
|
|
78
|
+
`managedByCode`. Upsert is by slug inside the workspace on the API
|
|
79
|
+
key. See section 5d.
|
|
74
80
|
5. **Toolkit boundary** — Client `defineAction` ≠ toolkit `createAction`.
|
|
75
81
|
|
|
76
82
|
---
|
|
@@ -85,7 +91,7 @@ app does not keep its own user directory and does not call Lunnoa with an
|
|
|
85
91
|
| Caller | Credential |
|
|
86
92
|
|---|---|
|
|
87
93
|
| **Person using the app** | `createBrowserClient({ baseUrl })` + `lunnoa.auth.login` (password, 2FA, or SSO). Permissions, agent shares, and task history are that user's. |
|
|
88
|
-
| **CLI (codegen, discovery, deploy)** | `lna_` API key in `.env` / `--api-key`. Not the app session. |
|
|
94
|
+
| **CLI (codegen, discovery, deploy, pull, push)** | `lna_` API key in `.env` / `--api-key`. Not the app session. |
|
|
89
95
|
|
|
90
96
|
Hard rules:
|
|
91
97
|
|
|
@@ -455,6 +461,42 @@ Wire step settings in code with `ref`. Do not leave them for the builder.
|
|
|
455
461
|
node id across deploys, so run history and builder mappings stay attached.
|
|
456
462
|
The trigger keeps its id while its type and settings stay the same.
|
|
457
463
|
|
|
464
|
+
### Branches (paths)
|
|
465
|
+
|
|
466
|
+
Put `paths` on the **last** step of a list. Steps after the branch go inside
|
|
467
|
+
each path. Paths do not merge back together.
|
|
468
|
+
|
|
469
|
+
```ts
|
|
470
|
+
const approval = defineAction({ id: 'flow-control_action_request-approval', input: { /* from tools get */ } });
|
|
471
|
+
const split = defineAction({ id: 'flow-control_action_conditional-paths' });
|
|
472
|
+
|
|
473
|
+
steps: [
|
|
474
|
+
{ key: 'review', use: approval, paths: [
|
|
475
|
+
{ label: 'Approved', steps: [{ key: 'apply', use: applyChange }] },
|
|
476
|
+
{ label: 'Rejected', steps: [{ use: notify, input: { text: ref.step('review', 'comment') } }] },
|
|
477
|
+
] },
|
|
478
|
+
]
|
|
479
|
+
|
|
480
|
+
// Conditional Paths: groups in `when` are OR-ed, conditions in a group AND-ed.
|
|
481
|
+
{ key: 'route', use: split, paths: [
|
|
482
|
+
{ label: 'Large', when: [[{ field: ref.step('load', 'amount'), condition: 'is_greater_than', value: '1000' }]], steps: [...] },
|
|
483
|
+
{ label: 'Other', steps: [...] }, // no `when`: always taken
|
|
484
|
+
] }
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
- `paths` works on Request Approval (at most two: approved first, then
|
|
488
|
+
rejected; labels `Approved` / `Rejected`), Conditional Paths (with `when`),
|
|
489
|
+
and Manually Decide Path (no `when`).
|
|
490
|
+
- `when` conditions: `field` is exactly one `ref.trigger` / `ref.step`.
|
|
491
|
+
`condition` is one of `PATH_CONDITIONS` (equals, contains, is_empty,
|
|
492
|
+
is_true, is_greater_than, is_less_than, matches_regex, is_in_the_past, ...).
|
|
493
|
+
`value` is text; omit it for exists / is_empty / is_true style conditions.
|
|
494
|
+
- Conditional Paths takes every path whose `when` matches. A path without
|
|
495
|
+
`when` always runs. For if/else, give the else path the opposite condition.
|
|
496
|
+
- Steps on a path can `ref.step` the branching step and earlier steps on
|
|
497
|
+
the same path, not steps on another path.
|
|
498
|
+
- `runs.start` rejects `paths`. Deploy, then execute the workflow.
|
|
499
|
+
|
|
458
500
|
Discover the catalogue **before** writing `defineAction` (do not guess ids):
|
|
459
501
|
|
|
460
502
|
```bash
|
|
@@ -486,11 +528,138 @@ npx @lunnoa/client workflows deploy ./ping.ts --url … --api-key …
|
|
|
486
528
|
npx @lunnoa/client agents deploy ./triage.ts --url … --api-key …
|
|
487
529
|
```
|
|
488
530
|
|
|
531
|
+
Those two commands deploy TypeScript and set `codeSource: generated`
|
|
532
|
+
(locked in the UI). For the JSON round-trip, use section 5d. The binary
|
|
533
|
+
name is `lunnoa-client`, so `npx lunnoa-client` and `npx @lunnoa/client`
|
|
534
|
+
are the same command.
|
|
535
|
+
|
|
489
536
|
`defineAction` binds catalogue actions only. Toolkit `createAction` is a
|
|
490
537
|
different job (in-platform apps).
|
|
491
538
|
|
|
492
539
|
---
|
|
493
540
|
|
|
541
|
+
## 5d. Sync JSON definitions with the UI
|
|
542
|
+
|
|
543
|
+
Default for a saved agent or workflow: a JSON file in `lunnoa/`, then
|
|
544
|
+
`push`. Use a TypeScript `defineAgent` / `defineWorkflow` file only when
|
|
545
|
+
the definition needs code (you also pass it to `runs.start`, or the
|
|
546
|
+
tools are built in code). TypeScript deploy sets `codeSource: generated`
|
|
547
|
+
and the UI badge **Managed by code**. The UI will not edit it. Deploy
|
|
548
|
+
the TypeScript file again to change it.
|
|
549
|
+
|
|
550
|
+
`codeSource` (`none` | `synced` | `generated`) replaces `managedByCode`
|
|
551
|
+
on agents and workflows. Tables and entity types still use
|
|
552
|
+
`managedByCode`.
|
|
553
|
+
|
|
554
|
+
Folder (default `./lunnoa`, or `definitionsDir` in `lunnoa.config.*`, or
|
|
555
|
+
`--dir`):
|
|
556
|
+
|
|
557
|
+
```text
|
|
558
|
+
lunnoa/agents/<slug>.json
|
|
559
|
+
lunnoa/agents/<slug>.instructions.md
|
|
560
|
+
lunnoa/workflows/<slug>.json
|
|
561
|
+
lunnoa/lunnoa.lock.json
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
The JSON holds `"instructions": { "file": "<slug>.instructions.md" }`.
|
|
565
|
+
`pull` writes that split. A hand-written file may use a string. Every
|
|
566
|
+
JSON file starts with `$schema`, a relative path to
|
|
567
|
+
`node_modules/@lunnoa/client/schemas/agent.schema.json` or
|
|
568
|
+
`workflow.schema.json` (draft 2020-12). The CLI strips `$schema` before
|
|
569
|
+
sending and checks the file shape (slug, instructions, `use.id`, unique
|
|
570
|
+
keys, one trigger on a graph). Commit `lunnoa.lock.json`. It stores id,
|
|
571
|
+
revision, and file hash per deployment key
|
|
572
|
+
`<base url>#<workspace id>`.
|
|
573
|
+
|
|
574
|
+
Not in the file, and push does not change them: credentials (connection
|
|
575
|
+
ids only), canvas positions, the workflow on/off switch (`isActive`),
|
|
576
|
+
profile image, shares, memories, skills. Pull warns, and still writes
|
|
577
|
+
the file, when an input field name looks like a secret
|
|
578
|
+
(`authorization`, `token`, `secret`, `password`, `apiKey`, `bearer`,
|
|
579
|
+
`cookie`, `privateKey`, …) and the value is a literal string. Keep that
|
|
580
|
+
value on a Lunnoa connection.
|
|
581
|
+
|
|
582
|
+
Workflow pull writes `"format": "steps"` when a trigger plus a step list
|
|
583
|
+
round-trips. It writes `"format": "graph"` when steps would drop
|
|
584
|
+
something (paths that join, a for-each loop). Edit the format pull
|
|
585
|
+
wrote.
|
|
586
|
+
|
|
587
|
+
Commands (reads `LUNNOA_URL` and `LUNNOA_API_KEY` from the environment
|
|
588
|
+
or `./.env`):
|
|
589
|
+
|
|
590
|
+
```bash
|
|
591
|
+
npx lunnoa-client status
|
|
592
|
+
npx lunnoa-client status --json
|
|
593
|
+
npx lunnoa-client pull
|
|
594
|
+
npx lunnoa-client pull <slug>
|
|
595
|
+
npx lunnoa-client pull agents/<slug>
|
|
596
|
+
npx lunnoa-client pull workflows/<slug>
|
|
597
|
+
npx lunnoa-client pull --all
|
|
598
|
+
npx lunnoa-client push
|
|
599
|
+
npx lunnoa-client push <path-or-slug>
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
Run `status` before editing. Run `pull` before `push` when the UI has
|
|
603
|
+
moved. `pull` with no slug updates lock entries for this deployment.
|
|
604
|
+
`pull --all` fetches every agent and workflow in the workspace. `push`
|
|
605
|
+
with no path sends every JSON file, workflows first, then agents, with
|
|
606
|
+
`codeSource: synced` and `baseRevision` from the lock. On success it
|
|
607
|
+
rewrites the file from the stored definition and updates the lock.
|
|
608
|
+
|
|
609
|
+
`status` columns: `LOCAL` is `unchanged`, `modified`, `new`, or
|
|
610
|
+
`missing`. `REMOTE` is `unchanged`, `changed in UI`, or
|
|
611
|
+
`missing in Lunnoa`. `CONFLICT` is true when both sides differ from the
|
|
612
|
+
lock. `--json` prints `{ kind, slug, local, remote, conflict }`.
|
|
613
|
+
`remote` uses the words `changed in UI`.
|
|
614
|
+
|
|
615
|
+
How to read a conflict:
|
|
616
|
+
|
|
617
|
+
- Pull exits 1 before it writes anything. The message starts with
|
|
618
|
+
`Local and server both changed. Run pull --force to overwrite.` Then
|
|
619
|
+
a diff: `--- local` / `+++ server`. `-` lines are the file. `+` lines
|
|
620
|
+
are the server. Secret values in that diff are `***`.
|
|
621
|
+
- Push exits 1 and prints the server text. Stale lock:
|
|
622
|
+
`Changed in the UI since your last pull. Run pull first. (currentRevision: …)`.
|
|
623
|
+
First push of a file whose slug already exists and is not in the lock:
|
|
624
|
+
`A workflow with slug "<slug>" already exists in Lunnoa. Run pull first, or push with --force.`
|
|
625
|
+
Agents use `agent` in that sentence. Pull that slug, then push.
|
|
626
|
+
- Ask the human before `--force`. `pull --force` overwrites the local
|
|
627
|
+
edit with the UI. `push --force` overwrites the UI with the file.
|
|
628
|
+
`agents deploy file.ts --force` does not send the flag. A `.json`
|
|
629
|
+
deploy is a push and does.
|
|
630
|
+
|
|
631
|
+
To turn a generated resource into a synced one: `pull` the slug, then
|
|
632
|
+
`push` the JSON. After that the UI can edit it, unless the workspace
|
|
633
|
+
switch **Lock code-synced resources** is on (Admin Panel, workspace,
|
|
634
|
+
Settings, LLM Audit Policy card). That switch makes synced resources
|
|
635
|
+
read-only and shows **Managed by code**. Badges: **Synced with code**,
|
|
636
|
+
**Changed in UI, not yet in code**, **Managed by code**. A successful
|
|
637
|
+
push clears **Changed in UI, not yet in code**. The docs CI recipe runs
|
|
638
|
+
that push on main.
|
|
639
|
+
|
|
640
|
+
App code may pass a parsed file to the factories. `$schema` is stripped.
|
|
641
|
+
`defineAgent` keeps `instructions: { file }` as that object. The CLI
|
|
642
|
+
reads the markdown on push. Do not upsert from the app for this loop.
|
|
643
|
+
|
|
644
|
+
```typescript
|
|
645
|
+
import { readFileSync } from 'node:fs';
|
|
646
|
+
import { defineAgent, defineWorkflow } from '@lunnoa/client';
|
|
647
|
+
|
|
648
|
+
const agent = defineAgent(
|
|
649
|
+
JSON.parse(readFileSync('lunnoa/agents/triage.json', 'utf8')),
|
|
650
|
+
);
|
|
651
|
+
const workflow = defineWorkflow(
|
|
652
|
+
JSON.parse(readFileSync('lunnoa/workflows/sync-orders.json', 'utf8')),
|
|
653
|
+
);
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
Human detail: Lunnoa Docs, Develop, TypeScript client SDK, Sync with the
|
|
657
|
+
UI (`/guides/develop/client/sync`), including the CI pull request and
|
|
658
|
+
the main-branch push. Do not add that workflow to a brand-new repo that
|
|
659
|
+
has no secrets.
|
|
660
|
+
|
|
661
|
+
---
|
|
662
|
+
|
|
494
663
|
## 6. Recipe: resume a NEEDS_INPUT execution from a custom form
|
|
495
664
|
|
|
496
665
|
Workflows can pause for human input. The waiting step appears in
|
|
@@ -532,7 +701,7 @@ for await (const chunk of stream) {
|
|
|
532
701
|
}
|
|
533
702
|
```
|
|
534
703
|
|
|
535
|
-
A turn from an app runs in **preview**: 10 model rounds, so the agent can call tools a few times and still send a final reply. The SDK sends `chatMode: 'preview'` for you. Do not pass `chatMode: 'builder'` from an app. That is the agent-editor interview: 4 rounds, and it stops a tool-using agent halfway. The workflow then fails with "Agent did not generate a response" even when a tool already saved the data. A workflow Message Agent step uses the same preview budget. For a job that chains many tool calls (read every record, then write a result), turn on **Long-horizon mode** on that agent in Lunnoa. That raises the budget to 50 rounds. `defineAgent`
|
|
704
|
+
A turn from an app runs in **preview**: 10 model rounds, so the agent can call tools a few times and still send a final reply. The SDK sends `chatMode: 'preview'` for you. Do not pass `chatMode: 'builder'` from an app. That is the agent-editor interview: 4 rounds, and it stops a tool-using agent halfway. The workflow then fails with "Agent did not generate a response" even when a tool already saved the data. A workflow Message Agent step uses the same preview budget. For a job that chains many tool calls (read every record, then write a result), turn on **Long-horizon mode** on that agent in Lunnoa. That raises the budget to 50 rounds. A JSON definition includes `settings.longHorizonMode`, and a push writes it. A TypeScript `defineAgent` does not set the switch, and a later TypeScript deploy leaves it as you set it.
|
|
536
705
|
|
|
537
706
|
- **React apps:** prefer `useAgentChat` from `@lunnoa/client/react`, which wraps AI SDK `useChat` + `DefaultChatTransport`
|
|
538
707
|
pointed at
|
|
@@ -630,7 +799,7 @@ with emails (capped; not Azure object IDs).
|
|
|
630
799
|
3. Call `discovery.enabledFeatures()` and hide unsupported areas.
|
|
631
800
|
4. Build forms/tables from `attributeSchema` / `customInputConfig` (or the generated types).
|
|
632
801
|
5. Render workflow progress from `executionPath`; handle `NEEDS_INPUT` with `submitInput`.
|
|
633
|
-
6. For single-action calls, use `actions.run`; for linear multi-step, use `runs.start` / `defineWorkflow`.
|
|
802
|
+
6. For single-action calls, use `actions.run`; for linear multi-step, use `runs.start` / `defineWorkflow`. Save agents and workflows as JSON in `lunnoa/` and use `npx lunnoa-client status`, then `pull`, then `push` (section 5d). Ask the human before `--force`. Use `npx @lunnoa/client workflows|agents deploy` for a TypeScript file that needs code (`codeSource: generated`). Discover actions via `npx @lunnoa/client tools list|get|search` (or `workflowApps.list()`). Discover triggers via `apps get <appId> --json` and pass `defineTrigger` on `defineWorkflow` (omit it for Manually Run with no fields). `runs.start` does not save the trigger. Pass trigger inputs and earlier step outputs into step settings with `ref.trigger` / `ref.step` (key the source step). Branch with `paths` on Request Approval or Conditional Paths (last step only). Discover agent models via `npx @lunnoa/client ai-connections list --json` (or `aiConnections.list()`).
|
|
634
803
|
7. For Request Approval nodes, use `approvals.inbox` / `approvals.decide` (or `executions.submitApproval`).
|
|
635
804
|
8. Chat: stream via SSE, handle resume (reconnects) and stop, load history from the task.
|
|
636
805
|
9. Handle `LunnoaApiError`: 401 → re-auth; 403 → hide the capability; 429 → back off.
|
|
@@ -74,7 +74,7 @@ interrogation.
|
|
|
74
74
|
| End users | Auth |
|
|
75
75
|
|---|---|
|
|
76
76
|
| People using the app | Lunnoa user JWT: `createBrowserClient` + `lunnoa.auth.login` (or SSO). Provision missing people with invite, SSO, or SCIM first. |
|
|
77
|
-
| Coding agent / CLI | `LUNNOA_API_KEY` for discovery, codegen, and
|
|
77
|
+
| Coding agent / CLI | `LUNNOA_API_KEY` for discovery, codegen, deploy, pull, and push. Not the app session. |
|
|
78
78
|
| Anonymous embed chat (no full login) | Out of scope for v1 client path; scoped embed tokens are a later platform bet |
|
|
79
79
|
|
|
80
80
|
---
|
|
@@ -86,7 +86,7 @@ interrogation.
|
|
|
86
86
|
| One catalogue call | `actions.run` | One-step Execution (`source: SDK`) |
|
|
87
87
|
| Linear multi-step, experiment | `runs.start` / `defineWorkflow` then run | One Execution; optional later deploy |
|
|
88
88
|
| Reusable automation + triggers | Saved **Workflow** (`defineWorkflow` + `defineTrigger`, or the UI) | Discover trigger ids with `apps get <appId> --json`. Omit `trigger` for Manually Run with no fields. `runs.start` ignores the trigger. Wire step settings to trigger fields and earlier steps with `ref.trigger` / `ref.step`. |
|
|
89
|
-
| Branches (approve / reject, if/else) |
|
|
89
|
+
| Branches (approve / reject, if/else) | `defineWorkflow` with `paths` on Request Approval or Conditional Paths | The steps format does not merge paths. Repeat shared steps in each path, or build the join in the UI and `pull`: the file becomes graph JSON and `push` keeps the join. A TypeScript deploy (`codeSource: generated`) replaces the stored graph and locks the UI. |
|
|
90
90
|
| Reasoning + tools + conversation | **Agent** | Tools = workflows, actions, knowledge |
|
|
91
91
|
| Structured CRUD / portals | **Entities** + schemas | Codegen for typed accessors |
|
|
92
92
|
| Reference / lookup data | **Tables** | `tables.upsertBySlug`; workflows use the Tables Lookup action |
|
|
@@ -131,8 +131,13 @@ interrogation.
|
|
|
131
131
|
as a fixed value, "from trigger `caseId`", or "from step `load` →
|
|
132
132
|
`rows.0.status`". The build uses `ref.trigger` / `ref.step` for these, so
|
|
133
133
|
"connect it in the builder" is not a fallback for data wiring.
|
|
134
|
-
5. **Durable vs ephemeral:** experiments → `runs.start
|
|
135
|
-
|
|
134
|
+
5. **Durable vs ephemeral:** experiments → `runs.start`. Shared production
|
|
135
|
+
agents and workflows → JSON in `lunnoa/` (`npx lunnoa-client status`,
|
|
136
|
+
`pull`, `push`), `codeSource: synced`. TypeScript
|
|
137
|
+
`workflows deploy` / `agents deploy` only when the definition needs
|
|
138
|
+
code (`codeSource: generated`, locked in the UI). A `.json` deploy is
|
|
139
|
+
a push. `codeSource` replaces `managedByCode` on agents and workflows.
|
|
140
|
+
Ask the human before `--force`. Commit `lunnoa/lunnoa.lock.json`.
|
|
136
141
|
6. **UI is headless.** `@lunnoa/client` has no design system. Optional React
|
|
137
142
|
hooks in `@lunnoa/client/react`. Partners bring their own UI.
|
|
138
143
|
7. **Reuse before create.** Before proposing any new entity type, table,
|
|
@@ -176,6 +181,7 @@ can execute. Use this shape:
|
|
|
176
181
|
- [ ] `npx @lunnoa/client ai-connections list --json` before choosing an agent model
|
|
177
182
|
- [ ] agents / workflows / entity-types / tables list — reuse before create
|
|
178
183
|
- [ ] codegen — if building typed entity/workflow accessors
|
|
184
|
+
- [ ] `npx lunnoa-client status` before editing `lunnoa/`, then `pull` before `push`
|
|
179
185
|
|
|
180
186
|
### Resources (every row says REUSED or CREATED)
|
|
181
187
|
| Kind | Slug / id | Reused / created | Notes |
|
|
@@ -188,7 +194,8 @@ can execute. Use this shape:
|
|
|
188
194
|
|
|
189
195
|
### Runtime paths
|
|
190
196
|
- Ad-hoc: actions.run / runs.start / workflows.execute
|
|
191
|
-
-
|
|
197
|
+
- Sync: JSON in `lunnoa/`. `npx lunnoa-client status`, then pull, then push. Commit `lunnoa/lunnoa.lock.json`. No `--force` without a yes from the human.
|
|
198
|
+
- TypeScript deploy (generated, locked in the UI): npx lunnoa-client workflows|agents deploy …
|
|
192
199
|
- Chat: agent stream-message / useAgentChat
|
|
193
200
|
|
|
194
201
|
### UI surfaces (headless)
|